1 /*
2  * Copyright 2020-2023 NXP
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "fsl_phyksz8081.h"
8 
9 /*******************************************************************************
10  * Definitions
11  ******************************************************************************/
12 
13 /*! @brief Defines the PHY KSZ8081 vendor defined registers. */
14 #define PHY_INTR_CONTROL_STATUS_REG (0x1BU) /*!< The PHY interrupt control/status register. */
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 KSZ8081 ID number. */
19 #define PHY_CONTROL_ID1 0x22U /*!< The PHY ID1 */
20 
21 /*! @brief Defines the mask flag of operation mode in interrupt control/status registers */
22 #define PHY_INTR_CONTROL_STATUS_LINK_UP_MASK   ((uint16_t)0x0100U) /*!< The PHY link up interrupt mask. */
23 #define PHY_INTR_CONTROL_STATUS_LINK_DOWN_MASK ((uint16_t)0x0400U) /*!< The PHY link down interrupt mask. */
24 
25 /*! @brief Defines the mask flag of operation mode in control registers */
26 #define PHY_CTL2_REMOTELOOP_MASK    ((uint16_t)0x0004U) /*!< The PHY remote loopback mask. */
27 #define PHY_CTL2_REFCLK_SELECT_MASK ((uint16_t)0x0080U) /*!< The PHY RMII reference clock select. */
28 #define PHY_CTL2_INTR_LEVEL_MASK    ((uint16_t)0x0200U) /*!< The PHY interrupt level mask. */
29 #define PHY_CTL1_10HALFDUPLEX_MASK  ((uint16_t)0x0001U) /*!< The PHY 10M half duplex mask. */
30 #define PHY_CTL1_100HALFDUPLEX_MASK ((uint16_t)0x0002U) /*!< The PHY 100M half duplex mask. */
31 #define PHY_CTL1_10FULLDUPLEX_MASK  ((uint16_t)0x0005U) /*!< The PHY 10M full duplex mask. */
32 #define PHY_CTL1_100FULLDUPLEX_MASK ((uint16_t)0x0006U) /*!< The PHY 100M full duplex mask. */
33 #define PHY_CTL1_SPEEDUPLX_MASK     ((uint16_t)0x0007U) /*!< The PHY speed and duplex mask. */
34 #define PHY_CTL1_ENERGYDETECT_MASK  ((uint16_t)0x0010U) /*!< The PHY signal present on rx differential pair. */
35 #define PHY_CTL1_LINKUP_MASK        ((uint16_t)0x0100U) /*!< The PHY link up. */
36 #define PHY_LINK_READY_MASK         (PHY_CTL1_ENERGYDETECT_MASK | PHY_CTL1_LINKUP_MASK)
37 
38 /*! @brief Defines the timeout macro. */
39 #define PHY_READID_TIMEOUT_COUNT (1000U)
40 
41 /*! @brief Defines the PHY resource interface. */
42 #define PHY_KSZ8081_WRITE(handle, regAddr, data) \
43     (((phy_ksz8081_resource_t *)(handle)->resource)->write((handle)->phyAddr, regAddr, data))
44 #define PHY_KSZ8081_READ(handle, regAddr, pData) \
45     (((phy_ksz8081_resource_t *)(handle)->resource)->read((handle)->phyAddr, regAddr, pData))
46 
47 /*******************************************************************************
48  * Prototypes
49  ******************************************************************************/
50 
51 /*******************************************************************************
52  * Variables
53  ******************************************************************************/
54 const phy_operations_t phyksz8081_ops = {.phyInit             = PHY_KSZ8081_Init,
55                                          .phyWrite            = PHY_KSZ8081_Write,
56                                          .phyRead             = PHY_KSZ8081_Read,
57                                          .getAutoNegoStatus   = PHY_KSZ8081_GetAutoNegotiationStatus,
58                                          .getLinkStatus       = PHY_KSZ8081_GetLinkStatus,
59                                          .getLinkSpeedDuplex  = PHY_KSZ8081_GetLinkSpeedDuplex,
60                                          .setLinkSpeedDuplex  = PHY_KSZ8081_SetLinkSpeedDuplex,
61                                          .enableLoopback      = PHY_KSZ8081_EnableLoopback,
62                                          .enableLinkInterrupt = PHY_KSZ8081_EnableLinkInterrupt,
63                                          .clearInterrupt      = PHY_KSZ8081_ClearInterrupt};
64 
65 /*******************************************************************************
66  * Code
67  ******************************************************************************/
68 
PHY_KSZ8081_Init(phy_handle_t * handle,const phy_config_t * config)69 status_t PHY_KSZ8081_Init(phy_handle_t *handle, const phy_config_t *config)
70 {
71     uint32_t counter  = PHY_READID_TIMEOUT_COUNT;
72     status_t result   = kStatus_Success;
73     uint16_t regValue = 0;
74 
75     /* Assign PHY address and operation resource. */
76     handle->phyAddr  = config->phyAddr;
77     handle->resource = config->resource;
78 
79     /* Check PHY ID. */
80     do
81     {
82         result = PHY_KSZ8081_READ(handle, PHY_ID1_REG, &regValue);
83         if (result != kStatus_Success)
84         {
85             return result;
86         }
87         counter--;
88     } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U));
89 
90     if (counter == 0U)
91     {
92         return kStatus_Fail;
93     }
94 
95     /* Reset PHY. */
96     result = PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK);
97     if (result == kStatus_Success)
98     {
99 #if defined(FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE)
100         result = PHY_KSZ8081_READ(handle, PHY_CONTROL2_REG, &regValue);
101         if (result != kStatus_Success)
102         {
103             return result;
104         }
105         result = PHY_KSZ8081_WRITE(handle, PHY_CONTROL2_REG, (regValue | PHY_CTL2_REFCLK_SELECT_MASK));
106         if (result != kStatus_Success)
107         {
108             return result;
109         }
110 #endif /* FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE */
111 
112         if (config->autoNeg)
113         {
114             /* Set the auto-negotiation then start it. */
115             result = PHY_KSZ8081_WRITE(
116                 handle, PHY_AUTONEG_ADVERTISE_REG,
117                 (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | PHY_10BASETX_FULLDUPLEX_MASK |
118                  PHY_10BASETX_HALFDUPLEX_MASK | PHY_IEEE802_3_SELECTOR_MASK));
119             if (result == kStatus_Success)
120             {
121                 result = PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG,
122                                            (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK));
123             }
124         }
125         else
126         {
127             /* This PHY only supports 10/100M speed. */
128             assert(config->speed <= kPHY_Speed100M);
129 
130             /* Disable isolate mode */
131             result = PHY_KSZ8081_READ(handle, PHY_BASICCONTROL_REG, &regValue);
132             if (result != kStatus_Success)
133             {
134                 return result;
135             }
136             regValue &= ~PHY_BCTL_ISOLATE_MASK;
137             result = PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
138             if (result != kStatus_Success)
139             {
140                 return result;
141             }
142 
143             /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */
144             result = PHY_KSZ8081_SetLinkSpeedDuplex(handle, config->speed, config->duplex);
145         }
146         if (result != kStatus_Success)
147         {
148             return result;
149         }
150 
151         /* Set PHY link status management interrupt. */
152         result = PHY_KSZ8081_EnableLinkInterrupt(handle, config->intrType);
153     }
154     return result;
155 }
156 
PHY_KSZ8081_Write(phy_handle_t * handle,uint8_t phyReg,uint16_t data)157 status_t PHY_KSZ8081_Write(phy_handle_t *handle, uint8_t phyReg, uint16_t data)
158 {
159     return PHY_KSZ8081_WRITE(handle, phyReg, data);
160 }
161 
PHY_KSZ8081_Read(phy_handle_t * handle,uint8_t phyReg,uint16_t * pData)162 status_t PHY_KSZ8081_Read(phy_handle_t *handle, uint8_t phyReg, uint16_t *pData)
163 {
164     return PHY_KSZ8081_READ(handle, phyReg, pData);
165 }
166 
PHY_KSZ8081_GetAutoNegotiationStatus(phy_handle_t * handle,bool * status)167 status_t PHY_KSZ8081_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status)
168 {
169     assert(status);
170 
171     status_t result;
172     uint16_t regValue;
173 
174     *status = false;
175 
176     /* Check auto negotiation complete. */
177     result = PHY_KSZ8081_READ(handle, PHY_BASICSTATUS_REG, &regValue);
178     if (result == kStatus_Success)
179     {
180         if ((regValue & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0U)
181         {
182             *status = true;
183         }
184     }
185     return result;
186 }
187 
PHY_KSZ8081_GetLinkStatus(phy_handle_t * handle,bool * status)188 status_t PHY_KSZ8081_GetLinkStatus(phy_handle_t *handle, bool *status)
189 {
190     assert(status);
191 
192     status_t result;
193     uint16_t regValue;
194 
195     /* Read the basic status register. */
196     result = PHY_KSZ8081_READ(handle, PHY_BASICSTATUS_REG, &regValue);
197     if (result == kStatus_Success)
198     {
199         if ((PHY_BSTATUS_LINKSTATUS_MASK & regValue) != 0U)
200         {
201             /* Link up. */
202             *status = true;
203         }
204         else
205         {
206             /* Link down. */
207             *status = false;
208         }
209     }
210     return result;
211 }
212 
PHY_KSZ8081_GetLinkSpeedDuplex(phy_handle_t * handle,phy_speed_t * speed,phy_duplex_t * duplex)213 status_t PHY_KSZ8081_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex)
214 {
215     assert(!((speed == NULL) && (duplex == NULL)));
216 
217     status_t result;
218     uint16_t regValue;
219     uint16_t flag;
220 
221     /* Read the control register. */
222     result = PHY_KSZ8081_READ(handle, PHY_CONTROL1_REG, &regValue);
223     if (result == kStatus_Success)
224     {
225         if (speed != NULL)
226         {
227             flag = regValue & PHY_CTL1_SPEEDUPLX_MASK;
228             if ((PHY_CTL1_100HALFDUPLEX_MASK == flag) || (PHY_CTL1_100FULLDUPLEX_MASK == flag))
229             {
230                 *speed = kPHY_Speed100M;
231             }
232             else
233             {
234                 *speed = kPHY_Speed10M;
235             }
236         }
237 
238         if (duplex != NULL)
239         {
240             flag = regValue & PHY_CTL1_SPEEDUPLX_MASK;
241             if ((PHY_CTL1_10FULLDUPLEX_MASK == flag) || (PHY_CTL1_100FULLDUPLEX_MASK == flag))
242             {
243                 *duplex = kPHY_FullDuplex;
244             }
245             else
246             {
247                 *duplex = kPHY_HalfDuplex;
248             }
249         }
250     }
251     return result;
252 }
253 
PHY_KSZ8081_SetLinkSpeedDuplex(phy_handle_t * handle,phy_speed_t speed,phy_duplex_t duplex)254 status_t PHY_KSZ8081_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex)
255 {
256     /* This PHY only supports 10/100M speed. */
257     assert(speed <= kPHY_Speed100M);
258 
259     status_t result;
260     uint16_t regValue;
261 
262     result = PHY_KSZ8081_READ(handle, PHY_BASICCONTROL_REG, &regValue);
263     if (result == kStatus_Success)
264     {
265         /* Disable the auto-negotiation and set according to user-defined configuration. */
266         regValue &= ~PHY_BCTL_AUTONEG_MASK;
267         if (speed == kPHY_Speed100M)
268         {
269             regValue |= PHY_BCTL_SPEED0_MASK;
270         }
271         else
272         {
273             regValue &= ~PHY_BCTL_SPEED0_MASK;
274         }
275         if (duplex == kPHY_FullDuplex)
276         {
277             regValue |= PHY_BCTL_DUPLEX_MASK;
278         }
279         else
280         {
281             regValue &= ~PHY_BCTL_DUPLEX_MASK;
282         }
283         result = PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
284     }
285     return result;
286 }
287 
PHY_KSZ8081_EnableLoopback(phy_handle_t * handle,phy_loop_t mode,phy_speed_t speed,bool enable)288 status_t PHY_KSZ8081_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable)
289 {
290     /* This PHY only supports local/remote loopback and 10/100M speed. */
291     assert(mode <= kPHY_RemoteLoop);
292     assert(speed <= kPHY_Speed100M);
293 
294     status_t result;
295     uint16_t regValue;
296 
297     /* Set the loop mode. */
298     if (enable)
299     {
300         if (mode == kPHY_LocalLoop)
301         {
302             if (speed == kPHY_Speed100M)
303             {
304                 regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
305             }
306             else
307             {
308                 regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
309             }
310             return PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
311         }
312         else
313         {
314             /* Remote loopback only supports 100M full-duplex. */
315             assert(speed == kPHY_Speed100M);
316 
317             regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
318             result   = PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, regValue);
319             if (result != kStatus_Success)
320             {
321                 return result;
322             }
323             /* Set the remote loopback bit. */
324             result = PHY_KSZ8081_READ(handle, PHY_CONTROL2_REG, &regValue);
325             if (result == kStatus_Success)
326             {
327                 return PHY_KSZ8081_WRITE(handle, PHY_CONTROL2_REG, (regValue | PHY_CTL2_REMOTELOOP_MASK));
328             }
329         }
330     }
331     else
332     {
333         /* Disable the loop mode. */
334         if (mode == kPHY_LocalLoop)
335         {
336             /* First read the current status in control register. */
337             result = PHY_KSZ8081_READ(handle, PHY_BASICCONTROL_REG, &regValue);
338             if (result == kStatus_Success)
339             {
340                 regValue &= ~PHY_BCTL_LOOP_MASK;
341                 return PHY_KSZ8081_WRITE(handle, PHY_BASICCONTROL_REG, (regValue | PHY_BCTL_RESTART_AUTONEG_MASK));
342             }
343         }
344         else
345         {
346             /* First read the current status in control one register. */
347             result = PHY_KSZ8081_READ(handle, PHY_CONTROL2_REG, &regValue);
348             if (result == kStatus_Success)
349             {
350                 return PHY_KSZ8081_WRITE(handle, PHY_CONTROL2_REG, regValue & ~PHY_CTL2_REMOTELOOP_MASK);
351             }
352         }
353     }
354     return result;
355 }
356 
PHY_KSZ8081_EnableLinkInterrupt(phy_handle_t * handle,phy_interrupt_type_t type)357 status_t PHY_KSZ8081_EnableLinkInterrupt(phy_handle_t *handle, phy_interrupt_type_t type)
358 {
359     status_t result;
360     uint16_t regValue;
361 
362     /* Read operation will clear pending interrupt before enable interrupt. */
363     result = PHY_KSZ8081_READ(handle, PHY_INTR_CONTROL_STATUS_REG, &regValue);
364     if (result == kStatus_Success)
365     {
366         /* Enable/Disable link up+down interrupt. */
367         if (type != kPHY_IntrDisable)
368         {
369             regValue |= (PHY_INTR_CONTROL_STATUS_LINK_UP_MASK | PHY_INTR_CONTROL_STATUS_LINK_DOWN_MASK);
370         }
371         else
372         {
373             regValue &= ~(PHY_INTR_CONTROL_STATUS_LINK_UP_MASK | PHY_INTR_CONTROL_STATUS_LINK_DOWN_MASK);
374         }
375         result = PHY_KSZ8081_WRITE(handle, PHY_INTR_CONTROL_STATUS_REG, regValue);
376     }
377     if (result != kStatus_Success)
378     {
379         return result;
380     }
381 
382     if (type != kPHY_IntrDisable)
383     {
384         /* Set link interrupt type. */
385         result = PHY_KSZ8081_READ(handle, PHY_CONTROL2_REG, &regValue);
386         if (result == kStatus_Success)
387         {
388             if (type == kPHY_IntrActiveLow)
389             {
390                 regValue &= ~PHY_CTL2_INTR_LEVEL_MASK;
391             }
392             else
393             {
394                 regValue |= PHY_CTL2_INTR_LEVEL_MASK;
395             }
396             result = PHY_KSZ8081_WRITE(handle, PHY_CONTROL2_REG, regValue);
397         }
398     }
399 
400     return result;
401 }
402 
PHY_KSZ8081_ClearInterrupt(phy_handle_t * handle)403 status_t PHY_KSZ8081_ClearInterrupt(phy_handle_t *handle)
404 {
405     uint16_t regValue;
406 
407     return PHY_KSZ8081_READ(handle, PHY_INTR_CONTROL_STATUS_REG, &regValue);
408 }
409