📄 ns9750_eth.c
字号:
*get_eth_reg_addr (NS9750_ETH_MCFG) = ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); /* reset PHY */ ns9750_mii_write (PHY_COMMON_CTRL, PHY_COMMON_CTRL_RESET); ns9750_mii_write (PHY_COMMON_CTRL, 0); /* @TODO check time */ udelay (3000); /* [2] p.70 says at least 300us reset recovery time. But go sure, it didn't worked stable at higher timer frequencies under LxNETES-2.x */ /* MII clock has been setup to default, ns9750_mii_identify_phy should work for all */ if (!ns9750_mii_identify_phy ()) { printk (KERN_ERR NS9750_DRIVER_NAME ": Unsupported PHY, aborting\n"); return 0; } /* now take the highest MDIO clock possible after detection */ *get_eth_reg_addr (NS9750_ETH_MCFG) = ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); /* PHY has been detected, so there can be no abort reason and we can finish initializing ethernet */ uiLastLinkStatus = 0xff; /* undefined */ if ((ucLinkMode & FS_EEPROM_AUTONEG_ENABLE_MASK) == FS_EEPROM_AUTONEG_DISABLE) /* use parameters defined */ ns9750_link_force (); else ns9750_link_auto_negotiate (); if (phyDetected == PHY_LXT971A) /* set LED2 to link mode */ ns9750_mii_write (PHY_LXT971_LED_CFG, PHY_LXT971_LED_CFG_LINK_ACT << PHY_LXT971_LED_CFG_SHIFT_LED2); return 1;}/*********************************************************************** * @Function: ns9750_link_force * @Return: void * @Descr: configures eth and MII to use the link mode defined in * ucLinkMode ***********************************************************************/static void ns9750_link_force (void){ unsigned short uiControl; DEBUG_FN (DEBUG_LINK); uiControl = ns9750_mii_read (PHY_COMMON_CTRL); uiControl &= ~(PHY_COMMON_CTRL_SPD_MA | PHY_COMMON_CTRL_AUTO_NEG | PHY_COMMON_CTRL_DUPLEX); uiLastLinkStatus = 0; if ((ucLinkMode & FS_EEPROM_AUTONEG_SPEED_MASK) == FS_EEPROM_AUTONEG_SPEED_100) { uiControl |= PHY_COMMON_CTRL_SPD_100; uiLastLinkStatus |= PHY_LXT971_STAT2_100BTX; } else uiControl |= PHY_COMMON_CTRL_SPD_10; if ((ucLinkMode & FS_EEPROM_AUTONEG_DUPLEX_MASK) == FS_EEPROM_AUTONEG_DUPLEX_FULL) { uiControl |= PHY_COMMON_CTRL_DUPLEX; uiLastLinkStatus |= PHY_LXT971_STAT2_DUPLEX_MODE; } ns9750_mii_write (PHY_COMMON_CTRL, uiControl); ns9750_link_print_changed (); ns9750_link_update_egcr ();}/*********************************************************************** * @Function: ns9750_link_auto_negotiate * @Return: void * @Descr: performs auto-negotation of link. ***********************************************************************/static void ns9750_link_auto_negotiate (void){ unsigned long ulStartJiffies; unsigned short uiStatus; DEBUG_FN (DEBUG_LINK); /* run auto-negotation */ /* define what we are capable of */ ns9750_mii_write (PHY_COMMON_AUTO_ADV, PHY_COMMON_AUTO_ADV_100BTXFD | PHY_COMMON_AUTO_ADV_100BTX | PHY_COMMON_AUTO_ADV_10BTFD | PHY_COMMON_AUTO_ADV_10BT | PHY_COMMON_AUTO_ADV_802_3); /* start auto-negotiation */ ns9750_mii_write (PHY_COMMON_CTRL, PHY_COMMON_CTRL_AUTO_NEG | PHY_COMMON_CTRL_RES_AUTO); /* wait for completion */ ulStartJiffies = get_ticks (); while (get_ticks () < ulStartJiffies + NS9750_MII_NEG_DELAY) { uiStatus = ns9750_mii_read (PHY_COMMON_STAT); if ((uiStatus & (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)) == (PHY_COMMON_STAT_AN_COMP | PHY_COMMON_STAT_LNK_STAT)) { /* lucky we are, auto-negotiation succeeded */ ns9750_link_print_changed (); ns9750_link_update_egcr (); return; } } DEBUG_ARGS0 (DEBUG_LINK, "auto-negotiation timed out\n"); /* ignore invalid link settings */}/*********************************************************************** * @Function: ns9750_link_update_egcr * @Return: void * @Descr: updates the EGCR and MAC2 link status after mode change or * auto-negotation ***********************************************************************/static void ns9750_link_update_egcr (void){ unsigned int unEGCR; unsigned int unMAC2; unsigned int unIPGT; DEBUG_FN (DEBUG_LINK); unEGCR = *get_eth_reg_addr (NS9750_ETH_EGCR1); unMAC2 = *get_eth_reg_addr (NS9750_ETH_MAC2); unIPGT = *get_eth_reg_addr (NS9750_ETH_IPGT) & ~NS9750_ETH_IPGT_MA; unMAC2 &= ~NS9750_ETH_MAC2_FULLD; if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) == PHY_LXT971_STAT2_DUPLEX_MODE) { unMAC2 |= NS9750_ETH_MAC2_FULLD; unIPGT |= 0x15; /* see [1] p. 339 */ } else unIPGT |= 0x12; /* see [1] p. 339 */ *get_eth_reg_addr (NS9750_ETH_MAC2) = unMAC2; *get_eth_reg_addr (NS9750_ETH_EGCR1) = unEGCR; *get_eth_reg_addr (NS9750_ETH_IPGT) = unIPGT;}/*********************************************************************** * @Function: ns9750_link_print_changed * @Return: void * @Descr: checks whether the link status has changed and if so prints * the new mode ***********************************************************************/static void ns9750_link_print_changed (void){ unsigned short uiStatus; unsigned short uiControl; DEBUG_FN (DEBUG_LINK); uiControl = ns9750_mii_read (PHY_COMMON_CTRL); if ((uiControl & PHY_COMMON_CTRL_AUTO_NEG) == PHY_COMMON_CTRL_AUTO_NEG) { /* PHY_COMMON_STAT_LNK_STAT is only set on autonegotiation */ uiStatus = ns9750_mii_read (PHY_COMMON_STAT); if (!(uiStatus & PHY_COMMON_STAT_LNK_STAT)) { printk (KERN_WARNING NS9750_DRIVER_NAME ": link down\n"); /* @TODO Linux: carrier_off */ } else { /* @TODO Linux: carrier_on */ if (phyDetected == PHY_LXT971A) { uiStatus = ns9750_mii_read (PHY_LXT971_STAT2); uiStatus &= (PHY_LXT971_STAT2_100BTX | PHY_LXT971_STAT2_DUPLEX_MODE | PHY_LXT971_STAT2_AUTO_NEG); /* mask out all uninteresting parts */ } /* other PHYs must store there link information in uiStatus as PHY_LXT971 */ } } else { /* mode has been forced, so uiStatus should be the same as the last link status, enforce printing */ uiStatus = uiLastLinkStatus; uiLastLinkStatus = 0xff; } if (uiStatus != uiLastLinkStatus) { /* save current link status */ uiLastLinkStatus = uiStatus; /* print new link status */ printk (KERN_INFO NS9750_DRIVER_NAME ": link mode %i Mbps %s duplex %s\n", (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10, (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : "half", (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : ""); }}/*********************************************************************** * the MII low level stuff ***********************************************************************//*********************************************************************** * @Function: ns9750_mii_identify_phy * @Return: 1 if supported PHY has been detected otherwise 0 * @Descr: checks for supported PHY and prints the IDs. ***********************************************************************/static char ns9750_mii_identify_phy (void){ unsigned short uiID1; unsigned short uiID2; unsigned char *szName; char cRes = 0; DEBUG_FN (DEBUG_MII); phyDetected = (PhyType) uiID1 = ns9750_mii_read (PHY_COMMON_ID1); switch (phyDetected) { case PHY_LXT971A: szName = "LXT971A"; uiID2 = ns9750_mii_read (PHY_COMMON_ID2); nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK; cRes = 1; break; case PHY_NONE: default: /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong address or reset sets the wrong NS9750_ETH_MCFG_CLKS */ uiID2 = 0; szName = "unknown"; nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; phyDetected = PHY_NONE; } printk (KERN_INFO NS9750_DRIVER_NAME ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); return cRes;}/*********************************************************************** * @Function: ns9750_mii_read * @Return: the data read from PHY register uiRegister * @Descr: the data read may be invalid if timed out. If so, a message * is printed but the invalid data is returned. * The fixed device address is being used. ***********************************************************************/static unsigned short ns9750_mii_read (unsigned short uiRegister){ DEBUG_FN (DEBUG_MII_LOW); /* write MII register to be read */ *get_eth_reg_addr (NS9750_ETH_MADR) = NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; *get_eth_reg_addr (NS9750_ETH_MCMD) = NS9750_ETH_MCMD_READ; if (!ns9750_mii_poll_busy ()) printk (KERN_WARNING NS9750_DRIVER_NAME ": MII still busy in read\n"); /* continue to read */ *get_eth_reg_addr (NS9750_ETH_MCMD) = 0; return (unsigned short) (*get_eth_reg_addr (NS9750_ETH_MRDD));}/*********************************************************************** * @Function: ns9750_mii_write * @Return: nothing * @Descr: writes the data to the PHY register. In case of a timeout, * no special handling is performed but a message printed * The fixed device address is being used. ***********************************************************************/static void ns9750_mii_write (unsigned short uiRegister, unsigned short uiData){ DEBUG_FN (DEBUG_MII_LOW); /* write MII register to be written */ *get_eth_reg_addr (NS9750_ETH_MADR) = NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; *get_eth_reg_addr (NS9750_ETH_MWTD) = uiData; if (!ns9750_mii_poll_busy ()) { printf (KERN_WARNING NS9750_DRIVER_NAME ": MII still busy in write\n"); }}/*********************************************************************** * @Function: ns9750_mii_get_clock_divisor * @Return: the clock divisor that should be used in NS9750_ETH_MCFG_CLKS * @Descr: if no clock divisor can be calculated for the * current SYSCLK and the maximum MDIO Clock, a warning is printed * and the greatest divisor is taken ***********************************************************************/static unsigned int ns9750_mii_get_clock_divisor (unsigned int unMaxMDIOClk){ struct { unsigned int unSysClkDivisor; unsigned int unClks; /* field for NS9750_ETH_MCFG_CLKS */ } PHYClockDivisors[] = { { 4, NS9750_ETH_MCFG_CLKS_4}, { 6, NS9750_ETH_MCFG_CLKS_6}, { 8, NS9750_ETH_MCFG_CLKS_8}, { 10, NS9750_ETH_MCFG_CLKS_10}, { 20, NS9750_ETH_MCFG_CLKS_20}, { 30, NS9750_ETH_MCFG_CLKS_30}, { 40, NS9750_ETH_MCFG_CLKS_40} }; int nIndexSysClkDiv; int nArraySize = sizeof (PHYClockDivisors) / sizeof (PHYClockDivisors[0]); unsigned int unClks = NS9750_ETH_MCFG_CLKS_40; /* defaults to greatest div */ DEBUG_FN (DEBUG_INIT); for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize; nIndexSysClkDiv++) { /* find first sysclock divisor that isn't higher than 2.5 MHz clock */ if (AHB_CLK_FREQ / PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <= unMaxMDIOClk) { unClks = PHYClockDivisors[nIndexSysClkDiv].unClks; break; } } DEBUG_ARGS2 (DEBUG_INIT, "Taking MDIO Clock bit mask 0x%0x for max clock %i\n", unClks, unMaxMDIOClk); /* return greatest divisor */ return unClks;}/*********************************************************************** * @Function: ns9750_mii_poll_busy * @Return: 0 if timed out otherwise the remaing timeout * @Descr: waits until the MII has completed a command or it times out * code may be interrupted by hard interrupts. * It is not checked what happens on multiple actions when * the first is still being busy and we timeout. ***********************************************************************/static unsigned int ns9750_mii_poll_busy (void){ unsigned int unTimeout = 10000; DEBUG_FN (DEBUG_MII_LOW); while (((*get_eth_reg_addr (NS9750_ETH_MIND) & NS9750_ETH_MIND_BUSY) == NS9750_ETH_MIND_BUSY) && unTimeout) unTimeout--; return unTimeout;}#endif /* CONFIG_DRIVER_NS9750_ETHERNET */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -