/********************************************************************** * $Id$ lpc_phy_dp83848.c 2011-11-20 *//** * @file lpc_phy_dp83848.c * @brief DP83848C PHY status and control. * @version 1.0 * @date 20 Nov. 2011 * @author NXP MCU SW Application Team * * Copyright(C) 2011, NXP Semiconductor * All rights reserved. * *********************************************************************** * Software that is described herein is for illustrative purposes only * which provides customers with programming information regarding the * products. This software is supplied "AS IS" without any warranties. * NXP Semiconductors assumes no responsibility or liability for the * use of the software, conveys no license or title under any patent, * copyright, or mask work right to the product. NXP Semiconductors * reserves the right to make changes in the software without * notification. NXP Semiconductors also make no representation or * warranty that such application will be suitable for the specified * use without further testing or modification. **********************************************************************/ #include "lwip/opt.h" #include "lwip/err.h" #include "lwip/tcpip.h" #include "lwip/snmp.h" #include "lpc_emac_config.h" #include "lpc_phy.h" #include "lpc17xx_emac.h" /** @defgroup dp83848_phy PHY status and control for the DP83848. * @ingroup lwip_phy * * Various functions for controlling and monitoring the status of the * DP83848 PHY. In polled (standalone) systems, the PHY state must be * monitored as part of the application. In a threaded (RTOS) system, * the PHY state is monitored by the PHY handler thread. The MAC * driver will not transmit unless the PHY link is active. * @{ */ /** \brief DP83848 PHY register offsets */ #define DP8_BMCR_REG 0x0 /**< Basic Mode Control Register */ #define DP8_BMSR_REG 0x1 /**< Basic Mode Status Reg */ #define DP8_IDR1_REG 0x2 /**< Basic Mode Status Reg */ #define DP8_IDR2_REG 0x3 /**< Basic Mode Status Reg */ #define DP8_ANADV_REG 0x4 /**< Auto_Neg Advt Reg */ #define DP8_ANLPA_REG 0x5 /**< Auto_neg Link Partner Ability Reg */ #define DP8_ANEEXP_REG 0x6 /**< Auto-neg Expansion Reg */ #define DP8_PHY_STAT_REG 0x10 /**< PHY Status Register */ #define DP8_PHY_INT_CTL_REG 0x11 /**< PHY Interrupt Control Register */ #define DP8_PHY_RBR_REG 0x17 /**< PHY RMII and Bypass Register */ #define DP8_PHY_STS_REG 0x19 /**< PHY Status Register */ #define DP8_PHY_SCSR_REG 0x1f /**< PHY Special Control/Status Register (LAN8720) */ /** \brief DP83848 Control register definitions */ #define DP8_RESET (1 << 15) /**< 1= S/W Reset */ #define DP8_LOOPBACK (1 << 14) /**< 1=loopback Enabled */ #define DP8_SPEED_SELECT (1 << 13) /**< 1=Select 100MBps */ #define DP8_AUTONEG (1 << 12) /**< 1=Enable auto-negotiation */ #define DP8_POWER_DOWN (1 << 11) /**< 1=Power down PHY */ #define DP8_ISOLATE (1 << 10) /**< 1=Isolate PHY */ #define DP8_RESTART_AUTONEG (1 << 9) /**< 1=Restart auto-negoatiation */ #define DP8_DUPLEX_MODE (1 << 8) /**< 1=Full duplex mode */ #define DP8_COLLISION_TEST (1 << 7) /**< 1=Perform collsion test */ /** \brief DP83848 Status register definitions */ #define DP8_100BASE_T4 (1 << 15) /**< T4 mode */ #define DP8_100BASE_TX_FD (1 << 14) /**< 100MBps full duplex */ #define DP8_100BASE_TX_HD (1 << 13) /**< 100MBps half duplex */ #define DP8_10BASE_T_FD (1 << 12) /**< 100Bps full duplex */ #define DP8_10BASE_T_HD (1 << 11) /**< 10MBps half duplex */ #define DP8_MF_PREAMB_SUPPR (1 << 6) /**< Preamble suppress */ #define DP8_AUTONEG_COMP (1 << 5) /**< Auto-negotation complete */ #define DP8_RMT_FAULT (1 << 4) /**< Fault */ #define DP8_AUTONEG_ABILITY (1 << 3) /**< Auto-negotation supported */ #define DP8_LINK_STATUS (1 << 2) /**< 1=Link active */ #define DP8_JABBER_DETECT (1 << 1) /**< Jabber detect */ #define DP8_EXTEND_CAPAB (1 << 0) /**< Supports extended capabilities */ /** \brief DP83848 PHY RBR MII dode definitions */ #define DP8_RBR_RMII_MODE (1 << 5) /**< Use RMII mode */ /** \brief DP83848 PHY status definitions */ #define DP8_REMOTEFAULT (1 << 6) /**< Remote fault */ #define DP8_FULLDUPLEX (1 << 2) /**< 1=full duplex */ #define DP8_SPEED10MBPS (1 << 1) /**< 1=10MBps speed */ #define DP8_VALID_LINK (1 << 0) /**< 1=Link active */ /** \brief DP83848 PHY ID register definitions */ #define DP8_PHYID1_OUI 0x2000 /**< Expected PHY ID1 */ #define DP8_PHYID2_OUI 0x5c90 /**< Expected PHY ID2 */ /** \brief LAN8720 PHY Special Control/Status Register */ #define PHY_SCSR_100MBIT 0x0008 /**< Speed: 1=100 MBit, 0=10Mbit */ #define PHY_SCSR_DUPLEX 0x0010 /**< PHY Duplex Mask */ /** \brief Link status bits */ #define LNK_STAT_VALID 0x01 #define LNK_STAT_FULLDUPLEX 0x02 #define LNK_STAT_SPEED10MPS 0x04 /** \brief PHY ID definitions */ #define DP83848C_ID 0x20005C90 /**< PHY Identifier - DP83848C */ #define LAN8720_ID 0x0007C0F0 /**< PHY Identifier - LAN8720 */ /** \brief PHY status structure used to indicate current status of PHY. */ typedef struct { u32_t phy_speed_100mbs:1; /**< 10/100 MBS connection speed flag. */ u32_t phy_full_duplex:1; /**< Half/full duplex connection speed flag. */ u32_t phy_link_active:1; /**< Phy link active flag. */ } PHY_STATUS_TYPE; /** \brief PHY update flags */ static PHY_STATUS_TYPE physts; /** \brief Last PHY update flags, used for determing if something has changed */ static PHY_STATUS_TYPE olddphysts; /** \brief PHY update counter for state machine */ static s32_t phyustate; /** \brief Holds the PHY ID */ static u32_t phy_id; /** \brief Temporary holder of link status for LAN7420 */ static u32_t phy_lan7420_sts_tmp; /* Write a value via the MII link (non-blocking) */ void lpc_mii_write_noblock(u32_t PhyReg, u32_t Value) { /* Write value at PHY address and register */ LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg; LPC_EMAC->MWTD = Value; } /* Write a value via the MII link (blocking) */ err_t lpc_mii_write(u32_t PhyReg, u32_t Value) { u32_t mst = 250; err_t sts = ERR_OK; /* Write value at PHY address and register */ lpc_mii_write_noblock(PhyReg, Value); /* Wait for unbusy status */ while (mst > 0) { sts = LPC_EMAC->MIND; if ((sts & EMAC_MIND_BUSY) == 0) mst = 0; else { mst--; osDelay(1); } } if (sts != 0) sts = ERR_TIMEOUT; return sts; } /* Reads current MII link busy status */ u32_t lpc_mii_is_busy(void) { return (u32_t) (LPC_EMAC->MIND & EMAC_MIND_BUSY); } /* Starts a read operation via the MII link (non-blocking) */ u32_t lpc_mii_read_data(void) { u32_t data = LPC_EMAC->MRDD; LPC_EMAC->MCMD = 0; return data; } /* Starts a read operation via the MII link (non-blocking) */ void lpc_mii_read_noblock(u32_t PhyReg) { /* Read value at PHY address and register */ LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg; LPC_EMAC->MCMD = EMAC_MCMD_READ; } /* Read a value via the MII link (blocking) */ err_t lpc_mii_read(u32_t PhyReg, u32_t *data) { u32_t mst = 250; err_t sts = ERR_OK; /* Read value at PHY address and register */ lpc_mii_read_noblock(PhyReg); /* Wait for unbusy status */ while (mst > 0) { sts = LPC_EMAC->MIND & ~EMAC_MIND_MII_LINK_FAIL; if ((sts & EMAC_MIND_BUSY) == 0) { mst = 0; *data = LPC_EMAC->MRDD; } else { mst--; osDelay(1); } } LPC_EMAC->MCMD = 0; if (sts != 0) sts = ERR_TIMEOUT; return sts; } /** \brief Update PHY status from passed value * * This function updates the current PHY status based on the * passed PHY status word. The PHY status indicate if the link * is active, the connection speed, and duplex. * * \param[in] netif NETIF structure * \param[in] linksts Status word from PHY * \return 1 if the status has changed, otherwise 0 */ static s32_t lpc_update_phy_sts(struct netif *netif, u32_t linksts) { s32_t changed = 0; /* Update link active status */ if (linksts & LNK_STAT_VALID) physts.phy_link_active = 1; else physts.phy_link_active = 0; /* Full or half duplex */ if (linksts & LNK_STAT_FULLDUPLEX) physts.phy_full_duplex = 1; else physts.phy_full_duplex = 0; /* Configure 100MBit/10MBit mode. */ if (linksts & LNK_STAT_SPEED10MPS) physts.phy_speed_100mbs = 0; else physts.phy_speed_100mbs = 1; if (physts.phy_speed_100mbs != olddphysts.phy_speed_100mbs) { changed = 1; if (physts.phy_speed_100mbs) { /* 100MBit mode. */ lpc_emac_set_speed(1); NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100000000); } else { /* 10MBit mode. */ lpc_emac_set_speed(0); NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000); } olddphysts.phy_speed_100mbs = physts.phy_speed_100mbs; } if (physts.phy_full_duplex != olddphysts.phy_full_duplex) { changed = 1; if (physts.phy_full_duplex) lpc_emac_set_duplex(1); else lpc_emac_set_duplex(0); olddphysts.phy_full_duplex = physts.phy_full_duplex; } if (physts.phy_link_active != olddphysts.phy_link_active) { changed = 1; #if NO_SYS == 1 if (physts.phy_link_active) netif_set_link_up(netif); else netif_set_link_down(netif); #else if (physts.phy_link_active) tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_up, (void*) netif, 1); else tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_down, (void*) netif, 1); #endif olddphysts.phy_link_active = physts.phy_link_active; } return changed; } /** \brief Initialize the DP83848 PHY. * * This function initializes the DP83848 PHY. It will block until * complete. This function is called as part of the EMAC driver * initialization. Configuration of the PHY at startup is * controlled by setting up configuration defines in lpc_phy.h. * * \param[in] netif NETIF structure * \param[in] rmii If set, configures the PHY for RMII mode * \return ERR_OK if the setup was successful, otherwise ERR_TIMEOUT */ err_t lpc_phy_init(struct netif *netif, int rmii) { u32_t tmp; s32_t i; physts.phy_speed_100mbs = olddphysts.phy_speed_100mbs = 0; physts.phy_full_duplex = olddphysts.phy_full_duplex = 0; physts.phy_link_active = olddphysts.phy_link_active = 0; phyustate = 0; /* Only first read and write are checked for failure */ /* Put the DP83848C in reset mode and wait for completion */ if (lpc_mii_write(DP8_BMCR_REG, DP8_RESET) != 0) return ERR_TIMEOUT; i = 400; while (i > 0) { osDelay(1); /* 1 ms */ if (lpc_mii_read(DP8_BMCR_REG, &tmp) != 0) return ERR_TIMEOUT; if (!(tmp & (DP8_RESET | DP8_POWER_DOWN))) i = -1; else i--; } /* Timeout? */ if (i == 0) return ERR_TIMEOUT; // read PHY ID lpc_mii_read(DP8_IDR1_REG, &tmp); phy_id = (tmp << 16); lpc_mii_read(DP8_IDR2_REG, &tmp); phy_id |= (tmp & 0XFFF0); /* Setup link based on configuration options */ #if PHY_USE_AUTONEG==1 tmp = DP8_AUTONEG; #else tmp = 0; #endif #if PHY_USE_100MBS==1 tmp |= DP8_SPEED_SELECT; #endif #if PHY_USE_FULL_DUPLEX==1 tmp |= DP8_DUPLEX_MODE; #endif lpc_mii_write(DP8_BMCR_REG, tmp); /* Enable RMII mode for PHY */ if (rmii) lpc_mii_write(DP8_PHY_RBR_REG, DP8_RBR_RMII_MODE); /* The link is not set active at this point, but will be detected later */ return ERR_OK; } /* Phy status update state machine */ s32_t lpc_phy_sts_sm(struct netif *netif) { s32_t changed = 0; u32_t data = 0; u32_t tmp; switch (phyustate) { default: case 0: if (phy_id == DP83848C_ID) { lpc_mii_read_noblock(DP8_PHY_STAT_REG); phyustate = 2; } else if (phy_id == LAN8720_ID) { lpc_mii_read_noblock(DP8_PHY_SCSR_REG); phyustate = 1; } break; case 1: if (phy_id == LAN8720_ID) { tmp = lpc_mii_read_data(); // we get speed and duplex here. phy_lan7420_sts_tmp = (tmp & PHY_SCSR_DUPLEX) ? LNK_STAT_FULLDUPLEX : 0; phy_lan7420_sts_tmp |= (tmp & PHY_SCSR_100MBIT) ? 0 : LNK_STAT_SPEED10MPS; //read the status register to get link status lpc_mii_read_noblock(DP8_BMSR_REG); phyustate = 2; } break; case 2: /* Wait for read status state */ if (!lpc_mii_is_busy()) { /* Update PHY status */ tmp = lpc_mii_read_data(); if (phy_id == DP83848C_ID) { // STS register contains all needed status bits data = (tmp & DP8_VALID_LINK) ? LNK_STAT_VALID : 0; data |= (tmp & DP8_FULLDUPLEX) ? LNK_STAT_FULLDUPLEX : 0; data |= (tmp & DP8_SPEED10MBPS) ? LNK_STAT_SPEED10MPS : 0; } else if (phy_id == LAN8720_ID) { // we only get the link status here. phy_lan7420_sts_tmp |= (tmp & DP8_LINK_STATUS) ? LNK_STAT_VALID : 0; data = phy_lan7420_sts_tmp; } changed = lpc_update_phy_sts(netif, data); phyustate = 0; } break; } return changed; } /** * @} */ /* --------------------------------- End Of File ------------------------------ */