455 lines
13 KiB
C
455 lines
13 KiB
C
|
/**
|
||
|
* \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
|
||
|
|
||
|
/**
|
||
|
* \}
|
||
|
*/
|