phylib: add the ICPlus IP175C PHY driver
Michael Barkowski [Fri, 11 May 2007 23:24:51 +0000 (18:24 -0500)]
The ICPlus IP175C sports a 100Mbit/s 4-port switch in addition
to a dedicated 100Mbit/s WAN port.

Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
Signed-off-by: Kim Phillips <kim.phillips@freescale.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>

drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/icplus.c [new file with mode: 0644]

index 09b6f25..dd09011 100644 (file)
@@ -55,6 +55,11 @@ config BROADCOM_PHY
        ---help---
          Currently supports the BCM5411, BCM5421 and BCM5461 PHYs.
 
+config ICPLUS_PHY
+       tristate "Drivers for ICPlus PHYs"
+       ---help---
+         Currently supports the IP175C PHY.
+
 config FIXED_PHY
        tristate "Drivers for PHY emulation on fixed speed/link"
        ---help---
index bcd1efb..8885650 100644 (file)
@@ -11,4 +11,5 @@ obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
 obj-$(CONFIG_SMSC_PHY)         += smsc.o
 obj-$(CONFIG_VITESSE_PHY)      += vitesse.o
 obj-$(CONFIG_BROADCOM_PHY)     += broadcom.o
+obj-$(CONFIG_ICPLUS_PHY)       += icplus.o
 obj-$(CONFIG_FIXED_PHY)                += fixed.o
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
new file mode 100644 (file)
index 0000000..af3f1f2
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Driver for ICPlus PHYs
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("ICPlus IP175C PHY driver");
+MODULE_AUTHOR("Michael Barkowski");
+MODULE_LICENSE("GPL");
+
+static int ip175c_config_init(struct phy_device *phydev)
+{
+       int err, i;
+       static int full_reset_performed = 0;
+
+       if (full_reset_performed == 0) {
+
+               /* master reset */
+               err = phydev->bus->write(phydev->bus, 30, 0, 0x175c);
+               if (err < 0)
+                       return err;
+
+               /* ensure no bus delays overlap reset period */
+               err = phydev->bus->read(phydev->bus, 30, 0);
+
+               /* data sheet specifies reset period is 2 msec */
+               mdelay(2);
+
+               /* enable IP175C mode */
+               err = phydev->bus->write(phydev->bus, 29, 31, 0x175c);
+               if (err < 0)
+                       return err;
+
+               /* Set MII0 speed and duplex (in PHY mode) */
+               err = phydev->bus->write(phydev->bus, 29, 22, 0x420);
+               if (err < 0)
+                       return err;
+
+               /* reset switch ports */
+               for (i = 0; i < 5; i++) {
+                       err = phydev->bus->write(phydev->bus, i,
+                                                MII_BMCR, BMCR_RESET);
+                       if (err < 0)
+                               return err;
+               }
+
+               for (i = 0; i < 5; i++)
+                       err = phydev->bus->read(phydev->bus, i, MII_BMCR);
+
+               mdelay(2);
+
+               full_reset_performed = 1;
+       }
+
+       if (phydev->addr != 4) {
+               phydev->state = PHY_RUNNING;
+               phydev->speed = SPEED_100;
+               phydev->duplex = DUPLEX_FULL;
+               phydev->link = 1;
+               netif_carrier_on(phydev->attached_dev);
+       }
+
+       return 0;
+}
+
+static int ip175c_read_status(struct phy_device *phydev)
+{
+       if (phydev->addr == 4) /* WAN port */
+               genphy_read_status(phydev);
+       else
+               /* Don't need to read status for switch ports */
+               phydev->irq = PHY_IGNORE_INTERRUPT;
+
+       return 0;
+}
+
+static int ip175c_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->addr == 4) /* WAN port */
+               genphy_config_aneg(phydev);
+
+       return 0;
+}
+
+static struct phy_driver ip175c_driver = {
+       .phy_id         = 0x02430d80,
+       .name           = "ICPlus IP175C",
+       .phy_id_mask    = 0x0ffffff0,
+       .features       = PHY_BASIC_FEATURES,
+       .config_init    = &ip175c_config_init,
+       .config_aneg    = &ip175c_config_aneg,
+       .read_status    = &ip175c_read_status,
+       .driver         = { .owner = THIS_MODULE,},
+};
+
+static int __init ip175c_init(void)
+{
+       return phy_driver_register(&ip175c_driver);
+}
+
+static void __exit ip175c_exit(void)
+{
+       phy_driver_unregister(&ip175c_driver);
+}
+
+module_init(ip175c_init);
+module_exit(ip175c_exit);