📄 sunhme.c
字号:
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->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 { 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;}static int happy_meal_init(struct happy_meal *hp, int from_irq);static void happy_meal_timer(unsigned long data){ struct happy_meal *hp = (struct happy_meal *) data; struct hmeal_tcvregs *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("%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); /* 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("%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) { /* 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) { 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("%s: Link down, cable problem?\n", hp->dev->name); ret = happy_meal_init(hp, 0); if(ret) { /* ho hum... */ printk("%s: Error, cannot re-init the " "Happy Meal.\n", hp->dev->name); } return; } 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("%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 inline void happy_meal_tx_reset(struct happy_meal *hp, struct hmeal_bigmacregs *bregs){ int tries = TX_RESET_TRIES; HMD(("happy_meal_tx_reset: reset, ")); /* Would you like to try our SMCC Delux? */ hme_write32(hp, &bregs->tx_swreset, 0); while((hme_read32(hp, &bregs->tx_swreset) & 1) && --tries) udelay(20); /* Lettuce, tomato, buggy hardware (no extra charge)? */ if(!tries) printk("happy meal: Transceiver BigMac ATTACK!"); /* Take care. */ HMD(("done\n"));}static inline void happy_meal_rx_reset(struct happy_meal *hp, struct hmeal_bigmacregs *bregs){ int tries = RX_RESET_TRIES; HMD(("happy_meal_rx_reset: reset, ")); /* We have a special on GNU/Viking hardware bugs today. */ hme_write32(hp, &bregs->rx_swreset, 0); while((hme_read32(hp, &bregs->rx_swreset) & 1) && --tries) udelay(20); /* Will that be all? */ if(!tries) printk("happy meal: Receiver BigMac ATTACK!"); /* Don't forget your vik_1137125_wa. Have a nice day. */ HMD(("done\n"));}#define STOP_TRIES 16static inline void happy_meal_stop(struct happy_meal *hp, struct hmeal_gregs *gregs){ int tries = STOP_TRIES; HMD(("happy_meal_stop: reset, ")); /* We're consolidating our STB products, it's your lucky day. */ hme_write32(hp, &gregs->sw_reset, GREG_RESET_ALL); while(hme_read32(hp, &gregs->sw_reset) && --tries) udelay(20); /* Come back next week when we are "Sun Microelectronics". */ if(!tries) printk("happy meal: Fry guys."); /* Remember: "Different name, same old buggy as shit hardware." */ HMD(("done\n"));}static void happy_meal_get_counters(struct happy_meal *hp, struct hmeal_bigmacregs *bregs){ struct net_device_stats *stats = &hp->net_stats; stats->rx_crc_errors += hme_read32(hp, &bregs->rcrce_ctr); hme_write32(hp, &bregs->rcrce_ctr, 0); stats->rx_frame_errors += hme_read32(hp, &bregs->unale_ctr); hme_write32(hp, &bregs->unale_ctr, 0); stats->rx_length_errors += hme_read32(hp, &bregs->gle_ctr); hme_write32(hp, &bregs->gle_ctr, 0); stats->tx_aborted_errors += hme_read32(hp, &bregs->ex_ctr); stats->collisions += (hme_read32(hp, &bregs->ex_ctr) + hme_read32(hp, &bregs->lt_ctr)); hme_write32(hp, &bregs->ex_ctr, 0); hme_write32(hp, &bregs->lt_ctr, 0);}static inline void happy_meal_poll_start(struct happy_meal *hp, struct hmeal_tcvregs *tregs){ unsigned long 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->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->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 = tregs->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->int_mask, 0xfffb); else hme_write32(hp, &tregs->int_mask, 0xfff9); ASD(("done\n"));}static inline void happy_meal_poll_stop(struct happy_meal *hp, struct hmeal_tcvregs *tregs){ ASD(("happy_meal_poll_stop: ")); /* If polling disabled or not polling already, nothing to do. */ if((hp->happy_flags & (HFLAG_POLLENABLE | HFLAG_POLL)) != (HFLAG_POLLENABLE | HFLAG_POLL)) { HMD(("not polling, return\n")); return; } /* Shut up the MIF. */ ASD(("were polling, mif ints off, ")); hme_write32(hp, &tregs->int_mask, 0xffff); /* Turn off polling. */ ASD(("polling off, ")); hme_write32(hp, &tregs->cfg, hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_PENABLE)); /* We are no longer polling. */ hp->happy_flags &= ~(HFLAG_POLL); /* Let the bits set. */ udelay(200); ASD(("done\n"));}/* Only Sun can take such nice parts and fuck up the programming interface * like this. Good job guys... */#define TCVR_RESET_TRIES 16 /* It should reset quickly */#define TCVR_UNISOLATE_TRIES 32 /* Dis-isolation can take longer. */static int happy_meal_tcvr_reset(struct happy_meal *hp, struct hmeal_tcvregs *tregs){ unsigned long tconfig; int result, tries = TCVR_RESET_TRIES; tconfig = hme_read32(hp, &tregs->cfg); ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig)); if(hp->tcvr_type == external) { ASD(("external<")); hme_write32(hp, &tregs->cfg, tconfig & ~(TCV_CFG_PSELECT)); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if(result == TCVR_FAILURE) { ASD(("phyread_fail>\n")); return -1; } ASD(("phyread_ok,PSELECT>")); hme_write32(hp, &tregs->cfg, tconfig | TCV_CFG_PSELECT); hp->tcvr_type = external; hp->paddr = TCV_PADDR_ETX; } else { if(tconfig & TCV_CFG_MDIO1) { ASD(("internal<PSELECT,")); hme_write32(hp, &tregs->cfg, (tconfig | TCV_CFG_PSELECT)); ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if(result == TCVR_FAILURE) { ASD(("phyread_fail>\n")); return -1; } ASD(("phyread_ok,~PSELECT>")); hme_write32(hp, &tregs->cfg, (tconfig & ~(TCV_CFG_PSELECT))); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; } } ASD(("BMCR_RESET ")); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, BMCR_RESET); while(--tries) { result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if(result == TCVR_FAILURE) return -1; hp->sw_bmcr = result; if(!(result & BMCR_RESET)) break; udelay(20); } if(!tries) { ASD(("BMCR RESET FAILED!\n")); return -1; } ASD(("RESET_OK\n")); /* Get fresh copies of the PHY registers. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR); hp->sw_physid1 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID1); hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, DP83840_PHYSID2); hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, DP83840_ADVERTISE); ASD(("UNISOLATE")); hp->sw_bmcr &= ~(BMCR_ISOLATE); happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr); tries = TCVR_UNISOLATE_TRIES; while(--tries) { result = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR); if(result == TCVR_FAILURE) return -1; if(!(result & BMCR_ISOLATE)) break; udelay(20); } if(!tries) { ASD((" FAILED!\n")); return -1; } ASD((" SUCCESS and CSCONFIG_DFBYPASS\n")); result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, (result | CSCONFIG_DFBYPASS));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -