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