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