1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the People's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 //#include <common.h>
34 #include <typedef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <hal_thread.h>
40 #include <sunxi_hal_common.h>
41 #include <sunxi_hal_miiphy.h>
42 #include <sunxi_hal_phy.h>
43 
44 //#include <asm/types.h>
45 //#include <list.h>
46 #include <hal_mem.h>
47 //#include <net.h>
48 
49 /* local debug macro */
50 #undef MII_DEBUG
51 
52 #undef debug
53 #ifdef MII_DEBUG
54 #define debug(fmt, args...) printf(fmt, ##args)
55 #else
56 #define debug(fmt, args...)
57 #endif /* MII_DEBUG */
58 
59 static struct list_head mii_devs;
60 static struct mii_dev *current_mii;
61 
62 /*
63  * Lookup the mii_dev struct by the registered device name.
64  */
miiphy_get_dev_by_name(const char * devname)65 struct mii_dev *miiphy_get_dev_by_name(const char *devname)
66 {
67     struct list_head *entry;
68     struct mii_dev *dev;
69 
70     if (!devname) {
71         printf("NULL device name!\n");
72         return NULL;
73     }
74 
75     list_for_each(entry, &mii_devs) {
76         dev = list_entry(entry, struct mii_dev, link);
77         if (strcmp(dev->name, devname) == 0)
78             return dev;
79     }
80 
81     return NULL;
82 }
83 
84 /*****************************************************************************
85  *
86  * Initialize global data. Need to be called before any other miiphy routine.
87  */
miiphy_init(void)88 void miiphy_init(void)
89 {
90     INIT_LIST_HEAD(&mii_devs);
91     current_mii = NULL;
92 }
93 
legacy_miiphy_read(struct mii_dev * bus,int addr,int devad,int reg)94 static int legacy_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg)
95 {
96     unsigned short val;
97     int ret;
98     struct legacy_mii_dev *ldev = bus->priv;
99 
100     ret = ldev->read(bus->name, addr, reg, &val);
101 
102     return ret ? -1 : (int)val;
103 }
104 
legacy_miiphy_write(struct mii_dev * bus,int addr,int devad,int reg,uint16_t val)105 static int legacy_miiphy_write(struct mii_dev *bus, int addr, int devad,
106                 int reg, uint16_t val)
107 {
108     struct legacy_mii_dev *ldev = bus->priv;
109 
110     return ldev->write(bus->name, addr, reg, val);
111 }
112 
113 /*****************************************************************************
114  *
115  * Register read and write MII access routines for the device <name>.
116  * This API is now deprecated. Please use mdio_alloc and mdio_register, instead.
117  */
miiphy_register(const char * name,int (* read)(const char * devname,unsigned char addr,unsigned char reg,unsigned short * value),int (* write)(const char * devname,unsigned char addr,unsigned char reg,unsigned short value))118 void miiphy_register(const char *name,
119               int (*read)(const char *devname, unsigned char addr,
120                    unsigned char reg, unsigned short *value),
121               int (*write)(const char *devname, unsigned char addr,
122                     unsigned char reg, unsigned short value))
123 {
124     struct mii_dev *new_dev;
125     struct legacy_mii_dev *ldev;
126 
127     BUG_ON(strlen(name) >= MDIO_NAME_LEN);
128 
129     /* check if we have unique name */
130     new_dev = miiphy_get_dev_by_name(name);
131     if (new_dev) {
132         printf("miiphy_register: non unique device name '%s'\n", name);
133         return;
134     }
135 
136     /* allocate memory */
137     new_dev = mdio_alloc();
138     ldev = malloc(sizeof(*ldev));
139 
140     if (new_dev == NULL || ldev == NULL) {
141         printf("miiphy_register: cannot allocate memory for '%s'\n",
142             name);
143         return;
144     }
145 
146     /* initalize mii_dev struct fields */
147     new_dev->read = legacy_miiphy_read;
148     new_dev->write = legacy_miiphy_write;
149     strncpy(new_dev->name, name, MDIO_NAME_LEN);
150     new_dev->name[MDIO_NAME_LEN - 1] = 0;
151     ldev->read = read;
152     ldev->write = write;
153     new_dev->priv = ldev;
154 
155     printf("miiphy_register: added '%s', read=0x%08lx, write=0x%08lx\n",
156            new_dev->name, ldev->read, ldev->write);
157 
158     /* add it to the list */
159     list_add_tail(&new_dev->link, &mii_devs);
160 
161     if (!current_mii)
162         current_mii = new_dev;
163 }
164 
mdio_alloc(void)165 struct mii_dev *mdio_alloc(void)
166 {
167     struct mii_dev *bus;
168 
169     bus = malloc(sizeof(*bus));
170     if (!bus)
171         return bus;
172 
173     memset(bus, 0, sizeof(*bus));
174 
175     /* initalize mii_dev struct fields */
176     INIT_LIST_HEAD(&bus->link);
177 
178     return bus;
179 }
180 
mdio_register(struct mii_dev * bus)181 int mdio_register(struct mii_dev *bus)
182 {
183     if (!bus || !bus->name || !bus->read || !bus->write)
184         return -1;
185 
186     /* check if we have unique name */
187     if (miiphy_get_dev_by_name(bus->name)) {
188         printf("mdio_register: non unique device name '%s'\n",
189             bus->name);
190         return -1;
191     }
192 
193     /* add it to the list */
194     list_add_tail(&bus->link, &mii_devs);
195 
196     if (!current_mii)
197         current_mii = bus;
198 
199     return 0;
200 }
201 
mdio_list_devices(void)202 void mdio_list_devices(void)
203 {
204     struct list_head *entry;
205 
206     list_for_each(entry, &mii_devs) {
207         int i;
208         struct mii_dev *bus = list_entry(entry, struct mii_dev, link);
209 
210         printf("%s:\n", bus->name);
211 
212         for (i = 0; i < PHY_MAX_ADDR; i++) {
213             struct phy_device *phydev = bus->phymap[i];
214 
215             if (phydev) {
216                 printf("%d - %s", i, phydev->drv->name);
217 
218                 if (phydev->dev)
219                     printf(" <--> %s\n", "eth0");
220                 else
221                     printf("\n");
222             }
223         }
224     }
225 }
226 
miiphy_set_current_dev(const char * devname)227 int miiphy_set_current_dev(const char *devname)
228 {
229     struct mii_dev *dev;
230 
231     dev = miiphy_get_dev_by_name(devname);
232     if (dev) {
233         current_mii = dev;
234         return 0;
235     }
236 
237     printf("No such device: %s\n", devname);
238 
239     return 1;
240 }
241 
mdio_get_current_dev(void)242 struct mii_dev *mdio_get_current_dev(void)
243 {
244     return current_mii;
245 }
246 
mdio_phydev_for_ethname(const char * ethname)247 struct phy_device *mdio_phydev_for_ethname(const char *ethname)
248 {
249     struct list_head *entry;
250     struct mii_dev *bus;
251 
252     list_for_each(entry, &mii_devs) {
253         int i;
254         bus = list_entry(entry, struct mii_dev, link);
255 
256         for (i = 0; i < PHY_MAX_ADDR; i++) {
257             if (!bus->phymap[i] || !bus->phymap[i]->dev)
258                 continue;
259 
260             /*if (strcmp(bus->phymap[i]->dev->name, ethname) == 0)
261                 return bus->phymap[i];*/
262         }
263     }
264 
265     printf("%s is not a known ethernet\n", ethname);
266     return NULL;
267 }
268 
miiphy_get_current_dev(void)269 const char *miiphy_get_current_dev(void)
270 {
271     if (current_mii)
272         return current_mii->name;
273 
274     return NULL;
275 }
276 
miiphy_get_active_dev(const char * devname)277 static struct mii_dev *miiphy_get_active_dev(const char *devname)
278 {
279     /* If the current mii is the one we want, return it */
280     if (current_mii)
281         if (strcmp(current_mii->name, devname) == 0)
282             return current_mii;
283 
284     /* Otherwise, set the active one to the one we want */
285     if (miiphy_set_current_dev(devname))
286         return NULL;
287     else
288         return current_mii;
289 }
290 
291 /*****************************************************************************
292  *
293  * Read to variable <value> from the PHY attached to device <devname>,
294  * use PHY address <addr> and register <reg>.
295  *
296  * This API is deprecated. Use phy_read on a phy_device found via phy_connect
297  *
298  * Returns:
299  *   0 on success
300  */
miiphy_read(const char * devname,unsigned char addr,unsigned char reg,unsigned short * value)301 int miiphy_read(const char *devname, unsigned char addr, unsigned char reg,
302          unsigned short *value)
303 {
304     struct mii_dev *bus;
305     int ret;
306 
307     bus = miiphy_get_active_dev(devname);
308     if (!bus)
309         return 1;
310 
311     ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg);
312     if (ret < 0)
313         return 1;
314 
315     *value = (unsigned short)ret;
316     return 0;
317 }
318 
319 /*****************************************************************************
320  *
321  * Write <value> to the PHY attached to device <devname>,
322  * use PHY address <addr> and register <reg>.
323  *
324  * This API is deprecated. Use phy_write on a phy_device found by phy_connect
325  *
326  * Returns:
327  *   0 on success
328  */
miiphy_write(const char * devname,unsigned char addr,unsigned char reg,unsigned short value)329 int miiphy_write(const char *devname, unsigned char addr, unsigned char reg,
330           unsigned short value)
331 {
332     struct mii_dev *bus;
333 
334     bus = miiphy_get_active_dev(devname);
335     if (bus)
336         return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value);
337 
338     return 1;
339 }
340 
341 /*****************************************************************************
342  *
343  * Print out list of registered MII capable devices.
344  */
miiphy_listdev(void)345 void miiphy_listdev(void)
346 {
347     struct list_head *entry;
348     struct mii_dev *dev;
349 
350     puts("MII devices: ");
351     list_for_each(entry, &mii_devs) {
352         dev = list_entry(entry, struct mii_dev, link);
353         printf("'%s' ", dev->name);
354     }
355     puts("\n");
356 
357     if (current_mii)
358         printf("Current device: '%s'\n", current_mii->name);
359 }
360 
361 /*****************************************************************************
362  *
363  * Read the OUI, manufacture's model number, and revision number.
364  *
365  * OUI:     22 bits (unsigned int)
366  * Model:    6 bits (unsigned char)
367  * Revision: 4 bits (unsigned char)
368  *
369  * This API is deprecated.
370  *
371  * Returns:
372  *   0 on success
373  */
miiphy_info(const char * devname,unsigned char addr,unsigned int * oui,unsigned char * model,unsigned char * rev)374 int miiphy_info(const char *devname, unsigned char addr, unsigned int *oui,
375          unsigned char *model, unsigned char *rev)
376 {
377     unsigned int reg = 0;
378     unsigned short tmp;
379 
380     if (miiphy_read(devname, addr, MII_PHYSID2, &tmp) != 0) {
381         printf("PHY ID register 2 read failed\n");
382         return -1;
383     }
384     reg = tmp;
385 
386     printf("MII_PHYSID2 @ 0x%x = 0x%04x\n", addr, reg);
387 
388     if (reg == 0xFFFF) {
389         /* No physical device present at this address */
390         return -1;
391     }
392 
393     if (miiphy_read(devname, addr, MII_PHYSID1, &tmp) != 0) {
394         printf("PHY ID register 1 read failed\n");
395         return -1;
396     }
397     reg |= tmp << 16;
398     printf("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg);
399 
400     *oui = (reg >> 10);
401     *model = (unsigned char)((reg >> 4) & 0x0000003F);
402     *rev = (unsigned char)(reg & 0x0000000F);
403     return 0;
404 }
405 
406 #ifndef CONFIG_PHYLIB
407 /*****************************************************************************
408  *
409  * Reset the PHY.
410  *
411  * This API is deprecated. Use PHYLIB.
412  *
413  * Returns:
414  *   0 on success
415  */
416 #if 0
417 int miiphy_reset(const char *devname, unsigned char addr)
418 {
419     unsigned short reg;
420     int timeout = 500;
421 
422     if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
423         printf("PHY status read failed\n");
424         return -1;
425     }
426     if (miiphy_write(devname, addr, MII_BMCR, reg | BMCR_RESET) != 0) {
427         printf("PHY reset failed\n");
428         return -1;
429     }
430 #ifdef CONFIG_PHY_RESET_DELAY
431     udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */
432 #endif
433     /*
434      * Poll the control register for the reset bit to go to 0 (it is
435      * auto-clearing).  This should happen within 0.5 seconds per the
436      * IEEE spec.
437      */
438     reg = 0x8000;
439     while (((reg & 0x8000) != 0) && timeout--) {
440         if (miiphy_read(devname, addr, MII_BMCR, &reg) != 0) {
441             printf("PHY status read failed\n");
442             return -1;
443         }
444         udelay(1000);
445     }
446     if ((reg & 0x8000) == 0) {
447         return 0;
448     } else {
449         printf("PHY reset timed out\n");
450         return -1;
451     }
452     return 0;
453 }
454 #endif
455 #endif /* !PHYLIB */
456 
457 /*****************************************************************************
458  *
459  * Determine the ethernet speed (10/100/1000).  Return 10 on error.
460  */
miiphy_speed(const char * devname,unsigned char addr)461 int miiphy_speed(const char *devname, unsigned char addr)
462 {
463     uint16_t bmcr, anlpar;
464 
465 #if defined(CONFIG_PHY_GIGE)
466     uint16_t btsr;
467 
468     /*
469      * Check for 1000BASE-X.  If it is supported, then assume that the speed
470      * is 1000.
471      */
472     if (miiphy_is_1000base_x(devname, addr))
473         return _1000BASET;
474 
475     /*
476      * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
477      */
478     /* Check for 1000BASE-T. */
479     if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
480         printf("PHY 1000BT status");
481         goto miiphy_read_failed;
482     }
483     if (btsr != 0xFFFF &&
484             (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)))
485         return _1000BASET;
486 #endif /* CONFIG_PHY_GIGE */
487 
488     /* Check Basic Management Control Register first. */
489     if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
490         printf("PHY speed");
491         goto miiphy_read_failed;
492     }
493     /* Check if auto-negotiation is on. */
494     if (bmcr & BMCR_ANENABLE) {
495         /* Get auto-negotiation results. */
496         if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
497             printf("PHY AN speed");
498             goto miiphy_read_failed;
499         }
500         return (anlpar & LPA_100) ? _100BASET : _10BASET;
501     }
502     /* Get speed from basic control settings. */
503     return (bmcr & BMCR_SPEED100) ? _100BASET : _10BASET;
504 
505 miiphy_read_failed:
506     printf(" read failed, assuming 10BASE-T\n");
507     return _10BASET;
508 }
509 
510 /*****************************************************************************
511  *
512  * Determine full/half duplex.  Return half on error.
513  */
miiphy_duplex(const char * devname,unsigned char addr)514 int miiphy_duplex(const char *devname, unsigned char addr)
515 {
516     uint16_t bmcr, anlpar;
517 
518 #if defined(CONFIG_PHY_GIGE)
519     uint16_t btsr;
520 
521     /* Check for 1000BASE-X. */
522     if (miiphy_is_1000base_x(devname, addr)) {
523         /* 1000BASE-X */
524         if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
525             printf("1000BASE-X PHY AN duplex");
526             goto miiphy_read_failed;
527         }
528     }
529     /*
530      * No 1000BASE-X, so assume 1000BASE-T/100BASE-TX/10BASE-T register set.
531      */
532     /* Check for 1000BASE-T. */
533     if (miiphy_read(devname, addr, MII_STAT1000, &btsr)) {
534         printf("PHY 1000BT status");
535         goto miiphy_read_failed;
536     }
537     if (btsr != 0xFFFF) {
538         if (btsr & PHY_1000BTSR_1000FD) {
539             return FULL;
540         } else if (btsr & PHY_1000BTSR_1000HD) {
541             return HALF;
542         }
543     }
544 #endif /* CONFIG_PHY_GIGE */
545 
546     /* Check Basic Management Control Register first. */
547     if (miiphy_read(devname, addr, MII_BMCR, &bmcr)) {
548         printf("PHY duplex");
549         goto miiphy_read_failed;
550     }
551     /* Check if auto-negotiation is on. */
552     if (bmcr & BMCR_ANENABLE) {
553         /* Get auto-negotiation results. */
554         if (miiphy_read(devname, addr, MII_LPA, &anlpar)) {
555             printf("PHY AN duplex");
556             goto miiphy_read_failed;
557         }
558         return (anlpar & (LPA_10FULL | LPA_100FULL)) ?
559             FULL : HALF;
560     }
561     /* Get speed from basic control settings. */
562     return (bmcr & BMCR_FULLDPLX) ? FULL : HALF;
563 
564 miiphy_read_failed:
565     printf(" read failed, assuming half duplex\n");
566     return HALF;
567 }
568 
569 /*****************************************************************************
570  *
571  * Return 1 if PHY supports 1000BASE-X, 0 if PHY supports 10BASE-T/100BASE-TX/
572  * 1000BASE-T, or on error.
573  */
miiphy_is_1000base_x(const char * devname,unsigned char addr)574 int miiphy_is_1000base_x(const char *devname, unsigned char addr)
575 {
576 #if defined(CONFIG_PHY_GIGE)
577     uint16_t exsr;
578 
579     if (miiphy_read(devname, addr, MII_ESTATUS, &exsr)) {
580         printf("PHY extended status read failed, assuming no "
581             "1000BASE-X\n");
582         return 0;
583     }
584     return 0 != (exsr & (ESTATUS_1000XF | ESTATUS_1000XH));
585 #else
586     return 0;
587 #endif
588 }
589 
590 #ifdef CONFIG_SYS_FAULT_ECHO_LINK_DOWN
591 /*****************************************************************************
592  *
593  * Determine link status
594  */
miiphy_link(const char * devname,unsigned char addr)595 int miiphy_link(const char *devname, unsigned char addr)
596 {
597     unsigned short reg;
598 
599     /* dummy read; needed to latch some phys */
600     (void)miiphy_read(devname, addr, MII_BMSR, &reg);
601     if (miiphy_read(devname, addr, MII_BMSR, &reg)) {
602         printf("MII_BMSR read failed, assuming no link\n");
603         return 0;
604     }
605 
606     /* Determine if a link is active */
607     if ((reg & BMSR_LSTATUS) != 0) {
608         return 1;
609     } else {
610         return 0;
611     }
612 }
613 #endif
614