📄 pmcmspeth.c
字号:
break; } } } else { /* no acceptable PROM settings so we have to make something up */ if (lp->option & MSP_OPT_SWITCH) { /* commandline set us to a switch so no phy settings required. * consider changing this later so that we can access the registers * in the switch through MDIO etc. Could be autoprobed too. */ } else { /* search through the list of phys and use the first unassigned one */ for (phyindex = 0; phyindex < MSPETH_MAX_UNITS; phyindex++) { if (!phy_ctrl[phyindex].assigned) { lp->phyindex = phyindex; phy_ctrl[phyindex].assigned = true; break; } } } /* rudimentary error checking */ if (phystr != NULL) { printk(KERN_INFO "MSPETH (init_phyaddr) %s: bad phyaddr value %s\n", dev->name, phystr); } }}/************************************************************************** * Scan the environment to get the kernel command line options for ethernet */static void __init mspeth_init_cmdline(struct net_device *dev){ struct mspeth_local *lp = (struct mspeth_local *) dev->priv; int index; int unit; char c = ' '; char command_line[COMMAND_LINE_SIZE]; char *ptr = &command_line[0]; char *ethptr = NULL; /* default options */ lp->option = MSP_OPT_AUTO; /* scan the command line looking for static configurations */ strcpy(command_line, prom_getcmdline()); while (c != '\0') { if (c != ' ' || memcmp(ptr, "ip=", 3) != 0) { c = *ptr++; continue; } c = *ptr++; index = 0; unit = -1; while (index < 8) { c = *ptr++; if (c == '\0' || c == ' ') { if (index == 7) { index++; *--ptr = '\0'; ptr++; } break; } if (c == ':') { index++; if (index == 5) { if (memcmp(ptr, "eth", 3) != 0) { break; } ethptr = &ptr[3]; ptr = ethptr; } if (index == 6) { *--ptr = '\0'; ptr++; unit = simple_strtol(ethptr, NULL, 0); } if (index == 7) { ethptr = ptr; } if (index == 8) { *--ptr = '\0'; ptr++; } } } if (index < 8 || unit < 0 || unit > MSPETH_MAX_UNITS) continue; /* check to see if this our option and parse them out */ if (lp->unit == unit) { if (memcmp(ethptr, "100fs", 5) == 0) { /* 100M full-duplex switch */ lp->option = MSP_OPT_100M | MSP_OPT_FDUP | MSP_OPT_SWITCH; } else if (memcmp(ethptr, "100hs", 5) == 0) { /* 100M half-duplex switch */ lp->option = MSP_OPT_100M | MSP_OPT_HDUP | MSP_OPT_SWITCH; } else if (memcmp(ethptr, "10fs", 4) == 0) { /* 10M full-duplex switch */ lp->option = MSP_OPT_10M | MSP_OPT_FDUP | MSP_OPT_SWITCH; } else if (memcmp(ethptr, "10hs", 4) == 0) { /* 10M half-duplex switch */ lp->option = MSP_OPT_100M | MSP_OPT_HDUP | MSP_OPT_SWITCH; } else if (memcmp(ethptr, "100f", 4) == 0) /* 100M full-duplex */ lp->option = MSP_OPT_100M | MSP_OPT_FDUP; else if (memcmp(ethptr, "100h", 4) == 0) /* 100M half-duplex */ lp->option = MSP_OPT_100M | MSP_OPT_HDUP; else if (memcmp(ethptr, "10f", 3) == 0) /* 10M full-duplex */ lp->option = MSP_OPT_10M | MSP_OPT_FDUP; else if (memcmp(ethptr, "10h", 3) == 0) /* 100M half-duplex */ lp->option = MSP_OPT_10M | MSP_OPT_HDUP; } if (mspeth_debug > 0) printk(KERN_INFO "MSPETH (init_cmdline)%s: boot = %s, option = %02x\n", dev->name, command_line, lp->option); }}/************************************************************************** * Probe the hardware and fill out the array of PHY control elements */static void mspeth_phyprobe(){ u32 reg1; int hwindex, phyindex, phyaddr; /* scan the phys and build the phy_ctrl array */ for (phyindex = 0, hwindex = 0; hwindex < MSPETH_MAX_UNITS; hwindex++) { /* if the hardware actually exists then ... */ if (identify_enet(hwindex) != FEATURE_NOEXIST) { for (phyaddr = 0; phyaddr < MD_MAX_PHY; phyaddr++) { phy_ctrl[phyindex].hwindex = hwindex; phy_ctrl[phyindex].phyaddr = phyaddr; phy_ctrl[phyindex].lock = SPIN_LOCK_UNLOCKED; phy_ctrl[phyindex].assigned = false; reg1 = read_mspphyreg(phyindex, MII_BMSR); if (reg1 != 0 && reg1 != 0xffff && (reg1 & BMSR_MEDIAMASK) != 0) { printk(KERN_INFO "MSPETH (phyprobe): phyindex = %d, phyaddr = %d, hwindex = %d\n", phyindex, phyaddr, hwindex); if (read_mspphyreg (phyindex, MII_BMSR) & BMSR_LSTATUS) phy_ctrl[phyindex].linkup = true; else phy_ctrl[phyindex].linkup = false; phyindex++; /* Show MII status register */ if (MSPETH_DEBUG > 1) { printk(KERN_INFO "MSPETH (phyprobe): MII status register = %08x\n", reg1); } } } } }}/************************************************************************** * Reset the phy */static void mspeth_phy_reset(struct net_device *dev){ struct mspeth_local *lp = (struct mspeth_local *) dev->priv; u32 id0, id1; /* reset the phy */ write_mspphyreg(BMCR_RESET, lp->phyindex, MII_BMCR); while ((read_mspphyreg(lp->phyindex, MII_BMCR) & BMCR_RESET) != 0) udelay(100); if (mspeth_debug > 0) { printk(KERN_INFO "MSPETH (phy_chip_init) %s: phyindex = %d\n", dev->name, lp->phyindex); id0 = read_mspphyreg(lp->phyindex, MII_PHYSID1); id1 = read_mspphyreg(lp->phyindex, MII_PHYSID2); printk(KERN_INFO "MSPETH (phy_chip_init) %s: PHY ID %04x %04x\n", dev->name, id0, id1); printk(KERN_INFO "MSPETH (phy_chip_init) %s: speed = %d, duplex = %s\n", dev->name, lp->speed, (lp->fullduplex ? "FULL" : "HALF")); }}/************************************************************************** * Initialize the phy -- set the speed and duplex. Wait for autonegotiation * to complete. If it doesn't then force the renegotiation. If *that* fails * then reset the phy and try again. Finally just make some assumptions. If * autonegotiation is disabled then just force values */static void mspeth_phy_init(struct net_device *dev){ struct mspeth_local *lp = (struct mspeth_local *) dev->priv; struct mspeth_regs *tr = (struct mspeth_regs *) dev->base_addr; u32 ctl, neg_result; int i; enum { AUTONEG, AUTONEG_FORCE, PHYRESET } auto_status; char *link_type; char *link_stat; /* check for defaults and autonegotiate */ if (lp->option == MSP_OPT_AUTO) { /* make sure the autonegotiation is enabled and then wait for the * autonegotion to complete */ link_type = "Autoneg"; for (auto_status = AUTONEG; auto_status <= PHYRESET; auto_status++) { /* run through all the various autonegotion methods until we fail */ switch (auto_status) { case AUTONEG: write_mspphyreg(BMCR_ANENABLE, lp->phyindex, MII_BMCR); break; case AUTONEG_FORCE: printk(KERN_WARNING "MSPETH %s: Forcing autonegotiation\n", dev->name); write_mspphyreg(BMCR_ANENABLE | BMCR_ANRESTART, lp->phyindex, MII_BMCR); break; case PHYRESET: printk(KERN_WARNING "MSPETH %s: Resetting phy\n", dev->name); write_mspphyreg(BMCR_RESET, lp->phyindex, MII_BMCR); while ((read_mspphyreg (lp->phyindex, MII_BMCR) & BMCR_RESET) != 0) udelay(100); write_mspphyreg(BMCR_ANENABLE | BMCR_ANRESTART, lp->phyindex, MII_BMCR); break; default: printk(KERN_DEBUG "MSPETH %s: Unknown autonegotation mode??\n", dev->name); return; } /* ok. Autoneg should be underway, so lets do the loop thingy and wait * for it to exit */ printk(KERN_INFO "MSPETH %s: Auto Negotiation...", dev->name); for (i = 0; i < 2000 && !(read_mspphyreg(lp->phyindex, MII_BMSR) & BMSR_ANEGCOMPLETE); i++) { mdelay(1); } if (i == 2000) { /* autonegotiation failed to complete so go to next level of * negotiation. */ printk(" failed.\n"); continue; } /* must have succeeded so we can set the speed etc */ printk(" done.\n"); neg_result = read_mspphyreg(lp->phyindex, MII_LPA); if (neg_result & (LPA_100FULL | LPA_100HALF)) lp->speed = 100; else lp->speed = 10; if (neg_result & (LPA_100FULL | LPA_10FULL)) lp->fullduplex = true; else lp->fullduplex = false; break; } /* check to see if *everything* failed and try to set some default values */ if (auto_status > PHYRESET) { printk ("Autonegotion failed. Assume 10Mbps, half-duplex\n"); link_type = "Autoneg (failed)"; lp->speed = 10; lp->fullduplex = false; } } else { /* if speed and duplex are statically configured then set that here */ link_type = "Static"; ctl = 0; if (lp->option & MSP_OPT_100M) { lp->speed = 100; ctl |= BMCR_SPEED100; } else { lp->speed = 10; ctl &= ~BMCR_SPEED100; } if (lp->option & MSP_OPT_FDUP) { lp->fullduplex = true; ctl |= BMCR_FULLDPLX; } else { lp->fullduplex = false; ctl &= ~BMCR_FULLDPLX; } if (!(lp->option & MSP_OPT_SWITCH)) write_mspphyreg(ctl, lp->phyindex, MII_BMCR); } if (!(lp->option & MSP_OPT_SWITCH)) { /* wait for a little bit to see if we've got a carrier -- don't go crazy though */ printk(KERN_INFO "MSPETH %s: Waiting for carrier ...", dev->name); for (i = 0; i < 2000 && !(read_mspphyreg(lp->phyindex, MII_BMSR) & BMSR_LSTATUS); i++) { mdelay(1); } if (i == 2000) { printk(" no carrier.\n"); phy_ctrl[lp->phyindex].linkup = false; netif_carrier_off(dev); link_stat = "Link down"; } else { printk(" carrier detected.\n"); phy_ctrl[lp->phyindex].linkup = true; netif_carrier_on(dev); link_stat = "Link up"; } } else { /* Assume we're connected. If we're using a switch we always should be.*/ printk(KERN_INFO "MSPETH %s: Using internal switch\n", dev->name); phy_ctrl[lp->phyindex].linkup = true; //netif_carrier_off(dev); link_stat = "Link up"; } /* configure the MAC with the duplex setting -- it doesn't care about speed */ if (lp->fullduplex) { write_mspreg(read_mspreg(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl); } else { write_mspreg(read_mspreg(&tr->MAC_Ctl) & ~MAC_FullDup, &tr->MAC_Ctl); } if (mspeth_debug > 2) { printk(KERN_INFO "MSPETH %s: PHY control register: 0x%08x\n", dev->name, read_mspphyreg(lp->phyindex, MII_BMCR)); printk(KERN_INFO "MSPETH %s: PHY status register: 0x%08x\n", dev->name, read_mspphyreg(lp->phyindex, MII_BMSR)); } printk(KERN_INFO "MSPETH %s: %s, %s, linkspeed %dMbps, %s Duplex\n", dev->name, link_type, link_stat, lp->speed, (lp->fullduplex) ? "Full" : "Half");}/************************************************************************** * Check the link for a carrier when the link check timer expires. If the * link is down and it has been down for a while (at least 1 timer delay) * then change the upper layer link state to match. Do a soft-restart if * we're bringing the link up. Reschedule the timer of course. */static void mspeth_link_check(unsigned long data){ struct net_device *dev = (struct net_device *) data; struct mspeth_local *lp = (struct mspeth_local *) dev->priv; enum { LINKGOOD, LINKBAD, LINKUNKNOWN } linkstatus; /* check the current link status */ linkstatus = LINKUNKNOWN; if (read_mspphyreg(lp->phyindex, MII_BMSR) & BMSR_LSTATUS) { if (phy_ctrl[lp->phyindex].linkup) linkstatus = LINKGOOD; phy_ctrl[lp->phyindex].linkup = true; } else { if (!phy_ctrl[lp->phyindex].linkup) linkstatus = LINKBAD; phy_ctrl[lp->phyindex].linkup = false; } /* check the upper layer status */ if (netif_carrier_ok(dev)) { /* upper layer thinks we're ok but the link is bad, so * take the link down */ if (linkstatus == LINKBAD) { printk(KERN_INFO "%s: NO LINK DETECTED\n", dev->name); netif_stop_queue(dev); netif_carrier_off(dev); } } else { /* the upper layer thinks we're broken but we've recovered so * do a soft-restart and bring the link back up */ if (linkstatus == LINKGOOD) { printk(KERN_INFO "%s: LINK DETECTED\n", dev->name); mspeth_soft_restart(dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -