uboot/cmd/mdio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2011 Freescale Semiconductor, Inc
   4 * Andy Fleming
   5 */
   6
   7/*
   8 * MDIO Commands
   9 */
  10
  11#include <common.h>
  12#include <command.h>
  13#include <miiphy.h>
  14#include <phy.h>
  15
  16static char last_op[2];
  17static uint last_data;
  18static uint last_addr_lo;
  19static uint last_addr_hi;
  20static uint last_devad_lo;
  21static uint last_devad_hi;
  22static uint last_reg_lo;
  23static uint last_reg_hi;
  24
  25static int extract_range(char *input, int *plo, int *phi)
  26{
  27        char *end;
  28        *plo = simple_strtol(input, &end, 16);
  29        if (end == input)
  30                return -1;
  31
  32        if ((*end == '-') && *(++end))
  33                *phi = simple_strtol(end, NULL, 16);
  34        else if (*end == '\0')
  35                *phi = *plo;
  36        else
  37                return -1;
  38
  39        return 0;
  40}
  41
  42static int mdio_write_ranges(struct mii_dev *bus,
  43                             int addrlo,
  44                             int addrhi, int devadlo, int devadhi,
  45                             int reglo, int reghi, unsigned short data,
  46                             int extended)
  47{
  48        struct phy_device *phydev;
  49        int addr, devad, reg;
  50        int err = 0;
  51
  52        for (addr = addrlo; addr <= addrhi; addr++) {
  53                phydev = bus->phymap[addr];
  54
  55                for (devad = devadlo; devad <= devadhi; devad++) {
  56                        for (reg = reglo; reg <= reghi; reg++) {
  57                                if (!phydev)
  58                                        err = bus->write(bus, addr, devad,
  59                                                         reg, data);
  60                                else if (!extended)
  61                                        err = phy_write_mmd(phydev, devad,
  62                                                            reg, data);
  63                                else
  64                                        err = phydev->drv->writeext(phydev,
  65                                                        addr, devad, reg, data);
  66
  67                                if (err)
  68                                        goto err_out;
  69                        }
  70                }
  71        }
  72
  73err_out:
  74        return err;
  75}
  76
  77static int mdio_read_ranges(struct mii_dev *bus,
  78                            int addrlo,
  79                            int addrhi, int devadlo, int devadhi,
  80                            int reglo, int reghi, int extended)
  81{
  82        int addr, devad, reg;
  83        struct phy_device *phydev;
  84
  85        printf("Reading from bus %s\n", bus->name);
  86        for (addr = addrlo; addr <= addrhi; addr++) {
  87                phydev = bus->phymap[addr];
  88                printf("PHY at address %x:\n", addr);
  89
  90                for (devad = devadlo; devad <= devadhi; devad++) {
  91                        for (reg = reglo; reg <= reghi; reg++) {
  92                                int val;
  93
  94                                if (!phydev)
  95                                        val = bus->read(bus, addr, devad, reg);
  96                                else if (!extended)
  97                                        val = phy_read_mmd(phydev, devad, reg);
  98                                else
  99                                        val = phydev->drv->readext(phydev, addr,
 100                                                devad, reg);
 101
 102                                if (val < 0) {
 103                                        printf("Error\n");
 104
 105                                        return val;
 106                                }
 107
 108                                if (devad >= 0)
 109                                        printf("%d.", devad);
 110
 111                                printf("%d - 0x%x\n", reg, val & 0xffff);
 112                        }
 113                }
 114        }
 115
 116        return 0;
 117}
 118
 119/* The register will be in the form [a[-b].]x[-y] */
 120static int extract_reg_range(char *input, int *devadlo, int *devadhi,
 121                             int *reglo, int *reghi)
 122{
 123        char *regstr;
 124
 125        /* use strrchr to find the last string after a '.' */
 126        regstr = strrchr(input, '.');
 127
 128        /* If it exists, extract the devad(s) */
 129        if (regstr) {
 130                char devadstr[32];
 131
 132                strncpy(devadstr, input, regstr - input);
 133                devadstr[regstr - input] = '\0';
 134
 135                if (extract_range(devadstr, devadlo, devadhi))
 136                        return -1;
 137
 138                regstr++;
 139        } else {
 140                /* Otherwise, we have no devad, and we just got regs */
 141                *devadlo = *devadhi = MDIO_DEVAD_NONE;
 142
 143                regstr = input;
 144        }
 145
 146        return extract_range(regstr, reglo, reghi);
 147}
 148
 149static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus,
 150                             struct phy_device **phydev,
 151                             int *addrlo, int *addrhi)
 152{
 153        struct phy_device *dev = *phydev;
 154
 155        if ((argc < 1) || (argc > 2))
 156                return -1;
 157
 158        /* If there are two arguments, it's busname addr */
 159        if (argc == 2) {
 160                *bus = miiphy_get_dev_by_name(argv[0]);
 161
 162                if (!*bus)
 163                        return -1;
 164
 165                return extract_range(argv[1], addrlo, addrhi);
 166        }
 167
 168        /* It must be one argument, here */
 169
 170        /*
 171         * This argument can be one of two things:
 172         * 1) Ethernet device name
 173         * 2) Just an address (use the previously-used bus)
 174         *
 175         * We check all buses for a PHY which is connected to an ethernet
 176         * device by the given name.  If none are found, we call
 177         * extract_range() on the string, and see if it's an address range.
 178         */
 179        dev = mdio_phydev_for_ethname(argv[0]);
 180
 181        if (dev) {
 182                *addrlo = *addrhi = dev->addr;
 183                *bus = dev->bus;
 184
 185                return 0;
 186        }
 187
 188        /* It's an address or nothing useful */
 189        return extract_range(argv[0], addrlo, addrhi);
 190}
 191
 192/* ---------------------------------------------------------------- */
 193static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 194{
 195        char op[2];
 196        int addrlo, addrhi, reglo, reghi, devadlo, devadhi;
 197        unsigned short  data;
 198        int pos = argc - 1;
 199        struct mii_dev *bus;
 200        struct phy_device *phydev = NULL;
 201        int extended = 0;
 202
 203        if (argc < 2)
 204                return CMD_RET_USAGE;
 205
 206#ifdef CONFIG_DM_MDIO
 207        /* probe DM MII device before any operation so they are all accesible */
 208        dm_mdio_probe_devices();
 209#endif
 210
 211        /*
 212         * We use the last specified parameters, unless new ones are
 213         * entered.
 214         */
 215        op[0] = argv[1][0];
 216        addrlo = last_addr_lo;
 217        addrhi = last_addr_hi;
 218        devadlo = last_devad_lo;
 219        devadhi = last_devad_hi;
 220        reglo  = last_reg_lo;
 221        reghi  = last_reg_hi;
 222        data   = last_data;
 223
 224        bus = mdio_get_current_dev();
 225
 226        if (flag & CMD_FLAG_REPEAT)
 227                op[0] = last_op[0];
 228
 229        if (strlen(argv[1]) > 1) {
 230                op[1] = argv[1][1];
 231                if (op[1] == 'x') {
 232                        phydev = mdio_phydev_for_ethname(argv[2]);
 233
 234                        if (phydev) {
 235                                addrlo = phydev->addr;
 236                                addrhi = addrlo;
 237                                bus = phydev->bus;
 238                                extended = 1;
 239                        } else {
 240                                return CMD_RET_FAILURE;
 241                        }
 242
 243                        if (!phydev->drv ||
 244                            (!phydev->drv->writeext && (op[0] == 'w')) ||
 245                            (!phydev->drv->readext && (op[0] == 'r'))) {
 246                                puts("PHY does not have extended functions\n");
 247                                return CMD_RET_FAILURE;
 248                        }
 249                }
 250        }
 251
 252        switch (op[0]) {
 253        case 'w':
 254                if (pos > 1)
 255                        data = simple_strtoul(argv[pos--], NULL, 16);
 256                /* Intentional fall-through - Get reg for read and write */
 257        case 'r':
 258                if (pos > 1)
 259                        if (extract_reg_range(argv[pos--], &devadlo, &devadhi,
 260                                              &reglo, &reghi))
 261                                return CMD_RET_FAILURE;
 262                /* Intentional fall-through - Get phy for all commands */
 263        default:
 264                if (pos > 1)
 265                        if (extract_phy_range(&argv[2], pos - 1, &bus,
 266                                              &phydev, &addrlo, &addrhi))
 267                                return CMD_RET_FAILURE;
 268
 269                break;
 270        }
 271
 272        if (!bus) {
 273                puts("No MDIO bus found\n");
 274                return CMD_RET_FAILURE;
 275        }
 276
 277        if (op[0] == 'l') {
 278                mdio_list_devices();
 279
 280                return 0;
 281        }
 282
 283        /* Save the chosen bus */
 284        miiphy_set_current_dev(bus->name);
 285
 286        switch (op[0]) {
 287        case 'w':
 288                mdio_write_ranges(bus, addrlo, addrhi, devadlo, devadhi,
 289                                  reglo, reghi, data, extended);
 290                break;
 291
 292        case 'r':
 293                mdio_read_ranges(bus, addrlo, addrhi, devadlo, devadhi,
 294                                 reglo, reghi, extended);
 295                break;
 296        }
 297
 298        /*
 299         * Save the parameters for repeats.
 300         */
 301        last_op[0] = op[0];
 302        last_addr_lo = addrlo;
 303        last_addr_hi = addrhi;
 304        last_devad_lo = devadlo;
 305        last_devad_hi = devadhi;
 306        last_reg_lo  = reglo;
 307        last_reg_hi  = reghi;
 308        last_data    = data;
 309
 310        return 0;
 311}
 312
 313/***************************************************/
 314
 315U_BOOT_CMD(
 316        mdio,   6,      1,      do_mdio,
 317        "MDIO utility commands",
 318        "list                   - List MDIO buses\n"
 319        "mdio read <phydev> [<devad>.]<reg> - "
 320                "read PHY's register at <devad>.<reg>\n"
 321        "mdio write <phydev> [<devad>.]<reg> <data> - "
 322                "write PHY's register at <devad>.<reg>\n"
 323        "mdio rx <phydev> [<devad>.]<reg> - "
 324                "read PHY's extended register at <devad>.<reg>\n"
 325        "mdio wx <phydev> [<devad>.]<reg> <data> - "
 326                "write PHY's extended register at <devad>.<reg>\n"
 327        "<phydev> may be:\n"
 328        "   <busname>  <addr>\n"
 329        "   <addr>\n"
 330        "   <eth name>\n"
 331        "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n"
 332);
 333