📄 ioc3-eth.c
字号:
* if the auto negotiation has completed, we assume here that the DP83840 PHY * will time out at some point and just tell us what (didn't) happen. For * complete coverage we only allow so many of the ticks at this level to run, * when this has expired we print a warning message and try another strategy. * This "other" strategy is to force the interface into various speed/duplex * configurations and we stop when we see a link-up condition before the * maximum number of "peek" ticks have occurred. * * Once a valid link status has been detected we configure the IOC3 to speak * the most efficient protocol we could get a clean link for. The priority * for link configurations, highest first is: * * 100 Base-T Full Duplex * 100 Base-T Half Duplex * 10 Base-T Full Duplex * 10 Base-T Half Duplex * * We start a new timer now, after a successful auto negotiation status has * been detected. This timer just waits for the link-up bit to get set in * the BMCR of the DP83840. When this occurs we print a kernel log message * describing the link type in use and the fact that it is up. * * If a fatal error of some sort is signalled and detected in the interrupt * service routine, and the chip is reset, or the link is ifconfig'd down * and then back up, this entire process repeats itself all over again. */static int ioc3_try_next_permutation(struct ioc3_private *ip){ ip->sw_bmcr = mii_read(ip, MII_BMCR); /* Downgrade from full to half duplex. Only possible via ethtool. */ if (ip->sw_bmcr & BMCR_FULLDPLX) { ip->sw_bmcr &= ~BMCR_FULLDPLX; mii_write(ip, MII_BMCR, ip->sw_bmcr); return 0; } /* Downgrade from 100 to 10. */ if (ip->sw_bmcr & BMCR_SPEED100) { ip->sw_bmcr &= ~BMCR_SPEED100; mii_write(ip, MII_BMCR, ip->sw_bmcr); return 0; } /* We've tried everything. */ return -1;}static voidioc3_display_link_mode(struct ioc3_private *ip){ char *tmode = ""; ip->sw_lpa = mii_read(ip, MII_LPA); if (ip->sw_lpa & (LPA_100HALF | LPA_100FULL)) { if (ip->sw_lpa & LPA_100FULL) tmode = "100Mb/s, Full Duplex"; else tmode = "100Mb/s, Half Duplex"; } else { if (ip->sw_lpa & LPA_10FULL) tmode = "10Mb/s, Full Duplex"; else tmode = "10Mb/s, Half Duplex"; } printk(KERN_INFO "%s: Link is up at %s.\n", ip->dev->name, tmode);}static voidioc3_display_forced_link_mode(struct ioc3_private *ip){ char *speed = "", *duplex = ""; ip->sw_bmcr = mii_read(ip, MII_BMCR); if (ip->sw_bmcr & BMCR_SPEED100) speed = "100Mb/s, "; else speed = "10Mb/s, "; if (ip->sw_bmcr & BMCR_FULLDPLX) duplex = "Full Duplex.\n"; else duplex = "Half Duplex.\n"; printk(KERN_INFO "%s: Link has been forced up at %s%s", ip->dev->name, speed, duplex);}static int ioc3_set_link_modes(struct ioc3_private *ip){ struct ioc3 *ioc3 = ip->regs; int full; /* * All we care about is making sure the bigmac tx_cfg has a * proper duplex setting. */ if (ip->timer_state == arbwait) { ip->sw_lpa = mii_read(ip, MII_LPA); if (!(ip->sw_lpa & (LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL))) goto no_response; if (ip->sw_lpa & LPA_100FULL) full = 1; else if (ip->sw_lpa & LPA_100HALF) full = 0; else if (ip->sw_lpa & LPA_10FULL) full = 1; else full = 0; } else { /* Forcing a link mode. */ ip->sw_bmcr = mii_read(ip, MII_BMCR); if (ip->sw_bmcr & BMCR_FULLDPLX) full = 1; else full = 0; } if (full) ip->emcr |= EMCR_DUPLEX; else ip->emcr &= ~EMCR_DUPLEX; ioc3->emcr = ip->emcr; ioc3->emcr; return 0;no_response: return 1;}static int is_lucent_phy(struct ioc3_private *ip){ unsigned short mr2, mr3; int ret = 0; mr2 = mii_read(ip, MII_PHYSID1); mr3 = mii_read(ip, MII_PHYSID2); if ((mr2 & 0xffff) == 0x0180 && ((mr3 & 0xffff) >> 10) == 0x1d) { ret = 1; } return ret;}static void ioc3_timer(unsigned long data){ struct ioc3_private *ip = (struct ioc3_private *) data; int restart_timer = 0; ip->timer_ticks++; switch (ip->timer_state) { case arbwait: /* * Only allow for 5 ticks, thats 10 seconds and much too * long to wait for arbitration to complete. */ if (ip->timer_ticks >= 10) { /* Enter force mode. */ do_force_mode: ip->sw_bmcr = mii_read(ip, MII_BMCR); printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful," " trying force link mode\n", ip->dev->name); ip->sw_bmcr = BMCR_SPEED100; mii_write(ip, MII_BMCR, ip->sw_bmcr); if (!is_lucent_phy(ip)) { /* * OK, seems we need do disable the transceiver * for the first tick to make sure we get an * accurate link state at the second tick. */ ip->sw_csconfig = mii_read(ip, MII_CSCONFIG); ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB); mii_write(ip, MII_CSCONFIG, ip->sw_csconfig); } ip->timer_state = ltrywait; ip->timer_ticks = 0; restart_timer = 1; } else { /* Anything interesting happen? */ ip->sw_bmsr = mii_read(ip, MII_BMSR); if (ip->sw_bmsr & BMSR_ANEGCOMPLETE) { int ret; /* Just what we've been waiting for... */ ret = ioc3_set_link_modes(ip); if (ret) { /* Ooops, something bad happened, go to * force mode. * * XXX Broken hubs which don't support * XXX 802.3u auto-negotiation make this * XXX happen as well. */ goto do_force_mode; } /* * Success, at least so far, advance our state * engine. */ ip->timer_state = lupwait; restart_timer = 1; } else { restart_timer = 1; } } break; case lupwait: /* * Auto negotiation was successful and we are awaiting a * link up status. I have decided to let this timer run * forever until some sort of error is signalled, reporting * a message to the user at 10 second intervals. */ ip->sw_bmsr = mii_read(ip, MII_BMSR); if (ip->sw_bmsr & BMSR_LSTATUS) { /* * Wheee, it's up, display the link mode in use and put * the timer to sleep. */ ioc3_display_link_mode(ip); ip->timer_state = asleep; restart_timer = 0; } else { if (ip->timer_ticks >= 10) { printk(KERN_NOTICE "%s: Auto negotiation successful, link still " "not completely up.\n", ip->dev->name); ip->timer_ticks = 0; restart_timer = 1; } else { restart_timer = 1; } } break; case ltrywait: /* * Making the timeout here too long can make it take * annoyingly long to attempt all of the link mode * permutations, but then again this is essentially * error recovery code for the most part. */ ip->sw_bmsr = mii_read(ip, MII_BMSR); ip->sw_csconfig = mii_read(ip, MII_CSCONFIG); if (ip->timer_ticks == 1) { if (!is_lucent_phy(ip)) { /* * Re-enable transceiver, we'll re-enable the * transceiver next tick, then check link state * on the following tick. */ ip->sw_csconfig |= CSCONFIG_TCVDISAB; mii_write(ip, MII_CSCONFIG, ip->sw_csconfig); } restart_timer = 1; break; } if (ip->timer_ticks == 2) { if (!is_lucent_phy(ip)) { ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB); mii_write(ip, MII_CSCONFIG, ip->sw_csconfig); } restart_timer = 1; break; } if (ip->sw_bmsr & BMSR_LSTATUS) { /* Force mode selection success. */ ioc3_display_forced_link_mode(ip); ioc3_set_link_modes(ip); /* XXX error? then what? */ ip->timer_state = asleep; restart_timer = 0; } else { if (ip->timer_ticks >= 4) { /* 6 seconds or so... */ int ret; ret = ioc3_try_next_permutation(ip); if (ret == -1) { /* * Aieee, tried them all, reset the * chip and try all over again. */ printk(KERN_NOTICE "%s: Link down, " "cable problem?\n", ip->dev->name); ioc3_init(ip); return; } if (!is_lucent_phy(ip)) { ip->sw_csconfig = mii_read(ip, MII_CSCONFIG); ip->sw_csconfig |= CSCONFIG_TCVDISAB; mii_write(ip, MII_CSCONFIG, ip->sw_csconfig); } ip->timer_ticks = 0; restart_timer = 1; } else { restart_timer = 1; } } break; case asleep: default: /* Can't happens.... */ printk(KERN_ERR "%s: Aieee, link timer is asleep but we got " "one anyways!\n", ip->dev->name); restart_timer = 0; ip->timer_ticks = 0; ip->timer_state = asleep; /* foo on you */ break; }; if (restart_timer) { ip->ioc3_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2s */ add_timer(&ip->ioc3_timer); }}static voidioc3_start_auto_negotiation(struct ioc3_private *ip, struct ethtool_cmd *ep){ int timeout; /* Read all of the registers we are interested in now. */ ip->sw_bmsr = mii_read(ip, MII_BMSR); ip->sw_bmcr = mii_read(ip, MII_BMCR); ip->sw_physid1 = mii_read(ip, MII_PHYSID1); ip->sw_physid2 = mii_read(ip, MII_PHYSID2); /* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */ ip->sw_advertise = mii_read(ip, MII_ADVERTISE); if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { /* Advertise everything we can support. */ if (ip->sw_bmsr & BMSR_10HALF) ip->sw_advertise |= ADVERTISE_10HALF; else ip->sw_advertise &= ~ADVERTISE_10HALF; if (ip->sw_bmsr & BMSR_10FULL) ip->sw_advertise |= ADVERTISE_10FULL; else ip->sw_advertise &= ~ADVERTISE_10FULL; if (ip->sw_bmsr & BMSR_100HALF) ip->sw_advertise |= ADVERTISE_100HALF; else ip->sw_advertise &= ~ADVERTISE_100HALF; if (ip->sw_bmsr & BMSR_100FULL) ip->sw_advertise |= ADVERTISE_100FULL; else ip->sw_advertise &= ~ADVERTISE_100FULL; mii_write(ip, MII_ADVERTISE, ip->sw_advertise); /* * XXX Currently no IOC3 card I know off supports 100BaseT4, * XXX and this is because the DP83840 does not support it, * XXX changes XXX would need to be made to the tx/rx logic in * XXX the driver as well so I completely skip checking for it * XXX in the BMSR for now. */#ifdef AUTO_SWITCH_DEBUG ASD(("%s: Advertising [ ", ip->dev->name)); if (ip->sw_advertise & ADVERTISE_10HALF) ASD(("10H ")); if (ip->sw_advertise & ADVERTISE_10FULL) ASD(("10F ")); if (ip->sw_advertise & ADVERTISE_100HALF) ASD(("100H ")); if (ip->sw_advertise & ADVERTISE_100FULL) ASD(("100F "));#endif /* Enable Auto-Negotiation, this is usually on already... */ ip->sw_bmcr |= BMCR_ANENABLE; mii_write(ip, MII_BMCR, ip->sw_bmcr); /* Restart it to make sure it is going. */ ip->sw_bmcr |= BMCR_ANRESTART; mii_write(ip, MII_BMCR, ip->sw_bmcr); /* BMCR_ANRESTART self clears when the process has begun. */ timeout = 64; /* More than enough. */ while (--timeout) { ip->sw_bmcr = mii_read(ip, MII_BMCR); if (!(ip->sw_bmcr & BMCR_ANRESTART)) break; /* got it. */ udelay(10); } if (!timeout) { printk(KERN_ERR "%s: IOC3 would not start auto " "negotiation BMCR=0x%04x\n", ip->dev->name, ip->sw_bmcr); printk(KERN_NOTICE "%s: Performing force link " "detection.\n", ip->dev->name); goto force_link; } else { ip->timer_state = arbwait; } } else {force_link: /* * Force the link up, trying first a particular mode. Either * we are here at the request of ethtool or because the IOC3 * would not start to autoneg. */ /* * Disable auto-negotiation in BMCR, enable the duplex and * speed setting, init the timer state machine, and fire it off. */ if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { ip->sw_bmcr = BMCR_SPEED100; } else { if (ep->speed == SPEED_100) ip->sw_bmcr = BMCR_SPEED100; else ip->sw_bmcr = 0; if (ep->duplex == DUPLEX_FULL) ip->sw_bmcr |= BMCR_FULLDPLX; } mii_write(ip, MII_BMCR, ip->sw_bmcr); if (!is_lucent_phy(ip)) { /* * OK, seems we need do disable the transceiver for the * first tick to make sure we get an accurate link * state at the second tick. */ ip->sw_csconfig = mii_read(ip, MII_CSCONFIG); ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB); mii_write(ip, MII_CSCONFIG, ip->sw_csconfig); } ip->timer_state = ltrywait; } del_timer(&ip->ioc3_timer); ip->timer_ticks = 0; ip->ioc3_timer.expires = jiffies + (12 * HZ)/10; /* 1.2 sec. */ ip->ioc3_timer.data = (unsigned long) ip; ip->ioc3_timer.function = &ioc3_timer; add_timer(&ip->ioc3_timer);}static int ioc3_mii_init(struct ioc3_private *ip){ int i, found; u16 word; found = 0; spin_lock_irq(&ip->ioc3_lock); for (i = 0; i < 32; i++) { ip->phy = i; word = mii_read(ip, 2); if ((word != 0xffff) && (word != 0x0000)) { found = 1; break; /* Found a PHY */ } } if (!found) { spin_unlock_irq(&ip->ioc3_lock); return -ENODEV; } ioc3_start_auto_negotiation(ip, NULL); // XXX ethtool spin_unlock_irq(&ip->ioc3_lock); return 0;}static inline voidioc3_clean_rx_ring(struct ioc3_private *ip){ struct sk_buff *skb; int i; for (i = ip->rx_ci; i & 15; i++) { ip->rx_skbs[ip->rx_pi] = ip->rx_skbs[ip->rx_ci]; ip->rxr[ip->rx_pi++] = ip->rxr[ip->rx_ci++]; } ip->rx_pi &= 511; ip->rx_ci &= 511; for (i = ip->rx_ci; i != ip->rx_pi; i = (i+1) & 511) { struct ioc3_erxbuf *rxb; skb = ip->rx_skbs[i]; rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET); rxb->w0 = 0; }}static inline voidioc3_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; } ip->tx_pi = 0; ip->tx_ci = 0;}static voidioc3_free_rings(struct ioc3_private *ip){ struct sk_buff *skb; int rx_entry, n_entry; if (ip->txr) { ioc3_clean_tx_ring(ip); free_pages((unsigned long)ip->txr, 2); ip->txr = NULL; } 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); n_entry = (n_entry + 1) & 511; } free_page((unsigned long)ip->rxr); ip->rxr = NULL; }}static voidioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3){ struct ioc3_erxbuf *rxb; unsigned long *rxr; int i; if (ip->rxr == NULL) { /* Allocate and initialize rx ring. 4kb = 512 entries */ ip->rxr = (unsigned long *) get_free_page(GFP_ATOMIC); rxr = (unsigned long *) ip->rxr; if (!rxr) printk("ioc3_alloc_rings(): get_free_page() failed!\n"); /* 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, GFP_ATOMIC); if (!skb) { show_free_areas(); continue; } ip->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; } if (ip->txr == NULL) { /* Allocate and initialize tx rings. 16kb = 128 bufs. */ ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2); if (!ip->txr) printk("ioc3_alloc_rings(): get_free_page() failed!\n"); ip->tx_pi = 0; ip->tx_ci = 0; }}static voidioc3_init_rings(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3){ unsigned long ring; ioc3_free_rings(ip);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -