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