These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / phy / aquantia.c
diff --git a/kernel/drivers/net/phy/aquantia.c b/kernel/drivers/net/phy/aquantia.c
new file mode 100644 (file)
index 0000000..f1936b7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Driver for Aquantia PHY
+ *
+ * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
+ *
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+
+#define PHY_ID_AQ1202  0x03a1b445
+#define PHY_ID_AQ2104  0x03a1b460
+#define PHY_ID_AQR105  0x03a1b4a2
+#define PHY_ID_AQR405  0x03a1b4b0
+
+#define PHY_AQUANTIA_FEATURES  (SUPPORTED_10000baseT_Full | \
+                                SUPPORTED_1000baseT_Full | \
+                                SUPPORTED_100baseT_Full | \
+                                PHY_DEFAULT_FEATURES)
+
+static int aquantia_config_aneg(struct phy_device *phydev)
+{
+       phydev->supported = PHY_AQUANTIA_FEATURES;
+       phydev->advertising = phydev->supported;
+
+       return 0;
+}
+
+static int aquantia_aneg_done(struct phy_device *phydev)
+{
+       int reg;
+
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+       return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
+}
+
+static int aquantia_config_intr(struct phy_device *phydev)
+{
+       int err;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+               err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
+               if (err < 0)
+                       return err;
+
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
+               if (err < 0)
+                       return err;
+
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
+       } else {
+               err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
+               if (err < 0)
+                       return err;
+
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
+               if (err < 0)
+                       return err;
+
+               err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
+       }
+
+       return err;
+}
+
+static int aquantia_ack_interrupt(struct phy_device *phydev)
+{
+       int reg;
+
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
+       return (reg < 0) ? reg : 0;
+}
+
+static int aquantia_read_status(struct phy_device *phydev)
+{
+       int reg;
+
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
+       if (reg & MDIO_STAT1_LSTATUS)
+               phydev->link = 1;
+       else
+               phydev->link = 0;
+
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
+       mdelay(10);
+       reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
+
+       switch (reg) {
+       case 0x9:
+               phydev->speed = SPEED_2500;
+               break;
+       case 0x5:
+               phydev->speed = SPEED_1000;
+               break;
+       case 0x3:
+               phydev->speed = SPEED_100;
+               break;
+       case 0x7:
+       default:
+               phydev->speed = SPEED_10000;
+               break;
+       }
+       phydev->duplex = DUPLEX_FULL;
+
+       return 0;
+}
+
+static struct phy_driver aquantia_driver[] = {
+{
+       .phy_id         = PHY_ID_AQ1202,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Aquantia AQ1202",
+       .features       = PHY_AQUANTIA_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .config_intr    = aquantia_config_intr,
+       .ack_interrupt  = aquantia_ack_interrupt,
+       .read_status    = aquantia_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+},
+{
+       .phy_id         = PHY_ID_AQ2104,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Aquantia AQ2104",
+       .features       = PHY_AQUANTIA_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .config_intr    = aquantia_config_intr,
+       .ack_interrupt  = aquantia_ack_interrupt,
+       .read_status    = aquantia_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+},
+{
+       .phy_id         = PHY_ID_AQR105,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Aquantia AQR105",
+       .features       = PHY_AQUANTIA_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .config_intr    = aquantia_config_intr,
+       .ack_interrupt  = aquantia_ack_interrupt,
+       .read_status    = aquantia_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+},
+{
+       .phy_id         = PHY_ID_AQR405,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Aquantia AQR405",
+       .features       = PHY_AQUANTIA_FEATURES,
+       .flags          = PHY_HAS_INTERRUPT,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .config_intr    = aquantia_config_intr,
+       .ack_interrupt  = aquantia_ack_interrupt,
+       .read_status    = aquantia_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+},
+};
+
+module_phy_driver(aquantia_driver);
+
+static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
+       { PHY_ID_AQ1202, 0xfffffff0 },
+       { PHY_ID_AQ2104, 0xfffffff0 },
+       { PHY_ID_AQR105, 0xfffffff0 },
+       { PHY_ID_AQR405, 0xfffffff0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
+
+MODULE_DESCRIPTION("Aquantia PHY driver");
+MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
+MODULE_LICENSE("GPL v2");