blob: 794b9ec81ba59ec05121090e8c78acb6b2db7ac5 [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
Jon Masonb14995a2016-11-04 01:10:58 -040049static int bcm54810_config(struct phy_device *phydev)
50{
51 int rc, val;
52
53 val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
54 val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
55 rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
56 val);
57 if (rc < 0)
58 return rc;
59
60 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
61 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
62 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
63 rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
64 val);
65 if (rc < 0)
66 return rc;
67
68 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
69 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
70 rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
71 if (rc < 0)
72 return rc;
73
74 return 0;
75}
76
Matt Carlson47b1b532009-11-02 14:28:04 +000077/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
Matt Carlson772638b2008-11-03 16:56:51 -080078static int bcm50610_a0_workaround(struct phy_device *phydev)
79{
80 int err;
81
Arun Parameswarana1cba562015-10-06 12:25:48 -070082 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
Matt Carlson47b1b532009-11-02 14:28:04 +000083 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
84 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
85 if (err < 0)
86 return err;
87
Arun Parameswarana1cba562015-10-06 12:25:48 -070088 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
89 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
Matt Carlson47b1b532009-11-02 14:28:04 +000090 if (err < 0)
91 return err;
92
Arun Parameswarana1cba562015-10-06 12:25:48 -070093 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
Matt Carlson47b1b532009-11-02 14:28:04 +000094 MII_BCM54XX_EXP_EXP75_VDACCTRL);
95 if (err < 0)
96 return err;
97
Arun Parameswarana1cba562015-10-06 12:25:48 -070098 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
Matt Carlson47b1b532009-11-02 14:28:04 +000099 MII_BCM54XX_EXP_EXP96_MYST);
100 if (err < 0)
101 return err;
102
Arun Parameswarana1cba562015-10-06 12:25:48 -0700103 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
Matt Carlson47b1b532009-11-02 14:28:04 +0000104 MII_BCM54XX_EXP_EXP97_MYST);
105
106 return err;
107}
108
109static int bcm54xx_phydsp_config(struct phy_device *phydev)
110{
111 int err, err2;
112
113 /* Enable the SMDSP clock */
Matt Carlson772638b2008-11-03 16:56:51 -0800114 err = bcm54xx_auxctl_write(phydev,
115 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
116 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
117 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
118 if (err < 0)
119 return err;
120
Matt Carlson219c6ef2009-11-02 14:28:33 +0000121 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
122 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
123 /* Clear bit 9 to fix a phy interop issue. */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700124 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
Matt Carlson219c6ef2009-11-02 14:28:33 +0000125 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
126 if (err < 0)
127 goto error;
128
129 if (phydev->drv->phy_id == PHY_ID_BCM50610) {
130 err = bcm50610_a0_workaround(phydev);
131 if (err < 0)
132 goto error;
133 }
134 }
Matt Carlson772638b2008-11-03 16:56:51 -0800135
Matt Carlson47b1b532009-11-02 14:28:04 +0000136 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
137 int val;
Matt Carlson772638b2008-11-03 16:56:51 -0800138
Arun Parameswarana1cba562015-10-06 12:25:48 -0700139 val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
Matt Carlson47b1b532009-11-02 14:28:04 +0000140 if (val < 0)
141 goto error;
Matt Carlson772638b2008-11-03 16:56:51 -0800142
Matt Carlson47b1b532009-11-02 14:28:04 +0000143 val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700144 err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
Matt Carlson47b1b532009-11-02 14:28:04 +0000145 }
Matt Carlson772638b2008-11-03 16:56:51 -0800146
147error:
Matt Carlson47b1b532009-11-02 14:28:04 +0000148 /* Disable the SMDSP clock */
149 err2 = bcm54xx_auxctl_write(phydev,
150 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
151 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
Matt Carlson772638b2008-11-03 16:56:51 -0800152
Matt Carlson47b1b532009-11-02 14:28:04 +0000153 /* Return the first error reported. */
154 return err ? err : err2;
Matt Carlson772638b2008-11-03 16:56:51 -0800155}
156
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000157static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
158{
Roel Kluin5ee6f6a2009-12-18 20:16:10 -0800159 u32 orig;
160 int val;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000161 bool clk125en = true;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000162
163 /* Abort if we are using an untested phy. */
roel kluin7ec4e7d2009-12-30 06:43:06 +0000164 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
165 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000166 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
167 return;
168
Arun Parameswarana1cba562015-10-06 12:25:48 -0700169 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000170 if (val < 0)
171 return;
172
173 orig = val;
174
Matt Carlsonc704dc22009-11-02 14:32:12 +0000175 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
176 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
177 BRCM_PHY_REV(phydev) >= 0x3) {
178 /*
179 * Here, bit 0 _disables_ CLK125 when set.
180 * This bit is set by default.
181 */
182 clk125en = false;
183 } else {
184 if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000185 /* Here, bit 0 _enables_ CLK125 when set */
186 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000187 clk125en = false;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000188 }
189 }
190
Joe Perches23677ce2012-02-09 11:17:23 +0000191 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000192 val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
193 else
194 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
195
Matt Carlson52fae082009-11-02 14:32:38 +0000196 if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
197 val |= BCM54XX_SHD_SCR3_TRDDAPD;
198
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000199 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700200 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000201
Arun Parameswarana1cba562015-10-06 12:25:48 -0700202 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000203 if (val < 0)
204 return;
205
206 orig = val;
207
Joe Perches23677ce2012-02-09 11:17:23 +0000208 if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlsonc704dc22009-11-02 14:32:12 +0000209 val |= BCM54XX_SHD_APD_EN;
210 else
211 val &= ~BCM54XX_SHD_APD_EN;
212
213 if (orig != val)
Arun Parameswarana1cba562015-10-06 12:25:48 -0700214 bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000215}
216
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100217static int bcm54xx_config_init(struct phy_device *phydev)
218{
219 int reg, err;
220
221 reg = phy_read(phydev, MII_BCM54XX_ECR);
222 if (reg < 0)
223 return reg;
224
225 /* Mask interrupts globally. */
226 reg |= MII_BCM54XX_ECR_IM;
227 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
228 if (err < 0)
229 return err;
230
231 /* Unmask events we are interested in. */
232 reg = ~(MII_BCM54XX_INT_DUPLEX |
233 MII_BCM54XX_INT_SPEED |
234 MII_BCM54XX_INT_LINK);
235 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
236 if (err < 0)
237 return err;
Matt Carlson772638b2008-11-03 16:56:51 -0800238
Matt Carlson63a14ce2009-11-02 14:30:40 +0000239 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
240 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
241 (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
Arun Parameswarana1cba562015-10-06 12:25:48 -0700242 bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
Matt Carlson63a14ce2009-11-02 14:30:40 +0000243
Matt Carlsonc704dc22009-11-02 14:32:12 +0000244 if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
Matt Carlson52fae082009-11-02 14:32:38 +0000245 (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
Matt Carlsonc704dc22009-11-02 14:32:12 +0000246 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000247 bcm54xx_adjust_rxrefclk(phydev);
248
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100249 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) {
250 err = bcm54210e_config_init(phydev);
251 if (err)
252 return err;
253 } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
Jon Masonb14995a2016-11-04 01:10:58 -0400254 err = bcm54810_config(phydev);
255 if (err)
256 return err;
257 }
258
Matt Carlson47b1b532009-11-02 14:28:04 +0000259 bcm54xx_phydsp_config(phydev);
Matt Carlsond9221e62009-08-25 10:11:26 +0000260
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100261 return 0;
262}
263
Nate Casecd9af3d2008-05-17 06:40:39 +0100264static int bcm5482_config_init(struct phy_device *phydev)
265{
266 int err, reg;
267
268 err = bcm54xx_config_init(phydev);
269
270 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
271 /*
272 * Enable secondary SerDes and its use as an LED source
273 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700274 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD);
275 bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,
Nate Casecd9af3d2008-05-17 06:40:39 +0100276 reg |
277 BCM5482_SHD_SSD_LEDM |
278 BCM5482_SHD_SSD_EN);
279
280 /*
281 * Enable SGMII slave mode and auto-detection
282 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800283 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700284 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800285 if (err < 0)
286 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700287 err = bcm_phy_write_exp(phydev, reg, err |
Matt Carlson042a75b2008-11-03 16:56:29 -0800288 BCM5482_SSD_SGMII_SLAVE_EN |
289 BCM5482_SSD_SGMII_SLAVE_AD);
290 if (err < 0)
291 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100292
293 /*
294 * Disable secondary SerDes powerdown
295 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800296 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700297 err = bcm_phy_read_exp(phydev, reg);
Matt Carlson042a75b2008-11-03 16:56:29 -0800298 if (err < 0)
299 return err;
Arun Parameswarana1cba562015-10-06 12:25:48 -0700300 err = bcm_phy_write_exp(phydev, reg,
Matt Carlson042a75b2008-11-03 16:56:29 -0800301 err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
302 if (err < 0)
303 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100304
305 /*
306 * Select 1000BASE-X register set (primary SerDes)
307 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700308 reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE);
309 bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,
Nate Casecd9af3d2008-05-17 06:40:39 +0100310 reg | BCM5482_SHD_MODE_1000BX);
311
312 /*
313 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
314 * (Use LED1 as secondary SerDes ACTIVITY LED)
315 */
Arun Parameswarana1cba562015-10-06 12:25:48 -0700316 bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,
Nate Casecd9af3d2008-05-17 06:40:39 +0100317 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
318 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
319
320 /*
321 * Auto-negotiation doesn't seem to work quite right
322 * in this mode, so we disable it and force it to the
323 * right speed/duplex setting. Only 'link status'
324 * is important.
325 */
326 phydev->autoneg = AUTONEG_DISABLE;
327 phydev->speed = SPEED_1000;
328 phydev->duplex = DUPLEX_FULL;
329 }
330
331 return err;
332}
333
334static int bcm5482_read_status(struct phy_device *phydev)
335{
336 int err;
337
338 err = genphy_read_status(phydev);
339
340 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
341 /*
342 * Only link status matters for 1000Base-X mode, so force
343 * 1000 Mbit/s full-duplex status
344 */
345 if (phydev->link) {
346 phydev->speed = SPEED_1000;
347 phydev->duplex = DUPLEX_FULL;
348 }
349 }
350
351 return err;
352}
353
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300354static int bcm5481_config_aneg(struct phy_device *phydev)
355{
Jon Masonb14995a2016-11-04 01:10:58 -0400356 struct device_node *np = phydev->mdio.dev.of_node;
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300357 int ret;
358
359 /* Aneg firsly. */
360 ret = genphy_config_aneg(phydev);
361
362 /* Then we can set up the delay. */
363 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
364 u16 reg;
365
366 /*
367 * There is no BCM5481 specification available, so down
368 * here is everything we know about "register 0x18". This
Joe Perchesbfb90352011-08-17 06:58:04 -0700369 * at least helps BCM5481 to successfully receive packets
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300370 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
371 * says: "This sets delay between the RXD and RXC signals
372 * instead of using trace lengths to achieve timing".
373 */
374
375 /* Set RDX clk delay. */
376 reg = 0x7 | (0x7 << 12);
377 phy_write(phydev, 0x18, reg);
378
379 reg = phy_read(phydev, 0x18);
380 /* Set RDX-RXC skew. */
381 reg |= (1 << 8);
382 /* Write bits 14:0. */
383 reg |= (1 << 15);
384 phy_write(phydev, 0x18, reg);
385 }
386
Jon Masonb14995a2016-11-04 01:10:58 -0400387 if (of_property_read_bool(np, "enet-phy-lane-swap")) {
388 /* Lane Swap - Undocumented register...magic! */
389 ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
390 0x11B);
391 if (ret < 0)
392 return ret;
393 }
394
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300395 return ret;
396}
397
Xo Wangd92ead12016-10-21 10:20:13 -0700398static int bcm54612e_config_aneg(struct phy_device *phydev)
399{
400 int ret;
401
402 /* First, auto-negotiate. */
403 ret = genphy_config_aneg(phydev);
404
405 /* Clear TX internal delay unless requested. */
406 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
407 (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
408 /* Disable TXD to GTXCLK clock delay (default set) */
409 /* Bit 9 is the only field in shadow register 00011 */
410 bcm_phy_write_shadow(phydev, 0x03, 0);
411 }
412
413 /* Clear RX internal delay unless requested. */
414 if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
415 (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
416 u16 reg;
417
Rafał Miłecki85b46852017-01-25 21:00:25 +0100418 reg = bcm54xx_auxctl_read(phydev,
419 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
Xo Wangd92ead12016-10-21 10:20:13 -0700420 /* Disable RXD to RXC delay (default set) */
Rafał Miłecki8293c7bc2017-01-25 21:00:26 +0100421 reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
Xo Wangd92ead12016-10-21 10:20:13 -0700422 /* Clear shadow selector field */
423 reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
424 bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
425 MII_BCM54XX_AUXCTL_MISC_WREN | reg);
426 }
427
428 return ret;
429}
430
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000431static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
432{
433 int val;
434
435 val = phy_read(phydev, reg);
436 if (val < 0)
437 return val;
438
439 return phy_write(phydev, reg, val | set);
440}
441
442static int brcm_fet_config_init(struct phy_device *phydev)
443{
444 int reg, err, err2, brcmtest;
445
446 /* Reset the PHY to bring it to a known state. */
447 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
448 if (err < 0)
449 return err;
450
451 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
452 if (reg < 0)
453 return reg;
454
455 /* Unmask events we are interested in and mask interrupts globally. */
456 reg = MII_BRCM_FET_IR_DUPLEX_EN |
457 MII_BRCM_FET_IR_SPEED_EN |
458 MII_BRCM_FET_IR_LINK_EN |
459 MII_BRCM_FET_IR_ENABLE |
460 MII_BRCM_FET_IR_MASK;
461
462 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
463 if (err < 0)
464 return err;
465
466 /* Enable shadow register access */
467 brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
468 if (brcmtest < 0)
469 return brcmtest;
470
471 reg = brcmtest | MII_BRCM_FET_BT_SRE;
472
473 err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
474 if (err < 0)
475 return err;
476
477 /* Set the LED mode */
478 reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
479 if (reg < 0) {
480 err = reg;
481 goto done;
482 }
483
484 reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
485 reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
486
487 err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
488 if (err < 0)
489 goto done;
490
491 /* Enable auto MDIX */
492 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
493 MII_BRCM_FET_SHDW_MC_FAME);
494 if (err < 0)
495 goto done;
496
Matt Carlsoncdd4e09d2009-11-02 14:31:11 +0000497 if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
498 /* Enable auto power down */
499 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
500 MII_BRCM_FET_SHDW_AS2_APDE);
501 }
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000502
503done:
504 /* Disable shadow register access */
505 err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
506 if (!err)
507 err = err2;
508
509 return err;
510}
511
512static int brcm_fet_ack_interrupt(struct phy_device *phydev)
513{
514 int reg;
515
516 /* Clear pending interrupts. */
517 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
518 if (reg < 0)
519 return reg;
520
521 return 0;
522}
523
524static int brcm_fet_config_intr(struct phy_device *phydev)
525{
526 int reg, err;
527
528 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
529 if (reg < 0)
530 return reg;
531
532 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
533 reg &= ~MII_BRCM_FET_IR_MASK;
534 else
535 reg |= MII_BRCM_FET_IR_MASK;
536
537 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
538 return err;
539}
540
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000541static struct phy_driver broadcom_drivers[] = {
542{
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000543 .phy_id = PHY_ID_BCM5411,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100544 .phy_id_mask = 0xfffffff0,
545 .name = "Broadcom BCM5411",
Timur Tabi529ed122016-12-07 13:20:51 -0600546 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100547 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
548 .config_init = bcm54xx_config_init,
549 .config_aneg = genphy_config_aneg,
550 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700551 .ack_interrupt = bcm_phy_ack_intr,
552 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000553}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000554 .phy_id = PHY_ID_BCM5421,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100555 .phy_id_mask = 0xfffffff0,
556 .name = "Broadcom BCM5421",
Timur Tabi529ed122016-12-07 13:20:51 -0600557 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100558 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
559 .config_init = bcm54xx_config_init,
560 .config_aneg = genphy_config_aneg,
561 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700562 .ack_interrupt = bcm_phy_ack_intr,
563 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000564}, {
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100565 .phy_id = PHY_ID_BCM54210E,
566 .phy_id_mask = 0xfffffff0,
567 .name = "Broadcom BCM54210E",
568 .features = PHY_GBIT_FEATURES,
569 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
570 .config_init = bcm54xx_config_init,
571 .config_aneg = genphy_config_aneg,
572 .read_status = genphy_read_status,
573 .ack_interrupt = bcm_phy_ack_intr,
574 .config_intr = bcm_phy_config_intr,
575}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000576 .phy_id = PHY_ID_BCM5461,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100577 .phy_id_mask = 0xfffffff0,
578 .name = "Broadcom BCM5461",
Timur Tabi529ed122016-12-07 13:20:51 -0600579 .features = PHY_GBIT_FEATURES,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100580 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
581 .config_init = bcm54xx_config_init,
582 .config_aneg = genphy_config_aneg,
583 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700584 .ack_interrupt = bcm_phy_ack_intr,
585 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000586}, {
Xo Wangd92ead12016-10-21 10:20:13 -0700587 .phy_id = PHY_ID_BCM54612E,
588 .phy_id_mask = 0xfffffff0,
589 .name = "Broadcom BCM54612E",
Timur Tabi529ed122016-12-07 13:20:51 -0600590 .features = PHY_GBIT_FEATURES,
Xo Wangd92ead12016-10-21 10:20:13 -0700591 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
592 .config_init = bcm54xx_config_init,
593 .config_aneg = bcm54612e_config_aneg,
594 .read_status = genphy_read_status,
595 .ack_interrupt = bcm_phy_ack_intr,
596 .config_intr = bcm_phy_config_intr,
597}, {
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200598 .phy_id = PHY_ID_BCM54616S,
599 .phy_id_mask = 0xfffffff0,
600 .name = "Broadcom BCM54616S",
Timur Tabi529ed122016-12-07 13:20:51 -0600601 .features = PHY_GBIT_FEATURES,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200602 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
603 .config_init = bcm54xx_config_init,
604 .config_aneg = genphy_config_aneg,
605 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700606 .ack_interrupt = bcm_phy_ack_intr,
607 .config_intr = bcm_phy_config_intr,
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200608}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000609 .phy_id = PHY_ID_BCM5464,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400610 .phy_id_mask = 0xfffffff0,
611 .name = "Broadcom BCM5464",
Timur Tabi529ed122016-12-07 13:20:51 -0600612 .features = PHY_GBIT_FEATURES,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400613 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
614 .config_init = bcm54xx_config_init,
615 .config_aneg = genphy_config_aneg,
616 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700617 .ack_interrupt = bcm_phy_ack_intr,
618 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000619}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000620 .phy_id = PHY_ID_BCM5481,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300621 .phy_id_mask = 0xfffffff0,
622 .name = "Broadcom BCM5481",
Timur Tabi529ed122016-12-07 13:20:51 -0600623 .features = PHY_GBIT_FEATURES,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300624 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
625 .config_init = bcm54xx_config_init,
626 .config_aneg = bcm5481_config_aneg,
627 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700628 .ack_interrupt = bcm_phy_ack_intr,
629 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000630}, {
Jon Masonb14995a2016-11-04 01:10:58 -0400631 .phy_id = PHY_ID_BCM54810,
632 .phy_id_mask = 0xfffffff0,
633 .name = "Broadcom BCM54810",
Timur Tabi529ed122016-12-07 13:20:51 -0600634 .features = PHY_GBIT_FEATURES,
Jon Masonb14995a2016-11-04 01:10:58 -0400635 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
636 .config_init = bcm54xx_config_init,
637 .config_aneg = bcm5481_config_aneg,
638 .read_status = genphy_read_status,
639 .ack_interrupt = bcm_phy_ack_intr,
640 .config_intr = bcm_phy_config_intr,
641}, {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000642 .phy_id = PHY_ID_BCM5482,
Nate Case03157ac2008-01-29 10:19:00 -0600643 .phy_id_mask = 0xfffffff0,
644 .name = "Broadcom BCM5482",
Timur Tabi529ed122016-12-07 13:20:51 -0600645 .features = PHY_GBIT_FEATURES,
Nate Case03157ac2008-01-29 10:19:00 -0600646 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Nate Casecd9af3d2008-05-17 06:40:39 +0100647 .config_init = bcm5482_config_init,
Nate Case03157ac2008-01-29 10:19:00 -0600648 .config_aneg = genphy_config_aneg,
Nate Casecd9af3d2008-05-17 06:40:39 +0100649 .read_status = bcm5482_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700650 .ack_interrupt = bcm_phy_ack_intr,
651 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000652}, {
Matt Carlson772638b2008-11-03 16:56:51 -0800653 .phy_id = PHY_ID_BCM50610,
654 .phy_id_mask = 0xfffffff0,
655 .name = "Broadcom BCM50610",
Timur Tabi529ed122016-12-07 13:20:51 -0600656 .features = PHY_GBIT_FEATURES,
Matt Carlson772638b2008-11-03 16:56:51 -0800657 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
658 .config_init = bcm54xx_config_init,
659 .config_aneg = genphy_config_aneg,
660 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700661 .ack_interrupt = bcm_phy_ack_intr,
662 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000663}, {
Matt Carlson4f4598f2009-08-25 10:10:30 +0000664 .phy_id = PHY_ID_BCM50610M,
665 .phy_id_mask = 0xfffffff0,
666 .name = "Broadcom BCM50610M",
Timur Tabi529ed122016-12-07 13:20:51 -0600667 .features = PHY_GBIT_FEATURES,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000668 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
669 .config_init = bcm54xx_config_init,
670 .config_aneg = genphy_config_aneg,
671 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700672 .ack_interrupt = bcm_phy_ack_intr,
673 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000674}, {
Matt Carlsond9221e62009-08-25 10:11:26 +0000675 .phy_id = PHY_ID_BCM57780,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800676 .phy_id_mask = 0xfffffff0,
677 .name = "Broadcom BCM57780",
Timur Tabi529ed122016-12-07 13:20:51 -0600678 .features = PHY_GBIT_FEATURES,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800679 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
680 .config_init = bcm54xx_config_init,
681 .config_aneg = genphy_config_aneg,
682 .read_status = genphy_read_status,
Arun Parameswarana1cba562015-10-06 12:25:48 -0700683 .ack_interrupt = bcm_phy_ack_intr,
684 .config_intr = bcm_phy_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000685}, {
Matt Carlson6a443a02010-02-17 15:17:04 +0000686 .phy_id = PHY_ID_BCMAC131,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000687 .phy_id_mask = 0xfffffff0,
688 .name = "Broadcom BCMAC131",
Timur Tabi529ed122016-12-07 13:20:51 -0600689 .features = PHY_BASIC_FEATURES,
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000690 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
691 .config_init = brcm_fet_config_init,
692 .config_aneg = genphy_config_aneg,
693 .read_status = genphy_read_status,
694 .ack_interrupt = brcm_fet_ack_interrupt,
695 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000696}, {
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000697 .phy_id = PHY_ID_BCM5241,
698 .phy_id_mask = 0xfffffff0,
699 .name = "Broadcom BCM5241",
Timur Tabi529ed122016-12-07 13:20:51 -0600700 .features = PHY_BASIC_FEATURES,
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000701 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
702 .config_init = brcm_fet_config_init,
703 .config_aneg = genphy_config_aneg,
704 .read_status = genphy_read_status,
705 .ack_interrupt = brcm_fet_ack_interrupt,
706 .config_intr = brcm_fet_config_intr,
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000707} };
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000708
Johan Hovold50fd7152014-11-11 19:45:59 +0100709module_phy_driver(broadcom_drivers);
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000710
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +0000711static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000712 { PHY_ID_BCM5411, 0xfffffff0 },
713 { PHY_ID_BCM5421, 0xfffffff0 },
Rafał Miłecki0fc9ae12017-01-27 14:07:01 +0100714 { PHY_ID_BCM54210E, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000715 { PHY_ID_BCM5461, 0xfffffff0 },
Xo Wangd92ead12016-10-21 10:20:13 -0700716 { PHY_ID_BCM54612E, 0xfffffff0 },
Alessio Igor Bogani3bca4cf62015-04-08 12:15:18 +0200717 { PHY_ID_BCM54616S, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000718 { PHY_ID_BCM5464, 0xfffffff0 },
Aaro Koskinen3c25a862015-11-22 01:08:54 +0200719 { PHY_ID_BCM5481, 0xfffffff0 },
Jon Masonb14995a2016-11-04 01:10:58 -0400720 { PHY_ID_BCM54810, 0xfffffff0 },
Dmitry Baryshkovfcb26ec2010-06-16 23:02:23 +0000721 { PHY_ID_BCM5482, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000722 { PHY_ID_BCM50610, 0xfffffff0 },
723 { PHY_ID_BCM50610M, 0xfffffff0 },
724 { PHY_ID_BCM57780, 0xfffffff0 },
725 { PHY_ID_BCMAC131, 0xfffffff0 },
Dmitry Baryshkov7a938f82010-06-16 23:02:24 +0000726 { PHY_ID_BCM5241, 0xfffffff0 },
David Woodhouse4e4f10f2010-04-02 01:05:56 +0000727 { }
728};
729
730MODULE_DEVICE_TABLE(mdio, broadcom_tbl);