net: phy: add driver for aquantia phy
authorShaohui Xie <Shaohui.Xie@freescale.com>
Fri, 31 Jul 2015 08:58:42 +0000 (16:58 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 31 Jul 2015 22:28:19 +0000 (15:28 -0700)
This patch added driver to support Aquantia PHYs AQ1202, AQ2104, AQR105,
AQR405, which accessed through clause 45.

Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/aquantia.c [new file with mode: 0644]

index 8ef81914422049ffb353c822f51c69fe777f1296..c07030dbe7484b50e1f49c9dff6a35ba6cf0fc95 100644 (file)
@@ -14,6 +14,11 @@ if PHYLIB
 
 comment "MII PHY device drivers"
 
+config AQUANTIA_PHY
+        tristate "Drivers for the Aquantia PHYs"
+        ---help---
+          Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
+
 config AT803X_PHY
        tristate "Drivers for Atheros AT803X PHYs"
        ---help---
index 16aac1c3e70367879aef7d1b1db5d14702fbf69b..9bb103358c74d2c87054c08f0c35d1d83609a43a 100644 (file)
@@ -3,6 +3,7 @@
 libphy-objs                    := phy.o phy_device.o mdio_bus.o
 
 obj-$(CONFIG_PHYLIB)           += libphy.o
+obj-$(CONFIG_AQUANTIA_PHY)     += aquantia.o
 obj-$(CONFIG_MARVELL_PHY)      += marvell.o
 obj-$(CONFIG_DAVICOM_PHY)      += davicom.o
 obj-$(CONFIG_CICADA_PHY)       += cicada.o
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
new file mode 100644 (file)
index 0000000..73d347d
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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_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,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .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,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .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,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .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,
+       .aneg_done      = aquantia_aneg_done,
+       .config_aneg    = aquantia_config_aneg,
+       .read_status    = aquantia_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+},
+};
+
+static int __init aquantia_init(void)
+{
+       return phy_drivers_register(aquantia_driver,
+                                   ARRAY_SIZE(aquantia_driver));
+}
+
+static void __exit aquantia_exit(void)
+{
+       return phy_drivers_unregister(aquantia_driver,
+                                     ARRAY_SIZE(aquantia_driver));
+}
+
+module_init(aquantia_init);
+module_exit(aquantia_exit);
+
+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");