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-2019 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.6
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
200  * handle periodic 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)
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)
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))
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)
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)
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)
318  {
319  //Read PHY status register
320  value = lm3sEthReadPhyReg(PHY_MR1);
321 
322  //Check link state
323  if(value & PHY_MR1_LINK)
324  {
325  //Read PHY diagnostic register
326  value = lm3sEthReadPhyReg(PHY_MR18);
327 
328  //Get current speed
329  if(value & PHY_MR18_RATE)
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)
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)
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)
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  * @return Error code
395  **/
396 
398  const NetBuffer *buffer, size_t offset)
399 {
400  size_t i;
401  size_t length;
402  uint32_t *p;
403 
404  //Retrieve the length of the packet
405  length = netBufferGetLength(buffer) - offset;
406 
407  //Check the frame length
408  if(length < sizeof(EthHeader) || length > ETH_MAX_FRAME_SIZE)
409  {
410  //The transmitter can accept another packet
411  osSetEvent(&interface->nicTxEvent);
412  //Report an error
413  return ERROR_INVALID_LENGTH;
414  }
415 
416  //Make sure the transmit FIFO is available for writing
417  if(MAC_TR_R & MAC_TR_NEWTX)
418  return ERROR_FAILURE;
419 
420  //Copy user data
421  netBufferRead(txBuffer + 2, buffer, offset, length);
422 
423  //The packet is preceded by a 16-bit length field
424  txBuffer[0] = LSB(length - sizeof(EthHeader));
425  txBuffer[1] = MSB(length - sizeof(EthHeader));
426 
427  //Point to the beginning of the packet
428  p = (uint32_t *) txBuffer;
429  //Compute the length of the packet in 32-bit words
430  length = (length + 5) / 4;
431 
432  //Copy packet to transmit FIFO
433  for(i = 0; i < length; i++)
434  MAC_DATA_R = p[i];
435 
436  //Start transmitting
437  MAC_TR_R = MAC_TR_NEWTX;
438 
439  //Data successfully written
440  return NO_ERROR;
441 }
442 
443 
444 /**
445  * @brief Receive a packet
446  * @return Error code
447  **/
448 
450 {
451  error_t error;
452  size_t i;
453  size_t n;
454  size_t length;
455  uint32_t data;
456  uint16_t *p;
457 
458  //Make sure the FIFO is not empty
459  if(MAC_NP_R & MAC_NP_NPR_M)
460  {
461  //Read the first word
462  data = MAC_DATA_R;
463  //Retrieve the total length of the packet
464  length = data & 0xFFFF;
465 
466  //Make sure the length field is valid
467  if(length > 2)
468  {
469  //Point to the beginning of the buffer
470  p = (uint16_t *) rxBuffer;
471 
472  //Retrieve the length of the frame
473  length -= 2;
474  //Limit the number of data to be read
476 
477  //Copy the first half word
478  if(n > 0)
479  *(p++) = (uint16_t) (data >> 16);
480 
481  //Copy data from receive FIFO
482  for(i = 2; i < n; i += 4)
483  {
484  //Read a 32-bit word from the FIFO
485  data = MAC_DATA_R;
486  //Write the 32-bit to the receive buffer
487  *(p++) = (uint16_t) data;
488  *(p++) = (uint16_t) (data >> 16);
489  }
490 
491  //Skip the remaining bytes
492  while(i < length)
493  {
494  //Read a 32-bit word from the FIFO
495  data = MAC_DATA_R;
496  //Increment byte counter
497  i += 4;
498  }
499 
500  //Valid packet received
501  error = NO_ERROR;
502  }
503  else
504  {
505  //Disable receiver
506  MAC_RCTL_R &= ~MAC_RCTL_RXEN;
507  //Flush the receive FIFO
508  MAC_RCTL_R |= MAC_RCTL_RSTFIFO;
509  //Re-enable receiver
510  MAC_RCTL_R |= MAC_RCTL_RXEN;
511 
512  //The packet is not valid
513  error = ERROR_INVALID_PACKET;
514  }
515  }
516  else
517  {
518  //No more data in the receive buffer
519  error = ERROR_BUFFER_EMPTY;
520  }
521 
522  //Check whether a valid packet has been received
523  if(!error)
524  {
525  //Pass the packet to the upper layer
526  nicProcessPacket(interface, rxBuffer, n);
527  }
528 
529  //Return status code
530  return error;
531 }
532 
533 
534 /**
535  * @brief Configure MAC address filtering
536  * @param[in] interface Underlying network interface
537  * @return Error code
538  **/
539 
541 {
542  uint_t i;
543  bool_t acceptMulticast;
544 
545  //Debug message
546  TRACE_DEBUG("Updating MAC filter...\r\n");
547 
548  //Set the MAC address of the station
549  MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
550  MAC_IA1_R = interface->macAddr.w[2];
551 
552  //This flag will be set if multicast addresses should be accepted
553  acceptMulticast = FALSE;
554 
555  //The MAC address filter contains the list of MAC addresses to accept
556  //when receiving an Ethernet frame
557  for(i = 0; i < MAC_ADDR_FILTER_SIZE; i++)
558  {
559  //Valid entry?
560  if(interface->macAddrFilter[i].refCount > 0)
561  {
562  //Accept multicast addresses
563  acceptMulticast = TRUE;
564  //We are done
565  break;
566  }
567  }
568 
569  //Enable the reception of multicast frames if necessary
570  if(acceptMulticast)
571  MAC_RCTL_R |= MAC_RCTL_AMUL;
572  else
573  MAC_RCTL_R &= ~MAC_RCTL_AMUL;
574 
575  //Successful processing
576  return NO_ERROR;
577 }
578 
579 
580 /**
581  * @brief Write PHY register
582  * @param[in] address PHY register address
583  * @param[in] data Register value
584  **/
585 
586 void lm3sEthWritePhyReg(uint8_t address, uint16_t data)
587 {
588  //Data to be written in the PHY register
589  MAC_MTXD_R = data & MAC_MTXD_MDTX_M;
590  //Start a write operation
591  MAC_MCTL_R = (address << 3) | MAC_MCTL_WRITE | MAC_MCTL_START;
592 
593  //Wait for the write to complete
594  while(MAC_MCTL_R & MAC_MCTL_START)
595  {
596  }
597 }
598 
599 
600 /**
601  * @brief Read PHY register
602  * @param[in] address PHY register address
603  * @return Register value
604  **/
605 
606 uint16_t lm3sEthReadPhyReg(uint8_t address)
607 {
608  //Start a read operation
609  MAC_MCTL_R = (address << 3) | MAC_MCTL_START;
610 
611  //Wait for the read to complete
612  while(MAC_MCTL_R & MAC_MCTL_START)
613  {
614  }
615 
616  //Return PHY register contents
617  return MAC_MRXD_R & MAC_MRXD_MDRX_M;
618 }
619 
620 
621 /**
622  * @brief Dump PHY registers for debugging purpose
623  **/
624 
626 {
627  uint8_t i;
628 
629  //Loop through PHY registers
630  for(i = 0; i < 32; i++)
631  {
632  //Display current PHY register
633  TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
634  lm3sEthReadPhyReg(i));
635  }
636 
637  //Terminate with a line feed
638  TRACE_DEBUG("\r\n");
639 }
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:525
uint8_t length
Definition: dtls_misc.h:149
int bool_t
Definition: compiler_port.h:49
void lm3sEthDumpPhyReg(void)
Dump PHY registers for debugging purpose.
@ NIC_FULL_DUPLEX_MODE
Definition: nic.h:119
#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
void nicProcessPacket(NetInterface *interface, uint8_t *packet, size_t length)
Handle a packet received by the network controller.
Definition: nic.c:383
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
#define MAC_ADDR_FILTER_SIZE
Definition: ethernet.h:74
#define TRUE
Definition: os_port.h:50
#define ETH_MAX_FRAME_SIZE
Definition: ethernet.h:89
#define LM3S_ETH_IRQ_PRIORITY
#define MAC_IM_R
void lm3sEthWritePhyReg(uint8_t address, uint16_t data)
Write PHY register.
#define osExitIsr(flag)
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.
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define txBuffer
@ ERROR_INVALID_PACKET
Definition: error.h:138
#define NetInterface
Definition: net.h:36
@ NIC_LINK_SPEED_10MBPS
Definition: nic.h:105
@ ERROR_INVALID_LENGTH
Definition: error.h:109
@ ERROR_BUFFER_EMPTY
Definition: error.h:139
OsEvent netEvent
Definition: net.c:77
void lm3sEthInitGpio(NetInterface *interface)
#define MSB(x)
Definition: os_port.h:58
#define TRACE_INFO(...)
Definition: debug.h:94
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:106
#define MAC_IACK_R
Luminary Stellaris LM3S Ethernet controller.
void lm3sEthDisableIrq(NetInterface *interface)
Disable interrupts.
#define ETH_MTU
Definition: ethernet.h:91
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()
@ NIC_HALF_DUPLEX_MODE
Definition: nic.h:118
#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.
@ NIC_LINK_SPEED_100MBPS
Definition: nic.h:106
uint16_t lm3sEthReadPhyReg(uint8_t address)
Read PHY register.
uint8_t value[]
Definition: dtls_misc.h:150
#define MAC_MCTL_R
unsigned int uint_t
Definition: compiler_port.h:45
__start_packed struct @110 EthHeader
Ethernet frame header.
TCP/IP stack core.
uint8_t data[]
Definition: dtls_misc.h:176
#define MAC_MRXD_R
NIC driver.
Definition: nic.h:179
error_t lm3sEthSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset)
Send a packet.
#define MAC_TCTL_R
#define MAC_IA0_R
error_t lm3sEthUpdateMacAddrFilter(NetInterface *interface)
Configure MAC address filtering.
@ NO_ERROR
Success.
Definition: error.h:44
__attribute__((naked))
AVR32 Ethernet MAC interrupt wrapper.
Debugging facilities.
#define LM3S_ETH_IRQ_PRIORITY_GROUPING
@ NIC_TYPE_ETHERNET
Ethernet interface.
Definition: nic.h:79