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