2 * drivers/net/phy/micrel.c
4 * Driver for Micrel PHYs
6 * Author: David J. Choi
8 * Copyright (c) 2010-2013 Micrel, Inc.
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.
15 * Support : Micrel Phys:
16 * Giga phys: ksz9021, ksz9031
17 * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
18 * ksz8021, ksz8031, ksz8051,
21 * Switch : ksz8873, ksz886x
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/phy.h>
27 #include <linux/micrel_phy.h>
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)
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)
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)
56 static int kszphy_ack_interrupt(struct phy_device
*phydev
)
58 /* bit[7..0] int status, which is a read and clear register. */
61 rc
= phy_read(phydev
, MII_KSZPHY_INTCS
);
63 return (rc
< 0) ? rc
: 0;
66 static int kszphy_set_interrupt(struct phy_device
*phydev
)
69 temp
= (PHY_INTERRUPT_ENABLED
== phydev
->interrupts
) ?
71 return phy_write(phydev
, MII_KSZPHY_INTCS
, temp
);
74 static int kszphy_config_intr(struct phy_device
*phydev
)
78 /* set the interrupt pin active low */
79 temp
= phy_read(phydev
, MII_KSZPHY_CTRL
);
80 temp
&= ~KSZPHY_CTRL_INT_ACTIVE_HIGH
;
81 phy_write(phydev
, MII_KSZPHY_CTRL
, temp
);
82 rc
= kszphy_set_interrupt(phydev
);
83 return rc
< 0 ? rc
: 0;
86 static int ksz9021_config_intr(struct phy_device
*phydev
)
90 /* set the interrupt pin active low */
91 temp
= phy_read(phydev
, MII_KSZPHY_CTRL
);
92 temp
&= ~KSZ9021_CTRL_INT_ACTIVE_HIGH
;
93 phy_write(phydev
, MII_KSZPHY_CTRL
, temp
);
94 rc
= kszphy_set_interrupt(phydev
);
95 return rc
< 0 ? rc
: 0;
98 static int ks8737_config_intr(struct phy_device
*phydev
)
102 /* set the interrupt pin active low */
103 temp
= phy_read(phydev
, MII_KSZPHY_CTRL
);
104 temp
&= ~KS8737_CTRL_INT_ACTIVE_HIGH
;
105 phy_write(phydev
, MII_KSZPHY_CTRL
, temp
);
106 rc
= kszphy_set_interrupt(phydev
);
107 return rc
< 0 ? rc
: 0;
110 static int kszphy_config_init(struct phy_device
*phydev
)
115 static int ksz8021_config_init(struct phy_device
*phydev
)
117 const u16 val
= KSZPHY_OMSO_B_CAST_OFF
| KSZPHY_OMSO_RMII_OVERRIDE
;
118 phy_write(phydev
, MII_KSZPHY_OMSO
, val
);
122 static int ks8051_config_init(struct phy_device
*phydev
)
126 if (phydev
->dev_flags
& MICREL_PHY_50MHZ_CLK
) {
127 regval
= phy_read(phydev
, MII_KSZPHY_CTRL
);
128 regval
|= KSZ8051_RMII_50MHZ_CLK
;
129 phy_write(phydev
, MII_KSZPHY_CTRL
, regval
);
135 #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
136 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6)
137 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4)
138 int ksz8873mll_read_status(struct phy_device
*phydev
)
143 regval
= phy_read(phydev
, KSZ8873MLL_GLOBAL_CONTROL_4
);
145 regval
= phy_read(phydev
, KSZ8873MLL_GLOBAL_CONTROL_4
);
147 if (regval
& KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX
)
148 phydev
->duplex
= DUPLEX_HALF
;
150 phydev
->duplex
= DUPLEX_FULL
;
152 if (regval
& KSZ8873MLL_GLOBAL_CONTROL_4_SPEED
)
153 phydev
->speed
= SPEED_10
;
155 phydev
->speed
= SPEED_100
;
158 phydev
->pause
= phydev
->asym_pause
= 0;
163 static int ksz8873mll_config_aneg(struct phy_device
*phydev
)
168 static struct phy_driver ksphy_driver
[] = {
170 .phy_id
= PHY_ID_KS8737
,
171 .phy_id_mask
= 0x00fffff0,
172 .name
= "Micrel KS8737",
173 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
),
174 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
175 .config_init
= kszphy_config_init
,
176 .config_aneg
= genphy_config_aneg
,
177 .read_status
= genphy_read_status
,
178 .ack_interrupt
= kszphy_ack_interrupt
,
179 .config_intr
= ks8737_config_intr
,
180 .driver
= { .owner
= THIS_MODULE
,},
182 .phy_id
= PHY_ID_KSZ8021
,
183 .phy_id_mask
= 0x00ffffff,
184 .name
= "Micrel KSZ8021 or KSZ8031",
185 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
|
186 SUPPORTED_Asym_Pause
),
187 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
188 .config_init
= ksz8021_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
,},
195 .phy_id
= PHY_ID_KSZ8041
,
196 .phy_id_mask
= 0x00fffff0,
197 .name
= "Micrel KSZ8041",
198 .features
= (PHY_BASIC_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
= kszphy_config_intr
,
206 .driver
= { .owner
= THIS_MODULE
,},
208 .phy_id
= PHY_ID_KSZ8051
,
209 .phy_id_mask
= 0x00fffff0,
210 .name
= "Micrel KSZ8051",
211 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
212 | SUPPORTED_Asym_Pause
),
213 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
214 .config_init
= ks8051_config_init
,
215 .config_aneg
= genphy_config_aneg
,
216 .read_status
= genphy_read_status
,
217 .ack_interrupt
= kszphy_ack_interrupt
,
218 .config_intr
= kszphy_config_intr
,
219 .driver
= { .owner
= THIS_MODULE
,},
221 .phy_id
= PHY_ID_KSZ8001
,
222 .name
= "Micrel KSZ8001 or KS8721",
223 .phy_id_mask
= 0x00ffffff,
224 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
),
225 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
226 .config_init
= kszphy_config_init
,
227 .config_aneg
= genphy_config_aneg
,
228 .read_status
= genphy_read_status
,
229 .ack_interrupt
= kszphy_ack_interrupt
,
230 .config_intr
= kszphy_config_intr
,
231 .driver
= { .owner
= THIS_MODULE
,},
233 .phy_id
= PHY_ID_KSZ8081
,
234 .name
= "Micrel KSZ8081 or KSZ8091",
235 .phy_id_mask
= 0x00fffff0,
236 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
),
237 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
238 .config_init
= kszphy_config_init
,
239 .config_aneg
= genphy_config_aneg
,
240 .read_status
= genphy_read_status
,
241 .ack_interrupt
= kszphy_ack_interrupt
,
242 .config_intr
= kszphy_config_intr
,
243 .driver
= { .owner
= THIS_MODULE
,},
245 .phy_id
= PHY_ID_KSZ8061
,
246 .name
= "Micrel KSZ8061",
247 .phy_id_mask
= 0x00fffff0,
248 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_Pause
),
249 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
250 .config_init
= kszphy_config_init
,
251 .config_aneg
= genphy_config_aneg
,
252 .read_status
= genphy_read_status
,
253 .ack_interrupt
= kszphy_ack_interrupt
,
254 .config_intr
= kszphy_config_intr
,
255 .driver
= { .owner
= THIS_MODULE
,},
257 .phy_id
= PHY_ID_KSZ9021
,
258 .phy_id_mask
= 0x000ffffe,
259 .name
= "Micrel KSZ9021 Gigabit PHY",
260 .features
= (PHY_GBIT_FEATURES
| SUPPORTED_Pause
),
261 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
262 .config_init
= kszphy_config_init
,
263 .config_aneg
= genphy_config_aneg
,
264 .read_status
= genphy_read_status
,
265 .ack_interrupt
= kszphy_ack_interrupt
,
266 .config_intr
= ksz9021_config_intr
,
267 .driver
= { .owner
= THIS_MODULE
, },
269 .phy_id
= PHY_ID_KSZ9031
,
270 .phy_id_mask
= 0x00fffff0,
271 .name
= "Micrel KSZ9031 Gigabit PHY",
272 .features
= (PHY_GBIT_FEATURES
| SUPPORTED_Pause
273 | SUPPORTED_Asym_Pause
),
274 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
275 .config_init
= kszphy_config_init
,
276 .config_aneg
= genphy_config_aneg
,
277 .read_status
= genphy_read_status
,
278 .ack_interrupt
= kszphy_ack_interrupt
,
279 .config_intr
= ksz9021_config_intr
,
280 .driver
= { .owner
= THIS_MODULE
, },
282 .phy_id
= PHY_ID_KSZ8873MLL
,
283 .phy_id_mask
= 0x00fffff0,
284 .name
= "Micrel KSZ8873MLL Switch",
285 .features
= (SUPPORTED_Pause
| SUPPORTED_Asym_Pause
),
286 .flags
= PHY_HAS_MAGICANEG
,
287 .config_init
= kszphy_config_init
,
288 .config_aneg
= ksz8873mll_config_aneg
,
289 .read_status
= ksz8873mll_read_status
,
290 .driver
= { .owner
= THIS_MODULE
, },
292 .phy_id
= PHY_ID_KSZ886X
,
293 .phy_id_mask
= 0x00fffff0,
294 .name
= "Micrel KSZ886X Switch",
295 .features
= (PHY_BASIC_FEATURES
| SUPPORTED_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 .driver
= { .owner
= THIS_MODULE
, },
303 static int __init
ksphy_init(void)
305 return phy_drivers_register(ksphy_driver
,
306 ARRAY_SIZE(ksphy_driver
));
309 static void __exit
ksphy_exit(void)
311 phy_drivers_unregister(ksphy_driver
,
312 ARRAY_SIZE(ksphy_driver
));
315 module_init(ksphy_init
);
316 module_exit(ksphy_exit
);
318 MODULE_DESCRIPTION("Micrel PHY driver");
319 MODULE_AUTHOR("David J. Choi");
320 MODULE_LICENSE("GPL");
322 static struct mdio_device_id __maybe_unused micrel_tbl
[] = {
323 { PHY_ID_KSZ9021
, 0x000ffffe },
324 { PHY_ID_KSZ9031
, 0x00fffff0 },
325 { PHY_ID_KSZ8001
, 0x00ffffff },
326 { PHY_ID_KS8737
, 0x00fffff0 },
327 { PHY_ID_KSZ8021
, 0x00ffffff },
328 { PHY_ID_KSZ8041
, 0x00fffff0 },
329 { PHY_ID_KSZ8051
, 0x00fffff0 },
330 { PHY_ID_KSZ8061
, 0x00fffff0 },
331 { PHY_ID_KSZ8081
, 0x00fffff0 },
332 { PHY_ID_KSZ8873MLL
, 0x00fffff0 },
333 { PHY_ID_KSZ886X
, 0x00fffff0 },
337 MODULE_DEVICE_TABLE(mdio
, micrel_tbl
);