1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-10-14 wangqiang the first version
9 * 2022-08-29 xjy198903 add rt1170 support
10 * 2024-04-18 Jiading add a parameter passed into rt_phy_ops APIs
11 */
12
13 #include <rtthread.h>
14
15 #ifdef PHY_USING_KSZ8081
16
17 #include <rtdevice.h>
18 #include "drv_gpio.h"
19 #include "drv_mdio.h"
20
21
22 /*******************************************************************************
23 * Definitions
24 ******************************************************************************/
25
26 /*! @brief Defines the PHY registers. */
27 #define PHY_BASICCONTROL_REG 0x00U /*!< The PHY basic control register. */
28 #define PHY_BASICSTATUS_REG 0x01U /*!< The PHY basic status register. */
29 #define PHY_ID1_REG 0x02U /*!< The PHY ID one register. */
30 #define PHY_ID2_REG 0x03U /*!< The PHY ID two register. */
31 #define PHY_AUTONEG_ADVERTISE_REG 0x04U /*!< The PHY auto-negotiate advertise register. */
32 #define PHY_CONTROL1_REG 0x1EU /*!< The PHY control one register. */
33 #define PHY_CONTROL2_REG 0x1FU /*!< The PHY control two register. */
34
35 #define PHY_CONTROL_ID1 0x22U /*!< The PHY ID1*/
36
37 /*! @brief Defines the mask flag in basic control register. */
38 #define PHY_BCTL_DUPLEX_MASK 0x0100U /*!< The PHY duplex bit mask. */
39 #define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */
40 #define PHY_BCTL_AUTONEG_MASK 0x1000U /*!< The PHY auto negotiation bit mask. */
41 #define PHY_BCTL_SPEED_MASK 0x2000U /*!< The PHY speed bit mask. */
42 #define PHY_BCTL_LOOP_MASK 0x4000U /*!< The PHY loop bit mask. */
43 #define PHY_BCTL_RESET_MASK 0x8000U /*!< The PHY reset bit mask. */
44 #define PHY_BCTL_SPEED_100M_MASK 0x2000U /*!< The PHY 100M speed mask. */
45
46 /*!@brief Defines the mask flag of operation mode in control two register*/
47 #define PHY_CTL2_REMOTELOOP_MASK 0x0004U /*!< The PHY remote loopback mask. */
48 #define PHY_CTL2_REFCLK_SELECT_MASK 0x0080U /*!< The PHY RMII reference clock select. */
49 #define PHY_CTL1_10HALFDUPLEX_MASK 0x0001U /*!< The PHY 10M half duplex mask. */
50 #define PHY_CTL1_100HALFDUPLEX_MASK 0x0002U /*!< The PHY 100M half duplex mask. */
51 #define PHY_CTL1_10FULLDUPLEX_MASK 0x0005U /*!< The PHY 10M full duplex mask. */
52 #define PHY_CTL1_100FULLDUPLEX_MASK 0x0006U /*!< The PHY 100M full duplex mask. */
53 #define PHY_CTL1_SPEEDUPLX_MASK 0x0007U /*!< The PHY speed and duplex mask. */
54 #define PHY_CTL1_ENERGYDETECT_MASK 0x10U /*!< The PHY signal present on rx differential pair. */
55 #define PHY_CTL1_LINKUP_MASK 0x100U /*!< The PHY link up. */
56 #define PHY_LINK_READY_MASK (PHY_CTL1_ENERGYDETECT_MASK | PHY_CTL1_LINKUP_MASK)
57
58 /*! @brief Defines the mask flag in basic status register. */
59 #define PHY_BSTATUS_LINKSTATUS_MASK 0x0004U /*!< The PHY link status mask. */
60 #define PHY_BSTATUS_AUTONEGABLE_MASK 0x0008U /*!< The PHY auto-negotiation ability mask. */
61 #define PHY_BSTATUS_AUTONEGCOMP_MASK 0x0020U /*!< The PHY auto-negotiation complete mask. */
62
63 /*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */
64 #define PHY_100BaseT4_ABILITY_MASK 0x200U /*!< The PHY have the T4 ability. */
65 #define PHY_100BASETX_FULLDUPLEX_MASK 0x100U /*!< The PHY has the 100M full duplex ability.*/
66 #define PHY_100BASETX_HALFDUPLEX_MASK 0x080U /*!< The PHY has the 100M full duplex ability.*/
67 #define PHY_10BASETX_FULLDUPLEX_MASK 0x040U /*!< The PHY has the 10M full duplex ability.*/
68 #define PHY_10BASETX_HALFDUPLEX_MASK 0x020U /*!< The PHY has the 10M full duplex ability.*/
69
70
71
72 /*! @brief Defines the timeout macro. */
73 #define PHY_TIMEOUT_COUNT 0x3FFFFFFU
74
75 /* defined the Reset pin, PORT and PIN config by menuconfig */
76 #define RESET_PIN GET_PIN(PHY_RESET_KSZ8081_PORT, PHY_RESET_KSZ8081_PIN)
77
78 /*******************************************************************************
79 * Prototypes
80 ******************************************************************************/
81
82
83 /*******************************************************************************
84 * Variables
85 ******************************************************************************/
86 static struct rt_phy_device phy_ksz8081;
87
88 /*******************************************************************************
89 * Code
90 ******************************************************************************/
91
92
93
read_reg(rt_mdio_t * bus,rt_uint32_t addr,rt_uint32_t reg_id,rt_uint32_t * value)94 static inline rt_bool_t read_reg(rt_mdio_t *bus, rt_uint32_t addr, rt_uint32_t reg_id, rt_uint32_t *value)
95 {
96 if (4 != bus->ops->read(bus, addr, reg_id, value, 4))
97 {
98 return RT_FALSE;
99 }
100 return RT_TRUE;
101 }
102
write_reg(rt_mdio_t * bus,rt_uint32_t addr,rt_uint32_t reg_id,rt_uint32_t value)103 static inline rt_bool_t write_reg(rt_mdio_t *bus, rt_uint32_t addr, rt_uint32_t reg_id, rt_uint32_t value)
104 {
105 if (4 != bus->ops->write(bus, addr, reg_id, &value, 4))
106 {
107 return RT_FALSE;
108 }
109 return RT_TRUE;
110 }
111
rt_phy_init(void * object,rt_uint32_t phy_addr,rt_uint32_t src_clock_hz)112 static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz)
113 {
114 rt_bool_t ret;
115 rt_phy_status result;
116 rt_uint32_t counter = PHY_TIMEOUT_COUNT;
117 rt_uint32_t id_reg = 0;
118 rt_uint32_t time_delay;
119 rt_uint32_t bss_reg;
120 rt_uint32_t ctl_reg = 0;
121
122 // reset phy device by gpio
123 rt_pin_mode(RESET_PIN, PIN_MODE_OUTPUT);
124 rt_pin_write(RESET_PIN, PIN_LOW);
125 rt_thread_mdelay(100);
126 rt_pin_write(RESET_PIN, PIN_HIGH);
127
128 rt_mdio_t *mdio_bus = rt_hw_mdio_register(object, "phy_mdio");
129 if (RT_NULL == mdio_bus)
130 {
131 return PHY_STATUS_FAIL;
132 }
133 phy_ksz8081.bus = mdio_bus;
134 phy_ksz8081.addr = phy_addr;
135 ret = mdio_bus->ops->init(mdio_bus, src_clock_hz);
136 if ( !ret )
137 {
138 return PHY_STATUS_FAIL;
139 }
140
141 /* Initialization after PHY stars to work. */
142 while ((id_reg != PHY_CONTROL_ID1) && (counter != 0))
143 {
144 phy_ksz8081.ops->read(NULL, PHY_ID1_REG, &id_reg);
145 counter--;
146 }
147
148 if (!counter)
149 {
150 return PHY_STATUS_FAIL;
151 }
152
153 /* Reset PHY. */
154 counter = PHY_TIMEOUT_COUNT;
155 result = phy_ksz8081.ops->write(NULL, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK);
156 if (PHY_STATUS_OK == result)
157 {
158 #if defined(FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE)
159 rt_uint32_t data = 0;
160 result = phy_ksz8081.ops->read(NULL, PHY_CONTROL2_REG, &data);
161 if (PHY_STATUS_FAIL == result)
162 {
163 return PHY_STATUS_FAIL;
164 }
165 result = phy_ksz8081.ops->write(NULL, PHY_CONTROL2_REG, (data | PHY_CTL2_REFCLK_SELECT_MASK));
166 if (PHY_STATUS_FAIL == result)
167 {
168 return PHY_STATUS_FAIL;
169 }
170 #endif /* FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE */
171
172 /* Set the negotiation. */
173 result = phy_ksz8081.ops->write(NULL, PHY_AUTONEG_ADVERTISE_REG,
174 (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK |
175 PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | 0x1U));
176 if (PHY_STATUS_OK == result)
177 {
178 result = phy_ksz8081.ops->write(NULL, PHY_BASICCONTROL_REG, (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK));
179 if (PHY_STATUS_OK == result)
180 {
181 /* Check auto negotiation complete. */
182 while (counter--)
183 {
184 result = phy_ksz8081.ops->read(NULL, PHY_BASICSTATUS_REG, &bss_reg);
185 if (PHY_STATUS_OK == result)
186 {
187 phy_ksz8081.ops->read(NULL, PHY_CONTROL1_REG, &ctl_reg);
188 if (((bss_reg & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0) && (ctl_reg & PHY_LINK_READY_MASK))
189 {
190 /* Wait a moment for Phy status stable. */
191 for (time_delay = 0; time_delay < PHY_TIMEOUT_COUNT; time_delay++)
192 {
193 __ASM("nop");
194 }
195 break;
196 }
197 }
198
199 if (!counter)
200 {
201 return PHY_STATUS_FAIL;
202 }
203 }
204 }
205 }
206 }
207
208 return PHY_STATUS_OK;
209 }
210
211
rt_phy_read(rt_phy_t * phy,rt_uint32_t reg,rt_uint32_t * data)212 static rt_phy_status rt_phy_read(rt_phy_t *phy, rt_uint32_t reg, rt_uint32_t *data)
213 {
214 rt_mdio_t *mdio_bus = phy_ksz8081.bus;
215 rt_uint32_t device_id = phy_ksz8081.addr;
216
217 if (read_reg(mdio_bus, device_id, reg, data))
218 {
219 return PHY_STATUS_OK;
220 }
221 return PHY_STATUS_FAIL;
222 }
223
rt_phy_write(rt_phy_t * phy,rt_uint32_t reg,rt_uint32_t data)224 static rt_phy_status rt_phy_write(rt_phy_t *phy, rt_uint32_t reg, rt_uint32_t data)
225 {
226 rt_mdio_t *mdio_bus = phy_ksz8081.bus;
227 rt_uint32_t device_id = phy_ksz8081.addr;
228
229 if (write_reg(mdio_bus, device_id, reg, data))
230 {
231 return PHY_STATUS_OK;
232 }
233 return PHY_STATUS_FAIL;
234 }
235
rt_phy_loopback(rt_phy_t * phy,rt_uint32_t mode,rt_uint32_t speed,rt_bool_t enable)236 static rt_phy_status rt_phy_loopback(rt_phy_t *phy, rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable)
237 {
238 rt_uint32_t data = 0;
239 rt_phy_status result;
240
241 /* Set the loop mode. */
242 if (enable)
243 {
244 if (PHY_LOCAL_LOOP == mode)
245 {
246 if (PHY_SPEED_100M == speed)
247 {
248 data = PHY_BCTL_SPEED_100M_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
249 }
250 else
251 {
252 data = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
253 }
254 return phy_ksz8081.ops->write(NULL, PHY_BASICCONTROL_REG, data);
255 }
256 else
257 {
258 /* First read the current status in control register. */
259 result = phy_ksz8081.ops->read(NULL, PHY_CONTROL2_REG, &data);
260 if (PHY_STATUS_OK == result)
261 {
262 return phy_ksz8081.ops->write(NULL, PHY_CONTROL2_REG, (data | PHY_CTL2_REMOTELOOP_MASK));
263 }
264 }
265 }
266 else
267 {
268 /* Disable the loop mode. */
269 if (PHY_LOCAL_LOOP == mode)
270 {
271 /* First read the current status in control register. */
272 result = phy_ksz8081.ops->read(NULL, PHY_BASICCONTROL_REG, &data);
273 if (PHY_STATUS_OK == result)
274 {
275 data &= ~PHY_BCTL_LOOP_MASK;
276 return phy_ksz8081.ops->write(NULL, PHY_BASICCONTROL_REG, (data | PHY_BCTL_RESTART_AUTONEG_MASK));
277 }
278 }
279 else
280 {
281 /* First read the current status in control one register. */
282 result = phy_ksz8081.ops->read(NULL, PHY_CONTROL2_REG, &data);
283 if (PHY_STATUS_OK == result)
284 {
285 return phy_ksz8081.ops->write(NULL, PHY_CONTROL2_REG, (data & ~PHY_CTL2_REMOTELOOP_MASK));
286 }
287 }
288 }
289 return result;
290 }
291
get_link_status(rt_phy_t * phy,rt_bool_t * status)292 static rt_phy_status get_link_status(rt_phy_t *phy, rt_bool_t *status)
293 {
294 rt_phy_status result;
295 rt_uint32_t data;
296
297 /* Read the basic status register. */
298 result = phy_ksz8081.ops->read(NULL, PHY_BASICSTATUS_REG, &data);
299 if (PHY_STATUS_OK == result)
300 {
301 if (!(PHY_BSTATUS_LINKSTATUS_MASK & data))
302 {
303 /* link down. */
304 *status = RT_FALSE;
305 }
306 else
307 {
308 /* link up. */
309 *status = RT_TRUE;
310 }
311 }
312 return result;
313 }
get_link_speed_duplex(rt_phy_t * phy,rt_uint32_t * speed,rt_uint32_t * duplex)314 static rt_phy_status get_link_speed_duplex(rt_phy_t *phy, rt_uint32_t *speed, rt_uint32_t *duplex)
315 {
316 rt_phy_status result = PHY_STATUS_OK;
317 rt_uint32_t data, ctl_reg;
318
319 /* Read the control two register. */
320 result = phy_ksz8081.ops->read(NULL, PHY_CONTROL1_REG, &ctl_reg);
321 if (PHY_STATUS_OK == result)
322 {
323 data = ctl_reg & PHY_CTL1_SPEEDUPLX_MASK;
324 if ((PHY_CTL1_10FULLDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data))
325 {
326 /* Full duplex. */
327 *duplex = PHY_FULL_DUPLEX;
328 }
329 else
330 {
331 /* Half duplex. */
332 *duplex = PHY_HALF_DUPLEX;
333 }
334
335 data = ctl_reg & PHY_CTL1_SPEEDUPLX_MASK;
336 if ((PHY_CTL1_100HALFDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data))
337 {
338 /* 100M speed. */
339 *speed = PHY_SPEED_100M;
340 }
341 else
342 { /* 10M speed. */
343 *speed = PHY_SPEED_10M;
344 }
345 }
346
347 return result;
348 }
349
350 static struct rt_phy_ops phy_ops =
351 {
352 .init = rt_phy_init,
353 .read = rt_phy_read,
354 .write = rt_phy_write,
355 .loopback = rt_phy_loopback,
356 .get_link_status = get_link_status,
357 .get_link_speed_duplex = get_link_speed_duplex,
358 };
359
rt_phy_ksz8081_register(void)360 static int rt_phy_ksz8081_register( void )
361 {
362 phy_ksz8081.ops = &phy_ops;
363 rt_hw_phy_register(&phy_ksz8081, "ksz8081");
364 return 1;
365 }
366
367 INIT_DEVICE_EXPORT(rt_phy_ksz8081_register);
368
369 #endif /* PHY_USING_KSZ8081 */
370