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 ®lo, ®hi)) 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