Coverage Report

Created: 2026-03-11 06:21

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