root/drivers/net/phy/smsc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. smsc_phy_config_intr
  2. smsc_phy_ack_interrupt
  3. smsc_phy_config_init
  4. smsc_phy_reset
  5. lan911x_config_init
  6. lan87xx_read_status
  7. smsc_get_sset_count
  8. smsc_get_strings
  9. smsc_get_stat
  10. smsc_get_stats
  11. smsc_phy_probe

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * drivers/net/phy/smsc.c
   4  *
   5  * Driver for SMSC PHYs
   6  *
   7  * Author: Herbert Valerio Riedel
   8  *
   9  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
  10  *
  11  * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
  12  *
  13  */
  14 
  15 #include <linux/kernel.h>
  16 #include <linux/module.h>
  17 #include <linux/mii.h>
  18 #include <linux/ethtool.h>
  19 #include <linux/of.h>
  20 #include <linux/phy.h>
  21 #include <linux/netdevice.h>
  22 #include <linux/smscphy.h>
  23 
  24 struct smsc_hw_stat {
  25         const char *string;
  26         u8 reg;
  27         u8 bits;
  28 };
  29 
  30 static struct smsc_hw_stat smsc_hw_stats[] = {
  31         { "phy_symbol_errors", 26, 16},
  32 };
  33 
  34 struct smsc_phy_priv {
  35         bool energy_enable;
  36 };
  37 
  38 static int smsc_phy_config_intr(struct phy_device *phydev)
  39 {
  40         int rc = phy_write (phydev, MII_LAN83C185_IM,
  41                         ((PHY_INTERRUPT_ENABLED == phydev->interrupts)
  42                         ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
  43                         : 0));
  44 
  45         return rc < 0 ? rc : 0;
  46 }
  47 
  48 static int smsc_phy_ack_interrupt(struct phy_device *phydev)
  49 {
  50         int rc = phy_read (phydev, MII_LAN83C185_ISF);
  51 
  52         return rc < 0 ? rc : 0;
  53 }
  54 
  55 static int smsc_phy_config_init(struct phy_device *phydev)
  56 {
  57         struct smsc_phy_priv *priv = phydev->priv;
  58 
  59         int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
  60 
  61         if (rc < 0)
  62                 return rc;
  63 
  64         if (priv->energy_enable) {
  65                 /* Enable energy detect mode for this SMSC Transceivers */
  66                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
  67                                rc | MII_LAN83C185_EDPWRDOWN);
  68                 if (rc < 0)
  69                         return rc;
  70         }
  71 
  72         return smsc_phy_ack_interrupt(phydev);
  73 }
  74 
  75 static int smsc_phy_reset(struct phy_device *phydev)
  76 {
  77         int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
  78         if (rc < 0)
  79                 return rc;
  80 
  81         /* If the SMSC PHY is in power down mode, then set it
  82          * in all capable mode before using it.
  83          */
  84         if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
  85                 /* set "all capable" mode */
  86                 rc |= MII_LAN83C185_MODE_ALL;
  87                 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
  88         }
  89 
  90         /* reset the phy */
  91         return genphy_soft_reset(phydev);
  92 }
  93 
  94 static int lan911x_config_init(struct phy_device *phydev)
  95 {
  96         return smsc_phy_ack_interrupt(phydev);
  97 }
  98 
  99 /*
 100  * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable
 101  * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to
 102  * unstable detection of plugging in Ethernet cable.
 103  * This workaround disables Energy Detect Power-Down mode and waiting for
 104  * response on link pulses to detect presence of plugged Ethernet cable.
 105  * The Energy Detect Power-Down mode is enabled again in the end of procedure to
 106  * save approximately 220 mW of power if cable is unplugged.
 107  */
 108 static int lan87xx_read_status(struct phy_device *phydev)
 109 {
 110         struct smsc_phy_priv *priv = phydev->priv;
 111 
 112         int err = genphy_read_status(phydev);
 113 
 114         if (!phydev->link && priv->energy_enable) {
 115                 int i;
 116 
 117                 /* Disable EDPD to wake up PHY */
 118                 int 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                 /* Wait max 640 ms to detect energy */
 128                 for (i = 0; i < 64; i++) {
 129                         /* Sleep to allow link test pulses to be sent */
 130                         msleep(10);
 131                         rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
 132                         if (rc < 0)
 133                                 return rc;
 134                         if (rc & MII_LAN83C185_ENERGYON)
 135                                 break;
 136                 }
 137 
 138                 /* Re-enable EDPD */
 139                 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
 140                 if (rc < 0)
 141                         return rc;
 142 
 143                 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
 144                                rc | MII_LAN83C185_EDPWRDOWN);
 145                 if (rc < 0)
 146                         return rc;
 147         }
 148 
 149         return err;
 150 }
 151 
 152 static int smsc_get_sset_count(struct phy_device *phydev)
 153 {
 154         return ARRAY_SIZE(smsc_hw_stats);
 155 }
 156 
 157 static void smsc_get_strings(struct phy_device *phydev, u8 *data)
 158 {
 159         int i;
 160 
 161         for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) {
 162                 strncpy(data + i * ETH_GSTRING_LEN,
 163                        smsc_hw_stats[i].string, ETH_GSTRING_LEN);
 164         }
 165 }
 166 
 167 static u64 smsc_get_stat(struct phy_device *phydev, int i)
 168 {
 169         struct smsc_hw_stat stat = smsc_hw_stats[i];
 170         int val;
 171         u64 ret;
 172 
 173         val = phy_read(phydev, stat.reg);
 174         if (val < 0)
 175                 ret = U64_MAX;
 176         else
 177                 ret = val;
 178 
 179         return ret;
 180 }
 181 
 182 static void smsc_get_stats(struct phy_device *phydev,
 183                            struct ethtool_stats *stats, u64 *data)
 184 {
 185         int i;
 186 
 187         for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++)
 188                 data[i] = smsc_get_stat(phydev, i);
 189 }
 190 
 191 static int smsc_phy_probe(struct phy_device *phydev)
 192 {
 193         struct device *dev = &phydev->mdio.dev;
 194         struct device_node *of_node = dev->of_node;
 195         struct smsc_phy_priv *priv;
 196 
 197         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 198         if (!priv)
 199                 return -ENOMEM;
 200 
 201         priv->energy_enable = true;
 202 
 203         if (of_property_read_bool(of_node, "smsc,disable-energy-detect"))
 204                 priv->energy_enable = false;
 205 
 206         phydev->priv = priv;
 207 
 208         return 0;
 209 }
 210 
 211 static struct phy_driver smsc_phy_driver[] = {
 212 {
 213         .phy_id         = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
 214         .phy_id_mask    = 0xfffffff0,
 215         .name           = "SMSC LAN83C185",
 216 
 217         /* PHY_BASIC_FEATURES */
 218 
 219         .probe          = smsc_phy_probe,
 220 
 221         /* basic functions */
 222         .config_init    = smsc_phy_config_init,
 223         .soft_reset     = smsc_phy_reset,
 224 
 225         /* IRQ related */
 226         .ack_interrupt  = smsc_phy_ack_interrupt,
 227         .config_intr    = smsc_phy_config_intr,
 228 
 229         .suspend        = genphy_suspend,
 230         .resume         = genphy_resume,
 231 }, {
 232         .phy_id         = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
 233         .phy_id_mask    = 0xfffffff0,
 234         .name           = "SMSC LAN8187",
 235 
 236         /* PHY_BASIC_FEATURES */
 237 
 238         .probe          = smsc_phy_probe,
 239 
 240         /* basic functions */
 241         .config_init    = smsc_phy_config_init,
 242         .soft_reset     = smsc_phy_reset,
 243 
 244         /* IRQ related */
 245         .ack_interrupt  = smsc_phy_ack_interrupt,
 246         .config_intr    = smsc_phy_config_intr,
 247 
 248         /* Statistics */
 249         .get_sset_count = smsc_get_sset_count,
 250         .get_strings    = smsc_get_strings,
 251         .get_stats      = smsc_get_stats,
 252 
 253         .suspend        = genphy_suspend,
 254         .resume         = genphy_resume,
 255 }, {
 256         .phy_id         = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
 257         .phy_id_mask    = 0xfffffff0,
 258         .name           = "SMSC LAN8700",
 259 
 260         /* PHY_BASIC_FEATURES */
 261 
 262         .probe          = smsc_phy_probe,
 263 
 264         /* basic functions */
 265         .read_status    = lan87xx_read_status,
 266         .config_init    = smsc_phy_config_init,
 267         .soft_reset     = smsc_phy_reset,
 268 
 269         /* IRQ related */
 270         .ack_interrupt  = smsc_phy_ack_interrupt,
 271         .config_intr    = smsc_phy_config_intr,
 272 
 273         /* Statistics */
 274         .get_sset_count = smsc_get_sset_count,
 275         .get_strings    = smsc_get_strings,
 276         .get_stats      = smsc_get_stats,
 277 
 278         .suspend        = genphy_suspend,
 279         .resume         = genphy_resume,
 280 }, {
 281         .phy_id         = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
 282         .phy_id_mask    = 0xfffffff0,
 283         .name           = "SMSC LAN911x Internal PHY",
 284 
 285         /* PHY_BASIC_FEATURES */
 286 
 287         .probe          = smsc_phy_probe,
 288 
 289         /* basic functions */
 290         .config_init    = lan911x_config_init,
 291 
 292         /* IRQ related */
 293         .ack_interrupt  = smsc_phy_ack_interrupt,
 294         .config_intr    = smsc_phy_config_intr,
 295 
 296         .suspend        = genphy_suspend,
 297         .resume         = genphy_resume,
 298 }, {
 299         .phy_id         = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
 300         .phy_id_mask    = 0xfffffff0,
 301         .name           = "SMSC LAN8710/LAN8720",
 302 
 303         /* PHY_BASIC_FEATURES */
 304         .flags          = PHY_RST_AFTER_CLK_EN,
 305 
 306         .probe          = smsc_phy_probe,
 307 
 308         /* basic functions */
 309         .read_status    = lan87xx_read_status,
 310         .config_init    = smsc_phy_config_init,
 311         .soft_reset     = smsc_phy_reset,
 312 
 313         /* IRQ related */
 314         .ack_interrupt  = smsc_phy_ack_interrupt,
 315         .config_intr    = smsc_phy_config_intr,
 316 
 317         /* Statistics */
 318         .get_sset_count = smsc_get_sset_count,
 319         .get_strings    = smsc_get_strings,
 320         .get_stats      = smsc_get_stats,
 321 
 322         .suspend        = genphy_suspend,
 323         .resume         = genphy_resume,
 324 }, {
 325         .phy_id         = 0x0007c110,
 326         .phy_id_mask    = 0xfffffff0,
 327         .name           = "SMSC LAN8740",
 328 
 329         /* PHY_BASIC_FEATURES */
 330         .flags          = PHY_RST_AFTER_CLK_EN,
 331 
 332         .probe          = smsc_phy_probe,
 333 
 334         /* basic functions */
 335         .read_status    = lan87xx_read_status,
 336         .config_init    = smsc_phy_config_init,
 337         .soft_reset     = smsc_phy_reset,
 338 
 339         /* IRQ related */
 340         .ack_interrupt  = smsc_phy_ack_interrupt,
 341         .config_intr    = smsc_phy_config_intr,
 342 
 343         /* Statistics */
 344         .get_sset_count = smsc_get_sset_count,
 345         .get_strings    = smsc_get_strings,
 346         .get_stats      = smsc_get_stats,
 347 
 348         .suspend        = genphy_suspend,
 349         .resume         = genphy_resume,
 350 } };
 351 
 352 module_phy_driver(smsc_phy_driver);
 353 
 354 MODULE_DESCRIPTION("SMSC PHY driver");
 355 MODULE_AUTHOR("Herbert Valerio Riedel");
 356 MODULE_LICENSE("GPL");
 357 
 358 static struct mdio_device_id __maybe_unused smsc_tbl[] = {
 359         { 0x0007c0a0, 0xfffffff0 },
 360         { 0x0007c0b0, 0xfffffff0 },
 361         { 0x0007c0c0, 0xfffffff0 },
 362         { 0x0007c0d0, 0xfffffff0 },
 363         { 0x0007c0f0, 0xfffffff0 },
 364         { 0x0007c110, 0xfffffff0 },
 365         { }
 366 };
 367 
 368 MODULE_DEVICE_TABLE(mdio, smsc_tbl);

/* [<][>][^][v][top][bottom][index][help] */