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        return phy_write(phydev, MII_BCM63XX_IR, reg);
  43}
  44
  45static int bcm63xx_ack_interrupt(struct phy_device *phydev)
  46{
  47        int reg;
  48
  49        /* Clear pending interrupts.  */
  50        reg = phy_read(phydev, MII_BCM63XX_IR);
  51        if (reg < 0)
  52                return reg;
  53
  54        return 0;
  55}
  56
  57static int bcm63xx_config_intr(struct phy_device *phydev)
  58{
  59        int reg, err;
  60
  61        reg = phy_read(phydev, MII_BCM63XX_IR);
  62        if (reg < 0)
  63                return reg;
  64
  65        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  66                reg &= ~MII_BCM63XX_IR_GMASK;
  67        else
  68                reg |= MII_BCM63XX_IR_GMASK;
  69
  70        err = phy_write(phydev, MII_BCM63XX_IR, reg);
  71        return err;
  72}
  73
  74static struct phy_driver bcm63xx_driver[] = {
  75{
  76        .phy_id         = 0x00406000,
  77        .phy_id_mask    = 0xfffffc00,
  78        .name           = "Broadcom BCM63XX (1)",
  79        /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */
  80        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
  81        .flags          = PHY_HAS_INTERRUPT,
  82        .config_init    = bcm63xx_config_init,
  83        .config_aneg    = genphy_config_aneg,
  84        .read_status    = genphy_read_status,
  85        .ack_interrupt  = bcm63xx_ack_interrupt,
  86        .config_intr    = bcm63xx_config_intr,
  87        .driver         = { .owner = THIS_MODULE },
  88}, {
  89        /* same phy as above, with just a different OUI */
  90        .phy_id         = 0x002bdc00,
  91        .phy_id_mask    = 0xfffffc00,
  92        .name           = "Broadcom BCM63XX (2)",
  93        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
  94        .flags          = PHY_HAS_INTERRUPT,
  95        .config_init    = bcm63xx_config_init,
  96        .config_aneg    = genphy_config_aneg,
  97        .read_status    = genphy_read_status,
  98        .ack_interrupt  = bcm63xx_ack_interrupt,
  99        .config_intr    = bcm63xx_config_intr,
 100        .driver         = { .owner = THIS_MODULE },
 101} };
 102
 103static int __init bcm63xx_phy_init(void)
 104{
 105        return phy_drivers_register(bcm63xx_driver,
 106                ARRAY_SIZE(bcm63xx_driver));
 107}
 108
 109static void __exit bcm63xx_phy_exit(void)
 110{
 111        phy_drivers_unregister(bcm63xx_driver,
 112                ARRAY_SIZE(bcm63xx_driver));
 113}
 114
 115module_init(bcm63xx_phy_init);
 116module_exit(bcm63xx_phy_exit);
 117
 118static struct mdio_device_id __maybe_unused bcm63xx_tbl[] = {
 119        { 0x00406000, 0xfffffc00 },
 120        { 0x002bdc00, 0xfffffc00 },
 121        { }
 122};
 123
 124MODULE_DEVICE_TABLE(mdio, bcm63xx_tbl);
 125