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