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); 369