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