xref: /openbmc/linux/drivers/net/phy/micrel.c (revision ee89bd6b)
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