xref: /openbmc/linux/drivers/net/phy/dp83822.c (revision c8ed9fc9)
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 		/* Clear any pending WoL interrupt */
141 		phy_read(phydev, MII_DP83822_MISR2);
142 
143 		value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
144 			 DP83822_WOL_CLR_INDICATION;
145 
146 		return phy_write_mmd(phydev, DP83822_DEVADDR,
147 				     MII_DP83822_WOL_CFG, value);
148 	} else {
149 		return phy_clear_bits_mmd(phydev, DP83822_DEVADDR,
150 					  MII_DP83822_WOL_CFG, DP83822_WOL_EN);
151 	}
152 }
153 
154 static void dp83822_get_wol(struct phy_device *phydev,
155 			    struct ethtool_wolinfo *wol)
156 {
157 	int value;
158 	u16 sopass_val;
159 
160 	wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
161 	wol->wolopts = 0;
162 
163 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
164 
165 	if (value & DP83822_WOL_MAGIC_EN)
166 		wol->wolopts |= WAKE_MAGIC;
167 
168 	if (value & DP83822_WOL_SECURE_ON) {
169 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
170 					  MII_DP83822_RXSOP1);
171 		wol->sopass[0] = (sopass_val & 0xff);
172 		wol->sopass[1] = (sopass_val >> 8);
173 
174 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
175 					  MII_DP83822_RXSOP2);
176 		wol->sopass[2] = (sopass_val & 0xff);
177 		wol->sopass[3] = (sopass_val >> 8);
178 
179 		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
180 					  MII_DP83822_RXSOP3);
181 		wol->sopass[4] = (sopass_val & 0xff);
182 		wol->sopass[5] = (sopass_val >> 8);
183 
184 		wol->wolopts |= WAKE_MAGICSECURE;
185 	}
186 
187 	/* WoL is not enabled so set wolopts to 0 */
188 	if (!(value & DP83822_WOL_EN))
189 		wol->wolopts = 0;
190 }
191 
192 static int dp83822_config_intr(struct phy_device *phydev)
193 {
194 	int misr_status;
195 	int physcr_status;
196 	int err;
197 
198 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
199 		misr_status = phy_read(phydev, MII_DP83822_MISR1);
200 		if (misr_status < 0)
201 			return misr_status;
202 
203 		misr_status |= (DP83822_RX_ERR_HF_INT_EN |
204 				DP83822_FALSE_CARRIER_HF_INT_EN |
205 				DP83822_ANEG_COMPLETE_INT_EN |
206 				DP83822_DUP_MODE_CHANGE_INT_EN |
207 				DP83822_SPEED_CHANGED_INT_EN |
208 				DP83822_LINK_STAT_INT_EN |
209 				DP83822_ENERGY_DET_INT_EN |
210 				DP83822_LINK_QUAL_INT_EN);
211 
212 		err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
213 		if (err < 0)
214 			return err;
215 
216 		misr_status = phy_read(phydev, MII_DP83822_MISR2);
217 		if (misr_status < 0)
218 			return misr_status;
219 
220 		misr_status |= (DP83822_JABBER_DET_INT_EN |
221 				DP83822_WOL_PKT_INT_EN |
222 				DP83822_SLEEP_MODE_INT_EN |
223 				DP83822_MDI_XOVER_INT_EN |
224 				DP83822_LB_FIFO_INT_EN |
225 				DP83822_PAGE_RX_INT_EN |
226 				DP83822_ANEG_ERR_INT_EN |
227 				DP83822_EEE_ERROR_CHANGE_INT_EN);
228 
229 		err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
230 		if (err < 0)
231 			return err;
232 
233 		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
234 		if (physcr_status < 0)
235 			return physcr_status;
236 
237 		physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN;
238 
239 	} else {
240 		err = phy_write(phydev, MII_DP83822_MISR1, 0);
241 		if (err < 0)
242 			return err;
243 
244 		err = phy_write(phydev, MII_DP83822_MISR1, 0);
245 		if (err < 0)
246 			return err;
247 
248 		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
249 		if (physcr_status < 0)
250 			return physcr_status;
251 
252 		physcr_status &= ~DP83822_PHYSCR_INTEN;
253 	}
254 
255 	return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
256 }
257 
258 static int dp83822_config_init(struct phy_device *phydev)
259 {
260 	int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN |
261 		    DP83822_WOL_SECURE_ON;
262 
263 	return phy_clear_bits_mmd(phydev, DP83822_DEVADDR,
264 				  MII_DP83822_WOL_CFG, value);
265 }
266 
267 static int dp83822_phy_reset(struct phy_device *phydev)
268 {
269 	int err;
270 
271 	err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
272 	if (err < 0)
273 		return err;
274 
275 	dp83822_config_init(phydev);
276 
277 	return 0;
278 }
279 
280 static int dp83822_suspend(struct phy_device *phydev)
281 {
282 	int value;
283 
284 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
285 
286 	if (!(value & DP83822_WOL_EN))
287 		genphy_suspend(phydev);
288 
289 	return 0;
290 }
291 
292 static int dp83822_resume(struct phy_device *phydev)
293 {
294 	int value;
295 
296 	genphy_resume(phydev);
297 
298 	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
299 
300 	phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value |
301 		      DP83822_WOL_CLR_INDICATION);
302 
303 	return 0;
304 }
305 
306 #define DP83822_PHY_DRIVER(_id, _name)				\
307 	{							\
308 		PHY_ID_MATCH_MODEL(_id),			\
309 		.name		= (_name),			\
310 		/* PHY_BASIC_FEATURES */			\
311 		.soft_reset	= dp83822_phy_reset,		\
312 		.config_init	= dp83822_config_init,		\
313 		.get_wol = dp83822_get_wol,			\
314 		.set_wol = dp83822_set_wol,			\
315 		.ack_interrupt = dp83822_ack_interrupt,		\
316 		.config_intr = dp83822_config_intr,		\
317 		.suspend = dp83822_suspend,			\
318 		.resume = dp83822_resume,			\
319 	}
320 
321 static struct phy_driver dp83822_driver[] = {
322 	DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
323 	DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
324 	DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
325 	DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
326 	DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
327 	DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
328 	DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
329 };
330 module_phy_driver(dp83822_driver);
331 
332 static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
333 	{ DP83822_PHY_ID, 0xfffffff0 },
334 	{ DP83825I_PHY_ID, 0xfffffff0 },
335 	{ DP83826C_PHY_ID, 0xfffffff0 },
336 	{ DP83826NC_PHY_ID, 0xfffffff0 },
337 	{ DP83825S_PHY_ID, 0xfffffff0 },
338 	{ DP83825CM_PHY_ID, 0xfffffff0 },
339 	{ DP83825CS_PHY_ID, 0xfffffff0 },
340 	{ },
341 };
342 MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
343 
344 MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver");
345 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
346 MODULE_LICENSE("GPL v2");
347