1 /* 2 * drivers/net/phy/micrel.c 3 * 4 * Driver for Micrel PHYs 5 * 6 * Author: David J. Choi 7 * 8 * Copyright (c) 2010-2013 Micrel, Inc. 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 : Micrel Phys: 16 * Giga phys: ksz9021, ksz9031 17 * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041 18 * ksz8021, ksz8031, ksz8051, 19 * ksz8081, ksz8091, 20 * ksz8061, 21 * Switch : ksz8873, ksz886x 22 */ 23 24 #include <linux/kernel.h> 25 #include <linux/module.h> 26 #include <linux/phy.h> 27 #include <linux/micrel_phy.h> 28 29 /* Operation Mode Strap Override */ 30 #define MII_KSZPHY_OMSO 0x16 31 #define KSZPHY_OMSO_B_CAST_OFF (1 << 9) 32 #define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1) 33 #define KSZPHY_OMSO_MII_OVERRIDE (1 << 0) 34 35 /* general Interrupt control/status reg in vendor specific block. */ 36 #define MII_KSZPHY_INTCS 0x1B 37 #define KSZPHY_INTCS_JABBER (1 << 15) 38 #define KSZPHY_INTCS_RECEIVE_ERR (1 << 14) 39 #define KSZPHY_INTCS_PAGE_RECEIVE (1 << 13) 40 #define KSZPHY_INTCS_PARELLEL (1 << 12) 41 #define KSZPHY_INTCS_LINK_PARTNER_ACK (1 << 11) 42 #define KSZPHY_INTCS_LINK_DOWN (1 << 10) 43 #define KSZPHY_INTCS_REMOTE_FAULT (1 << 9) 44 #define KSZPHY_INTCS_LINK_UP (1 << 8) 45 #define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\ 46 KSZPHY_INTCS_LINK_DOWN) 47 48 /* general PHY control reg in vendor specific block. */ 49 #define MII_KSZPHY_CTRL 0x1F 50 /* bitmap of PHY register to set interrupt mode */ 51 #define KSZPHY_CTRL_INT_ACTIVE_HIGH (1 << 9) 52 #define KSZ9021_CTRL_INT_ACTIVE_HIGH (1 << 14) 53 #define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14) 54 #define KSZ8051_RMII_50MHZ_CLK (1 << 7) 55 56 static int ksz_config_flags(struct phy_device *phydev) 57 { 58 int regval; 59 60 if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { 61 regval = phy_read(phydev, MII_KSZPHY_CTRL); 62 regval |= KSZ8051_RMII_50MHZ_CLK; 63 return phy_write(phydev, MII_KSZPHY_CTRL, regval); 64 } 65 return 0; 66 } 67 68 static int kszphy_ack_interrupt(struct phy_device *phydev) 69 { 70 /* bit[7..0] int status, which is a read and clear register. */ 71 int rc; 72 73 rc = phy_read(phydev, MII_KSZPHY_INTCS); 74 75 return (rc < 0) ? rc : 0; 76 } 77 78 static int kszphy_set_interrupt(struct phy_device *phydev) 79 { 80 int temp; 81 temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ? 82 KSZPHY_INTCS_ALL : 0; 83 return phy_write(phydev, MII_KSZPHY_INTCS, temp); 84 } 85 86 static int kszphy_config_intr(struct phy_device *phydev) 87 { 88 int temp, rc; 89 90 /* set the interrupt pin active low */ 91 temp = phy_read(phydev, MII_KSZPHY_CTRL); 92 temp &= ~KSZPHY_CTRL_INT_ACTIVE_HIGH; 93 phy_write(phydev, MII_KSZPHY_CTRL, temp); 94 rc = kszphy_set_interrupt(phydev); 95 return rc < 0 ? rc : 0; 96 } 97 98 static int ksz9021_config_intr(struct phy_device *phydev) 99 { 100 int temp, rc; 101 102 /* set the interrupt pin active low */ 103 temp = phy_read(phydev, MII_KSZPHY_CTRL); 104 temp &= ~KSZ9021_CTRL_INT_ACTIVE_HIGH; 105 phy_write(phydev, MII_KSZPHY_CTRL, temp); 106 rc = kszphy_set_interrupt(phydev); 107 return rc < 0 ? rc : 0; 108 } 109 110 static int ks8737_config_intr(struct phy_device *phydev) 111 { 112 int temp, rc; 113 114 /* set the interrupt pin active low */ 115 temp = phy_read(phydev, MII_KSZPHY_CTRL); 116 temp &= ~KS8737_CTRL_INT_ACTIVE_HIGH; 117 phy_write(phydev, MII_KSZPHY_CTRL, temp); 118 rc = kszphy_set_interrupt(phydev); 119 return rc < 0 ? rc : 0; 120 } 121 122 static int kszphy_config_init(struct phy_device *phydev) 123 { 124 return 0; 125 } 126 127 static int ksz8021_config_init(struct phy_device *phydev) 128 { 129 int rc; 130 const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE; 131 phy_write(phydev, MII_KSZPHY_OMSO, val); 132 rc = ksz_config_flags(phydev); 133 return rc < 0 ? rc : 0; 134 } 135 136 static int ks8051_config_init(struct phy_device *phydev) 137 { 138 int rc; 139 140 rc = ksz_config_flags(phydev); 141 return rc < 0 ? rc : 0; 142 } 143 144 #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 145 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6) 146 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4) 147 int ksz8873mll_read_status(struct phy_device *phydev) 148 { 149 int regval; 150 151 /* dummy read */ 152 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 153 154 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 155 156 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) 157 phydev->duplex = DUPLEX_HALF; 158 else 159 phydev->duplex = DUPLEX_FULL; 160 161 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) 162 phydev->speed = SPEED_10; 163 else 164 phydev->speed = SPEED_100; 165 166 phydev->link = 1; 167 phydev->pause = phydev->asym_pause = 0; 168 169 return 0; 170 } 171 172 static int ksz8873mll_config_aneg(struct phy_device *phydev) 173 { 174 return 0; 175 } 176 177 static struct phy_driver ksphy_driver[] = { 178 { 179 .phy_id = PHY_ID_KS8737, 180 .phy_id_mask = 0x00fffff0, 181 .name = "Micrel KS8737", 182 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 183 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 184 .config_init = kszphy_config_init, 185 .config_aneg = genphy_config_aneg, 186 .read_status = genphy_read_status, 187 .ack_interrupt = kszphy_ack_interrupt, 188 .config_intr = ks8737_config_intr, 189 .driver = { .owner = THIS_MODULE,}, 190 }, { 191 .phy_id = PHY_ID_KSZ8021, 192 .phy_id_mask = 0x00ffffff, 193 .name = "Micrel KSZ8021 or KSZ8031", 194 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | 195 SUPPORTED_Asym_Pause), 196 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 197 .config_init = ksz8021_config_init, 198 .config_aneg = genphy_config_aneg, 199 .read_status = genphy_read_status, 200 .ack_interrupt = kszphy_ack_interrupt, 201 .config_intr = kszphy_config_intr, 202 .driver = { .owner = THIS_MODULE,}, 203 }, { 204 .phy_id = PHY_ID_KSZ8031, 205 .phy_id_mask = 0x00ffffff, 206 .name = "Micrel KSZ8031", 207 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | 208 SUPPORTED_Asym_Pause), 209 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 210 .config_init = ksz8021_config_init, 211 .config_aneg = genphy_config_aneg, 212 .read_status = genphy_read_status, 213 .ack_interrupt = kszphy_ack_interrupt, 214 .config_intr = kszphy_config_intr, 215 .driver = { .owner = THIS_MODULE,}, 216 }, { 217 .phy_id = PHY_ID_KSZ8041, 218 .phy_id_mask = 0x00fffff0, 219 .name = "Micrel KSZ8041", 220 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 221 | SUPPORTED_Asym_Pause), 222 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 223 .config_init = kszphy_config_init, 224 .config_aneg = genphy_config_aneg, 225 .read_status = genphy_read_status, 226 .ack_interrupt = kszphy_ack_interrupt, 227 .config_intr = kszphy_config_intr, 228 .driver = { .owner = THIS_MODULE,}, 229 }, { 230 .phy_id = PHY_ID_KSZ8051, 231 .phy_id_mask = 0x00fffff0, 232 .name = "Micrel KSZ8051", 233 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 234 | SUPPORTED_Asym_Pause), 235 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 236 .config_init = ks8051_config_init, 237 .config_aneg = genphy_config_aneg, 238 .read_status = genphy_read_status, 239 .ack_interrupt = kszphy_ack_interrupt, 240 .config_intr = kszphy_config_intr, 241 .driver = { .owner = THIS_MODULE,}, 242 }, { 243 .phy_id = PHY_ID_KSZ8001, 244 .name = "Micrel KSZ8001 or KS8721", 245 .phy_id_mask = 0x00ffffff, 246 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 247 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 248 .config_init = kszphy_config_init, 249 .config_aneg = genphy_config_aneg, 250 .read_status = genphy_read_status, 251 .ack_interrupt = kszphy_ack_interrupt, 252 .config_intr = kszphy_config_intr, 253 .driver = { .owner = THIS_MODULE,}, 254 }, { 255 .phy_id = PHY_ID_KSZ8081, 256 .name = "Micrel KSZ8081 or KSZ8091", 257 .phy_id_mask = 0x00fffff0, 258 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 259 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 260 .config_init = kszphy_config_init, 261 .config_aneg = genphy_config_aneg, 262 .read_status = genphy_read_status, 263 .ack_interrupt = kszphy_ack_interrupt, 264 .config_intr = kszphy_config_intr, 265 .driver = { .owner = THIS_MODULE,}, 266 }, { 267 .phy_id = PHY_ID_KSZ8061, 268 .name = "Micrel KSZ8061", 269 .phy_id_mask = 0x00fffff0, 270 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 271 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 272 .config_init = kszphy_config_init, 273 .config_aneg = genphy_config_aneg, 274 .read_status = genphy_read_status, 275 .ack_interrupt = kszphy_ack_interrupt, 276 .config_intr = kszphy_config_intr, 277 .driver = { .owner = THIS_MODULE,}, 278 }, { 279 .phy_id = PHY_ID_KSZ9021, 280 .phy_id_mask = 0x000ffffe, 281 .name = "Micrel KSZ9021 Gigabit PHY", 282 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), 283 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 284 .config_init = kszphy_config_init, 285 .config_aneg = genphy_config_aneg, 286 .read_status = genphy_read_status, 287 .ack_interrupt = kszphy_ack_interrupt, 288 .config_intr = ksz9021_config_intr, 289 .driver = { .owner = THIS_MODULE, }, 290 }, { 291 .phy_id = PHY_ID_KSZ9031, 292 .phy_id_mask = 0x00fffff0, 293 .name = "Micrel KSZ9031 Gigabit PHY", 294 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause 295 | SUPPORTED_Asym_Pause), 296 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 297 .config_init = kszphy_config_init, 298 .config_aneg = genphy_config_aneg, 299 .read_status = genphy_read_status, 300 .ack_interrupt = kszphy_ack_interrupt, 301 .config_intr = ksz9021_config_intr, 302 .driver = { .owner = THIS_MODULE, }, 303 }, { 304 .phy_id = PHY_ID_KSZ8873MLL, 305 .phy_id_mask = 0x00fffff0, 306 .name = "Micrel KSZ8873MLL Switch", 307 .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), 308 .flags = PHY_HAS_MAGICANEG, 309 .config_init = kszphy_config_init, 310 .config_aneg = ksz8873mll_config_aneg, 311 .read_status = ksz8873mll_read_status, 312 .driver = { .owner = THIS_MODULE, }, 313 }, { 314 .phy_id = PHY_ID_KSZ886X, 315 .phy_id_mask = 0x00fffff0, 316 .name = "Micrel KSZ886X Switch", 317 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 318 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 319 .config_init = kszphy_config_init, 320 .config_aneg = genphy_config_aneg, 321 .read_status = genphy_read_status, 322 .driver = { .owner = THIS_MODULE, }, 323 } }; 324 325 static int __init ksphy_init(void) 326 { 327 return phy_drivers_register(ksphy_driver, 328 ARRAY_SIZE(ksphy_driver)); 329 } 330 331 static void __exit ksphy_exit(void) 332 { 333 phy_drivers_unregister(ksphy_driver, 334 ARRAY_SIZE(ksphy_driver)); 335 } 336 337 module_init(ksphy_init); 338 module_exit(ksphy_exit); 339 340 MODULE_DESCRIPTION("Micrel PHY driver"); 341 MODULE_AUTHOR("David J. Choi"); 342 MODULE_LICENSE("GPL"); 343 344 static struct mdio_device_id __maybe_unused micrel_tbl[] = { 345 { PHY_ID_KSZ9021, 0x000ffffe }, 346 { PHY_ID_KSZ9031, 0x00fffff0 }, 347 { PHY_ID_KSZ8001, 0x00ffffff }, 348 { PHY_ID_KS8737, 0x00fffff0 }, 349 { PHY_ID_KSZ8021, 0x00ffffff }, 350 { PHY_ID_KSZ8031, 0x00ffffff }, 351 { PHY_ID_KSZ8041, 0x00fffff0 }, 352 { PHY_ID_KSZ8051, 0x00fffff0 }, 353 { PHY_ID_KSZ8061, 0x00fffff0 }, 354 { PHY_ID_KSZ8081, 0x00fffff0 }, 355 { PHY_ID_KSZ8873MLL, 0x00fffff0 }, 356 { PHY_ID_KSZ886X, 0x00fffff0 }, 357 { } 358 }; 359 360 MODULE_DEVICE_TABLE(mdio, micrel_tbl); 361