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