uboot/drivers/net/phy/b53.c
<<
>>
Prefs
   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