summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-10-02 21:02:19 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-10-02 21:02:19 +0000
commit0a1ec74006a91f8683bd8fe2645762b387ae62fd (patch)
tree55a220f4dc9c8ef635463a88f37996921159c251 /drivers
parent08e47c5800543e2a36ef2551ababa353ee247e02 (diff)
Fixes for IOC3 driver reset.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ioc3-eth.c124
1 files changed, 84 insertions, 40 deletions
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c
index 37dc0d2ec..91b6763f4 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ioc3-eth.c
@@ -69,7 +69,7 @@
#include <asm/pci/bridge.h>
/* 32 RX buffers. This is tunable in the range of 16 <= x < 512. */
-#define RX_BUFFS 32
+#define RX_BUFFS 64
/* Private ioctls that de facto are well known and used for examply
by mii-tool. */
@@ -96,6 +96,7 @@ struct ioc3_private {
int tx_pi; /* TX producer index */
int txqlen;
u32 emcr, ehar_h, ehar_l;
+ struct timer_list negtimer;
spinlock_t ioc3_lock;
};
@@ -104,7 +105,7 @@ 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 inline void ioc3_stop(struct net_device *dev);
static void ioc3_init(struct net_device *dev);
static const char ioc3_str[] = "IOC3 Ethernet";
@@ -392,6 +393,9 @@ static void mii_write(struct ioc3 *ioc3, int phy, int reg, u16 data)
while (ioc3->micr & MICR_BUSY);
}
+static int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3);
+
static struct net_device_stats *ioc3_get_stats(struct net_device *dev)
{
struct ioc3_private *ip = (struct ioc3_private *) dev->priv;
@@ -426,6 +430,8 @@ ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
+ ip->rx_skbs[rx_entry] = NULL; /* Poison */
+
new_skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
if (!new_skb) {
/* Ouch, drop packet and just recycle packet
@@ -545,6 +551,7 @@ ioc3_error(struct net_device *dev, struct ioc3_private *ip,
ioc3_stop(dev);
ioc3_init(dev);
+ ioc3_mii_init(dev, ip, ioc3);
dev->trans_start = jiffies;
netif_wake_queue(dev);
@@ -563,6 +570,7 @@ static void ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
u32 eisr;
eisr = ioc3->eisr & enabled;
+
while (eisr) {
ioc3->eisr = eisr;
ioc3->eisr; /* Flush */
@@ -579,12 +587,19 @@ static void ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
}
}
-/* One day this will do the autonegotiation. */
-int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
- struct ioc3 *ioc3)
+static void negotiate(unsigned long data)
{
- u16 word, mii0, mii_status, mii2, mii3, mii4;
- u32 vendor, model, rev;
+ struct net_device *dev = (struct net_device *) data;
+ struct ioc3_private *ip = (struct ioc3_private *) dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+
+ mod_timer(&ip->negtimer, jiffies + 20 * HZ);
+}
+
+static int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3)
+{
+ u16 word, mii0;
int i, phy;
spin_lock_irq(&ip->ioc3_lock);
@@ -598,27 +613,17 @@ int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
}
if (phy == -1) {
spin_unlock_irq(&ip->ioc3_lock);
- printk("Didn't find a PHY, goodbye.\n");
return -ENODEV;
}
ip->phy = phy;
- mii0 = mii_read(ioc3, phy, 0);
- mii_status = mii_read(ioc3, phy, 1);
- mii2 = mii_read(ioc3, phy, 2);
- mii3 = mii_read(ioc3, phy, 3);
- mii4 = mii_read(ioc3, phy, 4);
- vendor = (mii2 << 12) | (mii3 >> 4);
- model = (mii3 >> 4) & 0x3f;
- rev = mii3 & 0xf;
- printk("Ok, using PHY %d, vendor 0x%x, model %d, rev %d.\n",
- phy, vendor, model, rev);
- printk(KERN_INFO "%s: MII transceiver found at MDIO address "
- "%d, config %4.4x status %4.4x.\n",
- dev->name, phy, mii0, mii_status);
-
/* Autonegotiate 100mbit and fullduplex. */
- mii_write(ioc3, phy, 0, mii0 | 0x3100);
+ mii0 = mii_read(ioc3, ip->phy, 0);
+ mii_write(ioc3, ip->phy, 0, mii0 | 0x3100);
+
+ ip->negtimer.function = &negotiate;
+ ip->negtimer.data = (unsigned long) dev;
+ mod_timer(&ip->negtimer, jiffies); /* Run it now */
spin_unlock_irq(&ip->ioc3_lock);
@@ -670,22 +675,26 @@ ioc3_free_rings(struct ioc3_private *ip)
struct sk_buff *skb;
int rx_entry, n_entry;
- ioc3_clean_tx_ring(ip);
- ip->txr = NULL;
- free_pages((unsigned long)ip->txr, 2);
+ if (ip->txr) {
+ 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;
+ if (ip->rxr) {
+ 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);
+ while (n_entry != rx_entry) {
+ skb = ip->rx_skbs[n_entry];
+ if (skb)
+ dev_kfree_skb_any(skb);
- n_entry = (n_entry + 1) & 511;
+ n_entry = (n_entry + 1) & 511;
+ }
+ free_page((unsigned long)ip->rxr);
+ ip->rxr = NULL;
}
- free_page((unsigned long)ip->rxr);
- ip->rxr = NULL;
}
static void
@@ -698,7 +707,7 @@ ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip,
if (ip->rxr == NULL) {
/* Allocate and initialize rx ring. 4kb = 512 entries */
- ip->rxr = (unsigned long *) get_free_page(GFP_KERNEL);
+ ip->rxr = (unsigned long *) get_free_page(GFP_KERNEL|GFP_ATOMIC);
rxr = (unsigned long *) ip->rxr;
/* Now the rx buffers. The RX ring may be larger but
@@ -707,7 +716,7 @@ ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip,
for (i = 0; i < RX_BUFFS; i++) {
struct sk_buff *skb;
- skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
+ skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
if (!skb) {
show_free_areas();
continue;
@@ -729,7 +738,7 @@ ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip,
if (ip->txr == NULL) {
/* Allocate and initialize tx rings. 16kb = 128 bufs. */
- ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
+ ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL|GFP_ATOMIC, 2);
ip->tx_pi = 0;
ip->tx_ci = 0;
}
@@ -741,6 +750,7 @@ ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip,
{
unsigned long ring;
+ ioc3_free_rings(ip);
ioc3_alloc_rings(dev, ip, ioc3);
ioc3_clean_rx_ring(ip);
@@ -824,7 +834,7 @@ static void ioc3_init(struct net_device *dev)
ioc3->eier;
}
-static void ioc3_stop(struct net_device *dev)
+static inline void ioc3_stop(struct net_device *dev)
{
struct ioc3_private *ip = dev->priv;
struct ioc3 *ioc3 = ip->regs;
@@ -863,6 +873,7 @@ ioc3_close(struct net_device *dev)
{
struct ioc3_private *ip = dev->priv;
+ del_timer(&ip->negtimer);
netif_stop_queue(dev);
ioc3_stop(dev); /* Flush */
@@ -877,10 +888,13 @@ ioc3_close(struct net_device *dev)
static int ioc3_pci_init(struct pci_dev *pdev)
{
+ u16 mii0, mii_status, mii2, mii3, mii4;
struct net_device *dev = NULL; // XXX
struct ioc3_private *ip;
struct ioc3 *ioc3;
unsigned long ioc3_base, ioc3_size;
+ u32 vendor, model, rev;
+ int phy;
dev = init_etherdev(0, sizeof(struct ioc3_private));
@@ -909,12 +923,38 @@ static int ioc3_pci_init(struct pci_dev *pdev)
ioc3_stop(dev);
ip->emcr = 0;
ioc3_init(dev);
+
+ init_timer(&ip->negtimer);
ioc3_mii_init(dev, ip, ioc3);
+ phy = ip->phy;
+ if (phy == -1) {
+ printk(KERN_CRIT"%s: Didn't find a PHY, goodbye.\n", dev->name);
+ ioc3_stop(dev);
+ free_irq(dev->irq, dev);
+ ioc3_free_rings(ip);
+
+ return -ENODEV;
+ }
+
+ mii0 = mii_read(ioc3, phy, 0);
+ mii_status = mii_read(ioc3, phy, 1);
+ mii2 = mii_read(ioc3, phy, 2);
+ mii3 = mii_read(ioc3, phy, 3);
+ mii4 = mii_read(ioc3, phy, 4);
+ vendor = (mii2 << 12) | (mii3 >> 4);
+ model = (mii3 >> 4) & 0x3f;
+ rev = mii3 & 0xf;
+ printk(KERN_INFO"Using PHY %d, vendor 0x%x, model %d, rev %d.\n",
+ phy, vendor, model, rev);
+ printk(KERN_INFO "%s: MII transceiver found at MDIO address "
+ "%d, config %4.4x status %4.4x.\n",
+ dev->name, phy, mii0, mii_status);
+
ioc3_ssram_disc(ip);
printk("IOC3 SSRAM has %d kbyte.\n", ip->emcr & EMCR_BUFSIZ ? 128 : 64);
- ioc3_get_eaddr(dev, ioc3);
+ ioc3_get_eaddr(dev, ioc3);
/* The IOC3-specific entries in the device structure. */
dev->open = ioc3_open;
@@ -1024,10 +1064,14 @@ ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void ioc3_timeout(struct net_device *dev)
{
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
ioc3_stop(dev);
ioc3_init(dev);
+ ioc3_mii_init(dev, ip, ioc3);
dev->trans_start = jiffies;
netif_wake_queue(dev);