xref: /linux/drivers/net/phy/micrel.c (revision d0b73b488c55df905ea8faaad079f8535629ed26)
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 #define KSZ8873MLL_GLOBAL_CONTROL_4	0x06
131 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX	(1 << 6)
132 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED	(1 << 4)
133 int ksz8873mll_read_status(struct phy_device *phydev)
134 {
135 	int regval;
136 
137 	/* dummy read */
138 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
139 
140 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
141 
142 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
143 		phydev->duplex = DUPLEX_HALF;
144 	else
145 		phydev->duplex = DUPLEX_FULL;
146 
147 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
148 		phydev->speed = SPEED_10;
149 	else
150 		phydev->speed = SPEED_100;
151 
152 	phydev->link = 1;
153 	phydev->pause = phydev->asym_pause = 0;
154 
155 	return 0;
156 }
157 
158 static int ksz8873mll_config_aneg(struct phy_device *phydev)
159 {
160 	return 0;
161 }
162 
163 static struct phy_driver ksphy_driver[] = {
164 {
165 	.phy_id		= PHY_ID_KS8737,
166 	.phy_id_mask	= 0x00fffff0,
167 	.name		= "Micrel KS8737",
168 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause),
169 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
170 	.config_init	= kszphy_config_init,
171 	.config_aneg	= genphy_config_aneg,
172 	.read_status	= genphy_read_status,
173 	.ack_interrupt	= kszphy_ack_interrupt,
174 	.config_intr	= ks8737_config_intr,
175 	.driver		= { .owner = THIS_MODULE,},
176 }, {
177 	.phy_id		= PHY_ID_KSZ8021,
178 	.phy_id_mask	= 0x00ffffff,
179 	.name		= "Micrel KSZ8021",
180 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
181 			   SUPPORTED_Asym_Pause),
182 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
183 	.config_init	= ksz8021_config_init,
184 	.config_aneg	= genphy_config_aneg,
185 	.read_status	= genphy_read_status,
186 	.ack_interrupt	= kszphy_ack_interrupt,
187 	.config_intr	= kszphy_config_intr,
188 	.driver		= { .owner = THIS_MODULE,},
189 }, {
190 	.phy_id		= PHY_ID_KSZ8041,
191 	.phy_id_mask	= 0x00fffff0,
192 	.name		= "Micrel KSZ8041",
193 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
194 				| SUPPORTED_Asym_Pause),
195 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
196 	.config_init	= kszphy_config_init,
197 	.config_aneg	= genphy_config_aneg,
198 	.read_status	= genphy_read_status,
199 	.ack_interrupt	= kszphy_ack_interrupt,
200 	.config_intr	= kszphy_config_intr,
201 	.driver		= { .owner = THIS_MODULE,},
202 }, {
203 	.phy_id		= PHY_ID_KSZ8051,
204 	.phy_id_mask	= 0x00fffff0,
205 	.name		= "Micrel KSZ8051",
206 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause
207 				| SUPPORTED_Asym_Pause),
208 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
209 	.config_init	= ks8051_config_init,
210 	.config_aneg	= genphy_config_aneg,
211 	.read_status	= genphy_read_status,
212 	.ack_interrupt	= kszphy_ack_interrupt,
213 	.config_intr	= kszphy_config_intr,
214 	.driver		= { .owner = THIS_MODULE,},
215 }, {
216 	.phy_id		= PHY_ID_KSZ8001,
217 	.name		= "Micrel KSZ8001 or KS8721",
218 	.phy_id_mask	= 0x00ffffff,
219 	.features	= (PHY_BASIC_FEATURES | SUPPORTED_Pause),
220 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
221 	.config_init	= kszphy_config_init,
222 	.config_aneg	= genphy_config_aneg,
223 	.read_status	= genphy_read_status,
224 	.ack_interrupt	= kszphy_ack_interrupt,
225 	.config_intr	= kszphy_config_intr,
226 	.driver		= { .owner = THIS_MODULE,},
227 }, {
228 	.phy_id		= PHY_ID_KSZ9021,
229 	.phy_id_mask	= 0x000ffffe,
230 	.name		= "Micrel KSZ9021 Gigabit PHY",
231 	.features	= (PHY_GBIT_FEATURES | SUPPORTED_Pause
232 				| SUPPORTED_Asym_Pause),
233 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
234 	.config_init	= kszphy_config_init,
235 	.config_aneg	= genphy_config_aneg,
236 	.read_status	= genphy_read_status,
237 	.ack_interrupt	= kszphy_ack_interrupt,
238 	.config_intr	= ksz9021_config_intr,
239 	.driver		= { .owner = THIS_MODULE, },
240 }, {
241 	.phy_id		= PHY_ID_KSZ8873MLL,
242 	.phy_id_mask	= 0x00fffff0,
243 	.name		= "Micrel KSZ8873MLL Switch",
244 	.features	= (SUPPORTED_Pause | SUPPORTED_Asym_Pause),
245 	.flags		= PHY_HAS_MAGICANEG,
246 	.config_init	= kszphy_config_init,
247 	.config_aneg	= ksz8873mll_config_aneg,
248 	.read_status	= ksz8873mll_read_status,
249 	.driver		= { .owner = THIS_MODULE, },
250 } };
251 
252 static int __init ksphy_init(void)
253 {
254 	return phy_drivers_register(ksphy_driver,
255 		ARRAY_SIZE(ksphy_driver));
256 }
257 
258 static void __exit ksphy_exit(void)
259 {
260 	phy_drivers_unregister(ksphy_driver,
261 		ARRAY_SIZE(ksphy_driver));
262 }
263 
264 module_init(ksphy_init);
265 module_exit(ksphy_exit);
266 
267 MODULE_DESCRIPTION("Micrel PHY driver");
268 MODULE_AUTHOR("David J. Choi");
269 MODULE_LICENSE("GPL");
270 
271 static struct mdio_device_id __maybe_unused micrel_tbl[] = {
272 	{ PHY_ID_KSZ9021, 0x000ffffe },
273 	{ PHY_ID_KSZ8001, 0x00ffffff },
274 	{ PHY_ID_KS8737, 0x00fffff0 },
275 	{ PHY_ID_KSZ8021, 0x00ffffff },
276 	{ PHY_ID_KSZ8041, 0x00fffff0 },
277 	{ PHY_ID_KSZ8051, 0x00fffff0 },
278 	{ PHY_ID_KSZ8873MLL, 0x00fffff0 },
279 	{ }
280 };
281 
282 MODULE_DEVICE_TABLE(mdio, micrel_tbl);
283