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, ®); 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, ®); 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