Commit | Line | Data |
---|---|---|
b753a9fa DM |
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 | |
42036383 | 24 | #define MII_DP83811_INT_STAT3 0x18 |
b753a9fa DM |
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 | ||
42036383 DM |
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 | ||
b753a9fa DM |
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 | ||
42036383 DM |
90 | err = phy_read(phydev, MII_DP83811_INT_STAT3); |
91 | if (err < 0) | |
92 | return err; | |
93 | ||
b753a9fa DM |
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(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 | (mac[1] << 8) | mac[0]); | |
115 | phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA2, | |
116 | (mac[3] << 8) | mac[2]); | |
117 | phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_DA3, | |
118 | (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 | (wol->sopass[1] << 8) | wol->sopass[0]); | |
131 | phy_write_mmd(phydev, DP83811_DEVADDR, | |
132 | MII_DP83811_RXSOP2, | |
133 | (wol->sopass[3] << 8) | wol->sopass[2]); | |
134 | phy_write_mmd(phydev, DP83811_DEVADDR, | |
135 | MII_DP83811_RXSOP3, | |
136 | (wol->sopass[5] << 8) | wol->sopass[4]); | |
137 | value |= DP83811_WOL_SECURE_ON; | |
138 | } else { | |
139 | value &= ~DP83811_WOL_SECURE_ON; | |
140 | } | |
141 | ||
6c599044 DM |
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, value); | |
b753a9fa | 150 | } else { |
6c599044 DM |
151 | return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, |
152 | MII_DP83811_WOL_CFG, DP83811_WOL_EN); | |
b753a9fa DM |
153 | } |
154 | ||
b753a9fa DM |
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) { | |
aa2d603a IC |
200 | err = dp83811_ack_interrupt(phydev); |
201 | if (err) | |
202 | return err; | |
203 | ||
b753a9fa DM |
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, 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, misr_status); | |
42036383 DM |
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, misr_status); | |
b753a9fa DM |
245 | |
246 | } else { | |
247 | err = phy_write(phydev, MII_DP83811_INT_STAT1, 0); | |
248 | if (err < 0) | |
249 | return err; | |
250 | ||
713b4a33 | 251 | err = phy_write(phydev, MII_DP83811_INT_STAT2, 0); |
42036383 DM |
252 | if (err < 0) |
253 | return err; | |
254 | ||
255 | err = phy_write(phydev, MII_DP83811_INT_STAT3, 0); | |
aa2d603a IC |
256 | if (err < 0) |
257 | return err; | |
258 | ||
259 | err = dp83811_ack_interrupt(phydev); | |
b753a9fa DM |
260 | } |
261 | ||
262 | return err; | |
263 | } | |
264 | ||
1d1ae3c6 IC |
265 | static irqreturn_t dp83811_handle_interrupt(struct phy_device *phydev) |
266 | { | |
73f476aa | 267 | bool trigger_machine = false; |
1d1ae3c6 IC |
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)) | |
73f476aa | 283 | trigger_machine = true; |
1d1ae3c6 IC |
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)) | |
73f476aa | 291 | trigger_machine = true; |
1d1ae3c6 IC |
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)) | |
73f476aa | 299 | trigger_machine = true; |
1d1ae3c6 | 300 | |
73f476aa IC |
301 | if (!trigger_machine) |
302 | return IRQ_NONE; | |
1d1ae3c6 | 303 | |
1d1ae3c6 IC |
304 | phy_trigger_machine(phydev); |
305 | ||
306 | return IRQ_HANDLED; | |
307 | } | |
308 | ||
b753a9fa DM |
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 | (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 | (~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 | ||
00f55366 | 335 | value = phy_read(phydev, MII_DP83811_SGMII_CTRL); |
b753a9fa | 336 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
00f55366 | 337 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, |
b753a9fa | 338 | (DP83811_SGMII_EN | value)); |
00f55366 DM |
339 | } else { |
340 | err = phy_write(phydev, MII_DP83811_SGMII_CTRL, | |
341 | (~DP83811_SGMII_EN & value)); | |
b753a9fa DM |
342 | } |
343 | ||
00f55366 DM |
344 | if (err < 0) |
345 | ||
346 | return err; | |
347 | ||
b753a9fa DM |
348 | value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN; |
349 | ||
6c599044 DM |
350 | return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, |
351 | value); | |
b753a9fa DM |
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 | { | |
b753a9fa DM |
379 | genphy_resume(phydev); |
380 | ||
b52c018d HK |
381 | phy_set_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG, |
382 | DP83811_WOL_CLR_INDICATION); | |
b753a9fa DM |
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", | |
dcdecdcf | 392 | /* PHY_BASIC_FEATURES */ |
b753a9fa DM |
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, | |
b753a9fa | 398 | .config_intr = dp83811_config_intr, |
1d1ae3c6 | 399 | .handle_interrupt = dp83811_handle_interrupt, |
b753a9fa DM |
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"); |