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_CTRL_2 0x0a |
27 | #define MII_DP83822_PHYSTS 0x10 |
28 | #define MII_DP83822_PHYSCR 0x11 |
29 | #define MII_DP83822_MISR1 0x12 |
30 | #define MII_DP83822_MISR2 0x13 |
31 | #define MII_DP83822_FCSCR 0x14 |
32 | #define MII_DP83822_RCSR 0x17 |
33 | #define MII_DP83822_RESET_CTRL 0x1f |
34 | #define MII_DP83822_GENCFG 0x465 |
35 | #define MII_DP83822_SOR1 0x467 |
36 | |
37 | /* GENCFG */ |
38 | #define DP83822_SIG_DET_LOW BIT(0) |
39 | |
40 | /* Control Register 2 bits */ |
41 | #define DP83822_FX_ENABLE BIT(14) |
42 | |
43 | #define DP83822_HW_RESET BIT(15) |
44 | #define DP83822_SW_RESET BIT(14) |
45 | |
46 | /* PHY STS bits */ |
47 | #define DP83822_PHYSTS_DUPLEX BIT(2) |
48 | #define DP83822_PHYSTS_10 BIT(1) |
49 | #define DP83822_PHYSTS_LINK BIT(0) |
50 | |
51 | /* PHYSCR Register Fields */ |
52 | #define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ |
53 | #define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ |
54 | |
55 | /* MISR1 bits */ |
56 | #define DP83822_RX_ERR_HF_INT_EN BIT(0) |
57 | #define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1) |
58 | #define DP83822_ANEG_COMPLETE_INT_EN BIT(2) |
59 | #define DP83822_DUP_MODE_CHANGE_INT_EN BIT(3) |
60 | #define DP83822_SPEED_CHANGED_INT_EN BIT(4) |
61 | #define DP83822_LINK_STAT_INT_EN BIT(5) |
62 | #define DP83822_ENERGY_DET_INT_EN BIT(6) |
63 | #define DP83822_LINK_QUAL_INT_EN BIT(7) |
64 | |
65 | /* MISR2 bits */ |
66 | #define DP83822_JABBER_DET_INT_EN BIT(0) |
67 | #define DP83822_WOL_PKT_INT_EN BIT(1) |
68 | #define DP83822_SLEEP_MODE_INT_EN BIT(2) |
69 | #define DP83822_MDI_XOVER_INT_EN BIT(3) |
70 | #define DP83822_LB_FIFO_INT_EN BIT(4) |
71 | #define DP83822_PAGE_RX_INT_EN BIT(5) |
72 | #define DP83822_ANEG_ERR_INT_EN BIT(6) |
73 | #define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7) |
74 | |
75 | /* INT_STAT1 bits */ |
76 | #define DP83822_WOL_INT_EN BIT(4) |
77 | #define DP83822_WOL_INT_STAT BIT(12) |
78 | |
79 | #define MII_DP83822_RXSOP1 0x04a5 |
80 | #define MII_DP83822_RXSOP2 0x04a6 |
81 | #define MII_DP83822_RXSOP3 0x04a7 |
82 | |
83 | /* WoL Registers */ |
84 | #define MII_DP83822_WOL_CFG 0x04a0 |
85 | #define MII_DP83822_WOL_STAT 0x04a1 |
86 | #define MII_DP83822_WOL_DA1 0x04a2 |
87 | #define MII_DP83822_WOL_DA2 0x04a3 |
88 | #define MII_DP83822_WOL_DA3 0x04a4 |
89 | |
90 | /* WoL bits */ |
91 | #define DP83822_WOL_MAGIC_EN BIT(0) |
92 | #define DP83822_WOL_SECURE_ON BIT(5) |
93 | #define DP83822_WOL_EN BIT(7) |
94 | #define DP83822_WOL_INDICATION_SEL BIT(8) |
95 | #define DP83822_WOL_CLR_INDICATION BIT(11) |
96 | |
97 | /* RCSR bits */ |
98 | #define DP83822_RGMII_MODE_EN BIT(9) |
99 | #define DP83822_RX_CLK_SHIFT BIT(12) |
100 | #define DP83822_TX_CLK_SHIFT BIT(11) |
101 | |
102 | /* SOR1 mode */ |
103 | #define DP83822_STRAP_MODE1 0 |
104 | #define DP83822_STRAP_MODE2 BIT(0) |
105 | #define DP83822_STRAP_MODE3 BIT(1) |
106 | #define DP83822_STRAP_MODE4 GENMASK(1, 0) |
107 | |
108 | #define DP83822_COL_STRAP_MASK GENMASK(11, 10) |
109 | #define DP83822_COL_SHIFT 10 |
110 | #define DP83822_RX_ER_STR_MASK GENMASK(9, 8) |
111 | #define DP83822_RX_ER_SHIFT 8 |
112 | |
113 | #define MII_DP83822_FIBER_ADVERTISE (ADVERTISED_TP | ADVERTISED_MII | \ |
114 | ADVERTISED_FIBRE | \ |
115 | ADVERTISED_Pause | ADVERTISED_Asym_Pause) |
116 | |
117 | struct dp83822_private { |
118 | bool fx_signal_det_low; |
119 | int fx_enabled; |
120 | u16 fx_sd_enable; |
121 | }; |
122 | |
123 | static int dp83822_set_wol(struct phy_device *phydev, |
124 | struct ethtool_wolinfo *wol) |
125 | { |
126 | struct net_device *ndev = phydev->attached_dev; |
127 | u16 value; |
128 | const u8 *mac; |
129 | |
130 | if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { |
131 | mac = (const u8 *)ndev->dev_addr; |
132 | |
133 | if (!is_valid_ether_addr(addr: mac)) |
134 | return -EINVAL; |
135 | |
136 | /* MAC addresses start with byte 5, but stored in mac[0]. |
137 | * 822 PHYs store bytes 4|5, 2|3, 0|1 |
138 | */ |
139 | phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1, |
140 | val: (mac[1] << 8) | mac[0]); |
141 | phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2, |
142 | val: (mac[3] << 8) | mac[2]); |
143 | phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, |
144 | val: (mac[5] << 8) | mac[4]); |
145 | |
146 | value = phy_read_mmd(phydev, DP83822_DEVADDR, |
147 | MII_DP83822_WOL_CFG); |
148 | if (wol->wolopts & WAKE_MAGIC) |
149 | value |= DP83822_WOL_MAGIC_EN; |
150 | else |
151 | value &= ~DP83822_WOL_MAGIC_EN; |
152 | |
153 | if (wol->wolopts & WAKE_MAGICSECURE) { |
154 | phy_write_mmd(phydev, DP83822_DEVADDR, |
155 | MII_DP83822_RXSOP1, |
156 | val: (wol->sopass[1] << 8) | wol->sopass[0]); |
157 | phy_write_mmd(phydev, DP83822_DEVADDR, |
158 | MII_DP83822_RXSOP2, |
159 | val: (wol->sopass[3] << 8) | wol->sopass[2]); |
160 | phy_write_mmd(phydev, DP83822_DEVADDR, |
161 | MII_DP83822_RXSOP3, |
162 | val: (wol->sopass[5] << 8) | wol->sopass[4]); |
163 | value |= DP83822_WOL_SECURE_ON; |
164 | } else { |
165 | value &= ~DP83822_WOL_SECURE_ON; |
166 | } |
167 | |
168 | /* Clear any pending WoL interrupt */ |
169 | phy_read(phydev, MII_DP83822_MISR2); |
170 | |
171 | value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL | |
172 | DP83822_WOL_CLR_INDICATION; |
173 | |
174 | return phy_write_mmd(phydev, DP83822_DEVADDR, |
175 | MII_DP83822_WOL_CFG, val: value); |
176 | } else { |
177 | return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, |
178 | MII_DP83822_WOL_CFG, DP83822_WOL_EN); |
179 | } |
180 | } |
181 | |
182 | static void dp83822_get_wol(struct phy_device *phydev, |
183 | struct ethtool_wolinfo *wol) |
184 | { |
185 | int value; |
186 | u16 sopass_val; |
187 | |
188 | wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); |
189 | wol->wolopts = 0; |
190 | |
191 | value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); |
192 | |
193 | if (value & DP83822_WOL_MAGIC_EN) |
194 | wol->wolopts |= WAKE_MAGIC; |
195 | |
196 | if (value & DP83822_WOL_SECURE_ON) { |
197 | sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, |
198 | MII_DP83822_RXSOP1); |
199 | wol->sopass[0] = (sopass_val & 0xff); |
200 | wol->sopass[1] = (sopass_val >> 8); |
201 | |
202 | sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, |
203 | MII_DP83822_RXSOP2); |
204 | wol->sopass[2] = (sopass_val & 0xff); |
205 | wol->sopass[3] = (sopass_val >> 8); |
206 | |
207 | sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, |
208 | MII_DP83822_RXSOP3); |
209 | wol->sopass[4] = (sopass_val & 0xff); |
210 | wol->sopass[5] = (sopass_val >> 8); |
211 | |
212 | wol->wolopts |= WAKE_MAGICSECURE; |
213 | } |
214 | |
215 | /* WoL is not enabled so set wolopts to 0 */ |
216 | if (!(value & DP83822_WOL_EN)) |
217 | wol->wolopts = 0; |
218 | } |
219 | |
220 | static int dp83822_config_intr(struct phy_device *phydev) |
221 | { |
222 | struct dp83822_private *dp83822 = phydev->priv; |
223 | int misr_status; |
224 | int physcr_status; |
225 | int err; |
226 | |
227 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
228 | misr_status = phy_read(phydev, MII_DP83822_MISR1); |
229 | if (misr_status < 0) |
230 | return misr_status; |
231 | |
232 | misr_status |= (DP83822_LINK_STAT_INT_EN | |
233 | DP83822_ENERGY_DET_INT_EN | |
234 | DP83822_LINK_QUAL_INT_EN); |
235 | |
236 | /* Private data pointer is NULL on DP83825/26 */ |
237 | if (!dp83822 || !dp83822->fx_enabled) |
238 | misr_status |= DP83822_ANEG_COMPLETE_INT_EN | |
239 | DP83822_DUP_MODE_CHANGE_INT_EN | |
240 | DP83822_SPEED_CHANGED_INT_EN; |
241 | |
242 | |
243 | err = phy_write(phydev, MII_DP83822_MISR1, val: misr_status); |
244 | if (err < 0) |
245 | return err; |
246 | |
247 | misr_status = phy_read(phydev, MII_DP83822_MISR2); |
248 | if (misr_status < 0) |
249 | return misr_status; |
250 | |
251 | misr_status |= (DP83822_JABBER_DET_INT_EN | |
252 | DP83822_SLEEP_MODE_INT_EN | |
253 | DP83822_LB_FIFO_INT_EN | |
254 | DP83822_PAGE_RX_INT_EN | |
255 | DP83822_EEE_ERROR_CHANGE_INT_EN); |
256 | |
257 | /* Private data pointer is NULL on DP83825/26 */ |
258 | if (!dp83822 || !dp83822->fx_enabled) |
259 | misr_status |= DP83822_ANEG_ERR_INT_EN | |
260 | DP83822_WOL_PKT_INT_EN; |
261 | |
262 | err = phy_write(phydev, MII_DP83822_MISR2, val: misr_status); |
263 | if (err < 0) |
264 | return err; |
265 | |
266 | physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); |
267 | if (physcr_status < 0) |
268 | return physcr_status; |
269 | |
270 | physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN; |
271 | |
272 | } else { |
273 | err = phy_write(phydev, MII_DP83822_MISR1, val: 0); |
274 | if (err < 0) |
275 | return err; |
276 | |
277 | err = phy_write(phydev, MII_DP83822_MISR2, val: 0); |
278 | if (err < 0) |
279 | return err; |
280 | |
281 | physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); |
282 | if (physcr_status < 0) |
283 | return physcr_status; |
284 | |
285 | physcr_status &= ~DP83822_PHYSCR_INTEN; |
286 | } |
287 | |
288 | return phy_write(phydev, MII_DP83822_PHYSCR, val: physcr_status); |
289 | } |
290 | |
291 | static irqreturn_t dp83822_handle_interrupt(struct phy_device *phydev) |
292 | { |
293 | bool trigger_machine = false; |
294 | int irq_status; |
295 | |
296 | /* The MISR1 and MISR2 registers are holding the interrupt status in |
297 | * the upper half (15:8), while the lower half (7:0) is used for |
298 | * controlling the interrupt enable state of those individual interrupt |
299 | * sources. To determine the possible interrupt sources, just read the |
300 | * MISR* register and use it directly to know which interrupts have |
301 | * been enabled previously or not. |
302 | */ |
303 | irq_status = phy_read(phydev, MII_DP83822_MISR1); |
304 | if (irq_status < 0) { |
305 | phy_error(phydev); |
306 | return IRQ_NONE; |
307 | } |
308 | if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) |
309 | trigger_machine = true; |
310 | |
311 | irq_status = phy_read(phydev, MII_DP83822_MISR2); |
312 | if (irq_status < 0) { |
313 | phy_error(phydev); |
314 | return IRQ_NONE; |
315 | } |
316 | if (irq_status & ((irq_status & GENMASK(7, 0)) << 8)) |
317 | trigger_machine = true; |
318 | |
319 | if (!trigger_machine) |
320 | return IRQ_NONE; |
321 | |
322 | phy_trigger_machine(phydev); |
323 | |
324 | return IRQ_HANDLED; |
325 | } |
326 | |
327 | static int dp8382x_disable_wol(struct phy_device *phydev) |
328 | { |
329 | return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, |
330 | DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | |
331 | DP83822_WOL_SECURE_ON); |
332 | } |
333 | |
334 | static int dp83822_read_status(struct phy_device *phydev) |
335 | { |
336 | struct dp83822_private *dp83822 = phydev->priv; |
337 | int status = phy_read(phydev, MII_DP83822_PHYSTS); |
338 | int ctrl2; |
339 | int ret; |
340 | |
341 | if (dp83822->fx_enabled) { |
342 | if (status & DP83822_PHYSTS_LINK) { |
343 | phydev->speed = SPEED_UNKNOWN; |
344 | phydev->duplex = DUPLEX_UNKNOWN; |
345 | } else { |
346 | ctrl2 = phy_read(phydev, MII_DP83822_CTRL_2); |
347 | if (ctrl2 < 0) |
348 | return ctrl2; |
349 | |
350 | if (!(ctrl2 & DP83822_FX_ENABLE)) { |
351 | ret = phy_write(phydev, MII_DP83822_CTRL_2, |
352 | DP83822_FX_ENABLE | ctrl2); |
353 | if (ret < 0) |
354 | return ret; |
355 | } |
356 | } |
357 | } |
358 | |
359 | ret = genphy_read_status(phydev); |
360 | if (ret) |
361 | return ret; |
362 | |
363 | if (status < 0) |
364 | return status; |
365 | |
366 | if (status & DP83822_PHYSTS_DUPLEX) |
367 | phydev->duplex = DUPLEX_FULL; |
368 | else |
369 | phydev->duplex = DUPLEX_HALF; |
370 | |
371 | if (status & DP83822_PHYSTS_10) |
372 | phydev->speed = SPEED_10; |
373 | else |
374 | phydev->speed = SPEED_100; |
375 | |
376 | return 0; |
377 | } |
378 | |
379 | static int dp83822_config_init(struct phy_device *phydev) |
380 | { |
381 | struct dp83822_private *dp83822 = phydev->priv; |
382 | struct device *dev = &phydev->mdio.dev; |
383 | int rgmii_delay; |
384 | s32 rx_int_delay; |
385 | s32 tx_int_delay; |
386 | int err = 0; |
387 | int bmcr; |
388 | |
389 | if (phy_interface_is_rgmii(phydev)) { |
390 | rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, size: 0, |
391 | is_rx: true); |
392 | |
393 | if (rx_int_delay <= 0) |
394 | rgmii_delay = 0; |
395 | else |
396 | rgmii_delay = DP83822_RX_CLK_SHIFT; |
397 | |
398 | tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, size: 0, |
399 | is_rx: false); |
400 | if (tx_int_delay <= 0) |
401 | rgmii_delay &= ~DP83822_TX_CLK_SHIFT; |
402 | else |
403 | rgmii_delay |= DP83822_TX_CLK_SHIFT; |
404 | |
405 | if (rgmii_delay) { |
406 | err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, |
407 | MII_DP83822_RCSR, val: rgmii_delay); |
408 | if (err) |
409 | return err; |
410 | } |
411 | |
412 | phy_set_bits_mmd(phydev, DP83822_DEVADDR, |
413 | MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); |
414 | } else { |
415 | phy_clear_bits_mmd(phydev, DP83822_DEVADDR, |
416 | MII_DP83822_RCSR, DP83822_RGMII_MODE_EN); |
417 | } |
418 | |
419 | if (dp83822->fx_enabled) { |
420 | err = phy_modify(phydev, MII_DP83822_CTRL_2, |
421 | DP83822_FX_ENABLE, set: 1); |
422 | if (err < 0) |
423 | return err; |
424 | |
425 | /* Only allow advertising what this PHY supports */ |
426 | linkmode_and(dst: phydev->advertising, a: phydev->advertising, |
427 | b: phydev->supported); |
428 | |
429 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_FIBRE_BIT, |
430 | addr: phydev->supported); |
431 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_FIBRE_BIT, |
432 | addr: phydev->advertising); |
433 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseFX_Full_BIT, |
434 | addr: phydev->supported); |
435 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseFX_Half_BIT, |
436 | addr: phydev->supported); |
437 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseFX_Full_BIT, |
438 | addr: phydev->advertising); |
439 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseFX_Half_BIT, |
440 | addr: phydev->advertising); |
441 | |
442 | /* Auto neg is not supported in fiber mode */ |
443 | bmcr = phy_read(phydev, MII_BMCR); |
444 | if (bmcr < 0) |
445 | return bmcr; |
446 | |
447 | if (bmcr & BMCR_ANENABLE) { |
448 | err = phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, set: 0); |
449 | if (err < 0) |
450 | return err; |
451 | } |
452 | phydev->autoneg = AUTONEG_DISABLE; |
453 | linkmode_clear_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, |
454 | addr: phydev->supported); |
455 | linkmode_clear_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, |
456 | addr: phydev->advertising); |
457 | |
458 | /* Setup fiber advertisement */ |
459 | err = phy_modify_changed(phydev, MII_ADVERTISE, |
460 | MII_DP83822_FIBER_ADVERTISE, |
461 | MII_DP83822_FIBER_ADVERTISE); |
462 | |
463 | if (err < 0) |
464 | return err; |
465 | |
466 | if (dp83822->fx_signal_det_low) { |
467 | err = phy_set_bits_mmd(phydev, DP83822_DEVADDR, |
468 | MII_DP83822_GENCFG, |
469 | DP83822_SIG_DET_LOW); |
470 | if (err) |
471 | return err; |
472 | } |
473 | } |
474 | return dp8382x_disable_wol(phydev); |
475 | } |
476 | |
477 | static int dp8382x_config_init(struct phy_device *phydev) |
478 | { |
479 | return dp8382x_disable_wol(phydev); |
480 | } |
481 | |
482 | static int dp83822_phy_reset(struct phy_device *phydev) |
483 | { |
484 | int err; |
485 | |
486 | err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_SW_RESET); |
487 | if (err < 0) |
488 | return err; |
489 | |
490 | return phydev->drv->config_init(phydev); |
491 | } |
492 | |
493 | #ifdef CONFIG_OF_MDIO |
494 | static int dp83822_of_init(struct phy_device *phydev) |
495 | { |
496 | struct dp83822_private *dp83822 = phydev->priv; |
497 | struct device *dev = &phydev->mdio.dev; |
498 | |
499 | /* Signal detection for the PHY is only enabled if the FX_EN and the |
500 | * SD_EN pins are strapped. Signal detection can only enabled if FX_EN |
501 | * is strapped otherwise signal detection is disabled for the PHY. |
502 | */ |
503 | if (dp83822->fx_enabled && dp83822->fx_sd_enable) |
504 | dp83822->fx_signal_det_low = device_property_present(dev, |
505 | propname: "ti,link-loss-low" ); |
506 | if (!dp83822->fx_enabled) |
507 | dp83822->fx_enabled = device_property_present(dev, |
508 | propname: "ti,fiber-mode" ); |
509 | |
510 | return 0; |
511 | } |
512 | #else |
513 | static int dp83822_of_init(struct phy_device *phydev) |
514 | { |
515 | return 0; |
516 | } |
517 | #endif /* CONFIG_OF_MDIO */ |
518 | |
519 | static int dp83822_read_straps(struct phy_device *phydev) |
520 | { |
521 | struct dp83822_private *dp83822 = phydev->priv; |
522 | int fx_enabled, fx_sd_enable; |
523 | int val; |
524 | |
525 | val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1); |
526 | if (val < 0) |
527 | return val; |
528 | |
529 | phydev_dbg(phydev, "SOR1 strap register: 0x%04x\n" , val); |
530 | |
531 | fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT; |
532 | if (fx_enabled == DP83822_STRAP_MODE2 || |
533 | fx_enabled == DP83822_STRAP_MODE3) |
534 | dp83822->fx_enabled = 1; |
535 | |
536 | if (dp83822->fx_enabled) { |
537 | fx_sd_enable = (val & DP83822_RX_ER_STR_MASK) >> DP83822_RX_ER_SHIFT; |
538 | if (fx_sd_enable == DP83822_STRAP_MODE3 || |
539 | fx_sd_enable == DP83822_STRAP_MODE4) |
540 | dp83822->fx_sd_enable = 1; |
541 | } |
542 | |
543 | return 0; |
544 | } |
545 | |
546 | static int dp83822_probe(struct phy_device *phydev) |
547 | { |
548 | struct dp83822_private *dp83822; |
549 | int ret; |
550 | |
551 | dp83822 = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*dp83822), |
552 | GFP_KERNEL); |
553 | if (!dp83822) |
554 | return -ENOMEM; |
555 | |
556 | phydev->priv = dp83822; |
557 | |
558 | ret = dp83822_read_straps(phydev); |
559 | if (ret) |
560 | return ret; |
561 | |
562 | dp83822_of_init(phydev); |
563 | |
564 | if (dp83822->fx_enabled) |
565 | phydev->port = PORT_FIBRE; |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static int dp83822_suspend(struct phy_device *phydev) |
571 | { |
572 | int value; |
573 | |
574 | value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); |
575 | |
576 | if (!(value & DP83822_WOL_EN)) |
577 | genphy_suspend(phydev); |
578 | |
579 | return 0; |
580 | } |
581 | |
582 | static int dp83822_resume(struct phy_device *phydev) |
583 | { |
584 | int value; |
585 | |
586 | genphy_resume(phydev); |
587 | |
588 | value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); |
589 | |
590 | phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, val: value | |
591 | DP83822_WOL_CLR_INDICATION); |
592 | |
593 | return 0; |
594 | } |
595 | |
596 | #define DP83822_PHY_DRIVER(_id, _name) \ |
597 | { \ |
598 | PHY_ID_MATCH_MODEL(_id), \ |
599 | .name = (_name), \ |
600 | /* PHY_BASIC_FEATURES */ \ |
601 | .probe = dp83822_probe, \ |
602 | .soft_reset = dp83822_phy_reset, \ |
603 | .config_init = dp83822_config_init, \ |
604 | .read_status = dp83822_read_status, \ |
605 | .get_wol = dp83822_get_wol, \ |
606 | .set_wol = dp83822_set_wol, \ |
607 | .config_intr = dp83822_config_intr, \ |
608 | .handle_interrupt = dp83822_handle_interrupt, \ |
609 | .suspend = dp83822_suspend, \ |
610 | .resume = dp83822_resume, \ |
611 | } |
612 | |
613 | #define DP8382X_PHY_DRIVER(_id, _name) \ |
614 | { \ |
615 | PHY_ID_MATCH_MODEL(_id), \ |
616 | .name = (_name), \ |
617 | /* PHY_BASIC_FEATURES */ \ |
618 | .soft_reset = dp83822_phy_reset, \ |
619 | .config_init = dp8382x_config_init, \ |
620 | .get_wol = dp83822_get_wol, \ |
621 | .set_wol = dp83822_set_wol, \ |
622 | .config_intr = dp83822_config_intr, \ |
623 | .handle_interrupt = dp83822_handle_interrupt, \ |
624 | .suspend = dp83822_suspend, \ |
625 | .resume = dp83822_resume, \ |
626 | } |
627 | |
628 | static struct phy_driver dp83822_driver[] = { |
629 | DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822" ), |
630 | DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I" ), |
631 | DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C" ), |
632 | DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC" ), |
633 | DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S" ), |
634 | DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M" ), |
635 | DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS" ), |
636 | }; |
637 | module_phy_driver(dp83822_driver); |
638 | |
639 | static struct mdio_device_id __maybe_unused dp83822_tbl[] = { |
640 | { DP83822_PHY_ID, 0xfffffff0 }, |
641 | { DP83825I_PHY_ID, 0xfffffff0 }, |
642 | { DP83826C_PHY_ID, 0xfffffff0 }, |
643 | { DP83826NC_PHY_ID, 0xfffffff0 }, |
644 | { DP83825S_PHY_ID, 0xfffffff0 }, |
645 | { DP83825CM_PHY_ID, 0xfffffff0 }, |
646 | { DP83825CS_PHY_ID, 0xfffffff0 }, |
647 | { }, |
648 | }; |
649 | MODULE_DEVICE_TABLE(mdio, dp83822_tbl); |
650 | |
651 | MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver" ); |
652 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com" ); |
653 | MODULE_LICENSE("GPL v2" ); |
654 | |