Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/common/miiphyutil.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
 * (C) Copyright 2001
4
 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
5
 */
6
7
/*
8
 * This provides a bit-banged interface to the ethernet MII management
9
 * channel.
10
 */
11
12
#include <dm.h>
13
#include <log.h>
14
#include <miiphy.h>
15
#include <phy.h>
16
#include <linux/delay.h>
17
18
#include <asm/types.h>
19
#include <linux/list.h>
20
#include <malloc.h>
21
#include <net.h>
22
23
/* local debug macro */
24
#undef MII_DEBUG
25
26
#undef debug
27
#ifdef MII_DEBUG
28
#define debug(fmt, args...) printf(fmt, ##args)
29
#else
30
#define debug(fmt, args...)
31
#endif /* MII_DEBUG */
32
33
static LIST_HEAD(mii_devs);
34
static struct mii_dev *current_mii;
35
36
/*
37
 * Lookup the mii_dev struct by the registered device name.
38
 */
39
struct mii_dev *miiphy_get_dev_by_name(const char *devname)
40
0
{
41
0
  struct list_head *entry;
42
0
  struct mii_dev *dev;
43
44
0
  if (!devname) {
45
0
    printf("NULL device name!\n");
46
0
    return NULL;
47
0
  }
48
49
0
  list_for_each(entry, &mii_devs) {
50
0
    dev = list_entry(entry, struct mii_dev, link);
51
0
    if (strcmp(dev->name, devname) == 0)
52
0
      return dev;
53
0
  }
54
55
0
  return NULL;
56
0
}
57
58
struct mii_dev *mdio_alloc(void)
59
0
{
60
0
  struct mii_dev *bus;
61
62
0
  bus = malloc(sizeof(*bus));
63
0
  if (!bus)
64
0
    return bus;
65
66
0
  memset(bus, 0, sizeof(*bus));
67
68
  /* initialize mii_dev struct fields */
69
0
  INIT_LIST_HEAD(&bus->link);
70
71
0
  return bus;
72
0
}
73
74
void mdio_free(struct mii_dev *bus)
75
0
{
76
0
  free(bus);
77
0
}
78
79
int mdio_register(struct mii_dev *bus)
80
0
{
81
0
  if (!bus || !bus->read || !bus->write)
82
0
    return -1;
83
84
  /* check if we have unique name */
85
0
  if (miiphy_get_dev_by_name(bus->name)) {
86
0
    printf("mdio_register: non unique device name '%s'\n",
87
0
      bus->name);
88
0
    return -1;
89
0
  }
90
91
  /* add it to the list */
92
0
  list_add_tail(&bus->link, &mii_devs);
93
94
0
  if (!current_mii)
95
0
    current_mii = bus;
96
97
0
  return 0;
98
0
}
99
100
int mdio_register_seq(struct mii_dev *bus, int seq)
101
0
{
102
0
  int ret;
103
104
  /* Setup a unique name for each mdio bus */
105
0
  ret = snprintf(bus->name, MDIO_NAME_LEN, "eth%d", seq);
106
0
  if (ret < 0)
107
0
    return ret;
108
109
0
  return mdio_register(bus);
110
0
}
111
112
int mdio_unregister(struct mii_dev *bus)
113
0
{
114
0
  if (!bus)
115
0
    return 0;
116
117
  /* delete it from the list */
118
0
  list_del(&bus->link);
119
120
0
  if (current_mii == bus)
121
0
    current_mii = NULL;
122
123
0
  return 0;
124
0
}
125
126
void mdio_list_devices(void)
127
0
{
128
0
  struct list_head *entry;
129
130
0
  list_for_each(entry, &mii_devs) {
131
0
    int i;
132
0
    struct mii_dev *bus = list_entry(entry, struct mii_dev, link);
133
134
0
    printf("%s:\n", bus->name);
135
136
0
    for (i = 0; i < PHY_MAX_ADDR; i++) {
137
0
      struct phy_device *phydev = bus->phymap[i];
138
139
0
      if (phydev) {
140
0
        printf("%x - %s", i, phydev->drv->name);
141
142
0
        if (phydev->dev)
143
0
          printf(" <--> %s\n", phydev->dev->name);
144
0
        else
145
0
          printf("\n");
146
0
      }
147
0
    }
148
0
  }
149
0
}
150
151
int miiphy_set_current_dev(const char *devname)
152
0
{
153
0
  struct mii_dev *dev;
154
155
0
  dev = miiphy_get_dev_by_name(devname);
156
0
  if (dev) {
157
0
    current_mii = dev;
158
0
    return 0;
159
0
  }
160
161
0
  printf("No such device: %s\n", devname);
162
163
0
  return 1;
164
0
}
165
166
struct mii_dev *mdio_get_current_dev(void)
167
0
{
168
0
  return current_mii;
169
0
}
170
171
struct list_head *mdio_get_list_head(void)
172
0
{
173
0
  return &mii_devs;
174
0
}
175
176
struct phy_device *mdio_phydev_for_ethname(const char *ethname)
177
0
{
178
0
  struct list_head *entry;
179
0
  struct mii_dev *bus;
180
181
0
  list_for_each(entry, &mii_devs) {
182
0
    int i;
183
0
    bus = list_entry(entry, struct mii_dev, link);
184
185
0
    for (i = 0; i < PHY_MAX_ADDR; i++) {
186
0
      if (!bus->phymap[i] || !bus->phymap[i]->dev)
187
0
        continue;
188
189
0
      if (strcmp(bus->phymap[i]->dev->name, ethname) == 0)
190
0
        return bus->phymap[i];
191
0
    }
192
0
  }
193
194
0
  printf("%s is not a known ethernet\n", ethname);
195
0
  return NULL;
196
0
}
197
198
const char *miiphy_get_current_dev(void)
199
0
{
200
0
  if (current_mii)
201
0
    return current_mii->name;
202
203
0
  return NULL;
204
0
}
205
206
static struct mii_dev *miiphy_get_active_dev(const char *devname)
207
0
{
208
  /* If the current mii is the one we want, return it */
209
0
  if (current_mii)
210
0
    if (strcmp(current_mii->name, devname) == 0)
211
0
      return current_mii;
212
213
  /* Otherwise, set the active one to the one we want */
214
0
  if (miiphy_set_current_dev(devname))
215
0
    return NULL;
216
0
  else
217
0
    return current_mii;
218
0
}
219
220
/*****************************************************************************
221
 *
222
 * Read to variable <value> from the PHY attached to device <devname>,
223
 * use PHY address <addr> and register <reg>.
224
 *
225
 * This API is deprecated. Use phy_read on a phy_device found via phy_connect
226
 *
227
 * Returns:
228
 *   0 on success
229
 */
230
int miiphy_read(const char *devname, unsigned char addr, unsigned char reg,
231
     unsigned short *value)
232
0
{
233
0
  struct mii_dev *bus;
234
0
  int ret;
235
236
0
  bus = miiphy_get_active_dev(devname);
237
0
  if (!bus)
238
0
    return 1;
239
240
0
  ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg);
241
0
  if (ret < 0)
242
0
    return 1;
243
244
0
  *value = (unsigned short)ret;
245
0
  return 0;
246
0
}
247
248
/*****************************************************************************
249
 *
250
 * Write <value> to the PHY attached to device <devname>,
251
 * use PHY address <addr> and register <reg>.
252
 *
253
 * This API is deprecated. Use phy_write on a phy_device found by phy_connect
254
 *
255
 * Returns:
256
 *   0 on success
257
 */
258
int miiphy_write(const char *devname, unsigned char addr, unsigned char reg,
259
      unsigned short value)
260
0
{
261
0
  struct mii_dev *bus;
262
263
0
  bus = miiphy_get_active_dev(devname);
264
0
  if (bus)
265
0
    return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value);
266
267
0
  return 1;
268
0
}
269
270
/*****************************************************************************
271
 *
272
 * Print out list of registered MII capable devices.
273
 */
274
void miiphy_listdev(void)
275
0
{
276
0
  struct list_head *entry;
277
0
  struct mii_dev *dev;
278
279
0
  puts("MII devices: ");
280
0
  list_for_each(entry, &mii_devs) {
281
0
    dev = list_entry(entry, struct mii_dev, link);
282
0
    printf("'%s' ", dev->name);
283
0
  }
284
0
  puts("\n");
285
286
0
  if (current_mii)
287
0
    printf("Current device: '%s'\n", current_mii->name);
288
0
}
289
290
/*****************************************************************************
291
 *
292
 * Read the OUI, manufacture's model number, and revision number.
293
 *
294
 * OUI:     22 bits (unsigned int)
295
 * Model:    6 bits (unsigned char)
296
 * Revision: 4 bits (unsigned char)
297
 *
298
 * This API is deprecated.
299
 *
300
 * Returns:
301
 *   0 on success
302
 */
303
int miiphy_info(const char *devname, unsigned char addr, unsigned int *oui,
304
     unsigned char *model, unsigned char *rev)
305
0
{
306
0
  unsigned int reg = 0;
307
0
  unsigned short tmp;
308
309
0
  if (miiphy_read(devname, addr, MII_PHYSID2, &tmp) != 0) {
310
0
    debug("PHY ID register 2 read failed\n");
311
0
    return -1;
312
0
  }
313
0
  reg = tmp;
314
315
0
  debug("MII_PHYSID2 @ 0x%x = 0x%04x\n", addr, reg);
316
317
0
  if (reg == 0xFFFF) {
318
    /* No physical device present at this address */
319
0
    return -1;
320
0
  }
321
322
0
  if (miiphy_read(devname, addr, MII_PHYSID1, &tmp) != 0) {
323
0
    debug("PHY ID register 1 read failed\n");
324
0
    return -1;
325
0
  }
326
0
  reg |= tmp << 16;
327
0
  debug("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg);
328
329
0
  *oui = (reg >> 10);
330
0
  *model = (unsigned char)((reg >> 4) & 0x0000003F);
331
0
  *rev = (unsigned char)(reg & 0x0000000F);
332
0
  return 0;
333
0
}
334
335
#ifndef CONFIG_PHYLIB
336
/*****************************************************************************
337
 *
338
 * Reset the PHY.
339
 *
340
 * This API is deprecated. Use PHYLIB.
341
 *
342
 * Returns:
343
 *   0 on success
344
 */
345
int miiphy_reset(const char *devname, unsigned char addr)
346
{
347
  unsigned short reg;
348
  int timeout = 500;
349
350
  if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
351
    debug("PHY status read failed\n");
352
    return -1;
353
  }
354
  if (miiphy_write(devname, addr, MII_BMCR, reg | BMCR_RESET) != 0) {
355
    debug("PHY reset failed\n");
356
    return -1;
357
  }
358
#if CONFIG_PHY_RESET_DELAY > 0
359
  udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */
360
#endif
361
  /*
362
   * Poll the control register for the reset bit to go to 0 (it is
363
   * auto-clearing).  This should happen within 0.5 seconds per the
364
   * IEEE spec.
365
   */
366
  reg = 0x8000;
367
  while (((reg & 0x8000) != 0) && timeout--) {
368
    if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
369
      debug("PHY status read failed\n");
370
      return -1;
371
    }
372
    udelay(1000);
373
  }
374
  if ((reg & 0x8000) == 0) {
375
    return 0;
376
  } else {
377
    puts("PHY reset timed out\n");
378
    return -1;
379
  }
380
  return 0;
381
}
382
#endif /* !PHYLIB */
383
384
/*****************************************************************************
385
 *
386
 * Determine the ethernet speed (10/100/1000).  Return 10 on error.
387
 */
388
int miiphy_speed(const char *devname, unsigned char addr)
389
0
{
390
0
  u16 bmcr, anlpar, adv;
391
392
#if defined(CONFIG_PHY_GIGE)
393
  u16 btsr;
394
395
  /*
396
   * Check for 1000BASE-X.  If it is supported, then assume that the speed
397
   * is 1000.
398
   */
399
  if (miiphy_is_1000base_x(devname, addr))
400
    return _1000BASET;
401
402
  /*
403
   * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
404
   */
405
  /* Check for 1000BASE-T. */
406
  if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
407
    printf("PHY 1000BT status");
408
    goto miiphy_read_failed;
409
  }
410
  if (btsr != 0xFFFF &&
411
      (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)))
412
    return _1000BASET;
413
#endif /* CONFIG_PHY_GIGE */
414
415
  /* Check Basic Management Control Register first. */
416
0
  if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
417
0
    printf("PHY speed");
418
0
    goto miiphy_read_failed;
419
0
  }
420
  /* Check if auto-negotiation is on. */
421
0
  if (bmcr & BMCR_ANENABLE) {
422
    /* Get auto-negotiation results. */
423
0
    if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
424
0
      printf("PHY AN speed");
425
0
      goto miiphy_read_failed;
426
0
    }
427
428
0
    if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) {
429
0
      puts("PHY AN adv speed");
430
0
      goto miiphy_read_failed;
431
0
    }
432
0
    return ((anlpar & adv) & LPA_100) ? _100BASET : _10BASET;
433
0
  }
434
  /* Get speed from basic control settings. */
435
0
  return (bmcr & BMCR_SPEED100) ? _100BASET : _10BASET;
436
437
0
miiphy_read_failed:
438
0
  printf(" read failed, assuming 10BASE-T\n");
439
0
  return _10BASET;
440
0
}
441
442
/*****************************************************************************
443
 *
444
 * Determine full/half duplex.  Return half on error.
445
 */
446
int miiphy_duplex(const char *devname, unsigned char addr)
447
0
{
448
0
  u16 bmcr, anlpar, adv;
449
450
#if defined(CONFIG_PHY_GIGE)
451
  u16 btsr;
452
453
  /* Check for 1000BASE-X. */
454
  if (miiphy_is_1000base_x(devname, addr)) {
455
    /* 1000BASE-X */
456
    if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
457
      printf("1000BASE-X PHY AN duplex");
458
      goto miiphy_read_failed;
459
    }
460
  }
461
  /*
462
   * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
463
   */
464
  /* Check for 1000BASE-T. */
465
  if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
466
    printf("PHY 1000BT status");
467
    goto miiphy_read_failed;
468
  }
469
  if (btsr != 0xFFFF) {
470
    if (btsr & PHY_1000BTSR_1000FD) {
471
      return FULL;
472
    } else if (btsr & PHY_1000BTSR_1000HD) {
473
      return HALF;
474
    }
475
  }
476
#endif /* CONFIG_PHY_GIGE */
477
478
  /* Check Basic Management Control Register first. */
479
0
  if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
480
0
    puts("PHY duplex");
481
0
    goto miiphy_read_failed;
482
0
  }
483
  /* Check if auto-negotiation is on. */
484
0
  if (bmcr & BMCR_ANENABLE) {
485
    /* Get auto-negotiation results. */
486
0
    if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
487
0
      puts("PHY AN duplex");
488
0
      goto miiphy_read_failed;
489
0
    }
490
491
0
    if (miiphy_read(devname, addr, MII_ADVERTISE, &adv)) {
492
0
      puts("PHY AN adv duplex");
493
0
      goto miiphy_read_failed;
494
0
    }
495
0
    return ((anlpar & adv) & (LPA_10FULL | LPA_100FULL)) ?
496
0
        FULL : HALF;
497
0
  }
498
  /* Get speed from basic control settings. */
499
0
  return (bmcr & BMCR_FULLDPLX) ? FULL : HALF;
500
501
0
miiphy_read_failed:
502
0
  printf(" read failed, assuming half duplex\n");
503
0
  return HALF;
504
0
}
505
506
/*****************************************************************************
507
 *
508
 * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/
509
 * 1000BASE-T, or on error.
510
 */
511
int miiphy_is_1000base_x(const char *devname, unsigned char addr)
512
0
{
513
#if defined(CONFIG_PHY_GIGE)
514
  u16 exsr;
515
516
  if (miiphy_read(devname, addr, MII_ESTATUS, &exsr)) {
517
    printf("PHY extended status read failed, assuming no "
518
      "1000BASE-X\n");
519
    return 0;
520
  }
521
  return 0 != (exsr & (ESTATUS_1000XF | ESTATUS_1000XH));
522
#else
523
0
  return 0;
524
0
#endif
525
0
}