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@smsc.com 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 rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); 47 if (rc < 0) 48 return rc; 49 50 /* Enable energy detect mode for this SMSC Transceivers */ 51 rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, 52 rc | MII_LAN83C185_EDPWRDOWN); 53 if (rc < 0) 54 return rc; 55 56 return smsc_phy_ack_interrupt (phydev); 57 } 58 59 static int lan911x_config_init(struct phy_device *phydev) 60 { 61 return smsc_phy_ack_interrupt(phydev); 62 } 63 64 static struct phy_driver lan83c185_driver = { 65 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 66 .phy_id_mask = 0xfffffff0, 67 .name = "SMSC LAN83C185", 68 69 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 70 | SUPPORTED_Asym_Pause), 71 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 72 73 /* basic functions */ 74 .config_aneg = genphy_config_aneg, 75 .read_status = genphy_read_status, 76 .config_init = smsc_phy_config_init, 77 78 /* IRQ related */ 79 .ack_interrupt = smsc_phy_ack_interrupt, 80 .config_intr = smsc_phy_config_intr, 81 82 .suspend = genphy_suspend, 83 .resume = genphy_resume, 84 85 .driver = { .owner = THIS_MODULE, } 86 }; 87 88 static struct phy_driver lan8187_driver = { 89 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 90 .phy_id_mask = 0xfffffff0, 91 .name = "SMSC LAN8187", 92 93 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 94 | SUPPORTED_Asym_Pause), 95 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 96 97 /* basic functions */ 98 .config_aneg = genphy_config_aneg, 99 .read_status = genphy_read_status, 100 .config_init = smsc_phy_config_init, 101 102 /* IRQ related */ 103 .ack_interrupt = smsc_phy_ack_interrupt, 104 .config_intr = smsc_phy_config_intr, 105 106 .suspend = genphy_suspend, 107 .resume = genphy_resume, 108 109 .driver = { .owner = THIS_MODULE, } 110 }; 111 112 static struct phy_driver lan8700_driver = { 113 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 114 .phy_id_mask = 0xfffffff0, 115 .name = "SMSC LAN8700", 116 117 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 118 | SUPPORTED_Asym_Pause), 119 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 120 121 /* basic functions */ 122 .config_aneg = genphy_config_aneg, 123 .read_status = genphy_read_status, 124 .config_init = smsc_phy_config_init, 125 126 /* IRQ related */ 127 .ack_interrupt = smsc_phy_ack_interrupt, 128 .config_intr = smsc_phy_config_intr, 129 130 .suspend = genphy_suspend, 131 .resume = genphy_resume, 132 133 .driver = { .owner = THIS_MODULE, } 134 }; 135 136 static struct phy_driver lan911x_int_driver = { 137 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 138 .phy_id_mask = 0xfffffff0, 139 .name = "SMSC LAN911x Internal PHY", 140 141 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 142 | SUPPORTED_Asym_Pause), 143 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 144 145 /* basic functions */ 146 .config_aneg = genphy_config_aneg, 147 .read_status = genphy_read_status, 148 .config_init = lan911x_config_init, 149 150 /* IRQ related */ 151 .ack_interrupt = smsc_phy_ack_interrupt, 152 .config_intr = smsc_phy_config_intr, 153 154 .suspend = genphy_suspend, 155 .resume = genphy_resume, 156 157 .driver = { .owner = THIS_MODULE, } 158 }; 159 160 static struct phy_driver lan8710_driver = { 161 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 162 .phy_id_mask = 0xfffffff0, 163 .name = "SMSC LAN8710/LAN8720", 164 165 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 166 | SUPPORTED_Asym_Pause), 167 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 168 169 /* basic functions */ 170 .config_aneg = genphy_config_aneg, 171 .read_status = genphy_read_status, 172 .config_init = smsc_phy_config_init, 173 174 /* IRQ related */ 175 .ack_interrupt = smsc_phy_ack_interrupt, 176 .config_intr = smsc_phy_config_intr, 177 178 .suspend = genphy_suspend, 179 .resume = genphy_resume, 180 181 .driver = { .owner = THIS_MODULE, } 182 }; 183 184 static int __init smsc_init(void) 185 { 186 int ret; 187 188 ret = phy_driver_register (&lan83c185_driver); 189 if (ret) 190 goto err1; 191 192 ret = phy_driver_register (&lan8187_driver); 193 if (ret) 194 goto err2; 195 196 ret = phy_driver_register (&lan8700_driver); 197 if (ret) 198 goto err3; 199 200 ret = phy_driver_register (&lan911x_int_driver); 201 if (ret) 202 goto err4; 203 204 ret = phy_driver_register (&lan8710_driver); 205 if (ret) 206 goto err5; 207 208 return 0; 209 210 err5: 211 phy_driver_unregister (&lan911x_int_driver); 212 err4: 213 phy_driver_unregister (&lan8700_driver); 214 err3: 215 phy_driver_unregister (&lan8187_driver); 216 err2: 217 phy_driver_unregister (&lan83c185_driver); 218 err1: 219 return ret; 220 } 221 222 static void __exit smsc_exit(void) 223 { 224 phy_driver_unregister (&lan8710_driver); 225 phy_driver_unregister (&lan911x_int_driver); 226 phy_driver_unregister (&lan8700_driver); 227 phy_driver_unregister (&lan8187_driver); 228 phy_driver_unregister (&lan83c185_driver); 229 } 230 231 MODULE_DESCRIPTION("SMSC PHY driver"); 232 MODULE_AUTHOR("Herbert Valerio Riedel"); 233 MODULE_LICENSE("GPL"); 234 235 module_init(smsc_init); 236 module_exit(smsc_exit); 237 238 static struct mdio_device_id __maybe_unused smsc_tbl[] = { 239 { 0x0007c0a0, 0xfffffff0 }, 240 { 0x0007c0b0, 0xfffffff0 }, 241 { 0x0007c0c0, 0xfffffff0 }, 242 { 0x0007c0d0, 0xfffffff0 }, 243 { 0x0007c0f0, 0xfffffff0 }, 244 { } 245 }; 246 247 MODULE_DEVICE_TABLE(mdio, smsc_tbl); 248