linux/drivers/net/phy/smsc.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/smsc.c
   3 *
   4 * Driver for SMSC PHYs
   5 *
   6 * Author: Herbert Valerio Riedel
   7 *
   8 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
   9 *
  10 * This program is free software; you can redistribute  it and/or modify it
  11 * under  the terms of  the GNU General  Public License as published by the
  12 * Free Software Foundation;  either version 2 of the  License, or (at your
  13 * option) any later version.
  14 *
  15 * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
  16 *
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/mii.h>
  22#include <linux/ethtool.h>
  23#include <linux/phy.h>
  24#include <linux/netdevice.h>
  25#include <linux/smscphy.h>
  26
  27static int smsc_phy_config_intr(struct phy_device *phydev)
  28{
  29        int rc = phy_write (phydev, MII_LAN83C185_IM,
  30                        ((PHY_INTERRUPT_ENABLED == phydev->interrupts)
  31                        ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
  32                        : 0));
  33
  34        return rc < 0 ? rc : 0;
  35}
  36
  37static int smsc_phy_ack_interrupt(struct phy_device *phydev)
  38{
  39        int rc = phy_read (phydev, MII_LAN83C185_ISF);
  40
  41        return rc < 0 ? rc : 0;
  42}
  43
  44static int smsc_phy_config_init(struct phy_device *phydev)
  45{
  46        int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
  47        if (rc < 0)
  48                return rc;
  49
  50        /* If the SMSC PHY is in power down mode, then set it
  51         * in all capable mode before using it.
  52         */
  53        if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
  54                int timeout = 50000;
  55
  56                /* set "all capable" mode and reset the phy */
  57                rc |= MII_LAN83C185_MODE_ALL;
  58                phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
  59                phy_write(phydev, MII_BMCR, BMCR_RESET);
  60
  61                /* wait end of reset (max 500 ms) */
  62                do {
  63                        udelay(10);
  64                        if (timeout-- == 0)
  65                                return -1;
  66                        rc = phy_read(phydev, MII_BMCR);
  67                } while (rc & BMCR_RESET);
  68        }
  69
  70        rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
  71        if (rc < 0)
  72                return rc;
  73
  74        /* Enable energy detect mode for this SMSC Transceivers */
  75        rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
  76                       rc | MII_LAN83C185_EDPWRDOWN);
  77        if (rc < 0)
  78                return rc;
  79
  80        return smsc_phy_ack_interrupt (phydev);
  81}
  82
  83static int lan911x_config_init(struct phy_device *phydev)
  84{
  85        return smsc_phy_ack_interrupt(phydev);
  86}
  87
  88/*
  89 * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each
  90 * other in order to set the ENERGYON bit and exit EDPD mode.  If a link partner
  91 * does send the pulses within this interval, the PHY will remained powered
  92 * down.
  93 *
  94 * This workaround will manually toggle the PHY on/off upon calls to read_status
  95 * in order to generate link test pulses if the link is down.  If a link partner
  96 * is present, it will respond to the pulses, which will cause the ENERGYON bit
  97 * to be set and will cause the EDPD mode to be exited.
  98 */
  99static int lan87xx_read_status(struct phy_device *phydev)
 100{
 101        int err = genphy_read_status(phydev);
 102
 103        if (!phydev->link) {
 104                /* Disable EDPD to wake up PHY */
 105                int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
 106                if (rc < 0)
 107                        return rc;
 108
 109                rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
 110                               rc & ~MII_LAN83C185_EDPWRDOWN);
 111                if (rc < 0)
 112                        return rc;
 113
 114                /* Sleep 64 ms to allow ~5 link test pulses to be sent */
 115                msleep(64);
 116
 117                /* Re-enable EDPD */
 118                rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
 119                if (rc < 0)
 120                        return rc;
 121
 122                rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
 123                               rc | MII_LAN83C185_EDPWRDOWN);
 124                if (rc < 0)
 125                        return rc;
 126        }
 127
 128        return err;
 129}
 130
 131static struct phy_driver smsc_phy_driver[] = {
 132{
 133        .phy_id         = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
 134        .phy_id_mask    = 0xfffffff0,
 135        .name           = "SMSC LAN83C185",
 136
 137        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 138                                | SUPPORTED_Asym_Pause),
 139        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 140
 141        /* basic functions */
 142        .config_aneg    = genphy_config_aneg,
 143        .read_status    = genphy_read_status,
 144        .config_init    = smsc_phy_config_init,
 145
 146        /* IRQ related */
 147        .ack_interrupt  = smsc_phy_ack_interrupt,
 148        .config_intr    = smsc_phy_config_intr,
 149
 150        .suspend        = genphy_suspend,
 151        .resume         = genphy_resume,
 152
 153        .driver         = { .owner = THIS_MODULE, }
 154}, {
 155        .phy_id         = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
 156        .phy_id_mask    = 0xfffffff0,
 157        .name           = "SMSC LAN8187",
 158
 159        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 160                                | SUPPORTED_Asym_Pause),
 161        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 162
 163        /* basic functions */
 164        .config_aneg    = genphy_config_aneg,
 165        .read_status    = genphy_read_status,
 166        .config_init    = smsc_phy_config_init,
 167
 168        /* IRQ related */
 169        .ack_interrupt  = smsc_phy_ack_interrupt,
 170        .config_intr    = smsc_phy_config_intr,
 171
 172        .suspend        = genphy_suspend,
 173        .resume         = genphy_resume,
 174
 175        .driver         = { .owner = THIS_MODULE, }
 176}, {
 177        .phy_id         = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
 178        .phy_id_mask    = 0xfffffff0,
 179        .name           = "SMSC LAN8700",
 180
 181        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 182                                | SUPPORTED_Asym_Pause),
 183        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 184
 185        /* basic functions */
 186        .config_aneg    = genphy_config_aneg,
 187        .read_status    = genphy_read_status,
 188        .config_init    = smsc_phy_config_init,
 189
 190        /* IRQ related */
 191        .ack_interrupt  = smsc_phy_ack_interrupt,
 192        .config_intr    = smsc_phy_config_intr,
 193
 194        .suspend        = genphy_suspend,
 195        .resume         = genphy_resume,
 196
 197        .driver         = { .owner = THIS_MODULE, }
 198}, {
 199        .phy_id         = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
 200        .phy_id_mask    = 0xfffffff0,
 201        .name           = "SMSC LAN911x Internal PHY",
 202
 203        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 204                                | SUPPORTED_Asym_Pause),
 205        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 206
 207        /* basic functions */
 208        .config_aneg    = genphy_config_aneg,
 209        .read_status    = genphy_read_status,
 210        .config_init    = lan911x_config_init,
 211
 212        /* IRQ related */
 213        .ack_interrupt  = smsc_phy_ack_interrupt,
 214        .config_intr    = smsc_phy_config_intr,
 215
 216        .suspend        = genphy_suspend,
 217        .resume         = genphy_resume,
 218
 219        .driver         = { .owner = THIS_MODULE, }
 220}, {
 221        .phy_id         = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
 222        .phy_id_mask    = 0xfffffff0,
 223        .name           = "SMSC LAN8710/LAN8720",
 224
 225        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 226                                | SUPPORTED_Asym_Pause),
 227        .flags          = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
 228
 229        /* basic functions */
 230        .config_aneg    = genphy_config_aneg,
 231        .read_status    = lan87xx_read_status,
 232        .config_init    = smsc_phy_config_init,
 233
 234        /* IRQ related */
 235        .ack_interrupt  = smsc_phy_ack_interrupt,
 236        .config_intr    = smsc_phy_config_intr,
 237
 238        .suspend        = genphy_suspend,
 239        .resume         = genphy_resume,
 240
 241        .driver         = { .owner = THIS_MODULE, }
 242} };
 243
 244static int __init smsc_init(void)
 245{
 246        return phy_drivers_register(smsc_phy_driver,
 247                ARRAY_SIZE(smsc_phy_driver));
 248}
 249
 250static void __exit smsc_exit(void)
 251{
 252        return phy_drivers_unregister(smsc_phy_driver,
 253                ARRAY_SIZE(smsc_phy_driver));
 254}
 255
 256MODULE_DESCRIPTION("SMSC PHY driver");
 257MODULE_AUTHOR("Herbert Valerio Riedel");
 258MODULE_LICENSE("GPL");
 259
 260module_init(smsc_init);
 261module_exit(smsc_exit);
 262
 263static struct mdio_device_id __maybe_unused smsc_tbl[] = {
 264        { 0x0007c0a0, 0xfffffff0 },
 265        { 0x0007c0b0, 0xfffffff0 },
 266        { 0x0007c0c0, 0xfffffff0 },
 267        { 0x0007c0d0, 0xfffffff0 },
 268        { 0x0007c0f0, 0xfffffff0 },
 269        { }
 270};
 271
 272MODULE_DEVICE_TABLE(mdio, smsc_tbl);
 273