1 /*******************************************************************************
2  * Copyright 2020 Microchip Corporation.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Microsemi VSC8541 PHY interface driver implementation to support the FU540
7  * Aloe board.
8  *
9  */
10 #include "mpfs_hal/mss_hal.h"
11 #include "hal/hal.h"
12 
13 #include "drivers/mss_ethernet_mac/mss_ethernet_registers.h"
14 #include "drivers/mss_ethernet_mac/mss_ethernet_mac_regs.h"
15 #include "drivers/mss_ethernet_mac/mss_ethernet_mac_sw_cfg.h"
16 #include "drivers/mss_ethernet_mac/mss_ethernet_mac.h"
17 #include "drivers/mss_ethernet_mac/phy.h"
18 
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22 
23 #if MSS_MAC_USE_PHY_VSC8541
24 
25 /**************************************************************************/
26 /* Preprocessor Macros                                                    */
27 /**************************************************************************/
28 
29 #define BMSR_AUTO_NEGOTIATION_COMPLETE  (0x0020U)
30 
31 /**************************************************************************//**
32  *
33  */
34 
35 typedef struct
36 {
37     __IO uint32_t  INPUT_VAL;   /* 0x0000 */
38     __IO uint32_t  INPUT_EN;    /* 0x0004 */
39     __IO uint32_t  OUTPUT_VAL;  /* 0x0008 */
40     __IO uint32_t  OUTPUT_EN;   /* 0x000C */
41     __IO uint32_t  PUE;         /* 0x0010 */
42     __IO uint32_t  DS;          /* 0x0014 */
43     __IO uint32_t  RISE_IE;     /* 0x0018 */
44     __IO uint32_t  RISE_IP;     /* 0x001C */
45     __IO uint32_t  FALL_IE;     /* 0x0020 */
46     __IO uint32_t  FALL_IP;     /* 0x0024 */
47     __IO uint32_t  HIGH_IE;     /* 0x0028 */
48     __IO uint32_t  HIGH_IP;     /* 0x002C */
49     __IO uint32_t  LOW_IE;      /* 0x0030 */
50     __IO uint32_t  LOW_IP;      /* 0x0034 */
51     __IO uint32_t  reserved0;   /* 0x0038 */
52     __IO uint32_t  reserved1;   /* 0x003C */
53     __IO uint32_t  OUT_XOR;     /* 0x0040 */
54 } AloeGPIO_TypeDef;
55 
56 
MSS_MAC_VSC8541_phy_init(const void * v_this_mac,uint8_t phy_addr)57 void MSS_MAC_VSC8541_phy_init(/* mss_mac_instance_t */ const void *v_this_mac, uint8_t phy_addr)
58 {
59 #if defined(TARGET_ALOE)
60     volatile uint32_t loop;
61     AloeGPIO_TypeDef *g_aloe_gpio = (AloeGPIO_TypeDef *)0x10060000UL;
62 
63     (void)v_this_mac;
64     (void)phy_addr;
65 /*
66  * Init includes toggling the reset line which is connected to GPIO 0 pin 12.
67  * This is the only pin I can see on the 16 GPIO which is currently set as an.
68  * output. We will hard code the setup here to avoid having to have a GPIO
69  * driver as well...
70  *
71  * The Aloe board is strapped for unmanaged mode and needs two pulses of the
72  * reset line to configure the device properly.
73  *
74  * The RX_CLK, TX_CLK and RXD7 pins are strapped high and the remainder low.
75  * This selects GMII mode with auto 10/100/1000 and 125MHz clkout.
76  */
77     g_aloe_gpio->OUTPUT_EN  |= 0x00001000UL;  /* Configure pin 12 as an output */
78     g_aloe_gpio->OUTPUT_VAL &= 0x0000EFFFUL;  /* Clear pin 12 to reset PHY */
79     for(loop = 0U; loop != 1000U; loop++)     /* Short delay, I'm not sure how much is needed... */
80     {
81         ;
82     }
83     g_aloe_gpio->OUTPUT_VAL  |= 0x00001000UL; /* Take PHY^ out of reset */
84     for(loop = 0U; loop != 1000U; loop++)     /* Short delay, I'm not sure how much is needed... */
85     {
86         ;
87     }
88     g_aloe_gpio->OUTPUT_VAL &= 0x0000EFFFUL;  /* Second reset pulse */
89     for(loop = 0U; loop != 1000U; loop++)     /* Short delay, I'm not sure how much is needed... */
90     {
91         ;
92     }
93     g_aloe_gpio->OUTPUT_VAL  |= 0x00001000UL; /* Out of reset once more */
94 
95     /* Need at least 15mS delay before accessing PHY after reset... */
96     for(loop = 0U; loop != 10000000U; loop++)     /* Long delay, I'm not sure how much is needed... */
97     {
98         ;
99     }
100 #else
101     volatile uint16_t temp_reg;
102     const mss_mac_instance_t *this_mac = (const mss_mac_instance_t *)v_this_mac;
103     mss_mac_instance_t *phy_mac;
104 
105     /* Assume this is done by app for the moment */
106 #if 0
107     /* Do hardware reset if possible/necessary */
108 
109     /* Phy is actually attached to another MAC... */
110     if((struct mss_mac_instance *)0UL != this_mac->phy_controller)
111     {
112         phy_mac = (mss_mac_instance_t *)this_mac->phy_controller;
113     }
114     else
115     {
116         phy_mac = (mss_mac_instance_t *)this_mac;
117     }
118 
119     if(0 == phy_mac->phy_hard_reset_done)
120     {
121         /* Active low reset pulse */
122         MSS_MAC_phy_reset(phy_mac, MSS_MAC_SOFT_RESET, false);
123         MSS_MAC_phy_reset(phy_mac, MSS_MAC_SOFT_RESET, true);
124 
125 #if defined(TARGET_G5_SOC)
126         /*
127          * Multiple MACs may be involved and we need to ensure all relevant MACs
128          * have the phy_hard_reset_done flag set
129          */
130 
131         if(phy_mac != this_mac) /* Simple case, set flag for all 4 MACs */
132         {
133             g_mac0.phy_hard_reset_done  = true;
134             g_emac0.phy_hard_reset_done = true;
135             g_mac1.phy_hard_reset_done  = true;
136             g_emac1.phy_hard_reset_done = true;
137         }
138         else if((&g_mac0 == phy_mac) || (&g_emac0 == phy_mac))
139         {
140             g_mac0.phy_hard_reset_done  = true;
141             g_emac0.phy_hard_reset_done = true;
142         }
143         else
144         {
145             g_mac1.phy_hard_reset_done  = true;
146             g_emac1.phy_hard_reset_done = true;
147         }
148 #else
149         phy_mac->phy_hard_reset_done = true;
150 #endif
151     }
152 #endif
153 
154     /* Select standard registers page */
155     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0000U);
156 
157     /* Select GMII mode */
158     MSS_MAC_write_phy_reg(this_mac, phy_addr, 23, (uint16_t)(0x0000));
159 
160     /* Software Reset */
161     temp_reg = MSS_MAC_read_phy_reg(this_mac, phy_addr, 0);
162     temp_reg |= 0x8000U;
163     MSS_MAC_write_phy_reg(this_mac, phy_addr, 0, temp_reg);
164 
165     /* Wait for soft reset to complete */
166     do
167     {
168         temp_reg = MSS_MAC_read_phy_reg(this_mac, phy_addr, 0);
169     } while(0 != (temp_reg & 0x8000U));
170 
171     /* Full duplex, autonegotiation and 1000Mbps as starting point */
172     MSS_MAC_write_phy_reg(this_mac, phy_addr, MII_BMCR, (uint16_t)(BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000));
173 
174     /* Full duplex modes */
175     MSS_MAC_write_phy_reg(this_mac, phy_addr, MII_ADVERTISE, (uint16_t)(ADVERTISE_FULL));
176 
177     /* Full duplex mode */
178     MSS_MAC_write_phy_reg(this_mac, phy_addr, MII_CTRL1000, (uint16_t)(ADVERTISE_1000FULL));
179 
180 
181     /* Select page 2 */
182     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0002U);
183 
184     /* Adjust MAC interface slew rate - default is 111 but recommended for 3.3V i/o is 100 */
185     temp_reg = MSS_MAC_read_phy_reg(this_mac, phy_addr, 27);
186     temp_reg &= 0xFF1FU;
187     temp_reg |= 0x0080U;
188     MSS_MAC_write_phy_reg(this_mac, phy_addr, 27, temp_reg);
189 
190     /* Select page 0 */
191     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0000U);
192 
193     #if 0
194     /* Auto MDI/MDI-X, Do not ignore advertised ability, disable CLKOUT */
195     MSS_MAC_write_phy_reg(this_mac, phy_addr, 18, (uint16_t)(0x0084U));
196 
197     /* Enable SGMII MAC link autonegotiation and Force copper media only */
198     MSS_MAC_write_phy_reg(this_mac, phy_addr, 23, (uint16_t)(0x2880U));
199 
200     /* SGMII no input preamble, 2 byte output preamble, 9/12K jumbo frames (12K for 60ppm clock) */
201     MSS_MAC_write_phy_reg(this_mac, phy_addr, 23, (uint16_t)(0xC050U));
202 
203     /* Select selection of LED activity modes for 4 LEDs */
204     MSS_MAC_write_phy_reg(this_mac, phy_addr, 29, (uint16_t)(0x0123U));
205 
206     /* Clear this for consistency as many bits are CMODE selections */
207     MSS_MAC_write_phy_reg(this_mac, phy_addr, 30, (uint16_t)(0x0000U));
208 
209     /* Select extended registers page */
210     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0001U);
211 
212     /* Clear SIGDET polarity to active high */
213     temp_reg = MSS_MAC_read_phy_reg(this_mac, phy_addr, 19);
214     temp_reg &= 0xFFFE;
215     MSS_MAC_write_phy_reg(this_mac, phy_addr, 19, temp_reg);
216 
217     /* Ensure all CMODE bits are clear */
218     temp_reg = MSS_MAC_read_phy_reg(this_mac, phy_addr, 20);
219     temp_reg &= 0xFE6F;
220     MSS_MAC_write_phy_reg(this_mac, phy_addr, 20, temp_reg);
221 
222     /* Select general purpose registers page */
223     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0010U);
224 
225     /* Disable SIGDET operation */
226     MSS_MAC_write_phy_reg(this_mac, phy_addr, 13, (uint16_t)(0x000FU));
227 
228     /* Select standard registers page */
229     MSS_MAC_write_phy_reg(this_mac, phy_addr, 31, 0x0000U);
230 #endif
231 
232 #endif
233 
234 }
235 
236 /**************************************************************************//**
237  *
238  */
MSS_MAC_VSC8541_phy_set_link_speed(void * v_this_mac,uint32_t speed_duplex_select,mss_mac_speed_mode_t speed_mode)239 void MSS_MAC_VSC8541_phy_set_link_speed(/* mss_mac_instance_t */ void *v_this_mac, uint32_t speed_duplex_select, mss_mac_speed_mode_t speed_mode)
240 {
241     mss_mac_instance_t *this_mac = (mss_mac_instance_t *)v_this_mac;
242     uint16_t phy_reg;
243     uint32_t inc;
244     uint32_t speed_select;
245     const uint16_t mii_advertise_bits[4] = {ADVERTISE_10FULL, ADVERTISE_10HALF,
246                                             ADVERTISE_100FULL, ADVERTISE_100HALF};
247 
248     this_mac->speed_mode = speed_mode;
249 
250     if(MSS_MAC_SPEED_AN == speed_mode) /* Set auto-negotiation advertisement. */
251     {
252         /* Set 10Mbps and 100Mbps advertisement. */
253         phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_ADVERTISE);
254         phy_reg &= (uint16_t)(~(ADVERTISE_10HALF | ADVERTISE_10FULL |
255                      ADVERTISE_100HALF | ADVERTISE_100FULL));
256 
257         speed_select = speed_duplex_select;
258         for(inc = 0U; inc < 4U; ++inc)
259         {
260             uint32_t advertise;
261             advertise = speed_select & 0x00000001U;
262             if(advertise != 0U)
263             {
264                 phy_reg |= mii_advertise_bits[inc];
265             }
266             speed_select = speed_select >> 1U;
267         }
268 
269         MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_ADVERTISE, phy_reg);
270 
271         /* Set 1000Mbps advertisement. */
272         phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000);
273         phy_reg &= (uint16_t)(~(ADVERTISE_1000FULL | ADVERTISE_1000HALF));
274 
275         if((speed_duplex_select & MSS_MAC_ANEG_1000M_FD) != 0U)
276         {
277             phy_reg |= ADVERTISE_1000FULL;
278         }
279 
280         if((speed_duplex_select & MSS_MAC_ANEG_1000M_HD) != 0U)
281         {
282             phy_reg |= ADVERTISE_1000HALF;
283         }
284 
285         MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000, phy_reg);
286     }
287     else
288     {
289         uint16_t temp_reg = 0x0000U; /* Default with 10M, half duplex */
290 
291         if((MSS_MAC_10_FDX == this_mac->speed_mode) || (MSS_MAC_100_FDX == this_mac->speed_mode) || (MSS_MAC_1000_FDX == this_mac->speed_mode))
292         {
293             temp_reg |= BMCR_FULLDPLX;
294         }
295 
296         if((MSS_MAC_100_FDX == this_mac->speed_mode) || (MSS_MAC_100_HDX == this_mac->speed_mode))
297         {
298             temp_reg |= BMCR_SPEED100;
299         }
300 
301         if((MSS_MAC_1000_FDX == this_mac->speed_mode) || (MSS_MAC_1000_HDX == this_mac->speed_mode))
302         {
303             temp_reg |=  BMCR_SPEED1000;
304             /* Set Master mode */
305             phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000);
306             phy_reg |= 0x1800U;
307             MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000, phy_reg);
308         }
309 
310         /* Apply static speed/duplex selection */
311         MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_BMCR, temp_reg);
312 
313         /* Full duplex mode or half duplex, single port device */
314         if((MSS_MAC_10_FDX == this_mac->speed_mode) || (MSS_MAC_100_FDX == this_mac->speed_mode) || (MSS_MAC_1000_FDX == this_mac->speed_mode))
315         {
316             MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000, (uint16_t)(ADVERTISE_1000FULL));
317         }
318         else
319         {
320             MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_CTRL1000, (uint16_t)(ADVERTISE_1000HALF));
321         }
322     }
323 }
324 
325 /**************************************************************************//**
326  *
327  */
MSS_MAC_VSC8541_phy_autonegotiate(const void * v_this_mac)328 void MSS_MAC_VSC8541_phy_autonegotiate(/* mss_mac_instance_t */ const void *v_this_mac)
329 {
330     const mss_mac_instance_t *this_mac = (const mss_mac_instance_t *)v_this_mac;
331     volatile uint16_t phy_reg;
332     uint16_t autoneg_complete;
333     volatile uint32_t copper_aneg_timeout = 10000U;
334 
335     if(MSS_MAC_SPEED_AN == this_mac->speed_mode) /* Only do if allowed */
336     {
337         phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 2U);
338         phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 3U);
339 
340         /* Enable auto-negotiation. */
341         phy_reg = 0x1340U;
342         MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_BMCR, phy_reg);
343 
344         /* Wait for copper auto-negotiation to complete. */
345         do {
346             phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_BMSR);
347             autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE;
348             --copper_aneg_timeout;
349         } while(((0U == autoneg_complete) && (copper_aneg_timeout != 0u)) || (0xFFFF == phy_reg));
350     }
351 }
352 
353 
354 /**************************************************************************//**
355  *
356  */
MSS_MAC_VSC8541_mac_autonegotiate(const void * v_this_mac)357 void MSS_MAC_VSC8541_mac_autonegotiate(/* mss_mac_instance_t */ const void *v_this_mac)
358 {
359     (void)v_this_mac;
360 }
361 
362 
363 /**************************************************************************//**
364  *
365  */
MSS_MAC_VSC8541_phy_get_link_status(const void * v_this_mac,mss_mac_speed_t * speed,uint8_t * fullduplex)366 uint8_t MSS_MAC_VSC8541_phy_get_link_status
367 (
368     /* mss_mac_instance_t*/ const void *v_this_mac,
369     mss_mac_speed_t * speed,
370     uint8_t *     fullduplex
371 )
372 {
373     const mss_mac_instance_t *this_mac = (const mss_mac_instance_t *)v_this_mac;
374     uint16_t phy_reg;
375     uint16_t link_up;
376     uint8_t link_status;
377 
378     phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, MII_BMSR);
379     link_up = phy_reg & BMSR_LSTATUS;
380     if(link_up != MSS_MAC_LINK_DOWN)
381     {
382         uint16_t duplex;
383         uint16_t speed_field;
384 
385         /* Link is up. */
386         link_status = MSS_MAC_LINK_UP;
387 
388         phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1CU); /* Device Auxillary Control and Status */
389         duplex = phy_reg & 0x0020U;
390         speed_field = phy_reg & 0x0018U;
391 
392         if(MSS_MAC_HALF_DUPLEX == duplex)
393         {
394             *fullduplex = MSS_MAC_HALF_DUPLEX;
395         }
396         else
397         {
398             *fullduplex = MSS_MAC_FULL_DUPLEX;
399         }
400 
401         switch(speed_field >> 3)
402         {
403             case 0U:
404                 *speed = MSS_MAC_10MBPS;
405             break;
406 
407             case 1U:
408                 *speed = MSS_MAC_100MBPS;
409             break;
410 
411             case 2U:
412                 *speed = MSS_MAC_1000MBPS;
413             break;
414 
415             default:
416                 link_status = (uint8_t)MSS_MAC_LINK_DOWN;
417             break;
418         }
419     }
420     else
421     {
422         /* Link is down. */
423         link_status = (uint8_t)MSS_MAC_LINK_DOWN;
424     }
425 
426     return link_status;
427 }
428 
429 
430 /**************************************************************************//**
431  *
432  */
433 uint16_t VSC8541_reg_0[32];
434 uint16_t VSC8541_reg_1[16];
435 uint16_t VSC8541_reg_2[16];
436 uint16_t VSC8541_reg_16[32];
437 
438 void dump_vsc8541_regs(const mss_mac_instance_t * this_mac);
dump_vsc8541_regs(const mss_mac_instance_t * this_mac)439 void dump_vsc8541_regs(const mss_mac_instance_t * this_mac)
440 {
441     volatile int32_t count;
442     volatile uint16_t page;
443     volatile uint16_t old_page;
444     volatile uint16_t *pdata;
445     volatile psr_t lev;
446 
447     for(page = 0U; page <= 0x10U; page++)
448     {
449         if(0U == page)
450         {
451             pdata = VSC8541_reg_0;
452         }
453         else if(1U == page)
454         {
455             pdata = VSC8541_reg_1;
456         }
457         else if(2U == page)
458         {
459             pdata = VSC8541_reg_2;
460         }
461         else if(16U == page)
462         {
463             pdata = VSC8541_reg_16;
464         }
465         else
466         {
467             pdata = VSC8541_reg_0;
468         }
469 
470         if((0U == page) || (0x10U == page))
471         {
472             for(count = 0; count <= 0x1F; count++)
473             {
474                 lev = HAL_disable_interrupts();
475                 old_page = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU);
476 
477                 MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU, page);
478 
479                 pdata[count] = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, (uint8_t)count);
480 
481                 MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU, old_page);
482                 MSS_MAC_read_phy_reg(this_mac, (uint8_t)10, (uint8_t)count);
483                 HAL_restore_interrupts(lev);
484             }
485         }
486         else
487         {
488             for(count = 0x10; count <= 0x1F; count++)
489             {
490                 lev = HAL_disable_interrupts();
491                 old_page = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU);
492 
493                 MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU, page);
494 
495                 pdata[count - 0X10] = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, (uint8_t)count);
496 
497                 MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->phy_addr, 0x1FU, old_page);
498                 HAL_restore_interrupts(lev);
499             }
500         }
501 
502         if(2U == page)
503         {
504             page = 0x0FU;
505         }
506     }
507 }
508 
509 #endif /* #if defined(TARGET_ALOE) */
510 #ifdef __cplusplus
511 }
512 #endif
513 
514 /******************************** END OF FILE ******************************/
515 
516 
517 
518 
519 
520 
521