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