summaryrefslogtreecommitdiff
path: root/drivers/net/phy/ti.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/ti.c')
-rw-r--r--drivers/net/phy/ti.c93
1 files changed, 92 insertions, 1 deletions
diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c
index f870e6d662..6db6edd0d0 100644
--- a/drivers/net/phy/ti.c
+++ b/drivers/net/phy/ti.c
@@ -24,6 +24,7 @@
/* Extended Registers */
#define DP83867_CFG4 0x0031
#define DP83867_RGMIICTL 0x0032
+#define DP83867_STRAP_STS1 0x006E
#define DP83867_RGMIIDCTL 0x0086
#define DP83867_IO_MUX_CFG 0x0170
@@ -48,8 +49,12 @@
#define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1)
#define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0)
+/* STRAP_STS1 bits */
+#define DP83867_STRAP_STS1_RESERVED BIT(11)
+
/* PHY CTRL bits */
#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 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
@@ -88,6 +93,18 @@
#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_SEL_SHIFT 8
+#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK \
+ GENMASK(0x1f, DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT)
+
+/* CFG4 bits */
+#define DP83867_CFG4_PORT_MIRROR_EN BIT(0)
+
+enum {
+ DP83867_PORT_MIRRORING_KEEP,
+ DP83867_PORT_MIRRORING_EN,
+ DP83867_PORT_MIRRORING_DIS,
+};
struct dp83867_private {
int rx_id_delay;
@@ -95,6 +112,8 @@ struct dp83867_private {
int fifo_depth;
int io_impedance;
bool rxctrl_strap_quirk;
+ int port_mirroring;
+ int clk_output_sel;
};
/**
@@ -163,6 +182,26 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
phy_write(phydev, addr, MII_MMD_DATA, data);
}
+static int dp83867_config_port_mirroring(struct phy_device *phydev)
+{
+ struct dp83867_private *dp83867 =
+ (struct dp83867_private *)phydev->priv;
+ u16 val;
+
+ val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR,
+ phydev->addr);
+
+ if (dp83867->port_mirroring == DP83867_PORT_MIRRORING_EN)
+ val |= DP83867_CFG4_PORT_MIRROR_EN;
+ else
+ val &= ~DP83867_CFG4_PORT_MIRROR_EN;
+
+ phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR,
+ phydev->addr, val);
+
+ return 0;
+}
+
#if defined(CONFIG_DM_ETH)
/**
* dp83867_data_init - Convenience function for setting PHY specific data
@@ -173,6 +212,18 @@ static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
ofnode node;
+ u16 val;
+
+ /* Optional configuration */
+
+ /*
+ * Keep the default value if ti,clk-output-sel is not set
+ * or to high
+ */
+
+ dp83867->clk_output_sel =
+ ofnode_read_u32_default(node, "ti,clk-output-sel",
+ DP83867_CLK_O_SEL_REF_CLK);
node = phy_get_ofnode(phydev);
if (!ofnode_valid(node))
@@ -197,6 +248,23 @@ static int dp83867_of_init(struct phy_device *phydev)
dp83867->fifo_depth = ofnode_read_u32_default(node, "ti,fifo-depth",
-1);
+ if (ofnode_read_bool(node, "enet-phy-lane-swap"))
+ dp83867->port_mirroring = DP83867_PORT_MIRRORING_EN;
+
+ 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_indirect(phydev, DP83867_IO_MUX_CFG,
+ DP83867_DEVADDR, phydev->addr);
+ 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_indirect(phydev, DP83867_IO_MUX_CFG,
+ DP83867_DEVADDR, phydev->addr, val);
+ }
return 0;
}
@@ -218,7 +286,7 @@ static int dp83867_config(struct phy_device *phydev)
{
struct dp83867_private *dp83867;
unsigned int val, delay, cfg2;
- int ret;
+ int ret, bs;
if (!phydev->priv) {
dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL);
@@ -253,6 +321,26 @@ static int dp83867_config(struct phy_device *phydev)
(dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT));
if (ret)
goto err_out;
+
+ /* The code below checks if "port mirroring" N/A MODE4 has been
+ * enabled during power on bootstrap.
+ *
+ * Such N/A mode enabled by mistake can put PHY IC in some
+ * internal testing mode and disable RGMII transmission.
+ *
+ * In this particular case one needs to check STRAP_STS1
+ * register's bit 11 (marked as RESERVED).
+ */
+
+ bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1,
+ DP83867_DEVADDR, phydev->addr);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL);
+ 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)) {
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
(BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000));
@@ -315,6 +403,9 @@ static int dp83867_config(struct phy_device *phydev)
}
}
+ if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
+ dp83867_config_port_mirroring(phydev);
+
genphy_config_aneg(phydev);
return 0;