linux/drivers/net/phy/bcm63xx.c
<<
>>
Prefs
   1/*
   2 *      Driver for Broadcom 63xx SOCs integrated PHYs
   3 *
   4 *      This program is free software; you can redistribute it and/or
   5 *      modify it under the terms of the GNU General Public License
   6 *      as published by the Free Software Foundation; either version
   7 *      2 of the License, or (at your option) any later version.
   8 */
   9#include <linux/module.h>
  10#include <linux/phy.h>
  11
  12#define MII_BCM63XX_IR          0x1a    /* interrupt register */
  13#define MII_BCM63XX_IR_EN       0x4000  /* global interrupt enable */
  14#define MII_BCM63XX_IR_DUPLEX   0x0800  /* duplex changed */
  15#define MII_BCM63XX_IR_SPEED    0x0400  /* speed changed */
  16#define MII_BCM63XX_IR_LINK     0x0200  /* link changed */
  17#define MII_BCM63XX_IR_GMASK    0x0100  /* global interrupt mask */
  18
  19MODULE_DESCRIPTION("Broadcom 63xx internal PHY driver");
  20MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
  21MODULE_LICENSE("GPL");
  22
  23static int bcm63xx_config_init(struct phy_device *phydev)
  24{
  25        int reg, err;
  26
  27        reg = phy_read(phydev, MII_BCM63XX_IR);
  28        if (reg < 0)
  29                return reg;
  30
  31        /* Mask interrupts globally.  */
  32        reg |= MII_BCM63XX_IR_GMASK;
  33        err = phy_write(phydev, MII_BCM63XX_IR, reg);
  34        if (err < 0)
  35                return err;
  36
  37        /* Unmask events we are interested in  */
  38        reg = ~(MII_BCM63XX_IR_DUPLEX |
  39                MII_BCM63XX_IR_SPEED |
  40                MII_BCM63XX_IR_LINK) |
  41                MII_BCM63XX_IR_EN;
  42        err = phy_write(phydev, MII_BCM63XX_IR, reg);
  43        if (err < 0)
  44                return err;
  45        return 0;
  46}
  47
  48static int bcm63xx_ack_interrupt(struct phy_device *phydev)
  49{
  50        int reg;
  51
  52        /* Clear pending interrupts.  */
  53        reg = phy_read(phydev, MII_BCM63XX_IR);
  54        if (reg < 0)
  55                return reg;
  56
  57        return 0;
  58}
  59
  60static int bcm63xx_config_intr(struct phy_device *phydev)
  61{
  62        int reg, err;
  63
  64        reg = phy_read(phydev, MII_BCM63XX_IR);
  65        if (reg < 0)
  66                return reg;
  67
  68        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  69                reg &= ~MII_BCM63XX_IR_GMASK;
  70        else
  71                reg |= MII_BCM63XX_IR_GMASK;
  72
  73        err = phy_write(phydev, MII_BCM63XX_IR, reg);
  74        return err;
  75}
  76
  77static struct phy_driver bcm63xx_1_driver = {
  78        .phy_id         = 0x00406000,
  79        .phy_id_mask    = 0xfffffc00,
  80        .name           = "Broadcom BCM63XX (1)",
  81        /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */
  82        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
  83        .flags          = PHY_HAS_INTERRUPT,
  84        .config_init    = bcm63xx_config_init,
  85        .config_aneg    = genphy_config_aneg,
  86        .read_status    = genphy_read_status,
  87        .ack_interrupt  = bcm63xx_ack_interrupt,
  88        .config_intr    = bcm63xx_config_intr,
  89        .driver         = { .owner = THIS_MODULE },
  90};
  91
  92/* same phy as above, with just a different OUI */
  93static struct phy_driver bcm63xx_2_driver = {
  94        .phy_id         = 0x002bdc00,
  95        .phy_id_mask    = 0xfffffc00,
  96        .name           = "Broadcom BCM63XX (2)",
  97        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
  98        .flags          = PHY_HAS_INTERRUPT,
  99        .config_init    = bcm63xx_config_init,
 100        .config_aneg    = genphy_config_aneg,
 101        .read_status    = genphy_read_status,
 102        .ack_interrupt  = bcm63xx_ack_interrupt,
 103        .config_intr    = bcm63xx_config_intr,
 104        .driver         = { .owner = THIS_MODULE },
 105};
 106
 107static int __init bcm63xx_phy_init(void)
 108{
 109        int ret;
 110
 111        ret = phy_driver_register(&bcm63xx_1_driver);
 112        if (ret)
 113                goto out_63xx_1;
 114        ret = phy_driver_register(&bcm63xx_2_driver);
 115        if (ret)
 116                goto out_63xx_2;
 117        return ret;
 118
 119out_63xx_2:
 120        phy_driver_unregister(&bcm63xx_1_driver);
 121out_63xx_1:
 122        return ret;
 123}
 124
 125static void __exit bcm63xx_phy_exit(void)
 126{
 127        phy_driver_unregister(&bcm63xx_1_driver);
 128        phy_driver_unregister(&bcm63xx_2_driver);
 129}
 130
 131module_init(bcm63xx_phy_init);
 132module_exit(bcm63xx_phy_exit);
 133