1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2013-05-19 Bernard porting from LPC17xx drivers.
9 */
10
11 #include <rtthread.h>
12 #include "lwipopts.h"
13 #include <netif/ethernetif.h>
14
15 #include "lpc_iap.h"
16 #include "drv_emac.h"
17
18 #define EMAC_PHY_AUTO 0
19 #define EMAC_PHY_10MBIT 1
20 #define EMAC_PHY_100MBIT 2
21
22 #define MAX_ADDR_LEN 6
23 static rt_uint32_t ETH_RAM_BASE[4 * 1024] rt_section("ETH_RAM");
24
25 /* EMAC variables located in 16K Ethernet SRAM */
26 #define RX_DESC_BASE (uint32_t)Ð_RAM_BASE[0]
27 #define RX_STAT_BASE (RX_DESC_BASE + NUM_RX_FRAG*8)
28 #define TX_DESC_BASE (RX_STAT_BASE + NUM_RX_FRAG*8)
29 #define TX_STAT_BASE (TX_DESC_BASE + NUM_TX_FRAG*8)
30 #define RX_BUF_BASE (TX_STAT_BASE + NUM_TX_FRAG*4)
31 #define TX_BUF_BASE (RX_BUF_BASE + NUM_RX_FRAG*ETH_FRAG_SIZE)
32
33 /* RX and TX descriptor and status definitions. */
34 #define RX_DESC_PACKET(i) (*(unsigned int *)(RX_DESC_BASE + 8*i))
35 #define RX_DESC_CTRL(i) (*(unsigned int *)(RX_DESC_BASE+4 + 8*i))
36 #define RX_STAT_INFO(i) (*(unsigned int *)(RX_STAT_BASE + 8*i))
37 #define RX_STAT_HASHCRC(i) (*(unsigned int *)(RX_STAT_BASE+4 + 8*i))
38 #define TX_DESC_PACKET(i) (*(unsigned int *)(TX_DESC_BASE + 8*i))
39 #define TX_DESC_CTRL(i) (*(unsigned int *)(TX_DESC_BASE+4 + 8*i))
40 #define TX_STAT_INFO(i) (*(unsigned int *)(TX_STAT_BASE + 4*i))
41 #define RX_BUF(i) (RX_BUF_BASE + ETH_FRAG_SIZE*i)
42 #define TX_BUF(i) (TX_BUF_BASE + ETH_FRAG_SIZE*i)
43
44 struct lpc_emac
45 {
46 /* inherit from ethernet device */
47 struct eth_device parent;
48
49 rt_uint8_t phy_mode;
50
51 /* interface address info. */
52 rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */
53 };
54 static struct lpc_emac lpc_emac_device;
55 static struct rt_semaphore sem_lock;
56 static struct rt_event tx_event;
57
58 /* Local Function Prototypes */
59 static void write_PHY(rt_uint32_t PhyReg, rt_uint32_t Value);
60 static rt_uint16_t read_PHY(rt_uint8_t PhyReg) ;
61
ENET_IRQHandler(void)62 void ENET_IRQHandler(void)
63 {
64 rt_uint32_t status;
65
66 /* enter interrupt */
67 rt_interrupt_enter();
68
69 status = LPC_EMAC->IntStatus;
70
71 if (status & INT_RX_DONE)
72 {
73 /* Disable EMAC RxDone interrupts. */
74 LPC_EMAC->IntEnable = INT_TX_DONE;
75
76 /* a frame has been received */
77 eth_device_ready(&(lpc_emac_device.parent));
78 }
79 else if (status & INT_TX_DONE)
80 {
81 /* set event */
82 rt_event_send(&tx_event, 0x01);
83 }
84
85 if (status & INT_RX_OVERRUN)
86 {
87 rt_kprintf("rx overrun\n");
88 }
89
90 if (status & INT_TX_UNDERRUN)
91 {
92 rt_kprintf("tx underrun\n");
93 }
94
95 /* Clear the interrupt. */
96 LPC_EMAC->IntClear = status;
97
98 /* leave interrupt */
99 rt_interrupt_leave();
100 }
101
102 /* phy write */
write_PHY(rt_uint32_t PhyReg,rt_uint32_t Value)103 static void write_PHY(rt_uint32_t PhyReg, rt_uint32_t Value)
104 {
105 unsigned int tout;
106
107 LPC_EMAC->MADR = DP83848C_DEF_ADR | PhyReg;
108 LPC_EMAC->MWTD = Value;
109
110 /* Wait utill operation completed */
111 tout = 0;
112 for (tout = 0; tout < MII_WR_TOUT; tout++)
113 {
114 if ((LPC_EMAC->MIND & MIND_BUSY) == 0)
115 {
116 break;
117 }
118 }
119 }
120
121 /* phy read */
read_PHY(rt_uint8_t PhyReg)122 static rt_uint16_t read_PHY(rt_uint8_t PhyReg)
123 {
124 rt_uint32_t tout;
125
126 LPC_EMAC->MADR = DP83848C_DEF_ADR | PhyReg;
127 LPC_EMAC->MCMD = MCMD_READ;
128
129 /* Wait until operation completed */
130 tout = 0;
131 for (tout = 0; tout < MII_RD_TOUT; tout++)
132 {
133 if ((LPC_EMAC->MIND & MIND_BUSY) == 0)
134 {
135 break;
136 }
137 }
138 LPC_EMAC->MCMD = 0;
139 return (LPC_EMAC->MRDD);
140 }
141
142 /* init rx descriptor */
rx_descr_init(void)143 rt_inline void rx_descr_init(void)
144 {
145 rt_uint32_t i;
146
147 for (i = 0; i < NUM_RX_FRAG; i++)
148 {
149 RX_DESC_PACKET(i) = RX_BUF(i);
150 RX_DESC_CTRL(i) = RCTRL_INT | (ETH_FRAG_SIZE - 1);
151 RX_STAT_INFO(i) = 0;
152 RX_STAT_HASHCRC(i) = 0;
153 }
154
155 /* Set EMAC Receive Descriptor Registers. */
156 LPC_EMAC->RxDescriptor = RX_DESC_BASE;
157 LPC_EMAC->RxStatus = RX_STAT_BASE;
158 LPC_EMAC->RxDescriptorNumber = NUM_RX_FRAG - 1;
159
160 /* Rx Descriptors Point to 0 */
161 LPC_EMAC->RxConsumeIndex = 0;
162 }
163
164 /* init tx descriptor */
tx_descr_init(void)165 rt_inline void tx_descr_init(void)
166 {
167 rt_uint32_t i;
168
169 for (i = 0; i < NUM_TX_FRAG; i++)
170 {
171 TX_DESC_PACKET(i) = TX_BUF(i);
172 TX_DESC_CTRL(i) = (1ul << 31) | (1ul << 30) | (1ul << 29) | (1ul << 28) | (1ul << 26) | (ETH_FRAG_SIZE - 1);
173 TX_STAT_INFO(i) = 0;
174 }
175
176 /* Set EMAC Transmit Descriptor Registers. */
177 LPC_EMAC->TxDescriptor = TX_DESC_BASE;
178 LPC_EMAC->TxStatus = TX_STAT_BASE;
179 LPC_EMAC->TxDescriptorNumber = NUM_TX_FRAG - 1;
180
181 /* Tx Descriptors Point to 0 */
182 LPC_EMAC->TxProduceIndex = 0;
183 }
184
185 /*
186 TX_EN P1_4
187 TXD0 P1_0
188 TXD1 P1_1
189
190 RXD0 P1_9
191 RXD1 P1_10
192 RX_ER P1_14
193 CRS_DV P1_8
194
195 MDC P1_16
196 MDIO P1_17
197
198 REF_CLK P1_15
199 */
lpc_emac_init(rt_device_t dev)200 static rt_err_t lpc_emac_init(rt_device_t dev)
201 {
202 /* Initialize the EMAC ethernet controller. */
203 rt_uint32_t regv, tout;
204
205 /* Power Up the EMAC controller. */
206 LPC_SC->PCONP |= (1UL << 30);
207
208 /* Enable P1 Ethernet Pins. */
209
210 /**< P1_0 ENET_TXD0 */
211 LPC_IOCON->P1_0 &= ~(0x07);
212 LPC_IOCON->P1_0 |= 0x01;
213 /**< P1_1 ENET_TXD1 */
214 LPC_IOCON->P1_1 &= ~(0x07);
215 LPC_IOCON->P1_1 |= 0x01;
216 /**< P1_4 ENET_TX_EN */
217 LPC_IOCON->P1_4 &= ~(0x07);
218 LPC_IOCON->P1_4 |= 0x01;
219 /**< P1_8 ENET_CRS_DV */
220 LPC_IOCON->P1_8 &= ~(0x07);
221 LPC_IOCON->P1_8 |= 0x01;
222 /**< P1_9 ENET_RXD0 */
223 LPC_IOCON->P1_9 &= ~(0x07);
224 LPC_IOCON->P1_9 |= 0x01;
225 /**< P1_10 ENET_RXD1 */
226 LPC_IOCON->P1_10 &= ~(0x07);
227 LPC_IOCON->P1_10 |= 0x01;
228 /**< P1_14 ENET_RX_ER */
229 LPC_IOCON->P1_14 &= ~(0x07);
230 LPC_IOCON->P1_14 |= 0x01;
231 /**< P1_15 ENET_REF_CLK */
232 LPC_IOCON->P1_15 &= ~(0x07);
233 LPC_IOCON->P1_15 |= 0x01;
234 /**< P1_16 ENET_MDC */
235 LPC_IOCON->P1_16 &= ~(0x07);
236 LPC_IOCON->P1_16 |= 0x01;
237 /**< P1_17 ENET_MDIO */
238 LPC_IOCON->P1_17 &= ~(0x07);
239 LPC_IOCON->P1_17 |= 0x01;
240
241 /* Reset all EMAC internal modules. */
242 LPC_EMAC->MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX |
243 MAC1_SIM_RES | MAC1_SOFT_RES;
244 LPC_EMAC->Command = CR_REG_RES | CR_TX_RES | CR_RX_RES;
245
246 /* A short delay after reset. */
247 for (tout = 100; tout; tout--);
248
249 /* Initialize MAC control registers. */
250 LPC_EMAC->MAC1 = MAC1_PASS_ALL;
251 LPC_EMAC->MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
252 LPC_EMAC->MAXF = ETH_MAX_FLEN;
253 LPC_EMAC->CLRT = CLRT_DEF;
254 LPC_EMAC->IPGR = IPGR_DEF;
255
256 /* PCLK=18MHz, clock select=6, MDC=18/6=3MHz */
257 /* Enable Reduced MII interface. */
258 LPC_EMAC->MCFG = MCFG_CLK_DIV20 | MCFG_RES_MII;
259 for (tout = 100; tout; tout--);
260 LPC_EMAC->MCFG = MCFG_CLK_DIV20;
261
262 /* Enable Reduced MII interface. */
263 LPC_EMAC->Command = CR_RMII | CR_PASS_RUNT_FRM | CR_PASS_RX_FILT;
264
265 /* Reset Reduced MII Logic. */
266 LPC_EMAC->SUPP = SUPP_RES_RMII | SUPP_SPEED;
267 for (tout = 100; tout; tout--);
268 LPC_EMAC->SUPP = SUPP_SPEED;
269
270 /* Put the PHY in reset mode */
271 write_PHY(PHY_REG_BMCR, 0x8000);
272 for (tout = 1000; tout; tout--);
273
274 /* Configure the PHY device */
275 /* Configure the PHY device */
276 switch (lpc_emac_device.phy_mode)
277 {
278 case EMAC_PHY_AUTO:
279 /* Use autonegotiation about the link speed. */
280 write_PHY(PHY_REG_BMCR, PHY_AUTO_NEG);
281 break;
282 case EMAC_PHY_10MBIT:
283 /* Connect at 10MBit */
284 write_PHY(PHY_REG_BMCR, PHY_FULLD_10M);
285 break;
286 case EMAC_PHY_100MBIT:
287 /* Connect at 100MBit */
288 write_PHY(PHY_REG_BMCR, PHY_FULLD_100M);
289 break;
290 }
291 if (tout >= 0x100000) return -RT_ERROR; // auto_neg failed
292
293 regv = 0x0004;
294 /* Configure Full/Half Duplex mode. */
295 if (regv & 0x0004)
296 {
297 /* Full duplex is enabled. */
298 LPC_EMAC->MAC2 |= MAC2_FULL_DUP;
299 LPC_EMAC->Command |= CR_FULL_DUP;
300 LPC_EMAC->IPGT = IPGT_FULL_DUP;
301 }
302 else
303 {
304 /* Half duplex mode. */
305 LPC_EMAC->IPGT = IPGT_HALF_DUP;
306 }
307
308 /* Configure 100MBit/10MBit mode. */
309 if (regv & 0x0002)
310 {
311 /* 10MBit mode. */
312 LPC_EMAC->SUPP = 0;
313 }
314 else
315 {
316 /* 100MBit mode. */
317 LPC_EMAC->SUPP = SUPP_SPEED;
318 }
319
320 /* Set the Ethernet MAC Address registers */
321 LPC_EMAC->SA0 = (lpc_emac_device.dev_addr[1] << 8) | lpc_emac_device.dev_addr[0];
322 LPC_EMAC->SA1 = (lpc_emac_device.dev_addr[3] << 8) | lpc_emac_device.dev_addr[2];
323 LPC_EMAC->SA2 = (lpc_emac_device.dev_addr[5] << 8) | lpc_emac_device.dev_addr[4];
324
325 /* Initialize Tx and Rx DMA Descriptors */
326 rx_descr_init();
327 tx_descr_init();
328
329 /* Receive Broadcast and Perfect Match Packets */
330 LPC_EMAC->RxFilterCtrl = RFC_BCAST_EN | RFC_PERFECT_EN;
331
332 /* Reset all interrupts */
333 LPC_EMAC->IntClear = 0xFFFF;
334
335 /* Enable EMAC interrupts. */
336 LPC_EMAC->IntEnable = INT_RX_DONE | INT_TX_DONE;
337
338 /* Enable receive and transmit mode of MAC Ethernet core */
339 LPC_EMAC->Command |= (CR_RX_EN | CR_TX_EN);
340 LPC_EMAC->MAC1 |= MAC1_REC_EN;
341
342 /* Enable the ENET Interrupt */
343 NVIC_EnableIRQ(ENET_IRQn);
344
345 return RT_EOK;
346 }
347
lpc_emac_open(rt_device_t dev,rt_uint16_t oflag)348 static rt_err_t lpc_emac_open(rt_device_t dev, rt_uint16_t oflag)
349 {
350 return RT_EOK;
351 }
352
lpc_emac_close(rt_device_t dev)353 static rt_err_t lpc_emac_close(rt_device_t dev)
354 {
355 return RT_EOK;
356 }
357
lpc_emac_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)358 static rt_ssize_t lpc_emac_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
359 {
360 rt_set_errno(-RT_ENOSYS);
361 return 0;
362 }
363
lpc_emac_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)364 static rt_ssize_t lpc_emac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
365 {
366 rt_set_errno(-RT_ENOSYS);
367 return 0;
368 }
369
lpc_emac_control(rt_device_t dev,int cmd,void * args)370 static rt_err_t lpc_emac_control(rt_device_t dev, int cmd, void *args)
371 {
372 switch (cmd)
373 {
374 case NIOCTL_GADDR:
375 /* get mac address */
376 if (args) rt_memcpy(args, lpc_emac_device.dev_addr, 6);
377 else return -RT_ERROR;
378 break;
379
380 default :
381 break;
382 }
383
384 return RT_EOK;
385 }
386
387 /* EtherNet Device Interface */
388 /* transmit packet. */
lpc_emac_tx(rt_device_t dev,struct pbuf * p)389 rt_err_t lpc_emac_tx(rt_device_t dev, struct pbuf *p)
390 {
391 rt_uint32_t Index, IndexNext;
392 rt_uint8_t *ptr;
393
394 /* calculate next index */
395 IndexNext = LPC_EMAC->TxProduceIndex + 1;
396 if (IndexNext > LPC_EMAC->TxDescriptorNumber) IndexNext = 0;
397
398 /* check whether block is full */
399 while (IndexNext == LPC_EMAC->TxConsumeIndex)
400 {
401 rt_err_t result;
402 rt_uint32_t recved;
403
404 /* there is no block yet, wait a flag */
405 result = rt_event_recv(&tx_event, 0x01,
406 RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recved);
407
408 RT_ASSERT(result == RT_EOK);
409 }
410
411 /* lock EMAC device */
412 rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
413
414 /* get produce index */
415 Index = LPC_EMAC->TxProduceIndex;
416
417 /* calculate next index */
418 IndexNext = LPC_EMAC->TxProduceIndex + 1;
419 if (IndexNext > LPC_EMAC->TxDescriptorNumber)
420 IndexNext = 0;
421
422 /* copy data to tx buffer */
423 ptr = (rt_uint8_t *)TX_BUF(Index);
424 pbuf_copy_partial(p, ptr, p->tot_len, 0);
425
426 TX_DESC_CTRL(Index) &= ~0x7ff;
427 TX_DESC_CTRL(Index) |= (p->tot_len - 1) & 0x7ff;
428
429 /* change index to the next */
430 LPC_EMAC->TxProduceIndex = IndexNext;
431
432 /* unlock EMAC device */
433 rt_sem_release(&sem_lock);
434
435 return RT_EOK;
436 }
437
438 /* reception packet. */
lpc_emac_rx(rt_device_t dev)439 struct pbuf *lpc_emac_rx(rt_device_t dev)
440 {
441 struct pbuf *p;
442 rt_uint32_t size;
443 rt_uint32_t Index;
444
445 /* init p pointer */
446 p = RT_NULL;
447
448 /* lock EMAC device */
449 rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
450
451 Index = LPC_EMAC->RxConsumeIndex;
452 if (Index != LPC_EMAC->RxProduceIndex)
453 {
454 size = (RX_STAT_INFO(Index) & 0x7ff) + 1;
455 if (size > ETH_FRAG_SIZE) size = ETH_FRAG_SIZE;
456
457 /* allocate buffer */
458 p = pbuf_alloc(PBUF_LINK, size, PBUF_RAM);
459 if (p != RT_NULL)
460 {
461 pbuf_take(p, (rt_uint8_t *)RX_BUF(Index), size);
462 }
463
464 /* move Index to the next */
465 if (++Index > LPC_EMAC->RxDescriptorNumber)
466 Index = 0;
467
468 /* set consume index */
469 LPC_EMAC->RxConsumeIndex = Index;
470 }
471 else
472 {
473 /* Enable RxDone interrupt */
474 LPC_EMAC->IntEnable = INT_RX_DONE | INT_TX_DONE;
475 }
476
477 /* unlock EMAC device */
478 rt_sem_release(&sem_lock);
479
480 return p;
481 }
482
lpc_emac_hw_init(void)483 int lpc_emac_hw_init(void)
484 {
485 uint32_t result[4];
486
487 rt_event_init(&tx_event, "tx_event", RT_IPC_FLAG_FIFO);
488 rt_sem_init(&sem_lock, "eth_lock", 1, RT_IPC_FLAG_FIFO);
489
490 /* set autonegotiation mode */
491 lpc_emac_device.phy_mode = EMAC_PHY_AUTO;
492
493 // OUI 00-60-37 NXP Semiconductors
494 lpc_emac_device.dev_addr[0] = 0x00;
495 lpc_emac_device.dev_addr[1] = 0x60;
496 lpc_emac_device.dev_addr[2] = 0x37;
497 /* set mac address: (only for test) */
498 ReadDeviceSerialNum(result);
499 lpc_emac_device.dev_addr[3] = result[0] ^ result[1];
500 lpc_emac_device.dev_addr[4] = result[1] ^ result[2];
501 lpc_emac_device.dev_addr[5] = result[2] ^ result[3];
502
503 lpc_emac_device.parent.parent.init = lpc_emac_init;
504 lpc_emac_device.parent.parent.open = lpc_emac_open;
505 lpc_emac_device.parent.parent.close = lpc_emac_close;
506 lpc_emac_device.parent.parent.read = lpc_emac_read;
507 lpc_emac_device.parent.parent.write = lpc_emac_write;
508 lpc_emac_device.parent.parent.control = lpc_emac_control;
509 lpc_emac_device.parent.parent.user_data = RT_NULL;
510
511 lpc_emac_device.parent.eth_rx = lpc_emac_rx;
512 lpc_emac_device.parent.eth_tx = lpc_emac_tx;
513
514 eth_device_init(&(lpc_emac_device.parent), "e0");
515 return 0;
516 }
517 INIT_DEVICE_EXPORT(lpc_emac_hw_init);
518
519 #ifdef RT_USING_FINSH
520 #include <finsh.h>
emac_dump()521 void emac_dump()
522 {
523 rt_kprintf("Command : %08x\n", LPC_EMAC->Command);
524 rt_kprintf("Status : %08x\n", LPC_EMAC->Status);
525 rt_kprintf("RxStatus : %08x\n", LPC_EMAC->RxStatus);
526 rt_kprintf("TxStatus : %08x\n", LPC_EMAC->TxStatus);
527 rt_kprintf("IntEnable: %08x\n", LPC_EMAC->IntEnable);
528 rt_kprintf("IntStatus: %08x\n", LPC_EMAC->IntStatus);
529 }
530 FINSH_FUNCTION_EXPORT(emac_dump, dump emac register);
531 #endif
532
533