xref: /openbmc/linux/drivers/net/phy/micrel.c (revision 9d749629)
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 kszphy_ack_interrupt(struct phy_device *phydev)
57 {
58 	/* bit[7..0] int status, which is a read and clear register. */
59 	int rc;
60 
61 	rc = phy_read(phydev, MII_KSZPHY_INTCS);
62 
63 	return (rc < 0) ? rc : 0;
64 }
65 
66 static int kszphy_set_interrupt(struct phy_device *phydev)
67 {
68 	int temp;
69 	temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ?
70 		KSZPHY_INTCS_ALL : 0;
71 	return phy_write(phydev, MII_KSZPHY_INTCS, temp);
72 }
73 
74 static int kszphy_config_intr(struct phy_device *phydev)
75 {
76 	int temp, rc;
77 
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;
84 }
85 
86 static int ksz9021_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 &= ~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;
96 }
97 
98 static int ks8737_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 &= ~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;
108 }
109 
110 static int kszphy_config_init(struct phy_device *phydev)
111 {
112 	return 0;
113 }
114 
115 static int ksz8021_config_init(struct phy_device *phydev)
116 {
117 	const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
118 	phy_write(phydev, MII_KSZPHY_OMSO, val);
119 	return 0;
120 }
121 
122 static int ks8051_config_init(struct phy_device *phydev)
123 {
124 	int regval;
125 
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);
130 	}
131 
132 	return 0;
133 }
134 
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)
139 {
140 	int regval;
141 
142 	/* dummy read */
143 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
144 
145 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
146 
147 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
148 		phydev->duplex = DUPLEX_HALF;
149 	else
150 		phydev->duplex = DUPLEX_FULL;
151 
152 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
153 		phydev->speed = SPEED_10;
154 	else
155 		phydev->speed = SPEED_100;
156 
157 	phydev->link = 1;
158 	phydev->pause = phydev->asym_pause = 0;
159 
160 	return 0;
161 }
162 
163 static int ksz8873mll_config_aneg(struct phy_device *phydev)
164 {
165 	return 0;
166 }
167 
168 static struct phy_driver ksphy_driver[] = {
169 {
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,},
181 }, {
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,},
194 }, {
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,},
207 }, {
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,},
220 }, {
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,},
232 }, {
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,},
244 }, {
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,},
256 }, {
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 				| SUPPORTED_Asym_Pause),
262 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
263 	.config_init	= kszphy_config_init,
264 	.config_aneg	= genphy_config_aneg,
265 	.read_status	= genphy_read_status,
266 	.ack_interrupt	= kszphy_ack_interrupt,
267 	.config_intr	= ksz9021_config_intr,
268 	.driver		= { .owner = THIS_MODULE, },
269 }, {
270 	.phy_id		= PHY_ID_KSZ9031,
271 	.phy_id_mask	= 0x00fffff0,
272 	.name		= "Micrel KSZ9031 Gigabit PHY",
273 	.features	= (PHY_GBIT_FEATURES | SUPPORTED_Pause
274 				| SUPPORTED_Asym_Pause),
275 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
276 	.config_init	= kszphy_config_init,
277 	.config_aneg	= genphy_config_aneg,
278 	.read_status	= genphy_read_status,
279 	.ack_interrupt	= kszphy_ack_interrupt,
280 	.config_intr	= ksz9021_config_intr,
281 	.driver		= { .owner = THIS_MODULE, },
282 }, {
283 	.phy_id		= PHY_ID_KSZ8873MLL,
284 	.phy_id_mask	= 0x00fffff0,
285 	.name		= "Micrel KSZ8873MLL Switch",
286 	.features	= (SUPPORTED_Pause | SUPPORTED_Asym_Pause),
287 	.flags		= PHY_HAS_MAGICANEG,
288 	.config_init	= kszphy_config_init,
289 	.config_aneg	= ksz8873mll_config_aneg,
290 	.read_status	= ksz8873mll_read_status,
291 	.driver		= { .owner = THIS_MODULE, },
292 }, {
293 	.phy_id		= PHY_ID_KSZ886X,
294 	.phy_id_mask	= 0x00fffff0,
295 	.name		= "Micrel KSZ886X Switch",
296 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause),
297 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
298 	.config_init	= kszphy_config_init,
299 	.config_aneg	= genphy_config_aneg,
300 	.read_status	= genphy_read_status,
301 	.driver		= { .owner = THIS_MODULE, },
302 } };
303 
304 static int __init ksphy_init(void)
305 {
306 	return phy_drivers_register(ksphy_driver,
307 		ARRAY_SIZE(ksphy_driver));
308 }
309 
310 static void __exit ksphy_exit(void)
311 {
312 	phy_drivers_unregister(ksphy_driver,
313 		ARRAY_SIZE(ksphy_driver));
314 }
315 
316 module_init(ksphy_init);
317 module_exit(ksphy_exit);
318 
319 MODULE_DESCRIPTION("Micrel PHY driver");
320 MODULE_AUTHOR("David J. Choi");
321 MODULE_LICENSE("GPL");
322 
323 static struct mdio_device_id __maybe_unused micrel_tbl[] = {
324 	{ PHY_ID_KSZ9021, 0x000ffffe },
325 	{ PHY_ID_KSZ9031, 0x00fffff0 },
326 	{ PHY_ID_KSZ8001, 0x00ffffff },
327 	{ PHY_ID_KS8737, 0x00fffff0 },
328 	{ PHY_ID_KSZ8021, 0x00ffffff },
329 	{ PHY_ID_KSZ8041, 0x00fffff0 },
330 	{ PHY_ID_KSZ8051, 0x00fffff0 },
331 	{ PHY_ID_KSZ8061, 0x00fffff0 },
332 	{ PHY_ID_KSZ8081, 0x00fffff0 },
333 	{ PHY_ID_KSZ8873MLL, 0x00fffff0 },
334 	{ PHY_ID_KSZ886X, 0x00fffff0 },
335 	{ }
336 };
337 
338 MODULE_DEVICE_TABLE(mdio, micrel_tbl);
339