diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-04-28 01:09:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-04-28 01:09:25 +0000 |
commit | b9ba7aeb165cffecdffb60aec8c3fa8d590d9ca9 (patch) | |
tree | 42d07b0c7246ae2536a702e7c5de9e2732341116 /drivers/net/am79c961a.c | |
parent | 7406b0a326f2d70ade2671c37d1beef62249db97 (diff) |
Merge with 2.3.99-pre6.
Diffstat (limited to 'drivers/net/am79c961a.c')
-rw-r--r-- | drivers/net/am79c961a.c | 300 |
1 files changed, 190 insertions, 110 deletions
diff --git a/drivers/net/am79c961a.c b/drivers/net/am79c961a.c index 9c00c4cbd..c7e7f2fe5 100644 --- a/drivers/net/am79c961a.c +++ b/drivers/net/am79c961a.c @@ -33,40 +33,49 @@ #include "am79c961a.h" +static int am79c961_probe1 (struct net_device *dev); +static int am79c961_open (struct net_device *dev); +static int am79c961_sendpacket (struct sk_buff *skb, struct net_device *dev); +static void am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static void am79c961_rx (struct net_device *dev, struct dev_priv *priv); +static void am79c961_tx (struct net_device *dev, struct dev_priv *priv); +static int am79c961_close (struct net_device *dev); +static struct enet_statistics *am79c961_getstats (struct net_device *dev); +static void am79c961_setmulticastlist (struct net_device *dev); +static void am79c961_timeout(struct net_device *dev); + static unsigned int net_debug = NET_DEBUG; static void am79c961_setmulticastlist (struct net_device *dev); -static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.00\n"; +static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.01\n"; #define FUNC_PROLOGUE \ struct dev_priv *priv = (struct dev_priv *)dev->priv /* --------------------------------------------------------------------------- */ +#ifdef __arm__ static void write_rreg (unsigned long base, unsigned int reg, unsigned short val) { - __asm__(" - strh %1, [%2] @ NET_RAP - strh %0, [%2, #-4] @ NET_RDP + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #-4] @ NET_RDP " : : "r" (val), "r" (reg), "r" (0xf0000464)); } static inline void write_ireg (unsigned long base, unsigned int reg, unsigned short val) { - __asm__(" - strh %1, [%2] @ NET_RAP - strh %0, [%2, #8] @ NET_RDP + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #8] @ NET_RDP " : : "r" (val), "r" (reg), "r" (0xf0000464)); } #define am_writeword(dev,off,val)\ - __asm__("\ - strh %0, [%1]\ - " : : "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1))); + __asm__("str%?h %0, [%1]" : : \ + "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1))); static inline void am_writebuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, unsigned int length) @@ -74,30 +83,28 @@ am_writebuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, offset = 0xe0000000 + (offset << 1); length = (length + 1) & ~1; if ((int)buf & 2) { - __asm__ __volatile__(" - strh %2, [%0], #4 - " : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); buf += 2; length -= 2; } while (length > 8) { unsigned int tmp, tmp2; __asm__ __volatile__(" - ldmia %1!, {%2, %3} - strh %2, [%0], #4 - mov %2, %2, lsr #16 - strh %2, [%0], #4 - strh %3, [%0], #4 - mov %3, %3, lsr #16 - strh %3, [%0], #4 + ldm%?ia %1!, {%2, %3} + str%?h %2, [%0], #4 + mov%? %2, %2, lsr #16 + str%?h %2, [%0], #4 + str%?h %3, [%0], #4 + mov%? %3, %3, lsr #16 + str%?h %3, [%0], #4 " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) : "0" (offset), "1" (buf)); length -= 8; } while (length > 0) { - __asm__ __volatile__(" - strh %2, [%0], #4 - " : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); buf += 2; length -= 2; } @@ -107,9 +114,8 @@ static inline unsigned short read_rreg (unsigned int base_addr, unsigned int reg) { unsigned short v; - __asm__(" - strh %1, [%2] @ NET_RAP - ldrh %0, [%2, #-4] @ NET_IDP + __asm__("str%?h %1, [%2] @ NET_RAP + ldr%?h %0, [%2, #-4] @ NET_IDP " : "=r" (v): "r" (reg), "r" (0xf0000464)); return v; } @@ -120,9 +126,7 @@ am_readword (struct net_device *dev, unsigned long off) unsigned long address = 0xe0000000 + (off << 1); unsigned short val; - __asm__(" - ldrh %0, [%1] - " : "=r" (val): "r" (address)); + __asm__("ldr%?h %0, [%1]" : "=r" (val): "r" (address)); return val; } @@ -134,23 +138,23 @@ am_readbuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, u if ((int)buf & 2) { unsigned int tmp; __asm__ __volatile__(" - ldrh %2, [%0], #4 - strb %2, [%1], #1 - mov %2, %2, lsr #8 - strb %2, [%1], #1 + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); length -= 2; } while (length > 8) { unsigned int tmp, tmp2, tmp3; __asm__ __volatile__(" - ldrh %2, [%0], #4 - ldrh %3, [%0], #4 - orr %2, %2, %3, lsl #16 - ldrh %3, [%0], #4 - ldrh %4, [%0], #4 - orr %3, %3, %4, lsl #16 - stmia %1!, {%2, %3} + ldr%?h %2, [%0], #4 + ldr%?h %3, [%0], #4 + orr%? %2, %2, %3, lsl #16 + ldr%?h %3, [%0], #4 + ldr%?h %4, [%0], #4 + orr%? %3, %3, %4, lsl #16 + stm%?ia %1!, {%2, %3} " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) : "0" (offset), "1" (buf)); length -= 8; @@ -158,14 +162,17 @@ am_readbuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, u while (length > 0) { unsigned int tmp; __asm__ __volatile__(" - ldrh %2, [%0], #4 - strb %2, [%1], #1 - mov %2, %2, lsr #8 - strb %2, [%1], #1 + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); length -= 2; } } +#else +#error Not compatable +#endif static int am79c961_ramtest(struct net_device *dev, unsigned int val) @@ -259,7 +266,7 @@ am79c961_init_for_open(struct net_device *dev) write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM); + write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); } @@ -304,7 +311,7 @@ am79c961_probe1(struct net_device *dev) /* * The PNP initialisation should have been done by the ether bootp loader. */ - inb ((dev->base_addr + NET_RESET) >> 1); /* reset the device */ + inb((dev->base_addr + NET_RESET) >> 1); /* reset the device */ udelay (5); @@ -343,6 +350,7 @@ am79c961_probe1(struct net_device *dev) dev->hard_start_xmit = am79c961_sendpacket; dev->get_stats = am79c961_getstats; dev->set_multicast_list = am79c961_setmulticastlist; + dev->tx_timeout = am79c961_timeout; /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); @@ -382,14 +390,15 @@ am79c961_open(struct net_device *dev) memset (&priv->stats, 0, sizeof (priv->stats)); - if (request_irq(dev->irq, am79c961_interrupt, 0, "am79c961", dev)) + if (request_irq(dev->irq, am79c961_interrupt, 0, "am79c961", dev)) { + MOD_DEC_USE_COUNT; return -EAGAIN; + } am79c961_init_for_open(dev); - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; + netif_start_queue(dev); + return 0; } @@ -399,8 +408,7 @@ am79c961_open(struct net_device *dev) static int am79c961_close(struct net_device *dev) { - dev->tbusy = 1; - dev->start = 0; + netif_stop_queue(dev); am79c961_init(dev); @@ -420,26 +428,116 @@ static struct enet_statistics *am79c961_getstats (struct net_device *dev) return &priv->stats; } +static inline u32 update_crc(u32 crc, u8 byte) +{ + int i; + + for (i = 8; i != 0; i--) { + byte ^= crc & 1; + crc >>= 1; + + if (byte & 1) + crc ^= 0xedb88320; + + byte >>= 1; + } + + return crc; +} + +static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash) +{ + if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) { + int i, idx, bit; + u32 crc; + + crc = 0xffffffff; + + for (i = 0; i < ETH_ALEN; i++) + crc = update_crc(crc, dmi->dmi_addr[i]); + + idx = crc >> 30; + bit = (crc >> 26) & 15; + + hash[idx] |= 1 << bit; + } +} + /* * Set or clear promiscuous/multicast mode filter for this adaptor. - * - * We don't attempt any packet filtering. The card may have a SEEQ 8004 - * in which does not have the other ethernet address registers present... */ static void am79c961_setmulticastlist (struct net_device *dev) { unsigned long flags; - int i; + unsigned short multi_hash[4], mode; + int i, stopped; - dev->flags &= ~IFF_ALLMULTI; + mode = MODE_PORT0; - i = MODE_PORT0; - if (dev->flags & IFF_PROMISC) - i |= MODE_PROMISC; + if (dev->flags & IFF_PROMISC) { + mode |= MODE_PROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + memset(multi_hash, 0xff, sizeof(multi_hash)); + } else { + struct dev_mc_list *dmi; - save_flags_cli (flags); - write_rreg (dev->base_addr, MODE, i); - restore_flags (flags); + memset(multi_hash, 0x00, sizeof(multi_hash)); + + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + am79c961_mc_hash(dmi, multi_hash); + } + + save_flags_cli(flags); + + stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; + + if (!stopped) { + /* + * Put the chip into suspend mode + */ + write_rreg(dev->base_addr, CTRL1, CTRL1_SPND); + + /* + * Spin waiting for chip to report suspend mode + */ + while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { + restore_flags(flags); + nop(); + save_flags_cli(flags); + } + } + + /* + * Update the multicast hash table + */ + for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++) + write_rreg(dev->base_addr, i + LADRL, multi_hash[i]); + + /* + * Write the mode register + */ + write_rreg(dev->base_addr, MODE, mode); + + if (!stopped) { + /* + * Put the chip back into running mode + */ + write_rreg(dev->base_addr, CTRL1, 0); + } + + restore_flags(flags); +} + +static void am79c961_timeout(struct net_device *dev) +{ + printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", + dev->name); + + /* + * ought to do some setup of the tx side here + */ + + netif_wake_queue(dev); } /* @@ -449,46 +547,34 @@ static int am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) { struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned int hdraddr, bufaddr; + unsigned int head; + unsigned long flags; - if (!dev->tbusy) { -again: - if (!test_and_set_bit(0, (void*)&dev->tbusy)) { - unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned int hdraddr, bufaddr; - unsigned long flags; - - hdraddr = priv->txhdr + (priv->txhead << 3); - bufaddr = priv->txbuffer[priv->txhead]; - priv->txhead ++; - if (priv->txhead >= TX_BUFFERS) - priv->txhead = 0; - - am_writebuffer (dev, bufaddr, skb->data, length); - am_writeword (dev, hdraddr + 4, -length); - am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); - - save_flags_cli (flags); - write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); - dev->trans_start = jiffies; - restore_flags (flags); - - if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)) - dev->tbusy = 0; - dev_kfree_skb (skb); - return 0; - } else - printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); - return 1; - } else { - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) - return 1; - printk (KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name); - /* Try to restart the adaptor. */ - dev->tbusy = 0; - dev->trans_start = jiffies; - goto again; - } + head = priv->txhead; + hdraddr = priv->txhdr + (head << 3); + bufaddr = priv->txbuffer[head]; + head += 1; + if (head >= TX_BUFFERS) + head = 0; + + am_writebuffer (dev, bufaddr, skb->data, length); + am_writeword (dev, hdraddr + 4, -length); + am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); + priv->txhead = head; + + save_flags_cli (flags); + write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); + dev->trans_start = jiffies; + restore_flags (flags); + + if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)) + netif_stop_queue(dev); + + dev_kfree_skb(skb); + + return 0; } static void @@ -503,8 +589,6 @@ am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) printk(KERN_DEBUG "am79c961irq: %d ", irq); #endif - dev->interrupt = 1; - status = read_rreg (dev->base_addr, CSR0); write_rreg (dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); @@ -515,8 +599,6 @@ am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (status & CSR0_MISS) priv->stats.rx_dropped ++; - dev->interrupt = 0; - #if NET_DEBUG > 1 if(net_debug & DEBUG_INT) printk("done\n"); @@ -613,7 +695,7 @@ am79c961_tx(struct net_device *dev, struct dev_priv *priv) am_writeword (dev, hdraddr + 6, 0); if (status2 & TST_RTRY) - priv->stats.collisions += 1; + priv->stats.collisions += 16; if (status2 & TST_LCOL) priv->stats.tx_window_errors ++; if (status2 & TST_LCAR) @@ -625,7 +707,5 @@ am79c961_tx(struct net_device *dev, struct dev_priv *priv) priv->stats.tx_packets ++; } while (priv->txtail != priv->txhead); - dev->tbusy = 0; - mark_bh (NET_BH); + netif_wake_queue(dev); } - |