summaryrefslogtreecommitdiff
path: root/bsp/lpc54608-LPCXpresso/drivers/drv_emac.c
blob: 6556d241256474cb576a940328c4c40dd78cf1de (plain)
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
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-08-08     Yang        the first version
 */

#include <rtthread.h>

#include "lwipopts.h"
#include <netif/ethernetif.h>
#include <board.h>

#include "drv_emac.h"

#include "fsl_iocon.h"
#include "fsl_sctimer.h"
#include "fsl_phy.h"

#define DEBUG
//#define ETH_RX_DUMP
//#define ETH_TX_DUMP
#define ETH_STATISTICS

#ifdef DEBUG
#define ETH_PRINTF          rt_kprintf
#else
#define ETH_PRINTF(...)
#endif

#define IOCON_PIO_DIGITAL_EN        0x0100u   /*!< Enables digital function */
#define IOCON_PIO_FUNC0               0x00u   /*!< Selects pin function 0 */
#define IOCON_PIO_FUNC1               0x01u   /*!< Selects pin function 1 */
#define IOCON_PIO_FUNC7               0x07u   /*!< Selects pin function 7 */
#define IOCON_PIO_INPFILT_OFF       0x0200u   /*!< Input filter disabled */
#define IOCON_PIO_INV_DI              0x00u   /*!< Input function is not inverted */
#define IOCON_PIO_MODE_INACT          0x00u   /*!< No addition pin function */
#define IOCON_PIO_MODE_PULLUP         0x20u   /*!< Selects pull-up function */
#define IOCON_PIO_OPENDRAIN_DI        0x00u   /*!< Open drain is disabled */
#define IOCON_PIO_SLEW_FAST         0x0400u   /*!< Fast mode, slew rate control is disabled */
#define IOCON_PIO_SLEW_STANDARD       0x00u   /*!< Standard mode, output slew rate control is enabled */
#define PIN8_IDX                         8u   /*!< Pin number for pin 8 in a port 4 */
#define PIN10_IDX                       10u   /*!< Pin number for pin 10 in a port 4 */
#define PIN11_IDX                       11u   /*!< Pin number for pin 11 in a port 4 */
#define PIN12_IDX                       12u   /*!< Pin number for pin 12 in a port 4 */
#define PIN13_IDX                       13u   /*!< Pin number for pin 13 in a port 4 */
#define PIN14_IDX                       14u   /*!< Pin number for pin 14 in a port 4 */
#define PIN15_IDX                       15u   /*!< Pin number for pin 15 in a port 4 */
#define PIN16_IDX                       16u   /*!< Pin number for pin 16 in a port 4 */
#define PIN17_IDX                       17u   /*!< Pin number for pin 17 in a port 0 */
#define PIN26_IDX                       26u   /*!< Pin number for pin 26 in a port 2 */
#define PIN29_IDX                       29u   /*!< Pin number for pin 29 in a port 0 */
#define PIN30_IDX                       30u   /*!< Pin number for pin 30 in a port 0 */
#define PORT0_IDX                        0u   /*!< Port index */
#define PORT2_IDX                        2u   /*!< Port index */
#define PORT4_IDX                        4u   /*!< Port index */

#define MAX_ADDR_LEN                     6u
#define ENET_RXBD_NUM                    4u
#define ENET_TXBD_NUM                    4u

#define ENET_ALIGN(x) \
    ((unsigned int)((x) + ((ENET_BUFF_ALIGNMENT)-1)) & (unsigned int)(~(unsigned int)((ENET_BUFF_ALIGNMENT)-1)))

#define ENET_RXBUFF_SIZE                (ENET_FRAME_MAX_FRAMELEN)
#define ENET_TXBUFF_SIZE                (ENET_FRAME_MAX_FRAMELEN)

struct lpc_emac
{
    /* inherit from ethernet device */
    struct eth_device parent;
    struct rt_semaphore tx_wait;

    ENET_Type *base;
    enet_handle_t handle;

    /* interface address info. */
    rt_uint8_t  dev_addr[MAX_ADDR_LEN];     /* hw address   */
    uint32_t phyAddr;

    uint8_t RxBuffDescrip[ENET_RXBD_NUM * sizeof(enet_rx_bd_struct_t) + ENET_BUFF_ALIGNMENT];
    uint8_t TxBuffDescrip[ENET_TXBD_NUM * sizeof(enet_tx_bd_struct_t) + ENET_BUFF_ALIGNMENT];
    uint8_t RxDataBuff[ENET_RXBD_NUM * ENET_ALIGN(ENET_RXBUFF_SIZE) + ENET_BUFF_ALIGNMENT];
    uint8_t TxDataBuff[ENET_TXBD_NUM * ENET_ALIGN(ENET_TXBUFF_SIZE) + ENET_BUFF_ALIGNMENT];
    uint8_t txIdx;
};

static struct lpc_emac lpc_emac_device;

#ifdef ETH_STATISTICS
static uint32_t isr_rx_counter = 0;
static uint32_t isr_tx_counter = 0;
#endif

static inline enet_rx_bd_struct_t *get_rx_desc(uint32_t index)
{
    return (enet_rx_bd_struct_t *)ENET_ALIGN(&lpc_emac_device.RxBuffDescrip[index * sizeof(enet_rx_bd_struct_t)]);
}

static inline enet_tx_bd_struct_t *get_tx_desc(uint32_t index)
{
    return (enet_tx_bd_struct_t *)ENET_ALIGN(&lpc_emac_device.TxBuffDescrip[index * sizeof(enet_tx_bd_struct_t)]);
}

#if defined(ETH_RX_DUMP) ||  defined(ETH_TX_DUMP)
static void packet_dump(const char * msg, const struct pbuf* p)
{
    const struct pbuf* q;
    rt_uint32_t i,j;
    rt_uint8_t *ptr;

    rt_kprintf("%s %d byte\n", msg, p->tot_len);

    i=0;
    for(q=p; q != RT_NULL; q= q->next)
    {
        ptr = q->payload;

        for(j=0; j<q->len; j++)
        {
            if( (i%8) == 0 )
            {
                rt_kprintf("  ");
            }
            if( (i%16) == 0 )
            {
                rt_kprintf("\r\n");
            }
            rt_kprintf("%02x ",*ptr);

            i++;
            ptr++;
        }
    }

    rt_kprintf("\n\n");
}
#else
#define packet_dump(...)
#endif /* dump */

static void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, uint8_t channel, void *param)
{
    switch (event)
    {
        case kENET_RxIntEvent:
#ifdef ETH_STATISTICS
            isr_rx_counter++;
#endif
            /* a frame has been received */
            eth_device_ready(&(lpc_emac_device.parent));
            break;
        case kENET_TxIntEvent:
#ifdef ETH_STATISTICS
            isr_tx_counter++;
#endif
            /* set event */
            rt_sem_release(&lpc_emac_device.tx_wait);
            break;
        default:
            break;
    }
}

static void lcp_emac_io_init(void)
{
    const uint32_t port0_pin17_config = (
                                            IOCON_PIO_FUNC7 |                                        /* Pin is configured as ENET_TXD1 */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT0_IDX, PIN17_IDX, port0_pin17_config); /* PORT0 PIN17 (coords: E14) is configured as ENET_TXD1 */
    const uint32_t port2_pin26_config = (
                                            IOCON_PIO_FUNC0 |                                        /* Pin is configured as PIO2_26 */
                                            IOCON_PIO_MODE_PULLUP |                                  /* Selects pull-up function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN26_IDX, port2_pin26_config); /* PORT2 PIN26 (coords: H11) is configured as PIO2_26 */
    const uint32_t port4_pin10_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_RX_DV */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN10_IDX, port4_pin10_config); /* PORT4 PIN10 (coords: B9) is configured as ENET_RX_DV */
    const uint32_t port4_pin11_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_RXD0 */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN11_IDX, port4_pin11_config); /* PORT4 PIN11 (coords: A9) is configured as ENET_RXD0 */
    const uint32_t port4_pin12_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_RXD1 */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN12_IDX, port4_pin12_config); /* PORT4 PIN12 (coords: A6) is configured as ENET_RXD1 */
    const uint32_t port4_pin13_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_TX_EN */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN13_IDX, port4_pin13_config); /* PORT4 PIN13 (coords: B6) is configured as ENET_TX_EN */
    const uint32_t port4_pin14_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_RX_CLK */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN14_IDX, port4_pin14_config); /* PORT4 PIN14 (coords: B5) is configured as ENET_RX_CLK */
    const uint32_t port4_pin15_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_MDC */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN15_IDX, port4_pin15_config); /* PORT4 PIN15 (coords: A4) is configured as ENET_MDC */
    const uint32_t port4_pin16_config = (
                                            IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_MDIO */
                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                        );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN16_IDX, port4_pin16_config); /* PORT4 PIN16 (coords: C4) is configured as ENET_MDIO */
    const uint32_t port4_pin8_config = (
                                           IOCON_PIO_FUNC1 |                                        /* Pin is configured as ENET_TXD0 */
                                           IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
                                           IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
                                           IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
                                           IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
                                           IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
                                           IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
                                       );
    IOCON_PinMuxSet(IOCON, PORT4_IDX, PIN8_IDX, port4_pin8_config); /* PORT4 PIN8 (coords: B14) is configured as ENET_TXD0 */
}

static rt_err_t lpc_emac_phy_init(phy_speed_t * speed, phy_duplex_t * duplex)
{
    bool link = false;
    int32_t status;

    RT_ASSERT(speed != NULL);
    RT_ASSERT(duplex != NULL);

    status = PHY_Init(lpc_emac_device.base, lpc_emac_device.phyAddr, 0);
    if (status != kStatus_Success)
    {
        /* Half duplex. */
        *duplex = kPHY_HalfDuplex;
        /* 10M speed. */
        *speed = kPHY_Speed10M;

        eth_device_linkchange(&lpc_emac_device.parent, RT_FALSE);

        ETH_PRINTF("PHY_Init failed!\n");
        return RT_ERROR;
    }

    /* Wait for link up and get the actual PHY link speed. */
    PHY_GetLinkStatus(lpc_emac_device.base, lpc_emac_device.phyAddr, &link);
    while (!link)
    {
        uint32_t timedelay;
        ETH_PRINTF("PHY Wait for link up!\n");
        for (timedelay = 0; timedelay < 0xFFFFFU; timedelay++)
        {
            __ASM("nop");
        }
        PHY_GetLinkStatus(lpc_emac_device.base, lpc_emac_device.phyAddr, &link);
    }

    PHY_GetLinkSpeedDuplex(lpc_emac_device.base, lpc_emac_device.phyAddr, speed, duplex);

    eth_device_linkchange(&lpc_emac_device.parent, RT_TRUE);

    return RT_EOK;
}

static rt_err_t lpc_emac_init(rt_device_t dev)
{
    int i;
    phy_speed_t speed;
    phy_duplex_t duplex;
    enet_config_t config;
    enet_buffer_config_t buffCfg;
    uint32_t rxBufferStartAddr[ENET_RXBD_NUM];

    lcp_emac_io_init();

    lpc_emac_phy_init(&speed, &duplex);

    /* calculate start addresses of all rx buffers */
    for (i = 0; i < ENET_RXBD_NUM; i++)
    {
        rxBufferStartAddr[i] = ENET_ALIGN(&lpc_emac_device.RxDataBuff[i * ENET_ALIGN(ENET_RXBUFF_SIZE)]);
    }

    buffCfg.rxRingLen = ENET_RXBD_NUM;
    buffCfg.txRingLen = ENET_TXBD_NUM;
    buffCfg.txDescStartAddrAlign = get_tx_desc(0U);
    buffCfg.txDescTailAddrAlign  = get_tx_desc(0U);
    buffCfg.rxDescStartAddrAlign = get_rx_desc(0U);
    buffCfg.rxDescTailAddrAlign  = get_rx_desc(ENET_RXBD_NUM);
    buffCfg.rxBufferStartAddr    = rxBufferStartAddr;
    buffCfg.rxBuffSizeAlign      = ENET_ALIGN(ENET_RXBUFF_SIZE);

    /* Get default configuration 100M RMII. */
    ENET_GetDefaultConfig(&config);
    /* Use the actual speed and duplex when phy success to finish the autonegotiation. */
    config.miiSpeed = (enet_mii_speed_t)speed;
    config.miiDuplex = (enet_mii_duplex_t)duplex;

    ETH_PRINTF("Auto negotiation, Speed: ");
    if (config.miiSpeed == kENET_MiiSpeed100M)
        ETH_PRINTF("100M");
    else
        ETH_PRINTF("10M");

    ETH_PRINTF(", Duplex: ");
    if (config.miiSpeed == kENET_MiiSpeed100M)
        ETH_PRINTF("Full\n");
    else
        ETH_PRINTF("Half\n");

    /* Initialize lpc_emac_device.base. */
    ENET_Init(lpc_emac_device.base, &config, &lpc_emac_device.dev_addr[0], CLOCK_GetFreq(kCLOCK_CoreSysClk));

    /* Enable the tx/rx interrupt. */
    ENET_EnableInterrupts(lpc_emac_device.base, (kENET_DmaTx | kENET_DmaRx));
    ENET_CreateHandler(lpc_emac_device.base, &lpc_emac_device.handle, &config, &buffCfg, ethernet_callback, NULL);

    /* Initialize Descriptor. */
    ENET_DescriptorInit(lpc_emac_device.base, &config, &buffCfg);

    /* Active TX/RX. */
    ENET_StartRxTx(lpc_emac_device.base, 1, 1);

    return RT_EOK;
}

static rt_err_t lpc_emac_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t lpc_emac_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_ssize_t lpc_emac_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_ssize_t lpc_emac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_err_t lpc_emac_control(rt_device_t dev, int cmd, void *args)
{
    switch (cmd)
    {
    case NIOCTL_GADDR:
        /* get mac address */
        if (args) rt_memcpy(args, lpc_emac_device.dev_addr, 6);
        else return -RT_ERROR;
        break;

    default :
        break;
    }

    return RT_EOK;
}

/* EtherNet Device Interface */
/* transmit packet. */
rt_err_t lpc_emac_tx(rt_device_t dev, struct pbuf *p)
{
    rt_err_t result = RT_EOK;
    enet_handle_t * enet_handle = &lpc_emac_device.handle;
    ENET_Type *enet_base = lpc_emac_device.base;
    uint8_t * data;

    uint16_t    len;

    RT_ASSERT(p != NULL);
    RT_ASSERT(enet_handle != RT_NULL);

    if (p->tot_len > ENET_TXBUFF_SIZE)
    {
        return RT_ERROR;
    }

    packet_dump("TX dump", p);

    /* get free tx buffer */
    {
        rt_err_t result;
        result = rt_sem_take(&lpc_emac_device.tx_wait, RT_TICK_PER_SECOND/10);
        if (result != RT_EOK)
        {
            return RT_ERROR;
        }
    }

    //  fix RxDataBuff -> TxDataBuff, ENET_RXBUFF_SIZE -> ENET_TXBUFF_SIZE
    data = (uint8_t *)ENET_ALIGN(&lpc_emac_device.TxDataBuff[lpc_emac_device.txIdx * ENET_ALIGN(ENET_TXBUFF_SIZE)]);
    len = pbuf_copy_partial(p, data, p->tot_len, 0);
    lpc_emac_device.txIdx = (lpc_emac_device.txIdx + 1) / ENET_TXBD_NUM;

    // fix 'p->len' to 'len', avoid send wrong partial packet.
    result = ENET_SendFrame(enet_base, enet_handle, data, len);

    if ((result == kStatus_ENET_TxFrameFail) || (result == kStatus_ENET_TxFrameOverLen) || (result == kStatus_ENET_TxFrameBusy))
    {
        return RT_ERROR;
    }

    return RT_EOK;
}

/* reception packet. */
struct pbuf *lpc_emac_rx(rt_device_t dev)
{
    uint32_t length = 0;
    status_t status;

    struct pbuf* p = RT_NULL;
    enet_handle_t * enet_handle = &lpc_emac_device.handle;
    ENET_Type *enet_base = lpc_emac_device.base;

    /* Get the Frame size */
    status = ENET_GetRxFrameSize(enet_base, enet_handle, &length, 0);

    /* Call ENET_ReadFrame when there is a received frame. */
    if (length != 0)
    {
        /* Received valid frame. Deliver the rx buffer with the size equal to length. */
        p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);

        if (p != NULL)
        {
            status = ENET_ReadFrame(enet_base, enet_handle, p->payload, length, 0);
            if (status == kStatus_Success)
            {
                packet_dump("RX dump", p);
                return p;
            }
            else
            {
                ETH_PRINTF(" A frame read failed\n");
                pbuf_free(p);
            }
        }
        else
        {
            ETH_PRINTF(" pbuf_alloc faild\n");
        }
    }
    else if (status == kStatus_ENET_RxFrameError)
    {
        ETH_PRINTF("ENET_GetRxFrameSize: kStatus_ENET_RxFrameError\n");
        ENET_ReadFrame(enet_base, enet_handle, NULL, 0, 0);
    }

    return NULL;
}

int lpc_emac_hw_init(void)
{
    /* init tx semaphore */
    rt_sem_init(&lpc_emac_device.tx_wait, "tx_wait", ENET_TXBD_NUM, RT_IPC_FLAG_FIFO);

    lpc_emac_device.phyAddr = 0;
    lpc_emac_device.txIdx = 0;
    lpc_emac_device.base = ENET;

    // OUI 00-60-37 NXP Semiconductors
    lpc_emac_device.dev_addr[0] = 0x00;
    lpc_emac_device.dev_addr[1] = 0x60;
    lpc_emac_device.dev_addr[2] = 0x37;
    /* set mac address: (only for test) */
    lpc_emac_device.dev_addr[3] = 0x12;
    lpc_emac_device.dev_addr[4] = 0x34;
    lpc_emac_device.dev_addr[5] = 0x56;

    lpc_emac_device.parent.parent.init      = lpc_emac_init;
    lpc_emac_device.parent.parent.open      = lpc_emac_open;
    lpc_emac_device.parent.parent.close     = lpc_emac_close;
    lpc_emac_device.parent.parent.read      = lpc_emac_read;
    lpc_emac_device.parent.parent.write     = lpc_emac_write;
    lpc_emac_device.parent.parent.control   = lpc_emac_control;
    lpc_emac_device.parent.parent.user_data = RT_NULL;

    lpc_emac_device.parent.eth_rx           = lpc_emac_rx;
    lpc_emac_device.parent.eth_tx           = lpc_emac_tx;

    eth_device_init(&(lpc_emac_device.parent), "e0");

    return 0;
}
INIT_DEVICE_EXPORT(lpc_emac_hw_init);

#ifdef ETH_STATISTICS
int emac_stat(void)
{
    rt_kprintf("enter rx isr coutner : %d\n", isr_rx_counter);
    rt_kprintf("enter tx isr coutner : %d\n", isr_tx_counter);

    return 0;
}
#endif

void phy_dump(void)
{
    status_t PHY_Read(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t *dataPtr);

    int i;

    for (i = 0; i < 31; i++)
    {
        status_t result = kStatus_Success;
        uint32_t reg;

        result = PHY_Read(lpc_emac_device.base, lpc_emac_device.phyAddr, i, &reg);

        if (result == kStatus_Success)
        {
            rt_kprintf("%02d: %08d\n", i, reg);
        }
        else
        {
            rt_kprintf("read register %d faild\n", i);
        }
    }
}

void emac_dump(void)
{
    #define DUMP_REG(__NAME)    \
        rt_kprintf("%-40s, %08x: %08x\n", #__NAME, (uint32_t)&(lpc_emac_device.base->__NAME), lpc_emac_device.base->__NAME)

    DUMP_REG(MAC_CONFIG);
    DUMP_REG(MAC_EXT_CONFIG);
    DUMP_REG(MAC_FRAME_FILTER);
    DUMP_REG(MAC_WD_TIMEROUT);
    DUMP_REG(MAC_VLAN_TAG);
    DUMP_REG(MAC_TX_FLOW_CTRL_Q[0]);
    DUMP_REG(MAC_TX_FLOW_CTRL_Q[1]);
    DUMP_REG(MAC_RX_FLOW_CTRL);
    DUMP_REG(MAC_TXQ_PRIO_MAP);
    DUMP_REG(MAC_RXQ_CTRL[0]);
    DUMP_REG(MAC_RXQ_CTRL[1]);
    DUMP_REG(MAC_RXQ_CTRL[2]);
    DUMP_REG(MAC_INTR_STAT);
    DUMP_REG(MAC_INTR_EN);
    DUMP_REG(MAC_RXTX_STAT);
    DUMP_REG(MAC_PMT_CRTL_STAT);
    DUMP_REG(MAC_RWAKE_FRFLT);
    DUMP_REG(MAC_LPI_CTRL_STAT);
    DUMP_REG(MAC_LPI_TIMER_CTRL);
    DUMP_REG(MAC_LPI_ENTR_TIMR);
    DUMP_REG(MAC_1US_TIC_COUNTR);
    DUMP_REG(MAC_VERSION);
    DUMP_REG(MAC_DBG);
    DUMP_REG(MAC_HW_FEAT[0]);
    DUMP_REG(MAC_HW_FEAT[1]);
    DUMP_REG(MAC_HW_FEAT[2]);
    DUMP_REG(MAC_MDIO_ADDR);
    DUMP_REG(MAC_MDIO_DATA);
    DUMP_REG(MAC_ADDR_HIGH);
    DUMP_REG(MAC_ADDR_LOW);
    DUMP_REG(MAC_TIMESTAMP_CTRL);
    DUMP_REG(MAC_SUB_SCND_INCR);
    DUMP_REG(MAC_SYS_TIME_SCND);
    DUMP_REG(MAC_SYS_TIME_NSCND);
    DUMP_REG(MAC_SYS_TIME_SCND_UPD);
    DUMP_REG(MAC_SYS_TIME_NSCND_UPD);
    DUMP_REG(MAC_SYS_TIMESTMP_ADDEND);
    DUMP_REG(MAC_SYS_TIME_HWORD_SCND);
    DUMP_REG(MAC_SYS_TIMESTMP_STAT);
    DUMP_REG(MAC_TX_TIMESTAMP_STATUS_NANOSECONDS);
    DUMP_REG(MAC_TX_TIMESTAMP_STATUS_SECONDS);
    DUMP_REG(MAC_TIMESTAMP_INGRESS_CORR_NANOSECOND);
    DUMP_REG(MAC_TIMESTAMP_EGRESS_CORR_NANOSECOND);
    DUMP_REG(MTL_OP_MODE);
    DUMP_REG(MTL_INTR_STAT);
    DUMP_REG(MTL_RXQ_DMA_MAP);
    DUMP_REG(DMA_MODE);
    DUMP_REG(DMA_SYSBUS_MODE);
    DUMP_REG(DMA_INTR_STAT);
    DUMP_REG(DMA_DBG_STAT);

    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_OP_MODE);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_UNDRFLW);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_DBG);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_ETS_CTRL);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_ETS_STAT);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_QNTM_WGHT);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_SNDSLP_CRDT);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_HI_CRDT);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_LO_CRDT);
    DUMP_REG(MTL_QUEUE[0].MTL_TXQX_INTCTRL_STAT);
    DUMP_REG(MTL_QUEUE[0].MTL_RXQX_OP_MODE);
    DUMP_REG(MTL_QUEUE[0].MTL_RXQX_MISSPKT_OVRFLW_CNT);
    DUMP_REG(MTL_QUEUE[0].MTL_RXQX_DBG);
    DUMP_REG(MTL_QUEUE[0].MTL_RXQX_CTRL);

    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_OP_MODE);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_UNDRFLW);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_DBG);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_ETS_CTRL);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_ETS_STAT);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_QNTM_WGHT);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_SNDSLP_CRDT);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_HI_CRDT);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_LO_CRDT);
    DUMP_REG(MTL_QUEUE[1].MTL_TXQX_INTCTRL_STAT);
    DUMP_REG(MTL_QUEUE[1].MTL_RXQX_OP_MODE);
    DUMP_REG(MTL_QUEUE[1].MTL_RXQX_MISSPKT_OVRFLW_CNT);
    DUMP_REG(MTL_QUEUE[1].MTL_RXQX_DBG);
    DUMP_REG(MTL_QUEUE[1].MTL_RXQX_CTRL);

    DUMP_REG(DMA_CH[0].DMA_CHX_CTRL);
    DUMP_REG(DMA_CH[0].DMA_CHX_TX_CTRL);
    DUMP_REG(DMA_CH[0].DMA_CHX_RX_CTRL);
    DUMP_REG(DMA_CH[0].DMA_CHX_TXDESC_LIST_ADDR);
    DUMP_REG(DMA_CH[0].DMA_CHX_RXDESC_LIST_ADDR);
    DUMP_REG(DMA_CH[0].DMA_CHX_TXDESC_TAIL_PTR);
    DUMP_REG(DMA_CH[0].DMA_CHX_RXDESC_TAIL_PTR);
    DUMP_REG(DMA_CH[0].DMA_CHX_TXDESC_RING_LENGTH);
    DUMP_REG(DMA_CH[0].DMA_CHX_RXDESC_RING_LENGTH);
    DUMP_REG(DMA_CH[0].DMA_CHX_INT_EN);
    DUMP_REG(DMA_CH[0].DMA_CHX_RX_INT_WDTIMER);
    DUMP_REG(DMA_CH[0].DMA_CHX_SLOT_FUNC_CTRL_STAT);
    DUMP_REG(DMA_CH[0].DMA_CHX_CUR_HST_TXDESC);
    DUMP_REG(DMA_CH[0].DMA_CHX_CUR_HST_RXDESC);
    DUMP_REG(DMA_CH[0].DMA_CHX_CUR_HST_TXBUF);
    DUMP_REG(DMA_CH[0].DMA_CHX_CUR_HST_RXBUF);
    DUMP_REG(DMA_CH[0].DMA_CHX_STAT);

    DUMP_REG(DMA_CH[1].DMA_CHX_CTRL);
    DUMP_REG(DMA_CH[1].DMA_CHX_TX_CTRL);
    DUMP_REG(DMA_CH[1].DMA_CHX_RX_CTRL);
    DUMP_REG(DMA_CH[1].DMA_CHX_TXDESC_LIST_ADDR);
    DUMP_REG(DMA_CH[1].DMA_CHX_RXDESC_LIST_ADDR);
    DUMP_REG(DMA_CH[1].DMA_CHX_TXDESC_TAIL_PTR);
    DUMP_REG(DMA_CH[1].DMA_CHX_RXDESC_TAIL_PTR);
    DUMP_REG(DMA_CH[1].DMA_CHX_TXDESC_RING_LENGTH);
    DUMP_REG(DMA_CH[1].DMA_CHX_RXDESC_RING_LENGTH);
    DUMP_REG(DMA_CH[1].DMA_CHX_INT_EN);
    DUMP_REG(DMA_CH[1].DMA_CHX_RX_INT_WDTIMER);
    DUMP_REG(DMA_CH[1].DMA_CHX_SLOT_FUNC_CTRL_STAT);
    DUMP_REG(DMA_CH[1].DMA_CHX_CUR_HST_TXDESC);
    DUMP_REG(DMA_CH[1].DMA_CHX_CUR_HST_RXDESC);
    DUMP_REG(DMA_CH[1].DMA_CHX_CUR_HST_TXBUF);
    DUMP_REG(DMA_CH[1].DMA_CHX_CUR_HST_RXBUF);
    DUMP_REG(DMA_CH[1].DMA_CHX_STAT);
}

void emac_bd_dump(void)
{
    int i;

    rt_kprintf("rx bd dump: \n");
    for (i = 0; i < ENET_RXBD_NUM; i++)
    {
        enet_rx_bd_struct_t * rx_bd = get_rx_desc(i);
        rt_kprintf("buf1: %p, buf2: %p, ctrl: %08x\n",
                        rx_bd->buff1Addr,
                        rx_bd->buff2Addr,
                        rx_bd->control);
    }

    rt_kprintf("tx bd dump: \n");
    for (i = 0; i < ENET_TXBD_NUM; i++)
    {
        enet_tx_bd_struct_t * tx_bd = get_tx_desc(i);
        rt_kprintf("buf1: %p, buf2: %p, len: %08x, ctrl: %08x\n",
                        tx_bd->buff1Addr,
                        tx_bd->buff2Addr,
                        tx_bd->buffLen,
                        tx_bd->controlStat);
    }
}

#ifdef RT_USING_FINSH
#include <finsh.h>
FINSH_FUNCTION_EXPORT(emac_stat, dump emac stat data);
FINSH_FUNCTION_EXPORT(phy_dump, dump phy registers);
FINSH_FUNCTION_EXPORT(emac_dump, dump emac registers);
FINSH_FUNCTION_EXPORT(emac_bd_dump, dump emac tx and rx descriptor);
#endif