// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2011 Freescale Semiconductor, Inc * Andy Fleming */ /* * MDIO Commands */ #include <common.h> #include <command.h> #include <miiphy.h> #include <phy.h> static char last_op[2]; static uint last_data; static uint last_addr_lo; static uint last_addr_hi; static uint last_devad_lo; static uint last_devad_hi; static uint last_reg_lo; static uint last_reg_hi; static int extract_range(char *input, int *plo, int *phi) { char *end; *plo = simple_strtol(input, &end, 16); if (end == input) return -1; if ((*end == '-') && *(++end)) *phi = simple_strtol(end, NULL, 16); else if (*end == '\0') *phi = *plo; else return -1; return 0; } static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus, int addrlo, int addrhi, int devadlo, int devadhi, int reglo, int reghi, unsigned short data, int extended) { int addr, devad, reg; int err = 0; for (addr = addrlo; addr <= addrhi; addr++) { for (devad = devadlo; devad <= devadhi; devad++) { for (reg = reglo; reg <= reghi; reg++) { if (!extended) err = bus->write(bus, addr, devad, reg, data); else err = phydev->drv->writeext(phydev, addr, devad, reg, data); if (err) goto err_out; } } } err_out: return err; } static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus, int addrlo, int addrhi, int devadlo, int devadhi, int reglo, int reghi, int extended) { int addr, devad, reg; printf("Reading from bus %s\n", bus->name); for (addr = addrlo; addr <= addrhi; addr++) { printf("PHY at address %x:\n", addr); for (devad = devadlo; devad <= devadhi; devad++) { for (reg = reglo; reg <= reghi; reg++) { int val; if (!extended) val = bus->read(bus, addr, devad, reg); else val = phydev->drv->readext(phydev, addr, devad, reg); if (val < 0) { printf("Error\n"); return val; } if (devad >= 0) printf("%d.", devad); printf("%d - 0x%x\n", reg, val & 0xffff); } } } return 0; } /* The register will be in the form [a[-b].]x[-y] */ static int extract_reg_range(char *input, int *devadlo, int *devadhi, int *reglo, int *reghi) { char *regstr; /* use strrchr to find the last string after a '.' */ regstr = strrchr(input, '.'); /* If it exists, extract the devad(s) */ if (regstr) { char devadstr[32]; strncpy(devadstr, input, regstr - input); devadstr[regstr - input] = '\0'; if (extract_range(devadstr, devadlo, devadhi)) return -1; regstr++; } else { /* Otherwise, we have no devad, and we just got regs */ *devadlo = *devadhi = MDIO_DEVAD_NONE; regstr = input; } return extract_range(regstr, reglo, reghi); } static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus, struct phy_device **phydev, int *addrlo, int *addrhi) { struct phy_device *dev = *phydev; if ((argc < 1) || (argc > 2)) return -1; /* If there are two arguments, it's busname addr */ if (argc == 2) { *bus = miiphy_get_dev_by_name(argv[0]); if (!*bus) return -1; return extract_range(argv[1], addrlo, addrhi); } /* It must be one argument, here */ /* * This argument can be one of two things: * 1) Ethernet device name * 2) Just an address (use the previously-used bus) * * We check all buses for a PHY which is connected to an ethernet * device by the given name. If none are found, we call * extract_range() on the string, and see if it's an address range. */ dev = mdio_phydev_for_ethname(argv[0]); if (dev) { *addrlo = *addrhi = dev->addr; *bus = dev->bus; return 0; } /* It's an address or nothing useful */ return extract_range(argv[0], addrlo, addrhi); } /* ---------------------------------------------------------------- */ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char op[2]; int addrlo, addrhi, reglo, reghi, devadlo, devadhi; unsigned short data; int pos = argc - 1; struct mii_dev *bus; struct phy_device *phydev = NULL; int extended = 0; if (argc < 2) return CMD_RET_USAGE; /* * We use the last specified parameters, unless new ones are * entered. */ op[0] = argv[1][0]; addrlo = last_addr_lo; addrhi = last_addr_hi; devadlo = last_devad_lo; devadhi = last_devad_hi; reglo = last_reg_lo; reghi = last_reg_hi; data = last_data; bus = mdio_get_current_dev(); if (flag & CMD_FLAG_REPEAT) op[0] = last_op[0]; if (strlen(argv[1]) > 1) { op[1] = argv[1][1]; if (op[1] == 'x') { phydev = mdio_phydev_for_ethname(argv[2]); if (phydev) { addrlo = phydev->addr; addrhi = addrlo; bus = phydev->bus; extended = 1; } else { return -1; } if (!phydev->drv || (!phydev->drv->writeext && (op[0] == 'w')) || (!phydev->drv->readext && (op[0] == 'r'))) { puts("PHY does not have extended functions\n"); return -1; } } } switch (op[0]) { case 'w': if (pos > 1) data = simple_strtoul(argv[pos--], NULL, 16); case 'r': if (pos > 1) if (extract_reg_range(argv[pos--], &devadlo, &devadhi, ®lo, ®hi)) return -1; default: if (pos > 1) if (extract_phy_range(&argv[2], pos - 1, &bus, &phydev, &addrlo, &addrhi)) return -1; break; } if (op[0] == 'l') { mdio_list_devices(); return 0; } /* Save the chosen bus */ miiphy_set_current_dev(bus->name); switch (op[0]) { case 'w': mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, reglo, reghi, data, extended); break; case 'r': mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi, reglo, reghi, extended); break; } /* * Save the parameters for repeats. */ last_op[0] = op[0]; last_addr_lo = addrlo; last_addr_hi = addrhi; last_devad_lo = devadlo; last_devad_hi = devadhi; last_reg_lo = reglo; last_reg_hi = reghi; last_data = data; return 0; } /***************************************************/ U_BOOT_CMD( mdio, 6, 1, do_mdio, "MDIO utility commands", "list - List MDIO buses\n" "mdio read <phydev> [<devad>.]<reg> - " "read PHY's register at <devad>.<reg>\n" "mdio write <phydev> [<devad>.]<reg> <data> - " "write PHY's register at <devad>.<reg>\n" "mdio rx <phydev> [<devad>.]<reg> - " "read PHY's extended register at <devad>.<reg>\n" "mdio wx <phydev> [<devad>.]<reg> <data> - " "write PHY's extended register at <devad>.<reg>\n" "<phydev> may be:\n" " <busname> <addr>\n" " <addr>\n" " <eth name>\n" "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n" );