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