1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
|
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-10-10 Tanek the first version
* 2019-5-10 misonyo add DMA TX and RX function
* 2020-10-14 wangqiang use phy device in phy monitor thread
* 2022-08-29 xjy198903 add 1170 rgmii support
*/
#include <rtthread.h>
#include <rtdevice.h>
#ifdef RT_USING_FINSH
#include <finsh.h>
#endif
#include "fsl_enet.h"
#ifdef RT_USING_LWIP
#include <netif/ethernetif.h>
#include "lwipopts.h"
#define DBG_TAG "drv.eth"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* The PHY ID one register */
#define PHY_ID1_REG 0x02U
/* The PHY ID two register */
#define PHY_ID2_REG 0x03U
/* The PHY auto-negotiate advertise register */
#define PHY_AUTONEG_ADVERTISE_REG 0x04U
/* The PHY basic control register */
#define PHY_BASIC_CONTROL_REG 0x00U
#define PHY_RESET_MASK (1<<15)
#define PHY_AUTO_NEGOTIATION_MASK (1<<12)
#define PHY_LINK (1 << 0)
#define PHY_100M (1 << 1)
#define PHY_FULL_DUPLEX (1 << 2)
/* The PHY basic status register */
#define PHY_BASIC_STATUS_REG 0x01U
#define PHY_LINKED_STATUS_MASK (1<<2)
#define PHY_AUTONEGO_COMPLETE_MASK (1<<5)
/* The PHY status register. */
#define PHY_Status_REG 0x1FU
#define PHY_100M_MASK (1<<3)
#define PHY_FULL_DUPLEX_MASK (1<<4)
#define PHY_Status_SPEED_100M(sr) ((sr) & PHY_100M_MASK)
#define PHY_Status_FULL_DUPLEX(sr) ((sr) & PHY_FULL_DUPLEX_MASK)
//extern phy_lan8741_resource_t g_phy_resource;
#define EXAMPLE_ENET_BASE ENET0
#define EXAMPLE_PHY_ADDRESS 0x00U
#define ENET_EXAMPLE_IRQ ETHERNET_IRQn
#define EXAMPLE_CLOCK_FREQ (50000000U)
#define ENET_RXBD_NUM (4)
#define ENET_TXBD_NUM (4)
#define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN)
#define ENET_BuffSizeAlign(n) ENET_ALIGN(n, ENET_BUFF_ALIGNMENT)
#define ENET_ALIGN(x, align) ((unsigned int)((x) + ((align)-1)) & (unsigned int)(~(unsigned int)((align)-1)))
rt_align(4) enet_rx_bd_struct_t g_rxBuffDescrip[ENET_RXBD_NUM];
rt_align(4) enet_tx_bd_struct_t g_txBuffDescrip[ENET_RXBD_NUM];
static enet_tx_reclaim_info_t g_txDirty[ENET_TXBD_NUM];
#define MAX_ADDR_LEN 6
struct rt_stm32_eth
{
/* inherit from ethernet device */
struct eth_device parent;
#ifndef PHY_USING_INTERRUPT_MODE
rt_timer_t poll_link_timer;
#endif
/* interface address info, hw address */
rt_uint8_t dev_addr[MAX_ADDR_LEN];
enet_handle_t g_handle;
rt_uint32_t rx_channel;
};
static struct rt_stm32_eth stm32_eth_device;
static void ENET_IntCallback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, uint8_t channel, enet_tx_reclaim_info_t *txReclaimInfo, void *param)
{
rt_err_t result;
switch (event)
{
case kENET_TxIntEvent:
/* Get frame info after whole frame transmits out */
// if (txReclaimInfo != NULL)
// {
// rt_free((*txReclaimInfo).context);
// }
break;
case kENET_RxIntEvent:
stm32_eth_device.rx_channel = channel;
result = eth_device_ready(&(stm32_eth_device.parent));
if (result != RT_EOK)
{
LOG_I("_enet_rx_data err = %d", result);
}
default:
break;
}
}
static rt_err_t rt_stm32_eth_init(rt_device_t dev)
{
uint32_t count = 0;
bool link = false;
bool autonego = false;
status_t status;
enet_config_t config;
int i;
uint32_t rxbuffer[ENET_RXBD_NUM];
struct rt_stm32_eth *eth = (struct rt_stm32_eth*)dev->user_data;
for (i = 0; i < ENET_RXBD_NUM; i++)
{
/* This is for rx buffers, static alloc and dynamic alloc both ok. use as your wish. */
rxbuffer[i] = (uint32_t)rt_malloc(ENET_RXBUFF_SIZE);
}
/* prepare the buffer configuration. */
enet_buffer_config_t buffConfig[1] = {{
ENET_RXBD_NUM,
ENET_TXBD_NUM,
&g_txBuffDescrip[0],
&g_txBuffDescrip[0],
&g_txDirty[0],
&g_rxBuffDescrip[0],
&g_rxBuffDescrip[ENET_RXBD_NUM],
&rxbuffer[0],
ENET_BuffSizeAlign(ENET_RXBUFF_SIZE),
}};
CLOCK_AttachClk(kNONE_to_ENETRMII);
CLOCK_EnableClock(kCLOCK_Enet);
ENET_SetSMI(EXAMPLE_ENET_BASE, CLOCK_GetCoreSysClkFreq());
ENET_GetDefaultConfig(&config);
/* Use the actual speed and duplex when phy success to finish the autonegotiation. */
config.miiSpeed = kENET_MiiSpeed100M;
config.miiDuplex = kENET_MiiFullDuplex;
config.interrupt = (kENET_DmaRx) | (kENET_DmaTx);
ENET_Init(EXAMPLE_ENET_BASE, &config, &stm32_eth_device.dev_addr[0], EXAMPLE_CLOCK_FREQ);
ENET_DescriptorInit(EXAMPLE_ENET_BASE, &config, &buffConfig[0]);
ENET_CreateHandler(EXAMPLE_ENET_BASE, ð->g_handle, &config, &buffConfig[0], ENET_IntCallback, NULL);
ENET_StartRxTx(EXAMPLE_ENET_BASE, 1, 1);
return RT_EOK;
}
static rt_err_t rt_stm32_eth_control(rt_device_t dev, int cmd, void *args)
{
switch (cmd)
{
case NIOCTL_GADDR:
/* get mac address */
if (args)
{
SMEMCPY(args, stm32_eth_device.dev_addr, 6);
}
else
{
return -RT_ERROR;
}
break;
default :
break;
}
return RT_EOK;
}
rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf *p)
{
struct pbuf *q;
status_t status;
struct rt_stm32_eth *eth = (struct rt_stm32_eth*)dev->user_data;
// LOG_D("rt_stm32_eth_tx: len: %d, tot_len:%d", p->len, p->tot_len);
enet_buffer_struct_t txBuff[ENET_TXBD_NUM];
enet_tx_frame_struct_t txFrame = {0};
for (q = p; q != NULL; q = q->next)
{
txBuff[txFrame.txBuffNum].buffer = q->payload;
txBuff[txFrame.txBuffNum].length = q->len;
txFrame.txBuffNum++;
}
txFrame.txBuffArray = txBuff;
txFrame.txConfig.intEnable = true;
txFrame.txConfig.tsEnable = true;
txFrame.context = RT_NULL;
status = ENET_SendFrame(EXAMPLE_ENET_BASE, ð->g_handle, &txFrame, 0);
return (status == kStatus_Success);
}
struct pbuf *rt_stm32_eth_rx(rt_device_t dev)
{
struct pbuf *p = RT_NULL;
uint32_t len = 0;
status_t status;
struct rt_stm32_eth *eth = (struct rt_stm32_eth*)dev->user_data;
status = ENET_GetRxFrameSize(EXAMPLE_ENET_BASE, ð->g_handle, &len, eth->rx_channel);
// LOG_D("rt_stm32_eth_rx: status:%d, length: %d, channel:%d", status, len, eth->rx_channel);
if (len != 0 && (status == kStatus_Success))
{
/* Received valid frame. Deliver the rx buffer with the size equal to length. */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL)
{
status = ENET_ReadFrame(EXAMPLE_ENET_BASE, ð->g_handle, p->payload, len, eth->rx_channel, NULL);
if (status == kStatus_Success)
{
return p;
}
else
{
LOG_D(" A frame read failed\n");
pbuf_free(p);
}
}
else
{
LOG_D(" pbuf_alloc faild\n");
}
}
else if (status == kStatus_ENET_RxFrameError)
{
/* update the receive buffer. */
ENET_ReadFrame(EXAMPLE_ENET_BASE, ð->g_handle, NULL, 0, eth->rx_channel, NULL);
}
return RT_NULL;
}
static void phy_monitor_thread_entry(void *parameter)
{
uint8_t phy_addr = 0xFF;
uint8_t detected_count = 0;
while(phy_addr == 0xFF)
{
/* phy search */
rt_uint16_t i, temp;
for (i = 0; i <= 0x1F; i++)
{
// EthHandle.Init.PhyAddress = i;
ENET_MDIORead(EXAMPLE_ENET_BASE, i, PHY_ID1_REG, &temp);
if (temp != 0xFFFF && temp != 0x00)
{
phy_addr = i;
break;
}
}
detected_count++;
rt_thread_mdelay(1000);
if (detected_count > 10)
{
LOG_E("No PHY device was detected, please check hardware!");
}
}
LOG_D("Found a phy, address:0x%02X", phy_addr);
/* RESET PHY */
LOG_D("RESET PHY!");
ENET_MDIOWrite(EXAMPLE_ENET_BASE, phy_addr, PHY_BASIC_CONTROL_REG, PHY_RESET_MASK);
rt_thread_mdelay(2000);
ENET_MDIOWrite(EXAMPLE_ENET_BASE, phy_addr, PHY_BASIC_CONTROL_REG, PHY_AUTO_NEGOTIATION_MASK);
while(1)
{
static rt_uint8_t phy_speed = 0;
uint8_t phy_speed_new = 0;
uint16_t status;
ENET_MDIORead(EXAMPLE_ENET_BASE, phy_addr, PHY_BASIC_STATUS_REG, &status);
LOG_D("phy basic status reg is 0x%X", status);
if (status & (PHY_AUTONEGO_COMPLETE_MASK | PHY_LINKED_STATUS_MASK))
{
uint16_t SR = 0;
phy_speed_new |= PHY_LINK;
ENET_MDIORead(EXAMPLE_ENET_BASE, phy_addr, PHY_Status_REG, &SR);
LOG_D("phy control status reg is 0x%X", SR);
if (PHY_Status_SPEED_100M(SR))
{
phy_speed_new |= PHY_100M;
}
if (PHY_Status_FULL_DUPLEX(SR))
{
phy_speed_new |= PHY_FULL_DUPLEX;
}
}
if (phy_speed != phy_speed_new)
{
phy_speed = phy_speed_new;
if (phy_speed & PHY_LINK)
{
LOG_D("link up");
if (phy_speed & PHY_100M)
{
LOG_D("100Mbps");
}
else
{
LOG_D("10Mbps");
}
if (phy_speed & PHY_FULL_DUPLEX)
{
LOG_D("full-duplex");
}
else
{
LOG_D("half-duplex");
}
/* send link up. */
eth_device_linkchange(&stm32_eth_device.parent, RT_TRUE);
}
else
{
LOG_I("link down");
eth_device_linkchange(&stm32_eth_device.parent, RT_FALSE);
}
}
rt_thread_mdelay(1000);
}
}
static int rt_hw_imxrt_eth_init(void)
{
rt_err_t state = RT_EOK;
stm32_eth_device.dev_addr[0] = 0x00;
stm32_eth_device.dev_addr[1] = 0x80;
stm32_eth_device.dev_addr[2] = 0xE1;
stm32_eth_device.dev_addr[3] = 0x01;
stm32_eth_device.dev_addr[4] = 0x02;
stm32_eth_device.dev_addr[5] = 0x03;
stm32_eth_device.parent.parent.init = rt_stm32_eth_init;
stm32_eth_device.parent.parent.open = RT_NULL;
stm32_eth_device.parent.parent.close = RT_NULL;
stm32_eth_device.parent.parent.read = RT_NULL;
stm32_eth_device.parent.parent.write = RT_NULL;
stm32_eth_device.parent.parent.control = rt_stm32_eth_control;
stm32_eth_device.parent.parent.user_data = &stm32_eth_device;
stm32_eth_device.parent.eth_rx = rt_stm32_eth_rx;
stm32_eth_device.parent.eth_tx = rt_stm32_eth_tx;
/* register eth device */
state = eth_device_init(&(stm32_eth_device.parent), "e0");
if (RT_EOK != state)
{
LOG_E("emac device init faild: %d", state);
state = -RT_ERROR;
}
/* start phy monitor */
rt_thread_startup(rt_thread_create("phy", phy_monitor_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX - 2, 2));
return state;
}
INIT_DEVICE_EXPORT(rt_hw_imxrt_eth_init);
#endif
|