/** * \brief Find a valid PHY Address ( from addrStart to 31 ). * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * \param uc_start_addr Start address of the PHY to be searched. * * \return 0xFF when no valid PHY address is found. */ static uint8_t ethernet_phy_find_valid(Emac *p_emac, uint8_t uc_phy_addr, uint8_t addrStart) { uint32_t ul_value = 0; uint8_t uc_rc; uint8_t uc_cnt; uint8_t uc_phy_address = uc_phy_addr; emac_enable_management(p_emac, true); /* Check the current PHY address */ uc_rc = uc_phy_address; if (emac_phy_read(p_emac, uc_phy_addr, MII_PHYID1, &ul_value) != EMAC_OK) { } /* Find another one */ if (ul_value != MII_OUI1) { uc_rc = 0xFF; for (uc_cnt = addrStart; uc_cnt <= ETH_PHY_MAX_ADDR; uc_cnt++) { uc_phy_address = (uc_phy_address + 1) & 0x1F; emac_phy_read(p_emac, uc_phy_address, MII_PHYID1, &ul_value); if (ul_value == MII_OUI1) { uc_rc = uc_phy_address; break; } } } emac_enable_management(p_emac, false); if (uc_rc != 0xFF) { emac_phy_read(p_emac, uc_phy_address, MII_OMSS, &ul_value); } return uc_rc; }
/** * \brief Issue a SW reset to reset all registers of the PHY. * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * * \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout. */ uint8_t ethernet_phy_reset(Emac *p_emac, uint8_t uc_phy_addr) { uint32_t ul_bmcr = MII_RESET; uint8_t uc_phy_address = uc_phy_addr; uint32_t ul_timeout = ETH_PHY_TIMEOUT; uint8_t uc_rc = EMAC_TIMEOUT; emac_enable_management(p_emac, true); ul_bmcr = MII_RESET; emac_phy_write(p_emac, uc_phy_address, MII_BMCR, ul_bmcr); do { emac_phy_read(p_emac, uc_phy_address, MII_BMCR, &ul_bmcr); ul_timeout--; } while ((ul_bmcr & MII_RESET) && ul_timeout); emac_enable_management(p_emac, false); if (!ul_timeout) { uc_rc = EMAC_OK; } return (uc_rc); }
void emac_phy_dump(struct net_device *dev) { struct ocp_enet_private *fep = dev->priv; unsigned long i; uint data; printk(KERN_DEBUG " Prepare for Phy dump....\n"); for (i = 0; i < 0x1A; i++) { data = emac_phy_read(dev, fep->mii_phy_addr, i); printk(KERN_DEBUG "Phy reg 0x%lx ==> %4x\n", i, data); if (i == 0x07) i = 0x0f; } }
/** * \brief Read a Register of the PHY. * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * \param ul_phy_reg PHY Register to be read * * Return Register contents */ uint8_t ethernet_phy_read_register(Emac *p_emac, uint8_t uc_phy_addr, uint32_t ul_phy_reg,uint32_t *p_ul_reg_cont) { uint32_t ul_phy_register; uint8_t uc_phy_address, uc_speed, uc_fd; uint8_t uc_rc = EMAC_TIMEOUT; emac_enable_management(p_emac, true); uc_phy_address = uc_phy_addr; ul_phy_register = ul_phy_reg; uc_rc = emac_phy_read(p_emac, uc_phy_address, ul_phy_reg, p_ul_reg_cont); if (uc_rc != EMAC_OK) { /* Disable PHY management and start the EMAC transfer */ emac_enable_management(p_emac, false); return uc_rc; } /* Start the EMAC transfers */ emac_enable_management(p_emac, false); return uc_rc; }
/** * \brief Issue an auto negotiation of the PHY. * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout (in which case we can keep calling this). * This has been refactored as a state machine, because we must not spend much time calling this when * no network cable is connected, otherwise it slows down printing. */ uint8_t ethernet_phy_auto_negotiate(Emac *p_emac, uint8_t uc_phy_addr) { static uint8_t state = 0; static uint8_t ul_retry_count; static uint32_t ul_value; static uint32_t ul_phy_anar; switch(state) { case 0: { emac_enable_management(p_emac, true); /* Set up control register */ uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ++state; } break; case 1: { ul_value &= ~MII_AUTONEG; /* Remove auto-negotiation enable */ ul_value &= ~(MII_LOOPBACK | MII_POWER_DOWN); ul_value |= MII_ISOLATE; /* Electrically isolate PHY */ uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ++state; } break; case 2: { /* * Set the Auto_negotiation Advertisement Register. * MII advertising for Next page. * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3. */ ul_phy_anar = MII_TX_FDX | MII_TX_HDX | MII_10_FDX | MII_10_HDX | MII_AN_IEEE_802_3; uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_ANAR, ul_phy_anar); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ++state; } break; case 3: { /* Read & modify control register */ uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ++state; } break; case 4: { ul_value |= MII_SPEED_SELECT | MII_AUTONEG | MII_DUPLEX_MODE; uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ++state; } break; case 5: { /* Restart auto negotiation */ ul_value |= MII_RESTART_AUTONEG; ul_value &= ~MII_ISOLATE; uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } ul_retry_count = 0; ++state; } break; case 6: { /* Check if auto negotiation is completed */ uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMSR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } /* Done successfully */ if (!(ul_value & MII_AUTONEG_COMP)) { ++ul_retry_count; if (ul_retry_count >= 10000) // timeout check { state = 0; // start again } return EMAC_TIMEOUT; } ++state; } break; case 7: { uint32_t ul_phy_analpar; /* Get the auto negotiate link partner base page */ uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_ANLPAR, &ul_phy_analpar); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); state = 0; return uc_rc; } // The rest only happens once. so we do it all in one go uint8_t uc_fd = false; uint8_t uc_speed = false; /* Set up the EMAC link speed */ if ((ul_phy_anar & ul_phy_analpar) & MII_TX_FDX) { /* Set MII for 100BaseTX and Full Duplex */ uc_speed = true; uc_fd = true; } else if ((ul_phy_anar & ul_phy_analpar) & MII_10_FDX) { /* Set MII for 10BaseT and Full Duplex */ uc_speed = false; uc_fd = true; } else if ((ul_phy_anar & ul_phy_analpar) & MII_TX_HDX) { /* Set MII for 100BaseTX and half Duplex */ uc_speed = true; uc_fd = false; } else if ((ul_phy_anar & ul_phy_analpar) & MII_10_HDX) { /* Set MII for 10BaseT and half Duplex */ uc_speed = false; uc_fd = false; } emac_set_speed(p_emac, uc_speed); emac_enable_full_duplex(p_emac, uc_fd); emac_enable_rmii(p_emac, ETH_PHY_MODE); emac_enable_transceiver_clock(p_emac, true); emac_enable_management(p_emac, false); state = 0; // in case we get called again return EMAC_OK; } } return EMAC_TIMEOUT; // this just means that we needs to be called again }
/** * \brief Get the Link & speed settings, and automatically set up the EMAC with the * settings. * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * \param uc_apply_setting_flag Set to 0 to not apply the PHY configurations, else to apply. * * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout. */ uint8_t ethernet_phy_set_link(Emac *p_emac, uint8_t uc_phy_addr, uint8_t uc_apply_setting_flag) { uint32_t ul_stat1; uint32_t ul_stat2; uint8_t uc_phy_address, uc_speed, uc_fd; uint8_t uc_rc = EMAC_TIMEOUT; emac_enable_management(p_emac, true); uc_phy_address = uc_phy_addr; uc_rc = emac_phy_read(p_emac, uc_phy_address, MII_BMSR, &ul_stat1); if (uc_rc != EMAC_OK) { /* Disable PHY management and start the EMAC transfer */ emac_enable_management(p_emac, false); return uc_rc; } if ((ul_stat1 & MII_LINK_STATUS) == 0) { /* Disable PHY management and start the EMAC transfer */ emac_enable_management(p_emac, false); return EMAC_INVALID; } if (uc_apply_setting_flag == 0) { /* Disable PHY management and start the EMAC transfer */ emac_enable_management(p_emac, false); return uc_rc; } // Re-configure Link speed uc_rc = emac_phy_read(p_emac, uc_phy_address, MII_PC1, &ul_stat2); if (uc_rc != EMAC_OK) { //Disable PHY management and start the EMAC transfer emac_enable_management(p_emac, false); return uc_rc; } if ((ul_stat1 & MII_100BASE_TX_FD) && (ul_stat2 & MII_OMI_100_FD)) { // Set EMAC for 100BaseTX and Full Duplex uc_speed = true; uc_fd = true; } else if ((ul_stat1 & MII_10BASE_T_FD) && (ul_stat2 & MII_OMI_10_FD)) { // Set MII for 10BaseT and Full Duplex uc_speed = false; uc_fd = true; } else if ((ul_stat1 & MII_100BASE_TX_HD) && (ul_stat2 & MII_OMI_100_HD)) { // Set MII for 100BaseTX and Half Duplex uc_speed = true; uc_fd = false; } else if ((ul_stat1 & MII_10BASE_T_HD) && (ul_stat2 & MII_OMI_10_HD)) { // Set MII for 10BaseT and Half Duplex uc_speed = false; uc_fd = false; } emac_set_speed(p_emac, uc_speed); emac_enable_full_duplex(p_emac, uc_fd); /* Start the EMAC transfers */ emac_enable_management(p_emac, false); return uc_rc; }
/** * \brief Issue an auto negotiation of the PHY. * * \param p_emac Pointer to the EMAC instance. * \param uc_phy_addr PHY address. * * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout. */ uint8_t ethernet_phy_auto_negotiate(Emac *p_emac, uint8_t uc_phy_addr) { uint32_t ul_retry_max = ETH_PHY_RETRY_MAX; uint32_t ul_value; uint32_t ul_phy_anar; uint32_t ul_phy_analpar; uint32_t ul_retry_count = 0; uint8_t uc_fd = 0; uint8_t uc_speed = 0; uint8_t uc_rc = EMAC_TIMEOUT; emac_enable_management(p_emac, true); /* Set up control register */ uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } ul_value &= ~MII_AUTONEG; /* Remove auto-negotiation enable */ ul_value &= ~(MII_LOOPBACK | MII_POWER_DOWN); ul_value |= MII_ISOLATE; /* Electrically isolate PHY */ uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* * Set the Auto_negotiation Advertisement Register. * MII advertising for Next page. * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3. */ ul_phy_anar = MII_TX_FDX | MII_TX_HDX | MII_10_FDX | MII_10_HDX | MII_AN_IEEE_802_3; uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_ANAR, ul_phy_anar); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* Read & modify control register */ uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } ul_value |= MII_SPEED_SELECT | MII_AUTONEG | MII_DUPLEX_MODE; uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* Restart auto negotiation */ ul_value |= MII_RESTART_AUTONEG; ul_value &= ~MII_ISOLATE; uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* Check if auto negotiation is completed */ while (1) { uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMSR, &ul_value); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* Done successfully */ if (ul_value & MII_AUTONEG_COMP) { break; } /* Timeout check */ if (ul_retry_max) { if (++ul_retry_count >= ul_retry_max) { return EMAC_TIMEOUT; } } } /* Get the auto negotiate link partner base page */ uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_ANLPAR, &ul_phy_analpar); if (uc_rc != EMAC_OK) { emac_enable_management(p_emac, false); return uc_rc; } /* Set up the EMAC link speed */ if ((ul_phy_anar & ul_phy_analpar) & MII_TX_FDX) { /* Set MII for 100BaseTX and Full Duplex */ uc_speed = true; uc_fd = true; } else if ((ul_phy_anar & ul_phy_analpar) & MII_10_FDX) { /* Set MII for 10BaseT and Full Duplex */ uc_speed = false; uc_fd = true; } else if ((ul_phy_anar & ul_phy_analpar) & MII_TX_HDX) { /* Set MII for 100BaseTX and half Duplex */ uc_speed = true; uc_fd = false; } else if ((ul_phy_anar & ul_phy_analpar) & MII_10_HDX) { /* Set MII for 10BaseT and half Duplex */ uc_speed = false; uc_fd = false; } emac_set_speed(p_emac, uc_speed); emac_enable_full_duplex(p_emac, uc_fd); emac_enable_rmii(p_emac, ETH_PHY_MODE); emac_enable_transceiver_clock(p_emac, true); emac_enable_management(p_emac, false); return uc_rc; }