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/of.h> 24 #include <linux/phy.h> 25 #include <linux/netdevice.h> 26 #include <linux/smscphy.h> 27 28 struct smsc_phy_priv { 29 bool energy_enable; 30 }; 31 32 static int smsc_phy_config_intr(struct phy_device *phydev) 33 { 34 int rc = phy_write (phydev, MII_LAN83C185_IM, 35 ((PHY_INTERRUPT_ENABLED == phydev->interrupts) 36 ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS 37 : 0)); 38 39 return rc < 0 ? rc : 0; 40 } 41 42 static int smsc_phy_ack_interrupt(struct phy_device *phydev) 43 { 44 int rc = phy_read (phydev, MII_LAN83C185_ISF); 45 46 return rc < 0 ? rc : 0; 47 } 48 49 static int smsc_phy_config_init(struct phy_device *phydev) 50 { 51 struct smsc_phy_priv *priv = phydev->priv; 52 53 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 54 55 if (rc < 0) 56 return rc; 57 58 if (priv->energy_enable) { 59 /* Enable energy detect mode for this SMSC Transceivers */ 60 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 61 rc | MII_LAN83C185_EDPWRDOWN); 62 if (rc < 0) 63 return rc; 64 } 65 66 return smsc_phy_ack_interrupt(phydev); 67 } 68 69 static int smsc_phy_reset(struct phy_device *phydev) 70 { 71 int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); 72 if (rc < 0) 73 return rc; 74 75 /* If the SMSC PHY is in power down mode, then set it 76 * in all capable mode before using it. 77 */ 78 if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { 79 /* set "all capable" mode */ 80 rc |= MII_LAN83C185_MODE_ALL; 81 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); 82 } 83 84 /* reset the phy */ 85 return genphy_soft_reset(phydev); 86 } 87 88 static int lan911x_config_init(struct phy_device *phydev) 89 { 90 return smsc_phy_ack_interrupt(phydev); 91 } 92 93 /* 94 * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable 95 * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to 96 * unstable detection of plugging in Ethernet cable. 97 * This workaround disables Energy Detect Power-Down mode and waiting for 98 * response on link pulses to detect presence of plugged Ethernet cable. 99 * The Energy Detect Power-Down mode is enabled again in the end of procedure to 100 * save approximately 220 mW of power if cable is unplugged. 101 */ 102 static int lan87xx_read_status(struct phy_device *phydev) 103 { 104 struct smsc_phy_priv *priv = phydev->priv; 105 106 int err = genphy_read_status(phydev); 107 108 if (!phydev->link && priv->energy_enable) { 109 int i; 110 111 /* Disable EDPD to wake up PHY */ 112 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 113 if (rc < 0) 114 return rc; 115 116 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 117 rc & ~MII_LAN83C185_EDPWRDOWN); 118 if (rc < 0) 119 return rc; 120 121 /* Wait max 640 ms to detect energy */ 122 for (i = 0; i < 64; i++) { 123 /* Sleep to allow link test pulses to be sent */ 124 msleep(10); 125 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 126 if (rc < 0) 127 return rc; 128 if (rc & MII_LAN83C185_ENERGYON) 129 break; 130 } 131 132 /* Re-enable EDPD */ 133 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 134 if (rc < 0) 135 return rc; 136 137 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 138 rc | MII_LAN83C185_EDPWRDOWN); 139 if (rc < 0) 140 return rc; 141 } 142 143 return err; 144 } 145 146 static int smsc_phy_probe(struct phy_device *phydev) 147 { 148 struct device *dev = &phydev->mdio.dev; 149 struct device_node *of_node = dev->of_node; 150 struct smsc_phy_priv *priv; 151 152 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 153 if (!priv) 154 return -ENOMEM; 155 156 priv->energy_enable = true; 157 158 if (of_property_read_bool(of_node, "smsc,disable-energy-detect")) 159 priv->energy_enable = false; 160 161 phydev->priv = priv; 162 163 return 0; 164 } 165 166 static struct phy_driver smsc_phy_driver[] = { 167 { 168 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 169 .phy_id_mask = 0xfffffff0, 170 .name = "SMSC LAN83C185", 171 172 .features = PHY_BASIC_FEATURES, 173 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 174 175 .probe = smsc_phy_probe, 176 177 /* basic functions */ 178 .config_aneg = genphy_config_aneg, 179 .read_status = genphy_read_status, 180 .config_init = smsc_phy_config_init, 181 .soft_reset = smsc_phy_reset, 182 183 /* IRQ related */ 184 .ack_interrupt = smsc_phy_ack_interrupt, 185 .config_intr = smsc_phy_config_intr, 186 187 .suspend = genphy_suspend, 188 .resume = genphy_resume, 189 }, { 190 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 191 .phy_id_mask = 0xfffffff0, 192 .name = "SMSC LAN8187", 193 194 .features = PHY_BASIC_FEATURES, 195 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 196 197 .probe = smsc_phy_probe, 198 199 /* basic functions */ 200 .config_aneg = genphy_config_aneg, 201 .read_status = genphy_read_status, 202 .config_init = smsc_phy_config_init, 203 .soft_reset = smsc_phy_reset, 204 205 /* IRQ related */ 206 .ack_interrupt = smsc_phy_ack_interrupt, 207 .config_intr = smsc_phy_config_intr, 208 209 .suspend = genphy_suspend, 210 .resume = genphy_resume, 211 }, { 212 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 213 .phy_id_mask = 0xfffffff0, 214 .name = "SMSC LAN8700", 215 216 .features = PHY_BASIC_FEATURES, 217 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 218 219 .probe = smsc_phy_probe, 220 221 /* basic functions */ 222 .config_aneg = genphy_config_aneg, 223 .read_status = lan87xx_read_status, 224 .config_init = smsc_phy_config_init, 225 .soft_reset = smsc_phy_reset, 226 227 /* IRQ related */ 228 .ack_interrupt = smsc_phy_ack_interrupt, 229 .config_intr = smsc_phy_config_intr, 230 231 .suspend = genphy_suspend, 232 .resume = genphy_resume, 233 }, { 234 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 235 .phy_id_mask = 0xfffffff0, 236 .name = "SMSC LAN911x Internal PHY", 237 238 .features = PHY_BASIC_FEATURES, 239 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 240 241 .probe = smsc_phy_probe, 242 243 /* basic functions */ 244 .config_aneg = genphy_config_aneg, 245 .read_status = genphy_read_status, 246 .config_init = lan911x_config_init, 247 248 /* IRQ related */ 249 .ack_interrupt = smsc_phy_ack_interrupt, 250 .config_intr = smsc_phy_config_intr, 251 252 .suspend = genphy_suspend, 253 .resume = genphy_resume, 254 }, { 255 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 256 .phy_id_mask = 0xfffffff0, 257 .name = "SMSC LAN8710/LAN8720", 258 259 .features = PHY_BASIC_FEATURES, 260 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 261 262 .probe = smsc_phy_probe, 263 264 /* basic functions */ 265 .config_aneg = genphy_config_aneg, 266 .read_status = lan87xx_read_status, 267 .config_init = smsc_phy_config_init, 268 .soft_reset = smsc_phy_reset, 269 270 /* IRQ related */ 271 .ack_interrupt = smsc_phy_ack_interrupt, 272 .config_intr = smsc_phy_config_intr, 273 274 .suspend = genphy_suspend, 275 .resume = genphy_resume, 276 }, { 277 .phy_id = 0x0007c110, 278 .phy_id_mask = 0xfffffff0, 279 .name = "SMSC LAN8740", 280 281 .features = PHY_BASIC_FEATURES, 282 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 283 284 .probe = smsc_phy_probe, 285 286 /* basic functions */ 287 .config_aneg = genphy_config_aneg, 288 .read_status = lan87xx_read_status, 289 .config_init = smsc_phy_config_init, 290 .soft_reset = smsc_phy_reset, 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 300 module_phy_driver(smsc_phy_driver); 301 302 MODULE_DESCRIPTION("SMSC PHY driver"); 303 MODULE_AUTHOR("Herbert Valerio Riedel"); 304 MODULE_LICENSE("GPL"); 305 306 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 307 { 0x0007c0a0, 0xfffffff0 }, 308 { 0x0007c0b0, 0xfffffff0 }, 309 { 0x0007c0c0, 0xfffffff0 }, 310 { 0x0007c0d0, 0xfffffff0 }, 311 { 0x0007c0f0, 0xfffffff0 }, 312 { 0x0007c110, 0xfffffff0 }, 313 { } 314 }; 315 316 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 317