diff options
Diffstat (limited to 'drivers/net/phy/intel-xway.c')
| -rw-r--r-- | drivers/net/phy/intel-xway.c | 122 |
1 files changed, 114 insertions, 8 deletions
diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index a44771e8acdc..3cee31bb931f 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -10,11 +10,21 @@ #include <linux/of.h> #include <linux/bitfield.h> +#define XWAY_MDIO_ERRCNT 0x15 /* error counter */ #define XWAY_MDIO_MIICTRL 0x17 /* mii control */ #define XWAY_MDIO_IMASK 0x19 /* interrupt mask */ #define XWAY_MDIO_ISTAT 0x1A /* interrupt status */ #define XWAY_MDIO_LED 0x1B /* led control */ +#define XWAY_MDIO_GCTRL_TM_MASK GENMASK(15, 13) +#define XWAY_MDIO_GCTRL_TM(mode) FIELD_PREP(XWAY_MDIO_GCTRL_TM_MASK, (mode)) +#define XWAY_MDIO_GCTRL_TM_NOP XWAY_MDIO_GCTRL_TM(0) /* Normal operation */ +#define XWAY_MDIO_GCTRL_TM_CDIAG XWAY_MDIO_GCTRL_TM(6) /* Cable diagnostics */ + +#define XWAY_MDIO_ERRCNT_SEL GENMASK(11, 8) +#define XWAY_MDIO_ERRCNT_COUNT GENMASK(7, 0) +#define XWAY_MDIO_ERRCNT_SEL_RXERR 0 + #define XWAY_MDIO_MIICTRL_RXSKEW_MASK GENMASK(14, 12) #define XWAY_MDIO_MIICTRL_TXSKEW_MASK GENMASK(10, 8) @@ -169,12 +179,15 @@ #define PHY_ID_PHY11G_VR9_1_2 0xD565A409 #define PHY_ID_PHY22F_VR9_1_2 0xD565A419 +struct xway_gphy_priv { + u64 rx_errors; +}; + static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500, 3000, 3500}; static int xway_gphy_rgmii_init(struct phy_device *phydev) { - struct device *dev = &phydev->mdio.dev; unsigned int delay_size = ARRAY_SIZE(xway_internal_delay); s32 int_delay; int val = 0; @@ -207,8 +220,7 @@ static int xway_gphy_rgmii_init(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - int_delay = phy_get_internal_delay(phydev, dev, - xway_internal_delay, + int_delay = phy_get_internal_delay(phydev, xway_internal_delay, delay_size, true); /* if rx-internal-delay-ps is missing, use default of 2.0 ns */ @@ -220,8 +232,7 @@ static int xway_gphy_rgmii_init(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - int_delay = phy_get_internal_delay(phydev, dev, - xway_internal_delay, + int_delay = phy_get_internal_delay(phydev, xway_internal_delay, delay_size, false); /* if tx-internal-delay-ps is missing, use default of 2.0 ns */ @@ -280,7 +291,7 @@ static int xway_gphy_init_leds(struct phy_device *phydev) static int xway_gphy_config_init(struct phy_device *phydev) { - struct device_node *np = phydev->mdio.dev.of_node; + struct device_node *np; int err; /* Mask all interrupts */ @@ -289,7 +300,10 @@ static int xway_gphy_config_init(struct phy_device *phydev) return err; /* Use default LED configuration if 'leds' node isn't defined */ - if (!of_get_child_by_name(np, "leds")) + np = of_get_child_by_name(phydev->mdio.dev.of_node, "leds"); + if (np) + of_node_put(np); + else xway_gphy_init_leds(phydev); /* Clear all pending interrupts */ @@ -299,9 +313,46 @@ static int xway_gphy_config_init(struct phy_device *phydev) if (err) return err; + /* Count MDI RX errors (SymbolErrorDuringCarrier) */ + return phy_write(phydev, XWAY_MDIO_ERRCNT, + FIELD_PREP(XWAY_MDIO_ERRCNT_SEL, XWAY_MDIO_ERRCNT_SEL_RXERR)); +} + +static int xway_gphy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct xway_gphy_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + phydev->priv = priv; + return 0; } +static int xway_11g_int_config_init(struct phy_device *phydev) +{ + int err; + + /* An issue has been sporadically observed after device power-on on the + * first link-up attempt in 100BASE-TX mode resulting in either the + * link-up taking a long time, or failing to link-up altogether. + * + * Workaround: + * After power-on, enable Cable Diagnostic Mode for all ports and + * disable it. + */ + err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_CDIAG); + if (err) + return err; + err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_NOP); + if (err) + return err; + + return xway_gphy_config_init(phydev); +} + static int xway_gphy14_config_aneg(struct phy_device *phydev) { int reg, err; @@ -532,6 +583,31 @@ static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index, return -EINVAL; } +static int xway_gphy_update_stats(struct phy_device *phydev) +{ + struct xway_gphy_priv *priv = phydev->priv; + int ret; + + /* XWAY_MDIO_ERRCNT: 8-bit read-clear counter, SEL set to RXERR */ + ret = phy_read(phydev, XWAY_MDIO_ERRCNT); + if (ret < 0) + return ret; + + priv->rx_errors += FIELD_GET(XWAY_MDIO_ERRCNT_COUNT, ret); + + return 0; +} + +static void xway_gphy_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct xway_gphy_priv *priv = phydev->priv; + + eth_stats->SymbolErrorDuringCarrier = priv->rx_errors; + stats->rx_errors = priv->rx_errors; +} + static struct phy_driver xway_gphy[] = { { .phy_id = PHY_ID_PHY11G_1_3, @@ -539,6 +615,7 @@ static struct phy_driver xway_gphy[] = { .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -549,12 +626,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_3, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.3", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -565,12 +645,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -581,12 +664,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.4", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -597,12 +683,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -612,12 +701,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -627,12 +719,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_VR9_1_1, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (xRX v1.1 integrated)", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -642,12 +737,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_VR9_1_1, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (xRX v1.1 integrated)", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -657,12 +755,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_VR9_1_2, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (xRX v1.2 integrated)", /* PHY_GBIT_FEATURES */ - .config_init = xway_gphy_config_init, + .config_init = xway_11g_int_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -672,12 +773,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_VR9_1_2, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (xRX v1.2 integrated)", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -687,6 +791,8 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, }; module_phy_driver(xway_gphy); |
