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 static struct phy_driver ksphy_driver[] = { 131 { 132 .phy_id = PHY_ID_KS8737, 133 .phy_id_mask = 0x00fffff0, 134 .name = "Micrel KS8737", 135 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 136 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 137 .config_init = kszphy_config_init, 138 .config_aneg = genphy_config_aneg, 139 .read_status = genphy_read_status, 140 .ack_interrupt = kszphy_ack_interrupt, 141 .config_intr = ks8737_config_intr, 142 .driver = { .owner = THIS_MODULE,}, 143 }, { 144 .phy_id = PHY_ID_KSZ8021, 145 .phy_id_mask = 0x00ffffff, 146 .name = "Micrel KSZ8021", 147 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | 148 SUPPORTED_Asym_Pause), 149 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 150 .config_init = ksz8021_config_init, 151 .config_aneg = genphy_config_aneg, 152 .read_status = genphy_read_status, 153 .ack_interrupt = kszphy_ack_interrupt, 154 .config_intr = kszphy_config_intr, 155 .driver = { .owner = THIS_MODULE,}, 156 }, { 157 .phy_id = PHY_ID_KSZ8041, 158 .phy_id_mask = 0x00fffff0, 159 .name = "Micrel KSZ8041", 160 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 161 | SUPPORTED_Asym_Pause), 162 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 163 .config_init = kszphy_config_init, 164 .config_aneg = genphy_config_aneg, 165 .read_status = genphy_read_status, 166 .ack_interrupt = kszphy_ack_interrupt, 167 .config_intr = kszphy_config_intr, 168 .driver = { .owner = THIS_MODULE,}, 169 }, { 170 .phy_id = PHY_ID_KSZ8051, 171 .phy_id_mask = 0x00fffff0, 172 .name = "Micrel KSZ8051", 173 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause 174 | SUPPORTED_Asym_Pause), 175 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 176 .config_init = ks8051_config_init, 177 .config_aneg = genphy_config_aneg, 178 .read_status = genphy_read_status, 179 .ack_interrupt = kszphy_ack_interrupt, 180 .config_intr = kszphy_config_intr, 181 .driver = { .owner = THIS_MODULE,}, 182 }, { 183 .phy_id = PHY_ID_KSZ8001, 184 .name = "Micrel KSZ8001 or KS8721", 185 .phy_id_mask = 0x00ffffff, 186 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), 187 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 188 .config_init = kszphy_config_init, 189 .config_aneg = genphy_config_aneg, 190 .read_status = genphy_read_status, 191 .ack_interrupt = kszphy_ack_interrupt, 192 .config_intr = kszphy_config_intr, 193 .driver = { .owner = THIS_MODULE,}, 194 }, { 195 .phy_id = PHY_ID_KSZ9021, 196 .phy_id_mask = 0x000ffffe, 197 .name = "Micrel KSZ9021 Gigabit PHY", 198 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause 199 | SUPPORTED_Asym_Pause), 200 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, 201 .config_init = kszphy_config_init, 202 .config_aneg = genphy_config_aneg, 203 .read_status = genphy_read_status, 204 .ack_interrupt = kszphy_ack_interrupt, 205 .config_intr = ksz9021_config_intr, 206 .driver = { .owner = THIS_MODULE, }, 207 } }; 208 209 static int __init ksphy_init(void) 210 { 211 return phy_drivers_register(ksphy_driver, 212 ARRAY_SIZE(ksphy_driver)); 213 } 214 215 static void __exit ksphy_exit(void) 216 { 217 phy_drivers_unregister(ksphy_driver, 218 ARRAY_SIZE(ksphy_driver)); 219 } 220 221 module_init(ksphy_init); 222 module_exit(ksphy_exit); 223 224 MODULE_DESCRIPTION("Micrel PHY driver"); 225 MODULE_AUTHOR("David J. Choi"); 226 MODULE_LICENSE("GPL"); 227 228 static struct mdio_device_id __maybe_unused micrel_tbl[] = { 229 { PHY_ID_KSZ9021, 0x000ffffe }, 230 { PHY_ID_KSZ8001, 0x00ffffff }, 231 { PHY_ID_KS8737, 0x00fffff0 }, 232 { PHY_ID_KSZ8021, 0x00ffffff }, 233 { PHY_ID_KSZ8041, 0x00fffff0 }, 234 { PHY_ID_KSZ8051, 0x00fffff0 }, 235 { } 236 }; 237 238 MODULE_DEVICE_TABLE(mdio, micrel_tbl); 239