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