mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-03-10 04:11:17 +00:00
687 lines
18 KiB
C
687 lines
18 KiB
C
/* ----OB------------------------------------------------------------------------
|
|
* SAM Software Package License
|
|
* ----------------------------------------------------------------------------
|
|
* Copyright (c) 2015-2016, Atmel Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the disclaimer below.
|
|
*
|
|
* Atmel's name may not be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* DISCLAIMER: 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
|
|
* 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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/** \file */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Headers
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include "chip.h"
|
|
#include "debug.h"
|
|
#ifdef CONFIG_HAVE_EMAC
|
|
#include "network/emac.h"
|
|
#endif
|
|
#ifdef CONFIG_HAVE_GMAC
|
|
#include "network/gmac.h"
|
|
#endif
|
|
#include "network/gmii.h"
|
|
#include "network/phy.h"
|
|
#include "hardware.h"
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Constants
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#define GMII_ID2_MASK (~0xfu)
|
|
|
|
enum _phy_model {
|
|
/* Micrel */
|
|
PHY_KSZ8051,
|
|
PHY_KSZ8081,
|
|
PHY_KSZ8061,
|
|
PHY_KSZ9021,
|
|
PHY_KSZ9031,
|
|
/* Davicom */
|
|
PHY_DM9161AEP,
|
|
/* Microchip */
|
|
PHY_LAN8740A,
|
|
};
|
|
|
|
struct _phy_dev {
|
|
enum _phy_model model;
|
|
const char* name;
|
|
uint16_t id1;
|
|
uint16_t id2;
|
|
};
|
|
|
|
static const struct _phy_dev _phy_devices[] = {
|
|
{ PHY_KSZ8051, "Micrel KSZ8051", 0x0022, 0x1550 },
|
|
{ PHY_KSZ8081, "Micrel KSZ8081", 0x0022, 0x1560 },
|
|
{ PHY_KSZ8061, "Micrel KSZ8061", 0x0022, 0x1570 },
|
|
{ PHY_KSZ9021, "Micrel KSZ9021", 0x0022, 0x1610 },
|
|
{ PHY_KSZ9031, "Micrel KSZ9031", 0x0022, 0x1620 },
|
|
{ PHY_DM9161AEP, "Davicom DM9161AEP", 0x0181, 0xB8A0 },
|
|
{ PHY_LAN8740A, "Microchip LAN8740A",0x0007, 0xC110 },
|
|
};
|
|
|
|
#ifdef CONFIG_HAVE_EMAC
|
|
static const struct _eth_phy_op _emac_op = {
|
|
.phy_write = (eth_phy_write)emac_phy_write,
|
|
.phy_read = (eth_phy_read)emac_phy_read,
|
|
.enable_mido = (eth_enable_mdio)emac_enable_mdio,
|
|
.disable_mido = (eth_disable_mdio)emac_disable_mdio,
|
|
.enable_rmii = (eth_enable_rmii)emac_enable_rmii,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAVE_GMAC
|
|
static const struct _eth_phy_op _gmac_op = {
|
|
.phy_read = (eth_phy_read)gmac_phy_read,
|
|
.phy_write = (eth_phy_write)gmac_phy_write,
|
|
.enable_mido = (eth_enable_mdio)gmac_enable_mdio,
|
|
.disable_mido = (eth_disable_mdio)gmac_disable_mdio,
|
|
.enable_rmii = (eth_enable_rmii)gmac_enable_rmii,
|
|
};
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Local functions
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
static const struct _phy_dev* _phy_find_dev(uint16_t id1, uint16_t id2)
|
|
{
|
|
int i;
|
|
id2 = id2 & GMII_ID2_MASK;
|
|
for (i = 0; i < ARRAY_SIZE(_phy_devices); i++) {
|
|
if (_phy_devices[i].id1 == id1 &&
|
|
_phy_devices[i].id2 == id2) {
|
|
return &_phy_devices[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int _phy_get_id(const struct _phy* phy, void* eth, uint8_t phy_addr,
|
|
uint32_t idle_timeout, uint16_t* id1, uint16_t* id2)
|
|
{
|
|
int err;
|
|
|
|
err = phy->op->phy_read(eth, phy_addr, GMII_PHYID1R, id1, idle_timeout);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = phy->op->phy_read(eth, phy_addr, GMII_PHYID2R, id2, idle_timeout);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool _phy_find_addr(struct _phy* phy)
|
|
{
|
|
uint8_t phy_addr;
|
|
uint16_t id1, id2;
|
|
const struct _phy_dev *dev;
|
|
int i;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
/* start searching at desc->phy_addr */
|
|
phy_addr = phy->desc->phy_addr;
|
|
dev = NULL;
|
|
for (i = 0; i < 32; i++) {
|
|
id1 = id2 = 0;
|
|
if (_phy_get_id(phy, phy->desc->addr, phy_addr,
|
|
phy->desc->timeout.idle, &id1, &id2) >= 0) {
|
|
dev = _phy_find_dev(id1, id2);
|
|
if (dev)
|
|
break;
|
|
}
|
|
phy_addr = (phy_addr + 1) & 0x1F;
|
|
}
|
|
|
|
if (dev) {
|
|
eth_info("%ld: Found PHY %s at 0x%02x",
|
|
(uint32_t)GetRTTC(), dev->name, phy_addr);
|
|
phy->dev = dev;
|
|
phy->phy_addr = phy_addr;
|
|
} else {
|
|
eth_error("No known PHY detected\r\n");
|
|
phy->phy_addr = 0xff;
|
|
}
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
return dev != NULL;
|
|
}
|
|
|
|
static int phy_reset_omsor(const struct _phy* phy)
|
|
{
|
|
uint16_t value, temp, timeout;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
/* Reconfig OMSOR */
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_OMSOR, &value,
|
|
phy->desc->timeout.idle);
|
|
|
|
/* Override strap-in for RMII mode */
|
|
value |= GMII_OMSOR_RMII_OVERRIDE;
|
|
timeout = 10;
|
|
do {
|
|
phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_OMSOR, value,
|
|
phy->desc->timeout.idle);
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_OMSOR, &temp,
|
|
phy->desc->timeout.idle);
|
|
timeout--;
|
|
} while ((temp != GMII_OMSOR_RMII_OVERRIDE) && timeout);
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
if (timeout == 0)
|
|
return -ETIMEDOUT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int phy_disable_qwf(const struct _phy* phy)
|
|
{
|
|
uint16_t value, temp, timeout;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
/* Reconfig OMSOR */
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_OMSOR, &value,
|
|
phy->desc->timeout.idle);
|
|
|
|
/* Override strap-in for RMII mode */
|
|
value |= 0x1000; // Disable QWF
|
|
timeout = 10;
|
|
do {
|
|
phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_OMSOR, value,
|
|
phy->desc->timeout.idle);
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_OMSOR, &temp,
|
|
phy->desc->timeout.idle);
|
|
timeout--;
|
|
} while ((temp != value) && timeout);
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
if (timeout == 0)
|
|
return -ETIMEDOUT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Exported functions
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
int phy_configure(struct _phy* phy)
|
|
{
|
|
phy->op = NULL;
|
|
#ifdef CONFIG_HAVE_EMAC
|
|
if (PHY_IF_EMAC == phy->desc->phy_if)
|
|
phy->op = &_emac_op;
|
|
#endif
|
|
#ifdef CONFIG_HAVE_GMAC
|
|
if (PHY_IF_GMAC == phy->desc->phy_if)
|
|
phy->op = &_gmac_op;
|
|
#endif
|
|
|
|
if (phy->op == NULL)
|
|
return -EINVAL;
|
|
|
|
if (!_phy_find_addr(phy))
|
|
return -ENODEV;
|
|
|
|
if (phy_reset(phy))
|
|
return -EFAULT;
|
|
|
|
if (phy->dev->model == PHY_KSZ8051 || phy->dev->model == PHY_KSZ8081)
|
|
return phy_reset_omsor(phy);
|
|
|
|
// if (phy_disable_qwf(phy))
|
|
// return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int phy_get_id(const struct _phy* phy, uint16_t* id1, uint16_t* id2)
|
|
{
|
|
return _phy_get_id(phy, phy->desc->addr, phy->phy_addr,
|
|
phy->desc->timeout.idle, id1, id2);
|
|
}
|
|
|
|
int phy_reset(const struct _phy* phy)
|
|
{
|
|
int err;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
err = phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_BMCR,
|
|
GMII_BMCR_RESET, phy->desc->timeout.idle);
|
|
|
|
if (err == 0) {
|
|
unsigned long timeout;
|
|
timeout = GetTimer(100);
|
|
while (1) {
|
|
uint16_t bmcr;
|
|
err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMCR,
|
|
&bmcr, phy->desc->timeout.idle);
|
|
if (err == 0 && (bmcr & GMII_BMCR_RESET) == 0)
|
|
break;
|
|
if (CheckTimer(timeout)) {
|
|
err = -ETIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
return err;
|
|
}
|
|
|
|
int phy_powerdown(const struct _phy* phy)
|
|
{
|
|
uint16_t bmcr;
|
|
int err;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMCR, &bmcr,
|
|
phy->desc->timeout.idle);
|
|
if (err >= 0) {
|
|
bmcr |= GMII_BMCR_POWER_DOWN;
|
|
|
|
err = phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_BMCR,
|
|
bmcr, phy->desc->timeout.idle);
|
|
}
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
void phy_dump_registers(const struct _phy* phy)
|
|
{
|
|
uint16_t value;
|
|
|
|
eth_info_wp(" -- GMII Registers @0x%02x --", phy->phy_addr);
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMCR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - BMCR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMSR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - BMSR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ANAR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ANAR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ANLPAR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ANLPAR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ANER, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ANER : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ANNPR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ANNPR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ANLPNPAR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ANLPNPAR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_RXERCR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - RXERCR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ICSR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - ICSR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_OMSOR, &value,
|
|
phy->desc->timeout.idle);
|
|
eth_info_wp(" - OMSOR : 0x%x", (unsigned)value);
|
|
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
}
|
|
|
|
int phy_auto_negotiate_wait_for_completion(const struct _phy* phy)
|
|
{
|
|
int err = 0;
|
|
uint16_t value;
|
|
|
|
uint16_t base_tc, base_ts;
|
|
unsigned long timeout;
|
|
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_1000BTCR, &base_tc, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
base_tc |= GMII_1000BTCR_1000BASET_HD | GMII_1000BTCR_1000BASET_FD;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_1000BTCR, base_tc, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
/* Wait for auto-negotiation completion */
|
|
timeout = GetTimer(phy->desc->timeout.autoneg);
|
|
while (1) {
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_BMSR, &value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error reading PHY BMSR");
|
|
goto exit;
|
|
}
|
|
|
|
/* auto-negotiation completed */
|
|
if (value & GMII_BMSR_AUTONEG_COMP)
|
|
break;
|
|
|
|
/* Time out check */
|
|
if (phy->desc->timeout.autoneg) {
|
|
if (CheckTimer(timeout)) {
|
|
eth_error("Time out auto-negotiation");
|
|
err = -ETIMEDOUT;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set local link mode */
|
|
while (1) {
|
|
if (phy->dev->model == PHY_KSZ9021 || phy->dev->model == PHY_KSZ9031) {
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_1000BTSR, &base_ts, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
/* Setup the MAC link speed */
|
|
if ((base_ts & GMII_1000BTSR_LP_1000BASET_FD) &&
|
|
(base_tc & GMII_1000BTCR_1000BASET_FD)) {
|
|
/* set RGMII for 1000BaseTX and Full Duplex */
|
|
eth_info("PHY Auto-Negotiation complete -> 1000BaseT/Full");
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_1000M, ETH_DUPLEX_FULL);
|
|
break;
|
|
} else if ((base_ts & GMII_1000BTSR_LP_1000BASET_HD) &&
|
|
(base_tc & GMII_1000BTCR_1000BASET_HD)) {
|
|
/* set RGMII for 1000BaseT and Half Duplex*/
|
|
eth_info("PHY Auto-Negotiation complete -> 1000BaseT/Half");
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_1000M, ETH_DUPLEX_HALF);
|
|
break;
|
|
}
|
|
}
|
|
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ANLPAR, &value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error reading PHY ANLPAR");
|
|
goto exit;
|
|
}
|
|
|
|
/* Setup the GMAC link speed */
|
|
if (value & GMII_ANLPAR_100BASETX_FD) {
|
|
eth_info("%ld: PHY Auto-Negotiation complete -> 100BaseT/Full", (uint32_t)GetRTTC());
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_100M, ETH_DUPLEX_FULL);
|
|
break;
|
|
} else if (value & GMII_ANLPAR_100BASETX_HD) {
|
|
eth_info("PHY Auto-Negotiation complete -> 100BaseT/Half");
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_100M, ETH_DUPLEX_HALF);
|
|
break;
|
|
} else if (value & GMII_ANLPAR_10BASET_FD) {
|
|
eth_info("PHY Auto-Negotiation complete -> 10BaseT/Full");
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_10M, ETH_DUPLEX_FULL);
|
|
break;
|
|
} else if (value & GMII_ANLPAR_10BASET_HD) {
|
|
eth_info("PHY Auto-Negotiation complete -> 10BaseT/Half");
|
|
phy->op->enable_rmii(phy->desc->addr, ETH_SPEED_10M, ETH_DUPLEX_HALF);
|
|
break;
|
|
}
|
|
|
|
/* Time out check */
|
|
if (phy->desc->timeout.autoneg) {
|
|
if (CheckTimer(timeout)) {
|
|
eth_error("Time out setup MAC link speed");
|
|
err = -ETIMEDOUT;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (err < 0)
|
|
eth_error("PHY Auto-Negotiation failed!");
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
return err;
|
|
}
|
|
|
|
int phy_auto_negotiate(const struct _phy* phy, bool block)
|
|
{
|
|
int err = 0;
|
|
uint16_t value;
|
|
|
|
uint16_t base_tc;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
if (phy->dev->model == PHY_KSZ9021 || phy->dev->model == PHY_KSZ9031) {
|
|
value = GMII_RCCPSR | 0x8000;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ERCR, value, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
value = 0xF2F4;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ERDWR, value, phy->desc->timeout.idle);
|
|
|
|
value = GMII_RRDPSR | 0x8000;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ERCR, value, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
value = 0x2222;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ERDWR, value, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
value = 0xFF00;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_ICSR, value, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_1000BTCR, &base_tc, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
base_tc |= GMII_1000BTCR_1000BASET_HD | GMII_1000BTCR_1000BASET_FD;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr,
|
|
GMII_1000BTCR, base_tc, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
|
|
/* Set the Auto_negotiation Advertisement Register, MII advertising for
|
|
* Next page, 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3 */
|
|
value = 0;
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr, GMII_ANAR,
|
|
&value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error reading PHY ANAR");
|
|
goto exit;
|
|
}
|
|
|
|
value = GMII_ANAR_100BASETX_FD | GMII_ANAR_100BASETX_HD |
|
|
GMII_ANAR_10BASET_FD | GMII_ANAR_10BASET_HD | GMII_ANAR_SEL_IEEE_8023;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr, GMII_ANAR,
|
|
value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error writing PHY ANAR");
|
|
goto exit;
|
|
}
|
|
|
|
/* Read & modify control register */
|
|
value = 0;
|
|
err = phy->op->phy_read(phy->desc->addr, phy->desc->phy_addr, GMII_BMCR,
|
|
&value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error reading PHY BMCR");
|
|
goto exit;
|
|
}
|
|
|
|
/* Restart AutoNegotiation */
|
|
value |= GMII_BMCR_AUTONEG | GMII_BMCR_RESTART_AUTONEG;
|
|
err = phy->op->phy_write(phy->desc->addr, phy->desc->phy_addr, GMII_BMCR,
|
|
value, phy->desc->timeout.idle);
|
|
if (err < 0) {
|
|
eth_error("Error writing PHY BMCR");
|
|
goto exit;
|
|
}
|
|
|
|
if (!block)
|
|
return -EAGAIN;
|
|
|
|
return phy_auto_negotiate_wait_for_completion(phy);
|
|
|
|
exit:
|
|
if (err < 0)
|
|
eth_error("PHY Auto-Negotiation failed!");
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
return err;
|
|
}
|
|
|
|
int phy_set_speed_duplex(const struct _phy* phy, enum _eth_speed speed, enum _eth_duplex duplex)
|
|
{
|
|
unsigned long timeout;
|
|
int err = 0;
|
|
uint16_t bmcr;
|
|
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
|
|
err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMCR,
|
|
&bmcr, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
switch (duplex) {
|
|
case ETH_DUPLEX_HALF:
|
|
bmcr &= ~GMII_BMCR_DUPLEX_MODE;
|
|
break;
|
|
case ETH_DUPLEX_FULL:
|
|
bmcr |= GMII_BMCR_DUPLEX_MODE;
|
|
break;
|
|
}
|
|
|
|
switch (speed) {
|
|
case ETH_SPEED_10M:
|
|
bmcr &= ~GMII_BMCR_SPEED_SELECT_MSB;
|
|
bmcr &= ~GMII_BMCR_SPEED_SELECT_LSB;
|
|
break;
|
|
case ETH_SPEED_100M:
|
|
bmcr &= ~GMII_BMCR_SPEED_SELECT_MSB;
|
|
bmcr |= GMII_BMCR_SPEED_SELECT_LSB;
|
|
break;
|
|
case ETH_SPEED_1000M:
|
|
bmcr |= GMII_BMCR_SPEED_SELECT_MSB;
|
|
bmcr &= ~GMII_BMCR_SPEED_SELECT_LSB;
|
|
break;
|
|
}
|
|
|
|
err = phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_BMCR,
|
|
bmcr, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
timeout = GetTimer(phy->desc->timeout.autoneg);
|
|
uint16_t bmsr = 0;
|
|
while ((bmsr & GMII_BMSR_LINK_STATUS) == 0) {
|
|
err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMSR,
|
|
&bmsr, phy->desc->timeout.idle);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
if (CheckTimer(timeout)) {
|
|
eth_error("PHY Link Detection timed-out!");
|
|
err = -ETIMEDOUT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
eth_info("%ld: PHY Link setup and detection complete", (uint32_t)GetRTTC());
|
|
phy->op->enable_rmii(phy->desc->addr, speed, duplex);
|
|
|
|
exit:
|
|
if (err < 0)
|
|
eth_error("PHY Speed/Duplex setting failed!");
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
return err;
|
|
}
|
|
|
|
int phy_get_link_status(const struct _phy* phy)
|
|
{
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
uint16_t bmsr = 0;
|
|
int ret;
|
|
int err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_BMSR,
|
|
&bmsr, phy->desc->timeout.idle);
|
|
if (err)
|
|
ret = -1;
|
|
else
|
|
ret = ((bmsr & GMII_BMSR_LINK_STATUS) != 0);
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int phy_write_icsr(const struct _phy* phy, uint16_t icsr)
|
|
{
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
int err = phy->op->phy_write(phy->desc->addr, phy->phy_addr, GMII_ICSR,
|
|
icsr, phy->desc->timeout.idle);
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
return err;
|
|
}
|
|
|
|
int phy_read_icsr(const struct _phy* phy, uint16_t *icsr)
|
|
{
|
|
phy->op->enable_mido(phy->desc->addr);
|
|
int err = phy->op->phy_read(phy->desc->addr, phy->phy_addr, GMII_ICSR,
|
|
icsr, phy->desc->timeout.idle);
|
|
phy->op->disable_mido(phy->desc->addr);
|
|
return err;
|
|
}
|