text: /**
   * \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 */
  
  /**
   * \}
   */
  