2859 lines
84 KiB
C
2859 lines
84 KiB
C
/****************************************************************************
|
||
* arch/arm/src/pic32mx/pic32mx_ethernet.c
|
||
*
|
||
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||
*
|
||
* This driver derives from the PIC32MX Ethernet Driver
|
||
*
|
||
* 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_PIC32MX_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 <arch/irq.h>
|
||
#include <arch/board/board.h>
|
||
|
||
#include "chip.h"
|
||
#include "up_arch.h"
|
||
#include "pic32mx-config.h"
|
||
#include "pic32mx-ethernet.h"
|
||
#include "pic32mx-internal.h"
|
||
|
||
/* Does this chip have and ethernet controller? */
|
||
|
||
#if CHIP_NETHERNET > 0
|
||
|
||
/****************************************************************************
|
||
* Definitions
|
||
****************************************************************************/
|
||
|
||
/* Configuration ************************************************************/
|
||
/* CONFIG_PIC32MX_NINTERFACES determines the number of physical interfaces
|
||
* that will be supported -- unless it is more than actually supported by the
|
||
* hardware!
|
||
*/
|
||
|
||
#if !defined(CONFIG_PIC32MX_NINTERFACES) || CONFIG_PIC32MX_NINTERFACES > CHIP_NETHERNET
|
||
# undef CONFIG_PIC32MX_NINTERFACES
|
||
# define CONFIG_PIC32MX_NINTERFACES CHIP_NETHERNET
|
||
#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 PIC32MX).
|
||
*/
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
# warning "Only a single ethernet controller is supported"
|
||
# undef CONFIG_PIC32MX_NINTERFACES
|
||
# define CONFIG_PIC32MX_NINTERFACES 1
|
||
#endif
|
||
|
||
/* CONFIG_NET_MULTIBUFFER is required */
|
||
|
||
#ifndef CONFIG_NET_MULTIBUFFER
|
||
# error "CONFIG_NET_MULTIBUFFER=y is required"
|
||
#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
|
||
|
||
/* Use defaults if the number of discriptors is not provided */
|
||
|
||
#ifndef CONFIG_NET_NTXDESC
|
||
# define CONFIG_NET_NTXDESC 2
|
||
#endif
|
||
|
||
#ifndef CONFIG_NET_NRXDESC
|
||
# define CONFIG_NET_NRXDESC 4
|
||
#endif
|
||
|
||
/* The number of buffers will, then, be one for each descriptor plus one extra */
|
||
|
||
#define PIC32MX_NBUFFERS (CONFIG_NET_NRXDESC + CONFIG_NET_NTXDESC + 1)
|
||
|
||
/* 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 pic32mx_dumppacket(m,a,n) lib_dumpbuffer(m,a,n)
|
||
#else
|
||
# define pic32mx_dumppacket(m,a,n)
|
||
#endif
|
||
|
||
/* Timing *******************************************************************/
|
||
|
||
/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
|
||
|
||
#define PIC32MX_WDDELAY (1*CLK_TCK)
|
||
#define PIC32MX_POLLHSEC (1*2)
|
||
|
||
/* TX timeout = 1 minute */
|
||
|
||
#define PIC32MX_TXTIMEOUT (60*CLK_TCK)
|
||
|
||
/* Ethernet MII clocking.
|
||
*
|
||
* The clock divider used to create the MII Management Clock (MDC). The MIIM
|
||
* module uses the SYSCLK as an input clock. According to the IEEE 802.3
|
||
* Specification this should be no faster than 2.5 MHz. However, some PHYs
|
||
* support clock rates up to 12.5 MHz.
|
||
*
|
||
* The board.h file provides the "ideal" divisor as BOARD_EMAC_MIIM_DIV. We
|
||
* pick the closest, actual divisor greater than or equal to this.
|
||
*/
|
||
|
||
#if BOARD_EMAC_MIIM_DIV <= 4
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV4
|
||
#elif BOARD_EMAC_MIIM_DIV <= 6
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV6
|
||
#elif BOARD_EMAC_MIIM_DIV <= 8
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV8
|
||
#elif BOARD_EMAC_MIIM_DIV <= 10
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV10
|
||
#elif BOARD_EMAC_MIIM_DIV <= 14
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV14
|
||
#elif BOARD_EMAC_MIIM_DIV <= 20
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV20
|
||
#elif BOARD_EMAC_MIIM_DIV <= 40
|
||
# define EMAC1_MCFG_CLKSEL_DIV EMAC1_MCFG_CLKSEL_DIV40
|
||
#else
|
||
# error "MIIM divider cannot be realized"
|
||
#endif
|
||
|
||
/* Interrupts ***************************************************************/
|
||
|
||
#define ETH_RXINTS (ETH_INT_RXOVFLW | ETH_INT_RXBUFNA | ETH_INT_RXDONE | ETH_INT_RXBUSE)
|
||
#define ETH_TXINTS (ETH_INT_TXABORT | ETH_INT_TXDONE | ETH_INT_TXBUSE)
|
||
|
||
/* Misc. Helpers ***********************************************************/
|
||
|
||
/* This is a helper pointer for accessing the contents of the Ethernet header */
|
||
|
||
#define BUF ((struct uip_eth_hdr *)priv->pd_dev.d_buf)
|
||
|
||
/* PHYs *********************************************************************/
|
||
/* Select PHY-specific values. Add more PHYs as needed. */
|
||
|
||
#if defined(CONFIG_PHY_KS8721)
|
||
# define PIC32MX_PHYNAME "KS8721"
|
||
# define PIC32MX_PHYID1 MII_PHYID1_KS8721
|
||
# define PIC32MX_PHYID2 MII_PHYID2_KS8721
|
||
# define PIC32MX_HAVE_PHY 1
|
||
#elif defined(CONFIG_PHY_DP83848C)
|
||
# define PIC32MX_PHYNAME "DP83848C"
|
||
# define PIC32MX_PHYID1 MII_PHYID1_DP83848C
|
||
# define PIC32MX_PHYID2 MII_PHYID2_DP83848C
|
||
# define PIC32MX_HAVE_PHY 1
|
||
#elif defined(CONFIG_PHY_LAN8720)
|
||
# define PIC32MX_PHYNAME "LAN8720"
|
||
# define PIC32MX_PHYID1 MII_PHYID1_LAN8720
|
||
# define PIC32MX_PHYID2 MII_PHYID2_LAN8720
|
||
# define PIC32MX_HAVE_PHY 1
|
||
#else
|
||
# warning "No PHY specified!"
|
||
# undef PIC32MX_HAVE_PHY
|
||
#endif
|
||
|
||
#define MII_BIG_TIMEOUT 666666
|
||
|
||
/* These definitions are used to remember the speed/duplex settings */
|
||
|
||
#define PIC32MX_SPEED_MASK 0x01
|
||
#define PIC32MX_SPEED_100 0x01
|
||
#define PIC32MX_SPEED_10 0x00
|
||
|
||
#define PIC32MX_DUPLEX_MASK 0x02
|
||
#define PIC32MX_DUPLEX_FULL 0x02
|
||
#define PIC32MX_DUPLEX_HALF 0x00
|
||
|
||
#define PIC32MX_10BASET_HD (PIC32MX_SPEED_10 | PIC32MX_DUPLEX_HALF)
|
||
#define PIC32MX_10BASET_FD (PIC32MX_SPEED_10 | PIC32MX_DUPLEX_FULL)
|
||
#define PIC32MX_100BASET_HD (PIC32MX_SPEED_100 | PIC32MX_DUPLEX_HALF)
|
||
#define PIC32MX_100BASET_FD (PIC32MX_SPEED_100 | PIC32MX_DUPLEX_FULL)
|
||
|
||
#ifdef CONFIG_PHY_SPEED100
|
||
# ifdef CONFIG_PHY_FDUPLEX
|
||
# define PIC32MX_MODE_DEFLT PIC32MX_100BASET_FD
|
||
# else
|
||
# define PIC32MX_MODE_DEFLT PIC32MX_100BASET_HD
|
||
# endif
|
||
#else
|
||
# ifdef CONFIG_PHY_FDUPLEX
|
||
# define PIC32MX_MODE_DEFLT PIC32MX_10BASET_FD
|
||
# else
|
||
# define PIC32MX_MODE_DEFLT PIC32MX_10BASET_HD
|
||
# endif
|
||
#endif
|
||
|
||
/* Misc Helper Macros *******************************************************/
|
||
|
||
#define PHYS_ADDR(va) ((uint32_t)(va) & 0x1fffffff)
|
||
#define VIRT_ADDR(pa) (KSEG1_BASE | (uint32_t)(pa))
|
||
|
||
/* Ever-present MIN and MAX macros */
|
||
|
||
#ifndef MIN
|
||
# define MIN(a,b) (a < b ? a : b)
|
||
#endif
|
||
|
||
#ifndef MAX
|
||
# define MAX(a,b) (a > b ? a : b)
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Private Types
|
||
****************************************************************************/
|
||
|
||
/* EMAC statistics (debug only) */
|
||
|
||
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
|
||
struct pic32mx_statistics_s
|
||
{
|
||
uint32_t rx_done; /* Rx done interrupts */
|
||
uint32_t rx_errors; /* Number of Rx error interrupts */
|
||
uint32_t rx_ovflw; /* Number of Rx overflow error interrupts */
|
||
uint32_t rx_bufna; /* Number of Rx buffer not available errors */
|
||
uint32_t rx_buse; /* Number of Rx BVCI bus 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_done; /* Tx done interrupts */
|
||
uint32_t tx_errors; /* Number of Tx error interrupts (OR of other errors) */
|
||
uint32_t tx_abort; /* Number of Tx abort interrupts */
|
||
uint32_t tx_buse; /* Number of Tx bus errors */
|
||
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_timeouts; /* Number of Tx timeout errors */
|
||
};
|
||
# define EMAC_STAT(priv,name) priv->pd_stat.name++
|
||
#else
|
||
# define EMAC_STAT(priv,name)
|
||
#endif
|
||
|
||
/* The pic32mx_driver_s encapsulates all state information for a single hardware
|
||
* interface
|
||
*/
|
||
|
||
struct pic32mx_driver_s
|
||
{
|
||
/* The following fields would only be necessary on chips that support
|
||
* multiple Ethernet controllers.
|
||
*/
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
uint32_t pd_base; /* Ethernet controller base address */
|
||
int pd_irq; /* Ethernet controller IRQ vector number */
|
||
int pd_irqsrc; /* Ethernet controller IRQ source number */
|
||
#endif
|
||
|
||
bool pd_ifup; /* true:ifup false:ifdown */
|
||
bool pd_mode; /* speed/duplex */
|
||
bool pd_txpending; /* There is a pending Tx in pd_dev */
|
||
#ifdef PIC32MX_HAVE_PHY
|
||
uint8_t pd_phyaddr; /* PHY device address */
|
||
#endif
|
||
uint32_t pd_inten; /* Shadow copy of INTEN register */
|
||
WDOG_ID pd_txpoll; /* TX poll timer */
|
||
WDOG_ID pd_txtimeout; /* TX timeout timer */
|
||
|
||
sq_queue_t pd_freebuffers; /* The free buffer list */
|
||
|
||
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
|
||
struct pic32mx_statistics_s pd_stat;
|
||
#endif
|
||
|
||
/* This holds the information visible to uIP/NuttX */
|
||
|
||
struct uip_driver_s pd_dev; /* Interface understood by uIP */
|
||
|
||
/* Descriptors and packet buffers */
|
||
|
||
struct pic32mx_rxdesc_s pd_rxdesc[CONFIG_NET_NRXDESC];
|
||
struct pic32mx_txdesc_s pd_txdesc[CONFIG_NET_NTXDESC];
|
||
uint8_t pd_buffers[PIC32MX_NBUFFERS * CONFIG_NET_BUFSIZE];
|
||
};
|
||
|
||
/****************************************************************************
|
||
* Private Data
|
||
****************************************************************************/
|
||
|
||
/* Array of ethernet driver status structures */
|
||
|
||
static struct pic32mx_driver_s g_ethdrvr[CONFIG_PIC32MX_NINTERFACES];
|
||
|
||
/****************************************************************************
|
||
* Private Function Prototypes
|
||
****************************************************************************/
|
||
|
||
/* Register operations */
|
||
|
||
#ifdef CONFIG_NET_REGDEBUG
|
||
static void pic32mx_printreg(uint32_t addr, uint32_t val, bool iswrite);
|
||
static void pic32mx_checkreg(uint32_t addr, uint32_t val, bool iswrite);
|
||
static uint32_t pic32mx_getreg(uint32_t addr);
|
||
static void pic32mx_putreg(uint32_t val, uint32_t addr);
|
||
#else
|
||
# define pic32mx_getreg(addr) getreg32(addr)
|
||
# define pic32mx_putreg(val,addr) putreg32(val,addr)
|
||
#endif
|
||
|
||
/* Buffer and descriptor management */
|
||
|
||
static inline void pic32mx_bufferinit(struct pic32mx_driver_s *priv);
|
||
static uint8_t *pic32mx_allocbuffer(struct pic32mx_driver_s *priv);
|
||
static void pic32mx_freebuffer(struct pic32mx_driver_s *priv, uint8_t *buffer);
|
||
|
||
static inline void pic32mx_txdescinit(struct pic32mx_driver_s *priv);
|
||
static inline void pic32mx_rxdescinit(struct pic32mx_driver_s *priv);
|
||
static struct pic32mx_txdesc_s *pic32mx_txdesc(struct pic32mx_driver_s *priv);
|
||
static struct pic32mx_rxdesc_s *pic32mx_rxdesc(struct pic32mx_driver_s *priv);
|
||
|
||
/* Common TX logic */
|
||
|
||
static int pic32mx_transmit(struct pic32mx_driver_s *priv);
|
||
static int pic32mx_uiptxpoll(struct uip_driver_s *dev);
|
||
|
||
/* Interrupt handling */
|
||
|
||
static void pic32mx_response(struct pic32mx_driver_s *priv);
|
||
static void pic32mx_rxdone(struct pic32mx_driver_s *priv);
|
||
static void pic32mx_txdone(struct pic32mx_driver_s *priv);
|
||
static int pic32mx_interrupt(int irq, void *context);
|
||
|
||
/* Watchdog timer expirations */
|
||
|
||
static void pic32mx_polltimer(int argc, uint32_t arg, ...);
|
||
static void pic32mx_txtimeout(int argc, uint32_t arg, ...);
|
||
|
||
/* NuttX callback functions */
|
||
|
||
static int pic32mx_ifup(struct uip_driver_s *dev);
|
||
static int pic32mx_ifdown(struct uip_driver_s *dev);
|
||
static int pic32mx_txavail(struct uip_driver_s *dev);
|
||
#ifdef CONFIG_NET_IGMP
|
||
static int pic32mx_addmac(struct uip_driver_s *dev, const uint8_t *mac);
|
||
static int pic32mx_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
|
||
#endif
|
||
|
||
/* PHY initialization functions */
|
||
|
||
#ifdef PIC32MX_HAVE_PHY
|
||
# ifdef CONFIG_NET_REGDEBUG
|
||
static void pic32mx_showmii(uint8_t phyaddr, const char *msg);
|
||
# else
|
||
# define pic32mx_showmii(phyaddr,msg)
|
||
# endif
|
||
|
||
static void pic32mx_phywrite(uint8_t phyaddr, uint8_t regaddr,
|
||
uint16_t phydata);
|
||
static uint16_t pic32mx_phyread(uint8_t phyaddr, uint8_t regaddr);
|
||
static inline int pic32mx_phyreset(uint8_t phyaddr);
|
||
# ifdef CONFIG_PHY_AUTONEG
|
||
static inline int pic32mx_phyautoneg(uint8_t phyaddr);
|
||
# endif
|
||
static int pic32mx_phymode(uint8_t phyaddr, uint8_t mode);
|
||
static inline int pic32mx_phyinit(struct pic32mx_driver_s *priv);
|
||
#else
|
||
# define pic32mx_phyinit(priv)
|
||
#endif
|
||
|
||
/* EMAC Initialization functions */
|
||
|
||
static void pic32mx_macmode(uint8_t mode);
|
||
static void pic32mx_ethreset(struct pic32mx_driver_s *priv);
|
||
|
||
/****************************************************************************
|
||
* Private Functions
|
||
****************************************************************************/
|
||
|
||
/*******************************************************************************
|
||
* Name: pic32mx_printreg
|
||
*
|
||
* Description:
|
||
* Print the contents of an PIC32MX register operation
|
||
*
|
||
*******************************************************************************/
|
||
|
||
#ifdef CONFIG_NET_REGDEBUG
|
||
static void pic32mx_printreg(uint32_t addr, uint32_t val, bool iswrite)
|
||
{
|
||
dbg("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val);
|
||
}
|
||
#endif
|
||
|
||
/*******************************************************************************
|
||
* Name: pic32mx_checkreg
|
||
*
|
||
* Description:
|
||
* Get the contents of an PIC32MX register
|
||
*
|
||
*******************************************************************************/
|
||
|
||
#ifdef CONFIG_NET_REGDEBUG
|
||
static void pic32mx_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 */
|
||
|
||
pic32mx_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 */
|
||
|
||
pic32mx_printreg(addr, val, iswrite);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/*******************************************************************************
|
||
* Name: pic32mx_getreg
|
||
*
|
||
* Description:
|
||
* Get the contents of an PIC32MX register
|
||
*
|
||
*******************************************************************************/
|
||
|
||
#ifdef CONFIG_NET_REGDEBUG
|
||
static uint32_t pic32mx_getreg(uint32_t addr)
|
||
{
|
||
/* Read the value from the register */
|
||
|
||
uint32_t val = getreg32(addr);
|
||
|
||
/* Check if we need to print this value */
|
||
|
||
pic32mx_checkreg(addr, val, false);
|
||
return val;
|
||
}
|
||
#endif
|
||
|
||
/*******************************************************************************
|
||
* Name: pic32mx_putreg
|
||
*
|
||
* Description:
|
||
* Set the contents of an PIC32MX register to a value
|
||
*
|
||
*******************************************************************************/
|
||
|
||
#ifdef CONFIG_NET_REGDEBUG
|
||
static void pic32mx_putreg(uint32_t val, uint32_t addr)
|
||
{
|
||
/* Check if we need to print this value */
|
||
|
||
pic32mx_checkreg(addr, val, true);
|
||
|
||
/* Write the value */
|
||
|
||
putreg32(val, addr);
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_bufferinit
|
||
*
|
||
* Description:
|
||
* Initialize the buffers by placing them all in a free list
|
||
*
|
||
* Parameters:
|
||
* priv - Pointer to EMAC device driver structure
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void pic32mx_bufferinit(struct pic32mx_driver_s *priv)
|
||
{
|
||
uint8_t *buffer;
|
||
int i;
|
||
|
||
for (i = 0, buffer = priv->pd_buffers; i < PIC32MX_NBUFFERS; i++)
|
||
{
|
||
sq_addlast((sq_entry_t*)buffer, &priv->pd_freebuffers);
|
||
buffer += CONFIG_NET_BUFSIZE;
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_allocbuffer
|
||
*
|
||
* Description:
|
||
* Allocate one buffer by removing it from the free list
|
||
*
|
||
* Parameters:
|
||
* priv - Pointer to EMAC device driver structure
|
||
*
|
||
* Returned Value:
|
||
* Pointer to the allocated buffer (or NULL on failure)
|
||
*
|
||
****************************************************************************/
|
||
|
||
static uint8_t *pic32mx_allocbuffer(struct pic32mx_driver_s *priv)
|
||
{
|
||
return (uint8_t*)sq_remfirst(&priv->pd_freebuffers);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_freebuffer
|
||
*
|
||
* Description:
|
||
* Free one buffer by returning it to the free list
|
||
*
|
||
* Parameters:
|
||
* priv - Pointer to EMAC device driver structure
|
||
*
|
||
* Returned Value:
|
||
* Pointer to the allocated buffer (or NULL on failure)
|
||
*
|
||
****************************************************************************/
|
||
|
||
static void pic32mx_freebuffer(struct pic32mx_driver_s *priv, uint8_t *buffer)
|
||
{
|
||
sq_addlast((sq_entry_t*)buffer, &priv->pd_freebuffers);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_txdescinit
|
||
*
|
||
* Description:
|
||
* Initialize the EMAC Tx descriptor table
|
||
*
|
||
* Parameters:
|
||
* priv - Pointer to EMAC device driver structure
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void pic32mx_txdescinit(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_txdesc_s *txdesc;
|
||
int i;
|
||
|
||
/* Assign a buffer to each TX descriptor. For now, just mark each TX
|
||
* descriptor as owned by softare andnot linked.
|
||
*/
|
||
|
||
for (i = 0; i < CONFIG_NET_NTXDESC; i++)
|
||
{
|
||
/* Point to the next entry */
|
||
|
||
txdesc = &priv->pd_txdesc[i];
|
||
|
||
/* Initialize the buffer. It is idle, owned by software and has
|
||
* no buffer assigned to it.
|
||
*/
|
||
|
||
txdesc->status = TXDESC_STATUS_SOWN | TXDESC_STATUS_NPV;
|
||
txdesc->address = 0;
|
||
txdesc->tsv1 = 0;
|
||
txdesc->tsv2 = 0;
|
||
|
||
/* Set the NEXTED pointer. If this is the last descriptor in the
|
||
* list, then set the NEXTED pointer back to the first entry,
|
||
* creating a ring.
|
||
*/
|
||
|
||
if (i == (CONFIG_NET_NRXDESC-1))
|
||
{
|
||
txdesc->nexted = (uint32_t)priv->pd_txdesc;
|
||
}
|
||
else
|
||
{
|
||
txdesc->nexted = (uint32_t)&priv->pd_txdesc[i+1];
|
||
}
|
||
}
|
||
|
||
/* Update the ETHTXST register with the physical address of the head of
|
||
* the TX descriptors list.
|
||
*/
|
||
|
||
pic32mx_putreg(PHYS_ADDR(priv->pd_txdesc), PIC32MX_ETH_TXST);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_rxdescinit
|
||
*
|
||
* Description:
|
||
* Initialize the EMAC Rx descriptor table
|
||
*
|
||
* Parameters:
|
||
* priv - Pointer to EMAC device driver structure
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
static inline void pic32mx_rxdescinit(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_rxdesc_s *rxdesc;
|
||
int i;
|
||
|
||
/* Prepare a list of RX descriptors populated with valid buffers for
|
||
* messages to be received. Properly update the NPV, EOWN = 1 and
|
||
* DATA_BUFFER_ADDRESS fields in the RX descriptors. The
|
||
* DATA_BUFFER_ADDRESS should contain the physical address of the
|
||
* corresponding RX buffer.
|
||
*/
|
||
|
||
for (i = 0; i < CONFIG_NET_NRXDESC; i++)
|
||
{
|
||
/* Point to the next entry */
|
||
|
||
rxdesc = &priv->pd_rxdesc[i];
|
||
|
||
/* Initialize the descriptor. Assign it a buffer and make it ready
|
||
* for reception.
|
||
*/
|
||
|
||
rxdesc->status = RXDESC_STATUS_EOWN | TXDESC_STATUS_NPV;
|
||
rxdesc->address = PHYS_ADDR(pic32mx_allocbuffer(priv));
|
||
rxdesc->rsv1 = 0;
|
||
rxdesc->rsv2 = 0;
|
||
|
||
/* Set the NEXTED pointer. If this is the last descriptor in the
|
||
* list, then set the NEXTED pointer back to the first entry,
|
||
* creating a ring.
|
||
*/
|
||
|
||
if (i == (CONFIG_NET_NRXDESC-1))
|
||
{
|
||
rxdesc->nexted = (uint32_t)priv->pd_rxdesc;
|
||
}
|
||
else
|
||
{
|
||
rxdesc->nexted = (uint32_t)&priv->pd_rxdesc[i+1];
|
||
}
|
||
}
|
||
|
||
/* Update the ETHRXST register with the physical address of the head of the
|
||
* RX descriptors list.
|
||
*/
|
||
|
||
pic32mx_putreg(PHYS_ADDR(priv->pd_rxdesc), PIC32MX_ETH_RXST);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_txdesc
|
||
*
|
||
* Description:
|
||
* Check if a free TX descriptor is available.
|
||
*
|
||
* Parameters:
|
||
* priv - Reference to the driver state structure
|
||
*
|
||
* Returned Value:
|
||
* A pointer to an available TX descriptor 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 struct pic32mx_txdesc_s *pic32mx_txdesc(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_txdesc_s *txdesc;
|
||
int i;
|
||
|
||
/* Inspect the list of TX descriptors to see if the EOWN bit is cleared. If it
|
||
* is, this descriptor is now under software control and the message was
|
||
* transmitted.
|
||
*/
|
||
|
||
for (i = 0; i < CONFIG_NET_NTXDESC; i++)
|
||
{
|
||
/* Check if software owns this descriptor */
|
||
|
||
txdesc = &priv->pd_txdesc[i];
|
||
if ((txdesc->status & TXDESC_STATUS_EOWN) == 0)
|
||
{
|
||
/* Yes.. return a pointer to the desciptor */
|
||
|
||
return txdesc;
|
||
}
|
||
}
|
||
|
||
/* All descriptors are owned by the Ethernet controller.. return NULL */
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_rxdesc
|
||
*
|
||
* Description:
|
||
* Check if a RX descriptor is owned by the software.
|
||
*
|
||
* Parameters:
|
||
* priv - Reference to the driver state structure
|
||
*
|
||
* Returned Value:
|
||
* A pointer to the RX descriptor 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 struct pic32mx_rxdesc_s *pic32mx_rxdesc(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_rxdesc_s *rxdesc;
|
||
int i;
|
||
|
||
/* Inspect the list of RX descriptors to see if the EOWN bit is cleared.
|
||
* If it is, this descriptor is now under software control and a message was
|
||
* received. Use SOP and EOP to extract the message, use BYTE_COUNT, RXF_RSV,
|
||
* RSV and PKT_CHECKSUM to get the message characteristics.
|
||
*/
|
||
|
||
for (i = 0; i < CONFIG_NET_NRXDESC; i++)
|
||
{
|
||
/* Check if software owns this descriptor */
|
||
|
||
rxdesc = &priv->pd_rxdesc[i];
|
||
if ((rxdesc->status & RXDESC_STATUS_EOWN) == 0)
|
||
{
|
||
/* Yes.. return a pointer to the desciptor */
|
||
|
||
return rxdesc;
|
||
}
|
||
}
|
||
|
||
/* All descriptors are owned by the Ethernet controller.. return NULL */
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_transmit(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_txdesc_s *txdesc;
|
||
|
||
/* 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(priv->pd_dev.d_buf && priv->pd_dev.d_len < CONFIG_NET_BUFSIZE);
|
||
|
||
/* Increment statistics and dump the packet *if so configured) */
|
||
|
||
EMAC_STAT(priv, tx_packets);
|
||
pic32mx_dumppacket("Transmit packet", priv->pd_dev.d_buf, priv->pd_dev.d_len);
|
||
|
||
/* In order to transmit a message:
|
||
*
|
||
* The SOP, EOP, DATA_BUFFER_ADDRESS and BYTE_COUNT will be updated when a
|
||
* particular message has to be transmitted. The DATA_BUFFER_ADDRESS will
|
||
* contain the physical address of the message, the BYTE_COUNT message size.
|
||
* SOP and EOP are set depending on how many packets are needed to transmit
|
||
* the message.
|
||
*/
|
||
|
||
/* Find the first available TX descriptor. We are guaranteed that is will
|
||
* not fail by upstream logic that assures that a TX packet is available
|
||
* before polling uIP.
|
||
*/
|
||
|
||
txdesc = pic32mx_txdesc(priv);
|
||
DEBUGASSERT(txdesc != NULL);
|
||
|
||
/* Remove the transmit buffer from the device structure and assign it to
|
||
* the TX descriptor.
|
||
*/
|
||
|
||
txdesc->address = PHYS_ADDR(priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
|
||
/* Set the BYTE_COUNT for in the TX descriptor with the number of bytes
|
||
* contained in the buffer.
|
||
*/
|
||
|
||
txdesc->tsv2 &= TXDESC_TSV2_BYTECOUNT_MASK;
|
||
txdesc->tsv2 |= ((uint32_t)priv->pd_dev.d_len << TXDESC_TSV2_BYTECOUNT_SHIFT);
|
||
priv->pd_dev.d_len = 0;
|
||
|
||
/* Set EOWN = 1 to indicate that the packet belongs to Ethernet and set both
|
||
* SOP and EOP to indicate that the packet both begins and ends with this
|
||
* frame.
|
||
*/
|
||
|
||
txdesc->status |= (TXDESC_STATUS_EOWN | TXDESC_STATUS_EOP | TXDESC_STATUS_SOP);
|
||
|
||
/* Enable the transmission of the message by setting the TXRTS bit (ETHCON1:9). */
|
||
|
||
pic32mx_putreg(ETH_CON1_TXRTS | ETH_CON1_ON, PIC32MX_ETH_CON1SET);
|
||
|
||
/* Enable Tx interrupts */
|
||
|
||
priv->pd_inten |= ETH_TXINTS;
|
||
pic32mx_putreg(priv->pd_inten, PIC32MX_ETH_IEN);
|
||
|
||
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
|
||
|
||
(void)wd_start(priv->pd_txtimeout, PIC32MX_TXTIMEOUT, pic32mx_txtimeout,
|
||
1, (uint32_t)priv);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_uiptxpoll(struct uip_driver_s *dev)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_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->pd_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->pd_dev);
|
||
pic32mx_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.
|
||
*/
|
||
|
||
if (pic32mx_txdesc(priv) == NULL || sq_empty(&priv->pd_freebuffers))
|
||
{
|
||
/* There are no more TX descriptors/buffers available.. stop the poll */
|
||
|
||
return -EAGAIN;
|
||
}
|
||
}
|
||
|
||
/* If zero is returned, the polling will continue until all connections have
|
||
* been examined.
|
||
*/
|
||
|
||
return ret;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_response(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_txdesc_s *txdesc;
|
||
|
||
/* Check if there is room in the device to hold another packet. */
|
||
|
||
txdesc = pic32mx_txdesc(priv);
|
||
if (txdesc != NULL)
|
||
{
|
||
/* Yes.. queue the packet now. */
|
||
|
||
pic32mx_transmit(priv);
|
||
}
|
||
else
|
||
{
|
||
/* No.. mark the Tx as pending and halt further Tx interrupts */
|
||
|
||
DEBUGASSERT((priv->pd_inten & ETH_INT_TXDONE) != 0);
|
||
|
||
priv->pd_txpending = true;
|
||
priv->pd_inten &= ~ETH_RXINTS;
|
||
pic32mx_putreg(priv->pd_inten, PIC32MX_ETH_IEN);
|
||
EMAC_STAT(priv, tx_pending);
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_rxdone(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_rxdesc_s *rxdesc;
|
||
|
||
/* Loop while there are incoming packets to be processed, that is, while
|
||
* the producer index is not equal to the consumer index.
|
||
*/
|
||
|
||
for (;;)
|
||
{
|
||
/* Check if any RX descriptor has the EOWN bit cleared meaning that the
|
||
* this descriptor is now under software control and a message was
|
||
* received.
|
||
*/
|
||
|
||
rxdesc = pic32mx_rxdesc(priv);
|
||
if (rxdesc == NULL)
|
||
{
|
||
/* All RX descriptors are owned by the Ethernet controller... we
|
||
* are finished here.
|
||
*/
|
||
|
||
return;
|
||
}
|
||
|
||
/* Update statistics */
|
||
|
||
EMAC_STAT(priv, rx_packets);
|
||
|
||
/* Get the packet length */
|
||
|
||
priv->pd_dev.d_len = (rxdesc->rsv1 & RXDESC_RSV1_BYTECOUNT_MASK) >> RXDESC_RSV1_BYTECOUNT_SHIFT;
|
||
|
||
/* Check for errors */
|
||
|
||
if ((rxdesc->status & RXDESC_RSV1_OK) == 0)
|
||
{
|
||
nlldbg("Error. rxdesc: %08x\n", rxdesc->status);
|
||
EMAC_STAT(priv, rx_pkterr);
|
||
}
|
||
|
||
/* If the packet length 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 (priv->pd_dev.d_len > CONFIG_NET_BUFSIZE)
|
||
{
|
||
nlldbg("Too big. packet length: %d rxdesc: %08x\n", priv->pd_dev.d_len, rxdesc->status);
|
||
EMAC_STAT(priv, rx_pktsize);
|
||
}
|
||
|
||
/* We don't have any logic here for reassembling packets from fragments. */
|
||
|
||
else if ((rxdesc->status & (RXDESC_STATUS_EOP|RXDESC_STATUS_SOP)) != (RXDESC_STATUS_EOP|RXDESC_STATUS_SOP))
|
||
{
|
||
nlldbg("Fragment. packet length: %d rxdesc: %08x\n", priv->pd_dev.d_len, rxdesc->status);
|
||
EMAC_STAT(priv, rx_fragment);
|
||
}
|
||
else
|
||
{
|
||
uint8_t *rxbuffer;
|
||
|
||
/* Get the Rx buffer address from the Rx descriptor */
|
||
|
||
priv->pd_dev.d_buf = (uint8_t*)VIRT_ADDR(rxdesc->address);
|
||
|
||
/* Replace the buffer in the RX descriptor with a new one */
|
||
|
||
rxbuffer = pic32mx_allocbuffer(priv);
|
||
DEBUGASSERT(rxbuffer != NULL);
|
||
rxdesc->address = PHYS_ADDR(rxbuffer);
|
||
|
||
/* And give the RX descriptor back to the hardware */
|
||
|
||
rxdesc->status = RXDESC_STATUS_EOWN | TXDESC_STATUS_NPV;
|
||
rxdesc->rsv1 = 0;
|
||
rxdesc->rsv1 = 0;
|
||
|
||
pic32mx_dumppacket("Received packet",
|
||
priv->pd_dev.d_buf, priv->pd_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->pd_dev);
|
||
uip_input(&priv->pd_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->pd_dev.d_len > 0)
|
||
{
|
||
uip_arp_out(&priv->pd_dev);
|
||
pic32mx_response(priv);
|
||
}
|
||
}
|
||
else if (BUF->type == htons(UIP_ETHTYPE_ARP))
|
||
{
|
||
EMAC_STAT(priv, rx_arp);
|
||
uip_arp_arpin(&priv->pd_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->pd_dev.d_len > 0)
|
||
{
|
||
pic32mx_response(priv);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Unrecognized... drop it. */
|
||
|
||
EMAC_STAT(priv, rx_dropped);
|
||
}
|
||
|
||
/* Discard any buffers still attached to the device structure */
|
||
|
||
priv->pd_dev.d_len = 0;
|
||
if (priv->pd_dev.d_buf)
|
||
{
|
||
pic32mx_freebuffer(priv, priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_txdone(struct pic32mx_driver_s *priv)
|
||
{
|
||
struct pic32mx_txdesc_s *txdesc;
|
||
int i;
|
||
|
||
/* Cancel the pending Tx timeout */
|
||
|
||
wd_cancel(priv->pd_txtimeout);
|
||
|
||
/* Disable further Tx interrupts. Tx interrupts may be re-enabled again
|
||
* depending upon the result of the poll.
|
||
*/
|
||
|
||
priv->pd_inten &= ~ETH_TXINTS;
|
||
pic32mx_putreg(priv->pd_inten, PIC32MX_ETH_IEN);
|
||
|
||
/* Verify that the hardware is ready to send another packet. Since a Tx
|
||
* just completed, this must be the case.
|
||
*/
|
||
|
||
DEBUGASSERT(pic32mx_txdesc(priv) != NULL);
|
||
|
||
/* Inspect the list of TX descriptors to see if the EOWN bit is cleared. If it
|
||
* is, this descriptor is now under software control and the message was
|
||
* transmitted. Use TSV to check for the transmission result.
|
||
*/
|
||
|
||
for (i = 0; i < CONFIG_NET_NTXDESC; i++)
|
||
{
|
||
txdesc = &priv->pd_txdesc[i];
|
||
|
||
/* Check if software owns this descriptor */
|
||
|
||
if ((txdesc->status & TXDESC_STATUS_EOWN) == 0)
|
||
{
|
||
/* Yes.. Check if there is a buffer attached? */
|
||
|
||
if (txdesc->address != 0)
|
||
{
|
||
/* Reset status */
|
||
|
||
txdesc->status = TXDESC_STATUS_SOWN | TXDESC_STATUS_NPV;
|
||
txdesc->tsv1 = 0;
|
||
txdesc->tsv2 = 0;
|
||
|
||
/* Free the TX buffer */
|
||
|
||
pic32mx_freebuffer(priv, (uint8_t *)VIRT_ADDR(txdesc->address));
|
||
txdesc->address = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 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->pd_txpending)
|
||
{
|
||
/* Clear the pending condition, send the packet, and restore Rx interrupts */
|
||
|
||
priv->pd_txpending = false;
|
||
EMAC_STAT(priv, tx_unpend);
|
||
|
||
pic32mx_transmit(priv);
|
||
|
||
priv->pd_inten |= ETH_RXINTS;
|
||
pic32mx_putreg(priv->pd_inten, PIC32MX_ETH_IEN);
|
||
}
|
||
|
||
/* Otherwise poll uIP for new XMIT data */
|
||
|
||
else
|
||
{
|
||
/* Assign a buffer for the poll */
|
||
|
||
DEBUGASSERT(priv->pd_dev.d_buf == NULL);
|
||
priv->pd_dev.d_buf = pic32mx_allocbuffer(priv);
|
||
if (priv->pd_dev.d_buf)
|
||
{
|
||
/* And perform the poll */
|
||
|
||
(void)uip_poll(&priv->pd_dev, pic32mx_uiptxpoll);
|
||
|
||
/* Free any buffer left attached after the poll */
|
||
|
||
if (priv->pd_dev.d_buf != NULL)
|
||
{
|
||
pic32mx_freebuffer(priv, priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_interrupt(int irq, void *context)
|
||
{
|
||
register struct pic32mx_driver_s *priv;
|
||
uint32_t status;
|
||
|
||
#if CONFIG_PIC32MX_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 = pic32mx_getreg(PIC32MX_ETH_IRQ);
|
||
if (status != 0)
|
||
{
|
||
/* Clear all pending interrupts */
|
||
|
||
pic32mx_putreg(status, PIC32MX_ETH_IRQCLR);
|
||
|
||
/* Handle each pending interrupt **************************************/
|
||
/* Receive Errors *****************************************************/
|
||
/* RXOVFLW: Receive FIFO Over Flow Error. RXOVFLW is set by the RXBM
|
||
* Logic for an RX FIFO Overflow condition. It is cleared by either a
|
||
* Reset or CPU write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_RXOVFLW) != 0)
|
||
{
|
||
nlldbg("RX Overrun. status: %08x\n", status);
|
||
EMAC_STAT(priv, rx_errors);
|
||
EMAC_STAT(priv, rx_ovflw);
|
||
}
|
||
|
||
/* RXBUFNA: Receive Buffer Not Available Interrupt. This bit is set by
|
||
* a RX Buffer Descriptor Overrun condition. It is cleared by either a
|
||
* Reset or a CPU write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_RXBUFNA) != 0)
|
||
{
|
||
nlldbg("RX buffer descriptor overrun. status: %08x\n", status);
|
||
EMAC_STAT(priv, rx_errors);
|
||
EMAC_STAT(priv, rx_bufna);
|
||
}
|
||
|
||
/* RXBUSE: Receive BVCI Bus Error Interrupt. This bit is set when the
|
||
* RX DMA encounters a BVCI Bus error during a memory access. It is
|
||
* cleared by either a Reset or CPU write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_RXBUSE) != 0)
|
||
{
|
||
nlldbg("RX BVCI bus error. status: %08x\n", status);
|
||
EMAC_STAT(priv, rx_errors);
|
||
EMAC_STAT(priv, rx_buse);
|
||
}
|
||
|
||
/* Receive Normal Events **********************************************/
|
||
/* RXACT: Receive Activity Interrupt. This bit is set whenever RX packet
|
||
* data is stored in the RXBM FIFO. It is cleared by either a Reset or CPU
|
||
* write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
/* PKTPEND: Packet Pending Interrupt. This bit is set when the BUFCNT
|
||
* counter has a value other than <20>0<EFBFBD>. It is cleared by either a Reset
|
||
* or by writing the BUFCDEC bit to decrement the BUFCNT counter.
|
||
* Writing a <20>0<EFBFBD> or a <20>1<EFBFBD> has no effect.
|
||
*/
|
||
|
||
/* RXDONE: Receive Done Interrupt. This bit is set whenever an RX packet
|
||
* is successfully received. It is cleared by either a Reset or CPU
|
||
* write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_RXDONE) != 0)
|
||
{
|
||
EMAC_STAT(priv, rx_done);
|
||
|
||
/* We have received at least one new incoming packet. */
|
||
|
||
pic32mx_rxdone(priv);
|
||
}
|
||
|
||
/* Transmit Errors ****************************************************/
|
||
/* TXABORT: Transmit Abort Condition Interrupt. This bit is set when
|
||
* the MAC aborts the transmission of a TX packet for one of the
|
||
* following reasons:
|
||
* - Jumbo TX packet abort
|
||
* - Underrun abort
|
||
* - Excessive defer abort
|
||
* - Late collision abort
|
||
* - Excessive collisions abort
|
||
* This bit is cleared by either a Reset or CPU write of a <20>1<EFBFBD> to the
|
||
* CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_TXABORT) != 0)
|
||
{
|
||
nlldbg("TX abort. status: %08x\n", status);
|
||
EMAC_STAT(priv, tx_errors);
|
||
EMAC_STAT(priv, tx_abort);
|
||
}
|
||
|
||
/* TXBUSE: Transmit BVCI Bus Error Interrupt. This bit is set when the
|
||
* TX DMA encounters a BVCI Bus error during a memory access. It is
|
||
* cleared by either a Reset or CPU write of a <20>1<EFBFBD> to the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_TXBUSE) != 0)
|
||
{
|
||
nlldbg("TX BVCI bus error. status: %08x\n", status);
|
||
EMAC_STAT(priv, tx_errors);
|
||
EMAC_STAT(priv, tx_buse);
|
||
}
|
||
|
||
/* TXDONE: Transmit Done Interrupt. This bit is set when the currently
|
||
* transmitted TX packet completes transmission, and the Transmit
|
||
* Status Vector is loaded into the first descriptor used for the
|
||
* packet. It is cleared by either a Reset or CPU write of a <20>1<EFBFBD> to
|
||
* the CLR register.
|
||
*/
|
||
|
||
if ((status & ETH_INT_TXDONE) != 0)
|
||
{
|
||
EMAC_STAT(priv, tx_done);
|
||
|
||
/* A packet transmission just completed */
|
||
|
||
pic32mx_txdone(priv);
|
||
}
|
||
|
||
/* Watermark Events ***************************************************/
|
||
/* EWMARK: Empty Watermark Interrupt. This bit is set when the RX
|
||
* Descriptor Buffer Count is less than or equal to the value in the
|
||
* RXEWM bit (ETHRXWM:0-7) value. It is cleared by BUFCNT bit
|
||
* (ETHSTAT:16-23) being incremented by hardware. Writing a <20>0<EFBFBD> or a <20>1<EFBFBD>
|
||
* has no effect.
|
||
*/
|
||
|
||
/* FWMARK: Full Watermark Interrupt. This bit is set when the RX
|
||
* escriptor Buffer Count is greater than or equal to the value in the
|
||
* RXFWM bit (ETHRXWM:16-23) field. It is cleared by writing the BUFCDEC
|
||
* (ETHCON1:0) bit to decrement the BUFCNT counter. Writing a <20>0<EFBFBD> or a
|
||
* <20>1<EFBFBD> has no effect.
|
||
*/
|
||
|
||
}
|
||
|
||
/* Clear the pending interrupt */
|
||
|
||
# if CONFIG_PIC32MX_NINTERFACES > 1
|
||
up_clrpend_irq(priv->pd_irqsrc);
|
||
# else
|
||
up_clrpend_irq(PIC32MX_IRQSRC_ETH);
|
||
# endif
|
||
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_txtimeout(int argc, uint32_t arg, ...)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_driver_s *)arg;
|
||
|
||
/* Increment statistics and dump debug info */
|
||
|
||
EMAC_STAT(priv, tx_timeouts);
|
||
if (priv->pd_ifup)
|
||
{
|
||
/* Then reset the hardware. ifup() will reset the interface, then bring
|
||
* it back up.
|
||
*/
|
||
|
||
(void)pic32mx_ifup(&priv->pd_dev);
|
||
|
||
/* Then poll uIP for new XMIT data (We are guaranteed to have a free
|
||
* buffer here).
|
||
*/
|
||
|
||
priv->pd_dev.d_buf = pic32mx_allocbuffer(priv);
|
||
(void)uip_poll(&priv->pd_dev, pic32mx_uiptxpoll);
|
||
|
||
/* Free any buffer left attached after the poll */
|
||
|
||
if (priv->pd_dev.d_buf != NULL)
|
||
{
|
||
pic32mx_freebuffer(priv, priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_polltimer(int argc, uint32_t arg, ...)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_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 (pic32mx_txdesc(priv) != NULL)
|
||
{
|
||
/* 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?
|
||
*/
|
||
|
||
DEBUGASSERT(priv->pd_dev.d_buf == NULL);
|
||
priv->pd_dev.d_buf = pic32mx_allocbuffer(priv);
|
||
if (priv->pd_dev.d_buf != NULL)
|
||
{
|
||
/* And perform the poll */
|
||
|
||
(void)uip_timer(&priv->pd_dev, pic32mx_uiptxpoll, PIC32MX_POLLHSEC);
|
||
|
||
/* Free any buffer left attached after the poll */
|
||
|
||
if (priv->pd_dev.d_buf != NULL)
|
||
{
|
||
pic32mx_freebuffer(priv, priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Setup the watchdog poll timer again */
|
||
|
||
(void)wd_start(priv->pd_txpoll, PIC32MX_WDDELAY, pic32mx_polltimer, 1, arg);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_ifup(struct uip_driver_s *dev)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_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) */
|
||
|
||
pic32mx_ethreset(priv);
|
||
|
||
/* MAC Initialization *****************************************************/
|
||
/* Configuration:
|
||
* - Use the configuration fuse setting FETHIO bit (DEVCFG3:25) to detect
|
||
* the alternate/default I/O configuration
|
||
* - Use the configuration fuse setting FMIIEN (DEVCFG3:24) to detect the
|
||
* MII/RMII operation mode.
|
||
*/
|
||
|
||
/* Pin Configuration:
|
||
*
|
||
* No GPIO pin configuration is required. Enabling the Ethernet Controller
|
||
* will configure the I/O pin direction as defined by the Ethernet Controller
|
||
* control bits. The port TRIS and LATCH registers will be overridden.
|
||
*
|
||
* I/O Pin MII RMII Pin Description
|
||
* Name Required Required Type
|
||
* EMDC Yes Yes O Ethernet MII Management Clock
|
||
* EMDIO Yes Yes I/O Ethernet MII Management IO
|
||
* ETXCLK Yes No I Ethernet MII TX Clock
|
||
* ETXEN Yes Yes O Ethernet Transmit Enable
|
||
* ETXD0 Yes Yes O Ethernet Data Transmit 0
|
||
* ETXD1 Yes Yes O Ethernet Data Transmit 1
|
||
* ETXD2 Yes No O Ethernet Data Transmit 2
|
||
* ETXD3 Yes No O Ethernet Data Transmit 3
|
||
* ETXERR Yes No O Ethernet Transmit Error
|
||
* ERXCLK Yes No I Ethernet MII RX Clock
|
||
* EREF_CLK No Yes I Ethernet RMII Ref Clock
|
||
* ERXDV Yes No I Ethernet MII Receive Data Valid
|
||
* ECRS_DV No Yes I Ethernet RMII Carrier Sense/Receive Data Valid
|
||
* ERXD0 Yes Yes I Ethernet Data Receive 0
|
||
* ERXD1 Yes Yes I Ethernet Data Receive 1
|
||
* ERXD2 Yes No I Ethernet Data Receive 2
|
||
* ERXD3 Yes No I Ethernet Data Receive 3
|
||
* ERXERR Yes Yes I Ethernet Receive Error
|
||
* ECRS Yes No I Ethernet Carrier Sense
|
||
* ECOL Yes No I Ethernet Collision Detected
|
||
*
|
||
* All that is required is to assure that the pins are initialized as
|
||
* digital (normally only those pins that have shared analog functionality
|
||
* need to be configured).
|
||
*/
|
||
|
||
/* Initialize the MIIM interface
|
||
*
|
||
* If the RMII operation is selected, reset the RMII module by using the
|
||
* RESETRMII (EMAC1SUPP:11) bit and set the proper speed in the SPEEDRMII
|
||
* bit (EMAC1SUPP:8) bit.
|
||
*/
|
||
|
||
#if CONFIG_PIC32MX_FMIIEN == 0
|
||
# warning "Missing logic"
|
||
#endif
|
||
|
||
/* Issue an MIIM block reset, by setting the RESETMGMT (EMAC1MCFG:15) bit,
|
||
* and then clear the reset bit.
|
||
*/
|
||
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_MCFG);
|
||
pic32mx_putreg(EMAC1_MCFG_MGMTRST, PIC32MX_EMAC1_MCFGSET);
|
||
|
||
regval &= ~EMAC1_MCFG_MGMTRST;
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_MCFG);
|
||
|
||
/* Select a proper divider in the CLKSEL bit (EMAC1CFG:2-5) for the MIIM
|
||
* PHY communication based on the system running clock frequency and the
|
||
* external PHY supported clock.
|
||
*
|
||
* MII configuration: host clocked divider per board.h, no suppress
|
||
* preamble, no scan increment.
|
||
*/
|
||
|
||
regval &= ~(EMAC1_MCFG_CLKSEL_MASK | EMAC1_MCFG_NOPRE | EMAC1_MCFG_SCANINC);
|
||
regval |= EMAC1_MCFG_CLKSEL_DIV;
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_MCFG);
|
||
|
||
/* PHY Initialization *****************************************************/
|
||
/* Initialize the PHY and wait for the link to be established */
|
||
|
||
ret = pic32mx_phyinit(priv);
|
||
if (ret != 0)
|
||
{
|
||
ndbg("pic32mx_phyinit failed: %d\n", ret);
|
||
return ret;
|
||
}
|
||
|
||
/* MAC Configuration ******************************************************/
|
||
/* Set other misc configuration-related registers to default values */
|
||
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_CFG2);
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_SUPP);
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_TEST);
|
||
|
||
/* Having available the Duplex and Speed settings, configure the MAC
|
||
* accordingly, using the following steps:
|
||
*
|
||
* Enable the RXENABLE bit (EMAC1CFG1:0), selecting both the TXPAUSE and
|
||
* RXPAUSE bit (EMAC1CFG1:2-3) (the PIC32 MAC supports both).
|
||
*/
|
||
|
||
pic32mx_putreg(EMAC1_CFG1_RXEN | EMAC1_CFG1_RXPAUSE | EMAC1_CFG1_TXPAUSE,
|
||
PIC32MX_EMAC1_MCFGSET);
|
||
|
||
/* Select the desired auto-padding and CRC capabilities, and the enabling
|
||
* of the huge frames and the Duplex type in the EMAC1CFG2 register.
|
||
* (This was done in the PHY initialization logic).
|
||
*/
|
||
|
||
/* Program EMAC1IPGT with the back-to-back inter-packet gap */
|
||
/* Use EMAC1IPGR for setting the non back-to-back inter-packet gap */
|
||
|
||
pic32mx_putreg(((12 << EMAC1_IPGR_GAP1_SHIFT) | (12 << EMAC1_IPGR_GAP2_SHIFT)),
|
||
PIC32MX_EMAC1_IPGR);
|
||
|
||
/* Set the collision window and the maximum number of retransmissions in
|
||
* EMAC1CLRT.
|
||
*/
|
||
|
||
pic32mx_putreg(((15 << EMAC1_CLRT_RETX_SHIFT) | (55 << EMAC1_CLRT_CWINDOW_SHIFT)),
|
||
PIC32MX_EMAC1_CLRT);
|
||
|
||
/* Set the maximum frame length in EMAC1MAXF. "This field resets to
|
||
* 0x05EE, which represents a maximum receive frame of 1518 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/longer maximum
|
||
* length restriction is desired, program this 16-bit field.
|
||
*/
|
||
|
||
pic32mx_putreg(CONFIG_NET_BUFSIZE, PIC32MX_EMAC1_MAXF);
|
||
|
||
/* Configure the MAC station address in the EMAC1SA0, EMAC1SA1 and
|
||
* EMAC1SA2 registers (these registers are loaded at reset from the
|
||
* factory preprogrammed station address).
|
||
*/
|
||
|
||
#if 0
|
||
regval = (uint32_t)priv->pd_dev.d_mac.ether_addr_octet[5] << 8 |
|
||
(uint32_t)priv->pd_dev.d_mac.ether_addr_octet[4];
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_SA0);
|
||
|
||
regval = (uint32_t)priv->pd_dev.d_mac.ether_addr_octet[3] << 8 |
|
||
(uint32_t)priv->pd_dev.d_mac.ether_addr_octet[2];
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_SA1);
|
||
|
||
regval = (uint32_t)priv->pd_dev.d_mac.ether_addr_octet[1] << 8 |
|
||
(uint32_t)priv->pd_dev.d_mac.ether_addr_octet[0];
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_SA2);
|
||
#else
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_SA0);
|
||
priv->pd_dev.d_mac.ether_addr_octet[4] = (uint32_t)(regval & 0xff);
|
||
priv->pd_dev.d_mac.ether_addr_octet[5] = (uint32_t)((regval >> 8) & 0xff);
|
||
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_SA1);
|
||
priv->pd_dev.d_mac.ether_addr_octet[2] = (uint32_t)(regval & 0xff);
|
||
priv->pd_dev.d_mac.ether_addr_octet[3] = (uint32_t)((regval >> 8) & 0xff);
|
||
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_SA2);
|
||
priv->pd_dev.d_mac.ether_addr_octet[0] = (uint32_t)(regval & 0xff);
|
||
priv->pd_dev.d_mac.ether_addr_octet[1] = (uint32_t)((regval >> 8) & 0xff);
|
||
#endif
|
||
|
||
/* Continue Ethernet Controller Initialization ****************************/
|
||
/* If planning to turn on the flow control, update the PTV value
|
||
*(ETHCON1:16-31).
|
||
*/
|
||
|
||
/* If using the auto-flow control, set the full and empty watermarks: RXFWM
|
||
* and RXEWM (ETHRXWM:16-23 and ETHRXWM:0-7).
|
||
*/
|
||
|
||
/* If needed, enable the auto-flow control by setting AUTOFC (ETHCON1:7). */
|
||
|
||
/* Set the RX filters by updating the ETHHT0, ETHHT1, ETHPMM0, ETHPMM1,
|
||
* ETHPMCS and ETHRXFC registers.
|
||
*
|
||
* Set up RX filter and configure to accept broadcast addresses and multicast
|
||
* addresses (if so configured). NOTE: There is a selection
|
||
* CONFIG_NET_BROADCAST, but this enables receipt of UDP broadcast packets
|
||
* inside of the stack.
|
||
*/
|
||
|
||
regval = ETH_RXFC_BCEN | ETH_RXFC_PMMODE_DISABLED;
|
||
#ifdef CONFIG_NET_MULTICAST
|
||
regval |= (ETH_RXFC_MCEN | ETH_RXFC_UCEN);
|
||
#endif
|
||
pic32mx_putreg(regval, PIC32MX_ETH_RXFC);
|
||
|
||
/* Set the size of the RX buffers in the RXBUFSZ bit (ETHCON2:4-10) (all
|
||
* receive descriptors use the same buffer size). Keep in mind that using
|
||
* packets that are too small leads to packet fragmentation and has a
|
||
* noticeable impact on the performance.
|
||
*/
|
||
|
||
pic32mx_putreg(ETH_CON2_RXBUFSZ(CONFIG_NET_BUFSIZE), PIC32MX_ETH_CON2);
|
||
|
||
/* Initialize the buffer list */
|
||
|
||
pic32mx_bufferinit(priv);
|
||
|
||
/* Initialize the TX descriptor list */
|
||
|
||
pic32mx_txdescinit(priv);
|
||
|
||
/* Initialize the RX descriptor list */
|
||
|
||
pic32mx_rxdescinit(priv);
|
||
|
||
/* Enable the Ethernet Controller by setting the ON bit (ETHCON1:15).
|
||
* Enable the receiving of messages by setting the RXEN bit (ETHCON1:8).
|
||
*/
|
||
|
||
pic32mx_putreg(ETH_CON1_RXEN | ETH_CON1_ON, PIC32MX_ETH_CON1SET);
|
||
|
||
/* Initialize Ethernet interface for the PHY setup */
|
||
|
||
pic32mx_macmode(priv->pd_mode);
|
||
|
||
/* Configure to pass all received frames */
|
||
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_CFG1);
|
||
regval |= EMAC1_CFG1_PASSALL;
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_CFG1);
|
||
|
||
/* Clear any pending interrupts (shouldn't be any) */
|
||
|
||
pic32mx_putreg(0xffffffff, PIC32MX_ETH_IRQCLR);
|
||
|
||
/* 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.
|
||
*/
|
||
|
||
/* If the user provided an interrupt priority, then set the interrupt to that
|
||
* priority
|
||
*/
|
||
|
||
#if defined(CONFIG_NET_PRIORITY) && defined(CONFIG_ARCH_IRQPRIO)
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
(void)up_prioritize_irq(priv->pd_irq, CONFIG_NET_PRIORITY);
|
||
#else
|
||
(void)up_prioritize_irq(PIC32MX_IRQ_ETH, CONFIG_NET_PRIORITY);
|
||
#endif
|
||
#endif
|
||
|
||
/* 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->pd_inten = ETH_RXINTS;
|
||
pic32mx_putreg(ETH_RXINTS, PIC32MX_ETH_IENSET);
|
||
|
||
/* Set and activate a timer process */
|
||
|
||
(void)wd_start(priv->pd_txpoll, PIC32MX_WDDELAY, pic32mx_polltimer, 1,
|
||
(uint32_t)priv);
|
||
|
||
/* Finally, make the interface up and enable the Ethernet interrupt at
|
||
* the interrupt controller
|
||
*/
|
||
|
||
priv->pd_ifup = true;
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
up_enable_irq(priv->pd_irqsrc);
|
||
#else
|
||
up_enable_irq(PIC32MX_IRQSRC_ETH);
|
||
#endif
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_ifdown
|
||
*
|
||
* Description:
|
||
* NuttX Callback: Stop the interface.
|
||
*
|
||
* Parameters:
|
||
* dev - Reference to the NuttX driver state structure
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
static int pic32mx_ifdown(struct uip_driver_s *dev)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_driver_s *)dev->d_private;
|
||
irqstate_t flags;
|
||
|
||
/* Disable the Ethernet interrupt */
|
||
|
||
flags = irqsave();
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
up_disable_irq(priv->pd_irqsrc);
|
||
#else
|
||
up_disable_irq(PIC32MX_IRQSRC_ETH);
|
||
#endif
|
||
|
||
/* Cancel the TX poll timer and TX timeout timers */
|
||
|
||
wd_cancel(priv->pd_txpoll);
|
||
wd_cancel(priv->pd_txtimeout);
|
||
|
||
/* Reset the device and mark it as down. */
|
||
|
||
pic32mx_ethreset(priv);
|
||
priv->pd_ifup = false;
|
||
irqrestore(flags);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_txavail(struct uip_driver_s *dev)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_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->pd_ifup)
|
||
{
|
||
/* Check if there is room in the hardware to hold another outgoing packet. */
|
||
|
||
if (pic32mx_txdesc(priv) != NULL)
|
||
{
|
||
/* If so, then poll uIP for new XMIT data. First allocate a buffer
|
||
* to perform the poll
|
||
*/
|
||
|
||
DEBUGASSERT(priv->pd_dev.d_buf == NULL);
|
||
priv->pd_dev.d_buf = pic32mx_allocbuffer(priv);
|
||
if (priv->pd_dev.d_buf)
|
||
{
|
||
/* And perform the poll */
|
||
|
||
(void)uip_poll(&priv->pd_dev, pic32mx_uiptxpoll);
|
||
|
||
/* Free any buffer left attached after the poll */
|
||
|
||
if (priv->pd_dev.d_buf != NULL)
|
||
{
|
||
pic32mx_freebuffer(priv, priv->pd_dev.d_buf);
|
||
priv->pd_dev.d_buf = NULL;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
irqrestore(flags);
|
||
return OK;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_addmac(struct uip_driver_s *dev, const uint8_t *mac)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_driver_s *)dev->d_private;
|
||
|
||
/* Add the MAC address to the hardware multicast routing table */
|
||
|
||
#warning "Not implemented"
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
|
||
{
|
||
struct pic32mx_driver_s *priv = (struct pic32mx_driver_s *)dev->d_private;
|
||
|
||
/* Add the MAC address to the hardware multicast routing table */
|
||
|
||
#warning "Not implemented"
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/*******************************************************************************
|
||
* Name: pic32mx_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(PIC32MX_HAVE_PHY)
|
||
static void pic32mx_showmii(uint8_t phyaddr, const char *msg)
|
||
{
|
||
dbg("PHY " PIC32MX_PHYNAME ": %s\n", msg);
|
||
dbg(" MCR: %04x\n", pic32mx_phyread(phyaddr, MII_MCR));
|
||
dbg(" MSR: %04x\n", pic32mx_phyread(phyaddr, MII_MSR));
|
||
dbg(" ADVERTISE: %04x\n", pic32mx_phyread(phyaddr, MII_ADVERTISE));
|
||
dbg(" LPA: %04x\n", pic32mx_phyread(phyaddr, MII_LPA));
|
||
dbg(" EXPANSION: %04x\n", pic32mx_phyread(phyaddr, MII_EXPANSION));
|
||
#ifdef CONFIG_PHY_KS8721
|
||
dbg(" 10BTCR: %04x\n", pic32mx_phyread(phyaddr, MII_KS8721_10BTCR));
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 PIC32MX_HAVE_PHY
|
||
static void pic32mx_phywrite(uint8_t phyaddr, uint8_t regaddr, uint16_t phydata)
|
||
{
|
||
uint32_t regval;
|
||
|
||
/* Set PHY address and PHY register address */
|
||
|
||
regval = ((uint32_t)phyaddr << EMAC1_MADR_PHYADDR_SHIFT) |
|
||
((uint32_t)regaddr << EMAC1_MADR_REGADDR_SHIFT);
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_MADR);
|
||
|
||
/* Set up to write */
|
||
|
||
pic32mx_putreg(EMAC1_MCMD_WRITE, PIC32MX_EMAC1_MCMD);
|
||
|
||
/* Write the register data to the PHY */
|
||
|
||
pic32mx_putreg((uint32_t)phydata, PIC32MX_EMAC1_MWTD);
|
||
|
||
/* Wait for the PHY command to complete */
|
||
|
||
while ((pic32mx_getreg(PIC32MX_EMAC1_MIND) & EMAC1_MIND_MIIMBUSY) != 0);
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 PIC32MX_HAVE_PHY
|
||
static uint16_t pic32mx_phyread(uint8_t phyaddr, uint8_t regaddr)
|
||
{
|
||
uint32_t regval;
|
||
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_MCMD);
|
||
|
||
/* Set PHY address and PHY register address */
|
||
|
||
regval = ((uint32_t)phyaddr << EMAC1_MADR_PHYADDR_SHIFT) |
|
||
((uint32_t)regaddr << EMAC1_MADR_REGADDR_SHIFT);
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_MADR);
|
||
|
||
/* Set up to read */
|
||
|
||
pic32mx_putreg(EMAC1_MCMD_READ, PIC32MX_EMAC1_MCMD);
|
||
|
||
/* Wait for the PHY command to complete */
|
||
|
||
while ((pic32mx_getreg(PIC32MX_EMAC1_MIND) & (EMAC1_MIND_MIIMBUSY|EMAC1_MIND_NOTVALID)) != 0);
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_MCMD);
|
||
|
||
/* Return the PHY register data */
|
||
|
||
return (uint16_t)(pic32mx_getreg(PIC32MX_EMAC1_MRDD) & EMAC1_MRDD_MASK);
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_phyreset
|
||
*
|
||
* Description:
|
||
* Reset the PHY
|
||
*
|
||
* Parameters:
|
||
* phyaddr - The device address where the PHY was discovered
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef PIC32MX_HAVE_PHY
|
||
static inline int pic32mx_phyreset(uint8_t phyaddr)
|
||
{
|
||
int32_t timeout;
|
||
uint16_t phyreg;
|
||
|
||
/* Reset the PHY. Needs a minimal 50uS delay after reset. */
|
||
|
||
pic32mx_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 = pic32mx_phyread(phyaddr, MII_MCR);
|
||
if ((phyreg & MII_MCR_RESET) == 0)
|
||
{
|
||
return OK;
|
||
}
|
||
}
|
||
|
||
ndbg("Reset failed. MCR: %04x\n", phyreg);
|
||
return -ETIMEDOUT;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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(PIC32MX_HAVE_PHY) && defined(CONFIG_PHY_AUTONEG)
|
||
static inline int pic32mx_phyautoneg(uint8_t phyaddr)
|
||
{
|
||
int32_t timeout;
|
||
uint16_t phyreg;
|
||
|
||
/* Start auto-negotiation */
|
||
|
||
pic32mx_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 = pic32mx_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: pic32mx_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 PIC32MX_HAVE_PHY
|
||
static int pic32mx_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 & PIC32MX_SPEED_MASK) == PIC32MX_SPEED_100)
|
||
{
|
||
phyreg = MII_MCR_SPEED100;
|
||
}
|
||
|
||
if ((mode & PIC32MX_DUPLEX_MASK) == PIC32MX_DUPLEX_FULL)
|
||
{
|
||
phyreg |= MII_MCR_FULLDPLX;
|
||
}
|
||
|
||
pic32mx_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 = pic32mx_phyread(phyaddr, MII_DP83848C_STS);
|
||
if ((phyreg & 0x0001) != 0)
|
||
{
|
||
/* Yes.. return success */
|
||
|
||
return OK;
|
||
}
|
||
#else
|
||
phyreg = pic32mx_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: pic32mx_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->pd_phyaddr
|
||
* and priv->pd_phymode.
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef PIC32MX_HAVE_PHY
|
||
static inline int pic32mx_phyinit(struct pic32mx_driver_s *priv)
|
||
{
|
||
unsigned int phyaddr;
|
||
uint16_t phyreg;
|
||
uint32_t regval;
|
||
int ret;
|
||
|
||
/* Clear any ongoing PHY command bits */
|
||
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_MCMD);
|
||
|
||
/* 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)pic32mx_phyread(phyaddr, MII_PHYID1);
|
||
nvdbg("Addr: %d PHY ID1: %04x\n", phyaddr, phyreg);
|
||
|
||
if (phyreg == PIC32MX_PHYID1)
|
||
{
|
||
phyreg = pic32mx_phyread(phyaddr, MII_PHYID2);
|
||
nvdbg("Addr: %d PHY ID2: %04x\n", phyaddr, phyreg);
|
||
|
||
if (phyreg == PIC32MX_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->pd_phyaddr = phyaddr;
|
||
|
||
/* Reset the PHY */
|
||
|
||
ret = pic32mx_phyreset(phyaddr);
|
||
if (ret < 0)
|
||
{
|
||
return ret;
|
||
}
|
||
pic32mx_showmii(phyaddr, "After reset");
|
||
|
||
/* Set the MII/RMII operation mode. This usually requires access to a
|
||
* vendor-specific control register.
|
||
*/
|
||
|
||
/* Set the normal, swapped or auto (preferred) MDIX. This usually requires
|
||
* access to a vendor-specific control register.
|
||
*/
|
||
|
||
/* Check the PHY capabilities by investigating the Status Register 1. */
|
||
|
||
/* Check for preamble suppression support */
|
||
|
||
phyreg = pic32mx_phyread(phyaddr, MII_MSR);
|
||
if ((phyreg & MII_MSR_MFRAMESUPPRESS) != 0)
|
||
{
|
||
/* The PHY supports preamble suppression */
|
||
|
||
regval = pic32mx_getreg(PIC32MX_EMAC1_MCFG);
|
||
regval |= EMAC1_MCFG_NOPRE;
|
||
pic32mx_putreg(regval, PIC32MX_EMAC1_MCFG);
|
||
}
|
||
|
||
/* Are we configured to do auto-negotiation?
|
||
*
|
||
* Preferably the auto-negotiation should be selected if the PHY supports
|
||
* it. Expose the supported capabilities: Half/Full Duplex, 10BaseT/100Base
|
||
* TX, etc. (Extended Register 4). Start the negotiation (Control Register
|
||
* 0) and wait for the negotiation complete and get the link partner
|
||
* capabilities (Extended Register 5) and negotiation result (vendor-
|
||
* specific register).
|
||
*/
|
||
|
||
#ifdef CONFIG_PHY_AUTONEG
|
||
/* Setup the Auto-negotiation advertisement: 100 or 10, and HD or FD */
|
||
|
||
pic32mx_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 = pic32mx_phyautoneg(phyaddr);
|
||
if (ret < 0)
|
||
{
|
||
return ret;
|
||
}
|
||
#else
|
||
/* Set up the fixed PHY configuration
|
||
*
|
||
* If auto-negotiation is not supported/selected, update the PHY Duplex and
|
||
* Speed settings directly (use Control Register 0 and possibly some vendor-
|
||
* pecific registers).
|
||
*/
|
||
|
||
ret = pic32mx_phymode(phyaddr, PIC32MX_MODE_DEFLT);
|
||
if (ret < 0)
|
||
{
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
/* The link is established */
|
||
|
||
pic32mx_showmii(phyaddr, "After link established");
|
||
|
||
/* Check configuration */
|
||
|
||
#if defined(CONFIG_PHY_KS8721)
|
||
phyreg = pic32mx_phyread(phyaddr, MII_KS8721_10BTCR);
|
||
|
||
switch (phyreg & KS8721_10BTCR_MODE_MASK)
|
||
{
|
||
case KS8721_10BTCR_MODE_10BTHD: /* 10BASE-T half duplex */
|
||
priv->pd_mode = PIC32MX_10BASET_HD;
|
||
break;
|
||
case KS8721_10BTCR_MODE_100BTHD: /* 100BASE-T half duplex */
|
||
priv->pd_mode = PIC32MX_100BASET_HD;
|
||
break;
|
||
case KS8721_10BTCR_MODE_10BTFD: /* 10BASE-T full duplex */
|
||
priv->pd_mode = PIC32MX_10BASET_FD;
|
||
break;
|
||
case KS8721_10BTCR_MODE_100BTFD: /* 100BASE-T full duplex */
|
||
priv->pd_mode = PIC32MX_100BASET_FD;
|
||
break;
|
||
default:
|
||
ndbg("Unrecognized mode: %04x\n", phyreg);
|
||
return -ENODEV;
|
||
}
|
||
#elif defined(CONFIG_PHY_DP83848C)
|
||
phyreg = pic32mx_phyread(phyaddr, MII_DP83848C_STS);
|
||
|
||
/* Configure for full/half duplex mode and speed */
|
||
|
||
switch (phyreg & 0x0006)
|
||
{
|
||
case 0x0000:
|
||
priv->pd_mode = PIC32MX_100BASET_HD;
|
||
break;
|
||
case 0x0002:
|
||
priv->pd_mode = PIC32MX_10BASET_HD;
|
||
break;
|
||
case 0x0004:
|
||
priv->pd_mode = PIC32MX_100BASET_FD;
|
||
break;
|
||
case 0x0006:
|
||
priv->pd_mode = PIC32MX_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 = pic32mx_phyread(phyaddr, MII_ADVERTISE);
|
||
lpa = pic32mx_phyread(phyaddr, MII_LPA);
|
||
|
||
/* Check for 100BASETX full duplex */
|
||
|
||
if ((advertise & MII_ADVERTISE_100BASETXFULL) != 0 &&
|
||
(lpa & MII_LPA_100BASETXFULL) != 0)
|
||
{
|
||
priv->pd_mode = PIC32MX_100BASET_FD;
|
||
}
|
||
|
||
/* Check for 100BASETX half duplex */
|
||
|
||
else if ((advertise & MII_ADVERTISE_100BASETXHALF) != 0 &&
|
||
(lpa & MII_LPA_100BASETXHALF) != 0)
|
||
{
|
||
priv->pd_mode = PIC32MX_100BASET_HD;
|
||
}
|
||
|
||
/* Check for 10BASETX full duplex */
|
||
|
||
else if ((advertise & MII_ADVERTISE_10BASETXFULL) != 0 &&
|
||
(lpa & MII_LPA_10BASETXFULL) != 0)
|
||
{
|
||
priv->pd_mode = PIC32MX_10BASET_FD;
|
||
}
|
||
|
||
/* Check for 10BASETX half duplex */
|
||
|
||
else if ((advertise & MII_ADVERTISE_10BASETXHALF) != 0 &&
|
||
(lpa & MII_LPA_10BASETXHALF) != 0)
|
||
{
|
||
priv->pd_mode = PIC32MX_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->pd_mode & PIC32MX_SPEED_MASK) == PIC32MX_SPEED_100 ? 100 : 10,
|
||
(priv->pd_mode & PIC32MX_DUPLEX_MASK) == PIC32MX_DUPLEX_FULL ?"full" : "half");
|
||
|
||
/* Disable auto-configuration. Set the fixed speed/duplex mode.
|
||
* (probably more than little redundant).
|
||
*/
|
||
|
||
ret = pic32mx_phymode(phyaddr, priv->pd_mode);
|
||
pic32mx_showmii(phyaddr, "After final configuration");
|
||
return ret;
|
||
}
|
||
#else
|
||
static inline int pic32mx_phyinit(struct pic32mx_driver_s *priv)
|
||
{
|
||
priv->pd_mode = PIC32MX_MODE_DEFLT;
|
||
return OK;
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_macmode
|
||
*
|
||
* Description:
|
||
* Set the MAC to operate at a selected speed/duplex mode.
|
||
*
|
||
* Parameters:
|
||
* mode - speed/duplex mode
|
||
*
|
||
* Returned Value:
|
||
* None
|
||
*
|
||
* Assumptions:
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef PIC32MX_HAVE_PHY
|
||
static void pic32mx_macmode(uint8_t mode)
|
||
{
|
||
/* Set up for full or half duplex operation */
|
||
|
||
if ((mode & PIC32MX_DUPLEX_MASK) == PIC32MX_DUPLEX_FULL)
|
||
{
|
||
/* Set the back-to-back inter-packet gap */
|
||
|
||
pic32mx_putreg(21, PIC32MX_EMAC1_IPGT);
|
||
|
||
/* Set MAC to operate in full duplex mode with CRC and Pad enabled */
|
||
|
||
pic32mx_putreg((EMAC1_CFG2_FULLDPLX | EMAC1_CFG2_CRCEN | EMAC1_CFG2_PADCRCEN),
|
||
PIC32MX_EMAC1_CFG2SET);
|
||
}
|
||
else
|
||
{
|
||
/* Set the back-to-back inter-packet gap */
|
||
|
||
pic32mx_putreg(18, PIC32MX_EMAC1_IPGT);
|
||
|
||
/* Set MAC to operate in half duplex mode with CRC and Pad enabled */
|
||
|
||
pic32mx_putreg(EMAC1_CFG2_FULLDPLX, PIC32MX_EMAC1_CFG2CLR);
|
||
pic32mx_putreg((EMAC1_CFG2_CRCEN | EMAC1_CFG2_PADCRCEN), PIC32MX_EMAC1_CFG2SET);
|
||
}
|
||
|
||
/* Set the MAC speed. */
|
||
|
||
if ((mode & PIC32MX_SPEED_MASK) == PIC32MX_SPEED_100)
|
||
{
|
||
pic32mx_putreg(EMAC1_SUPP_SPEEDRMII, PIC32MX_EMAC1_SUPPSET);
|
||
}
|
||
else
|
||
{
|
||
pic32mx_putreg(EMAC1_SUPP_SPEEDRMII, PIC32MX_EMAC1_SUPPCLR);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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 pic32mx_ethreset(struct pic32mx_driver_s *priv)
|
||
{
|
||
irqstate_t flags;
|
||
|
||
/* Reset the MAC */
|
||
|
||
flags = irqsave();
|
||
|
||
/* Ethernet Controller Initialization *************************************/
|
||
/* Disable Ethernet interrupts in the EVIC */
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
up_disable_irq(priv->pd_irqsrc);
|
||
#else
|
||
up_disable_irq(PIC32MX_IRQSRC_ETH);
|
||
#endif
|
||
|
||
/* Turn the Ethernet Controller off: Clear the ON, RXEN and TXRTS bits */
|
||
|
||
pic32mx_putreg(ETH_CON1_RXEN | ETH_CON1_TXRTS | ETH_CON1_ON, PIC32MX_ETH_CON1CLR);
|
||
|
||
/* Wait activity abort by polling the ETHBUSY bit */
|
||
|
||
while ((pic32mx_getreg(PIC32MX_ETH_STAT) & ETH_STAT_ETHBUSY) != 0);
|
||
|
||
/* Clear the Ethernet Interrupt Flag (ETHIF) bit in the Interrupts module */
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
up_pending_irq(priv->pd_irqsrc);
|
||
#else
|
||
up_pending_irq(PIC32MX_IRQSRC_ETH);
|
||
#endif
|
||
|
||
/* Disable any Ethernet Controller interrupt generation by clearing the IEN
|
||
* register.
|
||
*/
|
||
|
||
pic32mx_putreg(ETH_INT_ALLINTS, PIC32MX_ETH_IENCLR);
|
||
|
||
/* Clear the TX and RX start addresses by using ETHTXSTCLR and ETHRXSTCLR */
|
||
|
||
pic32mx_putreg(0xffffffff, PIC32MX_ETH_TXSTCLR);
|
||
pic32mx_putreg(0xffffffff, PIC32MX_ETH_RXSTSET);
|
||
|
||
/* MAC Initialization *****************************************************/
|
||
/* Put the MAC into the reset state */
|
||
|
||
pic32mx_putreg((EMAC1_CFG1_TXRST | EMAC1_CFG1_MCSTXRST | EMAC1_CFG1_RXRST |
|
||
EMAC1_CFG1_MCSRXRST | EMAC1_CFG1_SIMRST | EMAC1_CFG1_SOFTRST),
|
||
PIC32MX_EMAC1_CFG1);
|
||
|
||
/* Take the MAC out of the reset state */
|
||
|
||
up_udelay(50);
|
||
pic32mx_putreg(0, PIC32MX_EMAC1_CFG1);
|
||
irqrestore(flags);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Public Functions
|
||
****************************************************************************/
|
||
|
||
/****************************************************************************
|
||
* Function: pic32mx_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_PIC32MX_NINTERFACES > 1
|
||
int pic32mx_ethinitialize(int intf)
|
||
#else
|
||
static inline int pic32mx_ethinitialize(int intf)
|
||
#endif
|
||
{
|
||
struct pic32mx_driver_s *priv;
|
||
int ret;
|
||
|
||
DEBUGASSERT(intf < CONFIG_PIC32MX_NINTERFACES);
|
||
priv = &g_ethdrvr[intf];
|
||
|
||
/* Initialize the driver structure */
|
||
|
||
memset(priv, 0, sizeof(struct pic32mx_driver_s));
|
||
priv->pd_dev.d_ifup = pic32mx_ifup; /* I/F down callback */
|
||
priv->pd_dev.d_ifdown = pic32mx_ifdown; /* I/F up (new IP address) callback */
|
||
priv->pd_dev.d_txavail = pic32mx_txavail; /* New TX data callback */
|
||
#ifdef CONFIG_NET_IGMP
|
||
priv->pd_dev.d_addmac = pic32mx_addmac; /* Add multicast MAC address */
|
||
priv->pd_dev.d_rmmac = pic32mx_rmmac; /* Remove multicast MAC address */
|
||
#endif
|
||
priv->pd_dev.d_private = (void*)priv; /* Used to recover private state from dev */
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
# error "A mechanism to associate base address an IRQ with an interface is needed"
|
||
priv->pd_base = ??; /* Ethernet controller base address */
|
||
priv->pd_irq = ??; /* Ethernet controller IRQ vector number */
|
||
priv->pd_irqsrc = ??; /* Ethernet controller IRQ source number */
|
||
#endif
|
||
|
||
/* Create a watchdog for timing polling for and timing of transmisstions */
|
||
|
||
priv->pd_txpoll = wd_create(); /* Create periodic poll timer */
|
||
priv->pd_txtimeout = wd_create(); /* Create TX timeout timer */
|
||
|
||
/* Reset the Ethernet controller and leave in the ifdown state. The
|
||
* Ethernet controller will be properly re-initialized each time
|
||
* pic32mx_ifup() is called.
|
||
*/
|
||
|
||
pic32mx_ifdown(&priv->pd_dev);
|
||
|
||
/* Attach the IRQ to the driver */
|
||
|
||
#if CONFIG_PIC32MX_NINTERFACES > 1
|
||
ret = irq_attach(priv->pd_irq, pic32mx_interrupt);
|
||
#else
|
||
ret = irq_attach(PIC32MX_IRQ_ETH, pic32mx_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->pd_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_PIC32MX_NINTERFACES == 1
|
||
void up_netinitialize(void)
|
||
{
|
||
(void)pic32mx_ethinitialize(0);
|
||
}
|
||
#endif
|
||
#endif /* CHIP_NETHERNET > 0 */
|
||
#endif /* CONFIG_NET && CONFIG_PIC32MX_ETHERNET */
|