[PATCH] 2.6.18: sb1250-mac: Broadcom PHY support
authorMaciej W. Rozycki <macro@linux-mips.org>
Tue, 3 Oct 2006 15:18:13 +0000 (16:18 +0100)
committerJeff Garzik <jeff@garzik.org>
Sat, 2 Dec 2006 05:11:54 +0000 (00:11 -0500)
 This patch adds support for interrupt-driven operation of the Broadcom
Gigabit Ethernet PHYs.  I have included device IDs for the parts used on
Broadcom SiByte evaluation boards; more can be added as a need arises.
They are apparently generally software-compatible with one another.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
patch-mips-2.6.18-20060920-broadcom-phy-15
Signed-off-by: Jeff Garzik <jeff@garzik.org>
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/broadcom.c [new file with mode: 0644]

index ecb61f8..f994f12 100644 (file)
@@ -56,6 +56,12 @@ config SMSC_PHY
        ---help---
          Currently supports the LAN83C185 PHY
 
+config BROADCOM_PHY
+       tristate "Drivers for Broadcom PHYs"
+       depends on PHYLIB
+       ---help---
+         Currently supports the BCM5411, BCM5421 and BCM5461 PHYs.
+
 config FIXED_PHY
        tristate "Drivers for PHY emulation on fixed speed/link"
        depends on PHYLIB
index 320f832..bcd1efb 100644 (file)
@@ -10,4 +10,5 @@ obj-$(CONFIG_LXT_PHY)         += lxt.o
 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_FIXED_PHY)                += fixed.o
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
new file mode 100644 (file)
index 0000000..29666c8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *     drivers/net/phy/broadcom.c
+ *
+ *     Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
+ *     transceivers.
+ *
+ *     Copyright (c) 2006  Maciej W. Rozycki
+ *
+ *     Inspired by code written by Amy Fong.
+ *
+ *     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/module.h>
+#include <linux/phy.h>
+
+#define MII_BCM54XX_ECR                0x10    /* BCM54xx extended control register */
+#define MII_BCM54XX_ECR_IM     0x1000  /* Interrupt mask */
+#define MII_BCM54XX_ECR_IF     0x0800  /* Interrupt force */
+
+#define MII_BCM54XX_ESR                0x11    /* BCM54xx extended status register */
+#define MII_BCM54XX_ESR_IS     0x1000  /* Interrupt status */
+
+#define MII_BCM54XX_ISR                0x1a    /* BCM54xx interrupt status register */
+#define MII_BCM54XX_IMR                0x1b    /* BCM54xx interrupt mask register */
+#define MII_BCM54XX_INT_CRCERR 0x0001  /* CRC error */
+#define MII_BCM54XX_INT_LINK   0x0002  /* Link status changed */
+#define MII_BCM54XX_INT_SPEED  0x0004  /* Link speed change */
+#define MII_BCM54XX_INT_DUPLEX 0x0008  /* Duplex mode changed */
+#define MII_BCM54XX_INT_LRS    0x0010  /* Local receiver status changed */
+#define MII_BCM54XX_INT_RRS    0x0020  /* Remote receiver status changed */
+#define MII_BCM54XX_INT_SSERR  0x0040  /* Scrambler synchronization error */
+#define MII_BCM54XX_INT_UHCD   0x0080  /* Unsupported HCD negotiated */
+#define MII_BCM54XX_INT_NHCD   0x0100  /* No HCD */
+#define MII_BCM54XX_INT_NHCDL  0x0200  /* No HCD link */
+#define MII_BCM54XX_INT_ANPR   0x0400  /* Auto-negotiation page received */
+#define MII_BCM54XX_INT_LC     0x0800  /* All counters below 128 */
+#define MII_BCM54XX_INT_HC     0x1000  /* Counter above 32768 */
+#define MII_BCM54XX_INT_MDIX   0x2000  /* MDIX status change */
+#define MII_BCM54XX_INT_PSERR  0x4000  /* Pair swap error */
+
+MODULE_DESCRIPTION("Broadcom PHY driver");
+MODULE_AUTHOR("Maciej W. Rozycki");
+MODULE_LICENSE("GPL");
+
+static int bcm54xx_config_init(struct phy_device *phydev)
+{
+       int reg, err;
+
+       reg = phy_read(phydev, MII_BCM54XX_ECR);
+       if (reg < 0)
+               return reg;
+
+       /* Mask interrupts globally.  */
+       reg |= MII_BCM54XX_ECR_IM;
+       err = phy_write(phydev, MII_BCM54XX_ECR, reg);
+       if (err < 0)
+               return err;
+
+       /* Unmask events we are interested in.  */
+       reg = ~(MII_BCM54XX_INT_DUPLEX |
+               MII_BCM54XX_INT_SPEED |
+               MII_BCM54XX_INT_LINK);
+       err = phy_write(phydev, MII_BCM54XX_IMR, reg);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int bcm54xx_ack_interrupt(struct phy_device *phydev)
+{
+       int reg;
+
+       /* Clear pending interrupts.  */
+       reg = phy_read(phydev, MII_BCM54XX_ISR);
+       if (reg < 0)
+               return reg;
+
+       return 0;
+}
+
+static int bcm54xx_config_intr(struct phy_device *phydev)
+{
+       int reg, err;
+
+       reg = phy_read(phydev, MII_BCM54XX_ECR);
+       if (reg < 0)
+               return reg;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               reg &= ~MII_BCM54XX_ECR_IM;
+       else
+               reg |= MII_BCM54XX_ECR_IM;
+
+       err = phy_write(phydev, MII_BCM54XX_ECR, reg);
+       return err;
+}
+
+static struct phy_driver bcm5411_driver = {
+       .phy_id         = 0x00206070,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Broadcom BCM5411",
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+       .config_init    = bcm54xx_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
+       .ack_interrupt  = bcm54xx_ack_interrupt,
+       .config_intr    = bcm54xx_config_intr,
+       .driver         = { .owner = THIS_MODULE },
+};
+
+static struct phy_driver bcm5421_driver = {
+       .phy_id         = 0x002060e0,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Broadcom BCM5421",
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+       .config_init    = bcm54xx_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
+       .ack_interrupt  = bcm54xx_ack_interrupt,
+       .config_intr    = bcm54xx_config_intr,
+       .driver         = { .owner = THIS_MODULE },
+};
+
+static struct phy_driver bcm5461_driver = {
+       .phy_id         = 0x002060c0,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Broadcom BCM5461",
+       .features       = PHY_GBIT_FEATURES,
+       .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+       .config_init    = bcm54xx_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
+       .ack_interrupt  = bcm54xx_ack_interrupt,
+       .config_intr    = bcm54xx_config_intr,
+       .driver         = { .owner = THIS_MODULE },
+};
+
+static int __init broadcom_init(void)
+{
+       int ret;
+
+       ret = phy_driver_register(&bcm5411_driver);
+       if (ret)
+               goto out_5411;
+       ret = phy_driver_register(&bcm5421_driver);
+       if (ret)
+               goto out_5421;
+       ret = phy_driver_register(&bcm5461_driver);
+       if (ret)
+               goto out_5461;
+       return ret;
+
+out_5461:
+       phy_driver_unregister(&bcm5421_driver);
+out_5421:
+       phy_driver_unregister(&bcm5411_driver);
+out_5411:
+       return ret;
+}
+
+static void __exit broadcom_exit(void)
+{
+       phy_driver_unregister(&bcm5461_driver);
+       phy_driver_unregister(&bcm5421_driver);
+       phy_driver_unregister(&bcm5411_driver);
+}
+
+module_init(broadcom_init);
+module_exit(broadcom_exit);