354 lines
9.2 KiB
C
354 lines
9.2 KiB
C
|
|
#include "phy_8720.h"
|
|
|
|
#define CHECK_PARAM(expr) ((void)0)
|
|
|
|
struct bflb_device_s *emac_dev = NULL;
|
|
static struct emac_phy_cfg_s *phy_8720_cfg = NULL;
|
|
|
|
/**
|
|
* @brief phy 8720 reset
|
|
* @return int
|
|
*
|
|
*/
|
|
int phy_8720_reset(void)
|
|
{
|
|
int timeout = 1000;
|
|
uint16_t regval = PHY_RESET;
|
|
|
|
/* pull the PHY from power down mode if it is in */
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_SPECIAL_MODES, ®val)) {
|
|
printf("emac phy reg read fail 0x%04x\r\n", regval);
|
|
return -1;
|
|
}
|
|
printf("emac phy reg read 0x%04x\r\n", regval);
|
|
if (PHY_SPECIAL_MODES_MODE_PWRDOWN == (regval & PHY_SPECIAL_MODES_MODE)) {
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_SPECIAL_MODES, regval | PHY_SPECIAL_MODES_MODE_ALL) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* disable energy detect powerdown mode for cable detect, this increase the power by 220mW */
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_CTRL_STATUS, ®val)) {
|
|
return -1;
|
|
}
|
|
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_CTRL_STATUS, regval & (~PHY_CTRL_STATUS_EDPWRDOWN)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* do sw reset */
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_BCR, PHY_RESET) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
for (; timeout; timeout--) {
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_BCR, ®val)) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(regval & PHY_RESET)) {
|
|
return 0;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(1);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief phy 8720 auto negotiate
|
|
* @param cfg phy config
|
|
* @return int
|
|
*
|
|
*/
|
|
int phy_8720_auto_negotiate(struct emac_phy_cfg_s *cfg)
|
|
{
|
|
uint16_t regval = 0;
|
|
uint16_t phyid1 = 0, phyid2 = 0;
|
|
uint16_t advertise = 0;
|
|
uint16_t lpa = 0;
|
|
uint32_t timeout = 100; //10s,in 100ms
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_PHYID1, &phyid1)) {
|
|
printf("read emac phy id 1 error\r\n");
|
|
return -1;
|
|
}
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_PHYID2, &phyid2)) {
|
|
printf("read emac phy id 2 error\r\n");
|
|
return -1;
|
|
}
|
|
printf("emac phy id 1 =%08x\r\n", (unsigned int)phyid1);
|
|
printf("emac phy id 2 =%08x\r\n", (unsigned int)phyid2);
|
|
if (cfg->phy_id != (((phyid1 << 16) | phyid2) & 0x000FFFF0)) {
|
|
/* ID error */
|
|
return -1;
|
|
} else {
|
|
cfg->phy_id = (phyid1 << 16) | phyid2;
|
|
}
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_BCR, ®val)) {
|
|
return -1;
|
|
}
|
|
|
|
regval &= ~PHY_AUTONEGOTIATION;
|
|
regval &= ~(PHY_LOOPBACK | PHY_POWERDOWN);
|
|
regval |= PHY_ISOLATE;
|
|
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_BCR, regval) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* set advertisement mode */
|
|
advertise = PHY_ADVERTISE_100BASETXFULL | PHY_ADVERTISE_100BASETXHALF |
|
|
PHY_ADVERTISE_10BASETXFULL | PHY_ADVERTISE_10BASETXHALF |
|
|
PHY_ADVERTISE_8023;
|
|
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_ADVERTISE, advertise) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_BCR, ®val)) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
regval |= (PHY_FULLDUPLEX_100M | PHY_AUTONEGOTIATION);
|
|
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_BCR, regval) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
regval |= PHY_RESTART_AUTONEGOTIATION;
|
|
regval &= ~PHY_ISOLATE;
|
|
|
|
if (bflb_emac_phy_reg_write(emac_dev, PHY_BCR, regval) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(100);
|
|
|
|
while (1) {
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_BSR, ®val)) {
|
|
return -1;
|
|
}
|
|
|
|
if (regval & PHY_AUTONEGO_COMPLETE) {
|
|
/* complete */
|
|
break;
|
|
}
|
|
|
|
if (!(--timeout)) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(100);
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(100);
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_LPA, &lpa)) {
|
|
return -1;
|
|
}
|
|
|
|
if (((advertise & lpa) & PHY_ADVERTISE_100BASETXFULL) != 0) {
|
|
/* 100BaseTX and Full Duplex */
|
|
cfg->full_duplex = 1;
|
|
cfg->speed = 100;
|
|
cfg->phy_state = PHY_STATE_READY;
|
|
} else if (((advertise & lpa) & PHY_ADVERTISE_10BASETXFULL) != 0) {
|
|
/* 10BaseT and Full Duplex */
|
|
cfg->full_duplex = 1;
|
|
cfg->speed = 10;
|
|
cfg->phy_state = PHY_STATE_READY;
|
|
} else if (((advertise & lpa) & PHY_ADVERTISE_100BASETXHALF) != 0) {
|
|
/* 100BaseTX and half Duplex */
|
|
cfg->full_duplex = 0;
|
|
cfg->speed = 100;
|
|
cfg->phy_state = PHY_STATE_READY;
|
|
} else if (((advertise & lpa) & PHY_ADVERTISE_10BASETXHALF) != 0) {
|
|
/* 10BaseT and half Duplex */
|
|
cfg->full_duplex = 0;
|
|
cfg->speed = 10;
|
|
cfg->phy_state = PHY_STATE_READY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief phy 8720 link up
|
|
* @param cfg phy config
|
|
* @return int
|
|
*
|
|
*/
|
|
int phy_8720_link_up(struct emac_phy_cfg_s *cfg)
|
|
{
|
|
uint16_t phy_bsr = 0;
|
|
uint16_t phy_sr = 0;
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_BSR, &phy_bsr)) {
|
|
return -1;
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
|
|
if (!(PHY_LINKED_STATUS & phy_bsr)) {
|
|
return 1; /* error */
|
|
}
|
|
|
|
bflb_mtimer_delay_ms(16);
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_SR, &phy_sr)) {
|
|
return -1;
|
|
}
|
|
|
|
if ((phy_bsr & PHY_BSR_100BASETXFULL) && PHY_SR_SPEED_MODE_COMPARE(phy_sr, PHY_SR_SPEED_100BASETXFULL)) {
|
|
/* 100BaseTX and Full Duplex */
|
|
cfg->full_duplex = 1;
|
|
cfg->speed = 100;
|
|
cfg->phy_state = PHY_STATE_UP;
|
|
} else if ((phy_bsr & PHY_BSR_10BASETXFULL) && PHY_SR_SPEED_MODE_COMPARE(phy_sr, PHY_SR_SPEED_10BASETXFULL)) {
|
|
/* 10BaseT and Full Duplex */
|
|
cfg->full_duplex = 1;
|
|
cfg->speed = 10;
|
|
cfg->phy_state = PHY_STATE_UP;
|
|
} else if ((phy_bsr & PHY_BSR_100BASETXHALF) && PHY_SR_SPEED_MODE_COMPARE(phy_sr, PHY_SR_SPEED_100BASETXHALF)) {
|
|
/* 100BaseTX and half Duplex */
|
|
cfg->full_duplex = 0;
|
|
cfg->speed = 100;
|
|
cfg->phy_state = PHY_STATE_UP;
|
|
} else if ((phy_bsr & PHY_BSR_10BASETXHALF) && PHY_SR_SPEED_MODE_COMPARE(phy_sr, PHY_SR_SPEED_10BASETXHALF)) {
|
|
/* 10BaseT and half Duplex */
|
|
cfg->full_duplex = 0;
|
|
cfg->speed = 10;
|
|
cfg->phy_state = PHY_STATE_UP;
|
|
} else {
|
|
/* 10BaseT and half Duplex */
|
|
cfg->full_duplex = -1;
|
|
cfg->speed = -1;
|
|
cfg->phy_state = PHY_STATE_DOWN;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Use energy detector for cable plug in/out detect.
|
|
* @param cfg: phy config
|
|
* @return int
|
|
*
|
|
*/
|
|
int phy_8720_poll_cable_status(struct emac_phy_cfg_s *cfg)
|
|
{
|
|
uint16_t phy_regval = 0;
|
|
|
|
CHECK_PARAM(NULL != phy_8720_cfg);
|
|
|
|
if (0 != bflb_emac_phy_reg_read(emac_dev, PHY_CTRL_STATUS, &phy_regval)) {
|
|
return -1;
|
|
}
|
|
|
|
phy_8720_cfg->phy_state = (PHY_CTRL_STATUS_ENERGYON & phy_regval) ? PHY_STATE_UP : PHY_STATE_DOWN;
|
|
|
|
return !!(PHY_CTRL_STATUS_ENERGYON & phy_regval);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize EMAC PHY 8720 module
|
|
* @param cfg: phy config
|
|
* @return int
|
|
*
|
|
*/
|
|
int phy_8720_init(struct bflb_device_s *emac, struct emac_phy_cfg_s *cfg)
|
|
{
|
|
uint16_t phyReg;
|
|
|
|
CHECK_PARAM(NULL != cfg);
|
|
|
|
phy_8720_cfg = cfg;
|
|
emac_dev = emac;
|
|
|
|
printf("emac phy addr:0x%04x\r\n", cfg->phy_address);
|
|
// bflb_emac_phy_set_address(emac_dev, cfg->phy_address);
|
|
bflb_emac_feature_control(emac, EMAC_CMD_SET_PHY_ADDRESS, cfg->phy_address);
|
|
|
|
if (0 != phy_8720_reset()) {
|
|
return -1;
|
|
}
|
|
|
|
if (cfg->auto_negotiation) {
|
|
/*
|
|
uint32_t cnt=0;
|
|
do{
|
|
if(bflb_emac_phy_reg_read(emac_dev, PHY_BSR, &phyReg) != SUCCESS){
|
|
return -1;
|
|
}
|
|
cnt++;
|
|
if(cnt>PHY_LINK_TO){
|
|
return -1;
|
|
}
|
|
}while((phyReg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS);
|
|
*/
|
|
if (0 != phy_8720_auto_negotiate(cfg)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (bflb_emac_phy_reg_read(emac_dev, PHY_BCR, &phyReg) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
phyReg &= (~PHY_FULLDUPLEX_100M);
|
|
|
|
if (cfg->speed == 10) {
|
|
if (cfg->full_duplex == 1) {
|
|
phyReg |= PHY_FULLDUPLEX_10M;
|
|
} else {
|
|
phyReg |= PHY_HALFDUPLEX_10M;
|
|
}
|
|
} else {
|
|
if (cfg->full_duplex == 1) {
|
|
phyReg |= PHY_FULLDUPLEX_100M;
|
|
} else {
|
|
phyReg |= PHY_HALFDUPLEX_100M;
|
|
}
|
|
}
|
|
|
|
if ((bflb_emac_phy_reg_write(emac_dev, PHY_BCR, phyReg)) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// bflb_emac_phy_config_full_duplex(emac_dev, cfg->full_duplex);
|
|
bflb_emac_feature_control(emac, EMAC_CMD_FULL_DUPLEX, cfg->full_duplex);
|
|
|
|
return phy_8720_link_up(cfg);
|
|
}
|
|
|
|
/**
|
|
* @brief get phy 8720 module status
|
|
* @return emac_phy_status_t @ref emac_phy_status_t enum
|
|
*/
|
|
emac_phy_status_t phy_8720_status_get(void)
|
|
{
|
|
CHECK_PARAM(NULL != phy_8720_cfg);
|
|
|
|
if ((100 == phy_8720_cfg->speed) &&
|
|
(phy_8720_cfg->full_duplex) &&
|
|
(PHY_STATE_UP == phy_8720_cfg->phy_state)) {
|
|
return EMAC_PHY_STAT_100MBITS_FULLDUPLEX;
|
|
} else if (PHY_STATE_UP == phy_8720_cfg->phy_state) {
|
|
return EMAC_PHY_STAT_LINK_UP;
|
|
} else {
|
|
return EMAC_PHY_STAT_LINK_DOWN;
|
|
}
|
|
}
|