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->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 .driver = { .owner = THIS_MODULE, } 176 }, { 177 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 178 .phy_id_mask = 0xfffffff0, 179 .name = "SMSC LAN8187", 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 .soft_reset = smsc_phy_reset, 190 191 /* IRQ related */ 192 .ack_interrupt = smsc_phy_ack_interrupt, 193 .config_intr = smsc_phy_config_intr, 194 195 .suspend = genphy_suspend, 196 .resume = genphy_resume, 197 198 .driver = { .owner = THIS_MODULE, } 199 }, { 200 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 201 .phy_id_mask = 0xfffffff0, 202 .name = "SMSC LAN8700", 203 204 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 205 | SUPPORTED_Asym_Pause), 206 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 207 208 /* basic functions */ 209 .config_aneg = genphy_config_aneg, 210 .read_status = lan87xx_read_status, 211 .config_init = smsc_phy_config_init, 212 .soft_reset = smsc_phy_reset, 213 214 /* IRQ related */ 215 .ack_interrupt = smsc_phy_ack_interrupt, 216 .config_intr = smsc_phy_config_intr, 217 218 .suspend = genphy_suspend, 219 .resume = genphy_resume, 220 221 .driver = { .owner = THIS_MODULE, } 222 }, { 223 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 224 .phy_id_mask = 0xfffffff0, 225 .name = "SMSC LAN911x Internal PHY", 226 227 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 228 | SUPPORTED_Asym_Pause), 229 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 230 231 /* basic functions */ 232 .config_aneg = genphy_config_aneg, 233 .read_status = genphy_read_status, 234 .config_init = lan911x_config_init, 235 236 /* IRQ related */ 237 .ack_interrupt = smsc_phy_ack_interrupt, 238 .config_intr = smsc_phy_config_intr, 239 240 .suspend = genphy_suspend, 241 .resume = genphy_resume, 242 243 .driver = { .owner = THIS_MODULE, } 244 }, { 245 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 246 .phy_id_mask = 0xfffffff0, 247 .name = "SMSC LAN8710/LAN8720", 248 249 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 250 | SUPPORTED_Asym_Pause), 251 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 252 253 /* basic functions */ 254 .config_aneg = genphy_config_aneg, 255 .read_status = lan87xx_read_status, 256 .config_init = smsc_phy_config_init, 257 .soft_reset = smsc_phy_reset, 258 259 /* IRQ related */ 260 .ack_interrupt = smsc_phy_ack_interrupt, 261 .config_intr = smsc_phy_config_intr, 262 263 .suspend = genphy_suspend, 264 .resume = genphy_resume, 265 266 .driver = { .owner = THIS_MODULE, } 267 } }; 268 269 module_phy_driver(smsc_phy_driver); 270 271 MODULE_DESCRIPTION("SMSC PHY driver"); 272 MODULE_AUTHOR("Herbert Valerio Riedel"); 273 MODULE_LICENSE("GPL"); 274 275 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 276 { 0x0007c0a0, 0xfffffff0 }, 277 { 0x0007c0b0, 0xfffffff0 }, 278 { 0x0007c0c0, 0xfffffff0 }, 279 { 0x0007c0d0, 0xfffffff0 }, 280 { 0x0007c0f0, 0xfffffff0 }, 281 { } 282 }; 283 284 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 285