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 27 static 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 37 static 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 44 static int smsc_phy_config_init(struct phy_device *phydev) 45 { 46 int __maybe_unused len; 47 struct device *dev __maybe_unused = &phydev->mdio.dev; 48 struct device_node *of_node __maybe_unused = dev->of_node; 49 int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 50 int enable_energy = 1; 51 52 if (rc < 0) 53 return rc; 54 55 if (of_find_property(of_node, "smsc,disable-energy-detect", &len)) 56 enable_energy = 0; 57 58 if (enable_energy) { 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 int timeout = 50000; 80 81 /* set "all capable" mode and reset the phy */ 82 rc |= MII_LAN83C185_MODE_ALL; 83 phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); 84 phy_write(phydev, MII_BMCR, BMCR_RESET); 85 86 /* wait end of reset (max 500 ms) */ 87 do { 88 udelay(10); 89 if (timeout-- == 0) 90 return -1; 91 rc = phy_read(phydev, MII_BMCR); 92 } while (rc & BMCR_RESET); 93 } 94 return 0; 95 } 96 97 static int lan911x_config_init(struct phy_device *phydev) 98 { 99 return smsc_phy_ack_interrupt(phydev); 100 } 101 102 /* 103 * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable 104 * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to 105 * unstable detection of plugging in Ethernet cable. 106 * This workaround disables Energy Detect Power-Down mode and waiting for 107 * response on link pulses to detect presence of plugged Ethernet cable. 108 * The Energy Detect Power-Down mode is enabled again in the end of procedure to 109 * save approximately 220 mW of power if cable is unplugged. 110 */ 111 static int lan87xx_read_status(struct phy_device *phydev) 112 { 113 int err = genphy_read_status(phydev); 114 int i; 115 116 if (!phydev->link) { 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 struct phy_driver smsc_phy_driver[] = { 153 { 154 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 155 .phy_id_mask = 0xfffffff0, 156 .name = "SMSC LAN83C185", 157 158 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 159 | SUPPORTED_Asym_Pause), 160 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 161 162 /* basic functions */ 163 .config_aneg = genphy_config_aneg, 164 .read_status = genphy_read_status, 165 .config_init = smsc_phy_config_init, 166 .soft_reset = smsc_phy_reset, 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 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 176 .phy_id_mask = 0xfffffff0, 177 .name = "SMSC LAN8187", 178 179 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 180 | SUPPORTED_Asym_Pause), 181 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 182 183 /* basic functions */ 184 .config_aneg = genphy_config_aneg, 185 .read_status = genphy_read_status, 186 .config_init = smsc_phy_config_init, 187 .soft_reset = smsc_phy_reset, 188 189 /* IRQ related */ 190 .ack_interrupt = smsc_phy_ack_interrupt, 191 .config_intr = smsc_phy_config_intr, 192 193 .suspend = genphy_suspend, 194 .resume = genphy_resume, 195 }, { 196 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 197 .phy_id_mask = 0xfffffff0, 198 .name = "SMSC LAN8700", 199 200 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 201 | SUPPORTED_Asym_Pause), 202 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 203 204 /* basic functions */ 205 .config_aneg = genphy_config_aneg, 206 .read_status = lan87xx_read_status, 207 .config_init = smsc_phy_config_init, 208 .soft_reset = smsc_phy_reset, 209 210 /* IRQ related */ 211 .ack_interrupt = smsc_phy_ack_interrupt, 212 .config_intr = smsc_phy_config_intr, 213 214 .suspend = genphy_suspend, 215 .resume = genphy_resume, 216 }, { 217 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 218 .phy_id_mask = 0xfffffff0, 219 .name = "SMSC LAN911x Internal PHY", 220 221 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 222 | SUPPORTED_Asym_Pause), 223 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 224 225 /* basic functions */ 226 .config_aneg = genphy_config_aneg, 227 .read_status = genphy_read_status, 228 .config_init = lan911x_config_init, 229 230 /* IRQ related */ 231 .ack_interrupt = smsc_phy_ack_interrupt, 232 .config_intr = smsc_phy_config_intr, 233 234 .suspend = genphy_suspend, 235 .resume = genphy_resume, 236 }, { 237 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 238 .phy_id_mask = 0xfffffff0, 239 .name = "SMSC LAN8710/LAN8720", 240 241 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 242 | SUPPORTED_Asym_Pause), 243 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 244 245 /* basic functions */ 246 .config_aneg = genphy_config_aneg, 247 .read_status = lan87xx_read_status, 248 .config_init = smsc_phy_config_init, 249 .soft_reset = smsc_phy_reset, 250 251 /* IRQ related */ 252 .ack_interrupt = smsc_phy_ack_interrupt, 253 .config_intr = smsc_phy_config_intr, 254 255 .suspend = genphy_suspend, 256 .resume = genphy_resume, 257 }, { 258 .phy_id = 0x0007c110, 259 .phy_id_mask = 0xfffffff0, 260 .name = "SMSC LAN8740", 261 262 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 263 | SUPPORTED_Asym_Pause), 264 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 265 266 /* basic functions */ 267 .config_aneg = genphy_config_aneg, 268 .read_status = lan87xx_read_status, 269 .config_init = smsc_phy_config_init, 270 .soft_reset = smsc_phy_reset, 271 272 /* IRQ related */ 273 .ack_interrupt = smsc_phy_ack_interrupt, 274 .config_intr = smsc_phy_config_intr, 275 276 .suspend = genphy_suspend, 277 .resume = genphy_resume, 278 } }; 279 280 module_phy_driver(smsc_phy_driver); 281 282 MODULE_DESCRIPTION("SMSC PHY driver"); 283 MODULE_AUTHOR("Herbert Valerio Riedel"); 284 MODULE_LICENSE("GPL"); 285 286 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 287 { 0x0007c0a0, 0xfffffff0 }, 288 { 0x0007c0b0, 0xfffffff0 }, 289 { 0x0007c0c0, 0xfffffff0 }, 290 { 0x0007c0d0, 0xfffffff0 }, 291 { 0x0007c0f0, 0xfffffff0 }, 292 { 0x0007c110, 0xfffffff0 }, 293 { } 294 }; 295 296 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 297