1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2015-2017 Broadcom
6 #include "bcm-phy-lib.h"
7 #include <linux/brcmphy.h>
8 #include <linux/export.h>
9 #include <linux/mdio.h>
10 #include <linux/module.h>
11 #include <linux/phy.h>
12 #include <linux/ethtool.h>
14 #define MII_BCM_CHANNEL_WIDTH 0x2000
15 #define BCM_CL45VEN_EEE_ADV 0x3c
17 int bcm_phy_write_exp(struct phy_device
*phydev
, u16 reg
, u16 val
)
21 rc
= phy_write(phydev
, MII_BCM54XX_EXP_SEL
, reg
);
25 return phy_write(phydev
, MII_BCM54XX_EXP_DATA
, val
);
27 EXPORT_SYMBOL_GPL(bcm_phy_write_exp
);
29 int bcm_phy_read_exp(struct phy_device
*phydev
, u16 reg
)
33 val
= phy_write(phydev
, MII_BCM54XX_EXP_SEL
, reg
);
37 val
= phy_read(phydev
, MII_BCM54XX_EXP_DATA
);
39 /* Restore default value. It's O.K. if this write fails. */
40 phy_write(phydev
, MII_BCM54XX_EXP_SEL
, 0);
44 EXPORT_SYMBOL_GPL(bcm_phy_read_exp
);
46 int bcm54xx_auxctl_read(struct phy_device
*phydev
, u16 regnum
)
48 /* The register must be written to both the Shadow Register Select and
49 * the Shadow Read Register Selector
51 phy_write(phydev
, MII_BCM54XX_AUX_CTL
, MII_BCM54XX_AUXCTL_SHDWSEL_MASK
|
52 regnum
<< MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT
);
53 return phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
55 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read
);
57 int bcm54xx_auxctl_write(struct phy_device
*phydev
, u16 regnum
, u16 val
)
59 return phy_write(phydev
, MII_BCM54XX_AUX_CTL
, regnum
| val
);
61 EXPORT_SYMBOL(bcm54xx_auxctl_write
);
63 int bcm_phy_write_misc(struct phy_device
*phydev
,
64 u16 reg
, u16 chl
, u16 val
)
69 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
,
70 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
74 tmp
= phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
75 tmp
|= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA
;
76 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
, tmp
);
80 tmp
= (chl
* MII_BCM_CHANNEL_WIDTH
) | reg
;
81 rc
= bcm_phy_write_exp(phydev
, tmp
, val
);
85 EXPORT_SYMBOL_GPL(bcm_phy_write_misc
);
87 int bcm_phy_read_misc(struct phy_device
*phydev
,
93 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
,
94 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
98 tmp
= phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
99 tmp
|= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA
;
100 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
, tmp
);
104 tmp
= (chl
* MII_BCM_CHANNEL_WIDTH
) | reg
;
105 rc
= bcm_phy_read_exp(phydev
, tmp
);
109 EXPORT_SYMBOL_GPL(bcm_phy_read_misc
);
111 int bcm_phy_ack_intr(struct phy_device
*phydev
)
115 /* Clear pending interrupts. */
116 reg
= phy_read(phydev
, MII_BCM54XX_ISR
);
122 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr
);
124 int bcm_phy_config_intr(struct phy_device
*phydev
)
128 reg
= phy_read(phydev
, MII_BCM54XX_ECR
);
132 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
)
133 reg
&= ~MII_BCM54XX_ECR_IM
;
135 reg
|= MII_BCM54XX_ECR_IM
;
137 return phy_write(phydev
, MII_BCM54XX_ECR
, reg
);
139 EXPORT_SYMBOL_GPL(bcm_phy_config_intr
);
141 int bcm_phy_read_shadow(struct phy_device
*phydev
, u16 shadow
)
143 phy_write(phydev
, MII_BCM54XX_SHD
, MII_BCM54XX_SHD_VAL(shadow
));
144 return MII_BCM54XX_SHD_DATA(phy_read(phydev
, MII_BCM54XX_SHD
));
146 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow
);
148 int bcm_phy_write_shadow(struct phy_device
*phydev
, u16 shadow
,
151 return phy_write(phydev
, MII_BCM54XX_SHD
,
152 MII_BCM54XX_SHD_WRITE
|
153 MII_BCM54XX_SHD_VAL(shadow
) |
154 MII_BCM54XX_SHD_DATA(val
));
156 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow
);
158 int bcm_phy_enable_apd(struct phy_device
*phydev
, bool dll_pwr_down
)
163 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR3
);
167 val
|= BCM54XX_SHD_SCR3_DLLAPD_DIS
;
168 bcm_phy_write_shadow(phydev
, BCM54XX_SHD_SCR3
, val
);
171 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_APD
);
176 val
&= BCM_APD_CLR_MASK
;
178 if (phydev
->autoneg
== AUTONEG_ENABLE
)
179 val
|= BCM54XX_SHD_APD_EN
;
181 val
|= BCM_NO_ANEG_APD_EN
;
183 /* Enable energy detect single link pulse for easy wakeup */
184 val
|= BCM_APD_SINGLELP_EN
;
186 /* Enable Auto Power-Down (APD) for the PHY */
187 return bcm_phy_write_shadow(phydev
, BCM54XX_SHD_APD
, val
);
189 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd
);
191 int bcm_phy_set_eee(struct phy_device
*phydev
, bool enable
)
195 /* Enable EEE at PHY level */
196 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, BRCM_CL45VEN_EEE_CONTROL
);
201 val
|= LPI_FEATURE_EN
| LPI_FEATURE_EN_DIG1000X
;
203 val
&= ~(LPI_FEATURE_EN
| LPI_FEATURE_EN_DIG1000X
);
205 phy_write_mmd(phydev
, MDIO_MMD_AN
, BRCM_CL45VEN_EEE_CONTROL
, (u32
)val
);
208 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, BCM_CL45VEN_EEE_ADV
);
213 val
|= (MDIO_EEE_100TX
| MDIO_EEE_1000T
);
215 val
&= ~(MDIO_EEE_100TX
| MDIO_EEE_1000T
);
217 phy_write_mmd(phydev
, MDIO_MMD_AN
, BCM_CL45VEN_EEE_ADV
, (u32
)val
);
221 EXPORT_SYMBOL_GPL(bcm_phy_set_eee
);
223 int bcm_phy_downshift_get(struct phy_device
*phydev
, u8
*count
)
227 val
= bcm54xx_auxctl_read(phydev
, MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
231 /* Check if wirespeed is enabled or not */
232 if (!(val
& MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
)) {
233 *count
= DOWNSHIFT_DEV_DISABLE
;
237 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR2
);
241 /* Downgrade after one link attempt */
242 if (val
& BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
) {
245 /* Downgrade after configured retry count */
246 val
>>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
247 val
&= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
;
248 *count
= val
+ BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
;
253 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get
);
255 int bcm_phy_downshift_set(struct phy_device
*phydev
, u8 count
)
257 int val
= 0, ret
= 0;
259 /* Range check the number given */
260 if (count
- BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
>
261 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
&&
262 count
!= DOWNSHIFT_DEV_DEFAULT_COUNT
) {
266 val
= bcm54xx_auxctl_read(phydev
, MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
270 /* Se the write enable bit */
271 val
|= MII_BCM54XX_AUXCTL_MISC_WREN
;
273 if (count
== DOWNSHIFT_DEV_DISABLE
) {
274 val
&= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
;
275 return bcm54xx_auxctl_write(phydev
,
276 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
,
279 val
|= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
;
280 ret
= bcm54xx_auxctl_write(phydev
,
281 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
,
287 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR2
);
288 val
&= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
<<
289 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
|
290 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
);
294 val
|= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
;
296 case DOWNSHIFT_DEV_DEFAULT_COUNT
:
297 val
|= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
300 val
|= (count
- BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
) <<
301 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
305 return bcm_phy_write_shadow(phydev
, BCM54XX_SHD_SCR2
, val
);
307 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set
);
309 struct bcm_phy_hw_stat
{
316 /* Counters freeze at either 0xffff or 0xff, better than nothing */
317 static const struct bcm_phy_hw_stat bcm_phy_hw_stats
[] = {
318 { "phy_receive_errors", MII_BRCM_CORE_BASE12
, 0, 16 },
319 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13
, 8, 8 },
320 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13
, 0, 8 },
321 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14
, 8, 8 },
322 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14
, 0, 8 },
325 int bcm_phy_get_sset_count(struct phy_device
*phydev
)
327 return ARRAY_SIZE(bcm_phy_hw_stats
);
329 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count
);
331 void bcm_phy_get_strings(struct phy_device
*phydev
, u8
*data
)
335 for (i
= 0; i
< ARRAY_SIZE(bcm_phy_hw_stats
); i
++)
336 strlcpy(data
+ i
* ETH_GSTRING_LEN
,
337 bcm_phy_hw_stats
[i
].string
, ETH_GSTRING_LEN
);
339 EXPORT_SYMBOL_GPL(bcm_phy_get_strings
);
341 /* Caller is supposed to provide appropriate storage for the library code to
342 * access the shadow copy
344 static u64
bcm_phy_get_stat(struct phy_device
*phydev
, u64
*shadow
,
347 struct bcm_phy_hw_stat stat
= bcm_phy_hw_stats
[i
];
351 val
= phy_read(phydev
, stat
.reg
);
356 val
= val
& ((1 << stat
.bits
) - 1);
364 void bcm_phy_get_stats(struct phy_device
*phydev
, u64
*shadow
,
365 struct ethtool_stats
*stats
, u64
*data
)
369 for (i
= 0; i
< ARRAY_SIZE(bcm_phy_hw_stats
); i
++)
370 data
[i
] = bcm_phy_get_stat(phydev
, shadow
, i
);
372 EXPORT_SYMBOL_GPL(bcm_phy_get_stats
);
374 MODULE_DESCRIPTION("Broadcom PHY Library");
375 MODULE_LICENSE("GPL v2");
376 MODULE_AUTHOR("Broadcom Corporation");