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