--- /dev/null +++ b/drivers/net/ethernet/cns21xx/cns21xx_gec_main.c @@ -0,0 +1,2464 @@ +/* + * Copyright (c) 2010-2012 Gabor Juhos + * + * This driver has been derived from the ethernet driver of the + * Star STR81xx SoC. + * Copyright (c) 2008 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "cns21xx-gec" + +/* VSC8601 and WavePlus Phy are the same */ +#define CNS21XX_GEC_PHY_ADDR 0 + +#define CNS21XX_GEC_TX_HW_CHECKSUM +#define CNS21XX_GEC_RX_HW_CHECKSUM + +#define CNS21XX_PEND_INT_COUNT 16 +#define CNS21XX_PEND_INT_TIME 5 /* 5 x 20 usecs */ + +#define MAX_PACKET_LEN 1536 + +#define CNS21XX_GEC_NUM_TXDS 48 /* FIXME: original 64 will cause UDP fail */ +#define CNS21XX_GEC_NUM_RXDS 64 + +struct cns21xx_gec_mib_info { + u32 mib_rx_ok_pkt; + u64 mib_rx_ok_byte; + u32 mib_rx_runt; + u32 mib_rx_over_size; + u32 mib_rx_no_buffer_drop; + u32 mib_rx_crc_err; + u32 mib_rx_arl_drop; + u32 mib_rx_myvid_drop; + u32 mib_rx_csum_err; + u32 mib_rx_pause_frame; + u32 mib_tx_ok_pkt; + u64 mib_tx_ok_byte; + u32 mib_tx_pause_frame; +}; + +/* + * Network Driver, Receive/Send and Initial Buffer Function + */ +struct cns21xx_gec_txd { + /* 1st 32Bits */ + u32 sdp; + + /* 2nd 32Bits */ + u32 length:16; + u32 reserved0:7; + u32 tco:1; + u32 uco:1; + u32 ico:1; + u32 insv:1; + u32 intr:1; + u32 ls:1; + u32 fs:1; + u32 eor:1; + u32 cown:1; + + /* 3rd 32Bits */ + u32 vid:12; + u32 cfi:1; + u32 pri:3; + u32 epid:16; + + /* 4th 32Bits */ + u32 reserved1; +} __packed; + +struct cns21xx_gec_rxd { + /* 1st 32Bits */ + u32 sdp; + + /* 2nd 32Bits */ + u32 length:16; + u32 l4f:1; + u32 ipf:1; + u32 prot:2; + u32 vted:1; + u32 mymac:1; + u32 hhit:1; + u32 rmc:1; + u32 crce:1; + u32 osize:1; + u32 reserved0:2; + u32 ls:1; + u32 fs:1; + u32 eor:1; + u32 cown:1; + + /* 3rd 32Bits */ + u32 vid:12; + u32 cfi:1; + u32 pri:3; + u32 epid:16; + + /* 4th 32Bits */ + u32 reserved1; +} __packed; + +struct cns21xx_gec_ring { + u32 desc_dma; + void *desc_cpu; + u32 curr; + u32 dirty; + u32 count; + struct sk_buff **skbs; +}; + +#define CNS21XX_GEC_NUM_VLANS 4 +struct cns21xx_gec_vlan { + u32 vid; /* 0~4095 */ + u32 control; /* ENABLE or DISABLE */ +}; + +/* store this information for the driver.. */ +struct cns21xx_gec { + struct napi_struct napi; + struct net_device *netdev; + struct device *parent; + struct cns21xx_gec_ring txring; + struct cns21xx_gec_ring rxring; + + void __iomem *base; + struct resource *mem_res; + struct cns21xx_gec_plat_data *pdata; + spinlock_t lock; + spinlock_t tx_lock; + + int status_irq; + int rxrc_irq; + int rxqf_irq; + int txtc_irq; + int txqe_irq; + unsigned long rx_queue_full; + + struct cns21xx_gec_vlan vlans[CNS21XX_GEC_NUM_VLANS]; + + struct timer_list internal_phy_timer; + struct timer_list nic_timer; + u8 phy_addr; + u16 phy_id; + struct cns21xx_gec_mib_info mib_info; +}; + +#define GEC_REG_PHY_CTRL0 0x000 +#define GEC_REG_PHY_CTRL1 0x004 +#define GEC_REG_MAC_CFG 0x008 +#define GEC_REG_FC_CFG 0x00c +#define GEC_REG_ARL_CFG 0x010 +#define GEC_REG_MY_MAC_H 0x014 +#define GEC_REG_MY_MAC_L 0x018 +#define GEC_REG_HASH_CTRL 0x01c +#define GEC_REG_VLAN_CTRL 0x020 +#define GEC_REG_VLAN_ID_0_1 0x024 +#define GEC_REG_VLAN_ID_2_3 0x028 +#define GEC_REG_DMA_CFG 0x030 +#define GEC_REG_TX_DMA_CTRL 0x034 +#define GEC_REG_RX_DMA_CTRL 0x038 +#define GEC_REG_TX_DPTR 0x03c +#define GEC_REG_RX_DPTR 0x040 +#define GEC_REG_TX_BASE_ADDR 0x044 +#define GEC_REG_RX_BASE_ADDR 0x048 +#define GEC_REG_DLY_INT_CFG 0x04c +#define GEC_REG_INT 0x050 +#define GEC_REG_INT_MASK 0x054 +#define GEC_REG_TEST0 0x058 +#define GEC_REG_TEST1 0x05c +#define GEC_REG_EXTEND_CFG 0x060 + +#define GEC_REG_RX_OK_PKT_CNTR 0x100 +#define GEC_REG_RX_OK_BYTE_CNTR 0x104 +#define GEC_REG_RX_RUNT_BYTE_CNTR 0x108 +#define GEC_REG_RX_OSIZE_DROP_PKT_CNTR 0x10c +#define GEC_REG_RX_NO_BUF_DROP_PKT_CNTR 0x110 +#define GEC_REG_RX_CRC_ERR_PKT_CNTR 0x114 +#define GEC_REG_RX_ARL_DROP_PKT_CNTR 0x118 +#define GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR 0x11c +#define GEC_REG_RX_CHKSUM_ERR_PKT_CNTR 0x120 +#define GEC_REG_RX_PAUSE_FRAME_PKT_CNTR 0x124 +#define GEC_REG_TX_OK_PKT_CNTR 0x128 +#define GEC_REG_TX_OK_BYTE_CNTR 0x12c +#define GEC_REG_TX_COLLISION_CNTR 0x130 +#define GEC_REG_TX_PAUSE_FRAME_CNTR 0x130 +#define GEC_REG_TX_FIFO_UNDERRUN_RETX_CNTR 0x134 + +#define GEC_INT_MIB_COUNTER_TH BIT(3) +#define GEC_INT_PORT_STATUS_CHG BIT(2) + +#define FE_PHY_LED_MODE (0x1 << 12) + +static void internal_phy_init_timer(struct cns21xx_gec *gec); +static void internal_phy_start_timer(struct cns21xx_gec *gec); +static void internal_phy_stop_timer(struct cns21xx_gec *gec); + +static void cns21xx_gec_phy_powerdown(struct cns21xx_gec *gec); +static void cns21xx_gec_phy_powerup(struct cns21xx_gec *gec); + +static inline u32 cns21xx_gec_rr(struct cns21xx_gec *gec, unsigned int reg) +{ + return __raw_readl(gec->base + reg); +} + +static inline void cns21xx_gec_wr(struct cns21xx_gec *gec, unsigned int reg, + u32 val) +{ + __raw_writel(val, gec->base + reg); +} + +static void cns21xx_gec_timer_func(unsigned long data) +{ + struct cns21xx_gec *gec = (struct cns21xx_gec *) data; + struct cns21xx_gec_ring *txring = &gec->txring; + int i; + int txsd_index; + int txsd_current; + int skb_free_count = 0; + struct cns21xx_gec_txd *txd; + unsigned long flags; + + local_irq_save(flags); + txsd_current = cns21xx_gec_rr(gec, GEC_REG_TX_DPTR); + txsd_index = (txsd_current - (u32)txring->desc_dma) >> 4; + if (txsd_index > txring->dirty) { + skb_free_count = txsd_index - txring->dirty; + } else if (txsd_index <= txring->dirty) { + skb_free_count = txring->count + txsd_index - + txring->dirty; + } + for (i = 0; i < skb_free_count; i++) { + txd = ((struct cns21xx_gec_txd *) txring->desc_cpu) + + txring->dirty; + + if (txd->cown == 0) + break; + + if (txring->skbs[txring->dirty]) { + dev_kfree_skb_any(txring->skbs[txring->dirty]); + txring->skbs[txring->dirty] = NULL; + + dma_unmap_single(gec->parent, + txd->sdp, + txd->length, + DMA_TO_DEVICE); + } + + txring->dirty++; + if (txring->dirty == txring->count) + txring->dirty = 0; + } + local_irq_restore(flags); +} + +static void __init cns21xx_gec_timer_init(struct cns21xx_gec *gec) +{ + init_timer(&gec->nic_timer); + gec->nic_timer.function = &cns21xx_gec_timer_func; + gec->nic_timer.data = (unsigned long) gec; +} + +static void cns21xx_gec_timer_modify(struct cns21xx_gec *gec, unsigned int t) +{ + mod_timer(&gec->nic_timer, jiffies + t); +} + +static int cns21xx_gec_write_phy(struct cns21xx_gec *gec, + u8 addr, u8 reg, u16 val) +{ + int i; + + if (addr > 31 || reg > 31) + return -EINVAL; + + /* clear previous rw_ok status */ + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, 0x1 << 15); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, + addr | (reg << 8) | (val << 16) | (0x1 << 13)); + + for (i = 0; i < 10000; i++) { + u32 status; + + status = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL0); + if (status & (0x1 << 15)) { + /* + * clear the rw_ok status, + * and clear other bits value + */ + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, (0x1 << 15)); + return 0; + } + udelay(1000); + } + + dev_err(&gec->netdev->dev, + "%s timed out, phy_addr:0x%x, phy_reg:0x%x, write_data:0x%x\n", + __func__, addr, reg, val); + + return -EIO; +} + +static int cns21xx_gec_read_phy(struct cns21xx_gec *gec, + u8 addr, u8 reg, u16 *val) +{ + int i; + + if (addr > 31 || reg > 31) + return -EINVAL; + + /* clear previous rw_ok status */ + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, 0x1 << 15); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, + addr | (reg << 8) | (0x1 << 14)); + + for (i = 0; i < 10000; i++) { + u32 status; + + status = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL0); + if (status & (0x1 << 15)) { + /* + * clear the rw_ok status, + * and clear other bits value + */ + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, (0x1 << 15)); + *val = (status >> 16) & 0xffff; + return 0; + } + udelay(1000); + } + + dev_err(&gec->netdev->dev, + "%s timed out, phy_addr:0x%x, phy_reg:0x%x\n", + __func__, addr, reg); + + *val = 0xffff; + return -EIO; +} + +static void cns21xx_gec_dma_config(struct cns21xx_gec *gec) +{ + u32 dma_config = 0; + + dma_config = cns21xx_gec_rr(gec, GEC_REG_DMA_CFG); + + /* Config TX DMA */ + /* TX auto polling: 1 us */ + dma_config &= ~(0x3 << 6); + /* TX auto polling :100us */ + dma_config |= (0x2 << 6); + /* TX auto polling C-bit enable */ + dma_config |= (0x1 << 5); + /* TX can transmit packets, No suspend */ + dma_config &= ~(0x1 << 4); + + /* Config RX DMA */ + /* RX auto polling: 1 us */ + dma_config &= ~(0x3 << 2); + /* RX auto polling :100us */ + dma_config |= (0x2 << 2); + /* RX auto polling C-bit enable */ + dma_config |= (0x1 << 1); + /* RX can receive packets, No suspend */ + dma_config &= ~0x1; + + /* 4N+2(for Linux) */ + dma_config &= ~(0x1 << 16); + + cns21xx_gec_wr(gec, GEC_REG_DMA_CFG, dma_config); +} + +static void cns21xx_gec_mac_config(struct cns21xx_gec *gec) +{ + u32 mac_config; + + mac_config = cns21xx_gec_rr(gec, GEC_REG_MAC_CFG); + +#ifdef CNS21XX_GEC_TX_HW_CHECKSUM + /* Tx ChkSum offload On: TCP/UDP/IP */ + mac_config |= (0x1 << 26); +#else + /* Tx ChkSum offload Off: TCP/UDP/IP */ + mac_config &= ~(0x1 << 26); +#endif + +#ifdef CNS21XX_GEC_RX_HW_CHECKSUM + /* Rx ChkSum offload On: TCP/UDP/IP */ + mac_config |= (0x1 << 25); +#else + /* Rx ChkSum offload Off: TCP/UDP/IP */ + mac_config &= ~(0x1 << 25); +#endif + + /* Accept CSUM error pkt */ + mac_config |= (0x1 << 24); + /* IST disable */ + mac_config &= ~(0x1 << 23); + /* Strip vlan tag */ + mac_config |= (0x1 << 22); + /* Accept CRC error pkt */ + mac_config |= (0x1 << 21); + /* CRC strip */ + mac_config |= (0x1 << 20); + + /* Discard oversize pkt */ + mac_config &= ~(0x1 << 18); + + /* clear, set 1518 */ + mac_config &= ~(0x3 << 16); + + /* 1536 */ + mac_config |= (0x2 << 16); + + /* IPG */ + mac_config |= (0x1f << 10); + + /* Do not skip 16 consecutive collisions pkt */ + /* allow to re-tx */ + mac_config |= (0x1 << 9); + /* Fast retry */ + mac_config |= (0x1 << 8); + + cns21xx_gec_wr(gec, GEC_REG_MAC_CFG, mac_config); +} + +static void cns21xx_gec_fc_config(struct cns21xx_gec *gec) +{ + u32 fc_config; + + fc_config = cns21xx_gec_rr(gec, GEC_REG_FC_CFG); + + /* Send pause on frame threshold */ + /* Clear */ + fc_config &= ~(0xfff << 16); + fc_config |= (0x360 << 16); + /* Disable UC_PAUSE */ + fc_config &= ~(0x1 << 8); + /* Enable Half Duplex backpressure */ + fc_config |= (0x1 << 7); + /* Collision-based BP */ + fc_config &= ~(0x1 << 6); + /* Disable max BP collision */ + fc_config &= ~(0x1 << 5); + /* Clear */ + fc_config &= ~(0x1f); + /* Set */ + fc_config |= (0xc); + + cns21xx_gec_wr(gec, GEC_REG_FC_CFG, fc_config); +} + +static void cns21xx_gec_internal_phy_config(struct cns21xx_gec *gec) +{ + u32 phy_ctrl1; + u32 phy_addr; + + dev_info(&gec->netdev->dev, "Internal PHY\n"); + + phy_addr = CNS21XX_GEC_PHY_ADDR; + gec->phy_addr = phy_addr; + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + + /* set phy addr for auto-polling */ + phy_ctrl1 |= (phy_addr & 0x1f) << 24; + + /* set internal phy mode */ + /* internel 10/100 phy */ + phy_ctrl1 |= 0x1 << 18; + + /* MII */ + phy_ctrl1 &= ~(0x1 << 17); + + /* MAC mode */ + phy_ctrl1 &= ~(0x1 << 16); + + /* config PHY LED bit[13:12] */ + cns21xx_gec_read_phy(gec, phy_addr, 31, (u16 *)(&phy_ctrl1)); + /* clear LED control */ + phy_ctrl1 &= ~(0x3 << 12); + phy_ctrl1 |= FE_PHY_LED_MODE; + cns21xx_gec_write_phy(gec, phy_addr, 31, phy_ctrl1); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); +} + +static void cns21xx_gec_vsc8601_phy_config(struct cns21xx_gec *gec) +{ + u32 phy_ctrl1; + u32 phy_addr; + u16 phy_data; + + phy_addr = CNS21XX_GEC_PHY_ADDR; + gec->phy_addr = phy_addr; + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + + /* phy addr for auto-polling */ + phy_ctrl1 |= phy_addr << 24; + + /* set external phy mode */ + phy_ctrl1 &= ~(0x1 << 18); + + /* set RGMII */ + phy_ctrl1 |= (0x1 << 17); + + /* set MII interface */ + phy_ctrl1 &= ~(0x1 << 16); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); + + /* set phy addr for auto-polling */ + phy_ctrl1 |= phy_addr << 24; + + /* set external phy mode */ + /* MII/RGMII interface */ + phy_ctrl1 &= ~(0x1 << 18); + + /* RGMII */ + phy_ctrl1 |= (0x1 << 17); + + /* MAC mode */ + phy_ctrl1 &= ~(0x1 << 16); + + cns21xx_gec_read_phy(gec, phy_addr, 3, &phy_data); + if ((phy_data & 0x000f) == 0x0000) { + /* type A chip */ + u16 tmp16; + + dev_info(&gec->netdev->dev, "VSC8601 Type A Chip\n"); + cns21xx_gec_write_phy(gec, phy_addr, 31, 0x52B5); + cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF8A); + + phy_data = 0x0; + cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16); + phy_data |= (tmp16 & ~0x0); + cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data); + + phy_data = 0x0008; + cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16); + phy_data |= (tmp16 & ~0x000C); + cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data); + + cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F8A); + cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF86); + + phy_data = 0x0008; + cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16); + phy_data |= (tmp16 & ~0x000C); + cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data); + + phy_data = 0x0; + cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16); + phy_data |= (tmp16 & ~0x0); + cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data); + + cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F8A); + + cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF82); + + phy_data = 0x0; + cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16); + phy_data |= (tmp16 & ~0x0); + cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data); + + phy_data = 0x0100; + cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16); + phy_data |= (tmp16 & ~0x0180); + cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data); + + cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F82); + + cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0); + + /* Set port type: single port */ + cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data); + phy_data &= ~(0x1 << 10); + cns21xx_gec_write_phy(gec, phy_addr, 9, phy_data); + } else if ((phy_data & 0x000f) == 0x0001) { + /* type B chip */ + dev_info(&gec->netdev->dev, "VSC8601 Type B Chip\n"); + + cns21xx_gec_read_phy(gec, phy_addr, 23, &phy_data); + phy_data |= (0x1 << 8); /* set RGMII timing skew */ + cns21xx_gec_write_phy(gec, phy_addr, 23, phy_data); + } + + /* change to extened registers */ + cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0001); + + cns21xx_gec_read_phy(gec, phy_addr, 28, &phy_data); + phy_data &= ~(0x3 << 14); /* set RGMII TX timing skew */ + phy_data |= (0x3 << 14); /* 2.0ns */ + phy_data &= ~(0x3 << 12); /* set RGMII RX timing skew */ + phy_data |= (0x3 << 12); /* 2.0ns */ + cns21xx_gec_write_phy(gec, phy_addr, 28, phy_data); + + /* change to normal registers */ + cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0000); + +#if 0 + /* set TX and RX clock skew */ + cns21xx_gec_wr(gec, GEC_REG_TEST0, (0x2 << 2) | (0x2 << 0)); +#endif + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); +} + +static void cns21xx_gec_ip101a_phy_config(struct cns21xx_gec *gec) +{ + u32 phy_ctrl1; + u32 phy_addr; + + dev_info(&gec->netdev->dev, "ICPlus IP101A\n"); + + phy_addr = 1; + gec->phy_addr = phy_addr; + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + + /* set phy addr for auto-polling */ + phy_ctrl1 |= phy_addr << 24; + + /* set external phy mode */ + /* MII/RGMII interface */ + phy_ctrl1 &= ~(0x1 << 18); + + /* MII */ + phy_ctrl1 &= ~(0x1 << 17); + + /* MAC mode */ + phy_ctrl1 &= ~(0x1 << 16); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); +} + +static void cns21xx_gec_ip1001_phy_config(struct cns21xx_gec *gec) +{ + u32 phy_ctrl1; + u32 phy_addr; + u16 phy_data; + + dev_info(&gec->netdev->dev, "ICPlus IP1001\n"); + + phy_addr = 1; + gec->phy_addr = phy_addr; + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + + /* set phy addr for auto-polling */ + phy_ctrl1 |= phy_addr << 24; + + /* set external phy mode */ + /* MII/RGMII interface */ + phy_ctrl1 &= ~(0x1 << 18); + + /* RGMII */ + phy_ctrl1 |= (0x1 << 17); + + /* MAC mode */ + phy_ctrl1 &= ~(0x1 << 16); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); + cns21xx_gec_read_phy(gec, phy_addr, 2, &phy_data); + + /* set AN capability */ + cns21xx_gec_read_phy(gec, phy_addr, 4, &phy_data); + /* clear existing values */ + phy_data &= ~(0xf << 5); + /* 10Half */ + phy_data |= (0x1 << 5); + /* 10Full */ + phy_data |= (0x1 << 6); + /* 100Half */ + phy_data |= (0x1 << 7); + /* 100Full */ + phy_data |= (0x1 << 8); + /* FC on */ + phy_data |= (0x1 << 10); + cns21xx_gec_write_phy(gec, phy_addr, 4, phy_data); + + cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data); + /* 1000Full on */ + phy_data |= (0x1 << 9); + phy_data &= ~(0x1 << 10); + phy_data |= (0x1 << 12); + cns21xx_gec_write_phy(gec, phy_addr, 9, phy_data); + + cns21xx_gec_read_phy(gec, phy_addr, 16, &phy_data); + /* Smart function off */ + phy_data &= ~(0x1 << 11); + /* TX delay */ + phy_data |= (0x1 << 0); + /* RX delay */ + phy_data |= (0x1 << 1); + cns21xx_gec_write_phy(gec, phy_addr, 16, phy_data); + + cns21xx_gec_read_phy(gec, phy_addr, 16, &phy_data); + +#if 0 + cns21xx_gec_read_phy(gec, phy_addr, 20, &phy_data); + phy_data &= ~(0x1<<2); + + phy_data |= (0x1<<9); + cns21xx_gec_write_phy(gec, phy_addr, 20, phy_data); +#endif + + cns21xx_gec_read_phy(gec, phy_addr, 0, &phy_data); + phy_data |= (0x1 << 9); /* re-AN */ + cns21xx_gec_write_phy(gec, phy_addr, 0, phy_data); + + cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); +} + +static int cns21xx_gec_phy_config(struct cns21xx_gec *gec) +{ + u32 phy_ctrl1; + + switch (gec->pdata->phy_type) { + case CNS21XX_GEC_PHY_TYPE_INTERNAL: + cns21xx_gec_internal_phy_config(gec); + break; + + case CNS21XX_GEC_PHY_TYPE_VSC8601: + cns21xx_gec_vsc8601_phy_config(gec); + break; + + case CNS21XX_GEC_PHY_TYPE_IP101A: + cns21xx_gec_ip101a_phy_config(gec); + break; + + case CNS21XX_GEC_PHY_TYPE_IP1001: + cns21xx_gec_ip1001_phy_config(gec); + break; + + default: + return -EINVAL; + } + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + + /* AN On */ + phy_ctrl1 |= (0x1 << 8); + if (!((phy_ctrl1 >> 8) & 0x1)) { /* AN disable */ + /* Force to FullDuplex mode */ + phy_ctrl1 &= ~(0x1 << 11); /* Half */ + + /* Force to 100Mbps mode */ + phy_ctrl1 &= ~(0x3 << 9); /* clear to 10M */ + phy_ctrl1 |= (0x1 << 9); /* set to 100M */ + } + + /* Force TX FlowCtrl On,in 1000M */ + phy_ctrl1 |= (0x1 << 13); + + /* Force TX FlowCtrl On, in 10/100M */ + phy_ctrl1 |= (0x1 << 12); + + /* Enable MII auto polling */ + phy_ctrl1 &= ~(0x1 << 7); + + cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1); + cns21xx_gec_phy_powerdown(gec); + + return 0; +} + +static void cns21xx_gec_vlan_config(struct cns21xx_gec *gec) +{ + /* setup VLAN entries */ + gec->vlans[0].vid = 2; + gec->vlans[0].control = 0; + gec->vlans[1].vid = 2; + gec->vlans[1].control = 1; + gec->vlans[2].vid = 1; + gec->vlans[2].control = 1; + gec->vlans[3].vid = 1; + gec->vlans[3].control = 0; + + cns21xx_gec_wr(gec, GEC_REG_VLAN_ID_0_1, + (gec->vlans[0].vid & 0x0fff) | + ((gec->vlans[1].vid & 0x0fff) << 16)); + + cns21xx_gec_wr(gec, GEC_REG_VLAN_ID_2_3, + (gec->vlans[2].vid & 0x0fff) | + ((gec->vlans[3].vid & 0x0fff) << 16)); + + cns21xx_gec_wr(gec, GEC_REG_VLAN_CTRL, + (gec->vlans[0].control << 0) | + (gec->vlans[1].control << 1) | + (gec->vlans[2].control << 2) | + (gec->vlans[3].control << 3)); +} + +static int cns21xx_gec_arl_config(struct cns21xx_gec *gec) +{ + u32 arl_config; + + arl_config = cns21xx_gec_rr(gec, GEC_REG_ARL_CFG); + + /* Misc Mode ON */ + arl_config |= (0x1 << 4); + + /* My MAC only enable */ + arl_config |= (0x1 << 3); + + /* Learn SA On */ + arl_config &= ~(0x1 << 2); + + /* Forward MC to CPU */ + arl_config &= ~(0x1 << 1); + + /* Hash direct mode */ + arl_config &= ~(0x1); + + cns21xx_gec_wr(gec, GEC_REG_ARL_CFG, arl_config); + + return 0; +} + +static void cns21xx_gec_phy_powerdown(struct cns21xx_gec *gec) +{ + u16 phy_data = 0; + + cns21xx_gec_read_phy(gec, gec->phy_addr, 0, &phy_data); + phy_data |= (0x1 << 11); + cns21xx_gec_write_phy(gec, gec->phy_addr, 0, phy_data); + + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); + PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15); +} + +static void cns21xx_gec_phy_powerup(struct cns21xx_gec *gec) +{ + u16 phy_data = 0; + + cns21xx_gec_read_phy(gec, gec->phy_addr, 0, &phy_data); + phy_data &= ~(0x1 << 11); + cns21xx_gec_write_phy(gec, gec->phy_addr, 0, phy_data); + + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); +} + +static void cns21xx_gec_enable(struct cns21xx_gec *gec) +{ + /* start Rx DMA */ + cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 1); + + cns21xx_gec_phy_powerup(gec); + internal_phy_start_timer(gec); +} + +static void cns21xx_gec_shutdown(struct cns21xx_gec *gec) +{ + /* stop Rx and Tx DMA */ + cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 0); + cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 0); + + internal_phy_stop_timer(gec); +} + +static irqreturn_t cns21xx_gec_receive_isr(int irq, void *dev_id) +{ + struct cns21xx_gec *gec = dev_id; + + if (!test_bit(NAPI_STATE_SCHED, &gec->napi.state)) { + if (likely(napi_schedule_prep(&gec->napi))) + __napi_schedule(&gec->napi); + } + + return IRQ_HANDLED; +} + +static int cns21xx_gec_install_receive_isr(struct cns21xx_gec *gec) +{ + int err; + + err = request_irq(gec->rxrc_irq, cns21xx_gec_receive_isr, + IRQF_SHARED, dev_name(&gec->netdev->dev), gec); + if (err) + dev_err(&gec->netdev->dev, + "unable to get IRQ %d (err=%d)\n", + gec->rxrc_irq, err); + + return err; +} + +static void cns21xx_gec_uninstall_receive_isr(struct cns21xx_gec *gec) +{ + free_irq(gec->rxrc_irq, gec); +} + +static irqreturn_t cns21xx_gec_rxqf_isr(int irq, void *dev_id) +{ + struct cns21xx_gec *gec = dev_id; + + /* + * because in normal state, fsql only invoke once + * and set_bit is atomic function, so don't mask it + */ + set_bit(0, &gec->rx_queue_full); + if (!test_bit(NAPI_STATE_SCHED, &gec->napi.state)) { + if (likely(napi_schedule_prep(&gec->napi))) + __napi_schedule(&gec->napi); + } + + return IRQ_HANDLED; +} + +static int cns21xx_gec_install_rxqf_isr(struct cns21xx_gec *gec) +{ + int err; + + /* QUEUE full interrupt handler */ + err = request_irq(gec->rxqf_irq, cns21xx_gec_rxqf_isr, + IRQF_SHARED, dev_name(&gec->netdev->dev), gec); + if (err) + dev_err(&gec->netdev->dev, + "unable to get IRQ %d (err=%d)\n", + gec->rxqf_irq, err); + + return err; +} + +static void cns21xx_gec_uninstall_rxqf_isr(struct cns21xx_gec *gec) +{ + free_irq(gec->rxqf_irq, gec); +} + +static void cns21xx_gec_mib_reset(struct cns21xx_gec *gec) +{ + unsigned long flags; + + local_irq_save(flags); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_OK_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_OK_BYTE_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_RUNT_BYTE_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_OSIZE_DROP_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_NO_BUF_DROP_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_CRC_ERR_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_ARL_DROP_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_CHKSUM_ERR_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_RX_PAUSE_FRAME_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_TX_OK_PKT_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_TX_OK_BYTE_CNTR); + (void) cns21xx_gec_rr(gec, GEC_REG_TX_PAUSE_FRAME_CNTR); + local_irq_restore(flags); +} + +static void cns21xx_gec_mib_read(struct cns21xx_gec *gec) +{ + unsigned long flags; + + local_irq_save(flags); + gec->mib_info.mib_rx_ok_pkt += + cns21xx_gec_rr(gec, GEC_REG_RX_OK_PKT_CNTR); + gec->mib_info.mib_rx_ok_byte += + cns21xx_gec_rr(gec, GEC_REG_RX_OK_BYTE_CNTR); + gec->mib_info.mib_rx_runt += + cns21xx_gec_rr(gec, GEC_REG_RX_RUNT_BYTE_CNTR); + gec->mib_info.mib_rx_over_size += + cns21xx_gec_rr(gec, GEC_REG_RX_OSIZE_DROP_PKT_CNTR); + gec->mib_info.mib_rx_no_buffer_drop += + cns21xx_gec_rr(gec, GEC_REG_RX_NO_BUF_DROP_PKT_CNTR); + gec->mib_info.mib_rx_crc_err += + cns21xx_gec_rr(gec, GEC_REG_RX_CRC_ERR_PKT_CNTR); + gec->mib_info.mib_rx_arl_drop += + cns21xx_gec_rr(gec, GEC_REG_RX_ARL_DROP_PKT_CNTR); + gec->mib_info.mib_rx_myvid_drop += + cns21xx_gec_rr(gec, GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR); + gec->mib_info.mib_rx_csum_err += + cns21xx_gec_rr(gec, GEC_REG_RX_CHKSUM_ERR_PKT_CNTR); + gec->mib_info.mib_rx_pause_frame += + cns21xx_gec_rr(gec, GEC_REG_RX_PAUSE_FRAME_PKT_CNTR); + gec->mib_info.mib_tx_ok_pkt += + cns21xx_gec_rr(gec, GEC_REG_TX_OK_PKT_CNTR); + gec->mib_info.mib_tx_ok_byte += + cns21xx_gec_rr(gec, GEC_REG_TX_OK_BYTE_CNTR); + gec->mib_info.mib_tx_pause_frame += + cns21xx_gec_rr(gec, GEC_REG_TX_PAUSE_FRAME_CNTR); + local_irq_restore(flags); +} + +static const char *cns21xx_gec_speed_str(u32 phy_ctrl1) +{ + switch ((phy_ctrl1 >> 2) & 3) { + case 0: + return "10"; + case 1: + return "100"; + case 2: + return "1000"; + } + + return "???"; +} + +static irqreturn_t cns21xx_gec_status_isr(int irq, void *dev_id) +{ + struct cns21xx_gec *gec = dev_id; + u32 int_status; + + int_status = cns21xx_gec_rr(gec, GEC_REG_INT); + cns21xx_gec_wr(gec, GEC_REG_INT, int_status); + + /* flush write */ + (void) cns21xx_gec_rr(gec, GEC_REG_INT); + + if (int_status & GEC_INT_MIB_COUNTER_TH) + cns21xx_gec_mib_read(gec); + + if (int_status & GEC_INT_PORT_STATUS_CHG) { + u32 phy_ctrl1; + + phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1); + if (phy_ctrl1 & BIT(0)) { + netif_carrier_on(gec->netdev); + dev_info(&gec->netdev->dev, + "link up (%sMbps/%s duplex)\n", + cns21xx_gec_speed_str(phy_ctrl1), + (phy_ctrl1 & BIT(4)) ? "Full" : "Half"); + } else { + netif_carrier_off(gec->netdev); + dev_info(&gec->netdev->dev, "link down\n"); + } + } + + return IRQ_HANDLED; +} + +static inline void cns21xx_gec_enable_interrupt(struct cns21xx_gec *gec, + u32 mask) +{ + cns21xx_gec_wr(gec, GEC_REG_INT_MASK, + cns21xx_gec_rr(gec, GEC_REG_INT_MASK) & ~mask); +} + +static int cns21xx_gec_install_status_isr(struct cns21xx_gec *gec) +{ + int err; + + err = request_irq(gec->status_irq, cns21xx_gec_status_isr, + IRQF_DISABLED, dev_name(&gec->netdev->dev), gec); + + if (err) { + dev_err(&gec->netdev->dev, + "unable to get IRQ %d (err=%d)\n", + gec->status_irq, err); + return err; + } + + cns21xx_gec_enable_interrupt(gec, GEC_INT_MIB_COUNTER_TH | + GEC_INT_PORT_STATUS_CHG); + + return 0; +} + +static inline void cns21xx_gec_uninstall_status_isr(struct cns21xx_gec *gec) +{ + free_irq(gec->status_irq, gec); +} + +static void cns21xx_gec_uninstall_isr(struct cns21xx_gec *gec) +{ + cns21xx_gec_uninstall_rxqf_isr(gec); + cns21xx_gec_uninstall_status_isr(gec); + cns21xx_gec_uninstall_receive_isr(gec); +} + +static int cns21xx_gec_install_isr(struct cns21xx_gec *gec) +{ + int err; + + /* setup delayed interrupts */ + cns21xx_gec_wr(gec, GEC_REG_DLY_INT_CFG, + (1 << 16) | + ((CNS21XX_PEND_INT_COUNT & 0xFF) << 8) | + (CNS21XX_PEND_INT_TIME & 0xFF)); + + err = cns21xx_gec_install_receive_isr(gec); + if (err) + goto err_out; + + err = cns21xx_gec_install_rxqf_isr(gec); + if (err) + goto err_uninstall_receive; + + err = cns21xx_gec_install_status_isr(gec); + if (err) + goto err_uninstall_rxqf; + + return 0; + + err_uninstall_rxqf: + cns21xx_gec_uninstall_rxqf_isr(gec); + err_uninstall_receive: + cns21xx_gec_uninstall_receive_isr(gec); + err_out: + return err; +} + +static int cns21xx_gec_lan_open(struct net_device *dev) +{ + struct cns21xx_gec *gec = netdev_priv(dev); + int err; + + dev_dbg(&gec->netdev->dev, "open\n"); + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + napi_enable(&gec->napi); + netif_start_queue(dev); + err = cns21xx_gec_install_isr(gec); + if (err) + goto err; + + cns21xx_gec_enable(gec); + + return 0; + + err: + netif_stop_queue(dev); + napi_disable(&gec->napi); + return err; +} + +static void cns21xx_gec_timeout(struct net_device *dev) +{ + dev_dbg(&dev->dev, "timeout\n"); + netif_wake_queue(dev); + dev->trans_start = jiffies; +} + +static int cns21xx_gec_close(struct net_device *dev) +{ + struct cns21xx_gec *gec = netdev_priv(dev); + + cns21xx_gec_phy_powerdown(gec); + cns21xx_gec_uninstall_isr(gec); + napi_disable(&gec->napi); + netif_stop_queue(dev); + cns21xx_gec_shutdown(gec); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +static inline struct sk_buff *cns21xx_gec_alloc_skb(void) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(MAX_PACKET_LEN + 2); + + if (unlikely(!skb)) + return NULL; + + /* Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + */ + skb_reserve(skb, 2); /* 16 bit alignment */ + + return skb; +} + +static inline int cns21xx_gec_tx_dma_size(struct cns21xx_gec *gec) +{ + return gec->txring.count * sizeof(struct cns21xx_gec_txd); +} + +static inline int cns21xx_gec_rx_dma_size(struct cns21xx_gec *gec) +{ + return gec->rxring.count * sizeof(struct cns21xx_gec_rxd); +} + +static void __init cns21xx_gec_buffer_free(struct cns21xx_gec *gec) +{ + struct cns21xx_gec_ring *txring = &gec->txring; + struct cns21xx_gec_ring *rxring = &gec->rxring; + int i; + + if (rxring->desc_cpu) { + for (i = 0; i < rxring->count; i++) { + if (rxring->skbs[i]) + dev_kfree_skb(rxring->skbs[i]); + } + + dma_free_coherent(gec->parent, cns21xx_gec_rx_dma_size(gec), + rxring->desc_cpu, rxring->desc_dma); + memset((void *)&rxring, 0, cns21xx_gec_rx_dma_size(gec)); + } + + if (txring->desc_cpu) { + dma_free_coherent(gec->parent, cns21xx_gec_tx_dma_size(gec), + txring->desc_cpu, txring->desc_dma); + memset((void *)&txring, 0, cns21xx_gec_tx_dma_size(gec)); + } + + kfree(txring->skbs); + kfree(rxring->skbs); +} + +static int __init cns21xx_gec_buffer_alloc(struct cns21xx_gec *gec) +{ + struct cns21xx_gec_ring *txring = &gec->txring; + struct cns21xx_gec_ring *rxring = &gec->rxring; + struct cns21xx_gec_rxd *rxd; + struct cns21xx_gec_txd *txd; + struct sk_buff *skb; + int err = -ENOMEM; + int i; + + rxring->skbs = kzalloc(rxring->count * sizeof(struct skb *), + GFP_KERNEL); + + if (rxring->skbs == NULL) { + dev_err(&gec->netdev->dev, + "%s allocation failed\n", "RX buffer"); + goto err_out; + } + + txring->skbs = kzalloc(txring->count * sizeof(struct skb *), + GFP_KERNEL); + + if (txring->skbs == NULL) { + dev_err(&gec->netdev->dev, + "%s allocation failed\n", "TX buffer"); + goto err_out; + } + + rxring->desc_cpu = dma_alloc_coherent(gec->parent, + cns21xx_gec_rx_dma_size(gec), + &rxring->desc_dma, + GFP_KERNEL); + if (!rxring->desc_cpu) { + dev_err(&gec->netdev->dev, + "%s allocation failed\n", "RX ring"); + goto err_out; + } + + txring->desc_cpu = dma_alloc_coherent(gec->parent, + cns21xx_gec_tx_dma_size(gec), + &txring->desc_dma, + GFP_KERNEL); + if (!txring->desc_cpu) { + dev_err(&gec->netdev->dev, + "%s allocation failed\n", "TX ring"); + goto err_out; + } + + /* Clean RX Memory */ + memset((void *)rxring->desc_cpu, 0, cns21xx_gec_rx_dma_size(gec)); + dev_dbg(&gec->netdev->dev, + "rxring->desc_cpu=0x%08X rxring->desc_dma=0x%08X\n", + (u32) rxring->desc_cpu, + (u32) rxring->desc_dma); + + /* Set cur_index Point to Zero */ + rxring->curr = 0; + rxd = rxring->desc_cpu; + for (i = 0; i < rxring->count; i++, rxd++) { + if (i == (rxring->count - 1)) { + /* End bit == 0; */ + rxd->eor = 1; + } + skb = cns21xx_gec_alloc_skb(); + if (!skb) { + dev_err(&gec->netdev->dev, + "%s allocation failed\n", "skb"); + goto err_out; + } + + /* Trans Packet from Virtual Memory to Physical Memory */ + rxring->skbs[i] = skb; + rxd->sdp = dma_map_single(gec->parent, + skb->data, + MAX_PACKET_LEN, + DMA_TO_DEVICE); + rxd->length = MAX_PACKET_LEN; + } + + /* Clear TX Memory */ + memset((void *)txring->desc_cpu, 0, cns21xx_gec_tx_dma_size(gec)); + dev_dbg(&gec->netdev->dev, + "txring->desc_cpu=0x%08X txring->desc_dma=0x%08X\n", + (u32) txring->desc_cpu, + (u32) txring->desc_dma); + + /* Set cur_index Point to Zero */ + txring->curr = 0; + txd = txring->desc_cpu; + for (i = 0; i < txring->count; i++, txd++) { + if (i == (txring->count - 1)) { + /* End of Ring ==1 */ + txd->eor = 1; + } + /* TX Ring , Cown == 1 */ + txd->cown = 1; + +#ifdef CNS21XX_GEC_TX_HW_CHECKSUM + /* Enable Checksum */ + txd->ico = 1; + txd->uco = 1; + txd->tco = 1; +#else + txd->ico = 0; + txd->uco = 0; + txd->tco = 0; +#endif + /* clear txring->skbs */ + txring->skbs[i] = NULL; + } + + return 0; + +err_out: + cns21xx_gec_buffer_free(gec); + return err; +} + +static int cns21xx_gec_get_rfd_buff(struct cns21xx_gec *gec, int index) +{ + struct cns21xx_gec_ring *rxring = &gec->rxring; + struct cns21xx_gec_rxd *rxd; + struct sk_buff *skb; + unsigned char *data; + int len; + + /* TODO: get rxdesc ptr */ + rxd = ((struct cns21xx_gec_rxd *) rxring->desc_cpu) + index; + skb = rxring->skbs[index]; + + len = rxd->length; + + dma_unmap_single(gec->parent, rxd->sdp, len, + DMA_FROM_DEVICE); + + data = skb_put(skb, len); + + skb->dev = gec->netdev; + +#ifdef CNS21XX_GEC_RX_HW_CHECKSUM + if (rxd->ipf == 1 || rxd->l4f == 1) { + if (rxd->prot != 0x11) { + skb->ip_summed = CHECKSUM_NONE; + } else { + /* CheckSum Fail */ + skb->dev->stats.rx_errors++; + goto freepacket; + } + } else { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } +#else + skb->ip_summed = CHECKSUM_NONE; +#endif + + /* this line must, if no, packet will not send to network layer */ + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += len; + skb->dev->last_rx = jiffies; + + /* if netif_rx any package, will let this driver core dump. */ + netif_receive_skb(skb); + + return 0; + +freepacket: + dev_kfree_skb_any(skb); + return 0; +} + +static void cns21xx_gec_receive_packet(struct cns21xx_gec *gec, + int mode, int *work_done, int work_to_do) +{ + struct cns21xx_gec_ring *rxring = &gec->rxring; + int rxsd_index; + u32 rxsd_current; + struct cns21xx_gec_rxd *rxd; + struct sk_buff *skb; + int i, rxcount = 0; + + rxd = ((struct cns21xx_gec_rxd *) rxring->desc_cpu) + rxring->curr; + rxsd_current = cns21xx_gec_rr(gec, GEC_REG_RX_DPTR); + rxsd_index = (rxsd_current - (u32)rxring->desc_dma) >> 4; + + if (rxsd_index > rxring->curr) { + rxcount = rxsd_index - rxring->curr; + } else if (rxsd_index < rxring->curr) { + rxcount = (rxring->count - rxring->curr) + + rxsd_index; + } else { + if (rxd->cown == 0) { + goto receive_packet_exit; + } else { + /* Queue Full */ + rxcount = rxring->count; + } + } + + for (i = 0; i < rxcount; i++) { + if (*work_done >= work_to_do) + break; + + ++(*work_done); + + if (rxd->cown != 0) { + /* Alloc New skb_buff */ + skb = cns21xx_gec_alloc_skb(); + + /* Check skb_buff */ + if (skb != NULL) { + cns21xx_gec_get_rfd_buff(gec, rxring->curr); + rxring->skbs[rxring->curr] = skb; + rxd->sdp = + dma_map_single(gec->parent, + skb->data, + MAX_PACKET_LEN, + DMA_TO_DEVICE); + rxd->length = MAX_PACKET_LEN; + + /* set cbit to 0 for CPU Transfer */ + rxd->cown = 0; + } else { + /* + * TODO: I will add dev->lp.stats->rx_dropped, + * it will effect the performance + */ + dev_warn(&gec->netdev->dev, + "skb allocation failed, reuse the buffer\n"); + + /* set cbit to 0 for CPU Transfer */ + rxd->cown = 0; + return; + } + } else { +#if 0 + dev_err(&gec->netdev->dev, "encounter COWN == 0 BUG\n"); +#endif + } + + if (rxring->curr == (rxring->count - 1)) { + rxring->curr = 0; + rxd = rxring->desc_cpu; + } else { + rxring->curr++; + rxd++; + } + } + + receive_packet_exit: + return; +} + +static int cns21xx_gec_poll(struct napi_struct *napi, int budget) +{ + struct cns21xx_gec *gec; + int work_done = 0; + int work_to_do = budget; + + gec = container_of(napi, struct cns21xx_gec, napi); + cns21xx_gec_receive_packet(gec, 0, &work_done, work_to_do); + + budget -= work_done; + + /* if no Tx and not enough Rx work done, exit the polling mode */ + if (work_done) { + if (test_bit(0, &gec->rx_queue_full) == 1) { + /* queue full */ + clear_bit(0, &gec->rx_queue_full); + /* start Rx DMA */ + cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 1); + return 1; + } + } else { + napi_complete(&gec->napi); +#ifdef CONFIG_STAR_NIC_NAPI_MASK_IRQ + enable_irq(gec->rxrc_irq); +#endif + return 0; + } + + return work_done; +} + +static int cns21xx_gec_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct cns21xx_gec *gec = netdev_priv(dev); + struct cns21xx_gec_txd *txd; + struct cns21xx_gec_ring *txring = &gec->txring; + struct sk_buff *skb_free = NULL; + unsigned long flags; + unsigned int len; + + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + + len = max_t(unsigned int, skb->len, ETH_ZLEN); + + spin_lock_irqsave(&gec->tx_lock, flags); + + txd = ((struct cns21xx_gec_txd *) txring->desc_cpu) + txring->curr; + if (txd->cown == 0) { + /* This TFD is busy */ + spin_unlock_irqrestore(&gec->tx_lock, flags); + /* re-queue the skb */ + return NETDEV_TX_BUSY; + } + + if (txd->sdp != 0) { + /* MUST TODO: Free skbuff */ + skb_free = txring->skbs[txring->curr]; + + dma_unmap_single(gec->parent, + txd->sdp, + txd->length, + DMA_TO_DEVICE); + txring->dirty = txring->curr + 1; + if (txring->dirty == txring->count) + txring->dirty = 0; + } + +#ifdef CNS21XX_GEC_TX_HW_CHECKSUM + if (skb->protocol == __constant_htons(ETH_P_IP)) { + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { + txd->uco = 1; + txd->tco = 0; + } else if (ip_hdr(skb)->protocol == IPPROTO_TCP) { + txd->uco = 0; + txd->tco = 1; + } else { + txd->uco = 0; + txd->tco = 0; + } + } else { + txd->ico = 0; + txd->uco = 0; + txd->tco = 0; + } +#endif /* CNS21XX_GEC_TX_HW_CHECKSUM */ + + txring->skbs[txring->curr] = skb; + + txd->length = len; + txd->sdp = dma_map_single(gec->parent, skb->data, len, + DMA_TO_DEVICE); + + txd->fs = 1; + txd->ls = 1; + + /* Wake interrupt */ + txd->intr = 0; + txd->cown = 0; + + mb(); + + /* Start Tx DMA */ + cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 1); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + + txring->curr++; + if (txring->curr == txring->count) + txring->curr = 0; + + spin_unlock_irqrestore(&gec->tx_lock, flags); + + if (skb_free) + dev_kfree_skb(skb_free); + + cns21xx_gec_timer_modify(gec, 10); + + return NETDEV_TX_OK; +} + +static void cns21xx_gec_set_mac_addr(struct cns21xx_gec *gec, + const char *mac_addr) +{ + cns21xx_gec_wr(gec, GEC_REG_MY_MAC_H, + (mac_addr[0] << 8) | mac_addr[1]); + + cns21xx_gec_wr(gec, GEC_REG_MY_MAC_L, + (mac_addr[2] << 24) | (mac_addr[3] << 16) | + (mac_addr[4] << 8) | mac_addr[5]); + + dev_dbg(&gec->netdev->dev, "MAC address: %pM", mac_addr); +} + +static int cns21xx_gec_set_lan_mac_addr(struct net_device *dev, void *addr) +{ + struct sockaddr *sock_addr = addr; + struct cns21xx_gec *gec = netdev_priv(dev); + + spin_lock_irq(&gec->lock); + memcpy(dev->dev_addr, sock_addr->sa_data, 6); + cns21xx_gec_set_mac_addr(gec, sock_addr->sa_data); + spin_unlock_irq(&gec->lock); + + return 0; +} + +static int __init cns21xx_gec_setup(struct cns21xx_gec *gec) +{ + int err; + + /* set high */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); + /* set low */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15); + /* set high */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); + /* set NIC clock to 67.5MHz */ + PWRMGT_SYSTEM_CLOCK_CONTROL_REG |= (0x1 << 7); + + /* enable NIC clock */ + HAL_PWRMGT_ENABLE_NIC_CLOCK(); +#if 0 + cns21xx_gec_wr(gec, GEC_REG_MAC_CFG, 0x00527C00); +#endif + udelay(100); + + /* Configure GPIO for NIC MDC/MDIO pins */ + HAL_MISC_ENABLE_MDC_MDIO_PINS(); + HAL_MISC_ENABLE_NIC_COL_PINS(); + +#if 0 + MISC_GPIOA_PIN_ENABLE_REG |= (0x7 << 22); + MISC_FAST_ETHERNET_PHY_CONFIG_REG |= (FE_PHY_LED_MODE >> 12) & 0x3; + + /* set high */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); + /* set low */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15); + /* set high */ + PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15); +#endif + + /* disable all interrupt status sources */ + cns21xx_gec_wr(gec, GEC_REG_INT_MASK, ~(0)); + /* clear pending interrupts */ + cns21xx_gec_wr(gec, GEC_REG_INT, ~(0)); + + /* stop Rx and Tx DMA */ + cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 0); + cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 0); + + gec->txring.count = CNS21XX_GEC_NUM_TXDS; + gec->rxring.count = CNS21XX_GEC_NUM_RXDS; + err = cns21xx_gec_buffer_alloc(gec); + if (err) + return err; + + cns21xx_gec_mac_config(gec); + cns21xx_gec_fc_config(gec); + + err = cns21xx_gec_phy_config(gec); + if (err) { + cns21xx_gec_buffer_free(gec); + return err; + } + + cns21xx_gec_vlan_config(gec); + cns21xx_gec_arl_config(gec); + cns21xx_gec_set_mac_addr(gec, gec->netdev->dev_addr); + cns21xx_gec_mib_reset(gec); + + MISC_DEBUG_PROBE_SELECTION_REG = 0x00000125; /* 0x00000105 pb0_nic */ + + cns21xx_gec_wr(gec, GEC_REG_TX_DPTR, gec->txring.desc_dma); + cns21xx_gec_wr(gec, GEC_REG_TX_BASE_ADDR, gec->txring.desc_dma); + cns21xx_gec_wr(gec, GEC_REG_RX_DPTR, gec->rxring.desc_dma); + cns21xx_gec_wr(gec, GEC_REG_RX_BASE_ADDR, gec->rxring.desc_dma); + + cns21xx_gec_dma_config(gec); + internal_phy_init_timer(gec); + cns21xx_gec_timer_init(gec); + + return 0; +} + +static const struct net_device_ops cns21xx_gec_netdev_ops = { + .ndo_open = cns21xx_gec_lan_open, + .ndo_stop = cns21xx_gec_close, + .ndo_start_xmit = cns21xx_gec_send_packet, + .ndo_set_mac_address = cns21xx_gec_set_lan_mac_addr, + .ndo_tx_timeout = cns21xx_gec_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static int __init cns21xx_gec_probe(struct platform_device *pdev) +{ + struct net_device *netdev; + struct cns21xx_gec *gec; + struct cns21xx_gec_plat_data *pdata; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_dbg(&pdev->dev, "no platform data\n"); + err = -EINVAL; + goto err_out; + } + + if (!pdata->mac_addr) { + dev_dbg(&pdev->dev, "no mac address\n"); + err = -EINVAL; + goto err_out; + } + + netdev = alloc_etherdev(sizeof(struct cns21xx_gec)); + if (!netdev) { + err = -ENOMEM; + goto err_out; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + + gec = netdev_priv(netdev); + gec->pdata = pdata; + gec->parent = &pdev->dev; + + gec->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!gec->mem_res) { + dev_dbg(&pdev->dev, "no iomem resource\n"); + err = -EINVAL; + goto err_free_netdev; + } + + gec->status_irq = platform_get_irq_byname(pdev, + CNS21XX_GEC_STATUS_IRQ_NAME); + if (gec->status_irq < 0) { + dev_dbg(&pdev->dev, "%s irq not specified\n", + CNS21XX_GEC_STATUS_IRQ_NAME); + err = -EINVAL; + goto err_free_netdev; + } + + gec->rxrc_irq = platform_get_irq_byname(pdev, + CNS21XX_GEC_RXRC_IRQ_NAME); + if (gec->rxrc_irq < 0) { + dev_dbg(&pdev->dev, "%s irq not specified\n", + CNS21XX_GEC_RXRC_IRQ_NAME); + err = -EINVAL; + goto err_free_netdev; + } + + gec->rxqf_irq = platform_get_irq_byname(pdev, + CNS21XX_GEC_RXQF_IRQ_NAME); + if (gec->rxqf_irq < 0) { + dev_dbg(&pdev->dev, "%s irq not specified\n", + CNS21XX_GEC_RXQF_IRQ_NAME); + err = -EINVAL; + goto err_free_netdev; + } + + gec->txtc_irq = platform_get_irq_byname(pdev, + CNS21XX_GEC_TXTC_IRQ_NAME); + if (gec->txtc_irq < 0) { + dev_dbg(&pdev->dev, "%s irq not specified\n", + CNS21XX_GEC_TXTC_IRQ_NAME); + err = -EINVAL; + goto err_free_netdev; + } + + gec->txqe_irq = platform_get_irq_byname(pdev, + CNS21XX_GEC_TXQE_IRQ_NAME); + if (gec->txqe_irq < 0) { + dev_dbg(&pdev->dev, "%s irq not specified\n", + CNS21XX_GEC_TXQE_IRQ_NAME); + err = -EINVAL; + goto err_free_netdev; + } + + if (!request_mem_region(gec->mem_res->start, + resource_size(gec->mem_res), pdev->name)) { + dev_err(&pdev->dev, "unable to request mem region\n"); + err = -EBUSY; + goto err_free_netdev; + } + + gec->base = ioremap(gec->mem_res->start, resource_size(gec->mem_res)); + if (!gec->base) { + dev_err(&pdev->dev, "ioremap failed \n"); + err = -ENXIO; + goto err_release_mem; + } + + platform_set_drvdata(pdev, netdev); + + spin_lock_init(&gec->lock); + spin_lock_init(&gec->tx_lock); + + netdev->base_addr = gec->mem_res->start; + netdev->netdev_ops = &cns21xx_gec_netdev_ops; +#if defined(CNS21XX_GEC_TX_HW_CHECKSUM) + netdev->features = NETIF_F_IP_CSUM; +#endif + memcpy(netdev->dev_addr, pdata->mac_addr, 6); + netif_napi_add(netdev, &gec->napi, cns21xx_gec_poll, 64); + + gec->netdev = netdev; + err = register_netdev(netdev); + if (err) + goto err_unmap; + + err = cns21xx_gec_setup(gec); + if (err) + goto err_unregister_netdev; + + return 0; + + err_unregister_netdev: + unregister_netdev(netdev); + err_unmap: + platform_set_drvdata(pdev, NULL); + iounmap(gec->base); + err_release_mem: + release_mem_region(gec->mem_res->start, resource_size(gec->mem_res)); + err_free_netdev: + free_netdev(netdev); + err_out: + return err; +} + +static int __devexit cns21xx_gec_remove(struct platform_device *pdev) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct cns21xx_gec *gec = netdev_priv(netdev); + + unregister_netdev(netdev); + platform_set_drvdata(pdev, NULL); + iounmap(gec->base); + release_mem_region(gec->mem_res->start, resource_size(gec->mem_res)); + free_netdev(netdev); + + return 0; +} + +static struct platform_driver cns21xx_gec_driver = { + .remove = __devexit_p(cns21xx_gec_remove), + .driver = { + .name = "cns21xx-gec", + .owner = THIS_MODULE, + } +}; + +static int __init cns21xx_gec_init(void) +{ + return platform_driver_probe(&cns21xx_gec_driver, cns21xx_gec_probe); +} + +static void __exit cns21xx_gec_exit(void) +{ + platform_driver_unregister(&cns21xx_gec_driver); +} + +module_init(cns21xx_gec_init); +module_exit(cns21xx_gec_exit); + +#define INTERNAL_PHY_PATCH_CHECKCNT 16 +#define INTERNAL_PHY_PATCH_CHECK_PERIOD 1000 /* ms */ + +static void (*phy_statemachine)(struct cns21xx_gec*, int, int, int); + +#define ETH3220_PHY_MON_PERIOD INTERNAL_PHY_PATCH_CHECK_PERIOD + +/* phy monitor state */ +#define NUM_PHY 1 +#define PHY_STATE_INIT 0 +#define LINK_DOWN_POSITIVE 1 +#define WAIT_LINK_UP_POSITIVE 2 +#define LINK_UP_POSITIVE 3 +#define WAIT_BYPASS_LINK_UP_POSITIVE 4 +#define BYPASS_AND_LINK_UP_POSITIVE 5 +#define LINK_UP_8101_POSITIVE 6 +#define WAIT_8101_LINK_UP_POSITIVE 7 + +#define PHY_STATE_LAST (WAIT_8101_LINK_UP_POSITIVE+1) + +/* time setting */ +#define WAIT_BYPASS_LINK_UP_POSITIVE_TIMEOUT 5000 /* 5000 ms */ +#define WAIT_BYPASS_LINK_UP_NEGATIVE_TIMEOUT 5000 /* 5000 ms */ +#define LINK_DOWN_ABILITY_DETECT_TIMEOUT 5000 /* 5000 ms */ +#define DETECT_8101_PERIOD 7000 /* 7000 ms */ +#define WAIT_8101_LINK_UP_TIMEOUT 3000 /* 3000 ms */ + +#define MAX_PHY_PORT 1 +#define DEFAULT_AGC_TRAIN 16 +#define MAX_AGC_TRAIN 16 /* train 16 times */ +static int agc_train_num = DEFAULT_AGC_TRAIN; +u32 port_displaybuf[NUM_PHY][MAX_AGC_TRAIN + 1] = { + {0} +}; + +static int cuv[3][3] = { + {1, 1, 4}, + {1, 1, 0}, + {1, 1, -4} +}; + +static u32 link_status_old; + +struct eth3220_phy { + u16 state; + u16 linkdown_cnt; + u32 state_time; + u32 timer; +}; + +#define DEBUG_PHY_STATE_TRANSITION 1 +#if DEBUG_PHY_STATE_TRANSITION +/* + * show state transition of debug phy port. + * -1 for all ports + * -2 for disable all ports + * 0 - 4 for each port + */ +static int debug_phy_port = -2; +static char *phystate_name[] = { + "init", /* PHY_STATE_INIT */ + "ldp", /* LINK_DOWN_POSITIVE */ + "wait_lup", /* WAIT_LINK_UP_POSITIVE */ + "lup", /* LINK_UP_POSITIVE */ + "wait_bp_lup", /* WAIT_BYPASS_LINK_UP_POSITIVE */ + "bp_lup", /* BYPASS_AND_LINK_UP_POSITIVE */ + "8101_lup", /* LINK_UP_8101_POSITIVE */ + "wait_8101_lup", /* WAIT_8101_LINK_UP_POSITIVE */ + "err", +}; +#endif /* DEBUG_PHY_STATE_TRANSITION */ + +static struct eth3220_phy phy[5] = { + {PHY_STATE_INIT, 0, 0, 0}, + {PHY_STATE_INIT, 0, 0, 0}, + {PHY_STATE_INIT, 0, 0, 0}, + {PHY_STATE_INIT, 0, 0, 0}, + {PHY_STATE_INIT, 0, 0, 0} +}; + +static u16 long_cable_global_reg[32] = { + 0x0000, 0x19a0, 0x1d00, 0x0e80, + 0x0f60, 0x07c0, 0x07e0, 0x03e0, + 0x0000, 0x0000, 0x0000, 0x2000, + 0x8250, 0x1700, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x204b, 0x01c2, 0x0000, + 0x0000, 0x0000, 0x0fff, 0x4100, + 0x9319, 0x0021, 0x0034, 0x270a | FE_PHY_LED_MODE +}; + +static u16 long_cable_local_reg[32] = { + 0x3100, 0x786d, 0x01c1, 0xca51, + 0x05e1, 0x45e1, 0x0003, 0x001c, + 0x2000, 0x9828, 0xf3c4, 0x400c, + 0xf8ff, 0x6940, 0xb906, 0x503c, + 0x8000, 0x297a, 0x1010, 0x5010, + 0x6ae1, 0x7c73, 0x783c, 0xfbdf, + 0x2080, 0x3244, 0x1301, 0x1a80, + 0x8e8f, 0x8000, 0x9c29, 0xa70a | FE_PHY_LED_MODE +}; + +/*=============================================================* + * eth3220ac_rt8101_phy_setting + *=============================================================*/ +static void eth3220ac_rt8101_phy_setting(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 12, 0x18ff); + cns21xx_gec_write_phy(gec, port, 18, 0x6400); +} + +static void eth3220ac_release_bpf(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 18, 0x6210); +} + +static void eth3220ac_def_bpf(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 18, 0x6bff); +} + +static void eth3220ac_def_linkdown_setting(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 13, 0xe901); + cns21xx_gec_write_phy(gec, port, 14, 0xa3c6); +} + +static void eth3220ac_def_linkup_setting(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 13, 0x6901); + cns21xx_gec_write_phy(gec, port, 14, 0xa286); +} + +/*=============================================================* + * eth3220ac_link_agc: + *=============================================================*/ +static int eth3220ac_link_agc(struct cns21xx_gec *gec, int port, int speed) +{ + u16 reg; + u32 agc_data = 0; + u32 short_cable; + int i, jj; + + /* if speed = 100MHz, then continue */ + if (speed == 0) + return 0; + + short_cable = 0; + jj = 0; + for (i = 0; i < agc_train_num; i++) { + cns21xx_gec_read_phy(gec, port, 15, ®); + reg &= 0x7f; + if (reg <= 0x12) { + short_cable = 1; + jj++; + agc_data += (u32)reg; + } + } + + if (short_cable) + agc_data = (agc_data / jj) + 4; + else + agc_data = (cuv[2][0] * agc_data) / cuv[2][1] / + agc_train_num - 4; + + /* Fix AGC */ + agc_data = 0xd0 | (agc_data << 9); + cns21xx_gec_write_phy(gec, port, 15, agc_data); + udelay(1000); + cns21xx_gec_read_phy(gec, port, 15, ®); + reg &= ~(0x1 << 7); + cns21xx_gec_write_phy(gec, port, 15, reg); + + return 0; +} + +/*=============================================================* + * eth3220ac_unlink_agc: + *=============================================================*/ +static void eth3220ac_unlink_agc(struct cns21xx_gec *gec, int port) +{ + /* start AGC adaptive */ + cns21xx_gec_write_phy(gec, port, 15, 0xa050); +} + +/*=============================================================* + * eth3220ac_rt8100_check + *=============================================================*/ +static int eth3220ac_rt8100_check(struct cns21xx_gec *gec, int port) +{ + u16 reg, reg2; + + /* Read reg27 (error register) */ + cns21xx_gec_read_phy(gec, port, 27, ®); + /* if error exists, set Bypass Filter enable */ + if ((reg & 0xfffc)) { + cns21xx_gec_read_phy(gec, port, 15, ®); + cns21xx_gec_read_phy(gec, port, 27, ®2); + if ((reg2 & 0xfffc) && (((reg >> 9) & 0xff) < 0x1c)) { + dev_err(&gec->netdev->dev, "8100 pos err\n"); + + /* Bypass agcgain disable */ + cns21xx_gec_write_phy(gec, port, 15, (reg & (~(0x1 << 7)))); + + /* repeat counts when reaching threshold error */ + cns21xx_gec_write_phy(gec, port, 13, 0x4940); + + /* + * Speed up AN speed and compensate threshold + * phase error + */ + cns21xx_gec_write_phy(gec, port, 14, 0xa306); + + /* Bypass Filter enable */ + cns21xx_gec_read_phy(gec, port, 18, ®2); + + cns21xx_gec_write_phy(gec, port, 18, (reg | 0x400)); + + /* restart AN */ + cns21xx_gec_write_phy(gec, port, 0, 0x3300); + return 1; + } + } + return 0; +} + + +/*=============================================================* + * eth3220ac_rt8100_linkdown + *=============================================================*/ +static void eth3220ac_rt8100_linkdown(struct cns21xx_gec *gec, int port) +{ + u16 reg; + + /* Bypass Filter disable */ + cns21xx_gec_read_phy(gec, port, 18, ®); + cns21xx_gec_write_phy(gec, port, 18, (reg & (~(0x1 << 10)))); + eth3220ac_def_linkdown_setting(gec, port); +} + +static void eth3220ac_normal_phy_setting(struct cns21xx_gec *gec, int port) +{ + cns21xx_gec_write_phy(gec, port, 12, 0xd8ff); + eth3220ac_def_bpf(gec, port); +} + +/*=============================================================* + * wp3220ac_phystate + *=============================================================*/ +static void wp3220ac_phystate(struct cns21xx_gec *gec, int port, int link, int speed) +{ + int next_state; + u16 reg, reg2; + + phy[port].timer += ETH3220_PHY_MON_PERIOD; + + if (link) { + /* Link up state */ + switch (phy[port].state) { + case LINK_UP_POSITIVE: + next_state = eth3220ac_rt8100_check(gec, port) ? + WAIT_BYPASS_LINK_UP_POSITIVE : + LINK_UP_POSITIVE; + break; + + case PHY_STATE_INIT: + case WAIT_LINK_UP_POSITIVE: + case LINK_DOWN_POSITIVE: + next_state = LINK_UP_POSITIVE; + eth3220ac_def_linkup_setting(gec, port); + eth3220ac_link_agc(gec, port, speed); + eth3220ac_release_bpf(gec, port); + break; + + case WAIT_BYPASS_LINK_UP_POSITIVE: + case BYPASS_AND_LINK_UP_POSITIVE: + next_state = BYPASS_AND_LINK_UP_POSITIVE; + break; + + case WAIT_8101_LINK_UP_POSITIVE: + next_state = LINK_UP_8101_POSITIVE; + eth3220ac_link_agc(gec, port, speed); + cns21xx_gec_write_phy(gec, port, 12, 0x98ff); + break; + + case LINK_UP_8101_POSITIVE: + next_state = LINK_UP_8101_POSITIVE; + break; + + default: + next_state = LINK_UP_POSITIVE; + eth3220ac_def_linkup_setting(gec, port); + eth3220ac_link_agc(gec, port, speed); + } + } else { + /* Link down state */ + switch (phy[port].state) { + case LINK_DOWN_POSITIVE: + cns21xx_gec_read_phy(gec, port, 5, ®); + cns21xx_gec_read_phy(gec, port, 28, ®2); + + /* AN Link Partner Ability Register or NLP */ + if (reg || (reg2 & 0x100)) + next_state = WAIT_LINK_UP_POSITIVE; + else + next_state = LINK_DOWN_POSITIVE; + break; + + case WAIT_LINK_UP_POSITIVE: + if (phy[port].state_time > + LINK_DOWN_ABILITY_DETECT_TIMEOUT) + next_state = LINK_DOWN_POSITIVE; + else + next_state = WAIT_LINK_UP_POSITIVE; + break; + + case WAIT_BYPASS_LINK_UP_POSITIVE: + /* set timeout = 5 sec */ + if (phy[port].state_time > + WAIT_BYPASS_LINK_UP_POSITIVE_TIMEOUT) { + next_state = LINK_DOWN_POSITIVE; + + /* Bypass Filter disable */ + eth3220ac_rt8100_linkdown(gec, port); + eth3220ac_def_bpf(gec, port); + } else { + next_state = WAIT_BYPASS_LINK_UP_POSITIVE; + } + break; + + case BYPASS_AND_LINK_UP_POSITIVE: + next_state = LINK_DOWN_POSITIVE; + eth3220ac_rt8100_linkdown(gec, port); + eth3220ac_def_bpf(gec, port); + break; + + case WAIT_8101_LINK_UP_POSITIVE: + if (phy[port].state_time > WAIT_8101_LINK_UP_TIMEOUT) { + next_state = LINK_DOWN_POSITIVE; + eth3220ac_normal_phy_setting(gec, port); + eth3220ac_def_linkdown_setting(gec, port); + } else { + next_state = WAIT_8101_LINK_UP_POSITIVE; + } + break; + + case LINK_UP_POSITIVE: + eth3220ac_unlink_agc(gec, port); + eth3220ac_def_linkdown_setting(gec, port); + eth3220ac_def_bpf(gec, port); + if (phy[port].timer > DETECT_8101_PERIOD) { + next_state = LINK_DOWN_POSITIVE; + phy[port].timer = 0; + phy[port].linkdown_cnt = 1; + } else { + if (++phy[port].linkdown_cnt > 2) { + next_state = WAIT_8101_LINK_UP_POSITIVE; + eth3220ac_rt8101_phy_setting(gec, port); + } else { + next_state = LINK_DOWN_POSITIVE; + } + } + break; + + case LINK_UP_8101_POSITIVE: + eth3220ac_normal_phy_setting(gec, port); + /* fall down to phy normal state */ + case PHY_STATE_INIT: + eth3220ac_def_linkdown_setting(gec, port); + eth3220ac_unlink_agc(gec, port); + default: + next_state = LINK_DOWN_POSITIVE; + } + } + + if (phy[port].state != next_state) { + phy[port].state_time = 0; +#if DEBUG_PHY_STATE_TRANSITION + if (debug_phy_port == -1 || port == debug_phy_port) { + if ((phy[port].state < PHY_STATE_LAST) && + (next_state < PHY_STATE_LAST)) + dev_dbg(&gec->netdev->dev, + "p%d: %s->%s, %d, %d\n", + port, phystate_name[phy[port].state], + phystate_name[next_state], + phy[port].timer, + phy[port].linkdown_cnt); + else + dev_dbg(&gec->netdev->dev, + "p%d: %d->%d\n", + port, phy[port].state, next_state); + } +#endif /* DEBUG_PHY_STATE_TRANSITION */ + } else { + phy[port].state_time += ETH3220_PHY_MON_PERIOD; + } + phy[port].state = next_state; +} + +/*=============================================================* + * eth3220_phyinit: + *=============================================================*/ +static void eth3220ac_10m_agc(struct cns21xx_gec *gec) +{ + /* Force 10M AGC = 2c globally */ + cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a); + cns21xx_gec_write_phy(gec, 0, 12, 0x112c); + cns21xx_gec_write_phy(gec, 0, 13, 0x2e21); + cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a); +} + +static void eth3220ac_dfe_init(struct cns21xx_gec *gec) +{ + int i; + + cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a); + for (i = 0; i <= 7; i++) + cns21xx_gec_write_phy(gec, 0, i, 0); + cns21xx_gec_write_phy(gec, 0, 11, 0x0b50); + cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a); +} + +static void eth3220ac_phy_cdr_training_init(struct cns21xx_gec *gec) +{ + int i; + + /* Force all port in 10M FD mode */ + for (i = 0; i < NUM_PHY; i++) + cns21xx_gec_write_phy(gec, i, 0, 0x100); + + /* Global setting */ + cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a); + cns21xx_gec_write_phy(gec, 0, 29, 0x5021); + udelay(2000); /* 2ms, wait > 1 ms */ + cns21xx_gec_write_phy(gec, 0, 29, 0x4021); + udelay(2000); /* 2ms, wait > 1 ms */ + cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a); + + /* Enable phy AN */ + for (i = 0; i < NUM_PHY; i++) + cns21xx_gec_write_phy(gec, i, 0, 0x3100); +} + +static void eth3220_phyinit(struct cns21xx_gec *gec) +{ + eth3220ac_10m_agc(gec); + eth3220ac_dfe_init(gec); + eth3220ac_phy_cdr_training_init(gec); +} + +static void eth3220_phycfg(struct cns21xx_gec *gec, int phyaddr) +{ + eth3220ac_def_linkdown_setting(gec, phyaddr); + eth3220ac_normal_phy_setting(gec, phyaddr); + cns21xx_gec_write_phy(gec, phyaddr, 9, 0x7f); +} + +static void internal_phy_patch_check(struct cns21xx_gec *gec, int init) +{ + u32 short_cable_agc_detect_count; + u32 link_status = 0, link_speed; + u32 phy_addr = gec->phy_addr; + u16 phy_data; + u16 phy_data2; + int i; + + cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data); + udelay(100); + cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data2); + if (((phy_data & 0x0004) != 0x0004) && + ((phy_data2 & 0x0004) != 0x0004)) { + /* link down */ + short_cable_agc_detect_count = 0; + for (i = 0; i < INTERNAL_PHY_PATCH_CHECKCNT; i++) { + cns21xx_gec_read_phy(gec, phy_addr, 15, &phy_data); + udelay(1000); + if ((phy_data & 0x7F) <= 0x12) { + /* short cable */ + short_cable_agc_detect_count++; + break; + } + } + if (short_cable_agc_detect_count) { + u32 mac_cfg; + + /* short cable */ + phy_statemachine = wp3220ac_phystate; + eth3220_phyinit(gec); + cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data); + if (phy_data & 0x0040) + link_status = 1; + + mac_cfg = cns21xx_gec_rr(gec, GEC_REG_MAC_CFG); + if ((mac_cfg & 0xC) == 0x4) /* 100Mbps */ + link_speed = 1; + else + link_speed = 0; + + link_status_old = link_status; + for (i = 0; i < MAX_PHY_PORT; link_status >>= 1, i++) + eth3220_phycfg(gec, i); + } else { + /* long cable */ + /* set to global domain */ + cns21xx_gec_write_phy(gec, phy_addr, 31, + 0x2f1a); + for (i = 0; i < 32; i++) + cns21xx_gec_write_phy(gec, phy_addr, i, + long_cable_global_reg[i]); + + /* set to local domain */ + cns21xx_gec_write_phy(gec, phy_addr, 31, + 0xaf1a); + for (i = 0; i < 32; i++) + cns21xx_gec_write_phy(gec, phy_addr, i, + long_cable_local_reg[i]); + } + } +} + +static void internal_phy_timer_func(unsigned long data) +{ + struct cns21xx_gec *gec = (struct cns21xx_gec *) data; + + internal_phy_patch_check(gec, 0); + mod_timer(&gec->internal_phy_timer, + jiffies + INTERNAL_PHY_PATCH_CHECK_PERIOD / 10); +} + +static void internal_phy_init_timer(struct cns21xx_gec *gec) +{ + init_timer(&gec->internal_phy_timer); + gec->internal_phy_timer.function = internal_phy_timer_func; + gec->internal_phy_timer.data = (unsigned long) gec; +} + +static void internal_phy_start_timer(struct cns21xx_gec *gec) +{ + dev_dbg(&gec->netdev->dev, "starting patch check.\n"); + + internal_phy_patch_check(gec, 1); + mod_timer(&gec->internal_phy_timer, + jiffies + INTERNAL_PHY_PATCH_CHECK_PERIOD / 10); +} + +static void internal_phy_stop_timer(struct cns21xx_gec *gec) +{ + dev_dbg(&gec->netdev->dev, "stopping patch check.\n"); + + del_timer_sync(&gec->internal_phy_timer); +} --- /dev/null +++ b/drivers/net/ethernet/cns21xx/Kconfig @@ -0,0 +1,7 @@ +config CNS21XX_GEC + tristate "CNS21XX Gigabit Ethernet Controller support" + depends on ARCH_CNS21XX + help + If you wish to compile a kernel for Cavium Networks CNS21XX + with ethernet support, then you should always answer Y to this. + --- /dev/null +++ b/drivers/net/ethernet/cns21xx/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Cavium Networks CNS21XX ethernet driver +# + +cns21xx_gec-y += cns21xx_gec_main.o + +obj-$(CONFIG_CNS21XX_GEC) += cns21xx_gec.o --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -32,6 +32,7 @@ source "drivers/net/ethernet/calxeda/Kco source "drivers/net/ethernet/chelsio/Kconfig" source "drivers/net/ethernet/cirrus/Kconfig" source "drivers/net/ethernet/cisco/Kconfig" +source "drivers/net/ethernet/cns21xx/Kconfig" source "drivers/net/ethernet/davicom/Kconfig" config DNET --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_NET_CALXEDA_XGMAC) += calxe obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/ obj-$(CONFIG_NET_VENDOR_CIRRUS) += cirrus/ obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/ +obj-$(CONFIG_CNS21XX_GEC) += cns21xx/ obj-$(CONFIG_DM9000) += davicom/ obj-$(CONFIG_DNET) += dnet.o obj-$(CONFIG_NET_VENDOR_DEC) += dec/