blob: 25c6e6cea2dc18c4e1cb385a4d85381f5128763d [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
Jon Masonb14995a2016-11-04 01:10:58 -040033static int bcm54810_config(struct phy_device *phydev)
34{
35 int rc, val;
36
37 val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
38 val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
39 rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
40 val);
41 if (rc < 0)
42 return rc;
43
44 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
45 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
46 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
47 rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
48 val);
49 if (rc < 0)
50 return rc;
51
52 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
53 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
54 rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
55 if (rc < 0)
56 return rc;
57
58 return 0;
59}
60
Matt Carlson47b1b532009-11-02 14:28:04 +000061/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
Matt Carlson772638b2008-11-03 16:56:51 -080062static int bcm50610_a0_workaround(struct phy_device *phydev)
63{
64 int err;
65
Arun Parameswarana1cba562015-10-06 12:25:48 -070066 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
Matt Carlson47b1b532009-11-02 14:28:04 +000067 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
68 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
69 if (err < 0)
70 return err;
71
Arun Parameswarana1cba562015-10-06 12:25:48 -070072 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
73 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
Matt Carlson47b1b532009-11-02 14:28:04 +000074 if (err < 0)
75 return err;
76
Arun Parameswarana1cba562015-10-06 12:25:48 -070077 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
Matt Carlson47b1b532009-11-02 14:28:04 +000078 MII_BCM54XX_EXP_EXP75_VDACCTRL);
79 if (err < 0)
80 return err;
81
Arun Parameswarana1cba562015-10-06 12:25:48 -070082 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
Matt Carlson47b1b532009-11-02 14:28:04 +000083 MII_BCM54XX_EXP_EXP96_MYST);
84 if (err < 0)
85 return err;
86
Arun Parameswarana1cba562015-10-06 12:25:48 -070087 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
Matt Carlson47b1b532009-11-02 14:28:04 +000088 MII_BCM54XX_EXP_EXP97_MYST);
89
90 return err;
91}
92
93static int bcm54xx_phydsp_config(struct phy_device *phydev)
94{
95 int err, err2;
96
97 /* Enable the SMDSP clock */
Matt Carlson772638b2008-11-03 16:56:51 -080098 err = bcm54xx_auxctl_write(phydev,
99 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
100 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
101 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
102 if (err < 0)
103 return err;
104
Matt Carlson219c6ef2009-11-02 14:28:33 +0000105 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
106 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
107 /* Clear bit 9 to fix a phy interop issue. */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700108 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
Matt Carlson219c6ef2009-11-02 14:28:33 +0000109 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
110 if (err < 0)
111 goto error;
112
113 if (phydev->drv->phy_id == PHY_ID_BCM50610) {
114 err = bcm50610_a0_workaround(phydev);
115 if (err < 0)
116 goto error;
117 }
118 }
Matt Carlson772638b2008-11-03 16:56:51 -0800119
Matt Carlson47b1b532009-11-02 14:28:04 +0000120 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
121 int val;
Matt Carlson772638b2008-11-03 16:56:51 -0800122
Arun Parameswarana1cba562015-10-06 12:25:48 -0700123 val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
Matt Carlson47b1b532009-11-02 14:28:04 +0000124 if (val < 0)
125 goto error;
Matt Carlson772638b2008-11-03 16:56:51 -0800126
Matt Carlson47b1b532009-11-02 14:28:04 +0000127 val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700128 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
Matt Carlson47b1b532009-11-02 14:28:04 +0000129 }
Matt Carlson772638b2008-11-03 16:56:51 -0800130
131error:
Matt Carlson47b1b532009-11-02 14:28:04 +0000132 /* Disable the SMDSP clock */
133 err2 = bcm54xx_auxctl_write(phydev,
134 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
135 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
Matt Carlson772638b2008-11-03 16:56:51 -0800136
Matt Carlson47b1b532009-11-02 14:28:04 +0000137 /* Return the first error reported. */
138 return err ? err : err2;
Matt Carlson772638b2008-11-03 16:56:51 -0800139}
140
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000141static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
142{
Roel Kluin5ee6f6a2009-12-18 20:16:10 -0800143 u32 orig;
144 int val;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000145 bool clk125en = true;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000146
147 /* Abort if we are using an untested phy. */
roel kluin7ec4e7d2009-12-30 06:43:06 +0000148 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
149 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000150 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
151 return;
152
Arun Parameswarana1cba562015-10-06 12:25:48 -0700153 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000154 if (val < 0)
155 return;
156
157 orig = val;
158
Matt Carlsonc704dc22009-11-02 14:32:12 +0000159 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
160 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
161 BRCM_PHY_REV(phydev) >= 0x3) {
162 /*
163 * Here, bit 0 _disables_ CLK125 when set.
164 * This bit is set by default.
165 */
166 clk125en = false;
167 } else {
168 if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000169 /* Here, bit 0 _enables_ CLK125 when set */
170 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000171 clk125en = false;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000172 }
173 }
174
Joe Perches23677ce2012-02-09 11:17:23 +0000175 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000176 val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
177 else
178 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
179
Matt Carlson52fae082009-11-02 14:32:38 +0000180 if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
181 val |= BCM54XX_SHD_SCR3_TRDDAPD;
182
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000183 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700184 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000185
Arun Parameswarana1cba562015-10-06 12:25:48 -0700186 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000187 if (val < 0)
188 return;
189
190 orig = val;
191
Joe Perches23677ce2012-02-09 11:17:23 +0000192 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000193 val |= BCM54XX_SHD_APD_EN;
194 else
195 val &= ~BCM54XX_SHD_APD_EN;
196
197 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700198 bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000199}
200
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100201static int bcm54xx_config_init(struct phy_device *phydev)
202{
203 int reg, err;
204
205 reg = phy_read(phydev, MII_BCM54XX_ECR);
206 if (reg < 0)
207 return reg;
208
209 /* Mask interrupts globally. */
210 reg |= MII_BCM54XX_ECR_IM;
211 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
212 if (err < 0)
213 return err;
214
215 /* Unmask events we are interested in. */
216 reg = ~(MII_BCM54XX_INT_DUPLEX |
217 MII_BCM54XX_INT_SPEED |
218 MII_BCM54XX_INT_LINK);
219 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
220 if (err < 0)
221 return err;
Matt Carlson772638b2008-11-03 16:56:51 -0800222
Matt Carlson63a14ce2009-11-02 14:30:40 +0000223 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
224 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
225 (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
Arun Parameswarana1cba562015-10-06 12:25:48 -0700226 bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
Matt Carlson63a14ce2009-11-02 14:30:40 +0000227
Matt Carlsonc704dc22009-11-02 14:32:12 +0000228 if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
Matt Carlson52fae082009-11-02 14:32:38 +0000229 (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
Matt Carlsonc704dc22009-11-02 14:32:12 +0000230 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000231 bcm54xx_adjust_rxrefclk(phydev);
232
Jon Masonb14995a2016-11-04 01:10:58 -0400233 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
234 err = bcm54810_config(phydev);
235 if (err)
236 return err;
237 }
238
Matt Carlson47b1b532009-11-02 14:28:04 +0000239 bcm54xx_phydsp_config(phydev);
Matt Carlsond9221e62009-08-25 10:11:26 +0000240
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100241 return 0;
242}
243
Nate Casecd9af3d2008-05-17 06:40:39 +0100244static int bcm5482_config_init(struct phy_device *phydev)
245{
246 int err, reg;
247
248 err = bcm54xx_config_init(phydev);
249
250 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
251 /*
252 * Enable secondary SerDes and its use as an LED source
253 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700254 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
255 bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
Nate Casecd9af3d2008-05-17 06:40:39 +0100256 reg |
257 BCM5482_SHD_SSD_LEDM |
258 BCM5482_SHD_SSD_EN);
259
260 /*
261 * Enable SGMII slave mode and auto-detection
262 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800263 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700264 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800265 if (err < 0)
266 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700267 err = bcm_phy_write_exp(phydev, reg, err |
Matt Carlson042a75b2008-11-03 16:56:29 -0800268 BCM5482_SSD_SGMII_SLAVE_EN |
269 BCM5482_SSD_SGMII_SLAVE_AD);
270 if (err < 0)
271 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100272
273 /*
274 * Disable secondary SerDes powerdown
275 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800276 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700277 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800278 if (err < 0)
279 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700280 err = bcm_phy_write_exp(phydev, reg,
Matt Carlson042a75b2008-11-03 16:56:29 -0800281 err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
282 if (err < 0)
283 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100284
285 /*
286 * Select 1000BASE-X register set (primary SerDes)
287 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700288 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE);
289 bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,
Nate Casecd9af3d2008-05-17 06:40:39 +0100290 reg | BCM5482_SHD_MODE_1000BX);
291
292 /*
293 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
294 * (Use LED1 as secondary SerDes ACTIVITY LED)
295 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700296 bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
Nate Casecd9af3d2008-05-17 06:40:39 +0100297 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
298 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
299
300 /*
301 * Auto-negotiation doesn't seem to work quite right
302 * in this mode, so we disable it and force it to the
303 * right speed/duplex setting. Only 'link status'
304 * is important.
305 */
306 phydev->autoneg = AUTONEG_DISABLE;
307 phydev->speed = SPEED_1000;
308 phydev->duplex = DUPLEX_FULL;
309 }
310
311 return err;
312}
313
314static int bcm5482_read_status(struct phy_device *phydev)
315{
316 int err;
317
318 err = genphy_read_status(phydev);
319
320 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
321 /*
322 * Only link status matters for 1000Base-X mode, so force
323 * 1000 Mbit/s full-duplex status
324 */
325 if (phydev->link) {
326 phydev->speed = SPEED_1000;
327 phydev->duplex = DUPLEX_FULL;
328 }
329 }
330
331 return err;
332}
333
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300334static int bcm5481_config_aneg(struct phy_device *phydev)
335{
Jon Masonb14995a2016-11-04 01:10:58 -0400336 struct device_node *np = phydev->mdio.dev.of_node;
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300337 int ret;
338
339 /* Aneg firsly. */
340 ret = genphy_config_aneg(phydev);
341
342 /* Then we can set up the delay. */
343 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
344 u16 reg;
345
346 /*
347 * There is no BCM5481 specification available, so down
348 * here is everything we know about "register 0x18". This
Joe Perchesbfb90352011-08-17 06:58:04 -0700349 * at least helps BCM5481 to successfully receive packets
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300350 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
351 * says: "This sets delay between the RXD and RXC signals
352 * instead of using trace lengths to achieve timing".
353 */
354
355 /* Set RDX clk delay. */
356 reg = 0x7 | (0x7 << 12);
357 phy_write(phydev, 0x18, reg);
358
359 reg = phy_read(phydev, 0x18);
360 /* Set RDX-RXC skew. */
361 reg |= (1 << 8);
362 /* Write bits 14:0. */
363 reg |= (1 << 15);
364 phy_write(phydev, 0x18, reg);
365 }
366
Jon Masonb14995a2016-11-04 01:10:58 -0400367 if (of_property_read_bool(np, "enet-phy-lane-swap")) {
368 /* Lane Swap - Undocumented register...magic! */
369 ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
370 0x11B);
371 if (ret < 0)
372 return ret;
373 }
374
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300375 return ret;
376}
377
Xo Wangd92ead12016-10-21 10:20:13 -0700378static int bcm54612e_config_aneg(struct phy_device *phydev)
379{
380 int ret;
381
382 /* First, auto-negotiate. */
383 ret = genphy_config_aneg(phydev);
384
385 /* Clear TX internal delay unless requested. */
386 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
387 (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
388 /* Disable TXD to GTXCLK clock delay (default set) */
389 /* Bit 9 is the only field in shadow register 00011 */
390 bcm_phy_write_shadow(phydev, 0x03, 0);
391 }
392
393 /* Clear RX internal delay unless requested. */
394 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
395 (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
396 u16 reg;
397
Rafał Miłecki85b46852017-01-25 21:00:25 +0100398 reg = bcm54xx_auxctl_read(phydev,
399 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
Xo Wangd92ead12016-10-21 10:20:13 -0700400 /* Disable RXD to RXC delay (default set) */
401 reg &= ~MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW;
402 /* Clear shadow selector field */
403 reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
404 bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
405 MII_BCM54XX_AUXCTL_MISC_WREN | reg);
406 }
407
408 return ret;
409}
410
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000411static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
412{
413 int val;
414
415 val = phy_read(phydev, reg);
416 if (val < 0)
417 return val;
418
419 return phy_write(phydev, reg, val | set);
420}
421
422static int brcm_fet_config_init(struct phy_device *phydev)
423{
424 int reg, err, err2, brcmtest;
425
426 /* Reset the PHY to bring it to a known state. */
427 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
428 if (err < 0)
429 return err;
430
431 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
432 if (reg < 0)
433 return reg;
434
435 /* Unmask events we are interested in and mask interrupts globally. */
436 reg = MII_BRCM_FET_IR_DUPLEX_EN |
437 MII_BRCM_FET_IR_SPEED_EN |
438 MII_BRCM_FET_IR_LINK_EN |
439 MII_BRCM_FET_IR_ENABLE |
440 MII_BRCM_FET_IR_MASK;
441
442 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
443 if (err < 0)
444 return err;
445
446 /* Enable shadow register access */
447 brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
448 if (brcmtest < 0)
449 return brcmtest;
450
451 reg = brcmtest | MII_BRCM_FET_BT_SRE;
452
453 err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
454 if (err < 0)
455 return err;
456
457 /* Set the LED mode */
458 reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
459 if (reg < 0) {
460 err = reg;
461 goto done;
462 }
463
464 reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
465 reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
466
467 err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
468 if (err < 0)
469 goto done;
470
471 /* Enable auto MDIX */
472 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
473 MII_BRCM_FET_SHDW_MC_FAME);
474 if (err < 0)
475 goto done;
476
Matt Carlsoncdd4e09d2009-11-02 14:31:11 +0000477 if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
478 /* Enable auto power down */
479 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
480 MII_BRCM_FET_SHDW_AS2_APDE);
481 }
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000482
483done:
484 /* Disable shadow register access */
485 err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
486 if (!err)
487 err = err2;
488
489 return err;
490}
491
492static int brcm_fet_ack_interrupt(struct phy_device *phydev)
493{
494 int reg;
495
496 /* Clear pending interrupts. */
497 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
498 if (reg < 0)
499 return reg;
500
501 return 0;
502}
503
504static int brcm_fet_config_intr(struct phy_device *phydev)
505{
506 int reg, err;
507
508 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
509 if (reg < 0)
510 return reg;
511
512 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
513 reg &= ~MII_BRCM_FET_IR_MASK;
514 else
515 reg |= MII_BRCM_FET_IR_MASK;
516
517 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
518 return err;
519}
520
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000521static struct phy_driver broadcom_drivers[] = {
522{
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000523 .phy_id = PHY_ID_BCM5411,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100524 .phy_id_mask = 0xfffffff0,
525 .name = "Broadcom BCM5411",
Timur Tabi529ed122016-12-07 13:20:51 -0600526 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100527 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
528 .config_init = bcm54xx_config_init,
529 .config_aneg = genphy_config_aneg,
530 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700531 .ack_interrupt = bcm_phy_ack_intr,
532 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000533}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000534 .phy_id = PHY_ID_BCM5421,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100535 .phy_id_mask = 0xfffffff0,
536 .name = "Broadcom BCM5421",
Timur Tabi529ed122016-12-07 13:20:51 -0600537 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100538 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
539 .config_init = bcm54xx_config_init,
540 .config_aneg = genphy_config_aneg,
541 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700542 .ack_interrupt = bcm_phy_ack_intr,
543 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000544}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000545 .phy_id = PHY_ID_BCM5461,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100546 .phy_id_mask = 0xfffffff0,
547 .name = "Broadcom BCM5461",
Timur Tabi529ed122016-12-07 13:20:51 -0600548 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100549 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
550 .config_init = bcm54xx_config_init,
551 .config_aneg = genphy_config_aneg,
552 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700553 .ack_interrupt = bcm_phy_ack_intr,
554 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000555}, {
Xo Wangd92ead12016-10-21 10:20:13 -0700556 .phy_id = PHY_ID_BCM54612E,
557 .phy_id_mask = 0xfffffff0,
558 .name = "Broadcom BCM54612E",
Timur Tabi529ed122016-12-07 13:20:51 -0600559 .features = PHY_GBIT_FEATURES,
Xo Wangd92ead12016-10-21 10:20:13 -0700560 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
561 .config_init = bcm54xx_config_init,
562 .config_aneg = bcm54612e_config_aneg,
563 .read_status = genphy_read_status,
564 .ack_interrupt = bcm_phy_ack_intr,
565 .config_intr = bcm_phy_config_intr,
566}, {
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200567 .phy_id = PHY_ID_BCM54616S,
568 .phy_id_mask = 0xfffffff0,
569 .name = "Broadcom BCM54616S",
Timur Tabi529ed122016-12-07 13:20:51 -0600570 .features = PHY_GBIT_FEATURES,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200571 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
572 .config_init = bcm54xx_config_init,
573 .config_aneg = genphy_config_aneg,
574 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700575 .ack_interrupt = bcm_phy_ack_intr,
576 .config_intr = bcm_phy_config_intr,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200577}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000578 .phy_id = PHY_ID_BCM5464,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400579 .phy_id_mask = 0xfffffff0,
580 .name = "Broadcom BCM5464",
Timur Tabi529ed122016-12-07 13:20:51 -0600581 .features = PHY_GBIT_FEATURES,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400582 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
583 .config_init = bcm54xx_config_init,
584 .config_aneg = genphy_config_aneg,
585 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700586 .ack_interrupt = bcm_phy_ack_intr,
587 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000588}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000589 .phy_id = PHY_ID_BCM5481,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300590 .phy_id_mask = 0xfffffff0,
591 .name = "Broadcom BCM5481",
Timur Tabi529ed122016-12-07 13:20:51 -0600592 .features = PHY_GBIT_FEATURES,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300593 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
594 .config_init = bcm54xx_config_init,
595 .config_aneg = bcm5481_config_aneg,
596 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700597 .ack_interrupt = bcm_phy_ack_intr,
598 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000599}, {
Jon Masonb14995a2016-11-04 01:10:58 -0400600 .phy_id = PHY_ID_BCM54810,
601 .phy_id_mask = 0xfffffff0,
602 .name = "Broadcom BCM54810",
Timur Tabi529ed122016-12-07 13:20:51 -0600603 .features = PHY_GBIT_FEATURES,
Jon Masonb14995a2016-11-04 01:10:58 -0400604 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
605 .config_init = bcm54xx_config_init,
606 .config_aneg = bcm5481_config_aneg,
607 .read_status = genphy_read_status,
608 .ack_interrupt = bcm_phy_ack_intr,
609 .config_intr = bcm_phy_config_intr,
610}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000611 .phy_id = PHY_ID_BCM5482,
Nate Case03157ac2008-01-29 10:19:00 -0600612 .phy_id_mask = 0xfffffff0,
613 .name = "Broadcom BCM5482",
Timur Tabi529ed122016-12-07 13:20:51 -0600614 .features = PHY_GBIT_FEATURES,
Nate Case03157ac2008-01-29 10:19:00 -0600615 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Nate Casecd9af3d2008-05-17 06:40:39 +0100616 .config_init = bcm5482_config_init,
Nate Case03157ac2008-01-29 10:19:00 -0600617 .config_aneg = genphy_config_aneg,
Nate Casecd9af3d2008-05-17 06:40:39 +0100618 .read_status = bcm5482_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700619 .ack_interrupt = bcm_phy_ack_intr,
620 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000621}, {
Matt Carlson772638b2008-11-03 16:56:51 -0800622 .phy_id = PHY_ID_BCM50610,
623 .phy_id_mask = 0xfffffff0,
624 .name = "Broadcom BCM50610",
Timur Tabi529ed122016-12-07 13:20:51 -0600625 .features = PHY_GBIT_FEATURES,
Matt Carlson772638b2008-11-03 16:56:51 -0800626 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
627 .config_init = bcm54xx_config_init,
628 .config_aneg = genphy_config_aneg,
629 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700630 .ack_interrupt = bcm_phy_ack_intr,
631 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000632}, {
Matt Carlson4f4598f2009-08-25 10:10:30 +0000633 .phy_id = PHY_ID_BCM50610M,
634 .phy_id_mask = 0xfffffff0,
635 .name = "Broadcom BCM50610M",
Timur Tabi529ed122016-12-07 13:20:51 -0600636 .features = PHY_GBIT_FEATURES,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000637 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
638 .config_init = bcm54xx_config_init,
639 .config_aneg = genphy_config_aneg,
640 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700641 .ack_interrupt = bcm_phy_ack_intr,
642 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000643}, {
Matt Carlsond9221e62009-08-25 10:11:26 +0000644 .phy_id = PHY_ID_BCM57780,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800645 .phy_id_mask = 0xfffffff0,
646 .name = "Broadcom BCM57780",
Timur Tabi529ed122016-12-07 13:20:51 -0600647 .features = PHY_GBIT_FEATURES,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800648 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
649 .config_init = bcm54xx_config_init,
650 .config_aneg = genphy_config_aneg,
651 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700652 .ack_interrupt = bcm_phy_ack_intr,
653 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000654}, {
Matt Carlson6a443a02010-02-17 15:17:04 +0000655 .phy_id = PHY_ID_BCMAC131,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000656 .phy_id_mask = 0xfffffff0,
657 .name = "Broadcom BCMAC131",
Timur Tabi529ed122016-12-07 13:20:51 -0600658 .features = PHY_BASIC_FEATURES,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000659 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
660 .config_init = brcm_fet_config_init,
661 .config_aneg = genphy_config_aneg,
662 .read_status = genphy_read_status,
663 .ack_interrupt = brcm_fet_ack_interrupt,
664 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000665}, {
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000666 .phy_id = PHY_ID_BCM5241,
667 .phy_id_mask = 0xfffffff0,
668 .name = "Broadcom BCM5241",
Timur Tabi529ed122016-12-07 13:20:51 -0600669 .features = PHY_BASIC_FEATURES,
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000670 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
671 .config_init = brcm_fet_config_init,
672 .config_aneg = genphy_config_aneg,
673 .read_status = genphy_read_status,
674 .ack_interrupt = brcm_fet_ack_interrupt,
675 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000676} };
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000677
Johan Hovold50fd7152014-11-11 19:45:59 +0100678module_phy_driver(broadcom_drivers);
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000679
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +0000680static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000681 { PHY_ID_BCM5411, 0xfffffff0 },
682 { PHY_ID_BCM5421, 0xfffffff0 },
683 { PHY_ID_BCM5461, 0xfffffff0 },
Xo Wangd92ead12016-10-21 10:20:13 -0700684 { PHY_ID_BCM54612E, 0xfffffff0 },
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200685 { PHY_ID_BCM54616S, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000686 { PHY_ID_BCM5464, 0xfffffff0 },
Aaro Koskinen3c25a862015-11-22 01:08:54 +0200687 { PHY_ID_BCM5481, 0xfffffff0 },
Jon Masonb14995a2016-11-04 01:10:58 -0400688 { PHY_ID_BCM54810, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000689 { PHY_ID_BCM5482, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000690 { PHY_ID_BCM50610, 0xfffffff0 },
691 { PHY_ID_BCM50610M, 0xfffffff0 },
692 { PHY_ID_BCM57780, 0xfffffff0 },
693 { PHY_ID_BCMAC131, 0xfffffff0 },
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000694 { PHY_ID_BCM5241, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000695 { }
696};
697
698MODULE_DEVICE_TABLE(mdio, broadcom_tbl);