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