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