1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * drivers/net/phy/broadcom.c |
4 | * |
5 | * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet |
6 | * transceivers. |
7 | * |
8 | * Copyright (c) 2006 Maciej W. Rozycki |
9 | * |
10 | * Inspired by code written by Amy Fong. |
11 | */ |
12 | |
13 | #include "bcm-phy-lib.h" |
14 | #include <linux/delay.h> |
15 | #include <linux/module.h> |
16 | #include <linux/phy.h> |
17 | #include <linux/pm_wakeup.h> |
18 | #include <linux/brcmphy.h> |
19 | #include <linux/of.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/irq.h> |
22 | #include <linux/gpio/consumer.h> |
23 | |
24 | #define BRCM_PHY_MODEL(phydev) \ |
25 | ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) |
26 | |
27 | #define BRCM_PHY_REV(phydev) \ |
28 | ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask)) |
29 | |
30 | MODULE_DESCRIPTION("Broadcom PHY driver" ); |
31 | MODULE_AUTHOR("Maciej W. Rozycki" ); |
32 | MODULE_LICENSE("GPL" ); |
33 | |
34 | struct bcm54xx_phy_priv { |
35 | u64 *stats; |
36 | struct bcm_ptp_private *ptp; |
37 | int wake_irq; |
38 | bool wake_irq_enabled; |
39 | }; |
40 | |
41 | static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev) |
42 | { |
43 | struct bcm54xx_phy_priv *priv = phydev->priv; |
44 | |
45 | return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0; |
46 | } |
47 | |
48 | static int bcm54xx_config_clock_delay(struct phy_device *phydev) |
49 | { |
50 | int rc, val; |
51 | |
52 | /* handling PHY's internal RX clock delay */ |
53 | val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); |
54 | val |= MII_BCM54XX_AUXCTL_MISC_WREN; |
55 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII || |
56 | phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { |
57 | /* Disable RGMII RXC-RXD skew */ |
58 | val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; |
59 | } |
60 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || |
61 | phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { |
62 | /* Enable RGMII RXC-RXD skew */ |
63 | val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; |
64 | } |
65 | rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, |
66 | val); |
67 | if (rc < 0) |
68 | return rc; |
69 | |
70 | /* handling PHY's internal TX clock delay */ |
71 | val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL); |
72 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII || |
73 | phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { |
74 | /* Disable internal TX clock delay */ |
75 | val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; |
76 | } |
77 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || |
78 | phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { |
79 | /* Enable internal TX clock delay */ |
80 | val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN; |
81 | } |
82 | rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); |
83 | if (rc < 0) |
84 | return rc; |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static int bcm54210e_config_init(struct phy_device *phydev) |
90 | { |
91 | int val; |
92 | |
93 | bcm54xx_config_clock_delay(phydev); |
94 | |
95 | if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { |
96 | val = phy_read(phydev, MII_CTRL1000); |
97 | val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; |
98 | phy_write(phydev, MII_CTRL1000, val); |
99 | } |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static int bcm54612e_config_init(struct phy_device *phydev) |
105 | { |
106 | int reg; |
107 | |
108 | bcm54xx_config_clock_delay(phydev); |
109 | |
110 | /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ |
111 | if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { |
112 | int err; |
113 | |
114 | reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); |
115 | err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, |
116 | BCM54612E_LED4_CLK125OUT_EN | reg); |
117 | |
118 | if (err < 0) |
119 | return err; |
120 | } |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static int bcm54616s_config_init(struct phy_device *phydev) |
126 | { |
127 | int rc, val; |
128 | |
129 | if (phydev->interface != PHY_INTERFACE_MODE_SGMII && |
130 | phydev->interface != PHY_INTERFACE_MODE_1000BASEX) |
131 | return 0; |
132 | |
133 | /* Ensure proper interface mode is selected. */ |
134 | /* Disable RGMII mode */ |
135 | val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); |
136 | if (val < 0) |
137 | return val; |
138 | val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN; |
139 | val |= MII_BCM54XX_AUXCTL_MISC_WREN; |
140 | rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, |
141 | val); |
142 | if (rc < 0) |
143 | return rc; |
144 | |
145 | /* Select 1000BASE-X register set (primary SerDes) */ |
146 | val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); |
147 | if (val < 0) |
148 | return val; |
149 | val |= BCM54XX_SHD_MODE_1000BX; |
150 | rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); |
151 | if (rc < 0) |
152 | return rc; |
153 | |
154 | /* Power down SerDes interface */ |
155 | rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN); |
156 | if (rc < 0) |
157 | return rc; |
158 | |
159 | /* Select proper interface mode */ |
160 | val &= ~BCM54XX_SHD_INTF_SEL_MASK; |
161 | val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ? |
162 | BCM54XX_SHD_INTF_SEL_SGMII : |
163 | BCM54XX_SHD_INTF_SEL_GBIC; |
164 | rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); |
165 | if (rc < 0) |
166 | return rc; |
167 | |
168 | /* Power up SerDes interface */ |
169 | rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN); |
170 | if (rc < 0) |
171 | return rc; |
172 | |
173 | /* Select copper register set */ |
174 | val &= ~BCM54XX_SHD_MODE_1000BX; |
175 | rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); |
176 | if (rc < 0) |
177 | return rc; |
178 | |
179 | /* Power up copper interface */ |
180 | return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN); |
181 | } |
182 | |
183 | /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */ |
184 | static int bcm50610_a0_workaround(struct phy_device *phydev) |
185 | { |
186 | int err; |
187 | |
188 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0, |
189 | MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | |
190 | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); |
191 | if (err < 0) |
192 | return err; |
193 | |
194 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, |
195 | MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); |
196 | if (err < 0) |
197 | return err; |
198 | |
199 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, |
200 | MII_BCM54XX_EXP_EXP75_VDACCTRL); |
201 | if (err < 0) |
202 | return err; |
203 | |
204 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96, |
205 | MII_BCM54XX_EXP_EXP96_MYST); |
206 | if (err < 0) |
207 | return err; |
208 | |
209 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97, |
210 | MII_BCM54XX_EXP_EXP97_MYST); |
211 | |
212 | return err; |
213 | } |
214 | |
215 | static int bcm54xx_phydsp_config(struct phy_device *phydev) |
216 | { |
217 | int err, err2; |
218 | |
219 | /* Enable the SMDSP clock */ |
220 | err = bcm54xx_auxctl_write(phydev, |
221 | MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, |
222 | MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | |
223 | MII_BCM54XX_AUXCTL_ACTL_TX_6DB); |
224 | if (err < 0) |
225 | return err; |
226 | |
227 | if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || |
228 | BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { |
229 | /* Clear bit 9 to fix a phy interop issue. */ |
230 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, |
231 | MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); |
232 | if (err < 0) |
233 | goto error; |
234 | |
235 | if (phydev->drv->phy_id == PHY_ID_BCM50610) { |
236 | err = bcm50610_a0_workaround(phydev); |
237 | if (err < 0) |
238 | goto error; |
239 | } |
240 | } |
241 | |
242 | if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { |
243 | int val; |
244 | |
245 | val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75); |
246 | if (val < 0) |
247 | goto error; |
248 | |
249 | val |= MII_BCM54XX_EXP_EXP75_CM_OSC; |
250 | err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val); |
251 | } |
252 | |
253 | error: |
254 | /* Disable the SMDSP clock */ |
255 | err2 = bcm54xx_auxctl_write(phydev, |
256 | MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, |
257 | MII_BCM54XX_AUXCTL_ACTL_TX_6DB); |
258 | |
259 | /* Return the first error reported. */ |
260 | return err ? err : err2; |
261 | } |
262 | |
263 | static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) |
264 | { |
265 | u32 orig; |
266 | int val; |
267 | bool clk125en = true; |
268 | |
269 | /* Abort if we are using an untested phy. */ |
270 | if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 && |
271 | BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 && |
272 | BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M && |
273 | BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E && |
274 | BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 && |
275 | BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) |
276 | return; |
277 | |
278 | val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); |
279 | if (val < 0) |
280 | return; |
281 | |
282 | orig = val; |
283 | |
284 | if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || |
285 | BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && |
286 | BRCM_PHY_REV(phydev) >= 0x3) { |
287 | /* |
288 | * Here, bit 0 _disables_ CLK125 when set. |
289 | * This bit is set by default. |
290 | */ |
291 | clk125en = false; |
292 | } else { |
293 | if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) { |
294 | if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) { |
295 | /* Here, bit 0 _enables_ CLK125 when set */ |
296 | val &= ~BCM54XX_SHD_SCR3_DEF_CLK125; |
297 | } |
298 | clk125en = false; |
299 | } |
300 | } |
301 | |
302 | if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) |
303 | val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS; |
304 | else |
305 | val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; |
306 | |
307 | if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) { |
308 | if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E || |
309 | BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 || |
310 | BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811) |
311 | val |= BCM54XX_SHD_SCR3_RXCTXC_DIS; |
312 | else |
313 | val |= BCM54XX_SHD_SCR3_TRDDAPD; |
314 | } |
315 | |
316 | if (orig != val) |
317 | bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); |
318 | |
319 | val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); |
320 | if (val < 0) |
321 | return; |
322 | |
323 | orig = val; |
324 | |
325 | if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) |
326 | val |= BCM54XX_SHD_APD_EN; |
327 | else |
328 | val &= ~BCM54XX_SHD_APD_EN; |
329 | |
330 | if (orig != val) |
331 | bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); |
332 | } |
333 | |
334 | static void bcm54xx_ptp_stop(struct phy_device *phydev) |
335 | { |
336 | struct bcm54xx_phy_priv *priv = phydev->priv; |
337 | |
338 | if (priv->ptp) |
339 | bcm_ptp_stop(priv: priv->ptp); |
340 | } |
341 | |
342 | static void bcm54xx_ptp_config_init(struct phy_device *phydev) |
343 | { |
344 | struct bcm54xx_phy_priv *priv = phydev->priv; |
345 | |
346 | if (priv->ptp) |
347 | bcm_ptp_config_init(phydev); |
348 | } |
349 | |
350 | static int bcm54xx_config_init(struct phy_device *phydev) |
351 | { |
352 | int reg, err, val; |
353 | |
354 | reg = phy_read(phydev, MII_BCM54XX_ECR); |
355 | if (reg < 0) |
356 | return reg; |
357 | |
358 | /* Mask interrupts globally. */ |
359 | reg |= MII_BCM54XX_ECR_IM; |
360 | err = phy_write(phydev, MII_BCM54XX_ECR, val: reg); |
361 | if (err < 0) |
362 | return err; |
363 | |
364 | /* Unmask events we are interested in. */ |
365 | reg = ~(MII_BCM54XX_INT_DUPLEX | |
366 | MII_BCM54XX_INT_SPEED | |
367 | MII_BCM54XX_INT_LINK); |
368 | err = phy_write(phydev, MII_BCM54XX_IMR, val: reg); |
369 | if (err < 0) |
370 | return err; |
371 | |
372 | if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || |
373 | BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && |
374 | (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) |
375 | bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, val: 0); |
376 | |
377 | bcm54xx_adjust_rxrefclk(phydev); |
378 | |
379 | switch (BRCM_PHY_MODEL(phydev)) { |
380 | case PHY_ID_BCM50610: |
381 | case PHY_ID_BCM50610M: |
382 | err = bcm54xx_config_clock_delay(phydev); |
383 | break; |
384 | case PHY_ID_BCM54210E: |
385 | err = bcm54210e_config_init(phydev); |
386 | break; |
387 | case PHY_ID_BCM54612E: |
388 | err = bcm54612e_config_init(phydev); |
389 | break; |
390 | case PHY_ID_BCM54616S: |
391 | err = bcm54616s_config_init(phydev); |
392 | break; |
393 | case PHY_ID_BCM54810: |
394 | /* For BCM54810, we need to disable BroadR-Reach function */ |
395 | val = bcm_phy_read_exp(phydev, |
396 | BCM54810_EXP_BROADREACH_LRE_MISC_CTL); |
397 | val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; |
398 | err = bcm_phy_write_exp(phydev, |
399 | BCM54810_EXP_BROADREACH_LRE_MISC_CTL, |
400 | val); |
401 | break; |
402 | } |
403 | if (err) |
404 | return err; |
405 | |
406 | bcm54xx_phydsp_config(phydev); |
407 | |
408 | /* For non-SFP setups, encode link speed into LED1 and LED3 pair |
409 | * (green/amber). |
410 | * Also flash these two LEDs on activity. This means configuring |
411 | * them for MULTICOLOR and encoding link/activity into them. |
412 | * Don't do this for devices on an SFP module, since some of these |
413 | * use the LED outputs to control the SFP LOS signal, and changing |
414 | * these settings will cause LOS to malfunction. |
415 | */ |
416 | if (!phy_on_sfp(phydev)) { |
417 | val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) | |
418 | BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1); |
419 | bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val); |
420 | |
421 | val = BCM_LED_MULTICOLOR_IN_PHASE | |
422 | BCM54XX_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) | |
423 | BCM54XX_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT); |
424 | bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val); |
425 | } |
426 | |
427 | bcm54xx_ptp_config_init(phydev); |
428 | |
429 | /* Acknowledge any left over interrupt and charge the device for |
430 | * wake-up. |
431 | */ |
432 | err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); |
433 | if (err < 0) |
434 | return err; |
435 | |
436 | if (err) |
437 | pm_wakeup_event(dev: &phydev->mdio.dev, msec: 0); |
438 | |
439 | return 0; |
440 | } |
441 | |
442 | static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable) |
443 | { |
444 | int ret = 0; |
445 | |
446 | if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND)) |
447 | return ret; |
448 | |
449 | ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL); |
450 | if (ret < 0) |
451 | goto out; |
452 | |
453 | if (enable) |
454 | ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP; |
455 | else |
456 | ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP); |
457 | |
458 | ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, val: ret); |
459 | out: |
460 | return ret; |
461 | } |
462 | |
463 | static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state) |
464 | { |
465 | struct bcm54xx_phy_priv *priv = phydev->priv; |
466 | int ret = 0; |
467 | |
468 | if (!bcm54xx_phy_can_wakeup(phydev)) |
469 | return ret; |
470 | |
471 | if (priv->wake_irq_enabled != state) { |
472 | if (state) |
473 | ret = enable_irq_wake(irq: priv->wake_irq); |
474 | else |
475 | ret = disable_irq_wake(irq: priv->wake_irq); |
476 | priv->wake_irq_enabled = state; |
477 | } |
478 | |
479 | return ret; |
480 | } |
481 | |
482 | static int bcm54xx_suspend(struct phy_device *phydev) |
483 | { |
484 | int ret = 0; |
485 | |
486 | bcm54xx_ptp_stop(phydev); |
487 | |
488 | /* Acknowledge any Wake-on-LAN interrupt prior to suspend */ |
489 | ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); |
490 | if (ret < 0) |
491 | return ret; |
492 | |
493 | if (phydev->wol_enabled) |
494 | return bcm54xx_set_wakeup_irq(phydev, state: true); |
495 | |
496 | /* We cannot use a read/modify/write here otherwise the PHY gets into |
497 | * a bad state where its LEDs keep flashing, thus defeating the purpose |
498 | * of low power mode. |
499 | */ |
500 | ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN); |
501 | if (ret < 0) |
502 | return ret; |
503 | |
504 | return bcm54xx_iddq_set(phydev, enable: true); |
505 | } |
506 | |
507 | static int bcm54xx_resume(struct phy_device *phydev) |
508 | { |
509 | int ret = 0; |
510 | |
511 | if (phydev->wol_enabled) { |
512 | ret = bcm54xx_set_wakeup_irq(phydev, state: false); |
513 | if (ret) |
514 | return ret; |
515 | } |
516 | |
517 | ret = bcm54xx_iddq_set(phydev, enable: false); |
518 | if (ret < 0) |
519 | return ret; |
520 | |
521 | /* Writes to register other than BMCR would be ignored |
522 | * unless we clear the PDOWN bit first |
523 | */ |
524 | ret = genphy_resume(phydev); |
525 | if (ret < 0) |
526 | return ret; |
527 | |
528 | /* Upon exiting power down, the PHY remains in an internal reset state |
529 | * for 40us |
530 | */ |
531 | fsleep(usecs: 40); |
532 | |
533 | /* Issue a soft reset after clearing the power down bit |
534 | * and before doing any other configuration. |
535 | */ |
536 | if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) { |
537 | ret = genphy_soft_reset(phydev); |
538 | if (ret < 0) |
539 | return ret; |
540 | } |
541 | |
542 | return bcm54xx_config_init(phydev); |
543 | } |
544 | |
545 | static int bcm54810_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) |
546 | { |
547 | return -EOPNOTSUPP; |
548 | } |
549 | |
550 | static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, |
551 | u16 val) |
552 | { |
553 | return -EOPNOTSUPP; |
554 | } |
555 | |
556 | static int bcm54811_config_init(struct phy_device *phydev) |
557 | { |
558 | int err, reg; |
559 | |
560 | /* Disable BroadR-Reach function. */ |
561 | reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); |
562 | reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; |
563 | err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, |
564 | val: reg); |
565 | if (err < 0) |
566 | return err; |
567 | |
568 | err = bcm54xx_config_init(phydev); |
569 | |
570 | /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ |
571 | if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { |
572 | reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); |
573 | err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, |
574 | BCM54612E_LED4_CLK125OUT_EN | reg); |
575 | if (err < 0) |
576 | return err; |
577 | } |
578 | |
579 | return err; |
580 | } |
581 | |
582 | static int bcm5481_config_aneg(struct phy_device *phydev) |
583 | { |
584 | struct device_node *np = phydev->mdio.dev.of_node; |
585 | int ret; |
586 | |
587 | /* Aneg firstly. */ |
588 | ret = genphy_config_aneg(phydev); |
589 | |
590 | /* Then we can set up the delay. */ |
591 | bcm54xx_config_clock_delay(phydev); |
592 | |
593 | if (of_property_read_bool(np, propname: "enet-phy-lane-swap" )) { |
594 | /* Lane Swap - Undocumented register...magic! */ |
595 | ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9, |
596 | val: 0x11B); |
597 | if (ret < 0) |
598 | return ret; |
599 | } |
600 | |
601 | return ret; |
602 | } |
603 | |
604 | struct bcm54616s_phy_priv { |
605 | bool mode_1000bx_en; |
606 | }; |
607 | |
608 | static int bcm54616s_probe(struct phy_device *phydev) |
609 | { |
610 | struct bcm54616s_phy_priv *priv; |
611 | int val; |
612 | |
613 | priv = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*priv), GFP_KERNEL); |
614 | if (!priv) |
615 | return -ENOMEM; |
616 | |
617 | phydev->priv = priv; |
618 | |
619 | val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); |
620 | if (val < 0) |
621 | return val; |
622 | |
623 | /* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0] |
624 | * is 01b, and the link between PHY and its link partner can be |
625 | * either 1000Base-X or 100Base-FX. |
626 | * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX |
627 | * support is still missing as of now. |
628 | */ |
629 | if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) { |
630 | val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL); |
631 | if (val < 0) |
632 | return val; |
633 | |
634 | /* Bit 0 of the SerDes 100-FX Control register, when set |
635 | * to 1, sets the MII/RGMII -> 100BASE-FX configuration. |
636 | * When this bit is set to 0, it sets the GMII/RGMII -> |
637 | * 1000BASE-X configuration. |
638 | */ |
639 | if (!(val & BCM54616S_100FX_MODE)) |
640 | priv->mode_1000bx_en = true; |
641 | |
642 | phydev->port = PORT_FIBRE; |
643 | } |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | static int bcm54616s_config_aneg(struct phy_device *phydev) |
649 | { |
650 | struct bcm54616s_phy_priv *priv = phydev->priv; |
651 | int ret; |
652 | |
653 | /* Aneg firstly. */ |
654 | if (priv->mode_1000bx_en) |
655 | ret = genphy_c37_config_aneg(phydev); |
656 | else |
657 | ret = genphy_config_aneg(phydev); |
658 | |
659 | /* Then we can set up the delay. */ |
660 | bcm54xx_config_clock_delay(phydev); |
661 | |
662 | return ret; |
663 | } |
664 | |
665 | static int bcm54616s_read_status(struct phy_device *phydev) |
666 | { |
667 | struct bcm54616s_phy_priv *priv = phydev->priv; |
668 | int err; |
669 | |
670 | if (priv->mode_1000bx_en) |
671 | err = genphy_c37_read_status(phydev); |
672 | else |
673 | err = genphy_read_status(phydev); |
674 | |
675 | return err; |
676 | } |
677 | |
678 | static int brcm_fet_config_init(struct phy_device *phydev) |
679 | { |
680 | int reg, err, err2, brcmtest; |
681 | |
682 | /* Reset the PHY to bring it to a known state. */ |
683 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); |
684 | if (err < 0) |
685 | return err; |
686 | |
687 | /* The datasheet indicates the PHY needs up to 1us to complete a reset, |
688 | * build some slack here. |
689 | */ |
690 | usleep_range(min: 1000, max: 2000); |
691 | |
692 | /* The PHY requires 65 MDC clock cycles to complete a write operation |
693 | * and turnaround the line properly. |
694 | * |
695 | * We ignore -EIO here as the MDIO controller (e.g.: mdio-bcm-unimac) |
696 | * may flag the lack of turn-around as a read failure. This is |
697 | * particularly true with this combination since the MDIO controller |
698 | * only used 64 MDC cycles. This is not a critical failure in this |
699 | * specific case and it has no functional impact otherwise, so we let |
700 | * that one go through. If there is a genuine bus error, the next read |
701 | * of MII_BRCM_FET_INTREG will error out. |
702 | */ |
703 | err = phy_read(phydev, MII_BMCR); |
704 | if (err < 0 && err != -EIO) |
705 | return err; |
706 | |
707 | /* Read to clear status bits */ |
708 | reg = phy_read(phydev, MII_BRCM_FET_INTREG); |
709 | if (reg < 0) |
710 | return reg; |
711 | |
712 | /* Unmask events we are interested in and mask interrupts globally. */ |
713 | if (phydev->phy_id == PHY_ID_BCM5221) |
714 | reg = MII_BRCM_FET_IR_ENABLE | |
715 | MII_BRCM_FET_IR_MASK; |
716 | else |
717 | reg = MII_BRCM_FET_IR_DUPLEX_EN | |
718 | MII_BRCM_FET_IR_SPEED_EN | |
719 | MII_BRCM_FET_IR_LINK_EN | |
720 | MII_BRCM_FET_IR_ENABLE | |
721 | MII_BRCM_FET_IR_MASK; |
722 | |
723 | err = phy_write(phydev, MII_BRCM_FET_INTREG, val: reg); |
724 | if (err < 0) |
725 | return err; |
726 | |
727 | /* Enable shadow register access */ |
728 | brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST); |
729 | if (brcmtest < 0) |
730 | return brcmtest; |
731 | |
732 | reg = brcmtest | MII_BRCM_FET_BT_SRE; |
733 | |
734 | phy_lock_mdio_bus(phydev); |
735 | |
736 | err = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, val: reg); |
737 | if (err < 0) { |
738 | phy_unlock_mdio_bus(phydev); |
739 | return err; |
740 | } |
741 | |
742 | if (phydev->phy_id != PHY_ID_BCM5221) { |
743 | /* Set the LED mode */ |
744 | reg = __phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4); |
745 | if (reg < 0) { |
746 | err = reg; |
747 | goto done; |
748 | } |
749 | |
750 | err = __phy_modify(phydev, MII_BRCM_FET_SHDW_AUXMODE4, |
751 | MII_BRCM_FET_SHDW_AM4_LED_MASK, |
752 | MII_BRCM_FET_SHDW_AM4_LED_MODE1); |
753 | if (err < 0) |
754 | goto done; |
755 | |
756 | /* Enable auto MDIX */ |
757 | err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL, |
758 | MII_BRCM_FET_SHDW_MC_FAME); |
759 | if (err < 0) |
760 | goto done; |
761 | } |
762 | |
763 | if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) { |
764 | /* Enable auto power down */ |
765 | err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, |
766 | MII_BRCM_FET_SHDW_AS2_APDE); |
767 | } |
768 | |
769 | done: |
770 | /* Disable shadow register access */ |
771 | err2 = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, val: brcmtest); |
772 | if (!err) |
773 | err = err2; |
774 | |
775 | phy_unlock_mdio_bus(phydev); |
776 | |
777 | return err; |
778 | } |
779 | |
780 | static int brcm_fet_ack_interrupt(struct phy_device *phydev) |
781 | { |
782 | int reg; |
783 | |
784 | /* Clear pending interrupts. */ |
785 | reg = phy_read(phydev, MII_BRCM_FET_INTREG); |
786 | if (reg < 0) |
787 | return reg; |
788 | |
789 | return 0; |
790 | } |
791 | |
792 | static int brcm_fet_config_intr(struct phy_device *phydev) |
793 | { |
794 | int reg, err; |
795 | |
796 | reg = phy_read(phydev, MII_BRCM_FET_INTREG); |
797 | if (reg < 0) |
798 | return reg; |
799 | |
800 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
801 | err = brcm_fet_ack_interrupt(phydev); |
802 | if (err) |
803 | return err; |
804 | |
805 | reg &= ~MII_BRCM_FET_IR_MASK; |
806 | err = phy_write(phydev, MII_BRCM_FET_INTREG, val: reg); |
807 | } else { |
808 | reg |= MII_BRCM_FET_IR_MASK; |
809 | err = phy_write(phydev, MII_BRCM_FET_INTREG, val: reg); |
810 | if (err) |
811 | return err; |
812 | |
813 | err = brcm_fet_ack_interrupt(phydev); |
814 | } |
815 | |
816 | return err; |
817 | } |
818 | |
819 | static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev) |
820 | { |
821 | int irq_status; |
822 | |
823 | irq_status = phy_read(phydev, MII_BRCM_FET_INTREG); |
824 | if (irq_status < 0) { |
825 | phy_error(phydev); |
826 | return IRQ_NONE; |
827 | } |
828 | |
829 | if (irq_status == 0) |
830 | return IRQ_NONE; |
831 | |
832 | phy_trigger_machine(phydev); |
833 | |
834 | return IRQ_HANDLED; |
835 | } |
836 | |
837 | static int brcm_fet_suspend(struct phy_device *phydev) |
838 | { |
839 | int reg, err, err2, brcmtest; |
840 | |
841 | /* We cannot use a read/modify/write here otherwise the PHY continues |
842 | * to drive LEDs which defeats the purpose of low power mode. |
843 | */ |
844 | err = phy_write(phydev, MII_BMCR, BMCR_PDOWN); |
845 | if (err < 0) |
846 | return err; |
847 | |
848 | /* Enable shadow register access */ |
849 | brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST); |
850 | if (brcmtest < 0) |
851 | return brcmtest; |
852 | |
853 | reg = brcmtest | MII_BRCM_FET_BT_SRE; |
854 | |
855 | phy_lock_mdio_bus(phydev); |
856 | |
857 | err = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, val: reg); |
858 | if (err < 0) { |
859 | phy_unlock_mdio_bus(phydev); |
860 | return err; |
861 | } |
862 | |
863 | if (phydev->phy_id == PHY_ID_BCM5221) |
864 | /* Force Low Power Mode with clock enabled */ |
865 | reg = BCM5221_SHDW_AM4_EN_CLK_LPM | BCM5221_SHDW_AM4_FORCE_LPM; |
866 | else |
867 | /* Set standby mode */ |
868 | reg = MII_BRCM_FET_SHDW_AM4_STANDBY; |
869 | |
870 | err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXMODE4, val: reg); |
871 | |
872 | /* Disable shadow register access */ |
873 | err2 = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, val: brcmtest); |
874 | if (!err) |
875 | err = err2; |
876 | |
877 | phy_unlock_mdio_bus(phydev); |
878 | |
879 | return err; |
880 | } |
881 | |
882 | static int bcm5221_config_aneg(struct phy_device *phydev) |
883 | { |
884 | int ret, val; |
885 | |
886 | ret = genphy_config_aneg(phydev); |
887 | if (ret) |
888 | return ret; |
889 | |
890 | switch (phydev->mdix_ctrl) { |
891 | case ETH_TP_MDI: |
892 | val = BCM5221_AEGSR_MDIX_DIS; |
893 | break; |
894 | case ETH_TP_MDI_X: |
895 | val = BCM5221_AEGSR_MDIX_DIS | BCM5221_AEGSR_MDIX_MAN_SWAP; |
896 | break; |
897 | case ETH_TP_MDI_AUTO: |
898 | val = 0; |
899 | break; |
900 | default: |
901 | return 0; |
902 | } |
903 | |
904 | return phy_modify(phydev, BCM5221_AEGSR, BCM5221_AEGSR_MDIX_MAN_SWAP | |
905 | BCM5221_AEGSR_MDIX_DIS, |
906 | set: val); |
907 | } |
908 | |
909 | static int bcm5221_read_status(struct phy_device *phydev) |
910 | { |
911 | int ret; |
912 | |
913 | /* Read MDIX status */ |
914 | ret = phy_read(phydev, BCM5221_AEGSR); |
915 | if (ret < 0) |
916 | return ret; |
917 | |
918 | if (ret & BCM5221_AEGSR_MDIX_DIS) { |
919 | if (ret & BCM5221_AEGSR_MDIX_MAN_SWAP) |
920 | phydev->mdix_ctrl = ETH_TP_MDI_X; |
921 | else |
922 | phydev->mdix_ctrl = ETH_TP_MDI; |
923 | } else { |
924 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; |
925 | } |
926 | |
927 | if (ret & BCM5221_AEGSR_MDIX_STATUS) |
928 | phydev->mdix = ETH_TP_MDI_X; |
929 | else |
930 | phydev->mdix = ETH_TP_MDI; |
931 | |
932 | return genphy_read_status(phydev); |
933 | } |
934 | |
935 | static void bcm54xx_phy_get_wol(struct phy_device *phydev, |
936 | struct ethtool_wolinfo *wol) |
937 | { |
938 | /* We cannot wake-up if we do not have a dedicated PHY interrupt line |
939 | * or an out of band GPIO descriptor for wake-up. Zeroing |
940 | * wol->supported allows the caller (MAC driver) to play through and |
941 | * offer its own Wake-on-LAN scheme if available. |
942 | */ |
943 | if (!bcm54xx_phy_can_wakeup(phydev)) { |
944 | wol->supported = 0; |
945 | return; |
946 | } |
947 | |
948 | bcm_phy_get_wol(phydev, wol); |
949 | } |
950 | |
951 | static int bcm54xx_phy_set_wol(struct phy_device *phydev, |
952 | struct ethtool_wolinfo *wol) |
953 | { |
954 | int ret; |
955 | |
956 | /* We cannot wake-up if we do not have a dedicated PHY interrupt line |
957 | * or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP |
958 | * allows the caller (MAC driver) to play through and offer its own |
959 | * Wake-on-LAN scheme if available. |
960 | */ |
961 | if (!bcm54xx_phy_can_wakeup(phydev)) |
962 | return -EOPNOTSUPP; |
963 | |
964 | ret = bcm_phy_set_wol(phydev, wol); |
965 | if (ret < 0) |
966 | return ret; |
967 | |
968 | return 0; |
969 | } |
970 | |
971 | static int bcm54xx_phy_probe(struct phy_device *phydev) |
972 | { |
973 | struct bcm54xx_phy_priv *priv; |
974 | struct gpio_desc *wakeup_gpio; |
975 | int ret = 0; |
976 | |
977 | priv = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*priv), GFP_KERNEL); |
978 | if (!priv) |
979 | return -ENOMEM; |
980 | |
981 | priv->wake_irq = -ENXIO; |
982 | |
983 | phydev->priv = priv; |
984 | |
985 | priv->stats = devm_kcalloc(dev: &phydev->mdio.dev, |
986 | n: bcm_phy_get_sset_count(phydev), size: sizeof(u64), |
987 | GFP_KERNEL); |
988 | if (!priv->stats) |
989 | return -ENOMEM; |
990 | |
991 | priv->ptp = bcm_ptp_probe(phydev); |
992 | if (IS_ERR(ptr: priv->ptp)) |
993 | return PTR_ERR(ptr: priv->ptp); |
994 | |
995 | /* We cannot utilize the _optional variant here since we want to know |
996 | * whether the GPIO descriptor exists or not to advertise Wake-on-LAN |
997 | * support or not. |
998 | */ |
999 | wakeup_gpio = devm_gpiod_get(dev: &phydev->mdio.dev, con_id: "wakeup" , flags: GPIOD_IN); |
1000 | if (PTR_ERR(ptr: wakeup_gpio) == -EPROBE_DEFER) |
1001 | return PTR_ERR(ptr: wakeup_gpio); |
1002 | |
1003 | if (!IS_ERR(ptr: wakeup_gpio)) { |
1004 | priv->wake_irq = gpiod_to_irq(desc: wakeup_gpio); |
1005 | |
1006 | /* Dummy interrupt handler which is not enabled but is provided |
1007 | * in order for the interrupt descriptor to be fully set-up. |
1008 | */ |
1009 | ret = devm_request_irq(dev: &phydev->mdio.dev, irq: priv->wake_irq, |
1010 | handler: bcm_phy_wol_isr, |
1011 | IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN, |
1012 | devname: dev_name(dev: &phydev->mdio.dev), dev_id: phydev); |
1013 | if (ret) |
1014 | return ret; |
1015 | } |
1016 | |
1017 | /* If we do not have a main interrupt or a side-band wake-up interrupt, |
1018 | * then the device cannot be marked as wake-up capable. |
1019 | */ |
1020 | if (!bcm54xx_phy_can_wakeup(phydev)) |
1021 | return 0; |
1022 | |
1023 | return device_init_wakeup(dev: &phydev->mdio.dev, enable: true); |
1024 | } |
1025 | |
1026 | static void bcm54xx_get_stats(struct phy_device *phydev, |
1027 | struct ethtool_stats *stats, u64 *data) |
1028 | { |
1029 | struct bcm54xx_phy_priv *priv = phydev->priv; |
1030 | |
1031 | bcm_phy_get_stats(phydev, shadow: priv->stats, stats, data); |
1032 | } |
1033 | |
1034 | static void bcm54xx_link_change_notify(struct phy_device *phydev) |
1035 | { |
1036 | u16 mask = MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE | |
1037 | MII_BCM54XX_EXP_EXP08_FORCE_DAC_WAKE; |
1038 | int ret; |
1039 | |
1040 | if (phydev->state != PHY_RUNNING) |
1041 | return; |
1042 | |
1043 | /* Don't change the DAC wake settings if auto power down |
1044 | * is not requested. |
1045 | */ |
1046 | if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) |
1047 | return; |
1048 | |
1049 | ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08); |
1050 | if (ret < 0) |
1051 | return; |
1052 | |
1053 | /* Enable/disable 10BaseT auto and forced early DAC wake depending |
1054 | * on the negotiated speed, those settings should only be done |
1055 | * for 10Mbits/sec. |
1056 | */ |
1057 | if (phydev->speed == SPEED_10) |
1058 | ret |= mask; |
1059 | else |
1060 | ret &= ~mask; |
1061 | bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, val: ret); |
1062 | } |
1063 | |
1064 | static struct phy_driver broadcom_drivers[] = { |
1065 | { |
1066 | .phy_id = PHY_ID_BCM5411, |
1067 | .phy_id_mask = 0xfffffff0, |
1068 | .name = "Broadcom BCM5411" , |
1069 | /* PHY_GBIT_FEATURES */ |
1070 | .get_sset_count = bcm_phy_get_sset_count, |
1071 | .get_strings = bcm_phy_get_strings, |
1072 | .get_stats = bcm54xx_get_stats, |
1073 | .probe = bcm54xx_phy_probe, |
1074 | .config_init = bcm54xx_config_init, |
1075 | .config_intr = bcm_phy_config_intr, |
1076 | .handle_interrupt = bcm_phy_handle_interrupt, |
1077 | .link_change_notify = bcm54xx_link_change_notify, |
1078 | }, { |
1079 | .phy_id = PHY_ID_BCM5421, |
1080 | .phy_id_mask = 0xfffffff0, |
1081 | .name = "Broadcom BCM5421" , |
1082 | /* PHY_GBIT_FEATURES */ |
1083 | .get_sset_count = bcm_phy_get_sset_count, |
1084 | .get_strings = bcm_phy_get_strings, |
1085 | .get_stats = bcm54xx_get_stats, |
1086 | .probe = bcm54xx_phy_probe, |
1087 | .config_init = bcm54xx_config_init, |
1088 | .config_intr = bcm_phy_config_intr, |
1089 | .handle_interrupt = bcm_phy_handle_interrupt, |
1090 | .link_change_notify = bcm54xx_link_change_notify, |
1091 | }, { |
1092 | .phy_id = PHY_ID_BCM54210E, |
1093 | .phy_id_mask = 0xfffffff0, |
1094 | .name = "Broadcom BCM54210E" , |
1095 | /* PHY_GBIT_FEATURES */ |
1096 | .flags = PHY_ALWAYS_CALL_SUSPEND, |
1097 | .get_sset_count = bcm_phy_get_sset_count, |
1098 | .get_strings = bcm_phy_get_strings, |
1099 | .get_stats = bcm54xx_get_stats, |
1100 | .probe = bcm54xx_phy_probe, |
1101 | .config_init = bcm54xx_config_init, |
1102 | .config_intr = bcm_phy_config_intr, |
1103 | .handle_interrupt = bcm_phy_handle_interrupt, |
1104 | .link_change_notify = bcm54xx_link_change_notify, |
1105 | .suspend = bcm54xx_suspend, |
1106 | .resume = bcm54xx_resume, |
1107 | .get_wol = bcm54xx_phy_get_wol, |
1108 | .set_wol = bcm54xx_phy_set_wol, |
1109 | .led_brightness_set = bcm_phy_led_brightness_set, |
1110 | }, { |
1111 | .phy_id = PHY_ID_BCM5461, |
1112 | .phy_id_mask = 0xfffffff0, |
1113 | .name = "Broadcom BCM5461" , |
1114 | /* PHY_GBIT_FEATURES */ |
1115 | .get_sset_count = bcm_phy_get_sset_count, |
1116 | .get_strings = bcm_phy_get_strings, |
1117 | .get_stats = bcm54xx_get_stats, |
1118 | .probe = bcm54xx_phy_probe, |
1119 | .config_init = bcm54xx_config_init, |
1120 | .config_intr = bcm_phy_config_intr, |
1121 | .handle_interrupt = bcm_phy_handle_interrupt, |
1122 | .link_change_notify = bcm54xx_link_change_notify, |
1123 | .led_brightness_set = bcm_phy_led_brightness_set, |
1124 | }, { |
1125 | .phy_id = PHY_ID_BCM54612E, |
1126 | .phy_id_mask = 0xfffffff0, |
1127 | .name = "Broadcom BCM54612E" , |
1128 | /* PHY_GBIT_FEATURES */ |
1129 | .get_sset_count = bcm_phy_get_sset_count, |
1130 | .get_strings = bcm_phy_get_strings, |
1131 | .get_stats = bcm54xx_get_stats, |
1132 | .probe = bcm54xx_phy_probe, |
1133 | .config_init = bcm54xx_config_init, |
1134 | .config_intr = bcm_phy_config_intr, |
1135 | .handle_interrupt = bcm_phy_handle_interrupt, |
1136 | .link_change_notify = bcm54xx_link_change_notify, |
1137 | .led_brightness_set = bcm_phy_led_brightness_set, |
1138 | }, { |
1139 | .phy_id = PHY_ID_BCM54616S, |
1140 | .phy_id_mask = 0xfffffff0, |
1141 | .name = "Broadcom BCM54616S" , |
1142 | /* PHY_GBIT_FEATURES */ |
1143 | .soft_reset = genphy_soft_reset, |
1144 | .config_init = bcm54xx_config_init, |
1145 | .config_aneg = bcm54616s_config_aneg, |
1146 | .config_intr = bcm_phy_config_intr, |
1147 | .handle_interrupt = bcm_phy_handle_interrupt, |
1148 | .read_status = bcm54616s_read_status, |
1149 | .probe = bcm54616s_probe, |
1150 | .link_change_notify = bcm54xx_link_change_notify, |
1151 | .led_brightness_set = bcm_phy_led_brightness_set, |
1152 | }, { |
1153 | .phy_id = PHY_ID_BCM5464, |
1154 | .phy_id_mask = 0xfffffff0, |
1155 | .name = "Broadcom BCM5464" , |
1156 | /* PHY_GBIT_FEATURES */ |
1157 | .get_sset_count = bcm_phy_get_sset_count, |
1158 | .get_strings = bcm_phy_get_strings, |
1159 | .get_stats = bcm54xx_get_stats, |
1160 | .probe = bcm54xx_phy_probe, |
1161 | .config_init = bcm54xx_config_init, |
1162 | .config_intr = bcm_phy_config_intr, |
1163 | .handle_interrupt = bcm_phy_handle_interrupt, |
1164 | .suspend = genphy_suspend, |
1165 | .resume = genphy_resume, |
1166 | .link_change_notify = bcm54xx_link_change_notify, |
1167 | .led_brightness_set = bcm_phy_led_brightness_set, |
1168 | }, { |
1169 | .phy_id = PHY_ID_BCM5481, |
1170 | .phy_id_mask = 0xfffffff0, |
1171 | .name = "Broadcom BCM5481" , |
1172 | /* PHY_GBIT_FEATURES */ |
1173 | .get_sset_count = bcm_phy_get_sset_count, |
1174 | .get_strings = bcm_phy_get_strings, |
1175 | .get_stats = bcm54xx_get_stats, |
1176 | .probe = bcm54xx_phy_probe, |
1177 | .config_init = bcm54xx_config_init, |
1178 | .config_aneg = bcm5481_config_aneg, |
1179 | .config_intr = bcm_phy_config_intr, |
1180 | .handle_interrupt = bcm_phy_handle_interrupt, |
1181 | .link_change_notify = bcm54xx_link_change_notify, |
1182 | .led_brightness_set = bcm_phy_led_brightness_set, |
1183 | }, { |
1184 | .phy_id = PHY_ID_BCM54810, |
1185 | .phy_id_mask = 0xfffffff0, |
1186 | .name = "Broadcom BCM54810" , |
1187 | /* PHY_GBIT_FEATURES */ |
1188 | .get_sset_count = bcm_phy_get_sset_count, |
1189 | .get_strings = bcm_phy_get_strings, |
1190 | .get_stats = bcm54xx_get_stats, |
1191 | .probe = bcm54xx_phy_probe, |
1192 | .read_mmd = bcm54810_read_mmd, |
1193 | .write_mmd = bcm54810_write_mmd, |
1194 | .config_init = bcm54xx_config_init, |
1195 | .config_aneg = bcm5481_config_aneg, |
1196 | .config_intr = bcm_phy_config_intr, |
1197 | .handle_interrupt = bcm_phy_handle_interrupt, |
1198 | .suspend = bcm54xx_suspend, |
1199 | .resume = bcm54xx_resume, |
1200 | .link_change_notify = bcm54xx_link_change_notify, |
1201 | .led_brightness_set = bcm_phy_led_brightness_set, |
1202 | }, { |
1203 | .phy_id = PHY_ID_BCM54811, |
1204 | .phy_id_mask = 0xfffffff0, |
1205 | .name = "Broadcom BCM54811" , |
1206 | /* PHY_GBIT_FEATURES */ |
1207 | .get_sset_count = bcm_phy_get_sset_count, |
1208 | .get_strings = bcm_phy_get_strings, |
1209 | .get_stats = bcm54xx_get_stats, |
1210 | .probe = bcm54xx_phy_probe, |
1211 | .config_init = bcm54811_config_init, |
1212 | .config_aneg = bcm5481_config_aneg, |
1213 | .config_intr = bcm_phy_config_intr, |
1214 | .handle_interrupt = bcm_phy_handle_interrupt, |
1215 | .suspend = bcm54xx_suspend, |
1216 | .resume = bcm54xx_resume, |
1217 | .link_change_notify = bcm54xx_link_change_notify, |
1218 | .led_brightness_set = bcm_phy_led_brightness_set, |
1219 | }, { |
1220 | .phy_id = PHY_ID_BCM5482, |
1221 | .phy_id_mask = 0xfffffff0, |
1222 | .name = "Broadcom BCM5482" , |
1223 | /* PHY_GBIT_FEATURES */ |
1224 | .get_sset_count = bcm_phy_get_sset_count, |
1225 | .get_strings = bcm_phy_get_strings, |
1226 | .get_stats = bcm54xx_get_stats, |
1227 | .probe = bcm54xx_phy_probe, |
1228 | .config_init = bcm54xx_config_init, |
1229 | .config_intr = bcm_phy_config_intr, |
1230 | .handle_interrupt = bcm_phy_handle_interrupt, |
1231 | .link_change_notify = bcm54xx_link_change_notify, |
1232 | .led_brightness_set = bcm_phy_led_brightness_set, |
1233 | }, { |
1234 | .phy_id = PHY_ID_BCM50610, |
1235 | .phy_id_mask = 0xfffffff0, |
1236 | .name = "Broadcom BCM50610" , |
1237 | /* PHY_GBIT_FEATURES */ |
1238 | .get_sset_count = bcm_phy_get_sset_count, |
1239 | .get_strings = bcm_phy_get_strings, |
1240 | .get_stats = bcm54xx_get_stats, |
1241 | .probe = bcm54xx_phy_probe, |
1242 | .config_init = bcm54xx_config_init, |
1243 | .config_intr = bcm_phy_config_intr, |
1244 | .handle_interrupt = bcm_phy_handle_interrupt, |
1245 | .link_change_notify = bcm54xx_link_change_notify, |
1246 | .suspend = bcm54xx_suspend, |
1247 | .resume = bcm54xx_resume, |
1248 | .led_brightness_set = bcm_phy_led_brightness_set, |
1249 | }, { |
1250 | .phy_id = PHY_ID_BCM50610M, |
1251 | .phy_id_mask = 0xfffffff0, |
1252 | .name = "Broadcom BCM50610M" , |
1253 | /* PHY_GBIT_FEATURES */ |
1254 | .get_sset_count = bcm_phy_get_sset_count, |
1255 | .get_strings = bcm_phy_get_strings, |
1256 | .get_stats = bcm54xx_get_stats, |
1257 | .probe = bcm54xx_phy_probe, |
1258 | .config_init = bcm54xx_config_init, |
1259 | .config_intr = bcm_phy_config_intr, |
1260 | .handle_interrupt = bcm_phy_handle_interrupt, |
1261 | .link_change_notify = bcm54xx_link_change_notify, |
1262 | .suspend = bcm54xx_suspend, |
1263 | .resume = bcm54xx_resume, |
1264 | .led_brightness_set = bcm_phy_led_brightness_set, |
1265 | }, { |
1266 | .phy_id = PHY_ID_BCM57780, |
1267 | .phy_id_mask = 0xfffffff0, |
1268 | .name = "Broadcom BCM57780" , |
1269 | /* PHY_GBIT_FEATURES */ |
1270 | .get_sset_count = bcm_phy_get_sset_count, |
1271 | .get_strings = bcm_phy_get_strings, |
1272 | .get_stats = bcm54xx_get_stats, |
1273 | .probe = bcm54xx_phy_probe, |
1274 | .config_init = bcm54xx_config_init, |
1275 | .config_intr = bcm_phy_config_intr, |
1276 | .handle_interrupt = bcm_phy_handle_interrupt, |
1277 | .link_change_notify = bcm54xx_link_change_notify, |
1278 | .led_brightness_set = bcm_phy_led_brightness_set, |
1279 | }, { |
1280 | .phy_id = PHY_ID_BCMAC131, |
1281 | .phy_id_mask = 0xfffffff0, |
1282 | .name = "Broadcom BCMAC131" , |
1283 | /* PHY_BASIC_FEATURES */ |
1284 | .config_init = brcm_fet_config_init, |
1285 | .config_intr = brcm_fet_config_intr, |
1286 | .handle_interrupt = brcm_fet_handle_interrupt, |
1287 | .suspend = brcm_fet_suspend, |
1288 | .resume = brcm_fet_config_init, |
1289 | }, { |
1290 | .phy_id = PHY_ID_BCM5241, |
1291 | .phy_id_mask = 0xfffffff0, |
1292 | .name = "Broadcom BCM5241" , |
1293 | /* PHY_BASIC_FEATURES */ |
1294 | .config_init = brcm_fet_config_init, |
1295 | .config_intr = brcm_fet_config_intr, |
1296 | .handle_interrupt = brcm_fet_handle_interrupt, |
1297 | .suspend = brcm_fet_suspend, |
1298 | .resume = brcm_fet_config_init, |
1299 | }, { |
1300 | .phy_id = PHY_ID_BCM5221, |
1301 | .phy_id_mask = 0xfffffff0, |
1302 | .name = "Broadcom BCM5221" , |
1303 | /* PHY_BASIC_FEATURES */ |
1304 | .config_init = brcm_fet_config_init, |
1305 | .config_intr = brcm_fet_config_intr, |
1306 | .handle_interrupt = brcm_fet_handle_interrupt, |
1307 | .suspend = brcm_fet_suspend, |
1308 | .resume = brcm_fet_config_init, |
1309 | .config_aneg = bcm5221_config_aneg, |
1310 | .read_status = bcm5221_read_status, |
1311 | }, { |
1312 | .phy_id = PHY_ID_BCM5395, |
1313 | .phy_id_mask = 0xfffffff0, |
1314 | .name = "Broadcom BCM5395" , |
1315 | .flags = PHY_IS_INTERNAL, |
1316 | /* PHY_GBIT_FEATURES */ |
1317 | .get_sset_count = bcm_phy_get_sset_count, |
1318 | .get_strings = bcm_phy_get_strings, |
1319 | .get_stats = bcm54xx_get_stats, |
1320 | .probe = bcm54xx_phy_probe, |
1321 | .link_change_notify = bcm54xx_link_change_notify, |
1322 | .led_brightness_set = bcm_phy_led_brightness_set, |
1323 | }, { |
1324 | .phy_id = PHY_ID_BCM53125, |
1325 | .phy_id_mask = 0xfffffff0, |
1326 | .name = "Broadcom BCM53125" , |
1327 | .flags = PHY_IS_INTERNAL, |
1328 | /* PHY_GBIT_FEATURES */ |
1329 | .get_sset_count = bcm_phy_get_sset_count, |
1330 | .get_strings = bcm_phy_get_strings, |
1331 | .get_stats = bcm54xx_get_stats, |
1332 | .probe = bcm54xx_phy_probe, |
1333 | .config_init = bcm54xx_config_init, |
1334 | .config_intr = bcm_phy_config_intr, |
1335 | .handle_interrupt = bcm_phy_handle_interrupt, |
1336 | .link_change_notify = bcm54xx_link_change_notify, |
1337 | .led_brightness_set = bcm_phy_led_brightness_set, |
1338 | }, { |
1339 | .phy_id = PHY_ID_BCM53128, |
1340 | .phy_id_mask = 0xfffffff0, |
1341 | .name = "Broadcom BCM53128" , |
1342 | .flags = PHY_IS_INTERNAL, |
1343 | /* PHY_GBIT_FEATURES */ |
1344 | .get_sset_count = bcm_phy_get_sset_count, |
1345 | .get_strings = bcm_phy_get_strings, |
1346 | .get_stats = bcm54xx_get_stats, |
1347 | .probe = bcm54xx_phy_probe, |
1348 | .config_init = bcm54xx_config_init, |
1349 | .config_intr = bcm_phy_config_intr, |
1350 | .handle_interrupt = bcm_phy_handle_interrupt, |
1351 | .link_change_notify = bcm54xx_link_change_notify, |
1352 | .led_brightness_set = bcm_phy_led_brightness_set, |
1353 | }, { |
1354 | .phy_id = PHY_ID_BCM89610, |
1355 | .phy_id_mask = 0xfffffff0, |
1356 | .name = "Broadcom BCM89610" , |
1357 | /* PHY_GBIT_FEATURES */ |
1358 | .get_sset_count = bcm_phy_get_sset_count, |
1359 | .get_strings = bcm_phy_get_strings, |
1360 | .get_stats = bcm54xx_get_stats, |
1361 | .probe = bcm54xx_phy_probe, |
1362 | .config_init = bcm54xx_config_init, |
1363 | .config_intr = bcm_phy_config_intr, |
1364 | .handle_interrupt = bcm_phy_handle_interrupt, |
1365 | .link_change_notify = bcm54xx_link_change_notify, |
1366 | } }; |
1367 | |
1368 | module_phy_driver(broadcom_drivers); |
1369 | |
1370 | static struct mdio_device_id __maybe_unused broadcom_tbl[] = { |
1371 | { PHY_ID_BCM5411, 0xfffffff0 }, |
1372 | { PHY_ID_BCM5421, 0xfffffff0 }, |
1373 | { PHY_ID_BCM54210E, 0xfffffff0 }, |
1374 | { PHY_ID_BCM5461, 0xfffffff0 }, |
1375 | { PHY_ID_BCM54612E, 0xfffffff0 }, |
1376 | { PHY_ID_BCM54616S, 0xfffffff0 }, |
1377 | { PHY_ID_BCM5464, 0xfffffff0 }, |
1378 | { PHY_ID_BCM5481, 0xfffffff0 }, |
1379 | { PHY_ID_BCM54810, 0xfffffff0 }, |
1380 | { PHY_ID_BCM54811, 0xfffffff0 }, |
1381 | { PHY_ID_BCM5482, 0xfffffff0 }, |
1382 | { PHY_ID_BCM50610, 0xfffffff0 }, |
1383 | { PHY_ID_BCM50610M, 0xfffffff0 }, |
1384 | { PHY_ID_BCM57780, 0xfffffff0 }, |
1385 | { PHY_ID_BCMAC131, 0xfffffff0 }, |
1386 | { PHY_ID_BCM5221, 0xfffffff0 }, |
1387 | { PHY_ID_BCM5241, 0xfffffff0 }, |
1388 | { PHY_ID_BCM5395, 0xfffffff0 }, |
1389 | { PHY_ID_BCM53125, 0xfffffff0 }, |
1390 | { PHY_ID_BCM53128, 0xfffffff0 }, |
1391 | { PHY_ID_BCM89610, 0xfffffff0 }, |
1392 | { } |
1393 | }; |
1394 | |
1395 | MODULE_DEVICE_TABLE(mdio, broadcom_tbl); |
1396 | |