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