summaryrefslogtreecommitdiff
path: root/drivers/net/phy/intel-xway.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/intel-xway.c')
-rw-r--r--drivers/net/phy/intel-xway.c122
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);