lm3s_eth_driver.c
Go to the documentation of this file.
1 /**
2  * @file lm3s_eth_driver.c
3  * @brief Luminary Stellaris LM3S Ethernet controller
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2020 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.8
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL NIC_TRACE_LEVEL
33 
34 //LM3S6965 device?
35 #if defined(LM3S6965)
36  #include "lm3s6965.h"
37 //LM3S9B92 device?
38 #elif defined(LM3S9B92)
39  #include "lm3s9b92.h"
40 #endif
41 
42 //Dependencies
43 #include "inc/hw_ints.h"
44 #include "inc/hw_memmap.h"
45 #include "inc/hw_types.h"
46 #include "driverlib/gpio.h"
47 #include "driverlib/interrupt.h"
48 #include "driverlib/sysctl.h"
49 #include "core/net.h"
51 #include "debug.h"
52 
53 //Underlying network interface
54 static NetInterface *nicDriverInterface;
55 
56 //IAR EWARM compiler?
57 #if defined(__ICCARM__)
58 
59 //Transmit buffer
60 #pragma data_alignment = 4
61 static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2];
62 //Receive buffer
63 #pragma data_alignment = 4
64 static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE];
65 
66 //Keil MDK-ARM or GCC compiler?
67 #else
68 
69 //Transmit buffer
70 static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2] __attribute__((aligned(4)));
71 //Receive buffer
72 static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE] __attribute__((aligned(4)));
73 
74 #endif
75 
76 
77 /**
78  * @brief Stellaris LM3S Ethernet driver
79  **/
80 
82 {
84  ETH_MTU,
92  NULL,
93  NULL,
94  NULL,
95  TRUE,
96  TRUE,
97  TRUE,
98  FALSE
99 };
100 
101 
102 /**
103  * @brief Stellaris LM3S Ethernet controller initialization
104  * @param[in] interface Underlying network interface
105  * @return Error code
106  **/
107 
109 {
110  uint_t div;
111 
112  //Debug message
113  TRACE_INFO("Initializing Stellaris LM3S Ethernet controller...\r\n");
114 
115  //Save underlying network interface
116  nicDriverInterface = interface;
117 
118  //Enable Ethernet controller clock
119  SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
120  //Reset Ethernet controller
121  SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);
122 
123  //GPIO configuration
124  lm3sEthInitGpio(interface);
125 
126  //The MDC clock frequency cannot exceed 2.5MHz
127  div = SysCtlClockGet() / (2 * 2500000) - 1;
128  //Adjust MDC clock frequency
129  MAC_MDV_R = div & MAC_MDV_DIV_M;
130 
131  //Reset PHY transceiver
132  lm3sEthWritePhyReg(PHY_MR0, PHY_MR0_RESET);
133  //Wait for the reset to complete
134  while(lm3sEthReadPhyReg(PHY_MR0) & PHY_MR0_RESET)
135  {
136  }
137 
138  //Dump PHY registers for debugging purpose
140 
141  //Configure LED0 and LED1
142  lm3sEthWritePhyReg(PHY_MR23, PHY_MR23_LED0_RXTX | PHY_MR23_LED1_LINK);
143 
144  //Set the MAC address of the station
145  MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
146  MAC_IA1_R = interface->macAddr.w[2];
147 
148  //Enable automatic CRC generation and packet padding
149  MAC_TCTL_R = MAC_TCTL_DUPLEX | MAC_TCTL_CRC | MAC_TCTL_PADEN;
150  //Flush the receive FIFO and enable CRC verification
151  MAC_RCTL_R = MAC_RCTL_RSTFIFO | MAC_RCTL_BADCRC;
152 
153  //Configure Ethernet interrupts
154  MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM;
155  //Configure PHY interrupts
156  lm3sEthWritePhyReg(PHY_MR17, PHY_MR17_LSCHG_IE);
157 
158  //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
159  IntPriorityGroupingSet(LM3S_ETH_IRQ_PRIORITY_GROUPING);
160  //Configure Ethernet interrupt priority
161  IntPrioritySet(INT_ETH, LM3S_ETH_IRQ_PRIORITY);
162 
163  //Enable transmitter
164  MAC_TCTL_R |= MAC_TCTL_TXEN;
165  //Enable receiver
166  MAC_RCTL_R |= MAC_RCTL_RXEN;
167 
168  //Accept any packets from the upper layer
169  osSetEvent(&interface->nicTxEvent);
170 
171  //Successful initialization
172  return NO_ERROR;
173 }
174 
175 
176 //EK-LM3S6965 evaluation board?
177 #if defined(USE_EK_LM3S6965)
178 
179 /**
180  * @brief GPIO configuration
181  * @param[in] interface Underlying network interface
182  **/
183 
184 void lm3sEthInitGpio(NetInterface *interface)
185 {
186  //Enable GPIO clock
187  SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
188 
189  //Configure status LEDs
190  GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3);
191 }
192 
193 #endif
194 
195 
196 /**
197  * @brief Stellaris LM3S Ethernet timer handler
198  *
199  * This routine is periodically called by the TCP/IP stack to handle periodic
200  * operations such as polling the link state
201  *
202  * @param[in] interface Underlying network interface
203  **/
204 
205 void lm3sEthTick(NetInterface *interface)
206 {
207 }
208 
209 
210 /**
211  * @brief Enable interrupts
212  * @param[in] interface Underlying network interface
213  **/
214 
216 {
217  //Enable Ethernet interrupts
218  IntEnable(INT_ETH);
219 }
220 
221 
222 /**
223  * @brief Disable interrupts
224  * @param[in] interface Underlying network interface
225  **/
226 
228 {
229  //Disable Ethernet interrupts
230  IntDisable(INT_ETH);
231 }
232 
233 
234 /**
235  * @brief Stellaris LM3S Ethernet interrupt service routine
236  **/
237 
238 void ETH_IRQHandler(void)
239 {
240  bool_t flag;
241  uint32_t status;
242 
243  //Interrupt service routine prologue
244  osEnterIsr();
245 
246  //This flag will be set if a higher priority task must be woken
247  flag = FALSE;
248 
249  //Read interrupt status register
250  status = MAC_RIS_R;
251 
252  //PHY interrupt?
253  if((status & MAC_RIS_PHYINT) != 0)
254  {
255  //Disable PHYINT interrupt
256  MAC_IM_R &= ~MAC_IM_PHYINTM;
257 
258  //Set event flag
259  nicDriverInterface->nicEvent = TRUE;
260  //Notify the TCP/IP stack of the event
261  flag |= osSetEventFromIsr(&netEvent);
262  }
263 
264  //Transmit FIFO empty?
265  if((status & MAC_RIS_TXEMP) != 0)
266  {
267  //Acknowledge TXEMP interrupt
268  MAC_IACK_R = MAC_IACK_TXEMP;
269 
270  //Check whether the transmit FIFO is available for writing
271  if((MAC_TR_R & MAC_TR_NEWTX) == 0)
272  {
273  //Notify the TCP/IP stack that the transmitter is ready to send
274  flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
275  }
276  }
277 
278  //Packet received?
279  if((status & MAC_RIS_RXINT) != 0)
280  {
281  //Disable RXINT interrupt
282  MAC_IM_R &= ~MAC_IM_RXINTM;
283 
284  //Set event flag
285  nicDriverInterface->nicEvent = TRUE;
286  //Notify the TCP/IP stack of the event
287  flag |= osSetEventFromIsr(&netEvent);
288  }
289 
290  //Interrupt service routine epilogue
291  osExitIsr(flag);
292 }
293 
294 
295 /**
296  * @brief Stellaris LM3S Ethernet event handler
297  * @param[in] interface Underlying network interface
298  **/
299 
301 {
302  uint32_t status;
303  uint16_t value;
304 
305  //Read interrupt status register
306  status = MAC_RIS_R;
307 
308  //PHY interrupt?
309  if((status & MAC_RIS_PHYINT) != 0)
310  {
311  //Acknowledge PHYINT interrupt
312  MAC_IACK_R = MAC_IACK_PHYINT;
313  //Read PHY interrupt status register
314  value = lm3sEthReadPhyReg(PHY_MR17);
315 
316  //Check whether the link state has changed
317  if((value & PHY_MR17_LSCHG_IE) != 0)
318  {
319  //Read PHY status register
320  value = lm3sEthReadPhyReg(PHY_MR1);
321 
322  //Check link state
323  if((value & PHY_MR1_LINK) != 0)
324  {
325  //Read PHY diagnostic register
326  value = lm3sEthReadPhyReg(PHY_MR18);
327 
328  //Get current speed
329  if((value & PHY_MR18_RATE) != 0)
330  {
331  //100BASE-TX operation
332  interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
333  }
334  else
335  {
336  //10BASE-T operation
337  interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
338  }
339 
340  //Get current duplex mode
341  if((value & PHY_MR18_DPLX) != 0)
342  {
343  //Full-Duplex mode
344  interface->duplexMode = NIC_FULL_DUPLEX_MODE;
345  //Update MAC configuration
346  MAC_TCTL_R |= MAC_TCTL_DUPLEX;
347  }
348  else
349  {
350  //Half-Duplex mode
351  interface->duplexMode = NIC_HALF_DUPLEX_MODE;
352  //Update MAC configuration
353  MAC_TCTL_R &= ~MAC_TCTL_DUPLEX;
354  }
355 
356  //Update link state
357  interface->linkState = TRUE;
358  }
359  else
360  {
361  //Update link state
362  interface->linkState = FALSE;
363  }
364 
365  //Process link state change event
366  nicNotifyLinkChange(interface);
367  }
368  }
369 
370  //Packet received?
371  if((status & MAC_RIS_RXINT) != 0)
372  {
373  //Acknowledge RXINT interrupt
374  MAC_IACK_R = MAC_IACK_RXINT;
375 
376  //Process all the pending packets
377  while((MAC_NP_R & MAC_NP_NPR_M) != 0)
378  {
379  //Read incoming packet
380  lm3sEthReceivePacket(interface);
381  }
382  }
383 
384  //Re-enable Ethernet interrupts
385  MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM;
386 }
387 
388 
389 /**
390  * @brief Send a packet
391  * @param[in] interface Underlying network interface
392  * @param[in] buffer Multi-part buffer containing the data to send
393  * @param[in] offset Offset to the first data byte
394  * @param[in] ancillary Additional options passed to the stack along with
395  * the packet
396  * @return Error code
397  **/
398 
400  const NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
401 {
402  size_t i;
403  size_t length;
404  uint32_t *p;
405 
406  //Retrieve the length of the packet
407  length = netBufferGetLength(buffer) - offset;
408 
409  //Check the frame length
410  if(length < sizeof(EthHeader) || length > ETH_MAX_FRAME_SIZE)
411  {
412  //The transmitter can accept another packet
413  osSetEvent(&interface->nicTxEvent);
414  //Report an error
415  return ERROR_INVALID_LENGTH;
416  }
417 
418  //Make sure the transmit FIFO is available for writing
419  if((MAC_TR_R & MAC_TR_NEWTX) != 0)
420  {
421  return ERROR_FAILURE;
422  }
423 
424  //Copy user data
425  netBufferRead(txBuffer + 2, buffer, offset, length);
426 
427  //The packet is preceded by a 16-bit length field
428  txBuffer[0] = LSB(length - sizeof(EthHeader));
429  txBuffer[1] = MSB(length - sizeof(EthHeader));
430 
431  //Point to the beginning of the packet
432  p = (uint32_t *) txBuffer;
433  //Compute the length of the packet in 32-bit words
434  length = (length + 5) / 4;
435 
436  //Copy packet to transmit FIFO
437  for(i = 0; i < length; i++)
438  {
439  MAC_DATA_R = p[i];
440  }
441 
442  //Start transmitting
443  MAC_TR_R = MAC_TR_NEWTX;
444 
445  //Data successfully written
446  return NO_ERROR;
447 }
448 
449 
450 /**
451  * @brief Receive a packet
452  * @return Error code
453  **/
454 
456 {
457  error_t error;
458  size_t i;
459  size_t n;
460  size_t length;
461  uint32_t data;
462  uint16_t *p;
463 
464  //Make sure the FIFO is not empty
465  if((MAC_NP_R & MAC_NP_NPR_M) != 0)
466  {
467  //Read the first word
468  data = MAC_DATA_R;
469  //Retrieve the total length of the packet
470  length = data & 0xFFFF;
471 
472  //Make sure the length field is valid
473  if(length > 2)
474  {
475  //Point to the beginning of the buffer
476  p = (uint16_t *) rxBuffer;
477 
478  //Retrieve the length of the frame
479  length -= 2;
480  //Limit the number of data to be read
482 
483  //Copy the first half word
484  if(n > 0)
485  {
486  *(p++) = (uint16_t) (data >> 16);
487  }
488 
489  //Copy data from receive FIFO
490  for(i = 2; i < n; i += 4)
491  {
492  //Read a 32-bit word from the FIFO
493  data = MAC_DATA_R;
494  //Write the 32-bit to the receive buffer
495  *(p++) = (uint16_t) data;
496  *(p++) = (uint16_t) (data >> 16);
497  }
498 
499  //Skip the remaining bytes
500  while(i < length)
501  {
502  //Read a 32-bit word from the FIFO
503  data = MAC_DATA_R;
504  //Increment byte counter
505  i += 4;
506  }
507 
508  //Valid packet received
509  error = NO_ERROR;
510  }
511  else
512  {
513  //Disable receiver
514  MAC_RCTL_R &= ~MAC_RCTL_RXEN;
515  //Flush the receive FIFO
516  MAC_RCTL_R |= MAC_RCTL_RSTFIFO;
517  //Re-enable receiver
518  MAC_RCTL_R |= MAC_RCTL_RXEN;
519 
520  //The packet is not valid
521  error = ERROR_INVALID_PACKET;
522  }
523  }
524  else
525  {
526  //No more data in the receive buffer
527  error = ERROR_BUFFER_EMPTY;
528  }
529 
530  //Check whether a valid packet has been received
531  if(!error)
532  {
533  NetRxAncillary ancillary;
534 
535  //Additional options can be passed to the stack along with the packet
536  ancillary = NET_DEFAULT_RX_ANCILLARY;
537 
538  //Pass the packet to the upper layer
539  nicProcessPacket(interface, rxBuffer, n, &ancillary);
540  }
541 
542  //Return status code
543  return error;
544 }
545 
546 
547 /**
548  * @brief Configure MAC address filtering
549  * @param[in] interface Underlying network interface
550  * @return Error code
551  **/
552 
554 {
555  uint_t i;
556  bool_t acceptMulticast;
557 
558  //Debug message
559  TRACE_DEBUG("Updating MAC filter...\r\n");
560 
561  //Set the MAC address of the station
562  MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
563  MAC_IA1_R = interface->macAddr.w[2];
564 
565  //This flag will be set if multicast addresses should be accepted
566  acceptMulticast = FALSE;
567 
568  //The MAC address filter contains the list of MAC addresses to accept
569  //when receiving an Ethernet frame
570  for(i = 0; i < MAC_ADDR_FILTER_SIZE; i++)
571  {
572  //Valid entry?
573  if(interface->macAddrFilter[i].refCount > 0)
574  {
575  //Accept multicast addresses
576  acceptMulticast = TRUE;
577  //We are done
578  break;
579  }
580  }
581 
582  //Enable the reception of multicast frames if necessary
583  if(acceptMulticast)
584  {
585  MAC_RCTL_R |= MAC_RCTL_AMUL;
586  }
587  else
588  {
589  MAC_RCTL_R &= ~MAC_RCTL_AMUL;
590  }
591 
592  //Successful processing
593  return NO_ERROR;
594 }
595 
596 
597 /**
598  * @brief Write PHY register
599  * @param[in] address PHY register address
600  * @param[in] data Register value
601  **/
602 
603 void lm3sEthWritePhyReg(uint8_t address, uint16_t data)
604 {
605  //Data to be written in the PHY register
606  MAC_MTXD_R = data & MAC_MTXD_MDTX_M;
607  //Start a write operation
608  MAC_MCTL_R = (address << 3) | MAC_MCTL_WRITE | MAC_MCTL_START;
609 
610  //Wait for the write to complete
611  while((MAC_MCTL_R & MAC_MCTL_START) != 0)
612  {
613  }
614 }
615 
616 
617 /**
618  * @brief Read PHY register
619  * @param[in] address PHY register address
620  * @return Register value
621  **/
622 
623 uint16_t lm3sEthReadPhyReg(uint8_t address)
624 {
625  //Start a read operation
626  MAC_MCTL_R = (address << 3) | MAC_MCTL_START;
627 
628  //Wait for the read to complete
629  while((MAC_MCTL_R & MAC_MCTL_START) != 0)
630  {
631  }
632 
633  //Return PHY register contents
634  return MAC_MRXD_R & MAC_MRXD_MDRX_M;
635 }
636 
637 
638 /**
639  * @brief Dump PHY registers for debugging purpose
640  **/
641 
643 {
644  uint8_t i;
645 
646  //Loop through PHY registers
647  for(i = 0; i < 32; i++)
648  {
649  //Display current PHY register
650  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
651  lm3sEthReadPhyReg(i));
652  }
653 
654  //Terminate with a line feed
655  TRACE_DEBUG("\r\n");
656 }
bool_t osSetEventFromIsr(OsEvent *event)
Set an event object to the signaled state from an interrupt service routine.
void nicNotifyLinkChange(NetInterface *interface)
Process link state change notification.
Definition: nic.c:532
uint8_t length
Definition: coap_common.h:190
int bool_t
Definition: compiler_port.h:49
void lm3sEthDumpPhyReg(void)
Dump PHY registers for debugging purpose.
#define netEvent
Definition: net_legacy.h:267
uint8_t data[]
Definition: ethernet.h:209
#define MAC_TR_R
size_t netBufferRead(void *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Read data from a multi-part buffer.
Definition: net_mem.c:672
#define MAC_RCTL_R
uint8_t p
Definition: ndp.h:298
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
#define MAC_ADDR_FILTER_SIZE
Definition: ethernet.h:88
#define TRUE
Definition: os_port.h:50
#define ETH_MAX_FRAME_SIZE
Definition: ethernet.h:103
#define LM3S_ETH_IRQ_PRIORITY
#define MAC_IM_R
void lm3sEthWritePhyReg(uint8_t address, uint16_t data)
Write PHY register.
void nicProcessPacket(NetInterface *interface, uint8_t *packet, size_t length, NetRxAncillary *ancillary)
Handle a packet received by the network controller.
Definition: nic.c:388
#define osExitIsr(flag)
error_t lm3sEthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send a packet.
const NicDriver lm3sEthDriver
Stellaris LM3S Ethernet driver.
void lm3sEthEventHandler(NetInterface *interface)
Stellaris LM3S Ethernet event handler.
#define MAC_NP_R
#define FALSE
Definition: os_port.h:46
#define MAC_MTXD_R
error_t
Error codes.
Definition: error.h:42
void lm3sEthEnableIrq(NetInterface *interface)
Enable interrupts.
#define MAC_RIS_R
error_t lm3sEthReceivePacket(NetInterface *interface)
Receive a packet.
uint8_t value[]
Definition: tcp.h:332
const NetRxAncillary NET_DEFAULT_RX_ANCILLARY
Definition: net_misc.c:96
Generic error code.
Definition: error.h:45
#define txBuffer
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
#define NetTxAncillary
Definition: net_misc.h:36
void lm3sEthInitGpio(NetInterface *interface)
#define MSB(x)
Definition: os_port.h:58
#define TRACE_INFO(...)
Definition: debug.h:95
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
#define LSB(x)
Definition: os_port.h:54
#define MIN(a, b)
Definition: os_port.h:62
#define rxBuffer
#define MAC_DATA_R
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define MAC_IACK_R
Luminary Stellaris LM3S Ethernet controller.
__start_packed struct @7 EthHeader
Ethernet frame header.
void lm3sEthDisableIrq(NetInterface *interface)
Disable interrupts.
#define ETH_MTU
Definition: ethernet.h:105
void ETH_IRQHandler(void)
Stellaris LM3S Ethernet interrupt service routine.
uint8_t n
#define MAC_IA1_R
error_t lm3sEthInit(NetInterface *interface)
Stellaris LM3S Ethernet controller initialization.
#define osEnterIsr()
#define MAC_MDV_R
void lm3sEthTick(NetInterface *interface)
Stellaris LM3S Ethernet timer handler.
Ipv6Addr address
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
uint16_t lm3sEthReadPhyReg(uint8_t address)
Read PHY register.
#define MAC_MCTL_R
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
#define MAC_MRXD_R
NIC driver.
Definition: nic.h:257
#define MAC_TCTL_R
#define MAC_IA0_R
error_t lm3sEthUpdateMacAddrFilter(NetInterface *interface)
Configure MAC address filtering.
Success.
Definition: error.h:44
__attribute__((naked))
AVR32 Ethernet MAC interrupt wrapper.
Debugging facilities.
#define LM3S_ETH_IRQ_PRIORITY_GROUPING
Ethernet interface.
Definition: nic.h:82