blob: 82e3bbef7ddf89b9a8bdded86cf2874dc1a2d55a [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Andy Fleming60ca78b2011-04-07 21:56:05 -05002/*
3 * Broadcom PHY drivers
4 *
Andy Fleming60ca78b2011-04-07 21:56:05 -05005 * Copyright 2010-2011 Freescale Semiconductor, Inc.
6 * author Andy Fleming
Andy Fleming60ca78b2011-04-07 21:56:05 -05007 */
Andy Fleming60ca78b2011-04-07 21:56:05 -05008#include <common.h>
9#include <phy.h>
Simon Glassdbd79542020-05-10 11:40:11 -060010#include <linux/delay.h>
Andy Fleming60ca78b2011-04-07 21:56:05 -050011
12/* Broadcom BCM54xx -- taken from linux sungem_phy */
13#define MIIM_BCM54xx_AUXCNTL 0x18
14#define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
15#define MIIM_BCM54xx_AUXSTATUS 0x19
16#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700
17#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8
18
19#define MIIM_BCM54XX_SHD 0x1c
20#define MIIM_BCM54XX_SHD_WRITE 0x8000
21#define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
22#define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
23#define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \
24 (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
25 MIIM_BCM54XX_SHD_DATA(data))
26
27#define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
28#define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
29#define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
30#define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
31
Arun Parameswaran84248b22017-07-19 15:34:35 -070032#define MIIM_BCM_AUXCNTL_SHDWSEL_MISC 0x0007
Marek Vasutc9cade72023-08-05 16:10:08 +020033#define MIIM_BCM_AUXCNTL_SHDWSEL_MISC_WIRESPEED_EN 0x0010
34#define MIIM_BCM_AUXCNTL_SHDWSEL_MISC_RGMII_EN 0x0080
35#define MIIM_BCM_AUXCNTL_SHDWSEL_MISC_RGMII_SKEW_EN 0x0100
36#define MIIM_BCM_AUXCNTL_MISC_FORCE_AMDIX 0x0200
37#define MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN 0x0800
38#define MIIM_BCM_AUXCNTL_MISC_WREN 0x8000
Arun Parameswaran84248b22017-07-19 15:34:35 -070039
40#define MIIM_BCM_CHANNEL_WIDTH 0x2000
41
Marek Vasutc9cade72023-08-05 16:10:08 +020042#define BCM54810_SHD_CLK_CTL 0x3
43#define BCM54810_SHD_CLK_CTL_GTXCLK_EN BIT(9)
44
45static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
46{
47 /* The register must be written to both the Shadow Register Select and
48 * the Shadow Read Register Selector
49 */
50 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
51 MIIM_BCM54xx_AUXCNTL_ENCODE(regnum));
52 return phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL);
53}
54
55static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
56{
57 return phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, regnum | val);
58}
59
60static int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
61{
62 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
63 MIIM_BCM54XX_SHD_VAL(shadow));
64 return MIIM_BCM54XX_SHD_DATA(phy_read(phydev, MDIO_DEVAD_NONE,
65 MIIM_BCM54XX_SHD));
66}
67
68static int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, u16 val)
69{
70 return phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
71 MIIM_BCM54XX_SHD_WR_ENCODE(shadow, val));
72}
73
74static int bcm54xx_config_clock_delay(struct phy_device *phydev)
75{
76 int rc, val;
77
78 /* handling PHY's internal RX clock delay */
79 val = bcm54xx_auxctl_read(phydev, MIIM_BCM_AUXCNTL_SHDWSEL_MISC);
80 val |= MIIM_BCM_AUXCNTL_MISC_WREN;
81 if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
82 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
83 /* Disable RGMII RXC-RXD skew */
84 val &= ~MIIM_BCM_AUXCNTL_SHDWSEL_MISC_RGMII_SKEW_EN;
85 }
86 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
87 phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
88 /* Enable RGMII RXC-RXD skew */
89 val |= MIIM_BCM_AUXCNTL_SHDWSEL_MISC_RGMII_SKEW_EN;
90 }
91 rc = bcm54xx_auxctl_write(phydev, MIIM_BCM_AUXCNTL_SHDWSEL_MISC, val);
92 if (rc < 0)
93 return rc;
94
95 /* handling PHY's internal TX clock delay */
96 val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
97 if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
98 phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
99 /* Disable internal TX clock delay */
100 val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
101 }
102 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
103 phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
104 /* Enable internal TX clock delay */
105 val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
106 }
107 rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
108 if (rc < 0)
109 return rc;
110
111 return 0;
112}
113
Arun Parameswaran84248b22017-07-19 15:34:35 -0700114static void bcm_phy_write_misc(struct phy_device *phydev,
115 u16 reg, u16 chl, u16 value)
116{
117 int reg_val;
118
119 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
120 MIIM_BCM_AUXCNTL_SHDWSEL_MISC);
121
122 reg_val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL);
123 reg_val |= MIIM_BCM_AUXCNTL_ACTL_SMDSP_EN;
124 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg_val);
125
126 reg_val = (chl * MIIM_BCM_CHANNEL_WIDTH) | reg;
127 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL, reg_val);
128
129 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA, value);
130}
131
Andy Fleming60ca78b2011-04-07 21:56:05 -0500132/* Broadcom BCM5461S */
133static int bcm5461_config(struct phy_device *phydev)
134{
135 genphy_config_aneg(phydev);
136
137 phy_reset(phydev);
138
139 return 0;
140}
141
Marek Vasutc9cade72023-08-05 16:10:08 +0200142/* Broadcom BCM54210E */
143static int bcm54210e_config(struct phy_device *phydev)
144{
145 int ret;
146
147 ret = bcm54xx_config_clock_delay(phydev);
148 if (ret < 0)
149 return ret;
150
151 return bcm5461_config(phydev);
152}
153
Andy Fleming60ca78b2011-04-07 21:56:05 -0500154static int bcm54xx_parse_status(struct phy_device *phydev)
155{
156 unsigned int mii_reg;
157
158 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
159
160 switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
161 MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
162 case 1:
163 phydev->duplex = DUPLEX_HALF;
164 phydev->speed = SPEED_10;
165 break;
166 case 2:
167 phydev->duplex = DUPLEX_FULL;
168 phydev->speed = SPEED_10;
169 break;
170 case 3:
171 phydev->duplex = DUPLEX_HALF;
172 phydev->speed = SPEED_100;
173 break;
174 case 5:
175 phydev->duplex = DUPLEX_FULL;
176 phydev->speed = SPEED_100;
177 break;
178 case 6:
179 phydev->duplex = DUPLEX_HALF;
180 phydev->speed = SPEED_1000;
181 break;
182 case 7:
183 phydev->duplex = DUPLEX_FULL;
184 phydev->speed = SPEED_1000;
185 break;
186 default:
187 printf("Auto-neg error, defaulting to 10BT/HD\n");
188 phydev->duplex = DUPLEX_HALF;
189 phydev->speed = SPEED_10;
190 break;
191 }
192
193 return 0;
194}
195
196static int bcm54xx_startup(struct phy_device *phydev)
197{
Michal Simek5ff89662016-05-18 12:46:12 +0200198 int ret;
199
Andy Fleming60ca78b2011-04-07 21:56:05 -0500200 /* Read the Status (2x to make sure link is right) */
Michal Simek5ff89662016-05-18 12:46:12 +0200201 ret = genphy_update_link(phydev);
202 if (ret)
203 return ret;
Andy Fleming60ca78b2011-04-07 21:56:05 -0500204
Michal Simek5ff89662016-05-18 12:46:12 +0200205 return bcm54xx_parse_status(phydev);
Andy Fleming60ca78b2011-04-07 21:56:05 -0500206}
207
208/* Broadcom BCM5482S */
209/*
210 * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
211 * circumstances. eg a gigabit TSEC connected to a gigabit switch with
212 * a 4-wire ethernet cable. Both ends advertise gigabit, but can't
213 * link. "Ethernet@Wirespeed" reduces advertised speed until link
214 * can be achieved.
215 */
216static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
217{
218 return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
219}
220
221static int bcm5482_config(struct phy_device *phydev)
222{
223 unsigned int reg;
224
225 /* reset the PHY */
226 reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
227 reg |= BMCR_RESET;
228 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
229
230 /* Setup read from auxilary control shadow register 7 */
231 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
232 MIIM_BCM54xx_AUXCNTL_ENCODE(7));
233 /* Read Misc Control register and or in Ethernet@Wirespeed */
234 reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
235 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
236
237 /* Initial config/enable of secondary SerDes interface */
238 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
239 MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
240 /* Write intial value to secondary SerDes Contol */
241 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
242 MIIM_BCM54XX_EXP_SEL_SSD | 0);
243 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
244 BMCR_ANRESTART);
245 /* Enable copper/fiber auto-detect */
246 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
247 MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
248
249 genphy_config_aneg(phydev);
250
251 return 0;
252}
253
Arun Parameswaran84248b22017-07-19 15:34:35 -0700254static void bcm_cygnus_afe(struct phy_device *phydev)
255{
256 /* ensures smdspclk is enabled */
257 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, 0x0c30);
258
259 /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
260 bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
261
262 /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode for all modes*/
263 bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
264
265 /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
266 bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
267
268 /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
269 bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
270
271 /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
272 bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
273
274 /* Adjust bias current trim to overcome digital offSet */
275 phy_write(phydev, MDIO_DEVAD_NONE, 0x1E, 0x02);
276
277 /* make rcal=100, since rdb default is 000 */
278 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B1);
279 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
280
281 /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
282 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
283 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0010);
284
285 /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
286 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x00B0);
287 phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x0000);
288}
289
Jiandong Zheng68c2fd72015-07-15 16:28:13 -0700290static int bcm_cygnus_config(struct phy_device *phydev)
291{
292 genphy_config_aneg(phydev);
Jiandong Zheng68c2fd72015-07-15 16:28:13 -0700293 phy_reset(phydev);
Arun Parameswaran84248b22017-07-19 15:34:35 -0700294 /* AFE settings for PHY stability */
295 bcm_cygnus_afe(phydev);
296 /* Forcing aneg after applying the AFE settings */
297 genphy_restart_aneg(phydev);
Jiandong Zheng68c2fd72015-07-15 16:28:13 -0700298
299 return 0;
300}
301
Andy Fleming60ca78b2011-04-07 21:56:05 -0500302/*
303 * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
304 * 0x42 - "Operating Mode Status Register"
305 */
306static int bcm5482_is_serdes(struct phy_device *phydev)
307{
308 u16 val;
309 int serdes = 0;
310
311 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
312 MIIM_BCM54XX_EXP_SEL_ER | 0x42);
313 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
314
315 switch (val & 0x1f) {
316 case 0x0d: /* RGMII-to-100Base-FX */
317 case 0x0e: /* RGMII-to-SGMII */
318 case 0x0f: /* RGMII-to-SerDes */
319 case 0x12: /* SGMII-to-SerDes */
320 case 0x13: /* SGMII-to-100Base-FX */
321 case 0x16: /* SerDes-to-Serdes */
322 serdes = 1;
323 break;
324 case 0x6: /* RGMII-to-Copper */
325 case 0x14: /* SGMII-to-Copper */
326 case 0x17: /* SerDes-to-Copper */
327 break;
328 default:
329 printf("ERROR, invalid PHY mode (0x%x\n)", val);
330 break;
331 }
332
333 return serdes;
334}
335
336/*
337 * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
338 * Mode Status Register"
339 */
340static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
341{
342 u16 val;
343 int i = 0;
344
345 /* Wait 1s for link - Clause 37 autonegotiation happens very fast */
346 while (1) {
347 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
348 MIIM_BCM54XX_EXP_SEL_ER | 0x42);
349 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
350
351 if (val & 0x8000)
352 break;
353
354 if (i++ > 1000) {
355 phydev->link = 0;
356 return 1;
357 }
358
359 udelay(1000); /* 1 ms */
360 }
361
362 phydev->link = 1;
363 switch ((val >> 13) & 0x3) {
364 case (0x00):
365 phydev->speed = 10;
366 break;
367 case (0x01):
368 phydev->speed = 100;
369 break;
370 case (0x02):
371 phydev->speed = 1000;
372 break;
373 }
374
375 phydev->duplex = (val & 0x1000) == 0x1000;
376
377 return 0;
378}
379
380/*
381 * Figure out if BCM5482 is in serdes or copper mode and determine link
382 * configuration accordingly
383 */
384static int bcm5482_startup(struct phy_device *phydev)
385{
Michal Simek5ff89662016-05-18 12:46:12 +0200386 int ret;
387
Andy Fleming60ca78b2011-04-07 21:56:05 -0500388 if (bcm5482_is_serdes(phydev)) {
389 bcm5482_parse_serdes_sr(phydev);
390 phydev->port = PORT_FIBRE;
Michal Simek5ff89662016-05-18 12:46:12 +0200391 return 0;
Andy Fleming60ca78b2011-04-07 21:56:05 -0500392 }
393
Michal Simek5ff89662016-05-18 12:46:12 +0200394 /* Wait for auto-negotiation to complete or fail */
395 ret = genphy_update_link(phydev);
396 if (ret)
397 return ret;
398
399 /* Parse BCM54xx copper aux status register */
400 return bcm54xx_parse_status(phydev);
Andy Fleming60ca78b2011-04-07 21:56:05 -0500401}
402
Marek Vasutc9cade72023-08-05 16:10:08 +0200403U_BOOT_PHY_DRIVER(bcm54210e) = {
404 .name = "Broadcom BCM54210E",
405 .uid = 0x600d84a0,
406 .mask = 0xfffffff0,
407 .features = PHY_GBIT_FEATURES,
408 .config = &bcm54210e_config,
409 .startup = &bcm54xx_startup,
410 .shutdown = &genphy_shutdown,
411};
412
Marek Vasute06c37b2023-03-19 18:02:47 +0100413U_BOOT_PHY_DRIVER(bcm5461s) = {
Andy Fleming60ca78b2011-04-07 21:56:05 -0500414 .name = "Broadcom BCM5461S",
415 .uid = 0x2060c0,
416 .mask = 0xfffff0,
417 .features = PHY_GBIT_FEATURES,
418 .config = &bcm5461_config,
419 .startup = &bcm54xx_startup,
420 .shutdown = &genphy_shutdown,
421};
422
Marek Vasute06c37b2023-03-19 18:02:47 +0100423U_BOOT_PHY_DRIVER(bcm5464s) = {
Andy Fleming60ca78b2011-04-07 21:56:05 -0500424 .name = "Broadcom BCM5464S",
425 .uid = 0x2060b0,
426 .mask = 0xfffff0,
427 .features = PHY_GBIT_FEATURES,
428 .config = &bcm5461_config,
429 .startup = &bcm54xx_startup,
430 .shutdown = &genphy_shutdown,
431};
432
Marek Vasute06c37b2023-03-19 18:02:47 +0100433U_BOOT_PHY_DRIVER(bcm5482s) = {
Andy Fleming60ca78b2011-04-07 21:56:05 -0500434 .name = "Broadcom BCM5482S",
435 .uid = 0x143bcb0,
436 .mask = 0xffffff0,
437 .features = PHY_GBIT_FEATURES,
438 .config = &bcm5482_config,
439 .startup = &bcm5482_startup,
440 .shutdown = &genphy_shutdown,
441};
442
Marek Vasute06c37b2023-03-19 18:02:47 +0100443U_BOOT_PHY_DRIVER(bcm_cygnus) = {
Jiandong Zheng68c2fd72015-07-15 16:28:13 -0700444 .name = "Broadcom CYGNUS GPHY",
445 .uid = 0xae025200,
446 .mask = 0xfffff0,
447 .features = PHY_GBIT_FEATURES,
448 .config = &bcm_cygnus_config,
Rasmus Villemoes9b8aa542023-03-28 23:12:47 +0200449 .startup = &genphy_startup,
Jiandong Zheng68c2fd72015-07-15 16:28:13 -0700450 .shutdown = &genphy_shutdown,
451};