中兴PHY5201芯片驱动

原创
2020/06/13 08:32
阅读数 885

 

/*

 * ZX5201 PHY drivers

 *

 * modify by liguoqiang

 */

#include <config.h>

#include <common.h>

#include <miiphy.h>

#include <phy.h>

#include <asm/io.h>

#include <errno.h>

#include <linux/err.h>

 

/* TI ZX5201 */

#define ZX5201_DEVADDR                0x0e

#define ZX5201_ID_ADDR       0x02   /*5201 identity  register address*/

#define MII_ZX5201_BMCR                        0x00        /* Basic mode control register */

#define MII_ZX5201_LEDCR1                        0x18

 

#define MII_ZX5201_PHYCTRL        0x12 /*phy control resgister address*/

#define MII_PHY_STATUS           0x1a   /*phy status register address*/

 

#define MII_ZX5201_MICR        0x12

#define ZX5201_CTRL                0x00

#define ZX5201_STATUS 0x01

 

/* Extended Registers */

#define ZX5201_RGMIICTL        0x0032

#define ZX5201_RGMIIDCTL        0x0086

 

#define ZX_SPEED100   0x2000

#define ZX_AUTOONEG   0x1000

#define ZX_DISAUTOONEG 0x0000

#define ZX_SPEED_FULL 0x0100

 

#define ZX5201_SW_RESET        BIT(15)

#define ZX5201_SW_RESTART        BIT(14)

 

/* MICR Interrupt bits */

#define MII_ZX5201_MICR_AN_ERR_INT_EN                BIT(15)

#define MII_ZX5201_MICR_SPEED_CHNG_INT_EN        BIT(14)

#define MII_ZX5201_MICR_DUP_MODE_CHNG_INT_EN        BIT(13)

#define MII_ZX5201_MICR_PAGE_RXD_INT_EN        BIT(12)

#define MII_ZX5201_MICR_AUTONEG_COMP_INT_EN        BIT(11)

#define MII_ZX5201_MICR_LINK_STS_CHNG_INT_EN        BIT(10)

#define MII_ZX5201_MICR_FALSE_CARRIER_INT_EN        BIT(8)

#define MII_ZX5201_MICR_SLEEP_MODE_CHNG_INT_EN        BIT(4)

#define MII_ZX5201_MICR_WOL_INT_EN                BIT(3)

#define MII_ZX5201_MICR_XGMII_ERR_INT_EN        BIT(2)

#define MII_ZX5201_MICR_POL_CHNG_INT_EN        BIT(1)

#define MII_ZX5201_MICR_JABBER_INT_EN                BIT(0)

 

/* RGMIICTL bits */

#define ZX5201_RGMII_TX_CLK_DELAY_EN                BIT(1)

#define ZX5201_RGMII_RX_CLK_DELAY_EN                BIT(0)

 

/* PHY CTRL bits */

#define ZX5201_PHYCR_FIFO_DEPTH_SHIFT                14

 

/* RGMIIDCTL bits */

#define ZX5201_RGMII_TX_CLK_DELAY_SHIFT        4

 

#define MII_MMD_CTRL        0x10 /* MMD Access Control Register */

#define MII_MMD_DATA        0x11 /* MMD Access Data Register */

 

/* MMD Access Control register fields */

#define MII_MMD_CTRL_DEVAD_MASK        0x1f /* Mask MMD DEVAD*/

#define MII_MMD_CTRL_ADDR                0x0000 /* Address */

#define MII_MMD_CTRL_NOINCR                0x4000 /* no post increment */

#define MII_MMD_CTRL_INCR_RDWT        0x8000 /* post increment on reads & writes */

#define MII_MMD_CTRL_INCR_ON_WT        0xC000 /* post increment on writes only */

 

 

/**

 * phy_read_mmd_indirect - reads data from the MMD registers

 * @phydev: The PHY device bus

 * @prtad: MMD Address

 * @devad: MMD DEVAD

 * @addr: PHY address on the MII bus

 *

 * Description: it reads data from the MMD registers (clause 22 to access to

 * clause 45) of the specified phy address.

 * To read these registers we have:

 * 1) Write reg 13 // DEVAD

 * 2) Write reg 14 // MMD Address

 * 3) Write reg 13 // MMD Data Command for MMD DEVAD

 * 3) Read  reg 14 // Read MMD data

 */

int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,

  int devad, int addr)

{

int value = -1;

 

/* Write the desired MMD Devad */

phy_write(phydev, addr, MII_MMD_CTRL, devad);

 

/* Write the desired MMD register address */

phy_write(phydev, addr, MII_MMD_DATA, prtad);

 

/* Select the Function : DATA with no post increment */

phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));

 

/* Read the content of the MMD's selected register */

value = phy_read(phydev, addr, MII_MMD_DATA);

return value;

}

 

/**

 * phy_write_mmd_indirect - writes data to the MMD registers

 * @phydev: The PHY device

 * @prtad: MMD Address

 * @devad: MMD DEVAD

 * @addr: PHY address on the MII bus

 * @data: data to write in the MMD register

 *

 * Description: Write data from the MMD registers of the specified

 * phy address.

 * To write these registers we have:

 * 1) Write reg 13 // DEVAD

 * 2) Write reg 14 // MMD Address

 * 3) Write reg 13 // MMD Data Command for MMD DEVAD

 * 3) Write reg 14 // Write MMD data

 */

void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,

    int devad, int addr, u32 data)

{

/* Write the desired MMD Devad */

phy_write(phydev, addr, MII_MMD_CTRL, devad);

 

/* Write the desired MMD register address */

phy_write(phydev, addr, MII_MMD_DATA, prtad);

 

/* Select the Function : DATA with no post increment */

phy_write(phydev, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));

 

/* Write the data into MMD's selected register */

phy_write(phydev, addr, MII_MMD_DATA, data);

}

 

/**

 * phy_interface_is_rgmii - Convenience function for testing if a PHY interface

 * is RGMII (all variants)

 * @phydev: the phy_device struct

 */

static inline bool phy_interface_is_rgmii(struct phy_device *phydev)

{

return phydev->interface >= PHY_INTERFACE_MODE_RGMII &&

phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID;

}

 

/* User setting - can be taken from DTS */

#define RX_ID_DELAY        8

#define TX_ID_DELAY        0xa

#define FIFO_DEPTH        1

void print_zx5201_phy_status(struct phy_device *phydev)

{

unsigned int val = 0;

int link = 0;

val = phy_read(phydev, MDIO_DEVAD_NONE, ZX5201_STATUS);

link = ((val & 0x0004) && 0x0004);

debug("read zx5201 status register(%x) val:0X%x, link status:%d\n", ZX5201_STATUS, val, link);

val = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHY_STATUS);

link = ((val & 0x0040) && 0X0040);

debug("read phy status register(%x) val:%x, link status:%d\n", MII_PHY_STATUS, val, link);

}

 

static int zx5201_setup_forced(struct phy_device *phydev)

{

int err;

int ctl = 0;

 

phydev->pause = phydev->asym_pause = 0;

 

if (SPEED_1000 == phydev->speed)

ctl |= (ZX_AUTOONEG | 0x0040 | ZX_SPEED_FULL);

else if (SPEED_100 == phydev->speed)

ctl |= (ZX_SPEED100 | ZX_AUTOONEG | ZX_SPEED_FULL);

 

if (DUPLEX_FULL == phydev->duplex)

ctl |= BMCR_FULLDPLX;

 

err = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);

 

return err;

}

 

/**

 * @description:  config Autonegotiation Advertisement register fox zx5201

 * not support 1000M only 100M

 * @param {type}

 * @return:

 */

static int zx5201_config_advert(struct phy_device *phydev)

{

u32 advertise;

int oldadv, adv;

int err, changed = 0;

 

debug("enter zx5201_config_advert()\n");

/* Only allow advertising what

 * this PHY supports */

phydev->advertising &= phydev->supported;

advertise = phydev->advertising;

 

/* Setup standard advertisement */

oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);

//debug("enter genphy_config_advert\n");

//debug("MII_ADVERTISE11111 = 0x%x\n", adv);

 

if (adv < 0)

return adv;

 

adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |

 ADVERTISE_PAUSE_ASYM);

if (advertise & ADVERTISED_10baseT_Half)

adv |= ADVERTISE_10HALF;

if (advertise & ADVERTISED_10baseT_Full)

adv |= ADVERTISE_10FULL;

if (advertise & ADVERTISED_100baseT_Half)

adv |= ADVERTISE_100HALF;

if (advertise & ADVERTISED_100baseT_Full)

adv |= ADVERTISE_100FULL;

if (advertise & ADVERTISED_Pause)

adv |= ADVERTISE_PAUSE_CAP;

if (advertise & ADVERTISED_Asym_Pause)

adv |= ADVERTISE_PAUSE_ASYM;

 

// if (advertise & ADVERTISED_1000baseX_Half)

//         adv |= ADVERTISE_1000XHALF;

// if (advertise & ADVERTISED_1000baseX_Full)

//         adv |= ADVERTISE_1000XFULL;

if (adv != oldadv) {

err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv);

 

if (err < 0)

return err;

changed = 1;

}

adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);

 

debug("Autonegotiation Advertisement register(%x), value = 0x%x\n", MII_ADVERTISE, adv);

 

adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);

//debug("MII_CTRL1000  111111 = 0x%x\n", adv);

 

/* Configure gigabit if it's supported */

if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) {

//disable 1000baseT

adv = 0x0;

err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv);

if(err < 0) {

return err;

}

changed = 1;

// oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);

 

// if (adv < 0)

//         return adv;

 

// adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);

// if (advertise & SUPPORTED_1000baseT_Half)

//         adv |= ADVERTISE_1000HALF;

// if (advertise & SUPPORTED_1000baseT_Full)

//         adv |= ADVERTISE_1000FULL;

 

// if (adv != oldadv) {

//         err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000,

//                         adv);

 

//         if (err < 0)

//                 return err;

//         changed = 1;

// }

}

 

 

phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000,0x0);

adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);

debug("1000BASE-T Status(%x),value:0x%x\n", MII_STAT1000, adv);

 

return changed;

}

 

static int zx5201_config_aneg(struct phy_device *phydev)

{

int result;

 

if (AUTONEG_ENABLE != phydev->autoneg)

return zx5201_setup_forced(phydev);

 

result = zx5201_config_advert(phydev);

 

if (result < 0) /* error */

return result;

 

if (result == 0) {

/* Advertisment hasn't changed, but maybe aneg was never on to

 * begin with?  Or maybe phy was isolated? */

int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);

 

if (ctl < 0)

return ctl;

 

if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))

result = 1; /* do restart aneg */

}

 

/* Only restart aneg if we are advertising something different

 * than we were before.         */

 

 

//print_phy_status(phydev);

 

if (result > 0)

result = genphy_restart_aneg(phydev);

 

//print_phy_status(phydev);

//genphy_restart_aneg(phydev);

 

 

//        phy_write(phydev,MDIO_DEVAD_NONE, MII_BMCR,0x3100);

 

__udelay(1000000);

print_zx5201_phy_status(phydev);

 

return result;

}

 

static int zx5201_config(struct phy_device *phydev)

{

unsigned int val, delay;

int ret;

 

/*enable 100mbits and full duplex*/

ret = phy_write(phydev,MDIO_DEVAD_NONE, MII_ZX5201_BMCR, (ZX_SPEED100 | ZX_AUTOONEG | ZX_SPEED_FULL));

val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ZX5201_BMCR);

debug("read result from control register(%x), result:%x\n",  MII_ZX5201_BMCR, val);

 

if (phy_interface_is_rgmii(phydev)) {

debug("phy interface is rgmii\n");

 

ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_ZX5201_PHYCTRL,

(FIFO_DEPTH << ZX5201_PHYCR_FIFO_DEPTH_SHIFT));

if (ret)

return ret;

 

if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) &&

(phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) {

val = phy_read_mmd_indirect(phydev, ZX5201_RGMIICTL,

ZX5201_DEVADDR, phydev->addr);

 

if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)

val |= (ZX5201_RGMII_TX_CLK_DELAY_EN |

ZX5201_RGMII_RX_CLK_DELAY_EN);

 

if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)

val |= ZX5201_RGMII_TX_CLK_DELAY_EN;

 

if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)

val |= ZX5201_RGMII_RX_CLK_DELAY_EN;

 

phy_write_mmd_indirect(phydev, ZX5201_RGMIICTL,

ZX5201_DEVADDR, phydev->addr, val);

 

delay = (RX_ID_DELAY |

(TX_ID_DELAY << ZX5201_RGMII_TX_CLK_DELAY_SHIFT));

 

phy_write_mmd_indirect(phydev, ZX5201_RGMIIDCTL,

ZX5201_DEVADDR, phydev->addr, delay);

}

}

zx5201_config_aneg(phydev);

//__udelay(10000000);

return 0;

}

 

static int zx5201_update_link(struct phy_device *phydev)

{

unsigned int mii_reg;

int val = 0;

debug("enter zx5201_update_link()\n");

/*

 * Wait if the link is up, and autonegotiation is in progress

 * (ie - we're capable and it's not done)

 */

mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

 

//debug("MII_BMSR = 0x%x\n", mii_reg);

 

val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);

//debug("MII_BMCR = 0x%x\n", val);

/*

 * If we already saw the link up, and it hasn't gone down, then

 * we don't need to wait for autoneg again

 */

if (phydev->link && mii_reg & BMSR_LSTATUS)

return 0;

 

if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {

int i = 0;

 

printf("%s Waiting for PHY auto negotiation to complete", phydev->dev->name);

while (!(mii_reg & BMSR_ANEGCOMPLETE)) {

/*

 * Timeout reached ?

 */

if (i > PHY_ANEG_TIMEOUT) {

printf(" TIMEOUT !\n");

phydev->link = 0;

return 0;

}

 

if (ctrlc()) {

puts("user interrupt!\n");

phydev->link = 0;

return -EINTR;

}

 

if ((i++ % 500) == 0)

printf(".");

 

udelay(1000);        /* 1 ms */

mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

}

printf(" done\n");

phydev->link = 1;

} else {

/* Read the link a second time to clear the latched state */

mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

 

if (mii_reg & BMSR_LSTATUS)

phydev->link = 1;

else

phydev->link = 0;

}

 

debug("update_link, phydev->link:%d\n", phydev->link);

return 0;

}

 

static int zx5201_parse_link(struct phy_device *phydev)

{

int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

 

/* We're using autonegotiation */

if (phydev->supported & SUPPORTED_Autoneg) {

debug("phy support SUPPORTED_Autoneg\n");

u32 lpa = 0;

int gblpa = 0;

u32 estatus = 0;

 

/* Check for gigabit capability */

if (phydev->supported & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {

/* We want a list of states supported by

 * both PHYs in the link

 */

gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);

if (gblpa < 0) {

debug("Could not read MII_STAT1000. Ignoring gigabit capability\n");

gblpa = 0;

}

gblpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000) << 2;

}

 

/* Set the baseline so we only have to set them

 * if they're different

 */

phydev->speed = SPEED_100;

phydev->duplex = DUPLEX_FULL;

 

/* Check the gigabit fields */

if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD) ) {

//disable 1000M

phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000,0x0);

return 0;

}

 

lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);

lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);

 

if (lpa & (LPA_100FULL | LPA_100HALF)) {

phydev->speed = SPEED_100;

 

if (lpa & LPA_100FULL)

phydev->duplex = DUPLEX_FULL;

 

} else if (lpa & LPA_10FULL)

phydev->duplex = DUPLEX_FULL;

 

/*

 * Extended status may indicate that the PHY supports

 * 1000BASE-T/X even though the 1000BASE-T registers

 * are missing. In this case we can't tell whether the

 * peer also supports it, so we only check extended

 * status if the 1000BASE-T registers are actually

 * missing.

 */

if ((mii_reg & BMSR_ESTATEN) && !(mii_reg & BMSR_ERCAP))

estatus = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);

 

if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF | ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) {

debug("the extended status register(0xf) status support 1000M, we will disable it\n");

phydev->speed = SPEED_1000;

if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL)) {

phydev->duplex = DUPLEX_FULL;

}

}

 

} else {

u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);

 

phydev->speed = SPEED_100;

phydev->duplex = DUPLEX_HALF;

 

if (bmcr & BMCR_FULLDPLX)

phydev->duplex = DUPLEX_FULL;

 

if (bmcr & BMCR_SPEED1000)

phydev->speed = SPEED_1000;

else if (bmcr & BMCR_SPEED100)

phydev->speed = SPEED_100;

}

print_zx5201_phy_status(phydev);

 

return 0;

}

 

static int zx5201_startup(struct phy_device *phydev)

{

zx5201_update_link(phydev);

zx5201_parse_link(phydev);

return 0;

}

 

 

static struct phy_driver zx5201_driver = {

.name = "ZX5201 PHY",

.uid = 0x84b90000,

.mask = 0xffff0000,

.features = PHY_GBIT_FEATURES,

.config = &zx5201_config,

.startup = &zx5201_startup,

.shutdown = &genphy_shutdown,

};

 

int phy_zx5201_init(void)

{

debug("enter phy_zx5201_init()\n");

 

phy_register(&zx5201_driver);

return 0;

}

展开阅读全文
amp
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部