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