📄 ns7520_eth.c
字号:
* @Descr: performs auto-negotation of link. ***********************************************************************/static void ns7520_link_auto_negotiate(void){ unsigned long ulStartJiffies; unsigned short uiStatus; DEBUG_FN(DEBUG_LINK); /* run auto-negotation */ /* define what we are capable of */ ns7520_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 */ ns7520_mii_write(PHY_COMMON_CTRL, PHY_COMMON_CTRL_AUTO_NEG | PHY_COMMON_CTRL_RES_AUTO); /* wait for completion */ ulStartJiffies = get_timer(0); while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) { uiStatus = ns7520_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 */ ns7520_link_print_changed(); ns7520_link_update_egcr(); return; } } DEBUG_ARGS0(DEBUG_LINK, "auto-negotiation timed out\n"); /* ignore invalid link settings */}/*********************************************************************** * @Function: ns7520_link_update_egcr * @Return: void * @Descr: updates the EGCR and MAC2 link status after mode change or * auto-negotation ***********************************************************************/static void ns7520_link_update_egcr(void){ unsigned int unEGCR; unsigned int unMAC2; unsigned int unIPGT; DEBUG_FN(DEBUG_LINK); unEGCR = *get_eth_reg_addr(NS7520_ETH_EGCR); unMAC2 = *get_eth_reg_addr(NS7520_ETH_MAC2); unIPGT = *get_eth_reg_addr(NS7520_ETH_IPGT) & ~NS7520_ETH_IPGT_IPGT; unEGCR &= ~NS7520_ETH_EGCR_EFULLD; unMAC2 &= ~NS7520_ETH_MAC2_FULLD; if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) == PHY_LXT971_STAT2_DUPLEX_MODE) { unEGCR |= NS7520_ETH_EGCR_EFULLD; unMAC2 |= NS7520_ETH_MAC2_FULLD; unIPGT |= 0x15; /* see [1] p. 167 */ } else unIPGT |= 0x12; /* see [1] p. 167 */ *get_eth_reg_addr(NS7520_ETH_MAC2) = unMAC2; *get_eth_reg_addr(NS7520_ETH_EGCR) = unEGCR; *get_eth_reg_addr(NS7520_ETH_IPGT) = unIPGT;}/*********************************************************************** * @Function: ns7520_link_print_changed * @Return: void * @Descr: checks whether the link status has changed and if so prints * the new mode ***********************************************************************/static void ns7520_link_print_changed(void){ unsigned short uiStatus; unsigned short uiControl; DEBUG_FN(DEBUG_LINK); uiControl = ns7520_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 = ns7520_mii_read(PHY_COMMON_STAT); if (!(uiStatus & PHY_COMMON_STAT_LNK_STAT)) { printk(KERN_WARNING NS7520_DRIVER_NAME ": link down\n"); /* @TODO Linux: carrier_off */ } else { /* @TODO Linux: carrier_on */ if (phyDetected == PHY_LXT971A) { uiStatus = ns7520_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 NS7520_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: ns7520_mii_identify_phy * @Return: 1 if supported PHY has been detected otherwise 0 * @Descr: checks for supported PHY and prints the IDs. ***********************************************************************/static char ns7520_mii_identify_phy(void){ unsigned short uiID1; unsigned short uiID2; unsigned char *szName; char cRes = 0; DEBUG_FN(DEBUG_MII); phyDetected = (PhyType) uiID1 = ns7520_mii_read(PHY_COMMON_ID1); switch (phyDetected) { case PHY_LXT971A: szName = "LXT971A"; uiID2 = ns7520_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 NS7520_ETH_MCFG_CLKS */ uiID2 = 0; szName = "unknown"; nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; phyDetected = PHY_NONE; } printk(KERN_INFO NS7520_DRIVER_NAME ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); return cRes;}/*********************************************************************** * @Function: ns7520_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 ns7520_mii_read(unsigned short uiRegister){ DEBUG_FN(DEBUG_MII_LOW); /* write MII register to be read */ *get_eth_reg_addr(NS7520_ETH_MADR) = CONFIG_PHY_ADDR << 8 | uiRegister; *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; if (!ns7520_mii_poll_busy()) printk(KERN_WARNING NS7520_DRIVER_NAME ": MII still busy in read\n"); /* continue to read */ *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; return (unsigned short) (*get_eth_reg_addr(NS7520_ETH_MRDD));}/*********************************************************************** * @Function: ns7520_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 ns7520_mii_write(unsigned short uiRegister, unsigned short uiData){ DEBUG_FN(DEBUG_MII_LOW); /* write MII register to be written */ *get_eth_reg_addr(NS7520_ETH_MADR) = CONFIG_PHY_ADDR << 8 | uiRegister; *get_eth_reg_addr(NS7520_ETH_MWTD) = uiData; if (!ns7520_mii_poll_busy()) { printf(KERN_WARNING NS7520_DRIVER_NAME ": MII still busy in write\n"); }}/*********************************************************************** * @Function: ns7520_mii_get_clock_divisor * @Return: the clock divisor that should be used in NS7520_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 ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk){ struct { unsigned int unSysClkDivisor; unsigned int unClks; /* field for NS7520_ETH_MCFG_CLKS */ } PHYClockDivisors[] = { { 4, NS7520_ETH_MCFG_CLKS_4}, { 6, NS7520_ETH_MCFG_CLKS_6}, { 8, NS7520_ETH_MCFG_CLKS_8}, { 10, NS7520_ETH_MCFG_CLKS_10}, { 14, NS7520_ETH_MCFG_CLKS_14}, { 20, NS7520_ETH_MCFG_CLKS_20}, { 28, NS7520_ETH_MCFG_CLKS_28} }; int nIndexSysClkDiv; int nArraySize = sizeof(PHYClockDivisors) / sizeof(PHYClockDivisors[0]); unsigned int unClks = NS7520_ETH_MCFG_CLKS_28; /* 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 (NETARM_XTAL_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: ns7520_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 ns7520_mii_poll_busy(void){ unsigned int unTimeout = 1000; DEBUG_FN(DEBUG_MII_LOW); while (((*get_eth_reg_addr(NS7520_ETH_MIND) & NS7520_ETH_MIND_BUSY) == NS7520_ETH_MIND_BUSY) && unTimeout) unTimeout--; return unTimeout;}/* ---------------------------------------------------------------------------- * Net+ARM ethernet MII functionality. */#if defined(CONFIG_MII)/** * Maximum MII address we support */#define MII_ADDRESS_MAX (31)/** * Maximum MII register address we support */#define MII_REGISTER_MAX (31)/** * Ethernet MII interface return values for public functions. */enum mii_status { MII_STATUS_SUCCESS = 0, MII_STATUS_FAILURE = 1,};/** * Read a 16-bit value from an MII register. */extern int ns7520_miiphy_read(char *devname, unsigned char const addr, unsigned char const reg, unsigned short *const value){ int ret = MII_STATUS_FAILURE; /* Parameter checks */ if (addr > MII_ADDRESS_MAX) { ERROR(("invalid addr, 0x%02X", addr)); goto miiphy_read_failed_0; } if (reg > MII_REGISTER_MAX) { ERROR(("invalid reg, 0x%02X", reg)); goto miiphy_read_failed_0; } if (value == NULL) { ERROR(("NULL value")); goto miiphy_read_failed_0; } DEBUG_FN(DEBUG_MII_LOW); /* write MII register to be read */ *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; if (!ns7520_mii_poll_busy()) printk(KERN_WARNING NS7520_DRIVER_NAME ": MII still busy in read\n"); /* continue to read */ *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; *value = (*get_eth_reg_addr(NS7520_ETH_MRDD)); ret = MII_STATUS_SUCCESS; /* Fall through */ miiphy_read_failed_0: return (ret);}/** * Write a 16-bit value to an MII register. */extern int ns7520_miiphy_write(char *devname, unsigned char const addr, unsigned char const reg, unsigned short const value){ int ret = MII_STATUS_FAILURE; /* Parameter checks */ if (addr > MII_ADDRESS_MAX) { ERROR(("invalid addr, 0x%02X", addr)); goto miiphy_write_failed_0; } if (reg > MII_REGISTER_MAX) { ERROR(("invalid reg, 0x%02X", reg)); goto miiphy_write_failed_0; } /* write MII register to be written */ *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; *get_eth_reg_addr(NS7520_ETH_MWTD) = value; if (!ns7520_mii_poll_busy()) { printf(KERN_WARNING NS7520_DRIVER_NAME ": MII still busy in write\n"); } ret = MII_STATUS_SUCCESS; /* Fall through */ miiphy_write_failed_0: return (ret);}#endif /* defined(CONFIG_MII) */#endif /* CONFIG_DRIVER_NS7520_ETHERNET */int ns7520_miiphy_initialize(bd_t *bis){#if defined(CONFIG_DRIVER_NS7520_ETHERNET)#if defined(CONFIG_MII) miiphy_register("ns7520phy", ns7520_miiphy_read, ns7520_miiphy_write);#endif#endif return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -