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