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 26 #define MII_LAN83C185_ISF 29 /* Interrupt Source Flags */ 27 #define MII_LAN83C185_IM 30 /* Interrupt Mask */ 28 29 #define MII_LAN83C185_ISF_INT1 (1<<1) /* Auto-Negotiation Page Received */ 30 #define MII_LAN83C185_ISF_INT2 (1<<2) /* Parallel Detection Fault */ 31 #define MII_LAN83C185_ISF_INT3 (1<<3) /* Auto-Negotiation LP Ack */ 32 #define MII_LAN83C185_ISF_INT4 (1<<4) /* Link Down */ 33 #define MII_LAN83C185_ISF_INT5 (1<<5) /* Remote Fault Detected */ 34 #define MII_LAN83C185_ISF_INT6 (1<<6) /* Auto-Negotiation complete */ 35 #define MII_LAN83C185_ISF_INT7 (1<<7) /* ENERGYON */ 36 37 #define MII_LAN83C185_ISF_INT_ALL (0x0e) 38 39 #define MII_LAN83C185_ISF_INT_PHYLIB_EVENTS \ 40 (MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4) 41 42 43 static int smsc_phy_config_intr(struct phy_device *phydev) 44 { 45 int rc = phy_write (phydev, MII_LAN83C185_IM, 46 ((PHY_INTERRUPT_ENABLED == phydev->interrupts) 47 ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS 48 : 0)); 49 50 return rc < 0 ? rc : 0; 51 } 52 53 static int smsc_phy_ack_interrupt(struct phy_device *phydev) 54 { 55 int rc = phy_read (phydev, MII_LAN83C185_ISF); 56 57 return rc < 0 ? rc : 0; 58 } 59 60 static int smsc_phy_config_init(struct phy_device *phydev) 61 { 62 return smsc_phy_ack_interrupt (phydev); 63 } 64 65 66 static struct phy_driver lan83c185_driver = { 67 .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ 68 .phy_id_mask = 0xfffffff0, 69 .name = "SMSC LAN83C185", 70 71 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 72 | SUPPORTED_Asym_Pause), 73 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 74 75 /* basic functions */ 76 .config_aneg = genphy_config_aneg, 77 .read_status = genphy_read_status, 78 .config_init = smsc_phy_config_init, 79 80 /* IRQ related */ 81 .ack_interrupt = smsc_phy_ack_interrupt, 82 .config_intr = smsc_phy_config_intr, 83 84 .suspend = genphy_suspend, 85 .resume = genphy_resume, 86 87 .driver = { .owner = THIS_MODULE, } 88 }; 89 90 static struct phy_driver lan8187_driver = { 91 .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ 92 .phy_id_mask = 0xfffffff0, 93 .name = "SMSC LAN8187", 94 95 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 96 | SUPPORTED_Asym_Pause), 97 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 98 99 /* basic functions */ 100 .config_aneg = genphy_config_aneg, 101 .read_status = genphy_read_status, 102 .config_init = smsc_phy_config_init, 103 104 /* IRQ related */ 105 .ack_interrupt = smsc_phy_ack_interrupt, 106 .config_intr = smsc_phy_config_intr, 107 108 .suspend = genphy_suspend, 109 .resume = genphy_resume, 110 111 .driver = { .owner = THIS_MODULE, } 112 }; 113 114 static struct phy_driver lan8700_driver = { 115 .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ 116 .phy_id_mask = 0xfffffff0, 117 .name = "SMSC LAN8700", 118 119 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 120 | SUPPORTED_Asym_Pause), 121 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 122 123 /* basic functions */ 124 .config_aneg = genphy_config_aneg, 125 .read_status = genphy_read_status, 126 .config_init = smsc_phy_config_init, 127 128 /* IRQ related */ 129 .ack_interrupt = smsc_phy_ack_interrupt, 130 .config_intr = smsc_phy_config_intr, 131 132 .suspend = genphy_suspend, 133 .resume = genphy_resume, 134 135 .driver = { .owner = THIS_MODULE, } 136 }; 137 138 static struct phy_driver lan911x_int_driver = { 139 .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ 140 .phy_id_mask = 0xfffffff0, 141 .name = "SMSC LAN911x Internal PHY", 142 143 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 144 | SUPPORTED_Asym_Pause), 145 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 146 147 /* basic functions */ 148 .config_aneg = genphy_config_aneg, 149 .read_status = genphy_read_status, 150 .config_init = smsc_phy_config_init, 151 152 /* IRQ related */ 153 .ack_interrupt = smsc_phy_ack_interrupt, 154 .config_intr = smsc_phy_config_intr, 155 156 .suspend = genphy_suspend, 157 .resume = genphy_resume, 158 159 .driver = { .owner = THIS_MODULE, } 160 }; 161 162 static struct phy_driver lan8710_driver = { 163 .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ 164 .phy_id_mask = 0xfffffff0, 165 .name = "SMSC LAN8710/LAN8720", 166 167 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 168 | SUPPORTED_Asym_Pause), 169 .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, 170 171 /* basic functions */ 172 .config_aneg = genphy_config_aneg, 173 .read_status = genphy_read_status, 174 .config_init = smsc_phy_config_init, 175 176 /* IRQ related */ 177 .ack_interrupt = smsc_phy_ack_interrupt, 178 .config_intr = smsc_phy_config_intr, 179 180 .suspend = genphy_suspend, 181 .resume = genphy_resume, 182 183 .driver = { .owner = THIS_MODULE, } 184 }; 185 186 static int __init smsc_init(void) 187 { 188 int ret; 189 190 ret = phy_driver_register (&lan83c185_driver); 191 if (ret) 192 goto err1; 193 194 ret = phy_driver_register (&lan8187_driver); 195 if (ret) 196 goto err2; 197 198 ret = phy_driver_register (&lan8700_driver); 199 if (ret) 200 goto err3; 201 202 ret = phy_driver_register (&lan911x_int_driver); 203 if (ret) 204 goto err4; 205 206 ret = phy_driver_register (&lan8710_driver); 207 if (ret) 208 goto err5; 209 210 return 0; 211 212 err5: 213 phy_driver_unregister (&lan911x_int_driver); 214 err4: 215 phy_driver_unregister (&lan8700_driver); 216 err3: 217 phy_driver_unregister (&lan8187_driver); 218 err2: 219 phy_driver_unregister (&lan83c185_driver); 220 err1: 221 return ret; 222 } 223 224 static void __exit smsc_exit(void) 225 { 226 phy_driver_unregister (&lan8710_driver); 227 phy_driver_unregister (&lan911x_int_driver); 228 phy_driver_unregister (&lan8700_driver); 229 phy_driver_unregister (&lan8187_driver); 230 phy_driver_unregister (&lan83c185_driver); 231 } 232 233 MODULE_DESCRIPTION("SMSC PHY driver"); 234 MODULE_AUTHOR("Herbert Valerio Riedel"); 235 MODULE_LICENSE("GPL"); 236 237 module_init(smsc_init); 238 module_exit(smsc_exit); 239