Merge git://git.denx.de/u-boot-video
[platform/kernel/u-boot.git] / cmd / mdio.c
1 /*
2  * (C) Copyright 2011 Freescale Semiconductor, Inc
3  * Andy Fleming
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 /*
9  * MDIO Commands
10  */
11
12 #include <common.h>
13 #include <command.h>
14 #include <miiphy.h>
15 #include <phy.h>
16
17
18 static char last_op[2];
19 static uint last_data;
20 static uint last_addr_lo;
21 static uint last_addr_hi;
22 static uint last_devad_lo;
23 static uint last_devad_hi;
24 static uint last_reg_lo;
25 static uint last_reg_hi;
26
27 static int extract_range(char *input, int *plo, int *phi)
28 {
29         char *end;
30         *plo = simple_strtol(input, &end, 16);
31         if (end == input)
32                 return -1;
33
34         if ((*end == '-') && *(++end))
35                 *phi = simple_strtol(end, NULL, 16);
36         else if (*end == '\0')
37                 *phi = *plo;
38         else
39                 return -1;
40
41         return 0;
42 }
43
44 static int mdio_write_ranges(struct phy_device *phydev, struct mii_dev *bus,
45                              int addrlo,
46                              int addrhi, int devadlo, int devadhi,
47                              int reglo, int reghi, unsigned short data,
48                              int extended)
49 {
50         int addr, devad, reg;
51         int err = 0;
52
53         for (addr = addrlo; addr <= addrhi; addr++) {
54                 for (devad = devadlo; devad <= devadhi; devad++) {
55                         for (reg = reglo; reg <= reghi; reg++) {
56                                 if (!extended)
57                                         err = bus->write(bus, addr, devad,
58                                                          reg, data);
59                                 else
60                                         err = phydev->drv->writeext(phydev,
61                                                         addr, devad, reg, data);
62
63                                 if (err)
64                                         goto err_out;
65                         }
66                 }
67         }
68
69 err_out:
70         return err;
71 }
72
73 static int mdio_read_ranges(struct phy_device *phydev, struct mii_dev *bus,
74                             int addrlo,
75                             int addrhi, int devadlo, int devadhi,
76                             int reglo, int reghi, int extended)
77 {
78         int addr, devad, reg;
79
80         printf("Reading from bus %s\n", bus->name);
81         for (addr = addrlo; addr <= addrhi; addr++) {
82                 printf("PHY at address %x:\n", addr);
83
84                 for (devad = devadlo; devad <= devadhi; devad++) {
85                         for (reg = reglo; reg <= reghi; reg++) {
86                                 int val;
87
88                                 if (!extended)
89                                         val = bus->read(bus, addr, devad, reg);
90                                 else
91                                         val = phydev->drv->readext(phydev, addr,
92                                                 devad, reg);
93
94                                 if (val < 0) {
95                                         printf("Error\n");
96
97                                         return val;
98                                 }
99
100                                 if (devad >= 0)
101                                         printf("%d.", devad);
102
103                                 printf("%d - 0x%x\n", reg, val & 0xffff);
104                         }
105                 }
106         }
107
108         return 0;
109 }
110
111 /* The register will be in the form [a[-b].]x[-y] */
112 static int extract_reg_range(char *input, int *devadlo, int *devadhi,
113                              int *reglo, int *reghi)
114 {
115         char *regstr;
116
117         /* use strrchr to find the last string after a '.' */
118         regstr = strrchr(input, '.');
119
120         /* If it exists, extract the devad(s) */
121         if (regstr) {
122                 char devadstr[32];
123
124                 strncpy(devadstr, input, regstr - input);
125                 devadstr[regstr - input] = '\0';
126
127                 if (extract_range(devadstr, devadlo, devadhi))
128                         return -1;
129
130                 regstr++;
131         } else {
132                 /* Otherwise, we have no devad, and we just got regs */
133                 *devadlo = *devadhi = MDIO_DEVAD_NONE;
134
135                 regstr = input;
136         }
137
138         return extract_range(regstr, reglo, reghi);
139 }
140
141 static int extract_phy_range(char *const argv[], int argc, struct mii_dev **bus,
142                              struct phy_device **phydev,
143                              int *addrlo, int *addrhi)
144 {
145         struct phy_device *dev = *phydev;
146
147         if ((argc < 1) || (argc > 2))
148                 return -1;
149
150         /* If there are two arguments, it's busname addr */
151         if (argc == 2) {
152                 *bus = miiphy_get_dev_by_name(argv[0]);
153
154                 if (!*bus)
155                         return -1;
156
157                 return extract_range(argv[1], addrlo, addrhi);
158         }
159
160         /* It must be one argument, here */
161
162         /*
163          * This argument can be one of two things:
164          * 1) Ethernet device name
165          * 2) Just an address (use the previously-used bus)
166          *
167          * We check all buses for a PHY which is connected to an ethernet
168          * device by the given name.  If none are found, we call
169          * extract_range() on the string, and see if it's an address range.
170          */
171         dev = mdio_phydev_for_ethname(argv[0]);
172
173         if (dev) {
174                 *addrlo = *addrhi = dev->addr;
175                 *bus = dev->bus;
176
177                 return 0;
178         }
179
180         /* It's an address or nothing useful */
181         return extract_range(argv[0], addrlo, addrhi);
182 }
183
184 /* ---------------------------------------------------------------- */
185 static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
186 {
187         char op[2];
188         int addrlo, addrhi, reglo, reghi, devadlo, devadhi;
189         unsigned short  data;
190         int pos = argc - 1;
191         struct mii_dev *bus;
192         struct phy_device *phydev = NULL;
193         int extended = 0;
194
195         if (argc < 2)
196                 return CMD_RET_USAGE;
197
198         /*
199          * We use the last specified parameters, unless new ones are
200          * entered.
201          */
202         op[0] = argv[1][0];
203         addrlo = last_addr_lo;
204         addrhi = last_addr_hi;
205         devadlo = last_devad_lo;
206         devadhi = last_devad_hi;
207         reglo  = last_reg_lo;
208         reghi  = last_reg_hi;
209         data   = last_data;
210
211         bus = mdio_get_current_dev();
212
213         if (flag & CMD_FLAG_REPEAT)
214                 op[0] = last_op[0];
215
216         if (strlen(argv[1]) > 1) {
217                 op[1] = argv[1][1];
218                 if (op[1] == 'x') {
219                         phydev = mdio_phydev_for_ethname(argv[2]);
220
221                         if (phydev) {
222                                 addrlo = phydev->addr;
223                                 addrhi = addrlo;
224                                 bus = phydev->bus;
225                                 extended = 1;
226                         } else {
227                                 return -1;
228                         }
229
230                         if (!phydev->drv ||
231                             (!phydev->drv->writeext && (op[0] == 'w')) ||
232                             (!phydev->drv->readext && (op[0] == 'r'))) {
233                                 puts("PHY does not have extended functions\n");
234                                 return -1;
235                         }
236                 }
237         }
238
239         switch (op[0]) {
240         case 'w':
241                 if (pos > 1)
242                         data = simple_strtoul(argv[pos--], NULL, 16);
243         case 'r':
244                 if (pos > 1)
245                         if (extract_reg_range(argv[pos--], &devadlo, &devadhi,
246                                         &reglo, &reghi))
247                                 return -1;
248
249         default:
250                 if (pos > 1)
251                         if (extract_phy_range(&(argv[2]), pos - 1, &bus,
252                                         &phydev, &addrlo, &addrhi))
253                                 return -1;
254
255                 break;
256         }
257
258         if (op[0] == 'l') {
259                 mdio_list_devices();
260
261                 return 0;
262         }
263
264         /* Save the chosen bus */
265         miiphy_set_current_dev(bus->name);
266
267         switch (op[0]) {
268         case 'w':
269                 mdio_write_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi,
270                                   reglo, reghi, data, extended);
271                 break;
272
273         case 'r':
274                 mdio_read_ranges(phydev, bus, addrlo, addrhi, devadlo, devadhi,
275                                  reglo, reghi, extended);
276                 break;
277         }
278
279         /*
280          * Save the parameters for repeats.
281          */
282         last_op[0] = op[0];
283         last_addr_lo = addrlo;
284         last_addr_hi = addrhi;
285         last_devad_lo = devadlo;
286         last_devad_hi = devadhi;
287         last_reg_lo  = reglo;
288         last_reg_hi  = reghi;
289         last_data    = data;
290
291         return 0;
292 }
293
294 /***************************************************/
295
296 U_BOOT_CMD(
297         mdio,   6,      1,      do_mdio,
298         "MDIO utility commands",
299         "list                   - List MDIO buses\n"
300         "mdio read <phydev> [<devad>.]<reg> - "
301                 "read PHY's register at <devad>.<reg>\n"
302         "mdio write <phydev> [<devad>.]<reg> <data> - "
303                 "write PHY's register at <devad>.<reg>\n"
304         "mdio rx <phydev> [<devad>.]<reg> - "
305                 "read PHY's extended register at <devad>.<reg>\n"
306         "mdio wx <phydev> [<devad>.]<reg> <data> - "
307                 "write PHY's extended register at <devad>.<reg>\n"
308         "<phydev> may be:\n"
309         "   <busname>  <addr>\n"
310         "   <addr>\n"
311         "   <eth name>\n"
312         "<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.\n"
313 );