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