2 * drivers/net/phy/broadcom.c
4 * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
7 * Copyright (c) 2006 Maciej W. Rozycki
9 * Inspired by code written by Amy Fong.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version.
17 #include "bcm-phy-lib.h"
18 #include <linux/module.h>
19 #include <linux/phy.h>
20 #include <linux/brcmphy.h>
23 #define BRCM_PHY_MODEL(phydev) \
24 ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
26 #define BRCM_PHY_REV(phydev) \
27 ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
29 MODULE_DESCRIPTION("Broadcom PHY driver");
30 MODULE_AUTHOR("Maciej W. Rozycki");
31 MODULE_LICENSE("GPL");
33 static int bcm54xx_auxctl_write(struct phy_device
*phydev
, u16 regnum
, u16 val
)
35 return phy_write(phydev
, MII_BCM54XX_AUX_CTL
, regnum
| val
);
38 /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
39 static int bcm50610_a0_workaround(struct phy_device
*phydev
)
43 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_AADJ1CH0
,
44 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN
|
45 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF
);
49 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_AADJ1CH3
,
50 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ
);
54 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_EXP75
,
55 MII_BCM54XX_EXP_EXP75_VDACCTRL
);
59 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_EXP96
,
60 MII_BCM54XX_EXP_EXP96_MYST
);
64 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_EXP97
,
65 MII_BCM54XX_EXP_EXP97_MYST
);
70 static int bcm54xx_phydsp_config(struct phy_device
*phydev
)
74 /* Enable the SMDSP clock */
75 err
= bcm54xx_auxctl_write(phydev
,
76 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL
,
77 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA
|
78 MII_BCM54XX_AUXCTL_ACTL_TX_6DB
);
82 if (BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610
||
83 BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610M
) {
84 /* Clear bit 9 to fix a phy interop issue. */
85 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_EXP08
,
86 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ
);
90 if (phydev
->drv
->phy_id
== PHY_ID_BCM50610
) {
91 err
= bcm50610_a0_workaround(phydev
);
97 if (BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM57780
) {
100 val
= bcm_phy_read_exp(phydev
, MII_BCM54XX_EXP_EXP75
);
104 val
|= MII_BCM54XX_EXP_EXP75_CM_OSC
;
105 err
= bcm_phy_write_exp(phydev
, MII_BCM54XX_EXP_EXP75
, val
);
109 /* Disable the SMDSP clock */
110 err2
= bcm54xx_auxctl_write(phydev
,
111 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL
,
112 MII_BCM54XX_AUXCTL_ACTL_TX_6DB
);
114 /* Return the first error reported. */
115 return err
? err
: err2
;
118 static void bcm54xx_adjust_rxrefclk(struct phy_device
*phydev
)
122 bool clk125en
= true;
124 /* Abort if we are using an untested phy. */
125 if (BRCM_PHY_MODEL(phydev
) != PHY_ID_BCM57780
&&
126 BRCM_PHY_MODEL(phydev
) != PHY_ID_BCM50610
&&
127 BRCM_PHY_MODEL(phydev
) != PHY_ID_BCM50610M
)
130 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR3
);
136 if ((BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610
||
137 BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610M
) &&
138 BRCM_PHY_REV(phydev
) >= 0x3) {
140 * Here, bit 0 _disables_ CLK125 when set.
141 * This bit is set by default.
145 if (phydev
->dev_flags
& PHY_BRCM_RX_REFCLK_UNUSED
) {
146 /* Here, bit 0 _enables_ CLK125 when set */
147 val
&= ~BCM54XX_SHD_SCR3_DEF_CLK125
;
152 if (!clk125en
|| (phydev
->dev_flags
& PHY_BRCM_AUTO_PWRDWN_ENABLE
))
153 val
&= ~BCM54XX_SHD_SCR3_DLLAPD_DIS
;
155 val
|= BCM54XX_SHD_SCR3_DLLAPD_DIS
;
157 if (phydev
->dev_flags
& PHY_BRCM_DIS_TXCRXC_NOENRGY
)
158 val
|= BCM54XX_SHD_SCR3_TRDDAPD
;
161 bcm_phy_write_shadow(phydev
, BCM54XX_SHD_SCR3
, val
);
163 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_APD
);
169 if (!clk125en
|| (phydev
->dev_flags
& PHY_BRCM_AUTO_PWRDWN_ENABLE
))
170 val
|= BCM54XX_SHD_APD_EN
;
172 val
&= ~BCM54XX_SHD_APD_EN
;
175 bcm_phy_write_shadow(phydev
, BCM54XX_SHD_APD
, val
);
178 static int bcm54xx_config_init(struct phy_device
*phydev
)
182 reg
= phy_read(phydev
, MII_BCM54XX_ECR
);
186 /* Mask interrupts globally. */
187 reg
|= MII_BCM54XX_ECR_IM
;
188 err
= phy_write(phydev
, MII_BCM54XX_ECR
, reg
);
192 /* Unmask events we are interested in. */
193 reg
= ~(MII_BCM54XX_INT_DUPLEX
|
194 MII_BCM54XX_INT_SPEED
|
195 MII_BCM54XX_INT_LINK
);
196 err
= phy_write(phydev
, MII_BCM54XX_IMR
, reg
);
200 if ((BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610
||
201 BRCM_PHY_MODEL(phydev
) == PHY_ID_BCM50610M
) &&
202 (phydev
->dev_flags
& PHY_BRCM_CLEAR_RGMII_MODE
))
203 bcm_phy_write_shadow(phydev
, BCM54XX_SHD_RGMII_MODE
, 0);
205 if ((phydev
->dev_flags
& PHY_BRCM_RX_REFCLK_UNUSED
) ||
206 (phydev
->dev_flags
& PHY_BRCM_DIS_TXCRXC_NOENRGY
) ||
207 (phydev
->dev_flags
& PHY_BRCM_AUTO_PWRDWN_ENABLE
))
208 bcm54xx_adjust_rxrefclk(phydev
);
210 bcm54xx_phydsp_config(phydev
);
215 static int bcm5482_config_init(struct phy_device
*phydev
)
219 err
= bcm54xx_config_init(phydev
);
221 if (phydev
->dev_flags
& PHY_BCM_FLAGS_MODE_1000BX
) {
223 * Enable secondary SerDes and its use as an LED source
225 reg
= bcm_phy_read_shadow(phydev
, BCM5482_SHD_SSD
);
226 bcm_phy_write_shadow(phydev
, BCM5482_SHD_SSD
,
228 BCM5482_SHD_SSD_LEDM
|
232 * Enable SGMII slave mode and auto-detection
234 reg
= BCM5482_SSD_SGMII_SLAVE
| MII_BCM54XX_EXP_SEL_SSD
;
235 err
= bcm_phy_read_exp(phydev
, reg
);
238 err
= bcm_phy_write_exp(phydev
, reg
, err
|
239 BCM5482_SSD_SGMII_SLAVE_EN
|
240 BCM5482_SSD_SGMII_SLAVE_AD
);
245 * Disable secondary SerDes powerdown
247 reg
= BCM5482_SSD_1000BX_CTL
| MII_BCM54XX_EXP_SEL_SSD
;
248 err
= bcm_phy_read_exp(phydev
, reg
);
251 err
= bcm_phy_write_exp(phydev
, reg
,
252 err
& ~BCM5482_SSD_1000BX_CTL_PWRDOWN
);
257 * Select 1000BASE-X register set (primary SerDes)
259 reg
= bcm_phy_read_shadow(phydev
, BCM5482_SHD_MODE
);
260 bcm_phy_write_shadow(phydev
, BCM5482_SHD_MODE
,
261 reg
| BCM5482_SHD_MODE_1000BX
);
264 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
265 * (Use LED1 as secondary SerDes ACTIVITY LED)
267 bcm_phy_write_shadow(phydev
, BCM5482_SHD_LEDS1
,
268 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED
) |
269 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2
));
272 * Auto-negotiation doesn't seem to work quite right
273 * in this mode, so we disable it and force it to the
274 * right speed/duplex setting. Only 'link status'
277 phydev
->autoneg
= AUTONEG_DISABLE
;
278 phydev
->speed
= SPEED_1000
;
279 phydev
->duplex
= DUPLEX_FULL
;
285 static int bcm5482_read_status(struct phy_device
*phydev
)
289 err
= genphy_read_status(phydev
);
291 if (phydev
->dev_flags
& PHY_BCM_FLAGS_MODE_1000BX
) {
293 * Only link status matters for 1000Base-X mode, so force
294 * 1000 Mbit/s full-duplex status
297 phydev
->speed
= SPEED_1000
;
298 phydev
->duplex
= DUPLEX_FULL
;
305 static int bcm5481_config_aneg(struct phy_device
*phydev
)
310 ret
= genphy_config_aneg(phydev
);
312 /* Then we can set up the delay. */
313 if (phydev
->interface
== PHY_INTERFACE_MODE_RGMII_RXID
) {
317 * There is no BCM5481 specification available, so down
318 * here is everything we know about "register 0x18". This
319 * at least helps BCM5481 to successfully receive packets
320 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
321 * says: "This sets delay between the RXD and RXC signals
322 * instead of using trace lengths to achieve timing".
325 /* Set RDX clk delay. */
326 reg
= 0x7 | (0x7 << 12);
327 phy_write(phydev
, 0x18, reg
);
329 reg
= phy_read(phydev
, 0x18);
330 /* Set RDX-RXC skew. */
332 /* Write bits 14:0. */
334 phy_write(phydev
, 0x18, reg
);
340 static int brcm_phy_setbits(struct phy_device
*phydev
, int reg
, int set
)
344 val
= phy_read(phydev
, reg
);
348 return phy_write(phydev
, reg
, val
| set
);
351 static int brcm_fet_config_init(struct phy_device
*phydev
)
353 int reg
, err
, err2
, brcmtest
;
355 /* Reset the PHY to bring it to a known state. */
356 err
= phy_write(phydev
, MII_BMCR
, BMCR_RESET
);
360 reg
= phy_read(phydev
, MII_BRCM_FET_INTREG
);
364 /* Unmask events we are interested in and mask interrupts globally. */
365 reg
= MII_BRCM_FET_IR_DUPLEX_EN
|
366 MII_BRCM_FET_IR_SPEED_EN
|
367 MII_BRCM_FET_IR_LINK_EN
|
368 MII_BRCM_FET_IR_ENABLE
|
369 MII_BRCM_FET_IR_MASK
;
371 err
= phy_write(phydev
, MII_BRCM_FET_INTREG
, reg
);
375 /* Enable shadow register access */
376 brcmtest
= phy_read(phydev
, MII_BRCM_FET_BRCMTEST
);
380 reg
= brcmtest
| MII_BRCM_FET_BT_SRE
;
382 err
= phy_write(phydev
, MII_BRCM_FET_BRCMTEST
, reg
);
386 /* Set the LED mode */
387 reg
= phy_read(phydev
, MII_BRCM_FET_SHDW_AUXMODE4
);
393 reg
&= ~MII_BRCM_FET_SHDW_AM4_LED_MASK
;
394 reg
|= MII_BRCM_FET_SHDW_AM4_LED_MODE1
;
396 err
= phy_write(phydev
, MII_BRCM_FET_SHDW_AUXMODE4
, reg
);
400 /* Enable auto MDIX */
401 err
= brcm_phy_setbits(phydev
, MII_BRCM_FET_SHDW_MISCCTRL
,
402 MII_BRCM_FET_SHDW_MC_FAME
);
406 if (phydev
->dev_flags
& PHY_BRCM_AUTO_PWRDWN_ENABLE
) {
407 /* Enable auto power down */
408 err
= brcm_phy_setbits(phydev
, MII_BRCM_FET_SHDW_AUXSTAT2
,
409 MII_BRCM_FET_SHDW_AS2_APDE
);
413 /* Disable shadow register access */
414 err2
= phy_write(phydev
, MII_BRCM_FET_BRCMTEST
, brcmtest
);
421 static int brcm_fet_ack_interrupt(struct phy_device
*phydev
)
425 /* Clear pending interrupts. */
426 reg
= phy_read(phydev
, MII_BRCM_FET_INTREG
);
433 static int brcm_fet_config_intr(struct phy_device
*phydev
)
437 reg
= phy_read(phydev
, MII_BRCM_FET_INTREG
);
441 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
)
442 reg
&= ~MII_BRCM_FET_IR_MASK
;
444 reg
|= MII_BRCM_FET_IR_MASK
;
446 err
= phy_write(phydev
, MII_BRCM_FET_INTREG
, reg
);
450 static struct phy_driver broadcom_drivers
[] = {
452 .phy_id
= PHY_ID_BCM5411
,
453 .phy_id_mask
= 0xfffffff0,
454 .name
= "Broadcom BCM5411",
455 .features
= PHY_GBIT_FEATURES
|
456 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
457 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
458 .config_init
= bcm54xx_config_init
,
459 .config_aneg
= genphy_config_aneg
,
460 .read_status
= genphy_read_status
,
461 .ack_interrupt
= bcm_phy_ack_intr
,
462 .config_intr
= bcm_phy_config_intr
,
464 .phy_id
= PHY_ID_BCM5421
,
465 .phy_id_mask
= 0xfffffff0,
466 .name
= "Broadcom BCM5421",
467 .features
= PHY_GBIT_FEATURES
|
468 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
469 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
470 .config_init
= bcm54xx_config_init
,
471 .config_aneg
= genphy_config_aneg
,
472 .read_status
= genphy_read_status
,
473 .ack_interrupt
= bcm_phy_ack_intr
,
474 .config_intr
= bcm_phy_config_intr
,
476 .phy_id
= PHY_ID_BCM5461
,
477 .phy_id_mask
= 0xfffffff0,
478 .name
= "Broadcom BCM5461",
479 .features
= PHY_GBIT_FEATURES
|
480 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
481 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
482 .config_init
= bcm54xx_config_init
,
483 .config_aneg
= genphy_config_aneg
,
484 .read_status
= genphy_read_status
,
485 .ack_interrupt
= bcm_phy_ack_intr
,
486 .config_intr
= bcm_phy_config_intr
,
488 .phy_id
= PHY_ID_BCM54616S
,
489 .phy_id_mask
= 0xfffffff0,
490 .name
= "Broadcom BCM54616S",
491 .features
= PHY_GBIT_FEATURES
|
492 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
493 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
494 .config_init
= bcm54xx_config_init
,
495 .config_aneg
= genphy_config_aneg
,
496 .read_status
= genphy_read_status
,
497 .ack_interrupt
= bcm_phy_ack_intr
,
498 .config_intr
= bcm_phy_config_intr
,
500 .phy_id
= PHY_ID_BCM5464
,
501 .phy_id_mask
= 0xfffffff0,
502 .name
= "Broadcom BCM5464",
503 .features
= PHY_GBIT_FEATURES
|
504 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
505 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
506 .config_init
= bcm54xx_config_init
,
507 .config_aneg
= genphy_config_aneg
,
508 .read_status
= genphy_read_status
,
509 .ack_interrupt
= bcm_phy_ack_intr
,
510 .config_intr
= bcm_phy_config_intr
,
512 .phy_id
= PHY_ID_BCM5481
,
513 .phy_id_mask
= 0xfffffff0,
514 .name
= "Broadcom BCM5481",
515 .features
= PHY_GBIT_FEATURES
|
516 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
517 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
518 .config_init
= bcm54xx_config_init
,
519 .config_aneg
= bcm5481_config_aneg
,
520 .read_status
= genphy_read_status
,
521 .ack_interrupt
= bcm_phy_ack_intr
,
522 .config_intr
= bcm_phy_config_intr
,
524 .phy_id
= PHY_ID_BCM5482
,
525 .phy_id_mask
= 0xfffffff0,
526 .name
= "Broadcom BCM5482",
527 .features
= PHY_GBIT_FEATURES
|
528 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
529 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
530 .config_init
= bcm5482_config_init
,
531 .config_aneg
= genphy_config_aneg
,
532 .read_status
= bcm5482_read_status
,
533 .ack_interrupt
= bcm_phy_ack_intr
,
534 .config_intr
= bcm_phy_config_intr
,
536 .phy_id
= PHY_ID_BCM50610
,
537 .phy_id_mask
= 0xfffffff0,
538 .name
= "Broadcom BCM50610",
539 .features
= PHY_GBIT_FEATURES
|
540 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
541 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
542 .config_init
= bcm54xx_config_init
,
543 .config_aneg
= genphy_config_aneg
,
544 .read_status
= genphy_read_status
,
545 .ack_interrupt
= bcm_phy_ack_intr
,
546 .config_intr
= bcm_phy_config_intr
,
548 .phy_id
= PHY_ID_BCM50610M
,
549 .phy_id_mask
= 0xfffffff0,
550 .name
= "Broadcom BCM50610M",
551 .features
= PHY_GBIT_FEATURES
|
552 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
553 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
554 .config_init
= bcm54xx_config_init
,
555 .config_aneg
= genphy_config_aneg
,
556 .read_status
= genphy_read_status
,
557 .ack_interrupt
= bcm_phy_ack_intr
,
558 .config_intr
= bcm_phy_config_intr
,
560 .phy_id
= PHY_ID_BCM57780
,
561 .phy_id_mask
= 0xfffffff0,
562 .name
= "Broadcom BCM57780",
563 .features
= PHY_GBIT_FEATURES
|
564 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
565 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
566 .config_init
= bcm54xx_config_init
,
567 .config_aneg
= genphy_config_aneg
,
568 .read_status
= genphy_read_status
,
569 .ack_interrupt
= bcm_phy_ack_intr
,
570 .config_intr
= bcm_phy_config_intr
,
572 .phy_id
= PHY_ID_BCMAC131
,
573 .phy_id_mask
= 0xfffffff0,
574 .name
= "Broadcom BCMAC131",
575 .features
= PHY_BASIC_FEATURES
|
576 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
577 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
578 .config_init
= brcm_fet_config_init
,
579 .config_aneg
= genphy_config_aneg
,
580 .read_status
= genphy_read_status
,
581 .ack_interrupt
= brcm_fet_ack_interrupt
,
582 .config_intr
= brcm_fet_config_intr
,
584 .phy_id
= PHY_ID_BCM5241
,
585 .phy_id_mask
= 0xfffffff0,
586 .name
= "Broadcom BCM5241",
587 .features
= PHY_BASIC_FEATURES
|
588 SUPPORTED_Pause
| SUPPORTED_Asym_Pause
,
589 .flags
= PHY_HAS_MAGICANEG
| PHY_HAS_INTERRUPT
,
590 .config_init
= brcm_fet_config_init
,
591 .config_aneg
= genphy_config_aneg
,
592 .read_status
= genphy_read_status
,
593 .ack_interrupt
= brcm_fet_ack_interrupt
,
594 .config_intr
= brcm_fet_config_intr
,
597 module_phy_driver(broadcom_drivers
);
599 static struct mdio_device_id __maybe_unused broadcom_tbl
[] = {
600 { PHY_ID_BCM5411
, 0xfffffff0 },
601 { PHY_ID_BCM5421
, 0xfffffff0 },
602 { PHY_ID_BCM5461
, 0xfffffff0 },
603 { PHY_ID_BCM54616S
, 0xfffffff0 },
604 { PHY_ID_BCM5464
, 0xfffffff0 },
605 { PHY_ID_BCM5481
, 0xfffffff0 },
606 { PHY_ID_BCM5482
, 0xfffffff0 },
607 { PHY_ID_BCM50610
, 0xfffffff0 },
608 { PHY_ID_BCM50610M
, 0xfffffff0 },
609 { PHY_ID_BCM57780
, 0xfffffff0 },
610 { PHY_ID_BCMAC131
, 0xfffffff0 },
611 { PHY_ID_BCM5241
, 0xfffffff0 },
615 MODULE_DEVICE_TABLE(mdio
, broadcom_tbl
);