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