summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig2
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/aquantia.c241
-rw-r--r--drivers/net/phy/dp83867.c (renamed from drivers/net/phy/ti.c)243
-rw-r--r--drivers/net/phy/micrel_ksz8xxx.c21
-rw-r--r--drivers/net/phy/mv88e61xx.c226
-rw-r--r--drivers/net/phy/phy.c6
7 files changed, 586 insertions, 155 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index bcea8a0c3e..dceea1516f 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -46,7 +46,7 @@ config B53_PHY_PORTS
endif # B53_SWITCH
config MV88E61XX_SWITCH
- bool "Marvel MV88E61xx Ethernet switch PHY support."
+ bool "Marvell MV88E61xx Ethernet switch PHY support."
if MV88E61XX_SWITCH
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 76b6197009..78955c57a8 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -25,7 +25,7 @@ obj-$(CONFIG_PHY_NATSEMI) += natsemi.o
obj-$(CONFIG_PHY_REALTEK) += realtek.o
obj-$(CONFIG_PHY_SMSC) += smsc.o
obj-$(CONFIG_PHY_TERANETICS) += teranetics.o
-obj-$(CONFIG_PHY_TI) += ti.o
+obj-$(CONFIG_PHY_TI) += dp83867.o
obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
obj-$(CONFIG_PHY_VITESSE) += vitesse.o
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index 465ec2d342..c4bd443001 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -21,6 +21,7 @@
#define AQUNTIA_SPEED_MSB_MASK 0x40
#define AQUANTIA_SYSTEM_INTERFACE_SR 0xe812
+#define AQUANTIA_SYSTEM_INTERFACE_SR_READY BIT(0)
#define AQUANTIA_VENDOR_PROVISIONING_REG 0xC441
#define AQUANTIA_FIRMWARE_ID 0x20
#define AQUANTIA_RESERVED_STATUS 0xc885
@@ -33,10 +34,16 @@
#define AQUANTIA_SI_USXGMII 0x0018
/* registers in MDIO_MMD_VEND1 region */
+#define AQUANTIA_VND1_GLOBAL_SC 0x000
+#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb)
+
#define GLOBAL_FIRMWARE_ID 0x20
#define GLOBAL_FAULT 0xc850
#define GLOBAL_RSTATUS_1 0xc885
+#define GLOBAL_ALARM_1 0xcc00
+#define SYSTEM_READY_BIT 0x40
+
#define GLOBAL_STANDARD_CONTROL 0x0
#define SOFT_RESET BIT(15)
#define LOW_POWER BIT(11)
@@ -60,6 +67,36 @@
#define UP_RUN_STALL_OVERRIDE BIT(6)
#define UP_RUN_STALL BIT(0)
+#define AQUANTIA_PMA_RX_VENDOR_P1 0xe400
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_MSK GENMASK(1, 0)
+/* MDI reversal configured through registers */
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_CFG BIT(1)
+/* MDI reversal enabled */
+#define AQUANTIA_PMA_RX_VENDOR_P1_MDI_REV BIT(0)
+
+/*
+ * global start rate, the protocol associated with this speed is used by default
+ * on SI.
+ */
+#define AQUANTIA_VND1_GSTART_RATE 0x31a
+#define AQUANTIA_VND1_GSTART_RATE_OFF 0
+#define AQUANTIA_VND1_GSTART_RATE_100M 1
+#define AQUANTIA_VND1_GSTART_RATE_1G 2
+#define AQUANTIA_VND1_GSTART_RATE_10G 3
+#define AQUANTIA_VND1_GSTART_RATE_2_5G 4
+#define AQUANTIA_VND1_GSTART_RATE_5G 5
+
+/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
+#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b
+#define AQUANTIA_VND1_GSYSCFG_100M 0
+#define AQUANTIA_VND1_GSYSCFG_1G 1
+#define AQUANTIA_VND1_GSYSCFG_2_5G 2
+#define AQUANTIA_VND1_GSYSCFG_5G 3
+#define AQUANTIA_VND1_GSYSCFG_10G 4
+
+#define AQUANTIA_VND1_SMBUS0 0xc485
+#define AQUANTIA_VND1_SMBUS1 0xc495
+
/* addresses of memory segments in the phy */
#define DRAM_BASE_ADDR 0x3FFE0000
#define IRAM_BASE_ADDR 0x40000000
@@ -69,6 +106,12 @@
#define VERSION_STRING_OFFSET 0x0200
#define HEADER_OFFSET 0x300
+/* driver private data */
+#define AQUANTIA_NA 0
+#define AQUANTIA_GEN1 1
+#define AQUANTIA_GEN2 2
+#define AQUANTIA_GEN3 3
+
#pragma pack(1)
struct fw_header {
u8 padding[4];
@@ -254,10 +297,128 @@ static int aquantia_upload_firmware(struct phy_device *phydev)
}
#endif
+struct {
+ u16 syscfg;
+ int cnt;
+ u16 start_rate;
+} aquantia_syscfg[PHY_INTERFACE_MODE_COUNT] = {
+ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
+ AQUANTIA_VND1_GSTART_RATE_1G},
+ [PHY_INTERFACE_MODE_SGMII_2500] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
+ AQUANTIA_VND1_GSTART_RATE_2_5G},
+ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+ [PHY_INTERFACE_MODE_XFI] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G,
+ AQUANTIA_VND1_GSTART_RATE_10G},
+};
+
+static int aquantia_set_proto(struct phy_device *phydev)
+{
+ int i;
+
+ if (!aquantia_syscfg[phydev->interface].cnt)
+ return 0;
+
+ /* set the default rate to enable the SI link */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+ aquantia_syscfg[phydev->interface].start_rate);
+
+ /* set selected protocol for all relevant line side link speeds */
+ for (i = 0; i <= aquantia_syscfg[phydev->interface].cnt; i++)
+ phy_write(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i,
+ aquantia_syscfg[phydev->interface].syscfg);
+ return 0;
+}
+
+static int aquantia_dts_config(struct phy_device *phydev)
+{
+#ifdef CONFIG_DM_ETH
+ ofnode node = phydev->node;
+ u32 prop;
+ u16 reg;
+
+ /* this code only works on gen2 and gen3 PHYs */
+ if (phydev->drv->data != AQUANTIA_GEN2 &&
+ phydev->drv->data != AQUANTIA_GEN3)
+ return -ENOTSUPP;
+
+ if (!ofnode_valid(node))
+ return 0;
+
+ if (!ofnode_read_u32(node, "mdi-reversal", &prop)) {
+ debug("mdi-reversal = %d\n", (int)prop);
+ reg = phy_read(phydev, MDIO_MMD_PMAPMD,
+ AQUANTIA_PMA_RX_VENDOR_P1);
+ reg &= ~AQUANTIA_PMA_RX_VENDOR_P1_MDI_MSK;
+ reg |= AQUANTIA_PMA_RX_VENDOR_P1_MDI_CFG;
+ reg |= prop ? AQUANTIA_PMA_RX_VENDOR_P1_MDI_REV : 0;
+ phy_write(phydev, MDIO_MMD_PMAPMD, AQUANTIA_PMA_RX_VENDOR_P1,
+ reg);
+ }
+ if (!ofnode_read_u32(node, "smb-addr", &prop)) {
+ debug("smb-addr = %x\n", (int)prop);
+ /*
+ * there are two addresses here, normally just one bus would
+ * be in use so we're setting both regs using the same DT
+ * property.
+ */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_SMBUS0,
+ (u16)(prop << 1));
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_SMBUS1,
+ (u16)(prop << 1));
+ }
+
+#endif
+ return 0;
+}
+
+static bool aquantia_link_is_up(struct phy_device *phydev)
+{
+ u16 reg, regmask;
+ int devad, regnum;
+
+ /*
+ * On Gen 2 and 3 we have a bit that indicates that both system and
+ * line side are ready for data, use that if possible.
+ */
+ if (phydev->drv->data == AQUANTIA_GEN2 ||
+ phydev->drv->data == AQUANTIA_GEN3) {
+ devad = MDIO_MMD_PHYXS;
+ regnum = AQUANTIA_SYSTEM_INTERFACE_SR;
+ regmask = AQUANTIA_SYSTEM_INTERFACE_SR_READY;
+ } else {
+ devad = MDIO_MMD_AN;
+ regnum = MDIO_STAT1;
+ regmask = MDIO_AN_STAT1_COMPLETE;
+ }
+ /* the register should be latched, do a double read */
+ phy_read(phydev, devad, regnum);
+ reg = phy_read(phydev, devad, regnum);
+
+ return !!(reg & regmask);
+}
+
int aquantia_config(struct phy_device *phydev)
{
+ int interface = phydev->interface;
u32 val, id, rstatus, fault;
u32 reg_val1 = 0;
+ int num_retries = 5;
+ int usx_an = 0;
+
+ /*
+ * check if the system is out of reset and init sequence completed.
+ * chip-wide reset for gen1 quad phys takes longer
+ */
+ while (--num_retries) {
+ rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_ALARM_1);
+ if (rstatus & SYSTEM_READY_BIT)
+ break;
+ mdelay(10);
+ }
id = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FIRMWARE_ID);
rstatus = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_RSTATUS_1);
@@ -278,17 +439,57 @@ int aquantia_config(struct phy_device *phydev)
if (ret != 0)
return ret;
}
+ /*
+ * for backward compatibility convert XGMII into either XFI or USX based
+ * on FW config
+ */
+ if (interface == PHY_INTERFACE_MODE_XGMII) {
+ reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_SYSTEM_INTERFACE_SR);
+ if ((reg_val1 & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII)
+ interface = PHY_INTERFACE_MODE_USXGMII;
+ else
+ interface = PHY_INTERFACE_MODE_XFI;
+ }
+
+ /*
+ * if link is up already we can just use it, otherwise configure
+ * the protocols in the PHY. If link is down set the system
+ * interface protocol to use based on phydev->interface
+ */
+ if (!aquantia_link_is_up(phydev) &&
+ (phydev->drv->data == AQUANTIA_GEN2 ||
+ phydev->drv->data == AQUANTIA_GEN3)) {
+ /* set PHY in low power mode so we can configure protocols */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
+ AQUANTIA_VND1_GLOBAL_SC_LP);
+ mdelay(10);
+
+ /* configure protocol based on phydev->interface */
+ aquantia_set_proto(phydev);
+ /* apply custom configuration based on DT */
+ aquantia_dts_config(phydev);
+
+ /* wake PHY back up */
+ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
+ mdelay(10);
+ }
val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR);
- if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
/* 1000BASE-T mode */
phydev->advertising = SUPPORTED_1000baseT_Full;
phydev->supported = phydev->advertising;
val = (val & ~AQUNTIA_SPEED_LSB_MASK) | AQUNTIA_SPEED_MSB_MASK;
phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
- } else if (phydev->interface == PHY_INTERFACE_MODE_XGMII) {
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ usx_an = 1;
+ /* FALLTHROUGH */
+ case PHY_INTERFACE_MODE_XFI:
/* 10GBASE-T mode */
phydev->advertising = SUPPORTED_10000baseT_Full;
phydev->supported = phydev->advertising;
@@ -299,40 +500,40 @@ int aquantia_config(struct phy_device *phydev)
AQUNTIA_SPEED_LSB_MASK |
AQUNTIA_SPEED_MSB_MASK);
- val = phy_read(phydev, MDIO_MMD_PHYXS,
- AQUANTIA_SYSTEM_INTERFACE_SR);
/* If SI is USXGMII then start USXGMII autoneg */
- if ((val & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) {
- reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS,
- AQUANTIA_VENDOR_PROVISIONING_REG);
+ reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_VENDOR_PROVISIONING_REG);
+ if (usx_an) {
reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA;
-
- phy_write(phydev, MDIO_MMD_PHYXS,
- AQUANTIA_VENDOR_PROVISIONING_REG,
- reg_val1);
printf("%s: system interface USXGMII\n",
phydev->dev->name);
} else {
+ reg_val1 &= ~AQUANTIA_USX_AUTONEG_CONTROL_ENA;
printf("%s: system interface XFI\n",
phydev->dev->name);
}
- } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) {
+ phy_write(phydev, MDIO_MMD_PHYXS,
+ AQUANTIA_VENDOR_PROVISIONING_REG, reg_val1);
+ break;
+ case PHY_INTERFACE_MODE_SGMII_2500:
/* 2.5GBASE-T mode */
phydev->advertising = SUPPORTED_1000baseT_Full;
phydev->supported = phydev->advertising;
phy_write(phydev, MDIO_MMD_AN, AQUNTIA_10G_CTL, 1);
phy_write(phydev, MDIO_MMD_AN, AQUNTIA_VENDOR_P1, 0x9440);
- } else if (phydev->interface == PHY_INTERFACE_MODE_MII) {
+ break;
+ case PHY_INTERFACE_MODE_MII:
/* 100BASE-TX mode */
phydev->advertising = SUPPORTED_100baseT_Full;
phydev->supported = phydev->advertising;
val = (val & ~AQUNTIA_SPEED_MSB_MASK) | AQUNTIA_SPEED_LSB_MASK;
phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val);
- }
+ break;
+ };
val = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_RESERVED_STATUS);
reg_val1 = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_FIRMWARE_ID);
@@ -354,17 +555,14 @@ int aquantia_startup(struct phy_device *phydev)
phydev->duplex = DUPLEX_FULL;
/* if the AN is still in progress, wait till timeout. */
- phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
- reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
- if (!(reg & MDIO_AN_STAT1_COMPLETE)) {
+ if (!aquantia_link_is_up(phydev)) {
printf("%s Waiting for PHY auto negotiation to complete",
phydev->dev->name);
do {
udelay(1000);
- reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1);
if ((i++ % 500) == 0)
printf(".");
- } while (!(reg & MDIO_AN_STAT1_COMPLETE) &&
+ } while (!aquantia_link_is_up(phydev) &&
i < (4 * PHY_ANEG_TIMEOUT));
if (i > PHY_ANEG_TIMEOUT)
@@ -433,6 +631,7 @@ struct phy_driver aqr105_driver = {
.config = &aquantia_config,
.startup = &aquantia_startup,
.shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN1,
};
struct phy_driver aqr106_driver = {
@@ -459,6 +658,7 @@ struct phy_driver aqr107_driver = {
.config = &aquantia_config,
.startup = &aquantia_startup,
.shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN2,
};
struct phy_driver aqr112_driver = {
@@ -472,6 +672,7 @@ struct phy_driver aqr112_driver = {
.config = &aquantia_config,
.startup = &aquantia_startup,
.shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN3,
};
struct phy_driver aqr405_driver = {
@@ -485,6 +686,7 @@ struct phy_driver aqr405_driver = {
.config = &aquantia_config,
.startup = &aquantia_startup,
.shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN1,
};
struct phy_driver aqr412_driver = {
@@ -498,6 +700,7 @@ struct phy_driver aqr412_driver = {
.config = &aquantia_config,
.startup = &aquantia_startup,
.shutdown = &gen10g_shutdown,
+ .data = AQUANTIA_GEN3,
};
int phy_aquantia_init(void)
diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/dp83867.c
index 7509936465..a43793cd42 100644
--- a/drivers/net/phy/ti.c
+++ b/drivers/net/phy/dp83867.c
@@ -25,6 +25,7 @@
#define DP83867_CFG4 0x0031
#define DP83867_RGMIICTL 0x0032
#define DP83867_STRAP_STS1 0x006E
+#define DP83867_STRAP_STS2 0x006f
#define DP83867_RGMIIDCTL 0x0086
#define DP83867_IO_MUX_CFG 0x0170
@@ -52,18 +53,27 @@
/* STRAP_STS1 bits */
#define DP83867_STRAP_STS1_RESERVED BIT(11)
+/* STRAP_STS2 bits */
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK GENMASK(6, 4)
+#define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT 4
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK GENMASK(2, 0)
+#define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT 0
+#define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2)
+
/* PHY CTRL bits */
#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14
+#define DP83867_PHYCR_FIFO_DEPTH_MASK GENMASK(15, 14)
#define DP83867_PHYCR_RESERVED_MASK BIT(11)
#define DP83867_MDI_CROSSOVER 5
-#define DP83867_MDI_CROSSOVER_AUTO 2
#define DP83867_MDI_CROSSOVER_MDIX 2
#define DP83867_PHYCTRL_SGMIIEN 0x0800
#define DP83867_PHYCTRL_RXFIFO_SHIFT 12
#define DP83867_PHYCTRL_TXFIFO_SHIFT 14
/* RGMIIDCTL bits */
+#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf
#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4
+#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf
/* CFG2 bits */
#define MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040
@@ -74,8 +84,6 @@
#define MII_DP83867_CFG2_MASK 0x003F
/* User setting - can be taken from DTS */
-#define DEFAULT_RX_ID_DELAY DP83867_RGMIIDCTL_2_25_NS
-#define DEFAULT_TX_ID_DELAY DP83867_RGMIIDCTL_2_75_NS
#define DEFAULT_FIFO_DEPTH DP83867_PHYCR_FIFO_DEPTH_4_B_NIB
/* IO_MUX_CFG bits */
@@ -83,6 +91,7 @@
#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0
#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f
+#define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6)
#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8
#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK \
GENMASK(0x1f, DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT)
@@ -97,12 +106,13 @@ enum {
};
struct dp83867_private {
- int rx_id_delay;
- int tx_id_delay;
+ u32 rx_id_delay;
+ u32 tx_id_delay;
int fifo_depth;
int io_impedance;
bool rxctrl_strap_quirk;
int port_mirroring;
+ bool set_clk_output;
unsigned int clk_output_sel;
};
@@ -134,16 +144,28 @@ static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
ofnode node;
- u16 val;
+ int ret;
node = phy_get_ofnode(phydev);
if (!ofnode_valid(node))
return -EINVAL;
- /* Keep the default value if ti,clk-output-sel is not set */
- dp83867->clk_output_sel =
- ofnode_read_u32_default(node, "ti,clk-output-sel",
- DP83867_CLK_O_SEL_REF_CLK);
+ /* Optional configuration */
+ ret = ofnode_read_u32(node, "ti,clk-output-sel",
+ &dp83867->clk_output_sel);
+ /* If not set, keep default */
+ if (!ret) {
+ dp83867->set_clk_output = true;
+ /* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
+ * DP83867_CLK_O_SEL_OFF.
+ */
+ if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK &&
+ dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) {
+ pr_debug("ti,clk-output-sel value %u out of range\n",
+ dp83867->clk_output_sel);
+ return -EINVAL;
+ }
+ }
if (ofnode_read_bool(node, "ti,max-output-impedance"))
dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
@@ -154,13 +176,55 @@ static int dp83867_of_init(struct phy_device *phydev)
if (ofnode_read_bool(node, "ti,dp83867-rxctrl-strap-quirk"))
dp83867->rxctrl_strap_quirk = true;
- dp83867->rx_id_delay = ofnode_read_u32_default(node,
- "ti,rx-internal-delay",
- DEFAULT_RX_ID_DELAY);
- dp83867->tx_id_delay = ofnode_read_u32_default(node,
- "ti,tx-internal-delay",
- DEFAULT_TX_ID_DELAY);
+ /* Existing behavior was to use default pin strapping delay in rgmii
+ * mode, but rgmii should have meant no delay. Warn existing users.
+ */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
+ u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_STRAP_STS2);
+ u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
+ u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
+ DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
+
+ if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
+ rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
+ pr_warn("PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
+ "Should be 'rgmii-id' to use internal delays\n");
+ }
+
+ /* RX delay *must* be specified if internal delay of RX is used. */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ ret = ofnode_read_u32(node, "ti,rx-internal-delay",
+ &dp83867->rx_id_delay);
+ if (ret) {
+ pr_debug("ti,rx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
+ pr_debug("ti,rx-internal-delay value of %u out of range\n",
+ dp83867->rx_id_delay);
+ return -EINVAL;
+ }
+ }
+
+ /* TX delay *must* be specified if internal delay of RX is used. */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ ret = ofnode_read_u32(node, "ti,tx-internal-delay",
+ &dp83867->tx_id_delay);
+ if (ret) {
+ debug("ti,tx-internal-delay must be specified\n");
+ return ret;
+ }
+ if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
+ pr_debug("ti,tx-internal-delay value of %u out of range\n",
+ dp83867->tx_id_delay);
+ return -EINVAL;
+ }
+ }
dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth",
DEFAULT_FIFO_DEPTH);
@@ -170,18 +234,6 @@ static int dp83867_of_init(struct phy_device *phydev)
if (ofnode_read_bool(node, "enet-phy-lane-no-swap"))
dp83867->port_mirroring = DP83867_PORT_MIRRORING_DIS;
-
- /* Clock output selection if muxing property is set */
- if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
- val = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_IO_MUX_CFG);
- val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
- val |= (dp83867->clk_output_sel <<
- DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
- phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_IO_MUX_CFG, val);
- }
-
return 0;
}
#else
@@ -189,8 +241,8 @@ static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
- dp83867->rx_id_delay = DEFAULT_RX_ID_DELAY;
- dp83867->tx_id_delay = DEFAULT_TX_ID_DELAY;
+ dp83867->rx_id_delay = DP83867_RGMIIDCTL_2_25_NS;
+ dp83867->tx_id_delay = DP83867_RGMIIDCTL_2_75_NS;
dp83867->fifo_depth = DEFAULT_FIFO_DEPTH;
dp83867->io_impedance = -EINVAL;
@@ -204,18 +256,11 @@ static int dp83867_config(struct phy_device *phydev)
unsigned int val, delay, cfg2;
int ret, bs;
- if (!phydev->priv) {
- dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL);
- if (!dp83867)
- return -ENOMEM;
+ dp83867 = (struct dp83867_private *)phydev->priv;
- phydev->priv = dp83867;
- ret = dp83867_of_init(phydev);
- if (ret)
- goto err_out;
- } else {
- dp83867 = (struct dp83867_private *)phydev->priv;
- }
+ ret = dp83867_of_init(phydev);
+ if (ret)
+ return ret;
/* Restart the PHY. */
val = phy_read(phydev, MDIO_DEVAD_NONE, DP83867_CTRL);
@@ -232,11 +277,11 @@ static int dp83867_config(struct phy_device *phydev)
}
if (phy_interface_is_rgmii(phydev)) {
- ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
- (DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER) |
- (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT));
- if (ret)
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
+ if (val < 0)
goto err_out;
+ val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK;
+ val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT);
/* The code below checks if "port mirroring" N/A MODE4 has been
* enabled during power on bootstrap.
@@ -248,16 +293,39 @@ static int dp83867_config(struct phy_device *phydev)
* register's bit 11 (marked as RESERVED).
*/
- bs = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_STRAP_STS1);
- val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
- if (bs & DP83867_STRAP_STS1_RESERVED) {
+ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1);
+ if (bs & DP83867_STRAP_STS1_RESERVED)
val &= ~DP83867_PHYCR_RESERVED_MASK;
- phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL,
- val);
- }
- } else if (phy_interface_is_sgmii(phydev)) {
+ ret = phy_write(phydev, MDIO_DEVAD_NONE,
+ MII_DP83867_PHYCTRL, val);
+
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIICTL);
+
+ val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN |
+ DP83867_RGMII_RX_CLK_DELAY_EN);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ val |= (DP83867_RGMII_TX_CLK_DELAY_EN |
+ DP83867_RGMII_RX_CLK_DELAY_EN);
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val |= DP83867_RGMII_TX_CLK_DELAY_EN;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ val |= DP83867_RGMII_RX_CLK_DELAY_EN;
+
+ phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);
+
+ delay = (dp83867->rx_id_delay |
+ (dp83867->tx_id_delay <<
+ DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_RGMIIDCTL, delay);
+ }
+
+ if (phy_interface_is_sgmii(phydev)) {
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
(BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000));
@@ -282,57 +350,62 @@ static int dp83867_config(struct phy_device *phydev)
phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_BISCR, 0x0);
}
- if (phy_interface_is_rgmii(phydev)) {
- val = phy_read_mmd(phydev, DP83867_DEVADDR,
- DP83867_RGMIICTL);
-
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
- val |= (DP83867_RGMII_TX_CLK_DELAY_EN |
- DP83867_RGMII_RX_CLK_DELAY_EN);
-
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
- val |= DP83867_RGMII_TX_CLK_DELAY_EN;
-
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
- val |= DP83867_RGMII_RX_CLK_DELAY_EN;
-
+ if (dp83867->io_impedance >= 0) {
+ val = phy_read_mmd(phydev,
+ DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
+ val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
+ val |= dp83867->io_impedance &
+ DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_RGMIICTL, val);
+ DP83867_IO_MUX_CFG, val);
+ }
- delay = (dp83867->rx_id_delay |
- (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
+ if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
+ dp83867_config_port_mirroring(phydev);
- phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_RGMIIDCTL, delay);
+ /* Clock output selection if muxing property is set */
+ if (dp83867->set_clk_output) {
+ val = phy_read_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG);
- if (dp83867->io_impedance >= 0) {
- val = phy_read_mmd(phydev,
- DP83867_DEVADDR,
- DP83867_IO_MUX_CFG);
- val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
- val |= dp83867->io_impedance &
- DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL;
- phy_write_mmd(phydev, DP83867_DEVADDR,
- DP83867_IO_MUX_CFG, val);
+ if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
+ val |= DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+ } else {
+ val &= ~(DP83867_IO_MUX_CFG_CLK_O_SEL_MASK |
+ DP83867_IO_MUX_CFG_CLK_O_DISABLE);
+ val |= dp83867->clk_output_sel <<
+ DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
}
+ phy_write_mmd(phydev, DP83867_DEVADDR,
+ DP83867_IO_MUX_CFG, val);
}
- if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
- dp83867_config_port_mirroring(phydev);
-
genphy_config_aneg(phydev);
return 0;
err_out:
- kfree(dp83867);
return ret;
}
+static int dp83867_probe(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867;
+
+ dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL);
+ if (!dp83867)
+ return -ENOMEM;
+
+ phydev->priv = dp83867;
+ return 0;
+}
+
static struct phy_driver DP83867_driver = {
.name = "TI DP83867",
.uid = 0x2000a231,
.mask = 0xfffffff0,
.features = PHY_GBIT_FEATURES,
+ .probe = dp83867_probe,
.config = &dp83867_config,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
diff --git a/drivers/net/phy/micrel_ksz8xxx.c b/drivers/net/phy/micrel_ksz8xxx.c
index daa57ce33c..e27fc45a28 100644
--- a/drivers/net/phy/micrel_ksz8xxx.c
+++ b/drivers/net/phy/micrel_ksz8xxx.c
@@ -24,6 +24,7 @@ static struct phy_driver KSZ804_driver = {
};
#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_FACTORY_TEST BIT(15)
#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
static int ksz_genconfig_bcastoff(struct phy_device *phydev)
@@ -80,12 +81,30 @@ static struct phy_driver KSZ8051_driver = {
.shutdown = &genphy_shutdown,
};
+static int ksz8081_config(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~KSZPHY_OMSO_FACTORY_TEST;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO,
+ ret | KSZPHY_OMSO_B_CAST_OFF);
+ if (ret < 0)
+ return ret;
+
+ return genphy_config(phydev);
+}
+
static struct phy_driver KSZ8081_driver = {
.name = "Micrel KSZ8081",
.uid = 0x221560,
.mask = 0xfffff0,
.features = PHY_BASIC_FEATURES,
- .config = &ksz_genconfig_bcastoff,
+ .config = &ksz8081_config,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
};
diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c
index c1e2860329..5aff7ed397 100644
--- a/drivers/net/phy/mv88e61xx.c
+++ b/drivers/net/phy/mv88e61xx.c
@@ -39,15 +39,11 @@
#define PHY_AUTONEGOTIATE_TIMEOUT 5000
-#define PORT_COUNT 11
-#define PORT_MASK ((1 << PORT_COUNT) - 1)
+#define PORT_MASK(port_count) ((1 << (port_count)) - 1)
/* Device addresses */
#define DEVADDR_PHY(p) (p)
-#define DEVADDR_PORT(p) (0x10 + (p))
#define DEVADDR_SERDES 0x0F
-#define DEVADDR_GLOBAL_1 0x1B
-#define DEVADDR_GLOBAL_2 0x1C
/* SMI indirection registers for multichip addressing mode */
#define SMI_CMD_REG 0x00
@@ -88,11 +84,7 @@
#define GLOBAL1_MON_CTRL_CPUDEST_SHIFT 4
#define GLOBAL1_MON_CTRL_CPUDEST_WIDTH 4
-#define PORT_REG_STATUS_LINK BIT(11)
-#define PORT_REG_STATUS_DUPLEX BIT(10)
-
#define PORT_REG_STATUS_SPEED_SHIFT 8
-#define PORT_REG_STATUS_SPEED_WIDTH 2
#define PORT_REG_STATUS_SPEED_10 0
#define PORT_REG_STATUS_SPEED_100 1
#define PORT_REG_STATUS_SPEED_1000 2
@@ -111,6 +103,7 @@
#define PORT_REG_PHYS_CTRL_DUPLEX_VALUE BIT(3)
#define PORT_REG_PHYS_CTRL_DUPLEX_FORCE BIT(2)
#define PORT_REG_PHYS_CTRL_SPD1000 BIT(1)
+#define PORT_REG_PHYS_CTRL_SPD100 BIT(0)
#define PORT_REG_PHYS_CTRL_SPD_MASK (BIT(1) | BIT(0))
#define PORT_REG_CTRL_PSTATE_SHIFT 0
@@ -124,14 +117,12 @@
#define SERDES_REG_CTRL_1_FORCE_LINK BIT(10)
-#define PHY_REG_CTRL1_ENERGY_DET_SHIFT 8
-#define PHY_REG_CTRL1_ENERGY_DET_WIDTH 2
-
/* Field values */
#define PORT_REG_CTRL_PSTATE_DISABLED 0
#define PORT_REG_CTRL_PSTATE_FORWARD 3
#define PHY_REG_CTRL1_ENERGY_DET_OFF 0
+#define PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE 1
#define PHY_REG_CTRL1_ENERGY_DET_SENSE_ONLY 2
#define PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT 3
@@ -182,17 +173,32 @@
#endif
/* ID register values for different switch models */
+#define PORT_SWITCH_ID_6020 0x0200
+#define PORT_SWITCH_ID_6070 0x0700
+#define PORT_SWITCH_ID_6071 0x0710
#define PORT_SWITCH_ID_6096 0x0980
#define PORT_SWITCH_ID_6097 0x0990
#define PORT_SWITCH_ID_6172 0x1720
#define PORT_SWITCH_ID_6176 0x1760
+#define PORT_SWITCH_ID_6220 0x2200
#define PORT_SWITCH_ID_6240 0x2400
+#define PORT_SWITCH_ID_6250 0x2500
#define PORT_SWITCH_ID_6352 0x3520
struct mv88e61xx_phy_priv {
struct mii_dev *mdio_bus;
int smi_addr;
int id;
+ int port_count; /* Number of switch ports */
+ int port_reg_base; /* Base of the switch port registers */
+ u16 port_stat_link_mask;/* Bitmask for port link status bits */
+ u16 port_stat_dup_mask; /* Bitmask for port duplex status bits */
+ u8 port_stat_speed_width;/* Width of speed status bitfield */
+ u8 global1; /* Offset of Switch Global 1 registers */
+ u8 global2; /* Offset of Switch Global 2 registers */
+ u8 phy_ctrl1_en_det_shift; /* 'EDet' bit field offset */
+ u8 phy_ctrl1_en_det_width; /* Width of 'EDet' bit field */
+ u8 phy_ctrl1_en_det_ctrl; /* 'EDet' control value */
};
static inline int smi_cmd(int cmd, int addr, int reg)
@@ -329,11 +335,12 @@ static int mv88e61xx_reg_write(struct phy_device *phydev, int dev, int reg,
static int mv88e61xx_phy_wait(struct phy_device *phydev)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int val;
u32 timeout = 100;
do {
- val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_2,
+ val = mv88e61xx_reg_read(phydev, priv->global2,
GLOBAL2_REG_PHY_CMD);
if (val >= 0 && (val & SMI_BUSY) == 0)
return 0;
@@ -347,13 +354,15 @@ static int mv88e61xx_phy_wait(struct phy_device *phydev)
static int mv88e61xx_phy_read_indirect(struct mii_dev *smi_wrapper, int dev,
int devad, int reg)
{
+ struct mv88e61xx_phy_priv *priv;
struct phy_device *phydev;
int res;
phydev = (struct phy_device *)smi_wrapper->priv;
+ priv = phydev->priv;
/* Issue command to read */
- res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
+ res = mv88e61xx_reg_write(phydev, priv->global2,
GLOBAL2_REG_PHY_CMD,
smi_cmd_read(dev, reg));
@@ -363,25 +372,27 @@ static int mv88e61xx_phy_read_indirect(struct mii_dev *smi_wrapper, int dev,
return res;
/* Read retrieved data */
- return mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_2,
+ return mv88e61xx_reg_read(phydev, priv->global2,
GLOBAL2_REG_PHY_DATA);
}
static int mv88e61xx_phy_write_indirect(struct mii_dev *smi_wrapper, int dev,
int devad, int reg, u16 data)
{
+ struct mv88e61xx_phy_priv *priv;
struct phy_device *phydev;
int res;
phydev = (struct phy_device *)smi_wrapper->priv;
+ priv = phydev->priv;
/* Set the data to write */
- res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
+ res = mv88e61xx_reg_write(phydev, priv->global2,
GLOBAL2_REG_PHY_DATA, data);
if (res < 0)
return res;
/* Issue the write command */
- res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
+ res = mv88e61xx_reg_write(phydev, priv->global2,
GLOBAL2_REG_PHY_CMD,
smi_cmd_write(dev, reg));
if (res < 0)
@@ -408,13 +419,18 @@ static int mv88e61xx_phy_write(struct phy_device *phydev, int phy,
static int mv88e61xx_port_read(struct phy_device *phydev, u8 port, u8 reg)
{
- return mv88e61xx_reg_read(phydev, DEVADDR_PORT(port), reg);
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ return mv88e61xx_reg_read(phydev, priv->port_reg_base + port, reg);
}
static int mv88e61xx_port_write(struct phy_device *phydev, u8 port, u8 reg,
u16 val)
{
- return mv88e61xx_reg_write(phydev, DEVADDR_PORT(port), reg, val);
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ return mv88e61xx_reg_write(phydev, priv->port_reg_base + port,
+ reg, val);
}
static int mv88e61xx_set_page(struct phy_device *phydev, u8 phy, u8 page)
@@ -515,12 +531,13 @@ static int mv88e61xx_parse_status(struct phy_device *phydev)
static int mv88e61xx_switch_reset(struct phy_device *phydev)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int time;
int val;
u8 port;
/* Disable all ports */
- for (port = 0; port < PORT_COUNT; port++) {
+ for (port = 0; port < priv->port_count; port++) {
val = mv88e61xx_port_read(phydev, port, PORT_REG_CTRL);
if (val < 0)
return val;
@@ -536,19 +553,19 @@ static int mv88e61xx_switch_reset(struct phy_device *phydev)
udelay(2000);
/* Reset switch */
- val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1, GLOBAL1_CTRL);
+ val = mv88e61xx_reg_read(phydev, priv->global1, GLOBAL1_CTRL);
if (val < 0)
return val;
val |= GLOBAL1_CTRL_SWRESET;
- val = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_1,
- GLOBAL1_CTRL, val);
+ val = mv88e61xx_reg_write(phydev, priv->global1,
+ GLOBAL1_CTRL, val);
if (val < 0)
return val;
/* Wait up to 1 second for switch reset complete */
for (time = 1000; time; time--) {
- val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1,
- GLOBAL1_CTRL);
+ val = mv88e61xx_reg_read(phydev, priv->global1,
+ GLOBAL1_CTRL);
if (val >= 0 && ((val & GLOBAL1_CTRL_SWRESET) == 0))
break;
udelay(1000);
@@ -628,6 +645,7 @@ static int mv88e61xx_port_set_vlan(struct phy_device *phydev, u8 port,
static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int res;
int val;
bool forced = false;
@@ -635,7 +653,7 @@ static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
val = mv88e61xx_port_read(phydev, port, PORT_REG_STATUS);
if (val < 0)
return val;
- if (!(val & PORT_REG_STATUS_LINK)) {
+ if (!(val & priv->port_stat_link_mask)) {
/* Temporarily force link to read port configuration */
u32 timeout = 100;
forced = true;
@@ -658,7 +676,7 @@ static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
res = -EIO;
goto unforce;
}
- if (val & PORT_REG_STATUS_LINK)
+ if (val & priv->port_stat_link_mask)
break;
} while (--timeout);
@@ -668,13 +686,13 @@ static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
}
}
- if (val & PORT_REG_STATUS_DUPLEX)
+ if (val & priv->port_stat_dup_mask)
phydev->duplex = DUPLEX_FULL;
else
phydev->duplex = DUPLEX_HALF;
val = bitfield_extract(val, PORT_REG_STATUS_SPEED_SHIFT,
- PORT_REG_STATUS_SPEED_WIDTH);
+ priv->port_stat_speed_width);
switch (val) {
case PORT_REG_STATUS_SPEED_1000:
phydev->speed = SPEED_1000;
@@ -707,6 +725,7 @@ unforce:
static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int val;
val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
@@ -714,13 +733,19 @@ static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port)
return val;
val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK |
- PORT_REG_PHYS_CTRL_FC_VALUE);
- val |= PORT_REG_PHYS_CTRL_PCS_AN_EN |
- PORT_REG_PHYS_CTRL_PCS_AN_RST |
- PORT_REG_PHYS_CTRL_FC_FORCE |
+ PORT_REG_PHYS_CTRL_FC_VALUE |
+ PORT_REG_PHYS_CTRL_FC_FORCE);
+ val |= PORT_REG_PHYS_CTRL_FC_FORCE |
PORT_REG_PHYS_CTRL_DUPLEX_VALUE |
- PORT_REG_PHYS_CTRL_DUPLEX_FORCE |
- PORT_REG_PHYS_CTRL_SPD1000;
+ PORT_REG_PHYS_CTRL_DUPLEX_FORCE;
+
+ if (priv->id == PORT_SWITCH_ID_6071) {
+ val |= PORT_REG_PHYS_CTRL_SPD100;
+ } else {
+ val |= PORT_REG_PHYS_CTRL_PCS_AN_EN |
+ PORT_REG_PHYS_CTRL_PCS_AN_RST |
+ PORT_REG_PHYS_CTRL_SPD1000;
+ }
if (port == CONFIG_MV88E61XX_CPU_PORT)
val |= PORT_REG_PHYS_CTRL_LINK_VALUE |
@@ -732,22 +757,23 @@ static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port)
static int mv88e61xx_set_cpu_port(struct phy_device *phydev)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int val;
/* Set CPUDest */
- val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1, GLOBAL1_MON_CTRL);
+ val = mv88e61xx_reg_read(phydev, priv->global1, GLOBAL1_MON_CTRL);
if (val < 0)
return val;
val = bitfield_replace(val, GLOBAL1_MON_CTRL_CPUDEST_SHIFT,
GLOBAL1_MON_CTRL_CPUDEST_WIDTH,
CONFIG_MV88E61XX_CPU_PORT);
- val = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_1,
- GLOBAL1_MON_CTRL, val);
+ val = mv88e61xx_reg_write(phydev, priv->global1,
+ GLOBAL1_MON_CTRL, val);
if (val < 0)
return val;
/* Allow CPU to route to any port */
- val = PORT_MASK & ~(1 << CONFIG_MV88E61XX_CPU_PORT);
+ val = PORT_MASK(priv->port_count) & ~(1 << CONFIG_MV88E61XX_CPU_PORT);
val = mv88e61xx_port_set_vlan(phydev, CONFIG_MV88E61XX_CPU_PORT, val);
if (val < 0)
return val;
@@ -821,6 +847,7 @@ static int mv88e61xx_phy_enable(struct phy_device *phydev, u8 phy)
static int mv88e61xx_phy_setup(struct phy_device *phydev, u8 phy)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int val;
/*
@@ -830,9 +857,9 @@ static int mv88e61xx_phy_setup(struct phy_device *phydev, u8 phy)
val = mv88e61xx_phy_read(phydev, phy, PHY_REG_CTRL1);
if (val < 0)
return val;
- val = bitfield_replace(val, PHY_REG_CTRL1_ENERGY_DET_SHIFT,
- PHY_REG_CTRL1_ENERGY_DET_WIDTH,
- PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT);
+ val = bitfield_replace(val, priv->phy_ctrl1_en_det_shift,
+ priv->phy_ctrl1_en_det_width,
+ priv->phy_ctrl1_en_det_ctrl);
val = mv88e61xx_phy_write(phydev, phy, PHY_REG_CTRL1, val);
if (val < 0)
return val;
@@ -856,6 +883,48 @@ static int mv88e61xx_phy_config_port(struct phy_device *phydev, u8 phy)
return 0;
}
+/*
+ * This function is used to pre-configure the required register
+ * offsets, so that the indirect register access to the PHY registers
+ * is possible. This is necessary to be able to read the PHY ID
+ * while driver probing or in get_phy_id(). The globalN register
+ * offsets must be initialized correctly for a detected switch,
+ * otherwise detection of the PHY ID won't work!
+ */
+static int mv88e61xx_priv_reg_offs_pre_init(struct phy_device *phydev)
+{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
+
+ /*
+ * Initial 'port_reg_base' value must be an offset of existing
+ * port register, then reading the ID should succeed. First, try
+ * to read via port registers with device address 0x10 (88E6096
+ * and compatible switches).
+ */
+ priv->port_reg_base = 0x10;
+ priv->id = mv88e61xx_get_switch_id(phydev);
+ if (priv->id != 0xfff0) {
+ priv->global1 = 0x1B;
+ priv->global2 = 0x1C;
+ return 0;
+ }
+
+ /*
+ * Now try via port registers with device address 0x08
+ * (88E6020 and compatible switches).
+ */
+ priv->port_reg_base = 0x08;
+ priv->id = mv88e61xx_get_switch_id(phydev);
+ if (priv->id != 0xfff0) {
+ priv->global1 = 0x0F;
+ priv->global2 = 0x07;
+ return 0;
+ }
+
+ debug("%s Unknown ID 0x%x\n", __func__, priv->id);
+ return -ENODEV;
+}
+
static int mv88e61xx_probe(struct phy_device *phydev)
{
struct mii_dev *smi_wrapper;
@@ -910,13 +979,57 @@ static int mv88e61xx_probe(struct phy_device *phydev)
phydev->priv = priv;
- priv->id = mv88e61xx_get_switch_id(phydev);
+ res = mv88e61xx_priv_reg_offs_pre_init(phydev);
+ if (res < 0)
+ return res;
+
+ debug("%s ID 0x%x\n", __func__, priv->id);
+
+ switch (priv->id) {
+ case PORT_SWITCH_ID_6096:
+ case PORT_SWITCH_ID_6097:
+ case PORT_SWITCH_ID_6172:
+ case PORT_SWITCH_ID_6176:
+ case PORT_SWITCH_ID_6240:
+ case PORT_SWITCH_ID_6352:
+ priv->port_count = 11;
+ priv->port_stat_link_mask = BIT(11);
+ priv->port_stat_dup_mask = BIT(10);
+ priv->port_stat_speed_width = 2;
+ priv->phy_ctrl1_en_det_shift = 8;
+ priv->phy_ctrl1_en_det_width = 2;
+ priv->phy_ctrl1_en_det_ctrl =
+ PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT;
+ break;
+ case PORT_SWITCH_ID_6020:
+ case PORT_SWITCH_ID_6070:
+ case PORT_SWITCH_ID_6071:
+ case PORT_SWITCH_ID_6220:
+ case PORT_SWITCH_ID_6250:
+ priv->port_count = 7;
+ priv->port_stat_link_mask = BIT(12);
+ priv->port_stat_dup_mask = BIT(9);
+ priv->port_stat_speed_width = 1;
+ priv->phy_ctrl1_en_det_shift = 14;
+ priv->phy_ctrl1_en_det_width = 1;
+ priv->phy_ctrl1_en_det_ctrl =
+ PHY_REG_CTRL1_ENERGY_DET_SENSE_PULSE;
+ break;
+ default:
+ free(priv);
+ return -ENODEV;
+ }
+
+ res = mdio_register(smi_wrapper);
+ if (res)
+ printf("Failed to register SMI bus\n");
return 0;
}
static int mv88e61xx_phy_config(struct phy_device *phydev)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int res;
int i;
int ret = -1;
@@ -925,7 +1038,7 @@ static int mv88e61xx_phy_config(struct phy_device *phydev)
if (res < 0)
return res;
- for (i = 0; i < PORT_COUNT; i++) {
+ for (i = 0; i < priv->port_count; i++) {
if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
phydev->addr = i;
@@ -988,13 +1101,14 @@ static int mv88e61xx_phy_is_connected(struct phy_device *phydev)
static int mv88e61xx_phy_startup(struct phy_device *phydev)
{
+ struct mv88e61xx_phy_priv *priv = phydev->priv;
int i;
int link = 0;
int res;
int speed = phydev->speed;
int duplex = phydev->duplex;
- for (i = 0; i < PORT_COUNT; i++) {
+ for (i = 0; i < priv->port_count; i++) {
if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
phydev->addr = i;
if (!mv88e61xx_phy_is_connected(phydev))
@@ -1040,10 +1154,22 @@ static struct phy_driver mv88e609x_driver = {
.shutdown = &genphy_shutdown,
};
+static struct phy_driver mv88e6071_driver = {
+ .name = "Marvell MV88E6071",
+ .uid = 0x1410db0,
+ .mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES | SUPPORTED_MII,
+ .probe = mv88e61xx_probe,
+ .config = mv88e61xx_phy_config,
+ .startup = mv88e61xx_phy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
int phy_mv88e61xx_init(void)
{
phy_register(&mv88e61xx_driver);
phy_register(&mv88e609x_driver);
+ phy_register(&mv88e6071_driver);
return 0;
}
@@ -1068,6 +1194,16 @@ int get_phy_id(struct mii_dev *bus, int smi_addr, int devad, u32 *phy_id)
temp_phy.priv = &temp_priv;
temp_mii.priv = &temp_phy;
+ /*
+ * get_phy_id() can be called by framework before mv88e61xx driver
+ * probing, in this case the global register offsets are not
+ * initialized yet. Do this initialization here before indirect
+ * PHY register access.
+ */
+ val = mv88e61xx_priv_reg_offs_pre_init(&temp_phy);
+ if (val < 0)
+ return val;
+
val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID1);
if (val < 0)
return -EIO;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index f2d17aa91a..80a7664e49 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -256,11 +256,11 @@ int genphy_update_link(struct phy_device *phydev)
return -EINTR;
}
- if ((i++ % 500) == 0)
+ if ((i++ % 10) == 0)
printf(".");
- udelay(1000); /* 1 ms */
mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+ mdelay(50); /* 50 ms */
}
printf(" done\n");
phydev->link = 1;
@@ -997,7 +997,7 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr,
#endif
{
struct phy_device *phydev = NULL;
- uint mask = (addr > 0) ? (1 << addr) : 0xffffffff;
+ uint mask = (addr >= 0) ? (1 << addr) : 0xffffffff;
#ifdef CONFIG_PHY_FIXED
phydev = phy_connect_fixed(bus, dev, interface);