net: phy: add support for Marvell PHY drivers
This adds initial support for Marvell PHY specific drivers. As a first PHY, a driver for MV88E1121R is ported over from Linux. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
47fc75bc77
commit
f38c708c2d
|
@ -18,6 +18,11 @@ config LXT_PHY
|
|||
---help---
|
||||
Currently supports the lxt971 PHY.
|
||||
|
||||
config MARVELL_PHY
|
||||
tristate "Drivers for Marvell PHYs"
|
||||
---help---
|
||||
Add support for various Marvell PHYs (e.g. 88E1121R).
|
||||
|
||||
config MICREL_PHY
|
||||
bool "Driver for Micrel PHYs"
|
||||
---help---
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
obj-y += phy.o mdio_bus.o
|
||||
obj-$(CONFIG_AT803X_PHY) += at803x.o
|
||||
obj-$(CONFIG_LXT_PHY) += lxt.o
|
||||
obj-$(CONFIG_MARVELL_PHY) += marvell.o
|
||||
obj-$(CONFIG_MICREL_PHY) += micrel.o
|
||||
obj-$(CONFIG_NATIONAL_PHY) += national.o
|
||||
obj-$(CONFIG_SMSC_PHY) += smsc.o
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/* Known PHY IDs */
|
||||
#define MARVELL_PHY_ID_88E1101 0x01410c60
|
||||
#define MARVELL_PHY_ID_88E1112 0x01410c90
|
||||
#define MARVELL_PHY_ID_88E1111 0x01410cc0
|
||||
#define MARVELL_PHY_ID_88E1118 0x01410e10
|
||||
#define MARVELL_PHY_ID_88E1121R 0x01410cb0
|
||||
#define MARVELL_PHY_ID_88E1145 0x01410cd0
|
||||
#define MARVELL_PHY_ID_88E1149R 0x01410e50
|
||||
#define MARVELL_PHY_ID_88E1240 0x01410e30
|
||||
#define MARVELL_PHY_ID_88E1318S 0x01410e90
|
||||
#define MARVELL_PHY_ID_88E1116R 0x01410e40
|
||||
#define MARVELL_PHY_ID_88E1510 0x01410dd0
|
||||
|
||||
/* Mask used for ID comparisons */
|
||||
#define MARVELL_PHY_ID_MASK 0xfffffff0
|
||||
|
||||
/* 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)
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static struct phy_driver marvell_phys[] = {
|
||||
{
|
||||
.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,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init marvell_phy_init(void)
|
||||
{
|
||||
return phy_drivers_register(marvell_phys, ARRAY_SIZE(marvell_phys));
|
||||
}
|
||||
fs_initcall(marvell_phy_init);
|
Loading…
Reference in New Issue