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_INT_STAT3 0x18 |
25 | #define MII_DP83811_RESET_CTRL 0x1f |
26 | |
27 | #define DP83811_HW_RESET BIT(15) |
28 | #define DP83811_SW_RESET BIT(14) |
29 | |
30 | /* INT_STAT1 bits */ |
31 | #define DP83811_RX_ERR_HF_INT_EN BIT(0) |
32 | #define DP83811_MS_TRAINING_INT_EN BIT(1) |
33 | #define DP83811_ANEG_COMPLETE_INT_EN BIT(2) |
34 | #define DP83811_ESD_EVENT_INT_EN BIT(3) |
35 | #define DP83811_WOL_INT_EN BIT(4) |
36 | #define DP83811_LINK_STAT_INT_EN BIT(5) |
37 | #define DP83811_ENERGY_DET_INT_EN BIT(6) |
38 | #define DP83811_LINK_QUAL_INT_EN BIT(7) |
39 | |
40 | /* INT_STAT2 bits */ |
41 | #define DP83811_JABBER_DET_INT_EN BIT(0) |
42 | #define DP83811_POLARITY_INT_EN BIT(1) |
43 | #define DP83811_SLEEP_MODE_INT_EN BIT(2) |
44 | #define DP83811_OVERTEMP_INT_EN BIT(3) |
45 | #define DP83811_OVERVOLTAGE_INT_EN BIT(6) |
46 | #define DP83811_UNDERVOLTAGE_INT_EN BIT(7) |
47 | |
48 | /* INT_STAT3 bits */ |
49 | #define DP83811_LPS_INT_EN BIT(0) |
50 | #define DP83811_NO_FRAME_INT_EN BIT(3) |
51 | #define DP83811_POR_DONE_INT_EN BIT(4) |
52 | |
53 | #define MII_DP83811_RXSOP1 0x04a5 |
54 | #define MII_DP83811_RXSOP2 0x04a6 |
55 | #define MII_DP83811_RXSOP3 0x04a7 |
56 | |
57 | /* WoL Registers */ |
58 | #define MII_DP83811_WOL_CFG 0x04a0 |
59 | #define MII_DP83811_WOL_STAT 0x04a1 |
60 | #define MII_DP83811_WOL_DA1 0x04a2 |
61 | #define MII_DP83811_WOL_DA2 0x04a3 |
62 | #define MII_DP83811_WOL_DA3 0x04a4 |
63 | |
64 | /* WoL bits */ |
65 | #define DP83811_WOL_MAGIC_EN BIT(0) |
66 | #define DP83811_WOL_SECURE_ON BIT(5) |
67 | #define DP83811_WOL_EN BIT(7) |
68 | #define DP83811_WOL_INDICATION_SEL BIT(8) |
69 | #define DP83811_WOL_CLR_INDICATION BIT(11) |
70 | |
71 | /* SGMII CTRL bits */ |
72 | #define DP83811_TDR_AUTO BIT(8) |
73 | #define DP83811_SGMII_EN BIT(12) |
74 | #define DP83811_SGMII_AUTO_NEG_EN BIT(13) |
75 | #define DP83811_SGMII_TX_ERR_DIS BIT(14) |
76 | #define DP83811_SGMII_SOFT_RESET BIT(15) |
77 | |
78 | static int dp83811_ack_interrupt(struct phy_device *phydev) |
79 | { |
80 | int err; |
81 | |
82 | err = phy_read(phydev, MII_DP83811_INT_STAT1); |
83 | if (err < 0) |
84 | return err; |
85 | |
86 | err = phy_read(phydev, MII_DP83811_INT_STAT2); |
87 | if (err < 0) |
88 | return err; |
89 | |
90 | err = phy_read(phydev, MII_DP83811_INT_STAT3); |
91 | if (err < 0) |
92 | return err; |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static int dp83811_set_wol(struct phy_device *phydev, |
98 | struct ethtool_wolinfo *wol) |
99 | { |
100 | struct net_device *ndev = phydev->attached_dev; |
101 | const u8 *mac; |
102 | u16 value; |
103 | |
104 | if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { |
105 | mac = (const u8 *)ndev->dev_addr; |
106 | |
107 | if (!is_valid_ether_addr(addr: mac)) |
108 | return -EINVAL; |
109 | |
110 | /* MAC addresses start with byte 5, but stored in mac[0]. |
111 | * 811 PHYs store bytes 4|5, 2|3, 0|1 |
112 | */ |
113 | phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA1, |
114 | val: (mac[1] << 8) | mac[0]); |
115 | phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA2, |
116 | val: (mac[3] << 8) | mac[2]); |
117 | phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA3, |
118 | val: (mac[5] << 8) | mac[4]); |
119 | |
120 | value = phy_read_mmd(phydev, DP83811_DEVADDR, |
121 | MII_DP83811_WOL_CFG); |
122 | if (wol->wolopts & WAKE_MAGIC) |
123 | value |= DP83811_WOL_MAGIC_EN; |
124 | else |
125 | value &= ~DP83811_WOL_MAGIC_EN; |
126 | |
127 | if (wol->wolopts & WAKE_MAGICSECURE) { |
128 | phy_write_mmd(phydev, DP83811_DEVADDR, |
129 | MII_DP83811_RXSOP1, |
130 | val: (wol->sopass[1] << 8) | wol->sopass[0]); |
131 | phy_write_mmd(phydev, DP83811_DEVADDR, |
132 | MII_DP83811_RXSOP2, |
133 | val: (wol->sopass[3] << 8) | wol->sopass[2]); |
134 | phy_write_mmd(phydev, DP83811_DEVADDR, |
135 | MII_DP83811_RXSOP3, |
136 | val: (wol->sopass[5] << 8) | wol->sopass[4]); |
137 | value |= DP83811_WOL_SECURE_ON; |
138 | } else { |
139 | value &= ~DP83811_WOL_SECURE_ON; |
140 | } |
141 | |
142 | /* Clear any pending WoL interrupt */ |
143 | phy_read(phydev, MII_DP83811_INT_STAT1); |
144 | |
145 | value |= DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL | |
146 | DP83811_WOL_CLR_INDICATION; |
147 | |
148 | return phy_write_mmd(phydev, DP83811_DEVADDR, |
149 | MII_DP83811_WOL_CFG, val: value); |
150 | } else { |
151 | return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, |
152 | MII_DP83811_WOL_CFG, DP83811_WOL_EN); |
153 | } |
154 | |
155 | } |
156 | |
157 | static void dp83811_get_wol(struct phy_device *phydev, |
158 | struct ethtool_wolinfo *wol) |
159 | { |
160 | u16 sopass_val; |
161 | int value; |
162 | |
163 | wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); |
164 | wol->wolopts = 0; |
165 | |
166 | value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG); |
167 | |
168 | if (value & DP83811_WOL_MAGIC_EN) |
169 | wol->wolopts |= WAKE_MAGIC; |
170 | |
171 | if (value & DP83811_WOL_SECURE_ON) { |
172 | sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR, |
173 | MII_DP83811_RXSOP1); |
174 | wol->sopass[0] = (sopass_val & 0xff); |
175 | wol->sopass[1] = (sopass_val >> 8); |
176 | |
177 | sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR, |
178 | MII_DP83811_RXSOP2); |
179 | wol->sopass[2] = (sopass_val & 0xff); |
180 | wol->sopass[3] = (sopass_val >> 8); |
181 | |
182 | sopass_val = phy_read_mmd(phydev, DP83811_DEVADDR, |
183 | MII_DP83811_RXSOP3); |
184 | wol->sopass[4] = (sopass_val & 0xff); |
185 | wol->sopass[5] = (sopass_val >> 8); |
186 | |
187 | wol->wolopts |= WAKE_MAGICSECURE; |
188 | } |
189 | |
190 | /* WoL is not enabled so set wolopts to 0 */ |
191 | if (!(value & DP83811_WOL_EN)) |
192 | wol->wolopts = 0; |
193 | } |
194 | |
195 | static int dp83811_config_intr(struct phy_device *phydev) |
196 | { |
197 | int misr_status, err; |
198 | |
199 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
200 | err = dp83811_ack_interrupt(phydev); |
201 | if (err) |
202 | return err; |
203 | |
204 | misr_status = phy_read(phydev, MII_DP83811_INT_STAT1); |
205 | if (misr_status < 0) |
206 | return misr_status; |
207 | |
208 | misr_status |= (DP83811_RX_ERR_HF_INT_EN | |
209 | DP83811_MS_TRAINING_INT_EN | |
210 | DP83811_ANEG_COMPLETE_INT_EN | |
211 | DP83811_ESD_EVENT_INT_EN | |
212 | DP83811_WOL_INT_EN | |
213 | DP83811_LINK_STAT_INT_EN | |
214 | DP83811_ENERGY_DET_INT_EN | |
215 | DP83811_LINK_QUAL_INT_EN); |
216 | |
217 | err = phy_write(phydev, MII_DP83811_INT_STAT1, val: misr_status); |
218 | if (err < 0) |
219 | return err; |
220 | |
221 | misr_status = phy_read(phydev, MII_DP83811_INT_STAT2); |
222 | if (misr_status < 0) |
223 | return misr_status; |
224 | |
225 | misr_status |= (DP83811_JABBER_DET_INT_EN | |
226 | DP83811_POLARITY_INT_EN | |
227 | DP83811_SLEEP_MODE_INT_EN | |
228 | DP83811_OVERTEMP_INT_EN | |
229 | DP83811_OVERVOLTAGE_INT_EN | |
230 | DP83811_UNDERVOLTAGE_INT_EN); |
231 | |
232 | err = phy_write(phydev, MII_DP83811_INT_STAT2, val: misr_status); |
233 | if (err < 0) |
234 | return err; |
235 | |
236 | misr_status = phy_read(phydev, MII_DP83811_INT_STAT3); |
237 | if (misr_status < 0) |
238 | return misr_status; |
239 | |
240 | misr_status |= (DP83811_LPS_INT_EN | |
241 | DP83811_NO_FRAME_INT_EN | |
242 | DP83811_POR_DONE_INT_EN); |
243 | |
244 | err = phy_write(phydev, MII_DP83811_INT_STAT3, val: misr_status); |
245 | |
246 | } else { |
247 | err = phy_write(phydev, MII_DP83811_INT_STAT1, val: 0); |
248 | if (err < 0) |
249 | return err; |
250 | |
251 | err = phy_write(phydev, MII_DP83811_INT_STAT2, val: 0); |
252 | if (err < 0) |
253 | return err; |
254 | |
255 | err = phy_write(phydev, MII_DP83811_INT_STAT3, val: 0); |
256 | if (err < 0) |
257 | return err; |
258 | |
259 | err = dp83811_ack_interrupt(phydev); |
260 | } |
261 | |
262 | return err; |
263 | } |
264 | |
265 | static irqreturn_t dp83811_handle_interrupt(struct phy_device *phydev) |
266 | { |
267 | bool trigger_machine = false; |
268 | int irq_status; |
269 | |
270 | /* The INT_STAT registers 1, 2 and 3 are holding the interrupt status |
271 | * in the upper half (15:8), while the lower half (7:0) is used for |
272 | * controlling the interrupt enable state of those individual interrupt |
273 | * sources. To determine the possible interrupt sources, just read the |
274 | * INT_STAT* register and use it directly to know which interrupts have |
275 | * been enabled previously or not. |
276 | */ |
277 | irq_status = phy_read(phydev, MII_DP83811_INT_STAT1); |
278 | if (irq_status < 0) { |
279 | phy_error(phydev); |
280 | return IRQ_NONE; |
281 | } |
282 | if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) |
283 | trigger_machine = true; |
284 | |
285 | irq_status = phy_read(phydev, MII_DP83811_INT_STAT2); |
286 | if (irq_status < 0) { |
287 | phy_error(phydev); |
288 | return IRQ_NONE; |
289 | } |
290 | if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) |
291 | trigger_machine = true; |
292 | |
293 | irq_status = phy_read(phydev, MII_DP83811_INT_STAT3); |
294 | if (irq_status < 0) { |
295 | phy_error(phydev); |
296 | return IRQ_NONE; |
297 | } |
298 | if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) |
299 | trigger_machine = true; |
300 | |
301 | if (!trigger_machine) |
302 | return IRQ_NONE; |
303 | |
304 | phy_trigger_machine(phydev); |
305 | |
306 | return IRQ_HANDLED; |
307 | } |
308 | |
309 | static int dp83811_config_aneg(struct phy_device *phydev) |
310 | { |
311 | int value, err; |
312 | |
313 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
314 | value = phy_read(phydev, MII_DP83811_SGMII_CTRL); |
315 | if (phydev->autoneg == AUTONEG_ENABLE) { |
316 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, |
317 | val: (DP83811_SGMII_AUTO_NEG_EN | value)); |
318 | if (err < 0) |
319 | return err; |
320 | } else { |
321 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, |
322 | val: (~DP83811_SGMII_AUTO_NEG_EN & value)); |
323 | if (err < 0) |
324 | return err; |
325 | } |
326 | } |
327 | |
328 | return genphy_config_aneg(phydev); |
329 | } |
330 | |
331 | static int dp83811_config_init(struct phy_device *phydev) |
332 | { |
333 | int value, err; |
334 | |
335 | value = phy_read(phydev, MII_DP83811_SGMII_CTRL); |
336 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
337 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, |
338 | val: (DP83811_SGMII_EN | value)); |
339 | } else { |
340 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, |
341 | val: (~DP83811_SGMII_EN & value)); |
342 | } |
343 | |
344 | if (err < 0) |
345 | |
346 | return err; |
347 | |
348 | value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN; |
349 | |
350 | return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, |
351 | val: value); |
352 | } |
353 | |
354 | static int dp83811_phy_reset(struct phy_device *phydev) |
355 | { |
356 | int err; |
357 | |
358 | err = phy_write(phydev, MII_DP83811_RESET_CTRL, DP83811_HW_RESET); |
359 | if (err < 0) |
360 | return err; |
361 | |
362 | return 0; |
363 | } |
364 | |
365 | static int dp83811_suspend(struct phy_device *phydev) |
366 | { |
367 | int value; |
368 | |
369 | value = phy_read_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG); |
370 | |
371 | if (!(value & DP83811_WOL_EN)) |
372 | genphy_suspend(phydev); |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | static int dp83811_resume(struct phy_device *phydev) |
378 | { |
379 | genphy_resume(phydev); |
380 | |
381 | phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, |
382 | DP83811_WOL_CLR_INDICATION); |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | static struct phy_driver dp83811_driver[] = { |
388 | { |
389 | .phy_id = DP83TC811_PHY_ID, |
390 | .phy_id_mask = 0xfffffff0, |
391 | .name = "TI DP83TC811" , |
392 | /* PHY_BASIC_FEATURES */ |
393 | .config_init = dp83811_config_init, |
394 | .config_aneg = dp83811_config_aneg, |
395 | .soft_reset = dp83811_phy_reset, |
396 | .get_wol = dp83811_get_wol, |
397 | .set_wol = dp83811_set_wol, |
398 | .config_intr = dp83811_config_intr, |
399 | .handle_interrupt = dp83811_handle_interrupt, |
400 | .suspend = dp83811_suspend, |
401 | .resume = dp83811_resume, |
402 | }, |
403 | }; |
404 | module_phy_driver(dp83811_driver); |
405 | |
406 | static struct mdio_device_id __maybe_unused dp83811_tbl[] = { |
407 | { DP83TC811_PHY_ID, 0xfffffff0 }, |
408 | { }, |
409 | }; |
410 | MODULE_DEVICE_TABLE(mdio, dp83811_tbl); |
411 | |
412 | MODULE_DESCRIPTION("Texas Instruments DP83TC811 PHY driver" ); |
413 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com" ); |
414 | MODULE_LICENSE("GPL" ); |
415 | |