1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date           Author       Notes
8 * 2018-11-19     SummerGift   first version
9 * 2018-12-25     zylx         fix some bugs
10 * 2019-06-10     SummerGift   optimize PHY state detection process
11 * 2019-09-03     xiaofan      optimize link change detection process
12 * 2025-02-11     kurisaW      adaptation for RZ Ethernet driver
13 */
14 
15 #include "drv_config.h"
16 #include "drv_eth.h"
17 #include <hal_data.h>
18 #include <netif/ethernetif.h>
19 #include <lwipopts.h>
20 
21 /* debug option */
22 // #define ETH_RX_DUMP
23 // #define ETH_TX_DUMP
24 #define MINIMUM_ETHERNET_FRAME_SIZE (60U)
25 #define ETH_MAX_PACKET_SIZE (2048U)
26 #define ETHER_GMAC_INTERRUPT_FACTOR_RECEPTION (0x000000C0)
27 #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE
28 #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE
29 // #define DRV_DEBUG
30 #define LOG_TAG "drv.eth"
31 #ifdef DRV_DEBUG
32 #define DBG_LVL DBG_LOG
33 #else
34 #define DBG_LVL DBG_INFO
35 #endif /* DRV_DEBUG */
36 #include <rtdbg.h>
37 
38 struct rt_eth_dev
39 {
40     /* inherit from ethernet device */
41     struct eth_device parent;
42 #ifndef PHY_USING_INTERRUPT_MODE
43     rt_timer_t poll_link_timer;
44 #endif
45 };
46 
47 static rt_uint8_t *Rx_Buff, *Tx_Buff;
48 // static  ETH_HandleTypeDef EthHandle;
49 static struct rt_eth_dev ra_eth_device;
50 
51 static uint8_t g_link_change = 0; ///< Link change (bit0:port0, bit1:port1, bit2:port2)
52 static uint8_t g_link_status = 0; ///< Link status (bit0:port0, bit1:port1, bit2:port2)
53 static uint8_t previous_link_status = 0;
54 
55 #if defined(SOC_SERIES_R9A07G0)
56 
57 #define status_ecsr             status_link
58 #define ETHER_EVENT_INTERRUPT   ETHER_EVENT_SBD_INTERRUPT
59 
60 #define R_ETHER_Open        R_GMAC_Open
61 #define R_ETHER_Write       R_GMAC_Write
62 #define R_ETHER_Read        R_GMAC_Read
63 #define R_ETHER_LinkProcess R_GMAC_LinkProcess
64 
65 #endif
66 
67 #if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
68 #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
dump_hex(const rt_uint8_t * ptr,rt_size_t buflen)69 static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
70 {
71     unsigned char *buf = (unsigned char *)ptr;
72     int i, j;
73 
74     for (i = 0; i < buflen; i += 16)
75     {
76         rt_kprintf("%08X: ", i);
77 
78         for (j = 0; j < 16; j++)
79             if (i + j < buflen)
80                 rt_kprintf("%02X ", buf[i + j]);
81             else
82                 rt_kprintf("   ");
83         rt_kprintf(" ");
84 
85         for (j = 0; j < 16; j++)
86             if (i + j < buflen)
87                 rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
88         rt_kprintf("\n");
89     }
90 }
91 #endif
92 
93 extern void phy_reset(void);
94 /* EMAC initialization function */
rt_ra_eth_init(void)95 static rt_err_t rt_ra_eth_init(void)
96 {
97     fsp_err_t res;
98 
99     res = R_ETHER_Open(&g_ether0_ctrl, &g_ether0_cfg);
100     if (res != FSP_SUCCESS)
101         LOG_W("R_ETHER_Open failed!, res = %d", res);
102 
103     return RT_EOK;
104 }
105 
rt_ra_eth_open(rt_device_t dev,rt_uint16_t oflag)106 static rt_err_t rt_ra_eth_open(rt_device_t dev, rt_uint16_t oflag)
107 {
108     LOG_D("emac open");
109     return RT_EOK;
110 }
111 
rt_ra_eth_close(rt_device_t dev)112 static rt_err_t rt_ra_eth_close(rt_device_t dev)
113 {
114     LOG_D("emac close");
115     return RT_EOK;
116 }
117 
rt_ra_eth_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)118 static rt_ssize_t rt_ra_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
119 {
120     LOG_D("emac read");
121     rt_set_errno(-RT_ENOSYS);
122     return 0;
123 }
124 
rt_ra_eth_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)125 static rt_ssize_t rt_ra_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
126 {
127     LOG_D("emac write");
128     rt_set_errno(-RT_ENOSYS);
129     return 0;
130 }
131 
rt_ra_eth_control(rt_device_t dev,int cmd,void * args)132 static rt_err_t rt_ra_eth_control(rt_device_t dev, int cmd, void *args)
133 {
134     switch (cmd)
135     {
136     case NIOCTL_GADDR:
137         /* get mac address */
138         if (args)
139         {
140 #if defined(SOC_SERIES_R9A07G0)
141             SMEMCPY(args, g_ether0_ctrl.p_gmac_cfg->p_mac_address, 6);
142 #else
143             SMEMCPY(args, g_ether0_ctrl.p_ether_cfg->p_mac_address, 6);
144 #endif
145         }
146         else
147         {
148             return -RT_ERROR;
149         }
150         break;
151 
152     default:
153         break;
154     }
155 
156     return RT_EOK;
157 }
158 
159 /* ethernet device interface */
160 /* transmit data*/
rt_ra_eth_tx(rt_device_t dev,struct pbuf * p)161 rt_err_t rt_ra_eth_tx(rt_device_t dev, struct pbuf *p)
162 {
163     fsp_err_t res;
164     struct pbuf *q;
165     uint8_t *buffer = Tx_Buff;
166     uint32_t framelength = 0;
167     uint32_t bufferoffset = 0;
168     uint32_t byteslefttocopy = 0;
169     uint32_t payloadoffset = 0;
170     bufferoffset = 0;
171 
172     LOG_D("send frame len : %d", p->tot_len);
173 
174     /* copy frame from pbufs to driver buffers */
175     for (q = p; q != NULL; q = q->next)
176     {
177         /* Get bytes in current lwIP buffer */
178         byteslefttocopy = q->len;
179         payloadoffset = 0;
180 
181         /* Check if the length of data to copy is bigger than Tx buffer size*/
182         while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE)
183         {
184             /* Copy data to Tx buffer*/
185             SMEMCPY((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));
186 
187             byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
188             payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
189             framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
190             bufferoffset = 0;
191         }
192 
193         /* Copy the remaining bytes */
194         SMEMCPY((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), byteslefttocopy);
195         bufferoffset = bufferoffset + byteslefttocopy;
196         framelength = framelength + byteslefttocopy;
197     }
198 
199 #ifdef ETH_TX_DUMP
200     dump_hex(buffer, p->tot_len);
201 #endif
202 #ifdef ETH_RX_DUMP
203     if (p)
204     {
205         LOG_E("******p buf frame *********");
206         for (q = p; q != NULL; q = q->next)
207         {
208             dump_hex(q->payload, q->len);
209         }
210     }
211 #endif
212     res = R_ETHER_Write(&g_ether0_ctrl, buffer, p->tot_len);//>MINIMUM_ETHERNET_FRAME_SIZE?p->tot_len:MINIMUM_ETHERNET_FRAME_SIZE);
213     if (res != FSP_SUCCESS)
214         LOG_W("R_ETHER_Write failed!, res = %d", res);
215     return RT_EOK;
216 }
217 
218 /* receive data*/
rt_ra_eth_rx(rt_device_t dev)219 struct pbuf *rt_ra_eth_rx(rt_device_t dev)
220 {
221     struct pbuf *p = NULL;
222     struct pbuf *q = NULL;
223     uint32_t len = 0;
224     uint8_t *buffer = Rx_Buff;
225     fsp_err_t res;
226 
227     res = R_ETHER_Read(&g_ether0_ctrl, buffer, &len);
228     if (res != FSP_SUCCESS)
229         LOG_D("R_ETHER_Read failed!, res = %d", res);
230 
231     uint32_t bufferoffset = 0;
232     uint32_t payloadoffset = 0;
233     uint32_t byteslefttocopy = 0;
234 
235     LOG_D("receive frame len : %d", len);
236 
237     if (len > 0)
238     {
239         /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
240         p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
241     }
242 
243 #ifdef ETH_RX_DUMP
244     if (p)
245     {
246         dump_hex(buffer, p->tot_len);
247     }
248 #endif
249 
250     if (p != NULL)
251     {
252         bufferoffset = 0;
253         for (q = p; q != NULL; q = q->next)
254         {
255             byteslefttocopy = q->len;
256             payloadoffset = 0;
257 
258             /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
259             while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE)
260             {
261                 /* Copy data to pbuf */
262                 SMEMCPY((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
263 
264                 byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
265                 payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
266                 bufferoffset = 0;
267             }
268             /* Copy remaining data in pbuf */
269             SMEMCPY((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy);
270             bufferoffset = bufferoffset + byteslefttocopy;
271         }
272     }
273 
274 #ifdef ETH_RX_DUMP
275     if (p)
276     {
277         LOG_E("******p buf frame *********");
278         for (q = p; q != NULL; q = q->next)
279         {
280             dump_hex(q->payload, q->len);
281         }
282     }
283 #endif
284 
285     return p;
286 }
287 
phy_linkchange()288 static void phy_linkchange()
289 {
290     fsp_err_t res;
291     uint8_t port = 0;
292     uint8_t port_bit = 0;
293 
294 #if defined(SOC_SERIES_R9A07G0)
295     gmac_link_status_t port_status;
296 #endif
297 
298     res = R_ETHER_LinkProcess(&g_ether0_ctrl);
299     if (res != FSP_SUCCESS)
300         LOG_D("R_ETHER_LinkProcess failed!, res = %d", res);
301 
302     if (0 == g_ether0.p_cfg->p_callback)
303     {
304         for (port = 0; port < PING_PORT_COUNT; port++)
305         {
306 #if defined(SOC_SERIES_R9A07G0)
307             res = R_GMAC_GetLinkStatus(&g_ether0_ctrl, port, &port_status);
308 #else
309             res = R_ETHER_PHY_LinkStatusGet(&g_ether_phy0_ctrl);
310 #endif
311             if (FSP_SUCCESS != res)
312             {
313                 /* An error has occurred */
314                 LOG_E("PHY_LinkStatus get failed!, res = %d", res);
315                 break;
316             }
317 
318             /* Set link up */
319             g_link_status |= (uint8_t)(1U << port);
320         }
321         if (FSP_SUCCESS == res)
322         {
323             /* Set changed link status */
324             g_link_change = previous_link_status ^ g_link_status;
325         }
326     }
327 
328     for (port = 0; port < PING_PORT_COUNT; port++)
329     {
330         port_bit = (uint8_t)(1U << port);
331 
332         if (g_link_change & port_bit)
333         {
334             /* Link status changed */
335             g_link_change &= (uint8_t)(~port_bit); // change bit clear
336 
337             if (g_link_status & port_bit)
338             {
339                 /* Changed to Link-up */
340                 eth_device_linkchange(&ra_eth_device.parent, RT_TRUE);
341                 LOG_I("link up");
342             }
343             else
344             {
345                 /* Changed to Link-down */
346                 eth_device_linkchange(&ra_eth_device.parent, RT_FALSE);
347                 LOG_I("link down");
348             }
349         }
350     }
351 
352     previous_link_status = g_link_status;
353 }
354 
user_ether0_callback(ether_callback_args_t * p_args)355 void user_ether0_callback(ether_callback_args_t *p_args)
356 {
357     rt_interrupt_enter();
358 
359     switch (p_args->event)
360     {
361     case ETHER_EVENT_LINK_ON:                          ///< Link up detection event/
362         g_link_status |= (uint8_t)p_args->status_ecsr; ///< status up
363         g_link_change |= (uint8_t)p_args->status_ecsr; ///< change bit set
364         break;
365 
366     case ETHER_EVENT_LINK_OFF:                            ///< Link down detection event
367         g_link_status &= (uint8_t)(~p_args->status_ecsr); ///< status down
368         g_link_change |= (uint8_t)p_args->status_ecsr;    ///< change bit set
369         break;
370 
371     case ETHER_EVENT_WAKEON_LAN:    ///< Magic packet detection event
372     /* If EDMAC FR (Frame Receive Event) or FDE (Receive Descriptor Empty Event)
373         * interrupt occurs, send rx mailbox. */
374     case ETHER_EVENT_INTERRUPT: ///< BSD Interrupt event
375     {
376         rt_err_t result;
377         result = eth_device_ready(&(ra_eth_device.parent));
378         if (result != RT_EOK)
379             rt_kprintf("RX err =%d\n", result);
380         break;
381     }
382 
383     default:
384         break;
385     }
386 
387     rt_interrupt_leave();
388 }
389 
390 /* Register the EMAC device */
rt_hw_ra_eth_init(void)391 static int rt_hw_ra_eth_init(void)
392 {
393     rt_err_t state = RT_EOK;
394 
395     /* Prepare receive and send buffers */
396     Rx_Buff = (rt_uint8_t *)rt_calloc(1, ETH_MAX_PACKET_SIZE);
397     if (Rx_Buff == RT_NULL)
398     {
399         LOG_E("No memory");
400         state = -RT_ENOMEM;
401         goto __exit;
402     }
403 
404     Tx_Buff = (rt_uint8_t *)rt_calloc(1, ETH_MAX_PACKET_SIZE);
405     if (Tx_Buff == RT_NULL)
406     {
407         LOG_E("No memory");
408         state = -RT_ENOMEM;
409         goto __exit;
410     }
411 
412     ra_eth_device.parent.parent.init = NULL;
413     ra_eth_device.parent.parent.open = rt_ra_eth_open;
414     ra_eth_device.parent.parent.close = rt_ra_eth_close;
415     ra_eth_device.parent.parent.read = rt_ra_eth_read;
416     ra_eth_device.parent.parent.write = rt_ra_eth_write;
417     ra_eth_device.parent.parent.control = rt_ra_eth_control;
418     ra_eth_device.parent.parent.user_data = RT_NULL;
419 
420     ra_eth_device.parent.eth_rx = rt_ra_eth_rx;
421     ra_eth_device.parent.eth_tx = rt_ra_eth_tx;
422 
423     rt_ra_eth_init();
424 
425     /* register eth device */
426     state = eth_device_init(&(ra_eth_device.parent), "e0");
427     if (RT_EOK == state)
428     {
429         LOG_D("emac device init success");
430     }
431     else
432     {
433         LOG_E("emac device init faild: %d", state);
434         state = -RT_ERROR;
435         goto __exit;
436     }
437 
438     ra_eth_device.poll_link_timer = rt_timer_create("phylnk", (void (*)(void *))phy_linkchange,
439                                                     NULL, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC);
440     if (!ra_eth_device.poll_link_timer || rt_timer_start(ra_eth_device.poll_link_timer) != RT_EOK)
441     {
442         LOG_E("Start link change detection timer failed");
443     }
444 __exit:
445     if (state != RT_EOK)
446     {
447         if (Rx_Buff)
448         {
449             rt_free(Rx_Buff);
450         }
451 
452         if (Tx_Buff)
453         {
454             rt_free(Tx_Buff);
455         }
456     }
457 
458     return state;
459 }
460 INIT_DEVICE_EXPORT(rt_hw_ra_eth_init);
461