| /***************************************************************************//** | |
| * PHY access methods for DP83848C. | |
| * The implementation in this file is specific to the DP83848C, | |
| * If a different PHY support is required the PHY specific registers must | |
| * be updated. | |
| * (c) Copyright 2007 Actel Corporation | |
| * | |
| * SVN $Revision: 2324 $ | |
| * SVN $Date: 2010-02-26 10:47:36 +0000 (Fri, 26 Feb 2010) $ | |
| * | |
| ******************************************************************************/ | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| #include "mss_ethernet_mac.h" | |
| #include "mss_ethernet_mac_regs.h" | |
| #include "phy.h" | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| extern MAC_instance_t g_mss_mac; | |
| /***************************** MDIO FUNCTIONS *********************************/ | |
| /* Defines ********************************************************************/ | |
| #define MDIO_START 0x00004000UL | |
| #define MDIO_READ 0x00002000UL | |
| #define MDIO_WRITE 0x00001002UL | |
| #define MDIO_ADDR_OFFSET 7UL | |
| #define MDIO_ADDR_MASK 0x00000f80UL | |
| #define MDIO_REG_ADDR_OFFSET 2UL | |
| #define MDIO_REG_ADDR_MASK 0x0000007cUL | |
| #define PREAMBLECOUNT 32UL | |
| #define ONEMICROSECOND 20UL | |
| typedef enum { | |
| MDIO_CMD_READ, | |
| MDIO_CMD_WRITE | |
| }mdio_cmd_t; | |
| /***************************************************************************//** | |
| * Set clock high or low. | |
| */ | |
| static void | |
| MDIO_management_clock | |
| ( | |
| int32_t clock | |
| ) | |
| { | |
| int32_t volatile a; | |
| MAC_BITBAND->CSR9_MDC = (uint32_t)clock; | |
| /* delay for 1us */ | |
| for( a = 0; a < ONEMICROSECOND; a++ ){} | |
| } | |
| /***************************************************************************//** | |
| * Send read or write command to PHY. | |
| */ | |
| static void | |
| MDIO_send_cmd | |
| ( | |
| uint8_t regad, | |
| mdio_cmd_t mdio_cmd | |
| ) | |
| { | |
| int32_t i; | |
| uint16_t mask, data; | |
| /* enable MII output */ | |
| MAC_BITBAND->CSR9_MDEN = 1; | |
| /* send 32 1's preamble */ | |
| MAC_BITBAND->CSR9_MDO = 1; | |
| for (i = 0; i < PREAMBLECOUNT; i++) { | |
| MDIO_management_clock( 0 ); | |
| MDIO_management_clock( 1 ); | |
| } | |
| /* calculate data bits */ | |
| data = MDIO_START | | |
| (( mdio_cmd == MDIO_CMD_READ ) ? MDIO_READ : MDIO_WRITE ) | | |
| ((g_mss_mac.phy_address << MDIO_ADDR_OFFSET) & MDIO_ADDR_MASK) | | |
| ((regad << MDIO_REG_ADDR_OFFSET) & MDIO_REG_ADDR_MASK); | |
| /* sent out */ | |
| for( mask = 0x00008000L; mask>0; mask >>= 1 ) | |
| { | |
| if ((mask == 0x2) && (mdio_cmd == MDIO_CMD_READ)) { | |
| /* enable MII input */ | |
| MAC_BITBAND->CSR9_MDEN = 0; | |
| } | |
| MDIO_management_clock( 0 ); | |
| /* prepare MDO */ | |
| MAC_BITBAND->CSR9_MDO = (uint32_t)((mask & data) != 0 ? 1UL : 0UL); | |
| MDIO_management_clock( 1 ); | |
| } | |
| } | |
| /***************************************************************************//** | |
| * Reads a PHY register. | |
| */ | |
| static uint16_t | |
| MDIO_read | |
| ( | |
| uint8_t regad | |
| ) | |
| { | |
| uint16_t mask; | |
| uint16_t data; | |
| MDIO_send_cmd( regad, MDIO_CMD_READ); | |
| /* read data */ | |
| data = 0; | |
| for( mask = 0x00008000L; mask>0; mask >>= 1 ) | |
| { | |
| MDIO_management_clock( 0 ); | |
| /* read MDI */ | |
| if(MAC_BITBAND-> CSR9_MDI != 0){ | |
| data |= mask; | |
| } | |
| MDIO_management_clock( 1 ); | |
| } | |
| MDIO_management_clock( 0 ); | |
| return data; | |
| } | |
| /***************************************************************************//** | |
| * Writes to a PHY register. | |
| */ | |
| static void | |
| MDIO_write | |
| ( | |
| uint8_t regad, | |
| uint16_t data | |
| ) | |
| { | |
| uint16_t mask; | |
| MDIO_send_cmd(regad, MDIO_CMD_WRITE); | |
| /* write data */ | |
| for( mask = 0x00008000L; mask>0; mask >>= 1 ) | |
| { | |
| MDIO_management_clock( 0 ); | |
| /* prepare MDO */ | |
| MAC_BITBAND->CSR9_MDO = (uint32_t)((mask & data) != 0 ? 1UL : 0UL); | |
| MDIO_management_clock( 1 ); | |
| } | |
| MDIO_management_clock( 0 ); | |
| } | |
| /****************************** PHY FUNCTIONS *********************************/ | |
| /* Defines ********************************************************************/ | |
| /* Base registers */ | |
| #define PHYREG_MIIMCR 0x00 /**< MII Management Control Register */ | |
| #define MIIMCR_RESET (1<<15) | |
| #define MIIMCR_LOOPBACK (1<<14) | |
| #define MIIMCR_SPEED_SELECT (1<<13) | |
| #define MIIMCR_ENABLE_AUTONEGOTIATION (1<<12) | |
| #define MIIMCR_RESTART_AUTONEGOTIATION (1<<9) | |
| #define MIIMCR_DUPLEX_MODE (1<<8) | |
| #define MIIMCR_COLLISION_TEST (1<<7) | |
| #define PHYREG_MIIMSR 0x01 /**< MII Management Status Register */ | |
| #define MIIMSR_ANC (1<<5) /**< Auto-Negotiation Completed. */ | |
| #define MIIMSR_LINK (1<<2) /**< Link is established. */ | |
| #define PHYREG_PHYID1R 0x02 /**< PHY Identifier 1 Register */ | |
| #define PHYREG_PHYID2R 0x03 /**< PHY Identifier 2 Register */ | |
| #define PHYREG_ANAR 0x04 /**< Auto-Negotiation Advertisement Register */ | |
| #define ANAR_100FD (1<<8) | |
| #define ANAR_100HD (1<<7) | |
| #define ANAR_10FD (1<<6) | |
| #define ANAR_10HD (1<<5) | |
| #define PHYREG_ANLPAR 0x05 /**< Auto-Negotiation Link Partner Ability Register */ | |
| #define PHYREG_ANER 0x06 /**< Auto-Negotiation Expansion Register */ | |
| #define PHYREG_NPAR 0x07 /**< Next Page Advertisement Register */ | |
| /* 0x08- 0x0F Reserved */ | |
| #define PHYREG_MFR 0x10 /**< Miscellaneous Features Register */ | |
| #define PHYREG_ICSR 0x11 /**< Interrupt Control/Status Register */ | |
| #define PHYREG_DR 0x12 /**< Diagnostic Register */ | |
| #define DR_DPLX (1<<11) | |
| #define DR_DATA_RATE (1<<10) | |
| #define PHYREG_PMLR 0x13 /**< Power Management & Loopback Register */ | |
| /* 0x14 Reserved */ | |
| #define PHYREG_MCR 0x15 /**< Mode Control Register */ | |
| #define MCR_LED_SEL (1<<9) | |
| /* 0x16 Reserved */ | |
| #define PHYREG_DCR 0x17 /**< Disconnect Counter */ | |
| #define PHYREG_RECR 0x18 /**< Receive Error Counter */ | |
| /* 0x19-0x1F Reserved */ | |
| /***************************************************************************//** | |
| * Probe used PHY. | |
| * | |
| * return PHY address. If PHY don't fount, returns 255. | |
| */ | |
| uint8_t PHY_probe( void ) | |
| { | |
| uint8_t phy; | |
| uint8_t phy_found; | |
| uint16_t reg; | |
| phy_found = 0; | |
| for (phy = MSS_PHY_ADDRESS_MIN; phy <= MSS_PHY_ADDRESS_MAX; phy++) { | |
| g_mss_mac.phy_address = phy; | |
| reg = MDIO_read( PHYREG_PHYID1R ); | |
| if ((reg != 0x0000ffffUL) && (reg != 0x00000000UL)) { | |
| phy_found = 1; | |
| phy = MSS_PHY_ADDRESS_MAX + 1; | |
| } | |
| } | |
| if( phy_found == 0 ) { | |
| g_mss_mac.phy_address = MSS_PHY_ADDRESS_AUTO_DETECT; | |
| } | |
| return g_mss_mac.phy_address; | |
| } | |
| /***************************************************************************//** | |
| * Resets the PHY. | |
| */ | |
| void PHY_reset( void ) | |
| { | |
| MDIO_write( PHYREG_MIIMCR, MIIMCR_RESET ); | |
| MDIO_write( PHYREG_MIIMCR, | |
| MIIMCR_ENABLE_AUTONEGOTIATION | | |
| MIIMCR_RESTART_AUTONEGOTIATION | | |
| MIIMCR_COLLISION_TEST ); | |
| } | |
| /***************************************************************************//** | |
| * Restarts PHY auto-negotiation and wait until it's over. | |
| */ | |
| void PHY_auto_negotiate( void ) | |
| { | |
| uint16_t reg; | |
| reg = MDIO_read( PHYREG_MIIMCR ); | |
| MDIO_write( PHYREG_MIIMCR, | |
| (uint16_t)( MIIMCR_ENABLE_AUTONEGOTIATION | | |
| MIIMCR_RESTART_AUTONEGOTIATION | | |
| reg) ); | |
| for( ;; ) { | |
| reg = MDIO_read( PHYREG_MIIMSR ); | |
| if( (reg & MIIMSR_ANC) != 0 ) { | |
| break; | |
| } else { | |
| vTaskDelay( 200 ); | |
| } | |
| } | |
| } | |
| /***************************************************************************//** | |
| * Returns link status. | |
| * | |
| * @return #MAC_LINK_STATUS_LINK if link is up. | |
| */ | |
| uint8_t PHY_link_status( void ) | |
| { | |
| uint8_t retval = 0; | |
| if(( MDIO_read( PHYREG_MIIMSR ) & MIIMSR_LINK ) != 0 ){ | |
| retval = MSS_MAC_LINK_STATUS_LINK; | |
| } | |
| return retval; | |
| } | |
| /***************************************************************************//** | |
| * Returns link type. | |
| * | |
| * @return the logical OR of the following values: | |
| * #MAC_LINK_STATUS_100MB - Connection is 100Mb | |
| * #MAC_LINK_STATUS_FDX - Connection is full duplex | |
| */ | |
| uint8_t PHY_link_type( void ) | |
| { | |
| uint16_t diagnostic; | |
| uint8_t type = 0; | |
| diagnostic = MDIO_read( PHYREG_DR ); | |
| if( (diagnostic & DR_DPLX) != 0 ) { | |
| type = MSS_MAC_LINK_STATUS_FDX; | |
| } | |
| if( (diagnostic & DR_DATA_RATE) != 0 ) { | |
| type |= MSS_MAC_LINK_STATUS_100MB; | |
| } | |
| return type; | |
| } | |
| /***************************************************************************//** | |
| * Sets link type. | |
| */ | |
| void | |
| PHY_set_link_type | |
| ( | |
| uint8_t type | |
| ) | |
| { | |
| uint16_t reg; | |
| reg = MDIO_read( PHYREG_ANAR ); | |
| reg |= ANAR_100FD | ANAR_100HD | ANAR_10FD | ANAR_10HD; | |
| if( (type & MSS_MAC_LINK_STATUS_100MB) == 0 ) { | |
| reg &= ~(ANAR_100FD | ANAR_100HD); | |
| } | |
| if( (type & MSS_MAC_LINK_STATUS_FDX) == 0 ) { | |
| reg &= ~(ANAR_100FD | ANAR_10FD); | |
| } | |
| MDIO_write( PHYREG_ANAR, reg ); | |
| } | |
| /***************************************************************************//** | |
| * Puts the Phy in Loopback mode | |
| */ | |
| uint16_t | |
| PHY_set_loopback | |
| ( | |
| uint8_t enable | |
| ) | |
| { | |
| uint16_t reg = 0; | |
| reg = MDIO_read( PHYREG_MIIMCR ); | |
| // If set to one we need to set the LOCAL Phy loopback | |
| if(enable == 1) | |
| reg |= MIIMCR_LOOPBACK; | |
| else // else we want to clear the bit.. | |
| reg ^= MIIMCR_LOOPBACK; | |
| MDIO_write( PHYREG_MIIMCR,reg ); | |
| reg = MDIO_read( PHYREG_MIIMCR ); | |
| return reg; | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| /******************************** END OF FILE *********************************/ | |