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 ksz_config_flags(struct phy_device
*phydev
)
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
);
68 static int kszphy_ack_interrupt(struct phy_device
*phydev
)
70 /* bit[7..0] int status, which is a read and clear register. */
73 rc
= phy_read(phydev
, MII_KSZPHY_INTCS
);
75 return (rc
< 0) ? rc
: 0;
78 static int kszphy_set_interrupt(struct phy_device
*phydev
)
81 temp
= (PHY_INTERRUPT_ENABLED
== phydev
->interrupts
) ?
83 return phy_write(phydev
, MII_KSZPHY_INTCS
, temp
);
86 static int kszphy_config_intr(struct phy_device
*phydev
)
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;
98 static int ksz9021_config_intr(struct phy_device
*phydev
)
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;
110 static int ks8737_config_intr(struct phy_device
*phydev
)
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;
122 static int kszphy_config_init(struct phy_device
*phydev
)
127 static int ksz8021_config_init(struct phy_device
*phydev
)
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;
136 static int ks8051_config_init(struct phy_device
*phydev
)
140 rc
= ksz_config_flags(phydev
);
141 return rc
< 0 ? rc
: 0;
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
)
152 regval
= phy_read(phydev
, KSZ8873MLL_GLOBAL_CONTROL_4
);
154 regval
= phy_read(phydev
, KSZ8873MLL_GLOBAL_CONTROL_4
);
156 if (regval
& KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX
)
157 phydev
->duplex
= DUPLEX_HALF
;
159 phydev
->duplex
= DUPLEX_FULL
;
161 if (regval
& KSZ8873MLL_GLOBAL_CONTROL_4_SPEED
)
162 phydev
->speed
= SPEED_10
;
164 phydev
->speed
= SPEED_100
;
167 phydev
->pause
= phydev
->asym_pause
= 0;
172 static int ksz8873mll_config_aneg(struct phy_device
*phydev
)
177 static struct phy_driver ksphy_driver
[] = {
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
,},
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
,},
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
,},
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
,},
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
,},
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
,},
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
,},
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
,},
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
, },
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
, },
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
, },
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
, },
325 static int __init
ksphy_init(void)
327 return phy_drivers_register(ksphy_driver
,
328 ARRAY_SIZE(ksphy_driver
));
331 static void __exit
ksphy_exit(void)
333 phy_drivers_unregister(ksphy_driver
,
334 ARRAY_SIZE(ksphy_driver
));
337 module_init(ksphy_init
);
338 module_exit(ksphy_exit
);
340 MODULE_DESCRIPTION("Micrel PHY driver");
341 MODULE_AUTHOR("David J. Choi");
342 MODULE_LICENSE("GPL");
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 },
360 MODULE_DEVICE_TABLE(mdio
, micrel_tbl
);