2014-06-24 10:18:11 +00:00
|
|
|
/*
|
|
|
|
* drivers/net/phy/marvell.c
|
|
|
|
*
|
|
|
|
* Driver for Marvell PHYs based on Linux driver
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <init.h>
|
|
|
|
#include <linux/mii.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/phy.h>
|
|
|
|
#include <linux/smscphy.h>
|
2014-09-16 19:22:01 +00:00
|
|
|
#include <linux/marvell_phy.h>
|
2014-06-24 10:18:11 +00:00
|
|
|
|
|
|
|
/* Marvell Register Page register */
|
|
|
|
#define MII_MARVELL_PHY_PAGE 22
|
|
|
|
#define MII_MARVELL_PHY_DEFAULT_PAGE 0
|
|
|
|
|
|
|
|
#define MII_M1011_PHY_SCR 0x10
|
|
|
|
#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060
|
|
|
|
|
|
|
|
#define MII_M1011_PHY_STATUS 0x11
|
|
|
|
#define MII_M1011_PHY_STATUS_1000 BIT(15)
|
|
|
|
#define MII_M1011_PHY_STATUS_100 BIT(14)
|
|
|
|
#define MII_M1011_PHY_STATUS_SPD_MASK \
|
|
|
|
(MII_M1011_PHY_STATUS_1000 | MII_M1011_PHY_STATUS_100)
|
|
|
|
#define MII_M1011_PHY_STATUS_FULLDUPLEX BIT(13)
|
|
|
|
#define MII_M1011_PHY_STATUS_RESOLVED BIT(11)
|
|
|
|
#define MII_M1011_PHY_STATUS_LINK BIT(10)
|
|
|
|
|
|
|
|
#define MII_88E1121_PHY_MSCR_PAGE 2
|
|
|
|
#define MII_88E1121_PHY_MSCR 0x15
|
|
|
|
#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
|
|
|
|
#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
|
|
|
|
#define MII_88E1121_PHY_MSCR_DELAY_MASK \
|
|
|
|
(MII_88E1121_PHY_MSCR_RX_DELAY | MII_88E1121_PHY_MSCR_TX_DELAY)
|
|
|
|
|
2015-04-20 20:11:13 +00:00
|
|
|
#define MII_88E1318S_PHY_MSCR1_REG 16
|
|
|
|
#define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6)
|
|
|
|
|
2014-11-10 18:19:45 +00:00
|
|
|
#define MII_88E1540_LED_PAGE 0x3
|
|
|
|
#define MII_88E1540_LED_CONTROL 0x10
|
|
|
|
|
|
|
|
#define MII_88E1540_QSGMII_PAGE 0x4
|
|
|
|
#define MII_88E1540_QSGMII_CONTROL 0x0
|
|
|
|
#define MII_88E1540_QSGMII_AUTONEG_EN BIT(12)
|
|
|
|
|
2014-06-24 10:18:11 +00:00
|
|
|
/*
|
|
|
|
* marvell_read_status
|
|
|
|
*
|
|
|
|
* Generic status code does not detect Fiber correctly!
|
|
|
|
* Description:
|
|
|
|
* Check the link, then figure out the current state
|
|
|
|
* by comparing what we advertise with what the link partner
|
|
|
|
* advertises. Start by checking the gigabit possibilities,
|
|
|
|
* then move on to 10/100.
|
|
|
|
*/
|
|
|
|
static int marvell_read_status(struct phy_device *phydev)
|
|
|
|
{
|
|
|
|
int adv;
|
|
|
|
int err;
|
|
|
|
int lpa;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
/* Update the link, but return if there
|
|
|
|
* was an error */
|
|
|
|
err = genphy_update_link(phydev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (AUTONEG_ENABLE == phydev->autoneg) {
|
|
|
|
status = phy_read(phydev, MII_M1011_PHY_STATUS);
|
|
|
|
if (status < 0)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
lpa = phy_read(phydev, MII_LPA);
|
|
|
|
if (lpa < 0)
|
|
|
|
return lpa;
|
|
|
|
|
|
|
|
adv = phy_read(phydev, MII_ADVERTISE);
|
|
|
|
if (adv < 0)
|
|
|
|
return adv;
|
|
|
|
|
|
|
|
lpa &= adv;
|
|
|
|
|
|
|
|
if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
|
|
|
|
phydev->duplex = DUPLEX_FULL;
|
|
|
|
else
|
|
|
|
phydev->duplex = DUPLEX_HALF;
|
|
|
|
|
|
|
|
status = status & MII_M1011_PHY_STATUS_SPD_MASK;
|
|
|
|
phydev->pause = phydev->asym_pause = 0;
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case MII_M1011_PHY_STATUS_1000:
|
|
|
|
phydev->speed = SPEED_1000;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MII_M1011_PHY_STATUS_100:
|
|
|
|
phydev->speed = SPEED_100;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
phydev->speed = SPEED_10;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phydev->duplex == DUPLEX_FULL) {
|
|
|
|
phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
|
|
|
|
phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int bmcr = phy_read(phydev, MII_BMCR);
|
|
|
|
|
|
|
|
if (bmcr < 0)
|
|
|
|
return bmcr;
|
|
|
|
|
|
|
|
if (bmcr & BMCR_FULLDPLX)
|
|
|
|
phydev->duplex = DUPLEX_FULL;
|
|
|
|
else
|
|
|
|
phydev->duplex = DUPLEX_HALF;
|
|
|
|
|
|
|
|
if (bmcr & BMCR_SPEED1000)
|
|
|
|
phydev->speed = SPEED_1000;
|
|
|
|
else if (bmcr & BMCR_SPEED100)
|
|
|
|
phydev->speed = SPEED_100;
|
|
|
|
else
|
|
|
|
phydev->speed = SPEED_10;
|
|
|
|
|
|
|
|
phydev->pause = phydev->asym_pause = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-10 18:19:45 +00:00
|
|
|
static int m88e1540_config_init(struct phy_device *phydev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Configure QSGMII auto-negotiation */
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) {
|
|
|
|
ret = phy_write(phydev, MII_MARVELL_PHY_PAGE,
|
|
|
|
MII_88E1540_QSGMII_PAGE);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
reg = phy_read(phydev, MII_88E1540_QSGMII_CONTROL);
|
|
|
|
ret = phy_write(phydev, MII_88E1540_QSGMII_CONTROL,
|
|
|
|
reg | MII_88E1540_QSGMII_AUTONEG_EN);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure LED as:
|
|
|
|
* Activity: Blink
|
|
|
|
* Link: On
|
|
|
|
* No Link: Off
|
|
|
|
*/
|
|
|
|
phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1540_LED_PAGE);
|
|
|
|
phy_write(phydev, MII_88E1540_LED_CONTROL, 0x1111);
|
|
|
|
|
|
|
|
/* Power-up the PHY. When going from power down to normal operation,
|
|
|
|
* software reset and auto-negotiation restart are also performed.
|
|
|
|
*/
|
|
|
|
ret = phy_write(phydev, MII_MARVELL_PHY_PAGE,
|
|
|
|
MII_MARVELL_PHY_DEFAULT_PAGE);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
ret = phy_write(phydev, MII_BMCR,
|
|
|
|
phy_read(phydev, MII_BMCR) & ~BMCR_PDOWN);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-24 10:18:11 +00:00
|
|
|
static int m88e1121_config_init(struct phy_device *phydev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = phy_write(phydev, MII_MARVELL_PHY_PAGE,
|
|
|
|
MII_88E1121_PHY_MSCR_PAGE);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Setup RGMII TX/RX delay */
|
|
|
|
reg = phy_read(phydev, MII_88E1121_PHY_MSCR) &
|
|
|
|
~MII_88E1121_PHY_MSCR_DELAY_MASK;
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
|
|
|
|
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
|
|
|
|
reg |= MII_88E1121_PHY_MSCR_RX_DELAY;
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
|
|
|
|
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
|
|
|
|
reg |= MII_88E1121_PHY_MSCR_TX_DELAY;
|
|
|
|
ret = phy_write(phydev, MII_88E1121_PHY_MSCR, reg);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_PHY_DEFAULT_PAGE);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Enable auto-crossover */
|
|
|
|
ret = phy_write(phydev, MII_M1011_PHY_SCR,
|
|
|
|
MII_M1011_PHY_SCR_AUTO_CROSS);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Reset PHY */
|
|
|
|
ret = phy_write(phydev, MII_BMCR,
|
|
|
|
phy_read(phydev, MII_BMCR) | BMCR_RESET);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-20 20:11:13 +00:00
|
|
|
static int m88e1318s_config_init(struct phy_device *phydev)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = phy_write(phydev, MII_MARVELL_PHY_PAGE,
|
|
|
|
MII_88E1121_PHY_MSCR_PAGE);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
reg = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG);
|
|
|
|
reg |= MII_88E1318S_PHY_MSCR1_PAD_ODD;
|
|
|
|
ret = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, reg);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return m88e1121_config_init(phydev);
|
|
|
|
}
|
|
|
|
|
2014-06-24 10:18:11 +00:00
|
|
|
static struct phy_driver marvell_phys[] = {
|
2014-11-10 18:19:44 +00:00
|
|
|
{
|
|
|
|
.phy_id = MARVELL_PHY_ID_88E1121R,
|
|
|
|
.phy_id_mask = MARVELL_PHY_ID_MASK,
|
|
|
|
.drv.name = "Marvell 88E1121R",
|
|
|
|
.features = PHY_GBIT_FEATURES,
|
|
|
|
.config_init = m88e1121_config_init,
|
|
|
|
.config_aneg = genphy_config_aneg,
|
|
|
|
.read_status = marvell_read_status,
|
|
|
|
},
|
2015-04-20 20:11:13 +00:00
|
|
|
{
|
|
|
|
.phy_id = MARVELL_PHY_ID_88E1318S,
|
|
|
|
.phy_id_mask = MARVELL_PHY_ID_MASK,
|
|
|
|
.drv.name = "Marvell 88E1318S",
|
|
|
|
.features = PHY_GBIT_FEATURES,
|
|
|
|
.config_init = m88e1318s_config_init,
|
|
|
|
.config_aneg = genphy_config_aneg,
|
|
|
|
.read_status = marvell_read_status,
|
|
|
|
},
|
2014-11-10 18:19:46 +00:00
|
|
|
{
|
|
|
|
.phy_id = MARVELL_PHY_ID_88E1543,
|
|
|
|
.phy_id_mask = MARVELL_PHY_ID_MASK,
|
|
|
|
.drv.name = "Marvell 88E1543",
|
|
|
|
.features = PHY_GBIT_FEATURES,
|
|
|
|
.config_init = m88e1540_config_init,
|
|
|
|
.config_aneg = genphy_config_aneg,
|
|
|
|
.read_status = marvell_read_status,
|
|
|
|
},
|
2014-11-10 18:19:45 +00:00
|
|
|
{
|
|
|
|
.phy_id = MARVELL_PHY_ID_88E1545,
|
|
|
|
.phy_id_mask = MARVELL_PHY_ID_MASK,
|
|
|
|
.drv.name = "Marvell 88E1545",
|
|
|
|
.features = PHY_GBIT_FEATURES,
|
|
|
|
.config_init = m88e1540_config_init,
|
|
|
|
.config_aneg = genphy_config_aneg,
|
|
|
|
.read_status = marvell_read_status,
|
|
|
|
},
|
2014-06-24 10:18:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init marvell_phy_init(void)
|
|
|
|
{
|
|
|
|
return phy_drivers_register(marvell_phys, ARRAY_SIZE(marvell_phys));
|
|
|
|
}
|
|
|
|
fs_initcall(marvell_phy_init);
|