xref: /openbmc/linux/drivers/net/phy/dp83822.c (revision 76ce0265)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Driver for the Texas Instruments DP83822, DP83825 and DP83826 PHYs.
3  *
4  * Copyright (C) 2017 Texas Instruments Inc.
5  */
6 
7 #include <linux/ethtool.h>
8 #include <linux/etherdevice.h>
9 #include <linux/kernel.h>
10 #include <linux/mii.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/phy.h>
14 #include <linux/netdevice.h>
15 
16 #define DP83822_PHY_ID	        0x2000a240
17 #define DP83825S_PHY_ID		0x2000a140
18 #define DP83825I_PHY_ID		0x2000a150
19 #define DP83825CM_PHY_ID	0x2000a160
20 #define DP83825CS_PHY_ID	0x2000a170
21 #define DP83826C_PHY_ID		0x2000a130
22 #define DP83826NC_PHY_ID	0x2000a110
23 
24 #define DP83822_DEVADDR		0x1f
25 
26 #define MII_DP83822_PHYSCR	0x11
27 #define MII_DP83822_MISR1	0x12
28 #define MII_DP83822_MISR2	0x13
29 #define MII_DP83822_RESET_CTRL	0x1f
30 
31 #define DP83822_HW_RESET	BIT(15)
32 #define DP83822_SW_RESET	BIT(14)
33 
34 /* PHYSCR Register Fields */
35 #define DP83822_PHYSCR_INT_OE		BIT(0) /* Interrupt Output Enable */
36 #define DP83822_PHYSCR_INTEN		BIT(1) /* Interrupt Enable */
37 
38 /* MISR1 bits */
39 #define DP83822_RX_ERR_HF_INT_EN	BIT(0)
40 #define DP83822_FALSE_CARRIER_HF_INT_EN	BIT(1)
41 #define DP83822_ANEG_COMPLETE_INT_EN	BIT(2)
42 #define DP83822_DUP_MODE_CHANGE_INT_EN	BIT(3)
43 #define DP83822_SPEED_CHANGED_INT_EN	BIT(4)
44 #define DP83822_LINK_STAT_INT_EN	BIT(5)
45 #define DP83822_ENERGY_DET_INT_EN	BIT(6)
46 #define DP83822_LINK_QUAL_INT_EN	BIT(7)
47 
48 /* MISR2 bits */
49 #define DP83822_JABBER_DET_INT_EN	BIT(0)
50 #define DP83822_WOL_PKT_INT_EN		BIT(1)
51 #define DP83822_SLEEP_MODE_INT_EN	BIT(2)
52 #define DP83822_MDI_XOVER_INT_EN	BIT(3)
53 #define DP83822_LB_FIFO_INT_EN		BIT(4)
54 #define DP83822_PAGE_RX_INT_EN		BIT(5)
55 #define DP83822_ANEG_ERR_INT_EN		BIT(6)
56 #define DP83822_EEE_ERROR_CHANGE_INT_EN	BIT(7)
57 
58 /* INT_STAT1 bits */
59 #define DP83822_WOL_INT_EN	BIT(4)
60 #define DP83822_WOL_INT_STAT	BIT(12)
61 
62 #define MII_DP83822_RXSOP1	0x04a5
63 #define	MII_DP83822_RXSOP2	0x04a6
64 #define	MII_DP83822_RXSOP3	0x04a7
65 
66 /* WoL Registers */
67 #define	MII_DP83822_WOL_CFG	0x04a0
68 #define	MII_DP83822_WOL_STAT	0x04a1
69 #define	MII_DP83822_WOL_DA1	0x04a2
70 #define	MII_DP83822_WOL_DA2	0x04a3
71 #define	MII_DP83822_WOL_DA3	0x04a4
72 
73 /* WoL bits */
74 #define DP83822_WOL_MAGIC_EN	BIT(0)
75 #define DP83822_WOL_SECURE_ON	BIT(5)
76 #define DP83822_WOL_EN		BIT(7)
77 #define DP83822_WOL_INDICATION_SEL BIT(8)
78 #define DP83822_WOL_CLR_INDICATION BIT(11)
79 
80 static int dp83822_ack_interrupt(struct phy_device *phydev)
81 {
82 	int err;
83 
84 	err = phy_read(phydev, MII_DP83822_MISR1);
85 	if (err < 0)
86 		return err;
87 
88 	err = phy_read(phydev, MII_DP83822_MISR2);
89 	if (err < 0)
90 		return err;
91 
92 	return 0;
93 }
94 
95 static int dp83822_set_wol(struct phy_device *phydev,
96 			   struct ethtool_wolinfo *wol)
97 {
98 	struct net_device *ndev = phydev->attached_dev;
99 	u16 value;
100 	const u8 *mac;
101 
102 	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
103 		mac = (const u8 *)ndev->dev_addr;
104 
105 		if (!is_valid_ether_addr(mac))
106 			return -EINVAL;
107 
108 		/* MAC addresses start with byte 5, but stored in mac[0].
109 		 * 822 PHYs store bytes 4|5, 2|3, 0|1
110 		 */
111 		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1,
112 			      (mac[1] << 8) | mac[0]);
113 		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2,
114 			      (mac[3] << 8) | mac[2]);
115 		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3,
116 			      (mac[5] << 8) | mac[4]);
117 
118 		value = phy_read_mmd(phydev, DP83822_DEVADDR,
119 				     MII_DP83822_WOL_CFG);
120 		if (wol->wolopts & WAKE_MAGIC)
121 			value |= DP83822_WOL_MAGIC_EN;
122 		else
123 			value &= ~DP83822_WOL_MAGIC_EN;
124 
125 		if (wol->wolopts & WAKE_MAGICSECURE) {
126 			phy_write_mmd(phydev, DP83822_DEVADDR,
127 				      MII_DP83822_RXSOP1,
128 				      (wol->sopass[1] << 8) | wol->sopass[0]);
129 			phy_write_mmd(phydev, DP83822_DEVADDR,
130 				      MII_DP83822_RXSOP2,
131 				      (wol->sopass[3] << 8) | wol->sopass[2]);
132 			phy_write_mmd(phydev, DP83822_DEVADDR,
133 				      MII_DP83822_RXSOP3,
134 				      (wol->sopass[5] << 8) | wol->sopass[4]);
135 			value |= DP83822_WOL_SECURE_ON;
136 		} else {
137 			value &= ~DP83822_WOL_SECURE_ON;
138 		}
139 
140 		value |= (DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
141 			  DP83822_WOL_CLR_INDICATION);
142 		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
143 			      value);
144 	} else {
145 		value = phy_read_mmd(phydev, DP83822_DEVADDR,
146 				     MII_DP83822_WOL_CFG);
147 		value &= ~DP83822_WOL_EN;
148 		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
149 			      value);
150 	}
151 
152 	return 0;
153 }
154 
155 static void dp83822_get_wol(struct phy_device *phydev,
156 			    struct ethtool_wolinfo *wol)
157 {
158 	int value;
159 	u16 sopass_val;
160 
161 	wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
162 	wol->wolopts = 0;
163 
164 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
165 
166 	if (value & DP83822_WOL_MAGIC_EN)
167 		wol->wolopts |= WAKE_MAGIC;
168 
169 	if (value & DP83822_WOL_SECURE_ON) {
170 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
171 					  MII_DP83822_RXSOP1);
172 		wol->sopass[0] = (sopass_val & 0xff);
173 		wol->sopass[1] = (sopass_val >> 8);
174 
175 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
176 					  MII_DP83822_RXSOP2);
177 		wol->sopass[2] = (sopass_val & 0xff);
178 		wol->sopass[3] = (sopass_val >> 8);
179 
180 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
181 					  MII_DP83822_RXSOP3);
182 		wol->sopass[4] = (sopass_val & 0xff);
183 		wol->sopass[5] = (sopass_val >> 8);
184 
185 		wol->wolopts |= WAKE_MAGICSECURE;
186 	}
187 
188 	/* WoL is not enabled so set wolopts to 0 */
189 	if (!(value & DP83822_WOL_EN))
190 		wol->wolopts = 0;
191 }
192 
193 static int dp83822_config_intr(struct phy_device *phydev)
194 {
195 	int misr_status;
196 	int physcr_status;
197 	int err;
198 
199 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
200 		misr_status = phy_read(phydev, MII_DP83822_MISR1);
201 		if (misr_status < 0)
202 			return misr_status;
203 
204 		misr_status |= (DP83822_RX_ERR_HF_INT_EN |
205 				DP83822_FALSE_CARRIER_HF_INT_EN |
206 				DP83822_ANEG_COMPLETE_INT_EN |
207 				DP83822_DUP_MODE_CHANGE_INT_EN |
208 				DP83822_SPEED_CHANGED_INT_EN |
209 				DP83822_LINK_STAT_INT_EN |
210 				DP83822_ENERGY_DET_INT_EN |
211 				DP83822_LINK_QUAL_INT_EN);
212 
213 		err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
214 		if (err < 0)
215 			return err;
216 
217 		misr_status = phy_read(phydev, MII_DP83822_MISR2);
218 		if (misr_status < 0)
219 			return misr_status;
220 
221 		misr_status |= (DP83822_JABBER_DET_INT_EN |
222 				DP83822_WOL_PKT_INT_EN |
223 				DP83822_SLEEP_MODE_INT_EN |
224 				DP83822_MDI_XOVER_INT_EN |
225 				DP83822_LB_FIFO_INT_EN |
226 				DP83822_PAGE_RX_INT_EN |
227 				DP83822_ANEG_ERR_INT_EN |
228 				DP83822_EEE_ERROR_CHANGE_INT_EN);
229 
230 		err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
231 		if (err < 0)
232 			return err;
233 
234 		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
235 		if (physcr_status < 0)
236 			return physcr_status;
237 
238 		physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN;
239 
240 	} else {
241 		err = phy_write(phydev, MII_DP83822_MISR1, 0);
242 		if (err < 0)
243 			return err;
244 
245 		err = phy_write(phydev, MII_DP83822_MISR1, 0);
246 		if (err < 0)
247 			return err;
248 
249 		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
250 		if (physcr_status < 0)
251 			return physcr_status;
252 
253 		physcr_status &= ~DP83822_PHYSCR_INTEN;
254 	}
255 
256 	return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
257 }
258 
259 static int dp83822_config_init(struct phy_device *phydev)
260 {
261 	int value;
262 
263 	value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN;
264 
265 	return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
266 	      value);
267 }
268 
269 static int dp83822_phy_reset(struct phy_device *phydev)
270 {
271 	int err;
272 
273 	err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
274 	if (err < 0)
275 		return err;
276 
277 	dp83822_config_init(phydev);
278 
279 	return 0;
280 }
281 
282 static int dp83822_suspend(struct phy_device *phydev)
283 {
284 	int value;
285 
286 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
287 
288 	if (!(value & DP83822_WOL_EN))
289 		genphy_suspend(phydev);
290 
291 	return 0;
292 }
293 
294 static int dp83822_resume(struct phy_device *phydev)
295 {
296 	int value;
297 
298 	genphy_resume(phydev);
299 
300 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
301 
302 	phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value |
303 		      DP83822_WOL_CLR_INDICATION);
304 
305 	return 0;
306 }
307 
308 #define DP83822_PHY_DRIVER(_id, _name)				\
309 	{							\
310 		PHY_ID_MATCH_MODEL(_id),			\
311 		.name		= (_name),			\
312 		/* PHY_BASIC_FEATURES */			\
313 		.soft_reset	= dp83822_phy_reset,		\
314 		.config_init	= dp83822_config_init,		\
315 		.get_wol = dp83822_get_wol,			\
316 		.set_wol = dp83822_set_wol,			\
317 		.ack_interrupt = dp83822_ack_interrupt,		\
318 		.config_intr = dp83822_config_intr,		\
319 		.suspend = dp83822_suspend,			\
320 		.resume = dp83822_resume,			\
321 	}
322 
323 static struct phy_driver dp83822_driver[] = {
324 	DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
325 	DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
326 	DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
327 	DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
328 	DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
329 	DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
330 	DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
331 };
332 module_phy_driver(dp83822_driver);
333 
334 static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
335 	{ DP83822_PHY_ID, 0xfffffff0 },
336 	{ DP83825I_PHY_ID, 0xfffffff0 },
337 	{ DP83826C_PHY_ID, 0xfffffff0 },
338 	{ DP83826NC_PHY_ID, 0xfffffff0 },
339 	{ DP83825S_PHY_ID, 0xfffffff0 },
340 	{ DP83825CM_PHY_ID, 0xfffffff0 },
341 	{ DP83825CS_PHY_ID, 0xfffffff0 },
342 	{ },
343 };
344 MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
345 
346 MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver");
347 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
348 MODULE_LICENSE("GPL v2");
349