2 * Copyright (C) 2015-2017 Broadcom
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "bcm-phy-lib.h"
15 #include <linux/brcmphy.h>
16 #include <linux/export.h>
17 #include <linux/mdio.h>
18 #include <linux/module.h>
19 #include <linux/phy.h>
20 #include <linux/ethtool.h>
22 #define MII_BCM_CHANNEL_WIDTH 0x2000
23 #define BCM_CL45VEN_EEE_ADV 0x3c
25 int bcm_phy_write_exp(struct phy_device
*phydev
, u16 reg
, u16 val
)
29 rc
= phy_write(phydev
, MII_BCM54XX_EXP_SEL
, reg
);
33 return phy_write(phydev
, MII_BCM54XX_EXP_DATA
, val
);
35 EXPORT_SYMBOL_GPL(bcm_phy_write_exp
);
37 int bcm_phy_read_exp(struct phy_device
*phydev
, u16 reg
)
41 val
= phy_write(phydev
, MII_BCM54XX_EXP_SEL
, reg
);
45 val
= phy_read(phydev
, MII_BCM54XX_EXP_DATA
);
47 /* Restore default value. It's O.K. if this write fails. */
48 phy_write(phydev
, MII_BCM54XX_EXP_SEL
, 0);
52 EXPORT_SYMBOL_GPL(bcm_phy_read_exp
);
54 int bcm54xx_auxctl_read(struct phy_device
*phydev
, u16 regnum
)
56 /* The register must be written to both the Shadow Register Select and
57 * the Shadow Read Register Selector
59 phy_write(phydev
, MII_BCM54XX_AUX_CTL
, regnum
|
60 regnum
<< MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT
);
61 return phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
63 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read
);
65 int bcm54xx_auxctl_write(struct phy_device
*phydev
, u16 regnum
, u16 val
)
67 return phy_write(phydev
, MII_BCM54XX_AUX_CTL
, regnum
| val
);
69 EXPORT_SYMBOL(bcm54xx_auxctl_write
);
71 int bcm_phy_write_misc(struct phy_device
*phydev
,
72 u16 reg
, u16 chl
, u16 val
)
77 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
,
78 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
82 tmp
= phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
83 tmp
|= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA
;
84 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
, tmp
);
88 tmp
= (chl
* MII_BCM_CHANNEL_WIDTH
) | reg
;
89 rc
= bcm_phy_write_exp(phydev
, tmp
, val
);
93 EXPORT_SYMBOL_GPL(bcm_phy_write_misc
);
95 int bcm_phy_read_misc(struct phy_device
*phydev
,
101 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
,
102 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
106 tmp
= phy_read(phydev
, MII_BCM54XX_AUX_CTL
);
107 tmp
|= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA
;
108 rc
= phy_write(phydev
, MII_BCM54XX_AUX_CTL
, tmp
);
112 tmp
= (chl
* MII_BCM_CHANNEL_WIDTH
) | reg
;
113 rc
= bcm_phy_read_exp(phydev
, tmp
);
117 EXPORT_SYMBOL_GPL(bcm_phy_read_misc
);
119 int bcm_phy_ack_intr(struct phy_device
*phydev
)
123 /* Clear pending interrupts. */
124 reg
= phy_read(phydev
, MII_BCM54XX_ISR
);
130 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr
);
132 int bcm_phy_config_intr(struct phy_device
*phydev
)
136 reg
= phy_read(phydev
, MII_BCM54XX_ECR
);
140 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
)
141 reg
&= ~MII_BCM54XX_ECR_IM
;
143 reg
|= MII_BCM54XX_ECR_IM
;
145 return phy_write(phydev
, MII_BCM54XX_ECR
, reg
);
147 EXPORT_SYMBOL_GPL(bcm_phy_config_intr
);
149 int bcm_phy_read_shadow(struct phy_device
*phydev
, u16 shadow
)
151 phy_write(phydev
, MII_BCM54XX_SHD
, MII_BCM54XX_SHD_VAL(shadow
));
152 return MII_BCM54XX_SHD_DATA(phy_read(phydev
, MII_BCM54XX_SHD
));
154 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow
);
156 int bcm_phy_write_shadow(struct phy_device
*phydev
, u16 shadow
,
159 return phy_write(phydev
, MII_BCM54XX_SHD
,
160 MII_BCM54XX_SHD_WRITE
|
161 MII_BCM54XX_SHD_VAL(shadow
) |
162 MII_BCM54XX_SHD_DATA(val
));
164 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow
);
166 int bcm_phy_enable_apd(struct phy_device
*phydev
, bool dll_pwr_down
)
171 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR3
);
175 val
|= BCM54XX_SHD_SCR3_DLLAPD_DIS
;
176 bcm_phy_write_shadow(phydev
, BCM54XX_SHD_SCR3
, val
);
179 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_APD
);
184 val
&= BCM_APD_CLR_MASK
;
186 if (phydev
->autoneg
== AUTONEG_ENABLE
)
187 val
|= BCM54XX_SHD_APD_EN
;
189 val
|= BCM_NO_ANEG_APD_EN
;
191 /* Enable energy detect single link pulse for easy wakeup */
192 val
|= BCM_APD_SINGLELP_EN
;
194 /* Enable Auto Power-Down (APD) for the PHY */
195 return bcm_phy_write_shadow(phydev
, BCM54XX_SHD_APD
, val
);
197 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd
);
199 int bcm_phy_set_eee(struct phy_device
*phydev
, bool enable
)
203 /* Enable EEE at PHY level */
204 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, BRCM_CL45VEN_EEE_CONTROL
);
209 val
|= LPI_FEATURE_EN
| LPI_FEATURE_EN_DIG1000X
;
211 val
&= ~(LPI_FEATURE_EN
| LPI_FEATURE_EN_DIG1000X
);
213 phy_write_mmd(phydev
, MDIO_MMD_AN
, BRCM_CL45VEN_EEE_CONTROL
, (u32
)val
);
216 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, BCM_CL45VEN_EEE_ADV
);
221 val
|= (MDIO_EEE_100TX
| MDIO_EEE_1000T
);
223 val
&= ~(MDIO_EEE_100TX
| MDIO_EEE_1000T
);
225 phy_write_mmd(phydev
, MDIO_MMD_AN
, BCM_CL45VEN_EEE_ADV
, (u32
)val
);
229 EXPORT_SYMBOL_GPL(bcm_phy_set_eee
);
231 int bcm_phy_downshift_get(struct phy_device
*phydev
, u8
*count
)
235 val
= bcm54xx_auxctl_read(phydev
, MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
239 /* Check if wirespeed is enabled or not */
240 if (!(val
& MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
)) {
241 *count
= DOWNSHIFT_DEV_DISABLE
;
245 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR2
);
249 /* Downgrade after one link attempt */
250 if (val
& BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
) {
253 /* Downgrade after configured retry count */
254 val
>>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
255 val
&= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
;
256 *count
= val
+ BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
;
261 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get
);
263 int bcm_phy_downshift_set(struct phy_device
*phydev
, u8 count
)
265 int val
= 0, ret
= 0;
267 /* Range check the number given */
268 if (count
- BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
>
269 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
&&
270 count
!= DOWNSHIFT_DEV_DEFAULT_COUNT
) {
274 val
= bcm54xx_auxctl_read(phydev
, MII_BCM54XX_AUXCTL_SHDWSEL_MISC
);
278 /* Se the write enable bit */
279 val
|= MII_BCM54XX_AUXCTL_MISC_WREN
;
281 if (count
== DOWNSHIFT_DEV_DISABLE
) {
282 val
&= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
;
283 return bcm54xx_auxctl_write(phydev
,
284 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
,
287 val
|= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN
;
288 ret
= bcm54xx_auxctl_write(phydev
,
289 MII_BCM54XX_AUXCTL_SHDWSEL_MISC
,
295 val
= bcm_phy_read_shadow(phydev
, BCM54XX_SHD_SCR2
);
296 val
&= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK
<<
297 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
|
298 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
);
302 val
|= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS
;
304 case DOWNSHIFT_DEV_DEFAULT_COUNT
:
305 val
|= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
308 val
|= (count
- BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET
) <<
309 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT
;
313 return bcm_phy_write_shadow(phydev
, BCM54XX_SHD_SCR2
, val
);
315 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set
);
317 struct bcm_phy_hw_stat
{
324 /* Counters freeze at either 0xffff or 0xff, better than nothing */
325 static const struct bcm_phy_hw_stat bcm_phy_hw_stats
[] = {
326 { "phy_receive_errors", MII_BRCM_CORE_BASE12
, 0, 16 },
327 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13
, 8, 8 },
328 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13
, 0, 8 },
329 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14
, 8, 8 },
330 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14
, 0, 8 },
333 int bcm_phy_get_sset_count(struct phy_device
*phydev
)
335 return ARRAY_SIZE(bcm_phy_hw_stats
);
337 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count
);
339 void bcm_phy_get_strings(struct phy_device
*phydev
, u8
*data
)
343 for (i
= 0; i
< ARRAY_SIZE(bcm_phy_hw_stats
); i
++)
344 memcpy(data
+ i
* ETH_GSTRING_LEN
,
345 bcm_phy_hw_stats
[i
].string
, ETH_GSTRING_LEN
);
347 EXPORT_SYMBOL_GPL(bcm_phy_get_strings
);
350 #define UINT64_MAX (u64)(~((u64)0))
353 /* Caller is supposed to provide appropriate storage for the library code to
354 * access the shadow copy
356 static u64
bcm_phy_get_stat(struct phy_device
*phydev
, u64
*shadow
,
359 struct bcm_phy_hw_stat stat
= bcm_phy_hw_stats
[i
];
363 val
= phy_read(phydev
, stat
.reg
);
368 val
= val
& ((1 << stat
.bits
) - 1);
376 void bcm_phy_get_stats(struct phy_device
*phydev
, u64
*shadow
,
377 struct ethtool_stats
*stats
, u64
*data
)
381 for (i
= 0; i
< ARRAY_SIZE(bcm_phy_hw_stats
); i
++)
382 data
[i
] = bcm_phy_get_stat(phydev
, shadow
, i
);
384 EXPORT_SYMBOL_GPL(bcm_phy_get_stats
);
386 MODULE_DESCRIPTION("Broadcom PHY Library");
387 MODULE_LICENSE("GPL v2");
388 MODULE_AUTHOR("Broadcom Corporation");