📄 sunhme.c
字号:
BB_PUT_BIT(hp, tregs, 0); BB_PUT_BIT(hp, tregs, 1); BB_PUT_BIT(hp, tregs, 0); BB_PUT_BIT(hp, tregs, 1); /* Give it the PHY address. */ tmp = (hp->paddr & 0xff); for (i = 4; i >= 0; i--) BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1)); /* Tell it what register we will be writing. */ tmp = (reg & 0xff); for (i = 4; i >= 0; i--) BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1)); /* Tell it to become ready for the bits. */ BB_PUT_BIT(hp, tregs, 1); BB_PUT_BIT(hp, tregs, 0); for (i = 15; i >= 0; i--) BB_PUT_BIT(hp, tregs, ((value >> i) & 1)); /* Close down the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 0);}#define TCVR_READ_TRIES 16static int happy_meal_tcvr_read(struct happy_meal *hp, unsigned long tregs, int reg){ int tries = TCVR_READ_TRIES; int retval; ASD(("happy_meal_tcvr_read: reg=0x%02x ", reg)); if (hp->tcvr_type == none) { ASD(("no transceiver, value=TCVR_FAILURE\n")); return TCVR_FAILURE; } if (!(hp->happy_flags & HFLAG_FENABLE)) { ASD(("doing bit bang\n")); return happy_meal_bb_read(hp, tregs, reg); } hme_write32(hp, tregs + TCVR_FRAME, (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18))); while (!(hme_read32(hp, tregs + TCVR_FRAME) & 0x10000) && --tries) udelay(20); if (!tries) { printk(KERN_ERR "happy meal: Aieee, transceiver MIF read bolixed\n"); return TCVR_FAILURE; } retval = hme_read32(hp, tregs + TCVR_FRAME) & 0xffff; ASD(("value=%04x\n", retval)); return retval;}#define TCVR_WRITE_TRIES 16static void happy_meal_tcvr_write(struct happy_meal *hp, unsigned long tregs, int reg, unsigned short value){ int tries = TCVR_WRITE_TRIES; ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value)); /* Welcome to Sun Microsystems, can I take your order please? */ if (!hp->happy_flags & HFLAG_FENABLE) return happy_meal_bb_write(hp, tregs, reg, value); /* Would you like fries with that? */ hme_write32(hp, tregs + TCVR_FRAME, (FRAME_WRITE | (hp->paddr << 23) | ((reg & 0xff) << 18) | (value & 0xffff))); while (!(hme_read32(hp, tregs + TCVR_FRAME) & 0x10000) && --tries) udelay(20); /* Anything else? */ if (!tries) printk(KERN_ERR "happy meal: Aieee, transceiver MIF write bolixed\n"); /* Fifty-two cents is your change, have a nice day. */}/* Auto negotiation. The scheme is very simple. We have a timer routine * that keeps watching the auto negotiation process as it progresses. * The DP83840 is first told to start doing it's thing, we set up the time * and place the timer state machine in it's initial state. * * Here the timer peeks at the DP83840 status registers at each click to see * 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 BigMAC and * the rest of the Happy Meal 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 try_next_permutation(struct happy_meal *hp, unsigned long tregs){ hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); /* Downgrade from full to half duplex. Only possible * via ethtool. */ if (hp->sw_bmcr & BMCR_FULLDPLX) { hp->sw_bmcr &= ~(BMCR_FULLDPLX); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); return 0; } /* Downgrade from 100 to 10. */ if (hp->sw_bmcr & BMCR_SPEED100) { hp->sw_bmcr &= ~(BMCR_SPEED100); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); return 0; } /* We've tried everything. */ return -1;}static void display_link_mode(struct happy_meal *hp, unsigned long tregs){ printk(KERN_INFO "%s: Link is up using ", hp->dev->name); if (hp->tcvr_type == external) printk("external "); else printk("internal "); printk("transceiver at "); hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA); if (hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) { if (hp->sw_lpa & LPA_100FULL) printk("100Mb/s, Full Duplex.\n"); else printk("100Mb/s, Half Duplex.\n"); } else { if (hp->sw_lpa & LPA_10FULL) printk("10Mb/s, Full Duplex.\n"); else printk("10Mb/s, Half Duplex.\n"); }}static void display_forced_link_mode(struct happy_meal *hp, unsigned long tregs){ printk(KERN_INFO "%s: Link has been forced up using ", hp->dev->name); if (hp->tcvr_type == external) printk("external "); else printk("internal "); printk("transceiver at "); hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (hp->sw_bmcr & BMCR_SPEED100) printk("100Mb/s, "); else printk("10Mb/s, "); if (hp->sw_bmcr & BMCR_FULLDPLX) printk("Full Duplex.\n"); else printk("Half Duplex.\n");}static int set_happy_link_modes(struct happy_meal *hp, unsigned long tregs){ int full; /* All we care about is making sure the bigmac tx_cfg has a * proper duplex setting. */ if (hp->timer_state == arbwait) { hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA); if (!(hp->sw_lpa & (LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL))) goto no_response; if (hp->sw_lpa & LPA_100FULL) full = 1; else if (hp->sw_lpa & LPA_100HALF) full = 0; else if (hp->sw_lpa & LPA_10FULL) full = 1; else full = 0; } else { /* Forcing a link mode. */ hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if (hp->sw_bmcr & BMCR_FULLDPLX) full = 1; else full = 0; } /* 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 */ hme_write32(hp, hp->bigmacregs + BMAC_TXCFG, hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) & ~(BIGMAC_TXCFG_ENABLE)); while (hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) & BIGMAC_TXCFG_ENABLE) barrier(); if (full) { hp->happy_flags |= HFLAG_FULL; hme_write32(hp, hp->bigmacregs + BMAC_TXCFG, hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) | BIGMAC_TXCFG_FULLDPLX); } else { hp->happy_flags &= ~(HFLAG_FULL); hme_write32(hp, hp->bigmacregs + BMAC_TXCFG, hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) & ~(BIGMAC_TXCFG_FULLDPLX)); } hme_write32(hp, hp->bigmacregs + BMAC_TXCFG, hme_read32(hp, hp->bigmacregs + BMAC_TXCFG) | BIGMAC_TXCFG_ENABLE); return 0;no_response: return 1;}static int happy_meal_init(struct happy_meal *hp, int from_irq);static int is_lucent_phy(struct happy_meal *hp){ unsigned long tregs = hp->tcvregs; unsigned short mr2, mr3; int ret = 0; 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 ret = 1; } return ret;}static void happy_meal_timer(unsigned long data){ struct happy_meal *hp = (struct happy_meal *) data; unsigned long tregs = hp->tcvregs; int restart_timer = 0; hp->timer_ticks++; switch(hp->timer_state) { case arbwait: /* Only allow for 5 ticks, thats 10 seconds and much too * long to wait for arbitration to complete. */ if (hp->timer_ticks >= 10) { /* Enter force mode. */ do_force_mode: hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful, trying force link mode\n", hp->dev->name); hp->sw_bmcr = BMCR_SPEED100; happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); if (!is_lucent_phy(hp)) { /* 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. */ hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); } hp->timer_state = ltrywait; hp->timer_ticks = 0; restart_timer = 1; } else { /* Anything interesting happen? */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); if (hp->sw_bmsr & BMSR_ANEGCOMPLETE) { int ret; /* Just what we've been waiting for... */ ret = set_happy_link_modes(hp, tregs); if (ret) { /* Ooops, something bad happened, go to force * mode. * * XXX Broken hubs which don't support 802.3u * XXX auto-negotiation make this happen as well. */ goto do_force_mode; } /* Success, at least so far, advance our state engine. */ hp->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. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); if (hp->sw_bmsr & BMSR_LSTATUS) { /* Wheee, it's up, display the link mode in use and put * the timer to sleep. */ display_link_mode(hp, tregs); hp->timer_state = asleep; restart_timer = 0; } else { if (hp->timer_ticks >= 10) { printk(KERN_NOTICE "%s: Auto negotiation successful, link still " "not completely up.\n", hp->dev->name); hp->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. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); if (hp->timer_ticks == 1) { if (!is_lucent_phy(hp)) { /* Re-enable transceiver, we'll re-enable the transceiver next * tick, then check link state on the following tick. */ hp->sw_csconfig |= CSCONFIG_TCVDISAB; happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); } restart_timer = 1; break; } if (hp->timer_ticks == 2) { if (!is_lucent_phy(hp)) { hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB); happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); } restart_timer = 1; break; } if (hp->sw_bmsr & BMSR_LSTATUS) { /* Force mode selection success. */ display_forced_link_mode(hp, tregs); set_happy_link_modes(hp, tregs); /* XXX error? then what? */ hp->timer_state = asleep; restart_timer = 0; } else { if (hp->timer_ticks >= 4) { /* 6 seconds or so... */ int ret; ret = try_next_permutation(hp, tregs); if (ret == -1) { /* Aieee, tried them all, reset the * chip and try all over again. */ /* Let the user know... */ printk(KERN_NOTICE "%s: Link down, cable problem?\n", hp->dev->name); ret = happy_meal_init(hp, 0); if (ret) { /* ho hum... */ printk(KERN_ERR "%s: Error, cannot re-init the " "Happy Meal.\n", hp->dev->name); } return; } if (!is_lucent_phy(hp)) { hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); hp->sw_csconfig |= CSCONFIG_TCVDISAB; happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig); } hp->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", hp->dev->name); restart_timer = 0; hp->timer_ticks = 0; hp->timer_state = asleep; /* foo on you */ break; }; if (restart_timer) { hp->happy_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2 sec. */ add_timer(&hp->happy_timer); }}#define TX_RESET_TRIES 32#define RX_RESET_TRIES 32static void happy_meal_tx_reset(struct happy_meal *hp, unsigned long bregs){ int tries = TX_RESET_TRIES; HMD(("happy_meal_tx_reset: reset, ")); /* Would you like to try our SMCC Delux? */ hme_write32(hp, bregs + BMAC_TXSWRESET, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -