This patch adds the cirrus ep93xx ethernet driver. (Needs rewriting.) Index: linux-2.6.16-rc5/drivers/net/arm/Kconfig =================================================================== --- linux-2.6.16-rc5.orig/drivers/net/arm/Kconfig +++ linux-2.6.16-rc5/drivers/net/arm/Kconfig @@ -31,3 +31,12 @@ config ARM_ETHERH help If you have an Acorn system with one of these network cards, you should say Y to this option if you wish to use it with Linux. + +config EP93XX_ETHERNET + tristate "EP93xx Ethernet support" + depends on NET_ETHERNET && ARM && ARCH_EP93XX + select CRC32 + select MII + help + This is a driver for the ethernet hardware included in EP93xx CPUs. + Say Y if you are building a kernel for EP93xx based devices. Index: linux-2.6.16-rc5/drivers/net/arm/Makefile =================================================================== --- linux-2.6.16-rc5.orig/drivers/net/arm/Makefile +++ linux-2.6.16-rc5/drivers/net/arm/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ARM_AM79C961A) += am79c961a obj-$(CONFIG_ARM_ETHERH) += etherh.o obj-$(CONFIG_ARM_ETHER3) += ether3.o obj-$(CONFIG_ARM_ETHER1) += ether1.o +obj-$(CONFIG_EP93XX_ETHERNET) += ep93xx_eth_need_rewrite.o Index: linux-2.6.16-rc5/drivers/net/arm/ep93xx_eth_need_rewrite.c =================================================================== --- /dev/null +++ linux-2.6.16-rc5/drivers/net/arm/ep93xx_eth_need_rewrite.c @@ -0,0 +1,1417 @@ +/* + * ep93xx_eth.c + * Ethernet Device Driver for Cirrus Logic EP93xx. + * + * Copyright (C) 2003 by Cirrus Logic www.cirrus.com + * This software may be used and distributed according to the terms + * of the GNU Public License. + * + * This driver was written based on skeleton.c by Donald Becker and + * smc9194.c by Erik Stahlman. + * + * Getting a MAC address: + * + * Former versions of this driver got their MAC from I2C EEPROM or even used + * hardcoded ones. Unfortunately I had to remove the board dependant I2C stuff, + * and use a random generated MAC instead of the hardcoded one. Good news is + * the there is support for setting the MAC from userspace now. (see below) + * + * first consider some potential problems if you use this random generated MAC: + * + * you can no longer count on it to be really unique + * identifying a particular board over network will be difficult + * DHCP servers can no longer use the MAC for assigning static IPs + * DHCP servers with long leasetimes quickly run out of leases + * ... + * + * So how can you set a valid MAC from userspace then? + * + * Let's say you've just bought your OUI from IEEE and it's "aa:bb:cc" + * Now you'd like to assign the MAC for your EP93xx board with serial #5 + * MAC = OUI<<24 + serial number = aa:bb:cc:00:00:05 + * + * ifconfig eth0 hw ether aa:bb:cc:00:00:05 # first set the MAC + * ifconfig eth0 192.168.1.1 netmask 255.255.255.0 up # then set the IP + * + * Apart from hardcoding this lines in your startup scripts you could also use + * some userspace utility to read (and set) the MAC from eeprom, flash, ... + * + * History: + * 07/19/01 0.1 Sungwook Kim initial release + * 10/16/01 0.2 Sungwook Kim add workaround for ignorance of Tx request + * while sending frame + * add some error stuations handling + * + * 03/25/03 Melody Lee Modified for EP93xx + * + * 2004/2005 Michael Burian porting to linux-2.6 + * 2005-10-12 Michael Burian fix problems when setting MAC with ifconfig + * 2005-10-30 Michael Burian cleanups, ethtool support + */ + +/* TODO: + * 1. try if it's possible to use skbuff directly for RX/TX (avoid memcpy) + * 2. use kzalloc instead of kmalloc+memset + */ + +#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 "ep93xx_eth_need_rewrite.h" + +#define DRV_NAME "ep93xx_eth" +#define DRV_VERSION "2.8" +#define DRV_DATE "2005-11-08" + +static const char *version = DRV_NAME ": version " DRV_VERSION " " DRV_DATE \ + " Cirrus Logic\n"; + +/* total number of device instance, 0 means the 1st instance. */ +static int num_of_instance; +static struct net_device * ep93xx_etherdev; + +/* + * A List of default device port configuration for auto probing. + * At this time, the CPU has only one Ethernet device, + * but better to support multiple device configuration. + * Keep in mind that the array must end in zero. + */ + +/* We get the MAC_BASE from include/asm-arm/arch-ep93xx/regmap.h */ +static struct { + unsigned int base_addr; /* base address, (0:end mark) */ + int irq; /* IRQ number, (0:auto detect) */ +} port_list[] __initdata = { + { EP93XX_ETHERNET_BASE, IRQ_EP93XX_ETHERNET }, + { 0 /* end mark */ , 0 }, +}; + +/* + * Some definitions belong to the operation of this driver. + * You should understand how it affect to driver before any modification. + */ + +/* Interrupt Sources in Use */ +#define DEF_INT_SRC (IntEn_TxSQIE|IntEn_RxEOFIE|IntEn_RxEOBIE|IntEn_RxHDRIE) + +/* + * Length of Device Queue in number of entries + * (must be less than or equal to 255) + */ + +/* length of Rx Descriptor Queue (4 or bigger) Must be power of 2. */ +#define LQRXD 64 +#define LQRXS LQRXD /* length of Rx Status Queue */ + +/* length of Tx Descriptor Queue (4 or bigger) Must be power of 2. */ +#define LQTXD 8 +#define LQTXS LQTXD /* length of Tx Status Queue */ + +/* Tx Queue fill-up level control */ +#define LVL_TXSTOP LQTXD - 2 /* level to ask the stack to stop Tx */ +#define LVL_TXRESUME 2 /* level to ask the stack to resume Tx */ + +/* Rx Buffer length in byte */ +#define LRXB (1518+2+16) /* length of Rx buf, must be 4-byte aligned */ +#define LTXB LRXB + +#define EP93XX_DEF_MSG (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | \ + NETIF_MSG_HW) + +#define DBG(lvl,msg,args...) do { printk(lvl"%s:%d: %s(): " msg, __FILE__, \ + __LINE__, __FUNCTION__, ## args); } while (0) + +/* + * Custom Data Structures + */ + +/* + * the information about the buffer passed to device. + * there are matching bufdsc informations + * for each Tx/Rx Descriptor Queue entry to trace + * the buffer within those queues. + */ +struct bufdsc { + /* virtual address representing the buffer passed to device */ + void *vaddr; + /* free routine */ + int (*free_rout) (void *buf); +}; + +/* device private information */ +struct ep93xx_priv { + /* static device information */ + int id; /* device instance ID */ + /* pointers to various queues (virtual address) */ + struct rx_dsc *rdq; /* Rx Descriptor Queue */ + struct rx_sts *rsq; /* Rx Status Queue */ + struct tx_dsc *tdq; /* Tx Descriptor Queue */ + struct tx_sts *tsq; /* Tx Status Queue */ + unsigned char *rxbuf; /* base of Rx Buffer pool */ + unsigned char *txbuf; /* base of Tx Buffer pool */ + struct bufdsc *rxbd; /* Rx Buffers info */ + struct bufdsc *txbd; /* Tx Buffers info */ + /* physical addresses of the same queues */ + dma_addr_t p_qbase; /* base */ + dma_addr_t p_rdq; /* Rx Descriptor Queue */ + dma_addr_t p_rsq; /* Rx Status Queue */ + dma_addr_t p_tdq; /* Tx Descriptor Queue */ + dma_addr_t p_tsq; /* Tx Status Queue */ + dma_addr_t p_rxbuf; /* Rx Buffer pool */ + dma_addr_t p_txbuf; /* Tx Buffer pool */ + /* MII Bus ID of Ethernet PHY */ + struct mii_if_info mii; + /* lock for mii when using ioctls */ + spinlock_t mii_lock; + /* dynamic information, subject to clear when device open */ + struct net_device_stats stats; /* statistic data */ + /* next processing index of device queues */ + int idx_rdq; + int idx_rsq; + int idx_tdqhead; + int idx_tdqtail; + int idx_tsq; + void __iomem *base_addr; /* base address */ + u32 msg_enable; + int regs_len; +}; + +/* + * Internal Routines + */ + +static inline unsigned int next_index(unsigned int idx_cur, unsigned int len) +{ + return (idx_cur + 1) % len; /* next array index */ +} + +static inline u32 _rdl(struct net_device *dev, u32 reg) +{ + return readl(((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrl(struct net_device *dev, u32 val, u32 reg) +{ + writel(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrw(struct net_device *dev, u16 val, u32 reg) +{ + writew(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +static inline void _wrb(struct net_device *dev, u8 val, u32 reg) +{ + writeb(val, ((struct ep93xx_priv *)(netdev_priv(dev)))->base_addr + reg); +} + +/** + * wait_on_reg() + */ +static int wait_on_reg(struct net_device *dev, int reg, unsigned long mask, unsigned long expect) +{ + int i, dt; + + for (i = 0; i < 10000; i++) { + dt = _rdl(dev, reg); + dt = (dt ^ expect) & mask; + if (0 == dt) + break; + } + return dt; +} + +/** + * mdio_write() + */ +static void mdio_write(struct net_device *dev, int idPhy, int reg, int dt) +{ + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + _wrl(dev, dt, REG_MIIData); + _wrl(dev, MIICmd_OP_WR | ((idPhy & 0x1f) << 5) | ((reg & 0x1f) << 0), REG_MIICmd); +} + +/** + * mdio_read() + */ +static int mdio_read(struct net_device *dev, int idPhy, int reg) +{ + int dt; + + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + _wrl(dev, MIICmd_OP_RD | ((idPhy & 0x1f) << 5) | ((reg & 0x1f) << 0), REG_MIICmd); + + wait_on_reg(dev, REG_MIISts, MIISts_Busy, ~MIISts_Busy); + dt = _rdl(dev, REG_MIIData); + + return dt & 0xffff; +} + +/** + * phy_init() + */ +static void phy_init(struct net_device *dev) +{ + + u32 oldval; + struct ep93xx_priv *priv = netdev_priv(dev); + + oldval = _rdl(dev, REG_SelfCTL); + +#if 0 + /* Set MDC clock to be divided by 8 and disable PreambleSuppress bit */ + _wrl(dev, 0x0e00, REG_SelfCTL); +#else + /* Set MDC clock to be divided by 32 and disable PreambleSuppress bit */ + _wrl(dev, 0x6200, REG_SelfCTL); +#endif + + if (mii_link_ok(&(priv->mii))) + mii_check_media(&(priv->mii), netif_msg_link(priv), 1); + + /* restore the old value */ + _wrl(dev, oldval, REG_SelfCTL); +} + +/** + * devQue_start() + * + * make descriptor queues active + * allocate queue entries if needed + * and set device registers up to make it operational + * assume device has been initialized + */ +static int devQue_start(struct net_device *dev) +{ + int err; + int i; + void *buf; + u32 phy_addr; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* turn off device bus mastering */ + _wrl(dev, BMCtl_RxDis | BMCtl_TxDis | _rdl(dev, REG_BMCtl), REG_BMCtl); + err = wait_on_reg(dev, REG_BMSts, BMSts_TxAct, ~BMSts_TxAct); + err |= wait_on_reg(dev, REG_BMSts, BMSts_RxAct, ~BMSts_RxAct); + if (err && netif_msg_hw(priv)) + DBG(KERN_ERR, "%s: BM does not stop\n", dev->name); + + /* Tx Status Queue */ + memset(priv->tsq, 0, sizeof(priv->tsq[0]) * LQTXS); + priv->idx_tsq = 0; + _wrl(dev, priv->p_tsq, REG_TxSBA); + _wrl(dev, priv->p_tsq, REG_TxSCA); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSBL); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSCL); + + /* Tx Descriptor Queue */ + memset(priv->tdq, 0, sizeof(priv->tdq[0]) * LQTXD); + priv->idx_tdqhead = priv->idx_tdqtail = 0; + _wrl(dev, priv->p_tdq, REG_TxDBA); + _wrl(dev, priv->p_tdq, REG_TxDCA); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDBL); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDCL); + + /* Rx Status Queue */ + memset(priv->rsq, 0, sizeof(priv->rsq[0]) * LQRXS); + priv->idx_rsq = 0; + _wrl(dev, priv->p_rsq, REG_RxSBA); + _wrl(dev, priv->p_rsq, REG_RxSCA); + _wrw(dev, sizeof(priv->rsq[0]) * LQRXS, REG_RxSBL); + _wrw(dev, sizeof(priv->rsq[0]) * LQRXS, REG_RxSCL); + + /* Rx Descriptor Queue */ + memset(priv->rdq, 0, sizeof(priv->rdq[0]) * LQRXD); + phy_addr = priv->p_rxbuf; + for (i = 0; i < LQRXD; i++) { + priv->rdq[i].bi = i; /* index */ + priv->rdq[i].ba = phy_addr; /* physical address */ + priv->rdq[i].bl = LRXB; /* length */ + phy_addr += LRXB; + } + priv->idx_rdq = 0; + _wrl(dev, priv->p_rdq, REG_RxDBA); + _wrl(dev, priv->p_rdq, REG_RxDCA); + _wrw(dev, sizeof(priv->rdq[0]) * LQRXD, REG_RxDBL); + _wrw(dev, sizeof(priv->rdq[0]) * LQRXD, REG_RxDCL); + + /* init Rx Buffer Descriptors */ + buf = priv->rxbuf; + for (i = 0; i < LQRXD; i++) { + priv->rxbd[i].vaddr = buf; + priv->rxbd[i].free_rout = NULL; + buf += LRXB; + } + + /* init Tx Buffer Descriptors */ + memset(priv->txbd, 0x0, sizeof(*priv->txbd) * LQTXD); + + buf = priv->txbuf; + for (i = 0; i < LQTXD; i++) { + priv->txbd[i].vaddr = buf; + priv->txbd[i].free_rout = NULL; + buf += LTXB; + } + + /* turn on device bus mastering */ + _wrl(dev, BMCtl_TxEn | BMCtl_RxEn | _rdl(dev, REG_BMCtl), REG_BMCtl); + err = wait_on_reg(dev, REG_BMSts, BMSts_TxAct | BMSts_TxAct, BMSts_TxAct | BMSts_TxAct); + if (err && netif_msg_hw(priv)) + DBG(KERN_ERR, "%s: BM does not start\n", dev->name); + + /* Enqueue whole entries; this must be done after BM activation */ + _wrl(dev, LQRXS, REG_RxSEQ); /* Rx Status Queue */ + _wrl(dev, LQRXD, REG_RxDEQ); /* Rx Desc. queue */ + + return 0; +} + +/** + * devQue_init() + * init device descriptor queues at system level + * device access is not recommended at this point + * + */ +static int devQue_init(struct net_device *dev) +{ + void *buf; + void *tmp; + int size, size2; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* verify device Tx/Rx Descriptor/Status Queue data size */ + if (8 != sizeof(struct rx_dsc)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof rx_dsc != 8 bytes!\n"); + return -ENOMEM; + } else if (8 != sizeof(struct rx_sts)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof rx_sts != 8 bytes!\n"); + return -ENOMEM; + } else if (8 != sizeof(struct tx_dsc)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof tx_dsc != 8 bytes!\n"); + return -ENOMEM; + } else if (4 != sizeof(struct tx_sts)) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "sizeof tx_sts != 4 bytes!\n"); + return -ENOMEM; + } + + /* + allocate kernel memory for whole queues + best if non-cached memory block due to DMA access by the device + if CPU doesn't have bus snooping + */ + size = sizeof(struct rx_dsc) * (LQRXD + 1) + + sizeof(struct rx_sts) * (LQRXS + 1) + + sizeof(struct tx_dsc) * (LQTXD + 1) + + sizeof(struct tx_sts) * (LQTXS + 1) + sizeof(unsigned long) * 4; + + buf = tmp = dma_alloc_coherent(NULL, size, &priv->p_qbase, GFP_KERNEL | GFP_DMA); + if (!buf) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for queue\n"); + return -ENOMEM; + } + + /* + * assign memory to each queue + */ + priv->rdq = buf; + buf = buf + sizeof(struct rx_dsc) * (LQRXD + 1); + priv->rsq = buf; + buf = buf + sizeof(struct rx_sts) * (LQRXS + 1); + priv->tdq = buf; + buf = buf + sizeof(struct tx_dsc) * (LQTXD + 1); + priv->tsq = buf; + buf = buf + sizeof(struct tx_sts) * (LQTXS + 1); + + /* + * store physical address of each queue + */ + priv->p_rdq = priv->p_qbase; + priv->p_rsq = priv->p_rdq + ((u32) priv->rsq - (u32) priv->rdq); + priv->p_tdq = priv->p_rdq + ((u32) priv->tdq - (u32) priv->rdq); + priv->p_tsq = priv->p_rdq + ((u32) priv->tsq - (u32) priv->rdq); + + /* + * init queue entries + */ + memset(priv->rdq, 0, sizeof(struct rx_dsc) * LQRXD); + memset(priv->rsq, 0, sizeof(struct rx_sts) * LQRXS); + memset(priv->tdq, 0, sizeof(struct tx_dsc) * LQTXD); + memset(priv->tsq, 0, sizeof(struct tx_sts) * LQTXS); + + /* Allocate Rx Buffer + (We might need to copy from Rx buf to skbuff in whatever case, + because device bus master requires 32bit aligned Rx buffer address + but Linux network stack requires odd 16bit aligned Rx buf address) */ + priv->rxbuf = dma_alloc_coherent(NULL, LRXB * LQRXD, &priv->p_rxbuf, GFP_KERNEL | GFP_DMA); + + if (!priv->rxbuf) { + priv->rxbuf = NULL; + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for RxBuf\n"); + goto err_free_qbase_1; + } + + /* Allocate Tx Buffer */ + priv->txbuf = dma_alloc_coherent(NULL, LTXB * LQTXD, &priv->p_txbuf, GFP_KERNEL | GFP_DMA); + + if (!priv->txbuf) { + priv->txbuf = NULL; + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for TxBuf\n"); + goto err_free_rxbuf_2; + } + + /* + * allocate kernel memory for buffer descriptors + */ + size2 = sizeof(struct bufdsc) * (LQRXD + LQTXD); + buf = kmalloc(size2, GFP_KERNEL); + if (!buf) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "no memory for buf desc\n"); + goto err_free_txbuf_3; + } + memset(buf, 0x0, size2); /* clear with 0 */ + priv->rxbd = buf; + priv->txbd = buf + sizeof(struct bufdsc) * LQRXD; + + return 0; + +err_free_txbuf_3: + dma_free_coherent(NULL, LTXB * LQTXD, priv->txbuf, priv->p_txbuf); +err_free_rxbuf_2: + dma_free_coherent(NULL, LRXB * LQRXD, priv->rxbuf, priv->p_rxbuf); +err_free_qbase_1: + dma_free_coherent(NULL, size, tmp, priv->p_qbase); + return -ENOMEM; +} + +/** + * devQue_cleanup() + * Release queue, Tx buffers and Rx buffers memory + * Only call after unregister_netdev + */ +static void devQue_cleanup(struct net_device *dev) +{ + int size; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* descriptor queues size */ + size = sizeof(struct rx_dsc) * (LQRXD + 1) + + sizeof(struct rx_sts) * (LQRXS + 1) + + sizeof(struct tx_dsc) * (LQTXD + 1) + + sizeof(struct tx_sts) * (LQTXS + 1) + sizeof(unsigned long) * 4; + + dma_free_coherent(NULL, size, priv->rdq, priv->p_qbase); + dma_free_coherent(NULL, LTXB * LQTXD, priv->txbuf, priv->p_txbuf); + dma_free_coherent(NULL, LRXB * LQRXD, priv->rxbuf, priv->p_rxbuf); + kfree(priv->rxbd); + +} + +/** + * set_multicast_tbl() + */ +static void set_multicast_tbl(struct net_device *dev, u8 *buf) +{ + int i; + unsigned char position; + struct dev_mc_list *cur_addr; + + memset(buf, 0x00, 8); + + cur_addr = dev->mc_list; + for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) { + + if (!cur_addr) + break; + if (!(*cur_addr->dmi_addr & 1)) + continue; /* make sure multicast addr */ + position = ether_crc_le(6, cur_addr->dmi_addr) >> 26; + buf[position >> 3] |= 1 << (position & 0x07); + } +} + +/** + * ind_addr_wr() + */ +static int ind_addr_wr(struct net_device *dev, int afp, char *buf) +{ + u32 rxctl; + int i, len; + struct ep93xx_priv *priv = netdev_priv(dev); + afp &= 0x07; + if (4 == afp || 5 == afp) { + if (netif_msg_hw(priv)) + DBG(KERN_ERR, "invalid afp value\n"); + return -1; + } + len = (AFP_AFP_HASH == afp) ? 8 : 6; + + rxctl = _rdl(dev, REG_RxCTL); /* turn Rx off */ + _wrl(dev, ~RxCTL_SRxON & rxctl, REG_RxCTL); + _wrl(dev, afp, REG_AFP); /* load new address pattern */ + for (i = 0; i < len; i++) + _wrb(dev, buf[i], REG_IndAD + i); + _wrl(dev, rxctl, REG_RxCTL); /* turn Rx back */ + + return 0; +} + +/** + * rx_ctl() + */ +static int rx_ctl(struct net_device *dev, int sw) +{ + unsigned long tmp = _rdl(dev, REG_RxCTL); + + /* + * Workaround for MAC lost 60-byte-long frames: + * must enable Runt_CRC_Accept bit + */ + if (sw) + _wrl(dev, tmp | RxCTL_SRxON | RxCTL_RCRCA, REG_RxCTL); + else + _wrl(dev, tmp & ~RxCTL_SRxON, REG_RxCTL); + + return 0; +} + +/** + * chk_tx_lvl() + */ +static void chk_tx_lvl(struct net_device *dev) +{ + int filled; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* check Tx Descriptor Queue fill-up level */ + filled = priv->idx_tdqhead - priv->idx_tdqtail; + if (filled < 0) + filled += LQTXD; + + if (filled <= (LVL_TXRESUME + 1)) + netif_wake_queue(dev); +} + +/** + * cleanup_tx() + */ +static void cleanup_tx(struct net_device *dev) +{ + struct tx_sts *txsts; + int idxsts, bi; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* + * process Tx Status Queue (no need to limit processing of TxStatus + * Queue because each queue entry consist of 1 dword) + */ + while (priv->tsq[priv->idx_tsq].flags & TXSTS_TXFP) { + idxsts = priv->idx_tsq; + priv->idx_tsq = next_index(priv->idx_tsq, LQTXS); + txsts = &priv->tsq[idxsts]; + if (!(txsts->flags & TXSTS_TXFP)) { /* empty? */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "QueTxSts is empty\n"); + return; + } + txsts->flags &= ~TXSTS_TXFP; /* mark processed */ + + bi = txsts->bi & TXSTS_BI; /* buffer index */ + + /* statistics collection */ + if (txsts->flags & TXSTS_TXWE) { /* Sent without error */ + priv->stats.tx_packets++; + priv->stats.tx_bytes += ((struct tx_dsc *)(priv->txbd[bi].vaddr))->bl_af & TXDSC_BL; + } else { /* Tx failed due to error */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "Tx failed QueTxSts"); + priv->stats.tx_errors++; + if (txsts->flags & TXSTS_LCRS) + priv->stats.tx_carrier_errors++; + if (txsts->flags & TXSTS_TXU) + priv->stats.tx_fifo_errors++; + if (txsts->flags & TXSTS_ECOLL) + priv->stats.collisions++; + } + + /* free Tx buffer */ + if (priv->txbd[bi].free_rout) { + (*priv->txbd[bi].free_rout)(priv->txbd[bi].vaddr); + priv->txbd[bi].free_rout = NULL; + } + + /* ahead Tx Descriptor Queue tail index */ + priv->idx_tdqtail = next_index(priv->idx_tdqtail, LQTXD); + } +} + +/** + * restart_tx() + */ +static int restart_tx(struct net_device *dev) +{ + int i; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* disable int */ + + /* turn off master INT control */ + _wrl(dev, _rdl(dev, REG_GIntMsk) & ~GIntMsk_IntEn, REG_GIntMsk); + + /* stop Tx and disable Tx DMA */ + _wrl(dev, _rdl(dev, REG_TxCTL) & ~TxCTL_STxON, REG_TxCTL); + _wrl(dev, _rdl(dev, REG_BMCtl) | BMCtl_TxDis, REG_BMCtl); + + /* reset Tx DMA */ + _wrl(dev, BMCtl_TxChR | _rdl(dev, REG_BMCtl), REG_BMCtl); + + /* release Tx buffers */ + for (i = 0; i < LQTXD; i++) { + if (priv->txbd[i].free_rout) { + priv->txbd[i].free_rout(priv->txbd[i].vaddr); + priv->txbd[i].free_rout = NULL; + } + priv->stats.tx_dropped++; + } + + /* init Tx Queues and flush cache */ + memset(priv->tsq, 0, sizeof(priv->tsq[0]) * LQTXS); + + /* init variables */ + priv->idx_tsq = priv->idx_tdqhead = priv->idx_tdqtail = 0; + + /* init registers */ + wait_on_reg(dev, REG_BMSts, BMCtl_TxChR, ~BMCtl_TxChR); + _wrl(dev, priv->p_tsq, REG_TxSBA); + _wrl(dev, priv->p_tsq, REG_TxSCA); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSBL); + _wrw(dev, sizeof(priv->tsq[0]) * LQTXS, REG_TxSCL); + _wrl(dev, priv->p_tdq, REG_TxDBA); + _wrl(dev, priv->p_tdq, REG_TxDCA); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDBL); + _wrw(dev, sizeof(priv->tdq[0]) * LQTXD, REG_TxDCL); + + /* start Tx and enable Tx DMA */ + _wrl(dev, _rdl(dev, REG_TxCTL) | TxCTL_STxON, REG_TxCTL); + _wrl(dev, _rdl(dev, REG_BMCtl) | BMCtl_TxEn, REG_BMCtl); + + /* enable int again */ + _wrl(dev, _rdl(dev, REG_GIntMsk) | GIntMsk_IntEn, REG_GIntMsk); + + return 0; +} + +/** + * reset() + */ +static void reset(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + /* soft reset command */ + _wrb(dev, SelfCTL_RESET, REG_SelfCTL); + if (wait_on_reg(dev, REG_SelfCTL, SelfCTL_RESET, ~SelfCTL_RESET)) + if (netif_msg_drv(priv)) + DBG(KERN_WARNING, "Soft Reset does not self-clear\n"); +} + +/** + * eth_shutdown()- closes down the Ethernet module + * + * Make sure to: + * 1. disable all interrupt mask + * 2. disable Rx + * 3. disable Tx + * + * TODO: + * (1) maybe utilize power down mode. + * Why not yet? Because while the chip will go into power down mode, + * the manual says that it will wake up in response to any I/O requests + * in the register space. Empirical results do not show this working. + */ +static int eth_shutdown(struct net_device *dev) +{ + reset(dev); + return 0; +} + +/** + * eth_init() - Reset and initialize the device. + * + * Device should be initialized enough to function in polling mode. + * Tx and Rx must be disabled and no INT generation. + */ +static int eth_init(struct net_device *dev) +{ + /* reset device */ + reset(dev); + + /* init PHY */ + phy_init(dev); + + /* init MAC */ + + /*i Set MDC clock to be divided by 8 and enable PreambleSuppress bit */ + _wrl(dev, 0x0f00, REG_SelfCTL); + /* mask Interrupt */ + _wrl(dev, 0x00, REG_GIntMsk); + /* no Rx on at this point */ + _wrl(dev, RxCTL_BA | RxCTL_IA0, REG_RxCTL); + _wrl(dev, 0x00, REG_TxCTL); + _wrl(dev, 0x00, REG_GT); + _wrl(dev, 0x00, REG_BMCtl); + /* Buffer Threshold */ + _wrl(dev, (0x80 << 16) | (0x40 << 0), REG_RxBTH); + _wrl(dev, (0x80 << 16) | (0x40 << 0), REG_TxBTH); + /* Status Threshold */ + _wrl(dev, (4 << 16) | (2 << 0), REG_RxSTH); + _wrl(dev, (4 << 16) | (2 << 0), REG_TxSTH); + /* Descriptor Threshold */ + _wrl(dev, (4 << 16) | (2 << 0), REG_RxDTH); + _wrl(dev, (4 << 16) | (2 << 0), REG_TxDTH); + /* Max Frame Length & Tx Start Threshold */ + _wrl(dev, ((1518 + 1) << 16) | (944 << 0), REG_MaxFL); + + _rdl(dev, REG_TxCollCnt); /* clear Tx Collision Count */ + _rdl(dev, REG_RxMissCnt); /* clear Rx Miss Counter */ + _rdl(dev, REG_RxRntCnt); /* clear Rx Runt Counter */ + + /* clear Pending INT */ + _rdl(dev, REG_IntStsC); + /* Tx on */ + _wrl(dev, TxCTL_STxON | _rdl(dev, REG_TxCTL), REG_TxCTL); + + /* Set MAC address */ + ind_addr_wr(dev, AFP_AFP_IA0, &dev->dev_addr[0]); + + /* init queue */ + devQue_start(dev); + + return 0; +} + +/** + * rx_isr() - Receive Interrupt Service Routine + */ +static void rx_isr(struct net_device *dev) +{ + struct rx_sts *rxsts; + /* index of Rx Status Queue Head from device (next put point) */ + int idx_rsqhead; + int idxsts; + int cnt_sts_processed, cnt_desc_processed; + char *dest; + struct sk_buff *skb; + int len; + unsigned int dt; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* get Current Rx Status Queue pointer */ + dt = _rdl(dev, REG_RxSCA); + + /* convert to array index */ + idx_rsqhead = (dt - priv->p_rsq) / sizeof(priv->rsq[0]); + if (!(0 <= idx_rsqhead && idx_rsqhead < LQRXS)) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, " invalid REG_RxSCA\n"); + return; + } + + /* process Rx (limit to idx_rsqhead due to cache) */ + cnt_sts_processed = cnt_desc_processed = 0; + while (idx_rsqhead != priv->idx_rsq) { + idxsts = priv->idx_rsq; + priv->idx_rsq = next_index(priv->idx_rsq, LQRXS); + rxsts = &priv->rsq[idxsts]; + if (!(rxsts->w1 & RXSTS_RFP)) { /* empty? */ + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "QueRxSts empty\n"); + return; + } + rxsts->w1 &= ~RXSTS_RFP; /* mark processed */ + + cnt_sts_processed++; + + if (!(rxsts->w1 & RXSTS_EOB)) /* buffer has no data */ + continue; + + if ((rxsts->bi & RXSTS_BI) != priv->idx_rdq) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "unmatching idx_rdq\n"); + continue; + } + priv->idx_rdq = next_index(priv->idx_rdq, LQRXD); + cnt_desc_processed++; + + /* received a frame with error */ + if (!((rxsts->w1 & RXSTS_EOF) && (rxsts->w1 & RXSTS_RWE))) { + if (netif_msg_rx_err(priv)) + DBG(KERN_WARNING, "Rx error RxSts\n"); + priv->stats.rx_errors++; + if (rxsts->w1 & RXSTS_OE) + priv->stats.rx_fifo_errors++; + if (rxsts->w1 & RXSTS_FE) + priv->stats.rx_frame_errors++; + if ((rxsts->w1 & RXSTS_RUNT) || (rxsts->w1 & RXSTS_EDATA)) + priv->stats.rx_length_errors++; + if (rxsts->w1 & RXSTS_CRCE) + priv->stats.rx_crc_errors++; + continue; + } + + len = rxsts->fl; + + /* alloc buffer for protocal stack */ + skb = dev_alloc_skb(len + 5); + if (NULL == skb) { + if (netif_msg_rx_err(priv)) + DBG(KERN_ERR, "Low Memory, Rx dropped\n"); + priv->stats.rx_dropped++; + continue; + } + + /* odd 16 bit alignment to make protocal stack happy */ + skb_reserve(skb, 2); + skb->dev = dev; + dest = skb_put(skb, len); + memcpy(dest, priv->rxbd[(rxsts->bi & RXSTS_BI)].vaddr, len); + skb->protocol = eth_type_trans(skb, dev); + /* pass Rx packet to system */ + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + if (RXSTS_AM == (rxsts->w1 & RXSTS_AM)) + priv->stats.multicast++; + } + + /* enqueue */ + _wrl(dev, cnt_sts_processed, REG_RxSEQ); + _wrl(dev, cnt_desc_processed, REG_RxDEQ); +} + +/** + * tx_isr() - Transmit Interrupt Service Routine + */ +static int tx_isr(struct net_device *dev) +{ + cleanup_tx(dev); + chk_tx_lvl(dev); /* resume Tx if it was stopped */ + return 0; +} + +/** + * ep93xx_isr() + */ +static irqreturn_t ep93xx_isr(int irq, void *dev_id, struct pt_regs *pRegs) +{ + struct net_device *dev = dev_id; + int lpCnt; + u32 intS; + + lpCnt = 0; + do { + /* get INT status and then clear */ + intS = _rdl(dev, REG_IntStsC); + + if (!intS) + break; /* no INT */ + if (IntSts_RxSQ & intS) + rx_isr(dev); /* Rx INT */ + if (IntSts_TxSQ & intS) + tx_isr(dev); /* Tx INT */ + } while (lpCnt++ < 64); /* limit loop to serve other interrupts too */ + return IRQ_HANDLED; +} + +/* Exposed Driver Routines to the Outside World */ + +/** + * ep93xx_get_stats() + */ +static struct net_device_stats *ep93xx_get_stats(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return &priv->stats; +} + +/** + * ep93xx_set_multicast_list() + */ +static void ep93xx_set_multicast_list(struct net_device *dev) +{ + u8 tbl[8 + 1]; + + if (IFF_PROMISC & dev->flags) { + _wrl(dev, RxCTL_PA | _rdl(dev, REG_RxCTL), REG_RxCTL); + + } else if (IFF_ALLMULTI & dev->flags) { /* receive all multicast addr */ + _wrl(dev, RxCTL_MA | (~RxCTL_PA & _rdl(dev, REG_RxCTL)), REG_RxCTL); + memset(tbl, 0xff, 8); + ind_addr_wr(dev, AFP_AFP_HASH, &tbl[0]); + + } else if (dev->mc_count) { /* set H/W multicasting filter */ + _wrl(dev, RxCTL_MA | (~RxCTL_PA & _rdl(dev, REG_RxCTL)), REG_RxCTL); + set_multicast_tbl(dev, &tbl[0]); + ind_addr_wr(dev, AFP_AFP_HASH, &tbl[0]); + + } else { /* no multicasting */ + _wrl(dev, ~(RxCTL_PA | RxCTL_MA) & _rdl(dev, REG_RxCTL), REG_RxCTL); + } +} + +/** + * ep93xx_tx_timeout() + */ +static void ep93xx_tx_timeout(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + if (netif_msg_tx_err(priv)) + DBG(KERN_WARNING, "transmit timed out\n"); + + phy_init(dev); + + /* kick Tx engine */ + restart_tx(dev); + + /* ask the Network Stack to resume Tx if there is room available */ + chk_tx_lvl(dev); +} + +/** + * ep93xx_start_xmit() + */ +static int ep93xx_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +/* @swk check H/W defect of Tx Underrun Error caused by certain frame length */ + struct tx_dsc *txdsc; + int idx_tdqhd; + int filled; + struct ep93xx_priv *priv = netdev_priv(dev); + + idx_tdqhd = priv->idx_tdqhead; + txdsc = &priv->tdq[idx_tdqhd]; + + /* check Tx Descriptor Queue fill-up level */ + filled = idx_tdqhd - priv->idx_tdqtail; + if (filled < 0) + filled += LQTXD; + filled += 1; + + /* check Queue level */ + if (LVL_TXSTOP <= filled) { + netif_stop_queue(dev); /* no more Tx allowed */ + if (netif_msg_tx_err(priv)) + DBG(KERN_INFO, "%s: Tx STOP requested\n", dev->name); + if (LVL_TXSTOP < filled) { + /* this situation can not be happen */ + if (netif_msg_tx_err(priv)) + DBG(KERN_ERR, "%s: Tx Request while stopped\n", dev->name); + return NETDEV_TX_BUSY; + } + } + + /* fill up Tx Descriptor Queue entry */ + if (skb->len < ETH_ZLEN) { + txdsc->bl_af = ETH_ZLEN & TXDSC_BL; /* also clears AF! */ + skb = skb_padto(skb, ETH_ZLEN); + if (skb == NULL) + return NETDEV_TX_OK; + } else { + txdsc->bl_af = skb->len & TXDSC_BL; /* also clears AF! */ + } + txdsc->ba = priv->p_txbuf + (idx_tdqhd * LTXB); + txdsc->bi_eof = (idx_tdqhd & TXDSC_BI) | TXDSC_EOF; + + dev->trans_start = jiffies; + + /* copy data to Tx buffer */ + memcpy(priv->txbd[idx_tdqhd].vaddr, skb->data, skb->len); + priv->txbd[idx_tdqhd].free_rout = NULL; + + /* Free the data buffer passed by upper layer */ + dev_kfree_skb_any(skb); + + /* ahead Tx Desc Queue */ + priv->idx_tdqhead = next_index(priv->idx_tdqhead, LQTXD); + wmb(); + + /* Enqueue a Tx Descriptor to the device */ + _wrl(dev, 1, REG_TxDEQ); + + if (netif_msg_tx_queued(priv)) + DBG(KERN_DEBUG, "%s: Tx packet queued\n", dev->name); + + return NETDEV_TX_OK; +} + +/** + * ep93xx_close() + * + * this makes the board clean up everything that it can + * and not talk to the outside world. Caused by + *. an 'ifconfig ethX down' + */ +static int ep93xx_close(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + free_irq(dev->irq, dev); + + netif_stop_queue(dev); + eth_shutdown(dev); + + if (netif_msg_ifdown(priv)) + DBG(KERN_INFO, "%s: ep93xx_close()\n", dev->name); + + return 0; +} + +/** + * ep93xx_open() - Open and Initialize the board + * + * Set up everything, reset the card, etc .. + */ +static int ep93xx_open(struct net_device *dev) +{ + int status; + struct ep93xx_priv *priv = netdev_priv(dev); + + /* clear dynamic device info */ + memset(&priv->stats, 0, sizeof(priv->stats)); + priv->idx_rdq = 0; + priv->idx_rsq = 0; + priv->idx_tdqhead = 0; + priv->idx_tdqtail = 0; + priv->idx_tsq = 0; + + /* reset/init device */ + status = eth_init(dev); + if (status != 0) { + return -EAGAIN; + } + + /* turn on INT, turn on Rx */ + status = request_irq(dev->irq, &ep93xx_isr, 0, DRV_NAME, dev); + if (status) { + if (netif_msg_ifup(priv)) + DBG(KERN_ERR, "%s: unable to get IRQ\n", dev->name); + return status; + } + + /* Enable interrupt driven operation. Also turn on Rx but no Tx. */ + /* setup Interrupt sources */ + _wrl(dev, DEF_INT_SRC, REG_IntEn); + /* turn on INT */ + _wrl(dev, GIntMsk_IntEn, REG_GIntMsk); + /* turn on Rx */ + rx_ctl(dev, 1); + + /* link to upper layer */ + netif_start_queue(dev); + + if (netif_msg_ifup(priv)) + DBG(KERN_INFO, "%s: irq=%d\n", dev->name, dev->irq); + + return 0; +} + +static int ep93xx_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&priv->mii_lock); + rc = generic_mii_ioctl(&priv->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&priv->mii_lock); + + return rc; +} + +/* + * Ethtool support + */ + +static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); +} + +static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + spin_lock_irq(&priv->mii_lock); + mii_ethtool_gset(&priv->mii, cmd); + spin_unlock_irq(&priv->mii_lock); + return 0; +} + +static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + int rc; + spin_lock_irq(&priv->mii_lock); + rc = mii_ethtool_sset(&priv->mii, cmd); + spin_unlock_irq(&priv->mii_lock); + return rc; +} + +static int ep93xx_nway_reset(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return mii_nway_restart(&priv->mii); +} + +static u32 ep93xx_get_link(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return mii_link_ok(&priv->mii); +} + +static u32 ep93xx_get_msglevel(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void ep93xx_set_msglevel(struct net_device *dev, u32 datum) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + priv->msg_enable = datum; +} + +static void ep93xx_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + + spin_lock_irq(&priv->mii_lock); + memcpy_fromio(regbuf, priv->base_addr, regs->len); + spin_unlock_irq(&priv->mii_lock); +} + +static int ep93xx_get_regs_len(struct net_device *dev) +{ + struct ep93xx_priv *priv = netdev_priv(dev); + return priv->regs_len; +} + +static struct ethtool_ops ep93xx_ethtool_ops = { + .get_drvinfo = ep93xx_get_drvinfo, + .get_settings = ep93xx_get_settings, + .set_settings = ep93xx_set_settings, + .get_regs = ep93xx_get_regs, + .get_regs_len = ep93xx_get_regs_len, + .nway_reset = ep93xx_nway_reset, + .get_link = ep93xx_get_link, + .get_msglevel = ep93xx_get_msglevel, + .set_msglevel = ep93xx_set_msglevel, +}; + +/** + * driver_init() - Logical driver initialization for an individual device + * + * Minimum device H/W access at this point + * + * Task: + * Initialize the structure if needed + * print out my vanity message if not done so already + * print out what type of hardware is detected + * print out the ethernet address + * find the IRQ + * set up my private data + * configure the dev structure with my subroutines + * actually GRAB the irq. + * GRAB the region + * + */ +static int __init driver_init(struct net_device *dev, u32 baseA, int irq) +{ + int i; + struct resource *res; + struct sockaddr sa; + struct ep93xx_priv *priv = netdev_priv(dev); + + if (0 == num_of_instance) + printk("%s", version); + + /* skip probing for a second one, we _know_ that it does not exist */ + if (1 == num_of_instance) + return -ENODEV; + + memset(dev->priv, 0x00, sizeof(struct ep93xx_priv)); + + /* device instance ID */ + priv->id = num_of_instance; + + /* mii stuff */ + spin_lock_init(&priv->mii_lock); + priv->mii.dev = dev; + priv->mii.mdio_read = mdio_read; + priv->mii.mdio_write = mdio_write; + priv->mii.phy_id_mask = 0x1f; + priv->mii.reg_num_mask = 0x1f; + priv->msg_enable = EP93XX_DEF_MSG; + priv->regs_len = DEV_REG_SPACE; + + priv->base_addr = (void *)(baseA); + + dev->irq = irq; + + res = request_mem_region(baseA, DEV_REG_SPACE, DRV_NAME); + if (res == NULL) { + if (netif_msg_probe(priv)) + DBG(KERN_ERR, "request_mem_region failed!\n"); + goto err_free_priv_1; + } + + dev->open = &ep93xx_open; + dev->stop = &ep93xx_close; + dev->do_ioctl = &ep93xx_ioctl; + dev->hard_start_xmit = &ep93xx_start_xmit; + dev->tx_timeout = &ep93xx_tx_timeout; + dev->watchdog_timeo = HZ * 5; + dev->get_stats = &ep93xx_get_stats; + dev->set_multicast_list = &ep93xx_set_multicast_list; + dev->ethtool_ops = &ep93xx_ethtool_ops; + + ether_setup(dev); + + if (devQue_init(dev)) + goto err_free_bd_memregion_irq_2; + + reset(dev); + + /* + * use a random MAC for now - + * don't forget to set a valid MAC later on with ifconfig + */ + sa.sa_family = AF_INET; +// random_ether_addr(sa.sa_data); + memcpy(sa.sa_data, "\x00\xd0\x69\x40\x2b\x13", 6); + dev->set_mac_address(dev, &sa); + + if (netif_msg_probe(priv)) { + printk(KERN_INFO DRV_NAME ": #%d at 0x%p IRQ:%d\n", priv->id, priv->base_addr, dev->irq); + printk(KERN_INFO DRV_NAME ": using random number"); + for (i = 0; i < 6; i++) + printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); + + printk(" as MAC, don't forget to assign a valid MAC later!\n"); + } + num_of_instance++; + return 0; + +err_free_bd_memregion_irq_2: + kfree(priv->rxbd); + disable_irq(dev->irq); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); +err_free_priv_1: + kfree(dev->priv); + return -EAGAIN; +} + +/** + * ep93xx_probe - probe for ep93xx device + * + * Probe for the one (and only) ethernet device found on + * EP93xx SOC CPU. + */ +static int __init ep93xx_probe(void) +{ + int err; + struct ep93xx_priv *priv; + struct net_device *dev = alloc_etherdev(sizeof(struct ep93xx_priv)); + + if (!dev) + return -ENODEV; + + priv = netdev_priv(dev); + + SET_MODULE_OWNER(dev); + + sprintf(dev->name, "eth0"); + + priv->base_addr = (void *)(port_list[0].base_addr); + + dev->irq = port_list[0].irq; + + err = driver_init(dev, (u32)priv->base_addr, dev->irq); + if (err) + goto err_free_netdev; + + err = register_netdev(dev); + if (err) + goto err_free_memregion_irq_1; + + ep93xx_etherdev = dev; + disable_irq(dev->irq); + return 0; + +err_free_memregion_irq_1: + kfree(priv->rxbd); + disable_irq(dev->irq); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); +err_free_netdev: + free_netdev(dev); + return err; +} + +static void __exit ep93xx_exit(void) +{ + struct net_device *dev = ep93xx_etherdev; + struct ep93xx_priv *priv = netdev_priv(dev); + + if (dev) { + unregister_netdev(dev); + devQue_cleanup(dev); + free_irq(dev->irq, dev); + release_mem_region((u32)priv->base_addr, DEV_REG_SPACE); + free_netdev(dev); + } +} + +module_init(ep93xx_probe); +module_exit(ep93xx_exit); +MODULE_LICENSE("GPL"); Index: linux-2.6.16-rc5/drivers/net/arm/ep93xx_eth_need_rewrite.h =================================================================== --- /dev/null +++ linux-2.6.16-rc5/drivers/net/arm/ep93xx_eth_need_rewrite.h @@ -0,0 +1,355 @@ +/* + * ep93xx_eth.h + * : header file of Ethernet Device Driver for Cirrus Logic EP93xx. + * + * Copyright (C) 2003 by Cirrus Logic www.cirrus.com + * This software may be used and distributed according to the terms + * of the GNU Public License. + * + * This file contains device related information like register info + * and register access method macros for the Ethernet device + * embedded within Cirrus Logic's EP93xx SOC chip. + * + * Information contained in this file was obtained from + * the EP9312 Manual Revision 0.12 and 0.14 from Cirrus Logic. + * + * History + * 05/18/01 Sungwook Kim Initial release + * 03/25/2003 Melody Modified for EP92xx + */ + +#ifndef _EP93xx_ETH_H_ +#define _EP93xx_ETH_H_ + +/* + * Definition of the registers. + * For details, refer to the datasheet . + * + * Basically, most registers are 32 bits width register. + * But some are 16 bits and some are 6 or 8 bytes long. + */ + +#define REG_RxCTL 0x0000 /*offset to Receiver Control Reg */ +#define RxCTL_PauseA (1<<20) +#define RxCTL_RxFCE1 (1<<19) +#define RxCTL_RxFCE0 (1<<18) +#define RxCTL_BCRC (1<<17) +#define RxCTL_SRxON (1<<16) +#define RxCTL_RCRCA (1<<13) +#define RxCTL_RA (1<<12) +#define RxCTL_PA (1<<11) +#define RxCTL_BA (1<<10) +#define RxCTL_MA (1<<9) +#define RxCTL_IAHA (1<<8) +#define RxCTL_IA3 (1<<3) +#define RxCTL_IA2 (1<<2) +#define RxCTL_IA1 (1<<1) +#define RxCTL_IA0 (1<<0) + +#define REG_TxCTL 0x0004 /*offset to Transmit Control Reg */ +#define TxCTL_DefDis (1<<7) +#define TxCTL_MBE (1<<6) +#define TxCTL_ICRC (1<<5) +#define TxCTL_TxPD (1<<5) +#define TxCTL_OColl (1<<3) +#define TxCTL_SP (1<<2) +#define TxCTL_PB (1<<1) +#define TxCTL_STxON (1<<0) + +#define REG_TestCTL 0x0008 /*Test Control Reg, R/W */ +#define TestCTL_MACF (1<<7) +#define TestCTL_MFDX (1<<6) +#define TestCTL_DB (1<<5) +#define TestCTL_MIIF (1<<4) + +#define REG_MIICmd 0x0010 /*offset to MII Command Reg, R/W */ +#define MIICmd_OP (0x03<<14) +#define MIICmd_OP_RD (2<<14) +#define MIICmd_OP_WR (1<<14) +#define MIICmd_PHYAD (0x1f<<5) +#define MIICmd_REGAD (0x1f<<0) + +#define REG_MIIData 0x0014 /*offset to MII Data Reg, R/W */ +#define MIIData_MIIData (0xffff<<0) + +#define REG_MIISts 0x0018 /*offset to MII Status Reg, R */ +#define MIISts_Busy (1<<0) + +#define REG_SelfCTL 0x0020 /*offset to Self Control Reg */ +#define SelfCTL_RWP (1<<7) /*Remote Wake Pin */ +#define SelfCTL_GPO0 (1<<5) +#define SelfCTL_PUWE (1<<4) +#define SelfCTL_PDWE (1<<3) +#define SelfCTL_MIIL (1<<2) +#define SelfCTL_RESET (1<<0) + +#define REG_IntEn 0x0024 /*Interrupt Enable Reg, R/W */ +#define IntEn_RWIE (1<<30) +#define IntEn_RxMIE (1<<29) +#define IntEn_RxBIE (1<<28) +#define IntEn_RxSQIE (1<<27) +#define IntEn_TxLEIE (1<<26) +#define IntEn_ECIE (1<<25) +#define IntEn_TxUHIE (1<<24) +#define IntEn_MOIE (1<<18) +#define IntEn_TxCOIE (1<<17) +#define IntEn_RxROIE (1<<16) +#define IntEn_MIIIE (1<<12) +#define IntEn_PHYSIE (1<<11) +#define IntEn_TIE (1<<10) +#define IntEn_SWIE (1<<8) +#define IntEn_TxSQIE (1<<3) +#define IntEn_RxEOFIE (1<<2) +#define IntEn_RxEOBIE (1<<1) +#define IntEn_RxHDRIE (1<<0) + +#define REG_IntStsP 0x0028 /*offset to Interrupt Status Preserve Reg, R/W */ +#define REG_IntStsC 0x002c /*offset to Interrupt Status Clear Reg, R */ +#define IntSts_RWI (1<<30) +#define IntSts_RxMI (1<<29) +#define IntSts_RxBI (1<<28) +#define IntSts_RxSQI (1<<27) +#define IntSts_TxLEI (1<<26) +#define IntSts_ECI (1<<25) +#define IntSts_TxUHI (1<<24) +#define IntSts_MOI (1<<18) +#define IntSts_TxCOI (1<<17) +#define IntSts_RxROI (1<<16) +#define IntSts_MIII (1<<12) +#define IntSts_PHYSI (1<<11) +#define IntSts_TI (1<<10) +#define IntSts_AHBE (1<<9) +#define IntSts_SWI (1<<8) +#define IntSts_OTHER (1<<4) +#define IntSts_TxSQ (1<<3) +#define IntSts_RxSQ (1<<2) + +#define REG_GT 0x0040 /*offset to General Timer Reg */ +#define GT_GTC (0xffff<<16) +#define GT_GTP (0xffff<<0) + +#define REG_FCT 0x0044 /*offset to Flow Control Timer Reg */ +#define FCT_FCT (0x00ffffff<<0) + +#define REG_FCF 0x0048 /*offset to Flow Control Format Reg */ +#define FCF_MACCT (0xffff<<16) +#define FCF_TPT (0xffff<<0) + +#define REG_AFP 0x004c /*offset to Address Filter Pointer Reg */ +#define AFP_AFP (0x07<<0) /*Address Filter Pointer + (bank control for REG_IndAD) */ +#define AFP_AFP_IA0 0 /*Primary Individual Address (MAC Addr) */ +#define AFP_AFP_IA1 1 /*Individual Address 1 */ +#define AFP_AFP_IA2 2 /*Individual Address 2 */ +#define AFP_AFP_IA3 3 /*Individual Address 3 */ +#define AFP_AFP_DTxP 6 /*Destination Address of Tx Pause Frame */ +#define AFP_AFP_HASH 7 /*Hash Table */ + +#define REG_IndAD 0x0050 /*offset to Individual Address Reg, + n bytes, R/W */ + +#define REG_GIntSts 0x0060 /*offset to Global Interrupt + Status Reg (writing 1 will clear) */ +#define REG_GIntROS 0x0068 /*offset to Global Interrupt + Status Read Only Reg */ +#define GIntSts_INT (1<<15) /*Global Interrupt Request Status */ + +#define REG_GIntMsk 0x0064 /*offset to Global Interrupt Mask Reg */ +#define GIntMsk_IntEn (1<<15) /*Global Interrupt Enable */ + +#define REG_GIntFrc 0x006c /*offset to Global Interrupt Force Reg */ +#define GIntFrc_INT (1<<15) /*Force to set GIntSts */ + +#define REG_TxCollCnt 0x0070 /*Transmit Collision Count Reg, R */ +#define REG_RxMissCnt 0x0074 /*Receive Miss Count Reg, R */ +#define REG_RxRntCnt 0x0078 /*Receive Runt Count Reg, R */ + +#define REG_BMCtl 0x0080 /*offset to Bus Master Control Reg, R/W */ +#define BMCtl_MT (1<<13) +#define BMCtl_TT (1<<12) +#define BMCtl_UnH (1<<11) +#define BMCtl_TxChR (1<<10) +#define BMCtl_TxDis (1<<9) +#define BMCtl_TxEn (1<<8) +#define BMCtl_EH2 (1<<6) +#define BMCtl_EH1 (1<<5) +#define BMCtl_EEOB (1<<4) +#define BMCtl_RxChR (1<<2) +#define BMCtl_RxDis (1<<1) +#define BMCtl_RxEn (1<<0) + +#define REG_BMSts 0x0084 /*offset to Bus Master Status Reg, R */ +#define BMSts_TxAct (1<<7) +#define BMSts_TP (1<<4) +#define BMSts_RxAct (1<<3) +#define BMSts_QID (0x07<<0) +#define BMSts_QID_RxDt (0<<0) +#define BMSts_QID_TxDt (1<<0) +#define BMSts_QID_RxSts (2<<0) +#define BMSts_QID_TxSts (3<<0) +#define BMSts_QID_RxDesc (4<<0) +#define BMSts_QID_TxDesc (5<<0) + +#define REG_RBCA 0x0088 /*offset to Receive Buffer + Current Address Reg, R */ +#define REG_TBCA 0x008c /*offset to Transmit Buffer + Current Address Reg, R */ + +#define REG_RxDBA 0x0090 /*offset to Receive Descriptor Queue + Base Address Reg, R/W */ +#define REG_RxDBL 0x0094 /*offset to Receive Descriptor Queue + Base Length Reg, R/W, 16bits */ +#define REG_RxDCL 0x0096 /*offset to Receive Descriptor Queue + Current Length Reg, R/W, 16bits */ +#define REG_RxDCA 0x0098 /*offset to Receive Descriptor Queue + Current Address Reg, R/W */ + +#define REG_RxDEQ 0x009c /*offset to Receive Descriptor + Enqueue Reg, R/W */ +#define RxDEQ_RDV (0xffff<<16) /*R 16bit; Receive Descriptor Value */ +#define RxDEQ_RDI (0xff<<0) /*W 8bit; Receive Descriptor Increment */ + +#define REG_RxSBA 0x00a0 /*offset to Receive Status Queue + Base Address Reg, R/W */ +#define REG_RxSBL 0x00a4 /*offset to Receive Status Queue + Base Length Reg, R/W, 16bits */ +#define REG_RxSCL 0x00a6 /*offset to Receive Status Queue + Current Length Reg, R/W, 16bits */ +#define REG_RxSCA 0x00a8 /*offset to Receive Status Queue + Current Address Reg, R/W */ + +#define REG_RxSEQ 0x00ac /*offset to Receive Status Queue + Current Address Reg, R/W */ +#define RxSEQ_RSV (0xffff<<16) +#define RxSEQ_RSI (0xff<<0) + +#define REG_TxDBA 0x00b0 /*offset to Transmit Descriptor Queue + Base Address Reg, R/W */ +#define REG_TxDBL 0x00b4 /*offset to Transmit Descriptor Queue + Base Length Reg, R/W, 16bits */ +#define REG_TxDCL 0x00b6 /*offset to Transmit Descriptor Queue + Current Length Reg, R/W, 16bits */ +#define REG_TxDCA 0x00b8 /*offset to Transmit Descriptor Queue + Current Address Reg, R/W */ + +#define REG_TxDEQ 0x00bc /*offset to Transmit Descriptor Queue + Current Address Reg, R/W */ +#define TxDEQ_TDV (0xffff<<16) +#define TxDEQ_TDI (0xff<<0) + +#define REG_TxSBA 0x00c0 /*offset to Transmit Status Queue + Base Address Reg, R/W */ +#define REG_TxSBL 0x00c4 /*offset to Transmit Status Queue + Base Length Reg, R/W, 16bits */ +#define REG_TxSCL 0x00c6 /*offset to Transmit Status Queue + Current Length Reg, R/W, 16bits */ +#define REG_TxSCA 0x00c8 /*offset to Transmit Status Queue + Current Address Reg, R/W */ + +#define REG_RxBTH 0x00d0 /*offset to Receive Buffer + Threshold Reg, R/W */ +#define RxBTH_RDHT (0x03ff<<16) +#define RxBTH_RDST (0x03ff<<0) + +#define REG_TxBTH 0x00d4 /*offset to Transmit Buffer + Threshold Reg, R/W */ +#define TxBTH_TDHT (0x03ff<<16) +#define TxBTH_TDST (0x03ff<<0) + +#define REG_RxSTH 0x00d8 /*offset to Receive Status + Threshold Reg, R/W */ +#define RxSTH_RSHT (0x003f<<16) +#define RxSTH_RSST (0x003f<<0) + +#define REG_TxSTH 0x00dc /*offset to Transmit Status + Threshold Reg, R/W */ +#define TxSTH_TSHT (0x003f<<16) +#define TxSTH_TSST (0x003f<<0) + +#define REG_RxDTH 0x00e0 /*offset to Receive Descriptor + Threshold Reg, R/W */ +#define RxDTH_RDHT (0x003f<<16) +#define RxDTH_RDST (0x003f<<0) + +#define REG_TxDTH 0x00e4 /*offset to Transmit Descriptor + Threshold Reg, R/W */ +#define TxDTH_TDHT (0x003f<<16) +#define TxDTH_TDST (0x003f<<0) + +#define REG_MaxFL 0x00e8 /*offset to Max Frame Length Reg, R/W */ +#define MaxFL_MFL (0x07ff<<16) +#define MaxFL_TST (0x07ff<<0) + +#define REG_RxHL 0x00ec /*offset to Receive Header Length Reg, R/W */ +#define RxHL_RHL2 (0x07ff<<16) +#define RxHL_RHL1 (0x03ff<<0) + +#define REG_MACCFG0 0x0100 /*offset to Test Reg #0, R/W */ +#define MACCFG0_DbgSel (1<<7) +#define MACCFG0_LCKEN (1<<6) +#define MACCFG0_LRATE (1<<5) +#define MACCFG0_RXERR (1<<4) +#define MACCFG0_BIT33 (1<<2) +#define MACCFG0_PMEEN (1<<1) +#define MACCFG0_PMEST (1<<0) + +#define REG_MACCFG1 0x0104 /*offset to Test Reg #1, R/W */ +#define REG_MACCFG2 0x0108 /*offset to Test Reg #2, R */ +#define REG_MACCFG3 0x010c /*offset to Test Reg #3, R */ + +/*--------------------------------------------------------------- + * Definition of Descriptor/Status Queue Entry + *-------------------------------------------------------------*/ +struct rx_dsc { + __be32 ba; + __be16 bl; + __be16 bi; /* let nsof flag be part of bi */ +}; + +#define RXSTS_RFP 0x80000000 +#define RXSTS_RWE 0x40000000 +#define RXSTS_EOF 0x20000000 +#define RXSTS_EOB 0x10000000 +#define RXSTS_AM 0x00C00000 +#define RXSTS_OE 0x00100000 +#define RXSTS_FE 0x00080000 +#define RXSTS_RUNT 0x00040000 +#define RXSTS_EDATA 0x00020000 +#define RXSTS_CRCE 0x00010000 + +#define RXSTS_BI 0x7FFF +struct rx_sts { /* Receive Status Queue Entry */ + __be32 w1; + __be16 fl; + __be16 bi; /* bi and rfp2 */ +}; + +#define TXDSC_BL 0x0FFF +#define TXDSC_AF 0x8000 +#define TXDSC_BI 0x7FFF +#define TXDSC_EOF 0x8000 +struct tx_dsc { /* Transmit Descriptor Queue Entry */ + __be32 ba; /*b31-0: physical Buffer Address */ + __be16 bl_af; /* Buffer Length, Abort Frame */ + __be16 bi_eof; /* Buffer Index, End Of Frame */ +}; + +#define TXSTS_BI 0x7fff +#define TXSTS_TXFP 0x80 +#define TXSTS_TXWE 0x40 +#define TXSTS_LCRS 0x10 +#define TXSTS_TXU 0x02 +#define TXSTS_ECOLL 0x01 + +struct tx_sts { + __be16 bi; + u8 ncoll; + u8 flags; +}; + +/* + * Size of device registers occupied in memory/IO address map + */ +#define DEV_REG_SPACE 0x00010000 + +#endif /* _EP93xx_ETH_H_ */