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