1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2001 4 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 5 */ 6 7 /* 8 * MII Utilities 9 */ 10 11 #include <common.h> 12 #include <command.h> 13 #include <miiphy.h> 14 15 typedef struct _MII_reg_desc_t { 16 ushort regno; 17 char * name; 18 } MII_reg_desc_t; 19 20 static const MII_reg_desc_t reg_0_5_desc_tbl[] = { 21 { MII_BMCR, "PHY control register" }, 22 { MII_BMSR, "PHY status register" }, 23 { MII_PHYSID1, "PHY ID 1 register" }, 24 { MII_PHYSID2, "PHY ID 2 register" }, 25 { MII_ADVERTISE, "Autonegotiation advertisement register" }, 26 { MII_LPA, "Autonegotiation partner abilities register" }, 27 }; 28 29 typedef struct _MII_field_desc_t { 30 ushort hi; 31 ushort lo; 32 ushort mask; 33 char * name; 34 } MII_field_desc_t; 35 36 static const MII_field_desc_t reg_0_desc_tbl[] = { 37 { 15, 15, 0x01, "reset" }, 38 { 14, 14, 0x01, "loopback" }, 39 { 13, 6, 0x81, "speed selection" }, /* special */ 40 { 12, 12, 0x01, "A/N enable" }, 41 { 11, 11, 0x01, "power-down" }, 42 { 10, 10, 0x01, "isolate" }, 43 { 9, 9, 0x01, "restart A/N" }, 44 { 8, 8, 0x01, "duplex" }, /* special */ 45 { 7, 7, 0x01, "collision test enable" }, 46 { 5, 0, 0x3f, "(reserved)" } 47 }; 48 49 static const MII_field_desc_t reg_1_desc_tbl[] = { 50 { 15, 15, 0x01, "100BASE-T4 able" }, 51 { 14, 14, 0x01, "100BASE-X full duplex able" }, 52 { 13, 13, 0x01, "100BASE-X half duplex able" }, 53 { 12, 12, 0x01, "10 Mbps full duplex able" }, 54 { 11, 11, 0x01, "10 Mbps half duplex able" }, 55 { 10, 10, 0x01, "100BASE-T2 full duplex able" }, 56 { 9, 9, 0x01, "100BASE-T2 half duplex able" }, 57 { 8, 8, 0x01, "extended status" }, 58 { 7, 7, 0x01, "(reserved)" }, 59 { 6, 6, 0x01, "MF preamble suppression" }, 60 { 5, 5, 0x01, "A/N complete" }, 61 { 4, 4, 0x01, "remote fault" }, 62 { 3, 3, 0x01, "A/N able" }, 63 { 2, 2, 0x01, "link status" }, 64 { 1, 1, 0x01, "jabber detect" }, 65 { 0, 0, 0x01, "extended capabilities" }, 66 }; 67 68 static const MII_field_desc_t reg_2_desc_tbl[] = { 69 { 15, 0, 0xffff, "OUI portion" }, 70 }; 71 72 static const MII_field_desc_t reg_3_desc_tbl[] = { 73 { 15, 10, 0x3f, "OUI portion" }, 74 { 9, 4, 0x3f, "manufacturer part number" }, 75 { 3, 0, 0x0f, "manufacturer rev. number" }, 76 }; 77 78 static const MII_field_desc_t reg_4_desc_tbl[] = { 79 { 15, 15, 0x01, "next page able" }, 80 { 14, 14, 0x01, "(reserved)" }, 81 { 13, 13, 0x01, "remote fault" }, 82 { 12, 12, 0x01, "(reserved)" }, 83 { 11, 11, 0x01, "asymmetric pause" }, 84 { 10, 10, 0x01, "pause enable" }, 85 { 9, 9, 0x01, "100BASE-T4 able" }, 86 { 8, 8, 0x01, "100BASE-TX full duplex able" }, 87 { 7, 7, 0x01, "100BASE-TX able" }, 88 { 6, 6, 0x01, "10BASE-T full duplex able" }, 89 { 5, 5, 0x01, "10BASE-T able" }, 90 { 4, 0, 0x1f, "xxx to do" }, 91 }; 92 93 static const MII_field_desc_t reg_5_desc_tbl[] = { 94 { 15, 15, 0x01, "next page able" }, 95 { 14, 14, 0x01, "acknowledge" }, 96 { 13, 13, 0x01, "remote fault" }, 97 { 12, 12, 0x01, "(reserved)" }, 98 { 11, 11, 0x01, "asymmetric pause able" }, 99 { 10, 10, 0x01, "pause able" }, 100 { 9, 9, 0x01, "100BASE-T4 able" }, 101 { 8, 8, 0x01, "100BASE-X full duplex able" }, 102 { 7, 7, 0x01, "100BASE-TX able" }, 103 { 6, 6, 0x01, "10BASE-T full duplex able" }, 104 { 5, 5, 0x01, "10BASE-T able" }, 105 { 4, 0, 0x1f, "xxx to do" }, 106 }; 107 typedef struct _MII_field_desc_and_len_t { 108 const MII_field_desc_t *pdesc; 109 ushort len; 110 } MII_field_desc_and_len_t; 111 112 static const MII_field_desc_and_len_t desc_and_len_tbl[] = { 113 { reg_0_desc_tbl, ARRAY_SIZE(reg_0_desc_tbl) }, 114 { reg_1_desc_tbl, ARRAY_SIZE(reg_1_desc_tbl) }, 115 { reg_2_desc_tbl, ARRAY_SIZE(reg_2_desc_tbl) }, 116 { reg_3_desc_tbl, ARRAY_SIZE(reg_3_desc_tbl) }, 117 { reg_4_desc_tbl, ARRAY_SIZE(reg_4_desc_tbl) }, 118 { reg_5_desc_tbl, ARRAY_SIZE(reg_5_desc_tbl) }, 119 }; 120 121 static void dump_reg( 122 ushort regval, 123 const MII_reg_desc_t *prd, 124 const MII_field_desc_and_len_t *pdl); 125 126 static int special_field( 127 ushort regno, 128 const MII_field_desc_t *pdesc, 129 ushort regval); 130 131 static void MII_dump_0_to_5( 132 ushort regvals[6], 133 uchar reglo, 134 uchar reghi) 135 { 136 ulong i; 137 138 for (i = 0; i < 6; i++) { 139 if ((reglo <= i) && (i <= reghi)) 140 dump_reg(regvals[i], ®_0_5_desc_tbl[i], 141 &desc_and_len_tbl[i]); 142 } 143 } 144 145 static void dump_reg( 146 ushort regval, 147 const MII_reg_desc_t *prd, 148 const MII_field_desc_and_len_t *pdl) 149 { 150 ulong i; 151 ushort mask_in_place; 152 const MII_field_desc_t *pdesc; 153 154 printf("%u. (%04hx) -- %s --\n", 155 prd->regno, regval, prd->name); 156 157 for (i = 0; i < pdl->len; i++) { 158 pdesc = &pdl->pdesc[i]; 159 160 mask_in_place = pdesc->mask << pdesc->lo; 161 162 printf(" (%04hx:%04x) %u.", 163 mask_in_place, 164 regval & mask_in_place, 165 prd->regno); 166 167 if (special_field(prd->regno, pdesc, regval)) { 168 } 169 else { 170 if (pdesc->hi == pdesc->lo) 171 printf("%2u ", pdesc->lo); 172 else 173 printf("%2u-%2u", pdesc->hi, pdesc->lo); 174 printf(" = %5u %s", 175 (regval & mask_in_place) >> pdesc->lo, 176 pdesc->name); 177 } 178 printf("\n"); 179 180 } 181 printf("\n"); 182 } 183 184 /* Special fields: 185 ** 0.6,13 186 ** 0.8 187 ** 2.15-0 188 ** 3.15-0 189 ** 4.4-0 190 ** 5.4-0 191 */ 192 193 static int special_field( 194 ushort regno, 195 const MII_field_desc_t *pdesc, 196 ushort regval) 197 { 198 if ((regno == MII_BMCR) && (pdesc->lo == 6)) { 199 ushort speed_bits = regval & (BMCR_SPEED1000 | BMCR_SPEED100); 200 printf("%2u,%2u = b%u%u speed selection = %s Mbps", 201 6, 13, 202 (regval >> 6) & 1, 203 (regval >> 13) & 1, 204 speed_bits == BMCR_SPEED1000 ? "1000" : 205 speed_bits == BMCR_SPEED100 ? "100" : 206 "10"); 207 return 1; 208 } 209 210 else if ((regno == MII_BMCR) && (pdesc->lo == 8)) { 211 printf("%2u = %5u duplex = %s", 212 pdesc->lo, 213 (regval >> pdesc->lo) & 1, 214 ((regval >> pdesc->lo) & 1) ? "full" : "half"); 215 return 1; 216 } 217 218 else if ((regno == MII_ADVERTISE) && (pdesc->lo == 0)) { 219 ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; 220 printf("%2u-%2u = %5u selector = %s", 221 pdesc->hi, pdesc->lo, sel_bits, 222 sel_bits == PHY_ANLPAR_PSB_802_3 ? 223 "IEEE 802.3" : 224 sel_bits == PHY_ANLPAR_PSB_802_9 ? 225 "IEEE 802.9 ISLAN-16T" : 226 "???"); 227 return 1; 228 } 229 230 else if ((regno == MII_LPA) && (pdesc->lo == 0)) { 231 ushort sel_bits = (regval >> pdesc->lo) & pdesc->mask; 232 printf("%2u-%2u = %u selector = %s", 233 pdesc->hi, pdesc->lo, sel_bits, 234 sel_bits == PHY_ANLPAR_PSB_802_3 ? 235 "IEEE 802.3" : 236 sel_bits == PHY_ANLPAR_PSB_802_9 ? 237 "IEEE 802.9 ISLAN-16T" : 238 "???"); 239 return 1; 240 } 241 242 return 0; 243 } 244 245 static char last_op[2]; 246 static uint last_data; 247 static uint last_addr_lo; 248 static uint last_addr_hi; 249 static uint last_reg_lo; 250 static uint last_reg_hi; 251 static uint last_mask; 252 253 static void extract_range( 254 char * input, 255 unsigned char * plo, 256 unsigned char * phi) 257 { 258 char * end; 259 *plo = simple_strtoul(input, &end, 16); 260 if (*end == '-') { 261 end++; 262 *phi = simple_strtoul(end, NULL, 16); 263 } 264 else { 265 *phi = *plo; 266 } 267 } 268 269 /* ---------------------------------------------------------------- */ 270 static int do_mii(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 271 { 272 char op[2]; 273 unsigned char addrlo, addrhi, reglo, reghi; 274 unsigned char addr, reg; 275 unsigned short data, mask; 276 int rcode = 0; 277 const char *devname; 278 279 if (argc < 2) 280 return CMD_RET_USAGE; 281 282 #if defined(CONFIG_MII_INIT) 283 mii_init (); 284 #endif 285 286 /* 287 * We use the last specified parameters, unless new ones are 288 * entered. 289 */ 290 op[0] = last_op[0]; 291 op[1] = last_op[1]; 292 addrlo = last_addr_lo; 293 addrhi = last_addr_hi; 294 reglo = last_reg_lo; 295 reghi = last_reg_hi; 296 data = last_data; 297 mask = last_mask; 298 299 if ((flag & CMD_FLAG_REPEAT) == 0) { 300 op[0] = argv[1][0]; 301 if (strlen(argv[1]) > 1) 302 op[1] = argv[1][1]; 303 else 304 op[1] = '\0'; 305 306 if (argc >= 3) 307 extract_range(argv[2], &addrlo, &addrhi); 308 if (argc >= 4) 309 extract_range(argv[3], ®lo, ®hi); 310 if (argc >= 5) 311 data = simple_strtoul(argv[4], NULL, 16); 312 if (argc >= 6) 313 mask = simple_strtoul(argv[5], NULL, 16); 314 } 315 316 if (addrhi > 31) { 317 printf("Incorrect PHY address. Range should be 0-31\n"); 318 return CMD_RET_USAGE; 319 } 320 321 /* use current device */ 322 devname = miiphy_get_current_dev(); 323 324 /* 325 * check info/read/write. 326 */ 327 if (op[0] == 'i') { 328 unsigned char j, start, end; 329 unsigned int oui; 330 unsigned char model; 331 unsigned char rev; 332 333 /* 334 * Look for any and all PHYs. Valid addresses are 0..31. 335 */ 336 if (argc >= 3) { 337 start = addrlo; end = addrhi; 338 } else { 339 start = 0; end = 31; 340 } 341 342 for (j = start; j <= end; j++) { 343 if (miiphy_info (devname, j, &oui, &model, &rev) == 0) { 344 printf("PHY 0x%02X: " 345 "OUI = 0x%04X, " 346 "Model = 0x%02X, " 347 "Rev = 0x%02X, " 348 "%3dbase%s, %s\n", 349 j, oui, model, rev, 350 miiphy_speed (devname, j), 351 miiphy_is_1000base_x (devname, j) 352 ? "X" : "T", 353 (miiphy_duplex (devname, j) == FULL) 354 ? "FDX" : "HDX"); 355 } 356 } 357 } else if (op[0] == 'r') { 358 for (addr = addrlo; addr <= addrhi; addr++) { 359 for (reg = reglo; reg <= reghi; reg++) { 360 data = 0xffff; 361 if (miiphy_read (devname, addr, reg, &data) != 0) { 362 printf( 363 "Error reading from the PHY addr=%02x reg=%02x\n", 364 addr, reg); 365 rcode = 1; 366 } else { 367 if ((addrlo != addrhi) || (reglo != reghi)) 368 printf("addr=%02x reg=%02x data=", 369 (uint)addr, (uint)reg); 370 printf("%04X\n", data & 0x0000FFFF); 371 } 372 } 373 if ((addrlo != addrhi) && (reglo != reghi)) 374 printf("\n"); 375 } 376 } else if (op[0] == 'w') { 377 for (addr = addrlo; addr <= addrhi; addr++) { 378 for (reg = reglo; reg <= reghi; reg++) { 379 if (miiphy_write (devname, addr, reg, data) != 0) { 380 printf("Error writing to the PHY addr=%02x reg=%02x\n", 381 addr, reg); 382 rcode = 1; 383 } 384 } 385 } 386 } else if (op[0] == 'm') { 387 for (addr = addrlo; addr <= addrhi; addr++) { 388 for (reg = reglo; reg <= reghi; reg++) { 389 unsigned short val = 0; 390 if (miiphy_read(devname, addr, 391 reg, &val)) { 392 printf("Error reading from the PHY"); 393 printf(" addr=%02x", addr); 394 printf(" reg=%02x\n", reg); 395 rcode = 1; 396 } else { 397 val = (val & ~mask) | (data & mask); 398 if (miiphy_write(devname, addr, 399 reg, val)) { 400 printf("Error writing to the PHY"); 401 printf(" addr=%02x", addr); 402 printf(" reg=%02x\n", reg); 403 rcode = 1; 404 } 405 } 406 } 407 } 408 } else if (strncmp(op, "du", 2) == 0) { 409 ushort regs[6]; 410 int ok = 1; 411 if ((reglo > 5) || (reghi > 5)) { 412 printf( 413 "The MII dump command only formats the " 414 "standard MII registers, 0-5.\n"); 415 return 1; 416 } 417 for (addr = addrlo; addr <= addrhi; addr++) { 418 for (reg = reglo; reg < reghi + 1; reg++) { 419 if (miiphy_read(devname, addr, reg, ®s[reg]) != 0) { 420 ok = 0; 421 printf( 422 "Error reading from the PHY addr=%02x reg=%02x\n", 423 addr, reg); 424 rcode = 1; 425 } 426 } 427 if (ok) 428 MII_dump_0_to_5(regs, reglo, reghi); 429 printf("\n"); 430 } 431 } else if (strncmp(op, "de", 2) == 0) { 432 if (argc == 2) 433 miiphy_listdev (); 434 else 435 miiphy_set_current_dev (argv[2]); 436 } else { 437 return CMD_RET_USAGE; 438 } 439 440 /* 441 * Save the parameters for repeats. 442 */ 443 last_op[0] = op[0]; 444 last_op[1] = op[1]; 445 last_addr_lo = addrlo; 446 last_addr_hi = addrhi; 447 last_reg_lo = reglo; 448 last_reg_hi = reghi; 449 last_data = data; 450 last_mask = mask; 451 452 return rcode; 453 } 454 455 /***************************************************/ 456 457 U_BOOT_CMD( 458 mii, 6, 1, do_mii, 459 "MII utility commands", 460 "device - list available devices\n" 461 "mii device <devname> - set current device\n" 462 "mii info <addr> - display MII PHY info\n" 463 "mii read <addr> <reg> - read MII PHY <addr> register <reg>\n" 464 "mii write <addr> <reg> <data> - write MII PHY <addr> register <reg>\n" 465 "mii modify <addr> <reg> <data> <mask> - modify MII PHY <addr> register <reg>\n" 466 " updating bits identified in <mask>\n" 467 "mii dump <addr> <reg> - pretty-print <addr> <reg> (0-5 only)\n" 468 "Addr and/or reg may be ranges, e.g. 2-7." 469 ); 470