diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 5cb96e01e3..3474c2e165 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -265,6 +265,178 @@ if SAMA5_GMAC menu "GMAC device driver options" +config SAMA5_GMAC_NRXBUFFERS + int "Number of RX buffers" + default 16 + ---help--- + GMAC buffer memory is segmented into 128 byte units (not + configurable). This setting provides the number of such 128 byte + units used for reception. This is also equal to the number of RX + descriptors that will be allocated The selected value must be an + even power of 2. + +config SAMA5_GMAC_NTXBUFFERS + int "Number of TX buffers" + default 1 + ---help--- + GMAC buffer memory is segmented into full Ethernet packets (size + NET_BUFSIZE bytes). This setting provides the number of such packets + that can be in flight. This is also equal to the number of TX + descriptors that will be allocated. + +config SAMA5_GMAC_PREALLOCATE + bool "Preallocate buffers" + default n + ---help--- + Buffer an descriptor many may either be allocated from the memory + pool or pre-allocated to lie in .bss. This options selected pre- + allocated buffer memory. + +config SAMA5_GMAC_NBC + bool "Disable Broadcast" + default n + ---help--- + Select to disable receipt of broadcast packets. + +config SAMA5_GMAC_PHYADDR + int "PHY address" + default 1 + ---help--- + The 5-bit address of the PHY on the board. Default: 1 + +config SAMA5_GMAC_PHYINIT + bool "Board-specific PHY Initialization" + default n + ---help--- + Some boards require specialized initialization of the PHY before it can be used. + This may include such things as configuring GPIOs, resetting the PHY, etc. If + SAMA5_GMAC_PHYINIT is defined in the configuration then the board specific logic must + provide sam_phyinitialize(); The SAMA5 GMAC driver will call this function + one time before it first uses the PHY. + +config SAMA5_GMAC_GMII + bool "Use MII interface" + default n + ---help--- + Support Ethernet MII interface (vs RMII). + +config SAMA5_GMAC_RGMII + bool + default y if !SAMA5_GMAC_GMII + default n if SAMA5_GMAC_GMII + +config SAMA5_GMAC_AUTONEG + bool "Use autonegotiation" + default y + ---help--- + Use PHY autonegotiation to determine speed and mode + +config SAMA5_GMAC_ETHFD + bool "Full duplex" + default n + depends on !SAMA5_GMAC_AUTONEG + ---help--- + If SAMA5_GMAC_AUTONEG is not defined, then this may be defined to select full duplex + mode. Default: half-duplex + +config SAMA5_GMAC_ETH100MBPS + bool "100 Mbps" + default n + depends on !SAMA5_GMAC_AUTONEG + ---help--- + If SAMA5_GMAC_AUTONEG is not defined, then this may be defined to select 100 MBps + speed. Default: 10 Mbps + +config SAMA5_GMAC_PHYSR + int "PHY Status Register Address (decimal)" + depends on SAMA5_GMAC_AUTONEG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. The PHY status register + address may diff from PHY to PHY. This configuration sets the address of + the PHY status register. + +config SAMA5_GMAC_PHYSR_ALTCONFIG + bool "PHY Status Alternate Bit Layout" + default n + depends on SAMA5_GMAC_AUTONEG + ---help--- + Different PHYs present speed and mode information in different ways. Some + will present separate information for speed and mode (this is the default). + Those PHYs, for example, may provide a 10/100 Mbps indication and a separate + full/half duplex indication. This options selects an alternative representation + where speed and mode information are combined. This might mean, for example, + separate bits for 10HD, 100HD, 10FD and 100FD. + +config SAMA5_GMAC_PHYSR_SPEED + hex "PHY Speed Mask" + depends on SAMA5_GMAC_AUTONEG && !SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This provides bit mask + for isolating the 10 or 100MBps speed indication. + +config SAMA5_GMAC_PHYSR_100MBPS + hex "PHY 100Mbps Speed Value" + depends on SAMA5_GMAC_AUTONEG && !SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This provides the value + of the speed bit(s) indicating 100MBps speed. + +config SAMA5_GMAC_PHYSR_MODE + hex "PHY Mode Mask" + depends on SAMA5_GMAC_AUTONEG && !SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This provide bit mask + for isolating the full or half duplex mode bits. + +config SAMA5_GMAC_PHYSR_FULLDUPLEX + hex "PHY Full Duplex Mode Value" + depends on SAMA5_GMAC_AUTONEG && !SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This provides the + value of the mode bits indicating full duplex mode. + +config SAMA5_GMAC_PHYSR_ALTMODE + hex "PHY Mode Mask" + depends on SAMA5_GMAC_AUTONEG && SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This provide bit mask + for isolating the speed and full/half duplex mode bits. + +config SAMA5_GMAC_PHYSR_10HD + hex "10MBase-T Half Duplex Value" + depends on SAMA5_GMAC_AUTONEG && SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This is the value + under the bit mask that represents the 10Mbps, half duplex setting. + +config SAMA5_GMAC_PHYSR_100HD + hex "100Base-T Half Duplex Value" + depends on SAMA5_GMAC_AUTONEG && SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This is the value + under the bit mask that represents the 100Mbps, half duplex setting. + +config SAMA5_GMAC_PHYSR_10FD + hex "10Base-T Full Duplex Value" + depends on SAMA5_GMAC_AUTONEG && SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This is the value + under the bit mask that represents the 10Mbps, full duplex setting. + +config SAMA5_GMAC_PHYSR_100FD + hex "100Base-T Full Duplex Value" + depends on SAMA5_GMAC_AUTONEG && SAMA5_GMAC_PHYSR_ALTCONFIG + ---help--- + This must be provided if SAMA5_GMAC_AUTONEG is defined. This is the value + under the bit mask that represents the 100Mbps, full duplex setting. + +config SAMA5_GMAC_REGDEBUG + bool "Register-Level Debug" + default n + depends on DEBUG + ---help--- + Enable very low-level register access debug. Depends on DEBUG. + if SAMA5_EMAC config SAMA5_GMAC_ISETH0 @@ -272,6 +444,14 @@ config SAMA5_GMAC_ISETH0 default y endif # SAMA5_EMAC + +if !SAMA5_EMAC + +config SAMA5_GMAC_ISETH0 + bool + default y + +endif # !SAMA5_EMAC endmenu # GMAC device driver options endif # SAMA5_GMAC diff --git a/arch/arm/src/sama5/chip/sam_emac.h b/arch/arm/src/sama5/chip/sam_emac.h index c839e8a0a5..86f7c766ff 100644 --- a/arch/arm/src/sama5/chip/sam_emac.h +++ b/arch/arm/src/sama5/chip/sam_emac.h @@ -425,13 +425,13 @@ #define EMACTXD_STA_BUFLEN_MASK (0x000003ff << EMACTXD_STA_BUFLEN_SHIFT) /* Bits 11-14: Reserved */ #define EMACTXD_STA_LAST (1 << 15) /* Bit 15: Last buffer in the current frame */ -#define EMACTXD_STA_NOCRC (1 << 16) /* Bit 16: No CRC*/ +#define EMACTXD_STA_NOCRC (1 << 16) /* Bit 16: No CRC */ /* Bits 17-26: Reserved */ -#define EMACTXD_STA_NOBUFFER (1 << 27) /* Bit 27: Buffers exhausted in mid frame*/ -#define EMACTXD_STA_TXUR (1 << 28) /* Bit 28: Transmit underrun*/ -#define EMACTXD_STA_TXERR (1 << 29) /* Bit 29: Retry limit exceeded, transmit error detected*/ -#define EMACTXD_STA_WRAP (1 << 30) /* Bit 30: Last descriptor in descriptor list*/ -#define EMACTXD_STA_USED (1 << 31) /* Bit 31: Zero for the EMAC to read from buffer*/ +#define EMACTXD_STA_NOBUFFER (1 << 27) /* Bit 27: Buffers exhausted in mid frame */ +#define EMACTXD_STA_TXUR (1 << 28) /* Bit 28: Transmit underrun */ +#define EMACTXD_STA_TXERR (1 << 29) /* Bit 29: Retry limit exceeded, transmit error detected */ +#define EMACTXD_STA_WRAP (1 << 30) /* Bit 30: Last descriptor in descriptor list */ +#define EMACTXD_STA_USED (1 << 31) /* Bit 31: Zero for the EMAC to read from buffer */ /************************************************************************************ * Public Types @@ -451,4 +451,5 @@ struct emac_txdesc_s uint32_t addr; /* Buffer address */ uint32_t status; /* TX status and controls */ }; + #endif /* __ARCH_ARM_SRC_SAMA5_CHIP_SAM_EMAC_H */ diff --git a/arch/arm/src/sama5/chip/sam_gmac.h b/arch/arm/src/sama5/chip/sam_gmac.h index 57483f7ae1..6ed56589b9 100644 --- a/arch/arm/src/sama5/chip/sam_gmac.h +++ b/arch/arm/src/sama5/chip/sam_gmac.h @@ -590,8 +590,8 @@ #define GMAC_MAN_DATA_MASK (0x0000ffff << GMAC_MAN_DATA_SHIFT) # define GMAC_MAN_DATA(n) ((uint32_t)(n) << GMAC_MAN_DATA_SHIFT) #define GMAC_MAN_WTN_SHIFT (16) /* Bits 16-17: Must be written to b10 */ -#define GMAC_MAN_WTN_MASK (3 << GMAC_MAN_CODE_SHIFT) -# define GMAC_MAN_WTN (2 << GMAC_MAN_CODE_SHIFT) +#define GMAC_MAN_WTN_MASK (3 << GMAC_MAN_WTN_SHIFT) +# define GMAC_MAN_WTN (2 << GMAC_MAN_WTN_SHIFT) #define GMAC_MAN_REGA_SHIFT (18) /* Bits 18-22: Register Address */ #define GMAC_MAN_REGA_MASK (31 << GMAC_MAN_REGA_SHIFT) # define GMAC_MAN_REGA(n) ((uint32_t)(n) << GMAC_MAN_REGA_SHIFT) @@ -915,4 +915,99 @@ # define GMAC_ST2RPQ0_VLANP(n) ((n) << GMAC_ST2RPQ0_VLANP_SHIFT) #define GMAC_ST2RPQ0_VLANE (1 << 8) /* Bit 8: VLAN Enable */ +/* Descriptors **********************************************************************/ + +/* Receive buffer descriptor: Address word */ + +#define GMACRXD_ADDR_OWNER (1 << 0) /* Bit 0: 1=Software owns; 0=GMAC owns */ +#define GMACRXD_ADDR_WRAP (1 << 1) /* Bit 1: Last descriptor in list */ +#define GMACRXD_ADDR_MASK (0xfffffffc) /* Bits 2-31: Aligned buffer address */ + +/* Receive buffer descriptor: Control word */ + +#define GMACRXD_STA_FRLEN_SHIFT (0) /* Bits 0-12: Length of frame */ +#define GMACRXD_STA_FRLEN_MASK (0x00000fff << GMACRXD_STA_FRLEN_SHIFT) +#define GMACRXD_STA_JFRLEN_SHIFT (0) /* Bits 0-13: Length of jumbo frame */ +#define GMACRXD_STA_JFRLEN_MASK (0x00001fff << GMACRXD_STA_JFRLEN_SHIFT) +#define GMACRXD_STA_BADFCS (1 << 13) /* Bit 13: Frame had bad FCS */ +#define GMACRXD_STA_SOF (1 << 14) /* Bit 14: Start of frame */ +#define GMACRXD_STA_EOF (1 << 15) /* Bit 15: End of frame */ +#define GMACRXD_STA_CFI (1 << 16) /* Bit 16: Canonical format indicator (CFI) bit */ +#define GMACRXD_STA_VLPRIO_SHIFT (17) /* Bits 17-19: VLAN priority */ +#define GMACRXD_STA_VLPRIO_MASK (7 << GMACRXD_STA_VLANPRIO_SHIFT) +#define GMACRXD_STA_PRIODET (1 << 20) /* Bit 20: Priority tag detected */ +#define GMACRXD_STA_VLANTAG (1 << 21) /* Bit 21: VLAN tag detected */ +#define GMACRXD_STA_TYPID_SHIFT (22) /* Bits 22-23: Type ID register match */ +#define GMACRXD_STA_TYPID_MASK (3 << GMACRXD_STA_TYPID_SHIFT) +# define GMACRXD_STA_TYPID1 (0 << GMACRXD_STA_TYPID_SHIFT) /* Type ID register 1 match */ +# define GMACRXD_STA_TYPID2 (1 << GMACRXD_STA_TYPID_SHIFT) /* Type ID register 2 match */ +# define GMACRXD_STA_TYPID3 (2 << GMACRXD_STA_TYPID_SHIFT) /* Type ID register 3 match */ +# define GMACRXD_STA_TYPID4 (3 << GMACRXD_STA_TYPID_SHIFT) /* Type ID register 4 match */ +#define GMACRXD_STA_SNAP_SHIFT (22) /* Bits 22-23: Specific Address Register match */ +#define GMACRXD_STA_SNAP_MASK (3 << GMACRXD_STA_SNAP_SHIFT) +# define GMACRXD_STA_SNAP_NOCHK (0 << GMACRXD_STA_SNAP_SHIFT) /* Checksum not checked */ +# define GMACRXD_STA_SNAP_IPCHK (1 << GMACRXD_STA_SNAP_SHIFT) /* IP header checksum checked */ +# define GMACRXD_STA_SNAP_TCPCHK (2 << GMACRXD_STA_SNAP_SHIFT) /* IP header and TCP checksum checked */ +# define GMACRXD_STA_SNAP_UDPCHK (3 << GMACRXD_STA_SNAP_SHIFT) /* IP header and UDP checksum checked */ +#define GMACRXD_STA_TYPID (1 << 24) /* Bit 24: Type ID match found */ +#define GMACRXD_STA_SNAP (1 << 24) /* Bit 24: Frame was SNAP encoded */ +#define GMACRXD_STA_ADDR_SHIFT (25) /* Bits 25-26: Specific Address Register match */ +#define GMACRXD_STA_ADDR_MASK (3 << GMACRXD_STA_ADDR_SHIFT) +# define GMACRXD_STA_ADDR1_MATCH (0 << GMACRXD_STA_ADDR_SHIFT) /* Specific address register 1 match */ +# define GMACRXD_STA_ADDR2_MATCH (1 << GMACRXD_STA_ADDR_SHIFT) /* Specific address register 2 match */ +# define GMACRXD_STA_ADDR3_MATCH (2 << GMACRXD_STA_ADDR_SHIFT) /* Specific address register 3 match */ +# define GMACRXD_STA_ADDR4_MATCH (3 << GMACRXD_STA_ADDR_SHIFT) /* Specific address register 4 match */ +#define GMACRXD_STA_ADDRMATCH (1 << 27) /* Bit 27: Specific Address Register match found */ + /* Bit 28: Reserved */ +#define GMACRXD_STA_UCAST (1 << 29) /* Bit 29: Unicast hash match */ +#define GMACRXD_STA_MCAST (1 << 30) /* Bit 30: Multicast hash match */ +#define GMACRXD_STA_BCAST (1 << 31) /* Bit 31: Global all ones broadcast address detected */ + +/* Transmit buffer descriptor: Address word (un-aligned, 32-bit address */ + +/* Transmit buffer descriptor: Control word */ + +#define GMACTXD_STA_BUFLEN_SHIFT (0) /* Bits 0-13: Length of buffer */ +#define GMACTXD_STA_BUFLEN_MASK (0x00003fff << GMACTXD_STA_BUFLEN_SHIFT) + /* Bit 14: Reserved */ +#define GMACTXD_STA_LAST (1 << 15) /* Bit 15: Last buffer in the current frame */ +#define GMACTXD_STA_NOCRC (1 << 16) /* Bit 16: No CRC */ + /* Bits 17-19: Reserved */ +#define GMACTXD_STA_CKERR_SHIFT (20) /* Bits 20-22: Transmit checksum generation errors */ +#define GMACTXD_STA_CKERR_MASK (7 << GMACTXD_STA_CKERR_SHIFT) +# define GMACTXD_STA_CKERR_OK (0 << GMACTXD_STA_CKERR_SHIFT) /* No Error */ +# define GMACTXD_STA_CKERR_VLAN (1 << GMACTXD_STA_CKERR_SHIFT) /* VLAN header error */ +# define GMACTXD_STA_CKERR_SNAP (2 << GMACTXD_STA_CKERR_SHIFT) /* SNAP header error */ +# define GMACTXD_STA_CKERR_IP (3 << GMACTXD_STA_CKERR_SHIFT) /* Bad IP type */ +# define GMACTXD_STA_CKERR_UNK (4 << GMACTXD_STA_CKERR_SHIFT) /* Not VLAN, SNAP or IP */ +# define GMACTXD_STA_CKERR_FRAG (5 << GMACTXD_STA_CKERR_SHIFT) /* Bad packet fragmentation */ +# define GMACTXD_STA_CKERR_PROTO (6 << GMACTXD_STA_CKERR_SHIFT) /* Not TCP or UDP */ +# define GMACTXD_STA_CKERR_END (7 << GMACTXD_STA_CKERR_SHIFT) /* Premature end of packet */ + /* Bits 23-25: Reserved */ +#define GMACTXD_STA_LCOL (1 << 26) /* Bit 26: Late collision */ +#define GMACTXD_STA_TFC (1 << 27) /* Bit 27: Transmit Frame Corruption due to AHB error */ +#define GMACTXD_STA_TXUR (1 << 28) /* Bit 28: Transmit underrun */ +#define GMACTXD_STA_TXERR (1 << 29) /* Bit 29: Retry limit exceeded, transmit error detected */ +#define GMACTXD_STA_WRAP (1 << 30) /* Bit 30: Last descriptor in descriptor list */ +#define GMACTXD_STA_USED (1 << 31) /* Bit 31: Zero for the GMAC to read from buffer */ + +/************************************************************************************ + * Public Types + ************************************************************************************/ +/* Receive buffer descriptor */ + +struct gmac_rxdesc_s +{ + uint32_t addr; /* Buffer address */ + uint32_t status; /* RX status and controls */ +}; + +/* Transmit buffer descriptor */ + +struct gmac_txdesc_s +{ + uint32_t addr; /* Buffer address */ + uint32_t status; /* TX status and controls */ +}; + #endif /* __ARCH_ARM_SRC_SAMA5_CHIP_SAM_GMAC_H */ diff --git a/arch/arm/src/sama5/sam_gmac.c b/arch/arm/src/sama5/sam_gmac.c new file mode 100644 index 0000000000..db3c8ef071 --- /dev/null +++ b/arch/arm/src/sama5/sam_gmac.c @@ -0,0 +1,2942 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_gmac.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * References: + * SAMA5D3 Series Data Sheet + * Atmel NoOS sample code. + * + * The Atmel sample code has a BSD compatibile license that requires this + * copyright notice: + * + * Copyright (c) 2012, Atmel Corporation + * + * 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 names NuttX nor Atmel 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "up_arch.h" +#include "up_internal.h" +#include "cache.h" + +#include "chip.h" +#include "chip/sam_pinmap.h" +#include "sam_pio.h" +#include "sam_periphclks.h" +#include "sam_memories.h" +#include "sam_ethernet.h" + +#include + +#if defined(CONFIG_NET) && defined(CONFIG_SAMA5_GMAC) + +/**************************************************************************** + * Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +/* Number of buffer for RX */ + +#ifndef CONFIG_SAMA5_GMAC_NRXBUFFERS +# define CONFIG_SAMA5_GMAC_NRXBUFFERS 16 +#endif + +/* Number of buffer for TX */ + +#ifndef CONFIG_SAMA5_GMAC_NTXBUFFERS +# define CONFIG_SAMA5_GMAC_NTXBUFFERS 32 +#endif + +#undef CONFIG_SAMA5_GMAC_NBC + +#ifndef CONFIG_SAMA5_GMAC_PHYADDR +# error "CONFIG_SAMA5_GMAC_PHYADDR must be defined in the NuttX configuration" +#endif + +#if !defined(CONFIG_SAMA5_GMAC_GMII) && !defined(CONFIG_SAMA5_GMAC_RGMII) +# warning "Neither CONFIG_SAMA5_GMAC_GMII nor CONFIG_SAMA5_GMAC_RGMII defined" +#endif + +#if defined(CONFIG_SAMA5_GMAC_GMII) && defined(CONFIG_SAMA5_GMAC_RGMII) +# error "Both CONFIG_SAMA5_GMAC_GMII and CONFIG_SAMA5_GMAC_RGMII defined" +#endif + +#ifdef CONFIG_SAMA5_GMAC_AUTONEG +# ifndef CONFIG_SAMA5_GMAC_PHYSR +# error "CONFIG_SAMA5_GMAC_PHYSR must be defined in the NuttX configuration" +# endif +# ifdef CONFIG_SAMA5_GMAC_PHYSR_ALTCONFIG +# ifndef CONFIG_SAMA5_GMAC_PHYSR_ALTMODE +# error "CONFIG_SAMA5_GMAC_PHYSR_ALTMODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_10HD +# error "CONFIG_SAMA5_GMAC_PHYSR_10HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_100HD +# error "CONFIG_SAMA5_GMAC_PHYSR_100HD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_10FD +# error "CONFIG_SAMA5_GMAC_PHYSR_10FD must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_100FD +# error "CONFIG_SAMA5_GMAC_PHYSR_100FD must be defined in the NuttX configuration" +# endif +# else +# ifndef CONFIG_SAMA5_GMAC_PHYSR_SPEED +# error "CONFIG_SAMA5_GMAC_PHYSR_SPEED must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_100MBPS +# error "CONFIG_SAMA5_GMAC_PHYSR_100MBPS must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_MODE +# error "CONFIG_SAMA5_GMAC_PHYSR_MODE must be defined in the NuttX configuration" +# endif +# ifndef CONFIG_SAMA5_GMAC_PHYSR_FULLDUPLEX +# error "CONFIG_SAMA5_GMAC_PHYSR_FULLDUPLEX must be defined in the NuttX configuration" +# endif +# endif +#endif + +/* PHY definitions */ + +#if defined(SAMA5_GMAC_PHY_DM9161) +# define MII_OUI_MSB 0x0181 +# define MII_OUI_LSB 0x2e +#elif defined(SAMA5_GMAC_PHY_LAN8700) +# define MII_OUI_MSB 0x0007 +# define MII_OUI_LSB 0x30 +#elif defined(SAMA5_GMAC_PHY_KSZ8051) +# define MII_OUI_MSB 0x0022 +# define MII_OUI_LSB 0x05 +#else +# error GMAC PHY unrecognized +#endif + +#ifdef CONFIG_SAMA5_GMAC_PHYSR_ALTCONFIG + +# define PHYSR_MODE(sr) ((sr) & CONFIG_SAMA5_GMAC_PHYSR_ALTMODE) +# define PHYSR_ISMODE(sr,m) (PHYSR_MODE(sr) == (m)) + +# define PHYSR_IS10HDX(sr) PHYSR_ISMODE(sr,CONFIG_SAMA5_GMAC_PHYSR_10HD) +# define PHYSR_IS100HDX(sr) PHYSR_ISMODE(sr,CONFIG_SAMA5_GMAC_PHYSR_100HD) +# define PHYSR_IS10FDX(sr) PHYSR_ISMODE(sr,CONFIG_SAMA5_GMAC_PHYSR_10FD) +# define PHYSR_IS100FDX(sr) PHYSR_ISMODE(sr,CONFIG_SAMA5_GMAC_PHYSR_100FD) + +#else + +# define PHYSR_MODESPEED (CONFIG_PHYSR_MODE | CONFIG_PHYSR_SPEED) +# define PHYSR_10HDX (0) +# define PHYSR_100HDX (CONFIG_PHYSR_100MBPS) +# define PHYSR_10FDX (CONFIG_PHYSR_FULLDUPLEX) +# define PHYSR_100FDX (CONFIG_PHYSR_FULLDUPLEX | CONFIG_PHYSR_100MBPS) + +# define PHYSR_IS10HDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_10HDX) +# define PHYSR_IS100HDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_100HDX) +# define PHYSR_IS10FDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_10FDX) +# define PHYSR_IS100FDX(sr) (((sr) & PHYSR_MODESPEED) == PHYSR_100FDX) + +#endif + +/* GMAC buffer sizes, number of buffers, and number of descriptors. + * + * REVISIT: The CONFIG_NET_MULTIBUFFER might be useful. It might be possible + * to use this option to send and receive messages directly into the DMA + * buffers, saving a copy. There might be complications on the receiving + * side, however, where buffers may wrap and where the size of the received + * frame will typically be smaller than a full packet. + */ + +#ifdef CONFIG_NET_MULTIBUFFER +# error CONFIG_NET_MULTIBUFFER must not be set +#endif + +#define GMAC_RX_UNITSIZE 128 /* Fixed size for RX buffer */ +#define GMAC_TX_UNITSIZE CONFIG_NET_BUFSIZE /* MAX size for Ethernet packet */ + +/* We need at least one more free buffer than transmit buffers */ + +#define SAM_GMAC_NFREEBUFFERS (CONFIG_SAMA5_GMAC_NTXBUFFERS+1) + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_SAMA5_GMAC_REGDEBUG +#endif + +#ifdef CONFIG_NET_DUMPPACKET +# define sam_dumppacket(m,a,n) lib_dumpbuffer(m,a,n) +#else +# define sam_dumppacket(m,a,n) +#endif + +/* Timing *******************************************************************/ +/* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per + * second + */ + +#define SAM_WDDELAY (1*CLK_TCK) +#define SAM_POLLHSEC (1*2) + +/* TX timeout = 1 minute */ + +#define SAM_TXTIMEOUT (60*CLK_TCK) + +/* PHY read/write delays in loop counts */ + +#define PHY_RETRY_MAX 1000000 + +/* Helpers ******************************************************************/ +/* This is a helper pointer for accessing the contents of the GMAC + * header + */ + +#define BUF ((struct uip_eth_hdr *)priv->dev.d_buf) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The sam_gmac_s encapsulates all state information for the GMAC peripheral */ + +struct sam_gmac_s +{ + uint8_t ifup : 1; /* true:ifup false:ifdown */ + WDOG_ID txpoll; /* TX poll timer */ + WDOG_ID txtimeout; /* TX timeout timer */ + + /* This holds the information visible to uIP/NuttX */ + + struct uip_driver_s dev; /* Interface understood by uIP */ + + /* Used to track transmit and receive descriptors */ + + uint8_t phyaddr; /* PHY address (pre-defined by pins on reset) */ + uint16_t txhead; /* Circular buffer head index */ + uint16_t txtail; /* Circualr buffer tail index */ + uint16_t rxndx; /* RX index for current processing RX descriptor */ + + uint8_t *rxbuffer; /* Allocated RX buffers */ + uint8_t *txbuffer; /* Allocated TX buffers */ + struct gmac_rxdesc_s *rxdesc; /* Allocated RX descriptors */ + struct gmac_txdesc_s *txdesc; /* Allocated TX descriptors */ + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_GMAC_REGDEBUG + bool wrlast; /* Last was a write */ + uintptr_t addrlast; /* Last address */ + uint32_t vallast; /* Last value */ + int ntimes; /* Number of times */ +#endif +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The driver state singleton */ + +static struct sam_gmac_s g_gmac; + +#ifdef CONFIG_SAMA5_GMAC_PREALLOCATE +/* Preallocated data */ +/* TX descriptors list */ + +static struct gmac_txdesc_s g_txdesc[TX_BUFFERS] __attribute__((aligned(8))); + +/* RX descriptors list */ + +static struct gmac_rxdesc_s g_rxdesc[RX_BUFFERS]__attribute__((aligned(8))); + +/* Transmit Buffers + * + * Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries. + * Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address + * shall be set to 0 + */ + +static uint8_t g_txbuffer[CONFIG_SAMA5_GMAC_NTXBUFFERS * GMAC_TX_UNITSIZE]; + __attribute__((aligned(8))) + +/* Receive Buffers */ + +static uint8_t g_rxbuffer[CONFIG_SAMA5_GMAC_NRXBUFFERS * GMAC_RX_UNITSIZE] + __attribute__((aligned(8))); + +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Register operations ******************************************************/ + +#if defined(CONFIG_SAMA5_GMAC_REGDEBUG) && defined(CONFIG_DEBUG) +static bool sam_checkreg(struct sam_gmac_s *priv, bool wr, + uint32_t regval, uintptr_t address); +static uint32_t sam_getreg(struct sam_gmac_s *priv, uintptr_t addr); +static void sam_putreg(struct sam_gmac_s *priv, uintptr_t addr, uint32_t val); +#else +# define sam_getreg(priv,addr) getreg32(addr) +# define sam_putreg(priv,addr,val) putreg32(val,addr) +#endif + +/* Buffer management */ + +static uint16_t sam_txinuse(struct sam_gmac_s *priv); +static uint16_t sam_txfree(struct sam_gmac_s *priv); +static int sam_buffer_initialize(struct sam_gmac_s *priv); +static void sam_buffer_free(struct sam_gmac_s *priv); + +/* Common TX logic */ + +static int sam_transmit(struct sam_gmac_s *priv); +static int sam_uiptxpoll(struct uip_driver_s *dev); +static void sam_dopoll(struct sam_gmac_s *priv); + +/* Interrupt handling */ + +static int sam_recvframe(struct sam_gmac_s *priv); +static void sam_receive(struct sam_gmac_s *priv); +static void sam_txdone(struct sam_gmac_s *priv); +static int sam_gmac_interrupt(int irq, void *context); + +/* Watchdog timer expirations */ + +static void sam_polltimer(int argc, uint32_t arg, ...); +static void sam_txtimeout(int argc, uint32_t arg, ...); + +/* NuttX callback functions */ + +static int sam_ifup(struct uip_driver_s *dev); +static int sam_ifdown(struct uip_driver_s *dev); +static int sam_txavail(struct uip_driver_s *dev); +#ifdef CONFIG_NET_IGMP +static int sam_addmac(struct uip_driver_s *dev, const uint8_t *mac); +static int sam_rmmac(struct uip_driver_s *dev, const uint8_t *mac); +#endif + +/* PHY Initialization */ + +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_VERBOSE) +static void sam_phydump(struct sam_gmac_s *priv); +#else +# define sam_phydump(priv) +#endif + +static int sam_phywait(struct sam_gmac_s *priv); +static int sam_phyreset(struct sam_gmac_s *priv); +static int sam_phyfind(struct sam_gmac_s *priv, uint8_t *phyaddr); +static int sam_phyread(struct sam_gmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t *phyval); +static int sam_phywrite(struct sam_gmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t phyval); +static int sam_autonegotiate(struct sam_gmac_s *priv); +static bool sam_linkup(struct sam_gmac_s *priv); +static int sam_phyinit(struct sam_gmac_s *priv); + +/* GMAC Initialization */ + +static void sam_txreset(struct sam_gmac_s *priv); +static void sam_rxreset(struct sam_gmac_s *priv); +static void sam_gmac_reset(struct sam_gmac_s *priv); +static void sam_macaddress(struct sam_gmac_s *priv); +static int sam_gmac_configure(struct sam_gmac_s *priv); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Name: sam_checkreg + * + * Description: + * Check if the current register access is a duplicate of the preceding. + * + * Input Parameters: + * regval - The value to be written + * address - The address of the register to write to + * + * Returned Value: + * true: This is the first register access of this type. + * flase: This is the same as the preceding register access. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_GMAC_REGDEBUG +static bool sam_checkreg(struct sam_gmac_s *priv, bool wr, uint32_t regval, + uintptr_t address) +{ + if (wr == priv->wrlast && /* Same kind of access? */ + regval == priv->vallast && /* Same value? */ + address == priv->addrlast) /* Same address? */ + { + /* Yes, then just keep a count of the number of times we did this. */ + + priv->ntimes++; + return false; + } + else + { + /* Did we do the previous operation more than once? */ + + if (priv->ntimes > 0) + { + /* Yes... show how many times we did it */ + + lldbg("...[Repeats %d times]...\n", priv->ntimes); + } + + /* Save information about the new access */ + + priv->wrlast = wr; + priv->vallast = regval; + priv->addrlast = address; + priv->ntimes = 0; + } + + /* Return true if this is the first time that we have done this operation */ + + return true; +} +#endif + +/**************************************************************************** + * Name: sam_getreg + * + * Description: + * Read any 32-bit register using an absolute + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_GMAC_REGDEBUG +static uint32_t sam_getreg(struct sam_gmac_s *priv, uintptr_t address) +{ + uint32_t regval = getreg32(address); + + if (sam_checkreg(priv, false, regval, address)) + { + lldbg("%08x->%08x\n", address, regval); + } + + return regval; +} +#endif + +/**************************************************************************** + * Name: sam_putreg + * + * Description: + * Write to any 32-bit register using an absolute address + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_GMAC_REGDEBUG +static void sam_putreg(struct sam_gmac_s *priv, uintptr_t address, + uint32_t regval) +{ + if (sam_checkreg(priv, true, regval, address)) + { + lldbg("%08x<-%08x\n", address, regval); + } + + putreg32(regval, address); +} +#endif + +/**************************************************************************** + * Function: sam_txinuse + * + * Description: + * Return the number of TX buffers in-use + * + * Input Parameters: + * priv - The GMAC driver state + * + * Returned Value: + * The number of TX buffers in-use + * + ****************************************************************************/ + +static uint16_t sam_txinuse(struct sam_gmac_s *priv) +{ + uint32_t txhead32 = (uint32_t)priv->txhead; + if ((uint32_t)priv->txtail > txhead32) + { + return txhead32 += CONFIG_SAMA5_GMAC_NTXBUFFERS; + } + + return (uint16_t)(txhead32 - (uint32_t)priv->txtail); +} + +/**************************************************************************** + * Function: sam_txfree + * + * Description: + * Return the number of TX buffers available + * + * Input Parameters: + * priv - The GMAC driver state + * + * Returned Value: + * The number of TX buffers available + * + ****************************************************************************/ + +static uint16_t sam_txfree(struct sam_gmac_s *priv) +{ + /* The number available is equal to the total number of buffers, minus the + * number of buffers in use. Notice that that actual number of buffers is + * the configured size minus 1. + */ + + return (CONFIG_SAMA5_GMAC_NTXBUFFERS-1) - sam_txinuse(priv); +} + +/**************************************************************************** + * Function: sam_buffer_initialize + * + * Description: + * Allocate aligned TX and RX descriptors and buffers. For the case of + * pre-allocated structures, the function degenerates to a few assignements. + * + * Input Parameters: + * priv - The GMAC driver state + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * Called very early in the initialization sequence. + * + ****************************************************************************/ + +static int sam_buffer_initialize(struct sam_gmac_s *priv) +{ +#ifdef CONFIG_SAMA5_GMAC_PREALLOCATE + /* Use pre-allocated buffers */ + + priv->txdesc = g_txdesc; + priv->rxdesc = g_rxdesc; + priv->txbuffer = g_txbuffer; + priv->rxbuffer = g_rxbuffer; + +#else + size_t allocsize; + + /* Allocate buffers */ + + allocsize = CONFIG_SAMA5_GMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s); + priv->txdesc = (struct gmac_txdesc_s *)kmemalign(8, allocsize); + if (!priv->txdesc) + { + nlldbg("ERROR: Failed to allocate TX descriptors\n"); + return -ENOMEM; + } + + memset(priv->txdesc, 0, allocsize); + + allocsize = CONFIG_SAMA5_GMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s); + priv->rxdesc = (struct gmac_rxdesc_s *)kmemalign(8, allocsize); + if (!priv->rxdesc) + { + nlldbg("ERROR: Failed to allocate RX descriptors\n"); + sam_buffer_free(priv); + return -ENOMEM; + } + + memset(priv->rxdesc, 0, allocsize); + + allocsize = CONFIG_SAMA5_GMAC_NTXBUFFERS * GMAC_TX_UNITSIZE; + priv->txbuffer = (uint8_t *)kmemalign(8, allocsize); + if (!priv->txbuffer) + { + nlldbg("ERROR: Failed to allocate TX buffer\n"); + sam_buffer_free(priv); + return -ENOMEM; + } + + allocsize = CONFIG_SAMA5_GMAC_NRXBUFFERS * GMAC_RX_UNITSIZE; + priv->rxbuffer = (uint8_t *)kmemalign(8, allocsize); + if (!priv->rxbuffer) + { + nlldbg("ERROR: Failed to allocate RX buffer\n"); + sam_buffer_free(priv); + return -ENOMEM; + } + +#endif + + DEBUGASSERT(((uintptr_t)priv->rxdesc & 7) == 0 && + ((uintptr_t)priv->rxbuffer & 7) == 0 && + ((uintptr_t)priv->txdesc & 7) == 0 && + ((uintptr_t)priv->txbuffer & 7) == 0); + return OK; +} + +/**************************************************************************** + * Function: sam_buffer_free + * + * Description: + * Free aligned TX and RX descriptors and buffers. For the case of + * pre-allocated structures, the function does nothing. + * + * Input Parameters: + * priv - The GMAC driver state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_buffer_free(struct sam_gmac_s *priv) +{ +#ifndef CONFIG_SAMA5_GMAC_PREALLOCATE + /* Free allocated buffers */ + + if (priv->txdesc) + { + kfree(priv->txdesc); + priv->txdesc = NULL; + } + + if (priv->rxdesc) + { + kfree(priv->rxdesc); + priv->rxdesc = NULL; + } + + if (priv->txbuffer) + { + kfree(priv->txbuffer); + priv->txbuffer = NULL; + } + + if (priv->rxbuffer) + { + kfree(priv->rxbuffer); + priv->rxbuffer = NULL; + } +#endif +} + +/**************************************************************************** + * Function: sam_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 sam_transmit(struct sam_gmac_s *priv) +{ + struct uip_driver_s *dev = &priv->dev; + volatile struct gmac_txdesc_s *txdesc; + uintptr_t virtaddr; + uint32_t regval; + uint32_t status; + + nllvdbg("d_len: %d txhead: %d\n", dev->d_len, priv->txhead); + sam_dumppacket("Transmit packet", dev->d_buf, dev->d_len); + + /* Check parameter */ + + if (dev->d_len > GMAC_TX_UNITSIZE) + { + nlldbg("ERROR: Packet too big: %d\n", dev->d_len); + return -EINVAL; + } + + /* Pointer to the current TX descriptor */ + + txdesc = &priv->txdesc[priv->txhead]; + + /* If no free TX descriptor, buffer can't be sent */ + + if (sam_txfree(priv) < 1) + { + nlldbg("ERROR: No free TX descriptors\n"); + return -EBUSY; + } + + /* Setup/Copy data to transmition buffer */ + + if (dev->d_len > 0) + { + /* Driver managed the ring buffer */ + + virtaddr = sam_virtramaddr(txdesc->addr); + memcpy((void *)virtaddr, dev->d_buf, dev->d_len); + cp15_clean_dcache((uint32_t)virtaddr, (uint32_t)virtaddr + dev->d_len); + } + + /* Update TX descriptor status. */ + + status = dev->d_len | GMACTXD_STA_LAST; + if (priv->txhead == CONFIG_SAMA5_GMAC_NTXBUFFERS-1) + { + status |= GMACTXD_STA_WRAP; + } + + /* Update the descriptor status and flush the updated value to RAM */ + + txdesc->status = status; + cp15_clean_dcache((uint32_t)txdesc, + (uint32_t)txdesc + sizeof(struct gmac_txdesc_s)); + + /* Increment the head index */ + + if (++priv->txhead >= CONFIG_SAMA5_GMAC_NTXBUFFERS) + { + priv->txhead = 0; + } + + /* Now start transmission (if it is not already done) */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_TSTART; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Setup the TX timeout watchdog (perhaps restarting the timer) */ + + (void)wd_start(priv->txtimeout, SAM_TXTIMEOUT, sam_txtimeout, 1, + (uint32_t)priv); + + /* Set d_len to zero meaning that the d_buf[] packet buffer is again + * available. + */ + + dev->d_len = 0; + + /* If we have no more available TX descriptors, then we must disable the + * RCOMP interrupt to stop further RX processing. Why? Because EACH RX + * packet that is dispatch is also an opportunity to replay with the a TX + * packet. So, if we cannot handle an RX packet replay, then we disable + * all RX packet processing. + */ + + if (sam_txfree(priv) < 1) + { + nllvdbg("Disabling RX interrupts\n"); + sam_putreg(priv, SAM_GMAC_IDR, GMAC_INT_RCOMP); + } + + return OK; +} + +/**************************************************************************** + * Function: sam_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 sam_uiptxpoll(struct uip_driver_s *dev) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Send the packet */ + + uip_arp_out(&priv->dev); + sam_transmit(priv); + + /* Check if the there are any free TX descriptors. We cannot perform + * the TX poll if we do not have buffering for another packet. + */ + + if (sam_txfree(priv) == 0) + { + /* We have to terminate the poll if we have no more descriptors + * available for another transfer. + */ + + return -EBUSY; + } + } + + /* If zero is returned, the polling will continue until all connections have + * been examined. + */ + + return 0; +} + +/**************************************************************************** + * Function: sam_dopoll + * + * Description: + * Perform the uIP poll. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void sam_dopoll(struct sam_gmac_s *priv) +{ + struct uip_driver_s *dev = &priv->dev; + + /* Check if the there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + if (sam_txfree(priv) > 0) + { + /* If we have the descriptor, then poll uIP for new XMIT data. */ + + (void)uip_poll(dev, sam_uiptxpoll); + } +} + +/**************************************************************************** + * Function: sam_recvframe + * + * Description: + * The function is called when a frame is received. It scans the RX + * descriptors of the received frame and assembles the full packet/ + * + * NOTE: This function will silently discard any packets containing errors. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK if a packet was successfully returned; -EAGAIN if there are no + * further packets available + * + * Assumptions: + * - Global interrupts are disabled by interrupt handling logic. + * - The RX descriptor D-cache list has been invalided to force fetching + * from RAM. + * + ****************************************************************************/ + +static int sam_recvframe(struct sam_gmac_s *priv) +{ + struct gmac_rxdesc_s *rxdesc; + struct uip_driver_s *dev; + const uint8_t *src; + uint8_t *dest; + uintptr_t physaddr; + uint32_t rxndx; + uint32_t pktlen; + uint16_t copylen; + bool isframe; + + /* Process received RX descriptor. The ownership bit is set by the GMAC + * once it has successfully written a frame to memory. + */ + + dev = &priv->dev; + dev->d_len = 0; + + rxndx = priv->rxndx; + rxdesc = &priv->rxdesc[rxndx]; + isframe = false; + + /* Invalidate the RX descriptor to force re-fetching from RAM */ + + cp15_invalidate_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct gmac_rxdesc_s)); + + nllvdbg("rxndx: %d\n", rxndx); + + while ((rxdesc->addr & GMACRXD_ADDR_OWNER) != 0) + { + /* The start of frame bit indicates the beginning of a frame. Discard + * any previous fragments. + */ + + if ((rxdesc->status & GMACRXD_STA_SOF) != 0) + { + /* Skip previous fragments */ + + while (rxndx != priv->rxndx) + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->rxdesc[priv->rxndx]; + rxdesc->addr &= ~(GMACRXD_ADDR_OWNER); + + /* Flush the modified RX descriptor to RAM */ + + cp15_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + + sizeof(struct gmac_rxdesc_s)); + + /* Increment the RX index */ + + if (++priv->rxndx >= CONFIG_SAMA5_GMAC_NRXBUFFERS) + { + priv->rxndx = 0; + } + } + + /* Reset the packet data pointer and packet length */ + + dest = dev->d_buf; + pktlen = 0; + + /* Start to gather buffers into the packet buffer */ + + isframe = true; + } + + /* Increment the working index */ + + if (++rxndx >= CONFIG_SAMA5_GMAC_NRXBUFFERS) + { + rxndx = 0; + } + + /* Copy data into the packet buffer */ + + if (isframe) + { + if (rxndx == priv->rxndx) + { + nllvdbg("ERROR: No EOF (Invalid of buffers too small)\n"); + do + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->rxdesc[priv->rxndx]; + rxdesc->addr &= ~(GMACRXD_ADDR_OWNER); + + /* Flush the modified RX descriptor to RAM */ + + cp15_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + + sizeof(struct gmac_rxdesc_s)); + + /* Increment the RX index */ + + if (++priv->rxndx >= CONFIG_SAMA5_GMAC_NRXBUFFERS) + { + priv->rxndx = 0; + } + } + while (rxndx != priv->rxndx); + return -EIO; + } + + /* Get the number of bytes to copy from the buffer */ + + copylen = GMAC_RX_UNITSIZE; + if ((pktlen + copylen) > CONFIG_NET_BUFSIZE) + { + copylen = CONFIG_NET_BUFSIZE - pktlen; + } + + /* Get the data source. Invalidate the source memory region to + * force reload from RAM. + */ + + physaddr = (uintptr_t)(rxdesc->addr & GMACRXD_ADDR_MASK); + src = (const uint8_t *)sam_virtramaddr(physaddr); + + cp15_invalidate_dcache((uintptr_t)src, (uintptr_t)src + copylen); + + /* And do the copy */ + + memcpy(dest, src, copylen); + dest += copylen; + pktlen += copylen; + + /* If the end of frame has been received, return the data */ + + if ((rxdesc->status & GMACRXD_STA_EOF) != 0) + { + /* Frame size from the GMAC */ + + dev->d_len = (rxdesc->status & GMACRXD_STA_FRLEN_MASK); + nllvdbg("packet %d-%d (%d)\n", priv->rxndx, rxndx, dev->d_len); + + /* All data have been copied in the application frame buffer, + * release the RX descriptor + */ + + while (priv->rxndx != rxndx) + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->rxdesc[priv->rxndx]; + rxdesc->addr &= ~(GMACRXD_ADDR_OWNER); + + /* Flush the modified RX descriptor to RAM */ + + cp15_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + + sizeof(struct gmac_rxdesc_s)); + + /* Increment the RX index */ + + if (++priv->rxndx >= CONFIG_SAMA5_GMAC_NRXBUFFERS) + { + priv->rxndx = 0; + } + } + + /* Check if the device packet buffer was large enough to accept + * all of the data. + */ + + nllvdbg("rxndx: %d d_len: %d\n", priv->rxndx, dev->d_len); + + if (pktlen < dev->d_len) + { + nlldbg("ERROR: Buffer size %d; frame size %d\n", dev->d_len, pktlen); + return -E2BIG; + } + + return OK; + } + } + + /* We have not encount the SOF yet... discard this fragment and keep looking */ + + else + { + /* Give ownership back to the GMAC */ + + rxdesc->addr &= ~(GMACRXD_ADDR_OWNER); + + /* Flush the modified RX descriptor to RAM */ + + cp15_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + + sizeof(struct gmac_rxdesc_s)); + + priv->rxndx = rxndx; + } + + /* Process the next buffer */ + + rxdesc = &priv->rxdesc[rxndx]; + + /* Invalidate the RX descriptor to force re-fetching from RAM */ + + cp15_invalidate_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct gmac_rxdesc_s)); + } + + /* No packet was found */ + + priv->rxndx = rxndx; + nllvdbg("rxndx: %d\n", priv->rxndx); + return -EAGAIN; +} + +/**************************************************************************** + * Function: sam_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * in FIFO memory. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void sam_receive(struct sam_gmac_s *priv) +{ + struct uip_driver_s *dev = &priv->dev; + + /* Loop while while sam_recvframe() successfully retrieves valid + * GMAC frames. + */ + + while (sam_recvframe(priv) == OK) + { + sam_dumppacket("Received packet", dev->d_buf, dev->d_len); + + /* Check if the packet is a valid size for the uIP buffer configuration + * (this should not happen) + */ + + if (dev->d_len > CONFIG_NET_BUFSIZE) + { + nlldbg("DROPPED: Too big: %d\n", dev->d_len); + } + + /* We only accept IP packets of the configured type and ARP packets */ + +#ifdef CONFIG_NET_IPv6 + else if (BUF->type == HTONS(UIP_ETHTYPE_IP6)) +#else + else if (BUF->type == HTONS(UIP_ETHTYPE_IP)) +#endif + { + nllvdbg("IP frame\n"); + + /* Handle ARP on input then give the IP packet to uIP */ + + uip_arp_ipin(&priv->dev); + uip_input(&priv->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->dev.d_len > 0) + { + uip_arp_out(&priv->dev); + sam_transmit(priv); + } + } + else if (BUF->type == htons(UIP_ETHTYPE_ARP)) + { + nllvdbg("ARP frame\n"); + + /* Handle ARP packet */ + + uip_arp_arpin(&priv->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->dev.d_len > 0) + { + sam_transmit(priv); + } + } + else + { + nlldbg("DROPPED: Unknown type: %04x\n", BUF->type); + } + } +} + +/**************************************************************************** + * Function: sam_txdone + * + * Description: + * An interrupt was received indicating that a frame has completed + * transmission. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void sam_txdone(struct sam_gmac_s *priv) +{ + struct gmac_txdesc_s *txdesc; + + /* Are there any outstanding transmssions? Loop until either (1) all of + * the TX have been examined, or (2) until we encounter the first + * descriptor that is still in use by the hardware. + */ + + while (priv->txhead != priv->txtail) + { + /* Yes.. check the next buffer at the tail of the list */ + + txdesc = &priv->txdesc[priv->txtail]; + cp15_invalidate_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct gmac_txdesc_s)); + + /* Is this TX descriptor still in use? */ + + if ((txdesc->status & GMACTXD_STA_USED) == 0) + { + /* Yes ... break out of the loop now */ + + break; + } + + /* Increment the tail index */ + + if (++priv->txtail >= CONFIG_SAMA5_GMAC_NTXBUFFERS) + { + /* Wrap to the beginning of the TX descriptor list */ + + priv->txtail = 0; + } + + /* At least one TX descriptor is available. Re-enable RX interrupts. + * RX interrupts may previously have been disabled when we ran out of + * TX desciptors (see commits in sam_transmit()). + */ + + sam_putreg(priv, SAM_GMAC_IER, GMAC_INT_RCOMP); + } + + /* Then poll uIP for new XMIT data */ + + sam_dopoll(priv); +} + +/**************************************************************************** + * Function: sam_gmac_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 sam_gmac_interrupt(int irq, void *context) +{ + struct sam_gmac_s *priv = &g_gmac; + uint32_t isr; + uint32_t rsr; + uint32_t tsr; + uint32_t imr; + uint32_t regval; + uint32_t pending; + uint32_t clrbits; + + isr = sam_getreg(priv, SAM_GMAC_ISR); + rsr = sam_getreg(priv, SAM_GMAC_RSR); + tsr = sam_getreg(priv, SAM_GMAC_TSR); + imr = sam_getreg(priv, SAM_GMAC_IMR); + + pending = isr & ~(imr | 0xffc300); + nllvdbg("isr: %08x pending: %08x\n", isr, pending); + + /* Check for the completion of a transmission. This should be done before + * checking for received data (because receiving can cause another transmission + * before we had a chance to handle the last one). + * + * ISR:TCOMP is set when a frame has been transmitted. Cleared on read. + * TSR:COMP is set when a frame has been transmitted. Cleared by writing a + * one to this bit. + */ + + if ((pending & GMAC_INT_TCOMP) != 0 || (tsr & GMAC_TSR_TXCOMP) != 0) + { + /* A frame has been transmitted */ + + clrbits = GMAC_TSR_TXCOMP; + + /* Check for Retry Limit Exceeded (RLE) */ + + if ((tsr & GMAC_TSR_RLE) != 0) + { + /* Status RLE & Number of discarded buffers */ + + clrbits = GMAC_TSR_RLE | sam_txinuse(priv); + sam_txreset(priv); + + nlldbg("ERROR: Retry Limit Exceeded TSR: %08x\n", tsr); + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_TXEN; + sam_putreg(priv, SAM_GMAC_NCR, regval); + } + + /* Check Collision Occurred (COL) */ + + if ((tsr & GMAC_TSR_COL) != 0) + { + nlldbg("ERROR: Collision occurred TSR: %08x\n", tsr); + clrbits |= GMAC_TSR_COL; + } + + /* Check for Transmit Frame Corruption due to AHB error (TFC) */ + + if ((tsr & GMAC_TSR_TFC) != 0) + { + nlldbg("ERROR: Buffers exhausted mid-frame TSR: %08x\n", tsr); + clrbits |= GMAC_TSR_TFC; + } + + /* Check for Transmit Underrun (UND) + * + * ISR:UND is set transmit DMA was not able to read data from memory, + * either because the bus was not granted in time, because a not + * OK hresp(bus error) was returned or because a used bit was read + * midway through frame transmission. If this occurs, the + * transmitter forces bad CRC. Cleared by writing a one to this bit. + */ + + if ((tsr & GMAC_TSR_UND) != 0) + { + nlldbg("ERROR: Transmit Underrun TSR: %08x\n", tsr); + clrbits |= GMAC_TSR_UND; + } + + /* Clear status */ + + sam_putreg(priv, SAM_GMAC_TSR, clrbits); + + /* And handle the TX done event */ + + sam_txdone(priv); + } + + /* Check for the receipt of an RX packet. + * + * RXCOMP indicates that a packet has been received and stored in memory. + * The RXCOMP bit is cleared whent he interrupt status register was read. + * RSR:REC indicates that one or more frames have been received and placed + * in memory. This indication is cleared by writing a one to this bit. + */ + + if ((pending & GMAC_INT_RCOMP) != 0 || (rsr & GMAC_RSR_REC) != 0) + { + clrbits = GMAC_RSR_REC; + + /* Check for Receive Overrun. + * + * RSR:RXOVR will be set if the RX FIFO is not able to store the + * receive frame due to a FIFO overflow, or if the receive status + * was not taken at the end of the frame. This bit is also set in + * DMA packet buffer mode if the packet buffer overflows. For DMA + * operation, the buffer will be recovered if an overrun occurs. This + * bit is cleared when set to 1. + */ + + if ((rsr & GMAC_RSR_RXOVR) != 0) + { + nlldbg("ERROR: Receiver overrun RSR: %08x\n", rsr); + clrbits |= GMAC_RSR_RXOVR; + } + + /* Check for buffer not available (BNA) + * + * RSR:BNA means that an attempt was made to get a new buffer and the + * pointer indicated that it was owned by the processor. The DMA will + * reread the pointer each time an end of frame is received until a + * valid pointer is found. This bit is set following each descriptor + * read attempt that fails, even if consecutive pointers are + * unsuccessful and software has in the mean time cleared the status + * flag. Cleared by writing a one to this bit. + */ + + if ((rsr & GMAC_RSR_BNA) != 0) + { + nlldbg("ERROR: Buffer not available RSR: %08x\n", rsr); + clrbits |= GMAC_RSR_BNA; + } + + /* Clear status */ + + sam_putreg(priv, SAM_GMAC_RSR, clrbits); + + /* Handle the received packet */ + + sam_receive(priv); + } + +#ifdef CONFIG_DEBUG_NET + /* Check for PAUSE Frame recieved (PFRE). + * + * ISR:PFRE indicates that a pause frame has been received. Cleared on a read. + */ + + if ((pending & GMAC_INT_PFNZ) != 0) + { + nlldbg("Pause frame received\n"); + } + + /* Check for Pause Time Zero (PTZ) + * + * ISR:PTZ is set Pause Time Zero + */ + + if ((pending & GMAC_INT_PTZ) != 0) + { + nlldbg("Pause TO!\n"); + } +#endif + + return OK; +} + +/**************************************************************************** + * Function: sam_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 sam_txtimeout(int argc, uint32_t arg, ...) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)arg; + + nlldbg("Timeout!\n"); + + /* Then reset the hardware. Just take the interface down, then back + * up again. + */ + + sam_ifdown(&priv->dev); + sam_ifup(&priv->dev); + + /* Then poll uIP for new XMIT data */ + + sam_dopoll(priv); +} + +/**************************************************************************** + * Function: sam_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 sam_polltimer(int argc, uint32_t arg, ...) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)arg; + struct uip_driver_s *dev = &priv->dev; + + /* Check if the there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + if (sam_txfree(priv) > 0) + { + /* Update TCP timing states and poll uIP for new XMIT data. */ + + (void)uip_timer(dev, sam_uiptxpoll, SAM_POLLHSEC); + } + + /* Setup the watchdog poll timer again */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, arg); +} + +/**************************************************************************** + * Function: sam_ifup + * + * Description: + * NuttX Callback: Bring up the GMAC interface when an IP address is + * provided + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_ifup(struct uip_driver_s *dev) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + int ret; + + nlldbg("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); + + /* Configure the GMAC interface for normal operation. */ + + nllvdbg("Initialize the GMAC\n"); + sam_gmac_configure(priv); + + /* Set the MAC address (should have been configured while we were down) */ + + sam_macaddress(priv); + + /* Initialize for PHY access */ + + ret = sam_phyinit(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_phyinit failed: %d\n", ret); + return ret; + } + + /* Auto Negotiate, working in RMII mode */ + + ret = sam_autonegotiate(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_autonegotiate failed: %d\n", ret); + return ret; + } + + while (sam_linkup(priv) == 0); + nllvdbg("Link detected \n"); + + /* Enable normal MAC operation */ + + nllvdbg("Enable normal operation\n"); + + /* Set and activate a timer process */ + + (void)wd_start(priv->txpoll, SAM_WDDELAY, sam_polltimer, 1, (uint32_t)priv); + + /* Enable the GMAC interrupt */ + + priv->ifup = true; + up_enable_irq(SAM_IRQ_GMAC); + return OK; +} + +/**************************************************************************** + * Function: sam_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_ifdown(struct uip_driver_s *dev) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + irqstate_t flags; + + nlldbg("Taking the network down\n"); + + /* Disable the GMAC interrupt */ + + flags = irqsave(); + up_disable_irq(SAM_IRQ_GMAC); + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(priv->txpoll); + wd_cancel(priv->txtimeout); + + /* Put the GMAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the sam_ifup() always + * successfully brings the interface back up. + */ + + sam_gmac_reset(priv); + + /* Mark the device "down" */ + + priv->ifup = false; + irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Function: sam_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 sam_txavail(struct uip_driver_s *dev) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + irqstate_t flags; + + nllvdbg("ifup: %d\n", priv->ifup); + + /* 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->ifup) + { + /* Poll uIP for new XMIT data */ + + sam_dopoll(priv); + } + + irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Function: sam_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 sam_addmac(struct uip_driver_s *dev, const uint8_t *mac) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + + nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Add the MAC address to the hardware multicast routing table */ + /* Add the MAC address to the hardware multicast routing table */ +#error "Missing logic" + + return OK; +} +#endif + +/**************************************************************************** + * Function: sam_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 sam_rmmac(struct uip_driver_s *dev, const uint8_t *mac) +{ + struct sam_gmac_s *priv = (struct sam_gmac_s *)dev->d_private; + + nllvdbg("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Add the MAC address to the hardware multicast routing table */ +#error "Missing logic" + + return OK; +} +#endif + +/**************************************************************************** + * Function: sam_phydump + * + * Description: + * Dump the contents of PHY registers + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_VERBOSE) +static void sam_phydump(struct sam_gmac_s *priv) +{ + uint32_t regval; + uint16_t phyval; + + /* Enable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + +#ifdef CONFIG_SAMA5_GMAC_RGMII + nllvdbg("RMII Registers (Address %02x)\n", priv->phyaddr); +#else /* defined(CONFIG_SAMA5_GMAC_GMII) */ + nllvdbg("MII Registers (Address %02x)\n", priv->phyaddr); +#endif + + sam_phyread(priv, priv->phyaddr, MII_MCR, &phyval); + nllvdbg(" MCR: %04x\n", phyval); + sam_phyread(priv, priv->phyaddr, MII_MSR, &phyval); + nllvdbg(" MSR: %04x\n", phyval); + sam_phyread(priv, priv->phyaddr, MII_ADVERTISE, &phyval); + nllvdbg(" ADVERTISE: %04x\n", phyval); + sam_phyread(priv, priv->phyaddr, MII_LPA, &phyval); + nllvdbg(" LPR: %04x\n", phyval); + sam_phyread(priv, priv->phyaddr, CONFIG_SAMA5_GMAC_PHYSR, &phyval); + nllvdbg(" PHYSR: %04x\n", phyval); + + /* Disable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); +} +#endif + +/**************************************************************************** + * Function: sam_phywait + * + * Description: + * Wait for the PHY to become IDLE + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno (-ETIMEDOUT) on failure. + * + ****************************************************************************/ + +static int sam_phywait(struct sam_gmac_s *priv) +{ + volatile unsigned int retries; + + /* Loop for the configured number of attempts */ + + for (retries = 0; retries < PHY_RETRY_MAX; retries++) + { + /* Is the PHY IDLE */ + + if ((sam_getreg(priv, SAM_GMAC_NSR) & GMAC_NSR_IDLE) != 0) + { + return OK; + } + } + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Function: sam_phyreset + * + * Description: + * Reset the PHY + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_phyreset(struct sam_gmac_s *priv) +{ + uint32_t regval; + uint16_t mcr; + int timeout; + int ret; + + nllvdbg(" sam_phyreset\n"); + + /* Enable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Reset the PHY */ + + ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, MII_MCR_RESET); + if (ret < 0) + { + nlldbg("ERROR: sam_phywrite failed: %d\n", ret); + } + + /* Wait for the PHY reset to complete */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < 10; timeout++) + { + mcr = MII_MCR_RESET; + int result = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr); + if (result < 0) + { + nlldbg("ERROR: Failed to read the MCR register: %d\n", ret); + ret = result; + } + else if ((mcr & MII_MCR_RESET) == 0) + { + ret = OK; + break; + } + } + + /* Disable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + return ret; +} + +/**************************************************************************** + * Function: sam_phyfind + * + * Description: + * Verify the PHY address and, if it is bad, try to one that works. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_phyfind(struct sam_gmac_s *priv, uint8_t *phyaddr) +{ + uint32_t regval; + uint16_t phyval; + uint8_t candidate; + unsigned int offset; + int ret = -ESRCH; + + nllvdbg("Find a valid PHY address\n"); + + /* Enable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + candidate = *phyaddr; + + /* Check current candidate address */ + + ret = sam_phyread(priv, candidate, MII_PHYID1, &phyval); + if (ret == OK && phyval == MII_OUI_MSB) + { + *phyaddr = candidate; + ret = OK; + } + + /* The current address does not work... try another */ + + else + { + nlldbg("ERROR: sam_phyread failed for PHY address %02x: %d\n", + candidate, ret); + + for (offset = 0; offset < 32; offset++) + { + /* Get the next candidate PHY address */ + + candidate = (candidate + 1) & 0x1f; + + /* Try reading the PHY ID from the candidate PHY address */ + + ret = sam_phyread(priv, candidate, MII_PHYID1, &phyval); + if (ret == OK && phyval == MII_OUI_MSB) + { + ret = OK; + break; + } + } + } + + if (ret == OK) + { + nllvdbg(" PHYID1: %04x PHY addr: %d\n", phyval, candidate); + *phyaddr = candidate; + sam_phyread(priv, candidate, CONFIG_SAMA5_GMAC_PHYSR, &phyval); + nllvdbg(" PHYSR: %04x PHY addr: %d\n", phyval, candidate); + } + + /* Disable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + return ret; +} + +/**************************************************************************** + * Function: sam_phyread + * + * Description: + * Read a PHY register. + * + * Parameters: + * priv - A reference to the private driver state structure + * phyaddr - The PHY device address + * regaddr - The PHY register address + * phyval - The location to return the 16-bit PHY register value. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_phyread(struct sam_gmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t *phyval) +{ + uint32_t regval; + int ret; + + /* Make sure that the PHY is idle */ + + ret = sam_phywait(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_phywait failed: %d\n", ret); + return ret; + } + + /* Write the PHY Maintenance register */ + + regval = GMAC_MAN_DATA(0) | GMAC_MAN_WTN | GMAC_MAN_REGA(regaddr) | + GMAC_MAN_PHYA(priv->phyaddr) | GMAC_MAN_READ; + sam_putreg(priv, SAM_GMAC_MAN, regval); + + /* Wait until the PHY is again idle */ + + ret = sam_phywait(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_phywait failed: %d\n", ret); + return ret; + } + + /* Return data */ + + *phyval = (uint16_t)(sam_getreg(priv, SAM_GMAC_MAN) & GMAC_MAN_DATA_MASK); + return OK; +} + +/**************************************************************************** + * Function: sam_phywrite + * + * Description: + * Write to a PHY register. + * + * Parameters: + * priv - A reference to the private driver state structure + * phyaddr - The PHY device address + * regaddr - The PHY register address + * phyval - The 16-bit value to write to the PHY register. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_phywrite(struct sam_gmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t phyval) +{ + uint32_t regval; + int ret; + + /* Make sure that the PHY is idle */ + + ret = sam_phywait(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_phywait failed: %d\n", ret); + return ret; + } + + /* Write the PHY Maintenance register */ + + regval = GMAC_MAN_DATA(phyval) | GMAC_MAN_WTN | GMAC_MAN_REGA(regaddr) | + GMAC_MAN_PHYA(priv->phyaddr) | GMAC_MAN_WRITE; + sam_putreg(priv, SAM_GMAC_MAN, regval); + + /* Wait until the PHY is again IDLE */ + + ret = sam_phywait(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_phywait failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Function: sam_autonegotiate + * + * Description: + * Autonegotiate speed and duplex. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int sam_autonegotiate(struct sam_gmac_s *priv) +{ + uint32_t regval; + uint16_t phyid1; + uint16_t phyid2; + uint16_t mcr; + uint16_t msr; + uint16_t advertise; + uint16_t lpa; + int timeout; + int ret; + + /* Enable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Verify tht we can read the PHYID register */ + + ret = sam_phyread(priv, priv->phyaddr, MII_PHYID1, &phyid1); + if (ret < 0) + { + nlldbg("ERROR: Failed to read PHYID1\n"); + goto errout; + } + + nllvdbg("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr); + + ret = sam_phyread(priv, priv->phyaddr, MII_PHYID2, &phyid2); + if (ret < 0) + { + nlldbg("ERROR: Failed to read PHYID2\n"); + goto errout; + } + + nllvdbg("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr); + + if (phyid1 == MII_OUI_MSB && + ((phyid2 & MII_PHYID2_OUI) >> 10) == MII_OUI_LSB) + { + nllvdbg(" Vendor Model Number: %04x\n", ((phyid2 >> 4) & 0x3f)); + nllvdbg(" Model Revision Number: %04x\n", (phyid2 & 7)); + } + else + { + nlldbg("ERROR: PHY not recognized\n"); + } + + /* Setup control register */ + + ret = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr); + if (ret < 0) + { + nlldbg("ERROR: Failed to read MCR\n"); + goto errout; + } + + mcr &= ~MII_MCR_ANENABLE; /* Remove autonegotiation enable */ + mcr &= ~(MII_MCR_LOOPBACK | MII_MCR_PDOWN); + mcr |= MII_MCR_ISOLATE; /* Electrically isolate PHY */ + + ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr); + if (ret < 0) + { + nlldbg("ERROR: Failed to write MCR\n"); + goto errout; + } + + /* Set the Auto_negotiation Advertisement Register MII advertising for + * Next page 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3 + */ + + advertise = MII_ADVERTISE_100BASETXFULL | MII_ADVERTISE_100BASETXHALF | + MII_ADVERTISE_10BASETXFULL | MII_ADVERTISE_10BASETXHALF | + MII_ADVERTISE_8023; + + ret = sam_phywrite(priv, priv->phyaddr, MII_ADVERTISE, advertise); + if (ret < 0) + { + nlldbg("ERROR: Failed to write ANAR\n"); + goto errout; + } + + /* Read and modify control register */ + + ret = sam_phyread(priv, priv->phyaddr, MII_MCR, &mcr); + if (ret < 0) + { + nlldbg("ERROR: Failed to read MCR\n"); + goto errout; + } + + mcr |= (MII_MCR_SPEED100 | MII_MCR_ANENABLE | MII_MCR_FULLDPLX); + ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr); + if (ret < 0) + { + nlldbg("ERROR: Failed to write MCR\n"); + goto errout; + } + + /* Restart Auto_negotiation */ + + mcr |= MII_MCR_ANRESTART; + mcr &= ~MII_MCR_ISOLATE; + + ret = sam_phywrite(priv, priv->phyaddr, MII_MCR, mcr); + if (ret < 0) + { + nlldbg("ERROR: Failed to write MCR\n"); + goto errout; + } + + nllvdbg(" MCR: %04x\n", mcr); + + /* Check AutoNegotiate complete */ + + timeout = 0; + for (;;) + { + ret = sam_phyread(priv, priv->phyaddr, MII_MSR, &msr); + if (ret < 0) + { + nlldbg("ERROR: Failed to read MSR\n"); + goto errout; + } + + /* Completed successfully? */ + + if ((msr & MII_MSR_ANEGCOMPLETE) != 0) + { + /* Yes.. break out of the loop */ + + nllvdbg("AutoNegotiate complete\n"); + break; + } + + /* No.. check for a timeout */ + + if (++timeout >= PHY_RETRY_MAX) + { + nlldbg("ERROR: TimeOut\n"); + sam_phydump(priv); + ret = -ETIMEDOUT; + goto errout; + } + } + + /* Get the AutoNeg Link partner base page */ + + ret = sam_phyread(priv, priv->phyaddr, MII_LPA, &lpa); + if (ret < 0) + { + nlldbg("ERROR: Failed to read ANLPAR\n"); + goto errout; + } + + /* Setup the GMAC link speed */ + + regval = sam_getreg(priv, SAM_GMAC_NCFGR); + regval &= (GMAC_NCFGR_SPD | GMAC_NCFGR_FD); + + if (((advertise & lpa) & MII_ADVERTISE_100BASETXFULL) != 0) + { + /* Set MII for 100BaseTX and Full Duplex */ + + regval |= (GMAC_NCFGR_SPD | GMAC_NCFGR_FD); + } + else if (((advertise & lpa) & MII_ADVERTISE_10BASETXFULL) != 0) + { + /* Set MII for 10BaseT and Full Duplex */ + + regval |= GMAC_NCFGR_FD; + } + else if (((advertise & lpa) & MII_ADVERTISE_100BASETXHALF) != 0) + { + /* Set MII for 100BaseTX and half Duplex */ + + regval |= GMAC_NCFGR_SPD; + } +#if 0 + else if (((advertise & lpa) & MII_ADVERTISE_10BASETXHALF) != 0) + { + /* set MII for 10BaseT and half Duplex */ + } +#endif + + sam_putreg(priv, SAM_GMAC_NCFGR, regval); + + /* Select RMII/MII */ + + regval = sam_getreg(priv, SAM_GMAC_UR); +#ifdef CONFIG_SAMA5_GMAC_RGMII + regval |= GMAC_UR_RGMII; +#else /* defined(CONFIG_SAMA5_GMAC_GMII) */ + regval &= ~GMAC_UR_RGMII; +#endif + sam_putreg(priv, SAM_GMAC_UR, regval); + +errout: + /* Disable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + return ret; +} + +/**************************************************************************** + * Function: sam_linkup + * + * Description: + * Check if the link is up + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * true: The link is up + * + ****************************************************************************/ + +static bool sam_linkup(struct sam_gmac_s *priv) +{ + uint32_t regval; + uint16_t msr; + uint16_t physr; + bool linkup = false; + int ret; + + /* Enable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + ret = sam_phyread(priv, priv->phyaddr, MII_MSR, &msr); + if (ret < 0) + { + nlldbg("ERROR: Failed to read MSR: %d\n", ret); + goto errout; + } + + if ((msr & MII_MSR_LINKSTATUS) == 0) + { + nlldbg("ERROR: MSR LinkStatus: %04x\n", msr); + goto errout; + } + + /* Re-configure Link speed */ + + ret = sam_phyread(priv, priv->phyaddr, CONFIG_SAMA5_GMAC_PHYSR, &physr); + if (ret < 0) + { + nlldbg("ERROR: Failed to read PHYSR: %d\n", ret); + goto errout; + } + + regval = sam_getreg(priv, SAM_GMAC_NCFGR); + regval &= ~(GMAC_NCFGR_SPD | GMAC_NCFGR_FD); + + if ((msr & MII_MSR_100BASETXFULL) != 0 && PHYSR_IS100FDX(physr)) + { + /* Set GMAC for 100BaseTX and Full Duplex */ + + regval |= (GMAC_NCFGR_SPD | GMAC_NCFGR_FD); + } + else if ((msr & MII_MSR_10BASETXFULL) != 0 && PHYSR_IS10FDX(physr)) + { + /* Set MII for 10BaseT and Full Duplex */ + + regval |= GMAC_NCFGR_FD; + } + + else if ((msr & MII_MSR_100BASETXHALF) != 0 && PHYSR_IS100HDX(physr)) + { + /* Set MII for 100BaseTX and Half Duplex */ + + regval |= GMAC_NCFGR_SPD; + } + +#if 0 + else if ((msr & MII_MSR_10BASETXHALF) != 0 && PHYSR_IS10HDX(physr)) + { + /* Set MII for 10BaseT and Half Duplex */ + } +#endif + + sam_putreg(priv, SAM_GMAC_NCFGR, regval); + + /* Start the GMAC transfers */ + + nllvdbg("Link is up\n"); + linkup = true; + +errout: + /* Disable management port */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_MPE; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + return linkup; +} + +/**************************************************************************** + * Function: sam_phyinit + * + * Description: + * Configure the PHY and determine the link speed/duplex. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int sam_phyinit(struct sam_gmac_s *priv) +{ + uint32_t regval; + int ret; + + /* Configure PHY clocking */ + + regval = sam_getreg(priv, SAM_GMAC_NCFGR); + regval &= ~GMAC_NCFGR_CLK_MASK; + +#if BOARD_MCK_FREQUENCY > (160*1000*1000) +# error Supported MCK frequency +#elif BOARD_MCK_FREQUENCY > (80*1000*1000) + regval |= GMAC_NCFGR_CLK_DIV64; /* MCK divided by 64 (MCK up to 160 MHz) */ +#elif BOARD_MCK_FREQUENCY > (40*1000*1000) + regval |= GMAC_NCFGR_CLK_DIV32; /* MCK divided by 32 (MCK up to 80 MHz) */ +#elif BOARD_MCK_FREQUENCY > (20*1000*1000) + regval |= GMAC_NCFGR_CLK_DIV16; /* MCK divided by 16 (MCK up to 40 MHz) */ +#else + regval |= GMAC_NCFGR_CLK_DIV8; /* MCK divided by 8 (MCK up to 20 MHz) */ +#endif + + sam_putreg(priv, SAM_GMAC_NCFGR, regval); + + /* Check the PHY Address */ + + priv->phyaddr = CONFIG_SAMA5_GMAC_PHYADDR; + ret = sam_phyfind(priv, &priv->phyaddr); + if (ret < 0) + { + nlldbg("ERROR: sam_phyfind failed: %d\n", ret); + return ret; + } + + if (priv->phyaddr != CONFIG_SAMA5_GMAC_PHYADDR) + { + sam_phyreset(priv); + } + + return OK; +} + +/**************************************************************************** + * Function: sam_ethgpioconfig + * + * Description: + * Configure GPIOs for the GMAC interface. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static inline void sam_ethgpioconfig(struct sam_gmac_s *priv) +{ + /* Configure PIO pins to support GMAC */ + /* Configure GMAC PIO pins common to both MII and RMII */ +#warning REVISIT: Do we need all of these? + sam_configpio(PIO_GMAC_TX0); + sam_configpio(PIO_GMAC_TX1); +#if 0 + sam_configpio(PIO_GMAC_TX2); + sam_configpio(PIO_GMAC_TX3); + sam_configpio(PIO_GMAC_TX4); + sam_configpio(PIO_GMAC_TX5); + sam_configpio(PIO_GMAC_TX6); + sam_configpio(PIO_GMAC_TX7); +#endif + sam_configpio(PIO_GMAC_RX0); + sam_configpio(PIO_GMAC_RX1); +#if 0 + sam_configpio(PIO_GMAC_RX2); + sam_configpio(PIO_GMAC_RX3); + sam_configpio(PIO_GMAC_RX4); + sam_configpio(PIO_GMAC_RX5); + sam_configpio(PIO_GMAC_RX6); + sam_configpio(PIO_GMAC_RX7); +#endif + sam_configpio(PIO_GMAC_TXEN); + sam_configpio(PIO_GMAC_TXER); + sam_configpio(PIO_GMAC_TXCK); + sam_configpio(PIO_GMAC_RXCK); + sam_configpio(PIO_GMAC_RXDV); + sam_configpio(PIO_GMAC_RXER); + sam_configpio(PIO_GMAC_125CK); + sam_configpio(PIO_GMAC_125CKO); + sam_configpio(PIO_GMAC_COL); + sam_configpio(PIO_GMAC_CRS); + sam_configpio(PIO_GMAC_MDC); + sam_configpio(PIO_GMAC_MDIO); +} + +/**************************************************************************** + * Function: sam_txreset + * + * Description: + * Reset the transmit logic + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void sam_txreset(struct sam_gmac_s *priv) +{ + uint8_t *txbuffer = priv->txbuffer; + struct gmac_txdesc_s *txdesc = priv->txdesc; + uintptr_t bufaddr; + uint32_t physaddr; + uint32_t regval; + int ndx; + + /* Disable TX */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_TXEN; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Configure the TX descriptors. */ + + priv->txhead = 0; + priv->txtail = 0; + + for (ndx = 0; ndx < CONFIG_SAMA5_GMAC_NTXBUFFERS; ndx++) + { + bufaddr = (uint32_t)(&(txbuffer[ndx * GMAC_TX_UNITSIZE])); + + /* Set the buffer address and mark the descriptor as in used by firmware */ + + physaddr = sam_physramaddr(bufaddr); + txdesc[ndx].addr = physaddr; + txdesc[ndx].status = GMACTXD_STA_USED; + } + + /* Mark the final descriptor in the list */ + + txdesc[CONFIG_SAMA5_GMAC_NTXBUFFERS - 1].status = + GMACTXD_STA_USED | GMACTXD_STA_WRAP; + + /* Set the Transmit Buffer Queue Base Register */ + + physaddr = sam_physramaddr((uintptr_t)txdesc); + sam_putreg(priv, SAM_GMAC_TBQB, physaddr); +} + +/**************************************************************************** + * Function: sam_rxreset + * + * Description: + * Reset the receive logic + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void sam_rxreset(struct sam_gmac_s *priv) +{ + struct gmac_rxdesc_s *rxdesc = priv->rxdesc; + uint8_t *rxbuffer = priv->rxbuffer; + uint32_t bufaddr; + uint32_t physaddr; + uint32_t regval; + int ndx; + + /* Disable RX */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval &= ~GMAC_NCR_RXEN; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Configure the RX descriptors. */ + + priv->rxndx = 0; + for (ndx = 0; ndx < CONFIG_SAMA5_GMAC_NRXBUFFERS; ndx++) + { + bufaddr = (uintptr_t)(&(rxbuffer[ndx * GMAC_RX_UNITSIZE])); + DEBUGASSERT((bufaddr & ~GMACRXD_ADDR_MASK) == 0); + + /* Set the buffer address and remove GMACRXD_ADDR_OWNER and + * GMACRXD_ADDR_WRAP. + */ + + physaddr = sam_physramaddr(bufaddr); + rxdesc[ndx].addr = physaddr; + rxdesc[ndx].status = 0; + } + + /* Mark the final descriptor in the list */ + + rxdesc[CONFIG_SAMA5_GMAC_NRXBUFFERS - 1].addr |= GMACRXD_ADDR_WRAP; + + /* Flush the entire RX descriptor table to RAM */ + + cp15_clean_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + + CONFIG_SAMA5_GMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s)); + + /* Set the Receive Buffer Queue Base Register */ + + physaddr = sam_physramaddr((uintptr_t)rxdesc); + sam_putreg(priv, SAM_GMAC_RBQB, physaddr); +} + +/**************************************************************************** + * Function: sam_gmac_reset + * + * Description: + * Reset the GMAC block. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void sam_gmac_reset(struct sam_gmac_s *priv) +{ + uint32_t regval; + + /* Disable all GMAC interrupts */ + + sam_putreg(priv, SAM_GMAC_IDR, GMAC_INT_ALL); + + /* Reset RX and TX logic */ + + sam_rxreset(priv); + sam_txreset(priv); + + /* Disable RX, TX, and statistics */ + + regval = GMAC_NCR_TXEN | GMAC_NCR_RXEN | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Disable clocking to the GMAC peripheral */ + + sam_gmac_disableclk(); +} + +/**************************************************************************** + * Function: sam_macaddress + * + * Description: + * Configure the selected MAC address. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static void sam_macaddress(struct sam_gmac_s *priv) +{ + struct uip_driver_s *dev = &priv->dev; + uint32_t regval; + + nllvdbg("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->d_ifname, + dev->d_mac.ether_addr_octet[0], dev->d_mac.ether_addr_octet[1], + dev->d_mac.ether_addr_octet[2], dev->d_mac.ether_addr_octet[3], + dev->d_mac.ether_addr_octet[4], dev->d_mac.ether_addr_octet[5]); + + /* Set the MAC address */ + + regval = (uint32_t)dev->d_mac.ether_addr_octet[0] | + (uint32_t)dev->d_mac.ether_addr_octet[1] << 8 | + (uint32_t)dev->d_mac.ether_addr_octet[2] << 16 | + (uint32_t)dev->d_mac.ether_addr_octet[3] << 24; + sam_putreg(priv, SAM_GMAC_SAB1, regval); + + regval = (uint32_t)dev->d_mac.ether_addr_octet[4] | + (uint32_t)dev->d_mac.ether_addr_octet[5] << 8; + sam_putreg(priv, SAM_GMAC_SAT1, regval); +} + +/**************************************************************************** + * Function: sam_gmac_configure + * + * Description: + * Configure the GMAC interface for normal operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int sam_gmac_configure(struct sam_gmac_s *priv) +{ + uint32_t regval; + + nllvdbg("Entry\n"); + + /* Enable clocking to the GMAC peripheral */ + + sam_gmac_enableclk(); + + /* Disable TX, RX, interrupts, etc. */ + + sam_putreg(priv, SAM_GMAC_NCR, 0); + sam_putreg(priv, SAM_GMAC_IDR, GMAC_INT_ALL); + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= GMAC_NCR_CLRSTAT; + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Clear all status bits in the receive status register. */ + + regval = (GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA); + sam_putreg(priv, SAM_GMAC_RSR, regval); + + /* Clear all status bits in the transmit status register */ + + regval = (GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE | GMAC_TSR_TFC | + GMAC_TSR_TXCOMP | GMAC_TSR_UND); + sam_putreg(priv, SAM_GMAC_TSR, regval); + + /* Clear any pending interrupts */ + + (void)sam_getreg(priv, SAM_GMAC_ISR); + + /* Enable/disable the copy of data into the buffers, ignore broadcasts. + * Don't copy FCS. + */ + + regval = sam_getreg(priv, SAM_GMAC_NCFGR); + regval |= (GMAC_NCFGR_RFCS | GMAC_NCFGR_PEN); + +#ifdef CONFIG_NET_PROMISCUOUS + regval |= GMAC_NCFGR_CAF; +#else + regval &= ~GMAC_NCFGR_CAF; +#endif + +#ifdef CONFIG_SAMA5_GMAC_NBC + regval |= GMAC_NCFGR_NBC; +#else + regval &= ~GMAC_NCFGR_NBC; +#endif + + sam_putreg(priv, SAM_GMAC_NCFGR, regval); + + /* Reset TX and RX */ + + sam_rxreset(priv); + sam_txreset(priv); + + /* Enable Rx and Tx, plus the stats register. */ + + regval = sam_getreg(priv, SAM_GMAC_NCR); + regval |= (GMAC_NCR_RXEN | GMAC_NCR_TXEN | GMAC_NCR_WESTAT); + sam_putreg(priv, SAM_GMAC_NCR, regval); + + /* Setup the interrupts for TX events, RX events, and error events */ + + regval = (GMAC_INT_RCOMP | GMAC_INT_RXUBR | GMAC_INT_TUR | GMAC_INT_RLEX | + GMAC_INT_TFC | GMAC_INT_TCOMP | GMAC_INT_ROVR | GMAC_INT_HRESP | + GMAC_INT_PFNZ | GMAC_INT_PTZ); + sam_putreg(priv, SAM_GMAC_IER, regval); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: sam_gmac_initialize + * + * Description: + * Initialize the GMAC driver. + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * Called very early in the initialization sequence. + * + ****************************************************************************/ + +int sam_gmac_initialize(void) +{ + struct sam_gmac_s *priv = &g_gmac; + int ret; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct sam_gmac_s)); + priv->dev.d_ifup = sam_ifup; /* I/F up (new IP address) callback */ + priv->dev.d_ifdown = sam_ifdown; /* I/F down callback */ + priv->dev.d_txavail = sam_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_IGMP + priv->dev.d_addmac = sam_addmac; /* Add multicast MAC address */ + priv->dev.d_rmmac = sam_rmmac; /* Remove multicast MAC address */ +#endif + priv->dev.d_private = (void*)&g_gmac; /* Used to recover private state from dev */ + + /* Create a watchdog for timing polling for and timing of transmisstions */ + + priv->txpoll = wd_create(); + if (!priv->txpoll) + { + nlldbg("ERROR: Failed to create periodic poll timer\n"); + ret = -EAGAIN; + goto errout; + } + + priv->txtimeout = wd_create(); /* Create TX timeout timer */ + if (!priv->txpoll) + { + nlldbg("ERROR: Failed to create periodic poll timer\n"); + ret = -EAGAIN; + goto errout_with_txpoll; + } + + /* Configure PIO pins to support GMAC */ + + sam_ethgpioconfig(priv); + + /* Allocate buffers */ + + ret = sam_buffer_initialize(priv); + if (ret < 0) + { + nlldbg("ERROR: sam_buffer_initialize failed: %d\n", ret); + goto errout_with_txtimeout; + } + + /* Attach the IRQ to the driver. It will not be enabled at the AIC until + * the interface is in the 'up' state. + */ + + ret = irq_attach(SAM_IRQ_GMAC, sam_gmac_interrupt); + if (ret < 0) + { + nlldbg("ERROR: Failed to attach the handler to the IRQ%d\n", SAM_IRQ_GMAC); + goto errout_with_buffers; + } + + /* Enable clocking to the GMAC peripheral (just for sam_ifdown()) */ + + sam_gmac_enableclk(); + + /* Put the interface in the down state (disabling clocking again). */ + + ret = sam_ifdown(&priv->dev); + if (ret < 0) + { + nlldbg("ERROR: Failed to put the interface in the down state: %d\n", ret); + goto errout_with_buffers; + } + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + ret = netdev_register(&priv->dev); + if (ret >= 0) + { + return ret; + } + + nlldbg("ERROR: netdev_register() failed: %d\n", ret); + +errout_with_buffers: + sam_buffer_free(priv); +errout_with_txtimeout: + wd_delete(priv->txtimeout); +errout_with_txpoll: + wd_delete(priv->txpoll); +errout: + return ret; +} + +#endif /* CONFIG_NET && CONFIG_SAMA5_GMAC */