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