This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / phy / broadcom.c
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);