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