blob: e0433bace9641b1665469e1ece8fd4ed1d53e4b3 [file] [log] [blame]
/***************************************************************************//**
* 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 *********************************/