diff options
Diffstat (limited to 'drivers/net/sunhme.c')
-rw-r--r-- | drivers/net/sunhme.c | 211 |
1 files changed, 141 insertions, 70 deletions
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index ebe502604..53bafee55 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -58,6 +58,70 @@ static struct happy_meal *root_happy_dev = NULL; #undef SXDEBUG #undef RXDEBUG #undef TXDEBUG +#undef TXLOGGING + +#ifdef TXLOGGING +struct hme_tx_logent { + unsigned int tstamp; + int tx_new, tx_old; + unsigned int action; +#define TXLOG_ACTION_IRQ 0x01 +#define TXLOG_ACTION_TXMIT 0x02 +#define TXLOG_ACTION_TBUSY 0x04 +#define TXLOG_ACTION_NBUFS 0x08 + unsigned int status; +}; +#define TX_LOG_LEN 128 +static struct hme_tx_logent tx_log[TX_LOG_LEN]; +static int txlog_cur_entry = 0; +static __inline__ void tx_add_log(struct happy_meal *hp, unsigned int a, unsigned int s) +{ + struct hme_tx_logent *tlp; + unsigned long flags; + + save_and_cli(flags); + tlp = &tx_log[txlog_cur_entry]; + tlp->tstamp = (unsigned int)jiffies; + tlp->tx_new = hp->tx_new; + tlp->tx_old = hp->tx_old; + tlp->action = a; + tlp->status = s; + txlog_cur_entry = (txlog_cur_entry + 1) & (TX_LOG_LEN - 1); + restore_flags(flags); +} +static __inline__ void tx_dump_log(void) +{ + int i, this; + + this = txlog_cur_entry; + for(i = 0; i < TX_LOG_LEN; i++) { + printk("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i, + tx_log[this].tstamp, + tx_log[this].tx_new, tx_log[this].tx_old, + tx_log[this].action, tx_log[this].status); + this = (this + 1) & (TX_LOG_LEN - 1); + } +} +static __inline__ void tx_dump_ring(struct happy_meal *hp) +{ + struct hmeal_init_block *hb = hp->happy_block; + struct happy_meal_txd *tp = &hb->happy_meal_txd[0]; + int i; + + for(i = 0; i < TX_RING_SIZE; i+=4) { + printk("TXD[%d..%d]: [%08x:%08x] [%08x:%08x] [%08x:%08x] [%08x:%08x]\n", + i, i + 4, + le32_to_cpu(tp[i].tx_flags), le32_to_cpu(tp[i].tx_addr), + le32_to_cpu(tp[i + 1].tx_flags), le32_to_cpu(tp[i + 1].tx_addr), + le32_to_cpu(tp[i + 2].tx_flags), le32_to_cpu(tp[i + 2].tx_addr), + le32_to_cpu(tp[i + 3].tx_flags), le32_to_cpu(tp[i + 3].tx_addr)); + } +} +#else +#define tx_add_log(hp, a, s) do { } while(0) +#define tx_dump_log() do { } while(0) +#define tx_dump_ring(hp) do { } while(0) +#endif #ifdef HMEDEBUG #define HMD(x) printk x @@ -73,7 +137,7 @@ static struct happy_meal *root_happy_dev = NULL; #define ASD(x) #endif -#define DEFAULT_IPG0 32 /* For lance-mode only */ +#define DEFAULT_IPG0 16 /* For lance-mode only */ #define DEFAULT_IPG1 8 /* For all modes */ #define DEFAULT_IPG2 4 /* For all modes */ #define DEFAULT_JAMSIZE 4 /* Toe jam */ @@ -376,18 +440,33 @@ static int set_happy_link_modes(struct happy_meal *hp, struct hmeal_tcvregs *tre full = 0; } - /* XXX This may not be enough, we may need to reinit the entire - * XXX Happy Meal front end for this to work every time. + /* Before changing other bits in the tx_cfg register, and in + * general any of other the TX config registers too, you + * must: + * 1) Clear Enable + * 2) Poll with reads until that bit reads back as zero + * 3) Make TX configuration changes + * 4) Set Enable once more */ - if(full) + hme_write32(hp, &hp->bigmacregs->tx_cfg, + hme_read32(hp, &hp->bigmacregs->tx_cfg) & + ~(BIGMAC_TXCFG_ENABLE)); + while(hme_read32(hp, &hp->bigmacregs->tx_cfg) & BIGMAC_TXCFG_ENABLE) + barrier(); + if(full) { + hp->happy_flags |= HFLAG_FULL; hme_write32(hp, &hp->bigmacregs->tx_cfg, hme_read32(hp, &hp->bigmacregs->tx_cfg) | BIGMAC_TXCFG_FULLDPLX); - else + } else { + hp->happy_flags &= ~(HFLAG_FULL); hme_write32(hp, &hp->bigmacregs->tx_cfg, hme_read32(hp, &hp->bigmacregs->tx_cfg) & ~(BIGMAC_TXCFG_FULLDPLX)); - + } + hme_write32(hp, &hp->bigmacregs->tx_cfg, + hme_read32(hp, &hp->bigmacregs->tx_cfg) | + BIGMAC_TXCFG_ENABLE); return 0; no_response: return 1; @@ -1220,8 +1299,8 @@ static int happy_meal_init(struct happy_meal *hp, int from_irq) /* Load up the MAC address and random seed. */ HMD(("rseed/macaddr, ")); - /* XXX use something less deterministic... */ - hme_write32(hp, &bregs->rand_seed, 0xbd); + /* The docs recommend to use the 10LSB of our MAC here. */ + hme_write32(hp, &bregs->rand_seed, ((e[5] | e[4]<<8)&0x3ff)); hme_write32(hp, &bregs->mac_addr2, ((e[4] << 8) | e[5])); hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3])); @@ -2013,12 +2092,13 @@ static void pci_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs hp->dev->tbusy = 0; mark_bh(NET_BH); } - + tx_add_log(hp, TXLOG_ACTION_IRQ, happy_status); dev->interrupt = 0; HMD(("done\n")); } #endif +#ifndef __sparc_v9__ static void sun4c_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *) dev_id; @@ -2062,6 +2142,7 @@ static void sun4c_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *re dev->interrupt = 0; HMD(("done\n")); } +#endif static int happy_meal_open(struct device *dev) { @@ -2069,6 +2150,7 @@ static int happy_meal_open(struct device *dev) int res; HMD(("happy_meal_open: ")); +#ifndef __sparc_v9__ if(sparc_cpu_model == sun4c) { if(request_irq(dev->irq, &sun4c_happy_meal_interrupt, SA_SHIRQ, "HAPPY MEAL", (void *) dev)) { @@ -2076,47 +2158,26 @@ static int happy_meal_open(struct device *dev) printk("happy meal: Can't order irq %d to go.\n", dev->irq); return -EAGAIN; } - } -#ifdef __sparc_v9__ - else if(sparc_cpu_model == sun4u) { - struct devid_cookie dcookie; - + } else +#else #ifdef CONFIG_PCI - if(hp->happy_flags & HFLAG_PCI) { - if(request_irq(dev->irq, &pci_happy_meal_interrupt, - SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) { - HMD(("EAGAIN\n")); - printk("happy_meal(PCI: Can't order irq %d to go.\n", - dev->irq); - return -EAGAIN; - } - goto v9_done; - } -#endif - dcookie.real_dev_id = dev; - dcookie.imap = dcookie.iclr = 0; - dcookie.pil = -1; - dcookie.bus_cookie = hp->happy_sbus_dev->my_bus; - if(request_irq(dev->irq, &happy_meal_interrupt, - (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), - "HAPPY MEAL", &dcookie)) { - HMD(("EAGAIN\n")); - printk("happy_meal(SBUS): Can't order irq %d to go.\n", - dev->irq); + if(hp->happy_flags & HFLAG_PCI) { + if(request_irq(dev->irq, &pci_happy_meal_interrupt, + SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) { + HMD(("EAGAIN\n")); + printk("happy_meal(PCI: Can't order irq %s to go.\n", + __irq_itoa(dev->irq)); return -EAGAIN; } -#ifdef CONFIG_PCI - v9_done: + } else #endif - } #endif - else { - if(request_irq(dev->irq, &happy_meal_interrupt, - SA_SHIRQ, "HAPPY MEAL", (void *) dev)) { - HMD(("EAGAIN\n")); - printk("happy meal: Can't order irq %d to go.\n", dev->irq); - return -EAGAIN; - } + if(request_irq(dev->irq, &happy_meal_interrupt, + SA_SHIRQ, "HAPPY MEAL", (void *)dev)) { + HMD(("EAGAIN\n")); + printk("happy_meal(SBUS): Can't order irq %s to go.\n", + __irq_itoa(dev->irq)); + return -EAGAIN; } HMD(("Init happy timer\n")); init_timer(&hp->happy_timer); @@ -2154,29 +2215,29 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) struct happy_meal *hp = (struct happy_meal *) dev->priv; int len, entry; - if(dev->tbusy) { + if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 40) { - return 1; - } else { + if (tickssofar >= 40) { printk ("%s: transmit timed out, resetting\n", dev->name); hp->net_stats.tx_errors++; + tx_dump_log(); + printk ("%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, + hme_read32(hp, &hp->gregs->stat), + hme_read32(hp, &hp->etxregs->cfg), + hme_read32(hp, &hp->bigmacregs->tx_cfg)); happy_meal_init(hp, 0); dev->tbusy = 0; dev->trans_start = jiffies; - return 0; - } - } - - if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { - printk("happy meal: Transmitter access conflict.\n"); + } else + tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_TBUSY, 0); return 1; } - if(!TX_BUFFS_AVAIL(hp)) + if(!TX_BUFFS_AVAIL(hp)) { + tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_NBUFS, 0); return 1; - + } len = skb->len; entry = hp->tx_new; @@ -2194,6 +2255,7 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) if(TX_BUFFS_AVAIL(hp)) dev->tbusy = 0; + tx_add_log(hp, TXLOG_ACTION_TXMIT, 0); return 0; } @@ -2203,29 +2265,32 @@ static int pci_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) struct happy_meal *hp = (struct happy_meal *) dev->priv; int len, entry; - if(dev->tbusy) { + if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 40) { - return 1; - } else { + if (tickssofar >= 40) { + unsigned long flags; + printk ("%s: transmit timed out, resetting\n", dev->name); + + save_and_cli(flags); + tx_dump_log(); + tx_dump_ring(hp); + restore_flags(flags); + hp->net_stats.tx_errors++; happy_meal_init(hp, 0); dev->tbusy = 0; dev->trans_start = jiffies; - return 0; - } - } - - if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { - printk("happy meal: Transmitter access conflict.\n"); + } else + tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_TBUSY, 0); return 1; } - if(!TX_BUFFS_AVAIL(hp)) + if(!TX_BUFFS_AVAIL(hp)) { + tx_add_log(hp, TXLOG_ACTION_TXMIT|TXLOG_ACTION_NBUFS, 0); return 1; - + } len = skb->len; entry = hp->tx_new; @@ -2243,6 +2308,7 @@ static int pci_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) if(TX_BUFFS_AVAIL(hp)) dev->tbusy = 0; + tx_add_log(hp, TXLOG_ACTION_TXMIT, 0); return 0; } #endif @@ -2510,7 +2576,7 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de dev->get_stats = &happy_meal_get_stats; dev->set_multicast_list = &happy_meal_set_multicast; - dev->irq = sdev->irqs[0].pri; + dev->irq = sdev->irqs[0]; dev->dma = 0; ether_setup(dev); #ifdef MODULE @@ -2630,6 +2696,11 @@ __initfunc(int happy_meal_pci_init(struct device *dev, struct pci_dev *pdev)) pdev->devfn, PCI_COMMAND, pci_command); + /* Set the latency timer as well, PROM leaves it at zero. */ + pcibios_write_config_byte(pdev->bus->number, + pdev->devfn, + PCI_LATENCY_TIMER, 240); + #ifdef MODULE /* We are home free at this point, link us in to the happy * module device list. |