tinyusb  0.4
Click here to lend your support to tinyusb donation and make a donation at pledgie.com
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
emac.c
1 /******************************************************************
2  ***** *****
3  ***** Name: cs8900.c *****
4  ***** Ver.: 1.0 *****
5  ***** Date: 07/05/2001 *****
6  ***** Auth: Andreas Dannenberg *****
7  ***** HTWK Leipzig *****
8  ***** university of applied sciences *****
9  ***** Germany *****
10  ***** Func: ethernet packet-driver for use with LAN- *****
11  ***** controller CS8900 from Crystal/Cirrus Logic *****
12  ***** *****
13  ***** NXP: Module modified for use with NXP *****
14  ***** lpc43xx EMAC Ethernet controller *****
15  ***** *****
16  ******************************************************************/
17 
18 #include "../../board.h"
19 
20 #if BOARD == BOARD_EA4357
21 
22 #include "EMAC.h"
23 //#include "tcpip.h"
24 #include "lpc43xx.h"
25 #include "lpc43xx_scu.h"
26 #include "lpc43xx_rgu.h"
27 
28 #define TIMEOUT 100000
29 
30 static unsigned short *rptr;
31 static unsigned short *tptr;
32 
33 static unsigned int TxDescIndex = 0;
34 static unsigned int RxDescIndex = 0;
35 
36 // Keil: function added to write PHY
37 static void write_PHY (unsigned int PhyReg, unsigned short Value) {
38 
39  unsigned int tout;
40 
41  /* Write a data 'Value' to PHY register 'PhyReg'. */
42  while(LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY); // Check GMII busy bit
43  LPC_ETHERNET->MAC_MII_ADDR = (DP83848C_DEF_ADR<<11) | (PhyReg<<6) | GMII_WRITE;
44  LPC_ETHERNET->MAC_MII_DATA = Value;
45  LPC_ETHERNET->MAC_MII_ADDR |= GMII_BUSY; // Start PHY Write Cycle
46 
47  /* Wait utill operation completed */
48  for (tout = 0; tout < MII_WR_TOUT; tout++) {
49  if ((LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY) == 0) {
50  break;
51  }
52  }
53  if (tout == MII_WR_TOUT) // Trap the timeout
54  while(1);
55 }
56 
57 
58 // Keil: function added to read PHY
59 static unsigned short read_PHY (unsigned int PhyReg) {
60 
61  unsigned int tout, val;
62 
63  /* Read a PHY register 'PhyReg'. */
64  while(LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY); // Check GMII busy bit
65  LPC_ETHERNET->MAC_MII_ADDR = (DP83848C_DEF_ADR<<11) | (PhyReg<<6) | GMII_READ;
66  LPC_ETHERNET->MAC_MII_ADDR |= GMII_BUSY; // Start PHY Read Cycle
67 
68  /* Wait until operation completed */
69  for (tout = 0; tout < MII_RD_TOUT; tout++) {
70  if ((LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY) == 0) {
71  break;
72  }
73  }
74  if (tout == MII_RD_TOUT) // Trap the timeout
75  while(1);
76  val = LPC_ETHERNET->MAC_MII_DATA;
77  return (val);
78 }
79 
80 
81 // Keil: function added to initialize Rx Descriptors
82 void rx_descr_init (void)
83 {
84  unsigned int i;
85 
86  for (i = 0; i < NUM_RX_DESC; i++) {
87  RX_DESC_STAT(i) = OWN_BIT;
88  RX_DESC_CTRL(i) = ETH_FRAG_SIZE;
89  RX_BUFADDR(i) = RX_BUF(i);
90  if (i == (NUM_RX_DESC-1)) // Last Descriptor?
91  RX_DESC_CTRL(i) |= RX_END_RING;
92  }
93 
94  /* Set Starting address of RX Descriptor list */
95  LPC_ETHERNET->DMA_REC_DES_ADDR = RX_DESC_BASE;
96 }
97 
98 
99 // Keil: function added to initialize Tx Descriptors
100 void tx_descr_init (void)
101 {
102  unsigned int i;
103 
104  for (i = 0; i < NUM_TX_DESC; i++) { // Take it out!!!!
105  TX_DESC_STAT(i) = 0;
106  TX_DESC_CTRL(i) = 0;
107  TX_BUFADDR(i) = 0;
108  }
109 
110  for (i = 0; i < NUM_TX_DESC; i++) {
111  TX_DESC_STAT(i) = TX_LAST_SEGM | TX_FIRST_SEGM;
112  TX_DESC_CTRL(i) = 0;
113  TX_BUFADDR(i) = TX_BUF(i);
114  if (i == (NUM_TX_DESC-1)) // Last Descriptor?
115  TX_DESC_STAT(i) |= TX_END_RING;
116  }
117 
118  /* Set Starting address of RX Descriptor list */
119  LPC_ETHERNET->DMA_TRANS_DES_ADDR = TX_DESC_BASE;
120 }
121 
122 
123 
124 // configure port-pins for use with LAN-controller,
125 // reset it and send the configuration-sequence
126 
127 void Init_EMAC(void)
128 {
129  int id1, id2, tout, regv;
130  unsigned phy_in_use = 0;
131 
132  /* Ethernet pins configuration */
133 #if MII
134  scu_pinmux(0xC ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_MDC: PC_1 -> FUNC3
135  scu_pinmux(0x1 ,17 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_MDIO: P1_17 -> FUNC3
136  scu_pinmux(0x1 ,18 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD0: P1_18 -> FUNC3
137  scu_pinmux(0x1 ,20 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD1: P1_20 -> FUNC3
138  scu_pinmux(0x1 ,19 , (MD_PLN | MD_EZI | MD_ZI), FUNC0); // ENET_REF: P1_19 -> FUNC0 (default)
139 
140 // scu_pinmux(0xC ,4 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TX_EN: PC_4 -> FUNC3
141  scu_pinmux(0x0 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC6); // ENET_TX_EN: P0_1 -> FUNC6
142 
143  scu_pinmux(0x1 ,15 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD0: P1_15 -> FUNC3
144  scu_pinmux(0x0 ,0 , (MD_PLN | MD_EZI | MD_ZI), FUNC2); // ENET_RXD1: P0_0 -> FUNC2
145 
146 // scu_pinmux(0x1 ,16 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_CRS: P1_16 -> FUNC3
147  scu_pinmux(0x9 ,0 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_CRS: P9_0 -> FUNC5
148 
149 // scu_pinmux(0xC ,9 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RX_ER: PC_9 -> FUNC3
150  scu_pinmux(0x9 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_RX_ER: P9_1 -> FUNC5
151 
152 // scu_pinmux(0xC ,8 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXDV: PC_8 -> FUNC3
153  scu_pinmux(0x1 ,16 , (MD_PLN | MD_EZI | MD_ZI), FUNC7); // ENET_RXDV: P1_16 -> FUNC7
154 
155 #else
156  scu_pinmux(0xC ,1 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_MDC: PC_1 -> FUNC3
157  scu_pinmux(0x1 ,17 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_MDIO: P1_17 -> FUNC3
158  scu_pinmux(0x1 ,18 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD0: P1_18 -> FUNC3
159  scu_pinmux(0x1 ,20 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD1: P1_20 -> FUNC3
160  scu_pinmux(0x1 ,19 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC0); // ENET_REF: P1_19 -> FUNC0 (default)
161 // scu_pinmux(0xC ,4 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TX_EN: PC_4 -> FUNC3
162  scu_pinmux(0x0 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC6); // ENET_TX_EN: P0_1 -> FUNC6
163  scu_pinmux(0x1 ,15 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD0: P1_15 -> FUNC3
164  scu_pinmux(0x0 ,0 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC2); // ENET_RXD1: P0_0 -> FUNC2
165 // scu_pinmux(0x1 ,16 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_CRS: P1_16 -> FUNC3
166 // scu_pinmux(0x9 ,0 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_CRS: P9_0 -> FUNC5
167 // scu_pinmux(0xC ,9 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RX_ER: PC_9 -> FUNC3
168 // scu_pinmux(0x9 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_RX_ER: P9_1 -> FUNC5
169 // scu_pinmux(0xC ,8 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXDV: PC_8 -> FUNC3
170  scu_pinmux(0x1 ,16 , (MD_PLN | MD_EZI | MD_ZI), FUNC7); // ENET_RXDV: P1_16 -> FUNC7
171 #endif
172 
173 
174 #if MII /* Select MII interface */ // check MUXING for new Eagle...
175 // scu_pinmux(0xC ,6 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD2: PC_6 -> FUNC3
176  scu_pinmux(0x9 ,3 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_RXD2: P9_3 -> FUNC5
177 
178 // scu_pinmux(0xC ,7 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD3: PC_7 -> FUNC3
179  scu_pinmux(0x9 ,2 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_RXD3: P9_2 -> FUNC5
180 
181  scu_pinmux(0xC ,0 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXLK: PC_0 -> FUNC3
182 
183 // scu_pinmux(0xC ,2 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD2: PC_2 -> FUNC3
184  scu_pinmux(0x9 ,4 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_TXD2: P9_4 -> FUNC5
185 
186 // scu_pinmux(0xC ,3 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD3: PC_3 -> FUNC3
187  scu_pinmux(0x9 ,5 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_TXD3: P9_5 -> FUNC5
188 
189 // scu_pinmux(0xC ,5 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TX_ER: PC_5 -> FUNC3
190  scu_pinmux(0xC ,5 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TX_ER: PC_5 -> FUNC3
191 
192 // scu_pinmux(0x0 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC2); // ENET_COL: P0_1 -> FUNC2
193  scu_pinmux(0x9 ,6 , (MD_PLN | MD_EZI | MD_ZI), FUNC5); // ENET_COL: P9_6 -> FUNC5
194 #else /* Select RMII interface */
195  LPC_CREG->CREG6 |= RMII_SELECT;
196 #endif
197 
198 
199  RGU_SoftReset(RGU_SIG_ETHERNET);
200  while(1){ // Confirm the reset happened
201  if (LPC_RGU->RESET_ACTIVE_STATUS0 & (1<<ETHERNET_RST))
202  break;
203  }
204 
205  LPC_ETHERNET->DMA_BUS_MODE |= DMA_SOFT_RESET; // Reset all GMAC Subsystem internal registers and logic
206  while(LPC_ETHERNET->DMA_BUS_MODE & DMA_SOFT_RESET); // Wait for software reset completion
207 
208  /* Put the DP83848C in reset mode */
209  write_PHY (PHY_REG_BMCR, PHY_BMCR_RESET);
210 
211  /* Wait for hardware reset to end. */
212  for (tout = 0; tout < TIMEOUT; tout++) {
213  regv = read_PHY (PHY_REG_BMCR);
214  if (!(regv & PHY_BMCR_RESET)) {
215  /* Reset complete */
216  break;
217  }
218  }
219 
220  /* Check if this is a DP83848C PHY. */
221  id1 = read_PHY (PHY_REG_IDR1);
222  id2 = read_PHY (PHY_REG_IDR2);
223  if (((id1 << 16) | (id2 & 0xFFF0)) == DP83848C_ID) {
224  phy_in_use = DP83848C_ID;
225  }
226  else if (((id1 << 16) | (id2 & 0xFFF0)) == LAN8720_ID) {
227  phy_in_use = LAN8720_ID;
228  }
229 
230  if (phy_in_use != 0) {
231  /* Configure the PHY device */
232 #if !MII
233  write_PHY (PHY_REG_RBR, 0x20);
234 #endif
235 
236  /* Use autonegotiation about the link speed. */
237  write_PHY (PHY_REG_BMCR, PHY_AUTO_NEG);
238  /* Wait to complete Auto_Negotiation. */
239  for (tout = 0; tout < TIMEOUT; tout++) {
240  regv = read_PHY (PHY_REG_BMSR);
241  if (regv & PHY_AUTO_NEG_DONE) {
242  /* Autonegotiation Complete. */
243  break;
244  }
245  }
246  }
247 
248 
249 
250  /* Check the link status. */
251  for (tout = 0; tout < TIMEOUT; tout++) {
252  regv = read_PHY (PHY_REG_STS);
253  if (regv & LINK_VALID_STS) {
254  /* Link is on. */
255  break;
256  }
257  }
258 
259  // Configure the EMAC with the established parameters
260  switch (phy_in_use) {
261  case DP83848C_ID:
262 
263  /* Configure Full/Half Duplex mode. */
264  if (regv & FULL_DUP_STS) {
265  /* Full duplex is enabled. */
266  LPC_ETHERNET->MAC_CONFIG |= MAC_DUPMODE;
267  }
268 
269  /* Configure 100MBit/10MBit mode. */
270  if (~(regv & SPEED_10M_STS)) {
271  /* 100MBit mode. */
272  LPC_ETHERNET->MAC_CONFIG |= MAC_100MPS;
273  }
274 
275 // value = ReadFromPHY (PHY_REG_STS); /* PHY Extended Status Register */
276 // // Now configure for full/half duplex mode
277 // if (value & 0x0004) {
278 // // We are in full duplex is enabled mode
279 // LPC_ETHERNET->MAC2 |= MAC2_FULL_DUP;
280 // LPC_ETHERNET->Command |= CR_FULL_DUP;
281 // LPC_ETHERNET->IPGT = IPGT_FULL_DUP;
282 // }
283 // else {
284 // // Otherwise we are in half duplex mode
285 // LPC_ETHERNET->IPGT = IPGT_HALF_DUP;
286 // }
287 
288 // // Now configure 100MBit or 10MBit mode
289 // if (value & 0x0002) {
290 // // 10MBit mode
291 // LPC_ETHERNET->SUPP = 0;
292 // }
293 // else {
294 // // 100MBit mode
295 // LPC_ETHERNET->SUPP = SUPP_SPEED;
296 // }
297  break;
298 
299  case LAN8720_ID:
300 
301  regv = read_PHY (PHY_REG_SCSR); /* PHY Extended Status Register */
302  // Now configure for full/half duplex mode
303  if (regv & (1<<4)) { /* bit 4: 1 = Full Duplex, 0 = Half Duplex */
304  // We are in full duplex is enabled mode
305  LPC_ETHERNET->MAC_CONFIG |= MAC_DUPMODE;
306  }
307 
308  // Now configure 100MBit or 10MBit mode
309  if (regv & (1<<3)) { /* bit 3: 1 = 100Mbps, 0 = 10Mbps */
310  // 100MBit mode
311  LPC_ETHERNET->MAC_CONFIG |= MAC_100MPS;
312  }
313 
314 
315  break;
316 
317  }
318 
319  /* Set the Ethernet MAC Address registers */
320  LPC_ETHERNET->MAC_ADDR0_HIGH = (MYMAC_6 << 8) | MYMAC_5;
321  LPC_ETHERNET->MAC_ADDR0_LOW = (MYMAC_4 << 24) | (MYMAC_3 << 16) | (MYMAC_2 << 8) | MYMAC_1;
322 
323  /* Initialize Descriptor Lists */
324  rx_descr_init();
325  tx_descr_init();
326 
327  /* Configure Filter */
328  LPC_ETHERNET->MAC_FRAME_FILTER = MAC_PROMISCUOUS | MAC_RECEIVEALL;
329 
330  /* Enable Receiver and Transmitter */
331  LPC_ETHERNET->MAC_CONFIG |= (MAC_TX_ENABLE | MAC_RX_ENABLE);
332 
333  /* Enable interrupts */
334  //LPC_ETHERNET->DMA_INT_EN = DMA_INT_NOR_SUM | DMA_INT_RECEIVE | DMA_INT_TRANSMIT;
335 
336  /* Start Transmission & Receive processes */
337  LPC_ETHERNET->DMA_OP_MODE |= (DMA_SS_TRANSMIT | DMA_SS_RECEIVE );
338 
339 }
340 
341 
342 // reads a word in little-endian byte order from RX_BUFFER
343 
344 unsigned short ReadFrame_EMAC(void)
345 {
346  return (*rptr++);
347 }
348 
349 
350 // easyWEB internal function
351 // help function to swap the byte order of a WORD
352 
353 unsigned short SwapBytes(unsigned short Data)
354 {
355  return (Data >> 8) | (Data << 8);
356 }
357 
358 // reads a word in big-endian byte order from RX_FRAME_PORT
359 // (useful to avoid permanent byte-swapping while reading
360 // TCP/IP-data)
361 
362 unsigned short ReadFrameBE_EMAC(void)
363 {
364  unsigned short ReturnValue;
365 
366  ReturnValue = SwapBytes (*rptr++);
367  return (ReturnValue);
368 }
369 
370 
371 // copies bytes from frame port to MCU-memory
372 // NOTES: * an odd number of byte may only be transfered
373 // if the frame is read to the end!
374 // * MCU-memory MUST start at word-boundary
375 
376 void CopyFromFrame_EMAC(void *Dest, unsigned short Size)
377 {
378  unsigned short * piDest; // Keil: Pointer added to correct expression
379 
380  piDest = Dest; // Keil: Line added
381  while (Size > 1) {
382  *piDest++ = ReadFrame_EMAC();
383  Size -= 2;
384  }
385 
386  if (Size) { // check for leftover byte...
387  *(unsigned char *)piDest = (char)ReadFrame_EMAC();// the LAN-Controller will return 0
388  } // for the highbyte
389 }
390 
391 // does a dummy read on frame-I/O-port
392 // NOTE: only an even number of bytes is read!
393 
394 void DummyReadFrame_EMAC(unsigned short Size) // discards an EVEN number of bytes
395 { // from RX-fifo
396  while (Size > 1) {
397  ReadFrame_EMAC();
398  Size -= 2;
399  }
400 }
401 
402 // Reads the length of the received ethernet frame and checks if the
403 // destination address is a broadcast message or not
404 // returns the frame length
405 unsigned short StartReadFrame(void) {
406  unsigned short RxLen;
407 
408  if ((RX_DESC_STAT(RxDescIndex) & OWN_BIT) == 0) {
409  RxLen = (RX_DESC_STAT(RxDescIndex) >> 16) & 0x03FFF;
410  rptr = (unsigned short *)RX_BUFADDR(RxDescIndex);
411  return(RxLen);
412  }
413  return 0;
414 
415 }
416 
417 void EndReadFrame(void) {
418 
419  RX_DESC_STAT(RxDescIndex) = OWN_BIT;
420  RxDescIndex++;
421  if (RxDescIndex == NUM_RX_DESC)
422  RxDescIndex = 0;
423 }
424 
425 unsigned int CheckFrameReceived(void) { // Packet received ?
426 
427  if ((RX_DESC_STAT(RxDescIndex) & OWN_BIT) == 0)
428  return(1);
429  else
430  return(0);
431 }
432 
433 // requests space in EMAC memory for storing an outgoing frame
434 
435 void RequestSend(unsigned short FrameSize)
436 {
437  tptr = (unsigned short *)TX_BUFADDR(TxDescIndex);
438  TX_DESC_CTRL(TxDescIndex) = FrameSize;
439 }
440 
441 // check if ethernet controller is ready to accept the
442 // frame we want to send
443 
444 unsigned int Rdy4Tx(void)
445 {
446  return (1); // the ethernet controller transmits much faster
447 } // than the CPU can load its buffers
448 
449 
450 // writes a word in little-endian byte order to TX_BUFFER
451 void WriteFrame_EMAC(unsigned short Data)
452 {
453  *tptr++ = Data;
454 }
455 
456 // copies bytes from MCU-memory to frame port
457 // NOTES: * an odd number of byte may only be transfered
458 // if the frame is written to the end!
459 // * MCU-memory MUST start at word-boundary
460 
461 void CopyToFrame_EMAC(void *Source, unsigned int Size)
462 {
463  unsigned short * piSource;
464 // unsigned int idx;
465 
466  piSource = Source;
467  Size = (Size + 1) & 0xFFFE; // round Size up to next even number
468  while (Size > 0) {
469  WriteFrame_EMAC(*piSource++);
470  Size -= 2;
471  }
472  TX_DESC_STAT(TxDescIndex) |= OWN_BIT;
473  LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1; // Wake Up the DMA if it's in Suspended Mode
474  TxDescIndex++;
475  if (TxDescIndex == NUM_TX_DESC)
476  TxDescIndex = 0;
477 }
478 
479 #endif
480