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    Email                    Notes
8  * 2022-04-06  Kevin.Liu kevin.liu.mchp@gmail.com First Release
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <rtdbg.h>
14 
15 #include <netif/ethernetif.h>
16 #include <lwipopts.h>
17 
18 #include <atmel_start.h>
19 #include <peripheral_clk_config.h>
20 #include <ieee8023_mii_standard_config.h>
21 
22 #include "board.h"
23 #include "sam_gmac.h"
24 
25 #ifdef RT_USING_LWIP
26 
27 struct rt_sam_eth
28 {
29     /* inherit from ethernet device */
30     struct eth_device parent;
31 
32     struct mac_async_descriptor    *macif;
33     struct ethernet_phy_descriptor *phyif;
34 
35 #ifdef RT_USING_TIMER_SOFT
36     rt_timer_t  phy_monitor_timer;
37 #else
38     rt_thread_t phy_monitor_tid;
39 #endif
40 
41     /* ethernet MAC address */
42     rt_uint8_t  mac_addr[NETIF_MAX_HWADDR_LEN];
43 
44     /* GMAC Link Speed */
45     gmac_speed_type  link_speed;
46     /* GMAC Link Mode */
47     gmac_duplex_type link_mode;
48 };
49 
50 static struct rt_sam_eth sam_eth_device;
51 
52 /**
53  * @brief Called by GMAC RX interrupt, will notify RX task
54  *
55  * @note  Will call eth_device_ready to notify RX task.
56  *
57  * @param
58  *
59  * @return
60  */
rt_sam_gmac_rxcb(void)61 static void rt_sam_gmac_rxcb(void)
62 {
63     rt_err_t result;
64 
65     /* enter interrupt */
66     rt_interrupt_enter();
67 
68     result = eth_device_ready(&sam_eth_device.parent);
69     if (result != RT_EOK)
70         LOG_E("rt_sam_gmac_rxcb error");
71 
72     /* leave interrupt */
73     rt_interrupt_leave();
74 }
75 
76 /**
77  * @brief Initialize the MAC hardware
78  *
79  * @note  Will set MAC filter by using input MAC address.
80  *
81  * @param gmac_dev GMAC device description.
82  *
83  * @return
84  */
rt_sam_gmac_init(struct rt_sam_eth * gmac_dev)85 static inline void rt_sam_gmac_init(struct rt_sam_eth *gmac_dev)
86 {
87     struct mac_async_filter filter;
88 
89     /* set MAC hardware address */
90     rt_memcpy(filter.mac, sam_eth_device.mac_addr, NETIF_MAX_HWADDR_LEN);
91     filter.tid_enable = false;
92     mac_async_set_filter(gmac_dev->macif, 0, &filter);
93     mac_async_register_callback(gmac_dev->macif, MAC_ASYNC_RECEIVE_CB, (FUNC_PTR)rt_sam_gmac_rxcb);
94 }
95 
rt_sam_eth_init(rt_device_t dev)96 static rt_err_t rt_sam_eth_init(rt_device_t dev)
97 {
98     LOG_D("gmac init");
99     return RT_EOK;
100 }
101 
rt_sam_eth_open(rt_device_t dev,rt_uint16_t oflag)102 static rt_err_t rt_sam_eth_open(rt_device_t dev, rt_uint16_t oflag)
103 {
104     LOG_D("gmac open");
105     return RT_EOK;
106 }
107 
rt_sam_eth_close(rt_device_t dev)108 static rt_err_t rt_sam_eth_close(rt_device_t dev)
109 {
110     LOG_D("gmac close");
111     return RT_EOK;
112 }
113 
rt_sam_eth_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)114 static rt_ssize_t rt_sam_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
115 {
116     LOG_D("gmac read");
117     rt_set_errno(-RT_ENOSYS);
118     return 0;
119 }
120 
rt_sam_eth_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)121 static rt_ssize_t rt_sam_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
122 {
123     LOG_D("gmac write");
124     rt_set_errno(-RT_ENOSYS);
125     return 0;
126 }
127 
rt_sam_eth_control(rt_device_t dev,int cmd,void * args)128 static rt_err_t rt_sam_eth_control(rt_device_t dev, int cmd, void *args)
129 {
130     rt_err_t ret = RT_EOK;
131 
132     switch (cmd)
133     {
134         case NIOCTL_GADDR:
135             /* get mac address */
136             if (args)
137                 rt_memcpy(args, sam_eth_device.mac_addr, 6);
138             break;
139 
140         default :
141             break;
142     }
143 
144     return ret;
145 }
146 
147 /**
148  * @brief Transmission packet though the MAC hardware
149  *
150  * @note  Send package to MAC.
151  *
152  * @param dev the RT net device input.
153  *
154  * @param p stored message will be sent to MAC.
155  *
156  * @return RT_EOK.
157  */
rt_sam_eth_tx(rt_device_t dev,struct pbuf * p)158 rt_err_t rt_sam_eth_tx(rt_device_t dev, struct pbuf *p)
159 {
160     struct rt_sam_eth *gmac_dev = (struct rt_sam_eth *)dev->user_data;
161     struct pbuf *      q;
162     void *             tbuf;
163     uint8_t *          pos;
164 
165 #if ETH_PAD_SIZE
166     pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
167 #endif
168 
169     if (p->tot_len == p->len)
170     {
171         mac_async_write(gmac_dev->macif, p->payload, p->tot_len);
172     }
173     else
174     {
175         tbuf = mem_malloc(LWIP_MEM_ALIGN_SIZE(p->tot_len));
176         pos  = tbuf;
177         if (tbuf == NULL)
178         {
179             return ERR_MEM;
180         }
181         for (q = p; q != NULL; q = q->next)
182         {
183             rt_memcpy(pos, q->payload, q->len);
184             pos += q->len;
185         }
186         mac_async_write(gmac_dev->macif, tbuf, p->tot_len);
187         mem_free(tbuf);
188     }
189 
190 #if ETH_PAD_SIZE
191     pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
192 #endif
193 
194     LINK_STATS_INC(link.xmit);
195 
196     return ERR_OK;
197 }
198 
199 /**
200  * @brief Receive packet from the MAC hardware
201  *
202  * @note  Returned pbuf filled with the received packet (including MAC header)
203  *
204  * @param dev the RT net device input.
205  *
206  * @return NULL on memory error
207  */
rt_sam_eth_rx(rt_device_t dev)208 struct pbuf *rt_sam_eth_rx(rt_device_t dev)
209 {
210     struct rt_sam_eth *gmac_dev = (struct rt_sam_eth *)dev->user_data;
211     struct pbuf *      p;
212     u16_t              len;
213 
214     len = mac_async_read_len(gmac_dev->macif); /* Obtain the size of the packet */
215     if (len == 0)
216     {
217         return NULL;
218     }
219 
220 #if ETH_PAD_SIZE
221     len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
222 #endif
223 
224     /* Allocate a pbuf as one large chunk, This include protocol header */
225     p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
226 
227     if (p != NULL)
228     {
229 #if ETH_PAD_SIZE
230         pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
231 #endif
232 
233         /* Read the entire packet into the pbuf. */
234         mac_async_read(gmac_dev->macif, p->payload, p->len);
235 
236 #if ETH_PAD_SIZE
237         pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
238 #endif
239 
240         LINK_STATS_INC(link.recv);
241     }
242     else
243     {
244         mac_async_read(gmac_dev->macif, NULL, 0);
245         LINK_STATS_INC(link.memerr);
246         LINK_STATS_INC(link.drop);
247     }
248 
249     return p;
250 }
251 
252 /**
253  * @brief  PHY link status monitor task - timer task or thread
254  *
255  * @note   Will check link status, link mode and link speed
256  *
257  * @param parameter input parameter passing to the function.
258  *
259  * @return
260  */
rt_sam_eth_monitor(void * parameter)261 static void rt_sam_eth_monitor(void *parameter)
262 {
263     struct rt_sam_eth *sam_eth = (struct rt_sam_eth *)parameter;
264     bool link_up;
265     int32_t  ret;
266     uint16_t val;
267     static rt_uint8_t link_count = 0;
268 
269 #ifndef RT_USING_TIMER_SOFT
270     while (1)
271     {
272 #endif
273         ret = ethernet_phy_get_link_status(sam_eth->phyif, &link_up);
274         if (ERR_NONE == ret)
275         {
276             if (link_up)
277             {
278                 /* send link up. */
279                 eth_device_linkchange(&sam_eth->parent, RT_TRUE);
280             }
281             else
282             {
283                 /* send link down. */
284                 eth_device_linkchange(&sam_eth->parent, RT_FALSE);;
285             }
286         }
287 
288         ret = ethernet_phy_read_reg(sam_eth->phyif, MDIO_REG1_BMSR, &val);
289         if (ERR_NONE == ret)
290         {
291             if (val & (MDIO_REG1_BIT_100BASE_TX_FD | MDIO_REG1_BIT_100BASE_TX_HD))
292             {
293                 LOG_D("100Mbps");
294                 sam_eth_device.link_speed = GMAC_SPEED_100MBPS;
295             }
296             else
297             {
298                 LOG_D("10Mbps");
299                 sam_eth_device.link_speed = GMAC_SPEED_10MBPS;
300             }
301 
302             if (val & (MDIO_REG1_BIT_100BASE_TX_FD | MDIO_REG1_BIT_10BASE_T_FD))
303             {
304                 LOG_D("100Mbps");
305                 sam_eth_device.link_mode = GMAC_FULL_DUPLEX;
306             }
307             else
308             {
309                 LOG_D("10Mbps");
310                 sam_eth_device.link_mode = GMAC_HALF_DUPLEX;
311             }
312         }
313 
314         if (link_count >= 10)
315         {
316             link_count = 0;
317 
318             /* Restart an auto-negotiation */
319             ethernet_phy_restart_autoneg(sam_eth->phyif);
320         }
321 
322 #ifndef RT_USING_TIMER_SOFT
323         rt_thread_mdelay(1000);
324     }
325 #endif
326 }
327 
328 /**
329  * @brief    Register the GMAC Ethernet device.
330  *
331  * @note
332  *
333  * @param
334  *
335  * @return   RT_OK or -RT_ERROR.
336  */
337 
rt_hw_sam_eth_init(void)338 static int rt_hw_sam_eth_init(void)
339 {
340     rt_err_t   state = RT_EOK;
341 #if CONF_AT24MAC_ADDRESS != 0
342     rt_uint8_t addr  = 0x9A;
343 #endif
344 
345     sam_eth_device.macif = &MACIF;
346     sam_eth_device.phyif = &MACIF_PHY_desc;
347 
348     sam_eth_device.link_speed = GMAC_SPEED_100MBPS;
349     sam_eth_device.link_mode  = GMAC_FULL_DUPLEX;
350 
351 #if CONF_AT24MAC_ADDRESS != 0
352     i2c_m_sync_enable(&I2C_0);
353     i2c_m_sync_set_slaveaddr(&I2C_0, CONF_AT24MAC_ADDRESS, I2C_M_SEVEN);
354     io_write(&(I2C_0.io), &addr, 1);
355     io_read(&(I2C_0.io),  sam_eth_device.mac_addr, 6);
356 #else
357     /* set mac to 0x11 if no EEPROM mounted */
358     memset(sam_eth_device.mac_addr, 0x11, 6);
359 #endif
360 
361     sam_eth_device.parent.parent.init      = rt_sam_eth_init;
362     sam_eth_device.parent.parent.open      = rt_sam_eth_open;
363     sam_eth_device.parent.parent.close     = rt_sam_eth_close;
364     sam_eth_device.parent.parent.read      = rt_sam_eth_read;
365     sam_eth_device.parent.parent.write     = rt_sam_eth_write;
366     sam_eth_device.parent.parent.control   = rt_sam_eth_control;
367     sam_eth_device.parent.parent.user_data = (void *)&sam_eth_device;
368 
369     sam_eth_device.parent.eth_rx = rt_sam_eth_rx;
370     sam_eth_device.parent.eth_tx = rt_sam_eth_tx;
371 
372     rt_sam_gmac_init(&sam_eth_device);
373 
374     /* register eth device */
375     state = eth_device_init(&(sam_eth_device.parent), "e0");
376 
377     if (RT_EOK == state)
378     {
379         LOG_D("gmac device init success");
380     }
381     else
382     {
383         LOG_E("gmac device init faild: %d", state);
384         state = -RT_ERROR;
385         goto outs;
386     }
387 
388     /* start SAM PHY monitor */
389 #ifdef RT_USING_TIMER_SOFT
390     sam_eth_device.phy_monitor_timer = rt_timer_create("phylnk",
391                                                        rt_sam_eth_monitor,
392                                                        (void *)&sam_eth_device,
393                                                        10*RT_TICK_PER_SECOND,
394                                                        RT_TIMER_FLAG_PERIODIC);
395 
396     if (RT_NULL != sam_eth_device.phy_monitor_timer)
397     {
398         rt_timer_start(sam_eth_device.phy_monitor_timer);
399     }
400     else
401     {
402         state = -RT_ERROR;
403         LOG_E("gmac rt_timer_create faild: %d", state);
404     }
405 #else
406     sam_eth_device.phy_monitor_tid = rt_thread_create("phy",
407                                                       rt_sam_eth_monitor,
408                                                       (void *)&sam_eth_device,
409                                                       1024,
410                                                       RT_THREAD_PRIORITY_MAX - 2,
411                                                       2);
412     if (sam_eth_device.phy_monitor_tid != RT_NULL)
413     {
414         rt_thread_startup(sam_eth_device.phy_monitor_tid);
415     }
416     else
417     {
418         state = -RT_ERROR;
419         LOG_E("gmac rt_thread_create faild: %d", state);
420     }
421 #endif
422 
423 outs:
424     return state;
425 }
426 INIT_DEVICE_EXPORT(rt_hw_sam_eth_init);
427 
428 #endif /* BSP_USING_ETH_ARTPI */
429