302 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/**********************************************************************
 | 
						|
* $Id$		lpc_phy_lan8720.c			2011-11-20
 | 
						|
*//**
 | 
						|
* @file		lpc_phy_lan8720.c
 | 
						|
* @brief	LAN8720 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 "boards/board.h"
 | 
						|
#include "arch/lpc_arch.h" // FIXME Ethernet
 | 
						|
#include "lpc_phy.h"
 | 
						|
 | 
						|
/** @defgroup lan8720_phy	PHY status and control for the LAN8720.
 | 
						|
 * @ingroup lwip_phy
 | 
						|
 *
 | 
						|
 * Various functions for controlling and monitoring the status of the
 | 
						|
 * LAN8720 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  LAN8720 PHY register offsets */
 | 
						|
#define LAN8_BCR_REG        0x0  /**< Basic Control Register */
 | 
						|
#define LAN8_BSR_REG        0x1  /**< Basic Status Reg */
 | 
						|
#define LAN8_PHYID1_REG     0x2  /**< PHY ID 1 Reg  */
 | 
						|
#define LAN8_PHYID2_REG     0x3  /**< PHY ID 2 Reg */
 | 
						|
#define LAN8_PHYSPLCTL_REG  0x1F /**< PHY special control/status Reg */
 | 
						|
 | 
						|
/* LAN8720 BCR register definitions */
 | 
						|
#define LAN8_RESET          (1 << 15)  /**< 1= S/W Reset */
 | 
						|
#define LAN8_LOOPBACK       (1 << 14)  /**< 1=loopback Enabled */
 | 
						|
#define LAN8_SPEED_SELECT   (1 << 13)  /**< 1=Select 100MBps */
 | 
						|
#define LAN8_AUTONEG        (1 << 12)  /**< 1=Enable auto-negotiation */
 | 
						|
#define LAN8_POWER_DOWN     (1 << 11)  /**< 1=Power down PHY */
 | 
						|
#define LAN8_ISOLATE        (1 << 10)  /**< 1=Isolate PHY */
 | 
						|
#define LAN8_RESTART_AUTONEG (1 << 9)  /**< 1=Restart auto-negoatiation */
 | 
						|
#define LAN8_DUPLEX_MODE    (1 << 8)   /**< 1=Full duplex mode */
 | 
						|
 | 
						|
/* LAN8720 BSR register definitions */
 | 
						|
#define LAN8_100BASE_T4     (1 << 15)  /**< T4 mode */
 | 
						|
#define LAN8_100BASE_TX_FD  (1 << 14)  /**< 100MBps full duplex */
 | 
						|
#define LAN8_100BASE_TX_HD  (1 << 13)  /**< 100MBps half duplex */
 | 
						|
#define LAN8_10BASE_T_FD    (1 << 12)  /**< 100Bps full duplex */
 | 
						|
#define LAN8_10BASE_T_HD    (1 << 11)  /**< 10MBps half duplex */
 | 
						|
#define LAN8_AUTONEG_COMP   (1 << 5)   /**< Auto-negotation complete */
 | 
						|
#define LAN8_RMT_FAULT      (1 << 4)   /**< Fault */
 | 
						|
#define LAN8_AUTONEG_ABILITY (1 << 3)  /**< Auto-negotation supported */
 | 
						|
#define LAN8_LINK_STATUS    (1 << 2)   /**< 1=Link active */
 | 
						|
#define LAN8_JABBER_DETECT  (1 << 1)   /**< Jabber detect */
 | 
						|
#define LAN8_EXTEND_CAPAB   (1 << 0)   /**< Supports extended capabilities */
 | 
						|
 | 
						|
/* LAN8720 PHYSPLCTL status definitions */
 | 
						|
#define LAN8_SPEEDMASK      (7 << 2)   /**< Speed and duplex mask */
 | 
						|
#define LAN8_SPEED100F      (6 << 2)   /**< 100BT full duplex */
 | 
						|
#define LAN8_SPEED10F       (5 << 2)   /**< 10BT full duplex */
 | 
						|
#define LAN8_SPEED100H      (2 << 2)   /**< 100BT half duplex */
 | 
						|
#define LAN8_SPEED10H       (1 << 2)   /**< 10BT half duplex */
 | 
						|
 | 
						|
/* LAN8720 PHY ID 1/2 register definitions */
 | 
						|
#define LAN8_PHYID1_OUI     0x0007     /**< Expected PHY ID1 */
 | 
						|
#define LAN8_PHYID2_OUI     0xC0F0     /**< Expected PHY ID2, except last 4 bits */
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief PHY status structure used to indicate current status of PHY.
 | 
						|
 */
 | 
						|
typedef struct {
 | 
						|
	u32_t     phy_speed_100mbs:2; /**< 10/100 MBS connection speed flag. */
 | 
						|
	u32_t     phy_full_duplex:2;  /**< Half/full duplex connection speed flag. */
 | 
						|
	u32_t     phy_link_active:2;  /**< 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  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 with link state
 | 
						|
 *  \param[in]    sdsts   Status word with speed and duplex states
 | 
						|
 *  \return        1 if the status has changed, otherwise 0
 | 
						|
 */
 | 
						|
static s32_t lpc_update_phy_sts(struct netif *netif, u32_t linksts, u32_t sdsts)
 | 
						|
{
 | 
						|
	s32_t changed = 0;
 | 
						|
 | 
						|
	/* Update link active status */
 | 
						|
	if (linksts & LAN8_LINK_STATUS)
 | 
						|
		physts.phy_link_active = 1;
 | 
						|
	else
 | 
						|
		physts.phy_link_active = 0;
 | 
						|
 | 
						|
	switch (sdsts & LAN8_SPEEDMASK) {
 | 
						|
		case LAN8_SPEED100F:
 | 
						|
		default:
 | 
						|
			physts.phy_speed_100mbs = 1;
 | 
						|
			physts.phy_full_duplex = 1;
 | 
						|
			break;
 | 
						|
 | 
						|
		case LAN8_SPEED10F:
 | 
						|
			physts.phy_speed_100mbs = 0;
 | 
						|
			physts.phy_full_duplex = 1;
 | 
						|
			break;
 | 
						|
 | 
						|
		case LAN8_SPEED100H:
 | 
						|
			physts.phy_speed_100mbs = 1;
 | 
						|
			physts.phy_full_duplex = 0;
 | 
						|
			break;
 | 
						|
 | 
						|
		case LAN8_SPEED10H:
 | 
						|
			physts.phy_speed_100mbs = 0;
 | 
						|
			physts.phy_full_duplex = 0;
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	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 LAN8720 PHY.
 | 
						|
 *
 | 
						|
 *  This function initializes the LAN8720 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_emac_config.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, tmp1;
 | 
						|
	s32_t i;
 | 
						|
 | 
						|
	physts.phy_speed_100mbs = olddphysts.phy_speed_100mbs = 2;
 | 
						|
	physts.phy_full_duplex = olddphysts.phy_full_duplex = 2;
 | 
						|
	physts.phy_link_active = olddphysts.phy_link_active = 2;
 | 
						|
	phyustate = 0;
 | 
						|
 | 
						|
	/* Only first read and write are checked for failure */
 | 
						|
	/* Put the LAN8720 in reset mode and wait for completion */
 | 
						|
	if (lpc_mii_write(LAN8_BCR_REG, LAN8_RESET) != 0)
 | 
						|
		return ERR_TIMEOUT;
 | 
						|
	i = 400;
 | 
						|
	while (i > 0) {
 | 
						|
		msDelay(1);   /* 1 ms */
 | 
						|
		if (lpc_mii_read(LAN8_BCR_REG, &tmp) != 0)
 | 
						|
			return ERR_TIMEOUT;
 | 
						|
 | 
						|
		if (!(tmp & (LAN8_RESET | LAN8_POWER_DOWN)))
 | 
						|
			i = -1;
 | 
						|
		else
 | 
						|
			i--;
 | 
						|
	}
 | 
						|
	/* Timeout? */
 | 
						|
	if (i == 0)
 | 
						|
		return ERR_TIMEOUT;
 | 
						|
 | 
						|
	/* Setup link based on configuration options */
 | 
						|
#if PHY_USE_AUTONEG==1
 | 
						|
	tmp = LAN8_AUTONEG;
 | 
						|
#else
 | 
						|
	tmp = 0;
 | 
						|
#endif
 | 
						|
#if PHY_USE_100MBS==1
 | 
						|
	tmp |= LAN8_SPEED_SELECT;
 | 
						|
#endif
 | 
						|
#if PHY_USE_FULL_DUPLEX==1
 | 
						|
	tmp |= LAN8_DUPLEX_MODE;
 | 
						|
#endif
 | 
						|
	lpc_mii_write(LAN8_BCR_REG, tmp);
 | 
						|
 | 
						|
	/* 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)
 | 
						|
{
 | 
						|
	static u32_t sts;
 | 
						|
	s32_t changed = 0;
 | 
						|
 | 
						|
	switch (phyustate) {
 | 
						|
		default:
 | 
						|
		case 0:
 | 
						|
			/* Read BMSR to clear faults */
 | 
						|
			lpc_mii_read_noblock(LAN8_BSR_REG);
 | 
						|
			phyustate = 1;
 | 
						|
			break;
 | 
						|
 | 
						|
		case 1:
 | 
						|
			/* Wait for read status state */
 | 
						|
			if (!lpc_mii_is_busy()) {
 | 
						|
				/* Get PHY status with link state */
 | 
						|
				sts = lpc_mii_read_data();
 | 
						|
				lpc_mii_read_noblock(LAN8_PHYSPLCTL_REG);
 | 
						|
				phyustate = 2;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		case 2:
 | 
						|
			/* Wait for read status state */
 | 
						|
			if (!lpc_mii_is_busy()) {
 | 
						|
				/* Update PHY status */
 | 
						|
				changed = lpc_update_phy_sts(netif, sts, lpc_mii_read_data());
 | 
						|
				phyustate = 0;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	return changed;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @}
 | 
						|
 */
 | 
						|
 | 
						|
/* --------------------------------- End Of File ------------------------------ */
 |