nuttx/arch/arm/src/lpc43xx/lpc43_ethernet.c

3925 lines
110 KiB
C
Raw Normal View History

/****************************************************************************
* arch/arm/src/lpc43xx/lpc43_ethernet.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_LPC43_ETHERNET)
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <queue.h>
#include <errno.h>
#include <arpa/inet.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/wdog.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/mii.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/netdev.h>
#if defined(CONFIG_NET_PKT)
# include <nuttx/net/pkt.h>
#endif
#include "arm_internal.h"
#include "chip.h"
#include "lpc43_pinconfig.h"
#include "lpc43_ethernet.h"
#include "hardware/lpc43_creg.h"
#include "hardware/lpc43_cgu.h"
#include "hardware/lpc43_ccu.h"
#include "lpc43_rgu.h"
#include "lpc43_gpio.h"
#include "arm_arch.h"
#include <arch/board/board.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* If processing is not done at the interrupt level, then work queue support
* is required.
*/
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required
#endif
/* The low priority work queue is preferred. If it is not enabled, LPWORK
* will be the same as HPWORK.
*
* NOTE: However, the network should NEVER run on the high priority work
* queue! That queue is intended only to service short back end interrupt
* processing that never suspends. Suspending the high priority work queue
* may bring the system to its knees!
*/
#define ETHWORK LPWORK
#ifndef CONFIG_LPC43_PHYADDR
# error "CONFIG_LPC43_PHYADDR must be defined in the NuttX configuration"
#endif
#if !defined(CONFIG_LPC43_MII) && !defined(CONFIG_LPC43_RMII)
# warning "Neither CONFIG_LPC43_MII nor CONFIG_LPC43_RMII defined"
#endif
#if defined(CONFIG_LPC43_MII) && defined(CONFIG_LPC43_RMII)
# error "Both CONFIG_LPC43_MII and CONFIG_LPC43_RMII defined"
#endif
#ifdef CONFIG_LPC43_AUTONEG
# ifndef CONFIG_LPC43_PHYSR
# error "CONFIG_LPC43_PHYSR must be defined in the NuttX configuration"
# endif
# ifdef CONFIG_LPC43_PHYSR_ALTCONFIG
# ifndef CONFIG_LPC43_PHYSR_ALTMODE
# error "CONFIG_LPC43_PHYSR_ALTMODE must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_10HD
# error "CONFIG_LPC43_PHYSR_10HD must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_100HD
# error "CONFIG_LPC43_PHYSR_100HD must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_10FD
# error "CONFIG_LPC43_PHYSR_10FD must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_100FD
# error "CONFIG_LPC43_PHYSR_100FD must be defined in the NuttX configuration"
# endif
# else
# ifndef CONFIG_LPC43_PHYSR_SPEED
# error "CONFIG_LPC43_PHYSR_SPEED must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_100MBPS
# error "CONFIG_LPC43_PHYSR_100MBPS must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_MODE
# error "CONFIG_LPC43_PHYSR_MODE must be defined in the NuttX configuration"
# endif
# ifndef CONFIG_LPC43_PHYSR_FULLDUPLEX
# error "CONFIG_LPC43_PHYSR_FULLDUPLEX must be defined in the NuttX configuration"
# endif
# endif
#endif
#ifdef CONFIG_LPC43_ETH_PTP
# warning "CONFIG_LPC43_ETH_PTP is not yet supported"
#endif
/* This driver does not use enhanced descriptors. Enhanced descriptors must
* be used, however, if time stamping or and/or IPv4 checksum offload is
* supported.
*/
#undef CONFIG_LPC43_ETH_ENHANCEDDESC
#undef CONFIG_LPC43_ETH_HWCHECKSUM
/* Add 4 to the configured buffer size to account for the 2 byte checksum
* memory needed at the end of the maximum size packet. Buffer sizes must
* be an even multiple of 4, 8, or 16 bytes (depending on buswidth). We
* will use the 16-byte alignment in all cases.
*/
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 14:10:40 -06:00
#define OPTIMAL_ETH_BUFSIZE ((CONFIG_NET_ETH_PKTSIZE + 4 + 15) & ~15)
#ifndef CONFIG_LPC43_ETH_BUFSIZE
# define CONFIG_LPC43_ETH_BUFSIZE OPTIMAL_ETH_BUFSIZE
#endif
#if CONFIG_LPC43_ETH_BUFSIZE > ETH_TDES1_TBS1_MASK
# error "CONFIG_LPC43_ETH_BUFSIZE is too large"
#endif
#if (CONFIG_LPC43_ETH_BUFSIZE & 15) != 0
# error "CONFIG_LPC43_ETH_BUFSIZE must be aligned"
#endif
#if CONFIG_LPC43_ETH_BUFSIZE != OPTIMAL_ETH_BUFSIZE
# warning "You using an incomplete/untested configuration"
#endif
#ifndef CONFIG_LPC43_ETH_NRXDESC
# define CONFIG_LPC43_ETH_NRXDESC 8
#endif
#ifndef CONFIG_LPC43_ETH_NTXDESC
# define CONFIG_LPC43_ETH_NTXDESC 4
#endif
/* We need at least one more free buffer than transmit buffers */
#define LPC43_ETH_NFREEBUFFERS (CONFIG_LPC43_ETH_NTXDESC+1)
/* Extremely detailed register debug that you would normally never want
* enabled.
*/
#ifndef CONFIG_DEBUG_NET_INFO
# undef CONFIG_LPC43_ETHMAC_REGDEBUG
#endif
/* Clocking *****************************************************************/
/* Set MACMIIAR CR bits depending on HCLK setting */
#if BOARD_FCLKOUT_FREQUENCY >= 20000000 && BOARD_FCLKOUT_FREQUENCY < 35000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_20_35
#elif BOARD_FCLKOUT_FREQUENCY >= 35000000 && BOARD_FCLKOUT_FREQUENCY < 60000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_35_60
#elif BOARD_FCLKOUT_FREQUENCY >= 60000000 && BOARD_FCLKOUT_FREQUENCY < 100000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_60_100
#elif BOARD_FCLKOUT_FREQUENCY >= 100000000 && BOARD_FCLKOUT_FREQUENCY < 150000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_100_150
#elif BOARD_FCLKOUT_FREQUENCY >= 150000000 && BOARD_FCLKOUT_FREQUENCY <= 250000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_150_250
#elif BOARD_FCLKOUT_FREQUENCY >= 250000000 && BOARD_FCLKOUT_FREQUENCY <= 350000000
# define ETH_MACMIIA_CR ETH_MACMIIA_CR_250_300
#else
# error "BOARD_FCLKOUT_FREQUENCY not supportable"
#endif
/* Timing *******************************************************************/
/* TX poll delay = 1 seconds.
* CLK_TCK is the number of clock ticks per second
*/
#define LPC43_WDDELAY (1*CLK_TCK)
/* TX timeout = 1 minute */
#define LPC43_TXTIMEOUT (60*CLK_TCK)
/* PHY reset/configuration delays in milliseconds */
#define PHY_RESET_DELAY (65)
#define PHY_CONFIG_DELAY (1000)
/* PHY read/write delays in loop counts */
#define PHY_READ_TIMEOUT (0x0004ffff)
#define PHY_WRITE_TIMEOUT (0x0004ffff)
#define PHY_RETRY_TIMEOUT (0x0004ffff)
/* Register values **********************************************************/
/* Clear the MACCR bits that will be setup during MAC initialization (or that
* are cleared unconditionally). Per the reference manual, all reserved bits
* must be retained at their reset value.
*
* ETH_MACCFG_RE Bit 2: Receiver enable
* ETH_MACCFG_TE Bit 3: Transmitter enable
* ETH_MACCFG_DC Bit 4: Deferral check
* ETH_MACCFG_BL Bits 5-6: Back-off limit
* ETH_MACCFG_APCS Bit 7: Automatic pad/CRC stripping
* ETH_MACCFG_RD Bit 9: Retry disable
* ETH_MACCFG_DM Bit 11: Duplex mode
* ETH_MACCFG_LM Bit 12: Loopback mode
* ETH_MACCFG_DO Bit 13: Receive own disable
* ETH_MACCFG_FES Bit 14: Fast Ethernet speed
* ETH_MACCFG_CSD Bit 16: Carrier sense disable
* ETH_MACCFG_IFG Bits 17-19: Interframe gap
* ETH_MACCFG_JD Bit 22: Jabber disable
* ETH_MACCFG_WD Bit 23: Watchdog disable
* ETH_MACCFG_CSTF Bits 25: CRC stripping for Type frames (F2/F4 only)
*/
#define MACCR_CLEAR_BITS \
(ETH_MACCFG_RE | ETH_MACCFG_TE | ETH_MACCFG_DF | ETH_MACCFG_BL_MASK | \
ETH_MACCFG_ACS | ETH_MACCFG_RD | ETH_MACCFG_DM | \
ETH_MACCFG_LM | ETH_MACCFG_DO | ETH_MACCFG_FES | ETH_MACCFG_DCRS | \
ETH_MACCFG_JE | ETH_MACCFG_IFG_MASK | ETH_MACCFG_JD | ETH_MACCFG_WD)
/* The following bits are set or left zero unconditionally in all modes.
*
* ETH_MACCFG_RE Receiver enable 0 (disabled)
* ETH_MACCFG_TE Transmitter enable 0 (disabled)
* ETH_MACCFG_DC Deferral check 0 (disabled)
* ETH_MACCFG_BL Back-off limit 0 (10)
* ETH_MACCFG_APCS Automatic pad/CRC stripping 0 (disabled)
* ETH_MACCFG_RD Retry disable 1 (disabled)
* ETH_MACCFG_LM Loopback mode 0 (disabled)
* ETH_MACCFG_ROD Receive own disable 0 (enabled)
* ETH_MACCFG_CSD Carrier sense disable 0 (enabled)
* ETH_MACCFG_IFG Interframe gap 0 (96 bits)
* ETH_MACCFG_JD Jabber disable 0 (enabled)
* ETH_MACCFG_WD Watchdog disable 0 (enabled)
* ETH_MACCFG_CSTF CRC stripping for Type frames 0 (disabled, F2/F4 only)
*
* The following are set conditioinally based on mode and speed.
*
* ETH_MACCFG_DM Duplex mode Depends on priv->fduplex
* ETH_MACCFG_FES Fast Ethernet speed Depends on priv->mbps100
*/
# define MACCR_SET_BITS \
(ETH_MACCFG_BL_10 | ETH_MACCFG_RD | ETH_MACCFG_IFG(96))
/* Clear the MACCR bits that will be setup during MAC initialization (or that
* are cleared unconditionally). Per the reference manual, all reserved bits
* must be retained at their reset value.
*
* ETH_MACFFR_PM Bit 0: Promiscuous mode
* ETH_MACFFR_HU Bit 1: Hash unicast
* ETH_MACFFR_HM Bit 2: Hash multicast
* ETH_MACFFR_DAIF Bit 3: Destination address inverse filtering
* ETH_MACFFR_PAM Bit 4: Pass all multicast
* ETH_MACFFR_BFD Bit 5: Broadcast frames disable
* ETH_MACFFR_PCF Bits 6-7: Pass control frames
* ETH_MACFFR_SAIF Bit 8: Source address inverse filtering
* ETH_MACFFR_SAF Bit 9: Source address filter
* ETH_MACFFR_HPF Bit 10: Hash or perfect filter
* ETH_MACFFR_RA Bit 31: Receive all
*/
#define MACFFR_CLEAR_BITS \
(ETH_MACFFLT_PR | ETH_MACFFLT_HUC | ETH_MACFFLT_HMC | ETH_MACFFLT_DAIF | \
ETH_MACFFLT_PM | ETH_MACFFLT_DBF | ETH_MACFFLT_PCF_MASK | ETH_MACFFLT_HPF | \
ETH_MACFFLT_RA)
/* The following bits are set or left zero unconditionally in all modes.
*
* ETH_MACFFLT_PM Promiscuous mode 0 (disabled)
* ETH_MACFFLT_HU Hash unicast 0 (perfect dest filtering)
* ETH_MACFFLT_HM Hash multicast 0 (perfect dest filtering)
* ETH_MACFFLT_DAIF Destination address 0 (normal)
* inverse filtering
* ETH_MACFFLT_PAM Pass all multicast 0 (Depends on HM bit)
* ETH_MACFFLT_BFD Broadcast frames disable 0 (enabled)
* ETH_MACFFLT_PCF Pass control frames 1 (block all but PAUSE)
* ETH_MACFFLT_HPF Hash or perfect filter 0 (Only matching frames passed)
* ETH_MACFFLT_RA Receive all 0 (disabled)
*/
#define MACFFR_SET_BITS (ETH_MACFFLT_PCF_PAUSE)
/* Clear the MACFCR bits that will be setup during MAC initialization (or
* that are cleared unconditionally). Per the reference manual, all reserved
* bits must be retained at their reset value.
*
* ETH_MACFCR_FCB_BPA Bit 0: Flow control busy/back pressure activate
* ETH_MACFCR_TFCE Bit 1: Transmit flow control enable
* ETH_MACFCR_RFCE Bit 2: Receive flow control enable
* ETH_MACFCR_UPFD Bit 3: Unicast pause frame detect
* ETH_MACFCR_PLT Bits 4-5: Pause low threshold
* ETH_MACFCR_ZQPD Bit 7: Zero-quanta pause disable
* ETH_MACFCR_PT Bits 16-31: Pause time
*/
#define MACFCR_CLEAR_MASK \
(ETH_MACFC_FCB | ETH_MACFC_TFE | ETH_MACFC_RFE | ETH_MACFC_UP | \
ETH_MACFC_PLT_MASK | ETH_MACFC_DZPQ | ETH_MACFC_PT_MASK)
/* The following bits are set or left zero unconditionally in all modes.
*
* ETH_MACFCR_FCB_BPA Flow control busy/back 0 (no pause control frame)
* activate pressure
* ETH_MACFCR_TFCE Transmit flow control enable 0 (disabled)
* ETH_MACFCR_RFCE Receive flow control enable 0 (disabled)
* ETH_MACFCR_UPFD Unicast pause frame detect 0 (disabled)
* ETH_MACFCR_PLT Pause low threshold 0 (pause time - 4)
* ETH_MACFCR_ZQPD Zero-quanta pause disable 1 (disabled)
* ETH_MACFCR_PT Pause time 0
*/
#define MACFCR_SET_MASK (ETH_MACFC_PLT(4) | ETH_MACFC_DZPQ)
/* Clear the DMAOMR bits that will be setup during MAC initialization (or
* that are cleared unconditionally). Per the reference manual, all reserved
* bits must be retained at their reset value.
*
* ETH_DMAOPMODE_SR Bit 1: Start/stop receive
* TH_DMAOMR_OSF Bit 2: Operate on second frame
* ETH_DMAOPMODE_RTC Bits 3-4: Receive threshold control
* ETH_DMAOPMODE_FUGF Bit 6: Forward undersized good frames
* ETH_DMAOPMODE_FEF Bit 7: Forward error frames
* ETH_DMAOPMODE_ST Bit 13: Start/stop transmission
* ETH_DMAOPMODE_TTC Bits 14-16: Transmit threshold control
* ETH_DMAOPMODE_FTF Bit 20: Flush transmit FIFO
* ETH_DMAOPMODE_TSF Bit 21: Transmit store and forward
* ETH_DMAOPMODE_DFRF Bit 24: Disable flushing of received frames
* ETH_DMAOPMODE_RSF Bit 25: Receive store and forward
* TH_DMAOMR_DTCEFD Bit 26: Dropping of TCP/IP checksum error
* frames disable
*/
#define DMAOMR_CLEAR_MASK \
(ETH_DMAOPMODE_SR | ETH_DMAOPMODE_OSF | ETH_DMAOPMODE_RTC_MASK | ETH_DMAOPMODE_FUF | \
ETH_DMAOPMODE_FEF | ETH_DMAOPMODE_ST | ETH_DMAOPMODE_TTC_MASK | ETH_DMAOPMODE_FTF | \
ETH_DMAOPMODE_DFF)
/* The following bits are set or left zero unconditionally in all modes.
*
* ETH_DMAOPMODE_SR Start/stop receive 0 (not running)
* TH_DMAOMR_OSF Operate on second frame 1 (enabled)
* ETH_DMAOPMODE_RTC Receive threshold control 0 (64 bytes)
* ETH_DMAOPMODE_FUGF Forward undersized good 0 (disabled)
* frames
* ETH_DMAOPMODE_FEF Forward error frames 0 (disabled)
* ETH_DMAOPMODE_ST Start/stop transmission 0 (not running)
* ETH_DMAOPMODE_TTC Transmit threshold control 0 (64 bytes)
* ETH_DMAOPMODE_FTF Flush transmit FIFO 0 (no flush)
* ETH_DMAOPMODE_TSF Transmit store and forward Depends on
* CONFIG_LPC43_ETH_HWCHECKSUM
* ETH_DMAOPMODE_DFRF Disable flushing of 0 (enabled)
* received frames
* ETH_DMAOPMODE_RSF Receive store and forward Depends on
* CONFIG_LPC43_ETH_HWCHECKSUM
* TH_DMAOMR_DTCEFD Dropping of TCP/IP checksum Depends on
* error frames disable CONFIG_LPC43_ETH_HWCHECKSUM
*
* When the checksum offload feature is enabled, we need to enable the Store
* and Forward mode: the store and forward guarantee that a whole frame is
* stored in the FIFO, so the MAC can insert/verify the checksum, if the
* checksum is OK the DMA can handle the frame otherwise the frame is dropped
*/
#ifdef CONFIG_LPC43_ETH_HWCHECKSUM
# define DMAOMR_SET_MASK \
(ETH_DMAOPMODE_OSF | ETH_DMAOPMODE_RTC_64 | ETH_DMAOPMODE_TTC_64 | \
ETH_DMAOPMODE_TSF | ETH_DMAOPMODE_RSF)
#else
# define DMAOMR_SET_MASK \
(ETH_DMAOPMODE_OSF | ETH_DMAOPMODE_RTC_64 | ETH_DMAOPMODE_TTC_64)
#endif
/* Clear the DMABMR bits that will be setup during MAC initialization (or
* that are cleared unconditionally). Per the reference manual, all reserved
* bits must be retained at their reset value.
*
* ETH_DMABMODE_SR Bit 0: Software reset
* ETH_DMABMODE_DA Bit 1: DMA Arbitration
* ETH_DMABMODE_DSL Bits 2-6: Descriptor skip length
* ETH_DMABMODE_ATDS Bit 7: Enhanced descriptor format enable
* ETH_DMABMODE_PBL Bits 8-13: Programmable burst length
* ETH_DMABMODE_RTPR Bits 14-15: RX TX priority ratio
* ETH_DMABMODE_FB Bit 16: Fixed burst
* ETH_DMABMODE_RDP Bits 17-22: RX DMA PBL
* ETH_DMABMODE_USP Bit 23: Use separate PBL
* ETH_DMABMODE_FPM Bit 24: 4xPBL mode
* ETH_DMABMODE_AAB Bit 25: Address-aligned beats
* ETH_DMABMODE_MB Bit 26: Mixed burst (F2/F4 only)
*/
#define DMABMR_CLEAR_MASK \
(ETH_DMABMODE_SWR | ETH_DMABMODE_DA | ETH_DMABMODE_DSL_MASK | ETH_DMABMODE_ATDS | \
ETH_DMABMODE_PBL_MASK | ETH_DMABMODE_PR_MASK | ETH_DMABMODE_FB | ETH_DMABMODE_RPBL_MASK | \
2015-10-04 14:59:08 -06:00
ETH_DMABMODE_USP | ETH_DMABMODE_PBL8X | ETH_DMABMODE_AAL | ETH_DMABMODE_MB | ETH_DMABMODE_TXPR)
/* The following bits are set or left zero unconditionally in all modes.
*
*
* ETH_DMABMODE_SR Software reset 0 (no reset)
* ETH_DMABMODE_DA DMA Arbitration 0 (round robin)
* ETH_DMABMODE_DSL Descriptor skip length 0
* ETH_DMABMODE_ATDS Enhanced descriptor format Depends on
* enable CONFIG_LPC43_ETH_ENHANCEDDESC
* ETH_DMABMODE_PBL Programmable burst length 32 beats
* ETH_DMABMODE_RTPR RX TX priority ratio 2:1
* ETH_DMABMODE_FB Fixed burst 1 (enabled)
* ETH_DMABMODE_RDP RX DMA PBL 32 beats
* ETH_DMABMODE_USP Use separate PBL 1 (enabled)
* ETH_DMABMODE_FPM 4xPBL mode 0 (disabled)
* ETH_DMABMODE_AAB Address-aligned beats 1 (enabled)
* ETH_DMABMODE_MB Mixed burst 0 (disabled, F2/F4 only)
*/
#ifdef CONFIG_LPC43_ETH_ENHANCEDDESC
# define DMABMR_SET_MASK \
(ETH_DMABMODE_DSL(0) | ETH_DMABMODE_PBL(32) | ETH_DMABMODE_ATDS | ETH_DMABMODE_RTPR_2TO1 | \
ETH_DMABMODE_FB | ETH_DMABMODE_RPBL(32) | ETH_DMABMODE_USP | ETH_DMABMODE_AAB)
#else
# define DMABMR_SET_MASK \
(ETH_DMABMODE_DSL(0) | ETH_DMABMODE_PBL(32) | ETH_DMABMODE_PR_2TO1 | ETH_DMABMODE_FB | \
ETH_DMABMODE_RPBL(32) | ETH_DMABMODE_USP | ETH_DMABMODE_AAL)
#endif
/* Interrupt bit sets *******************************************************/
/* All interrupts in the normal and abnormal interrupt summary. Early
* transmit interrupt (ETI) is excluded from the abnormal set because it
* causes too many interrupts and is not interesting.
*/
#define ETH_DMAINT_NORMAL \
2015-10-07 11:39:06 -06:00
(ETH_DMAINT_TI | ETH_DMAINT_TU | ETH_DMAINT_RI | ETH_DMAINT_ERI)
#define ETH_DMAINT_ABNORMAL \
(ETH_DMAINT_TPS | ETH_DMAINT_TJT | ETH_DMAINT_OVF | ETH_DMAINT_UNF | \
ETH_DMAINT_RU | ETH_DMAINT_RPS | ETH_DMAINT_RWT | /* ETH_DMAINT_ETI | */ \
ETH_DMAINT_FBI)
/* Normal receive, transmit, error interrupt enable bit sets */
#define ETH_DMAINT_RECV_ENABLE (ETH_DMAINT_NIS | ETH_DMAINT_RI)
#define ETH_DMAINT_XMIT_ENABLE (ETH_DMAINT_NIS | ETH_DMAINT_TI)
#define ETH_DMAINT_XMIT_DISABLE (ETH_DMAINT_TI)
#ifdef CONFIG_DEBUG_NET
# define ETH_DMAINT_ERROR_ENABLE (ETH_DMAINT_AIS | ETH_DMAINT_ABNORMAL)
#else
# define ETH_DMAINT_ERROR_ENABLE (0)
#endif
/* Helpers ******************************************************************/
/* This is a helper pointer for accessing the contents of the Ethernet
* header
*/
#define BUF ((struct eth_hdr_s *)priv->dev.d_buf)
/****************************************************************************
* Private Types
****************************************************************************/
/* The lpc43_ethmac_s encapsulates all state information for a single
* hardware interface
*/
struct lpc43_ethmac_s
{
uint8_t ifup : 1; /* true:ifup false:ifdown */
uint8_t mbps100 : 1; /* 100MBps operation (vs 10 MBps) */
uint8_t fduplex : 1; /* Full (vs. half) duplex */
struct wdog_s txpoll; /* TX poll timer */
struct wdog_s txtimeout; /* TX timeout timer */
struct work_s irqwork; /* For deferring work to the work queue */
struct work_s pollwork; /* For deferring work to the work queue */
/* This holds the information visible to the NuttX network */
struct net_driver_s dev; /* Interface understood by the network */
/* Used to track transmit and receive descriptors */
struct eth_txdesc_s *txhead; /* Next available TX descriptor */
struct eth_rxdesc_s *rxhead; /* Next available RX descriptor */
struct eth_txdesc_s *txtail; /* First "in_flight" TX descriptor */
struct eth_rxdesc_s *rxcurr; /* First RX descriptor of the segment */
uint16_t segments; /* RX segment count */
uint16_t inflight; /* Number of TX transfers "in_flight" */
sq_queue_t freeb; /* The free buffer list */
/* Descriptor allocations */
struct eth_rxdesc_s rxtable[CONFIG_LPC43_ETH_NRXDESC];
struct eth_txdesc_s txtable[CONFIG_LPC43_ETH_NTXDESC];
/* Buffer allocations */
uint8_t rxbuffer[CONFIG_LPC43_ETH_NRXDESC*CONFIG_LPC43_ETH_BUFSIZE];
uint8_t alloc[LPC43_ETH_NFREEBUFFERS*CONFIG_LPC43_ETH_BUFSIZE];
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct lpc43_ethmac_s g_lpc43ethmac;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Register operations ******************************************************/
#ifdef CONFIG_LPC43_ETHMAC_REGDEBUG
static uint32_t lpc43_getreg(uint32_t addr);
static void lpc43_putreg(uint32_t val, uint32_t addr);
static void lpc43_checksetup(void);
#else
# define lpc43_getreg(addr) getreg32(addr)
# define lpc43_putreg(val,addr) putreg32(val,addr)
# define lpc43_checksetup()
#endif
/* Free buffer management */
static void lpc43_initbuffer(FAR struct lpc43_ethmac_s *priv);
static inline uint8_t *lpc43_allocbuffer(FAR struct lpc43_ethmac_s *priv);
static inline void lpc43_freebuffer(FAR struct lpc43_ethmac_s *priv,
uint8_t *buffer);
static inline bool lpc43_isfreebuffer(FAR struct lpc43_ethmac_s *priv);
/* Common TX logic */
static int lpc43_transmit(FAR struct lpc43_ethmac_s *priv);
static int lpc43_txpoll(struct net_driver_s *dev);
static void lpc43_dopoll(FAR struct lpc43_ethmac_s *priv);
/* Interrupt handling */
static void lpc43_enableint(FAR struct lpc43_ethmac_s *priv,
uint32_t ierbit);
static void lpc43_disableint(FAR struct lpc43_ethmac_s *priv,
uint32_t ierbit);
static void lpc43_freesegment(FAR struct lpc43_ethmac_s *priv,
FAR struct eth_rxdesc_s *rxfirst, int segments);
static int lpc43_recvframe(FAR struct lpc43_ethmac_s *priv);
static void lpc43_receive(FAR struct lpc43_ethmac_s *priv);
static void lpc43_freeframe(FAR struct lpc43_ethmac_s *priv);
static void lpc43_txdone(FAR struct lpc43_ethmac_s *priv);
static void lpc43_interrupt_work(FAR void *arg);
static int lpc43_interrupt(int irq, FAR void *context, FAR void *arg);
/* Watchdog timer expirations */
static void lpc43_txtimeout_work(FAR void *arg);
static void lpc43_txtimeout_expiry(wdparm_t arg);
static void lpc43_poll_work(FAR void *arg);
static void lpc43_poll_expiry(wdparm_t arg);
/* NuttX callback functions */
static int lpc43_ifup(struct net_driver_s *dev);
static int lpc43_ifdown(struct net_driver_s *dev);
static void lpc43_txavail_work(FAR void *arg);
static int lpc43_txavail(struct net_driver_s *dev);
#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
static int lpc43_addmac(struct net_driver_s *dev, FAR const uint8_t *mac);
#endif
#ifdef CONFIG_NET_MCASTGROUP
static int lpc43_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac);
#endif
#ifdef CONFIG_NETDEV_IOCTL
static int lpc43_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg);
#endif
/* Descriptor Initialization */
static void lpc43_txdescinit(FAR struct lpc43_ethmac_s *priv);
static void lpc43_rxdescinit(FAR struct lpc43_ethmac_s *priv);
/* PHY Initialization */
#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
static int lpc43_phyintenable(FAR struct lpc43_ethmac_s *priv);
#endif
static int lpc43_phyread(uint16_t phydevaddr, uint16_t phyregaddr,
uint16_t *value);
static int lpc43_phywrite(uint16_t phydevaddr, uint16_t phyregaddr,
uint16_t value);
#ifdef CONFIG_ETH0_PHY_DM9161
static inline int lpc43_dm9161(FAR struct lpc43_ethmac_s *priv);
#endif
static int lpc43_phyinit(FAR struct lpc43_ethmac_s *priv);
/* MAC/DMA Initialization */
#ifdef CONFIG_LPC43_MII
static inline void lpc43_selectmii(void);
#endif
#ifdef CONFIG_LPC43_RMII
static inline void lpc43_selectrmii(void);
#endif
static inline void lpc43_ethgpioconfig(FAR struct lpc43_ethmac_s *priv);
static void lpc43_ethreset(FAR struct lpc43_ethmac_s *priv);
static int lpc43_macconfig(FAR struct lpc43_ethmac_s *priv);
static void lpc43_macaddress(FAR struct lpc43_ethmac_s *priv);
#ifdef CONFIG_NET_ICMPv6
static void lpc43_ipv6multicast(FAR struct lpc43_ethmac_s *priv);
#endif
static int lpc43_macenable(FAR struct lpc43_ethmac_s *priv);
static int lpc43_ethconfig(FAR struct lpc43_ethmac_s *priv);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lpc43_getreg
*
* Description:
* This function may to used to intercept an monitor all register accesses.
* Clearly this is nothing you would want to do unless you are debugging
* this driver.
*
* Input Parameters:
* addr - The register address to read
*
* Returned Value:
* The value read from the register
*
****************************************************************************/
#ifdef CONFIG_LPC43_ETHMAC_REGDEBUG
static uint32_t lpc43_getreg(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
/* Read the value from the register */
uint32_t val = getreg32(addr);
/* Is this the same value that we read from the same register last time?
* Are we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr && val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
2015-10-05 17:13:53 -06:00
if (count == 4)
{
ninfo("...\n");
2015-10-05 17:13:53 -06:00
}
return val;
}
}
/* No this is a new address or value */
else
{
2015-10-07 11:39:06 -06:00
/* Did we print "..." for the previous value? */
2015-10-07 11:39:06 -06:00
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
ninfo("[repeats %d more times]\n", count - 3);
2015-10-07 11:39:06 -06:00
}
2015-10-07 11:39:06 -06:00
/* Save the new address, value, and count */
2015-10-07 11:39:06 -06:00
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
ninfo("%08x->%08x\n", addr, val);
return val;
}
#endif
/****************************************************************************
* Name: lpc43_putreg
*
* Description:
* This function may to used to intercept an monitor all register accesses.
* Clearly this is nothing you would want to do unless you are debugging
* this driver.
*
* Input Parameters:
* val - The value to write to the register
* addr - The register address to read
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_LPC43_ETHMAC_REGDEBUG
static void lpc43_putreg(uint32_t val, uint32_t addr)
{
/* Show the register value being written */
ninfo("%08x<-%08x\n", addr, val);
/* Write the value */
putreg32(val, addr);
}
#endif
/****************************************************************************
* Name: lpc43_checksetup
*
* Description:
* Show the state of critical configuration registers.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_LPC43_ETHMAC_REGDEBUG
static void lpc43_checksetup(void)
{
}
#endif
/****************************************************************************
* Function: lpc43_initbuffer
*
* Description:
* Initialize the free buffer list.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Called during early driver initialization before Ethernet interrupts
* are enabled.
*
****************************************************************************/
static void lpc43_initbuffer(FAR struct lpc43_ethmac_s *priv)
{
uint8_t *buffer;
int i;
/* Initialize the head of the free buffer list */
sq_init(&priv->freeb);
/* Add all of the pre-allocated buffers to the free buffer list */
for (i = 0, buffer = priv->alloc;
i < LPC43_ETH_NFREEBUFFERS;
i++, buffer += CONFIG_LPC43_ETH_BUFSIZE)
{
sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb);
}
}
/****************************************************************************
* Function: lpc43_allocbuffer
*
* Description:
* Allocate one buffer from the free buffer list.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* Pointer to the allocated buffer on success; NULL on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static inline uint8_t *lpc43_allocbuffer(FAR struct lpc43_ethmac_s *priv)
{
/* Allocate a buffer by returning the head of the free buffer list */
return (uint8_t *)sq_remfirst(&priv->freeb);
}
/****************************************************************************
* Function: lpc43_freebuffer
*
* Description:
* Return a buffer to the free buffer list.
*
* Input Parameters:
* priv - Reference to the driver state structure
* buffer - A pointer to the buffer to be freed
*
* Returned Value:
* None
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static inline void lpc43_freebuffer(FAR struct lpc43_ethmac_s *priv,
uint8_t *buffer)
{
2019-09-29 12:52:20 -06:00
/* Free the buffer by adding it to the end of the free buffer list */
sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb);
}
/****************************************************************************
* Function: lpc43_isfreebuffer
*
* Description:
* Return TRUE if the free buffer list is not empty.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* True if there are one or more buffers in the free buffer list;
* false if the free buffer list is empty
*
* Assumptions:
* None.
*
****************************************************************************/
static inline bool lpc43_isfreebuffer(FAR struct lpc43_ethmac_s *priv)
{
/* Return TRUE if the free buffer list is not empty */
return !sq_empty(&priv->freeb);
}
/****************************************************************************
* Function: lpc43_transmit
*
* Description:
* Start hardware transmission. Called either from the txdone interrupt
* handling or from watchdog based polling.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static int lpc43_transmit(FAR struct lpc43_ethmac_s *priv)
{
struct eth_txdesc_s *txdesc;
struct eth_txdesc_s *txfirst;
/* The internal (optimal) network buffer size may be configured to be
* larger than the Ethernet buffer size.
*/
#if OPTIMAL_ETH_BUFSIZE > CONFIG_LPC43_ETH_BUFSIZE
uint8_t *buffer;
int bufcount;
int lastsize;
int i;
#endif
/* Verify that the hardware is ready to send another packet. If we get
* here, then we are committed to sending a packet; Higher level logic
* must have assured that there is no transmission in progress.
*/
txdesc = priv->txhead;
txfirst = txdesc;
ninfo("d_len: %d d_buf: %p txhead: %p tdes0: %08" PRIx32 "\n",
priv->dev.d_len, priv->dev.d_buf, txdesc, txdesc->tdes0);
DEBUGASSERT(txdesc && (txdesc->tdes0 & ETH_TDES0_OWN) == 0);
/* Is the size to be sent greater than the size of the Ethernet buffer? */
DEBUGASSERT(priv->dev.d_len > 0 && priv->dev.d_buf != NULL);
#if OPTIMAL_ETH_BUFSIZE > CONFIG_LPC43_ETH_BUFSIZE
if (priv->dev.d_len > CONFIG_LPC43_ETH_BUFSIZE)
{
/* Yes... how many buffers will be need to send the packet? */
bufcount = (priv->dev.d_len + (CONFIG_LPC43_ETH_BUFSIZE - 1)) /
CONFIG_LPC43_ETH_BUFSIZE;
lastsize = priv->dev.d_len - (bufcount - 1) * CONFIG_LPC43_ETH_BUFSIZE;
ninfo("bufcount: %d lastsize: %d\n", bufcount, lastsize);
/* Set the first segment bit in the first TX descriptor */
txdesc->tdes0 |= ETH_TDES0_FS;
/* Set up all but the last TX descriptor */
buffer = priv->dev.d_buf;
for (i = 0; i < bufcount; i++)
{
/* This could be a normal event but the design does not handle it */
DEBUGASSERT((txdesc->tdes0 & ETH_TDES0_OWN) == 0);
/* Set the Buffer1 address pointer */
txdesc->tdes2 = (uint32_t)buffer;
/* Set the buffer size in all TX descriptors */
if (i == (bufcount - 1))
{
/* This is the last segment. Set the last segment bit in the
* last TX descriptor and ask for an interrupt when this
* segment transfer completes.
*/
txdesc->tdes0 |= (ETH_TDES0_LS | ETH_TDES0_IC);
/* This segment is, most likely, of fractional buffersize */
txdesc->tdes1 = lastsize;
buffer += lastsize;
}
else
{
/* This is not the last segment. We don't want an interrupt
* when this segment transfer completes.
*/
txdesc->tdes0 &= ~ETH_TDES0_IC;
/* The size of the transfer is the whole buffer */
txdesc->tdes1 = CONFIG_LPC43_ETH_BUFSIZE;
buffer += CONFIG_LPC43_ETH_BUFSIZE;
}
/* Give the descriptor to DMA */
txdesc->tdes0 |= ETH_TDES0_OWN;
txdesc = (struct eth_txdesc_s *)txdesc->tdes3;
}
}
else
#endif
{
/* The single descriptor is both the first and last segment. And we do
* want an interrupt when the transfer completes.
*/
txdesc->tdes0 |= (ETH_TDES0_FS | ETH_TDES0_LS | ETH_TDES0_IC);
/* Set frame size */
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 14:10:40 -06:00
DEBUGASSERT(priv->dev.d_len <= CONFIG_NET_ETH_PKTSIZE);
txdesc->tdes1 = priv->dev.d_len;
/* Set the Buffer1 address pointer */
txdesc->tdes2 = (uint32_t)priv->dev.d_buf;
/* Set OWN bit of the TX descriptor tdes0. This gives the buffer to
* Ethernet DMA
*/
txdesc->tdes0 |= ETH_TDES0_OWN;
/* Point to the next available TX descriptor */
txdesc = (struct eth_txdesc_s *)txdesc->tdes3;
}
/* Remember where we left off in the TX descriptor chain */
priv->txhead = txdesc;
/* Detach the buffer from priv->dev structure. That buffer is now
* "in-flight".
*/
priv->dev.d_buf = NULL;
priv->dev.d_len = 0;
/* If there is no other TX buffer, in flight, then remember the location
* of the TX descriptor. This is the location to check for TX done events.
*/
if (!priv->txtail)
{
DEBUGASSERT(priv->inflight == 0);
priv->txtail = txfirst;
}
/* Increment the number of TX transfer in-flight */
priv->inflight++;
ninfo("txhead: %p txtail: %p inflight: %d\n",
priv->txhead, priv->txtail, priv->inflight);
/* If all TX descriptors are in-flight, then we have to disable receive
* interrupts too. This is because receive events can trigger more
* un-stoppable transmit events.
*/
if (priv->inflight >= CONFIG_LPC43_ETH_NTXDESC)
{
lpc43_disableint(priv, ETH_DMAINT_RI);
}
/* Check if the TX Buffer unavailable flag is set */
if ((lpc43_getreg(LPC43_ETH_DMASTAT) & ETH_DMAINT_TU) != 0)
{
/* Clear TX Buffer unavailable flag */
lpc43_putreg(ETH_DMAINT_TU, LPC43_ETH_DMASTAT);
/* Resume DMA transmission */
lpc43_putreg(0, LPC43_ETH_DMATXPD);
}
/* Enable TX interrupts */
lpc43_enableint(priv, ETH_DMAINT_TI);
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
wd_start(&priv->txtimeout, LPC43_TXTIMEOUT,
lpc43_txtimeout_expiry, (wdparm_t)priv);
return OK;
}
/****************************************************************************
* Function: lpc43_txpoll
*
* Description:
* The transmitter is available, check if the network has any outgoing
* packets ready to send. This is a callback from devif_poll().
* devif_poll() may be called:
*
* 1. When the preceding TX packet send is complete,
* 2. When the preceding TX packet send timesout and the interface is reset
* 3. During normal TX polling
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static int lpc43_txpoll(struct net_driver_s *dev)
{
FAR struct lpc43_ethmac_s *priv =
(FAR struct lpc43_ethmac_s *)dev->d_private;
DEBUGASSERT(priv->dev.d_buf != NULL);
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
/* Look up the destination MAC address and add it to the Ethernet
* header.
*/
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(priv->dev.d_flags))
#endif
{
arp_out(&priv->dev);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
neighbor_out(&priv->dev);
}
#endif /* CONFIG_NET_IPv6 */
if (!devif_loopback(&priv->dev))
{
/* Send the packet */
lpc43_transmit(priv);
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
/* Check if the next TX descriptor is owned by the Ethernet DMA or
* CPU. We cannot perform the TX poll if we are unable to accept
* another packet for transmission.
*
* In a race condition, ETH_TDES0_OWN may be cleared BUT still
* not available because lpc43_freeframe() has not yet run. If
* lpc43_freeframe() has run, the buffer1 pointer (tdes2) will be
* nullified (and inflight should be < CONFIG_LPC43_ETH_NTXDESC).
*/
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) != 0 ||
priv->txhead->tdes2 != 0)
{
/* We have to terminate the poll if we have no more descriptors
* available for another transfer.
*/
return -EBUSY;
}
/* We have the descriptor, we can continue the poll. Allocate a new
* buffer for the poll.
*/
dev->d_buf = lpc43_allocbuffer(priv);
/* We can't continue the poll if we have no buffers */
if (dev->d_buf == NULL)
{
/* Terminate the poll. */
return -ENOMEM;
}
}
}
/* If zero is returned, the polling will continue until all connections
* have been examined.
*/
return 0;
}
/****************************************************************************
* Function: lpc43_dopoll
*
* Description:
* The function is called in order to perform an out-of-sequence TX poll.
* This is done:
*
* 1. After completion of a transmission (lpc43_txdone),
* 2. When new TX data is available (lpc43_txavail_process), and
* 3. After a TX timeout to restart the sending process
* (lpc43_txtimeout_process).
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_dopoll(FAR struct lpc43_ethmac_s *priv)
{
FAR struct net_driver_s *dev = &priv->dev;
/* Check if the next TX descriptor is owned by the Ethernet DMA or
* CPU. We cannot perform the TX poll if we are unable to accept
* another packet for transmission.
*
* In a race condition, ETH_TDES0_OWN may be cleared BUT still
* not available because lpc43_freeframe() has not yet run. If
* lpc43_freeframe() has run, the buffer1 pointer (tdes2) will be
* nullified (and inflight should be < CONFIG_LPC43_ETH_NTXDESC).
*/
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0 &&
priv->txhead->tdes2 == 0)
{
/* If we have the descriptor, then poll for new XMIT data.
* Allocate a buffer for the poll.
*/
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
dev->d_buf = lpc43_allocbuffer(priv);
/* We can't poll if we have no buffers */
if (dev->d_buf)
{
devif_timer(dev, 0, lpc43_txpoll);
/* We will, most likely end up with a buffer to be freed. But it
* might not be the same one that we allocated above.
*/
if (dev->d_buf)
{
DEBUGASSERT(dev->d_len == 0);
lpc43_freebuffer(priv, dev->d_buf);
dev->d_buf = NULL;
}
}
}
}
/****************************************************************************
* Function: lpc43_enableint
*
* Description:
* Enable a "normal" interrupt
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_enableint(FAR struct lpc43_ethmac_s *priv,
uint32_t ierbit)
{
uint32_t regval;
/* Enable the specified "normal" interrupt */
regval = lpc43_getreg(LPC43_ETH_DMAINTEN);
regval |= (ETH_DMAINT_NIS | ierbit);
lpc43_putreg(regval, LPC43_ETH_DMAINTEN);
}
/****************************************************************************
* Function: lpc43_disableint
*
* Description:
* Disable a normal interrupt.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_disableint(FAR struct lpc43_ethmac_s *priv,
uint32_t ierbit)
{
uint32_t regval;
/* Disable the "normal" interrupt */
regval = lpc43_getreg(LPC43_ETH_DMAINTEN);
regval &= ~ierbit;
/* Are all "normal" interrupts now disabled? */
if ((regval & ETH_DMAINT_NORMAL) == 0)
{
/* Yes.. disable normal interrupts */
regval &= ~ETH_DMAINT_NIS;
}
lpc43_putreg(regval, LPC43_ETH_DMAINTEN);
}
/****************************************************************************
* Function: lpc43_freesegment
*
* Description:
* The function is called when a frame is received using the DMA receive
* interrupt. It scans the RX descriptors to the received frame.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_freesegment(FAR struct lpc43_ethmac_s *priv,
FAR struct eth_rxdesc_s *rxfirst, int segments)
{
struct eth_rxdesc_s *rxdesc;
int i;
ninfo("rxfirst: %p segments: %d\n", rxfirst, segments);
/* Set OWN bit in RX descriptors. This gives the buffers back to DMA */
rxdesc = rxfirst;
for (i = 0; i < segments; i++)
{
rxdesc->rdes0 = ETH_RDES0_OWN;
rxdesc = (struct eth_rxdesc_s *)rxdesc->rdes3;
}
/* Reset the segment management logic */
priv->rxcurr = NULL;
priv->segments = 0;
/* Check if the RX Buffer unavailable flag is set */
if ((lpc43_getreg(LPC43_ETH_DMASTAT) & ETH_DMAINT_RU) != 0)
{
/* Clear RBUS Ethernet DMA flag */
lpc43_putreg(ETH_DMAINT_RU, LPC43_ETH_DMASTAT);
/* Resume DMA reception */
lpc43_putreg(0, LPC43_ETH_DMARXPD);
}
}
/****************************************************************************
* Function: lpc43_recvframe
*
* Description:
* The function is called when a frame is received using the DMA receive
* interrupt. It scans the RX descriptors of the received frame.
*
* NOTE: This function will silently discard any packets containing errors.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* OK if a packet was successfully returned; -EAGAIN if there are no
* further packets available
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static int lpc43_recvframe(FAR struct lpc43_ethmac_s *priv)
{
struct eth_rxdesc_s *rxdesc;
struct eth_rxdesc_s *rxcurr;
uint8_t *buffer;
int i;
ninfo("rxhead: %p rxcurr: %p segments: %d\n",
priv->rxhead, priv->rxcurr, priv->segments);
/* Check if there are free buffers. We cannot receive new frames in this
* design unless there is at least one free buffer.
*/
if (!lpc43_isfreebuffer(priv))
{
nerr("ERROR: No free buffers\n");
return -ENOMEM;
}
/* Scan descriptors owned by the CPU. Scan until:
*
* 1) We find a descriptor still owned by the DMA,
* 2) We have examined all of the RX descriptors, or
* 3) All of the TX descriptors are in flight.
*
* This last case is obscure. It is due to that fact that each packet
* that we receive can generate an unstoppable transmission. So we have
* to stop receiving when we can not longer transmit. In this case, the
* transmit logic should also have disabled further RX interrupts.
*/
rxdesc = priv->rxhead;
for (i = 0;
(rxdesc->rdes0 & ETH_RDES0_OWN) == 0 &&
i < CONFIG_LPC43_ETH_NRXDESC &&
priv->inflight < CONFIG_LPC43_ETH_NTXDESC;
i++)
{
/* Check if this is the first segment in the frame */
if ((rxdesc->rdes0 & ETH_RDES0_FS) != 0 &&
(rxdesc->rdes0 & ETH_RDES0_LS) == 0)
{
priv->rxcurr = rxdesc;
priv->segments = 1;
}
/* Check if this is an intermediate segment in the frame */
2015-10-07 11:39:06 -06:00
else if (((rxdesc->rdes0 & ETH_RDES0_LS) == 0) &&
((rxdesc->rdes0 & ETH_RDES0_FS) == 0))
{
priv->segments++;
}
/* Otherwise, it is the last segment in the frame */
else
{
priv->segments++;
/* Check if there is only one segment in the frame */
if (priv->segments == 1)
{
rxcurr = rxdesc;
}
else
{
rxcurr = priv->rxcurr;
}
ninfo("rxhead: %p rxcurr: %p segments: %d\n",
priv->rxhead, priv->rxcurr, priv->segments);
/* Check if any errors are reported in the frame */
if ((rxdesc->rdes0 & ETH_RDES0_ES) == 0)
{
struct net_driver_s *dev = &priv->dev;
/* Get the Frame Length of the received packet: subtract 4
* bytes of the CRC
*/
dev->d_len = ((rxdesc->rdes0 & ETH_RDES0_FL_MASK) >>
ETH_RDES0_FL_SHIFT) - 4;
/* Get a buffer from the free list. We don't even check if
* this is successful because we already assure the free
* list is not empty above.
*/
buffer = lpc43_allocbuffer(priv);
/* Take the buffer from the RX descriptor of the first free
* segment, put it into the network device structure, then
* replace the buffer in the RX descriptor with the newly
* allocated buffer.
*/
DEBUGASSERT(dev->d_buf == NULL);
2015-10-07 11:39:06 -06:00
dev->d_buf = (uint8_t *)rxcurr->rdes2;
rxcurr->rdes2 = (uint32_t)buffer;
/* Return success, remembering where we should re-start
* scanning and resetting the segment scanning logic
*/
2015-10-07 11:39:06 -06:00
priv->rxhead = (struct eth_rxdesc_s *)rxdesc->rdes3;
lpc43_freesegment(priv, rxcurr, priv->segments);
ninfo("rxhead: %p d_buf: %p d_len: %d\n",
priv->rxhead, dev->d_buf, dev->d_len);
return OK;
}
else
{
/* Drop the frame that contains the errors, reset the segment
* scanning logic, and continue scanning with the next frame.
*/
nwarn("WARNING: Dropped, RX descriptor errors: %08" PRIx32
"\n",
rxdesc->rdes0);
lpc43_freesegment(priv, rxcurr, priv->segments);
}
}
/* Try the next descriptor */
2015-10-07 11:39:06 -06:00
rxdesc = (struct eth_rxdesc_s *)rxdesc->rdes3;
}
/* We get here after all of the descriptors have been scanned or when
* rxdesc points to the first descriptor owned by the DMA. Remember
* where we left off.
*/
priv->rxhead = rxdesc;
ninfo("rxhead: %p rxcurr: %p segments: %d\n",
priv->rxhead, priv->rxcurr, priv->segments);
return -EAGAIN;
}
/****************************************************************************
* Function: lpc43_receive
*
* Description:
* An interrupt was received indicating the availability of a new RX packet
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_receive(FAR struct lpc43_ethmac_s *priv)
{
struct net_driver_s *dev = &priv->dev;
/* Loop while while lpc43_recvframe() successfully retrieves valid
* Ethernet frames.
*/
while (lpc43_recvframe(priv) == OK)
{
#ifdef CONFIG_NET_PKT
/* When packet sockets are enabled, feed the frame into the tap */
pkt_input(&priv->dev);
#endif
/* Check if the packet is a valid size for the network buffer
* configuration (this should not happen)
*/
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 14:10:40 -06:00
if (dev->d_len > CONFIG_NET_ETH_PKTSIZE)
{
nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len);
/* Free dropped packet buffer */
if (dev->d_buf)
{
lpc43_freebuffer(priv, dev->d_buf);
dev->d_buf = NULL;
dev->d_len = 0;
}
continue;
}
#ifdef CONFIG_NET_PKT
/* When packet sockets are enabled, feed the frame into the tap */
pkt_input(&priv->dev);
#endif
/* We only accept IP packets of the configured type and ARP packets */
#ifdef CONFIG_NET_IPv4
if (BUF->type == HTONS(ETHTYPE_IP))
{
ninfo("IPv4 frame\n");
/* Handle ARP on input then give the IPv4 packet to the network
* layer
*/
arp_ipin(&priv->dev);
ipv4_input(&priv->dev);
/* If the above function invocation resulted in data that should be
* sent out on the network, d_len field will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(priv->dev.d_flags))
#endif
{
arp_out(&priv->dev);
}
#ifdef CONFIG_NET_IPv6
else
{
neighbor_out(&priv->dev);
}
#endif
/* And send the packet */
lpc43_transmit(priv);
}
}
else
2017-02-08 11:52:15 -06:00
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
if (BUF->type == HTONS(ETHTYPE_IP6))
{
2020-03-03 09:11:57 -06:00
ninfo("IPv6 frame\n");
/* Give the IPv6 packet to the network layer */
ipv6_input(&priv->dev);
/* If the above function invocation resulted in data that should be
* sent out on the network, d_len field will set to a value > 0.
*/
if (priv->dev.d_len > 0)
2015-10-07 11:39:06 -06:00
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv4(priv->dev.d_flags))
{
arp_out(&priv->dev);
}
else
#endif
#ifdef CONFIG_NET_IPv6
{
neighbor_out(&priv->dev);
}
#endif
/* And send the packet */
lpc43_transmit(priv);
}
}
else
2017-02-08 11:52:15 -06:00
#endif /* CONFIG_NET_IPv6 */
#ifdef CONFIG_NET_ARP
if (BUF->type == htons(ETHTYPE_ARP))
{
ninfo("ARP frame\n");
/* Handle ARP packet */
arp_arpin(&priv->dev);
/* If the above function invocation resulted in data that should be
* sent out on the network, d_len field will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
lpc43_transmit(priv);
}
}
else
#endif
{
nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type);
}
/* We are finished with the RX buffer. NOTE: If the buffer is
* re-used for transmission, the dev->d_buf field will have been
* nullified.
*/
if (dev->d_buf)
{
/* Free the receive packet buffer */
lpc43_freebuffer(priv, dev->d_buf);
dev->d_buf = NULL;
dev->d_len = 0;
}
}
}
/****************************************************************************
* Function: lpc43_freeframe
*
* Description:
* Scans the TX descriptors and frees the buffers of completed transfers.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None.
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc43_freeframe(FAR struct lpc43_ethmac_s *priv)
{
struct eth_txdesc_s *txdesc;
int i;
ninfo("txhead: %p txtail: %p inflight: %d\n",
priv->txhead, priv->txtail, priv->inflight);
/* Scan for "in-flight" descriptors owned by the CPU */
txdesc = priv->txtail;
if (txdesc)
{
DEBUGASSERT(priv->inflight > 0);
for (i = 0; (txdesc->tdes0 & ETH_TDES0_OWN) == 0; i++)
{
/* There should be a buffer assigned to all in-flight
* TX descriptors.
*/
ninfo("txtail: %p tdes0: %08" PRIx32
" tdes2: %08" PRIx32 " tdes3: %08" PRIx32 "\n",
txdesc, txdesc->tdes0, txdesc->tdes2, txdesc->tdes3);
DEBUGASSERT(txdesc->tdes2 != 0);
/* Check if this is the first segment of a TX frame. */
if ((txdesc->tdes0 & ETH_TDES0_FS) != 0)
{
/* Yes.. Free the buffer */
2015-10-07 11:39:06 -06:00
lpc43_freebuffer(priv, (uint8_t *)txdesc->tdes2);
}
/* In any event, make sure that TDES2 is nullified. */
txdesc->tdes2 = 0;
/* Check if this is the last segment of a TX frame */
if ((txdesc->tdes0 & ETH_TDES0_LS) != 0)
{
/* Yes.. Decrement the number of frames "in-flight". */
priv->inflight--;
/* If all of the TX descriptors were in-flight,
* then RX interrupts may have been disabled...
* we can re-enable them now.
*/
lpc43_enableint(priv, ETH_DMAINT_RI);
/* If there are no more frames in-flight, then bail. */
if (priv->inflight <= 0)
{
priv->txtail = NULL;
priv->inflight = 0;
return;
}
}
/* Try the next descriptor in the TX chain */
2015-10-07 11:39:06 -06:00
txdesc = (struct eth_txdesc_s *)txdesc->tdes3;
}
/* We get here if (1) there are still frames "in-flight". Remember
* where we left off.
*/
priv->txtail = txdesc;
ninfo("txhead: %p txtail: %p inflight: %d\n",
priv->txhead, priv->txtail, priv->inflight);
}
}
/****************************************************************************
* Function: lpc43_txdone
*
* Description:
* An interrupt was received indicating that the last TX packet(s) is done
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by the watchdog logic.
*
****************************************************************************/
static void lpc43_txdone(FAR struct lpc43_ethmac_s *priv)
{
DEBUGASSERT(priv->txtail != NULL);
/* Scan the TX descriptor change, returning buffers to free list */
lpc43_freeframe(priv);
/* If no further xmits are pending, then cancel the TX timeout */
if (priv->inflight <= 0)
{
/* Cancel the TX timeout */
wd_cancel(&priv->txtimeout);
/* And disable further TX interrupts. */
lpc43_disableint(priv, ETH_DMAINT_TI);
}
/* Then poll the network for new XMIT data */
lpc43_dopoll(priv);
}
/****************************************************************************
* Function: lpc43_interrupt_work
*
* Description:
* Perform interrupt related work from the worker thread
*
* Input Parameters:
* arg - The argument passed when work_queue() was called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Ethernet interrupts are disabled
*
****************************************************************************/
static void lpc43_interrupt_work(FAR void *arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
uint32_t dmasr;
DEBUGASSERT(priv);
/* Get the DMA interrupt status bits (no MAC interrupts are expected) */
net_lock();
dmasr = lpc43_getreg(LPC43_ETH_DMASTAT);
/* Mask only enabled interrupts. This depends on the fact that the
* interrupt related bits (0-16) correspond in these two registers.
*/
dmasr &= lpc43_getreg(LPC43_ETH_DMAINTEN);
/* Check if there are pending "normal" interrupts */
if ((dmasr & ETH_DMAINT_NIS) != 0)
{
/* Yes.. Check if we received an incoming packet, if so, call
* lpc43_receive()
*/
if ((dmasr & ETH_DMAINT_RI) != 0)
{
/* Clear the pending receive interrupt */
lpc43_putreg(ETH_DMAINT_RI, LPC43_ETH_DMASTAT);
/* Handle the received package */
lpc43_receive(priv);
}
/* Check if a packet transmission just completed. If so, call
* lpc43_txdone(). This may disable further TX interrupts if there
* are no pending tansmissions.
*/
if ((dmasr & ETH_DMAINT_TI) != 0)
{
/* Clear the pending receive interrupt */
lpc43_putreg(ETH_DMAINT_TI, LPC43_ETH_DMASTAT);
/* Check if there are pending transmissions */
lpc43_txdone(priv);
}
/* Clear the pending normal summary interrupt */
lpc43_putreg(ETH_DMAINT_NIS, LPC43_ETH_DMASTAT);
}
/* Handle error interrupt only if CONFIG_DEBUG_NET is eanbled */
#ifdef CONFIG_DEBUG_NET
/* Check if there are pending "abnormal" interrupts */
if ((dmasr & ETH_DMAINT_AIS) != 0)
{
/* Just let the user know what happened */
nerr("ERROR: Abnormal event(s): %08x\n", dmasr);
/* Clear all pending abnormal events */
lpc43_putreg(ETH_DMAINT_ABNORMAL, LPC43_ETH_DMASTAT);
/* Clear the pending abnormal summary interrupt */
lpc43_putreg(ETH_DMAINT_AIS, LPC43_ETH_DMASTAT);
}
2017-02-08 11:52:15 -06:00
#endif /* CONFIG_DEBUG_NET */
net_unlock();
/* Re-enable Ethernet interrupts at the NVIC */
up_enable_irq(LPC43M4_IRQ_ETHERNET);
}
/****************************************************************************
* Function: lpc43_interrupt
*
* Description:
* Hardware interrupt handler
*
* Input Parameters:
* irq - Number of the IRQ that generated the interrupt
* context - Interrupt register state save info (architecture-specific)
*
* Returned Value:
* OK on success
*
* Assumptions:
*
****************************************************************************/
static int lpc43_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct lpc43_ethmac_s *priv = &g_lpc43ethmac;
uint32_t dmasr;
/* Get the DMA interrupt status bits (no MAC interrupts are expected) */
dmasr = lpc43_getreg(LPC43_ETH_DMASTAT);
if (dmasr != 0)
{
/* Disable further Ethernet interrupts. Because Ethernet interrupts
* are also disabled if the TX timeout event occurs, there can be no
* race condition here.
*/
up_disable_irq(LPC43M4_IRQ_ETHERNET);
/* Check if a packet transmission just completed. */
if ((dmasr & ETH_DMAINT_TI) != 0)
{
/* If a TX transfer just completed, then cancel the TX timeout so
* there will be no race condition between any subsequent timeout
* expiration and the deferred interrupt processing.
*/
wd_cancel(&priv->txtimeout);
}
/* Schedule to perform the interrupt processing on the worker thread. */
work_queue(ETHWORK, &priv->irqwork, lpc43_interrupt_work, priv, 0);
}
return OK;
}
/****************************************************************************
* Function: lpc43_txtimeout_work
*
* Description:
* Perform TX timeout related work from the worker thread
*
* Input Parameters:
* arg - The argument passed when work_queue() as called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Ethernet interrupts are disabled
*
****************************************************************************/
static void lpc43_txtimeout_work(FAR void *arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
/* Then reset the hardware. Just take the interface down, then back
* up again.
*/
net_lock();
lpc43_ifdown(&priv->dev);
lpc43_ifup(&priv->dev);
/* Then poll the network for new XMIT data */
lpc43_dopoll(priv);
net_unlock();
}
/****************************************************************************
* Function: lpc43_txtimeout_expiry
*
* Description:
* Our TX watchdog timed out. Called from the timer interrupt handler.
* The last TX never completed. Reset the hardware and start again.
*
* Input Parameters:
* arg - The argument
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by the watchdog logic.
*
****************************************************************************/
static void lpc43_txtimeout_expiry(wdparm_t arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
ninfo("Timeout!\n");
/* Disable further Ethernet interrupts. This will prevent some race
* conditions with interrupt work. There is still a potential race
* condition with interrupt work that is already queued and in progress.
*
* Interrupts will be re-enabled when lpc43_ifup() is called.
*/
2015-09-09 07:22:02 -06:00
up_disable_irq(LPC43M4_IRQ_ETHERNET);
/* Schedule to perform the TX timeout processing on the worker thread,
* perhaps cancelling any pending IRQ processing.
*/
work_queue(ETHWORK, &priv->irqwork, lpc43_txtimeout_work, priv, 0);
}
/****************************************************************************
* Function: lpc43_poll_work
*
* Description:
* Perform periodic polling from the worker thread
*
* Input Parameters:
* arg - The argument passed when work_queue() as called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Ethernet interrupts are disabled
*
****************************************************************************/
static void lpc43_poll_work(FAR void *arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
FAR struct net_driver_s *dev = &priv->dev;
/* Check if the next TX descriptor is owned by the Ethernet DMA or
* CPU. We cannot perform the TX poll if we are unable to accept
* another packet for transmission.
*
* In a race condition, ETH_TDES0_OWN may be cleared BUT still
* not available because lpc43_freeframe() has not yet run. If
* lpc43_freeframe() has run, the buffer1 pointer (tdes2) will be
* nullified (and inflight should be < CONFIG_LPC43_ETH_NTXDESC).
*/
net_lock();
if ((priv->txhead->tdes0 & ETH_TDES0_OWN) == 0 &&
priv->txhead->tdes2 == 0)
{
/* If we have the descriptor, then perform the timer poll. Allocate a
* buffer for the poll.
*/
DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL);
dev->d_buf = lpc43_allocbuffer(priv);
/* We can't poll if we have no buffers */
if (dev->d_buf)
{
/* Update TCP timing states and poll for new XMIT data.
*/
devif_timer(dev, LPC43_WDDELAY, lpc43_txpoll);
/* We will, most likely end up with a buffer to be freed. But it
* might not be the same one that we allocated above.
*/
if (dev->d_buf)
{
DEBUGASSERT(dev->d_len == 0);
lpc43_freebuffer(priv, dev->d_buf);
dev->d_buf = NULL;
}
}
}
/* Setup the watchdog poll timer again */
wd_start(&priv->txpoll, LPC43_WDDELAY,
lpc43_poll_expiry, (wdparm_t)priv);
net_unlock();
}
/****************************************************************************
* Function: lpc43_poll_expiry
*
* Description:
* Periodic timer handler. Called from the timer interrupt handler.
*
* Input Parameters:
* arg - The argument
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by the watchdog logic.
*
****************************************************************************/
static void lpc43_poll_expiry(wdparm_t arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
/* Schedule to perform the interrupt processing on the worker thread. */
work_queue(ETHWORK, &priv->pollwork, lpc43_poll_work, priv, 0);
}
/****************************************************************************
* Function: lpc43_ifup
*
* Description:
* NuttX Callback: Bring up the Ethernet interface when an IP address is
* provided
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static int lpc43_ifup(struct net_driver_s *dev)
{
FAR struct lpc43_ethmac_s *priv =
(FAR struct lpc43_ethmac_s *)dev->d_private;
int ret;
#ifdef CONFIG_NET_IPv4
ninfo("Bringing up: %d.%d.%d.%d\n",
(int)(dev->d_ipaddr & 0xff),
(int)((dev->d_ipaddr >> 8) & 0xff),
(int)((dev->d_ipaddr >> 16) & 0xff),
(int)(dev->d_ipaddr >> 24));
#endif
#ifdef CONFIG_NET_IPv6
ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
#endif
/* Configure the Ethernet interface for DMA operation. */
ret = lpc43_ethconfig(priv);
if (ret < 0)
{
return ret;
}
/* Set and activate a timer process */
wd_start(&priv->txpoll, LPC43_WDDELAY,
lpc43_poll_expiry, (wdparm_t)priv);
/* Enable the Ethernet interrupt */
priv->ifup = true;
up_enable_irq(LPC43M4_IRQ_ETHERNET);
lpc43_checksetup();
return OK;
}
/****************************************************************************
* Function: lpc43_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static int lpc43_ifdown(struct net_driver_s *dev)
{
FAR struct lpc43_ethmac_s *priv =
(FAR struct lpc43_ethmac_s *)dev->d_private;
irqstate_t flags;
ninfo("Taking the network down\n");
/* Disable the Ethernet interrupt */
flags = enter_critical_section();
up_disable_irq(LPC43M4_IRQ_ETHERNET);
/* Cancel the TX poll timer and TX timeout timers */
wd_cancel(&priv->txpoll);
wd_cancel(&priv->txtimeout);
/* Put the EMAC in its reset, non-operational state. This should be
* a known configuration that will guarantee the lpc43_ifup() always
* successfully brings the interface back up.
*/
lpc43_ethreset(priv);
/* Mark the device "down" */
priv->ifup = false;
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Function: lpc43_txavail_work
*
* Description:
* Perform an out-of-cycle poll on the worker thread.
*
* Input Parameters:
* arg - Reference to the NuttX driver state structure (cast to void*)
*
* Returned Value:
* None
*
* Assumptions:
* Called on the higher priority worker thread.
*
****************************************************************************/
static void lpc43_txavail_work(FAR void *arg)
{
FAR struct lpc43_ethmac_s *priv = (FAR struct lpc43_ethmac_s *)arg;
/* Ignore the notification if the interface is not yet up */
net_lock();
ninfo("ifup: %d\n", priv->ifup);
if (priv->ifup)
{
/* Poll for new XMIT data */
lpc43_dopoll(priv);
}
net_unlock();
}
/****************************************************************************
* Function: lpc43_txavail
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Called in normal user mode
*
****************************************************************************/
static int lpc43_txavail(struct net_driver_s *dev)
{
FAR struct lpc43_ethmac_s *priv =
(FAR struct lpc43_ethmac_s *)dev->d_private;
/* Is our single work structure available? It may not be if there are
* pending interrupt actions and we will have to ignore the Tx
* availability action.
*/
if (work_available(&priv->pollwork))
{
/* Schedule to serialize the poll on the worker thread. */
work_queue(ETHWORK, &priv->pollwork, lpc43_txavail_work, priv, 0);
}
return OK;
}
/****************************************************************************
* Function: lpc43_calcethcrc
*
* Description:
* Function to calculate the CRC used by LPC43 to check an Ethernet frame
*
* Input Parameters:
* data - the data to be checked
* length - length of the data
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
static uint32_t lpc43_calcethcrc(const uint8_t *data, size_t length)
{
uint32_t crc = 0xffffffff;
size_t i;
int j;
for (i = 0; i < length; i++)
{
for (j = 0; j < 8; j++)
{
if (((crc >> 31) ^ (data[i] >> j)) & 0x01)
{
/* Return success, remembering where we should re-start
* scanning and resetting the segment scanning logic
*/
crc = (crc << 1) ^ 0x04c11db7;
}
else
{
crc = crc << 1;
}
}
}
return ~crc;
}
#endif
/****************************************************************************
* Function: lpc43_addmac
*
* Description:
* NuttX Callback: Add the specified MAC address to the hardware multicast
* address filtering
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be added
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
static int lpc43_addmac(struct net_driver_s *dev, FAR const uint8_t *mac)
{
uint32_t crc;
uint32_t hashindex;
uint32_t temp;
uint32_t registeraddress;
ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
/* Add the MAC address to the hardware multicast hash table */
2015-10-04 14:59:08 -06:00
crc = lpc43_calcethcrc(mac, 6);
hashindex = (crc >> 26) & 0x3f;
if (hashindex > 31)
{
registeraddress = LPC43_ETH_MACHTHI;
hashindex -= 32;
}
else
{
registeraddress = LPC43_ETH_MACHTLO;
}
temp = lpc43_getreg(registeraddress);
temp |= 1 << hashindex;
lpc43_putreg(temp, registeraddress);
temp = lpc43_getreg(LPC43_ETH_MACFFLT);
temp |= (ETH_MACFFLT_HM | ETH_MACFFLT_HPF);
lpc43_putreg(temp, LPC43_ETH_MACFFLT);
return OK;
}
#endif
/****************************************************************************
* Function: lpc43_rmmac
*
* Description:
* NuttX Callback: Remove the specified MAC address from the hardware
* multicast address filtering
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be removed
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
#ifdef CONFIG_NET_MCASTGROUP
static int lpc43_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac)
{
uint32_t crc;
uint32_t hashindex;
uint32_t temp;
uint32_t registeraddress;
ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
/* Remove the MAC address to the hardware multicast hash table */
2015-10-04 14:59:08 -06:00
crc = lpc43_calcethcrc(mac, 6);
hashindex = (crc >> 26) & 0x3f;
if (hashindex > 31)
{
registeraddress = LPC43_ETH_MACHTHI;
hashindex -= 32;
}
else
{
registeraddress = LPC43_ETH_MACHTLO;
}
temp = lpc43_getreg(registeraddress);
temp &= ~(1 << hashindex);
lpc43_putreg(temp, registeraddress);
/* If there is no address registered any more, delete multicast filtering */
2015-10-04 14:59:08 -06:00
if (lpc43_getreg(LPC43_ETH_MACHTHI) == 0 &&
lpc43_getreg(LPC43_ETH_MACHTLO) == 0)
{
temp = lpc43_getreg(LPC43_ETH_MACFFLT);
temp &= ~(ETH_MACFFLT_HM | ETH_MACFFLT_HPF);
lpc43_putreg(temp, LPC43_ETH_MACFFLT);
}
return OK;
}
#endif
/****************************************************************************
* Function: lpc43_txdescinit
*
* Description:
* Initializes the DMA TX descriptors in chain mode.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void lpc43_txdescinit(FAR struct lpc43_ethmac_s *priv)
{
struct eth_txdesc_s *txdesc;
int i;
/* priv->txhead point to the first, available TX descriptor in the chain.
* Set the priv->txhead pointer to the first descriptor in the table.
*/
priv->txhead = priv->txtable;
/* priv->txtail will point to the first segment of the oldest pending
* "in-flight" TX transfer. NULL means that there are no active TX
* transfers.
*/
priv->txtail = NULL;
priv->inflight = 0;
/* Initialize each TX descriptor */
for (i = 0; i < CONFIG_LPC43_ETH_NTXDESC; i++)
{
txdesc = &priv->txtable[i];
/* Set Second Address Chained bit */
txdesc->tdes0 = ETH_TDES0_TCH;
#ifdef CHECKSUM_BY_HARDWARE
/* Enable the checksum insertion for the TX frames */
txdesc->tdes0 |= ETH_TDES0_CIC_ALL;
#endif
/* Clear Buffer1 address pointer (buffers will be assigned as they
* are used)
*/
txdesc->tdes2 = 0;
/* Initialize the next descriptor with
* the Next Descriptor Polling Enable
*/
if (i < (CONFIG_LPC43_ETH_NTXDESC - 1))
{
/* Set next descriptor address register with next descriptor base
* address
*/
txdesc->tdes3 = (uint32_t)&priv->txtable[i + 1];
}
else
{
/* For last descriptor, set next descriptor address register equal
* to the first descriptor base address
*/
txdesc->tdes3 = (uint32_t)priv->txtable;
}
}
/* Set Transmit Descriptor List Address Register */
lpc43_putreg((uint32_t)priv->txtable, LPC43_ETH_DMATXDLA);
}
/****************************************************************************
* Function: lpc43_rxdescinit
*
* Description:
* Initializes the DMA RX descriptors in chain mode.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void lpc43_rxdescinit(FAR struct lpc43_ethmac_s *priv)
{
struct eth_rxdesc_s *rxdesc;
int i;
/* priv->rxhead will point to the first, RX descriptor in the chain.
* This will be where we receive the first incomplete frame.
*/
priv->rxhead = priv->rxtable;
/* If we accumulate the frame in segments, priv->rxcurr points to the
* RX descriptor of the first segment in the current TX frame.
*/
priv->rxcurr = NULL;
priv->segments = 0;
/* Initialize each TX descriptor */
for (i = 0; i < CONFIG_LPC43_ETH_NRXDESC; i++)
{
rxdesc = &priv->rxtable[i];
/* Set Own bit of the RX descriptor rdes0 */
rxdesc->rdes0 = ETH_RDES0_OWN;
/* Set Buffer1 size and Second Address Chained bit and enabled DMA
* RX desc receive interrupt
*/
rxdesc->rdes1 = ETH_RDES1_RCH | (uint32_t)CONFIG_LPC43_ETH_BUFSIZE;
/* Set Buffer1 address pointer */
rxdesc->rdes2 = (uint32_t)&priv->rxbuffer[i*CONFIG_LPC43_ETH_BUFSIZE];
/* Initialize the next descriptor with
* the Next Descriptor Polling Enable
*/
if (i < (CONFIG_LPC43_ETH_NRXDESC - 1))
{
/* Set next descriptor address register with next descriptor base
* address
*/
rxdesc->rdes3 = (uint32_t)&priv->rxtable[i + 1];
}
else
{
/* For last descriptor, set next descriptor address register equal
* to the first descriptor base address
*/
rxdesc->rdes3 = (uint32_t)priv->rxtable;
}
}
/* Set Receive Descriptor List Address Register */
lpc43_putreg((uint32_t)priv->rxtable, LPC43_ETH_DMARXDLA);
}
/****************************************************************************
* Function: lpc43_ioctl
*
* Description:
* Executes the SIOCxMIIxxx command and responds using the request struct
* that must be provided as its 2nd parameter.
*
* When called with SIOCGMIIPHY it will get the PHY address for the device
* and write it to the req->phy_id field of the request struct.
*
* When called with SIOCGMIIREG it will read a register of the PHY that is
* specified using the req->reg_no struct field and then write its output
* to the req->val_out field.
*
* When called with SIOCSMIIREG it will write to a register of the PHY that
* is specified using the req->reg_no struct field and use req->val_in as
* its input.
*
* Input Parameters:
* dev - Ethernet device structure
* cmd - SIOCxMIIxxx command code
* arg - Request structure also used to return values
*
* Returned Value: Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
#ifdef CONFIG_NETDEV_IOCTL
static int lpc43_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg)
{
#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
FAR struct lpc43_ethmac_s *priv =
(FAR struct lpc43_ethmac_s *)dev->d_private;
#endif
int ret;
switch (cmd)
{
#ifdef CONFIG_NETDEV_PHY_IOCTL
#ifdef CONFIG_ARCH_PHY_INTERRUPT
case SIOCMIINOTIFY: /* Set up for PHY event notifications */
{
struct mii_ioctl_notify_s *req =
(struct mii_ioctl_notify_s *)((uintptr_t)arg);
ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event);
if (ret == OK)
{
/* Enable PHY link up/down interrupts */
ret = lpc43_phyintenable(priv);
}
}
break;
#endif
case SIOCGMIIPHY: /* Get MII PHY address */
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
req->phy_id = CONFIG_LPC43_PHYADDR;
ret = OK;
}
break;
case SIOCGMIIREG: /* Get register from MII PHY */
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
ret = lpc43_phyread(req->phy_id, req->reg_num, &req->val_out);
}
break;
case SIOCSMIIREG: /* Set register in MII PHY */
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
ret = lpc43_phywrite(req->phy_id, req->reg_num, req->val_in);
}
break;
#endif /* ifdef CONFIG_NETDEV_PHY_IOCTL */
default:
ret = -ENOTTY;
break;
}
return ret;
}
#endif /* CONFIG_NETDEV_IOCTL */
/****************************************************************************
* Function: lpc43_phyintenable
*
* Description:
2015-10-07 20:24:19 -06:00
* Enable link up/down PHY interrupts. The interrupt protocol is like
* this:
*
* - Interrupt status is cleared when the interrupt is enabled.
* - Interrupt occurs. Interrupt is disabled (at the processor level) when
* is received.
* - Interrupt status is cleared when the interrupt is re-enabled.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno (-ETIMEDOUT) on failure.
*
****************************************************************************/
#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT)
static int lpc43_phyintenable(struct lpc43_ethmac_s *priv)
{
#warning Missing logic
return -ENOSYS;
}
#endif
/****************************************************************************
* Function: lpc43_phyread
*
* Description:
* Read a PHY register.
*
* Input Parameters:
* phydevaddr - The PHY device address
* phyregaddr - The PHY register address
* value - The location to return the 16-bit PHY register value.
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_phyread(uint16_t phydevaddr,
uint16_t phyregaddr, uint16_t *value)
{
volatile uint32_t timeout;
uint32_t regval;
/* Configure the MACMIIAR register,
* preserving CSR Clock Range CR[2:0] bits
*/
regval = lpc43_getreg(LPC43_ETH_MACMIIA);
regval &= ETH_MACMIIA_CR_MASK;
/* Set the PHY device address, PHY register address, and set the buy bit.
* the ETH_MACMIIA_WR is clear, indicating a read operation.
*/
regval |= (phydevaddr << ETH_MACMIIA_PA_SHIFT) & ETH_MACMIIA_PA_MASK;
regval |= (phyregaddr << ETH_MACMIIA_MR_SHIFT) & ETH_MACMIIA_MR_MASK;
regval |= ETH_MACMIIA_GB;
lpc43_putreg(regval, LPC43_ETH_MACMIIA);
/* Wait for the transfer to complete */
for (timeout = 0; timeout < PHY_READ_TIMEOUT; timeout++)
{
if ((lpc43_getreg(LPC43_ETH_MACMIIA) & ETH_MACMIIA_GB) == 0)
{
*value = (uint16_t)lpc43_getreg(LPC43_ETH_MACMIID);
return OK;
}
}
nerr("ERROR: MII transfer timed out: phydevaddr: %04x phyregaddr: %04x\n",
phydevaddr, phyregaddr);
return -ETIMEDOUT;
}
/****************************************************************************
* Function: lpc43_phywrite
*
* Description:
* Write to a PHY register.
*
* Input Parameters:
* phydevaddr - The PHY device address
* phyregaddr - The PHY register address
* value - The 16-bit value to write to the PHY register value.
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_phywrite(uint16_t phydevaddr,
uint16_t phyregaddr, uint16_t value)
{
volatile uint32_t timeout;
uint32_t regval;
/* Configure the MACMIIAR register,
* preserving CSR Clock Range CR[2:0] bits
*/
regval = lpc43_getreg(LPC43_ETH_MACMIIA);
regval &= ETH_MACMIIA_CR_MASK;
/* Set the PHY device address, PHY register address, and set the busy bit.
* the ETH_MACMIIA_WR is set, indicating a write operation.
*/
regval |= (phydevaddr << ETH_MACMIIA_PA_SHIFT) & ETH_MACMIIA_PA_MASK;
regval |= (phyregaddr << ETH_MACMIIA_MR_SHIFT) & ETH_MACMIIA_MR_MASK;
regval |= (ETH_MACMIIA_GB | ETH_MACMIIA_WR);
/* Write the value into the MACIIDR register before setting the new
* MACMIIAR register value.
*/
lpc43_putreg(value, LPC43_ETH_MACMIID);
lpc43_putreg(regval, LPC43_ETH_MACMIIA);
/* Wait for the transfer to complete */
for (timeout = 0; timeout < PHY_WRITE_TIMEOUT; timeout++)
{
if ((lpc43_getreg(LPC43_ETH_MACMIIA) & ETH_MACMIIA_GB) == 0)
{
return OK;
}
}
nerr("ERROR: MII transfer timed out: "
"phydevaddr: %04x phyregaddr: %04x value: %04x\n",
phydevaddr, phyregaddr, value);
return -ETIMEDOUT;
}
/****************************************************************************
* Function: lpc43_dm9161
*
* Description:
* Special workaround for the Davicom DM9161 PHY is required. On power,
* up, the PHY is not usually configured correctly but will work after
* a powered-up reset. This is really a workaround for some more
* fundamental issue with the PHY clocking initialization, but the
* root cause has not been studied (nor will it be with this workaround).
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_ETH0_PHY_DM9161
static inline int lpc43_dm9161(FAR struct lpc43_ethmac_s *priv)
{
uint16_t phyval;
int ret;
/* Read the PHYID1 register; A failure to read the PHY ID is one
* indication that check if the DM9161 PHY CHIP is not ready.
*/
ret = lpc43_phyread(CONFIG_LPC43_PHYADDR, MII_PHYID1, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY ID1: %d\n", ret);
return ret;
}
/* If we failed to read the PHY ID1 register,
* then reset the MCU to recover
*/
else if (phyval == 0xffff)
{
up_systemreset();
}
ninfo("PHY ID1: 0x%04X\n", phyval);
/* Now check the "DAVICOM Specified Configuration Register (DSCR)"(16) */
ret = lpc43_phyread(CONFIG_LPC43_PHYADDR, 16, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY Register 0x10: %d\n", ret);
return ret;
}
/* Bit 8 of the DSCR register is zero, then the DM9161 has not selected
* RMII. If RMII is not selected, then reset the MCU to recover.
*/
else if ((phyval & (1 << 8)) == 0)
{
up_systemreset();
}
return OK;
}
#endif
/****************************************************************************
* Function: lpc43_phyinit
*
* Description:
* Configure the PHY and determine the link speed/duplex.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_phyinit(FAR struct lpc43_ethmac_s *priv)
{
volatile uint32_t timeout;
uint32_t regval;
uint16_t phyval;
int ret;
/* Assume 10MBps and half duplex */
priv->mbps100 = 0;
priv->fduplex = 0;
/* Setup up PHY clocking by setting the SR field in the MACMIIAR register */
regval = lpc43_getreg(LPC43_ETH_MACMIIA);
regval &= ~ETH_MACMIIA_CR_MASK;
regval |= ETH_MACMIIA_CR;
lpc43_putreg(regval, LPC43_ETH_MACMIIA);
/* Put the PHY in reset mode */
ret = lpc43_phywrite(CONFIG_LPC43_PHYADDR, MII_MCR, MII_MCR_RESET);
if (ret < 0)
{
nerr("ERROR: Failed to reset the PHY: %d\n", ret);
return ret;
}
up_mdelay(PHY_RESET_DELAY);
#ifdef CONFIG_LPC43_PHYINIT
/* Perform any necessary, board-specific PHY initialization */
ret = lpc43_phy_boardinitialize(0);
if (ret < 0)
{
nerr("ERROR: Failed to initialize the PHY: %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_ETH0_PHY_DM9161
/* Special workaround for the Davicom DM9161 PHY is required. */
ret = lpc43_dm9161(priv);
if (ret < 0)
{
return ret;
}
#endif
/* Perform auto-negotiation if so configured */
#ifdef CONFIG_LPC43_AUTONEG
/* Wait for link status */
for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++)
{
ret = lpc43_phyread(CONFIG_LPC43_PHYADDR, MII_MSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY MSR: %d\n", ret);
return ret;
}
else if ((phyval & MII_MSR_LINKSTATUS) != 0)
{
break;
}
}
if (timeout >= PHY_RETRY_TIMEOUT)
{
nerr("ERROR: Timed out waiting for link status: %04x\n", phyval);
return -ETIMEDOUT;
}
/* Enable auto-negotiation */
ret = lpc43_phywrite(CONFIG_LPC43_PHYADDR, MII_MCR, MII_MCR_ANENABLE);
if (ret < 0)
{
nerr("ERROR: Failed to enable auto-negotiation: %d\n", ret);
return ret;
}
/* Wait until auto-negotiation completes */
for (timeout = 0; timeout < PHY_RETRY_TIMEOUT; timeout++)
{
ret = lpc43_phyread(CONFIG_LPC43_PHYADDR, MII_MSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read the PHY MSR: %d\n", ret);
return ret;
}
else if ((phyval & MII_MSR_ANEGCOMPLETE) != 0)
{
break;
}
}
if (timeout >= PHY_RETRY_TIMEOUT)
{
nerr("ERROR: Timed out waiting for auto-negotiation\n");
return -ETIMEDOUT;
}
/* Read the result of the auto-negotiation from the PHY-specific register */
ret = lpc43_phyread(CONFIG_LPC43_PHYADDR, CONFIG_LPC43_PHYSR, &phyval);
if (ret < 0)
{
nerr("ERROR: Failed to read PHY status register\n");
return ret;
}
/* Remember the selected speed and duplex modes */
ninfo("PHYSR[%d]: %04x\n", CONFIG_LPC43_PHYSR, phyval);
#ifdef CONFIG_ETH0_PHY_LAN8720
if ((phyval & (MII_MSR_100BASETXHALF | MII_MSR_100BASETXFULL)) != 0)
{
priv->mbps100 = 1;
}
if ((phyval & (MII_MSR_100BASETXFULL | MII_MSR_10BASETXFULL)) != 0)
{
priv->fduplex = 1;
}
#else
/* Different PHYs present speed and mode information in different ways.
* IF This CONFIG_LPC43_PHYSR_ALTCONFIG is selected, this indicates that
* the PHY represents speed and mode information are combined, for example,
* with separate bits for 10HD, 100HD, 10FD and 100FD.
*/
#ifdef CONFIG_LPC43_PHYSR_ALTCONFIG
switch (phyval & CONFIG_LPC43_PHYSR_ALTMODE)
{
case CONFIG_LPC43_PHYSR_100FD:
priv->fduplex = 1;
priv->mbps100 = 1;
break;
case CONFIG_LPC43_PHYSR_100HD:
priv->fduplex = 0;
priv->mbps100 = 1;
break;
case CONFIG_LPC43_PHYSR_10FD:
priv->fduplex = 1;
priv->mbps100 = 0;
break;
default:
case CONFIG_LPC43_PHYSR_10HD:
priv->fduplex = 0;
priv->mbps100 = 0;
break;
}
/* Different PHYs present speed and mode information in different ways.
* Some will present separate information for speed and mode (this is the
* default). Those PHYs, for example, may provide a 10/100 Mbps indication
* and a separate full/half duplex indication.
*/
#else
if ((phyval & CONFIG_LPC43_PHYSR_MODE) == CONFIG_LPC43_PHYSR_FULLDUPLEX)
{
priv->fduplex = 1;
}
if ((phyval & CONFIG_LPC43_PHYSR_SPEED) == CONFIG_LPC43_PHYSR_100MBPS)
{
priv->mbps100 = 1;
}
#endif
#endif
#else /* Auto-negotiation not selected */
#ifdef CONFIG_LPC43_ETHFD
priv->fduplex = 1;
#endif
#ifdef CONFIG_LPC43_ETH100MBPS
priv->mbps100 = 1;
#endif
phyval = 0;
if (priv->mbps100)
{
phyval |= MII_MCR_FULLDPLX;
}
if (priv->fduplex)
{
phyval |= MII_MCR_SPEED100;
}
ret = lpc43_phywrite(CONFIG_LPC43_PHYADDR, MII_MCR, phyval);
if (ret < 0)
{
nerr("ERROR: Failed to write the PHY MCR: %d\n", ret);
return ret;
}
up_mdelay(PHY_CONFIG_DELAY);
/* Remember the selected speed and duplex modes
* REVISIT: Isn't this redundant?
*/
#ifdef CONFIG_LPC43_ETHFD
priv->fduplex = 1;
#endif
#ifdef CONFIG_LPC43_ETH100MBPS
priv->mbps100 = 1;
#endif
#endif
ninfo("Duplex: %s Speed: %d MBps\n",
priv->fduplex ? "FULL" : "HALF",
priv->mbps100 ? 100 : 10);
return OK;
}
/****************************************************************************
* Name: lpc43_selectmii
*
* Description:
* Selects the MII interface.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_LPC43_MII
static inline void lpc43_selectmii(void)
{
uint32_t regval;
regval = getreg32(LPC43_CREG6);
regval &= ~SYSCFG_PMC_MII_RMII_SEL;
putreg32(regval, LPC43_CREG6);
}
#endif
/****************************************************************************
* Name: lpc43_selectrmii
*
* Description:
* Selects the RMII interface.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static inline void lpc43_selectrmii(void)
{
uint32_t regval;
regval = getreg32(LPC43_CREG6);
regval |= CREG6_ETHMODE_RMII;
putreg32(regval, LPC43_CREG6);
}
/****************************************************************************
* Function: lpc43_ethgpioconfig
*
* Description:
* Configure GPIOs for the Ethernet interface.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* None.
*
* Assumptions:
*
****************************************************************************/
static inline void lpc43_ethgpioconfig(FAR struct lpc43_ethmac_s *priv)
{
/* Configure GPIO pins to support Ethernet */
#if defined(CONFIG_LPC43_MII) || defined(CONFIG_LPC43_RMII)
/* MDC and MDIO are common to both modes */
lpc43_pin_config(PINCONF_ENET_MDC);
lpc43_pin_config(PINCONF_ENET_MDIO);
/* Set up the MII interface */
#if defined(CONFIG_LPC43_MII)
/* Select the MII interface */
lpc43_selectmii();
/* MII interface pins (17):
*
* MII_TX_CLK, MII_TXD[3:0], MII_TX_EN, MII_RX_CLK, MII_RXD[3:0],
* MII_RX_ER, MII_RX_DV, MII_CRS, MII_COL, MDC, MDIO
*/
lpc43_pin_config(PINCONF_ENET_MII_COL);
lpc43_pin_config(PINCONF_ENET_MII_CRS);
lpc43_pin_config(PINCONF_ENET_MII_RXD0);
lpc43_pin_config(PINCONF_ENET_MII_RXD1);
lpc43_pin_config(PINCONF_ENET_MII_RXD2);
lpc43_pin_config(PINCONF_ENET_MII_RXD3);
lpc43_pin_config(PINCONF_ENET_MII_RX_CLK);
lpc43_pin_config(PINCONF_ENET_MII_RX_DV);
lpc43_pin_config(PINCONF_ENET_MII_RX_ER);
lpc43_pin_config(PINCONF_ENET_MII_TXD0);
lpc43_pin_config(PINCONF_ENET_MII_TXD1);
lpc43_pin_config(PINCONF_ENET_MII_TXD2);
lpc43_pin_config(PINCONF_ENET_MII_TXD3);
lpc43_pin_config(PINCONF_ENET_MII_TX_CLK);
lpc43_pin_config(PINCONF_ENET_MII_TX_EN);
lpc43_pin_config(PINCONF_ENET_MII_TX_ER);
/* Set up the RMII interface. */
#elif defined(CONFIG_LPC43_RMII)
/* Select the RMII interface */
lpc43_selectrmii();
/* RMII interface pins (7):
*
* RMII_TXD[1:0], RMII_TX_EN, RMII_RXD[1:0], RMII_CRS_DV, MDC, MDIO,
* RMII_REF_CLK
*/
lpc43_pin_config(PINCONF_ENET_RX_DV);
lpc43_pin_config(PINCONF_ENET_REF_CLK);
lpc43_pin_config(PINCONF_ENET_RXD0);
lpc43_pin_config(PINCONF_ENET_RXD1);
lpc43_pin_config(PINCONF_ENET_TXD0);
lpc43_pin_config(PINCONF_ENET_TXD1);
lpc43_pin_config(PINCONF_ENET_TX_EN);
#ifdef PINCONF_ENET_RESET
lpc43_pin_config(PINCONF_ENET_RESET);
lpc43_gpio_config(GPIO_ENET_RESET);
lpc43_gpio_write(GPIO_ENET_RESET, 0);
up_mdelay(5);
lpc43_gpio_write(GPIO_ENET_RESET, 1);
up_mdelay(5);
#endif
#endif
#endif
}
/****************************************************************************
* Function: lpc43_ethreset
*
* Description:
* Reset the Ethernet block.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* None.
*
* Assumptions:
*
****************************************************************************/
static void lpc43_ethreset(FAR struct lpc43_ethmac_s *priv)
{
uint32_t regval;
lpc43_putreg(CCU_CLK_CFG_RUN | CCU_CLK_CFG_AUTO | CCU_CLK_CFG_WAKEUP,
LPC43_CCU1_M4_ETHERNET_CFG);
/* Reset the Ethernet */
lpc43_putreg(RGU_CTRL0_ETHERNET_RST, LPC43_RGU_CTRL0);
regval = lpc43_getreg(LPC43_ETH_DMABMODE);
regval |= ETH_DMABMODE_SWR;
lpc43_putreg(regval, LPC43_ETH_DMABMODE);
/* Wait for software reset to complete. The SR bit is cleared automatically
* after the reset operation has completed in all core clock domains.
*/
while ((lpc43_getreg(LPC43_ETH_DMABMODE) & ETH_DMABMODE_SWR) != 0);
}
/****************************************************************************
* Function: lpc43_macconfig
*
* Description:
* Configure the Ethernet MAC for DMA operation.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_macconfig(FAR struct lpc43_ethmac_s *priv)
{
uint32_t regval;
/* Set up the MACCR register */
regval = lpc43_getreg(LPC43_ETH_MACCR);
regval &= ~MACCR_CLEAR_BITS;
regval |= MACCR_SET_BITS;
if (priv->fduplex)
{
/* Set the DM bit for full duplex support */
regval |= ETH_MACCFG_DM;
}
if (priv->mbps100)
{
/* Set the FES bit for 100Mbps fast ethernet support */
regval |= ETH_MACCFG_FES;
}
lpc43_putreg(regval, LPC43_ETH_MACCR);
/* Set up the MACFFR register */
regval = lpc43_getreg(LPC43_ETH_MACFFLT);
regval &= ~MACFFR_CLEAR_BITS;
regval |= MACFFR_SET_BITS;
lpc43_putreg(regval, LPC43_ETH_MACFFLT);
/* Set up the MACHTHR and MACHTLR registers */
lpc43_putreg(0, LPC43_ETH_MACHTHI);
lpc43_putreg(0, LPC43_ETH_MACHTLO);
/* Setup up the MACFCR register */
regval = lpc43_getreg(LPC43_ETH_MACFC);
regval &= ~MACFCR_CLEAR_MASK;
regval |= MACFCR_SET_MASK;
lpc43_putreg(regval, LPC43_ETH_MACFC);
/* Setup up the MACVLANTR register */
lpc43_putreg(0, LPC43_ETH_MACVLANT);
/* DMA Configuration */
/* Set up the DMAOMR register */
regval = lpc43_getreg(LPC43_ETH_DMAOPMODE);
regval &= ~DMAOMR_CLEAR_MASK;
regval |= DMAOMR_SET_MASK;
lpc43_putreg(regval, LPC43_ETH_DMAOPMODE);
/* Set up the DMABMR register */
regval = lpc43_getreg(LPC43_ETH_DMABMODE);
regval &= ~DMABMR_CLEAR_MASK;
regval |= DMABMR_SET_MASK;
lpc43_putreg(regval, LPC43_ETH_DMABMODE);
return OK;
}
/****************************************************************************
* Function: lpc43_macaddress
*
* Description:
* Configure the selected MAC address.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static void lpc43_macaddress(FAR struct lpc43_ethmac_s *priv)
{
FAR struct net_driver_s *dev = &priv->dev;
uint32_t regval;
ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
dev->d_ifname,
dev->d_mac.ether.ether_addr_octet[0],
dev->d_mac.ether.ether_addr_octet[1],
dev->d_mac.ether.ether_addr_octet[2],
dev->d_mac.ether.ether_addr_octet[3],
dev->d_mac.ether.ether_addr_octet[4],
dev->d_mac.ether.ether_addr_octet[5]);
/* Set the MAC address high register */
regval = ((uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8) |
(uint32_t)dev->d_mac.ether.ether_addr_octet[4];
lpc43_putreg(regval, LPC43_ETH_MACA0HI);
/* Set the MAC address low register */
regval = ((uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24) |
((uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16) |
((uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8) |
(uint32_t)dev->d_mac.ether.ether_addr_octet[0];
lpc43_putreg(regval, LPC43_ETH_MACA0LO);
}
/****************************************************************************
* Function: lpc43_ipv6multicast
*
* Description:
* Configure the IPv6 multicast MAC address.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
#ifdef CONFIG_NET_ICMPv6
static void lpc43_ipv6multicast(FAR struct lpc43_ethmac_s *priv)
{
struct net_driver_s *dev;
uint16_t tmp16;
uint8_t mac[6];
/* For ICMPv6, we need to add the IPv6 multicast address
*
* For IPv6 multicast addresses, the Ethernet MAC is derived by
* the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
* so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
* to the Ethernet MAC address 33:33:00:01:00:03.
*
* NOTES: This appears correct for the ICMPv6 Router Solicitation
* Message, but the ICMPv6 Neighbor Solicitation message seems to
* use 33:33:ff:01:00:03.
*/
mac[0] = 0x33;
mac[1] = 0x33;
dev = &priv->dev;
tmp16 = dev->d_ipv6addr[6];
mac[2] = 0xff;
mac[3] = tmp16 >> 8;
tmp16 = dev->d_ipv6addr[7];
mac[4] = tmp16 & 0xff;
mac[5] = tmp16 >> 8;
ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
lpc43_addmac(dev, mac);
#ifdef CONFIG_NET_ICMPv6_AUTOCONF
/* Add the IPv6 all link-local nodes Ethernet address. This is the
* address that we expect to receive ICMPv6 Router Advertisement
* packets.
*/
lpc43_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
#ifdef CONFIG_NET_ICMPv6_ROUTER
/* Add the IPv6 all link-local routers Ethernet address. This is the
* address that we expect to receive ICMPv6 Router Solicitation
* packets.
*/
lpc43_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
#endif /* CONFIG_NET_ICMPv6_ROUTER */
}
#endif /* CONFIG_NET_ICMPv6 */
/****************************************************************************
* Function: lpc43_macenable
*
* Description:
* Enable normal MAC operation.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_macenable(FAR struct lpc43_ethmac_s *priv)
{
uint32_t regval;
/* Set the MAC address */
lpc43_macaddress(priv);
#ifdef CONFIG_NET_ICMPv6
/* Set up the IPv6 multicast address */
lpc43_ipv6multicast(priv);
#endif
/* Enable transmit state machine of the MAC for transmission on the MII */
regval = lpc43_getreg(LPC43_ETH_MACCR);
regval |= ETH_MACCFG_TE;
lpc43_putreg(regval, LPC43_ETH_MACCR);
/* Flush Transmit FIFO */
regval = lpc43_getreg(LPC43_ETH_DMAOPMODE);
regval |= ETH_DMAOPMODE_FTF;
lpc43_putreg(regval, LPC43_ETH_DMAOPMODE);
/* Enable receive state machine of the MAC for reception from the MII */
/* Enables or disables the MAC reception. */
regval = lpc43_getreg(LPC43_ETH_MACCR);
regval |= ETH_MACCFG_RE;
lpc43_putreg(regval, LPC43_ETH_MACCR);
/* Start DMA transmission */
regval = lpc43_getreg(LPC43_ETH_DMAOPMODE);
regval |= ETH_DMAOPMODE_ST;
lpc43_putreg(regval, LPC43_ETH_DMAOPMODE);
/* Start DMA reception */
regval = lpc43_getreg(LPC43_ETH_DMAOPMODE);
regval |= ETH_DMAOPMODE_SR;
lpc43_putreg(regval, LPC43_ETH_DMAOPMODE);
/* Enable Ethernet DMA interrupts.
*
* The LPC43 hardware supports two interrupts: (1) one dedicated to normal
* Ethernet operations and the other, used only for the Ethernet wakeup
* event. The wake-up interrupt is not used by this driver.
*
* The first Ethernet vector is reserved for interrupts generated by the
* MAC and the DMA. The MAC provides PMT and time stamp trigger interrupts,
* neither of which are used by this driver.
*/
lpc43_putreg(ETH_MACIM_ALLINTS, LPC43_ETH_MACIM);
/* Ethernet DMA supports two classes of interrupts: Normal interrupt
* summary (NIS) and Abnormal interrupt summary (AIS) with a variety
* individual normal and abnormal interrupting events. Here only
* the normal receive event is enabled (unless DEBUG is enabled). Transmit
* events will only be enabled when a transmit interrupt is expected.
*/
lpc43_putreg((ETH_DMAINT_RECV_ENABLE | ETH_DMAINT_ERROR_ENABLE),
LPC43_ETH_DMAINTEN);
return OK;
}
/****************************************************************************
* Function: lpc43_ethconfig
*
* Description:
* Configure the Ethernet interface for DMA operation.
*
* Input Parameters:
* priv - A reference to the private driver state structure
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static int lpc43_ethconfig(FAR struct lpc43_ethmac_s *priv)
{
int ret;
/* NOTE: The Ethernet clocks were initialized early in the boot-up
* sequence in lpc43_rcc.c.
*/
/* Reset the Ethernet block */
ninfo("Reset the Ethernet block\n");
lpc43_ethreset(priv);
/* Initialize the PHY */
ninfo("Initialize the PHY\n");
ret = lpc43_phyinit(priv);
if (ret < 0)
{
return ret;
}
/* Perform a software reset by setting the SR bit in the DMABMR register.
* This Resets all MAC subsystem internal registers and logic. After this
* reset all the registers holds their reset values.
*/
/* Initialize the MAC and DMA */
ninfo("Initialize the MAC and DMA\n");
ret = lpc43_macconfig(priv);
if (ret < 0)
{
return ret;
}
/* Initialize the free buffer list */
lpc43_initbuffer(priv);
/* Initialize TX Descriptors list: Chain Mode */
lpc43_txdescinit(priv);
/* Initialize RX Descriptors list: Chain Mode */
lpc43_rxdescinit(priv);
/* Enable normal MAC operation */
ninfo("Enable normal operation\n");
return lpc43_macenable(priv);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Function: lpc43_ethinitialize
*
* Description:
* Initialize the Ethernet driver for one interface.
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
*
****************************************************************************/
static inline int lpc43_ethinitialize(void)
{
struct lpc43_ethmac_s *priv;
/* Get the interface structure associated with this interface number. */
priv = &g_lpc43ethmac;
/* Initialize the driver structure */
memset(priv, 0, sizeof(struct lpc43_ethmac_s));
priv->dev.d_ifup = lpc43_ifup; /* I/F up (new IP address) callback */
priv->dev.d_ifdown = lpc43_ifdown; /* I/F down callback */
priv->dev.d_txavail = lpc43_txavail; /* New TX data callback */
#ifdef CONFIG_NET_MCASTGROUP
priv->dev.d_addmac = lpc43_addmac; /* Add multicast MAC address */
priv->dev.d_rmmac = lpc43_rmmac; /* Remove multicast MAC address */
#endif
#ifdef CONFIG_NETDEV_IOCTL
priv->dev.d_ioctl = lpc43_ioctl; /* Support PHY ioctl() calls */
#endif
priv->dev.d_private = &g_lpc43ethmac; /* Used to recover private state from dev */
/* Configure GPIO pins to support Ethernet */
lpc43_ethgpioconfig(priv);
/* Attach the IRQ to the driver */
if (irq_attach(LPC43M4_IRQ_ETHERNET, lpc43_interrupt, NULL))
{
/* We could not attach the ISR to the interrupt */
return -EAGAIN;
}
/* Put the interface in the down state. */
lpc43_ifdown(&priv->dev);
/* Register the device with the OS so that socket IOCTLs can be performed */
netdev_register(&priv->dev, NET_LL_ETHERNET);
return OK;
}
/****************************************************************************
* Function: arm_netinitialize
*
* Description:
* This is the "standard" network initialization logic called from the
* low-level initialization logic in arm_initialize.c.
*
* Input Parameters:
* None.
*
* Returned Value:
* None.
*
* Assumptions:
*
****************************************************************************/
#ifndef CONFIG_NETDEV_LATEINIT
void arm_netinitialize(void)
{
lpc43_ethinitialize();
}
#endif
#endif /* CONFIG_NET && CONFIG_LPC43_ETHERNET */