]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame_incremental - drivers/net/phy/broadcom.c
UBUNTU: Ubuntu-5.11.0-22.23
[mirror_ubuntu-hirsute-kernel.git] / drivers / net / phy / broadcom.c
... / ...
CommitLineData
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/module.h>
15#include <linux/phy.h>
16#include <linux/brcmphy.h>
17#include <linux/of.h>
18
19#define BRCM_PHY_MODEL(phydev) \
20 ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
21
22#define BRCM_PHY_REV(phydev) \
23 ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
24
25MODULE_DESCRIPTION("Broadcom PHY driver");
26MODULE_AUTHOR("Maciej W. Rozycki");
27MODULE_LICENSE("GPL");
28
29static int bcm54xx_config_clock_delay(struct phy_device *phydev)
30{
31 int rc, val;
32
33 /* handling PHY's internal RX clock delay */
34 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
35 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
36 if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
37 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
38 /* Disable RGMII RXC-RXD skew */
39 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
40 }
41 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
42 phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
43 /* Enable RGMII RXC-RXD skew */
44 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
45 }
46 rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
47 val);
48 if (rc < 0)
49 return rc;
50
51 /* handling PHY's internal TX clock delay */
52 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
53 if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
54 phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
55 /* Disable internal TX clock delay */
56 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
57 }
58 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
59 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
60 /* Enable internal TX clock delay */
61 val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
62 }
63 rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
64 if (rc < 0)
65 return rc;
66
67 return 0;
68}
69
70static int bcm54210e_config_init(struct phy_device *phydev)
71{
72 int val;
73
74 bcm54xx_config_clock_delay(phydev);
75
76 if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
77 val = phy_read(phydev, MII_CTRL1000);
78 val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
79 phy_write(phydev, MII_CTRL1000, val);
80 }
81
82 return 0;
83}
84
85static int bcm54612e_config_init(struct phy_device *phydev)
86{
87 int reg;
88
89 bcm54xx_config_clock_delay(phydev);
90
91 /* Enable CLK125 MUX on LED4 if ref clock is enabled. */
92 if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
93 int err;
94
95 reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
96 err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
97 BCM54612E_LED4_CLK125OUT_EN | reg);
98
99 if (err < 0)
100 return err;
101 }
102
103 return 0;
104}
105
106static int bcm54616s_config_init(struct phy_device *phydev)
107{
108 int rc, val;
109
110 if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
111 phydev->interface != PHY_INTERFACE_MODE_1000BASEX)
112 return 0;
113
114 /* Ensure proper interface mode is selected. */
115 /* Disable RGMII mode */
116 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
117 if (val < 0)
118 return val;
119 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN;
120 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
121 rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
122 val);
123 if (rc < 0)
124 return rc;
125
126 /* Select 1000BASE-X register set (primary SerDes) */
127 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
128 if (val < 0)
129 return val;
130 val |= BCM54XX_SHD_MODE_1000BX;
131 rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
132 if (rc < 0)
133 return rc;
134
135 /* Power down SerDes interface */
136 rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
137 if (rc < 0)
138 return rc;
139
140 /* Select proper interface mode */
141 val &= ~BCM54XX_SHD_INTF_SEL_MASK;
142 val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ?
143 BCM54XX_SHD_INTF_SEL_SGMII :
144 BCM54XX_SHD_INTF_SEL_GBIC;
145 rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
146 if (rc < 0)
147 return rc;
148
149 /* Power up SerDes interface */
150 rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
151 if (rc < 0)
152 return rc;
153
154 /* Select copper register set */
155 val &= ~BCM54XX_SHD_MODE_1000BX;
156 rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
157 if (rc < 0)
158 return rc;
159
160 /* Power up copper interface */
161 return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
162}
163
164/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
165static int bcm50610_a0_workaround(struct phy_device *phydev)
166{
167 int err;
168
169 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
170 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
171 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
172 if (err < 0)
173 return err;
174
175 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
176 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
177 if (err < 0)
178 return err;
179
180 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
181 MII_BCM54XX_EXP_EXP75_VDACCTRL);
182 if (err < 0)
183 return err;
184
185 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
186 MII_BCM54XX_EXP_EXP96_MYST);
187 if (err < 0)
188 return err;
189
190 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
191 MII_BCM54XX_EXP_EXP97_MYST);
192
193 return err;
194}
195
196static int bcm54xx_phydsp_config(struct phy_device *phydev)
197{
198 int err, err2;
199
200 /* Enable the SMDSP clock */
201 err = bcm54xx_auxctl_write(phydev,
202 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
203 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
204 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
205 if (err < 0)
206 return err;
207
208 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
209 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
210 /* Clear bit 9 to fix a phy interop issue. */
211 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
212 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
213 if (err < 0)
214 goto error;
215
216 if (phydev->drv->phy_id == PHY_ID_BCM50610) {
217 err = bcm50610_a0_workaround(phydev);
218 if (err < 0)
219 goto error;
220 }
221 }
222
223 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
224 int val;
225
226 val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
227 if (val < 0)
228 goto error;
229
230 val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
231 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
232 }
233
234error:
235 /* Disable the SMDSP clock */
236 err2 = bcm54xx_auxctl_write(phydev,
237 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
238 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
239
240 /* Return the first error reported. */
241 return err ? err : err2;
242}
243
244static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
245{
246 u32 orig;
247 int val;
248 bool clk125en = true;
249
250 /* Abort if we are using an untested phy. */
251 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
252 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
253 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
254 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
255 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
256 return;
257
258 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
259 if (val < 0)
260 return;
261
262 orig = val;
263
264 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
265 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
266 BRCM_PHY_REV(phydev) >= 0x3) {
267 /*
268 * Here, bit 0 _disables_ CLK125 when set.
269 * This bit is set by default.
270 */
271 clk125en = false;
272 } else {
273 if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
274 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
275 /* Here, bit 0 _enables_ CLK125 when set */
276 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
277 }
278 clk125en = false;
279 }
280 }
281
282 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
283 val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
284 else
285 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
286
287 if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
288 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
289 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
290 val |= BCM54810_SHD_SCR3_TRDDAPD;
291 else
292 val |= BCM54XX_SHD_SCR3_TRDDAPD;
293 }
294
295 if (orig != val)
296 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
297
298 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
299 if (val < 0)
300 return;
301
302 orig = val;
303
304 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
305 val |= BCM54XX_SHD_APD_EN;
306 else
307 val &= ~BCM54XX_SHD_APD_EN;
308
309 if (orig != val)
310 bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
311}
312
313static int bcm54xx_config_init(struct phy_device *phydev)
314{
315 int reg, err, val;
316
317 reg = phy_read(phydev, MII_BCM54XX_ECR);
318 if (reg < 0)
319 return reg;
320
321 /* Mask interrupts globally. */
322 reg |= MII_BCM54XX_ECR_IM;
323 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
324 if (err < 0)
325 return err;
326
327 /* Unmask events we are interested in. */
328 reg = ~(MII_BCM54XX_INT_DUPLEX |
329 MII_BCM54XX_INT_SPEED |
330 MII_BCM54XX_INT_LINK);
331 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
332 if (err < 0)
333 return err;
334
335 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
336 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
337 (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
338 bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
339
340 bcm54xx_adjust_rxrefclk(phydev);
341
342 switch (BRCM_PHY_MODEL(phydev)) {
343 case PHY_ID_BCM50610:
344 case PHY_ID_BCM50610M:
345 err = bcm54xx_config_clock_delay(phydev);
346 break;
347 case PHY_ID_BCM54210E:
348 err = bcm54210e_config_init(phydev);
349 break;
350 case PHY_ID_BCM54612E:
351 err = bcm54612e_config_init(phydev);
352 break;
353 case PHY_ID_BCM54616S:
354 err = bcm54616s_config_init(phydev);
355 break;
356 case PHY_ID_BCM54810:
357 /* For BCM54810, we need to disable BroadR-Reach function */
358 val = bcm_phy_read_exp(phydev,
359 BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
360 val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
361 err = bcm_phy_write_exp(phydev,
362 BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
363 val);
364 break;
365 }
366 if (err)
367 return err;
368
369 bcm54xx_phydsp_config(phydev);
370
371 /* Encode link speed into LED1 and LED3 pair (green/amber).
372 * Also flash these two LEDs on activity. This means configuring
373 * them for MULTICOLOR and encoding link/activity into them.
374 */
375 val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
376 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
377 bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val);
378
379 val = BCM_LED_MULTICOLOR_IN_PHASE |
380 BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
381 BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
382 bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
383
384 return 0;
385}
386
387static int bcm54xx_resume(struct phy_device *phydev)
388{
389 int ret;
390
391 /* Writes to register other than BMCR would be ignored
392 * unless we clear the PDOWN bit first
393 */
394 ret = genphy_resume(phydev);
395 if (ret < 0)
396 return ret;
397
398 /* Upon exiting power down, the PHY remains in an internal reset state
399 * for 40us
400 */
401 fsleep(40);
402
403 return bcm54xx_config_init(phydev);
404}
405
406static int bcm54811_config_init(struct phy_device *phydev)
407{
408 int err, reg;
409
410 /* Disable BroadR-Reach function. */
411 reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
412 reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
413 err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
414 reg);
415 if (err < 0)
416 return err;
417
418 err = bcm54xx_config_init(phydev);
419
420 /* Enable CLK125 MUX on LED4 if ref clock is enabled. */
421 if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
422 reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
423 err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
424 BCM54612E_LED4_CLK125OUT_EN | reg);
425 if (err < 0)
426 return err;
427 }
428
429 return err;
430}
431
432static int bcm5482_config_init(struct phy_device *phydev)
433{
434 int err, reg;
435
436 err = bcm54xx_config_init(phydev);
437
438 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
439 /*
440 * Enable secondary SerDes and its use as an LED source
441 */
442 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
443 bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
444 reg |
445 BCM5482_SHD_SSD_LEDM |
446 BCM5482_SHD_SSD_EN);
447
448 /*
449 * Enable SGMII slave mode and auto-detection
450 */
451 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
452 err = bcm_phy_read_exp(phydev, reg);
453 if (err < 0)
454 return err;
455 err = bcm_phy_write_exp(phydev, reg, err |
456 BCM5482_SSD_SGMII_SLAVE_EN |
457 BCM5482_SSD_SGMII_SLAVE_AD);
458 if (err < 0)
459 return err;
460
461 /*
462 * Disable secondary SerDes powerdown
463 */
464 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
465 err = bcm_phy_read_exp(phydev, reg);
466 if (err < 0)
467 return err;
468 err = bcm_phy_write_exp(phydev, reg,
469 err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
470 if (err < 0)
471 return err;
472
473 /*
474 * Select 1000BASE-X register set (primary SerDes)
475 */
476 reg = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
477 bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE,
478 reg | BCM54XX_SHD_MODE_1000BX);
479
480 /*
481 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
482 * (Use LED1 as secondary SerDes ACTIVITY LED)
483 */
484 bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
485 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
486 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
487
488 /*
489 * Auto-negotiation doesn't seem to work quite right
490 * in this mode, so we disable it and force it to the
491 * right speed/duplex setting. Only 'link status'
492 * is important.
493 */
494 phydev->autoneg = AUTONEG_DISABLE;
495 phydev->speed = SPEED_1000;
496 phydev->duplex = DUPLEX_FULL;
497 }
498
499 return err;
500}
501
502static int bcm5482_read_status(struct phy_device *phydev)
503{
504 int err;
505
506 err = genphy_read_status(phydev);
507
508 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
509 /*
510 * Only link status matters for 1000Base-X mode, so force
511 * 1000 Mbit/s full-duplex status
512 */
513 if (phydev->link) {
514 phydev->speed = SPEED_1000;
515 phydev->duplex = DUPLEX_FULL;
516 }
517 }
518
519 return err;
520}
521
522static int bcm5481_config_aneg(struct phy_device *phydev)
523{
524 struct device_node *np = phydev->mdio.dev.of_node;
525 int ret;
526
527 /* Aneg firstly. */
528 ret = genphy_config_aneg(phydev);
529
530 /* Then we can set up the delay. */
531 bcm54xx_config_clock_delay(phydev);
532
533 if (of_property_read_bool(np, "enet-phy-lane-swap")) {
534 /* Lane Swap - Undocumented register...magic! */
535 ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
536 0x11B);
537 if (ret < 0)
538 return ret;
539 }
540
541 return ret;
542}
543
544static int bcm54616s_probe(struct phy_device *phydev)
545{
546 int val;
547
548 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
549 if (val < 0)
550 return val;
551
552 /* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0]
553 * is 01b, and the link between PHY and its link partner can be
554 * either 1000Base-X or 100Base-FX.
555 * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
556 * support is still missing as of now.
557 */
558 if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
559 val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL);
560 if (val < 0)
561 return val;
562
563 /* Bit 0 of the SerDes 100-FX Control register, when set
564 * to 1, sets the MII/RGMII -> 100BASE-FX configuration.
565 * When this bit is set to 0, it sets the GMII/RGMII ->
566 * 1000BASE-X configuration.
567 */
568 if (!(val & BCM54616S_100FX_MODE))
569 phydev->dev_flags |= PHY_BCM_FLAGS_MODE_1000BX;
570
571 phydev->port = PORT_FIBRE;
572 }
573
574 return 0;
575}
576
577static int bcm54616s_config_aneg(struct phy_device *phydev)
578{
579 int ret;
580
581 /* Aneg firstly. */
582 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX)
583 ret = genphy_c37_config_aneg(phydev);
584 else
585 ret = genphy_config_aneg(phydev);
586
587 /* Then we can set up the delay. */
588 bcm54xx_config_clock_delay(phydev);
589
590 return ret;
591}
592
593static int bcm54616s_read_status(struct phy_device *phydev)
594{
595 int err;
596
597 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX)
598 err = genphy_c37_read_status(phydev);
599 else
600 err = genphy_read_status(phydev);
601
602 return err;
603}
604
605static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
606{
607 int val;
608
609 val = phy_read(phydev, reg);
610 if (val < 0)
611 return val;
612
613 return phy_write(phydev, reg, val | set);
614}
615
616static int brcm_fet_config_init(struct phy_device *phydev)
617{
618 int reg, err, err2, brcmtest;
619
620 /* Reset the PHY to bring it to a known state. */
621 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
622 if (err < 0)
623 return err;
624
625 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
626 if (reg < 0)
627 return reg;
628
629 /* Unmask events we are interested in and mask interrupts globally. */
630 reg = MII_BRCM_FET_IR_DUPLEX_EN |
631 MII_BRCM_FET_IR_SPEED_EN |
632 MII_BRCM_FET_IR_LINK_EN |
633 MII_BRCM_FET_IR_ENABLE |
634 MII_BRCM_FET_IR_MASK;
635
636 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
637 if (err < 0)
638 return err;
639
640 /* Enable shadow register access */
641 brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
642 if (brcmtest < 0)
643 return brcmtest;
644
645 reg = brcmtest | MII_BRCM_FET_BT_SRE;
646
647 err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
648 if (err < 0)
649 return err;
650
651 /* Set the LED mode */
652 reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
653 if (reg < 0) {
654 err = reg;
655 goto done;
656 }
657
658 reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
659 reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
660
661 err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
662 if (err < 0)
663 goto done;
664
665 /* Enable auto MDIX */
666 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
667 MII_BRCM_FET_SHDW_MC_FAME);
668 if (err < 0)
669 goto done;
670
671 if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
672 /* Enable auto power down */
673 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
674 MII_BRCM_FET_SHDW_AS2_APDE);
675 }
676
677done:
678 /* Disable shadow register access */
679 err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
680 if (!err)
681 err = err2;
682
683 return err;
684}
685
686static int brcm_fet_ack_interrupt(struct phy_device *phydev)
687{
688 int reg;
689
690 /* Clear pending interrupts. */
691 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
692 if (reg < 0)
693 return reg;
694
695 return 0;
696}
697
698static int brcm_fet_config_intr(struct phy_device *phydev)
699{
700 int reg, err;
701
702 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
703 if (reg < 0)
704 return reg;
705
706 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
707 err = brcm_fet_ack_interrupt(phydev);
708 if (err)
709 return err;
710
711 reg &= ~MII_BRCM_FET_IR_MASK;
712 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
713 } else {
714 reg |= MII_BRCM_FET_IR_MASK;
715 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
716 if (err)
717 return err;
718
719 err = brcm_fet_ack_interrupt(phydev);
720 }
721
722 return err;
723}
724
725static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev)
726{
727 int irq_status;
728
729 irq_status = phy_read(phydev, MII_BRCM_FET_INTREG);
730 if (irq_status < 0) {
731 phy_error(phydev);
732 return IRQ_NONE;
733 }
734
735 if (irq_status == 0)
736 return IRQ_NONE;
737
738 phy_trigger_machine(phydev);
739
740 return IRQ_HANDLED;
741}
742
743struct bcm53xx_phy_priv {
744 u64 *stats;
745};
746
747static int bcm53xx_phy_probe(struct phy_device *phydev)
748{
749 struct bcm53xx_phy_priv *priv;
750
751 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
752 if (!priv)
753 return -ENOMEM;
754
755 phydev->priv = priv;
756
757 priv->stats = devm_kcalloc(&phydev->mdio.dev,
758 bcm_phy_get_sset_count(phydev), sizeof(u64),
759 GFP_KERNEL);
760 if (!priv->stats)
761 return -ENOMEM;
762
763 return 0;
764}
765
766static void bcm53xx_phy_get_stats(struct phy_device *phydev,
767 struct ethtool_stats *stats, u64 *data)
768{
769 struct bcm53xx_phy_priv *priv = phydev->priv;
770
771 bcm_phy_get_stats(phydev, priv->stats, stats, data);
772}
773
774static struct phy_driver broadcom_drivers[] = {
775{
776 .phy_id = PHY_ID_BCM5411,
777 .phy_id_mask = 0xfffffff0,
778 .name = "Broadcom BCM5411",
779 /* PHY_GBIT_FEATURES */
780 .config_init = bcm54xx_config_init,
781 .config_intr = bcm_phy_config_intr,
782 .handle_interrupt = bcm_phy_handle_interrupt,
783}, {
784 .phy_id = PHY_ID_BCM5421,
785 .phy_id_mask = 0xfffffff0,
786 .name = "Broadcom BCM5421",
787 /* PHY_GBIT_FEATURES */
788 .config_init = bcm54xx_config_init,
789 .config_intr = bcm_phy_config_intr,
790 .handle_interrupt = bcm_phy_handle_interrupt,
791}, {
792 .phy_id = PHY_ID_BCM54210E,
793 .phy_id_mask = 0xfffffff0,
794 .name = "Broadcom BCM54210E",
795 /* PHY_GBIT_FEATURES */
796 .config_init = bcm54xx_config_init,
797 .config_intr = bcm_phy_config_intr,
798 .handle_interrupt = bcm_phy_handle_interrupt,
799}, {
800 .phy_id = PHY_ID_BCM5461,
801 .phy_id_mask = 0xfffffff0,
802 .name = "Broadcom BCM5461",
803 /* PHY_GBIT_FEATURES */
804 .config_init = bcm54xx_config_init,
805 .config_intr = bcm_phy_config_intr,
806 .handle_interrupt = bcm_phy_handle_interrupt,
807}, {
808 .phy_id = PHY_ID_BCM54612E,
809 .phy_id_mask = 0xfffffff0,
810 .name = "Broadcom BCM54612E",
811 /* PHY_GBIT_FEATURES */
812 .config_init = bcm54xx_config_init,
813 .config_intr = bcm_phy_config_intr,
814 .handle_interrupt = bcm_phy_handle_interrupt,
815}, {
816 .phy_id = PHY_ID_BCM54616S,
817 .phy_id_mask = 0xfffffff0,
818 .name = "Broadcom BCM54616S",
819 /* PHY_GBIT_FEATURES */
820 .config_init = bcm54xx_config_init,
821 .config_aneg = bcm54616s_config_aneg,
822 .config_intr = bcm_phy_config_intr,
823 .handle_interrupt = bcm_phy_handle_interrupt,
824 .read_status = bcm54616s_read_status,
825 .probe = bcm54616s_probe,
826}, {
827 .phy_id = PHY_ID_BCM5464,
828 .phy_id_mask = 0xfffffff0,
829 .name = "Broadcom BCM5464",
830 /* PHY_GBIT_FEATURES */
831 .config_init = bcm54xx_config_init,
832 .config_intr = bcm_phy_config_intr,
833 .handle_interrupt = bcm_phy_handle_interrupt,
834 .suspend = genphy_suspend,
835 .resume = genphy_resume,
836}, {
837 .phy_id = PHY_ID_BCM5481,
838 .phy_id_mask = 0xfffffff0,
839 .name = "Broadcom BCM5481",
840 /* PHY_GBIT_FEATURES */
841 .config_init = bcm54xx_config_init,
842 .config_aneg = bcm5481_config_aneg,
843 .config_intr = bcm_phy_config_intr,
844 .handle_interrupt = bcm_phy_handle_interrupt,
845}, {
846 .phy_id = PHY_ID_BCM54810,
847 .phy_id_mask = 0xfffffff0,
848 .name = "Broadcom BCM54810",
849 /* PHY_GBIT_FEATURES */
850 .config_init = bcm54xx_config_init,
851 .config_aneg = bcm5481_config_aneg,
852 .config_intr = bcm_phy_config_intr,
853 .handle_interrupt = bcm_phy_handle_interrupt,
854 .suspend = genphy_suspend,
855 .resume = bcm54xx_resume,
856}, {
857 .phy_id = PHY_ID_BCM54811,
858 .phy_id_mask = 0xfffffff0,
859 .name = "Broadcom BCM54811",
860 /* PHY_GBIT_FEATURES */
861 .config_init = bcm54811_config_init,
862 .config_aneg = bcm5481_config_aneg,
863 .config_intr = bcm_phy_config_intr,
864 .handle_interrupt = bcm_phy_handle_interrupt,
865 .suspend = genphy_suspend,
866 .resume = bcm54xx_resume,
867}, {
868 .phy_id = PHY_ID_BCM5482,
869 .phy_id_mask = 0xfffffff0,
870 .name = "Broadcom BCM5482",
871 /* PHY_GBIT_FEATURES */
872 .config_init = bcm5482_config_init,
873 .read_status = bcm5482_read_status,
874 .config_intr = bcm_phy_config_intr,
875 .handle_interrupt = bcm_phy_handle_interrupt,
876}, {
877 .phy_id = PHY_ID_BCM50610,
878 .phy_id_mask = 0xfffffff0,
879 .name = "Broadcom BCM50610",
880 /* PHY_GBIT_FEATURES */
881 .config_init = bcm54xx_config_init,
882 .config_intr = bcm_phy_config_intr,
883 .handle_interrupt = bcm_phy_handle_interrupt,
884}, {
885 .phy_id = PHY_ID_BCM50610M,
886 .phy_id_mask = 0xfffffff0,
887 .name = "Broadcom BCM50610M",
888 /* PHY_GBIT_FEATURES */
889 .config_init = bcm54xx_config_init,
890 .config_intr = bcm_phy_config_intr,
891 .handle_interrupt = bcm_phy_handle_interrupt,
892}, {
893 .phy_id = PHY_ID_BCM57780,
894 .phy_id_mask = 0xfffffff0,
895 .name = "Broadcom BCM57780",
896 /* PHY_GBIT_FEATURES */
897 .config_init = bcm54xx_config_init,
898 .config_intr = bcm_phy_config_intr,
899 .handle_interrupt = bcm_phy_handle_interrupt,
900}, {
901 .phy_id = PHY_ID_BCMAC131,
902 .phy_id_mask = 0xfffffff0,
903 .name = "Broadcom BCMAC131",
904 /* PHY_BASIC_FEATURES */
905 .config_init = brcm_fet_config_init,
906 .config_intr = brcm_fet_config_intr,
907 .handle_interrupt = brcm_fet_handle_interrupt,
908}, {
909 .phy_id = PHY_ID_BCM5241,
910 .phy_id_mask = 0xfffffff0,
911 .name = "Broadcom BCM5241",
912 /* PHY_BASIC_FEATURES */
913 .config_init = brcm_fet_config_init,
914 .config_intr = brcm_fet_config_intr,
915 .handle_interrupt = brcm_fet_handle_interrupt,
916}, {
917 .phy_id = PHY_ID_BCM5395,
918 .phy_id_mask = 0xfffffff0,
919 .name = "Broadcom BCM5395",
920 .flags = PHY_IS_INTERNAL,
921 /* PHY_GBIT_FEATURES */
922 .get_sset_count = bcm_phy_get_sset_count,
923 .get_strings = bcm_phy_get_strings,
924 .get_stats = bcm53xx_phy_get_stats,
925 .probe = bcm53xx_phy_probe,
926}, {
927 .phy_id = PHY_ID_BCM53125,
928 .phy_id_mask = 0xfffffff0,
929 .name = "Broadcom BCM53125",
930 .flags = PHY_IS_INTERNAL,
931 /* PHY_GBIT_FEATURES */
932 .get_sset_count = bcm_phy_get_sset_count,
933 .get_strings = bcm_phy_get_strings,
934 .get_stats = bcm53xx_phy_get_stats,
935 .probe = bcm53xx_phy_probe,
936 .config_init = bcm54xx_config_init,
937 .config_intr = bcm_phy_config_intr,
938 .handle_interrupt = bcm_phy_handle_interrupt,
939}, {
940 .phy_id = PHY_ID_BCM89610,
941 .phy_id_mask = 0xfffffff0,
942 .name = "Broadcom BCM89610",
943 /* PHY_GBIT_FEATURES */
944 .config_init = bcm54xx_config_init,
945 .config_intr = bcm_phy_config_intr,
946 .handle_interrupt = bcm_phy_handle_interrupt,
947} };
948
949module_phy_driver(broadcom_drivers);
950
951static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
952 { PHY_ID_BCM5411, 0xfffffff0 },
953 { PHY_ID_BCM5421, 0xfffffff0 },
954 { PHY_ID_BCM54210E, 0xfffffff0 },
955 { PHY_ID_BCM5461, 0xfffffff0 },
956 { PHY_ID_BCM54612E, 0xfffffff0 },
957 { PHY_ID_BCM54616S, 0xfffffff0 },
958 { PHY_ID_BCM5464, 0xfffffff0 },
959 { PHY_ID_BCM5481, 0xfffffff0 },
960 { PHY_ID_BCM54810, 0xfffffff0 },
961 { PHY_ID_BCM54811, 0xfffffff0 },
962 { PHY_ID_BCM5482, 0xfffffff0 },
963 { PHY_ID_BCM50610, 0xfffffff0 },
964 { PHY_ID_BCM50610M, 0xfffffff0 },
965 { PHY_ID_BCM57780, 0xfffffff0 },
966 { PHY_ID_BCMAC131, 0xfffffff0 },
967 { PHY_ID_BCM5241, 0xfffffff0 },
968 { PHY_ID_BCM5395, 0xfffffff0 },
969 { PHY_ID_BCM53125, 0xfffffff0 },
970 { PHY_ID_BCM89610, 0xfffffff0 },
971 { }
972};
973
974MODULE_DEVICE_TABLE(mdio, broadcom_tbl);