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