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