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