df43e1b890
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4450 42af7a65-404d-4744-a932-0658087f49c3
2503 lines
71 KiB
C
2503 lines
71 KiB
C
/****************************************************************************
|
|
* arch/arm/src/lpc17xx/lpc17_ethernet.c
|
|
*
|
|
* Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#if defined(CONFIG_NET) && defined(CONFIG_LPC17_ETHERNET)
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
#include <wdog.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/net/mii.h>
|
|
|
|
#include <nuttx/net/uip/uip.h>
|
|
#include <nuttx/net/uip/uipopt.h>
|
|
#include <nuttx/net/uip/uip-arp.h>
|
|
#include <nuttx/net/uip/uip-arch.h>
|
|
|
|
#include "chip.h"
|
|
#include "up_arch.h"
|
|
#include "lpc17_syscon.h"
|
|
#include "lpc17_ethernet.h"
|
|
#include "lpc17_emacram.h"
|
|
#include "lpc17_internal.h"
|
|
|
|
#include <arch/board/board.h>
|
|
|
|
/* Does this chip have and ethernet controller? */
|
|
|
|
#if LPC17_NETHCONTROLLERS > 0
|
|
|
|
/****************************************************************************
|
|
* Definitions
|
|
****************************************************************************/
|
|
|
|
/* Configuration ************************************************************/
|
|
/* CONFIG_LPC17_NINTERFACES determines the number of physical interfaces
|
|
* that will be supported -- unless it is more than actually supported by the
|
|
* hardware!
|
|
*/
|
|
|
|
#if !defined(CONFIG_LPC17_NINTERFACES) || CONFIG_LPC17_NINTERFACES > LPC17_NETHCONTROLLERS
|
|
# undef CONFIG_LPC17_NINTERFACES
|
|
# define CONFIG_LPC17_NINTERFACES LPC17_NETHCONTROLLERS
|
|
#endif
|
|
|
|
/* The logic here has a few hooks for support for multiple interfaces, but
|
|
* that capability is not yet in place (and I won't worry about it until I get
|
|
* the first multi-interface LPC17xx).
|
|
*/
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
# warning "Only a single ethernet controller is supported"
|
|
# undef CONFIG_LPC17_NINTERFACES
|
|
# define CONFIG_LPC17_NINTERFACES 1
|
|
#endif
|
|
|
|
/* If IGMP is enabled, then accept multi-cast frames. */
|
|
|
|
#if defined(CONFIG_NET_IGMP) && !defined(CONFIG_NET_MULTICAST)
|
|
# define CONFIG_NET_MULTICAST 1
|
|
#endif
|
|
|
|
/* If the user did not specify a priority for Ethernet interrupts, set the
|
|
* interrupt priority to the maximum.
|
|
*/
|
|
|
|
#ifndef CONFIG_NET_PRIORITY
|
|
# define CONFIG_NET_PRIORITY NVIC_SYSH_PRIORITY_MAX
|
|
#endif
|
|
|
|
/* Debug Configuration *****************************************************/
|
|
/* Register debug -- can only happen of CONFIG_DEBUG is selected */
|
|
|
|
#ifndef CONFIG_DEBUG
|
|
# undef CONFIG_NET_REGDEBUG
|
|
#endif
|
|
|
|
/* CONFIG_NET_DUMPPACKET will dump the contents of each packet to the
|
|
* console.
|
|
*/
|
|
|
|
#ifndef CONFIG_DEBUG
|
|
# undef CONFIG_NET_DUMPPACKET
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_DUMPPACKET
|
|
# define lpc17_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
|
|
#else
|
|
# define lpc17_dumppacket(m,a,n)
|
|
#endif
|
|
|
|
/* Timing *******************************************************************/
|
|
|
|
/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
|
|
|
|
#define LPC17_WDDELAY (1*CLK_TCK)
|
|
#define LPC17_POLLHSEC (1*2)
|
|
|
|
/* TX timeout = 1 minute */
|
|
|
|
#define LPC17_TXTIMEOUT (60*CLK_TCK)
|
|
|
|
/* Interrupts ***************************************************************/
|
|
|
|
#define ETH_RXINTS (ETH_INT_RXOVR | ETH_INT_RXERR | ETH_INT_RXFIN | ETH_INT_RXDONE)
|
|
#define ETH_TXINTS (ETH_INT_TXUNR | ETH_INT_TXERR | ETH_INT_TXFIN | ETH_INT_TXDONE)
|
|
|
|
/* Misc. Helpers ***********************************************************/
|
|
|
|
/* This is a helper pointer for accessing the contents of the Ethernet header */
|
|
|
|
#define BUF ((struct uip_eth_hdr *)priv->lp_dev.d_buf)
|
|
|
|
/* This is the number of ethernet GPIO pins that must be configured */
|
|
|
|
#define GPIO_NENET_PINS 10
|
|
|
|
/* PHYs *********************************************************************/
|
|
/* Select PHY-specific values. Add more PHYs as needed. */
|
|
|
|
#if defined(CONFIG_PHY_KS8721)
|
|
# define LPC17_PHYNAME "KS8721"
|
|
# define LPC17_PHYID1 MII_PHYID1_KS8721
|
|
# define LPC17_PHYID2 MII_PHYID2_KS8721
|
|
# define LPC17_HAVE_PHY 1
|
|
#elif defined(CONFIG_PHY_DP83848C)
|
|
# define LPC17_PHYNAME "DP83848C"
|
|
# define LPC17_PHYID1 MII_PHYID1_DP83848C
|
|
# define LPC17_PHYID2 MII_PHYID2_DP83848C
|
|
# define LPC17_HAVE_PHY 1
|
|
#elif defined(CONFIG_PHY_LAN8720)
|
|
# define LPC17_PHYNAME "LAN8720"
|
|
# define LPC17_PHYID1 MII_PHYID1_LAN8720
|
|
# define LPC17_PHYID2 MII_PHYID2_LAN8720
|
|
# define LPC17_HAVE_PHY 1
|
|
#else
|
|
# warning "No PHY specified!"
|
|
# undef LPC17_HAVE_PHY
|
|
#endif
|
|
|
|
#define MII_BIG_TIMEOUT 666666
|
|
|
|
/* These definitions are used to remember the speed/duplex settings */
|
|
|
|
#define LPC17_SPEED_MASK 0x01
|
|
#define LPC17_SPEED_100 0x01
|
|
#define LPC17_SPEED_10 0x00
|
|
|
|
#define LPC17_DUPLEX_MASK 0x02
|
|
#define LPC17_DUPLEX_FULL 0x02
|
|
#define LPC17_DUPLEX_HALF 0x00
|
|
|
|
#define LPC17_10BASET_HD (LPC17_SPEED_10 | LPC17_DUPLEX_HALF)
|
|
#define LPC17_10BASET_FD (LPC17_SPEED_10 | LPC17_DUPLEX_FULL)
|
|
#define LPC17_100BASET_HD (LPC17_SPEED_100 | LPC17_DUPLEX_HALF)
|
|
#define LPC17_100BASET_FD (LPC17_SPEED_100 | LPC17_DUPLEX_FULL)
|
|
|
|
#ifdef CONFIG_PHY_SPEED100
|
|
# ifdef CONFIG_PHY_FDUPLEX
|
|
# define LPC17_MODE_DEFLT LPC17_100BASET_FD
|
|
# else
|
|
# define LPC17_MODE_DEFLT LPC17_100BASET_HD
|
|
# endif
|
|
#else
|
|
# ifdef CONFIG_PHY_FDUPLEX
|
|
# define LPC17_MODE_DEFLT LPC17_10BASET_FD
|
|
# else
|
|
# define LPC17_MODE_DEFLT LPC17_10BASET_HD
|
|
# endif
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* EMAC statistics (debug only) */
|
|
|
|
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
|
|
struct lpc17_statistics_s
|
|
{
|
|
#ifdef ENABLE_WOL
|
|
uint32_t wol; /* Wake-up interrupts */
|
|
#endif
|
|
uint32_t rx_finished; /* Rx finished interrupts */
|
|
uint32_t rx_done; /* Rx done interrupts */
|
|
uint32_t rx_ovrerrors; /* Number of Rx overrun error interrupts */
|
|
uint32_t rx_errors; /* Number of Rx error interrupts (OR of other errors) */
|
|
uint32_t rx_packets; /* Number of packets received (sum of the following): */
|
|
uint32_t rx_ip; /* Number of Rx IP packets received */
|
|
uint32_t rx_arp; /* Number of Rx ARP packets received */
|
|
uint32_t rx_dropped; /* Number of dropped, unsupported Rx packets */
|
|
uint32_t rx_pkterr; /* Number of dropped, error in Rx descriptor */
|
|
uint32_t rx_pktsize; /* Number of dropped, too small or too big */
|
|
uint32_t rx_fragment; /* Number of dropped, packet fragments */
|
|
|
|
uint32_t tx_packets; /* Number of Tx packets queued */
|
|
uint32_t tx_pending; /* Number of Tx packets that had to wait for a TxDesc */
|
|
uint32_t tx_unpend; /* Number of pending Tx packets that were sent */
|
|
uint32_t tx_finished; /* Tx finished interrupts */
|
|
uint32_t tx_done; /* Tx done interrupts */
|
|
uint32_t tx_underrun; /* Number of Tx underrun error interrupts */
|
|
uint32_t tx_errors; /* Number of Tx error inerrupts (OR of other errors) */
|
|
uint32_t tx_timeouts; /* Number of Tx timeout errors */
|
|
};
|
|
# define EMAC_STAT(priv,name) priv->lp_stat.name++
|
|
#else
|
|
# define EMAC_STAT(priv,name)
|
|
#endif
|
|
|
|
/* The lpc17_driver_s encapsulates all state information for a single hardware
|
|
* interface
|
|
*/
|
|
|
|
struct lpc17_driver_s
|
|
{
|
|
/* The following fields would only be necessary on chips that support
|
|
* multiple Ethernet controllers.
|
|
*/
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
uint32_t lp_base; /* Ethernet controller base address */
|
|
int lp_irq; /* Ethernet controller IRQ */
|
|
#endif
|
|
|
|
bool lp_ifup; /* true:ifup false:ifdown */
|
|
bool lp_mode; /* speed/duplex */
|
|
bool lp_txpending; /* There is a pending Tx in lp_dev */
|
|
#ifdef LPC17_HAVE_PHY
|
|
uint8_t lp_phyaddr; /* PHY device address */
|
|
#endif
|
|
uint32_t lp_inten; /* Shadow copy of INTEN register */
|
|
WDOG_ID lp_txpoll; /* TX poll timer */
|
|
WDOG_ID lp_txtimeout; /* TX timeout timer */
|
|
|
|
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
|
|
struct lpc17_statistics_s lp_stat;
|
|
#endif
|
|
|
|
/* This holds the information visible to uIP/NuttX */
|
|
|
|
struct uip_driver_s lp_dev; /* Interface understood by uIP */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Array of ethernet driver status structures */
|
|
|
|
static struct lpc17_driver_s g_ethdrvr[CONFIG_LPC17_NINTERFACES];
|
|
|
|
/* ENET pins are on P1[0,1,4,6,8,9,10,14,15] + MDC on P1[16] or P2[8] and
|
|
* MDIO on P1[17] or P2[9]. The board.h file will define GPIO_ENET_MDC and
|
|
* PGIO_ENET_MDIO to selec which pin setting to use.
|
|
*
|
|
* On older Rev '-' devices, P1[6] ENET-TX_CLK would also have be to configured.
|
|
*/
|
|
|
|
static const uint16_t g_enetpins[GPIO_NENET_PINS] =
|
|
{
|
|
GPIO_ENET_TXD0, GPIO_ENET_TXD1, GPIO_ENET_TXEN, GPIO_ENET_CRS, GPIO_ENET_RXD0,
|
|
GPIO_ENET_RXD1, GPIO_ENET_RXER, GPIO_ENET_REFCLK, GPIO_ENET_MDC, GPIO_ENET_MDIO
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Register operations */
|
|
|
|
#ifdef CONFIG_NET_REGDEBUG
|
|
static void lpc17_printreg(uint32_t addr, uint32_t val, bool iswrite);
|
|
static void lpc17_checkreg(uint32_t addr, uint32_t val, bool iswrite);
|
|
static uint32_t lpc17_getreg(uint32_t addr);
|
|
static void lpc17_putreg(uint32_t val, uint32_t addr);
|
|
#else
|
|
# define lpc17_getreg(addr) getreg32(addr)
|
|
# define lpc17_putreg(val,addr) putreg32(val,addr)
|
|
#endif
|
|
|
|
/* Common TX logic */
|
|
|
|
static int lpc17_txdesc(struct lpc17_driver_s *priv);
|
|
static int lpc17_transmit(struct lpc17_driver_s *priv);
|
|
static int lpc17_uiptxpoll(struct uip_driver_s *dev);
|
|
|
|
/* Interrupt handling */
|
|
|
|
static void lpc17_response(struct lpc17_driver_s *priv);
|
|
static void lpc17_rxdone(struct lpc17_driver_s *priv);
|
|
static void lpc17_txdone(struct lpc17_driver_s *priv);
|
|
static int lpc17_interrupt(int irq, void *context);
|
|
|
|
/* Watchdog timer expirations */
|
|
|
|
static void lpc17_polltimer(int argc, uint32_t arg, ...);
|
|
static void lpc17_txtimeout(int argc, uint32_t arg, ...);
|
|
|
|
/* NuttX callback functions */
|
|
|
|
static int lpc17_ifup(struct uip_driver_s *dev);
|
|
static int lpc17_ifdown(struct uip_driver_s *dev);
|
|
static int lpc17_txavail(struct uip_driver_s *dev);
|
|
#ifdef CONFIG_NET_IGMP
|
|
static int lpc17_addmac(struct uip_driver_s *dev, const uint8_t *mac);
|
|
static int lpc17_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
|
|
#endif
|
|
|
|
/* Initialization functions */
|
|
|
|
#if defined(CONFIG_NET_REGDEBUG) && defined(CONFIG_DEBUG_GPIO)
|
|
static void lpc17_showpins(void);
|
|
#else
|
|
# define lpc17_showpins()
|
|
#endif
|
|
|
|
/* PHY initialization functions */
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
# ifdef CONFIG_NET_REGDEBUG
|
|
static void lpc17_showmii(uint8_t phyaddr, const char *msg);
|
|
# else
|
|
# define lpc17_showmii(phyaddr,msg)
|
|
# endif
|
|
|
|
static void lpc17_phywrite(uint8_t phyaddr, uint8_t regaddr,
|
|
uint16_t phydata);
|
|
static uint16_t lpc17_phyread(uint8_t phyaddr, uint8_t regaddr);
|
|
static inline int lpc17_phyreset(uint8_t phyaddr);
|
|
# ifdef CONFIG_PHY_AUTONEG
|
|
static inline int lpc17_phyautoneg(uint8_t phyaddr);
|
|
# endif
|
|
static int lpc17_phymode(uint8_t phyaddr, uint8_t mode);
|
|
static inline int lpc17_phyinit(struct lpc17_driver_s *priv);
|
|
#else
|
|
# define lpc17_phyinit(priv)
|
|
#endif
|
|
|
|
/* EMAC Initialization functions */
|
|
|
|
static inline void lpc17_txdescinit(struct lpc17_driver_s *priv);
|
|
static inline void lpc17_rxdescinit(struct lpc17_driver_s *priv);
|
|
static void lpc17_macmode(uint8_t mode);
|
|
static void lpc17_ethreset(struct lpc17_driver_s *priv);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_printreg
|
|
*
|
|
* Description:
|
|
* Print the contents of an LPC17xx register operation
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_REGDEBUG
|
|
static void lpc17_printreg(uint32_t addr, uint32_t val, bool iswrite)
|
|
{
|
|
dbg("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val);
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_checkreg
|
|
*
|
|
* Description:
|
|
* Get the contents of an LPC17xx register
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_REGDEBUG
|
|
static void lpc17_checkreg(uint32_t addr, uint32_t val, bool iswrite)
|
|
{
|
|
static uint32_t prevaddr = 0;
|
|
static uint32_t preval = 0;
|
|
static uint32_t count = 0;
|
|
static bool prevwrite = false;
|
|
|
|
/* Is this the same value that we read from/wrote to the same register last time?
|
|
* Are we polling the register? If so, suppress the output.
|
|
*/
|
|
|
|
if (addr == prevaddr && val == preval && prevwrite == iswrite)
|
|
{
|
|
/* Yes.. Just increment the count */
|
|
|
|
count++;
|
|
}
|
|
else
|
|
{
|
|
/* No this is a new address or value or operation. Were there any
|
|
* duplicate accesses before this one?
|
|
*/
|
|
|
|
if (count > 0)
|
|
{
|
|
/* Yes.. Just one? */
|
|
|
|
if (count == 1)
|
|
{
|
|
/* Yes.. Just one */
|
|
|
|
lpc17_printreg(prevaddr, preval, prevwrite);
|
|
}
|
|
else
|
|
{
|
|
/* No.. More than one. */
|
|
|
|
dbg("[repeats %d more times]\n", count);
|
|
}
|
|
}
|
|
|
|
/* Save the new address, value, count, and operation for next time */
|
|
|
|
prevaddr = addr;
|
|
preval = val;
|
|
count = 0;
|
|
prevwrite = iswrite;
|
|
|
|
/* Show the new regisgter access */
|
|
|
|
lpc17_printreg(addr, val, iswrite);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_getreg
|
|
*
|
|
* Description:
|
|
* Get the contents of an LPC17xx register
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_REGDEBUG
|
|
static uint32_t lpc17_getreg(uint32_t addr)
|
|
{
|
|
/* Read the value from the register */
|
|
|
|
uint32_t val = getreg32(addr);
|
|
|
|
/* Check if we need to print this value */
|
|
|
|
lpc17_checkreg(addr, val, false);
|
|
return val;
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_putreg
|
|
*
|
|
* Description:
|
|
* Set the contents of an LPC17xx register to a value
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_REGDEBUG
|
|
static void lpc17_putreg(uint32_t val, uint32_t addr)
|
|
{
|
|
/* Check if we need to print this value */
|
|
|
|
lpc17_checkreg(addr, val, true);
|
|
|
|
/* Write the value */
|
|
|
|
putreg32(val, addr);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_txdesc
|
|
*
|
|
* Description:
|
|
* Check if a free TX descriptor is available.
|
|
*
|
|
* 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 lpc17_txdesc(struct lpc17_driver_s *priv)
|
|
{
|
|
unsigned int prodidx;
|
|
unsigned int considx;
|
|
|
|
/* Get the next producer index */
|
|
|
|
prodidx = lpc17_getreg(LPC17_ETH_TXPRODIDX) & ETH_TXPRODIDX_MASK;
|
|
if (++prodidx >= CONFIG_NET_NTXDESC)
|
|
{
|
|
/* Wrap back to index zero */
|
|
|
|
prodidx = 0;
|
|
}
|
|
|
|
/* If the next producer index would overrun the consumer index, then there
|
|
* are no available Tx descriptors.
|
|
*/
|
|
|
|
considx = lpc17_getreg(LPC17_ETH_TXCONSIDX) & ETH_TXCONSIDX_MASK;
|
|
return prodidx != considx ? OK : -EAGAIN;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_transmit
|
|
*
|
|
* Description:
|
|
* Start hardware transmission. Called either from the txdone interrupt
|
|
* handling or from watchdog based polling.
|
|
*
|
|
* 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 lpc17_transmit(struct lpc17_driver_s *priv)
|
|
{
|
|
uint32_t *txdesc;
|
|
void *txbuffer;
|
|
unsigned int prodidx;
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
DEBUGASSERT(lpc17_txdesc(priv) == OK);
|
|
|
|
/* Increment statistics and dump the packet *if so configured) */
|
|
|
|
EMAC_STAT(priv, tx_packets);
|
|
lpc17_dumppacket("Transmit packet",
|
|
priv->lp_dev.d_buf, priv->lp_dev.d_len);
|
|
|
|
/* Get the current producer index */
|
|
|
|
prodidx = lpc17_getreg(LPC17_ETH_TXPRODIDX) & ETH_TXPRODIDX_MASK;
|
|
|
|
/* Get the packet address from the descriptor and set the descriptor control
|
|
* fields.
|
|
*/
|
|
|
|
txdesc = (uint32_t*)(LPC17_TXDESC_BASE + (prodidx << 3));
|
|
txbuffer = (void*)*txdesc++;
|
|
*txdesc = TXDESC_CONTROL_INT | TXDESC_CONTROL_LAST | TXDESC_CONTROL_CRC |
|
|
(priv->lp_dev.d_len - 1);
|
|
|
|
/* Copy the packet data into the Tx buffer assignd to this descriptor. It
|
|
* should fit because each packet buffer is the MTU size and breaking up
|
|
* largerTCP messasges is handled by higher level logic. The hardware
|
|
* does, however, support breaking up larger messages into many fragments,
|
|
* however, that capability is not exploited here.
|
|
*
|
|
* This would be a great performance improvement: Remove the buffer from
|
|
* the lp_dev structure and replace it a pointer directly into the EMAC
|
|
* DMA memory. This could eliminate the following, costly memcpy.
|
|
*/
|
|
|
|
DEBUGASSERT(priv->lp_dev.d_len <= LPC17_MAXPACKET_SIZE);
|
|
memcpy(txbuffer, priv->lp_dev.d_buf, priv->lp_dev.d_len);
|
|
|
|
/* Bump the producer index, making the packet available for transmission. */
|
|
|
|
if (++prodidx >= CONFIG_NET_NTXDESC)
|
|
{
|
|
/* Wrap back to index zero */
|
|
|
|
prodidx = 0;
|
|
}
|
|
lpc17_putreg(prodidx, LPC17_ETH_TXPRODIDX);
|
|
|
|
/* Enable Tx interrupts */
|
|
|
|
priv->lp_inten |= ETH_TXINTS;
|
|
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
|
|
|
|
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
|
|
|
|
(void)wd_start(priv->lp_txtimeout, LPC17_TXTIMEOUT, lpc17_txtimeout,
|
|
1, (uint32_t)priv);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_uiptxpoll
|
|
*
|
|
* Description:
|
|
* The transmitter is available, check if uIP has any outgoing packets ready
|
|
* to send. This is a callback from uip_poll(). uip_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
|
|
*
|
|
* 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 lpc17_uiptxpoll(struct uip_driver_s *dev)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
int ret = OK;
|
|
|
|
/* 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->lp_dev.d_len > 0)
|
|
{
|
|
/* Send this packet. In this context, we know that there is space for
|
|
* at least one more packet in the descriptor list.
|
|
*/
|
|
|
|
uip_arp_out(&priv->lp_dev);
|
|
lpc17_transmit(priv);
|
|
|
|
/* Check if there is room in the device to hold another packet. If not,
|
|
* return any non-zero value to terminate the poll.
|
|
*/
|
|
|
|
ret = lpc17_txdesc(priv);
|
|
}
|
|
|
|
/* If zero is returned, the polling will continue until all connections have
|
|
* been examined.
|
|
*/
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_response
|
|
*
|
|
* Description:
|
|
* While processing an RxDone event, higher logic decides to send a packet,
|
|
* possibly a response to the incoming packet (but probably not, in reality).
|
|
* However, since the Rx and Tx operations are decoupled, there is no
|
|
* guarantee that there will be a Tx descriptor available at that time.
|
|
* This function will perform that check and, if no Tx descriptor is
|
|
* available, this function will (1) stop incoming Rx processing (bad), and
|
|
* (2) hold the outgoing packet in a pending state until the next Tx
|
|
* interrupt occurs.
|
|
*
|
|
* Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by interrupt handling logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_response(struct lpc17_driver_s *priv)
|
|
{
|
|
int ret;
|
|
|
|
/* Check if there is room in the device to hold another packet. */
|
|
|
|
ret = lpc17_txdesc(priv);
|
|
if (ret == OK)
|
|
{
|
|
/* Yes.. queue the packet now. */
|
|
|
|
lpc17_transmit(priv);
|
|
}
|
|
else
|
|
{
|
|
/* No.. mark the Tx as pending and halt further Tx interrupts */
|
|
|
|
DEBUGASSERT((priv->lp_inten & ETH_INT_TXDONE) != 0);
|
|
|
|
priv->lp_txpending = true;
|
|
priv->lp_inten &= ~ETH_RXINTS;
|
|
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
|
|
EMAC_STAT(priv, tx_pending);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_rxdone
|
|
*
|
|
* Description:
|
|
* An interrupt was received indicating the availability of a new RX packet
|
|
*
|
|
* Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by interrupt handling logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_rxdone(struct lpc17_driver_s *priv)
|
|
{
|
|
uint32_t *rxstat;
|
|
bool fragment;
|
|
unsigned int prodidx;
|
|
unsigned int considx;
|
|
unsigned int pktlen;
|
|
|
|
/* Get the current producer and consumer indices */
|
|
|
|
considx = lpc17_getreg(LPC17_ETH_RXCONSIDX) & ETH_RXCONSIDX_MASK;
|
|
prodidx = lpc17_getreg(LPC17_ETH_RXPRODIDX) & ETH_RXPRODIDX_MASK;
|
|
|
|
/* Loop while there are incoming packets to be processed, that is, while
|
|
* the producer index is not equal to the consumer index.
|
|
*/
|
|
|
|
fragment = false;
|
|
while (considx != prodidx)
|
|
{
|
|
/* Update statistics */
|
|
|
|
EMAC_STAT(priv, rx_packets);
|
|
|
|
/* Get the Rx status and packet length (-4+1) */
|
|
|
|
rxstat = (uint32_t*)(LPC17_RXSTAT_BASE + (considx << 3));
|
|
pktlen = (*rxstat & RXSTAT_INFO_RXSIZE_MASK) - 3;
|
|
|
|
/* Check for errors. NOTE: The DMA engine reports bogus length errors,
|
|
* making this a pretty useless check.
|
|
*/
|
|
|
|
if ((*rxstat & RXSTAT_INFO_ERROR) != 0)
|
|
{
|
|
nlldbg("Error. considx: %08x prodidx: %08x rxstat: %08x\n",
|
|
considx, prodidx, *rxstat);
|
|
EMAC_STAT(priv, rx_pkterr);
|
|
}
|
|
|
|
/* If the pktlen is greater then the buffer, then we cannot accept
|
|
* the packet. Also, since the DMA packet buffers are set up to
|
|
* be the same size as our max packet size, any fragments also
|
|
* imply that the packet is too big.
|
|
*/
|
|
|
|
/* else */ if (pktlen > CONFIG_NET_BUFSIZE + CONFIG_NET_GUARDSIZE)
|
|
{
|
|
nlldbg("Too big. considx: %08x prodidx: %08x pktlen: %d rxstat: %08x\n",
|
|
considx, prodidx, pktlen, *rxstat);
|
|
EMAC_STAT(priv, rx_pktsize);
|
|
}
|
|
else if ((*rxstat & RXSTAT_INFO_LASTFLAG) == 0)
|
|
{
|
|
nlldbg("Fragment. considx: %08x prodidx: %08x pktlen: %d rxstat: %08x\n",
|
|
considx, prodidx, pktlen, *rxstat);
|
|
EMAC_STAT(priv, rx_fragment);
|
|
fragment = true;
|
|
}
|
|
else if (fragment)
|
|
{
|
|
nlldbg("Last fragment. considx: %08x prodidx: %08x pktlen: %d rxstat: %08x\n",
|
|
considx, prodidx, pktlen, *rxstat);
|
|
EMAC_STAT(priv, rx_fragment);
|
|
fragment = false;
|
|
}
|
|
else
|
|
{
|
|
uint32_t *rxdesc;
|
|
void *rxbuffer;
|
|
|
|
/* Get the Rx buffer address from the Rx descriptor */
|
|
|
|
rxdesc = (uint32_t*)(LPC17_RXDESC_BASE + (considx << 3));
|
|
rxbuffer = (void*)*rxdesc;
|
|
|
|
/* Copy the data data from the EMAC DMA RAM to priv->lp_dev.d_buf.
|
|
* Set amount of data in priv->lp_dev.d_len
|
|
*
|
|
* Here would be a great performance improvement: Remove the
|
|
* buffer from the lp_dev structure and replace it with a pointer
|
|
* directly into the EMAC DMA memory. This could eliminate the
|
|
* following, costly memcpy.
|
|
*/
|
|
|
|
memcpy(priv->lp_dev.d_buf, rxbuffer, pktlen);
|
|
priv->lp_dev.d_len = pktlen;
|
|
|
|
lpc17_dumppacket("Received packet",
|
|
priv->lp_dev.d_buf, priv->lp_dev.d_len);
|
|
|
|
/* We only accept IP packets of the configured type and ARP packets */
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (BUF->type == HTONS(UIP_ETHTYPE_IP6))
|
|
#else
|
|
if (BUF->type == HTONS(UIP_ETHTYPE_IP))
|
|
#endif
|
|
{
|
|
/* Handle the incoming Rx packet */
|
|
|
|
EMAC_STAT(priv, rx_ip);
|
|
uip_arp_ipin(&priv->lp_dev);
|
|
uip_input(&priv->lp_dev);
|
|
|
|
/* If the above function invocation resulted in data that
|
|
* should be sent out on the network, the field d_len will
|
|
* set to a value > 0.
|
|
*/
|
|
|
|
if (priv->lp_dev.d_len > 0)
|
|
{
|
|
uip_arp_out(&priv->lp_dev);
|
|
lpc17_response(priv);
|
|
}
|
|
}
|
|
else if (BUF->type == htons(UIP_ETHTYPE_ARP))
|
|
{
|
|
EMAC_STAT(priv, rx_arp);
|
|
uip_arp_arpin(&priv->lp_dev);
|
|
|
|
/* If the above function invocation resulted in data that
|
|
* should be sent out on the network, the field d_len will
|
|
* set to a value > 0.
|
|
*/
|
|
|
|
if (priv->lp_dev.d_len > 0)
|
|
{
|
|
lpc17_response(priv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Unrecognized... drop it. */
|
|
|
|
EMAC_STAT(priv, rx_dropped);
|
|
}
|
|
}
|
|
|
|
/* Bump up the consumer index and resample the producer index (which
|
|
* might also have gotten bumped up by the hardware).
|
|
*/
|
|
|
|
if (++considx >= CONFIG_NET_NRXDESC)
|
|
{
|
|
/* Wrap back to index zero */
|
|
|
|
considx = 0;
|
|
}
|
|
|
|
lpc17_putreg(considx, LPC17_ETH_RXCONSIDX);
|
|
prodidx = lpc17_getreg(LPC17_ETH_RXPRODIDX) & ETH_RXPRODIDX_MASK;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_txdone
|
|
*
|
|
* Description:
|
|
* An interrupt was received indicating that the last TX packet(s) is done
|
|
*
|
|
* Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by interrupt handling logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_txdone(struct lpc17_driver_s *priv)
|
|
{
|
|
/* Cancel the pending Tx timeout */
|
|
|
|
wd_cancel(priv->lp_txtimeout);
|
|
|
|
/* Disable further Tx interrupts. Tx interrupts may be re-enabled again
|
|
* depending upon the result of the poll.
|
|
*/
|
|
|
|
priv->lp_inten &= ~ETH_TXINTS;
|
|
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
|
|
|
|
/* Verify that the hardware is ready to send another packet. Since a Tx
|
|
* just completed, this must be the case.
|
|
*/
|
|
|
|
DEBUGASSERT(lpc17_txdesc(priv) == OK);
|
|
|
|
/* Check if there is a pending Tx transfer that was scheduled by Rx handling
|
|
* while the Tx logic was busy. If so, processing that pending Tx now.
|
|
*/
|
|
|
|
if (priv->lp_txpending)
|
|
{
|
|
/* Clear the pending condition, send the packet, and restore Rx interrupts */
|
|
|
|
priv->lp_txpending = false;
|
|
EMAC_STAT(priv, tx_unpend);
|
|
|
|
lpc17_transmit(priv);
|
|
|
|
priv->lp_inten |= ETH_RXINTS;
|
|
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
|
|
}
|
|
|
|
/* Otherwise poll uIP for new XMIT data */
|
|
|
|
else
|
|
{
|
|
(void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_interrupt
|
|
*
|
|
* Description:
|
|
* Hardware interrupt handler
|
|
*
|
|
* 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 lpc17_interrupt(int irq, void *context)
|
|
{
|
|
register struct lpc17_driver_s *priv;
|
|
uint32_t status;
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
# error "A mechanism to associate and interface with an IRQ is needed"
|
|
#else
|
|
priv = &g_ethdrvr[0];
|
|
#endif
|
|
|
|
/* Get the interrupt status (zero means no interrupts pending). */
|
|
|
|
status = lpc17_getreg(LPC17_ETH_INTST);
|
|
if (status != 0)
|
|
{
|
|
/* Clear all pending interrupts */
|
|
|
|
lpc17_putreg(status, LPC17_ETH_INTCLR);
|
|
|
|
/* Handle each pending interrupt **************************************/
|
|
/* Check for Wake-Up on Lan *******************************************/
|
|
|
|
#ifdef CONFIG_NET_WOL
|
|
if ((status & ETH_INT_WKUP) != 0)
|
|
{
|
|
EMAC_STAT(priv, wol);
|
|
# warning "Missing logic"
|
|
}
|
|
else
|
|
#endif
|
|
/* Fatal Errors *******************************************************/
|
|
/* RX OVERRUN -- Fatal overrun error in the receive queue. The fatal
|
|
* interrupt should be resolved by a Rx soft-reset. The bit is not
|
|
* set when there is a nonfatal overrun error.
|
|
*
|
|
* TX UNDERRUN -- Interrupt set on a fatal underrun error in the
|
|
* transmit queue. The fatal interrupt should be resolved by a Tx
|
|
* soft-reset. The bit is not set when there is a nonfatal underrun
|
|
* error.
|
|
*/
|
|
|
|
if ((status & (ETH_INT_RXOVR|ETH_INT_TXUNR)) != 0)
|
|
{
|
|
if ((status & ETH_INT_RXOVR) != 0)
|
|
{
|
|
nlldbg("RX Overrun. status: %08x\n", status);
|
|
EMAC_STAT(priv, rx_ovrerrors);
|
|
}
|
|
|
|
if ((status & ETH_INT_TXUNR) != 0)
|
|
{
|
|
nlldbg("TX Underrun. status: %08x\n", status);
|
|
EMAC_STAT(priv, tx_underrun);
|
|
}
|
|
|
|
/* ifup() will reset the EMAC and bring it back up */
|
|
|
|
(void)lpc17_ifup(&priv->lp_dev);
|
|
}
|
|
else
|
|
{
|
|
/* Check for receive events ***************************************/
|
|
/* RX ERROR -- Triggered on receive errors: AlignmentError,
|
|
* RangeError, LengthError, SymbolError, CRCError or NoDescriptor
|
|
* or Overrun. NOTE: (1) We will still need to call lpc17_rxdone
|
|
* on RX errors to bump the considx over the bad packet. (2) The
|
|
* DMA engine reports bogus length errors, making this a pretty
|
|
* useless check anyway.
|
|
*/
|
|
|
|
if ((status & ETH_INT_RXERR) != 0)
|
|
{
|
|
nlldbg("RX Error. status: %08x\n", status);
|
|
EMAC_STAT(priv, rx_errors);
|
|
}
|
|
|
|
/* RX FINISHED -- Triggered when all receive descriptors have
|
|
* been processed i.e. on the transition to the situation
|
|
* where ProduceIndex == ConsumeIndex.
|
|
*/
|
|
|
|
if ((status & ETH_INT_RXFIN) != 0)
|
|
{
|
|
EMAC_STAT(priv, rx_finished);
|
|
DEBUGASSERT(lpc17_getreg(LPC17_ETH_RXPRODIDX) == lpc17_getreg(LPC17_ETH_RXCONSIDX));
|
|
}
|
|
|
|
/* RX DONE -- Triggered when a receive descriptor has been
|
|
* processed while the Interrupt bit in the Control field of
|
|
* the descriptor was set.
|
|
*/
|
|
|
|
if ((status & ETH_INT_RXDONE) != 0)
|
|
{
|
|
EMAC_STAT(priv, rx_done);
|
|
|
|
/* We have received at least one new incoming packet. */
|
|
|
|
lpc17_rxdone(priv);
|
|
}
|
|
|
|
/* Check for Tx events ********************************************/
|
|
/* TX ERROR -- Triggered on transmit errors: LateCollision,
|
|
* ExcessiveCollision and ExcessiveDefer, NoDescriptor or Underrun.
|
|
* NOTE: We will still need to call lpc17_txdone() in order to
|
|
* clean up after the failed transmit.
|
|
*/
|
|
|
|
if ((status & ETH_INT_TXERR) != 0)
|
|
{
|
|
nlldbg("TX Error. status: %08x\n", status);
|
|
EMAC_STAT(priv, tx_errors);
|
|
}
|
|
|
|
/* TX FINISHED -- Triggered when all transmit descriptors have
|
|
* been processed i.e. on the transition to the situation
|
|
* where ProduceIndex == ConsumeIndex.
|
|
*/
|
|
|
|
if ((status & ETH_INT_TXFIN) != 0)
|
|
{
|
|
EMAC_STAT(priv, tx_finished);
|
|
}
|
|
|
|
/* TX DONE -- Triggered when a descriptor has been transmitted
|
|
* while the Interrupt bit in the Control field of the
|
|
* descriptor was set.
|
|
*/
|
|
|
|
if ((status & ETH_INT_TXDONE) != 0)
|
|
{
|
|
EMAC_STAT(priv, tx_done);
|
|
|
|
/* A packet transmission just completed */
|
|
|
|
lpc17_txdone(priv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear the pending interrupt */
|
|
|
|
#if 0 /* Apparently not necessary */
|
|
# if CONFIG_LPC17_NINTERFACES > 1
|
|
lpc17_clrpend(priv->irq);
|
|
# else
|
|
lpc17_clrpend(LPC17_IRQ_ETH);
|
|
# endif
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_txtimeout
|
|
*
|
|
* Description:
|
|
* Our TX watchdog timed out. Called from the timer interrupt handler.
|
|
* The last TX never completed. Reset the hardware and start again.
|
|
*
|
|
* Parameters:
|
|
* argc - The number of available arguments
|
|
* arg - The first argument
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by the watchdog logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_txtimeout(int argc, uint32_t arg, ...)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;
|
|
|
|
/* Increment statistics and dump debug info */
|
|
|
|
EMAC_STAT(priv, tx_timeouts);
|
|
if (priv->lp_ifup)
|
|
{
|
|
/* Then reset the hardware. ifup() will reset the interface, then bring
|
|
* it back up.
|
|
*/
|
|
|
|
(void)lpc17_ifup(&priv->lp_dev);
|
|
|
|
/* Then poll uIP for new XMIT data */
|
|
|
|
(void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_polltimer
|
|
*
|
|
* Description:
|
|
* Periodic timer handler. Called from the timer interrupt handler.
|
|
*
|
|
* Parameters:
|
|
* argc - The number of available arguments
|
|
* arg - The first argument
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by the watchdog logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_polltimer(int argc, uint32_t arg, ...)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;
|
|
|
|
/* Check if there is room in the send another TX packet. We cannot perform
|
|
* the TX poll if he are unable to accept another packet for transmission.
|
|
*/
|
|
|
|
if (lpc17_txdesc(priv) == OK)
|
|
{
|
|
/* If so, update TCP timing states and poll uIP for new XMIT data. Hmmm..
|
|
* might be bug here. Does this mean if there is a transmit in progress,
|
|
* we will missing TCP time state updates?
|
|
*/
|
|
|
|
(void)uip_timer(&priv->lp_dev, lpc17_uiptxpoll, LPC17_POLLHSEC);
|
|
}
|
|
|
|
/* Setup the watchdog poll timer again */
|
|
|
|
(void)wd_start(priv->lp_txpoll, LPC17_WDDELAY, lpc17_polltimer, 1, arg);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_ifup
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Bring up the Ethernet interface when an IP address is
|
|
* provided
|
|
*
|
|
* Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc17_ifup(struct uip_driver_s *dev)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
uint32_t regval;
|
|
int ret;
|
|
|
|
ndbg("Bringing up: %d.%d.%d.%d\n",
|
|
dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff,
|
|
(dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24);
|
|
|
|
/* Reset the Ethernet controller (again) */
|
|
|
|
lpc17_ethreset(priv);
|
|
|
|
/* Initialize the PHY and wait for the link to be established */
|
|
|
|
ret = lpc17_phyinit(priv);
|
|
if (ret != 0)
|
|
{
|
|
ndbg("lpc17_phyinit failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Configure the MAC station address */
|
|
|
|
regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[5] << 8 |
|
|
(uint32_t)priv->lp_dev.d_mac.ether_addr_octet[4];
|
|
lpc17_putreg(regval, LPC17_ETH_SA0);
|
|
|
|
regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[3] << 8 |
|
|
(uint32_t)priv->lp_dev.d_mac.ether_addr_octet[2];
|
|
lpc17_putreg(regval, LPC17_ETH_SA1);
|
|
|
|
regval = (uint32_t)priv->lp_dev.d_mac.ether_addr_octet[1] << 8 |
|
|
(uint32_t)priv->lp_dev.d_mac.ether_addr_octet[0];
|
|
lpc17_putreg(regval, LPC17_ETH_SA2);
|
|
|
|
/* Initialize Ethernet interface for the PHY setup */
|
|
|
|
lpc17_macmode(priv->lp_mode);
|
|
|
|
/* Initialize EMAC DMA memory -- descriptors, status, packet buffers, etc. */
|
|
|
|
lpc17_txdescinit(priv);
|
|
lpc17_rxdescinit(priv);
|
|
|
|
/* Configure to pass all received frames */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_MAC1);
|
|
regval |= ETH_MAC1_PARF;
|
|
lpc17_putreg(regval, LPC17_ETH_MAC1);
|
|
|
|
/* Set up RX filter and configure to accept broadcast addresses, multicast
|
|
* addresses, and perfect station address matches. We should also accept
|
|
* perfect matches and, most likely, broadcast (for example, for ARP requests).
|
|
* Other RX filter options will only be enabled if so selected. NOTE: There
|
|
* is a selection CONFIG_NET_BROADCAST, but this enables receipt of UDP
|
|
* broadcast packets inside of the stack.
|
|
*/
|
|
|
|
regval = ETH_RXFLCTRL_PERFEN | ETH_RXFLCTRL_BCASTEN;
|
|
#ifdef CONFIG_NET_MULTICAST
|
|
regval |= (ETH_RXFLCTRL_MCASTEN | ETH_RXFLCTRL_UCASTEN);
|
|
#endif
|
|
#ifdef CONFIG_NET_HASH
|
|
regval |= (ETH_RXFLCTRL_MCASTHASHEN | ETH_RXFLCTRL_UCASTHASHEN);
|
|
#endif
|
|
lpc17_putreg(regval, LPC17_ETH_RXFLCTRL);
|
|
|
|
/* Clear any pending interrupts (shouldn't be any) */
|
|
|
|
lpc17_putreg(0xffffffff, LPC17_ETH_INTCLR);
|
|
|
|
/* Configure interrupts. The Ethernet interrupt was attached during one-time
|
|
* initialization, so we only need to set the interrupt priority, configure
|
|
* interrupts, and enable them.
|
|
*/
|
|
|
|
/* Set the interrupt to the highest priority */
|
|
|
|
#ifdef CONFIG_ARCH_IRQPRIO
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
(void)up_prioritize_irq(priv->irq, CONFIG_NET_PRIORITY);
|
|
#else
|
|
(void)up_prioritize_irq(LPC17_IRQ_ETH, CONFIG_NET_PRIORITY);
|
|
#endif
|
|
#endif
|
|
|
|
/* Enable Ethernet interrupts. The way we do this depends on whether or
|
|
* not Wakeup on Lan (WoL) has been configured.
|
|
*/
|
|
|
|
#ifdef CONFIG_NET_WOL
|
|
/* Configure WoL: Clear all receive filter WoLs and enable the perfect
|
|
* match WoL interrupt. We will wait until the Wake-up to finish
|
|
* bringing things up.
|
|
*/
|
|
|
|
lpc17_putreg(0xffffffff, LPC17_ETH_RXFLWOLCLR);
|
|
lpc17_putreg(ETH_RXFLCTRL_RXFILEN, LPC17_ETH_RXFLCTRL);
|
|
|
|
priv->lp_inten = ETH_INT_WKUP;
|
|
lpc17_putreg(ETH_INT_WKUP, LPC17_ETH_INTEN);
|
|
#else
|
|
/* Otherwise, enable all Rx interrupts. Tx interrupts, SOFTINT and WoL are
|
|
* excluded. Tx interrupts will not be enabled until there is data to be
|
|
* sent.
|
|
*/
|
|
|
|
priv->lp_inten = ETH_RXINTS;
|
|
lpc17_putreg(ETH_RXINTS, LPC17_ETH_INTEN);
|
|
#endif
|
|
|
|
/* Enable Rx. "Enabling of the receive function is located in two places.
|
|
* The receive DMA manager needs to be enabled and the receive data path
|
|
* of the MAC needs to be enabled. To prevent overflow in the receive
|
|
* DMA engine the receive DMA engine should be enabled by setting the
|
|
* RxEnable bit in the Command register before enabling the receive data
|
|
* path in the MAC by setting the RECEIVE ENABLE bit in the MAC1 register."
|
|
*/
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_CMD);
|
|
regval |= ETH_CMD_RXEN;
|
|
lpc17_putreg(regval, LPC17_ETH_CMD);
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_MAC1);
|
|
regval |= ETH_MAC1_RE;
|
|
lpc17_putreg(regval, LPC17_ETH_MAC1);
|
|
|
|
/* Enable Tx */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_CMD);
|
|
regval |= ETH_CMD_TXEN;
|
|
lpc17_putreg(regval, LPC17_ETH_CMD);
|
|
|
|
/* Set and activate a timer process */
|
|
|
|
(void)wd_start(priv->lp_txpoll, LPC17_WDDELAY, lpc17_polltimer, 1,
|
|
(uint32_t)priv);
|
|
|
|
/* Finally, make the interface up and enable the Ethernet interrupt at
|
|
* the interrupt controller
|
|
*/
|
|
|
|
priv->lp_ifup = true;
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
up_enable_irq(priv->irq);
|
|
#else
|
|
up_enable_irq(LPC17_IRQ_ETH);
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_ifdown
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Stop the interface.
|
|
*
|
|
* Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc17_ifdown(struct uip_driver_s *dev)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
irqstate_t flags;
|
|
|
|
/* Disable the Ethernet interrupt */
|
|
|
|
flags = irqsave();
|
|
up_disable_irq(LPC17_IRQ_ETH);
|
|
|
|
/* Cancel the TX poll timer and TX timeout timers */
|
|
|
|
wd_cancel(priv->lp_txpoll);
|
|
wd_cancel(priv->lp_txtimeout);
|
|
|
|
/* Reset the device and mark it as down. */
|
|
|
|
lpc17_ethreset(priv);
|
|
priv->lp_ifup = false;
|
|
irqrestore(flags);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_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.
|
|
*
|
|
* Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Called in normal user mode
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc17_txavail(struct uip_driver_s *dev)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
irqstate_t flags;
|
|
|
|
/* Disable interrupts because this function may be called from interrupt
|
|
* level processing.
|
|
*/
|
|
|
|
flags = irqsave();
|
|
|
|
/* Ignore the notification if the interface is not yet up */
|
|
|
|
if (priv->lp_ifup)
|
|
{
|
|
/* Check if there is room in the hardware to hold another outgoing packet. */
|
|
|
|
if (lpc17_txdesc(priv) == OK)
|
|
{
|
|
/* If so, then poll uIP for new XMIT data */
|
|
|
|
(void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
|
|
}
|
|
}
|
|
|
|
irqrestore(flags);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_addmac
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Add the specified MAC address to the hardware multicast
|
|
* address filtering
|
|
*
|
|
* Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
* mac - The MAC address to be added
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IGMP
|
|
static int lpc17_addmac(struct uip_driver_s *dev, const uint8_t *mac)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
|
|
/* Add the MAC address to the hardware multicast routing table */
|
|
|
|
#warning "Not implemented"
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_rmmac
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Remove the specified MAC address from the hardware multicast
|
|
* address filtering
|
|
*
|
|
* Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
* mac - The MAC address to be removed
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IGMP
|
|
static int lpc17_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
|
|
{
|
|
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
|
|
|
|
/* Add the MAC address to the hardware multicast routing table */
|
|
|
|
#warning "Not implemented"
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_showpins
|
|
*
|
|
* Description:
|
|
* Dump GPIO registers
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#if defined(CONFIG_NET_REGDEBUG) && defined(CONFIG_DEBUG_GPIO)
|
|
static void lpc17_showpins(void)
|
|
{
|
|
lpc17_dumpgpio(GPIO_PORT1|GPIO_PIN0, "P1[1-15]");
|
|
lpc17_dumpgpio(GPIO_PORT1|GPIO_PIN16, "P1[16-31]");
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Name: lpc17_showmii
|
|
*
|
|
* Description:
|
|
* Dump PHY MII registers
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#if defined(CONFIG_NET_REGDEBUG) && defined(LPC17_HAVE_PHY)
|
|
static void lpc17_showmii(uint8_t phyaddr, const char *msg)
|
|
{
|
|
dbg("PHY " LPC17_PHYNAME ": %s\n", msg);
|
|
dbg(" MCR: %04x\n", lpc17_phyread(phyaddr, MII_MCR));
|
|
dbg(" MSR: %04x\n", lpc17_phyread(phyaddr, MII_MSR));
|
|
dbg(" ADVERTISE: %04x\n", lpc17_phyread(phyaddr, MII_ADVERTISE));
|
|
dbg(" LPA: %04x\n", lpc17_phyread(phyaddr, MII_LPA));
|
|
dbg(" EXPANSION: %04x\n", lpc17_phyread(phyaddr, MII_EXPANSION));
|
|
#ifdef CONFIG_PHY_KS8721
|
|
dbg(" 10BTCR: %04x\n", lpc17_phyread(phyaddr, MII_KS8721_10BTCR));
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phywrite
|
|
*
|
|
* Description:
|
|
* Write a value to an MII PHY register
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
* regaddr - The address of the PHY register to be written
|
|
* phydata - The data to write to the PHY register
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static void lpc17_phywrite(uint8_t phyaddr, uint8_t regaddr, uint16_t phydata)
|
|
{
|
|
uint32_t regval;
|
|
|
|
/* Set PHY address and PHY register address */
|
|
|
|
regval = ((uint32_t)phyaddr << ETH_MADR_PHYADDR_SHIFT) |
|
|
((uint32_t)regaddr << ETH_MADR_REGADDR_SHIFT);
|
|
lpc17_putreg(regval, LPC17_ETH_MADR);
|
|
|
|
/* Set up to write */
|
|
|
|
lpc17_putreg(ETH_MCMD_WRITE, LPC17_ETH_MCMD);
|
|
|
|
/* Write the register data to the PHY */
|
|
|
|
lpc17_putreg((uint32_t)phydata, LPC17_ETH_MWTD);
|
|
|
|
/* Wait for the PHY command to complete */
|
|
|
|
while ((lpc17_getreg(LPC17_ETH_MIND) & ETH_MIND_BUSY) != 0);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phyread
|
|
*
|
|
* Description:
|
|
* Read a value from an MII PHY register
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
* regaddr - The address of the PHY register to be written
|
|
*
|
|
* Returned Value:
|
|
* Data read from the PHY register
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static uint16_t lpc17_phyread(uint8_t phyaddr, uint8_t regaddr)
|
|
{
|
|
uint32_t regval;
|
|
|
|
lpc17_putreg(0, LPC17_ETH_MCMD);
|
|
|
|
/* Set PHY address and PHY register address */
|
|
|
|
regval = ((uint32_t)phyaddr << ETH_MADR_PHYADDR_SHIFT) |
|
|
((uint32_t)regaddr << ETH_MADR_REGADDR_SHIFT);
|
|
lpc17_putreg(regval, LPC17_ETH_MADR);
|
|
|
|
/* Set up to read */
|
|
|
|
lpc17_putreg(ETH_MCMD_READ, LPC17_ETH_MCMD);
|
|
|
|
/* Wait for the PHY command to complete */
|
|
|
|
while ((lpc17_getreg(LPC17_ETH_MIND) & (ETH_MIND_BUSY|ETH_MIND_NVALID)) != 0);
|
|
lpc17_putreg(0, LPC17_ETH_MCMD);
|
|
|
|
/* Return the PHY register data */
|
|
|
|
return (uint16_t)(lpc17_getreg(LPC17_ETH_MRDD) & ETH_MRDD_MASK);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phyreset
|
|
*
|
|
* Description:
|
|
* Reset the PHY
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static inline int lpc17_phyreset(uint8_t phyaddr)
|
|
{
|
|
int32_t timeout;
|
|
uint16_t phyreg;
|
|
|
|
/* Reset the PHY. Needs a minimal 50uS delay after reset. */
|
|
|
|
lpc17_phywrite(phyaddr, MII_MCR, MII_MCR_RESET);
|
|
|
|
/* Wait for a minimum of 50uS no matter what */
|
|
|
|
up_udelay(50);
|
|
|
|
/* The MCR reset bit is self-clearing. Wait for it to be clear indicating
|
|
* that the reset is complete.
|
|
*/
|
|
|
|
for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
|
|
{
|
|
phyreg = lpc17_phyread(phyaddr, MII_MCR);
|
|
if ((phyreg & MII_MCR_RESET) == 0)
|
|
{
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
ndbg("Reset failed. MCR: %04x\n", phyreg);
|
|
return -ETIMEDOUT;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phyautoneg
|
|
*
|
|
* Description:
|
|
* Enable auto-negotiation.
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* The adverisement regiser has already been configured.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if defined(LPC17_HAVE_PHY) && defined(CONFIG_PHY_AUTONEG)
|
|
static inline int lpc17_phyautoneg(uint8_t phyaddr)
|
|
{
|
|
int32_t timeout;
|
|
uint16_t phyreg;
|
|
|
|
/* Start auto-negotiation */
|
|
|
|
lpc17_phywrite(phyaddr, MII_MCR, MII_MCR_ANENABLE | MII_MCR_ANRESTART);
|
|
|
|
/* Wait for autonegotiation to complete */
|
|
|
|
for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
|
|
{
|
|
/* Check if auto-negotiation has completed */
|
|
|
|
phyreg = lpc17_phyread(phyaddr, MII_MSR);
|
|
if ((phyreg & MII_MSR_ANEGCOMPLETE) != 0)
|
|
{
|
|
/* Yes.. return success */
|
|
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
ndbg("Auto-negotiation failed. MSR: %04x\n", phyreg);
|
|
return -ETIMEDOUT;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phymode
|
|
*
|
|
* Description:
|
|
* Set the PHY to operate at a selected speed/duplex mode.
|
|
*
|
|
* Parameters:
|
|
* phyaddr - The device address where the PHY was discovered
|
|
* mode - speed/duplex mode
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static int lpc17_phymode(uint8_t phyaddr, uint8_t mode)
|
|
{
|
|
int32_t timeout;
|
|
uint16_t phyreg;
|
|
|
|
/* Disable auto-negotiation and set fixed Speed and Duplex settings:
|
|
*
|
|
* MII_MCR_UNIDIR 0=Disable unidirectional enable
|
|
* MII_MCR_SPEED1000 0=Reserved on 10/100
|
|
* MII_MCR_CTST 0=Disable collision test
|
|
* MII_MCR_FULLDPLX ?=Full duplex
|
|
* MII_MCR_ANRESTART 0=Don't restart auto negotiation
|
|
* MII_MCR_ISOLATE 0=Don't electronically isolate PHY from MII
|
|
* MII_MCR_PDOWN 0=Don't powerdown the PHY
|
|
* MII_MCR_ANENABLE 0=Disable auto negotiation
|
|
* MII_MCR_SPEED100 ?=Select 100Mbps
|
|
* MII_MCR_LOOPBACK 0=Disable loopback mode
|
|
* MII_MCR_RESET 0=No PHY reset
|
|
*/
|
|
|
|
phyreg = 0;
|
|
if ((mode & LPC17_SPEED_MASK) == LPC17_SPEED_100)
|
|
{
|
|
phyreg = MII_MCR_SPEED100;
|
|
}
|
|
|
|
if ((mode & LPC17_DUPLEX_MASK) == LPC17_DUPLEX_FULL)
|
|
{
|
|
phyreg |= MII_MCR_FULLDPLX;
|
|
}
|
|
|
|
lpc17_phywrite(phyaddr, MII_MCR, phyreg);
|
|
|
|
/* Then wait for the link to be established */
|
|
|
|
for (timeout = MII_BIG_TIMEOUT; timeout > 0; timeout--)
|
|
{
|
|
#ifdef CONFIG_PHY_DP83848C
|
|
phyreg = lpc17_phyread(phyaddr, MII_DP83848C_STS);
|
|
if ((phyreg & 0x0001) != 0)
|
|
{
|
|
/* Yes.. return success */
|
|
|
|
return OK;
|
|
}
|
|
#else
|
|
phyreg = lpc17_phyread(phyaddr, MII_MSR);
|
|
if ((phyreg & MII_MSR_LINKSTATUS) != 0)
|
|
{
|
|
/* Yes.. return success */
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ndbg("Link failed. MSR: %04x\n", phyreg);
|
|
return -ETIMEDOUT;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_phyinit
|
|
*
|
|
* Description:
|
|
* Initialize the PHY
|
|
*
|
|
* Parameters:
|
|
* priv - Pointer to EMAC device driver structure
|
|
*
|
|
* Returned Value:
|
|
* None directly. As a side-effect, it will initialize priv->lp_phyaddr
|
|
* and priv->lp_phymode.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static inline int lpc17_phyinit(struct lpc17_driver_s *priv)
|
|
{
|
|
unsigned int phyaddr;
|
|
uint16_t phyreg;
|
|
uint32_t regval;
|
|
int ret;
|
|
|
|
/* MII configuration: host clocked divided per board.h, no suppress
|
|
* preamble, no scan increment.
|
|
*/
|
|
|
|
lpc17_putreg(ETH_MCFG_CLKSEL_DIV, LPC17_ETH_MCFG);
|
|
lpc17_putreg(0, LPC17_ETH_MCMD);
|
|
|
|
/* Enter RMII mode and select 100 MBPS support */
|
|
|
|
lpc17_putreg(ETH_CMD_RMII, LPC17_ETH_CMD);
|
|
lpc17_putreg(ETH_SUPP_SPEED, LPC17_ETH_SUPP);
|
|
|
|
/* Find PHY Address. Because the controller has a pull-up and the
|
|
* PHY has pull-down resistors on RXD lines some times the PHY
|
|
* latches different at different addresses.
|
|
*/
|
|
|
|
for (phyaddr = 1; phyaddr < 32; phyaddr++)
|
|
{
|
|
/* Check if we can see the selected device ID at this
|
|
* PHY address.
|
|
*/
|
|
|
|
phyreg = (unsigned int)lpc17_phyread(phyaddr, MII_PHYID1);
|
|
nvdbg("Addr: %d PHY ID1: %04x\n", phyaddr, phyreg);
|
|
|
|
if (phyreg == LPC17_PHYID1)
|
|
{
|
|
phyreg = lpc17_phyread(phyaddr, MII_PHYID2);
|
|
nvdbg("Addr: %d PHY ID2: %04x\n", phyaddr, phyreg);
|
|
|
|
if (phyreg == LPC17_PHYID2)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if the PHY device address was found */
|
|
|
|
if (phyaddr > 31)
|
|
{
|
|
/* Failed to find PHY at any location */
|
|
|
|
ndbg("No PHY detected\n");
|
|
return -ENODEV;
|
|
}
|
|
nvdbg("phyaddr: %d\n", phyaddr);
|
|
|
|
/* Save the discovered PHY device address */
|
|
|
|
priv->lp_phyaddr = phyaddr;
|
|
|
|
/* Reset the PHY */
|
|
|
|
ret = lpc17_phyreset(phyaddr);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
lpc17_showmii(phyaddr, "After reset");
|
|
|
|
/* Check for preamble suppression support */
|
|
|
|
phyreg = lpc17_phyread(phyaddr, MII_MSR);
|
|
if ((phyreg & MII_MSR_MFRAMESUPPRESS) != 0)
|
|
{
|
|
/* The PHY supports preamble suppression */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_MCFG);
|
|
regval |= ETH_MCFG_SUPPRE;
|
|
lpc17_putreg(regval, LPC17_ETH_MCFG);
|
|
}
|
|
|
|
/* Are we configured to do auto-negotiation? */
|
|
|
|
#ifdef CONFIG_PHY_AUTONEG
|
|
/* Setup the Auto-negotiation advertisement: 100 or 10, and HD or FD */
|
|
|
|
lpc17_phywrite(phyaddr, MII_ADVERTISE,
|
|
(MII_ADVERTISE_100BASETXFULL | MII_ADVERTISE_100BASETXHALF |
|
|
MII_ADVERTISE_10BASETXFULL | MII_ADVERTISE_10BASETXHALF |
|
|
MII_ADVERTISE_CSMA));
|
|
|
|
/* Then perform the auto-negotiation */
|
|
|
|
ret = lpc17_phyautoneg(phyaddr);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#else
|
|
/* Set up the fixed PHY configuration */
|
|
|
|
ret = lpc17_phymode(phyaddr, LPC17_MODE_DEFLT);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* The link is established */
|
|
|
|
lpc17_showmii(phyaddr, "After link established");
|
|
|
|
/* Check configuration */
|
|
|
|
#if defined(CONFIG_PHY_KS8721)
|
|
phyreg = lpc17_phyread(phyaddr, MII_KS8721_10BTCR);
|
|
|
|
switch (phyreg & KS8721_10BTCR_MODE_MASK)
|
|
{
|
|
case KS8721_10BTCR_MODE_10BTHD: /* 10BASE-T half duplex */
|
|
priv->lp_mode = LPC17_10BASET_HD;
|
|
lpc17_putreg(0, LPC17_ETH_SUPP);
|
|
break;
|
|
case KS8721_10BTCR_MODE_100BTHD: /* 100BASE-T half duplex */
|
|
priv->lp_mode = LPC17_100BASET_HD;
|
|
break;
|
|
case KS8721_10BTCR_MODE_10BTFD: /* 10BASE-T full duplex */
|
|
priv->lp_mode = LPC17_10BASET_FD;
|
|
lpc17_putreg(0, LPC17_ETH_SUPP);
|
|
break;
|
|
case KS8721_10BTCR_MODE_100BTFD: /* 100BASE-T full duplex */
|
|
priv->lp_mode = LPC17_100BASET_FD;
|
|
break;
|
|
default:
|
|
ndbg("Unrecognized mode: %04x\n", phyreg);
|
|
return -ENODEV;
|
|
}
|
|
#elif defined(CONFIG_PHY_DP83848C)
|
|
phyreg = lpc17_phyread(phyaddr, MII_DP83848C_STS);
|
|
|
|
/* Configure for full/half duplex mode and speed */
|
|
|
|
switch (phyreg & 0x0006)
|
|
{
|
|
case 0x0000:
|
|
priv->lp_mode = LPC17_100BASET_HD;
|
|
break;
|
|
case 0x0002:
|
|
priv->lp_mode = LPC17_10BASET_HD;
|
|
break;
|
|
case 0x0004:
|
|
priv->lp_mode = LPC17_100BASET_FD;
|
|
break;
|
|
case 0x0006:
|
|
priv->lp_mode = LPC17_10BASET_FD;
|
|
break;
|
|
default:
|
|
ndbg("Unrecognized mode: %04x\n", phyreg);
|
|
return -ENODEV;
|
|
}
|
|
#elif defined(CONFIG_PHY_LAN8720)
|
|
{
|
|
uint16_t advertise;
|
|
uint16_t lpa;
|
|
|
|
up_udelay(500);
|
|
advertise = lpc17_phyread(phyaddr, MII_ADVERTISE);
|
|
lpa = lpc17_phyread(phyaddr, MII_LPA);
|
|
|
|
/* Check for 100BASETX full duplex */
|
|
|
|
if ((advertise & MII_ADVERTISE_100BASETXFULL) != 0 &&
|
|
(lpa & MII_LPA_100BASETXFULL) != 0)
|
|
{
|
|
priv->lp_mode = LPC17_100BASET_FD;
|
|
}
|
|
|
|
/* Check for 100BASETX half duplex */
|
|
|
|
else if ((advertise & MII_ADVERTISE_100BASETXHALF) != 0 &&
|
|
(lpa & MII_LPA_100BASETXHALF) != 0)
|
|
{
|
|
priv->lp_mode = LPC17_100BASET_HD;
|
|
}
|
|
|
|
/* Check for 10BASETX full duplex */
|
|
|
|
else if ((advertise & MII_ADVERTISE_10BASETXFULL) != 0 &&
|
|
(lpa & MII_LPA_10BASETXFULL) != 0)
|
|
{
|
|
priv->lp_mode = LPC17_10BASET_FD;
|
|
}
|
|
|
|
/* Check for 10BASETX half duplex */
|
|
|
|
else if ((advertise & MII_ADVERTISE_10BASETXHALF) != 0 &&
|
|
(lpa & MII_LPA_10BASETXHALF) != 0)
|
|
{
|
|
priv->lp_mode = LPC17_10BASET_HD;
|
|
}
|
|
else
|
|
{
|
|
ndbg("Unrecognized mode: %04x\n", phyreg);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
#else
|
|
# warning "PHY Unknown: speed and duplex are bogus"
|
|
#endif
|
|
|
|
ndbg("%dBase-T %s duplex\n",
|
|
(priv->lp_mode & LPC17_SPEED_MASK) == LPC17_SPEED_100 ? 100 : 10,
|
|
(priv->lp_mode & LPC17_DUPLEX_MASK) == LPC17_DUPLEX_FULL ?"full" : "half");
|
|
|
|
/* Disable auto-configuration. Set the fixed speed/duplex mode.
|
|
* (probably more than little redundant).
|
|
*/
|
|
|
|
ret = lpc17_phymode(phyaddr, priv->lp_mode);
|
|
lpc17_showmii(phyaddr, "After final configuration");
|
|
return ret;
|
|
}
|
|
#else
|
|
static inline int lpc17_phyinit(struct lpc17_driver_s *priv)
|
|
{
|
|
priv->lp_mode = LPC17_MODE_DEFLT;
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_txdescinit
|
|
*
|
|
* Description:
|
|
* Initialize the EMAC Tx descriptor table
|
|
*
|
|
* Parameters:
|
|
* priv - Pointer to EMAC device driver structure
|
|
*
|
|
* Returned Value:
|
|
* None directory.
|
|
* As a side-effect, it will initialize priv->lp_phyaddr and
|
|
* priv->lp_phymode.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lpc17_txdescinit(struct lpc17_driver_s *priv)
|
|
{
|
|
uint32_t *txdesc;
|
|
uint32_t *txstat;
|
|
uint32_t pktaddr;
|
|
int i;
|
|
|
|
/* Configure Tx descriptor and status tables */
|
|
|
|
lpc17_putreg(LPC17_TXDESC_BASE, LPC17_ETH_TXDESC);
|
|
lpc17_putreg(LPC17_TXSTAT_BASE, LPC17_ETH_TXSTAT);
|
|
lpc17_putreg(CONFIG_NET_NTXDESC-1, LPC17_ETH_TXDESCRNO);
|
|
|
|
/* Initialize Tx descriptors and link to packet buffers */
|
|
|
|
txdesc = (uint32_t*)LPC17_TXDESC_BASE;
|
|
pktaddr = LPC17_TXBUFFER_BASE;
|
|
|
|
for (i = 0; i < CONFIG_NET_NTXDESC; i++)
|
|
{
|
|
*txdesc++ = pktaddr;
|
|
*txdesc++ = (TXDESC_CONTROL_INT | (LPC17_MAXPACKET_SIZE - 1));
|
|
pktaddr += LPC17_MAXPACKET_SIZE;
|
|
}
|
|
|
|
/* Initialize Tx status */
|
|
|
|
txstat = (uint32_t*)LPC17_TXSTAT_BASE;
|
|
for (i = 0; i < CONFIG_NET_NTXDESC; i++)
|
|
{
|
|
*txstat++ = 0;
|
|
}
|
|
|
|
/* Point to first Tx descriptor */
|
|
|
|
lpc17_putreg(0, LPC17_ETH_TXPRODIDX);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_rxdescinit
|
|
*
|
|
* Description:
|
|
* Initialize the EMAC Rx descriptor table
|
|
*
|
|
* Parameters:
|
|
* priv - Pointer to EMAC device driver structure
|
|
*
|
|
* Returned Value:
|
|
* None directory.
|
|
* As a side-effect, it will initialize priv->lp_phyaddr and
|
|
* priv->lp_phymode.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lpc17_rxdescinit(struct lpc17_driver_s *priv)
|
|
{
|
|
uint32_t *rxdesc;
|
|
uint32_t *rxstat;
|
|
uint32_t pktaddr;
|
|
int i;
|
|
|
|
/* Configure Rx descriptor and status tables */
|
|
|
|
lpc17_putreg(LPC17_RXDESC_BASE, LPC17_ETH_RXDESC);
|
|
lpc17_putreg(LPC17_RXSTAT_BASE, LPC17_ETH_RXSTAT);
|
|
lpc17_putreg(CONFIG_NET_NRXDESC-1, LPC17_ETH_RXDESCNO);
|
|
|
|
/* Initialize Rx descriptors and link to packet buffers */
|
|
|
|
rxdesc = (uint32_t*)LPC17_RXDESC_BASE;
|
|
pktaddr = LPC17_RXBUFFER_BASE;
|
|
|
|
for (i = 0; i < CONFIG_NET_NRXDESC; i++)
|
|
{
|
|
*rxdesc++ = pktaddr;
|
|
*rxdesc++ = (RXDESC_CONTROL_INT | (LPC17_MAXPACKET_SIZE - 1));
|
|
pktaddr += LPC17_MAXPACKET_SIZE;
|
|
}
|
|
|
|
/* Initialize Rx status */
|
|
|
|
rxstat = (uint32_t*)LPC17_RXSTAT_BASE;
|
|
for (i = 0; i < CONFIG_NET_NRXDESC; i++)
|
|
{
|
|
*rxstat++ = 0;
|
|
*rxstat++ = 0;
|
|
}
|
|
|
|
/* Point to first Rx descriptor */
|
|
|
|
lpc17_putreg(0, LPC17_ETH_RXCONSIDX);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_macmode
|
|
*
|
|
* Description:
|
|
* Set the MAC to operate at a selected speed/duplex mode.
|
|
*
|
|
* Parameters:
|
|
* mode - speed/duplex mode
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef LPC17_HAVE_PHY
|
|
static void lpc17_macmode(uint8_t mode)
|
|
{
|
|
uint32_t regval;
|
|
|
|
/* Set up for full or half duplex operation */
|
|
|
|
if ((mode & LPC17_DUPLEX_MASK) == LPC17_DUPLEX_FULL)
|
|
{
|
|
/* Set the back-to-back inter-packet gap */
|
|
|
|
lpc17_putreg(21, LPC17_ETH_IPGT);
|
|
|
|
/* Set MAC to operate in full duplex mode with CRC and Pad enabled */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_MAC2);
|
|
regval |= (ETH_MAC2_FD | ETH_MAC2_CRCEN | ETH_MAC2_PADCRCEN);
|
|
lpc17_putreg(regval, LPC17_ETH_MAC2);
|
|
|
|
/* Select full duplex operation for ethernet controller */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_CMD);
|
|
regval |= (ETH_CMD_FD | ETH_CMD_RMII | ETH_CMD_PRFRAME);
|
|
lpc17_putreg(regval, LPC17_ETH_CMD);
|
|
}
|
|
else
|
|
{
|
|
/* Set the back-to-back inter-packet gap */
|
|
|
|
lpc17_putreg(18, LPC17_ETH_IPGT);
|
|
|
|
/* Set MAC to operate in half duplex mode with CRC and Pad enabled */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_MAC2);
|
|
regval &= ~ETH_MAC2_FD;
|
|
regval |= (ETH_MAC2_CRCEN | ETH_MAC2_PADCRCEN);
|
|
lpc17_putreg(regval, LPC17_ETH_MAC2);
|
|
|
|
/* Select half duplex operation for ethernet controller */
|
|
|
|
regval = lpc17_getreg(LPC17_ETH_CMD);
|
|
regval &= ~ETH_CMD_FD;
|
|
regval |= (ETH_CMD_RMII | ETH_CMD_PRFRAME);
|
|
lpc17_putreg(regval, LPC17_ETH_CMD);
|
|
}
|
|
|
|
/* This is currently done in lpc17_phyinit(). That doesn't
|
|
* seem like the right place. It should be done here.
|
|
*/
|
|
|
|
#if 0
|
|
regval = lpc17_getreg(LPC17_ETH_SUPP);
|
|
if ((mode & LPC17_SPEED_MASK) == LPC17_SPEED_100)
|
|
{
|
|
regval |= ETH_SUPP_SPEED;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~ETH_SUPP_SPEED;
|
|
}
|
|
lpc17_putreg(regval, LPC17_ETH_SUPP);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_ethreset
|
|
*
|
|
* Description:
|
|
* Configure and reset the Ethernet module, leaving it in a disabled state.
|
|
*
|
|
* Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* OK on success; a negated errno on failure
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc17_ethreset(struct lpc17_driver_s *priv)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
/* Reset the MAC */
|
|
|
|
flags = irqsave();
|
|
|
|
/* Put the MAC into the reset state */
|
|
|
|
lpc17_putreg((ETH_MAC1_TXRST | ETH_MAC1_MCSTXRST | ETH_MAC1_RXRST |
|
|
ETH_MAC1_MCSRXRST | ETH_MAC1_SIMRST | ETH_MAC1_SOFTRST),
|
|
LPC17_ETH_MAC1);
|
|
|
|
/* Disable RX/RX, clear modes, reset all control registers */
|
|
|
|
lpc17_putreg((ETH_CMD_REGRST | ETH_CMD_TXRST | ETH_CMD_RXRST),
|
|
LPC17_ETH_CMD);
|
|
|
|
/* Take the MAC out of the reset state */
|
|
|
|
up_udelay(50);
|
|
lpc17_putreg(0, LPC17_ETH_MAC1);
|
|
|
|
/* The RMII bit must be set on initialization (I'm not sure this needs
|
|
* to be done here but... oh well).
|
|
*/
|
|
|
|
lpc17_putreg(ETH_CMD_RMII, LPC17_ETH_CMD);
|
|
|
|
/* Set other misc configuration-related registers to default values */
|
|
|
|
lpc17_putreg(0, LPC17_ETH_MAC2);
|
|
lpc17_putreg(0, LPC17_ETH_SUPP);
|
|
lpc17_putreg(0, LPC17_ETH_TEST);
|
|
|
|
lpc17_putreg(18, LPC17_ETH_IPGR);
|
|
lpc17_putreg(((15 << ETH_CLRT_RMAX_SHIFT) | (55 << ETH_CLRT_COLWIN_SHIFT)),
|
|
LPC17_ETH_CLRT);
|
|
|
|
/* Set the Maximum Frame size register. "This field resets to the value
|
|
* 0x0600, which represents a maximum receive frame of 1536 octets. An
|
|
* untagged maximum size Ethernet frame is 1518 octets. A tagged frame adds
|
|
* four octets for a total of 1522 octets. If a shorter maximum length
|
|
* restriction is desired, program this 16-bit field."
|
|
*/
|
|
|
|
lpc17_putreg(LPC17_MAXPACKET_SIZE, LPC17_ETH_MAXF);
|
|
|
|
/* Disable all Ethernet controller interrupts */
|
|
|
|
lpc17_putreg(0, LPC17_ETH_INTEN);
|
|
|
|
/* Clear any pending interrupts (shouldn't be any) */
|
|
|
|
lpc17_putreg(0xffffffff, LPC17_ETH_INTCLR);
|
|
irqrestore(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Function: lpc17_ethinitialize
|
|
*
|
|
* Description:
|
|
* Initialize one Ethernet controller and driver structure.
|
|
*
|
|
* Parameters:
|
|
* intf - Selects the interface to be initialized.
|
|
*
|
|
* Returned Value:
|
|
* OK on success; Negated errno on failure.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
int lpc17_ethinitialize(int intf)
|
|
#else
|
|
static inline int lpc17_ethinitialize(int intf)
|
|
#endif
|
|
{
|
|
struct lpc17_driver_s *priv;
|
|
uint32_t regval;
|
|
int ret;
|
|
int i;
|
|
|
|
DEBUGASSERT(intf < CONFIG_LPC17_NINTERFACES);
|
|
priv = &g_ethdrvr[intf];
|
|
|
|
/* Turn on the ethernet MAC clock */
|
|
|
|
regval = lpc17_getreg(LPC17_SYSCON_PCONP);
|
|
regval |= SYSCON_PCONP_PCENET;
|
|
lpc17_putreg(regval, LPC17_SYSCON_PCONP);
|
|
|
|
/* Configure all GPIO pins needed by ENET */
|
|
|
|
for (i = 0; i < GPIO_NENET_PINS; i++)
|
|
{
|
|
(void)lpc17_configgpio(g_enetpins[i]);
|
|
}
|
|
lpc17_showpins();
|
|
|
|
/* Initialize the driver structure */
|
|
|
|
memset(priv, 0, sizeof(struct lpc17_driver_s));
|
|
priv->lp_dev.d_ifup = lpc17_ifup; /* I/F down callback */
|
|
priv->lp_dev.d_ifdown = lpc17_ifdown; /* I/F up (new IP address) callback */
|
|
priv->lp_dev.d_txavail = lpc17_txavail; /* New TX data callback */
|
|
#ifdef CONFIG_NET_IGMP
|
|
priv->lp_dev.d_addmac = lpc17_addmac; /* Add multicast MAC address */
|
|
priv->lp_dev.d_rmmac = lpc17_rmmac; /* Remove multicast MAC address */
|
|
#endif
|
|
priv->lp_dev.d_private = (void*)priv; /* Used to recover private state from dev */
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
# error "A mechanism to associate base address an IRQ with an interface is needed"
|
|
priv->lp_base = ??; /* Ethernet controller base address */
|
|
priv->lp_irq = ??; /* Ethernet controller IRQ number */
|
|
#endif
|
|
|
|
/* Create a watchdog for timing polling for and timing of transmisstions */
|
|
|
|
priv->lp_txpoll = wd_create(); /* Create periodic poll timer */
|
|
priv->lp_txtimeout = wd_create(); /* Create TX timeout timer */
|
|
|
|
/* Reset the Ethernet controller and leave in the ifdown statue. The
|
|
* Ethernet controller will be properly re-initialized each time
|
|
* lpc17_ifup() is called.
|
|
*/
|
|
|
|
lpc17_ifdown(&priv->lp_dev);
|
|
|
|
/* Attach the IRQ to the driver */
|
|
|
|
#if CONFIG_LPC17_NINTERFACES > 1
|
|
ret = irq_attach(priv->irq, lpc17_interrupt);
|
|
#else
|
|
ret = irq_attach(LPC17_IRQ_ETH, lpc17_interrupt);
|
|
#endif
|
|
if (ret != 0)
|
|
{
|
|
/* We could not attach the ISR to the the interrupt */
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* Register the device with the OS so that socket IOCTLs can be performed */
|
|
|
|
(void)netdev_register(&priv->lp_dev);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_netinitialize
|
|
*
|
|
* Description:
|
|
* Initialize the first network interface. If there are more than one
|
|
* interface in the chip, then board-specific logic will have to provide
|
|
* this function to determine which, if any, Ethernet controllers should
|
|
* be initialized.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if CONFIG_LPC17_NINTERFACES == 1
|
|
void up_netinitialize(void)
|
|
{
|
|
(void)lpc17_ethinitialize(0);
|
|
}
|
|
#endif
|
|
#endif /* LPC17_NETHCONTROLLERS > 0 */
|
|
#endif /* CONFIG_NET && CONFIG_LPC17_ETHERNET */
|