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