blob: 644861366d5442c7aaca5c29c7801ae73a70e61e [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0+
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
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.
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000011 */
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
David Brazdil0f672f62019-12-10 10:32:29 +000029static int bcm54xx_config_clock_delay(struct phy_device *phydev)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000030{
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
Olivier Deprez157378f2022-04-04 15:47:50 +020070static 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000164/* 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 &&
Olivier Deprez157378f2022-04-04 15:47:50 +0200253 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
254 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
255 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000256 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) {
Olivier Deprez157378f2022-04-04 15:47:50 +0200274 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
275 /* Here, bit 0 _enables_ CLK125 when set */
276 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
277 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000278 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
Olivier Deprez157378f2022-04-04 15:47:50 +0200287 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 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000294
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
Olivier Deprez157378f2022-04-04 15:47:50 +0200340 bcm54xx_adjust_rxrefclk(phydev);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000341
Olivier Deprez157378f2022-04-04 15:47:50 +0200342 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:
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000348 err = bcm54210e_config_init(phydev);
Olivier Deprez157378f2022-04-04 15:47:50 +0200349 break;
350 case PHY_ID_BCM54612E:
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000351 err = bcm54612e_config_init(phydev);
Olivier Deprez157378f2022-04-04 15:47:50 +0200352 break;
353 case PHY_ID_BCM54616S:
354 err = bcm54616s_config_init(phydev);
355 break;
356 case PHY_ID_BCM54810:
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000357 /* 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);
Olivier Deprez157378f2022-04-04 15:47:50 +0200364 break;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000365 }
Olivier Deprez157378f2022-04-04 15:47:50 +0200366 if (err)
367 return err;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000368
369 bcm54xx_phydsp_config(phydev);
370
David Brazdil0f672f62019-12-10 10:32:29 +0000371 /* 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000384 return 0;
385}
386
Olivier Deprez157378f2022-04-04 15:47:50 +0200387static 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000432static 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 */
Olivier Deprez157378f2022-04-04 15:47:50 +0200476 reg = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
477 bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE,
478 reg | BCM54XX_SHD_MODE_1000BX);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000479
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
Olivier Deprez157378f2022-04-04 15:47:50 +0200527 /* Aneg firstly. */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000528 ret = genphy_config_aneg(phydev);
529
530 /* Then we can set up the delay. */
David Brazdil0f672f62019-12-10 10:32:29 +0000531 bcm54xx_config_clock_delay(phydev);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000532
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
Olivier Deprez157378f2022-04-04 15:47:50 +0200544static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000577static int bcm54616s_config_aneg(struct phy_device *phydev)
578{
579 int ret;
580
Olivier Deprez157378f2022-04-04 15:47:50 +0200581 /* 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);
David Brazdil0f672f62019-12-10 10:32:29 +0000586
587 /* Then we can set up the delay. */
588 bcm54xx_config_clock_delay(phydev);
589
590 return ret;
591}
592
Olivier Deprez157378f2022-04-04 15:47:50 +0200593static 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000605static 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 reg &= ~MII_BRCM_FET_IR_MASK;
708 else
709 reg |= MII_BRCM_FET_IR_MASK;
710
711 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
712 return err;
713}
714
715struct bcm53xx_phy_priv {
716 u64 *stats;
717};
718
719static int bcm53xx_phy_probe(struct phy_device *phydev)
720{
721 struct bcm53xx_phy_priv *priv;
722
723 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
724 if (!priv)
725 return -ENOMEM;
726
727 phydev->priv = priv;
728
729 priv->stats = devm_kcalloc(&phydev->mdio.dev,
730 bcm_phy_get_sset_count(phydev), sizeof(u64),
731 GFP_KERNEL);
732 if (!priv->stats)
733 return -ENOMEM;
734
735 return 0;
736}
737
738static void bcm53xx_phy_get_stats(struct phy_device *phydev,
739 struct ethtool_stats *stats, u64 *data)
740{
741 struct bcm53xx_phy_priv *priv = phydev->priv;
742
743 bcm_phy_get_stats(phydev, priv->stats, stats, data);
744}
745
746static struct phy_driver broadcom_drivers[] = {
747{
748 .phy_id = PHY_ID_BCM5411,
749 .phy_id_mask = 0xfffffff0,
750 .name = "Broadcom BCM5411",
David Brazdil0f672f62019-12-10 10:32:29 +0000751 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000752 .config_init = bcm54xx_config_init,
753 .ack_interrupt = bcm_phy_ack_intr,
754 .config_intr = bcm_phy_config_intr,
755}, {
756 .phy_id = PHY_ID_BCM5421,
757 .phy_id_mask = 0xfffffff0,
758 .name = "Broadcom BCM5421",
David Brazdil0f672f62019-12-10 10:32:29 +0000759 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000760 .config_init = bcm54xx_config_init,
761 .ack_interrupt = bcm_phy_ack_intr,
762 .config_intr = bcm_phy_config_intr,
763}, {
764 .phy_id = PHY_ID_BCM54210E,
765 .phy_id_mask = 0xfffffff0,
766 .name = "Broadcom BCM54210E",
David Brazdil0f672f62019-12-10 10:32:29 +0000767 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000768 .config_init = bcm54xx_config_init,
769 .ack_interrupt = bcm_phy_ack_intr,
770 .config_intr = bcm_phy_config_intr,
771}, {
772 .phy_id = PHY_ID_BCM5461,
773 .phy_id_mask = 0xfffffff0,
774 .name = "Broadcom BCM5461",
David Brazdil0f672f62019-12-10 10:32:29 +0000775 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000776 .config_init = bcm54xx_config_init,
777 .ack_interrupt = bcm_phy_ack_intr,
778 .config_intr = bcm_phy_config_intr,
779}, {
780 .phy_id = PHY_ID_BCM54612E,
781 .phy_id_mask = 0xfffffff0,
782 .name = "Broadcom BCM54612E",
David Brazdil0f672f62019-12-10 10:32:29 +0000783 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000784 .config_init = bcm54xx_config_init,
785 .ack_interrupt = bcm_phy_ack_intr,
786 .config_intr = bcm_phy_config_intr,
787}, {
788 .phy_id = PHY_ID_BCM54616S,
789 .phy_id_mask = 0xfffffff0,
790 .name = "Broadcom BCM54616S",
David Brazdil0f672f62019-12-10 10:32:29 +0000791 /* PHY_GBIT_FEATURES */
Olivier Deprez157378f2022-04-04 15:47:50 +0200792 .soft_reset = genphy_soft_reset,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000793 .config_init = bcm54xx_config_init,
David Brazdil0f672f62019-12-10 10:32:29 +0000794 .config_aneg = bcm54616s_config_aneg,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000795 .ack_interrupt = bcm_phy_ack_intr,
796 .config_intr = bcm_phy_config_intr,
Olivier Deprez157378f2022-04-04 15:47:50 +0200797 .read_status = bcm54616s_read_status,
798 .probe = bcm54616s_probe,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000799}, {
800 .phy_id = PHY_ID_BCM5464,
801 .phy_id_mask = 0xfffffff0,
802 .name = "Broadcom BCM5464",
David Brazdil0f672f62019-12-10 10:32:29 +0000803 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000804 .config_init = bcm54xx_config_init,
805 .ack_interrupt = bcm_phy_ack_intr,
806 .config_intr = bcm_phy_config_intr,
David Brazdil0f672f62019-12-10 10:32:29 +0000807 .suspend = genphy_suspend,
808 .resume = genphy_resume,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000809}, {
810 .phy_id = PHY_ID_BCM5481,
811 .phy_id_mask = 0xfffffff0,
812 .name = "Broadcom BCM5481",
David Brazdil0f672f62019-12-10 10:32:29 +0000813 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000814 .config_init = bcm54xx_config_init,
815 .config_aneg = bcm5481_config_aneg,
816 .ack_interrupt = bcm_phy_ack_intr,
817 .config_intr = bcm_phy_config_intr,
818}, {
819 .phy_id = PHY_ID_BCM54810,
820 .phy_id_mask = 0xfffffff0,
821 .name = "Broadcom BCM54810",
David Brazdil0f672f62019-12-10 10:32:29 +0000822 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000823 .config_init = bcm54xx_config_init,
824 .config_aneg = bcm5481_config_aneg,
825 .ack_interrupt = bcm_phy_ack_intr,
826 .config_intr = bcm_phy_config_intr,
Olivier Deprez157378f2022-04-04 15:47:50 +0200827 .suspend = genphy_suspend,
828 .resume = bcm54xx_resume,
829}, {
830 .phy_id = PHY_ID_BCM54811,
831 .phy_id_mask = 0xfffffff0,
832 .name = "Broadcom BCM54811",
833 /* PHY_GBIT_FEATURES */
834 .config_init = bcm54811_config_init,
835 .config_aneg = bcm5481_config_aneg,
836 .ack_interrupt = bcm_phy_ack_intr,
837 .config_intr = bcm_phy_config_intr,
838 .suspend = genphy_suspend,
839 .resume = bcm54xx_resume,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000840}, {
841 .phy_id = PHY_ID_BCM5482,
842 .phy_id_mask = 0xfffffff0,
843 .name = "Broadcom BCM5482",
David Brazdil0f672f62019-12-10 10:32:29 +0000844 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000845 .config_init = bcm5482_config_init,
846 .read_status = bcm5482_read_status,
847 .ack_interrupt = bcm_phy_ack_intr,
848 .config_intr = bcm_phy_config_intr,
849}, {
850 .phy_id = PHY_ID_BCM50610,
851 .phy_id_mask = 0xfffffff0,
852 .name = "Broadcom BCM50610",
David Brazdil0f672f62019-12-10 10:32:29 +0000853 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000854 .config_init = bcm54xx_config_init,
855 .ack_interrupt = bcm_phy_ack_intr,
856 .config_intr = bcm_phy_config_intr,
857}, {
858 .phy_id = PHY_ID_BCM50610M,
859 .phy_id_mask = 0xfffffff0,
860 .name = "Broadcom BCM50610M",
David Brazdil0f672f62019-12-10 10:32:29 +0000861 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000862 .config_init = bcm54xx_config_init,
863 .ack_interrupt = bcm_phy_ack_intr,
864 .config_intr = bcm_phy_config_intr,
865}, {
866 .phy_id = PHY_ID_BCM57780,
867 .phy_id_mask = 0xfffffff0,
868 .name = "Broadcom BCM57780",
David Brazdil0f672f62019-12-10 10:32:29 +0000869 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000870 .config_init = bcm54xx_config_init,
871 .ack_interrupt = bcm_phy_ack_intr,
872 .config_intr = bcm_phy_config_intr,
873}, {
874 .phy_id = PHY_ID_BCMAC131,
875 .phy_id_mask = 0xfffffff0,
876 .name = "Broadcom BCMAC131",
David Brazdil0f672f62019-12-10 10:32:29 +0000877 /* PHY_BASIC_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000878 .config_init = brcm_fet_config_init,
879 .ack_interrupt = brcm_fet_ack_interrupt,
880 .config_intr = brcm_fet_config_intr,
881}, {
882 .phy_id = PHY_ID_BCM5241,
883 .phy_id_mask = 0xfffffff0,
884 .name = "Broadcom BCM5241",
David Brazdil0f672f62019-12-10 10:32:29 +0000885 /* PHY_BASIC_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000886 .config_init = brcm_fet_config_init,
887 .ack_interrupt = brcm_fet_ack_interrupt,
888 .config_intr = brcm_fet_config_intr,
889}, {
890 .phy_id = PHY_ID_BCM5395,
891 .phy_id_mask = 0xfffffff0,
892 .name = "Broadcom BCM5395",
893 .flags = PHY_IS_INTERNAL,
David Brazdil0f672f62019-12-10 10:32:29 +0000894 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000895 .get_sset_count = bcm_phy_get_sset_count,
896 .get_strings = bcm_phy_get_strings,
897 .get_stats = bcm53xx_phy_get_stats,
898 .probe = bcm53xx_phy_probe,
899}, {
Olivier Deprez157378f2022-04-04 15:47:50 +0200900 .phy_id = PHY_ID_BCM53125,
901 .phy_id_mask = 0xfffffff0,
902 .name = "Broadcom BCM53125",
903 .flags = PHY_IS_INTERNAL,
904 /* PHY_GBIT_FEATURES */
905 .get_sset_count = bcm_phy_get_sset_count,
906 .get_strings = bcm_phy_get_strings,
907 .get_stats = bcm53xx_phy_get_stats,
908 .probe = bcm53xx_phy_probe,
909 .config_init = bcm54xx_config_init,
910 .ack_interrupt = bcm_phy_ack_intr,
911 .config_intr = bcm_phy_config_intr,
912}, {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000913 .phy_id = PHY_ID_BCM89610,
914 .phy_id_mask = 0xfffffff0,
915 .name = "Broadcom BCM89610",
David Brazdil0f672f62019-12-10 10:32:29 +0000916 /* PHY_GBIT_FEATURES */
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000917 .config_init = bcm54xx_config_init,
918 .ack_interrupt = bcm_phy_ack_intr,
919 .config_intr = bcm_phy_config_intr,
920} };
921
922module_phy_driver(broadcom_drivers);
923
924static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
925 { PHY_ID_BCM5411, 0xfffffff0 },
926 { PHY_ID_BCM5421, 0xfffffff0 },
927 { PHY_ID_BCM54210E, 0xfffffff0 },
928 { PHY_ID_BCM5461, 0xfffffff0 },
929 { PHY_ID_BCM54612E, 0xfffffff0 },
930 { PHY_ID_BCM54616S, 0xfffffff0 },
931 { PHY_ID_BCM5464, 0xfffffff0 },
932 { PHY_ID_BCM5481, 0xfffffff0 },
933 { PHY_ID_BCM54810, 0xfffffff0 },
Olivier Deprez157378f2022-04-04 15:47:50 +0200934 { PHY_ID_BCM54811, 0xfffffff0 },
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000935 { PHY_ID_BCM5482, 0xfffffff0 },
936 { PHY_ID_BCM50610, 0xfffffff0 },
937 { PHY_ID_BCM50610M, 0xfffffff0 },
938 { PHY_ID_BCM57780, 0xfffffff0 },
939 { PHY_ID_BCMAC131, 0xfffffff0 },
940 { PHY_ID_BCM5241, 0xfffffff0 },
941 { PHY_ID_BCM5395, 0xfffffff0 },
Olivier Deprez157378f2022-04-04 15:47:50 +0200942 { PHY_ID_BCM53125, 0xfffffff0 },
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000943 { PHY_ID_BCM89610, 0xfffffff0 },
944 { }
945};
946
947MODULE_DEVICE_TABLE(mdio, broadcom_tbl);