Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * drivers/net/phy/smsc.c
0004  *
0005  * Driver for SMSC PHYs
0006  *
0007  * Author: Herbert Valerio Riedel
0008  *
0009  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
0010  *
0011  * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
0012  *
0013  */
0014 
0015 #include <linux/clk.h>
0016 #include <linux/kernel.h>
0017 #include <linux/module.h>
0018 #include <linux/mii.h>
0019 #include <linux/ethtool.h>
0020 #include <linux/of.h>
0021 #include <linux/phy.h>
0022 #include <linux/netdevice.h>
0023 #include <linux/smscphy.h>
0024 
0025 /* Vendor-specific PHY Definitions */
0026 /* EDPD NLP / crossover time configuration */
0027 #define PHY_EDPD_CONFIG         16
0028 #define PHY_EDPD_CONFIG_EXT_CROSSOVER_  0x0001
0029 
0030 /* Control/Status Indication Register */
0031 #define SPECIAL_CTRL_STS        27
0032 #define SPECIAL_CTRL_STS_OVRRD_AMDIX_   0x8000
0033 #define SPECIAL_CTRL_STS_AMDIX_ENABLE_  0x4000
0034 #define SPECIAL_CTRL_STS_AMDIX_STATE_   0x2000
0035 
0036 struct smsc_hw_stat {
0037     const char *string;
0038     u8 reg;
0039     u8 bits;
0040 };
0041 
0042 static struct smsc_hw_stat smsc_hw_stats[] = {
0043     { "phy_symbol_errors", 26, 16},
0044 };
0045 
0046 struct smsc_phy_priv {
0047     u16 intmask;
0048     bool energy_enable;
0049     struct clk *refclk;
0050 };
0051 
0052 static int smsc_phy_ack_interrupt(struct phy_device *phydev)
0053 {
0054     int rc = phy_read(phydev, MII_LAN83C185_ISF);
0055 
0056     return rc < 0 ? rc : 0;
0057 }
0058 
0059 static int smsc_phy_config_intr(struct phy_device *phydev)
0060 {
0061     struct smsc_phy_priv *priv = phydev->priv;
0062     int rc;
0063 
0064     if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0065         rc = smsc_phy_ack_interrupt(phydev);
0066         if (rc)
0067             return rc;
0068 
0069         priv->intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6;
0070         if (priv->energy_enable)
0071             priv->intmask |= MII_LAN83C185_ISF_INT7;
0072 
0073         rc = phy_write(phydev, MII_LAN83C185_IM, priv->intmask);
0074     } else {
0075         priv->intmask = 0;
0076 
0077         rc = phy_write(phydev, MII_LAN83C185_IM, 0);
0078         if (rc)
0079             return rc;
0080 
0081         rc = smsc_phy_ack_interrupt(phydev);
0082     }
0083 
0084     return rc < 0 ? rc : 0;
0085 }
0086 
0087 static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
0088 {
0089     struct smsc_phy_priv *priv = phydev->priv;
0090     int irq_status;
0091 
0092     irq_status = phy_read(phydev, MII_LAN83C185_ISF);
0093     if (irq_status < 0) {
0094         if (irq_status != -ENODEV)
0095             phy_error(phydev);
0096 
0097         return IRQ_NONE;
0098     }
0099 
0100     if (!(irq_status & priv->intmask))
0101         return IRQ_NONE;
0102 
0103     phy_trigger_machine(phydev);
0104 
0105     return IRQ_HANDLED;
0106 }
0107 
0108 static int smsc_phy_config_init(struct phy_device *phydev)
0109 {
0110     struct smsc_phy_priv *priv = phydev->priv;
0111     int rc;
0112 
0113     if (!priv->energy_enable || phydev->irq != PHY_POLL)
0114         return 0;
0115 
0116     rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
0117 
0118     if (rc < 0)
0119         return rc;
0120 
0121     /* Enable energy detect mode for this SMSC Transceivers */
0122     rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
0123                rc | MII_LAN83C185_EDPWRDOWN);
0124     return rc;
0125 }
0126 
0127 static int smsc_phy_reset(struct phy_device *phydev)
0128 {
0129     int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
0130     if (rc < 0)
0131         return rc;
0132 
0133     /* If the SMSC PHY is in power down mode, then set it
0134      * in all capable mode before using it.
0135      */
0136     if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
0137         /* set "all capable" mode */
0138         rc |= MII_LAN83C185_MODE_ALL;
0139         phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
0140     }
0141 
0142     /* reset the phy */
0143     return genphy_soft_reset(phydev);
0144 }
0145 
0146 static int lan87xx_config_aneg(struct phy_device *phydev)
0147 {
0148     int rc;
0149     int val;
0150 
0151     switch (phydev->mdix_ctrl) {
0152     case ETH_TP_MDI:
0153         val = SPECIAL_CTRL_STS_OVRRD_AMDIX_;
0154         break;
0155     case ETH_TP_MDI_X:
0156         val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
0157             SPECIAL_CTRL_STS_AMDIX_STATE_;
0158         break;
0159     case ETH_TP_MDI_AUTO:
0160         val = SPECIAL_CTRL_STS_AMDIX_ENABLE_;
0161         break;
0162     default:
0163         return genphy_config_aneg(phydev);
0164     }
0165 
0166     rc = phy_read(phydev, SPECIAL_CTRL_STS);
0167     if (rc < 0)
0168         return rc;
0169 
0170     rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
0171         SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
0172         SPECIAL_CTRL_STS_AMDIX_STATE_);
0173     rc |= val;
0174     phy_write(phydev, SPECIAL_CTRL_STS, rc);
0175 
0176     phydev->mdix = phydev->mdix_ctrl;
0177     return genphy_config_aneg(phydev);
0178 }
0179 
0180 static int lan95xx_config_aneg_ext(struct phy_device *phydev)
0181 {
0182     int rc;
0183 
0184     if (phydev->phy_id != 0x0007c0f0) /* not (LAN9500A or LAN9505A) */
0185         return lan87xx_config_aneg(phydev);
0186 
0187     /* Extend Manual AutoMDIX timer */
0188     rc = phy_read(phydev, PHY_EDPD_CONFIG);
0189     if (rc < 0)
0190         return rc;
0191 
0192     rc |= PHY_EDPD_CONFIG_EXT_CROSSOVER_;
0193     phy_write(phydev, PHY_EDPD_CONFIG, rc);
0194     return lan87xx_config_aneg(phydev);
0195 }
0196 
0197 /*
0198  * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable
0199  * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to
0200  * unstable detection of plugging in Ethernet cable.
0201  * This workaround disables Energy Detect Power-Down mode and waiting for
0202  * response on link pulses to detect presence of plugged Ethernet cable.
0203  * The Energy Detect Power-Down mode is enabled again in the end of procedure to
0204  * save approximately 220 mW of power if cable is unplugged.
0205  * The workaround is only applicable to poll mode. Energy Detect Power-Down may
0206  * not be used in interrupt mode lest link change detection becomes unreliable.
0207  */
0208 static int lan87xx_read_status(struct phy_device *phydev)
0209 {
0210     struct smsc_phy_priv *priv = phydev->priv;
0211 
0212     int err = genphy_read_status(phydev);
0213 
0214     if (!phydev->link && priv->energy_enable && phydev->irq == PHY_POLL) {
0215         /* Disable EDPD to wake up PHY */
0216         int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
0217         if (rc < 0)
0218             return rc;
0219 
0220         rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
0221                    rc & ~MII_LAN83C185_EDPWRDOWN);
0222         if (rc < 0)
0223             return rc;
0224 
0225         /* Wait max 640 ms to detect energy and the timeout is not
0226          * an actual error.
0227          */
0228         read_poll_timeout(phy_read, rc,
0229                   rc & MII_LAN83C185_ENERGYON || rc < 0,
0230                   10000, 640000, true, phydev,
0231                   MII_LAN83C185_CTRL_STATUS);
0232         if (rc < 0)
0233             return rc;
0234 
0235         /* Re-enable EDPD */
0236         rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
0237         if (rc < 0)
0238             return rc;
0239 
0240         rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
0241                    rc | MII_LAN83C185_EDPWRDOWN);
0242         if (rc < 0)
0243             return rc;
0244     }
0245 
0246     return err;
0247 }
0248 
0249 static int smsc_get_sset_count(struct phy_device *phydev)
0250 {
0251     return ARRAY_SIZE(smsc_hw_stats);
0252 }
0253 
0254 static void smsc_get_strings(struct phy_device *phydev, u8 *data)
0255 {
0256     int i;
0257 
0258     for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) {
0259         strncpy(data + i * ETH_GSTRING_LEN,
0260                smsc_hw_stats[i].string, ETH_GSTRING_LEN);
0261     }
0262 }
0263 
0264 static u64 smsc_get_stat(struct phy_device *phydev, int i)
0265 {
0266     struct smsc_hw_stat stat = smsc_hw_stats[i];
0267     int val;
0268     u64 ret;
0269 
0270     val = phy_read(phydev, stat.reg);
0271     if (val < 0)
0272         ret = U64_MAX;
0273     else
0274         ret = val;
0275 
0276     return ret;
0277 }
0278 
0279 static void smsc_get_stats(struct phy_device *phydev,
0280                struct ethtool_stats *stats, u64 *data)
0281 {
0282     int i;
0283 
0284     for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++)
0285         data[i] = smsc_get_stat(phydev, i);
0286 }
0287 
0288 static void smsc_phy_remove(struct phy_device *phydev)
0289 {
0290     struct smsc_phy_priv *priv = phydev->priv;
0291 
0292     clk_disable_unprepare(priv->refclk);
0293     clk_put(priv->refclk);
0294 }
0295 
0296 static int smsc_phy_probe(struct phy_device *phydev)
0297 {
0298     struct device *dev = &phydev->mdio.dev;
0299     struct device_node *of_node = dev->of_node;
0300     struct smsc_phy_priv *priv;
0301     int ret;
0302 
0303     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0304     if (!priv)
0305         return -ENOMEM;
0306 
0307     priv->energy_enable = true;
0308 
0309     if (of_property_read_bool(of_node, "smsc,disable-energy-detect"))
0310         priv->energy_enable = false;
0311 
0312     phydev->priv = priv;
0313 
0314     /* Make clk optional to keep DTB backward compatibility. */
0315     priv->refclk = clk_get_optional(dev, NULL);
0316     if (IS_ERR(priv->refclk))
0317         return dev_err_probe(dev, PTR_ERR(priv->refclk),
0318                      "Failed to request clock\n");
0319 
0320     ret = clk_prepare_enable(priv->refclk);
0321     if (ret)
0322         return ret;
0323 
0324     ret = clk_set_rate(priv->refclk, 50 * 1000 * 1000);
0325     if (ret) {
0326         clk_disable_unprepare(priv->refclk);
0327         return ret;
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 static struct phy_driver smsc_phy_driver[] = {
0334 {
0335     .phy_id     = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
0336     .phy_id_mask    = 0xfffffff0,
0337     .name       = "SMSC LAN83C185",
0338 
0339     /* PHY_BASIC_FEATURES */
0340 
0341     .probe      = smsc_phy_probe,
0342 
0343     /* basic functions */
0344     .config_init    = smsc_phy_config_init,
0345     .soft_reset = smsc_phy_reset,
0346 
0347     /* IRQ related */
0348     .config_intr    = smsc_phy_config_intr,
0349     .handle_interrupt = smsc_phy_handle_interrupt,
0350 
0351     .suspend    = genphy_suspend,
0352     .resume     = genphy_resume,
0353 }, {
0354     .phy_id     = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
0355     .phy_id_mask    = 0xfffffff0,
0356     .name       = "SMSC LAN8187",
0357 
0358     /* PHY_BASIC_FEATURES */
0359 
0360     .probe      = smsc_phy_probe,
0361 
0362     /* basic functions */
0363     .config_init    = smsc_phy_config_init,
0364     .soft_reset = smsc_phy_reset,
0365 
0366     /* IRQ related */
0367     .config_intr    = smsc_phy_config_intr,
0368     .handle_interrupt = smsc_phy_handle_interrupt,
0369 
0370     /* Statistics */
0371     .get_sset_count = smsc_get_sset_count,
0372     .get_strings    = smsc_get_strings,
0373     .get_stats  = smsc_get_stats,
0374 
0375     .suspend    = genphy_suspend,
0376     .resume     = genphy_resume,
0377 }, {
0378     /* This covers internal PHY (phy_id: 0x0007C0C3) for
0379      * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505)
0380      */
0381     .phy_id     = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
0382     .phy_id_mask    = 0xfffffff0,
0383     .name       = "SMSC LAN8700",
0384 
0385     /* PHY_BASIC_FEATURES */
0386 
0387     .probe      = smsc_phy_probe,
0388 
0389     /* basic functions */
0390     .read_status    = lan87xx_read_status,
0391     .config_init    = smsc_phy_config_init,
0392     .soft_reset = smsc_phy_reset,
0393     .config_aneg    = lan87xx_config_aneg,
0394 
0395     /* IRQ related */
0396     .config_intr    = smsc_phy_config_intr,
0397     .handle_interrupt = smsc_phy_handle_interrupt,
0398 
0399     /* Statistics */
0400     .get_sset_count = smsc_get_sset_count,
0401     .get_strings    = smsc_get_strings,
0402     .get_stats  = smsc_get_stats,
0403 
0404     .suspend    = genphy_suspend,
0405     .resume     = genphy_resume,
0406 }, {
0407     .phy_id     = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
0408     .phy_id_mask    = 0xfffffff0,
0409     .name       = "SMSC LAN911x Internal PHY",
0410 
0411     /* PHY_BASIC_FEATURES */
0412 
0413     .probe      = smsc_phy_probe,
0414 
0415     /* IRQ related */
0416     .config_intr    = smsc_phy_config_intr,
0417     .handle_interrupt = smsc_phy_handle_interrupt,
0418 
0419     .suspend    = genphy_suspend,
0420     .resume     = genphy_resume,
0421 }, {
0422     /* This covers internal PHY (phy_id: 0x0007C0F0) for
0423      * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01)
0424      */
0425     .phy_id     = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
0426     .phy_id_mask    = 0xfffffff0,
0427     .name       = "SMSC LAN8710/LAN8720",
0428 
0429     /* PHY_BASIC_FEATURES */
0430 
0431     .probe      = smsc_phy_probe,
0432     .remove     = smsc_phy_remove,
0433 
0434     /* basic functions */
0435     .read_status    = lan87xx_read_status,
0436     .config_init    = smsc_phy_config_init,
0437     .soft_reset = smsc_phy_reset,
0438     .config_aneg    = lan95xx_config_aneg_ext,
0439 
0440     /* IRQ related */
0441     .config_intr    = smsc_phy_config_intr,
0442     .handle_interrupt = smsc_phy_handle_interrupt,
0443 
0444     /* Statistics */
0445     .get_sset_count = smsc_get_sset_count,
0446     .get_strings    = smsc_get_strings,
0447     .get_stats  = smsc_get_stats,
0448 
0449     .suspend    = genphy_suspend,
0450     .resume     = genphy_resume,
0451 }, {
0452     .phy_id     = 0x0007c110,
0453     .phy_id_mask    = 0xfffffff0,
0454     .name       = "SMSC LAN8740",
0455 
0456     /* PHY_BASIC_FEATURES */
0457     .flags      = PHY_RST_AFTER_CLK_EN,
0458 
0459     .probe      = smsc_phy_probe,
0460 
0461     /* basic functions */
0462     .read_status    = lan87xx_read_status,
0463     .config_init    = smsc_phy_config_init,
0464     .soft_reset = smsc_phy_reset,
0465 
0466     /* IRQ related */
0467     .config_intr    = smsc_phy_config_intr,
0468     .handle_interrupt = smsc_phy_handle_interrupt,
0469 
0470     /* Statistics */
0471     .get_sset_count = smsc_get_sset_count,
0472     .get_strings    = smsc_get_strings,
0473     .get_stats  = smsc_get_stats,
0474 
0475     .suspend    = genphy_suspend,
0476     .resume     = genphy_resume,
0477 }, {
0478     .phy_id     = 0x0007c130,   /* 0x0007c130 and 0x0007c131 */
0479     /* This mask (0xfffffff2) is to differentiate from
0480      * LAN88xx (phy_id 0x0007c132)
0481      * and allows future phy_id revisions.
0482      */
0483     .phy_id_mask    = 0xfffffff2,
0484     .name       = "Microchip LAN8742",
0485 
0486     /* PHY_BASIC_FEATURES */
0487     .flags      = PHY_RST_AFTER_CLK_EN,
0488 
0489     .probe      = smsc_phy_probe,
0490 
0491     /* basic functions */
0492     .read_status    = lan87xx_read_status,
0493     .config_init    = smsc_phy_config_init,
0494     .soft_reset = smsc_phy_reset,
0495 
0496     /* IRQ related */
0497     .config_intr    = smsc_phy_config_intr,
0498     .handle_interrupt = smsc_phy_handle_interrupt,
0499 
0500     /* Statistics */
0501     .get_sset_count = smsc_get_sset_count,
0502     .get_strings    = smsc_get_strings,
0503     .get_stats  = smsc_get_stats,
0504 
0505     .suspend    = genphy_suspend,
0506     .resume     = genphy_resume,
0507 } };
0508 
0509 module_phy_driver(smsc_phy_driver);
0510 
0511 MODULE_DESCRIPTION("SMSC PHY driver");
0512 MODULE_AUTHOR("Herbert Valerio Riedel");
0513 MODULE_LICENSE("GPL");
0514 
0515 static struct mdio_device_id __maybe_unused smsc_tbl[] = {
0516     { 0x0007c0a0, 0xfffffff0 },
0517     { 0x0007c0b0, 0xfffffff0 },
0518     { 0x0007c0c0, 0xfffffff0 },
0519     { 0x0007c0d0, 0xfffffff0 },
0520     { 0x0007c0f0, 0xfffffff0 },
0521     { 0x0007c110, 0xfffffff0 },
0522     { 0x0007c130, 0xfffffff2 },
0523     { }
0524 };
0525 
0526 MODULE_DEVICE_TABLE(mdio, smsc_tbl);