patch-2.4.0-test2 linux/drivers/net/ioc3-eth.c
Next file: linux/drivers/net/irda/Makefile
Previous file: linux/drivers/net/ibmlana.h
Back to the patch index
Back to the overall index
- Lines: 1148
- Date:
Mon Jun 19 13:30:58 2000
- Orig file:
v2.4.0-test1/linux/drivers/net/ioc3-eth.c
- Orig date:
Tue May 23 15:31:35 2000
diff -u --recursive --new-file v2.4.0-test1/linux/drivers/net/ioc3-eth.c linux/drivers/net/ioc3-eth.c
@@ -1,5 +1,4 @@
-/* $Id$
- *
+/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
@@ -33,12 +32,8 @@
*
* To do:
*
- * - ioc3_close() should attempt to shutdown the adapter somewhat more
- * gracefully.
- * - Free rings and buffers when closing or before re-initializing rings.
* - Handle allocation failures in ioc3_alloc_skb() more gracefully.
* - Handle allocation failures in ioc3_init_rings().
- * - Maybe implement private_ioctl().
* - Use prefetching for large packets. What is a good lower limit for
* prefetching?
* - We're probably allocating a bit too much memory.
@@ -55,6 +50,7 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/pci_ids.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -75,20 +71,21 @@
/* 32 RX buffers. This is tunable in the range of 16 <= x < 512. */
#define RX_BUFFS 32
-static void ioc3_set_multicast_list(struct net_device *dev);
-static int ioc3_open(struct net_device *dev);
-static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void ioc3_timeout(struct net_device *dev);
-static int ioc3_close(struct net_device *dev);
-static inline unsigned int ioc3_hash(const unsigned char *addr);
-
-static const char ioc3_str[] = "IOC3 Ethernet";
+/* Private ioctls that de facto are well known and used for examply
+ by mii-tool. */
+#define SIOCGMIIPHY (SIOCDEVPRIVATE) /* Read from current PHY */
+#define SIOCGMIIREG (SIOCDEVPRIVATE+1) /* Read any PHY register */
+#define SIOCSMIIREG (SIOCDEVPRIVATE+2) /* Write any PHY register */
+
+/* These exist in other drivers; we don't use them at this time. */
+#define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters */
+#define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters */
/* Private per NIC data of the driver. */
struct ioc3_private {
struct ioc3 *regs;
int phy;
- unsigned long rxr; /* pointer to receiver ring */
+ unsigned long *rxr; /* pointer to receiver ring */
struct ioc3_etxd *txr;
struct sk_buff *rx_skbs[512];
struct sk_buff *tx_skbs[128];
@@ -97,9 +94,23 @@
int rx_pi; /* RX producer index */
int tx_ci; /* TX consumer index */
int tx_pi; /* TX producer index */
+ int txqlen;
+ u32 emcr, ehar_h, ehar_l;
spinlock_t ioc3_lock;
};
+static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void ioc3_set_multicast_list(struct net_device *dev);
+static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void ioc3_timeout(struct net_device *dev);
+static inline unsigned int ioc3_hash(const unsigned char *addr);
+static void ioc3_stop(struct net_device *dev);
+static void ioc3_clean_tx_ring(struct ioc3_private *ip);
+static void ioc3_clean_rx_ring(struct ioc3_private *ip);
+static void ioc3_init(struct net_device *dev);
+
+static const char ioc3_str[] = "IOC3 Ethernet";
+
/* We use this to acquire receive skb's that we can DMA directly into. */
#define ALIGNED_RX_SKB_ADDR(addr) \
((((unsigned long)(addr) + (128 - 1)) & ~(128 - 1)) - (unsigned long)(addr))
@@ -124,17 +135,6 @@
#define BARRIER() \
__asm__("sync" ::: "memory")
-/* This El Cheapo implementatin of TX_BUFFS_AVAIL may leave on entry unused.
- Since the TX ring has 128 entries which is fairly large we don't care and
- use this, more efficient implementation. */
-#define TX_BUFFS_AVAIL(ip) \
-({ \
- struct ioc3_private *_ip = (ip); \
- ((512 + _ip->tx_ci + 1) - _ip->tx_pi) & 511; \
-})
-//#undef TX_BUFFS_AVAIL
-//#define TX_BUFFS_AVAIL(ip) (1)
-
#define IOC3_SIZE 0x100000
@@ -182,6 +182,30 @@
return presence;
}
+static inline int
+nic_read_bit(struct ioc3 *ioc3)
+{
+ int result;
+
+ ioc3_w(mcr, mcr_pack(6, 13));
+ result = nic_wait(ioc3);
+ ioc3_w(mcr, mcr_pack(0, 100));
+ nic_wait(ioc3);
+
+ return result;
+}
+
+static inline void
+nic_write_bit(struct ioc3 *ioc3, int bit)
+{
+ if (bit)
+ ioc3_w(mcr, mcr_pack(6, 110));
+ else
+ ioc3_w(mcr, mcr_pack(80, 30));
+
+ nic_wait(ioc3);
+}
+
/*
* Read a byte from an iButton device
*/
@@ -191,13 +215,8 @@
u32 result = 0;
int i;
- for (i = 0; i < 8; i++) {
- ioc3_w(mcr, mcr_pack(6, 13));
- result = (result >> 1) | (nic_wait(ioc3) << 7);
-
- ioc3_w(mcr, mcr_pack(0, 100));
- nic_wait(ioc3);
- }
+ for (i = 0; i < 8; i++)
+ result = (result >> 1) | (nic_read_bit(ioc3) << 7);
return result;
}
@@ -214,34 +233,106 @@
bit = byte & 1;
byte >>= 1;
- if (bit)
- ioc3_w(mcr, mcr_pack(6, 110));
- else
- ioc3_w(mcr, mcr_pack(80, 30));
- nic_wait(ioc3);
+ nic_write_bit(ioc3, bit);
+ }
+}
+
+static u64
+nic_find(struct ioc3 *ioc3, int *last)
+{
+ int a, b, index, disc;
+ u64 address = 0;
+
+ nic_reset(ioc3);
+ /* Search ROM. */
+ nic_write_byte(ioc3, 0xf0);
+
+ /* Algorithm from ``Book of iButton Standards''. */
+ for (index = 0, disc = 0; index < 64; index++) {
+ a = nic_read_bit(ioc3);
+ b = nic_read_bit(ioc3);
+
+ if (a && b) {
+ printk("NIC search failed (not fatal).\n");
+ *last = 0;
+ return 0;
+ }
+
+ if (!a && !b) {
+ if (index == *last) {
+ address |= 1UL << index;
+ } else if (index > *last) {
+ address &= ~(1UL << index);
+ disc = index;
+ } else if ((address & (1UL << index)) == 0)
+ disc = index;
+ nic_write_bit(ioc3, address & (1UL << index));
+ continue;
+ } else {
+ if (a)
+ address |= 1UL << index;
+ else
+ address &= ~(1UL << index);
+ nic_write_bit(ioc3, a);
+ continue;
+ }
}
+
+ *last = disc;
+
+ return address;
}
-static void nic_show_regnr(struct ioc3 *ioc3)
+static int nic_init(struct ioc3 *ioc3)
{
const char *type;
- u8 regnr[8];
- int i;
+ u8 crc;
+ u8 serial[6];
+ int save = 0, i;
+
+ type = "unknown";
+
+ while (1) {
+ u64 reg;
+ reg = nic_find(ioc3, &save);
+
+ switch (reg & 0xff) {
+ case 0x91:
+ type = "DS1981U";
+ break;
+ default:
+ if (save == 0) {
+ /* Let the caller try again. */
+ return -1;
+ }
+ continue;
+ }
- nic_write_byte(ioc3, 0x33);
- for (i = 0; i < 8; i++)
- regnr[i] = nic_read_byte(ioc3);
+ nic_reset(ioc3);
+
+ /* Match ROM. */
+ nic_write_byte(ioc3, 0x55);
+ for (i = 0; i < 8; i++)
+ nic_write_byte(ioc3, (reg >> (i << 3)) & 0xff);
+
+ reg >>= 8; /* Shift out type. */
+ for (i = 0; i < 6; i++) {
+ serial[i] = reg & 0xff;
+ reg >>= 8;
+ }
+ crc = reg & 0xff;
+ break;
+ }
- switch(regnr[0]) {
- case 0x01: type = "DS1990A"; break;
- case 0x91: type = "DS1981U"; break;
- default: type = "unknown"; break;
+ printk("Found %s NIC", type);
+ if (type != "unknown") {
+ printk (" registration number %02x:%02x:%02x:%02x:%02x:%02x,"
+ " CRC %02x", serial[0], serial[1], serial[2],
+ serial[3], serial[4], serial[5], crc);
}
+ printk(".\n");
- printk("Found %s NIC, registration number "
- "%02x:%02x:%02x:%02x:%02x:%02x, CRC %02x.\n", type,
- regnr[1], regnr[2], regnr[3], regnr[4], regnr[5], regnr[6],
- regnr[7]);
+ return 0;
}
/*
@@ -251,14 +342,22 @@
{
u8 nic[14];
int i;
+ int tries = 2; /* There may be some problem with the battery? */
ioc3_w(gpcr_s, (1 << 21));
- nic_reset(ioc3);
- nic_show_regnr(ioc3);
+ while (tries--) {
+ if (!nic_init(ioc3))
+ break;
+ udelay(500);
+ }
- nic_reset(ioc3);
- nic_write_byte(ioc3, 0xcc);
+ if (tries < 0) {
+ printk("Failed to read MAC address\n");
+ return;
+ }
+
+ /* Read Memory. */
nic_write_byte(ioc3, 0xf0);
nic_write_byte(ioc3, 0x00);
nic_write_byte(ioc3, 0x00);
@@ -276,6 +375,8 @@
printk(".\n");
}
+/* Caller must hold the ioc3_lock ever for MII readers. This is also
+ used to protect the transmitter side but it's low contention. */
static u16 mii_read(struct ioc3 *ioc3, int phy, int reg)
{
while (ioc3->micr & MICR_BUSY);
@@ -320,9 +421,6 @@
w0 = rxb->w0;
while (w0 & ERXBUF_V) {
- ioc3->eisr = EISR_RXTIMERINT; /* Ack */
- ioc3->eisr; /* Flush */
-
err = rxb->err; /* It's valid ... */
if (err & ERXBUF_GOODPKT) {
len = (w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff;
@@ -349,21 +447,17 @@
ip->stats.rx_packets++; /* Statistics */
ip->stats.rx_bytes += len;
- goto next;
- }
- if (err & (ERXBUF_CRCERR | ERXBUF_FRAMERR | ERXBUF_CODERR |
- ERXBUF_INVPREAMB | ERXBUF_BADPKT | ERXBUF_CARRIER)) {
- /* We don't send the skbuf to the network layer, so
- just recycle it. */
- new_skb = skb;
-
- if (err & ERXBUF_CRCERR) /* Statistics */
- ip->stats.rx_crc_errors++;
- if (err & ERXBUF_FRAMERR)
- ip->stats.rx_frame_errors++;
- ip->stats.rx_errors++;
+ } else {
+ /* The frame is invalid and the skb never
+ reached the network layer so we can just
+ recycle it. */
+ new_skb = skb;
+ ip->stats.rx_errors++;
}
-
+ if (err & ERXBUF_CRCERR) /* Statistics */
+ ip->stats.rx_crc_errors++;
+ if (err & ERXBUF_FRAMERR)
+ ip->stats.rx_frame_errors++;
next:
ip->rx_skbs[n_entry] = new_skb;
rxr[n_entry] = (0xa5UL << 56) |
@@ -380,29 +474,28 @@
}
ip->rx_pi = n_entry;
ip->rx_ci = rx_entry;
-
- return;
}
static inline void
-ioc3_tx(struct ioc3_private *ip, struct ioc3 *ioc3)
+ioc3_tx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
{
+ unsigned long packets, bytes;
int tx_entry, o_entry;
struct sk_buff *skb;
u32 etcir;
spin_lock(&ip->ioc3_lock);
etcir = ioc3->etcir;
+
tx_entry = (etcir >> 7) & 127;
o_entry = ip->tx_ci;
+ packets = 0;
+ bytes = 0;
while (o_entry != tx_entry) {
- ioc3->eisr = EISR_TXEXPLICIT; /* Ack */
- ioc3->eisr; /* Flush */
-
+ packets++;
skb = ip->tx_skbs[o_entry];
- ip->stats.tx_packets++;
- ip->stats.tx_bytes += skb->len;
+ bytes += skb->len;
dev_kfree_skb_irq(skb);
ip->tx_skbs[o_entry] = NULL;
@@ -411,14 +504,24 @@
etcir = ioc3->etcir; /* More pkts sent? */
tx_entry = (etcir >> 7) & 127;
}
+
+ ip->stats.tx_packets += packets;
+ ip->stats.tx_bytes += bytes;
+ ip->txqlen -= packets;
+
+ if (ip->txqlen < 128)
+ netif_wake_queue(dev);
+
ip->tx_ci = o_entry;
spin_unlock(&ip->ioc3_lock);
}
/*
- * Deal with fatal IOC3 errors. For now let's panic. This condition might
- * be caused by a hard or software problems, so we should try to recover
- * more gracefully if this ever happens.
+ * Deal with fatal IOC3 errors. This condition might be caused by a hard or
+ * software problems, so we should try to recover
+ * more gracefully if this ever happens. In theory we might be flooded
+ * with such error interrupts if something really goes wrong, so we might
+ * also consider to take the interface down.
*/
static void
ioc3_error(struct net_device *dev, struct ioc3_private *ip,
@@ -426,12 +529,19 @@
{
if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
if (eisr & EISR_RXMEMERR) {
- panic("%s: RX PCI error.\n", dev->name);
+ printk(KERN_ERR "%s: RX PCI error.\n", dev->name);
}
if (eisr & EISR_TXMEMERR) {
- panic("%s: TX PCI error.\n", dev->name);
+ printk(KERN_ERR "%s: TX PCI error.\n", dev->name);
}
}
+
+ ioc3_stop(dev);
+ ioc3_clean_tx_ring(dev->priv);
+ ioc3_init(dev);
+
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
}
/* The interrupt handler does all of the Rx thread work and cleans up
@@ -441,47 +551,34 @@
struct net_device *dev = (struct net_device *)_dev;
struct ioc3_private *ip = dev->priv;
struct ioc3 *ioc3 = ip->regs;
- u32 eisr, eier;
-
- ip = dev->priv;
-
- eier = ioc3->eier; /* Disable eth ints */
- ioc3->eier = 0;
- eisr = ioc3->eisr;
- __sti();
-
- if (eisr & EISR_RXTIMERINT) {
- ioc3_rx(dev, ip, ioc3);
- }
- if (eisr & EISR_TXEXPLICIT) {
- ioc3_tx(ip, ioc3);
- }
- if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
- ioc3_error(dev, ip, ioc3, eisr);
- }
+ const u32 enabled = EISR_RXTIMERINT | EISR_TXEXPLICIT |
+ EISR_RXMEMERR | EISR_TXMEMERR;
+ u32 eisr;
+
+ eisr = ioc3->eisr & enabled;
+ while (eisr) {
+ ioc3->eisr = eisr;
+ ioc3->eisr; /* Flush */
- if ((TX_BUFFS_AVAIL(ip) >= 0) && netif_queue_stopped(dev)) {
- netif_wake_queue(dev);
+ if (eisr & EISR_RXTIMERINT)
+ ioc3_rx(dev, ip, ioc3);
+ if (eisr & EISR_TXEXPLICIT)
+ ioc3_tx(dev, ip, ioc3);
+ if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR))
+ ioc3_error(dev, ip, ioc3, eisr);
+ eisr = ioc3->eisr & enabled;
}
-
- __cli();
- ioc3->eier = eier;
-
- return;
}
-int
-ioc3_eth_init(struct net_device *dev, struct ioc3_private *p, struct ioc3 *ioc3)
+/* One day this will do the autonegotiation. */
+int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
{
u16 word, mii0, mii_status, mii2, mii3, mii4;
u32 vendor, model, rev;
int i, phy;
- ioc3->emcr = EMCR_RST; /* Reset */
- ioc3->emcr; /* flush WB */
- udelay(4); /* Give it time ... */
- ioc3->emcr = 0;
-
+ spin_lock_irq(&ip->ioc3_lock);
phy = -1;
for (i = 0; i < 32; i++) {
word = mii_read(ioc3, i, 2);
@@ -491,10 +588,11 @@
}
}
if (phy == -1) {
+ spin_unlock_irq(&ip->ioc3_lock);
printk("Didn't find a PHY, goodbye.\n");
return -ENODEV;
}
- p->phy = phy;
+ ip->phy = phy;
mii0 = mii_read(ioc3, phy, 0);
mii_status = mii_read(ioc3, phy, 1);
@@ -512,73 +610,150 @@
/* Autonegotiate 100mbit and fullduplex. */
mii_write(ioc3, phy, 0, mii0 | 0x3100);
- mdelay(1000);
+
+ spin_unlock_irq(&ip->ioc3_lock);
+ mdelay(1000); /* XXX Yikes XXX */
+ spin_lock_irq(&ip->ioc3_lock);
+
mii_status = mii_read(ioc3, phy, 1);
+ spin_unlock_irq(&ip->ioc3_lock);
- return 0; /* XXX */
+ return 0;
}
-/* To do: For reinit of the ring we have to cleanup old skbs first ... */
static void
-ioc3_init_rings(struct net_device *dev, struct ioc3_private *p,
- struct ioc3 *ioc3)
+ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
{
struct ioc3_erxbuf *rxb;
unsigned long *rxr;
- unsigned long ring;
int i;
- /* Allocate and initialize rx ring. 4kb = 512 entries */
- p->rxr = get_free_page(GFP_KERNEL);
- rxr = (unsigned long *) p->rxr;
-
- /* Now the rx buffers. The RX ring may be larger but we only
- allocate 16 buffers for now. Need to tune this for performance
- and memory later. */
- for (i = 0; i < RX_BUFFS; i++) {
- struct sk_buff *skb;
+ if (ip->rxr == NULL) {
+ /* Allocate and initialize rx ring. 4kb = 512 entries */
+ ip->rxr = (unsigned long *) get_free_page(GFP_KERNEL);
+ rxr = (unsigned long *) ip->rxr;
+
+ /* Now the rx buffers. The RX ring may be larger but
+ we only allocate 16 buffers for now. Need to tune
+ this for performance and memory later. */
+ for (i = 0; i < RX_BUFFS; i++) {
+ struct sk_buff *skb;
+
+ skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
+ if (!skb) {
+ show_free_areas();
+ continue;
+ }
- skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
- if (!skb) {
- show_free_areas();
- continue;
- }
+ ip->rx_skbs[i] = skb;
+ skb->dev = dev;
- p->rx_skbs[i] = skb;
- skb->dev = dev;
+ /* Because we reserve afterwards. */
+ skb_put(skb, (1664 + RX_OFFSET));
+ rxb = (struct ioc3_erxbuf *) skb->data;
+ rxr[i] = (0xa5UL << 56)
+ | ((unsigned long) rxb & TO_PHYS_MASK);
+ skb_reserve(skb, RX_OFFSET);
+ }
+ ip->rx_ci = 0;
+ ip->rx_pi = RX_BUFFS;
+ }
- /* Because we reserve afterwards. */
- skb_put(skb, (1664 + RX_OFFSET));
- rxb = (struct ioc3_erxbuf *) skb->data;
- rxb->w0 = 0; /* Clear valid bit */
- rxr[i] = (0xa5UL << 56) | ((unsigned long) rxb & TO_PHYS_MASK);
- skb_reserve(skb, RX_OFFSET);
+ if (ip->txr == NULL) {
+ /* Allocate and initialize tx rings. 16kb = 128 bufs. */
+ ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
+ ip->tx_pi = 0;
+ ip->tx_ci = 0;
}
+}
+
+static void
+ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
+{
+ unsigned long ring;
+
+ ioc3_alloc_rings(dev, ip, ioc3);
+
+ ioc3_clean_tx_ring(ip);
+ ioc3_clean_rx_ring(ip);
/* Now the rx ring base, consume & produce registers. */
- ring = (0xa5UL << 56) | (p->rxr & TO_PHYS_MASK);
+ ring = (0xa5UL << 56) | ((unsigned long)ip->rxr & TO_PHYS_MASK);
ioc3->erbr_h = ring >> 32;
ioc3->erbr_l = ring & 0xffffffff;
- p->rx_ci = 0;
- ioc3->ercir = (p->rx_ci << 3);
- p->rx_pi = RX_BUFFS;
- ioc3->erpir = (p->rx_pi << 3) | ERPIR_ARM;
-
- /* Allocate and initialize tx rings. 16kb = 128 bufs. */
- p->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
- ring = (0xa5UL << 56) | ((unsigned long)p->txr & TO_PHYS_MASK);
+ ioc3->ercir = (ip->rx_ci << 3);
+ ioc3->erpir = (ip->rx_pi << 3) | ERPIR_ARM;
+
+ ring = (0xa5UL << 56) | ((unsigned long)ip->txr & TO_PHYS_MASK);
+
+ ip->txqlen = 0; /* nothing queued */
/* Now the tx ring base, consume & produce registers. */
ioc3->etbr_h = ring >> 32;
ioc3->etbr_l = ring & 0xffffffff;
- p->tx_pi = 0;
- ioc3->etpir = (p->tx_pi << 7);
- p->tx_ci = 0;
- ioc3->etcir = (p->tx_pi << 7);
+ ioc3->etpir = (ip->tx_pi << 7);
+ ioc3->etcir = (ip->tx_ci << 7);
ioc3->etcir; /* Flush */
}
-void
+static void
+ioc3_clean_tx_ring(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i=0; i < 128; i++) {
+ skb = ip->tx_skbs[i];
+ if (skb) {
+ ip->tx_skbs[i] = NULL;
+ dev_kfree_skb_any(skb);
+ }
+ ip->txr[i].cmd = 0;
+ }
+}
+
+static void
+ioc3_clean_rx_ring(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < RX_BUFFS; i++) {
+ struct ioc3_erxbuf *rxb;
+ skb = ip->rx_skbs[i];
+ rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET);
+
+ rxb->w0 = 0;
+ }
+}
+
+static void
+ioc3_free_rings(struct ioc3_private *ip)
+{
+ struct sk_buff *skb;
+ int rx_entry, n_entry;
+
+ ioc3_clean_tx_ring(ip);
+ free_pages((unsigned long)ip->txr, 2);
+ ip->txr = NULL;
+
+ n_entry = ip->rx_ci;
+ rx_entry = ip->rx_pi;
+
+ while (n_entry != rx_entry) {
+ skb = ip->rx_skbs[n_entry];
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ n_entry = (n_entry + 1) & 511;
+ }
+ free_page((unsigned long)ip->rxr);
+ ip->rxr = NULL;
+}
+
+static inline void
ioc3_ssram_disc(struct ioc3_private *ip)
{
struct ioc3 *ioc3 = ip->regs;
@@ -595,40 +770,23 @@
if ((*ssram0 & IOC3_SSRAM_DM) != pattern ||
(*ssram1 & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) {
/* set ssram size to 64 KB */
+ ip->emcr = EMCR_RAMPAR;
ioc3->emcr &= ~EMCR_BUFSIZ;
- printk("IOC3 SSRAM has 64 kbyte.\n");
} else {
- //ei->ei_ssram_bits = EMCR_BUFSIZ | EMCR_RAMPAR;
- printk("IOC3 SSRAM has 64 kbyte.\n");
+ ip->emcr = EMCR_BUFSIZ | EMCR_RAMPAR;
}
}
-static void ioc3_probe1(struct net_device *dev, struct ioc3 *ioc3)
+static void ioc3_init(struct net_device *dev)
{
- struct ioc3_private *ip;
-
- dev = init_etherdev(dev, 0);
-
- /*
- * This probably needs to be register_netdevice, or call
- * init_etherdev so that it calls register_netdevice. Quick
- * hack for now.
- */
- netif_device_attach(dev);
-
- ip = (struct ioc3_private *) kmalloc(sizeof(*ip), GFP_KERNEL);
- memset(ip, 0, sizeof(*ip));
- dev->priv = ip;
- dev->irq = IOC3_ETH_INT;
-
- ip->regs = ioc3;
-
- ioc3_eth_init(dev, ip, ioc3);
- ioc3_ssram_disc(ip);
- ioc3_get_eaddr(dev, ioc3);
- ioc3_init_rings(dev, ip, ioc3);
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
- spin_lock_init(&ip->ioc3_lock);
+ ioc3->emcr = EMCR_RST; /* Reset */
+ ioc3->emcr; /* flush WB */
+ udelay(4); /* Give it time ... */
+ ioc3->emcr = 0;
+ ioc3->emcr;
/* Misc registers */
ioc3->erbar = 0;
@@ -640,65 +798,141 @@
ioc3->emar_l = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) |
(dev->dev_addr[1] << 8) | dev->dev_addr[0];
ioc3->ehar_h = ioc3->ehar_l = 0;
- ioc3->ersr = 42; /* XXX should be random */
+ ioc3->ersr = 42; /* XXX should be random */
//ioc3->erpir = ERPIR_ARM;
- /* The IOC3-specific entries in the device structure. */
- dev->open = &ioc3_open;
- dev->hard_start_xmit = &ioc3_start_xmit;
- dev->tx_timeout = ioc3_timeout;
- dev->watchdog_timeo = (400 * HZ) / 1000;
- dev->stop = &ioc3_close;
- dev->get_stats = &ioc3_get_stats;
- dev->set_multicast_list = &ioc3_set_multicast_list;
+ ioc3_init_rings(dev, ip, ioc3);
+
+ ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
+ EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
+ ioc3->emcr = ip->emcr;
+ ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */
+ EISR_RXMEMERR | EISR_TXMEMERR;
+ ioc3->eier;
}
-int
-ioc3_probe(struct net_device *dev)
+static void ioc3_stop(struct net_device *dev)
{
- static int initialized;
- struct ioc3 *ioc3;
- nasid_t nid;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
- if (initialized) /* Only initialize once ... */
- return 0;
- initialized++;
+ ioc3->emcr = 0; /* Shutup */
+ ip->emcr = 0;
+ ioc3->eier = 0; /* Disable interrupts */
+ ioc3->eier; /* Flush */
+}
+
+static int
+ioc3_open(struct net_device *dev)
+{
+ if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
+ printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq);
+
+ return -EAGAIN;
+ }
- nid = get_nasid();
- ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
- ioc3_probe1(dev, ioc3);
+ ((struct ioc3_private *)dev->priv)->ehar_h = 0;
+ ((struct ioc3_private *)dev->priv)->ehar_l = 0;
+ ioc3_init(dev);
+
+ netif_start_queue(dev);
+
+ MOD_INC_USE_COUNT;
return 0;
}
static int
-ioc3_open(struct net_device *dev)
+ioc3_close(struct net_device *dev)
{
struct ioc3_private *ip = dev->priv;
- struct ioc3 *ioc3 = ip->regs;
- unsigned long flags;
- save_flags(flags); cli();
- if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
- printk("%s: Can't get irq %d\n", dev->name, dev->irq);
- restore_flags(flags);
+ netif_stop_queue(dev);
- return -EAGAIN;
- }
+ ioc3_stop(dev); /* Flush */
+ free_irq(dev->irq, dev);
- //ioc3_eth_init(dev, p, ioc3);
+ ioc3_free_rings(ip);
- ioc3->emcr = ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
- EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
- ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */
- EISR_RXMEMERR | EISR_TXMEMERR;
+ MOD_DEC_USE_COUNT;
- netif_wake_queue(dev);
- restore_flags(flags);
+ return 0;
+}
- MOD_INC_USE_COUNT;
+static void ioc3_pci_init(struct pci_dev *pdev)
+{
+ struct net_device *dev = NULL; // XXX
+ struct ioc3_private *ip;
+ struct ioc3 *ioc3;
+ unsigned long ioc3_base, ioc3_size;
+
+ dev = init_etherdev(dev, 0);
+
+ /*
+ * This probably needs to be register_netdevice, or call
+ * init_etherdev so that it calls register_netdevice. Quick
+ * hack for now.
+ */
+ netif_device_attach(dev);
+
+ ip = (struct ioc3_private *) kmalloc(sizeof(*ip), GFP_KERNEL);
+ memset(ip, 0, sizeof(*ip));
+ dev->priv = ip;
+ dev->irq = pdev->irq;
+
+ ioc3_base = pdev->resource[0].start;
+ ioc3_size = pdev->resource[0].end - ioc3_base;
+ ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size);
+ ip->regs = ioc3;
+
+ spin_lock_init(&ip->ioc3_lock);
+
+ ioc3_stop(dev);
+ ip->emcr = 0;
+ ioc3_init(dev);
+ ioc3_mii_init(dev, ip, ioc3);
+
+ ioc3_ssram_disc(ip);
+ printk("IOC3 SSRAM has %d kbyte.\n", ip->emcr & EMCR_BUFSIZ ? 128 : 64);
+
+ ioc3_get_eaddr(dev, ioc3);
- return 0;
+ /* The IOC3-specific entries in the device structure. */
+ dev->open = ioc3_open;
+ dev->hard_start_xmit = ioc3_start_xmit;
+ dev->tx_timeout = ioc3_timeout;
+ dev->watchdog_timeo = 5 * HZ;
+ dev->stop = ioc3_close;
+ dev->get_stats = ioc3_get_stats;
+ dev->do_ioctl = ioc3_ioctl;
+ dev->set_multicast_list = ioc3_set_multicast_list;
+}
+
+static int __init ioc3_probe(void)
+{
+ static int called = 0;
+ int cards = 0;
+
+ if (called)
+ return -ENODEV;
+ called = 1;
+
+ if (pci_present()) {
+ struct pci_dev *pdev = NULL;
+
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_SGI,
+ PCI_DEVICE_ID_SGI_IOC3, pdev))) {
+ ioc3_pci_init(pdev);
+ cards++;
+ }
+ }
+
+ return cards ? -ENODEV : 0;
+}
+
+static void __exit ioc3_cleanup_module(void)
+{
+ /* Later, when we really support modules. */
}
static int
@@ -711,11 +945,8 @@
struct ioc3_etxd *desc;
int produce;
- if (!TX_BUFFS_AVAIL(ip)) {
- return 1;
- }
-
spin_lock_irq(&ip->ioc3_lock);
+
data = (unsigned long) skb->data;
len = skb->len;
@@ -759,8 +990,11 @@
ip->tx_pi = produce;
ioc3->etpir = produce << 7; /* Fire ... */
- if (TX_BUFFS_AVAIL(ip))
- netif_wake_queue(dev);
+ ip->txqlen++;
+
+ if (ip->txqlen > 127)
+ netif_stop_queue(dev);
+
spin_unlock_irq(&ip->ioc3_lock);
return 0;
@@ -768,55 +1002,16 @@
static void ioc3_timeout(struct net_device *dev)
{
- printk("%s: transmit timed out, resetting\n", dev->name);
- /* XXX should reset device here. */
+ printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+
+ ioc3_stop(dev);
+ ioc3_clean_tx_ring(dev->priv);
+ ioc3_init(dev);
dev->trans_start = jiffies;
netif_wake_queue(dev);
}
-static int
-ioc3_close(struct net_device *dev)
-{
- struct ioc3_private *ip = dev->priv;
- struct ioc3 *ioc3 = ip->regs;
-
- netif_stop_queue(dev);
-
- ioc3->emcr = 0; /* Shutup */
- ioc3->eier = 0; /* Disable interrupts */
- ioc3->eier; /* Flush */
- free_irq(dev->irq, dev);
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-}
-
-#if 0
-/* Initialize the NIC */
-static int
-ioc3_init(struct ioc3_private *p, struct pci_dev *dev)
-{
-#if 0
- unsigned long base;
- struct ioc3 *ioc3;
-
- pci_set_master(dev);
- base = dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
- printk("Base address at %08lx\n", base);
-
- p->regs = ioc3 = ioremap (base, IOC3_SIZE);
- printk("Remapped base address to %08lx\n", (unsigned long) regs);
-
- read_nic(ioc3);
-
- return 0;
-#endif
- panic(__FUNCTION__" has been called.\n");
-}
-#endif
-
/*
* Given a multicast ethernet address, this routine calculates the
* address's bit index in the logical address filter mask
@@ -853,33 +1048,68 @@
return temp;
}
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct ioc3_private *ip = (struct ioc3_private *) dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+ struct ioc3 *ioc3 = ip->regs;
+ int phy = ip->phy;
+
+ switch (cmd) {
+ case SIOCGMIIPHY: /* Get the address of the PHY in use. */
+ if (phy == -1)
+ return -ENODEV;
+ data[0] = phy;
+ return 0;
+
+ case SIOCGMIIREG: /* Read any PHY register. */
+ spin_lock_irq(&ip->ioc3_lock);
+ data[3] = mii_read(ioc3, data[0], data[1]);
+ spin_unlock_irq(&ip->ioc3_lock);
+ return 0;
+
+ case SIOCSMIIREG: /* Write any PHY register. */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ spin_lock_irq(&ip->ioc3_lock);
+ mii_write(ioc3, data[0], data[1], data[2]);
+ spin_unlock_irq(&ip->ioc3_lock);
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
static void ioc3_set_multicast_list(struct net_device *dev)
{
struct dev_mc_list *dmi = dev->mc_list;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
char *addr = dmi->dmi_addr;
- struct ioc3_private *p;
- struct ioc3 *ioc3;
u64 ehar = 0;
int i;
- p = dev->priv;
- ioc3 = p->regs;
-
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
/* Unconditionally log net taps. */
printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
- ioc3->emcr |= EMCR_PROMISC;
+ ip->emcr |= EMCR_PROMISC;
+ ioc3->emcr = ip->emcr;
ioc3->emcr;
} else {
- ioc3->emcr &= ~EMCR_PROMISC; /* Clear promiscuous. */
+ ip->emcr &= ~EMCR_PROMISC;
+ ioc3->emcr = ip->emcr; /* Clear promiscuous. */
ioc3->emcr;
if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
/* Too many for hashing to make sense or we want all
multicast packets anyway, so skip computing all the
hashes and just accept all packets. */
- ioc3->ehar_h = 0xffffffff;
- ioc3->ehar_l = 0xffffffff;
+ ip->ehar_h = 0xffffffff;
+ ip->ehar_l = 0xffffffff;
} else {
for (i = 0; i < dev->mc_count; i++) {
dmi = dmi->next;
@@ -889,31 +1119,18 @@
ehar |= (1 << ioc3_hash(addr));
}
- ioc3->ehar_h = ehar >> 32;
- ioc3->ehar_l = ehar & 0xffffffff;
+ ip->ehar_h = ehar >> 32;
+ ip->ehar_l = ehar & 0xffffffff;
}
+ ioc3->ehar_h = ip->ehar_h;
+ ioc3->ehar_l = ip->ehar_l;
}
}
#ifdef MODULE
MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");
MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
-
-int
-init_module(void)
-{
- struct pci_dev *dev = NULL;
-
- while ((dev = pci_find_device(PCI_VENDOR_ID_SGI, 0x0003, dev))) {
- ioc3_init(&p, dev);
- }
-
- return -EBUSY;
-}
-
-void
-cleanup_module(void)
-{
- printk("cleanup_module called\n");
-}
#endif /* MODULE */
+
+module_init(ioc3_probe);
+module_exit(ioc3_cleanup_module);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)