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