blob: 9cd8b27d12923c78b6f68bb1bf7e2afe993b30a6 [file] [log] [blame]
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +01001/*
2 * drivers/net/phy/broadcom.c
3 *
4 * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
5 * transceivers.
6 *
7 * Copyright (c) 2006 Maciej W. Rozycki
8 *
9 * Inspired by code written by Amy Fong.
10 *
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.
15 */
16
Arun Parameswarana1cba562015-10-06 12:25:48 -070017#include "bcm-phy-lib.h"
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +010018#include <linux/module.h>
19#include <linux/phy.h>
Matt Carlson8649f132009-11-02 14:30:00 +000020#include <linux/brcmphy.h>
Jon Masonb14995a2016-11-04 01:10:58 -040021#include <linux/of.h>
Matt Carlsond9221e62009-08-25 10:11:26 +000022
23#define BRCM_PHY_MODEL(phydev) \
24 ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
25
Matt Carlson32e5a8d2009-11-02 14:31:39 +000026#define BRCM_PHY_REV(phydev) \
27 ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
28
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +010029MODULE_DESCRIPTION("Broadcom PHY driver");
30MODULE_AUTHOR("Maciej W. Rozycki");
31MODULE_LICENSE("GPL");
32
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +010033static int bcm54210e_config_init(struct phy_device *phydev)
34{
35 int val;
36
37 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
38 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
39 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
40 bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val);
41
42 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
43 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
44 bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
45
46 return 0;
47}
48
Rafał Miłecki62e13092017-01-31 22:54:54 +010049static int bcm54612e_config_init(struct phy_device *phydev)
50{
51 /* Clear TX internal delay unless requested. */
52 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
53 (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
54 /* Disable TXD to GTXCLK clock delay (default set) */
55 /* Bit 9 is the only field in shadow register 00011 */
56 bcm_phy_write_shadow(phydev, 0x03, 0);
57 }
58
59 /* Clear RX internal delay unless requested. */
60 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
61 (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
62 u16 reg;
63
64 reg = bcm54xx_auxctl_read(phydev,
65 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
66 /* Disable RXD to RXC delay (default set) */
67 reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
68 /* Clear shadow selector field */
69 reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
70 bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
71 MII_BCM54XX_AUXCTL_MISC_WREN | reg);
72 }
73
74 return 0;
75}
76
Jon Masonb14995a2016-11-04 01:10:58 -040077static int bcm54810_config(struct phy_device *phydev)
78{
79 int rc, val;
80
81 val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
82 val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
83 rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
84 val);
85 if (rc < 0)
86 return rc;
87
88 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
89 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
90 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
91 rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
92 val);
93 if (rc < 0)
94 return rc;
95
96 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
97 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
98 rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
99 if (rc < 0)
100 return rc;
101
102 return 0;
103}
104
Matt Carlson47b1b532009-11-02 14:28:04 +0000105/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
Matt Carlson772638b2008-11-03 16:56:51 -0800106static int bcm50610_a0_workaround(struct phy_device *phydev)
107{
108 int err;
109
Arun Parameswarana1cba562015-10-06 12:25:48 -0700110 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
Matt Carlson47b1b532009-11-02 14:28:04 +0000111 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
112 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
113 if (err < 0)
114 return err;
115
Arun Parameswarana1cba562015-10-06 12:25:48 -0700116 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
117 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
Matt Carlson47b1b532009-11-02 14:28:04 +0000118 if (err < 0)
119 return err;
120
Arun Parameswarana1cba562015-10-06 12:25:48 -0700121 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
Matt Carlson47b1b532009-11-02 14:28:04 +0000122 MII_BCM54XX_EXP_EXP75_VDACCTRL);
123 if (err < 0)
124 return err;
125
Arun Parameswarana1cba562015-10-06 12:25:48 -0700126 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
Matt Carlson47b1b532009-11-02 14:28:04 +0000127 MII_BCM54XX_EXP_EXP96_MYST);
128 if (err < 0)
129 return err;
130
Arun Parameswarana1cba562015-10-06 12:25:48 -0700131 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
Matt Carlson47b1b532009-11-02 14:28:04 +0000132 MII_BCM54XX_EXP_EXP97_MYST);
133
134 return err;
135}
136
137static int bcm54xx_phydsp_config(struct phy_device *phydev)
138{
139 int err, err2;
140
141 /* Enable the SMDSP clock */
Matt Carlson772638b2008-11-03 16:56:51 -0800142 err = bcm54xx_auxctl_write(phydev,
143 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
144 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
145 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
146 if (err < 0)
147 return err;
148
Matt Carlson219c6ef2009-11-02 14:28:33 +0000149 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
150 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
151 /* Clear bit 9 to fix a phy interop issue. */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700152 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
Matt Carlson219c6ef2009-11-02 14:28:33 +0000153 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
154 if (err < 0)
155 goto error;
156
157 if (phydev->drv->phy_id == PHY_ID_BCM50610) {
158 err = bcm50610_a0_workaround(phydev);
159 if (err < 0)
160 goto error;
161 }
162 }
Matt Carlson772638b2008-11-03 16:56:51 -0800163
Matt Carlson47b1b532009-11-02 14:28:04 +0000164 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
165 int val;
Matt Carlson772638b2008-11-03 16:56:51 -0800166
Arun Parameswarana1cba562015-10-06 12:25:48 -0700167 val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
Matt Carlson47b1b532009-11-02 14:28:04 +0000168 if (val < 0)
169 goto error;
Matt Carlson772638b2008-11-03 16:56:51 -0800170
Matt Carlson47b1b532009-11-02 14:28:04 +0000171 val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700172 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
Matt Carlson47b1b532009-11-02 14:28:04 +0000173 }
Matt Carlson772638b2008-11-03 16:56:51 -0800174
175error:
Matt Carlson47b1b532009-11-02 14:28:04 +0000176 /* Disable the SMDSP clock */
177 err2 = bcm54xx_auxctl_write(phydev,
178 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
179 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
Matt Carlson772638b2008-11-03 16:56:51 -0800180
Matt Carlson47b1b532009-11-02 14:28:04 +0000181 /* Return the first error reported. */
182 return err ? err : err2;
Matt Carlson772638b2008-11-03 16:56:51 -0800183}
184
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000185static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
186{
Roel Kluin5ee6f6a2009-12-18 20:16:10 -0800187 u32 orig;
188 int val;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000189 bool clk125en = true;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000190
191 /* Abort if we are using an untested phy. */
roel kluin7ec4e7d2009-12-30 06:43:06 +0000192 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
193 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000194 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
195 return;
196
Arun Parameswarana1cba562015-10-06 12:25:48 -0700197 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000198 if (val < 0)
199 return;
200
201 orig = val;
202
Matt Carlsonc704dc22009-11-02 14:32:12 +0000203 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
204 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
205 BRCM_PHY_REV(phydev) >= 0x3) {
206 /*
207 * Here, bit 0 _disables_ CLK125 when set.
208 * This bit is set by default.
209 */
210 clk125en = false;
211 } else {
212 if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000213 /* Here, bit 0 _enables_ CLK125 when set */
214 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000215 clk125en = false;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000216 }
217 }
218
Joe Perches23677ce2012-02-09 11:17:23 +0000219 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000220 val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
221 else
222 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
223
Matt Carlson52fae082009-11-02 14:32:38 +0000224 if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
225 val |= BCM54XX_SHD_SCR3_TRDDAPD;
226
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000227 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700228 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000229
Arun Parameswarana1cba562015-10-06 12:25:48 -0700230 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000231 if (val < 0)
232 return;
233
234 orig = val;
235
Joe Perches23677ce2012-02-09 11:17:23 +0000236 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000237 val |= BCM54XX_SHD_APD_EN;
238 else
239 val &= ~BCM54XX_SHD_APD_EN;
240
241 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700242 bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000243}
244
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100245static int bcm54xx_config_init(struct phy_device *phydev)
246{
247 int reg, err;
248
249 reg = phy_read(phydev, MII_BCM54XX_ECR);
250 if (reg < 0)
251 return reg;
252
253 /* Mask interrupts globally. */
254 reg |= MII_BCM54XX_ECR_IM;
255 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
256 if (err < 0)
257 return err;
258
259 /* Unmask events we are interested in. */
260 reg = ~(MII_BCM54XX_INT_DUPLEX |
261 MII_BCM54XX_INT_SPEED |
262 MII_BCM54XX_INT_LINK);
263 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
264 if (err < 0)
265 return err;
Matt Carlson772638b2008-11-03 16:56:51 -0800266
Matt Carlson63a14ce2009-11-02 14:30:40 +0000267 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
268 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
269 (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
Arun Parameswarana1cba562015-10-06 12:25:48 -0700270 bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
Matt Carlson63a14ce2009-11-02 14:30:40 +0000271
Matt Carlsonc704dc22009-11-02 14:32:12 +0000272 if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
Matt Carlson52fae082009-11-02 14:32:38 +0000273 (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
Matt Carlsonc704dc22009-11-02 14:32:12 +0000274 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000275 bcm54xx_adjust_rxrefclk(phydev);
276
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100277 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) {
278 err = bcm54210e_config_init(phydev);
279 if (err)
280 return err;
Rafał Miłecki62e13092017-01-31 22:54:54 +0100281 } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54612E) {
282 err = bcm54612e_config_init(phydev);
283 if (err)
284 return err;
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100285 } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
Jon Masonb14995a2016-11-04 01:10:58 -0400286 err = bcm54810_config(phydev);
287 if (err)
288 return err;
289 }
290
Matt Carlson47b1b532009-11-02 14:28:04 +0000291 bcm54xx_phydsp_config(phydev);
Matt Carlsond9221e62009-08-25 10:11:26 +0000292
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100293 return 0;
294}
295
Nate Casecd9af3d2008-05-17 06:40:39 +0100296static int bcm5482_config_init(struct phy_device *phydev)
297{
298 int err, reg;
299
300 err = bcm54xx_config_init(phydev);
301
302 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
303 /*
304 * Enable secondary SerDes and its use as an LED source
305 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700306 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
307 bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
Nate Casecd9af3d2008-05-17 06:40:39 +0100308 reg |
309 BCM5482_SHD_SSD_LEDM |
310 BCM5482_SHD_SSD_EN);
311
312 /*
313 * Enable SGMII slave mode and auto-detection
314 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800315 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700316 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800317 if (err < 0)
318 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700319 err = bcm_phy_write_exp(phydev, reg, err |
Matt Carlson042a75b2008-11-03 16:56:29 -0800320 BCM5482_SSD_SGMII_SLAVE_EN |
321 BCM5482_SSD_SGMII_SLAVE_AD);
322 if (err < 0)
323 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100324
325 /*
326 * Disable secondary SerDes powerdown
327 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800328 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700329 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800330 if (err < 0)
331 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700332 err = bcm_phy_write_exp(phydev, reg,
Matt Carlson042a75b2008-11-03 16:56:29 -0800333 err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
334 if (err < 0)
335 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100336
337 /*
338 * Select 1000BASE-X register set (primary SerDes)
339 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700340 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE);
341 bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,
Nate Casecd9af3d2008-05-17 06:40:39 +0100342 reg | BCM5482_SHD_MODE_1000BX);
343
344 /*
345 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
346 * (Use LED1 as secondary SerDes ACTIVITY LED)
347 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700348 bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
Nate Casecd9af3d2008-05-17 06:40:39 +0100349 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
350 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
351
352 /*
353 * Auto-negotiation doesn't seem to work quite right
354 * in this mode, so we disable it and force it to the
355 * right speed/duplex setting. Only 'link status'
356 * is important.
357 */
358 phydev->autoneg = AUTONEG_DISABLE;
359 phydev->speed = SPEED_1000;
360 phydev->duplex = DUPLEX_FULL;
361 }
362
363 return err;
364}
365
366static int bcm5482_read_status(struct phy_device *phydev)
367{
368 int err;
369
370 err = genphy_read_status(phydev);
371
372 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
373 /*
374 * Only link status matters for 1000Base-X mode, so force
375 * 1000 Mbit/s full-duplex status
376 */
377 if (phydev->link) {
378 phydev->speed = SPEED_1000;
379 phydev->duplex = DUPLEX_FULL;
380 }
381 }
382
383 return err;
384}
385
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300386static int bcm5481_config_aneg(struct phy_device *phydev)
387{
Jon Masonb14995a2016-11-04 01:10:58 -0400388 struct device_node *np = phydev->mdio.dev.of_node;
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300389 int ret;
390
391 /* Aneg firsly. */
392 ret = genphy_config_aneg(phydev);
393
394 /* Then we can set up the delay. */
395 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
396 u16 reg;
397
398 /*
399 * There is no BCM5481 specification available, so down
400 * here is everything we know about "register 0x18". This
Joe Perchesbfb90352011-08-17 06:58:04 -0700401 * at least helps BCM5481 to successfully receive packets
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300402 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
403 * says: "This sets delay between the RXD and RXC signals
404 * instead of using trace lengths to achieve timing".
405 */
406
407 /* Set RDX clk delay. */
408 reg = 0x7 | (0x7 << 12);
409 phy_write(phydev, 0x18, reg);
410
411 reg = phy_read(phydev, 0x18);
412 /* Set RDX-RXC skew. */
413 reg |= (1 << 8);
414 /* Write bits 14:0. */
415 reg |= (1 << 15);
416 phy_write(phydev, 0x18, reg);
417 }
418
Jon Masonb14995a2016-11-04 01:10:58 -0400419 if (of_property_read_bool(np, "enet-phy-lane-swap")) {
420 /* Lane Swap - Undocumented register...magic! */
421 ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
422 0x11B);
423 if (ret < 0)
424 return ret;
425 }
426
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300427 return ret;
428}
429
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000430static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
431{
432 int val;
433
434 val = phy_read(phydev, reg);
435 if (val < 0)
436 return val;
437
438 return phy_write(phydev, reg, val | set);
439}
440
441static int brcm_fet_config_init(struct phy_device *phydev)
442{
443 int reg, err, err2, brcmtest;
444
445 /* Reset the PHY to bring it to a known state. */
446 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
447 if (err < 0)
448 return err;
449
450 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
451 if (reg < 0)
452 return reg;
453
454 /* Unmask events we are interested in and mask interrupts globally. */
455 reg = MII_BRCM_FET_IR_DUPLEX_EN |
456 MII_BRCM_FET_IR_SPEED_EN |
457 MII_BRCM_FET_IR_LINK_EN |
458 MII_BRCM_FET_IR_ENABLE |
459 MII_BRCM_FET_IR_MASK;
460
461 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
462 if (err < 0)
463 return err;
464
465 /* Enable shadow register access */
466 brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
467 if (brcmtest < 0)
468 return brcmtest;
469
470 reg = brcmtest | MII_BRCM_FET_BT_SRE;
471
472 err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
473 if (err < 0)
474 return err;
475
476 /* Set the LED mode */
477 reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
478 if (reg < 0) {
479 err = reg;
480 goto done;
481 }
482
483 reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
484 reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
485
486 err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
487 if (err < 0)
488 goto done;
489
490 /* Enable auto MDIX */
491 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
492 MII_BRCM_FET_SHDW_MC_FAME);
493 if (err < 0)
494 goto done;
495
Matt Carlsoncdd4e09d2009-11-02 14:31:11 +0000496 if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
497 /* Enable auto power down */
498 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
499 MII_BRCM_FET_SHDW_AS2_APDE);
500 }
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000501
502done:
503 /* Disable shadow register access */
504 err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
505 if (!err)
506 err = err2;
507
508 return err;
509}
510
511static int brcm_fet_ack_interrupt(struct phy_device *phydev)
512{
513 int reg;
514
515 /* Clear pending interrupts. */
516 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
517 if (reg < 0)
518 return reg;
519
520 return 0;
521}
522
523static int brcm_fet_config_intr(struct phy_device *phydev)
524{
525 int reg, err;
526
527 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
528 if (reg < 0)
529 return reg;
530
531 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
532 reg &= ~MII_BRCM_FET_IR_MASK;
533 else
534 reg |= MII_BRCM_FET_IR_MASK;
535
536 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
537 return err;
538}
539
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000540static struct phy_driver broadcom_drivers[] = {
541{
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000542 .phy_id = PHY_ID_BCM5411,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100543 .phy_id_mask = 0xfffffff0,
544 .name = "Broadcom BCM5411",
Timur Tabi529ed122016-12-07 13:20:51 -0600545 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100546 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
547 .config_init = bcm54xx_config_init,
548 .config_aneg = genphy_config_aneg,
549 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700550 .ack_interrupt = bcm_phy_ack_intr,
551 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000552}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000553 .phy_id = PHY_ID_BCM5421,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100554 .phy_id_mask = 0xfffffff0,
555 .name = "Broadcom BCM5421",
Timur Tabi529ed122016-12-07 13:20:51 -0600556 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100557 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
558 .config_init = bcm54xx_config_init,
559 .config_aneg = genphy_config_aneg,
560 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700561 .ack_interrupt = bcm_phy_ack_intr,
562 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000563}, {
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100564 .phy_id = PHY_ID_BCM54210E,
565 .phy_id_mask = 0xfffffff0,
566 .name = "Broadcom BCM54210E",
567 .features = PHY_GBIT_FEATURES,
568 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
569 .config_init = bcm54xx_config_init,
570 .config_aneg = genphy_config_aneg,
571 .read_status = genphy_read_status,
572 .ack_interrupt = bcm_phy_ack_intr,
573 .config_intr = bcm_phy_config_intr,
574}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000575 .phy_id = PHY_ID_BCM5461,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100576 .phy_id_mask = 0xfffffff0,
577 .name = "Broadcom BCM5461",
Timur Tabi529ed122016-12-07 13:20:51 -0600578 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100579 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
580 .config_init = bcm54xx_config_init,
581 .config_aneg = genphy_config_aneg,
582 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700583 .ack_interrupt = bcm_phy_ack_intr,
584 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000585}, {
Xo Wangd92ead12016-10-21 10:20:13 -0700586 .phy_id = PHY_ID_BCM54612E,
587 .phy_id_mask = 0xfffffff0,
588 .name = "Broadcom BCM54612E",
Timur Tabi529ed122016-12-07 13:20:51 -0600589 .features = PHY_GBIT_FEATURES,
Xo Wangd92ead12016-10-21 10:20:13 -0700590 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
591 .config_init = bcm54xx_config_init,
Rafał Miłecki62e13092017-01-31 22:54:54 +0100592 .config_aneg = genphy_config_aneg,
Xo Wangd92ead12016-10-21 10:20:13 -0700593 .read_status = genphy_read_status,
594 .ack_interrupt = bcm_phy_ack_intr,
595 .config_intr = bcm_phy_config_intr,
596}, {
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200597 .phy_id = PHY_ID_BCM54616S,
598 .phy_id_mask = 0xfffffff0,
599 .name = "Broadcom BCM54616S",
Timur Tabi529ed122016-12-07 13:20:51 -0600600 .features = PHY_GBIT_FEATURES,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200601 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
602 .config_init = bcm54xx_config_init,
603 .config_aneg = genphy_config_aneg,
604 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700605 .ack_interrupt = bcm_phy_ack_intr,
606 .config_intr = bcm_phy_config_intr,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200607}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000608 .phy_id = PHY_ID_BCM5464,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400609 .phy_id_mask = 0xfffffff0,
610 .name = "Broadcom BCM5464",
Timur Tabi529ed122016-12-07 13:20:51 -0600611 .features = PHY_GBIT_FEATURES,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400612 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
613 .config_init = bcm54xx_config_init,
614 .config_aneg = genphy_config_aneg,
615 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700616 .ack_interrupt = bcm_phy_ack_intr,
617 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000618}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000619 .phy_id = PHY_ID_BCM5481,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300620 .phy_id_mask = 0xfffffff0,
621 .name = "Broadcom BCM5481",
Timur Tabi529ed122016-12-07 13:20:51 -0600622 .features = PHY_GBIT_FEATURES,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300623 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
624 .config_init = bcm54xx_config_init,
625 .config_aneg = bcm5481_config_aneg,
626 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700627 .ack_interrupt = bcm_phy_ack_intr,
628 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000629}, {
Jon Masonb14995a2016-11-04 01:10:58 -0400630 .phy_id = PHY_ID_BCM54810,
631 .phy_id_mask = 0xfffffff0,
632 .name = "Broadcom BCM54810",
Timur Tabi529ed122016-12-07 13:20:51 -0600633 .features = PHY_GBIT_FEATURES,
Jon Masonb14995a2016-11-04 01:10:58 -0400634 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
635 .config_init = bcm54xx_config_init,
636 .config_aneg = bcm5481_config_aneg,
637 .read_status = genphy_read_status,
638 .ack_interrupt = bcm_phy_ack_intr,
639 .config_intr = bcm_phy_config_intr,
640}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000641 .phy_id = PHY_ID_BCM5482,
Nate Case03157ac2008-01-29 10:19:00 -0600642 .phy_id_mask = 0xfffffff0,
643 .name = "Broadcom BCM5482",
Timur Tabi529ed122016-12-07 13:20:51 -0600644 .features = PHY_GBIT_FEATURES,
Nate Case03157ac2008-01-29 10:19:00 -0600645 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Nate Casecd9af3d2008-05-17 06:40:39 +0100646 .config_init = bcm5482_config_init,
Nate Case03157ac2008-01-29 10:19:00 -0600647 .config_aneg = genphy_config_aneg,
Nate Casecd9af3d2008-05-17 06:40:39 +0100648 .read_status = bcm5482_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700649 .ack_interrupt = bcm_phy_ack_intr,
650 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000651}, {
Matt Carlson772638b2008-11-03 16:56:51 -0800652 .phy_id = PHY_ID_BCM50610,
653 .phy_id_mask = 0xfffffff0,
654 .name = "Broadcom BCM50610",
Timur Tabi529ed122016-12-07 13:20:51 -0600655 .features = PHY_GBIT_FEATURES,
Matt Carlson772638b2008-11-03 16:56:51 -0800656 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
657 .config_init = bcm54xx_config_init,
658 .config_aneg = genphy_config_aneg,
659 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700660 .ack_interrupt = bcm_phy_ack_intr,
661 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000662}, {
Matt Carlson4f4598f2009-08-25 10:10:30 +0000663 .phy_id = PHY_ID_BCM50610M,
664 .phy_id_mask = 0xfffffff0,
665 .name = "Broadcom BCM50610M",
Timur Tabi529ed122016-12-07 13:20:51 -0600666 .features = PHY_GBIT_FEATURES,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000667 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
668 .config_init = bcm54xx_config_init,
669 .config_aneg = genphy_config_aneg,
670 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700671 .ack_interrupt = bcm_phy_ack_intr,
672 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000673}, {
Matt Carlsond9221e62009-08-25 10:11:26 +0000674 .phy_id = PHY_ID_BCM57780,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800675 .phy_id_mask = 0xfffffff0,
676 .name = "Broadcom BCM57780",
Timur Tabi529ed122016-12-07 13:20:51 -0600677 .features = PHY_GBIT_FEATURES,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800678 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
679 .config_init = bcm54xx_config_init,
680 .config_aneg = genphy_config_aneg,
681 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700682 .ack_interrupt = bcm_phy_ack_intr,
683 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000684}, {
Matt Carlson6a443a02010-02-17 15:17:04 +0000685 .phy_id = PHY_ID_BCMAC131,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000686 .phy_id_mask = 0xfffffff0,
687 .name = "Broadcom BCMAC131",
Timur Tabi529ed122016-12-07 13:20:51 -0600688 .features = PHY_BASIC_FEATURES,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000689 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
690 .config_init = brcm_fet_config_init,
691 .config_aneg = genphy_config_aneg,
692 .read_status = genphy_read_status,
693 .ack_interrupt = brcm_fet_ack_interrupt,
694 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000695}, {
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000696 .phy_id = PHY_ID_BCM5241,
697 .phy_id_mask = 0xfffffff0,
698 .name = "Broadcom BCM5241",
Timur Tabi529ed122016-12-07 13:20:51 -0600699 .features = PHY_BASIC_FEATURES,
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000700 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
701 .config_init = brcm_fet_config_init,
702 .config_aneg = genphy_config_aneg,
703 .read_status = genphy_read_status,
704 .ack_interrupt = brcm_fet_ack_interrupt,
705 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000706} };
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000707
Johan Hovold50fd7152014-11-11 19:45:59 +0100708module_phy_driver(broadcom_drivers);
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000709
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +0000710static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000711 { PHY_ID_BCM5411, 0xfffffff0 },
712 { PHY_ID_BCM5421, 0xfffffff0 },
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100713 { PHY_ID_BCM54210E, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000714 { PHY_ID_BCM5461, 0xfffffff0 },
Xo Wangd92ead12016-10-21 10:20:13 -0700715 { PHY_ID_BCM54612E, 0xfffffff0 },
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200716 { PHY_ID_BCM54616S, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000717 { PHY_ID_BCM5464, 0xfffffff0 },
Aaro Koskinen3c25a862015-11-22 01:08:54 +0200718 { PHY_ID_BCM5481, 0xfffffff0 },
Jon Masonb14995a2016-11-04 01:10:58 -0400719 { PHY_ID_BCM54810, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000720 { PHY_ID_BCM5482, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000721 { PHY_ID_BCM50610, 0xfffffff0 },
722 { PHY_ID_BCM50610M, 0xfffffff0 },
723 { PHY_ID_BCM57780, 0xfffffff0 },
724 { PHY_ID_BCMAC131, 0xfffffff0 },
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000725 { PHY_ID_BCM5241, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000726 { }
727};
728
729MODULE_DEVICE_TABLE(mdio, broadcom_tbl);