LPC17xx Ethernet Support. Driver only, no network stack or application layer yet!
[clinton/Smoothieware.git] / src / libs / Network / Drivers / LPC17XX_Ethernet.cpp
1 #include "LPC17XX_Ethernet.h"
2
3 #include <cstring>
4 #include <cstdio>
5
6 #include "lpc17xx_clkpwr.h"
7
8 #include <mri.h>
9
10 // #include "netcore.h"
11
12 static const uint8_t EMAC_clkdiv[] = { 4, 6, 8, 10, 14, 20, 28 };
13
14 /*--------------------------- write_PHY -------------------------------------*/
15 /*********************************************************************//**
16 * @brief Write value to PHY device
17 * @param[in] PhyReg: PHY Register address
18 * @param[in] Value: Value to write
19 * @return 0 - if success
20 * 1 - if fail
21 ***********************************************************************/
22 static int32_t write_PHY (uint32_t PhyReg, uint16_t Value)
23 {
24 /* Write a data 'Value' to PHY register 'PhyReg'. */
25 uint32_t tout;
26
27 LPC_EMAC->MADR = EMAC_DEF_ADR | PhyReg;
28 LPC_EMAC->MWTD = Value;
29
30 /* Wait until operation completed */
31 tout = 0;
32 for (tout = 0; tout < EMAC_MII_WR_TOUT; tout++) {
33 if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0) {
34 return (0);
35 }
36 }
37 printf("write PHY %lu %04X failed!\n", PhyReg, Value);
38 // Time out!
39 return (-1);
40 }
41
42
43 /*--------------------------- read_PHY --------------------------------------*/
44 /*********************************************************************//**
45 * @brief Read value from PHY device
46 * @param[in] PhyReg: PHY Register address
47 * @return 0 - if success
48 * 1 - if fail
49 ***********************************************************************/
50 static int32_t read_PHY (uint32_t PhyReg)
51 {
52 /* Read a PHY register 'PhyReg'. */
53 uint32_t tout;
54
55 LPC_EMAC->MADR = EMAC_DEF_ADR | PhyReg;
56 LPC_EMAC->MCMD = EMAC_MCMD_READ;
57
58 /* Wait until operation completed */
59 tout = 0;
60 for (tout = 0; tout < EMAC_MII_RD_TOUT; tout++) {
61 if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0) {
62 LPC_EMAC->MCMD = 0;
63 return (LPC_EMAC->MRDD);
64 }
65 }
66 printf("read PHY %lu failed!\n", PhyReg);
67 // Time out!
68 return (-1);
69 }
70
71 /*********************************************************************//**
72 * @brief Set Station MAC address for EMAC module
73 * @param[in] abStationAddr Pointer to Station address that contains 6-bytes
74 * of MAC address (should be in order from MAC Address 1 to MAC Address 6)
75 * @return None
76 **********************************************************************/
77 static void setEmacAddr(uint8_t abStationAddr[])
78 {
79 /* Set the Ethernet MAC Address registers */
80 LPC_EMAC->SA0 = ((uint32_t)abStationAddr[5] << 8) | (uint32_t)abStationAddr[4];
81 LPC_EMAC->SA1 = ((uint32_t)abStationAddr[3] << 8) | (uint32_t)abStationAddr[2];
82 LPC_EMAC->SA2 = ((uint32_t)abStationAddr[1] << 8) | (uint32_t)abStationAddr[0];
83 }
84
85 /*********************************************************************//**
86 * @brief Set specified PHY mode in EMAC peripheral
87 * @param[in] ulPHYMode Specified PHY mode, should be:
88 * - EMAC_MODE_AUTO
89 * - EMAC_MODE_10M_FULL
90 * - EMAC_MODE_10M_HALF
91 * - EMAC_MODE_100M_FULL
92 * - EMAC_MODE_100M_HALF
93 * @return Return (0) if no error, otherwise return (-1)
94 **********************************************************************/
95 int32_t emac_SetPHYMode(uint32_t ulPHYMode)
96 {
97 int32_t id1, id2, tout, regv;
98
99 id1 = read_PHY (EMAC_PHY_REG_IDR1);
100 id2 = read_PHY (EMAC_PHY_REG_IDR2);
101
102 if (((id1 << 16) | (id2 & 0xFFF0)) == EMAC_SMSC_8720A) {
103 switch(ulPHYMode){
104 case EMAC_MODE_AUTO:
105 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_AUTO_NEG);
106 /* Wait to complete Auto_Negotiation */
107 for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
108 regv = read_PHY (EMAC_PHY_REG_BMSR);
109 if (regv & EMAC_PHY_BMSR_AUTO_DONE) {
110 /* Auto-negotiation Complete. */
111 break;
112 }
113 if (tout == 0){
114 // Time out, return error
115 return (-1);
116 }
117 }
118 break;
119 case EMAC_MODE_10M_FULL:
120 /* Connect at 10MBit full-duplex */
121 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_10M);
122 break;
123 case EMAC_MODE_10M_HALF:
124 /* Connect at 10MBit half-duplex */
125 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_10M);
126 break;
127 case EMAC_MODE_100M_FULL:
128 /* Connect at 100MBit full-duplex */
129 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_100M);
130 break;
131 case EMAC_MODE_100M_HALF:
132 /* Connect at 100MBit half-duplex */
133 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_100M);
134 break;
135 default:
136 // un-supported
137 return (-1);
138 }
139 }
140 // It's not correct module ID
141 else {
142 printf("PHY reports id %04lX %04lX - not an SMSC 8720A\n", id1, id2);
143 return (-1);
144 }
145
146 // Update EMAC configuration with current PHY status
147 if (EMAC_UpdatePHYStatus() < 0){
148 return (-1);
149 }
150
151 // Complete
152 return (0);
153 }
154
155 _rxbuf_t LPC17XX_Ethernet::rxbuf __attribute__ ((section ("AHBSRAM1")));
156 _txbuf_t LPC17XX_Ethernet::txbuf __attribute__ ((section ("AHBSRAM1")));
157
158 LPC17XX_Ethernet* LPC17XX_Ethernet::instance;
159
160 LPC17XX_Ethernet::LPC17XX_Ethernet()
161 {
162 mac_address[0] = 0xAE;
163 mac_address[1] = 0xF0;
164 mac_address[2] = 0x28;
165 mac_address[3] = 0x5D;
166 mac_address[4] = 0x66;
167 mac_address[5] = 0x41;
168
169 ip_address = IPA(192,168,1,27);
170 ip_mask = 0xFFFFFF00;
171
172 for (int i = 0; i < 5; i++)
173 {
174 rxbuf.rxdesc[i].packet = rxbuf.buf[i];
175 rxbuf.rxdesc[i].control = (1536 - 1) | EMAC_RCTRL_INT;
176
177 rxbuf.rxstat[i].Info = 0;
178 rxbuf.rxstat[i].HashCRC = 0;
179
180 txbuf.txdesc[i].packet = txbuf.buf[i];
181 txbuf.txdesc[i].control = (1536 - 1) | EMAC_TCTRL_PAD | EMAC_TCTRL_CRC | EMAC_TCTRL_LAST | EMAC_TCTRL_INT;
182
183 txbuf.txstat[i].Info = 0;
184 }
185
186 interface_name = (uint8_t*) malloc(5);
187 memcpy(interface_name, "eth0", 5);
188
189 instance = this;
190
191 up = false;
192 }
193
194 void LPC17XX_Ethernet::on_module_loaded()
195 {
196 LPC_PINCON->PINSEL2 = (1 << 0) | (1 << 2) | (1 << 8) | (1 << 16) | (1 << 18) | (1 << 20) | (1 << 28) | (1 << 30);
197 LPC_PINCON->PINSEL3 &= (2 << 0) | (2 << 2);
198 LPC_PINCON->PINSEL3 |= (1 << 0) | (1 << 2);
199
200 printf("EMAC_INIT\n");
201 emac_init();
202 printf("INIT OK\n");
203
204 register_for_event(ON_IDLE);
205 register_for_event(ON_SECOND_TICK);
206 }
207
208 void LPC17XX_Ethernet::on_idle(void*)
209 {
210 _receive_frame();
211 }
212
213 void LPC17XX_Ethernet::on_second_tick(void*)
214 {
215 // LPC_EMAC->Command = 0x303;
216 // setEmacAddr(mac_address);
217 uint32_t st;
218 st = read_PHY (EMAC_PHY_REG_BMSR);
219
220 if ((st & EMAC_PHY_BMSR_LINK_ESTABLISHED) && (st & EMAC_PHY_BMSR_AUTO_DONE) && (up == false))
221 {
222 // TODO: link up event
223 up = true;
224 // net->set_interface_status(this, up);
225 uint32_t scsr = read_PHY(EMAC_PHY_REG_SCSR);
226 printf("%s: link up: ", interface_name);
227 switch ((scsr >> 2) & 0x7)
228 {
229 case 1:
230 printf("10MBit Half Duplex\n");
231 break;
232 case 5:
233 printf("10MBit Full Duplex\n");
234 break;
235 case 2:
236 printf("100MBit Half Duplex\n");
237 break;
238 case 6:
239 printf("100MBit Full Duplex\n");
240 break;
241 default:
242 printf("Unknown speed: SCSR = 0x%04lX\n", scsr);
243 break;
244 }
245 }
246 else if (((st & EMAC_PHY_BMSR_LINK_ESTABLISHED) == 0) && up)
247 {
248 // TODO: link down event
249 up = false;
250 // net->set_interface_status(this, up);
251 printf("%s: link down\n", interface_name);
252 }
253
254 // printf("PHY: id:%04lX %04lX st:%04lX\n", id1, id2, st);
255 // printf("ETH: Rx:%lu/%lu Tx:%lu/%lu\n", LPC_EMAC->RxConsumeIndex, LPC_EMAC->RxProduceIndex, LPC_EMAC->TxProduceIndex, LPC_EMAC->TxConsumeIndex);
256 // printf("MII: 0x%1lX\n", LPC_EMAC->MIND);
257 // printf("Command: 0x%03lX Status: 0x%1lX\n", LPC_EMAC->Command, LPC_EMAC->Status);
258 // printf("RxN: %lu TxN: %lu\n", LPC_EMAC->RxDescriptorNumber, LPC_EMAC->TxDescriptorNumber);
259 // printf("MAC1: 0x%04lX MAC2: 0x%04lX\n", LPC_EMAC->MAC1, LPC_EMAC->MAC2);
260 // printf("MAC Address: %02lX:%02lX:%02lX:%02lX:%02lX:%02lX\n", (LPC_EMAC->SA2) & 0xFF, (LPC_EMAC->SA2 >> 8) & 0xFF, (LPC_EMAC->SA1) & 0xFF, (LPC_EMAC->SA1 >> 8) & 0xFF, (LPC_EMAC->SA0) & 0xFF, (LPC_EMAC->SA0 >> 8) & 0xFF);
261 }
262
263 void LPC17XX_Ethernet::emac_init()
264 {
265 /* Initialize the EMAC Ethernet controller. */
266 int32_t regv,tout, tmp;
267 volatile uint32_t d;
268
269 /* Set up clock and power for Ethernet module */
270 CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, ENABLE);
271
272 /* Reset all EMAC internal modules */
273 LPC_EMAC->MAC1 = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
274 EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;
275
276 LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES;
277
278 /* A short delay after reset. */
279 for (d = 256; d; d--);
280
281 /* Initialize MAC control registers. */
282 LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;
283 LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN | EMAC_MAC2_FULL_DUP;
284 LPC_EMAC->MAXF = EMAC_ETH_MAX_FLEN;
285 /*
286 * Find the clock that close to desired target clock
287 */
288 tmp = SystemCoreClock / EMAC_MCFG_MII_MAXCLK;
289 for (tout = 0; tout < (int32_t) sizeof (EMAC_clkdiv); tout++){
290 if (EMAC_clkdiv[tout] >= tmp) break;
291 }
292 tout++;
293 // Write to MAC configuration register and reset
294 LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(tout) | EMAC_MCFG_RES_MII;
295 // release reset
296 LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);
297 LPC_EMAC->CLRT = EMAC_CLRT_DEF;
298 LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;
299
300 /* Enable Reduced MII interface. */
301 LPC_EMAC->Command = EMAC_CR_RMII;
302
303 /* Reset Reduced MII Logic. */
304 LPC_EMAC->SUPP = EMAC_SUPP_RES_RMII;
305
306 for (d = 256; d; d--);
307 LPC_EMAC->SUPP = EMAC_SUPP_SPEED;
308
309 /* Put the DP83848C in reset mode */
310 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_BMCR_RESET);
311
312 /* Wait for hardware reset to end. */
313 for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
314 regv = read_PHY (EMAC_PHY_REG_BMCR);
315 if (!(regv & (EMAC_PHY_BMCR_RESET | EMAC_PHY_BMCR_POWERDOWN))) {
316 /* Reset complete, device not Power Down. */
317 break;
318 }
319 if (tout == 0){
320 // Time out, return ERROR
321 printf("ETH: PHY TIMEOUT\n");
322 return;
323 }
324 }
325
326 // Set PHY mode
327 // if (emac_SetPHYMode(EMAC_MODE_AUTO) < 0){
328 // printf("ETH: Error Setting Mode\n");
329 // return;
330 // }
331 write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_AUTO_NEG);
332
333 // Set EMAC address
334 setEmacAddr(mac_address);
335
336 /* Initialize Tx and Rx DMA Descriptors */
337 LPC_EMAC->RxDescriptor = (uint32_t) rxbuf.rxdesc;
338 LPC_EMAC->RxStatus = (uint32_t) rxbuf.rxstat;
339 LPC_EMAC->RxDescriptorNumber = 4;
340
341 LPC_EMAC->TxDescriptor = (uint32_t) txbuf.txdesc;
342 LPC_EMAC->TxStatus = (uint32_t) txbuf.txstat;
343 LPC_EMAC->TxDescriptorNumber = 4;
344
345 // Set Receive Filter register: enable broadcast and multicast
346 LPC_EMAC->RxFilterCtrl = EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;
347
348 /* Enable Rx Done and Tx Done interrupt for EMAC */
349 LPC_EMAC->IntEnable = EMAC_INT_RX_DONE | EMAC_INT_TX_DONE;
350
351 /* Reset all interrupts */
352 LPC_EMAC->IntClear = 0xFFFF;
353
354 /* Enable receive and transmit mode of MAC Ethernet core */
355 LPC_EMAC->Command = EMAC_CR_RX_EN | EMAC_CR_TX_EN | EMAC_CR_RMII | EMAC_CR_FULL_DUP | EMAC_CR_PASS_RUNT_FRM;
356 LPC_EMAC->MAC1 |= EMAC_MAC1_REC_EN;
357
358 printf("ETH:EMAC INITIALISED\n");
359 }
360
361 void LPC17XX_Ethernet::set_mac(uint8_t* newmac)
362 {
363 memcpy(mac_address, newmac, 6);
364 }
365
366 void LPC17XX_Ethernet::_receive_frame()
367 {
368 if (can_read_packet() && can_write_packet())
369 {
370 int i = LPC_EMAC->RxConsumeIndex;
371 RX_Stat* stat = &(rxbuf.rxstat[i]);
372 NET_PACKET packet = (NET_PACKET) rxbuf.buf[i];
373
374 int size = stat->Info & EMAC_RINFO_SIZE;
375 printf("Received %d byte Ethernet frame %lu/%lu\n", size, LPC_EMAC->RxProduceIndex, LPC_EMAC->RxConsumeIndex);
376
377 // TODO: feed received packet to network stack here
378 // int s = net->receive_packet(this, packet, size);
379 // if (s)
380 // {
381 // memcpy(request_packet_buffer(), packet, s);
382 // write_packet((uint8_t*) request_packet_buffer(), s);
383 // }
384
385 uint32_t r = LPC_EMAC->RxConsumeIndex + 1;
386 if (r > LPC_EMAC->RxDescriptorNumber)
387 r = 0;
388 LPC_EMAC->RxConsumeIndex = r;
389 }
390 }
391
392 void LPC17XX_Ethernet::irq()
393 {
394 if (EMAC_IntGetStatus(EMAC_INT_RX_DONE))
395 {
396 _receive_frame();
397 }
398
399 if (EMAC_IntGetStatus(EMAC_INT_TX_DONE))
400 {
401 }
402 }
403
404 bool LPC17XX_Ethernet::can_read_packet()
405 {
406 return (LPC_EMAC->RxProduceIndex != LPC_EMAC->RxConsumeIndex);
407 }
408
409 int LPC17XX_Ethernet::read_packet(uint8_t** buf)
410 {
411 *buf = rxbuf.buf[LPC_EMAC->RxConsumeIndex];
412 return rxbuf.rxstat[LPC_EMAC->RxConsumeIndex].Info & EMAC_RINFO_SIZE;
413 }
414
415 void LPC17XX_Ethernet::release_read_packet(uint8_t*)
416 {
417 uint32_t r = LPC_EMAC->RxConsumeIndex + 1;
418 if (r > LPC_EMAC->RxDescriptorNumber)
419 r = 0;
420 LPC_EMAC->RxConsumeIndex = r;
421 }
422
423 bool LPC17XX_Ethernet::can_write_packet()
424 {
425 uint32_t r = LPC_EMAC->TxProduceIndex + 1;
426 if (r > LPC_EMAC->TxDescriptorNumber)
427 r = 0;
428 return (r != LPC_EMAC->TxConsumeIndex);
429 }
430
431 int LPC17XX_Ethernet::write_packet(uint8_t* buf, int size)
432 {
433 txbuf.txdesc[LPC_EMAC->TxProduceIndex].control = size | EMAC_TCTRL_LAST | EMAC_TCTRL_CRC | EMAC_TCTRL_PAD | EMAC_TCTRL_INT;
434
435 uint32_t r = LPC_EMAC->TxProduceIndex + 1;
436 if (r > LPC_EMAC->TxDescriptorNumber)
437 r = 0;
438
439 if (r == LPC_EMAC->TxConsumeIndex)
440 return 0;
441
442 LPC_EMAC->TxProduceIndex = r;
443
444 return size;
445 }
446
447 void* LPC17XX_Ethernet::request_packet_buffer()
448 {
449 return txbuf.txdesc[LPC_EMAC->TxProduceIndex].packet;
450 }
451
452 NET_PACKET LPC17XX_Ethernet::get_new_packet_buffer(NetworkInterface* ni)
453 {
454 if (ni != this)
455 return NULL;
456
457 return (NET_PACKET) request_packet_buffer();
458 }
459
460 NET_PAYLOAD LPC17XX_Ethernet::get_payload_buffer(NET_PACKET packet)
461 {
462 return (NET_PAYLOAD) packet;
463 }
464
465 void LPC17XX_Ethernet::set_payload_length(NET_PACKET packet, int length)
466 {
467 uint32_t offset = ((uint8_t*) packet) - txbuf.buf[0];
468 int i = (offset / LPC17XX_MAX_PACKET);
469 if ((i < LPC17XX_TXBUFS) && ((offset % LPC17XX_MAX_PACKET) == 0))
470 {
471 txbuf.txdesc[i].control = (txbuf.txdesc[i].control & ~EMAC_TCTRL_SIZE) | (length & EMAC_TCTRL_SIZE);
472 }
473 }
474
475 int LPC17XX_Ethernet::receive(NetworkInterface* ni, NET_PACKET packet, int length)
476 {
477 if (can_write_packet())
478 return write_packet((uint8_t*) packet, length);
479 return 0;
480 }
481
482 int LPC17XX_Ethernet::construct(NetworkInterface* ni, NET_PACKET packet, int length)
483 {
484 return length;
485 }
486
487 extern "C" {
488 void ENET_IRQHandler()
489 {
490 LPC17XX_Ethernet::instance->irq();
491 }
492 }
493
494 // void LPC17XX_Ethernet::provide_net(netcore* n)
495 // {
496 // NetworkInterface::provide_net(n);
497 // up = false;
498 // n->set_interface_status(this, up);
499 // }