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