1 /*
2  * Copyright 2021-2022 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_phylan8720a.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /*! @brief Defines the PHY LAN8720A vendor defined registers. */
15 #define PHY_CONTROL_REG         0x11U /*!< The PHY control/status register. */
16 #define PHY_SEPCIAL_CONTROL_REG 0x1FU /*!< The PHY special control/status register. */
17 
18 /*! @brief Defines the PHY LAN8720A ID number. */
19 #define PHY_CONTROL_ID1 0x07U /*!< The PHY ID1*/
20 
21 /*!@brief Defines the mask flag of operation mode in control register*/
22 #define PHY_CTL_FARLOOPBACK_MASK 0x200U
23 
24 /*!@brief Defines the mask flag of operation mode in special control register*/
25 #define PHY_SPECIALCTL_AUTONEGDONE_MASK 0x1000U /*!< The PHY auto-negotiation complete mask. */
26 #define PHY_SPECIALCTL_DUPLEX_MASK      0x0010U /*!< The PHY duplex mask. */
27 #define PHY_SPECIALCTL_100SPEED_MASK    0x0008U /*!< The PHY speed mask. */
28 #define PHY_SPECIALCTL_10SPEED_MASK     0x0004U /*!< The PHY speed mask. */
29 #define PHY_SPECIALCTL_SPEEDUPLX_MASK   0x001CU /*!< The PHY speed and duplex mask. */
30 
31 /*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */
32 #define PHY_ALL_CAPABLE_MASK 0x1E0U
33 
34 /*! @brief Defines the timeout macro. */
35 #define PHY_TIMEOUT_COUNT 500000U
36 
37 /*! @brief Defines the PHY resource interface. */
38 #define PHY_LAN8720A_WRITE(handle, regAddr, data) \
39     ((phy_lan8720a_resource_t *)(handle)->resource)->write((handle)->phyAddr, regAddr, data)
40 #define PHY_LAN8720A_READ(handle, regAddr, pData) \
41     ((phy_lan8720a_resource_t *)(handle)->resource)->read((handle)->phyAddr, regAddr, pData)
42 
43 /*******************************************************************************
44  * Prototypes
45  ******************************************************************************/
46 
47 /*******************************************************************************
48  * Variables
49  ******************************************************************************/
50 
51 const phy_operations_t phylan8720a_ops = {.phyInit            = PHY_LAN8720A_Init,
52                                           .phyWrite           = PHY_LAN8720A_Write,
53                                           .phyRead            = PHY_LAN8720A_Read,
54                                           .getAutoNegoStatus  = PHY_LAN8720A_GetAutoNegotiationStatus,
55                                           .getLinkStatus      = PHY_LAN8720A_GetLinkStatus,
56                                           .getLinkSpeedDuplex = PHY_LAN8720A_GetLinkSpeedDuplex,
57                                           .setLinkSpeedDuplex = PHY_LAN8720A_SetLinkSpeedDuplex,
58                                           .enableLoopback     = PHY_LAN8720A_EnableLoopback};
59 
60 /*******************************************************************************
61  * Code
62  ******************************************************************************/
63 
PHY_LAN8720A_Init(phy_handle_t * handle,const phy_config_t * config)64 status_t PHY_LAN8720A_Init(phy_handle_t *handle, const phy_config_t *config)
65 {
66     uint32_t counter  = PHY_TIMEOUT_COUNT;
67     status_t result   = kStatus_Success;
68     uint16_t regValue = 0U;
69 
70     /* Assign PHY address and operation resource. */
71     handle->phyAddr  = config->phyAddr;
72     handle->resource = config->resource;
73 
74     /* Check PHY ID. */
75     do
76     {
77         result = PHY_LAN8720A_READ(handle, PHY_ID1_REG, &regValue);
78         if (result != kStatus_Success)
79         {
80             return result;
81         }
82         counter--;
83     } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U));
84 
85     if (counter == 0U)
86     {
87         return kStatus_Fail;
88     }
89     counter = PHY_TIMEOUT_COUNT;
90 
91     /* Reset PHY and wait until completion. */
92     result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK);
93     if (result != kStatus_Success)
94     {
95         return result;
96     }
97     do
98     {
99         result = PHY_LAN8720A_READ(handle, PHY_BASICCONTROL_REG, &regValue);
100         if (result != kStatus_Success)
101         {
102             return result;
103         }
104     } while ((counter-- != 0U) && (regValue & PHY_BCTL_RESET_MASK) != 0U);
105 
106     if (counter == 0U)
107     {
108         return kStatus_Fail;
109     }
110 
111     if (config->autoNeg)
112     {
113         /* Set the ability. */
114         result =
115             PHY_LAN8720A_WRITE(handle, PHY_AUTONEG_ADVERTISE_REG, (PHY_ALL_CAPABLE_MASK | PHY_IEEE802_3_SELECTOR_MASK));
116         if (result == kStatus_Success)
117         {
118             /* Start Auto negotiation and wait until auto negotiation completion */
119             result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG,
120                                         (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK));
121         }
122     }
123     else
124     {
125         /* This PHY only supports 10/100M speed. */
126         assert(config->speed <= kPHY_Speed100M);
127 
128         /* Disable isolate mode */
129         result = PHY_LAN8720A_READ(handle, PHY_BASICCONTROL_REG, &regValue);
130         if (result != kStatus_Success)
131         {
132             return result;
133         }
134         regValue &= PHY_BCTL_ISOLATE_MASK;
135         result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
136         if (result != kStatus_Success)
137         {
138             return result;
139         }
140 
141         /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */
142         result = PHY_LAN8720A_SetLinkSpeedDuplex(handle, config->speed, config->duplex);
143     }
144 
145     return result;
146 }
147 
PHY_LAN8720A_Write(phy_handle_t * handle,uint8_t phyReg,uint16_t data)148 status_t PHY_LAN8720A_Write(phy_handle_t *handle, uint8_t phyReg, uint16_t data)
149 {
150     return PHY_LAN8720A_WRITE(handle, phyReg, data);
151 }
152 
PHY_LAN8720A_Read(phy_handle_t * handle,uint8_t phyReg,uint16_t * pData)153 status_t PHY_LAN8720A_Read(phy_handle_t *handle, uint8_t phyReg, uint16_t *pData)
154 {
155     return PHY_LAN8720A_READ(handle, phyReg, pData);
156 }
157 
PHY_LAN8720A_GetAutoNegotiationStatus(phy_handle_t * handle,bool * status)158 status_t PHY_LAN8720A_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status)
159 {
160     assert(status);
161 
162     status_t result;
163     uint16_t regValue;
164 
165     *status = false;
166 
167     /* Check auto negotiation complete. */
168     result = PHY_LAN8720A_READ(handle, PHY_SEPCIAL_CONTROL_REG, &regValue);
169     if (result == kStatus_Success)
170     {
171         if ((regValue & PHY_SPECIALCTL_AUTONEGDONE_MASK) != 0U)
172         {
173             *status = true;
174         }
175     }
176     return result;
177 }
178 
PHY_LAN8720A_GetLinkStatus(phy_handle_t * handle,bool * status)179 status_t PHY_LAN8720A_GetLinkStatus(phy_handle_t *handle, bool *status)
180 {
181     assert(status);
182 
183     uint16_t regValue;
184     status_t result;
185 
186     /* Read the basic status register. */
187     result = PHY_LAN8720A_READ(handle, PHY_BASICSTATUS_REG, &regValue);
188     if (result == kStatus_Success)
189     {
190         if ((regValue & PHY_BSTATUS_LINKSTATUS_MASK) != 0U)
191         {
192             /* Link up. */
193             *status = true;
194         }
195         else
196         {
197             /* Link down. */
198             *status = false;
199         }
200     }
201     return result;
202 }
203 
PHY_LAN8720A_GetLinkSpeedDuplex(phy_handle_t * handle,phy_speed_t * speed,phy_duplex_t * duplex)204 status_t PHY_LAN8720A_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex)
205 {
206     assert(!((speed == NULL) && (duplex == NULL)));
207 
208     uint16_t regValue;
209     status_t result;
210 
211     /* Read the control register. */
212     result = PHY_LAN8720A_READ(handle, PHY_SEPCIAL_CONTROL_REG, &regValue);
213     if (result == kStatus_Success)
214     {
215         if (speed != NULL)
216         {
217             if ((regValue & PHY_SPECIALCTL_100SPEED_MASK) != 0U)
218             {
219                 *speed = kPHY_Speed100M;
220             }
221             else
222             {
223                 *speed = kPHY_Speed10M;
224             }
225         }
226 
227         if (duplex != NULL)
228         {
229             if ((regValue & PHY_SPECIALCTL_DUPLEX_MASK) != 0U)
230             {
231                 *duplex = kPHY_FullDuplex;
232             }
233             else
234             {
235                 *duplex = kPHY_HalfDuplex;
236             }
237         }
238     }
239     return result;
240 }
241 
PHY_LAN8720A_SetLinkSpeedDuplex(phy_handle_t * handle,phy_speed_t speed,phy_duplex_t duplex)242 status_t PHY_LAN8720A_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex)
243 {
244     /* This PHY only supports 10/100M speed. */
245     assert(speed <= kPHY_Speed100M);
246 
247     status_t result;
248     uint16_t regValue;
249 
250     result = PHY_LAN8720A_READ(handle, PHY_BASICCONTROL_REG, &regValue);
251     if (result == kStatus_Success)
252     {
253         /* Disable the auto-negotiation and set according to user-defined configuration. */
254         regValue &= ~PHY_BCTL_AUTONEG_MASK;
255         if (speed == kPHY_Speed100M)
256         {
257             regValue |= PHY_BCTL_SPEED0_MASK;
258         }
259         else
260         {
261             regValue &= ~PHY_BCTL_SPEED0_MASK;
262         }
263         if (duplex == kPHY_FullDuplex)
264         {
265             regValue |= PHY_BCTL_DUPLEX_MASK;
266         }
267         else
268         {
269             regValue &= ~PHY_BCTL_DUPLEX_MASK;
270         }
271         result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
272     }
273     return result;
274 }
275 
PHY_LAN8720A_EnableLoopback(phy_handle_t * handle,phy_loop_t mode,phy_speed_t speed,bool enable)276 status_t PHY_LAN8720A_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable)
277 {
278     /* This PHY only supports local/remote loopback and 10/100M speed. */
279     assert(mode <= kPHY_RemoteLoop);
280     assert(speed <= kPHY_Speed100M);
281 
282     status_t result;
283     uint16_t regValue;
284 
285     /* Set the loop mode. */
286     if (enable)
287     {
288         if (mode == kPHY_LocalLoop)
289         {
290             if (speed == kPHY_Speed100M)
291             {
292                 regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
293             }
294             else
295             {
296                 regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
297             }
298             result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
299         }
300         else
301         {
302             result = PHY_LAN8720A_READ(handle, PHY_CONTROL_REG, &regValue);
303             if (result == kStatus_Success)
304             {
305                 result = PHY_LAN8720A_WRITE(handle, PHY_CONTROL_REG, regValue | PHY_CTL_FARLOOPBACK_MASK);
306             }
307         }
308     }
309     else
310     {
311         if (mode == kPHY_LocalLoop)
312         {
313             /* First read the current status in control register. */
314             result = PHY_LAN8720A_READ(handle, PHY_BASICCONTROL_REG, &regValue);
315             if (result == kStatus_Success)
316             {
317                 regValue &= ~PHY_BCTL_LOOP_MASK;
318                 result = PHY_LAN8720A_WRITE(handle, PHY_BASICCONTROL_REG, regValue | PHY_BCTL_RESTART_AUTONEG_MASK);
319             }
320         }
321         else
322         {
323             result = PHY_LAN8720A_READ(handle, PHY_CONTROL_REG, &regValue);
324             if (result == kStatus_Success)
325             {
326                 result = PHY_LAN8720A_WRITE(handle, PHY_CONTROL_REG, regValue & ~PHY_CTL_FARLOOPBACK_MASK);
327             }
328         }
329     }
330     return result;
331 }
332