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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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, ®Value);
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