xref: /openbmc/u-boot/drivers/net/phy/b53.c (revision cf0bcd7d)
1 /*
2  * Copyright (C) 2017
3  * Broadcom
4  * Florian Fainelli <f.fainelli@gmail.com>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 /*
10  * PHY driver for Broadcom BCM53xx (roboswitch) Ethernet switches.
11  *
12  * This driver configures the b53 for basic use as a PHY. The switch supports
13  * vendor tags and VLAN configuration that can affect the switching decisions.
14  * This driver uses a simple configuration in which all ports are only allowed
15  * to send frames to the CPU port and receive frames from the CPU port this
16  * providing port isolation (no cross talk).
17  *
18  * The configuration determines which PHY ports to activate using the
19  * CONFIG_B53_PHY_PORTS bitmask. Set bit N will active port N and so on.
20  *
21  * This driver was written primarily for the Lamobo R1 platform using a BCM53152
22  * switch but the BCM53xx being largely register compatible, extending it to
23  * cover other switches would be trivial.
24  */
25 
26 #include <common.h>
27 
28 #include <errno.h>
29 #include <malloc.h>
30 #include <miiphy.h>
31 #include <netdev.h>
32 
33 /* Pseudo-PHY address (non configurable) to access internal registers */
34 #define BRCM_PSEUDO_PHY_ADDR		30
35 
36 /* Maximum number of ports possible */
37 #define B53_N_PORTS			9
38 
39 #define B53_CTRL_PAGE			0x00 /* Control */
40 #define B53_MGMT_PAGE			0x02 /* Management Mode */
41 /* Port VLAN Page */
42 #define B53_PVLAN_PAGE			0x31
43 
44 /* Control Page registers */
45 #define B53_PORT_CTRL(i)		(0x00 + (i))
46 #define   PORT_CTRL_RX_DISABLE		BIT(0)
47 #define   PORT_CTRL_TX_DISABLE		BIT(1)
48 #define   PORT_CTRL_RX_BCST_EN		BIT(2) /* Broadcast RX (P8 only) */
49 #define   PORT_CTRL_RX_MCST_EN		BIT(3) /* Multicast RX (P8 only) */
50 #define   PORT_CTRL_RX_UCST_EN		BIT(4) /* Unicast RX (P8 only) */
51 
52 /* Switch Mode Control Register (8 bit) */
53 #define B53_SWITCH_MODE			0x0b
54 #define   SM_SW_FWD_MODE		BIT(0)	/* 1 = Managed Mode */
55 #define   SM_SW_FWD_EN			BIT(1)	/* Forwarding Enable */
56 
57 /* IMP Port state override register (8 bit) */
58 #define B53_PORT_OVERRIDE_CTRL		0x0e
59 #define   PORT_OVERRIDE_LINK		BIT(0)
60 #define   PORT_OVERRIDE_FULL_DUPLEX	BIT(1) /* 0 = Half Duplex */
61 #define   PORT_OVERRIDE_SPEED_S		2
62 #define   PORT_OVERRIDE_SPEED_10M	(0 << PORT_OVERRIDE_SPEED_S)
63 #define   PORT_OVERRIDE_SPEED_100M	(1 << PORT_OVERRIDE_SPEED_S)
64 #define   PORT_OVERRIDE_SPEED_1000M	(2 << PORT_OVERRIDE_SPEED_S)
65 /* BCM5325 only */
66 #define   PORT_OVERRIDE_RV_MII_25	BIT(4)
67 #define   PORT_OVERRIDE_RX_FLOW		BIT(4)
68 #define   PORT_OVERRIDE_TX_FLOW		BIT(5)
69 /* BCM5301X only, requires setting 1000M */
70 #define   PORT_OVERRIDE_SPEED_2000M	BIT(6)
71 #define   PORT_OVERRIDE_EN		BIT(7) /* Use the register contents */
72 
73 #define B53_RGMII_CTRL_IMP		0x60
74 #define   RGMII_CTRL_ENABLE_GMII	BIT(7)
75 #define   RGMII_CTRL_TIMING_SEL		BIT(2)
76 #define   RGMII_CTRL_DLL_RXC		BIT(1)
77 #define   RGMII_CTRL_DLL_TXC		BIT(0)
78 
79 /* Switch control (8 bit) */
80 #define B53_SWITCH_CTRL			0x22
81 #define  B53_MII_DUMB_FWDG_EN		BIT(6)
82 
83 /* Software reset register (8 bit) */
84 #define B53_SOFTRESET			0x79
85 #define   SW_RST			BIT(7)
86 #define   EN_CH_RST			BIT(6)
87 #define   EN_SW_RST			BIT(4)
88 
89 /* Fast Aging Control register (8 bit) */
90 #define B53_FAST_AGE_CTRL		0x88
91 #define   FAST_AGE_STATIC		BIT(0)
92 #define   FAST_AGE_DYNAMIC		BIT(1)
93 #define   FAST_AGE_PORT			BIT(2)
94 #define   FAST_AGE_VLAN			BIT(3)
95 #define   FAST_AGE_STP			BIT(4)
96 #define   FAST_AGE_MC			BIT(5)
97 #define   FAST_AGE_DONE			BIT(7)
98 
99 /* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
100 #define B53_PVLAN_PORT_MASK(i)		((i) * 2)
101 
102 /* MII registers */
103 #define REG_MII_PAGE    0x10    /* MII Page register */
104 #define REG_MII_ADDR    0x11    /* MII Address register */
105 #define REG_MII_DATA0   0x18    /* MII Data register 0 */
106 #define REG_MII_DATA1   0x19    /* MII Data register 1 */
107 #define REG_MII_DATA2   0x1a    /* MII Data register 2 */
108 #define REG_MII_DATA3   0x1b    /* MII Data register 3 */
109 
110 #define REG_MII_PAGE_ENABLE     BIT(0)
111 #define REG_MII_ADDR_WRITE      BIT(0)
112 #define REG_MII_ADDR_READ       BIT(1)
113 
114 struct b53_device {
115 	struct mii_dev	*bus;
116 	unsigned int cpu_port;
117 };
118 
119 static int b53_mdio_op(struct mii_dev *bus, u8 page, u8 reg, u16 op)
120 {
121 	int ret;
122 	int i;
123 	u16 v;
124 
125 	/* set page number */
126 	v = (page << 8) | REG_MII_PAGE_ENABLE;
127 	ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
128 			 REG_MII_PAGE, v);
129 	if (ret)
130 		return ret;
131 
132 	/* set register address */
133 	v = (reg << 8) | op;
134 	ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
135 			 REG_MII_ADDR, v);
136 	if (ret)
137 		return ret;
138 
139 	/* check if operation completed */
140 	for (i = 0; i < 5; ++i) {
141 		v = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
142 			      REG_MII_ADDR);
143 		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
144 			break;
145 
146 		udelay(100);
147 	}
148 
149 	if (i == 5)
150 		return -EIO;
151 
152 	return 0;
153 }
154 
155 static int b53_mdio_read8(struct mii_dev *bus, u8 page, u8 reg, u8 *val)
156 {
157 	int ret;
158 
159 	ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
160 	if (ret)
161 		return ret;
162 
163 	*val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
164 			 REG_MII_DATA0) & 0xff;
165 
166 	return 0;
167 }
168 
169 static int b53_mdio_read16(struct mii_dev *bus, u8 page, u8 reg, u16 *val)
170 {
171 	int ret;
172 
173 	ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
174 	if (ret)
175 		return ret;
176 
177 	*val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
178 			 REG_MII_DATA0);
179 
180 	return 0;
181 }
182 
183 static int b53_mdio_read32(struct mii_dev *bus, u8 page, u8 reg, u32 *val)
184 {
185 	int ret;
186 
187 	ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
188 	if (ret)
189 		return ret;
190 
191 	*val = bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
192 			 REG_MII_DATA0);
193 	*val |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
194 			  REG_MII_DATA1) << 16;
195 
196 	return 0;
197 }
198 
199 static int b53_mdio_read48(struct mii_dev *bus, u8 page, u8 reg, u64 *val)
200 {
201 	u64 temp = 0;
202 	int i;
203 	int ret;
204 
205 	ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
206 	if (ret)
207 		return ret;
208 
209 	for (i = 2; i >= 0; i--) {
210 		temp <<= 16;
211 		temp |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
212 				  REG_MII_DATA0 + i);
213 	}
214 
215 	*val = temp;
216 
217 	return 0;
218 }
219 
220 static int b53_mdio_read64(struct mii_dev *bus, u8 page, u8 reg, u64 *val)
221 {
222 	u64 temp = 0;
223 	int i;
224 	int ret;
225 
226 	ret = b53_mdio_op(bus, page, reg, REG_MII_ADDR_READ);
227 	if (ret)
228 		return ret;
229 
230 	for (i = 3; i >= 0; i--) {
231 		temp <<= 16;
232 		temp |= bus->read(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
233 				  REG_MII_DATA0 + i);
234 	}
235 
236 	*val = temp;
237 
238 	return 0;
239 }
240 
241 static int b53_mdio_write8(struct mii_dev *bus, u8 page, u8 reg, u8 value)
242 {
243 	int ret;
244 
245 	ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
246 			 REG_MII_DATA0, value);
247 	if (ret)
248 		return ret;
249 
250 	return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
251 }
252 
253 static int b53_mdio_write16(struct mii_dev *bus, u8 page, u8 reg,
254 			    u16 value)
255 {
256 	int ret;
257 
258 	ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR, MDIO_DEVAD_NONE,
259 			 REG_MII_DATA0, value);
260 	if (ret)
261 		return ret;
262 
263 	return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
264 }
265 
266 static int b53_mdio_write32(struct mii_dev *bus, u8 page, u8 reg,
267 			    u32 value)
268 {
269 	unsigned int i;
270 	u32 temp = value;
271 
272 	for (i = 0; i < 2; i++) {
273 		int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
274 				     MDIO_DEVAD_NONE,
275 				     REG_MII_DATA0 + i, temp & 0xffff);
276 		if (ret)
277 			return ret;
278 		temp >>= 16;
279 	}
280 
281 	return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
282 }
283 
284 static int b53_mdio_write48(struct mii_dev *bus, u8 page, u8 reg,
285 			    u64 value)
286 {
287 	unsigned int i;
288 	u64 temp = value;
289 
290 	for (i = 0; i < 3; i++) {
291 		int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
292 				     MDIO_DEVAD_NONE,
293 				     REG_MII_DATA0 + i, temp & 0xffff);
294 		if (ret)
295 			return ret;
296 		temp >>= 16;
297 	}
298 
299 	return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
300 }
301 
302 static int b53_mdio_write64(struct mii_dev *bus, u8 page, u8 reg,
303 			    u64 value)
304 {
305 	unsigned int i;
306 	u64 temp = value;
307 
308 	for (i = 0; i < 4; i++) {
309 		int ret = bus->write(bus, BRCM_PSEUDO_PHY_ADDR,
310 				     MDIO_DEVAD_NONE,
311 				     REG_MII_DATA0 + i, temp & 0xffff);
312 		if (ret)
313 			return ret;
314 		temp >>= 16;
315 	}
316 
317 	return b53_mdio_op(bus, page, reg, REG_MII_ADDR_WRITE);
318 }
319 
320 static inline int b53_read8(struct b53_device *dev, u8 page,
321 			    u8 reg, u8 *value)
322 {
323 	return b53_mdio_read8(dev->bus, page, reg, value);
324 }
325 
326 static inline int b53_read16(struct b53_device *dev, u8 page,
327 			     u8 reg, u16 *value)
328 {
329 	return b53_mdio_read16(dev->bus, page, reg, value);
330 }
331 
332 static inline int b53_read32(struct b53_device *dev, u8 page,
333 			     u8 reg, u32 *value)
334 {
335 	return b53_mdio_read32(dev->bus, page, reg, value);
336 }
337 
338 static inline int b53_read48(struct b53_device *dev, u8 page,
339 			     u8 reg, u64 *value)
340 {
341 	return b53_mdio_read48(dev->bus, page, reg, value);
342 }
343 
344 static inline int b53_read64(struct b53_device *dev, u8 page,
345 			     u8 reg, u64 *value)
346 {
347 	return b53_mdio_read64(dev->bus, page, reg, value);
348 }
349 
350 static inline int b53_write8(struct b53_device *dev, u8 page,
351 			     u8 reg, u8 value)
352 {
353 	return b53_mdio_write8(dev->bus, page, reg, value);
354 }
355 
356 static inline int b53_write16(struct b53_device *dev, u8 page,
357 			      u8 reg, u16 value)
358 {
359 	return b53_mdio_write16(dev->bus, page, reg, value);
360 }
361 
362 static inline int b53_write32(struct b53_device *dev, u8 page,
363 			      u8 reg, u32 value)
364 {
365 	return b53_mdio_write32(dev->bus, page, reg, value);
366 }
367 
368 static inline int b53_write48(struct b53_device *dev, u8 page,
369 			      u8 reg, u64 value)
370 {
371 	return b53_mdio_write48(dev->bus, page, reg, value);
372 }
373 
374 static inline int b53_write64(struct b53_device *dev, u8 page,
375 			      u8 reg, u64 value)
376 {
377 	return b53_mdio_write64(dev->bus, page, reg, value);
378 }
379 
380 static int b53_flush_arl(struct b53_device *dev, u8 mask)
381 {
382 	unsigned int i;
383 
384 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
385 		   FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask);
386 
387 	for (i = 0; i < 10; i++) {
388 		u8 fast_age_ctrl;
389 
390 		b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
391 			  &fast_age_ctrl);
392 
393 		if (!(fast_age_ctrl & FAST_AGE_DONE))
394 			goto out;
395 
396 		mdelay(1);
397 	}
398 
399 	return -ETIMEDOUT;
400 out:
401 	/* Only age dynamic entries (default behavior) */
402 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DYNAMIC);
403 	return 0;
404 }
405 
406 static int b53_switch_reset(struct phy_device *phydev)
407 {
408 	struct b53_device *dev = phydev->priv;
409 	unsigned int timeout = 1000;
410 	u8 mgmt;
411 	u8 reg;
412 
413 	b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
414 	reg |= SW_RST | EN_SW_RST | EN_CH_RST;
415 	b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg);
416 
417 	do {
418 		b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, &reg);
419 		if (!(reg & SW_RST))
420 			break;
421 
422 		mdelay(1);
423 	} while (timeout-- > 0);
424 
425 	if (timeout == 0)
426 		return -ETIMEDOUT;
427 
428 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
429 
430 	if (!(mgmt & SM_SW_FWD_EN)) {
431 		mgmt &= ~SM_SW_FWD_MODE;
432 		mgmt |= SM_SW_FWD_EN;
433 
434 		b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
435 		b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
436 
437 		if (!(mgmt & SM_SW_FWD_EN)) {
438 			printf("Failed to enable switch!\n");
439 			return -EINVAL;
440 		}
441 	}
442 
443 	/* Include IMP port in dumb forwarding mode when no tagging protocol
444 	 * is configured
445 	 */
446 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt);
447 	mgmt |= B53_MII_DUMB_FWDG_EN;
448 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt);
449 
450 	return b53_flush_arl(dev, FAST_AGE_STATIC);
451 }
452 
453 static void b53_enable_cpu_port(struct phy_device *phydev)
454 {
455 	struct b53_device *dev = phydev->priv;
456 	u8 port_ctrl;
457 
458 	port_ctrl = PORT_CTRL_RX_BCST_EN |
459 		    PORT_CTRL_RX_MCST_EN |
460 		    PORT_CTRL_RX_UCST_EN;
461 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(dev->cpu_port), port_ctrl);
462 
463 	port_ctrl = PORT_OVERRIDE_EN | PORT_OVERRIDE_LINK |
464 		    PORT_OVERRIDE_FULL_DUPLEX | PORT_OVERRIDE_SPEED_1000M;
465 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, port_ctrl);
466 
467 	b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_IMP, &port_ctrl);
468 }
469 
470 static void b53_imp_vlan_setup(struct b53_device *dev, int cpu_port)
471 {
472 	unsigned int port;
473 	u16 pvlan;
474 
475 	/* Enable the IMP port to be in the same VLAN as the other ports
476 	 * on a per-port basis such that we only have Port i and IMP in
477 	 * the same VLAN.
478 	 */
479 	for (port = 0; port < B53_N_PORTS; port++) {
480 		if (!((1 << port) & CONFIG_B53_PHY_PORTS))
481 			continue;
482 
483 		b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port),
484 			   &pvlan);
485 		pvlan |= BIT(cpu_port);
486 		b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port),
487 			    pvlan);
488 	}
489 }
490 
491 static int b53_port_enable(struct phy_device *phydev, unsigned int port)
492 {
493 	struct b53_device *dev = phydev->priv;
494 	unsigned int cpu_port = dev->cpu_port;
495 	u16 pvlan;
496 
497 	/* Clear the Rx and Tx disable bits and set to no spanning tree */
498 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0);
499 
500 	/* Set this port, and only this one to be in the default VLAN */
501 	b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
502 	pvlan &= ~0x1ff;
503 	pvlan |= BIT(port);
504 	b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
505 
506 	b53_imp_vlan_setup(dev, cpu_port);
507 
508 	return 0;
509 }
510 
511 static int b53_switch_init(struct phy_device *phydev)
512 {
513 	static int init;
514 	int ret;
515 
516 	if (init)
517 		return 0;
518 
519 	ret = b53_switch_reset(phydev);
520 	if (ret < 0)
521 		return ret;
522 
523 	b53_enable_cpu_port(phydev);
524 
525 	init = 1;
526 
527 	return 0;
528 }
529 
530 static int b53_probe(struct phy_device *phydev)
531 {
532 	struct b53_device *dev;
533 	int ret;
534 
535 	dev = malloc(sizeof(*dev));
536 	if (!dev)
537 		return -ENOMEM;
538 
539 	memset(dev, 0, sizeof(*dev));
540 
541 	phydev->priv = dev;
542 	dev->bus = phydev->bus;
543 	dev->cpu_port = CONFIG_B53_CPU_PORT;
544 
545 	ret = b53_switch_reset(phydev);
546 	if (ret < 0)
547 		return ret;
548 
549 	return 0;
550 }
551 
552 static int b53_phy_config(struct phy_device *phydev)
553 {
554 	unsigned int port;
555 	int res;
556 
557 	res = b53_switch_init(phydev);
558 	if (res < 0)
559 		return res;
560 
561 	for (port = 0; port < B53_N_PORTS; port++) {
562 		if (!((1 << port) & CONFIG_B53_PHY_PORTS))
563 			continue;
564 
565 		res = b53_port_enable(phydev, port);
566 		if (res < 0) {
567 			printf("Error enabling port %i\n", port);
568 			continue;
569 		}
570 
571 		res = genphy_config_aneg(phydev);
572 		if (res < 0) {
573 			printf("Error setting PHY %i autoneg\n", port);
574 			continue;
575 		}
576 
577 		res = 0;
578 	}
579 
580 	return res;
581 }
582 
583 static int b53_phy_startup(struct phy_device *phydev)
584 {
585 	unsigned int port;
586 	int res;
587 
588 	for (port = 0; port < B53_N_PORTS; port++) {
589 		if (!((1 << port) & CONFIG_B53_PHY_PORTS))
590 			continue;
591 
592 		phydev->addr = port;
593 
594 		res = genphy_startup(phydev);
595 		if (res < 0)
596 			continue;
597 		else
598 			break;
599 	}
600 
601 	/* Since we are connected directly to the switch, hardcode the link
602 	 * parameters to match those of the CPU port configured in
603 	 * b53_enable_cpu_port, we cannot be dependent on the user-facing port
604 	 * settings (e.g: 100Mbits/sec would not work here)
605 	 */
606 	phydev->speed = 1000;
607 	phydev->duplex = 1;
608 	phydev->link = 1;
609 
610 	return 0;
611 }
612 
613 static struct phy_driver b53_driver = {
614 	.name = "Broadcom BCM53125",
615 	.uid = 0x03625c00,
616 	.mask = 0xfffffc00,
617 	.features = PHY_GBIT_FEATURES,
618 	.probe = b53_probe,
619 	.config = b53_phy_config,
620 	.startup = b53_phy_startup,
621 	.shutdown = &genphy_shutdown,
622 };
623 
624 int phy_b53_init(void)
625 {
626 	phy_register(&b53_driver);
627 
628 	return 0;
629 }
630 
631 int do_b53_reg_read(const char *name, int argc, char * const argv[])
632 {
633 	u8 page, offset, width;
634 	struct mii_dev *bus;
635 	int ret = -EINVAL;
636 	u64 value64 = 0;
637 	u32 value32 = 0;
638 	u16 value16 = 0;
639 	u8 value8 = 0;
640 
641 	bus = miiphy_get_dev_by_name(name);
642 	if (!bus) {
643 		printf("unable to find MDIO bus: %s\n", name);
644 		return ret;
645 	}
646 
647 	page = simple_strtoul(argv[1], NULL, 16);
648 	offset = simple_strtoul(argv[2], NULL, 16);
649 	width = simple_strtoul(argv[3], NULL, 10);
650 
651 	switch (width) {
652 	case 8:
653 		ret = b53_mdio_read8(bus, page, offset, &value8);
654 		printf("page=0x%02x, offset=0x%02x, value=0x%02x\n",
655 		       page, offset, value8);
656 		break;
657 	case 16:
658 		ret = b53_mdio_read16(bus, page, offset, &value16);
659 		printf("page=0x%02x, offset=0x%02x, value=0x%04x\n",
660 		       page, offset, value16);
661 		break;
662 	case 32:
663 		ret = b53_mdio_read32(bus, page, offset, &value32);
664 		printf("page=0x%02x, offset=0x%02x, value=0x%08x\n",
665 		       page, offset, value32);
666 		break;
667 	case 48:
668 		ret = b53_mdio_read48(bus, page, offset, &value64);
669 		printf("page=0x%02x, offset=0x%02x, value=0x%012llx\n",
670 		       page, offset, value64);
671 		break;
672 	case 64:
673 		ret = b53_mdio_read48(bus, page, offset, &value64);
674 		printf("page=0x%02x, offset=0x%02x, value=0x%016llx\n",
675 		       page, offset, value64);
676 		break;
677 	default:
678 		printf("Unsupported width: %d\n", width);
679 		break;
680 	}
681 
682 	return ret;
683 }
684 
685 int do_b53_reg_write(const char *name, int argc, char * const argv[])
686 {
687 	u8 page, offset, width;
688 	struct mii_dev *bus;
689 	int ret = -EINVAL;
690 	u64 value64 = 0;
691 	u32 value = 0;
692 
693 	bus = miiphy_get_dev_by_name(name);
694 	if (!bus) {
695 		printf("unable to find MDIO bus: %s\n", name);
696 		return ret;
697 	}
698 
699 	page = simple_strtoul(argv[1], NULL, 16);
700 	offset = simple_strtoul(argv[2], NULL, 16);
701 	width = simple_strtoul(argv[3], NULL, 10);
702 	if (width == 48 || width == 64)
703 		value64 = simple_strtoull(argv[4], NULL, 16);
704 	else
705 		value = simple_strtoul(argv[4], NULL, 16);
706 
707 	switch (width) {
708 	case 8:
709 		ret = b53_mdio_write8(bus, page, offset, value & 0xff);
710 		break;
711 	case 16:
712 		ret = b53_mdio_write16(bus, page, offset, value);
713 		break;
714 	case 32:
715 		ret = b53_mdio_write32(bus, page, offset, value);
716 		break;
717 	case 48:
718 		ret = b53_mdio_write48(bus, page, offset, value64);
719 		break;
720 	case 64:
721 		ret = b53_mdio_write64(bus, page, offset, value64);
722 		break;
723 	default:
724 		printf("Unsupported width: %d\n", width);
725 		break;
726 	}
727 
728 	return ret;
729 }
730 
731 int do_b53_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
732 {
733 	const char *cmd, *mdioname;
734 	int ret = 0;
735 
736 	if (argc < 2)
737 		return cmd_usage(cmdtp);
738 
739 	cmd = argv[1];
740 	--argc;
741 	++argv;
742 
743 	if (!strcmp(cmd, "write")) {
744 		if (argc < 4)
745 			return cmd_usage(cmdtp);
746 		mdioname = argv[1];
747 		--argc;
748 		++argv;
749 		ret = do_b53_reg_write(mdioname, argc, argv);
750 	} else if (!strcmp(cmd, "read")) {
751 		if (argc < 5)
752 			return cmd_usage(cmdtp);
753 		mdioname = argv[1];
754 		--argc;
755 		++argv;
756 		ret = do_b53_reg_read(mdioname, argc, argv);
757 	} else {
758 		return cmd_usage(cmdtp);
759 	}
760 
761 	return ret;
762 }
763 
764 U_BOOT_CMD(b53_reg, 7, 1, do_b53_reg,
765 	   "Broadcom B53 switch register access",
766 	   "write mdioname page (hex) offset (hex) width (dec) value (hex)\n"
767 	   "read mdioname page (hex) offset (hex) width (dec)\n"
768 	  );
769