patch-2.4.19 linux-2.4.19/drivers/net/sunhme.c
Next file: linux-2.4.19/drivers/net/sunhme.h
Previous file: linux-2.4.19/drivers/net/sungem.h
Back to the patch index
Back to the overall index
- Lines: 1166
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/net/sunhme.c
- Orig date:
Fri Oct 12 15:35:53 2001
diff -urN linux-2.4.18/drivers/net/sunhme.c linux-2.4.19/drivers/net/sunhme.c
@@ -3,7 +3,7 @@
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
*
- * Copyright (C) 1996, 1998, 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1996, 1998, 1999, 2002 David S. Miller (davem@redhat.com)
*
* Changes :
* 2000/11/11 Willy Tarreau <willy AT meta-x.org>
@@ -14,7 +14,7 @@
*/
static char version[] =
- "sunhme.c:v1.99 12/Sep/99 David S. Miller (davem@redhat.com)\n";
+ "sunhme.c:v2.01 26/Mar/2002 David S. Miller (davem@redhat.com)\n";
#include <linux/module.h>
@@ -33,6 +33,8 @@
#include <linux/init.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/random.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
@@ -695,7 +697,7 @@
return 1;
}
-static int happy_meal_init(struct happy_meal *hp, int from_irq);
+static int happy_meal_init(struct happy_meal *hp);
static int is_lucent_phy(struct happy_meal *hp)
{
@@ -706,12 +708,8 @@
mr2 = happy_meal_tcvr_read(hp, tregs, 2);
mr3 = happy_meal_tcvr_read(hp, tregs, 3);
if ((mr2 & 0xffff) == 0x0180 &&
- ((mr3 & 0xffff) >> 10) == 0x1d) {
-#if 0
- printk("HMEDEBUG: Lucent PHY detected.\n");
-#endif
+ ((mr3 & 0xffff) >> 10) == 0x1d)
ret = 1;
- }
return ret;
}
@@ -722,6 +720,8 @@
unsigned long tregs = hp->tcvregs;
int restart_timer = 0;
+ spin_lock_irq(&hp->happy_lock);
+
hp->timer_ticks++;
switch(hp->timer_state) {
case arbwait:
@@ -851,13 +851,13 @@
printk(KERN_NOTICE "%s: Link down, cable problem?\n",
hp->dev->name);
- ret = happy_meal_init(hp, 0);
+ ret = happy_meal_init(hp);
if (ret) {
/* ho hum... */
printk(KERN_ERR "%s: Error, cannot re-init the "
"Happy Meal.\n", hp->dev->name);
}
- return;
+ goto out;
}
if (!is_lucent_phy(hp)) {
hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs,
@@ -889,11 +889,15 @@
hp->happy_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */
add_timer(&hp->happy_timer);
}
+
+out:
+ spin_unlock_irq(&hp->happy_lock);
}
#define TX_RESET_TRIES 32
#define RX_RESET_TRIES 32
+/* hp->happy_lock must be held */
static void happy_meal_tx_reset(struct happy_meal *hp, unsigned long bregs)
{
int tries = TX_RESET_TRIES;
@@ -913,6 +917,7 @@
HMD(("done\n"));
}
+/* hp->happy_lock must be held */
static void happy_meal_rx_reset(struct happy_meal *hp, unsigned long bregs)
{
int tries = RX_RESET_TRIES;
@@ -934,6 +939,7 @@
#define STOP_TRIES 16
+/* hp->happy_lock must be held */
static void happy_meal_stop(struct happy_meal *hp, unsigned long gregs)
{
int tries = STOP_TRIES;
@@ -953,6 +959,7 @@
HMD(("done\n"));
}
+/* hp->happy_lock must be held */
static void happy_meal_get_counters(struct happy_meal *hp, unsigned long bregs)
{
struct net_device_stats *stats = &hp->net_stats;
@@ -975,53 +982,7 @@
hme_write32(hp, bregs + BMAC_LTCTR, 0);
}
-#if 0
-static void happy_meal_poll_start(struct happy_meal *hp, unsigned long tregs)
-{
- u32 tmp;
- int speed;
-
- ASD(("happy_meal_poll_start: "));
- if (!(hp->happy_flags & HFLAG_POLLENABLE)) {
- HMD(("polling disabled, return\n"));
- return;
- }
-
- /* Start the MIF polling on the external transceiver. */
- ASD(("polling on, "));
- tmp = hme_read32(hp, tregs + TCVR_CFG);
- tmp &= ~(TCV_CFG_PDADDR | TCV_CFG_PREGADDR);
- tmp |= ((hp->paddr & 0x1f) << 10);
- tmp |= (TCV_PADDR_ETX << 3);
- tmp |= TCV_CFG_PENABLE;
- hme_write32(hp, tregs + TCVR_CFG, tmp);
-
- /* Let the bits set. */
- udelay(200);
-
- /* We are polling now. */
- ASD(("now polling, "));
- hp->happy_flags |= HFLAG_POLL;
-
- /* Clear the poll flags, get the basic status as of now. */
- hp->poll_flag = 0;
- hp->poll_data = hme_read32(hp, tregs + TCVR_STATUS) >> 16;
-
- if (hp->happy_flags & HFLAG_AUTO)
- speed = hp->auto_speed;
- else
- speed = hp->forced_speed;
-
- /* Listen only for the MIF interrupts we want to hear. */
- ASD(("mif ints on, "));
- if (speed == 100)
- hme_write32(hp, tregs + TCVR_IMASK, 0xfffb);
- else
- hme_write32(hp, tregs + TCVR_IMASK, 0xfff9);
- ASD(("done\n"));
-}
-#endif
-
+/* hp->happy_lock must be held */
static void happy_meal_poll_stop(struct happy_meal *hp, unsigned long tregs)
{
ASD(("happy_meal_poll_stop: "));
@@ -1056,6 +1017,7 @@
#define TCVR_RESET_TRIES 16 /* It should reset quickly */
#define TCVR_UNISOLATE_TRIES 32 /* Dis-isolation can take longer. */
+/* hp->happy_lock must be held */
static int happy_meal_tcvr_reset(struct happy_meal *hp, unsigned long tregs)
{
u32 tconfig;
@@ -1150,7 +1112,10 @@
return 0;
}
-/* Figure out whether we have an internal or external transceiver. */
+/* Figure out whether we have an internal or external transceiver.
+ *
+ * hp->happy_lock must be held
+ */
static void happy_meal_transceiver_check(struct happy_meal *hp, unsigned long tregs)
{
unsigned long tconfig = hme_read32(hp, tregs + TCVR_CFG);
@@ -1302,14 +1267,12 @@
}
}
-static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
+/* hp->happy_lock must be held */
+static void happy_meal_init_rings(struct happy_meal *hp)
{
struct hmeal_init_block *hb = hp->happy_block;
struct net_device *dev = hp->dev;
- int i, gfp_flags = GFP_KERNEL;
-
- if (from_irq || in_interrupt())
- gfp_flags = GFP_ATOMIC;
+ int i;
HMD(("happy_meal_init_rings: counters to zero, "));
hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0;
@@ -1323,7 +1286,7 @@
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
- skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags);
+ skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
if (!skb) {
hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0);
continue;
@@ -1346,6 +1309,7 @@
HMD(("done\n"));
}
+/* hp->happy_lock must be held */
static void happy_meal_begin_auto_negotiation(struct happy_meal *hp,
unsigned long tregs,
struct ethtool_cmd *ep)
@@ -1469,10 +1433,8 @@
add_timer(&hp->happy_timer);
}
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
-
-static int happy_meal_init(struct happy_meal *hp, int from_irq)
+/* hp->happy_lock must be held */
+static int happy_meal_init(struct happy_meal *hp)
{
unsigned long gregs = hp->gregs;
unsigned long etxregs = hp->etxregs;
@@ -1503,7 +1465,7 @@
/* Alloc and reset the tx/rx descriptor chains. */
HMD(("happy_meal_init: to happy_meal_init_rings\n"));
- happy_meal_init_rings(hp, from_irq);
+ happy_meal_init_rings(hp);
/* Shut up the MIF. */
HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ",
@@ -1583,8 +1545,8 @@
u16 hash_table[4];
struct dev_mc_list *dmi = hp->dev->mc_list;
char *addrs;
- int i, j, bit, byte;
- u32 crc, poly = CRC_POLYNOMIAL_LE;
+ int i;
+ u32 crc;
for (i = 0; i < 4; i++)
hash_table[i] = 0;
@@ -1596,17 +1558,7 @@
if (!(*addrs & 1))
continue;
- crc = 0xffffffffU;
- for (byte = 0; byte < 6; byte++) {
- for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
- int test;
-
- test = ((bit ^ crc) & 0x01);
- crc >>= 1;
- if (test)
- crc = crc ^ poly;
- }
- }
+ crc = ether_crc_le(6, addrs);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -1623,12 +1575,23 @@
/* Set the RX and TX ring ptrs. */
HMD(("ring ptrs rxr[%08x] txr[%08x]\n",
- (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
- (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
hme_write32(hp, erxregs + ERX_RING,
- (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
hme_write32(hp, etxregs + ETX_RING,
- (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+
+ /* Parity issues in the ERX unit of some HME revisions can cause some
+ * registers to not be written unless their parity is even. Detect such
+ * lost writes and simply rewrite with a low bit set (which will be ignored
+ * since the rxring needs to be 2K aligned).
+ */
+ if (hme_read32(hp, erxregs + ERX_RING) !=
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)))
+ hme_write32(hp, erxregs + ERX_RING,
+ ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0))
+ | 0x4);
/* Set the supported burst sizes. */
HMD(("happy_meal_init: old[%08x] bursts<",
@@ -1710,7 +1673,7 @@
/* Enable Big Mac hash table filter. */
HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ",
hme_read32(hp, bregs + BMAC_RXCFG)));
- rxcfg = BIGMAC_RXCFG_HENABLE;
+ rxcfg = BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_REJME;
if (hp->dev->flags & IFF_PROMISC)
rxcfg |= BIGMAC_RXCFG_PMISC;
hme_write32(hp, bregs + BMAC_RXCFG, rxcfg);
@@ -1723,7 +1686,14 @@
regtmp = 0;
if (hp->happy_flags & HFLAG_FULL)
regtmp |= BIGMAC_TXCFG_FULLDPLX;
- hme_write32(hp, bregs + BMAC_TXCFG, regtmp | BIGMAC_TXCFG_DGIVEUP);
+
+ /* Don't turn on the "don't give up" bit for now. It could cause hme
+ * to deadlock with the PHY if a Jabber occurs.
+ */
+ hme_write32(hp, bregs + BMAC_TXCFG, regtmp /*| BIGMAC_TXCFG_DGIVEUP*/);
+
+ /* Give up after 16 TX attempts. */
+ hme_write32(hp, bregs + BMAC_ALIMIT, 16);
/* Enable the output drivers no matter what. */
regtmp = BIGMAC_XCFG_ODENABLE;
@@ -1756,6 +1726,7 @@
return 0;
}
+/* hp->happy_lock must be held */
static void happy_meal_set_initial_advertisement(struct happy_meal *hp)
{
unsigned long tregs = hp->tcvregs;
@@ -1813,6 +1784,8 @@
/* Once status is latched (by happy_meal_interrupt) it is cleared by
* the hardware, so we cannot re-read it and get a correct value.
+ *
+ * hp->happy_lock must be held
*/
static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status)
{
@@ -1921,12 +1894,13 @@
if (reset) {
printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name);
- happy_meal_init(hp, 1);
+ happy_meal_init(hp);
return 1;
}
return 0;
}
+/* hp->happy_lock must be held */
static void happy_meal_mif_interrupt(struct happy_meal *hp)
{
unsigned long tregs = hp->tcvregs;
@@ -1960,6 +1934,7 @@
#define TXD(x)
#endif
+/* hp->happy_lock must be held */
static void happy_meal_tx(struct happy_meal *hp)
{
struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
@@ -1967,8 +1942,6 @@
struct net_device *dev = hp->dev;
int elem;
- spin_lock(&hp->happy_lock);
-
elem = hp->tx_old;
TXD(("TX<"));
while (elem != hp->tx_new) {
@@ -2012,10 +1985,8 @@
TXD((">"));
if (netif_queue_stopped(dev) &&
- TX_BUFFS_AVAIL(hp) > 0)
+ TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1))
netif_wake_queue(dev);
-
- spin_unlock(&hp->happy_lock);
}
#ifdef RXDEBUG
@@ -2030,6 +2001,8 @@
* with all of the packets it has DMA'd in. So now I just drop the entire
* ring when we cannot get a new skb and give them all back to the happy meal,
* maybe things will be "happier" now.
+ *
+ * hp->happy_lock must be held
*/
static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
{
@@ -2139,10 +2112,12 @@
HMD(("happy_meal_interrupt: status=%08x ", happy_status));
+ spin_lock(&hp->happy_lock);
+
if (happy_status & GREG_STAT_ERRORS) {
HMD(("ERRORS "));
if (happy_meal_is_not_so_happy(hp, /* un- */ happy_status))
- return;
+ goto out;
}
if (happy_status & GREG_STAT_MIFIRQ) {
@@ -2161,6 +2136,8 @@
}
HMD(("done\n"));
+out:
+ spin_unlock(&hp->happy_lock);
}
#ifdef CONFIG_SBUS
@@ -2182,10 +2159,12 @@
GREG_STAT_RXTOHOST)))
continue;
+ spin_lock(&hp->happy_lock);
+
if (happy_status & GREG_STAT_ERRORS) {
HMD(("ERRORS "));
if (happy_meal_is_not_so_happy(hp, happy_status))
- break;
+ goto next;
}
if (happy_status & GREG_STAT_MIFIRQ) {
@@ -2202,6 +2181,9 @@
HMD(("RXTOHOST "));
happy_meal_rx(hp, dev);
}
+
+ next:
+ spin_unlock(&hp->happy_lock);
}
HMD(("done\n"));
}
@@ -2234,7 +2216,11 @@
}
HMD(("to happy_meal_init\n"));
- res = happy_meal_init(hp, 0);
+
+ spin_lock_irq(&hp->happy_lock);
+ res = happy_meal_init(hp);
+ spin_unlock_irq(&hp->happy_lock);
+
if (res && ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO))
free_irq(dev->irq, dev);
return res;
@@ -2244,12 +2230,15 @@
{
struct happy_meal *hp = dev->priv;
+ spin_lock_irq(&hp->happy_lock);
happy_meal_stop(hp, hp->gregs);
happy_meal_clean_rings(hp);
/* If auto-negotiation timer is running, kill it. */
del_timer(&hp->happy_timer);
+ spin_unlock_irq(&hp->happy_lock);
+
/* On Quattro QFE cards, all hme interrupts are concentrated
* into a single source which we register handling at probe
* time and never unregister.
@@ -2266,7 +2255,6 @@
#define SXD(x)
#endif
-#ifdef CONFIG_SBUS
static void happy_meal_tx_timeout(struct net_device *dev)
{
struct happy_meal *hp = dev->priv;
@@ -2277,10 +2265,13 @@
hme_read32(hp, hp->gregs + GREG_STAT),
hme_read32(hp, hp->etxregs + ETX_CFG),
hme_read32(hp, hp->bigmacregs + BMAC_TXCFG));
- happy_meal_init(hp, 0);
+
+ spin_lock_irq(&hp->happy_lock);
+ happy_meal_init(hp);
+ spin_unlock_irq(&hp->happy_lock);
+
netif_wake_queue(dev);
}
-#endif
static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
@@ -2305,6 +2296,8 @@
if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) {
netif_stop_queue(dev);
spin_unlock_irq(&hp->happy_lock);
+ printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n",
+ dev->name);
return 1;
}
@@ -2357,7 +2350,7 @@
hp->tx_new = entry;
- if (TX_BUFFS_AVAIL(hp) <= 0)
+ if (TX_BUFFS_AVAIL(hp) <= (MAX_SKB_FRAGS + 1))
netif_stop_queue(dev);
/* Get it going. */
@@ -2375,7 +2368,10 @@
{
struct happy_meal *hp = dev->priv;
+ spin_lock_irq(&hp->happy_lock);
happy_meal_get_counters(hp, hp->bigmacregs);
+ spin_unlock_irq(&hp->happy_lock);
+
return &hp->net_stats;
}
@@ -2385,10 +2381,11 @@
unsigned long bregs = hp->bigmacregs;
struct dev_mc_list *dmi = dev->mc_list;
char *addrs;
- int i, j, bit, byte;
- u32 crc, poly = CRC_POLYNOMIAL_LE;
+ int i;
+ u32 crc;
+
+ spin_lock_irq(&hp->happy_lock);
- /* Lock out others. */
netif_stop_queue(dev);
if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
@@ -2412,17 +2409,7 @@
if (!(*addrs & 1))
continue;
- crc = 0xffffffffU;
- for (byte = 0; byte < 6; byte++) {
- for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
- int test;
-
- test = ((bit ^ crc) & 0x01);
- crc >>= 1;
- if (test)
- crc = crc ^ poly;
- }
- }
+ crc = ether_crc_le(6, addrs);
crc >>= 26;
hash_table[crc >> 4] |= 1 << (crc & 0xf);
}
@@ -2432,8 +2419,9 @@
hme_write32(hp, bregs + BMAC_HTABLE3, hash_table[3]);
}
- /* Let us get going again. */
netif_wake_queue(dev);
+
+ spin_unlock_irq(&hp->happy_lock);
}
/* Ethtool support... */
@@ -2461,8 +2449,11 @@
ecmd.phy_address = 0; /* XXX fixed PHYAD */
/* Record PHY settings. */
+ spin_lock_irq(&hp->happy_lock);
hp->sw_bmcr = happy_meal_tcvr_read(hp, hp->tcvregs, MII_BMCR);
hp->sw_lpa = happy_meal_tcvr_read(hp, hp->tcvregs, MII_LPA);
+ spin_unlock_irq(&hp->happy_lock);
+
if (hp->sw_bmcr & BMCR_ANENABLE) {
ecmd.autoneg = AUTONEG_ENABLE;
ecmd.speed =
@@ -2504,10 +2495,12 @@
return -EINVAL;
/* Ok, do it to it. */
+ spin_lock_irq(&hp->happy_lock);
del_timer(&hp->happy_timer);
happy_meal_begin_auto_negotiation(hp,
hp->tcvregs,
&ecmd);
+ spin_unlock_irq(&hp->happy_lock);
return 0;
} else
@@ -2665,33 +2658,28 @@
struct happy_meal *hp;
struct net_device *dev;
int i, qfe_slot = -1;
+ int err = -ENODEV;
if (is_qfe) {
qp = quattro_sbus_find(sdev);
if (qp == NULL)
- return -ENODEV;
+ goto err_out;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
if (qp->happy_meals[qfe_slot] == NULL)
break;
if (qfe_slot == 4)
- return -ENODEV;
+ goto err_out;
}
- dev = init_etherdev(NULL, sizeof(struct happy_meal));
+ err = -ENOMEM;
+ dev = alloc_etherdev(sizeof(struct happy_meal));
if (!dev)
- return -ENOMEM;
+ goto err_out;
SET_MODULE_OWNER(dev);
if (hme_version_printed++ == 0)
printk(KERN_INFO "%s", version);
- if (qfe_slot != -1)
- printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
- dev->name, qfe_slot);
- else
- printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
- dev->name);
-
/* If user did not specify a MAC address specifically, use
* the Quattro local-mac-address property...
*/
@@ -2702,6 +2690,7 @@
if (i < 6) { /* a mac address was given */
for (i = 0; i < 6; i++)
dev->dev_addr[i] = macaddr[i];
+ macaddr[5]++;
} else if (qfe_slot != -1 &&
prom_getproplen(sdev->prom_node,
"local-mac-address") == 6) {
@@ -2711,11 +2700,6 @@
memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
}
- for (i = 0; i < 6; i++)
- printk("%2.2x%c",
- dev->dev_addr[i], i == 5 ? ' ' : ':');
- printk("\n");
-
hp = dev->priv;
memset(hp, 0, sizeof(*hp));
@@ -2723,11 +2707,12 @@
spin_lock_init(&hp->happy_lock);
+ err = -ENODEV;
if (sdev->num_registers != 5) {
printk(KERN_ERR "happymeal: Device does not have 5 regs, it has %d.\n",
sdev->num_registers);
printk(KERN_ERR "happymeal: Would you like that for here or to go?\n");
- return -ENODEV;
+ goto err_out_free_netdev;
}
if (qp != NULL) {
@@ -2741,35 +2726,35 @@
GREG_REG_SIZE, "HME Global Regs");
if (!hp->gregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal global registers.\n");
- return -ENODEV;
+ goto err_out_free_netdev;
}
hp->etxregs = sbus_ioremap(&sdev->resource[1], 0,
ETX_REG_SIZE, "HME TX Regs");
if (!hp->etxregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Transmit registers.\n");
- return -ENODEV;
+ goto err_out_iounmap;
}
hp->erxregs = sbus_ioremap(&sdev->resource[2], 0,
ERX_REG_SIZE, "HME RX Regs");
if (!hp->erxregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal MAC Receive registers.\n");
- return -ENODEV;
+ goto err_out_iounmap;
}
hp->bigmacregs = sbus_ioremap(&sdev->resource[3], 0,
BMAC_REG_SIZE, "HME BIGMAC Regs");
if (!hp->bigmacregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal BIGMAC registers.\n");
- return -ENODEV;
+ goto err_out_iounmap;
}
hp->tcvregs = sbus_ioremap(&sdev->resource[4], 0,
TCVR_REG_SIZE, "HME Tranceiver Regs");
if (!hp->tcvregs) {
printk(KERN_ERR "happymeal: Cannot map Happy Meal Tranceiver registers.\n");
- return -ENODEV;
+ goto err_out_iounmap;
}
hp->hm_revision = prom_getintdefault(sdev->prom_node, "hm-rev", 0xff);
@@ -2792,6 +2777,11 @@
hp->happy_block = sbus_alloc_consistent(hp->happy_dev,
PAGE_SIZE,
&hp->hblock_dvma);
+ err = -ENOMEM;
+ if (!hp->happy_block) {
+ printk(KERN_ERR "happymeal: Cannot allocate descriptors.\n");
+ goto err_out_iounmap;
+ }
/* Force check of the link first time we are brought up. */
hp->linkcheck = 0;
@@ -2832,22 +2822,161 @@
/* Grrr, Happy Meal comes up by default not advertising
* full duplex 100baseT capabilities, fix this.
*/
+ spin_lock_irq(&hp->happy_lock);
happy_meal_set_initial_advertisement(hp);
+ spin_unlock_irq(&hp->happy_lock);
+
+ if (register_netdev(hp->dev)) {
+ printk(KERN_ERR "happymeal: Cannot register net device, "
+ "aborting.\n");
+ goto err_out_free_consistent;
+ }
+
+ if (qfe_slot != -1)
+ printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
+ dev->name, qfe_slot);
+ else
+ printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
+ dev->name);
- ether_setup(dev);
+ for (i = 0; i < 6; i++)
+ printk("%2.2x%c",
+ dev->dev_addr[i], i == 5 ? ' ' : ':');
+ printk("\n");
/* We are home free at this point, link us in to the happy
* device list.
*/
- dev->ifindex = dev_new_index();
hp->next_module = root_happy_dev;
root_happy_dev = hp;
return 0;
+
+err_out_free_consistent:
+ sbus_free_consistent(hp->happy_dev,
+ PAGE_SIZE,
+ hp->happy_block,
+ hp->hblock_dvma);
+
+err_out_iounmap:
+ if (hp->gregs)
+ sbus_iounmap(hp->gregs, GREG_REG_SIZE);
+ if (hp->etxregs)
+ sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
+ if (hp->erxregs)
+ sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
+ if (hp->bigmacregs)
+ sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE);
+ if (hp->tcvregs)
+ sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE);
+
+err_out_free_netdev:
+ kfree(dev);
+
+err_out:
+ return err;
}
#endif
#ifdef CONFIG_PCI
+#ifndef __sparc__
+static int is_quattro_p(struct pci_dev *pdev)
+{
+ struct pci_dev *busdev = pdev->bus->self;
+ struct list_head *tmp;
+ int n_hmes;
+
+ if (busdev->vendor != PCI_VENDOR_ID_DEC ||
+ busdev->device != PCI_DEVICE_ID_DEC_21153)
+ return 0;
+
+ n_hmes = 0;
+ tmp = pdev->bus->devices.next;
+ while (tmp != &pdev->bus->devices) {
+ struct pci_dev *this_pdev = pci_dev_b(tmp);
+
+ if (this_pdev->vendor == PCI_VENDOR_ID_SUN &&
+ this_pdev->device == PCI_DEVICE_ID_SUN_HAPPYMEAL)
+ n_hmes++;
+
+ tmp = tmp->next;
+ }
+
+ if (n_hmes != 4)
+ return 0;
+
+ return 1;
+}
+
+/* Fetch MAC address from vital product data of PCI ROM. */
+static void find_eth_addr_in_vpd(void *rom_base, int len, int index, unsigned char *dev_addr)
+{
+ int this_offset;
+
+ for (this_offset = 0x20; this_offset < len; this_offset++) {
+ void *p = rom_base + this_offset;
+
+ if (readb(p + 0) != 0x90 ||
+ readb(p + 1) != 0x00 ||
+ readb(p + 2) != 0x09 ||
+ readb(p + 3) != 0x4e ||
+ readb(p + 4) != 0x41 ||
+ readb(p + 5) != 0x06)
+ continue;
+
+ this_offset += 6;
+ p += 6;
+
+ if (index == 0) {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ dev_addr[i] = readb(p + i);
+ break;
+ }
+ index--;
+ }
+}
+
+static void get_hme_mac_nonsparc(struct pci_dev *pdev, unsigned char *dev_addr)
+{
+ u32 rom_reg_orig;
+ void *p;
+ int index;
+
+ index = 0;
+ if (is_quattro_p(pdev))
+ index = PCI_SLOT(pdev->devfn);
+
+ if (pdev->resource[PCI_ROM_RESOURCE].parent == NULL) {
+ if (pci_assign_resource(pdev, PCI_ROM_RESOURCE) < 0)
+ goto use_random;
+ }
+
+ pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_reg_orig);
+ pci_write_config_dword(pdev, pdev->rom_base_reg,
+ rom_reg_orig | PCI_ROM_ADDRESS_ENABLE);
+
+ p = ioremap(pci_resource_start(pdev, PCI_ROM_RESOURCE), (64 * 1024));
+ if (p != NULL && readb(p) == 0x55 && readb(p + 1) == 0xaa)
+ find_eth_addr_in_vpd(p, (64 * 1024), index, dev_addr);
+
+ if (p != NULL)
+ iounmap(p);
+
+ pci_write_config_dword(pdev, pdev->rom_base_reg, rom_reg_orig);
+ return;
+
+use_random:
+ /* Sun MAC prefix then 3 random bytes. */
+ dev_addr[0] = 0x08;
+ dev_addr[1] = 0x00;
+ dev_addr[2] = 0x20;
+ get_random_bytes(dev_addr, 3);
+ return;
+}
+#endif /* !(__sparc__) */
+
static int __init happy_meal_pci_init(struct pci_dev *pdev)
{
struct quattro *qp = NULL;
@@ -2860,6 +2989,7 @@
unsigned long hpreg_base;
int i, qfe_slot = -1;
char prom_name[64];
+ int err;
/* Now make sure pci_dev cookie is there. */
#ifdef __sparc__
@@ -2872,52 +3002,33 @@
prom_getstring(node, "name", prom_name, sizeof(prom_name));
#else
-/* This needs to be corrected... -DaveM */
- strcpy(prom_name, "qfe");
+ if (is_quattro_p(pdev))
+ strcpy(prom_name, "SUNW,qfe");
+ else
+ strcpy(prom_name, "SUNW,hme");
#endif
+ err = -ENODEV;
if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) {
qp = quattro_pci_find(pdev);
if (qp == NULL)
- return -ENODEV;
+ goto err_out;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
if (qp->happy_meals[qfe_slot] == NULL)
break;
if (qfe_slot == 4)
- return -ENODEV;
+ goto err_out;
}
- dev = init_etherdev(NULL, sizeof(struct happy_meal));
+ dev = alloc_etherdev(sizeof(struct happy_meal));
+ err = -ENOMEM;
if (!dev)
- return -ENOMEM;
+ goto err_out;
SET_MODULE_OWNER(dev);
if (hme_version_printed++ == 0)
printk(KERN_INFO "%s", version);
- if (!qfe_slot) {
- struct pci_dev *qpdev = qp->quattro_dev;
-
- prom_name[0] = 0;
- if (!strncmp(dev->name, "eth", 3)) {
- int i = simple_strtoul(dev->name + 3, NULL, 10);
- sprintf(prom_name, "-%d", i + 3);
- }
- printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
- if (qpdev->vendor == PCI_VENDOR_ID_DEC &&
- qpdev->device == PCI_DEVICE_ID_DEC_21153)
- printk("DEC 21153 PCI Bridge\n");
- else
- printk("unknown bridge %04x.%04x\n",
- qpdev->vendor, qpdev->device);
- }
- if (qfe_slot != -1)
- printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
- dev->name, qfe_slot);
- else
- printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
- dev->name);
-
dev->base_addr = (long) pdev;
hp = (struct happy_meal *)dev->priv;
@@ -2934,13 +3045,20 @@
}
hpreg_base = pci_resource_start(pdev, 0);
+ err = -ENODEV;
if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0) {
printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n");
- return -ENODEV;
+ goto err_out_clear_quattro;
}
+ if (pci_request_regions(pdev, dev->name)) {
+ printk(KERN_ERR "happymeal(PCI): Cannot obtain PCI resources, "
+ "aborting.\n");
+ goto err_out_clear_quattro;
+ }
+
if ((hpreg_base = (unsigned long) ioremap(hpreg_base, 0x8000)) == 0) {
printk(KERN_ERR "happymeal(PCI): Unable to remap card memory.\n");
- return -ENODEV;
+ goto err_out_free_res;
}
for (i = 0; i < 6; i++) {
@@ -2950,6 +3068,7 @@
if (i < 6) { /* a mac address was given */
for (i = 0; i < 6; i++)
dev->dev_addr[i] = macaddr[i];
+ macaddr[5]++;
} else {
#ifdef __sparc__
if (qfe_slot != -1 &&
@@ -2960,15 +3079,10 @@
memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
}
#else
- memset(dev->dev_addr, 0, 6);
+ get_hme_mac_nonsparc(pdev, &dev->dev_addr[0]);
#endif
}
- for (i = 0; i < 6; i++)
- printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
-
- printk("\n");
-
/* Layout registers. */
hp->gregs = (hpreg_base + 0x0000UL);
hp->etxregs = (hpreg_base + 0x2000UL);
@@ -3005,9 +3119,10 @@
hp->happy_block = (struct hmeal_init_block *)
pci_alloc_consistent(pdev, PAGE_SIZE, &hp->hblock_dvma);
+ err = -ENODEV;
if (!hp->happy_block) {
printk(KERN_ERR "happymeal(PCI): Cannot get hme init block.\n");
- return -ENODEV;
+ goto err_out_iounmap;
}
hp->linkcheck = 0;
@@ -3022,6 +3137,8 @@
dev->hard_start_xmit = &happy_meal_start_xmit;
dev->get_stats = &happy_meal_get_stats;
dev->set_multicast_list = &happy_meal_set_multicast;
+ dev->tx_timeout = &happy_meal_tx_timeout;
+ dev->watchdog_timeo = 5*HZ;
dev->do_ioctl = &happy_meal_ioctl;
dev->irq = pdev->irq;
dev->dma = 0;
@@ -3044,18 +3161,67 @@
/* Grrr, Happy Meal comes up by default not advertising
* full duplex 100baseT capabilities, fix this.
*/
+ spin_lock_irq(&hp->happy_lock);
happy_meal_set_initial_advertisement(hp);
+ spin_unlock_irq(&hp->happy_lock);
+
+ if (register_netdev(hp->dev)) {
+ printk(KERN_ERR "happymeal(PCI): Cannot register net device, "
+ "aborting.\n");
+ goto err_out_iounmap;
+ }
+
+ if (!qfe_slot) {
+ struct pci_dev *qpdev = qp->quattro_dev;
+
+ prom_name[0] = 0;
+ if (!strncmp(dev->name, "eth", 3)) {
+ int i = simple_strtoul(dev->name + 3, NULL, 10);
+ sprintf(prom_name, "-%d", i + 3);
+ }
+ printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name);
+ if (qpdev->vendor == PCI_VENDOR_ID_DEC &&
+ qpdev->device == PCI_DEVICE_ID_DEC_21153)
+ printk("DEC 21153 PCI Bridge\n");
+ else
+ printk("unknown bridge %04x.%04x\n",
+ qpdev->vendor, qpdev->device);
+ }
+
+ if (qfe_slot != -1)
+ printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ",
+ dev->name, qfe_slot);
+ else
+ printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
+ dev->name);
- ether_setup(dev);
+ for (i = 0; i < 6; i++)
+ printk("%2.2x%c", dev->dev_addr[i], i == 5 ? ' ' : ':');
+
+ printk("\n");
/* We are home free at this point, link us in to the happy
* device list.
*/
- dev->ifindex = dev_new_index();
hp->next_module = root_happy_dev;
root_happy_dev = hp;
return 0;
+
+err_out_iounmap:
+ iounmap((void *)hp->gregs);
+
+err_out_free_res:
+ pci_release_regions(pdev);
+
+err_out_clear_quattro:
+ if (qp != NULL)
+ qp->happy_meals[qfe_slot] = NULL;
+
+ kfree(dev);
+
+err_out:
+ return err;
}
#endif
@@ -3102,6 +3268,7 @@
PCI_DEVICE_ID_SUN_HAPPYMEAL, pdev)) != NULL) {
if (pci_enable_device(pdev))
continue;
+ pci_set_master(pdev);
cards++;
happy_meal_pci_init(pdev);
}
@@ -3142,12 +3309,18 @@
while (root_happy_dev) {
struct happy_meal *hp = root_happy_dev;
struct happy_meal *next = root_happy_dev->next_module;
+ struct net_device *dev = hp->dev;
+
+ /* Unregister netdev before unmapping registers as this
+ * call can end up trying to access those registers.
+ */
+ unregister_netdev(dev);
#ifdef CONFIG_SBUS
if (!(hp->happy_flags & HFLAG_PCI)) {
if (hp->happy_flags & HFLAG_QUATTRO) {
if (hp->qfe_parent != last_seen_qfe) {
- free_irq(hp->dev->irq, hp->qfe_parent);
+ free_irq(dev->irq, hp->qfe_parent);
last_seen_qfe = hp->qfe_parent;
}
}
@@ -3170,12 +3343,35 @@
hp->happy_block,
hp->hblock_dvma);
iounmap((void *)hp->gregs);
+ pci_release_regions(hp->happy_dev);
}
#endif
- unregister_netdev(hp->dev);
- kfree(hp->dev);
+ kfree(dev);
+
root_happy_dev = next;
}
+
+ /* Now cleanup the quattro lists. */
+#ifdef CONFIG_SBUS
+ while (qfe_sbus_list) {
+ struct quattro *qfe = qfe_sbus_list;
+ struct quattro *next = qfe->next;
+
+ kfree(qfe);
+
+ qfe_sbus_list = next;
+ }
+#endif
+#ifdef CONFIG_PCI
+ while (qfe_pci_list) {
+ struct quattro *qfe = qfe_pci_list;
+ struct quattro *next = qfe->next;
+
+ kfree(qfe);
+
+ qfe_pci_list = next;
+ }
+#endif
}
module_init(happy_meal_probe);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)