/** | |
* \file | |
* | |
* \brief API driver for KSZ8051MNL PHY component. | |
* | |
* Copyright (c) 2013 Atmel Corporation. All rights reserved. | |
* | |
* \asf_license_start | |
* | |
* \page License | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* 2. Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* | |
* 3. The name of Atmel may not be used to endorse or promote products derived | |
* from this software without specific prior written permission. | |
* | |
* 4. This software may only be redistributed and used in connection with an | |
* Atmel microcontroller product. | |
* | |
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED | |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
* | |
* \asf_license_stop | |
* | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "FreeRTOSIPConfig.h" | |
#include "ethernet_phy.h" | |
#include "instance/gmac.h" | |
/// @cond 0 | |
/**INDENT-OFF**/ | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/**INDENT-ON**/ | |
/// @endcond | |
/** | |
* \defgroup ksz8051mnl_ethernet_phy_group PHY component (KSZ8051MNL) | |
* | |
* Driver for the ksz8051mnl component. This driver provides access to the main | |
* features of the PHY. | |
* | |
* \section dependencies Dependencies | |
* This driver depends on the following modules: | |
* - \ref gmac_group Ethernet Media Access Controller (GMAC) module. | |
* | |
* @{ | |
*/ | |
SPhyProps phyProps; | |
/* Max PHY number */ | |
#define ETH_PHY_MAX_ADDR 31 | |
/* Ethernet PHY operation max retry count */ | |
#define ETH_PHY_RETRY_MAX 1000000 | |
/* Ethernet PHY operation timeout */ | |
#define ETH_PHY_TIMEOUT 10 | |
/** | |
* \brief Find a valid PHY Address ( from addrStart to 31 ). | |
* | |
* \param p_gmac Pointer to the GMAC 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. | |
*/ | |
int ethernet_phy_addr = 0; | |
static uint8_t ethernet_phy_find_valid(Gmac *p_gmac, uint8_t uc_phy_addr, | |
uint8_t uc_start_addr) | |
{ | |
uint32_t ul_value = 0; | |
uint8_t uc_cnt; | |
uint8_t uc_phy_address = uc_phy_addr; | |
gmac_enable_management(p_gmac, true); | |
/* | |
#define GMII_OUI_MSB 0x0022 | |
#define GMII_OUI_LSB 0x05 | |
PHYID1 = 0x0022 | |
PHYID2 = 0x1550 | |
0001_0101_0101_0000 = 0x1550 <= mask should be 0xFFF0 | |
*/ | |
/* Check the current PHY address */ | |
gmac_phy_read(p_gmac, uc_phy_addr, GMII_PHYID1, &ul_value); | |
/* Find another one */ | |
if (ul_value != GMII_OUI_MSB) { | |
ethernet_phy_addr = 0xFF; | |
for (uc_cnt = uc_start_addr; uc_cnt <= ETH_PHY_MAX_ADDR; uc_cnt++) { | |
uc_phy_address = (uc_phy_address + 1) & 0x1F; | |
ul_value = 0; | |
gmac_phy_read(p_gmac, uc_phy_address, GMII_PHYID1, &ul_value); | |
if (ul_value == GMII_OUI_MSB) { | |
ethernet_phy_addr = uc_phy_address; | |
break; | |
} | |
} | |
} | |
gmac_enable_management(p_gmac, false); | |
if (ethernet_phy_addr != 0xFF) { | |
gmac_phy_read(p_gmac, uc_phy_address, GMII_BMSR, &ul_value); | |
} | |
return ethernet_phy_addr; | |
} | |
/** | |
* \brief Perform a HW initialization to the PHY and set up clocks. | |
* | |
* This should be called only once to initialize the PHY pre-settings. | |
* The PHY address is the reset status of CRS, RXD[3:0] (the emacPins' pullups). | |
* The COL pin is used to select MII mode on reset (pulled up for Reduced MII). | |
* The RXDV pin is used to select test mode on reset (pulled up for test mode). | |
* The above pins should be predefined for corresponding settings in resetPins. | |
* The GMAC peripheral pins are configured after the reset is done. | |
* | |
* \param p_gmac Pointer to the GMAC instance. | |
* \param uc_phy_addr PHY address. | |
* \param ul_mck GMAC MCK. | |
* | |
* Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. | |
*/ | |
uint8_t ethernet_phy_init(Gmac *p_gmac, uint8_t uc_phy_addr, uint32_t mck) | |
{ | |
uint8_t uc_rc = GMAC_TIMEOUT; | |
uint8_t uc_phy; | |
ethernet_phy_reset(GMAC,uc_phy_addr); | |
/* Configure GMAC runtime clock */ | |
uc_rc = gmac_set_mdc_clock(p_gmac, mck); | |
if (uc_rc != GMAC_OK) { | |
return 0; | |
} | |
/* Check PHY Address */ | |
uc_phy = ethernet_phy_find_valid(p_gmac, uc_phy_addr, 0); | |
if (uc_phy == 0xFF) { | |
return 0; | |
} | |
if (uc_phy != uc_phy_addr) { | |
ethernet_phy_reset(p_gmac, uc_phy_addr); | |
} | |
phy_props.phy_chn = uc_phy; | |
return uc_phy; | |
} | |
/** | |
* \brief Get the Link & speed settings, and automatically set up the GMAC with the | |
* settings. | |
* | |
* \param p_gmac Pointer to the GMAC 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 GMAC_OK if successfully, GMAC_TIMEOUT if timeout. | |
*/ | |
uint8_t ethernet_phy_set_link(Gmac *p_gmac, 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 = true, uc_fd = true; | |
uint8_t uc_rc = GMAC_TIMEOUT; | |
gmac_enable_management(p_gmac, true); | |
uc_phy_address = uc_phy_addr; | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_address, GMII_BMSR, &ul_stat1); | |
if (uc_rc != GMAC_OK) { | |
/* Disable PHY management and start the GMAC transfer */ | |
gmac_enable_management(p_gmac, false); | |
return uc_rc; | |
} | |
if ((ul_stat1 & GMII_LINK_STATUS) == 0) { | |
/* Disable PHY management and start the GMAC transfer */ | |
gmac_enable_management(p_gmac, false); | |
return GMAC_INVALID; | |
} | |
if (uc_apply_setting_flag == 0) { | |
/* Disable PHY management and start the GMAC transfer */ | |
gmac_enable_management(p_gmac, false); | |
return uc_rc; | |
} | |
/* Read advertisement */ | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_address, GMII_ANAR, &ul_stat2); | |
phy_props.phy_stat1 = ul_stat1; | |
phy_props.phy_stat2 = ul_stat2; | |
if (uc_rc != GMAC_OK) { | |
/* Disable PHY management and start the GMAC transfer */ | |
gmac_enable_management(p_gmac, false); | |
return uc_rc; | |
} | |
if ((ul_stat1 & GMII_100BASE_TX_FD) && (ul_stat2 & GMII_100TX_FDX)) { | |
/* Set GMAC for 100BaseTX and Full Duplex */ | |
uc_speed = true; | |
uc_fd = true; | |
} else | |
if ((ul_stat1 & GMII_100BASE_T4_HD) && (ul_stat2 & GMII_100TX_HDX)) { | |
/* Set MII for 100BaseTX and Half Duplex */ | |
uc_speed = true; | |
uc_fd = false; | |
} else | |
if ((ul_stat1 & GMII_10BASE_T_FD) && (ul_stat2 & GMII_10_FDX)) { | |
/* Set MII for 10BaseT and Full Duplex */ | |
uc_speed = false; | |
uc_fd = true; | |
} else | |
if ((ul_stat1 & GMII_10BASE_T_HD) && (ul_stat2 & GMII_10_HDX)) { | |
/* Set MII for 10BaseT and Half Duplex */ | |
uc_speed = false; | |
uc_fd = false; | |
} | |
gmac_set_speed(p_gmac, uc_speed); | |
gmac_enable_full_duplex(p_gmac, uc_fd); | |
/* Start the GMAC transfers */ | |
gmac_enable_management(p_gmac, false); | |
return uc_rc; | |
} | |
PhyProps_t phy_props; | |
/** | |
* \brief Issue an auto negotiation of the PHY. | |
* | |
* \param p_gmac Pointer to the GMAC instance. | |
* \param uc_phy_addr PHY address. | |
* | |
* Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. | |
*/ | |
uint8_t ethernet_phy_auto_negotiate(Gmac *p_gmac, 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_retry_count = 0; | |
uint8_t uc_speed = 0; | |
uint8_t uc_fd=0; | |
uint8_t uc_rc = GMAC_TIMEOUT; | |
gmac_enable_management(p_gmac, true); | |
/* Set up control register */ | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_addr, GMII_BMCR, &ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -1; | |
return uc_rc; | |
} | |
ul_value &= ~(uint32_t)GMII_AUTONEG; /* Remove auto-negotiation enable */ | |
ul_value &= ~(uint32_t)(GMII_LOOPBACK | GMII_POWER_DOWN); | |
ul_value |= (uint32_t)GMII_ISOLATE; /* Electrically isolate PHY */ | |
uc_rc = gmac_phy_write(p_gmac, uc_phy_addr, GMII_BMCR, ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -2; | |
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 = GMII_100TX_FDX | GMII_100TX_HDX | GMII_10_FDX | GMII_10_HDX | | |
GMII_AN_IEEE_802_3; | |
uc_rc = gmac_phy_write(p_gmac, uc_phy_addr, GMII_ANAR, ul_phy_anar); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -3; | |
return uc_rc; | |
} | |
/* Read & modify control register */ | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_addr, GMII_BMCR, &ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -4; | |
return uc_rc; | |
} | |
ul_value |= GMII_SPEED_SELECT | GMII_AUTONEG | GMII_DUPLEX_MODE; | |
uc_rc = gmac_phy_write(p_gmac, uc_phy_addr, GMII_BMCR, ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -5; | |
return uc_rc; | |
} | |
/* Restart auto negotiation */ | |
ul_value |= (uint32_t)GMII_RESTART_AUTONEG; | |
ul_value &= ~(uint32_t)GMII_ISOLATE; | |
uc_rc = gmac_phy_write(p_gmac, uc_phy_addr, GMII_BMCR, ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -6; | |
return uc_rc; | |
} | |
/* Check if auto negotiation is completed */ | |
while (1) { | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_addr, GMII_BMSR, &ul_value); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -7; | |
return uc_rc; | |
} | |
/* Done successfully */ | |
if (ul_value & GMII_AUTONEG_COMP) { | |
break; | |
} | |
/* Timeout check */ | |
if (ul_retry_max) { | |
if (++ul_retry_count >= ul_retry_max) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -8; | |
return GMAC_TIMEOUT; | |
} | |
} | |
} | |
/* Get the auto negotiate link partner base page */ | |
uc_rc = gmac_phy_read(p_gmac, uc_phy_addr, GMII_PCR1, &phy_props.phy_params); | |
if (uc_rc != GMAC_OK) { | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = -9; | |
return uc_rc; | |
} | |
/* Set up the GMAC link speed */ | |
if ((ul_phy_anar & phy_props.phy_params) & GMII_100TX_FDX) { | |
/* Set MII for 100BaseTX and Full Duplex */ | |
uc_speed = true; | |
uc_fd = true; | |
} else if ((ul_phy_anar & phy_props.phy_params) & GMII_10_FDX) { | |
/* Set MII for 10BaseT and Full Duplex */ | |
uc_speed = false; | |
uc_fd = true; | |
} else if ((ul_phy_anar & phy_props.phy_params) & GMII_100TX_HDX) { | |
/* Set MII for 100BaseTX and half Duplex */ | |
uc_speed = true; | |
uc_fd = false; | |
} else if ((ul_phy_anar & phy_props.phy_params) & GMII_10_HDX) { | |
/* Set MII for 10BaseT and half Duplex */ | |
uc_speed = false; | |
uc_fd = false; | |
} | |
gmac_set_speed(p_gmac, uc_speed); | |
gmac_enable_full_duplex(p_gmac, uc_fd); | |
/* Select Media Independent Interface type */ | |
gmac_select_mii_mode(p_gmac, ETH_PHY_MODE); | |
gmac_enable_transmit(GMAC, true); | |
gmac_enable_receive(GMAC, true); | |
gmac_enable_management(p_gmac, false); | |
phy_props.phy_result = 1; | |
return uc_rc; | |
} | |
/** | |
* \brief Issue a SW reset to reset all registers of the PHY. | |
* | |
* \param p_gmac Pointer to the GMAC instance. | |
* \param uc_phy_addr PHY address. | |
* | |
* \Return GMAC_OK if successfully, GMAC_TIMEOUT if timeout. | |
*/ | |
uint8_t ethernet_phy_reset(Gmac *p_gmac, uint8_t uc_phy_addr) | |
{ | |
uint32_t ul_bmcr = GMII_RESET; | |
uint8_t uc_phy_address = uc_phy_addr; | |
uint32_t ul_timeout = ETH_PHY_TIMEOUT; | |
uint8_t uc_rc = GMAC_TIMEOUT; | |
gmac_enable_management(p_gmac, true); | |
ul_bmcr = GMII_RESET; | |
gmac_phy_write(p_gmac, uc_phy_address, GMII_BMCR, ul_bmcr); | |
do { | |
gmac_phy_read(p_gmac, uc_phy_address, GMII_BMCR, &ul_bmcr); | |
ul_timeout--; | |
} while ((ul_bmcr & GMII_RESET) && ul_timeout); | |
gmac_enable_management(p_gmac, false); | |
if (!ul_timeout) { | |
uc_rc = GMAC_OK; | |
} | |
return (uc_rc); | |
} | |
/// @cond 0 | |
/**INDENT-OFF**/ | |
#ifdef __cplusplus | |
} | |
#endif | |
/**INDENT-ON**/ | |
/// @endcond | |
/** | |
* \} | |
*/ |