xref: /linux/drivers/net/phy/dp83tc811.c (revision 5bdd5fbb35ab0fe21bf2263106f51c5bee466a07)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for the Texas Instruments DP83TC811 PHY
4  *
5  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
6  *
7  */
8 
9 #include <linux/ethtool.h>
10 #include <linux/etherdevice.h>
11 #include <linux/kernel.h>
12 #include <linux/mii.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/phy.h>
16 #include <linux/netdevice.h>
17 
18 #define DP83TC811_PHY_ID	0x2000a253
19 #define DP83811_DEVADDR		0x1f
20 
21 #define MII_DP83811_SGMII_CTRL	0x09
22 #define MII_DP83811_INT_STAT1	0x12
23 #define MII_DP83811_INT_STAT2	0x13
24 #define MII_DP83811_RESET_CTRL	0x1f
25 
26 #define DP83811_HW_RESET	BIT(15)
27 #define DP83811_SW_RESET	BIT(14)
28 
29 /* INT_STAT1 bits */
30 #define DP83811_RX_ERR_HF_INT_EN	BIT(0)
31 #define DP83811_MS_TRAINING_INT_EN	BIT(1)
32 #define DP83811_ANEG_COMPLETE_INT_EN	BIT(2)
33 #define DP83811_ESD_EVENT_INT_EN	BIT(3)
34 #define DP83811_WOL_INT_EN		BIT(4)
35 #define DP83811_LINK_STAT_INT_EN	BIT(5)
36 #define DP83811_ENERGY_DET_INT_EN	BIT(6)
37 #define DP83811_LINK_QUAL_INT_EN	BIT(7)
38 
39 /* INT_STAT2 bits */
40 #define DP83811_JABBER_DET_INT_EN	BIT(0)
41 #define DP83811_POLARITY_INT_EN		BIT(1)
42 #define DP83811_SLEEP_MODE_INT_EN	BIT(2)
43 #define DP83811_OVERTEMP_INT_EN		BIT(3)
44 #define DP83811_OVERVOLTAGE_INT_EN	BIT(6)
45 #define DP83811_UNDERVOLTAGE_INT_EN	BIT(7)
46 
47 #define MII_DP83811_RXSOP1	0x04a5
48 #define MII_DP83811_RXSOP2	0x04a6
49 #define MII_DP83811_RXSOP3	0x04a7
50 
51 /* WoL Registers */
52 #define MII_DP83811_WOL_CFG	0x04a0
53 #define MII_DP83811_WOL_STAT	0x04a1
54 #define MII_DP83811_WOL_DA1	0x04a2
55 #define MII_DP83811_WOL_DA2	0x04a3
56 #define MII_DP83811_WOL_DA3	0x04a4
57 
58 /* WoL bits */
59 #define DP83811_WOL_MAGIC_EN	BIT(0)
60 #define DP83811_WOL_SECURE_ON	BIT(5)
61 #define DP83811_WOL_EN		BIT(7)
62 #define DP83811_WOL_INDICATION_SEL BIT(8)
63 #define DP83811_WOL_CLR_INDICATION BIT(11)
64 
65 /* SGMII CTRL bits */
66 #define DP83811_TDR_AUTO		BIT(8)
67 #define DP83811_SGMII_EN		BIT(12)
68 #define DP83811_SGMII_AUTO_NEG_EN	BIT(13)
69 #define DP83811_SGMII_TX_ERR_DIS	BIT(14)
70 #define DP83811_SGMII_SOFT_RESET	BIT(15)
71 
72 static int dp83811_ack_interrupt(struct phy_device *phydev)
73 {
74 	int err;
75 
76 	err = phy_read(phydev, MII_DP83811_INT_STAT1);
77 	if (err < 0)
78 		return err;
79 
80 	err = phy_read(phydev, MII_DP83811_INT_STAT2);
81 	if (err < 0)
82 		return err;
83 
84 	return 0;
85 }
86 
87 static int dp83811_set_wol(struct phy_device *phydev,
88 			   struct ethtool_wolinfo *wol)
89 {
90 	struct net_device *ndev = phydev->attached_dev;
91 	const u8 *mac;
92 	u16 value;
93 
94 	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
95 		mac = (const u8 *)ndev->dev_addr;
96 
97 		if (!is_valid_ether_addr(mac))
98 			return -EINVAL;
99 
100 		/* MAC addresses start with byte 5, but stored in mac[0].
101 		 * 811 PHYs store bytes 4|5, 2|3, 0|1
102 		 */
103 		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA1,
104 			      (mac[1] << 8) | mac[0]);
105 		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA2,
106 			      (mac[3] << 8) | mac[2]);
107 		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA3,
108 			      (mac[5] << 8) | mac[4]);
109 
110 		value = phy_read_mmd(phydev, DP83811_DEVADDR,
111 				     MII_DP83811_WOL_CFG);
112 		if (wol->wolopts & WAKE_MAGIC)
113 			value |= DP83811_WOL_MAGIC_EN;
114 		else
115 			value &= ~DP83811_WOL_MAGIC_EN;
116 
117 		if (wol->wolopts & WAKE_MAGICSECURE) {
118 			phy_write_mmd(phydev, DP83811_DEVADDR,
119 				      MII_DP83811_RXSOP1,
120 				      (wol->sopass[1] << 8) | wol->sopass[0]);
121 			phy_write_mmd(phydev, DP83811_DEVADDR,
122 				      MII_DP83811_RXSOP2,
123 				      (wol->sopass[3] << 8) | wol->sopass[2]);
124 			phy_write_mmd(phydev, DP83811_DEVADDR,
125 				      MII_DP83811_RXSOP3,
126 				      (wol->sopass[5] << 8) | wol->sopass[4]);
127 			value |= DP83811_WOL_SECURE_ON;
128 		} else {
129 			value &= ~DP83811_WOL_SECURE_ON;
130 		}
131 
132 		value |= (DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL |
133 			  DP83811_WOL_CLR_INDICATION);
134 		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
135 			      value);
136 	} else {
137 		value = phy_read_mmd(phydev, DP83811_DEVADDR,
138 				     MII_DP83811_WOL_CFG);
139 		value &= ~DP83811_WOL_EN;
140 		phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
141 			      value);
142 	}
143 
144 	return 0;
145 }
146 
147 static void dp83811_get_wol(struct phy_device *phydev,
148 			    struct ethtool_wolinfo *wol)
149 {
150 	u16 sopass_val;
151 	int value;
152 
153 	wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
154 	wol->wolopts = 0;
155 
156 	value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
157 
158 	if (value & DP83811_WOL_MAGIC_EN)
159 		wol->wolopts |= WAKE_MAGIC;
160 
161 	if (value & DP83811_WOL_SECURE_ON) {
162 		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
163 					  MII_DP83811_RXSOP1);
164 		wol->sopass[0] = (sopass_val & 0xff);
165 		wol->sopass[1] = (sopass_val >> 8);
166 
167 		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
168 					  MII_DP83811_RXSOP2);
169 		wol->sopass[2] = (sopass_val & 0xff);
170 		wol->sopass[3] = (sopass_val >> 8);
171 
172 		sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR,
173 					  MII_DP83811_RXSOP3);
174 		wol->sopass[4] = (sopass_val & 0xff);
175 		wol->sopass[5] = (sopass_val >> 8);
176 
177 		wol->wolopts |= WAKE_MAGICSECURE;
178 	}
179 
180 	/* WoL is not enabled so set wolopts to 0 */
181 	if (!(value & DP83811_WOL_EN))
182 		wol->wolopts = 0;
183 }
184 
185 static int dp83811_config_intr(struct phy_device *phydev)
186 {
187 	int misr_status, err;
188 
189 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
190 		misr_status = phy_read(phydev, MII_DP83811_INT_STAT1);
191 		if (misr_status < 0)
192 			return misr_status;
193 
194 		misr_status |= (DP83811_RX_ERR_HF_INT_EN |
195 				DP83811_MS_TRAINING_INT_EN |
196 				DP83811_ANEG_COMPLETE_INT_EN |
197 				DP83811_ESD_EVENT_INT_EN |
198 				DP83811_WOL_INT_EN |
199 				DP83811_LINK_STAT_INT_EN |
200 				DP83811_ENERGY_DET_INT_EN |
201 				DP83811_LINK_QUAL_INT_EN);
202 
203 		err = phy_write(phydev, MII_DP83811_INT_STAT1, misr_status);
204 		if (err < 0)
205 			return err;
206 
207 		misr_status = phy_read(phydev, MII_DP83811_INT_STAT2);
208 		if (misr_status < 0)
209 			return misr_status;
210 
211 		misr_status |= (DP83811_JABBER_DET_INT_EN |
212 				DP83811_POLARITY_INT_EN |
213 				DP83811_SLEEP_MODE_INT_EN |
214 				DP83811_OVERTEMP_INT_EN |
215 				DP83811_OVERVOLTAGE_INT_EN |
216 				DP83811_UNDERVOLTAGE_INT_EN);
217 
218 		err = phy_write(phydev, MII_DP83811_INT_STAT2, misr_status);
219 
220 	} else {
221 		err = phy_write(phydev, MII_DP83811_INT_STAT1, 0);
222 		if (err < 0)
223 			return err;
224 
225 		err = phy_write(phydev, MII_DP83811_INT_STAT1, 0);
226 	}
227 
228 	return err;
229 }
230 
231 static int dp83811_config_aneg(struct phy_device *phydev)
232 {
233 	int value, err;
234 
235 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
236 		value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
237 		if (phydev->autoneg == AUTONEG_ENABLE) {
238 			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
239 					(DP83811_SGMII_AUTO_NEG_EN | value));
240 			if (err < 0)
241 				return err;
242 		} else {
243 			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
244 					(~DP83811_SGMII_AUTO_NEG_EN & value));
245 			if (err < 0)
246 				return err;
247 		}
248 	}
249 
250 	return genphy_config_aneg(phydev);
251 }
252 
253 static int dp83811_config_init(struct phy_device *phydev)
254 {
255 	int value, err;
256 
257 	err = genphy_config_init(phydev);
258 	if (err < 0)
259 		return err;
260 
261 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
262 		value = phy_read(phydev, MII_DP83811_SGMII_CTRL);
263 		if (!(value & DP83811_SGMII_EN)) {
264 			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
265 					(DP83811_SGMII_EN | value));
266 			if (err < 0)
267 				return err;
268 		} else {
269 			err = phy_write(phydev, MII_DP83811_SGMII_CTRL,
270 					(~DP83811_SGMII_EN & value));
271 			if (err < 0)
272 				return err;
273 		}
274 	}
275 
276 	value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;
277 
278 	return phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
279 	      value);
280 }
281 
282 static int dp83811_phy_reset(struct phy_device *phydev)
283 {
284 	int err;
285 
286 	err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET);
287 	if (err < 0)
288 		return err;
289 
290 	return 0;
291 }
292 
293 static int dp83811_suspend(struct phy_device *phydev)
294 {
295 	int value;
296 
297 	value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
298 
299 	if (!(value & DP83811_WOL_EN))
300 		genphy_suspend(phydev);
301 
302 	return 0;
303 }
304 
305 static int dp83811_resume(struct phy_device *phydev)
306 {
307 	int value;
308 
309 	genphy_resume(phydev);
310 
311 	value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG);
312 
313 	phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, value |
314 		      DP83811_WOL_CLR_INDICATION);
315 
316 	return 0;
317 }
318 
319 static struct phy_driver dp83811_driver[] = {
320 	{
321 		.phy_id = DP83TC811_PHY_ID,
322 		.phy_id_mask = 0xfffffff0,
323 		.name = "TI DP83TC811",
324 		.features = PHY_BASIC_FEATURES,
325 		.flags = PHY_HAS_INTERRUPT,
326 		.config_init = dp83811_config_init,
327 		.config_aneg = dp83811_config_aneg,
328 		.soft_reset = dp83811_phy_reset,
329 		.get_wol = dp83811_get_wol,
330 		.set_wol = dp83811_set_wol,
331 		.ack_interrupt = dp83811_ack_interrupt,
332 		.config_intr = dp83811_config_intr,
333 		.suspend = dp83811_suspend,
334 		.resume = dp83811_resume,
335 	 },
336 };
337 module_phy_driver(dp83811_driver);
338 
339 static struct mdio_device_id __maybe_unused dp83811_tbl[] = {
340 	{ DP83TC811_PHY_ID, 0xfffffff0 },
341 	{ },
342 };
343 MODULE_DEVICE_TABLE(mdio, dp83811_tbl);
344 
345 MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver");
346 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
347 MODULE_LICENSE("GPL");
348