1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * drivers/net/phy/smsc.c |
4 | * |
5 | * Driver for SMSC PHYs |
6 | * |
7 | * Author: Herbert Valerio Riedel |
8 | * |
9 | * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org> |
10 | * |
11 | * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net |
12 | * |
13 | */ |
14 | |
15 | #include <linux/clk.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/mii.h> |
19 | #include <linux/ethtool.h> |
20 | #include <linux/of.h> |
21 | #include <linux/phy.h> |
22 | #include <linux/netdevice.h> |
23 | #include <linux/crc16.h> |
24 | #include <linux/etherdevice.h> |
25 | #include <linux/smscphy.h> |
26 | |
27 | /* Vendor-specific PHY Definitions */ |
28 | /* EDPD NLP / crossover time configuration */ |
29 | #define PHY_EDPD_CONFIG 16 |
30 | #define PHY_EDPD_CONFIG_EXT_CROSSOVER_ 0x0001 |
31 | |
32 | /* Control/Status Indication Register */ |
33 | #define SPECIAL_CTRL_STS 27 |
34 | #define SPECIAL_CTRL_STS_OVRRD_AMDIX_ 0x8000 |
35 | #define SPECIAL_CTRL_STS_AMDIX_ENABLE_ 0x4000 |
36 | #define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000 |
37 | |
38 | #define EDPD_MAX_WAIT_DFLT_MS 640 |
39 | /* interval between phylib state machine runs in ms */ |
40 | #define PHY_STATE_MACH_MS 1000 |
41 | |
42 | struct smsc_hw_stat { |
43 | const char *string; |
44 | u8 reg; |
45 | u8 bits; |
46 | }; |
47 | |
48 | static struct smsc_hw_stat smsc_hw_stats[] = { |
49 | { "phy_symbol_errors" , 26, 16}, |
50 | }; |
51 | |
52 | struct smsc_phy_priv { |
53 | unsigned int edpd_enable:1; |
54 | unsigned int edpd_mode_set_by_user:1; |
55 | unsigned int edpd_max_wait_ms; |
56 | bool wol_arp; |
57 | }; |
58 | |
59 | static int smsc_phy_ack_interrupt(struct phy_device *phydev) |
60 | { |
61 | int rc = phy_read(phydev, MII_LAN83C185_ISF); |
62 | |
63 | return rc < 0 ? rc : 0; |
64 | } |
65 | |
66 | int smsc_phy_config_intr(struct phy_device *phydev) |
67 | { |
68 | int rc; |
69 | |
70 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
71 | rc = smsc_phy_ack_interrupt(phydev); |
72 | if (rc) |
73 | return rc; |
74 | |
75 | rc = phy_write(phydev, MII_LAN83C185_IM, |
76 | MII_LAN83C185_ISF_INT_PHYLIB_EVENTS); |
77 | } else { |
78 | rc = phy_write(phydev, MII_LAN83C185_IM, val: 0); |
79 | if (rc) |
80 | return rc; |
81 | |
82 | rc = smsc_phy_ack_interrupt(phydev); |
83 | } |
84 | |
85 | return rc < 0 ? rc : 0; |
86 | } |
87 | EXPORT_SYMBOL_GPL(smsc_phy_config_intr); |
88 | |
89 | static int smsc_phy_config_edpd(struct phy_device *phydev) |
90 | { |
91 | struct smsc_phy_priv *priv = phydev->priv; |
92 | |
93 | if (priv->edpd_enable) |
94 | return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS, |
95 | MII_LAN83C185_EDPWRDOWN); |
96 | else |
97 | return phy_clear_bits(phydev, MII_LAN83C185_CTRL_STATUS, |
98 | MII_LAN83C185_EDPWRDOWN); |
99 | } |
100 | |
101 | irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) |
102 | { |
103 | int irq_status; |
104 | |
105 | irq_status = phy_read(phydev, MII_LAN83C185_ISF); |
106 | if (irq_status < 0) { |
107 | if (irq_status != -ENODEV) |
108 | phy_error(phydev); |
109 | |
110 | return IRQ_NONE; |
111 | } |
112 | |
113 | if (!(irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS)) |
114 | return IRQ_NONE; |
115 | |
116 | phy_trigger_machine(phydev); |
117 | |
118 | return IRQ_HANDLED; |
119 | } |
120 | EXPORT_SYMBOL_GPL(smsc_phy_handle_interrupt); |
121 | |
122 | int smsc_phy_config_init(struct phy_device *phydev) |
123 | { |
124 | struct smsc_phy_priv *priv = phydev->priv; |
125 | |
126 | if (!priv) |
127 | return 0; |
128 | |
129 | /* don't use EDPD in irq mode except overridden by user */ |
130 | if (!priv->edpd_mode_set_by_user && phydev->irq != PHY_POLL) |
131 | priv->edpd_enable = false; |
132 | |
133 | return smsc_phy_config_edpd(phydev); |
134 | } |
135 | EXPORT_SYMBOL_GPL(smsc_phy_config_init); |
136 | |
137 | static int smsc_phy_reset(struct phy_device *phydev) |
138 | { |
139 | int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES); |
140 | if (rc < 0) |
141 | return rc; |
142 | |
143 | /* If the SMSC PHY is in power down mode, then set it |
144 | * in all capable mode before using it. |
145 | */ |
146 | if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { |
147 | /* set "all capable" mode */ |
148 | rc |= MII_LAN83C185_MODE_ALL; |
149 | phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, val: rc); |
150 | } |
151 | |
152 | /* reset the phy */ |
153 | return genphy_soft_reset(phydev); |
154 | } |
155 | |
156 | static int lan87xx_config_aneg(struct phy_device *phydev) |
157 | { |
158 | int rc; |
159 | int val; |
160 | |
161 | switch (phydev->mdix_ctrl) { |
162 | case ETH_TP_MDI: |
163 | val = SPECIAL_CTRL_STS_OVRRD_AMDIX_; |
164 | break; |
165 | case ETH_TP_MDI_X: |
166 | val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ | |
167 | SPECIAL_CTRL_STS_AMDIX_STATE_; |
168 | break; |
169 | case ETH_TP_MDI_AUTO: |
170 | val = SPECIAL_CTRL_STS_AMDIX_ENABLE_; |
171 | break; |
172 | default: |
173 | return genphy_config_aneg(phydev); |
174 | } |
175 | |
176 | rc = phy_read(phydev, SPECIAL_CTRL_STS); |
177 | if (rc < 0) |
178 | return rc; |
179 | |
180 | rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ | |
181 | SPECIAL_CTRL_STS_AMDIX_ENABLE_ | |
182 | SPECIAL_CTRL_STS_AMDIX_STATE_); |
183 | rc |= val; |
184 | phy_write(phydev, SPECIAL_CTRL_STS, val: rc); |
185 | |
186 | phydev->mdix = phydev->mdix_ctrl; |
187 | return genphy_config_aneg(phydev); |
188 | } |
189 | |
190 | static int lan95xx_config_aneg_ext(struct phy_device *phydev) |
191 | { |
192 | if (phydev->phy_id == 0x0007c0f0) { /* LAN9500A or LAN9505A */ |
193 | /* Extend Manual AutoMDIX timer */ |
194 | int rc = phy_set_bits(phydev, PHY_EDPD_CONFIG, |
195 | PHY_EDPD_CONFIG_EXT_CROSSOVER_); |
196 | |
197 | if (rc < 0) |
198 | return rc; |
199 | } |
200 | |
201 | return lan87xx_config_aneg(phydev); |
202 | } |
203 | |
204 | /* |
205 | * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable |
206 | * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to |
207 | * unstable detection of plugging in Ethernet cable. |
208 | * This workaround disables Energy Detect Power-Down mode and waiting for |
209 | * response on link pulses to detect presence of plugged Ethernet cable. |
210 | * The Energy Detect Power-Down mode is enabled again in the end of procedure to |
211 | * save approximately 220 mW of power if cable is unplugged. |
212 | * The workaround is only applicable to poll mode. Energy Detect Power-Down may |
213 | * not be used in interrupt mode lest link change detection becomes unreliable. |
214 | */ |
215 | int lan87xx_read_status(struct phy_device *phydev) |
216 | { |
217 | struct smsc_phy_priv *priv = phydev->priv; |
218 | int err; |
219 | |
220 | err = genphy_read_status(phydev); |
221 | if (err) |
222 | return err; |
223 | |
224 | if (!phydev->link && priv && priv->edpd_enable && |
225 | priv->edpd_max_wait_ms) { |
226 | unsigned int max_wait = priv->edpd_max_wait_ms * 1000; |
227 | int rc; |
228 | |
229 | /* Disable EDPD to wake up PHY */ |
230 | rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); |
231 | if (rc < 0) |
232 | return rc; |
233 | |
234 | rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, |
235 | val: rc & ~MII_LAN83C185_EDPWRDOWN); |
236 | if (rc < 0) |
237 | return rc; |
238 | |
239 | /* Wait max 640 ms to detect energy and the timeout is not |
240 | * an actual error. |
241 | */ |
242 | read_poll_timeout(phy_read, rc, |
243 | rc & MII_LAN83C185_ENERGYON || rc < 0, |
244 | 10000, max_wait, true, phydev, |
245 | MII_LAN83C185_CTRL_STATUS); |
246 | if (rc < 0) |
247 | return rc; |
248 | |
249 | /* Re-enable EDPD */ |
250 | rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); |
251 | if (rc < 0) |
252 | return rc; |
253 | |
254 | rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, |
255 | val: rc | MII_LAN83C185_EDPWRDOWN); |
256 | if (rc < 0) |
257 | return rc; |
258 | } |
259 | |
260 | return err; |
261 | } |
262 | EXPORT_SYMBOL_GPL(lan87xx_read_status); |
263 | |
264 | static int lan874x_phy_config_init(struct phy_device *phydev) |
265 | { |
266 | u16 val; |
267 | int rc; |
268 | |
269 | /* Setup LED2/nINT/nPME pin to function as nPME. May need user option |
270 | * to use LED1/nINT/nPME. |
271 | */ |
272 | val = MII_LAN874X_PHY_PME2_SET; |
273 | |
274 | /* The bits MII_LAN874X_PHY_WOL_PFDA_FR, MII_LAN874X_PHY_WOL_WUFR, |
275 | * MII_LAN874X_PHY_WOL_MPR, and MII_LAN874X_PHY_WOL_BCAST_FR need to |
276 | * be cleared to de-assert PME signal after a WoL event happens, but |
277 | * using PME auto clear gets around that. |
278 | */ |
279 | val |= MII_LAN874X_PHY_PME_SELF_CLEAR; |
280 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR, |
281 | val); |
282 | if (rc < 0) |
283 | return rc; |
284 | |
285 | /* set nPME self clear delay time */ |
286 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_MCFGR, |
287 | MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY); |
288 | if (rc < 0) |
289 | return rc; |
290 | |
291 | return smsc_phy_config_init(phydev); |
292 | } |
293 | |
294 | static void lan874x_get_wol(struct phy_device *phydev, |
295 | struct ethtool_wolinfo *wol) |
296 | { |
297 | struct smsc_phy_priv *priv = phydev->priv; |
298 | int rc; |
299 | |
300 | wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC | |
301 | WAKE_ARP | WAKE_MCAST); |
302 | wol->wolopts = 0; |
303 | |
304 | rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR); |
305 | if (rc < 0) |
306 | return; |
307 | |
308 | if (rc & MII_LAN874X_PHY_WOL_PFDAEN) |
309 | wol->wolopts |= WAKE_UCAST; |
310 | |
311 | if (rc & MII_LAN874X_PHY_WOL_BCSTEN) |
312 | wol->wolopts |= WAKE_BCAST; |
313 | |
314 | if (rc & MII_LAN874X_PHY_WOL_MPEN) |
315 | wol->wolopts |= WAKE_MAGIC; |
316 | |
317 | if (rc & MII_LAN874X_PHY_WOL_WUEN) { |
318 | if (priv->wol_arp) |
319 | wol->wolopts |= WAKE_ARP; |
320 | else |
321 | wol->wolopts |= WAKE_MCAST; |
322 | } |
323 | } |
324 | |
325 | static u16 smsc_crc16(const u8 *buffer, size_t len) |
326 | { |
327 | return bitrev16(crc16(0xFFFF, buffer, len)); |
328 | } |
329 | |
330 | static int lan874x_chk_wol_pattern(const u8 pattern[], const u16 *mask, |
331 | u8 len, u8 *data, u8 *datalen) |
332 | { |
333 | size_t i, j, k; |
334 | int ret = 0; |
335 | u16 bits; |
336 | |
337 | /* Pattern filtering can match up to 128 bytes of frame data. There |
338 | * are 8 registers to program the 16-bit masks, where each bit means |
339 | * the byte will be compared. The frame data will then go through a |
340 | * CRC16 calculation for hardware comparison. This helper function |
341 | * makes sure only relevant frame data are included in this |
342 | * calculation. It provides a warning when the masks and expected |
343 | * data size do not match. |
344 | */ |
345 | i = 0; |
346 | k = 0; |
347 | while (len > 0) { |
348 | bits = *mask; |
349 | for (j = 0; j < 16; j++, i++, len--) { |
350 | /* No more pattern. */ |
351 | if (!len) { |
352 | /* The rest of bitmap is not empty. */ |
353 | if (bits) |
354 | ret = i + 1; |
355 | break; |
356 | } |
357 | if (bits & 1) |
358 | data[k++] = pattern[i]; |
359 | bits >>= 1; |
360 | } |
361 | mask++; |
362 | } |
363 | *datalen = k; |
364 | return ret; |
365 | } |
366 | |
367 | static int lan874x_set_wol_pattern(struct phy_device *phydev, u16 val, |
368 | const u8 data[], u8 datalen, |
369 | const u16 *mask, u8 masklen) |
370 | { |
371 | u16 crc, reg; |
372 | int rc; |
373 | |
374 | /* Starting pattern offset is set before calling this function. */ |
375 | val |= MII_LAN874X_PHY_WOL_FILTER_EN; |
376 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, |
377 | MII_LAN874X_PHY_MMD_WOL_WUF_CFGA, val); |
378 | if (rc < 0) |
379 | return rc; |
380 | |
381 | crc = smsc_crc16(buffer: data, len: datalen); |
382 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, |
383 | MII_LAN874X_PHY_MMD_WOL_WUF_CFGB, val: crc); |
384 | if (rc < 0) |
385 | return rc; |
386 | |
387 | masklen = (masklen + 15) & ~0xf; |
388 | reg = MII_LAN874X_PHY_MMD_WOL_WUF_MASK7; |
389 | while (masklen >= 16) { |
390 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: reg, val: *mask); |
391 | if (rc < 0) |
392 | return rc; |
393 | reg--; |
394 | mask++; |
395 | masklen -= 16; |
396 | } |
397 | |
398 | /* Clear out the rest of mask registers. */ |
399 | while (reg != MII_LAN874X_PHY_MMD_WOL_WUF_MASK0) { |
400 | phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: reg, val: 0); |
401 | reg--; |
402 | } |
403 | return rc; |
404 | } |
405 | |
406 | static int lan874x_set_wol(struct phy_device *phydev, |
407 | struct ethtool_wolinfo *wol) |
408 | { |
409 | struct net_device *ndev = phydev->attached_dev; |
410 | struct smsc_phy_priv *priv = phydev->priv; |
411 | u16 val, val_wucsr; |
412 | u8 data[128]; |
413 | u8 datalen; |
414 | int rc; |
415 | |
416 | /* lan874x has only one WoL filter pattern */ |
417 | if ((wol->wolopts & (WAKE_ARP | WAKE_MCAST)) == |
418 | (WAKE_ARP | WAKE_MCAST)) { |
419 | phydev_info(phydev, |
420 | "lan874x WoL supports one of ARP|MCAST at a time\n" ); |
421 | return -EOPNOTSUPP; |
422 | } |
423 | |
424 | rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR); |
425 | if (rc < 0) |
426 | return rc; |
427 | |
428 | val_wucsr = rc; |
429 | |
430 | if (wol->wolopts & WAKE_UCAST) |
431 | val_wucsr |= MII_LAN874X_PHY_WOL_PFDAEN; |
432 | else |
433 | val_wucsr &= ~MII_LAN874X_PHY_WOL_PFDAEN; |
434 | |
435 | if (wol->wolopts & WAKE_BCAST) |
436 | val_wucsr |= MII_LAN874X_PHY_WOL_BCSTEN; |
437 | else |
438 | val_wucsr &= ~MII_LAN874X_PHY_WOL_BCSTEN; |
439 | |
440 | if (wol->wolopts & WAKE_MAGIC) |
441 | val_wucsr |= MII_LAN874X_PHY_WOL_MPEN; |
442 | else |
443 | val_wucsr &= ~MII_LAN874X_PHY_WOL_MPEN; |
444 | |
445 | /* Need to use pattern matching */ |
446 | if (wol->wolopts & (WAKE_ARP | WAKE_MCAST)) |
447 | val_wucsr |= MII_LAN874X_PHY_WOL_WUEN; |
448 | else |
449 | val_wucsr &= ~MII_LAN874X_PHY_WOL_WUEN; |
450 | |
451 | if (wol->wolopts & WAKE_ARP) { |
452 | const u8 pattern[2] = { 0x08, 0x06 }; |
453 | const u16 mask[1] = { 0x0003 }; |
454 | |
455 | rc = lan874x_chk_wol_pattern(pattern, mask, len: 2, data, |
456 | datalen: &datalen); |
457 | if (rc) |
458 | phydev_dbg(phydev, "pattern not valid at %d\n" , rc); |
459 | |
460 | /* Need to match broadcast destination address and provided |
461 | * data pattern at offset 12. |
462 | */ |
463 | val = 12 | MII_LAN874X_PHY_WOL_FILTER_BCSTEN; |
464 | rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask, |
465 | masklen: 2); |
466 | if (rc < 0) |
467 | return rc; |
468 | priv->wol_arp = true; |
469 | } |
470 | |
471 | if (wol->wolopts & WAKE_MCAST) { |
472 | /* Need to match multicast destination address. */ |
473 | val = MII_LAN874X_PHY_WOL_FILTER_MCASTTEN; |
474 | rc = lan874x_set_wol_pattern(phydev, val, data, datalen: 0, NULL, masklen: 0); |
475 | if (rc < 0) |
476 | return rc; |
477 | priv->wol_arp = false; |
478 | } |
479 | |
480 | if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) { |
481 | const u8 *mac = (const u8 *)ndev->dev_addr; |
482 | int i, reg; |
483 | |
484 | reg = MII_LAN874X_PHY_MMD_WOL_RX_ADDRC; |
485 | for (i = 0; i < 6; i += 2, reg--) { |
486 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: reg, |
487 | val: ((mac[i + 1] << 8) | mac[i])); |
488 | if (rc < 0) |
489 | return rc; |
490 | } |
491 | } |
492 | |
493 | rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR, |
494 | val: val_wucsr); |
495 | if (rc < 0) |
496 | return rc; |
497 | |
498 | return 0; |
499 | } |
500 | |
501 | static int smsc_get_sset_count(struct phy_device *phydev) |
502 | { |
503 | return ARRAY_SIZE(smsc_hw_stats); |
504 | } |
505 | |
506 | static void smsc_get_strings(struct phy_device *phydev, u8 *data) |
507 | { |
508 | int i; |
509 | |
510 | for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) |
511 | ethtool_sprintf(data: &data, fmt: "%s" , smsc_hw_stats[i].string); |
512 | } |
513 | |
514 | static u64 smsc_get_stat(struct phy_device *phydev, int i) |
515 | { |
516 | struct smsc_hw_stat stat = smsc_hw_stats[i]; |
517 | int val; |
518 | u64 ret; |
519 | |
520 | val = phy_read(phydev, regnum: stat.reg); |
521 | if (val < 0) |
522 | ret = U64_MAX; |
523 | else |
524 | ret = val; |
525 | |
526 | return ret; |
527 | } |
528 | |
529 | static void smsc_get_stats(struct phy_device *phydev, |
530 | struct ethtool_stats *stats, u64 *data) |
531 | { |
532 | int i; |
533 | |
534 | for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) |
535 | data[i] = smsc_get_stat(phydev, i); |
536 | } |
537 | |
538 | static int smsc_phy_get_edpd(struct phy_device *phydev, u16 *edpd) |
539 | { |
540 | struct smsc_phy_priv *priv = phydev->priv; |
541 | |
542 | if (!priv) |
543 | return -EOPNOTSUPP; |
544 | |
545 | if (!priv->edpd_enable) |
546 | *edpd = ETHTOOL_PHY_EDPD_DISABLE; |
547 | else if (!priv->edpd_max_wait_ms) |
548 | *edpd = ETHTOOL_PHY_EDPD_NO_TX; |
549 | else |
550 | *edpd = PHY_STATE_MACH_MS + priv->edpd_max_wait_ms; |
551 | |
552 | return 0; |
553 | } |
554 | |
555 | static int smsc_phy_set_edpd(struct phy_device *phydev, u16 edpd) |
556 | { |
557 | struct smsc_phy_priv *priv = phydev->priv; |
558 | |
559 | if (!priv) |
560 | return -EOPNOTSUPP; |
561 | |
562 | switch (edpd) { |
563 | case ETHTOOL_PHY_EDPD_DISABLE: |
564 | priv->edpd_enable = false; |
565 | break; |
566 | case ETHTOOL_PHY_EDPD_NO_TX: |
567 | priv->edpd_enable = true; |
568 | priv->edpd_max_wait_ms = 0; |
569 | break; |
570 | case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: |
571 | edpd = PHY_STATE_MACH_MS + EDPD_MAX_WAIT_DFLT_MS; |
572 | fallthrough; |
573 | default: |
574 | if (phydev->irq != PHY_POLL) |
575 | return -EOPNOTSUPP; |
576 | if (edpd < PHY_STATE_MACH_MS || edpd > PHY_STATE_MACH_MS + 1000) |
577 | return -EINVAL; |
578 | priv->edpd_enable = true; |
579 | priv->edpd_max_wait_ms = edpd - PHY_STATE_MACH_MS; |
580 | } |
581 | |
582 | priv->edpd_mode_set_by_user = true; |
583 | |
584 | return smsc_phy_config_edpd(phydev); |
585 | } |
586 | |
587 | int smsc_phy_get_tunable(struct phy_device *phydev, |
588 | struct ethtool_tunable *tuna, void *data) |
589 | { |
590 | switch (tuna->id) { |
591 | case ETHTOOL_PHY_EDPD: |
592 | return smsc_phy_get_edpd(phydev, edpd: data); |
593 | default: |
594 | return -EOPNOTSUPP; |
595 | } |
596 | } |
597 | EXPORT_SYMBOL_GPL(smsc_phy_get_tunable); |
598 | |
599 | int smsc_phy_set_tunable(struct phy_device *phydev, |
600 | struct ethtool_tunable *tuna, const void *data) |
601 | { |
602 | switch (tuna->id) { |
603 | case ETHTOOL_PHY_EDPD: |
604 | return smsc_phy_set_edpd(phydev, edpd: *(u16 *)data); |
605 | default: |
606 | return -EOPNOTSUPP; |
607 | } |
608 | } |
609 | EXPORT_SYMBOL_GPL(smsc_phy_set_tunable); |
610 | |
611 | int smsc_phy_probe(struct phy_device *phydev) |
612 | { |
613 | struct device *dev = &phydev->mdio.dev; |
614 | struct smsc_phy_priv *priv; |
615 | struct clk *refclk; |
616 | |
617 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
618 | if (!priv) |
619 | return -ENOMEM; |
620 | |
621 | priv->edpd_enable = true; |
622 | priv->edpd_max_wait_ms = EDPD_MAX_WAIT_DFLT_MS; |
623 | |
624 | if (device_property_present(dev, propname: "smsc,disable-energy-detect" )) |
625 | priv->edpd_enable = false; |
626 | |
627 | phydev->priv = priv; |
628 | |
629 | /* Make clk optional to keep DTB backward compatibility. */ |
630 | refclk = devm_clk_get_optional_enabled(dev, NULL); |
631 | if (IS_ERR(ptr: refclk)) |
632 | return dev_err_probe(dev, err: PTR_ERR(ptr: refclk), |
633 | fmt: "Failed to request clock\n" ); |
634 | |
635 | return clk_set_rate(clk: refclk, rate: 50 * 1000 * 1000); |
636 | } |
637 | EXPORT_SYMBOL_GPL(smsc_phy_probe); |
638 | |
639 | static struct phy_driver smsc_phy_driver[] = { |
640 | { |
641 | .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ |
642 | .phy_id_mask = 0xfffffff0, |
643 | .name = "SMSC LAN83C185" , |
644 | |
645 | /* PHY_BASIC_FEATURES */ |
646 | |
647 | .probe = smsc_phy_probe, |
648 | |
649 | /* basic functions */ |
650 | .config_init = smsc_phy_config_init, |
651 | .soft_reset = smsc_phy_reset, |
652 | |
653 | /* IRQ related */ |
654 | .config_intr = smsc_phy_config_intr, |
655 | .handle_interrupt = smsc_phy_handle_interrupt, |
656 | |
657 | .suspend = genphy_suspend, |
658 | .resume = genphy_resume, |
659 | }, { |
660 | .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ |
661 | .phy_id_mask = 0xfffffff0, |
662 | .name = "SMSC LAN8187" , |
663 | |
664 | /* PHY_BASIC_FEATURES */ |
665 | |
666 | .probe = smsc_phy_probe, |
667 | |
668 | /* basic functions */ |
669 | .config_init = smsc_phy_config_init, |
670 | .soft_reset = smsc_phy_reset, |
671 | |
672 | /* IRQ related */ |
673 | .config_intr = smsc_phy_config_intr, |
674 | .handle_interrupt = smsc_phy_handle_interrupt, |
675 | |
676 | /* Statistics */ |
677 | .get_sset_count = smsc_get_sset_count, |
678 | .get_strings = smsc_get_strings, |
679 | .get_stats = smsc_get_stats, |
680 | |
681 | .suspend = genphy_suspend, |
682 | .resume = genphy_resume, |
683 | }, { |
684 | /* This covers internal PHY (phy_id: 0x0007C0C3) for |
685 | * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505) |
686 | */ |
687 | .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ |
688 | .phy_id_mask = 0xfffffff0, |
689 | .name = "SMSC LAN8700" , |
690 | |
691 | /* PHY_BASIC_FEATURES */ |
692 | |
693 | .probe = smsc_phy_probe, |
694 | |
695 | /* basic functions */ |
696 | .read_status = lan87xx_read_status, |
697 | .config_init = smsc_phy_config_init, |
698 | .soft_reset = smsc_phy_reset, |
699 | .config_aneg = lan87xx_config_aneg, |
700 | |
701 | /* IRQ related */ |
702 | .config_intr = smsc_phy_config_intr, |
703 | .handle_interrupt = smsc_phy_handle_interrupt, |
704 | |
705 | /* Statistics */ |
706 | .get_sset_count = smsc_get_sset_count, |
707 | .get_strings = smsc_get_strings, |
708 | .get_stats = smsc_get_stats, |
709 | |
710 | .get_tunable = smsc_phy_get_tunable, |
711 | .set_tunable = smsc_phy_set_tunable, |
712 | |
713 | .suspend = genphy_suspend, |
714 | .resume = genphy_resume, |
715 | }, { |
716 | .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ |
717 | .phy_id_mask = 0xfffffff0, |
718 | .name = "SMSC LAN911x Internal PHY" , |
719 | |
720 | /* PHY_BASIC_FEATURES */ |
721 | |
722 | .probe = smsc_phy_probe, |
723 | |
724 | /* IRQ related */ |
725 | .config_intr = smsc_phy_config_intr, |
726 | .handle_interrupt = smsc_phy_handle_interrupt, |
727 | |
728 | .suspend = genphy_suspend, |
729 | .resume = genphy_resume, |
730 | }, { |
731 | /* This covers internal PHY (phy_id: 0x0007C0F0) for |
732 | * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01) |
733 | */ |
734 | .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ |
735 | .phy_id_mask = 0xfffffff0, |
736 | .name = "SMSC LAN8710/LAN8720" , |
737 | |
738 | /* PHY_BASIC_FEATURES */ |
739 | |
740 | .probe = smsc_phy_probe, |
741 | |
742 | /* basic functions */ |
743 | .read_status = lan87xx_read_status, |
744 | .config_init = smsc_phy_config_init, |
745 | .soft_reset = smsc_phy_reset, |
746 | .config_aneg = lan95xx_config_aneg_ext, |
747 | |
748 | /* IRQ related */ |
749 | .config_intr = smsc_phy_config_intr, |
750 | .handle_interrupt = smsc_phy_handle_interrupt, |
751 | |
752 | /* Statistics */ |
753 | .get_sset_count = smsc_get_sset_count, |
754 | .get_strings = smsc_get_strings, |
755 | .get_stats = smsc_get_stats, |
756 | |
757 | .get_tunable = smsc_phy_get_tunable, |
758 | .set_tunable = smsc_phy_set_tunable, |
759 | |
760 | .suspend = genphy_suspend, |
761 | .resume = genphy_resume, |
762 | }, { |
763 | .phy_id = 0x0007c110, |
764 | .phy_id_mask = 0xfffffff0, |
765 | .name = "SMSC LAN8740" , |
766 | |
767 | /* PHY_BASIC_FEATURES */ |
768 | .flags = PHY_RST_AFTER_CLK_EN, |
769 | |
770 | .probe = smsc_phy_probe, |
771 | |
772 | /* basic functions */ |
773 | .read_status = lan87xx_read_status, |
774 | .config_init = lan874x_phy_config_init, |
775 | .soft_reset = smsc_phy_reset, |
776 | |
777 | /* IRQ related */ |
778 | .config_intr = smsc_phy_config_intr, |
779 | .handle_interrupt = smsc_phy_handle_interrupt, |
780 | |
781 | /* Statistics */ |
782 | .get_sset_count = smsc_get_sset_count, |
783 | .get_strings = smsc_get_strings, |
784 | .get_stats = smsc_get_stats, |
785 | |
786 | .get_tunable = smsc_phy_get_tunable, |
787 | .set_tunable = smsc_phy_set_tunable, |
788 | |
789 | /* WoL */ |
790 | .set_wol = lan874x_set_wol, |
791 | .get_wol = lan874x_get_wol, |
792 | |
793 | .suspend = genphy_suspend, |
794 | .resume = genphy_resume, |
795 | }, { |
796 | .phy_id = 0x0007c130, /* 0x0007c130 and 0x0007c131 */ |
797 | /* This mask (0xfffffff2) is to differentiate from |
798 | * LAN88xx (phy_id 0x0007c132) |
799 | * and allows future phy_id revisions. |
800 | */ |
801 | .phy_id_mask = 0xfffffff2, |
802 | .name = "Microchip LAN8742" , |
803 | |
804 | /* PHY_BASIC_FEATURES */ |
805 | .flags = PHY_RST_AFTER_CLK_EN, |
806 | |
807 | .probe = smsc_phy_probe, |
808 | |
809 | /* basic functions */ |
810 | .read_status = lan87xx_read_status, |
811 | .config_init = lan874x_phy_config_init, |
812 | .soft_reset = smsc_phy_reset, |
813 | |
814 | /* IRQ related */ |
815 | .config_intr = smsc_phy_config_intr, |
816 | .handle_interrupt = smsc_phy_handle_interrupt, |
817 | |
818 | /* Statistics */ |
819 | .get_sset_count = smsc_get_sset_count, |
820 | .get_strings = smsc_get_strings, |
821 | .get_stats = smsc_get_stats, |
822 | |
823 | .get_tunable = smsc_phy_get_tunable, |
824 | .set_tunable = smsc_phy_set_tunable, |
825 | |
826 | /* WoL */ |
827 | .set_wol = lan874x_set_wol, |
828 | .get_wol = lan874x_get_wol, |
829 | |
830 | .suspend = genphy_suspend, |
831 | .resume = genphy_resume, |
832 | } }; |
833 | |
834 | module_phy_driver(smsc_phy_driver); |
835 | |
836 | MODULE_DESCRIPTION("SMSC PHY driver" ); |
837 | MODULE_AUTHOR("Herbert Valerio Riedel" ); |
838 | MODULE_LICENSE("GPL" ); |
839 | |
840 | static struct mdio_device_id __maybe_unused smsc_tbl[] = { |
841 | { 0x0007c0a0, 0xfffffff0 }, |
842 | { 0x0007c0b0, 0xfffffff0 }, |
843 | { 0x0007c0c0, 0xfffffff0 }, |
844 | { 0x0007c0d0, 0xfffffff0 }, |
845 | { 0x0007c0f0, 0xfffffff0 }, |
846 | { 0x0007c110, 0xfffffff0 }, |
847 | { 0x0007c130, 0xfffffff2 }, |
848 | { } |
849 | }; |
850 | |
851 | MODULE_DEVICE_TABLE(mdio, smsc_tbl); |
852 | |