diff --git a/arch/risc-v/src/mpfs/Kconfig b/arch/risc-v/src/mpfs/Kconfig index a4cd8a6c3a2..14898d3045d 100755 --- a/arch/risc-v/src/mpfs/Kconfig +++ b/arch/risc-v/src/mpfs/Kconfig @@ -244,6 +244,26 @@ config MPFS_EMMCSD ---help--- Selects the MPFS eMMCSD driver. +config MPFS_ETHMAC + bool + default n + select NETDEVICES + select ARCH_HAVE_PHY + +config MPFS_ETHMAC_0 + bool "Ethernet MAC 0" + default n + select MPFS_ETHMAC + ---help--- + Enable MPFS ethernet MAC-0. + +config MPFS_ETHMAC_1 + bool "Ethernet MAC 1" + default n + select MPFS_ETHMAC + ---help--- + Enable MPFS ethernet MAC-1. + comment "CorePWM Options" config MPFS_HAVE_COREPWM @@ -317,10 +337,149 @@ config MPFS_DMA ---help--- Enable DMA Support. MPFS DMA is Memory-to-Memory only. -menu "MPFS Others" +menu "Ethernet MAC configuration" + depends on MPFS_ETHMAC + +choice + prompt "MII Interface mode" + +config MPFS_MAC_SGMII + bool "SGMII" + ---help--- + Use Ethernet SGMII interface. + +config MPFS_MAC_GMII + bool "GMII" + ---help--- + Use Ethernet GMII interface. +endchoice + +config MPFS_PHYADDR + int "PHY address" + default 1 + ---help--- + The 5-bit address of the PHY on the board. Default: 1 + +config MPFS_MAC_NO_BROADCAST + bool "Disable Broadcast" + default n + ---help--- + Select to disable receipt of broadcast packets. + +config MPFS_MAC_AUTONEG + bool "Use autonegotiation" + default y + ---help--- + Use PHY autonegotiation to determine speed and mode + +config MPFS_MAC_AUTONEG_DISABLE_1000MBPS + bool "Disable Gigabit mode" + default n + depends on MPFS_MAC_AUTONEG + ---help--- + Select to disable Gigabit speed support. + If disabled then autonegotiation don't advertise 1GB mode + +config MPFS_PHYINIT + bool "Use board phyinit" + default n + ---help--- + call mpfs_phy_boardinitialize() on init + +if !MPFS_MAC_AUTONEG + +config MPFS_MAC_ETHFD + bool "Full duplex" + default n + ---help--- + If MPFS_MAC_AUTONEG is not defined, then this may be defined to + select full duplex mode. Default: half-duplex + +choice + prompt "MAC Speed" + default MPFS_MAC_ETH100MBPS + ---help--- + If autonegotiation is not used, then you must select the fixed speed + of the PHY + +config MPFS_MAC_ETH10MBPS + bool "10 Mbps" + ---help--- + If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 10 MBps + speed. Default: 100 Mbps + +config MPFS_MAC_ETH100MBPS + bool "100 Mbps" + ---help--- + If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 100 MBps + speed. Default: 100 Mbps + +config MPFS_MAC_ETH1000MBPS + bool "1000 Mbps" + ---help--- + If MPFS_MAC_AUTONEG is not defined, then this may be defined to select 1000 MBps + speed. Default: 100 Mbps + +endchoice # GMAC speed + +endif # !MPFS_MAC_AUTONEG + +config MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ + int "MDC Clock Source (Hz)" + default 125000000 + range 1000000 240000000 + depends on MPFS_ETHMAC + ---help--- + Select the MDC clock source frequency. + +config MPFS_ETHMAC_NTXBUFFERS + int "Number of TX DMA Buffers" + default 8 + depends on MPFS_ETHMAC + ---help--- + Select the number of TX DMA buffers. + +config MPFS_ETHMAC_NRXBUFFERS + int "Number of RX DMA Buffers" + default 16 + depends on MPFS_ETHMAC + ---help--- + Select the number of RX DMA buffers. + +config MPFS_ETHMAC_64BIT_ADDRESS_MODE + bool "64-bit DMA address mode" + default n + depends on MPFS_ETHMAC + ---help--- + Select to enable 64-bit DMA address mode. This is only needed if the + Nuttx memory address is above 4GB. + +config MPFS_ETHMAC_REGDEBUG + bool "Register-Level Debug" + default n + depends on DEBUG_NET_INFO + ---help--- + Enable very low-level register access debug. Depends on + CONFIG_DEBUG_FEATURES. + +config MPFS_MPU_DMA_ENABLE + bool "Enable ALL memory access for eth DMA" + default n + ---help--- + Set ALL memory accessible for Ethernet DMA. + This should be done on bootloader. This is mostly for testing only. endmenu +config MPFS_HAVE_CORERMII + bool "CoreRMII FPGA IP block configured" + default n + +config MPFS_CORERMII_ADDRESS + int "CoreRMII Phy address" + default 1 + depends on MPFS_HAVE_CORERMII + # Override the default values for MPU / PMP parameters here config ARCH_MPU_MIN_BLOCK_SIZE diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index 827e27466af..460d5c7d62b 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -93,6 +93,10 @@ ifeq ($(CONFIG_MPFS_EMMCSD),y) CHIP_CSRCS += mpfs_emmcsd.c endif +ifeq ($(CONFIG_MPFS_ETHMAC),y) +CHIP_CSRCS += mpfs_ethernet.c +endif + ifeq (${CONFIG_MPFS_HAVE_COREPWM},y) CHIP_CSRCS += mpfs_corepwm.c endif diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h b/arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h new file mode 100755 index 00000000000..e98271696bb --- /dev/null +++ b/arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h @@ -0,0 +1,719 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/hardware/mpfs_ethernet.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H +#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/mpfs_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define NETWORK_CONTROL 0x0000 +#define NETWORK_CONFIG 0x0004 +#define NETWORK_STATUS 0x0008 +#define DMA_CONFIG 0x0010 +#define TRANSMIT_STATUS 0x0014 +#define RECEIVE_Q_PTR 0x0018 +#define TRANSMIT_Q_PTR 0x001C +#define RECEIVE_STATUS 0x0020 +#define INT_STATUS 0x0024 +#define INT_ENABLE 0x0028 +#define INT_DISABLE 0x002C +#define INT_MASK 0x0030 +#define PHY_MANAGEMENT 0x0034 +#define PAUSE_TIME 0x0038 +#define TX_PAUSE_QUANTUM 0x003C +#define PBUF_TXCUTTHRU 0x0040 +#define PBUF_RXCUTTHRU 0x0044 +#define JUMBO_MAX_LENGTH 0x0048 +#define AXI_MAX_PIPELINE 0x0054 +#define RSC_CONTROL 0x0058 +#define INT_MODERATION 0x005C +#define SYS_WAKE_TIME 0x0060 +#define LOCKUP_CONFIG 0x0068 +#define MAC_LOCKUP_TIME 0x006C +#define LOCKUP_CONFIG3 0x0070 +#define RX_WATER_MARK 0x007C +#define HASH_BOTTOM 0x0080 +#define HASH_TOP 0x0084 +#define SPEC_ADD1_BOTTOM 0x0088 +#define SPEC_ADD1_TOP 0x008C +#define SPEC_ADD2_BOTTOM 0x0090 +#define SPEC_ADD2_TOP 0x0094 +#define SPEC_ADD3_BOTTOM 0x0098 +#define SPEC_ADD3_TOP 0x009C +#define SPEC_ADD4_BOTTOM 0x00A0 +#define SPEC_ADD4_TOP 0x00A4 +#define SPEC_TYPE1 0x00A8 +#define SPEC_TYPE2 0x00AC +#define SPEC_TYPE3 0x00B0 +#define SPEC_TYPE4 0x00B4 +#define WOL_REGISTER 0x00B8 +#define STRETCH_RATIO 0x00BC +#define STACKED_VLAN 0x00C0 +#define TX_PFC_PAUSE 0x00C4 +#define MASK_ADD1_BOTTOM 0x00C8 +#define MASK_ADD1_TOP 0x00CC +#define DMA_ADDR_OR_MASK 0x00D0 +#define RX_PTP_UNICAST 0x00D4 +#define TX_PTP_UNICAST 0x00D8 +#define TSU_NSEC_CMP 0x00DC +#define TSU_SEC_CMP 0x00E0 +#define TSU_MSB_SEC_CMP 0x00E4 +#define TSU_PTP_TX_MSB_SEC 0x00E8 +#define TSU_PTP_RX_MSB_SEC 0x00EC +#define TSU_PEER_TX_MSB_SEC 0x00F0 +#define TSU_PEER_RX_MSB_SEC 0x00F4 +#define DPRAM_FILL_DBG 0x00F8 +#define REVISION_REG 0x00FC +#define OCTETS_TXED_BOTTOM 0x0100 +#define OCTETS_TXED_TOP 0x0104 +#define FRAMES_TXED_OK 0x0108 +#define BROADCAST_TXED 0x010C +#define MULTICAST_TXED 0x0110 +#define PAUSE_FRAMES_TXED 0x0114 +#define FRAMES_TXED_64 0x0118 +#define FRAMES_TXED_65 0x011C +#define FRAMES_TXED_128 0x0120 +#define FRAMES_TXED_256 0x0124 +#define FRAMES_TXED_512 0x0128 +#define FRAMES_TXED_1024 0x012C +#define FRAMES_TXED_1519 0x0130 +#define TX_UNDERRUNS 0x0134 +#define SINGLE_COLLISIONS 0x0138 +#define MULTIPLE_COLLISIONS 0x013C +#define EXCESSIVE_COLLISIONS 0x0140 +#define LATE_COLLISIONS 0x0144 +#define DEFERRED_FRAMES 0x0148 +#define CRS_ERRORS 0x014C +#define OCTETS_RXED_BOTTOM 0x0150 +#define OCTETS_RXED_TOP 0x0154 +#define FRAMES_RXED_OK 0x0158 +#define BROADCAST_RXED 0x015C +#define MULTICAST_RXED 0x0160 +#define PAUSE_FRAMES_RXED 0x0164 +#define FRAMES_RXED_64 0x0168 +#define FRAMES_RXED_65 0x016C +#define FRAMES_RXED_128 0x0170 +#define FRAMES_RXED_256 0x0174 +#define FRAMES_RXED_512 0x0178 +#define FRAMES_RXED_1024 0x017C +#define FRAMES_RXED_1519 0x0180 +#define UNDERSIZE_FRAMES 0x0184 +#define EXCESSIVE_RX_LENGTH 0x0188 +#define RX_JABBERS 0x018C +#define FCS_ERRORS 0x0190 +#define RX_LENGTH_ERRORS 0x0194 +#define RX_SYMBOL_ERRORS 0x0198 +#define ALIGNMENT_ERRORS 0x019C +#define RX_RESOURCE_ERRORS 0x01A0 +#define RX_OVERRUNS 0x01A4 +#define RX_IP_CK_ERRORS 0x01A8 +#define RX_TCP_CK_ERRORS 0x01AC +#define RX_UDP_CK_ERRORS 0x01B0 +#define AUTO_FLUSHED_PKTS 0x01B4 +#define TSU_TIMER_INCR_SUB_NSEC 0x01BC +#define TSU_TIMER_MSB_SEC 0x01C0 +#define TSU_STROBE_MSB_SEC 0x01C4 +#define TSU_STROBE_SEC 0x01C8 +#define TSU_STROBE_NSEC 0x01CC +#define TSU_TIMER_SEC 0x01D0 +#define TSU_TIMER_NSEC 0x01D4 +#define TSU_TIMER_ADJUST 0x01D8 +#define TSU_TIMER_INCR 0x01DC +#define TSU_PTP_TX_SEC 0x01E0 +#define TSU_PTP_TX_NSEC 0x01E4 +#define TSU_PTP_RX_SEC 0x01E8 +#define TSU_PTP_RX_NSEC 0x01EC +#define TSU_PEER_TX_SEC 0x01F0 +#define TSU_PEER_TX_NSEC 0x01F4 +#define TSU_PEER_RX_SEC 0x01F8 +#define TSU_PEER_RX_NSEC 0x01FC +#define PCS_CONTROL 0x0200 +#define PFC_STATUS 0x026C +#define RX_LPI 0x0270 +#define RX_LPI_TIME 0x0274 +#define TX_LPI 0x0278 +#define TX_LPI_TIME 0x027C +#define DESIGNCFG_DEBUG1 0x0280 +#define DESIGNCFG_DEBUG2 0x0284 +#define DESIGNCFG_DEBUG3 0x0288 +#define DESIGNCFG_DEBUG4 0x028C +#define DESIGNCFG_DEBUG5 0x0290 +#define DESIGNCFG_DEBUG6 0x0294 +#define DESIGNCFG_DEBUG7 0x0298 +#define DESIGNCFG_DEBUG8 0x029C +#define DESIGNCFG_DEBUG9 0x02A0 +#define DESIGNCFG_DEBUG10 0x02A4 +#define DESIGNCFG_DEBUG11 0x02A8 +#define DESIGNCFG_DEBUG12 0x02AC +#define AXI_QOS_CFG_0 0x02E0 +#define AXI_QOS_CFG_1 0x02E4 +#define AXI_QOS_CFG_2 0x02E8 +#define AXI_QOS_CFG_3 0x02EC +#define INT_Q1_STATUS 0x0400 +#define INT_Q2_STATUS 0x0404 +#define INT_Q3_STATUS 0x0408 +#define INT_Q4_STATUS 0x040C +#define INT_Q5_STATUS 0x0410 +#define INT_Q6_STATUS 0x0414 +#define INT_Q7_STATUS 0x0418 +#define INT_Q8_STATUS 0x041C +#define INT_Q9_STATUS 0x0420 +#define INT_Q10_STATUS 0x0424 +#define INT_Q11_STATUS 0x0428 +#define INT_Q12_STATUS 0x042C +#define INT_Q13_STATUS 0x0430 +#define INT_Q14_STATUS 0x0434 +#define INT_Q15_STATUS 0x0438 +#define TRANSMIT_Q1_PTR 0x0440 +#define TRANSMIT_Q2_PTR 0x0444 +#define TRANSMIT_Q3_PTR 0x0448 +#define TRANSMIT_Q4_PTR 0x044C +#define TRANSMIT_Q5_PTR 0x0450 +#define TRANSMIT_Q6_PTR 0x0454 +#define TRANSMIT_Q7_PTR 0x0458 +#define TRANSMIT_Q8_PTR 0x045C +#define TRANSMIT_Q9_PTR 0x0460 +#define TRANSMIT_Q10_PTR 0x0464 +#define TRANSMIT_Q11_PTR 0x0468 +#define TRANSMIT_Q12_PTR 0x046C +#define TRANSMIT_Q13_PTR 0x0470 +#define TRANSMIT_Q14_PTR 0x0474 +#define TRANSMIT_Q15_PTR 0x0478 +#define RECEIVE_Q1_PTR 0x0480 +#define RECEIVE_Q2_PTR 0x0484 +#define RECEIVE_Q3_PTR 0x0488 +#define RECEIVE_Q4_PTR 0x048C +#define RECEIVE_Q5_PTR 0x0490 +#define RECEIVE_Q6_PTR 0x0494 +#define RECEIVE_Q7_PTR 0x0498 +#define DMA_RXBUF_SIZE_Q1 0x04A0 +#define DMA_RXBUF_SIZE_Q2 0x04A4 +#define DMA_RXBUF_SIZE_Q3 0x04A8 +#define DMA_RXBUF_SIZE_Q4 0x04AC +#define DMA_RXBUF_SIZE_Q5 0x04B0 +#define DMA_RXBUF_SIZE_Q6 0x04B4 +#define DMA_RXBUF_SIZE_Q7 0x04B8 +#define CBS_CONTROL 0x04BC +#define CBS_IDLESLOPE_Q_A 0x04C0 +#define CBS_IDLESLOPE_Q_B 0x04C4 +#define UPPER_TX_Q_BASE_ADDR 0x04C8 +#define TX_BD_CONTROL 0x04CC +#define RX_BD_CONTROL 0x04D0 +#define UPPER_RX_Q_BASE_ADDR 0x04D4 +#define WD_COUNTER 0x04EC +#define AXI_TX_FULL_THRESH0 0x04F8 +#define AXI_TX_FULL_THRESH1 0x04FC +#define SCREENING_TYPE_1_REGISTER_0 0x0500 +#define SCREENING_TYPE_1_REGISTER_1 0x0504 +#define SCREENING_TYPE_1_REGISTER_2 0x0508 +#define SCREENING_TYPE_1_REGISTER_3 0x050C +#define SCREENING_TYPE_1_REGISTER_4 0x0510 +#define SCREENING_TYPE_1_REGISTER_5 0x0514 +#define SCREENING_TYPE_1_REGISTER_6 0x0518 +#define SCREENING_TYPE_1_REGISTER_7 0x051C +#define SCREENING_TYPE_1_REGISTER_8 0x0520 +#define SCREENING_TYPE_1_REGISTER_9 0x0524 +#define SCREENING_TYPE_1_REGISTER_10 0x0528 +#define SCREENING_TYPE_1_REGISTER_11 0x052C +#define SCREENING_TYPE_1_REGISTER_12 0x0530 +#define SCREENING_TYPE_1_REGISTER_13 0x0534 +#define SCREENING_TYPE_1_REGISTER_14 0x0538 +#define SCREENING_TYPE_1_REGISTER_15 0x053C +#define SCREENING_TYPE_2_REGISTER_0 0x0540 +#define SCREENING_TYPE_2_REGISTER_1 0x0544 +#define SCREENING_TYPE_2_REGISTER_2 0x0548 +#define SCREENING_TYPE_2_REGISTER_3 0x054C +#define SCREENING_TYPE_2_REGISTER_4 0x0550 +#define SCREENING_TYPE_2_REGISTER_5 0x0554 +#define SCREENING_TYPE_2_REGISTER_6 0x0558 +#define SCREENING_TYPE_2_REGISTER_7 0x055C +#define SCREENING_TYPE_2_REGISTER_8 0x0560 +#define SCREENING_TYPE_2_REGISTER_9 0x0564 +#define SCREENING_TYPE_2_REGISTER_10 0x0568 +#define SCREENING_TYPE_2_REGISTER_11 0x056C +#define SCREENING_TYPE_2_REGISTER_12 0x0570 +#define SCREENING_TYPE_2_REGISTER_13 0x0574 +#define SCREENING_TYPE_2_REGISTER_14 0x0578 +#define SCREENING_TYPE_2_REGISTER_15 0x057C +#define TX_SCHED_CTRL 0x0580 +#define BW_RATE_LIMIT_Q0TO3 0x0590 +#define BW_RATE_LIMIT_Q4TO7 0x0594 +#define BW_RATE_LIMIT_Q8TO11 0x0598 +#define BW_RATE_LIMIT_Q12TO15 0x059C +#define TX_Q_SEG_ALLOC_Q_LOWER 0x05A0 +#define TX_Q_SEG_ALLOC_Q_UPPER 0x05A4 +#define RECEIVE_Q8_PTR 0x05C0 +#define RECEIVE_Q9_PTR 0x05C4 +#define RECEIVE_Q10_PTR 0x05C8 +#define RECEIVE_Q11_PTR 0x05CC +#define RECEIVE_Q12_PTR 0x05D0 +#define RECEIVE_Q13_PTR 0x05D4 +#define RECEIVE_Q14_PTR 0x05D8 +#define RECEIVE_Q15_PTR 0x05DC +#define DMA_RXBUF_SIZE_Q8 0x05E0 +#define DMA_RXBUF_SIZE_Q9 0x05E4 +#define DMA_RXBUF_SIZE_Q10 0x05E8 +#define DMA_RXBUF_SIZE_Q11 0x05EC +#define DMA_RXBUF_SIZE_Q12 0x05F0 +#define DMA_RXBUF_SIZE_Q13 0x05F4 +#define DMA_RXBUF_SIZE_Q14 0x05F8 +#define DMA_RXBUF_SIZE_Q15 0x05FC +#define INT_Q1_ENABLE 0x0600 +#define INT_Q2_ENABLE 0x0604 +#define INT_Q3_ENABLE 0x0608 +#define INT_Q4_ENABLE 0x060C +#define INT_Q5_ENABLE 0x0610 +#define INT_Q6_ENABLE 0x0614 +#define INT_Q7_ENABLE 0x0618 +#define INT_Q1_DISABLE 0x0620 +#define INT_Q2_DISABLE 0x0624 +#define INT_Q3_DISABLE 0x0628 +#define INT_Q4_DISABLE 0x062C +#define INT_Q5_DISABLE 0x0630 +#define INT_Q6_DISABLE 0x0634 +#define INT_Q7_DISABLE 0x0638 +#define INT_Q1_MASK 0x0640 +#define INT_Q2_MASK 0x0644 +#define INT_Q3_MASK 0x0648 +#define INT_Q4_MASK 0x064C +#define INT_Q5_MASK 0x0650 +#define INT_Q6_MASK 0x0654 +#define INT_Q7_MASK 0x0658 +#define INT_Q8_ENABLE 0x0660 +#define INT_Q9_ENABLE 0x0664 +#define INT_Q10_ENABLE 0x0668 +#define INT_Q11_ENABLE 0x066C +#define INT_Q12_ENABLE 0x0670 +#define INT_Q13_ENABLE 0x0674 +#define INT_Q14_ENABLE 0x0678 +#define INT_Q15_ENABLE 0x067C +#define INT_Q8_DISABLE 0x0680 +#define INT_Q9_DISABLE 0x0684 +#define INT_Q10_DISABLE 0x0688 +#define INT_Q11_DISABLE 0x068C +#define INT_Q12_DISABLE 0x0690 +#define INT_Q13_DISABLE 0x0694 +#define INT_Q14_DISABLE 0x0698 +#define INT_Q15_DISABLE 0x069C +#define INT_Q8_MASK 0x06A0 +#define INT_Q9_MASK 0x06A4 +#define INT_Q10_MASK 0x06A8 +#define INT_Q11_MASK 0x06AC +#define INT_Q12_MASK 0x06B0 +#define INT_Q13_MASK 0x06B4 +#define INT_Q14_MASK 0x06B8 +#define INT_Q15_MASK 0x06BC +#define SCREENING_TYPE_2_ETHERTYPE_REG_0 0x06E0 +#define SCREENING_TYPE_2_ETHERTYPE_REG_1 0x06E4 +#define SCREENING_TYPE_2_ETHERTYPE_REG_2 0x06E8 +#define SCREENING_TYPE_2_ETHERTYPE_REG_3 0x06EC +#define SCREENING_TYPE_2_ETHERTYPE_REG_4 0x06F0 +#define SCREENING_TYPE_2_ETHERTYPE_REG_5 0x06F4 +#define SCREENING_TYPE_2_ETHERTYPE_REG_6 0x06F8 +#define SCREENING_TYPE_2_ETHERTYPE_REG_7 0x06FC +#define TYPE2_COMPARE_0_WORD_0 0x0700 +#define TYPE2_COMPARE_0_WORD_1 0x0704 +#define TYPE2_COMPARE_1_WORD_0 0x0708 +#define TYPE2_COMPARE_1_WORD_1 0x070C +#define TYPE2_COMPARE_2_WORD_0 0x0710 +#define TYPE2_COMPARE_2_WORD_1 0x0714 +#define TYPE2_COMPARE_3_WORD_0 0x0718 +#define TYPE2_COMPARE_3_WORD_1 0x071C +#define TYPE2_COMPARE_4_WORD_0 0x0720 +#define TYPE2_COMPARE_4_WORD_1 0x0724 +#define TYPE2_COMPARE_5_WORD_0 0x0728 +#define TYPE2_COMPARE_5_WORD_1 0x072C +#define TYPE2_COMPARE_6_WORD_0 0x0730 +#define TYPE2_COMPARE_6_WORD_1 0x0734 +#define TYPE2_COMPARE_7_WORD_0 0x0738 +#define TYPE2_COMPARE_7_WORD_1 0x073C +#define TYPE2_COMPARE_8_WORD_0 0x0740 +#define TYPE2_COMPARE_8_WORD_1 0x0744 +#define TYPE2_COMPARE_9_WORD_0 0x0748 +#define TYPE2_COMPARE_9_WORD_1 0x074C +#define TYPE2_COMPARE_10_WORD_0 0x0750 +#define TYPE2_COMPARE_10_WORD_1 0x0754 +#define TYPE2_COMPARE_11_WORD_0 0x0758 +#define TYPE2_COMPARE_11_WORD_1 0x075C +#define TYPE2_COMPARE_12_WORD_0 0x0760 +#define TYPE2_COMPARE_12_WORD_1 0x0764 +#define TYPE2_COMPARE_13_WORD_0 0x0768 +#define TYPE2_COMPARE_13_WORD_1 0x076C +#define TYPE2_COMPARE_14_WORD_0 0x0770 +#define TYPE2_COMPARE_14_WORD_1 0x0774 +#define TYPE2_COMPARE_15_WORD_0 0x0778 +#define TYPE2_COMPARE_15_WORD_1 0x077C +#define TYPE2_COMPARE_16_WORD_0 0x0780 +#define TYPE2_COMPARE_16_WORD_1 0x0784 +#define TYPE2_COMPARE_17_WORD_0 0x0788 +#define TYPE2_COMPARE_17_WORD_1 0x078C +#define TYPE2_COMPARE_18_WORD_0 0x0790 +#define TYPE2_COMPARE_18_WORD_1 0x0794 +#define TYPE2_COMPARE_19_WORD_0 0x0798 +#define TYPE2_COMPARE_19_WORD_1 0x079C +#define TYPE2_COMPARE_20_WORD_0 0x07A0 +#define TYPE2_COMPARE_20_WORD_1 0x07A4 +#define TYPE2_COMPARE_21_WORD_0 0x07A8 +#define TYPE2_COMPARE_21_WORD_1 0x07AC +#define TYPE2_COMPARE_22_WORD_0 0x07B0 +#define TYPE2_COMPARE_22_WORD_1 0x07B4 +#define TYPE2_COMPARE_23_WORD_0 0x07B8 +#define TYPE2_COMPARE_23_WORD_1 0x07BC +#define TYPE2_COMPARE_24_WORD_0 0x07C0 +#define TYPE2_COMPARE_24_WORD_1 0x07C4 +#define TYPE2_COMPARE_25_WORD_0 0x07C8 +#define TYPE2_COMPARE_25_WORD_1 0x07CC +#define TYPE2_COMPARE_26_WORD_0 0x07D0 +#define TYPE2_COMPARE_26_WORD_1 0x07D4 +#define TYPE2_COMPARE_27_WORD_0 0x07D8 +#define TYPE2_COMPARE_27_WORD_1 0x07DC +#define TYPE2_COMPARE_28_WORD_0 0x07E0 +#define TYPE2_COMPARE_28_WORD_1 0x07E4 +#define TYPE2_COMPARE_29_WORD_0 0x07E8 +#define TYPE2_COMPARE_29_WORD_1 0x07EC +#define TYPE2_COMPARE_30_WORD_0 0x07F0 +#define TYPE2_COMPARE_30_WORD_1 0x07F4 +#define TYPE2_COMPARE_31_WORD_0 0x07F8 +#define TYPE2_COMPARE_31_WORD_1 0x07FC +#define ENST_START_TIME_Q8 0x0800 +#define ENST_START_TIME_Q9 0x0804 +#define ENST_START_TIME_Q10 0x0808 +#define ENST_START_TIME_Q11 0x080C +#define ENST_START_TIME_Q12 0x0810 +#define ENST_START_TIME_Q13 0x0814 +#define ENST_START_TIME_Q14 0x0818 +#define ENST_START_TIME_Q15 0x081C +#define ENST_ON_TIME_Q8 0x0820 +#define ENST_ON_TIME_Q9 0x0824 +#define ENST_ON_TIME_Q10 0x0828 +#define ENST_ON_TIME_Q11 0x082C +#define ENST_ON_TIME_Q12 0x0830 +#define ENST_ON_TIME_Q13 0x0834 +#define ENST_ON_TIME_Q14 0x0838 +#define ENST_ON_TIME_Q15 0x083C +#define ENST_OFF_TIME_Q8 0x0840 +#define ENST_OFF_TIME_Q9 0x0844 +#define ENST_OFF_TIME_Q10 0x0848 +#define ENST_OFF_TIME_Q11 0x084C +#define ENST_OFF_TIME_Q12 0x0850 +#define ENST_OFF_TIME_Q13 0x0854 +#define ENST_OFF_TIME_Q14 0x0858 +#define ENST_OFF_TIME_Q15 0x085C +#define ENST_CONTROL 0x0880 +#define RX_Q0_FLUSH 0x0B00 +#define RX_Q1_FLUSH 0x0B04 +#define RX_Q2_FLUSH 0x0B08 +#define RX_Q3_FLUSH 0x0B0C +#define RX_Q4_FLUSH 0x0B10 +#define RX_Q5_FLUSH 0x0B14 +#define RX_Q6_FLUSH 0x0B18 +#define RX_Q7_FLUSH 0x0B1C +#define RX_Q8_FLUSH 0x0B20 +#define RX_Q9_FLUSH 0x0B24 +#define RX_Q10_FLUSH 0x0B28 +#define RX_Q11_FLUSH 0x0B2C +#define RX_Q12_FLUSH 0x0B30 +#define RX_Q13_FLUSH 0x0B34 +#define RX_Q14_FLUSH 0x0B38 +#define RX_Q15_FLUSH 0x0B3C +#define SCR2_REG0_RATE_LIMIT 0x0B40 +#define SCR2_REG1_RATE_LIMIT 0x0B44 +#define SCR2_REG2_RATE_LIMIT 0x0B48 +#define SCR2_REG3_RATE_LIMIT 0x0B4C +#define SCR2_REG4_RATE_LIMIT 0x0B50 +#define SCR2_REG5_RATE_LIMIT 0x0B54 +#define SCR2_REG6_RATE_LIMIT 0x0B58 +#define SCR2_REG7_RATE_LIMIT 0x0B5C +#define SCR2_REG8_RATE_LIMIT 0x0B60 +#define SCR2_REG9_RATE_LIMIT 0x0B64 +#define SCR2_REG10_RATE_LIMIT 0x0B68 +#define SCR2_REG11_RATE_LIMIT 0x0B6C +#define SCR2_REG12_RATE_LIMIT 0x0B70 +#define SCR2_REG13_RATE_LIMIT 0x0B74 +#define SCR2_REG14_RATE_LIMIT 0x0B78 +#define SCR2_REG15_RATE_LIMIT 0x0B7C +#define SCR2_RATE_STATUS 0x0B80 +#define ASF_INT_STATUS 0x0E00 +#define ASF_INT_RAW_STATUS 0x0E04 +#define ASF_INT_MASK 0x0E08 +#define ASF_INT_TEST 0x0E0C +#define ASF_FATAL_NONFATAL_SELECT 0x0E10 +#define ASF_TRANS_TO_FAULT_MASK 0x0E34 +#define ASF_TRANS_TO_FAULT_STATUS 0x0E38 +#define ASF_PROTOCOL_FAULT_MASK 0x0E40 +#define ASF_PROTOCOL_FAULT_STATUS 0x0E44 + +/* Register bit field definitions *******************************************/ + +/* NETWORK_CONTROL: + * The network control register contains general MAC control functions + * for both receiver and transmitter. + */ + +#define NETWORK_CONTROL_LOOPBACK (1 << 0) +#define NETWORK_CONTROL_LOOPBACK_LOCAL (1 << 1) +#define NETWORK_CONTROL_ENABLE_RECEIVE (1 << 2) +#define NETWORK_CONTROL_ENABLE_TRANSMIT (1 << 3) +#define NETWORK_CONTROL_MAN_PORT_EN (1 << 4) +#define NETWORK_CONTROL_CLEAR_ALL_STATS_REGS (1 << 5) +#define NETWORK_CONTROL_INC_ALL_STATS_REGS (1 << 6) +#define NETWORK_CONTROL_STATS_WRITE_EN (1 << 7) +#define NETWORK_CONTROL_BACK_PRESSURE (1 << 8) +#define NETWORK_CONTROL_TRANSMIT_START (1 << 9) +#define NETWORK_CONTROL_TRANSMIT_HALT (1 << 10) +#define NETWORK_CONTROL_TX_PAUSE_FRAME_REQ (1 << 11) +#define NETWORK_CONTROL_TX_PAUSE_FRAME_ZERO (1 << 12) +#define NETWORK_CONTROL_STORE_RX_TS (1 << 15) +#define NETWORK_CONTROL_PFC_ENABLE (1 << 16) +#define NETWORK_CONTROL_TRANSMIT_PFC_PRIORITY_BASED_PAUSE_FRAME (1 << 17) +#define NETWORK_CONTROL_FLUSH_RX_PKT_PCLK (1 << 18) +#define NETWORK_CONTROL_TX_LPI_EN (1 << 19) +#define NETWORK_CONTROL_PTP_UNICAST_ENA (1 << 20) +#define NETWORK_CONTROL_ALT_SGMII_MODE (1 << 21) +#define NETWORK_CONTROL_STORE_UDP_OFFSET (1 << 22) +#define NETWORK_CONTROL_EXT_TSU_PORT_ENABLE (1 << 23) +#define NETWORK_CONTROL_ONE_STEP_SYNC_MODE (1 << 24) +#define NETWORK_CONTROL_PFC_CTRL (1 << 25) +#define NETWORK_CONTROL_EXT_RXQ_SEL_EN (1 << 26) +#define NETWORK_CONTROL_OSS_CORRECTION_FIELD (1 << 27) +#define NETWORK_CONTROL_SEL_MII_ON_RGMII (1 << 28) +#define NETWORK_CONTROL_TWO_PT_FIVE_GIG (1 << 29) +#define NETWORK_CONTROL_IFG_EATS_QAV_CREDIT (1 << 30) + +/* NETWORK_CONFIG: + * The network configuration register contains functions for + * setting the mode of operation for the Gigabit Ethernet MAC. + */ + +#define NETWORK_CONFIG_SPEED (1 << 0) +#define NETWORK_CONFIG_FULL_DUPLEX (1 << 1) +#define NETWORK_CONFIG_DISCARD_NON_VLAN_FRAMES (1 << 2) +#define NETWORK_CONFIG_JUMBO_FRAMES (1 << 3) +#define NETWORK_CONFIG_COPY_ALL_FRAMES (1 << 4) +#define NETWORK_CONFIG_NO_BROADCAST (1 << 5) +#define NETWORK_CONFIG_MULTICAST_HASH_ENABLE (1 << 6) +#define NETWORK_CONFIG_UNICAST_HASH_ENABLE (1 << 7) +#define NETWORK_CONFIG_RECEIVE_1536_BYTE_FRAMES (1 << 8) +#define NETWORK_CONFIG_EXTERNAL_ADDRESS_MATCH_ENABLE (1 << 9) +#define NETWORK_CONFIG_GIGABIT_MODE_ENABLE (1 << 10) +#define NETWORK_CONFIG_PCS_SELECT (1 << 11) +#define NETWORK_CONFIG_RETRY_TEST (1 << 12) +#define NETWORK_CONFIG_PAUSE_ENABLE (1 << 13) +#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_MASK (3) +#define NETWORK_CONFIG_RECEIVE_BUFFER_OFFSET_SHIFT (14) +#define NETWORK_CONFIG_LENGTH_FIELD_ERROR_FRAME_DISCARD (1 << 16) +#define NETWORK_CONFIG_FCS_REMOVE (1 << 17) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT (18) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK (7) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8 (0 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16 (1 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32 (2 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48 (3 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64 (4 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96 (5 << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT) +#define NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT (21) +#define NETWORK_CONFIG_DATA_BUS_WIDTH_MASK (3) +#define NETWORK_CONFIG_DISABLE_COPY_OF_PAUSE_FRAMES (1 << 23) +#define NETWORK_CONFIG_RECEIVE_CHECKSUM_OFFLOAD_ENABLE (1 << 24) +#define NETWORK_CONFIG_EN_HALF_DUPLEX_RX (1 << 25) +#define NETWORK_CONFIG_IGNORE_RX_FCS (1 << 26) +#define NETWORK_CONFIG_SGMII_MODE_ENABLE (1 << 27) +#define NETWORK_CONFIG_IPG_STRETCH_ENABLE (1 << 28) +#define NETWORK_CONFIG_NSP_CHANGE (1 << 29) +#define NETWORK_CONFIG_IGNORE_IPG_RX_ER (1 << 30) +#define NETWORK_CONFIG_UNI_DIRECTION_ENABLE (1 << 31) + +/* NETWORK_STATUS: + * The network status register returns status information with respect + * to the PHY management MDIO interface, the PCS, priority flow control, + * LPI and other status. + */ + +#define NETWORK_STATUS_PCS_LINK_STATE (1 << 0) +#define NETWORK_STATUS_MDIO_IN (1 << 1) +#define NETWORK_STATUS_MAN_DONE (1 << 2) +#define NETWORK_STATUS_MAC_FULL_DUPLEX (1 << 3) +#define NETWORK_STATUS_MAC_PAUSE_RX_EN (1 << 4) +#define NETWORK_STATUS_MAC_PAUSE_TX_EN (1 << 5) +#define NETWORK_STATUS_PFC_NEGOTIATE_PCLK (1 << 6) +#define NETWORK_STATUS_LPI_INDICATE_PCLK (1 << 7) +#define NETWORK_STATUS_AXI_XACTION_OUTSTANDING (1 << 8) +#define NETWORK_STATUS_LINK_FAULT_INDICATION_SHIFT (1 << 9) +#define NETWORK_STATUS_LINK_FAULT_INDICATION_MASK (7) + +/* DMA_CONFIG: + * DMA Configuration Register + */ + +#define DMA_CONFIG_AMBA_BURST_LENGTH_SHIFT (0) /* Bits: 0-4 */ +#define DMA_CONFIG_AMBA_BURST_LENGTH_MASK (0x1f) +#define DMA_CONFIG_HDR_DATA_SPLITTING_EN (1 << 5) /* Bit 5 */ +#define DMA_CONFIG_ENDIAN_SWAP_MANAGEMENT (1 << 6) /* Bit 6 */ +#define DMA_CONFIG_ENDIAN_SWAP_PACKET (1 << 7) /* Bit 7 */ +#define DMA_CONFIG_RX_PBUF_SIZE_SHIFT (8) /* Bits: 8-9 */ +#define DMA_CONFIG_RX_PBUF_SIZE_MASK (3) +#define DMA_CONFIG_TX_PBUF_SIZE (1 << 10) /* Bit 10 */ +#define DMA_CONFIG_TX_PBUF_TCP_EN (1 << 11) /* Bit 11 */ +#define DMA_CONFIG_INFINITE_LAST_DBUF_SIZE_EN (1 << 12) /* Bit 12 */ +#define DMA_CONFIG_CRC_ERROR_REPORT (1 << 13) /* Bit 13 */ +#define DMA_CONFIG_RX_BUF_SIZE_MASK (0xff) +#define DMA_CONFIG_RX_BUF_SIZE_SHIFT (16) +#define DMA_CONFIG_FORCE_DISCARD_ON_ERR (1 << 24) /* Bit 24 */ +#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_RX (1 << 25) /* Bit 25 */ +#define DMA_CONFIG_FORCE_MAX_AMBA_BURST_TX (1 << 26) /* Bit 26 */ + /* Bit 27 reserved */ +#define DMA_CONFIG_RX_BD_EXTENDED_MODE_EN (1 << 28) /* Bit 28 */ +#define DMA_CONFIG_TX_BD_EXTENDED_MODE_EN (1 << 29) /* Bit 29 */ +#define DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1 (1 << 30) /* Bit 30 */ + /* Bit 31 reserved */ + +/* TRANSMIT_STATUS: + * This register, when read, provides details of the status + * of the transmit path. Once read, individual bits may be cleared + * by writing 1 to them. It is not possible to set a bit to 1 by writing + * to the register. + */ + +#define TRANSMIT_STATUS_USED_BIT_READ (1 << 0) +#define TRANSMIT_STATUS_COLLISION_OCCURED (1 << 1) +#define TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED (1 << 2) +#define TRANSMIT_STATUS_TRANSMIT_GO (1 << 3) +#define TRANSMIT_STATUS_AMBA_ERROR (1 << 4) +#define TRANSMIT_STATUS_TRANSMIT_COMPLETE (1 << 5) +#define TRANSMIT_STATUS_UNDERRUN (1 << 6) +#define TRANSMIT_STATUS_LATE_COLLISION (1 << 7) +#define TRANSMIT_STATUS_RESP_NOT_OK (1 << 8) +#define TRANSMIT_STATUS_TX_MAC_LOCKUP (1 << 9) +#define TRANSMIT_STATUS_TX_DMA_LOCKUP (1 << 10) + +/* RECEIVE_STATUS: + * This register, when read, provides details of the status + * of the receive path. Once read, individual bits may be cleared + * by writing 1 to them. It is not possible to set a bit to 1 by writing + * to the register. + */ +#define RECEIVE_STATUS_BUFFER_NOT_AVAILABLE (1 << 0) +#define RECEIVE_STATUS_FRAME_RECEIVED (1 << 1) +#define RECEIVE_STATUS_RECEIVE_OVERRUN (1 << 2) +#define RECEIVE_STATUS_RESP_NOT_OK (1 << 3) +#define RECEIVE_STATUS_RX_MAC_LOCKUP (1 << 4) +#define RECEIVE_STATUS_RX_DMA_LOCKUP (1 << 5) + +/* PHY_MANAGEMENT: + */ + +#define PHY_MANAGEMENT_PHY_DATA_SHIFT (0) +#define PHY_MANAGEMENT_PHY_DATA_MASK (0xffff) +#define PHY_MANAGEMENT_PHY_DATA(n) ((n & PHY_MANAGEMENT_PHY_DATA_MASK) << PHY_MANAGEMENT_PHY_DATA_SHIFT) +#define PHY_MANAGEMENT_WRITE10 (2 << PHY_MANAGEMENT_WRITE10_SHIFT) +#define PHY_MANAGEMENT_WRITE10_SHIFT (16) +#define PHY_MANAGEMENT_WRITE10_MASK (3) +#define PHY_MANAGEMENT_REG_ADDRESS_SHIFT (18) +#define PHY_MANAGEMENT_REG_ADDRESS_MASK (0x1f) +#define PHY_MANAGEMENT_REG_ADDRESS(n) ((n & PHY_MANAGEMENT_REG_ADDRESS_MASK ) << PHY_MANAGEMENT_REG_ADDRESS_SHIFT) +#define PHY_MANAGEMENT_PHY_ADDRESS_SHIFT (23) +#define PHY_MANAGEMENT_PHY_ADDRESS_MASK (0x1f) +#define PHY_MANAGEMENT_PHY_ADDRESS(n) ((n & PHY_MANAGEMENT_PHY_ADDRESS_MASK ) << PHY_MANAGEMENT_PHY_ADDRESS_SHIFT) +#define PHY_MANAGEMENT_OPERATION_SHIFT (28) +#define PHY_MANAGEMENT_OPERATION_MASK (3) +# define PHY_MANAGEMENT_OPERATION_READ (2 << PHY_MANAGEMENT_OPERATION_SHIFT) +# define PHY_MANAGEMENT_OPERATION_WRITE (1 << PHY_MANAGEMENT_OPERATION_SHIFT) +#define PHY_MANAGEMENT_WRITE_1 (1 << 30) +#define PHY_MANAGEMENT_WRITE_0 (1 << 31) + +/* irq status, enable and disable */ + +#define GEM_INT_MANAGEMENT_DONE (1 << 0) +#define GEM_INT_RECEIVE_COMPLETE (1 << 1) +#define GEM_INT_RECEIVE_BUFFER_USED_BIT_READ (1 << 2) +#define GEM_INT_TRANSMIT_BUFFER_USED_BIT_READ (1 << 3) +#define GEM_INT_TRANSMIT_BUFFER_UNDERRUN (1 << 4) +#define GEM_INT_RETRY_LIMIT_EXCEEDED (1 << 5) +#define GEM_INT_AMBA_ERROR (1 << 6) +#define GEM_INT_TRANSMIT_COMPLETE (1 << 7) +#define GEM_INT_RECEIVE_OVERRUN (1 << 10) +#define GEM_INT_RESP_NOT_OK (1 << 11) + +/* PCS_CONTROL: */ + +#define PCS_CONTROL_SPEED_SELECT_BIT_0 (1 << 6) +#define PCS_CONTROL_COLLISION_TEST (1 << 7) +#define PCS_CONTROL_COLLISION_TEST (1 << 7) +#define PCS_CONTROL_MAC_DUPLEX_STATE (1 << 8) +#define PCS_CONTROL_RESTART_AUTO_NEG (1 << 9) +#define PCS_CONTROL_RESTART_AUTO_NEG (1 << 9) +#define PCS_CONTROL_ENABLE_AUTO_NEG (1 << 12) +#define PCS_CONTROL_SPEED_SELECT_BIT_1 (1 << 13) +#define PCS_CONTROL_LOOPBACK_MODE (1 << 14) +#define PCS_CONTROL_LOOPBACK_MODE (1 << 14) +#define PCS_CONTROL_SOFTWARE_RESET (1 << 15) + +/* Receive buffer descriptor: Address word */ + +#define GEM_RX_DMA_ADDR_OWNER (1 << 0) /* Bit 0: 1=Software owns; 0=GMAC owns */ +#define GEM_RX_DMA_ADDR_WRAP (1 << 1) /* Bit 1: Last descriptor in list */ +#define GEM_RX_DMA_ADDR_MASK (0xfffffffc) /* Bits 2-31: Aligned buffer address */ + +/* Receive buffer descriptor: status word */ + +#define GEM_RX_DMA_STATUS_FRAME_LEN_SHIFT (0) /* Bits 0-12: Frame length */ +#define GEM_RX_DMA_STATUS_FRAME_LEN_MASK (0x00000fff) +#define GEM_RX_DMA_STATUS_SOF (1 << 14) /* Bit 14: Start of frame */ +#define GEM_RX_DMA_STATUS_EOF (1 << 15) /* Bit 15: End of frame */ + +/* Transmit buffer descriptor: status word */ + +#define GEM_TX_DMA_LAST (1 << 15) /* Bit 15: the last buffer in a frame */ +#define GEM_TX_DMA_NO_CRC (1 << 16) /* Bit 16: don't calc and append crc */ + /* Bits: 17 - 19: reserved */ +#define GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT (20) /* Bits: 20 - 22 Transmit checksum generation offload errors */ +#define GEM_TX_DMA_OFFLOAD_ERRORS_MASK (7) +#define GEM_TX_DMA_OFFLOAD_ERROR_OK (0 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_VLAN (1 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_SNAP (2 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_IP (3 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_UNK (4 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_FRAG (5 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_PROTO (6 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_OFFLOAD_ERROR_END (7 << GEM_TX_DMA_OFFLOAD_ERRORS_SHIFT) +#define GEM_TX_DMA_TS_PRESENT (1 << 23) /* Bit: 23 This descriptor holds a valid time stamp which indicates the transmit time. */ + /* Bits: 24 - 25 reserved */ +#define GEM_TX_DMA_LATE_COL_ERROR (1 << 26) /* Bit 26: Late collision transmit error detected. */ +#define GEM_TX_DMA_BUS_ERROR (1 << 27) /* Bit 27: Transmit frame corruption due to AHB/AXI error. */ +#define GEM_TX_DMA_UNDERRUN (1 << 28) /* Bit 28: Transmit underrun */ +#define GEM_TX_DMA_RETRY_ERROR (1 << 29) /* Bit 29: Retry limit exceeded, tx error detected. */ +#define GEM_TX_DMA_WRAP (1 << 30) /* Bit 30: This is the last descriptor in the chain and the DMA controller will next access the first descriptor in the chain. */ +#define GEM_TX_DMA_USED (1 << 31) /* Bit 31: Set to 0 by the driver to indicate buffer contains data to be transmitted. + * Set to 1 by the GEM once packet or packet fragment has been sent from the buffer. */ + +#define GEM_DMA_STATUS_LENGTH_MASK 0x3fffu + +#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_ETHERNET_H */ diff --git a/arch/risc-v/src/mpfs/mpfs_ethernet.c b/arch/risc-v/src/mpfs/mpfs_ethernet.c new file mode 100644 index 00000000000..0cd010e43d4 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_ethernet.c @@ -0,0 +1,3823 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_ethernet.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#endif + +#include "riscv_internal.h" +#include "hardware/mpfs_ethernet.h" +#include "mpfs_memorymap.h" + +#if defined(CONFIG_NET) && defined(CONFIG_MPFS_ETHMAC) + +#ifndef OK +# define OK 0 +#endif + +#define MPFS_PMPCFG_ETH0_0 (MPFS_MPUCFG_BASE + 0x400) +#define MPFS_PMPCFG_ETH0_1 (MPFS_MPUCFG_BASE + 0x408) +#define MPFS_PMPCFG_ETH0_2 (MPFS_MPUCFG_BASE + 0x410) +#define MPFS_PMPCFG_ETH0_3 (MPFS_MPUCFG_BASE + 0x418) +#define MPFS_PMPCFG_ETH1_0 (MPFS_MPUCFG_BASE + 0x500) +#define MPFS_PMPCFG_ETH1_1 (MPFS_MPUCFG_BASE + 0x508) +#define MPFS_PMPCFG_ETH1_2 (MPFS_MPUCFG_BASE + 0x510) +#define MPFS_PMPCFG_ETH1_3 (MPFS_MPUCFG_BASE + 0x518) + +#if defined(CONFIG_MPFS_ETHMAC_0) && defined(CONFIG_MPFS_ETHMAC_1) +# warning "Using 2 MACs is not yet supported." +# define MPFS_NETHERNET (2) +#else +# define MPFS_NETHERNET (1) +#endif + +#define MPFS_MAC_QUEUE_COUNT (4) + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +#else + +/* Select work queue */ + +# if defined(CONFIG_MPFS_ETHMAC_HPWORK) +# define ETHWORK HPWORK +# elif defined(CONFIG_MPFS_ETHMAC_LPWORK) +# define ETHWORK LPWORK +# else +# define ETHWORK LPWORK +# endif +#endif + +#ifndef CONFIG_MPFS_PHYADDR +# error "CONFIG_MPFS_PHYADDR must be defined in the NuttX configuration" +#endif + +#if !defined(CONFIG_MPFS_MAC_SGMII) && !defined(CONFIG_MPFS_MAC_GMII) +# warning "Neither CONFIG_MPFS_MAC_SGMII nor CONFIG_MPFS_MAC_GMII defined" +#endif + +#if defined(CONFIG_MPFS_MAC_SGMII) && defined(CONFIG_MPFS_MAC_GMII) +# error "Both CONFIG_MPFS_MAC_SGMII and CONFIG_MPFS_MAC_GMII defined" +#endif + +/* Number of buffers for RX */ + +#ifndef CONFIG_MPFS_ETHMAC_NRXBUFFERS +# define CONFIG_MPFS_ETHMAC_NRXBUFFERS (16) +#endif + +/* Number of buffers for TX */ + +#ifndef CONFIG_MPFS_ETHMAC_NTXBUFFERS +# define CONFIG_MPFS_ETHMAC_NTXBUFFERS (8) +#endif + +/* GMAC buffer sizes + * REVISIT: do we want to make GMAC_RX_UNITSIZE configurable? + * issue when using the MTU size receive block + */ + +#define GMAC_RX_UNITSIZE (512) /* Fixed size for RX buffer */ +#define GMAC_TX_UNITSIZE CONFIG_NET_ETH_PKTSIZE /* MAX size for Ethernet packet */ + +/* The MAC can support frame lengths up to 1536 bytes */ + +#define GMAC_MAX_FRAMELEN (1536) +#if CONFIG_NET_ETH_PKTSIZE > GMAC_MAX_FRAMELEN +# error CONFIG_NET_ETH_PKTSIZE is too large +#endif + +/* for DMA dma_rxbuf_size */ + +#define MPFS_MAC_RX_BUF_VALUE ((GMAC_RX_UNITSIZE + 63) / 64) + +/* We need at least one more free buffer than transmit buffers */ + +#define MPFS_GMAC_NFREEBUFFERS (CONFIG_MPFS_ETHMAC_NTXBUFFERS + 1) + +#ifdef CONFIG_NET_DUMPPACKET +# define mpfs_dumppacket(m,a,n) lib_dumpbuffer(m,a,n) +#else +# define mpfs_dumppacket(m,a,n) +#endif + +#ifdef CONFIG_MPFS_HAVE_CORERMII +# define CORE_RMII_RESET (1 << 15) +# define CORE_RMII_LOOPBACK (1 << 2) +# define CORE_RMII_FULL_DUPLEX (1 << 1) +# define CORE_RMII_100MBIT (1 << 0) +#endif + +/* Timing *******************************************************************/ + +/* TX poll delay = 1 seconds. + * CLK_TCK is the number of clock ticks per second + */ + +#define MPFS_WDDELAY (1 * CLK_TCK) + +/* TX timeout = 1 minute */ + +#define MPFS_TXTIMEOUT (60 * CLK_TCK) + +/* PHY reset tim in loop counts */ + +#define PHY_RESET_WAIT_COUNT (10) + +/* PHY re-try coount in loop counts */ + +#define PHY_RETRY_MAX 300000 + +/* This is a helper pointer for accessing the contents of the GMAC header */ + +#define BUF ((struct eth_hdr_s *)priv->dev.d_buf) + +#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16)) +#define lower_32_bits(n) ((uint32_t)(n)) + +/* Interrupt flags */ + +#define INT_RX (GEM_INT_RECEIVE_COMPLETE) +#define INT_TX (GEM_INT_TRANSMIT_COMPLETE) +#define INT_ERRORS (GEM_INT_TRANSMIT_BUFFER_UNDERRUN | \ + GEM_INT_RECEIVE_OVERRUN | \ + GEM_INT_AMBA_ERROR | GEM_INT_RESP_NOT_OK) +#define INT_ALL (INT_RX | INT_TX | INT_ERRORS) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct gmac_rxdesc_s +{ + uint32_t addr; /* Buffer address */ + uint32_t status; /* RX status and controls */ +#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE) + uint32_t addr_hi; /* Buffer address high 32-bits */ + uint32_t unused; +#endif +}; + +/* Transmit buffer descriptor */ + +struct gmac_txdesc_s +{ + uint32_t addr; /* Buffer address */ + uint32_t status; /* TX status and controls */ +#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE) + uint32_t addr_hi; /* Buffer address high 32-bits */ + uint32_t unused; /* */ +#endif +}; + +static uint8_t g_pktbuf[MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE]; + +#if defined(CONFIG_MPFS_GMAC_PREALLOCATE) +static uint8_t g_txbuffer[CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE] + aligned_data(8); +static uint8_t g_rxbuffer[CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE] + aligned_data(8); +#endif + +struct mpfs_mac_queue_s +{ +#if defined(CONFIG_MPFS_GMAC_PREALLOCATE) + struct gmac_txdesc_s tx_desc_tab[CONFIG_MPFS_ETHMAC_NTXBUFFERS]; /* Transmit descriptor table */ + struct gmac_rxdesc_s rx_desc_tab[CONFIG_MPFS_ETHMAC_NRXBUFFERS]; /* Receive descriptor table */ +#else + struct gmac_txdesc_s *tx_desc_tab; /* Transmit descriptor table */ + struct gmac_rxdesc_s *rx_desc_tab; /* Receive descriptor table */ + uint8_t *rxbuffer; /* Allocated RX buffers */ + uint8_t *txbuffer; /* Allocated TX buffers */ +#endif + uint32_t txhead; /* Circular buffer head index */ + uint32_t txtail; /* Circular buffer tail index */ + uint32_t rxndx; /* RX index for current processing RX descriptor */ + volatile uint32_t *int_status; /* interrupt status */ + volatile uint32_t *int_mask; /* interrupt mask */ + volatile uint32_t *int_enable; /* interrupt enable */ + volatile uint32_t *int_disable; /* interrupt disable */ + volatile uint32_t *rx_q_ptr; /* RX queue pointer */ + volatile uint32_t *tx_q_ptr; /* TX queue pointer */ + volatile uint32_t *dma_rxbuf_size; /* RX queue buffer size */ +}; + +/* The mpfs_ethmac_s encapsulates all state information for a single + * hardware interface + */ + +struct mpfs_ethmac_s +{ + uintptr_t regbase; /* mac base address */ + irq_t mac_q_int[MPFS_MAC_QUEUE_COUNT]; /* irq numbers */ + uint8_t ifup : 1; /* true:ifup false:ifdown */ + uint8_t intf; /* Ethernet interface number */ + uint8_t phyaddr; /* PHY address */ + struct wdog_s txpoll; /* TX poll timer */ + struct wdog_s txtimeout; /* TX timeout timer */ + struct work_s irqwork; /* For deferring interrupt work to the work queue */ + struct work_s pollwork; /* For deferring poll work to the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s dev; /* Interface understood by the network */ + struct mpfs_mac_queue_s queue[MPFS_MAC_QUEUE_COUNT]; +}; + +/* These are the pre-allocated Ethernet device structures */ + +static struct mpfs_ethmac_s g_mpfsethmac[MPFS_NETHERNET]; + +static uintptr_t g_regbases[MPFS_NETHERNET] = +{ +#ifdef CONFIG_MPFS_ETHMAC_0 + MPFS_GEM0_LO_BASE, +#endif +#ifdef CONFIG_MPFS_ETHMAC_1 + MPFS_GEM1_LO_BASE, +#endif + /* if support for emacs is added later + * (MPFS_GEM0_LO_BASE + 0x1000), + * (MPFS_GEM1_LO_BASE + 0x1000),' + */ +}; + +static const irq_t g_irq_numbers[MPFS_NETHERNET][MPFS_MAC_QUEUE_COUNT] = +{ +#ifdef CONFIG_MPFS_ETHMAC_0 + { MPFS_IRQ_MAC0_INT, MPFS_IRQ_MAC0_QUEUE1, + MPFS_IRQ_MAC0_QUEUE2, MPFS_IRQ_MAC0_QUEUE3 }, +#endif +#ifdef CONFIG_MPFS_ETHMAC_1 + { MPFS_IRQ_MAC1_INT, MPFS_IRQ_MAC1_QUEUE1, + MPFS_IRQ_MAC1_QUEUE2, MPFS_IRQ_MAC1_QUEUE3 }, +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +static uint16_t mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue); +static uint16_t mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue); +static int mpfs_buffer_initialize(struct mpfs_ethmac_s *priv, + unsigned int queue); +static void mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue); + +static void mpfs_poll_expiry(wdparm_t arg); +static void mpfs_poll_work(void *arg); + +static int mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue); +static int mpfs_txpoll(struct net_driver_s *dev); +static void mpfs_dopoll(struct mpfs_ethmac_s *priv); + +/* NuttX callback functions */ + +static int mpfs_ifup(struct net_driver_s *dev); +static int mpfs_ifdown(struct net_driver_s *dev); + +static void mpfs_txavail_work(void *arg); +static int mpfs_txavail(struct net_driver_s *dev); + +#ifdef CONFIG_NETDEV_PHY_IOCTL +static int mpfs_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static unsigned int mpfs_hashindx(const uint8_t *mac); +static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac); +#endif +#ifdef CONFIG_NET_MCASTGROUP +static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac); +#endif + +#ifdef CONFIG_NETDEV_IOCTL +static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg); +#endif + +/* PHY Initialization */ + +static void mpfs_enablemdio(struct mpfs_ethmac_s *priv); +static void mpfs_disablemdio(struct mpfs_ethmac_s *priv); +static int mpfs_phyreset(struct mpfs_ethmac_s *priv); +static int mpfs_phyinit(struct mpfs_ethmac_s *priv); +static int mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t *phyval); +static int mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t phyval); +static int mpfs_phywait(struct mpfs_ethmac_s *priv); +static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr); +#ifdef CONFIG_MPFS_MAC_AUTONEG +static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv); +#else +static void mpfs_linkspeed(struct mpfs_ethmac_s *priv); +#endif + +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO) +static void mpfs_phydump(struct mpfs_ethmac_s *priv); +#else +# define mpfs_phydump(priv) +#endif + +/* MAC/DMA Initialization */ + +static void mpfs_txreset(struct mpfs_ethmac_s *priv); +static void mpfs_rxreset(struct mpfs_ethmac_s *priv); +static int mpfs_macenable(struct mpfs_ethmac_s *priv); +static int mpfs_ethconfig(struct mpfs_ethmac_s *priv); +static void mpfs_ethreset(struct mpfs_ethmac_s *priv); +#ifdef CONFIG_NET_ICMPv6 +static void mpfs_ipv6multicast(struct sam_gmac_s *priv); +#endif + +static void mpfs_interrupt_work(void *arg); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mac_putreg + * + * Description: + * Write a value to an MAC register + * + * Input Parameters: + * val - The value to write to the register + * offset - The mac register address offset to write + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void mac_putreg(struct mpfs_ethmac_s *priv, + uint32_t offset, uint32_t val) +{ + uint32_t *addr = (uint32_t *)(priv->regbase + offset); +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG) + ninfo("0x%08x <- 0x%08x\n", addr, val); +#endif + putreg32(val, addr); +} + +/**************************************************************************** + * Name: mac_getreg + * + * Description: + * Read a value from an MAC register + * + * Input Parameters: + * priv - + * offset - The register address offset to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline uint32_t mac_getreg(struct mpfs_ethmac_s *priv, + uint32_t offset) +{ + uint32_t *addr = (uint32_t *)(priv->regbase + offset); + uint32_t value = getreg32(addr); +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_MPFS_ETHMAC_REGDEBUG) + ninfo("0x%08x -> 0x%08x\n", addr, value); +#endif + return value; +} + +static int mpfs_interrupt_0(int irq, void *context, void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + uint32_t isr; + UNUSED(irq); + UNUSED(context); + + /* Disable further Ethernet interrupts. */ + + *priv->queue[0].int_disable = 0xffffffff; + isr = *priv->queue[0].int_status; + ninfo("isr=0x%" PRIx32 "\n", isr); + + /* clear all pending... */ + + *priv->queue[0].int_status = isr; + + if ((isr & GEM_INT_TRANSMIT_COMPLETE) != 0) + { + /* If a TX transfer just completed, then cancel the TX timeout */ + + nwarn("TX complete: cancel timeout\n"); + wd_cancel(&priv->txtimeout); + } + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->irqwork, mpfs_interrupt_work, priv, 0); + + return OK; +} + +static int mpfs_interrupt_1(int irq, void *context, FAR void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + UNUSED(priv); + UNUSED(irq); + UNUSED(context); + + ninfo("IRQ-1"); + return 0; +} + +static int mpfs_interrupt_2(int irq, void *context, FAR void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + UNUSED(priv); + UNUSED(irq); + UNUSED(context); + + ninfo("IRQ-2"); + return 0; +} + +static int mpfs_interrupt_3(int irq, void *context, FAR void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + UNUSED(priv); + UNUSED(irq); + UNUSED(context); + + ninfo("IRQ-3"); + return 0; +} + +/**************************************************************************** + * Function: mpfs_txdone + * + * Description: + * An interrupt was received indicating that one or more frames have + * completed transmission. + * + * Input Parameters: + * priv - Reference to the driver state structure + * queue - The queue number that completed + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void mpfs_txdone(struct mpfs_ethmac_s *priv, unsigned int queue) +{ + struct gmac_txdesc_s *txdesc; + + /* Are there any outstanding transmissions? Loop until either (1) all of + * the TX descriptors have been examined, or (2) until we encounter the + * first descriptor that is still in use by the hardware. + */ + + while (priv->queue[queue].txhead != priv->queue[queue].txtail) + { + /* Yes. check the next buffer at the tail of the list */ + + txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txtail]; + + /* Is this TX descriptor done transmitting? + * First TX descriptor in chain has GEM_TX_DMA_USED = 1 + */ + + if ((txdesc->status & GEM_TX_DMA_USED) == 0) + { + break; + } + + /* Increment the tail index */ + + if (++priv->queue[queue].txtail >= CONFIG_MPFS_ETHMAC_NTXBUFFERS) + { + /* Wrap to the beginning of the TX descriptor list */ + + priv->queue[queue].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 descriptors (see comments in mpfs_transmit()). + */ + + *priv->queue[queue].int_enable = INT_RX; + } + + /* Then poll the network for new XMIT data */ + + mpfs_dopoll(priv); +} + +/**************************************************************************** + * Function: mpfs_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. + * + * Input Parameters: + * priv - Reference to the driver state structure + * qi - Queue index number + * + * 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 mpfs_recvframe(struct mpfs_ethmac_s *priv, unsigned int qi) +{ + volatile struct gmac_rxdesc_s *rxdesc; + struct net_driver_s *dev; + uintptr_t addr; + uint8_t *dest; + 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; + dest = dev->d_buf; + pktlen = 0; + rxndx = priv->queue[qi].rxndx; + rxdesc = &priv->queue[qi].rx_desc_tab[rxndx]; + isframe = false; + + ninfo("rxndx: %" PRId32 "\n", rxndx); + + while ((rxdesc->addr & GEM_RX_DMA_ADDR_OWNER) != 0) + { + /* The start of frame bit indicates the beginning of a frame. Discard + * any previous fragments. + */ + + if ((rxdesc->status & GEM_RX_DMA_STATUS_SOF) != 0) + { + /* Skip previous fragments */ + + while (rxndx != priv->queue[qi].rxndx) + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->queue[qi]. + rx_desc_tab[priv->queue[qi].rxndx]; + rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER; + + /* Increment the RX index */ + + if (++priv->queue[qi].rxndx >= CONFIG_MPFS_ETHMAC_NRXBUFFERS) + { + priv->queue[qi].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_MPFS_ETHMAC_NRXBUFFERS) + { + rxndx = 0; + } + + /* Copy data into the packet buffer */ + + if (isframe) + { + if (rxndx == priv->queue[qi].rxndx) + { + nerr("ERROR: No EOF (Invalid or buffers too small)\n"); + do + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->queue[qi]. + rx_desc_tab[priv->queue[qi].rxndx]; + rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER; + + /* Increment the RX index */ + + if (++priv->queue[qi].rxndx + >= CONFIG_MPFS_ETHMAC_NRXBUFFERS) + { + priv->queue[qi].rxndx = 0; + } + } + while (rxndx != priv->queue[qi].rxndx); + return -EIO; + } + + /* Get the number of bytes to copy from the buffer */ + + copylen = GMAC_RX_UNITSIZE; + if ((pktlen + copylen) > CONFIG_NET_ETH_PKTSIZE) + { + copylen = CONFIG_NET_ETH_PKTSIZE - pktlen; + } + + /* And do the copy */ + + addr = (uintptr_t)(rxdesc->addr & GEM_RX_DMA_ADDR_MASK); +#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE) + addr += (uintptr_t)(rxdesc->addr_hi) << 32; +#endif + memcpy(dest, (const void *)addr, copylen); + dest += copylen; + pktlen += copylen; + + /* If the end of frame has been received, return the data */ + + if ((rxdesc->status & GEM_RX_DMA_STATUS_EOF) != 0) + { + /* Frame size from the GMAC */ + + dev->d_len = rxdesc->status & GEM_RX_DMA_STATUS_FRAME_LEN_MASK; + ninfo("packet %d-%" PRId32 " (%d)\n", + priv->queue[qi].rxndx, rxndx, dev->d_len); + + /* All data have been copied in the application frame buffer, + * release the RX descriptor + */ + + while (priv->queue[qi].rxndx != rxndx) + { + /* Give ownership back to the GMAC */ + + rxdesc = &priv->queue[qi]. + rx_desc_tab[priv->queue[qi].rxndx]; + rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER; + + /* Increment the RX index */ + + if (++priv->queue[qi].rxndx + >= CONFIG_MPFS_ETHMAC_NRXBUFFERS) + { + priv->queue[qi].rxndx = 0; + } + } + + /* Check if the device packet buffer was large enough to accept + * all of the data. + */ + + ninfo("rxndx: %d d_len: %d\n", + priv->queue[qi].rxndx, dev->d_len); + + if (pktlen < dev->d_len) + { + nerr("ERROR: Buffer size %d; frame size %" PRId32 "\n", + dev->d_len, pktlen); + return -E2BIG; + } + + return OK; + } + } + + /* We have not encountered the SOF yet... discard this fragment + * and keep looking + */ + + else + { + /* Give ownership back to the GMAC */ + + rxdesc->addr &= ~GEM_RX_DMA_ADDR_OWNER; + priv->queue[qi].rxndx = rxndx; + } + + /* Process the next buffer */ + + rxdesc = &priv->queue[qi].rx_desc_tab[rxndx]; + } + + /* isframe indicates that we have found a SOF. If we've received a SOF, + * but not an EOF in the sequential buffers we own, it must mean that we + * have a partial packet. This should only happen if there was a Buffer + * Not Available (BNA) error. When bursts of data come in, quickly + * filling the available buffers, before our interrupts can even service + * them. Eventually, the ring buffer loops back on itself and the + * peripheral sees it cannot write the next fragment of the packet. + * + * In this case, we keep the rxndx at the start of the last frame, since + * the peripheral will finish writing the packet there next. + */ + + if (!isframe) + { + priv->queue[qi].rxndx = rxndx; + } + + ninfo("rxndx: %d\n", priv->queue[qi].rxndx); + return -EAGAIN; +} + +/**************************************************************************** + * Function: mpfs_receive + * + * Description: + * An interrupt was received indicating the availability of onr or more + * new RX packets in FIFO memory. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void mpfs_receive(struct mpfs_ethmac_s *priv, unsigned int queue) +{ + struct net_driver_s *dev = &priv->dev; + + /* Loop while while mpfs_recvframe() successfully retrieves valid + * GMAC frames. + */ + + while (mpfs_recvframe(priv, queue) == OK) + { + mpfs_dumppacket("Received packet", dev->d_buf, dev->d_len); + + /* Check if the packet is a valid size for the network buffer + * configuration (this should not happen) + */ + + if (dev->d_len > CONFIG_NET_ETH_PKTSIZE) + { + nwarn("WARNING: Dropped, Too big: %d\n", dev->d_len); + continue; + } + + #ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the packet + * tap + */ + + pkt_input(&priv->dev); + #endif + + /* We only accept IP packets of the configured type and ARP packets */ + + #ifdef CONFIG_NET_IPv4 + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + + /* Handle ARP on input then give the IPv4 packet to the network + * layer + */ + + arp_ipin(&priv->dev); + ipv4_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + + #ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) + #endif + { + arp_out(&priv->dev); + } + #ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->dev); + } + #endif + + /* And send the packet */ + + mpfs_transmit(priv, queue); + } + } + else + #endif + + #ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + + #ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(priv->dev.d_flags)) + { + arp_out(&priv->dev); + } + else + #endif + { + neighbor_out(&priv->dev); + } + + /* And send the packet */ + + mpfs_transmit(priv, queue); + } + } + else + #endif + + #ifdef CONFIG_NET_ARP + if (BUF->type == htons(ETHTYPE_ARP)) + { + ninfo("ARP frame\n"); + + /* Handle ARP packet */ + + arp_arpin(&priv->dev); + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a + * value > 0. + */ + + if (priv->dev.d_len > 0) + { + mpfs_transmit(priv, queue); + } + } + else + #endif + { + nwarn("WARNING: Dropped, Unknown type: %04x\n", BUF->type); + } + } + + ninfo("receive done.\n"); +} + +/**************************************************************************** + * Function: mpfs_interrupt_work + * + * Description: + * Perform interrupt related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() was called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void mpfs_interrupt_work(void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + uint32_t isr; + uint32_t rsr; + uint32_t tsr; + uint32_t regval; + uint32_t queue = 0; /* todo: get from arg */ + + /* Process pending Ethernet interrupts */ + + net_lock(); + isr = *priv->queue[queue].int_status; + rsr = mac_getreg(priv, RECEIVE_STATUS); + tsr = mac_getreg(priv, TRANSMIT_STATUS); + + ninfo("isr: %08" PRIx32 "\n", isr); + + /* 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 (tsr != 0) + { + ninfo("TX tsr=0x%X\n", tsr); + uint32_t tx_error = 0; + + /* Check for Retry Limit Exceeded */ + + if ((tsr & TRANSMIT_STATUS_RETRY_LIMIT_EXCEEDED) != 0) + { + ++tx_error; + nerr("ERROR: Retry Limit Exceeded TSR: %08" PRIx32 "\n", tsr); + } + + /* Check Collision Occurred */ + + if ((tsr & TRANSMIT_STATUS_COLLISION_OCCURED) != 0) + { + nerr("ERROR: Collision occurred TSR: %08" PRIx32 "\n", tsr); + ++tx_error; + } + + /* Check for Transmit Frame Corruption due to AMBA error */ + + if ((tsr & TRANSMIT_STATUS_AMBA_ERROR) != 0) + { + nerr("ERROR: AMBA error TSR: %08" PRIx32 "\n", tsr); + ++tx_error; + } + + /* 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 & TRANSMIT_STATUS_UNDERRUN) != 0) + { + nerr("ERROR: Transmit Underrun TSR: %08" PRIx32 "\n", tsr); + ++tx_error; + } + + /* Check for HRESP not OK */ + + if ((tsr & TRANSMIT_STATUS_RESP_NOT_OK) != 0) + { + nerr("ERROR: TX HRESP not OK: %08" PRIx32 "\n", tsr); + ++tx_error; + } + + /* Check for Late Collisions */ + + if ((tsr & TRANSMIT_STATUS_LATE_COLLISION) != 0) + { + nerr("ERROR: Late collision: %08" PRIx32 "\n", tsr); + ++tx_error; + } + + /* Clear status */ + + mac_putreg(priv, TRANSMIT_STATUS, tsr); + + /* Handle errors */ + + if (tx_error != 0) + { + mpfs_txreset(priv); + nerr("TX ERROR: reset\n"); + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval |= NETWORK_CONTROL_ENABLE_TRANSMIT; + mac_putreg(priv, NETWORK_CONTROL, regval); + } + + /* And handle the TX done event */ + + if ((tsr & TRANSMIT_STATUS_TRANSMIT_COMPLETE) != 0) + { + ninfo("TXCOMP: cancel timeout\n"); + wd_cancel(&priv->txtimeout); + + mpfs_txdone(priv, queue); + } + } + + /* Check for the receipt of an RX packet. + * + * RXCOMP indicates that a packet has been received and stored in memory. + * 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 (rsr != 0) + { + uint32_t rx_error = 0; + ninfo("RX: rsr=0x%X\n", rsr); + + if ((rsr & RECEIVE_STATUS_FRAME_RECEIVED) != 0) + { + /* Handle the received packet */ + + mpfs_receive(priv, queue); + } + + /* Check for Receive Overrun */ + + if ((rsr & RECEIVE_STATUS_RECEIVE_OVERRUN) != 0) + { + ++rx_error; + nerr("ERROR: Receiver overrun RSR: %08" PRIx32 "\n", rsr); + } + + /* Check for buffer not available (BNA) */ + + if ((rsr & RECEIVE_STATUS_BUFFER_NOT_AVAILABLE) != 0) + { + ++rx_error; + nerr("ERROR: Buffer not available RSR: %08" PRIx32 "\n", rsr); + } + + /* Check for HRESP not OK */ + + if ((rsr & RECEIVE_STATUS_RESP_NOT_OK) != 0) + { + ++rx_error; + nerr("ERROR: HRESP not OK: %08" PRIx32 "\n", rsr); + } + + /* Clear status */ + + mac_putreg(priv, RECEIVE_STATUS, rsr); + + if (rx_error != 0) + { + nerr("RX ERROR: reset\n"); + mpfs_rxreset(priv); + *priv->queue[queue].int_status = 0xffffffff; + mac_putreg(priv, RECEIVE_STATUS, 0xffffffff); + + /* rxreset disables reveiver so re-enable it */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval |= NETWORK_CONTROL_ENABLE_RECEIVE; + mac_putreg(priv, NETWORK_CONTROL, regval); + } + } + + net_unlock(); + + /* Re-enable Ethernet interrupts */ + + regval = INT_ALL; + *priv->queue[queue].int_enable = regval; +} + +/**************************************************************************** + * Function: mpfs_txreset + * + * Description: + * Reset the transmit logic + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void mpfs_txreset(struct mpfs_ethmac_s *priv) +{ + uint8_t *txbuffer; + struct gmac_txdesc_s *txdesc; + uintptr_t bufaddr; + uint32_t regval; + int qi; + int ndx; + + /* Disable TX */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval &= ~NETWORK_CONTROL_ENABLE_TRANSMIT; + mac_putreg(priv, NETWORK_CONTROL, regval); + + /* Configure the TX descriptors. */ + + /* first set the common upper32-bits of 64 bit address */ + + bufaddr = (uintptr_t)priv->queue[0].tx_desc_tab; + mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(bufaddr)); + + for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + txbuffer = priv->queue[qi].txbuffer; + txdesc = priv->queue[qi].tx_desc_tab; + priv->queue[qi].txhead = 0; + priv->queue[qi].txtail = 0; + + for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NTXBUFFERS; ndx++) + { + bufaddr = (uintptr_t)&txbuffer[ndx * GMAC_TX_UNITSIZE]; + + /* Set the buffer address and mark the descriptor as in used by + * firmware. + */ + + txdesc[ndx].addr = lower_32_bits(bufaddr); + txdesc[ndx].status = GEM_TX_DMA_USED; +#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE + txdesc[ndx].addr_hi = upper_32_bits(bufaddr); +#endif + } + + /* Mark the final descriptor in the list */ + + txdesc[CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1].status = GEM_TX_DMA_USED | + GEM_TX_DMA_WRAP; + + /* Set the Transmit Buffer Queue Base Register and Disable queue */ + + *priv->queue[qi].tx_q_ptr = lower_32_bits((uintptr_t)txdesc) | 1u; + } + + /* REVISIT: for now enable only queue 0 for DMA operation */ + + *priv->queue[0].tx_q_ptr = lower_32_bits((uintptr_t) + priv->queue[0].tx_desc_tab); +} + +/**************************************************************************** + * Function: mpfs_rxreset + * + * Description: + * Reset the receive logic + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void mpfs_rxreset(struct mpfs_ethmac_s *priv) +{ + uint8_t *rxbuffer; + struct gmac_rxdesc_s *rxdesc; + uintptr_t bufaddr; + uint32_t regval; + int qi; + int ndx; + + /* Disable RX */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval &= ~NETWORK_CONTROL_ENABLE_RECEIVE; + mac_putreg(priv, NETWORK_CONTROL, regval); + + /* first set the common upper32-bits of 64 bit address */ + + bufaddr = (uintptr_t)priv->queue[0].rx_desc_tab; + mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(bufaddr)); + + /* Configure the RX descriptors. */ + + for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + rxbuffer = priv->queue[qi].rxbuffer; + rxdesc = priv->queue[qi].rx_desc_tab; + priv->queue[qi].rxndx = 0; + + for (ndx = 0; ndx < CONFIG_MPFS_ETHMAC_NRXBUFFERS; ndx++) + { + bufaddr = (uintptr_t)&rxbuffer[ndx * GMAC_RX_UNITSIZE]; + + /* Set the buffer address and remove GMACRXD_ADDR_OWNER and + * GMACRXD_ADDR_WRAP. + */ + + rxdesc[ndx].addr = lower_32_bits(bufaddr); + rxdesc[ndx].status = 0; +#ifdef CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE + rxdesc[ndx].addr_hi = upper_32_bits(bufaddr); +#endif + } + + /* Mark the final descriptor in the list */ + + rxdesc[CONFIG_MPFS_ETHMAC_NRXBUFFERS - 1].addr |= GEM_RX_DMA_ADDR_WRAP; + + /* Set the Receive Buffer Queue Base Register and disable queue */ + + *priv->queue[qi].rx_q_ptr = lower_32_bits((uintptr_t)rxdesc) | 1u; + } + + /* REVISIT: enable only queue 0 for DMA operation */ + + *priv->queue[0].rx_q_ptr = lower_32_bits((uintptr_t) + priv->queue[0].rx_desc_tab); + *priv->queue[0].int_status = 0xffffffff; +} + +/**************************************************************************** + * Function: mpfs_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 mpfs_txinuse(struct mpfs_ethmac_s *priv, unsigned int queue) +{ + uint32_t txhead32 = priv->queue[queue].txhead; + if (priv->queue[queue].txtail > txhead32) + { + txhead32 += CONFIG_MPFS_ETHMAC_NTXBUFFERS; + } + + return (uint16_t)(txhead32 - priv->queue[queue].txtail); +} + +/**************************************************************************** + * Function: mpfs_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 mpfs_txfree(struct mpfs_ethmac_s *priv, unsigned int queue) +{ + /* 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_MPFS_ETHMAC_NTXBUFFERS - 1) - mpfs_txinuse(priv, queue); +} + +/**************************************************************************** + * Function: mpfs_macaddress + * + * Description: + * Configure the selected MAC address. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static void mpfs_macaddress(struct mpfs_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + uint32_t regval; + + ninfo("%s MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->d_ifname, + dev->d_mac.ether.ether_addr_octet[0], + dev->d_mac.ether.ether_addr_octet[1], + dev->d_mac.ether.ether_addr_octet[2], + dev->d_mac.ether.ether_addr_octet[3], + dev->d_mac.ether.ether_addr_octet[4], + dev->d_mac.ether.ether_addr_octet[5]); + + /* Set the MAC address */ + + regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[0] | + (uint32_t)dev->d_mac.ether.ether_addr_octet[1] << 8 | + (uint32_t)dev->d_mac.ether.ether_addr_octet[2] << 16 | + (uint32_t)dev->d_mac.ether.ether_addr_octet[3] << 24; + mac_putreg(priv, SPEC_ADD1_BOTTOM, regval); + + regval = (uint32_t)dev->d_mac.ether.ether_addr_octet[4] | + (uint32_t)dev->d_mac.ether.ether_addr_octet[5] << 8; + mac_putreg(priv, SPEC_ADD1_TOP, regval); +} + +/**************************************************************************** + * Function: mpfa_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int mpfs_txpoll(struct net_driver_s *dev) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_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) + { + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ + + #ifdef CONFIG_NET_IPv4 + #ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) + #endif + { + arp_out(&priv->dev); + } + #endif /* CONFIG_NET_IPv4 */ + + #ifdef CONFIG_NET_IPv6 + #ifdef CONFIG_NET_IPv4 + else + #endif + { + neighbor_out(&priv->dev); + } + #endif /* CONFIG_NET_IPv6 */ + + if (!devif_loopback(&priv->dev)) + { + /* Send the packet */ + + mpfs_transmit(priv, 0); + + /* Check if there are any free TX descriptors. We cannot perform + * the TX poll if we do not have buffering for another packet. + */ + + if (mpfs_txfree(priv, 0) == 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: mpfs_dopoll + * + * Description: + * The function is called in order to perform an out-of-sequence TX poll. + * This is done: + * + * 1. After completion of a transmission (mpfs_txdone), + * 2. When new TX data is available (mpfs_txavail), and + * 3. After a TX timeout to restart the sending process + * (mpfs_txtimeout_expiry). + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void mpfs_dopoll(struct mpfs_ethmac_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + + /* Check if there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + if (mpfs_txfree(priv, 0) > 0) + { + /* If we have the descriptor, + * then poll the network for new XMIT data. + */ + + devif_timer(dev, 0, mpfs_txpoll); + } +} + +/**************************************************************************** + * Function: mpfs_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Parameters: + * arg - The argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void mpfs_poll_expiry(wdparm_t arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + + /* Schedule to perform the interrupt processing on the worker thread. */ + + if (work_available(&priv->pollwork)) + { + work_queue(ETHWORK, &priv->pollwork, mpfs_poll_work, priv, 0); + } + else + { + wd_start(&priv->txpoll, MPFS_WDDELAY, + mpfs_poll_expiry, (wdparm_t)priv); + } +} + +/**************************************************************************** + * Function: mpfs_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void mpfs_poll_work(void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + struct net_driver_s *dev = &priv->dev; + + /* Check if there are any free TX descriptors. We cannot perform the + * TX poll if we do not have buffering for another packet. + */ + + net_lock(); + if (mpfs_txfree(priv, 0) > 0) + { + /* Update TCP timing states and poll the network for new XMIT data. */ + + devif_timer(dev, MPFS_WDDELAY, mpfs_txpoll); + } + + /* Setup the watchdog poll timer again */ + + wd_start(&priv->txpoll, MPFS_WDDELAY, mpfs_poll_expiry, (wdparm_t)priv); + net_unlock(); +} + +/**************************************************************************** + * Function: mpfs_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 mpfs_ifup(struct net_driver_s *dev) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; + int ret; + +#ifdef CONFIG_NET_IPv4 + ninfo("Bringing up: %d.%d.%d.%d\n", + (int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff), + (int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24)); +#endif +#ifdef CONFIG_NET_IPv6 + ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], + dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], + dev->d_ipv6addr[6], dev->d_ipv6addr[7]); +#endif + + /* Configure the Ethernet interface for DMA operation. */ + + ret = mpfs_ethconfig(priv); + if (ret < 0) + { + return ret; + } + + /* Set the MAC address (should have been configured while we were down) */ + + mpfs_macaddress(priv); + +#ifdef CONFIG_NET_ICMPv6 + /* Set up IPv6 multicast address filtering */ + + mpfs_ipv6multicast(priv); +#endif + + /* Initialize for PHY access */ + + ret = mpfs_phyinit(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_phyinit failed: %d\n", ret); + return ret; + } + +#ifdef CONFIG_MPFS_MAC_AUTONEG + + ret = mpfs_autonegotiate(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_autonegotiate failed: %d\n", ret); + return ret; + } +#else + /* Just force the configured link speed */ + + mpfs_linkspeed(priv); +#endif + + /* Enable normal MAC operation */ + + ninfo("Enable normal operation\n"); + + /* Set and activate a timer process */ + + wd_start(&priv->txpoll, MPFS_WDDELAY, + mpfs_poll_expiry, (wdparm_t)priv); + + /* Enable the Ethernet interrupts */ + + priv->ifup = true; + up_enable_irq(priv->mac_q_int[0]); + up_enable_irq(priv->mac_q_int[1]); + up_enable_irq(priv->mac_q_int[2]); + up_enable_irq(priv->mac_q_int[3]); + + return OK; +} + +/**************************************************************************** + * Function: mpfs_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_ifdown(struct net_driver_s *dev) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; + irqstate_t flags; + + ninfo("Taking the network down\n"); + + /* Disable the Ethernet interrupt */ + + flags = enter_critical_section(); + up_disable_irq(priv->mac_q_int[0]); + up_disable_irq(priv->mac_q_int[1]); + up_disable_irq(priv->mac_q_int[2]); + up_disable_irq(priv->mac_q_int[3]); + + *priv->queue[0].int_disable = 0xffffffff; + *priv->queue[1].int_disable = 0xffffffff; + *priv->queue[2].int_disable = 0xffffffff; + *priv->queue[3].int_disable = 0xffffffff; + + /* Cancel the TX poll timer and TX timeout timers */ + + wd_cancel(&priv->txpoll); + wd_cancel(&priv->txtimeout); + + /* Put the MAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the mpfs_ifup() always + * successfully brings the interface back up. + */ + + mpfs_ethreset(priv); + + /* Mark the device "down" */ + + priv->ifup = false; + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Function: mpfs_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void mpfs_txavail_work(void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + + ninfo("ifup: %d\n", priv->ifup); + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->ifup) + { + /* Poll the network for new XMIT data */ + + mpfs_dopoll(priv); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: mpfs_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 mpfs_txavail(struct net_driver_s *dev) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(ETHWORK, &priv->pollwork, mpfs_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_hashindx + * + * Description: + * Cacuclate the hash address register index. The hash address register + * is 64 bits long and takes up two locations in the memory map. The + * destination address is reduced to a 6-bit index into the 64-bit Hash + * Register using the following hash function: The hash function is an XOR + * of every sixth bit of the destination address. + * + * ndx:05 = da:05 ^ da:11 ^ da:17 ^ da:23 ^ da:29 ^ da:35 ^ da:41 ^ da:47 + * ndx:04 = da:04 ^ da:10 ^ da:16 ^ da:22 ^ da:28 ^ da:34 ^ da:40 ^ da:46 + * ndx:03 = da:03 ^ da:09 ^ da:15 ^ da:21 ^ da:27 ^ da:33 ^ da:39 ^ da:45 + * ndx:02 = da:02 ^ da:08 ^ da:14 ^ da:20 ^ da:26 ^ da:32 ^ da:38 ^ da:44 + * ndx:01 = da:01 ^ da:07 ^ da:13 ^ da:19 ^ da:25 ^ da:31 ^ da:37 ^ da:43 + * ndx:00 = da:00 ^ da:06 ^ da:12 ^ da:18 ^ da:24 ^ da:30 ^ da:36 ^ da:42 + * + * Where da:00 represents the least significant bit of the first byte + * received and da:47 represents the most significant bit of the last byte + * received. + * + * Input Parameters: + * mac - The multicast address to be hashed + * + * Returned Value: + * The 6-bit hash table index + * + ****************************************************************************/ + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static unsigned int mpfs_hashindx(const uint8_t *mac) +{ + unsigned int ndx; + + ndx = mac[0]; + ndx ^= (mac[1] << 2) | (mac[0] >> 6); + ndx ^= (mac[2] << 4) | (mac[1] >> 4); + ndx ^= (mac[2] >> 2); + ndx ^= mac[3]; + ndx ^= (mac[4] << 2) | (mac[3] >> 6); + ndx ^= (mac[5] << 4) | (mac[4] >> 4); + ndx ^= (mac[5] >> 2); + + return ndx & 0x3f; +} +#endif /* CONFIG_NET_MCASTGROUP || CONFIG_NET_ICMPv6 */ + +/**************************************************************************** + * Function: mpfs_addmac + * + * Description: + * NuttX Callback: Add the specified MAC address to the hardware multicast + * address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be added + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int mpfs_addmac(struct net_driver_s *dev, const uint8_t *mac) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; + uint32_t regoffset; + uint32_t regval; + unsigned int ndx; + UNUSED(priv); + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Calculate the 6-bit has table index */ + + ndx = mpfs_hashindx(mac); + + /* Add the multicast address to the hardware multicast hash table */ + + regoffset = (ndx >= 32) ? HASH_TOP : HASH_BOTTOM; + regval = mac_getreg(priv, regoffset); + regval |= 1 << (ndx % 32); + mac_putreg(priv, regoffset, regval); + + /* The unicast hash enable and the multicast hash enable bits in the + * Network Configuration Register enable the reception of hash matched + * frames: + * + * - A multicast match will be signalled if the multicast hash enable bit + * is set, da:00 is logic 1 and the hash index points to a bit set in + * the Hash Register. + * - A unicast match will be signalled if the unicast hash enable bit is + * set, da:00 is logic 0 and the hash index points to a bit set in the + * Hash Register. + */ + + regval = mac_getreg(priv, NETWORK_CONFIG); + regval &= ~NETWORK_CONFIG_UNICAST_HASH_ENABLE; + regval |= NETWORK_CONFIG_MULTICAST_HASH_ENABLE; + mac_putreg(priv, NETWORK_CONFIG, regval); + + return OK; +} +#endif + +/**************************************************************************** + * Function: mpfs_rmmac + * + * Description: + * NuttX Callback: Remove the specified MAC address from the hardware + * multicast address filtering + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * mac - The MAC address to be removed + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static int mpfs_rmmac(struct net_driver_s *dev, const uint8_t *mac) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; + uint32_t regval; + uint32_t regaddr1; + uint32_t regaddr2; + unsigned int ndx; + UNUSED(priv); + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + /* Calculate the 6-bit has table index */ + + ndx = mpfs_hashindx(mac); + + /* Remove the multicast address to the hardware multicast hast table */ + + if (ndx >= 32) + { + regaddr1 = HASH_TOP; /* Hash Register Top [63:32] Register */ + regaddr2 = HASH_BOTTOM; /* Hash Register Bottom [31:0] Register */ + } + else + { + regaddr1 = HASH_BOTTOM; /* Hash Register Bottom [31:0] Register */ + regaddr2 = HASH_TOP; /* Hash Register Top [63:32] Register */ + } + + regval = mac_getreg(priv, regaddr1); + regval &= ~(1 << (ndx % 32)); + mac_putreg(priv, regaddr1, regval); + + /* The unicast hash enable and the multicast hash enable bits in the + * Network Configuration Register enable the reception of hash matched + * frames: + * + * - A multicast match will be signalled if the multicast hash enable bit + * is set, da:00 is logic 1 and the hash index points to a bit set in + * the Hash Register. + * - A unicast match will be signalled if the unicast hash enable bit is + * set, da:00 is logic 0 and the hash index points to a bit set in the + * Hash Register. + */ + + /* Are all multicast address matches disabled? */ + + if (regval == 0 && mac_getreg(priv, regaddr2) == 0) + { + /* Yes.. disable all address matching */ + + regval = mac_getreg(priv, NETWORK_CONFIG); + regval &= ~(NETWORK_CONFIG_UNICAST_HASH_ENABLE | + NETWORK_CONFIG_MULTICAST_HASH_ENABLE); + mac_putreg(priv, NETWORK_CONFIG, regval); + } + + return OK; +} +#endif + +/**************************************************************************** + * Function: mpfs_enablemdio + * + * Description: + * Enable the management port + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_enablemdio(struct mpfs_ethmac_s *priv) +{ + uint32_t regval; + uint32_t enables; + + /* Enable management port */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT); + + regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT); + regval |= NETWORK_CONTROL_MAN_PORT_EN; + mac_putreg(priv, NETWORK_CONTROL, regval); + + regval |= enables; + mac_putreg(priv, NETWORK_CONTROL, regval); +} + +/**************************************************************************** + * Function: mpfs_disablemdio + * + * Description: + * Disable the management port + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_disablemdio(struct mpfs_ethmac_s *priv) +{ + uint32_t regval; + uint32_t enables; + + /* Disable management port */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + enables = regval & (NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT); + + regval &= ~(NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT); + mac_putreg(priv, NETWORK_CONTROL, regval); + + regval &= ~NETWORK_CONTROL_MAN_PORT_EN; + mac_putreg(priv, NETWORK_CONTROL, regval); + + regval |= enables; + mac_putreg(priv, NETWORK_CONTROL, regval); +} + +/**************************************************************************** + * Function: mpfs_phyread + * + * Description: + * Read a PHY register. + * + * Input 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 mpfs_phyread(struct mpfs_ethmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t *phyval) +{ + uint32_t regval; + int ret; + + /* Make sure that the PHY is idle */ + + ret = mpfs_phywait(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_phywait failed: %d\n", ret); + return ret; + } + + /* Write the PHY Maintenance register */ + + regval = PHY_MANAGEMENT_PHY_DATA(0) | PHY_MANAGEMENT_WRITE10 | + PHY_MANAGEMENT_REG_ADDRESS(regaddr) | + PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) | + PHY_MANAGEMENT_OPERATION_READ | + PHY_MANAGEMENT_WRITE_1; + + mac_putreg(priv, PHY_MANAGEMENT, regval); + + /* Wait until the PHY is again idle */ + + ret = mpfs_phywait(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_phywait failed: %d\n", ret); + return ret; + } + + /* Return the PHY data */ + + *phyval = (uint16_t)(mac_getreg(priv, PHY_MANAGEMENT) & + PHY_MANAGEMENT_PHY_DATA_MASK); + return OK; +} + +/**************************************************************************** + * Function: mpfs_phywrite + * + * Description: + * Write to a PHY register. + * + * Input 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 mpfs_phywrite(struct mpfs_ethmac_s *priv, uint8_t phyaddr, + uint8_t regaddr, uint16_t phyval) +{ + uint32_t regval; + int ret; + + /* Make sure that the PHY is idle */ + + ret = mpfs_phywait(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_phywait failed: %d\n", ret); + return ret; + } + + /* Write the PHY Maintenance register */ + + regval = PHY_MANAGEMENT_PHY_DATA(phyval) | PHY_MANAGEMENT_WRITE10 | + PHY_MANAGEMENT_REG_ADDRESS(regaddr) | + PHY_MANAGEMENT_PHY_ADDRESS(phyaddr) | + PHY_MANAGEMENT_OPERATION_WRITE | + PHY_MANAGEMENT_WRITE_1; + mac_putreg(priv, PHY_MANAGEMENT, regval); + + /* Wait until the PHY is again IDLE */ + + ret = mpfs_phywait(priv); + if (ret < 0) + { + nerr("ERROR: mpfs_phywait failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Function: mpfs_phywait + * + * Description: + * Wait for the PHY to become IDLE + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno (-ETIMEDOUT) on failure. + * + ****************************************************************************/ + +static int mpfs_phywait(struct mpfs_ethmac_s *priv) +{ + unsigned int retries; + + /* Loop for the configured number of attempts */ + + for (retries = 0; retries < PHY_RETRY_MAX; retries++) + { + /* Is the PHY IDLE */ + + if ((mac_getreg(priv, NETWORK_STATUS) & NETWORK_STATUS_MAN_DONE) != 0) + { + return OK; + } + } + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Function: mpfs_phyfind + * + * Description: + * Verify the PHY address and, if it is bad, try to one that works. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_phyfind(struct mpfs_ethmac_s *priv, uint8_t *phyaddr) +{ + uint16_t phyval; + uint8_t candidate; + unsigned int offset; + int ret = -ESRCH; + + ninfo("Find a valid PHY address\n"); + + /* Enable management port */ + + mpfs_enablemdio(priv); + +#ifdef CONFIG_MPFS_HAVE_CORERMII + ninfo("coreRMII: reset @address: %d\n", CONFIG_MPFS_CORERMII_ADDRESS); + + ret = mpfs_phywrite(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, + CORE_RMII_RESET | CORE_RMII_FULL_DUPLEX | + CORE_RMII_100MBIT); + if (ret != OK) + { + nerr("Core RMII reset write failed!\n"); + } + + ret = mpfs_phyread(priv, CONFIG_MPFS_CORERMII_ADDRESS, GMII_MCR, &phyval); + ninfo("CORE-RMII after reset MCR=%d\n", phyval); + if ((phyval != 0x03) || (ret != OK)) + { + nerr("Core RMII reset read failed! val=%d\n", phyval); + } +#endif + + /* Check initial candidate address */ + + candidate = *phyaddr; + + ret = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval); + if (ret == OK && phyval != 0xffff) + { + *phyaddr = candidate; + ret = OK; + } + + /* The current address does not work... try another */ + + else + { + nerr("ERROR: mpfs_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 = mpfs_phyread(priv, candidate, GMII_PHYID1, &phyval); + if (ret == OK && phyval != 0xffff) + { + ret = OK; + break; + } + } + } + + if (ret == OK) + { + ninfo(" PHYID1: %04x PHY addr: %d\n", phyval, candidate); + *phyaddr = candidate; + } + + /* Disable management port */ + + mpfs_disablemdio(priv); + return ret; +} + +/**************************************************************************** + * Function: mpfs_phydump + * + * Description: + * Dump the contents of PHY registers + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_INFO) +static void mpfs_phydump(struct mpfs_ethmac_s *priv) +{ + uint16_t phyval; + + /* Enable management port */ + + mpfs_enablemdio(priv); + + ninfo("GMII Registers (Address %02x)\n", priv->phyaddr); + mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval); + ninfo(" MCR: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval); + ninfo(" MSR: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_ADVERTISE, &phyval); + ninfo(" ADVERTISE: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &phyval); + ninfo(" LPR: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &phyval); + ninfo(" 1000BTCR: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &phyval); + ninfo(" 1000BTSR: %04x\n", phyval); + mpfs_phyread(priv, priv->phyaddr, GMII_ESTATUS, &phyval); + ninfo(" ESTATUS: %04x\n", phyval); + + /* Disable management port */ + + mpfs_disablemdio(priv); +} +#endif + +/**************************************************************************** + * Function: mpfs_autonegotiate + * + * Description: + * Autonegotiate speed and duplex. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_MPFS_MAC_AUTONEG +static int mpfs_autonegotiate(struct mpfs_ethmac_s *priv) +{ + uint32_t regval; + uint32_t control; + uint32_t linkmode; + uint16_t phyval; + uint16_t phyid1; + uint16_t phyid2; + uint16_t advertise; + uint16_t lpa; + int timeout; + int ret; + +#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS + uint16_t btsr; + uint16_t btcr; +#endif + + /* Enable management port */ + + mpfs_enablemdio(priv); + + /* Read the MSB bits of the OUI from the PHYID1 register */ + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID1, &phyid1); + if (ret < 0) + { + nerr("ERROR: Failed to read PHYID1 register\n"); + goto errout; + } + + ninfo("PHYID1: %04x PHY address: %02x\n", phyid1, priv->phyaddr); + + /* Read the LS bits of the OUI from the PHYID2 register */ + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_PHYID2, &phyid2); + if (ret < 0) + { + nerr("ERROR: Failed to read PHYID2 register\n"); + goto errout; + } + + ninfo("PHYID2: %04x PHY address: %02x\n", phyid2, priv->phyaddr); + + if ((phyid1 != 0xffff) && (phyid2 != 0xffff)) + { + ninfo(" Vendor Model Number: %04x\n", + (phyid2 & GMII_PHYID2_MODEL_MASK) >> GMII_PHYID2_MODEL_SHIFT); + ninfo(" Model Revision Number: %04x\n", + (phyid2 & GMII_PHYID2_REV_MASK) >> GMII_PHYID2_REV_SHIFT); + } + + /* Set the Auto_negotiation Advertisement Register, MII advertising for + * Next page 100BaseTxFD and HD, 10BaseTxFD and HD, IEEE 802.3 + */ + + advertise = GMII_ADVERTISE_100BASETXFULL | GMII_ADVERTISE_100BASETXHALF | + GMII_ADVERTISE_10BASETXFULL | GMII_ADVERTISE_10BASETXHALF | + GMII_ADVERTISE_8023; + + ret = mpfs_phywrite(priv, priv->phyaddr, GMII_ADVERTISE, advertise); + if (ret < 0) + { + nerr("ERROR: Failed to write ADVERTISE register\n"); + goto errout; + } + +#ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS + /* Modify the 1000Base-T control register to advertise 1000Base-T full + * and half duplex support. + */ + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTCR, &btcr); + if (ret < 0) + { + nerr("ERROR: Failed to read 1000BTCR register: %d\n", ret); + goto errout; + } + + btcr |= GMII_1000BTCR_1000BASETFULL | GMII_1000BTCR_1000BASETHALF; + + ret = mpfs_phywrite(priv, priv->phyaddr, GMII_1000BTCR, btcr); + if (ret < 0) + { + nerr("ERROR: Failed to write 1000BTCR register: %d\n", ret); + goto errout; + } + +#endif + + /* Restart Auto_negotiation */ + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read MCR register: %d\n", ret); + goto errout; + } + + phyval |= GMII_MCR_ANRESTART; + + ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, phyval); + if (ret < 0) + { + nerr("ERROR: Failed to write MCR register: %d\n", ret); + goto errout; + } + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_MCR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read MCR register: %d\n", ret); + goto errout; + } + + ninfo(" MCR: 0x%X\n", phyval); + + /* Wait for autonegotiation to complete */ + + timeout = 0; + for (; ; ) + { + ret = mpfs_phyread(priv, priv->phyaddr, GMII_MSR, &phyval); + if (ret < 0) + { + nerr("ERROR: Failed to read MSR register: %d\n", ret); + goto errout; + } + + /* Check for completion of autonegotiation */ + + if ((phyval & GMII_MSR_ANEGCOMPLETE) != 0) + { + /* Yes break out of the loop */ + + ninfo("AutoNegotiate complete\n"); + break; + } + + /* No check for a timeout */ + + if (++timeout >= PHY_RETRY_MAX) + { + nerr("ERROR: TimeOut\n"); + mpfs_phydump(priv); + ret = -ETIMEDOUT; + goto errout; + } + } + + /* Setup the GMAC local link speed */ + + linkmode = 0; /* 10Base-T Half-Duplex */ + timeout = 0; + + for (; ; ) + { + #ifndef CONFIG_MPFS_MAC_AUTONEG_DISABLE_1000MBPS + ret = mpfs_phyread(priv, priv->phyaddr, GMII_1000BTSR, &btsr); + if (ret < 0) + { + nerr("ERROR: Failed to read 1000BTSR register: %d\n", ret); + goto errout; + } + + /* Setup the GMAC link speed */ + + if ((btsr & GMII_1000BTSR_LP1000BASETFULL) != 0 && + (btcr & GMII_1000BTCR_1000BASETHALF) != 0) + { + /* Set RGMII for 1000BaseTX and Full Duplex */ + + ninfo("Link: FD - 1000\n"); + linkmode = NETWORK_CONFIG_FULL_DUPLEX | + NETWORK_CONFIG_GIGABIT_MODE_ENABLE; + break; + } + else if ((btsr & GMII_1000BTSR_LP1000BASETHALF) != 0 && + (btcr & GMII_1000BTCR_1000BASETFULL) != 0) + { + /* Set RGMII for 1000BaseT and Half Duplex */ + + ninfo("Link: HD - 1000\n"); + linkmode = NETWORK_CONFIG_GIGABIT_MODE_ENABLE; + break; + } + #endif + + /* Get the Autonegotiation Link partner base page */ + + ret = mpfs_phyread(priv, priv->phyaddr, GMII_LPA, &lpa); + if (ret < 0) + { + nerr("ERROR: Failed to read LPA register: %d\n", ret); + goto errout; + } + + /* Setup the GMAC link speed */ + + if ((advertise & GMII_ADVERTISE_100BASETXFULL) != 0 && + (lpa & GMII_LPA_100BASETXFULL) != 0) + { + /* Set RGMII for 100BaseTX and Full Duplex */ + + ninfo("Link: FD - 100\n"); + linkmode = (NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX); + break; + } + else if ((advertise & GMII_ADVERTISE_10BASETXFULL) != 0 && + (lpa & GMII_LPA_10BASETXFULL) != 0) + { + /* Set RGMII for 10BaseT and Full Duplex */ + + ninfo("Link: FD - 10\n"); + linkmode = NETWORK_CONFIG_FULL_DUPLEX; + break; + } + else if ((advertise & GMII_ADVERTISE_100BASETXHALF) != 0 && + (lpa & GMII_LPA_100BASETXHALF) != 0) + { + /* Set RGMII for 100BaseTX and half Duplex */ + + ninfo("Link: HD - 100\n"); + linkmode = NETWORK_CONFIG_SPEED; + break; + } + else if ((advertise & GMII_ADVERTISE_10BASETXHALF) != 0 && + (lpa & GMII_LPA_10BASETXHALF) != 0) + { + /* Set RGMII for 10BaseT and half Duplex */ + + ninfo("Link: HD - 10\n"); + break; + } + + /* Check for a timeout */ + + if (++timeout >= PHY_RETRY_MAX) + { + nerr("ERROR: TimeOut\n"); + mpfs_phydump(priv); + ret = -ETIMEDOUT; + goto errout; + } + } + + /* Disable RX and TX momentarily */ + + control = mac_getreg(priv, NETWORK_CONTROL); + mac_putreg(priv, NETWORK_CONTROL, + control & ~(NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT)); + + /* Modify the NETWORK_CONFIG register based on the negotiated + * speed and duplex + */ + + regval = mac_getreg(priv, NETWORK_CONFIG); + regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX | + NETWORK_CONFIG_GIGABIT_MODE_ENABLE); + regval |= linkmode; + mac_putreg(priv, NETWORK_CONFIG, regval); + mac_putreg(priv, NETWORK_CONTROL, control); + +errout: + + /* Disable the management port */ + + mpfs_disablemdio(priv); + return ret; +} +#endif + +/**************************************************************************** + * Function: mpfs_linkspeed + * + * Description: + * If autonegotiation is not configured, then just force the configuration + * mode + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_MPFS_MAC_AUTONEG +static void mpfs_linkspeed(struct mpfs_ethmac_s *priv) +{ + uint32_t regval; + uint32_t ncr; + + /* Disable RX and TX momentarily */ + + ncr = mac_getreg(priv, NETWORK_CONTROL); + mac_putreg(priv, NETWORK_CONTROL, + ncr & ~(NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT)); + + /* Modify the NETWORK_CONFIG register based on the configured + * speed and duplex + */ + + regval = mac_getreg(priv, NETWORK_CONFIG); + regval &= ~(NETWORK_CONFIG_SPEED | NETWORK_CONFIG_FULL_DUPLEX | + NETWORK_CONFIG_GIGABIT_MODE_ENABLE); + +#ifdef CONFIG_MPFS_MAC_ETHFD + regval |= NETWORK_CONFIG_FULL_DUPLEX; +#endif + +#if defined(CONFIG_MPFS_MAC_ETH100MBPS) + regval |= NETWORK_CONFIG_SPEED; +#elif defined(CONFIG_MPFS_MAC_ETH1000MBPS) + regval |= NETWORK_CONFIG_GIGABIT_MODE_ENABLE; +#endif + + ninfo("set linkspeed: NETWORK_CONFIG=0x%x\n", regval); + + mac_putreg(priv, NETWORK_CONFIG, regval); + mac_putreg(priv, NETWORK_CONTROL, ncr); +} +#endif + +/**************************************************************************** + * Function: mpfs_ioctl + * + * Description: + * Handles driver ioctl calls: + * + * SIOCMIINOTIFY - Set up to received notifications from PHY interrupting + * events. + * + * SIOCGMIIPHY, SIOCGMIIREG, and SIOCSMIIREG: + * Executes the SIOCxMIIxxx command and responds using the request struct + * that must be provided as its 2nd parameter. + * + * When called with SIOCGMIIPHY it will get the PHY address for the device + * and write it to the req->phy_id field of the request struct. + * + * When called with SIOCGMIIREG it will read a register of the PHY that is + * specified using the req->reg_no struct field and then write its output + * to the req->val_out field. + * + * When called with SIOCSMIIREG it will write to a register of the PHY + * that is specified using the req->reg_no struct field and use + * req->val_in as its input. + * + * Input Parameters: + * dev - Ethernet device structure + * cmd - SIOCxMIIxxx command code + * arg - Request structure also used to return values + * + * Returned Value: Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int mpfs_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg) +{ +#ifdef CONFIG_NETDEV_PHY_IOCTL + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)dev->d_private; +#endif + int ret; + + switch (cmd) + { +#ifdef CONFIG_NETDEV_PHY_IOCTL +#ifdef CONFIG_ARCH_PHY_INTERRUPT + case SIOCMIINOTIFY: /* Set up for PHY event notifications */ + { + struct mii_ioctl_notify_s *req = + (struct mii_ioctl_notify_s *)((uintptr_t)arg); + + ret = phy_notify_subscribe(dev->d_ifname, req->pid, &req->event); + if (ret == OK) + { + /* Enable PHY link up/down interrupts */ + + ret = mpfs_phyintenable(priv); + } + } + break; +#endif + + case SIOCGMIIPHY: /* Get MII PHY address */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + req->phy_id = priv->phyaddr; + ret = OK; + } + break; + + case SIOCGMIIREG: /* Get register from MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + + /* Enable the management port */ + + mpfs_enablemdio(priv); + + /* Read from the requested register */ + + ret = mpfs_phyread(priv, req->phy_id, req->reg_num, &req->val_out); + + /* Disable the management port */ + + mpfs_disablemdio(priv); + } + break; + + case SIOCSMIIREG: /* Set register in MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + + /* Enable the management port */ + + mpfs_enablemdio(priv); + + /* Write to the requested register */ + + ret = mpfs_phywrite(priv, req->phy_id, req->reg_num, req->val_in); + + /* Disable the management port */ + + mpfs_disablemdio(priv); + } + break; +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif /* CONFIG_NETDEV_IOCTL */ + +/**************************************************************************** + * Function: mpfs_buffer_initialize + * + * Description: + * Allocate aligned TX and RX descriptors and buffers. For the case of + * pre-allocated structures, the function degenerates to a few assignments. + * + * 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 mpfs_buffer_initialize(struct mpfs_ethmac_s *priv, + unsigned int queue) +{ +#ifdef CONFIG_MPFS_ETHMAC_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_MPFS_ETHMAC_NTXBUFFERS * sizeof(struct gmac_txdesc_s); + priv->queue[queue].tx_desc_tab = (struct gmac_txdesc_s *) + kmm_memalign(8, allocsize); + if (priv->queue[queue].tx_desc_tab == NULL) + { + nerr("ERROR: Failed to allocate TX descriptors\n"); + return -ENOMEM; + } + + memset(priv->queue[queue].tx_desc_tab, 0, allocsize); + + allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * sizeof(struct gmac_rxdesc_s); + priv->queue[queue].rx_desc_tab = kmm_memalign(8, allocsize); + if (priv->queue[queue].rx_desc_tab == NULL) + { + nerr("ERROR: Failed to allocate RX descriptors\n"); + mpfs_buffer_free(priv, queue); + return -ENOMEM; + } + + memset(priv->queue[queue].rx_desc_tab, 0, allocsize); + + allocsize = CONFIG_MPFS_ETHMAC_NTXBUFFERS * GMAC_TX_UNITSIZE; + priv->queue[queue].txbuffer = (uint8_t *)kmm_memalign(8, allocsize); + if (priv->queue[queue].txbuffer == NULL) + { + nerr("ERROR: Failed to allocate TX buffer\n"); + mpfs_buffer_free(priv, queue); + return -ENOMEM; + } + + allocsize = CONFIG_MPFS_ETHMAC_NRXBUFFERS * GMAC_RX_UNITSIZE; + priv->queue[queue].rxbuffer = (uint8_t *)kmm_memalign(8, allocsize); + if (priv->queue[queue].rxbuffer == NULL) + { + nerr("ERROR: Failed to allocate RX buffer\n"); + mpfs_buffer_free(priv, queue); + return -ENOMEM; + } + +#endif + + DEBUGASSERT(((uintptr_t)priv->queue[queue].rx_desc_tab & 7) == 0 && + ((uintptr_t)priv->queue[queue].rxbuffer & 7) == 0 && + ((uintptr_t)priv->queue[queue].tx_desc_tab & 7) == 0 && + ((uintptr_t)priv->queue[queue].txbuffer & 7) == 0); + return OK; +} + +/**************************************************************************** + * Function: mpfs_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 mpfs_buffer_free(struct mpfs_ethmac_s *priv, unsigned int queue) +{ +#ifndef CONFIG_MPFS_GMAC_PREALLOCATE + /* Free allocated buffers */ + + if (priv->queue[queue].rx_desc_tab != NULL) + { + kmm_free(priv->queue[queue].rx_desc_tab); + priv->queue[queue].rx_desc_tab = NULL; + } + + if (priv->queue[queue].rx_desc_tab != NULL) + { + kmm_free(priv->queue[queue].rx_desc_tab); + priv->queue[queue].rx_desc_tab = NULL; + } + + if (priv->queue[queue].txbuffer != NULL) + { + kmm_free(priv->queue[queue].txbuffer); + priv->queue[queue].txbuffer = NULL; + } + + if (priv->queue[queue].txbuffer != NULL) + { + kmm_free(priv->queue[queue].txbuffer); + priv->queue[queue].txbuffer = NULL; + } +#endif +} + +/**************************************************************************** + * Function: mpfs_txtimeout_work + * + * Description: + * Perform TX timeout related work from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Ethernet interrupts are disabled + * + ****************************************************************************/ + +static void mpfs_txtimeout_work(void *arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + + nerr("ERROR: TX-Timeout!\n"); + + /* Reset the hardware. Just take the interface down, then back up again. */ + + net_lock(); + mpfs_ifdown(&priv->dev); + mpfs_ifup(&priv->dev); + + /* Then poll the network for new XMIT data */ + + mpfs_dopoll(priv); + net_unlock(); +} + +/**************************************************************************** + * Function: mpfs_txtimeout_expiry + * + * Description: + * Our TX watchdog timed out. Called from the timer interrupt handler. + * The last TX never completed. Reset the hardware and start again. + * + * Input Parameters: + * arg - The argument + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * + ****************************************************************************/ + +static void mpfs_txtimeout_expiry(wdparm_t arg) +{ + struct mpfs_ethmac_s *priv = (struct mpfs_ethmac_s *)arg; + unsigned int qi; + + /* Disable further Ethernet interrupts. This will prevent some race + * conditions with interrupt work. There is still a potential race + * condition with interrupt work that is already queued and in progress. + */ + + for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + *priv->queue[qi].int_disable = 0xffffffff; + } + + /* Schedule to perform the TX timeout processing on the worker thread. */ + + work_queue(ETHWORK, &priv->irqwork, mpfs_txtimeout_work, priv, 0); +} + +/**************************************************************************** + * Function: mpfs_transmit + * + * Description: + * Start hardware transmission. Called either from the TX done interrupt + * handling or from watchdog based polling. + * + * Input Parameters: + * priv - Reference to the driver state structure + * queue - The queue to send from + * + * 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 mpfs_transmit(struct mpfs_ethmac_s *priv, unsigned int queue) +{ + struct net_driver_s *dev = &priv->dev; + volatile struct gmac_txdesc_s *txdesc; + uintptr_t addr; + uint32_t status; + uint32_t regval; + + ninfo("d_len: %d txhead: %d txtail: %d\n", + dev->d_len, priv->queue[queue].txhead, priv->queue[queue].txtail); + mpfs_dumppacket("Transmit packet", dev->d_buf, dev->d_len); + + /* Check parameter */ + + if (dev->d_len > GMAC_TX_UNITSIZE) + { + nerr("ERROR: Packet too big: %d\n", dev->d_len); + return -EINVAL; + } + + /* Pointer to the current TX descriptor */ + + txdesc = &priv->queue[queue].tx_desc_tab[priv->queue[queue].txhead]; + + /* If no free TX descriptor, buffer can't be sent */ + + if (mpfs_txfree(priv, queue) < 1) + { + nerr("ERROR: No free TX descriptors\n"); + return -EBUSY; + } + + /* Mark buffer as used temporarily so DMA doesn't operate on it */ + + status = GEM_TX_DMA_USED | GEM_TX_DMA_LAST; + txdesc->status = status; + + /* Setup/Copy data to transmission buffer */ + + if (dev->d_len > 0) + { + addr = txdesc->addr; +#ifdef MPFS_ETHMAC_64BIT_ADDRESS_MODE + addr += (uintptr_t)(txdesc->addr_hi) << 32; +#endif + memcpy((void *)addr, dev->d_buf, dev->d_len); + } + + /* Update TX descriptor status. */ + + status = (dev->d_len & GEM_DMA_STATUS_LENGTH_MASK) | GEM_TX_DMA_LAST; + + if (priv->queue[queue].txhead == CONFIG_MPFS_ETHMAC_NTXBUFFERS - 1) + { + status |= GEM_TX_DMA_WRAP; + } + + /* Update the descriptor status */ + + txdesc->status = status; + + /* Increment the head index */ + + if (++priv->queue[queue].txhead >= CONFIG_MPFS_ETHMAC_NTXBUFFERS) + { + priv->queue[queue].txhead = 0; + } + + /* Now start transmission (if it is not already done) */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval |= NETWORK_CONTROL_TRANSMIT_START; + mac_putreg(priv, NETWORK_CONTROL, regval); + + /* Set up the TX timeout watchdog (perhaps restarting the timer) */ + + wd_start(&priv->txtimeout, MPFS_TXTIMEOUT, + mpfs_txtimeout_expiry, (wdparm_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 dispatched is also an opportunity to reply with a TX + * packet. So, if we cannot handle an RX packet reply, then we disable + * all RX packet processing. + */ + + if (mpfs_txfree(priv, queue) < 1) + { + ninfo("Disabling RX interrupts\n"); + *priv->queue[queue].int_disable = INT_RX; + } + + return OK; +} + +/**************************************************************************** + * Function: mpfs_ethreset + * + * Description: + * Reset the Ethernet block. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +static void mpfs_ethreset(struct mpfs_ethmac_s *priv) +{ + int qi; + + /* if we are supporting PHY IOCTLs, then do not reset the MAC. */ + +#ifndef CONFIG_NETDEV_PHY_IOCTL + if (priv->regbase == MPFS_GEM0_LO_BASE) + { + /* reset */ + + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + 0, SYSREG_SOFT_RESET_CR_MAC0); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + SYSREG_SOFT_RESET_CR_MAC0, 0); + } + + if (priv->regbase == MPFS_GEM1_LO_BASE) + { + /* reset */ + + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + 0, SYSREG_SOFT_RESET_CR_MAC1); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + SYSREG_SOFT_RESET_CR_MAC1, 0); + } + +#endif + + /* Disable all GMAC interrupts */ + + for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + *priv->queue[qi].int_disable = 0xffffffff; + *priv->queue[qi].int_status = 0xffffffff; + } + + /* Reset RX and TX logic */ + + mpfs_rxreset(priv); + mpfs_txreset(priv); +} + +/**************************************************************************** + * Function: mpfs_macconfig + * + * Description: + * Configure the Ethernet MAC for DMA operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_macconfig(struct mpfs_ethmac_s *priv) +{ + uint32_t net_config = 0; + uint32_t net_control = 0; + uint32_t dma_config = 0; + uintptr_t addr; + unsigned int qi; + + net_control = NETWORK_CONTROL_CLEAR_ALL_STATS_REGS; + + net_config = (((uint32_t)1) << NETWORK_CONFIG_DATA_BUS_WIDTH_SHIFT) | + ((2 & NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK) + << NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT); + +#ifdef CONFIG_MPFS_MAC_SGMII + net_config |= NETWORK_CONFIG_SGMII_MODE_ENABLE | NETWORK_CONFIG_PCS_SELECT; +#endif + +#ifdef CONFIG_NET_LOOPBACK + net_control |= NETWORK_CONTROL_LOOPBACK_LOCAL; +#endif + +#ifdef CONFIG_NET_PROMISCUOUS + net_config |= NETWORK_CONFIG_COPY_ALL_FRAMES; +#endif + +#ifdef CONFIG_MPFS_MAC_NO_BROADCAST + net_config |= NETWORK_CONFIG_NO_BROADCAST; +#endif + + mac_putreg(priv, NETWORK_CONTROL, net_control); + mac_putreg(priv, NETWORK_CONFIG, net_config); + + mac_putreg(priv, RECEIVE_STATUS, 0xffffffff); + mac_putreg(priv, TRANSMIT_STATUS, 0xffffffff); + + /* Disable and clear all ints */ + + for (qi = 0; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + *priv->queue[qi].int_disable = 0xffffffff; + *priv->queue[qi].int_status = 0xffffffff; + } + + /* TODO: If using only queue0 use all memory for that. + * TX_Q_SEG_ALLOC_Q_LOWER xxx + */ + +#ifdef CONFIG_MPFS_MAC_SGMII + /* Reset PCS */ + + mac_putreg(priv, PCS_CONTROL, PCS_CONTROL_SOFTWARE_RESET); +#endif + + /* Configure MAC Network DMA Config register */ + + dma_config = (MPFS_MAC_RX_BUF_VALUE << DMA_CONFIG_RX_BUF_SIZE_SHIFT) | + DMA_CONFIG_TX_PBUF_SIZE | + (((uint32_t)0x3) << DMA_CONFIG_RX_PBUF_SIZE_SHIFT) | + (((uint32_t)0x04 & DMA_CONFIG_AMBA_BURST_LENGTH_MASK)); + +#if defined(CONFIG_MPFS_ETHMAC_64BIT_ADDRESS_MODE) + dma_config |= DMA_CONFIG_DMA_ADDR_BUS_WIDTH_1; +#endif + + mac_putreg(priv, DMA_CONFIG, dma_config); + + for (qi = 1; qi < MPFS_MAC_QUEUE_COUNT; qi++) + { + *priv->queue[qi].dma_rxbuf_size = MPFS_MAC_RX_BUF_VALUE; + } + + /* Disable the other queues as the GEM reset leaves them enabled with an + * address pointer of 0. Setting b0 of the queue pointer disables a queue. + */ + + addr = (uintptr_t)priv->queue[0].tx_desc_tab; + mac_putreg(priv, UPPER_TX_Q_BASE_ADDR, upper_32_bits(addr)); + addr = (uintptr_t)priv->queue[0].rx_desc_tab; + mac_putreg(priv, UPPER_RX_Q_BASE_ADDR, upper_32_bits(addr)); + + mac_putreg(priv, TRANSMIT_Q1_PTR, + ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U); + mac_putreg(priv, TRANSMIT_Q2_PTR, + ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U); + mac_putreg(priv, TRANSMIT_Q3_PTR, + ((uint32_t)(uint64_t)priv->queue[0].tx_desc_tab) | 1U); + mac_putreg(priv, RECEIVE_Q1_PTR, + ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U); + mac_putreg(priv, RECEIVE_Q2_PTR, + ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U); + mac_putreg(priv, RECEIVE_Q3_PTR, + ((uint32_t)(uint64_t)priv->queue[0].rx_desc_tab) | 1U); + + return OK; +} + +/**************************************************************************** + * Function: mpfs_macenable + * + * Description: + * Enable normal MAC operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_macenable(struct mpfs_ethmac_s *priv) +{ + uint32_t regval; + + /* Reset TX and RX */ + + mpfs_rxreset(priv); + mpfs_txreset(priv); + + /* enable queue-0 DMA */ + + uint32_t r = *priv->queue[0].rx_q_ptr & ~1u; + *priv->queue[0].rx_q_ptr = r; + + /* enable global irqs */ + + up_enable_irq(priv->mac_q_int[0]); + up_enable_irq(priv->mac_q_int[1]); + up_enable_irq(priv->mac_q_int[2]); + up_enable_irq(priv->mac_q_int[3]); + + /* Enable Rx and Tx, enable the statistics registers. */ + + regval = mac_getreg(priv, NETWORK_CONTROL); + regval |= (NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT | + NETWORK_CONTROL_STATS_WRITE_EN); + mac_putreg(priv, NETWORK_CONTROL, regval); + + /* enable queue-0 irqs */ + + *priv->queue[0].int_status = 0xffffffff; + *priv->queue[0].int_enable = INT_ALL; + + return OK; +} + +/**************************************************************************** + * Function: mpfs_mdcclock + * + * Description: + * Configure the MDC clocking + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_mdcclock(struct mpfs_ethmac_s *priv) +{ + uint32_t ncfgr; + uint32_t ncr; + uint32_t mck; + + /* Disable RX and TX momentarily */ + + ncr = mac_getreg(priv, NETWORK_CONTROL); + mac_putreg(priv, NETWORK_CONTROL, ncr & + ~(NETWORK_CONTROL_ENABLE_RECEIVE | + NETWORK_CONTROL_ENABLE_TRANSMIT)); + + /* Modify the NCFGR register based on the configured board MCK frequency */ + + ncfgr = mac_getreg(priv, NETWORK_CONFIG); + ncfgr &= ~(NETWORK_CONFIG_MDC_CLOCK_DIVISOR_MASK << + NETWORK_CONFIG_MDC_CLOCK_DIVISOR_SHIFT); + + mck = CONFIG_MPFS_ETHMAC_MDC_CLOCK_SOURCE_HZ; + DEBUGASSERT(mck <= 240000000); + + if (mck <= 20000000) + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_8; + } + else if (mck <= 40000000) + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_16; + } + else if (mck <= 80000000) + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_32; + } + else if (mck <= 120000000) + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_48; + } + else if (mck <= 160000000) + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_64; + } + else + { + ncfgr |= NETWORK_CONFIG_MDC_CLOCK_DIVISOR_96; + } + + mac_putreg(priv, NETWORK_CONFIG, ncfgr); + + /* Restore RX and TX enable settings */ + + mac_putreg(priv, NETWORK_CONTROL, ncr); +} + +/**************************************************************************** + * Function: mpfs_phyinit + * + * Description: + * Configure the PHY and determine the link speed/duplex. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +static int mpfs_phyinit(struct mpfs_ethmac_s *priv) +{ + int ret; + + /* Configure PHY clocking */ + + mpfs_mdcclock(priv); + + /* Check the PHY Address */ + + priv->phyaddr = CONFIG_MPFS_PHYADDR; + ret = mpfs_phyfind(priv, &priv->phyaddr); + if (ret < 0) + { + nerr("ERROR: mpfs_phyfind failed: %d\n", ret); + return ret; + } + + /* We have a PHY address. Reset the PHY */ + + mpfs_phyreset(priv); + return OK; +} + +/**************************************************************************** + * Function: mpfs_phyreset + * + * Description: + * Reset the PHY + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_phyreset(struct mpfs_ethmac_s *priv) +{ + uint16_t mcr; + int timeout; + int ret; + + ninfo(" mpfs_phyreset\n"); + + /* Enable management port */ + + mpfs_enablemdio(priv); + + /* Reset the PHY */ + + ret = mpfs_phywrite(priv, priv->phyaddr, GMII_MCR, + GMII_MCR_RESET); + if (ret < 0) + { + nerr("ERROR: mpfs_phywrite failed: %d\n", ret); + } + + /* Wait for the PHY reset to complete */ + + ret = -ETIMEDOUT; + for (timeout = 0; timeout < PHY_RESET_WAIT_COUNT; timeout++) + { + mcr = GMII_MCR_RESET; + int result = mpfs_phyread(priv, priv->phyaddr, + GMII_MCR, &mcr); + if (result < 0) + { + nerr("ERROR: Failed to read the MCR register: %d\n", ret); + ret = result; + } + else if ((mcr & GMII_MCR_RESET) == 0) + { + ret = OK; + break; + } + } + + /* Disable management port */ + + mpfs_disablemdio(priv); + return ret; +} + +/**************************************************************************** + * Function: mpfs_ethconfig + * + * Description: + * Configure the Ethernet interface for DMA operation. + * + * Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int mpfs_ethconfig(struct mpfs_ethmac_s *priv) +{ + int ret; + + ninfo("Entry\n"); + +#ifdef CONFIG_MPFS_PHYINIT + /* Perform any necessary, board-specific PHY initialization */ + + ret = mpfs_phy_boardinitialize(priv->intf); + if (ret < 0) + { + nerr("ERROR: Failed to initialize the PHY: %d\n", ret); + return ret; + } +#endif + + /* Reset the Ethernet block */ + + ninfo("Reset the Ethernet block\n"); + mpfs_ethreset(priv); + + /* Initialize the PHY */ + + ninfo("Initialize the PHY\n"); + ret = mpfs_phyinit(priv); + if (ret < 0) + { + return ret; + } + + /* Initialize the MAC and DMA */ + + ninfo("Initialize the MAC and DMA\n"); + ret = mpfs_macconfig(priv); + if (ret < 0) + { + return ret; + } + + /* Enable normal MAC operation */ + + ninfo("Enable normal operation\n"); + return mpfs_macenable(priv); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: mpfs_ethinitialize + * + * Description: + * Initialize the Ethernet driver for one interface. If the STM32 chip + * supports multiple Ethernet controllers, then board specific logic + * must implement arm_netinitialize() and call this function to initialize + * the desired interfaces. + * + * Parameters: + * intf - In the case where there are multiple EMACs, this value + * identifies which GMAC is to be initialized. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +int mpfs_ethinitialize(int intf) +{ + struct mpfs_ethmac_s *priv; + int ret = OK; + uintptr_t base; + + ninfo("intf: %d\n", intf); + + /* Get the interface structure associated with this interface number. */ + + DEBUGASSERT(intf < MPFS_NETHERNET); + priv = &g_mpfsethmac[intf]; + + /* Initialize the driver structure */ + + memset(priv, 0, sizeof(struct mpfs_ethmac_s)); + priv->dev.d_buf = g_pktbuf; /* Single packet buffer */ + priv->dev.d_ifup = mpfs_ifup; /* I/F up (new IP address) callback */ + priv->dev.d_ifdown = mpfs_ifdown; /* I/F down callback */ + priv->dev.d_txavail = mpfs_txavail; /* New TX data callback */ +#ifdef CONFIG_NET_MCASTGROUP + priv->dev.d_addmac = mpfs_addmac; /* Add multicast MAC address */ + priv->dev.d_rmmac = mpfs_rmmac; /* Remove multicast MAC address */ +#endif +#ifdef CONFIG_NETDEV_IOCTL + priv->dev.d_ioctl = mpfs_ioctl; /* Support PHY ioctl() calls */ +#endif + priv->dev.d_private = priv; /* Used to recover private state */ + priv->intf = intf; /* Remember the interface number */ + priv->regbase = g_regbases[intf]; + priv->mac_q_int[0] = g_irq_numbers[intf][0]; + priv->mac_q_int[1] = g_irq_numbers[intf][1]; + priv->mac_q_int[2] = g_irq_numbers[intf][2]; + priv->mac_q_int[3] = g_irq_numbers[intf][3]; + ninfo("mac @ 0x%" PRIx64 "\n", priv->regbase); + + base = priv->regbase; + priv->queue[0].int_status = (uint32_t *)(base + INT_STATUS); + priv->queue[1].int_status = (uint32_t *)(base + INT_Q1_STATUS); + priv->queue[2].int_status = (uint32_t *)(base + INT_Q2_STATUS); + priv->queue[3].int_status = (uint32_t *)(base + INT_Q3_STATUS); + priv->queue[0].int_mask = (uint32_t *)(base + INT_MASK); + priv->queue[1].int_mask = (uint32_t *)(base + INT_Q1_MASK); + priv->queue[2].int_mask = (uint32_t *)(base + INT_Q2_MASK); + priv->queue[3].int_mask = (uint32_t *)(base + INT_Q3_MASK); + priv->queue[0].int_enable = (uint32_t *)(base + INT_ENABLE); + priv->queue[1].int_enable = (uint32_t *)(base + INT_Q1_ENABLE); + priv->queue[2].int_enable = (uint32_t *)(base + INT_Q2_ENABLE); + priv->queue[3].int_enable = (uint32_t *)(base + INT_Q3_ENABLE); + priv->queue[0].int_disable = (uint32_t *)(base + INT_DISABLE); + priv->queue[1].int_disable = (uint32_t *)(base + INT_Q1_DISABLE); + priv->queue[2].int_disable = (uint32_t *)(base + INT_Q2_DISABLE); + priv->queue[3].int_disable = (uint32_t *)(base + INT_Q3_DISABLE); + priv->queue[0].rx_q_ptr = (uint32_t *)(base + RECEIVE_Q_PTR); + priv->queue[1].rx_q_ptr = (uint32_t *)(base + RECEIVE_Q1_PTR); + priv->queue[2].rx_q_ptr = (uint32_t *)(base + RECEIVE_Q2_PTR); + priv->queue[3].rx_q_ptr = (uint32_t *)(base + RECEIVE_Q3_PTR); + priv->queue[0].tx_q_ptr = (uint32_t *)(base + TRANSMIT_Q_PTR); + priv->queue[1].tx_q_ptr = (uint32_t *)(base + TRANSMIT_Q1_PTR); + priv->queue[2].tx_q_ptr = (uint32_t *)(base + TRANSMIT_Q2_PTR); + priv->queue[3].tx_q_ptr = (uint32_t *)(base + TRANSMIT_Q3_PTR); + priv->queue[0].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1); + priv->queue[1].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q1); + priv->queue[2].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q2); + priv->queue[3].dma_rxbuf_size = (uint32_t *)(base + DMA_RXBUF_SIZE_Q3); + + /* MPU hack for ETH DMA if not enabled by bootloader */ + +#ifdef CONFIG_MPFS_MPU_DMA_ENABLE +# ifdef CONFIG_MPFS_ETHMAC_0 + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH0_0); +# endif +# ifdef CONFIG_MPFS_ETHMAC_1 + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_ETH1_0); +# endif +#endif + + /* Allocate buffers */ + + ret = mpfs_buffer_initialize(priv, 0); + if (ret < 0) + { + nerr("ERROR: mpfs_buffer_initialize failed: %d\n", ret); + return ret; + } + + /* Attach the IRQ to the driver */ + + if (irq_attach(priv->mac_q_int[0], mpfs_interrupt_0, priv)) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + if (irq_attach(priv->mac_q_int[1], mpfs_interrupt_1, NULL)) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + if (irq_attach(priv->mac_q_int[2], mpfs_interrupt_2, NULL)) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + if (irq_attach(priv->mac_q_int[3], mpfs_interrupt_3, NULL)) + { + /* We could not attach the ISR to the interrupt */ + + return -EAGAIN; + } + + /* Enable clocking to the GMAC peripheral (just for mpfs_ifdown()) */ + + /* MAC HW clock enable and reset */ + + if (priv->regbase == MPFS_GEM0_LO_BASE) + { + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0, + SYSREG_SUBBLK_CLOCK_CR_MAC0); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + 0, SYSREG_SOFT_RESET_CR_MAC0); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + SYSREG_SOFT_RESET_CR_MAC0, 0); + } + + if (priv->regbase == MPFS_GEM1_LO_BASE) + { + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET, 0, + SYSREG_SUBBLK_CLOCK_CR_MAC1); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + 0, SYSREG_SOFT_RESET_CR_MAC1); + modifyreg32(MPFS_SYSREG_BASE + MPFS_SYSREG_SOFT_RESET_CR_OFFSET, + SYSREG_SOFT_RESET_CR_MAC1, 0); + } + + /* Put the interface in the down state. */ + + ret = mpfs_ifdown(&priv->dev); + if (ret < 0) + { + nerr("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, NET_LL_ETHERNET); + if (ret >= 0) + { + return ret; + } + + nerr("ERROR: netdev_register() failed: %d\n", ret); + +errout_with_buffers: + mpfs_buffer_free(priv, 0); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_MPFS_ETHMAC */ + +/**************************************************************************** + * Function: riscv_netinitialize + * + * Description: + * This is the "standard" network initialization logic called from the + * low-level initialization logic in riscv_initialize.c. If MPFS_NETHERNET + * greater than one, then board specific logic will have to supply a + * version of riscv_netinitialize() that calls mpfs_ethinitialize() with + * the appropriate interface number. + * + * Parameters: + * None. + * + * Returned Value: + * None. + * + * Assumptions: + * + ****************************************************************************/ + +#if !defined(CONFIG_NETDEV_LATEINIT) +void riscv_netinitialize(void) +{ + mpfs_ethinitialize(0); +} +#endif diff --git a/arch/risc-v/src/mpfs/mpfs_ethernet.h b/arch/risc-v/src/mpfs/mpfs_ethernet.h new file mode 100644 index 00000000000..5091df8d04a --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_ethernet.h @@ -0,0 +1,97 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_ethernet.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_MPFS_ETHERNET_H +#define __ARCH_RISCV_SRC_MPFS_MPFS_ETHERNET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "hardware/mpfs_ethernet.h" + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Function: mpfs_ethinitialize + * + * Description: + * Initialize the Ethernet driver for one interface. + * + * Input Parameters: + * intf - In the case where there are multiple EMACs, this value + * identifies which EMAC is to be initialized. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +int mpfs_ethinitialize(int intf); + +/**************************************************************************** + * Function: mpfs_phy_boardinitialize + * + * Description: + * 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 CONFIG_MPFS_PHYINIT is defined in the configuration + * then the board specific logic must provide mpfs_phyinitialize(); + * The MPFS Ethernet driver will call this function one time before + * it first uses the PHY. + * + * Input Parameters: + * intf - Always zero for now. + * + * Returned Value: + * OK on success; Negated errno on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_MPFS_PHYINIT +int mpfs_phy_boardinitialize(int intf); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_MPFS_MPFS_ETHERNET_H */