linux/drivers/net/phy/microchip.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Microchip Technology
   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 2
   7 * of the License, or (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  16 */
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/mii.h>
  20#include <linux/ethtool.h>
  21#include <linux/phy.h>
  22#include <linux/microchipphy.h>
  23
  24#define DRIVER_AUTHOR   "WOOJUNG HUH <woojung.huh@microchip.com>"
  25#define DRIVER_DESC     "Microchip LAN88XX PHY driver"
  26
  27struct lan88xx_priv {
  28        int     chip_id;
  29        int     chip_rev;
  30        __u32   wolopts;
  31};
  32
  33static int lan88xx_phy_config_intr(struct phy_device *phydev)
  34{
  35        int rc;
  36
  37        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  38                /* unmask all source and clear them before enable */
  39                rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF);
  40                rc = phy_read(phydev, LAN88XX_INT_STS);
  41                rc = phy_write(phydev, LAN88XX_INT_MASK,
  42                               LAN88XX_INT_MASK_MDINTPIN_EN_ |
  43                               LAN88XX_INT_MASK_LINK_CHANGE_);
  44        } else {
  45                rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
  46        }
  47
  48        return rc < 0 ? rc : 0;
  49}
  50
  51static int lan88xx_phy_ack_interrupt(struct phy_device *phydev)
  52{
  53        int rc = phy_read(phydev, LAN88XX_INT_STS);
  54
  55        return rc < 0 ? rc : 0;
  56}
  57
  58int lan88xx_suspend(struct phy_device *phydev)
  59{
  60        struct lan88xx_priv *priv = phydev->priv;
  61
  62        /* do not power down PHY when WOL is enabled */
  63        if (!priv->wolopts)
  64                genphy_suspend(phydev);
  65
  66        return 0;
  67}
  68
  69static int lan88xx_probe(struct phy_device *phydev)
  70{
  71        struct device *dev = &phydev->mdio.dev;
  72        struct lan88xx_priv *priv;
  73
  74        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  75        if (!priv)
  76                return -ENOMEM;
  77
  78        priv->wolopts = 0;
  79
  80        /* these values can be used to identify internal PHY */
  81        priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3);
  82        priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV,
  83                                               3);
  84
  85        phydev->priv = priv;
  86
  87        return 0;
  88}
  89
  90static void lan88xx_remove(struct phy_device *phydev)
  91{
  92        struct device *dev = &phydev->mdio.dev;
  93        struct lan88xx_priv *priv = phydev->priv;
  94
  95        if (priv)
  96                devm_kfree(dev, priv);
  97}
  98
  99static int lan88xx_set_wol(struct phy_device *phydev,
 100                           struct ethtool_wolinfo *wol)
 101{
 102        struct lan88xx_priv *priv = phydev->priv;
 103
 104        priv->wolopts = wol->wolopts;
 105
 106        return 0;
 107}
 108
 109static struct phy_driver microchip_phy_driver[] = {
 110{
 111        .phy_id         = 0x0007c130,
 112        .phy_id_mask    = 0xfffffff0,
 113        .name           = "Microchip LAN88xx",
 114
 115        .features       = (PHY_GBIT_FEATURES |
 116                           SUPPORTED_Pause | SUPPORTED_Asym_Pause),
 117        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 118
 119        .probe          = lan88xx_probe,
 120        .remove         = lan88xx_remove,
 121
 122        .config_init    = genphy_config_init,
 123        .config_aneg    = genphy_config_aneg,
 124        .read_status    = genphy_read_status,
 125
 126        .ack_interrupt  = lan88xx_phy_ack_interrupt,
 127        .config_intr    = lan88xx_phy_config_intr,
 128
 129        .suspend        = lan88xx_suspend,
 130        .resume         = genphy_resume,
 131        .set_wol        = lan88xx_set_wol,
 132} };
 133
 134module_phy_driver(microchip_phy_driver);
 135
 136static struct mdio_device_id __maybe_unused microchip_tbl[] = {
 137        { 0x0007c130, 0xfffffff0 },
 138        { }
 139};
 140
 141MODULE_DEVICE_TABLE(mdio, microchip_tbl);
 142
 143MODULE_AUTHOR(DRIVER_AUTHOR);
 144MODULE_DESCRIPTION(DRIVER_DESC);
 145MODULE_LICENSE("GPL");
 146