On Mon, 04 Dec 2006 10:01:01 -0800 Dan Nicolaescu <[EMAIL PROTECTED]> wrote:
> > Hi, > > Here is a driver for the Opencores Ethernet Controller. I started from > a 2.4 uClinux driver, ported it to 2.6, made it work, cleaned it up > and added the MII interface. > > The Opencores Ethernet Controller is Verilog code that can be used to > implement an Ethernet device in hardware. It needs to be coupled with > a PHY and some buffer memory. Because of that devices that implement > this controller can be very different. The code here tries to support > that by having some parameters that need to be defined at compile > time. > > This is my first Ethernet driver, so comments/advice would be > appreciated. > > Thanks > --dan > > Kconfig | 5 > open_eth.c | 1022 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > open_eth.h | 132 +++++++ > 3 files changed, 1159 insertions(+) Please run through scripts/Lindent or cleanup style. Also has trailing whitespace. > --- /dev/null 2006-09-20 11:38:04.545479250 -0700 > +++ drivers/net/open_eth.c 2006-12-04 09:20:17.000000000 -0800 > @@ -0,0 +1,1022 @@ > +/* > + * Ethernet driver for Open Ethernet Controller (www.opencores.org). > + * Copyright (c) 2002 Simon Srot ([EMAIL PROTECTED]) > + * Copyright (c) 2006 Tensilica Inc. > + * > + * Based on: > + * > + * Ethernet driver for Motorola MPC8xx. > + * Copyright (c) 1997 Dan Malek ([EMAIL PROTECTED]) > + * > + * mcen302.c: A Linux network driver for Mototrola 68EN302 MCU > + * > + * Copyright (C) 1999 Aplio S.A. Written by Vadim Lebedev > + * > + * > + * The Open Ethernet Controller is just a MAC, it needs to be > + * combined with a PHY and buffer memory in order to create an > + * ethernet device. Thus some of the hardware parameters are device > + * specific. They need to be defined in asm/hardware.h. Example: > + * > + * The IRQ for the device: > + * #define OETH_IRQ 1 > + * > + * The address where the MAC registers are mapped: > + * #define OETH_BASE_ADDR 0xFD030000 > + * > + * The address where the MAC RX/TX buffers are mapped: > + * #define OETH_SRAM_BUFF_BASE 0xFD800000 > + * > + * Sizes for a RX or TX buffer: > + * #define OETH_RX_BUFF_SIZE 2048 > + * #define OETH_TX_BUFF_SIZE 2048 > + * The number of RX and TX buffers: > + * #define OETH_RXBD_NUM 16 > + * #define OETH_TXBD_NUM 16 > + * The PHY ID (needed if MII is enabled): > + * #define OETH_PHY_ID 0 > + * > + * Code to perform the device specific initialization (REGS is a > + * struct oeth_regs*): > + * #define OETH_PLATFORM_SPECIFIC_INIT(REGS) > + * it should at least initialize the device MAC address in > + * REGS->mac_addr1 and REGS->mac_addr2. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/string.h> > +#include <linux/errno.h> > +#include <linux/ioport.h> > +#include <linux/slab.h> > +#include <linux/interrupt.h> > +#include <linux/delay.h> > +#include <linux/init.h> > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > +#include <linux/skbuff.h> > +#include <linux/module.h> > +#include <linux/ethtool.h> > +#include <linux/mii.h> > + > +#include <asm/hardware.h> > + > +#include "open_eth.h" > + > +#define DRV_NAME "OpencoresEthernet" > + > +/* The Opencores Ethernet driver needs some parameters from the > + * hardware implementation. They should be defined in the > + asm/hardware.h file. */ > + > +#if 1 > +#undef OETH_TXBD_NUM > +#undef OETH_RXBD_NUM > +#define OETH_RXBD_NUM 4 > +#define OETH_TXBD_NUM 4 > +/* #undef OETH_RX_BUFF_SIZE */ > +/* #undef OETH_TX_BUFF_SIZE */ > +/* #define OETH_RX_BUFF_SIZE 0x600 */ > +/* #define OETH_TX_BUFF_SIZE 0x600 */ > +#endif Gack, just put in correct define's avoid adding conditional compilation stuff. > +#define BUFFER_SCREWED 1 > +/* #define BUFFER_SCREWED_ADDR (OETH_SRAM_BUFF_BASE + OETH_TXBD_NUM * > OETH_TX_BUFF_SIZE + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE + 4) */ > +#define BUFFER_SCREWED_ADDR (0xfd803800 + 0x600) > + > +/* Debug helpers. */ > +/* #define OETH_DEBUG_TRANSMIT */ > +#ifdef OETH_DEBUG_TRANSMIT > +#define OEDTX(x) x > +#else > +#define OEDTX(x) > +#endif > + > +/* #define OETH_DEBUG_RECEIVE */ > +#ifdef OETH_DEBUG_RECEIVE > +#define OEDRX(x) x > +#else > +#define OEDRX(x) > +#endif > + > +#define OETH_REGS_SIZE 0x1000 /* MAC registers + RX and TX descriptors */ > +#define OETH_BD_BASE (OETH_BASE_ADDR + 0x400) > +#define OETH_TOTAL_BD 128 > + > +/* The transmitter timeout FIXME: dann this needs to be handled */ > +#define OETH_TX_TIMEOUT (2*HZ) > + > +/* The buffer descriptors track the ring buffers. */ > +struct oeth_private { > + struct oeth_regs *regs; /* Address of controller registers. */ > + struct oeth_bd *rx_bd_base; /* Address of Rx BDs. */ > + struct oeth_bd *tx_bd_base; /* Address of Tx BDs. */ > + u8 tx_next; /* Next buffer to be sent */ > + u8 tx_last; /* Next buffer to be checked if packet sent */ > + u8 tx_full; /* Buffer ring full indicator */ > + u8 rx_cur; /* Next buffer to be checked if packet received */ > + > +#if CONFIG_MII > + struct mii_if_info mii_if; /* MII lib hooks/info */ > +#endif Use select in Kconfig, to force MII > + spinlock_t lock; > + struct net_device_stats stats; > +}; > + > +static int oeth_open(struct net_device *dev); > +static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev); > +static void oeth_rx(struct net_device *dev); > +/* FIXME: static */void oeth_tx(struct net_device *dev); > +static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs > *regs); > +static int oeth_close(struct net_device *dev); > +static struct net_device_stats *oeth_get_stats(struct net_device *dev); > +static void oeth_set_multicast_list(struct net_device *dev); > +static int oeth_set_mac_address(struct net_device *dev, void *addr); > + > +#if defined(OETH_DEBUG_RECEIVE) || defined(OETH_DEBUG_TRANSMIT) > +static void oeth_print_packet(u32* add, int len) > +{ > + int i; > + > + printk("ipacket: add = %p len = %d\n", add, len); > + for(i = 0; i < len; i++) { > + if(!(i % 16)) > + printk("\n"); > + else if (!(i % 8)) > + printk(" "); > + printk(" %.2x", *(((unsigned char *)add) + i)); > + } > + printk("\n"); > +} > +#endif > + > +int dann_int_count = 0; > +int dann_rx_count = 0; > +int dann_tx_count = 0; > + > +static int oeth_open(struct net_device *dev) > +{ > + int ret; > + struct oeth_private *cep = netdev_priv(dev); > + struct oeth_regs *regs = cep->regs; > + > + /*FIXME: just for debugging....*/ > + memset((void*)OETH_SRAM_BUFF_BASE, 0, 0x4000); > + > + /* Install our interrupt handler. */ > + ret = request_irq(OETH_IRQ, oeth_interrupt, 0, "eth", (void *)dev); > + if (ret) > + { Why not use dev->name rather than "eth"? Indentation. See Documentation style. What about IRQF_SHARED? > + printk("request_irq failed for the Opencore ethernet device\n"); > + return ret; > + } > + /* Enable the receiver and transmiter. */ > + regs->moder |= OETH_MODER_RXEN | OETH_MODER_TXEN; > + > + /* Start the queue, we are ready to process packets now. */ > + netif_start_queue (dev); > + return 0; > +} > + > +static int oeth_close(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + struct oeth_regs *regs = cep->regs; > + volatile struct oeth_bd *bdp; > + int i; > + > + spin_lock_irq(&cep->lock); > + /* Disable the receiver and transmiter. */ > + regs->moder &= ~(OETH_MODER_RXEN | OETH_MODER_TXEN); > + > + bdp = cep->rx_bd_base; > + for (i = 0; i < OETH_RXBD_NUM; i++) { > + bdp->len_status &= ~(OETH_TX_BD_STATS | OETH_TX_BD_READY); > + bdp++; > + } > + > + bdp = cep->tx_bd_base; > + for (i = 0; i < OETH_TXBD_NUM; i++) { > + bdp->len_status &= ~(OETH_RX_BD_STATS | OETH_RX_BD_EMPTY); > + bdp++; > + } > + > + spin_unlock_irq(&cep->lock); > + > + return 0; > +} > + > +#if 1 > +static void* memcpy_hton (void *dest, void *data, size_t n) const ? > +{ > + int i; > + u32 *ldest = dest; > + u32 *ldata = data; > + if (n % 4) n += 4 - n % 4; > + for (i = 0; i < n / 4; i++) > + *ldest++ = /* htonl */ (*ldata++); > + return dest; > +} So you implemented a slow memcpy (since htonl) is commented out. Do some basic scrubbing of the code before submitting please > +static void* memcpy_ntoh (void *dest, void *data, size_t n) > +{ > + int i; > + u32 *ldest = dest; > + u32 *ldata = data; > + if (n % 4) n += 4 - n % 4; > + for (i = 0; i < n / 4; i++) > + *ldest++ = /* ntohl */ (*ldata++); > + return dest; > +} > +#else > +#define memcpy_ntoh memcpy > +#define memcpy_hton memcpy > +#endif > + > +static void print_queue(struct oeth_bd *bdp, u16 crt, int max) > +{ > + int i; > + printk ("Q Crt element: %d\n", (u32) crt); > + for (i = 0; i < max; bdp++, i++) > + { > + u32 val; > + val = bdp->len_status; > + printk("%d:%x (%x) ", val >> 16, val & 0xFFFF, bdp->addr); > + if (!((i + 1) % 4)) > + printk ("\n"); > + } > +} > + > +static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_bd *bdp; > + unsigned long flags; > + u32 len_status; > + > + spin_lock_irqsave(&cep->lock, flags); > + > + if (cep->tx_full) { > + /* All transmit buffers are full. Bail out. */ > + printk("%s: tx queue full!.\n", dev->name); > + print_queue(cep->tx_bd_base, cep->tx_next, OETH_TXBD_NUM); > + spin_unlock_irqrestore(&cep->lock, flags); > + return 1; return NETDEV_TX_BUSY. you forgot to call stop_queue > + } > + > + /* Fill in a Tx ring entry. */ > + bdp = cep->tx_bd_base + cep->tx_next; > + > + len_status = bdp->len_status; > + > + /* Clear all of the status flags. */ > + len_status &= ~OETH_TX_BD_STATS; > + > + /* If the frame is short, tell MAC to pad it. */ > + if (skb->len <= ETH_ZLEN) > + len_status |= OETH_TX_BD_PAD; > + else > + len_status &= ~OETH_TX_BD_PAD; > + > + OEDTX((printk("TX skb\n"))); > + OEDTX((oeth_print_packet((u32*)skb->data, skb->len))); > + OEDTX((printk("end TX skb print\n"))); > + > + if (skb->len > OETH_TX_BUFF_SIZE) { > + printk("%s: tx frame too long!.\n", dev->name); > + spin_unlock_irqrestore(&cep->lock, flags); > + return 1; > + } > + > + /* Copy data to TX buffer. */ > + memcpy_hton ((unsigned char *)bdp->addr, skb->data, skb->len); Use skb_copy_and_csum_dev and you get checksum offload for free. > + > + len_status = (len_status & 0x0000ffff) | (skb->len << 16); > + > + if ((bdp->addr + (len_status >> 16)) > + >= (OETH_SRAM_BUFF_BASE + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE > + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE)) > + panic("MEMORY OVERWRITE at address: %x !!!\n", (bdp->addr + > (len_status >> 16))); > + > + OEDTX((printk("TX controller buff\n"))); > + OEDTX((oeth_print_packet ((u32*)bdp->addr, bdp->len_status >> 16))); > + OEDTX(printk("end TX controller buff print\n")); > + > + dev_kfree_skb(skb); > + > + cep->tx_next = (cep->tx_next+1 == OETH_TXBD_NUM) ? 0 : (cep->tx_next+1); > + > + if (cep->tx_next == cep->tx_last){ > + cep->tx_full = 1; > + /* Do not transmit anymore if the TX queue is full. */ > + netif_stop_queue(dev); > + cep->stats.tx_compressed++; > + } > + > + /* Send it on its way. Tell controller its ready, interrupt when done, > + * and to put the CRC on the end. */ > + len_status |= (OETH_TX_BD_READY | OETH_TX_BD_IRQ | OETH_TX_BD_CRC); > + bdp->len_status = len_status; > + > + spin_unlock_irqrestore(&cep->lock, flags); > + > + dev->trans_start = jiffies; > + return 0; > +} > + > +/* The interrupt handler. */ > +static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs > *ptregs) > +{ > + struct net_device *dev = dev_id; > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_regs *regs = cep->regs; > + u32 int_events; > + > + dann_int_count++; > + > + spin_lock_irq(&cep->lock); > + You are in interrupt handler, calling spin_lock_irq is unnecessary and wrong. > + /* Get the interrupt events that caused us to be here. */ > + int_events = regs->int_src; > + /* Acknowledge interrupt. */ > + regs->int_src = int_events; > + > + if (int_events & OETH_INT_BUSY) > + cep->stats.rx_compressed++; > + > + /* Handle receive event in its own function. */ > + if (int_events & (OETH_INT_RXF | OETH_INT_RXE)) > + oeth_rx(dev); > + > + /* Handle transmit event in its own function. */ > + if (int_events & (OETH_INT_TXB | OETH_INT_TXE)) { > + oeth_tx(dev); > + } > + > + /* Check for receive busy, i.e. packets coming but no place to > + * put them. */ > + if (int_events & OETH_INT_BUSY) { > + if (!(int_events & (OETH_INT_RXF | OETH_INT_RXE))){ > +/* dann_tx_count++; */ > + oeth_rx(dev); > + } > + > + } > + > + spin_unlock_irq(&cep->lock); > + > + return IRQ_HANDLED; > +} > +static u32 dann_last_val = 0; > + > +void oeth_tx(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_bd *bdp; > + > + for (;;cep->tx_last = (cep->tx_last+1 == OETH_TXBD_NUM) ? 0 : > (cep->tx_last+1)) { > + u32 len_status; > + > + bdp = cep->tx_bd_base + cep->tx_last; > + len_status = bdp->len_status; > + > + /* If the OETH_TX_BD_READY is set the transmission has > + * not been done yet! */ > + if ((len_status & OETH_TX_BD_READY) > + || ((cep->tx_last == cep->tx_next) && !cep->tx_full)) > + break; > + > + /* Check status for errors. */ > + if (len_status & OETH_TX_BD_LATECOL) > + cep->stats.tx_window_errors++; > + if (len_status & OETH_TX_BD_RETLIM) > + cep->stats.tx_aborted_errors++; > + if (len_status & OETH_TX_BD_UNDERRUN) > + cep->stats.tx_fifo_errors++; > + if (len_status & OETH_TX_BD_CARRIER){ > + cep->stats.tx_carrier_errors++; > + } > +#if 0 > + if (len_status & (OETH_TX_BD_LATECOL | OETH_TX_BD_DEFER > + | OETH_TX_BD_UNDERRUN | OETH_TX_BD_RETRY)) > + { > + if (len_status & (OETH_TX_BD_DEFER | OETH_TX_BD_LATECOL)){ > + if (len_status & OETH_TX_BD_DEFER) > + if (len_status & OETH_TX_BD_LATECOL) > + printk ("defer late "); > + else > + printk ("defer "); > + else > + if (len_status & OETH_TX_BD_LATECOL) > + printk ("late "); > + } > + if (len_status & OETH_TX_BD_RETRY) > + printk ("retrys %d", (len_status & OETH_TX_BD_RETRY) >> 4); > + printk ("\n"); > + } > +#endif > + if (len_status & (OETH_TX_BD_LATECOL | OETH_TX_BD_RETLIM | > OETH_TX_BD_UNDERRUN)) > + cep->stats.tx_errors++; > + > + cep->stats.tx_packets++; > + cep->stats.tx_bytes += len_status >> 16; > + cep->stats.collisions += (len_status & OETH_TX_BD_RETRY) >> 4; > + dann_tx_count++; > +#if BUFFER_SCREWED > + { > + u32 *addr = (u32*)BUFFER_SCREWED_ADDR; > + int i = 0; > + while ((addr < OETH_SRAM_BUFF_BASE + 0x4000) && > (*addr != 0)){ > + *addr = 0; > + i++; > + addr++; > + } > + if (i) > + printk ("BUFFER screwed %d words !! > TX=%d rx=%d \n", i, dann_tx_count, dann_rx_count); > + > + } > +#endif > + if (0 & ((*(u32*)0xfd801FFC != 0) > + || (*(u32*)0xfd8017FC != 0) > + || (*(u32*)0xfd800FFC != 0) > + || (*(u32*)0xfd8007FC != 0))) > + printk ("SCREWED end! TX=%d rx=%d\n", dann_tx_count, > dann_rx_count); > + > + if (cep->tx_full){ > + cep->tx_full = 0; > + /* We have freed an entry in the TX queue, we can > + * start transmitting again. */ > + if (netif_queue_stopped(dev)) > + netif_wake_queue(dev); > + } > + } > +} > + > +static void oeth_rx(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_bd *bdp; > + struct sk_buff *skb; > + int bds_processed; > + int pkt_len; > + int bad = 0; > + int current_buff = cep->rx_cur; > + static int last_current_buff = 0; > + int count = 0; > + > + for (bds_processed = 0; /* bds_processed < OETH_RXBD_NUM */; > + cep->rx_cur = (cep->rx_cur+1 == OETH_RXBD_NUM) ? 0 : > (cep->rx_cur+1)) { > + u32 len_status; > + bds_processed++; > + bdp = cep->rx_bd_base + cep->rx_cur; > + > + /* First, grab all of the stats for the incoming > + * packet. These get messed up if we get called due > + * to a busy condition. */ > + len_status = bdp->len_status; > + > + if (len_status & OETH_RX_BD_EMPTY) > + break; > + > + /* Check status for errors. */ > + if (len_status & (OETH_RX_BD_TOOLONG | OETH_RX_BD_SHORT)) { > + cep->stats.rx_length_errors++; > + printk ("Length Error! l=%8x a=%8x s=%4x\n", len_status > >> 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + if (len_status & OETH_RX_BD_DRIBBLE) { > + cep->stats.rx_frame_errors++; > + printk ("Frame Error! l=%8x a=%8x s=%4x\n", len_status > >> 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + if (len_status & OETH_RX_BD_CRCERR) { > + cep->stats.rx_crc_errors++; > + printk ("CRC Error! l=%8x a=%8x s=%4x\n", len_status >> > 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + if (len_status & OETH_RX_BD_OVERRUN) { > + cep->stats.rx_crc_errors++; > + printk ("RECEIVER OVERRUN! l=%8x a=%8x s=%4x\n", > len_status >> 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + if (len_status & OETH_RX_BD_TOOLONG) { > + cep->stats.rx_crc_errors++; > + printk ("RECEIVER TOOLONG! l=%8x a=%8x s=%4x\n", > len_status >> 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + if (len_status & OETH_RX_BD_MISS) { > + > + } > + if (len_status & OETH_RX_BD_LATECOL) { > + cep->stats.rx_frame_errors++; > + printk ("LateCol Error! l=%8x a=%8x s=%4x\n", > len_status >> 16, bdp->addr, len_status && 0xFFFF); > + bad = 1; > + } > + > + > + if (bad) { > + bdp->len_status = (len_status & ~OETH_RX_BD_STATS) > + | OETH_RX_BD_EMPTY; > + continue; > + } > + > + > + /* Process the incoming frame. */ > + pkt_len = len_status >> 16; > + > + skb = dev_alloc_skb(pkt_len); netdev_alloc_skb now please > + > + if (skb == NULL) { > + printk("%s: Memory squeeze, dropping packet.\n", > dev->name); add KERN_WARNING, and use net_ratelimit > + cep->stats.rx_dropped++; > + } > + else { > + skb->dev = dev; > + OEDRX((printk("RX in ETH buf\n"))); > + OEDRX((oeth_print_packet((u32*)bdp->addr, pkt_len))); > + > + memcpy_ntoh(skb_put(skb, pkt_len), (unsigned char > *)bdp->addr, pkt_len); Copying packet in IRQ routine causes long latencies. > + OEDRX((printk("RX in memory\n"))); > + OEDRX((oeth_print_packet((u32*)skb->data, pkt_len))); > + skb->protocol = eth_type_trans(skb,dev); > + netif_rx(skb); > + count++; > + > + cep->stats.rx_packets++; > + cep->stats.rx_bytes += pkt_len; > + if (((u32) bdp->addr + pkt_len) > + >= (OETH_SRAM_BUFF_BASE + OETH_TXBD_NUM > + * OETH_TX_BUFF_SIZE > + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE)) > + panic ("address exceeds the buffer!\n"); > + dann_rx_count++; > +#if BUFFER_SCREWED > + { > + u32 *addr = (u32*)BUFFER_SCREWED_ADDR; > + u16 *shaddr = (u16*)BUFFER_SCREWED_ADDR; > + int i = 0; > + int consecutive = 0; > + if ((*shaddr + 1) == *(shaddr + 1)) > + consecutive = 1; > + else consecutive = 0; > + > + if (consecutive){ > + while ((*(shaddr - 1) == *shaddr - 1) > + && addr > OETH_SRAM_BUFF_BASE){ > + consecutive++; > + shaddr--; > + } > + } > + > + while ((addr < OETH_SRAM_BUFF_BASE + 0x4000) > + && (*addr != 0)){ > + *addr = 0; > + i++; > + addr++; > + } > + > + if (i) > + printk ("BUFFER screwed %d words !! > tx=%d RX=%d consecutive=%d crt=%d lastcrt=%d\n", i, dann_tx_count, > dann_rx_count, consecutive / 2, current_buff, last_current_buff); > + > + } > +#endif > + } > + > + bdp->len_status = (len_status & ~OETH_RX_BD_STATS) > + | OETH_RX_BD_EMPTY; > + } > + last_current_buff = cep->rx_cur; > + > + > + if (count == 0) > + cep->stats.rx_length_errors++; > + else if (count >= OETH_RXBD_NUM) > + cep->stats.rx_over_errors++; > + else > + cep->stats.rx_crc_errors++; > + > +} > + > +static int oeth_calc_crc(char *mac_addr) > +{ > + int result = 0; > + return (result & 0x3f); > +} Ah... that's not what you want, I'm sure. Did you read your own code? > +static struct net_device_stats *oeth_get_stats(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + return &cep->stats; > +} > + > +static void oeth_set_multicast_list(struct net_device *dev) > +{ > + struct dev_mc_list *dmi; > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_regs *regs = cep->regs; > + int i; > + > + if (dev->flags & IFF_PROMISC) { > + > + /* Log any net taps. */ > + printk("%s: Promiscuous mode enabled.\n", dev->name); > + regs->moder |= OETH_MODER_PRO; > + } else { > + > + regs->moder &= ~OETH_MODER_PRO; > + > + if (dev->flags & IFF_ALLMULTI) { > + > + /* Catch all multicast addresses, so set the > + * filter to all 1's. */ > + regs->hash_addr0 = 0xffffffff; > + regs->hash_addr1 = 0xffffffff; > + } > + else if (dev->mc_count) { > + > + /* Clear filter and add the addresses in the list. */ > + regs->hash_addr0 = 0x00000000; > + regs->hash_addr0 = 0x00000000; > + > + dmi = dev->mc_list; > + > + for (i = 0; i < dev->mc_count; i++) { > + > + int hash_b; > + > + /* Only support group multicast for now. */ > + if (!(dmi->dmi_addr[0] & 1)) > + continue; > + > + hash_b = oeth_calc_crc(dmi->dmi_addr); > + if(hash_b >= 32) > + regs->hash_addr1 |= 1 << (hash_b - 32); > + else > + regs->hash_addr0 |= 1 << hash_b; > + } > + } > + } > +} > + > +static int oeth_set_mac_address(struct net_device *dev, void *p) > +{ > + struct sockaddr *addr=p; > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_regs *regs = cep->regs; > + > + if (!is_valid_ether_addr(addr->sa_data)) > + return -EADDRNOTAVAIL; > + > + memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); > + > + regs->mac_addr1 = addr->sa_data[0] << 8 | > + addr->sa_data[1]; > + regs->mac_addr0 = addr->sa_data[2] << 24 | > + addr->sa_data[3] << 16 | > + addr->sa_data[4] << 8 | > + addr->sa_data[5]; > + return 0; > +} > + > +static int __init oeth_init(struct net_device *dev, unsigned int base_addr, > + unsigned int irq); > + > +/* > + * Probe for an Opencores ethernet controller. > + */ > +static struct net_device* __devinit oeth_probe(int unit) > +{ > + struct net_device *dev = alloc_etherdev(sizeof(struct oeth_private)); > + if (!dev) > + return ERR_PTR(-ENOMEM); > + sprintf(dev->name, "eth%d", unit); Unnecessary, already done by alloc_etherdev > + > + if (!check_mem_region(OETH_BASE_ADDR, OETH_REGS_SIZE)) { > + SET_MODULE_OWNER(dev); > + if (oeth_init(dev, OETH_BASE_ADDR, OETH_IRQ) == 0){ > + if (register_netdev(dev)) > + printk(KERN_WARNING "Openethernet: No card > found\n"); > + else > + return dev; > + } > + > + } > + return NULL; > +} > + > + > +#if CONFIG_MII > +static int check_if_running(struct net_device *dev) > +{ > + if (!netif_running(dev)) > + return -EINVAL; > + return 0; > +} Bogus wrapper. > +static void oeth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo > *info) > +{ > + strcpy(info->driver, DRV_NAME); > + strcpy(info->version, "0.0"); > + strcpy(info->bus_info, "none"/* pci_name(cep->pci_dev) */); > +} > + > +static int oeth_get_settings(struct net_device *dev, struct ethtool_cmd > *ecmd) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + spin_lock_irq(&cep->lock); > + mii_ethtool_gset(&cep->mii_if, ecmd); > + spin_unlock_irq(&cep->lock); > + return 0; > +} > + > +static int oeth_set_settings(struct net_device *dev, struct ethtool_cmd > *ecmd) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + int res; > + spin_lock_irq(&cep->lock); > + res = mii_ethtool_sset(&cep->mii_if, ecmd); > + spin_unlock_irq(&cep->lock); > + return res; > +} > + > +static int oeth_nway_reset(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + return mii_nway_restart(&cep->mii_if); > +} > + > +static u32 oeth_get_link(struct net_device *dev) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + return mii_link_ok(&cep->mii_if); > +} > + > +static struct ethtool_ops ethtool_ops = { > + .begin = check_if_running, > + .get_drvinfo = oeth_get_drvinfo, > + .get_settings = oeth_get_settings, > + .set_settings = oeth_set_settings, > + .nway_reset = oeth_nway_reset, > + .get_link = oeth_get_link, > + .get_strings = ethtool_op_net_device_stats_get_strings, > + .get_stats_count = ethtool_op_net_device_stats_get_stats_count, > + .get_ethtool_stats = ethtool_op_net_device_get_ethtool_stats, > +}; > + > +/* MII Data accesses. */ > +static int mdio_read(struct net_device *dev, int phy_id, int location) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + struct oeth_regs *regs = cep->regs; > + int read_value, i; > + volatile int v; > + > + regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD) > + | ((location << 8) & OETH_MIIADDRESS_RGAD); > + regs->miicommand = OETH_MIICOMMAND_RSTAT; > + > + /* Check if the MII is done. */ > + for (i = 10000; i >= 0; i--){ > + v = regs->miistatus; > + if (!(v & OETH_MIISTATUS_BUSY)){ > + read_value = regs->miirx_data; > + /* Don't leave miicommand in read status, it > + * seems to not be reset to 0 after completion. */ > + regs->miicommand = 0; > + return read_value; > + } > + } > + printk(KERN_ERR "mdio_read timeout %s\n", dev->name); > + return -1; > +} > +#endif > + > +static void mdio_write(struct net_device *dev, int phy_id, int location, int > value) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + struct oeth_regs *regs = cep->regs; > + int i; > + volatile int v; > + regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD) > + | ((location << 8) & OETH_MIIADDRESS_RGAD); > + regs->miitx_data = value; > + regs->miicommand = OETH_MIICOMMAND_WCTRLDATA; > + /* Check if the MII is done. */ > + for (i = 10000; i >= 0; i--){ > + v = regs->miistatus; > + if (!(v & OETH_MIISTATUS_BUSY)) > + return ; > + } > + printk(KERN_ERR "mdio_write timeout %s\n", dev->name); > +} > + > +/* Initialize the Open Ethernet MAC. */ > +static int oeth_init(struct net_device *dev, unsigned int base_addr, > + unsigned int irq) > +{ > + struct oeth_private *cep = netdev_priv(dev); > + volatile struct oeth_regs *regs; > + volatile struct oeth_bd *tx_bd, *rx_bd; > + int i; > + unsigned long mem_addr = OETH_SRAM_BUFF_BASE; > + > + /* Reset the private structure. */ > + memset(cep, 0, sizeof (struct oeth_private)); Unnecessary already zeroed by alloc_etherdev > + /* Initialize the lock. */ > + spin_lock_init(&cep->lock); > + > + /* Memory regions for the controller registers and buffer space. */ > + request_region(base_addr, OETH_REGS_SIZE, DRV_NAME); > + dev->base_addr = base_addr; > + request_region(OETH_SRAM_BUFF_BASE, > + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE > + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE, DRV_NAME); > + /* Get pointer ethernet controller configuration registers. */ > + regs = cep->regs = (struct oeth_regs *)(base_addr); > + > + > + /* Reset the controller. */ > + regs->moder = OETH_MODER_RST; /* Reset ON */ > + regs->moder &= ~OETH_MODER_RST; /* Reset OFF */ > + > + /* Setting TXBD base to OETH_TXBD_NUM. */ > + regs->tx_bd_num = OETH_TXBD_NUM; > + > + /* Initialize TXBD pointer. */ > + cep->tx_bd_base = (struct oeth_bd *)OETH_BD_BASE; > + tx_bd = cep->tx_bd_base; > + > + /* Initialize RXBD pointer. */ > + cep->rx_bd_base = cep->tx_bd_base + OETH_TXBD_NUM; > + rx_bd = cep->rx_bd_base; > + > + /* Initialize receive/transmit pointers. */ > + cep->rx_cur = 0; > + cep->tx_next = 0; > + cep->tx_last = 0; > + cep->tx_full = 0; > + > + /* Set min (64) and max (1536) packet length. */ > + regs->packet_len = (64 << 16) | 1536; > + > + /* Set IPGT, IPGR1, IPGR2 and COLLCONF registers to the > + * recommended values. */ > + regs->ipgt = 0x00000015; > + regs->ipgr1 = 0x0000000c; > + regs->ipgr2 = 0x00000012; > + regs->collconf = 0x000f003f; > + > + /* Set control module mode. Do not deal with PAUSE frames for now. */ > + regs->ctrlmoder = 0; > + > +#if CONFIG_MII > + /* Initialize MII. */ > + cep->mii_if.dev = dev; > + cep->mii_if.mdio_read = mdio_read; > + cep->mii_if.mdio_write = mdio_write; > + cep->mii_if.phy_id = OETH_PHY_ID; > + cep->mii_if.phy_id_mask = OETH_MIIADDRESS_FIAD; > + cep->mii_if.reg_num_mask = 0x1f; > + SET_ETHTOOL_OPS(dev, ðtool_ops); > +#endif > + > + /* Platform specific initialization. This function should set > + at least set regs->mac_addr1 and regs->mac_addr2. */ > +/* OETH_PLATFORM_SPECIFIC_INIT(regs); */ > +do { > + /* Set the clock divider to 2 (50MHz / 2) */ > + regs->miimoder = (OETH_MIIMODER_CLKDIV & 0x2); > + > + /* Reset the PHY. */ > + { > + int i, res; > + mdio_write(dev, cep->mii_if.phy_id, MII_BMCR, BMCR_RESET); > + /* Wait until the reset is complete. */ > + for (i = 10000; i >= 0; i--){ > + res = mdio_read(dev, cep->mii_if.phy_id, MII_BMCR); > + if (!(res & BMCR_RESET)) > + break; > + } > + if (res & BMCR_RESET) { > + printk(KERN_ERR "%s PHY reset timeout BMCR:0x%08x!\n", > dev->name, res); > + return -1; > + } > + } > + > + /* Tell the PHY to turn on the activity LED. */ > + mdio_write(dev, 0, MII_TPISTATUS, 0xce); > + > + > + { > + /* Test code to setup the networking parameters according to > + the DIP switches. */ > + u32 net_settings = ((*(u32*)DIP_SWITCHES_ADDR) & 0xc0) >> 6; > + if (net_settings){ > + /* Disable autonegotiation in order to disable full duplex. */ > + u32 cword; > + if (net_settings & 1) > + /* half duplex requested */ > + cword = 0x0000; > + else > + cword = BMCR_FULLDPLX; > + > + if (net_settings & 2) > + /* 10 Mbit requested */ > + cword |= 0x0000; > + else > + cword |= BMCR_SPEED100; > + > + mdio_write(dev, 0, MII_BMCR, cword); > + } > + } > + > + > + > + /* Initialize the MAC address. */ > + regs->mac_addr1 = OETH_MACADDR0 << 8 | OETH_MACADDR1; > + regs->mac_addr0 = OETH_MACADDR2 << 24 | OETH_MACADDR3 << 16 > + | OETH_MACADDR4 << 8 | ((*(u32*)DIP_SWITCHES_ADDR) & 0x3f); > + > + } while (0); Bogus indentation, bogus while loop?? > + > + /* Initialize TXBDs. */ > + for(i = 0; i < OETH_TXBD_NUM; i++) { > + tx_bd[i].len_status = OETH_TX_BD_PAD | OETH_TX_BD_CRC | > OETH_TX_BD_IRQ; > + tx_bd[i].addr = mem_addr; > + mem_addr += OETH_TX_BUFF_SIZE; > + } > + tx_bd[OETH_TXBD_NUM - 1].len_status |= OETH_TX_BD_WRAP; > + > + /* Initialize RXBDs. */ > + for(i = 0; i < OETH_RXBD_NUM; i++) { > + rx_bd[i].len_status = OETH_RX_BD_EMPTY | OETH_RX_BD_IRQ; > + rx_bd[i].addr = mem_addr; > + mem_addr += OETH_RX_BUFF_SIZE; > + } > + rx_bd[OETH_RXBD_NUM - 1].len_status |= OETH_RX_BD_WRAP; > + > + /* Set default ethernet MAC address. */ > + dev->dev_addr[0] = (regs->mac_addr1 >> 8) & 0xff; > + dev->dev_addr[1] = regs->mac_addr1 & 0xff; > + dev->dev_addr[2] = (regs->mac_addr0 >> 24) & 0xff; > + dev->dev_addr[3] = (regs->mac_addr0 >> 16) & 0xff; > + dev->dev_addr[4] = (regs->mac_addr0 >> 8) & 0xff; > + dev->dev_addr[5] = regs->mac_addr0 & 0xff; > + > + /* Clear all pending interrupts. */ > + regs->int_src = 0xffffffff; > + > + /* Promisc, IFG, CRCEn */ > + regs->moder |= OETH_MODER_PAD | OETH_MODER_IFG | OETH_MODER_CRCEN; > + > + /* Enable interrupt sources. */ > + regs->int_mask = OETH_INT_MASK_TXB | OETH_INT_MASK_TXE > + | OETH_INT_MASK_RXF | OETH_INT_MASK_RXE > + | OETH_INT_MASK_TXC | OETH_INT_MASK_RXC > + | OETH_INT_MASK_BUSY; > + > + /* Fill in the fields of the device structure with ethernet values. */ > + ether_setup(dev); Already done by alloc_etherdev > + > + /* The Open Ethernet specific entries in the device structure. */ > + dev->open = oeth_open; > + dev->hard_start_xmit = oeth_start_xmit; > + dev->stop = oeth_close; > + dev->get_stats = oeth_get_stats; > + dev->set_multicast_list = oeth_set_multicast_list; > + dev->set_mac_address = oeth_set_mac_address; > + dev->irq = irq; > + /* FIXME: Something needs to be done with dev->tx_timeout and > + dev->watchdog timeout here. */ > + > + printk("%s: Open Ethernet Core Version 1.0\n", dev->name); > + > + return 0; > +} > + > +static struct net_device *oeth_dev; > + > +static int __init oeth_init_module(void) > +{ > + oeth_dev = oeth_probe(0); > + if (!oeth_dev) > + return PTR_ERR(oeth_dev); > + return 0; > +} > + > +static void __exit oeth_cleanup_module(void) > +{ > + unregister_netdev(oeth_dev); > + release_region(oeth_dev->base_addr, OETH_REGS_SIZE); > + release_region(OETH_SRAM_BUFF_BASE, > + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE > + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE); > + free_netdev(oeth_dev); > +} > + > +module_init(oeth_init_module); > +module_exit(oeth_cleanup_module); > + > +MODULE_DESCRIPTION("Opencores ethernet driver."); > +MODULE_LICENSE("GPL"); > -- Stephen Hemminger <[EMAIL PROTECTED]> - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html