diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 01e027bf45f56..6772b2c27b410 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -316,6 +316,12 @@ endmenu # USB device controller driver (DCD) options endif # IMX9_USBDEV +config IMX9_ENET + bool "Ethernet" + default n + select ARCH_HAVE_PHY + select ARCH_HAVE_NETDEV_STATISTICS + config IMX9_GPIO_IRQ bool "GPIO Interrupt Support" default n @@ -783,6 +789,113 @@ config IMX9_LPSPI8_DMA endmenu # LPSPI Configuration +menu "Ethernet Configuration" + depends on IMX9_ENET + +config IMX9_ENET1 + bool "Ethernet MAC (non-QoS)" + depends on IMX9_ENET + default y + +config IMX9_ENET_NRXBUFFERS + int "Number of Rx buffers" + default 6 + +config IMX9_ENET_NTXBUFFERS + int "Number of Tx buffers" + default 2 + +config IMX9_ENET_USE_OTP_MAC + bool "Use MAC address from OCOTP" + default n + depends on IMX9_ENET + +config IMX9_ENET1_OTP_MAC_ADDR + hex "MAC address offset in OCOTP" + default 0x4ec + depends on IMX9_ENET_USE_OTP_MAC + +config IMX9_ENET1_PROMISCUOUS + bool "Set promiscuous mode" + depends on IMX9_ENET1 + default n + +choice + prompt "i.MX9 ENET1 interface type" + default IMX9_ENET1_RMII + depends on IMX9_ENET1 + +config IMX9_ENET1_RMII + bool "RMII" + +config IMX9_ENET1_RGMII + bool "RGMII" + +endchoice + +config IMX9_ENET1_PHY_AUTONEG + bool "ENET1 PHY autonegotiation enable" + default y + ---help--- + Enable PHY autonegotiation. If set to n, configure the speed + and duplex mode manually. Note that only disabling this doesn't + disable the autonegotiation completely; it just sets the MAC + speed and duplex, and disables autonegotiation advertisement + for other than the configured mode. To disable autonegotiation + completely, also set the FORCE_SPEED flag. + +choice + prompt "Select ENET1 PHY link duplex mode" + default IMX9_ENET1_PHY_FD + depends on !IMX9_ENET1_PHY_AUTONEG + +config IMX9_ENET1_PHY_FD + bool "Full Duplex" + +config IMX9_ENET1_PHY_HD + bool "Half Duplex" +endchoice + +choice + prompt "Select ENET1 PHY link speed" + default IMX9_ENET1_PHY_100MBPS if IMX9_ENET1_RMII + default IMX9_ENET1_PHY_1000MBPS if IMX9_ENET1_RGMII + depends on !IMX9_ENET1_PHY_AUTONEG + +config IMX9_ENET1_PHY_10MBPS + bool "10 MBPS" + +config IMX9_ENET1_PHY_100MBPS + bool "100 MBPS" + +config IMX9_ENET1_PHY_1000MBPS + bool "1000 MBPS" + depends on IMX9_ENET1_RGMII +endchoice + +config IMX9_ENET1_PHY_FORCE_SPEED + bool "Disable PHY autonegotiation and force speed and duplex" + depends on !IMX9_ENET1_PHY_AUTONEG + default n + ---help--- + This disables PHY autonegotiation completely. Note that + if the link partner has got autonegotiation enabled, the + duplex mode is not auto-detected by the link partner. Only + enable if you really know what you are doing! + +config IMX9_ENET1_PHYINIT + bool "Board-specific PHY Initialization for ENET1" + default n + ---help--- + Some boards require specialized initialization of the PHY before it + can be used. This may include such things as configuring GPIOs, + resetting the PHY, etc. If CONFIG_IMX9_ENET_PHYINIT is defined in + the configuration then the board specific logic must provide + imx9_phy_boardinitialize(); The i.MX9 ENET driver will call this + function one time before it first uses the PHY. + +endmenu # IMX9_ENET + endmenu # iMX Peripheral Selection endif # ARCH_CHIP_IMX9 diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 9bce853d4626a..1ededeea50fdb 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -59,3 +59,7 @@ endif ifeq ($(CONFIG_IMX9_DMA_ALLOC),y) CHIP_CSRCS += imx9_dma_alloc.c endif + +ifeq ($(CONFIG_IMX9_ENET),y) +CHIP_CSRCS += imx9_enet.c +endif diff --git a/arch/arm64/src/imx9/hardware/imx9_enet.h b/arch/arm64/src/imx9/hardware/imx9_enet.h new file mode 100644 index 0000000000000..67a7493722d5b --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_enet.h @@ -0,0 +1,646 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_enet.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_ARM64_SRC_IMX9_HARDWARE_IMX9_ENET_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_ENET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define IMX9_ENET_EIR_OFFSET 0x0004 /* Interrupt Event Register */ +#define IMX9_ENET_EIMR_OFFSET 0x0008 /* Interrupt Mask Register */ +#define IMX9_ENET_RDAR_OFFSET 0x0010 /* Receive Descriptor Active Register */ +#define IMX9_ENET_TDAR_OFFSET 0x0014 /* Transmit Descriptor Active Register */ +#define IMX9_ENET_ECR_OFFSET 0x0024 /* Ethernet Control Register */ +#define IMX9_ENET_MMFR_OFFSET 0x0040 /* MII Management Frame Register */ +#define IMX9_ENET_MSCR_OFFSET 0x0044 /* MII Speed Control Register */ +#define IMX9_ENET_MIBC_OFFSET 0x0064 /* MIB Control Register */ +#define IMX9_ENET_RCR_OFFSET 0x0084 /* Receive Control Register */ +#define IMX9_ENET_TCR_OFFSET 0x00c4 /* Transmit Control Register */ +#define IMX9_ENET_PALR_OFFSET 0x00e4 /* Physical Address Lower Register */ +#define IMX9_ENET_PAUR_OFFSET 0x00e8 /* Physical Address Upper Register */ +#define IMX9_ENET_OPD_OFFSET 0x00ec /* Opcode/Pause Duration Register */ +#define IMX9_ENET_TXIC_OFFSET 0x00f0 /* Transmit Interrupt Coalescing Register */ +#define IMX9_ENET_RXIC_OFFSET 0x0100 /* Receive Interrupt Coalescing Register */ +#define IMX9_ENET_IAUR_OFFSET 0x0118 /* Descriptor Individual Upper Address Register */ +#define IMX9_ENET_IALR_OFFSET 0x011c /* Descriptor Individual Lower Address Register */ +#define IMX9_ENET_GAUR_OFFSET 0x0120 /* Descriptor Group Upper Address Register */ +#define IMX9_ENET_GALR_OFFSET 0x0124 /* Descriptor Group Lower Address Register */ +#define IMX9_ENET_TFWR_OFFSET 0x0144 /* Transmit FIFO Watermark Register */ +#define IMX9_ENET_RDSR1_OFFSET 0x0160 /* Receive Descriptor Ring 1 Start Register */ +#define IMX9_ENET_TDSR1_OFFSET 0x0164 /* Transmit Buffer Descriptor Ring 1 Start Register */ +#define IMX9_ENET_MRBR1_OFFSET 0x0168 /* Maximum Receive Buffer Size Register - Ring 1 */ +#define IMX9_ENET_RDSR2_OFFSET 0x0170 /* Receive Descriptor Ring 2 Start Register */ +#define IMX9_ENET_TDSR2_OFFSET 0x0174 /* Transmit Buffer Descriptor Ring 2 Start Register */ +#define IMX9_ENET_MRBR2_OFFSET 0x0178 /* Maximum Receive Buffer Size Register - Ring 2 */ +#define IMX9_ENET_RDSR_OFFSET 0x0180 /* Receive Descriptor Ring Start Register */ +#define IMX9_ENET_TDSR_OFFSET 0x0184 /* Transmit Buffer Descriptor Ring Start Register */ +#define IMX9_ENET_MRBR_OFFSET 0x0188 /* Maximum Receive Buffer Size Register */ +#define IMX9_ENET_RSFL_OFFSET 0x0190 /* Receive FIFO Section Full Threshold */ +#define IMX9_ENET_RSEM_OFFSET 0x0194 /* Receive FIFO Section Empty Threshold */ +#define IMX9_ENET_RAEM_OFFSET 0x0198 /* Receive FIFO Almost Empty Threshold */ +#define IMX9_ENET_RAFL_OFFSET 0x019c /* Receive FIFO Almost Full Threshold */ +#define IMX9_ENET_TSEM_OFFSET 0x01a0 /* Transmit FIFO Section Empty Threshold */ +#define IMX9_ENET_TAEM_OFFSET 0x01a4 /* Transmit FIFO Almost Empty Threshold */ +#define IMX9_ENET_TAFL_OFFSET 0x01a8 /* Transmit FIFO Almost Full Threshold */ +#define IMX9_ENET_TIPG_OFFSET 0x01ac /* Transmit Inter-Packet Gap */ +#define IMX9_ENET_FTRL_OFFSET 0x01b0 /* Frame Truncation Length */ +#define IMX9_ENET_TACC_OFFSET 0x01c0 /* Transmit Accelerator Function Configuration */ +#define IMX9_ENET_RACC_OFFSET 0x01c4 /* Receive Accelerator Function Configuration */ + +#define IMX9_ENET_ATCR_OFFSET 0x0400 /* Timer Control Register */ +#define IMX9_ENET_ATVR_OFFSET 0x0404 /* Timer Value Register */ +#define IMX9_ENET_ATOFF_OFFSET 0x0408 /* Timer Offset Register */ +#define IMX9_ENET_ATPER_OFFSET 0x040c /* Timer Period Register */ +#define IMX9_ENET_ATCOR_OFFSET 0x0410 /* Timer Correction Register */ +#define IMX9_ENET_ATINC_OFFSET 0x0414 /* Time-Stamping Clock Period Register */ +#define IMX9_ENET_ATSTMP_OFFSET 0x0418 /* Timestamp of Last Transmitted Frame */ + +#define IMX9_ENET_TGSR_OFFSET 0x0604 /* Timer Global Status Register */ +#define IMX9_ENET_TCSR0_OFFSET 0x0608 /* Timer Control Status Register */ +#define IMX9_ENET_TCCR0_OFFSET 0x060c /* Timer Compare Capture Register */ +#define IMX9_ENET_TCSR1_OFFSET 0x0610 /* Timer Control Status Register */ +#define IMX9_ENET_TCCR1_OFFSET 0x0614 /* Timer Compare Capture Register */ +#define IMX9_ENET_TCSR2_OFFSET 0x0618 /* Timer Control Status Register */ +#define IMX9_ENET_TCCR2_OFFSET 0x061c /* Timer Compare Capture Register */ +#define IMX9_ENET_TCSR3_OFFSET 0x0620 /* Timer Control Status Register */ +#define IMX9_ENET_TCCR3_OFFSET 0x0624 /* Timer Compare Capture Register */ + +/* Register Addresses *******************************************************/ + +#define IMX9_ENET_EIR (IMX9_ENET_BASE+IMX9_ENET_EIR_OFFSET) +#define IMX9_ENET_EIMR (IMX9_ENET_BASE+IMX9_ENET_EIMR_OFFSET) +#define IMX9_ENET_RDAR (IMX9_ENET_BASE+IMX9_ENET_RDAR_OFFSET) +#define IMX9_ENET_TDAR (IMX9_ENET_BASE+IMX9_ENET_TDAR_OFFSET) +#define IMX9_ENET_ECR (IMX9_ENET_BASE+IMX9_ENET_ECR_OFFSET) +#define IMX9_ENET_MMFR (IMX9_ENET_BASE+IMX9_ENET_MMFR_OFFSET) +#define IMX9_ENET_MSCR (IMX9_ENET_BASE+IMX9_ENET_MSCR_OFFSET) +#define IMX9_ENET_MIBC (IMX9_ENET_BASE+IMX9_ENET_MIBC_OFFSET) +#define IMX9_ENET_RCR (IMX9_ENET_BASE+IMX9_ENET_RCR_OFFSET) +#define IMX9_ENET_TCR (IMX9_ENET_BASE+IMX9_ENET_TCR_OFFSET) +#define IMX9_ENET_PALR (IMX9_ENET_BASE+IMX9_ENET_PALR_OFFSET) +#define IMX9_ENET_PAUR (IMX9_ENET_BASE+IMX9_ENET_PAUR_OFFSET) +#define IMX9_ENET_OPD (IMX9_ENET_BASE+IMX9_ENET_OPD_OFFSET) +#define IMX9_ENET_IAUR (IMX9_ENET_BASE+IMX9_ENET_IAUR_OFFSET) +#define IMX9_ENET_IALR (IMX9_ENET_BASE+IMX9_ENET_IALR_OFFSET) +#define IMX9_ENET_GAUR (IMX9_ENET_BASE+IMX9_ENET_GAUR_OFFSET) +#define IMX9_ENET_GALR (IMX9_ENET_BASE+IMX9_ENET_GALR_OFFSET) +#define IMX9_ENET_TFWR (IMX9_ENET_BASE+IMX9_ENET_TFWR_OFFSET) +#define IMX9_ENET_RDSR (IMX9_ENET_BASE+IMX9_ENET_RDSR_OFFSET) +#define IMX9_ENET_TDSR (IMX9_ENET_BASE+IMX9_ENET_TDSR_OFFSET) +#define IMX9_ENET_MRBR (IMX9_ENET_BASE+IMX9_ENET_MRBR_OFFSET) +#define IMX9_ENET_RSFL (IMX9_ENET_BASE+IMX9_ENET_RSFL_OFFSET) +#define IMX9_ENET_RSEM (IMX9_ENET_BASE+IMX9_ENET_RSEM_OFFSET) +#define IMX9_ENET_RAEM (IMX9_ENET_BASE+IMX9_ENET_RAEM_OFFSET) +#define IMX9_ENET_RAFL (IMX9_ENET_BASE+IMX9_ENET_RAFL_OFFSET) +#define IMX9_ENET_TSEM (IMX9_ENET_BASE+IMX9_ENET_TSEM_OFFSET) +#define IMX9_ENET_TAEM (IMX9_ENET_BASE+IMX9_ENET_TAEM_OFFSET) +#define IMX9_ENET_TAFL (IMX9_ENET_BASE+IMX9_ENET_TAFL_OFFSET) +#define IMX9_ENET_TIPG (IMX9_ENET_BASE+IMX9_ENET_TIPG_OFFSET) +#define IMX9_ENET_FTRL (IMX9_ENET_BASE+IMX9_ENET_FTRL_OFFSET) +#define IMX9_ENET_TACC (IMX9_ENET_BASE+IMX9_ENET_TACC_OFFSET) +#define IMX9_ENET_RACC (IMX9_ENET_BASE+IMX9_ENET_RACC_OFFSET) + +#define IMX9_ENET_ATCR (IMX9_ENET_BASE+IMX9_ENET_ATCR_OFFSET) +#define IMX9_ENET_ATVR (IMX9_ENET_BASE+IMX9_ENET_ATVR_OFFSET) +#define IMX9_ENET_ATOFF (IMX9_ENET_BASE+IMX9_ENET_ATOFF_OFFSET) +#define IMX9_ENET_ATPER (IMX9_ENET_BASE+IMX9_ENET_ATPER_OFFSET) +#define IMX9_ENET_ATCOR (IMX9_ENET_BASE+IMX9_ENET_ATCOR_OFFSET) +#define IMX9_ENET_ATINC (IMX9_ENET_BASE+IMX9_ENET_ATINC_OFFSET) +#define IMX9_ENET_ATSTMP (IMX9_ENET_BASE+IMX9_ENET_ATSTMP_OFFSET) + +#define IMX9_ENET_TGSR (IMX9_ENET_BASE+IMX9_ENET_TGSR_OFFSET) +#define IMX9_ENET_TCSR0 (IMX9_ENET_BASE+IMX9_ENET_TCSR0_OFFSET) +#define IMX9_ENET_TCCR0 (IMX9_ENET_BASE+IMX9_ENET_TCCR0_OFFSET) +#define IMX9_ENET_TCSR1 (IMX9_ENET_BASE+IMX9_ENET_TCSR1_OFFSET) +#define IMX9_ENET_TCCR1 (IMX9_ENET_BASE+IMX9_ENET_TCCR1_OFFSET) +#define IMX9_ENET_TCSR2 (IMX9_ENET_BASE+IMX9_ENET_TCSR2_OFFSET) +#define IMX9_ENET_TCCR2 (IMX9_ENET_BASE+IMX9_ENET_TCCR2_OFFSET) +#define IMX9_ENET_TCSR3 (IMX9_ENET_BASE+IMX9_ENET_TCSR3_OFFSET) +#define IMX9_ENET_TCCR3 (IMX9_ENET_BASE+IMX9_ENET_TCCR3_OFFSET) + +/* Register Bit Definitions *************************************************/ + +/* Interrupt Event Register, Interrupt Mask Register */ + +#define ENET_RXB1 (1 << 0) /* Receive buffer interrupt, class 1 */ +#define ENET_RXF1 (1 << 1) /* Receive frame interrupt, class 1 */ +#define ENET_TXB1 (1 << 2) /* Transmit buffer interrupt, class 1 */ +#define ENET_TXF1 (1 << 3) /* Transmit frame interrupt, class 1 */ +#define ENET_RXB2 (1 << 4) /* Receive buffer interrupt, class 2 */ +#define ENET_RXF2 (1 << 5) /* Receive frame interrupt, class 2 */ +#define ENET_TXB2 (1 << 6) /* Transmit buffer interrupt, class 2 */ +#define ENET_TXF2 (1 << 7) /* Transmit frame interrupt, class 2 */ +#define ENET_RXFLUSH_0 (1 << 12) /* RX DMA Ring 0 flush indication */ +#define ENET_RXFLUSH_1 (1 << 13) /* RX DMA Ring 1 flush indication */ +#define ENET_RXFLUSH_2 (1 << 14) /* RX DMA Ring 2 flush indication */ +#define ENET_INT_TS_TIMER (1 << 15) /* Bit 15: Timestamp timer */ +#define ENET_INT_TS_AVAIL (1 << 16) /* Bit 16: Transmit timestamp available */ +#define ENET_INT_WAKEUP (1 << 17) /* Bit 17: Node wake-up request indication */ +#define ENET_INT_PLR (1 << 18) /* Bit 18: Payload receive error */ +#define ENET_INT_UN (1 << 19) /* Bit 19: Transmit FIFO underrun */ +#define ENET_INT_RL (1 << 20) /* Bit 20: Collision Retry Limit */ +#define ENET_INT_LC (1 << 21) /* Bit 21: Late Collision */ +#define ENET_INT_EBERR (1 << 22) /* Bit 22: Ethernet Bus Error */ +#define ENET_INT_MII (1 << 23) /* Bit 23: MII Interrupt */ +#define ENET_INT_RXB (1 << 24) /* Bit 24: Receive Buffer Interrupt */ +#define ENET_INT_RXF (1 << 25) /* Bit 25: Receive Frame Interrupt */ +#define ENET_INT_TXB (1 << 26) /* Bit 26: Transmit Buffer Interrupt */ +#define ENET_INT_TXF (1 << 27) /* Bit 27: Transmit Frame Interrupt */ +#define ENET_INT_GRA (1 << 28) /* Bit 28: Graceful Stop Complete */ +#define ENET_INT_BABT (1 << 29) /* Bit 29: Babbling Transmit Error */ +#define ENET_INT_BABR (1 << 30) /* Bit 30: Babbling Receive Error */ + /* Bit 31: Reserved */ + +/* Receive Descriptor Active Register */ + + /* Bits 0-23: Reserved */ +#define ENET_RDAR (1 << 24) /* Bit 24: Receive descriptor active */ + /* Bits 25-31: Reserved */ + +/* Transmit Descriptor Active Register */ + + /* Bits 0-23: Reserved */ +#define ENET_TDAR (1 << 24) /* Bit 24: Transmit descriptor active */ + /* Bits 25-31: Reserved */ + +/* Ethernet Control Register */ + +#define ENET_ECR_RESET (1 << 0) /* Bit 0: Ethernet MAC reset */ +#define ENET_ECR_ETHEREN (1 << 1) /* Bit 1: Ethernet enable */ +#define ENET_ECR_MAGICEN (1 << 2) /* Bit 2: Magic packet detection enable */ +#define ENET_ECR_SLEEP (1 << 3) /* Bit 3: Sleep mode enable */ +#define ENET_ECR_EN1588 (1 << 4) /* Bit 4: EN1588 enable */ +#define ENET_ECR_SPEED (1 << 5) /* Bit 5: 10/100-Mbit/s or 1000-Mbit/s mode */ +#define ENET_ECR_DBGEN (1 << 6) /* Bit 6: Debug enable */ + /* Bit 7: Reserved, always write 0 */ +#define ENET_ECR_DBSWP (1 << 8) /* Bit 8: Swap bytes; always write 1 after reset */ +#define ENET_ECR_SVLANEN (1 << 9) /* Bit 9: S-VLAN enable */ +#define ENET_ECR_VLANUSE2ND (1 << 10) /* Bit 10: VLAN use second tag */ +#define ENET_ECR_SVLANDBL (1 << 11) /* Bit 11: S-VLAN double tag */ +#define ENET_ECR_TXC_DLY (1 << 16) /* Bit 16: Transmit clock delay */ +#define ENET_ECR_RXC_DLY (1 << 17) /* Bit 17: Receive clock delay */ + /* Bits 12-15: Reserved, always write 0 */ +#define ENET_ECR_RESV_MASK (0x3ffff << 18) /* Reserved, always write 0x1c00 */ + +/* MII Management Frame Register */ + +#define ENET_MMFR_DATA_SHIFT (0) /* Bits 0-15: Management frame data */ +#define ENET_MMFR_DATA_MASK (0xffff << ENET_MMFR_DATA_SHIFT) +#define ENET_MMFR_TA_SHIFT (16) /* Bits 16-17: Turn around */ +#define ENET_MMFR_TA_MASK (0x3 << ENET_MMFR_TA_SHIFT) +#define ENET_MMFR_RA_SHIFT (18) /* Bits 18-22: Register address */ +#define ENET_MMFR_RA_MASK (0x1f << ENET_MMFR_RA_SHIFT) +#define ENET_MMFR_PA_SHIFT (23) /* Bits 23-27: PHY address */ +#define ENET_MMFR_PA_MASK (0x1f << ENET_MMFR_PA_SHIFT) +#define ENET_MMFR_OP_SHIFT (28) /* Bits 28-29: Operation code */ +#define ENET_MMFR_OP_MASK (0x3 << ENET_MMFR_OP_SHIFT) +# define ENET_MMFR_OP_WRNOTMII (0 << ENET_MMFR_OP_SHIFT) /* Write frame, not MII compliant */ +# define ENET_MMFR_OP_WRMII (1 << ENET_MMFR_OP_SHIFT) /* Write frame, MII management frame */ +# define ENET_MMFR_OP_RDMII (2 << ENET_MMFR_OP_SHIFT) /* Read frame, MII management frame */ +# define ENET_MMFR_OP_RDNOTMII (3 << ENET_MMFR_OP_SHIFT) /* Read frame, not MII compliant */ + +#define ENET_MMFR_ST_SHIFT (30) /* Bits 30-31: Start of frame delimiter */ +#define ENET_MMFR_ST_MASK (0x3 << ENET_MMFR_ST_SHIFT) + +/* MII Speed Control Register */ + + /* Bit 0: Reserved */ +#define ENET_MSCR_MII_SPEED_SHIFT (1) /* Bits 1-6: MII speed */ +#define ENET_MSCR_MII_SPEED_MASK (0x3f << ENET_MSCR_MII_SPEED_SHIFT) +# define ENET_MSCR_MII_SPEED_25MHz (0x4) /* Optimum value for IPS bus 25 MHz clock */ +# define ENET_MSCR_MII_SPEED_33MHz (0x6) /* Optimum value for IPS bus 33 MHz clock */ +# define ENET_MSCR_MII_SPEED_40MHz (0x7) /* Optimum value for IPS bus 40 MHz clock */ +# define ENET_MSCR_MII_SPEED_50MHz (0x9) /* Optimum value for IPS bus 50 MHz clock */ +# define ENET_MSCR_MII_SPEED_66MHz (0xd) /* Optimum value for IPS bus 60 MHz clock */ +#define ENET_MSCR_DIS_PRE (1 << 7) /* Bit 7: Disable preamble */ +#define ENET_MSCR_HOLDTIME_SHIFT (8) /* Bits 8-10: Holdtime on MDIO output */ +#define ENET_MSCR_HOLDTIME_MASK (0x7 << ENET_MSCR_HOLDTIME_SHIFT) +# define ENET_MSCR_HOLDTIME_1CYCLE (0 << ENET_MSCR_HOLDTIME_SHIFT) /* 1 internal module clock cycle */ +# define ENET_MSCR_HOLDTIME_2CYCLES (1 << ENET_MSCR_HOLDTIME_SHIFT) /* 2 internal module clock cycles */ +# define ENET_MSCR_HOLDTIME_3CYCLES (2 << ENET_MSCR_HOLDTIME_SHIFT) /* 3 internal module clock cycles */ +# define ENET_MSCR_HOLDTIME_8CYCLES (7 << ENET_MSCR_HOLDTIME_SHIFT) /* 8 internal module clock cycles */ + +/* MIB Control Register */ + + /* Bits 0-28: Reserved */ +#define ENET_MIBC_MIB_CLEAR (1 << 29) /* Bit 29: MIB clear */ +#define ENET_MIBC_MIB_IDLE (1 << 30) /* Bit 30: MIB idle */ +#define ENET_MIBC_MIB_DIS (1 << 31) /* Bit 31: Disable MIB logic */ + +/* Receive Control Register */ + +#define ENET_RCR_LOOP (1 << 0) /* Bit 0: Internal loopback */ +#define ENET_RCR_DRT (1 << 1) /* Bit 1: Disable receive on transmit */ +#define ENET_RCR_MII_MODE (1 << 2) /* Bit 2: Media independent interface mode */ +#define ENET_RCR_PROM (1 << 3) /* Bit 3: Promiscuous mode */ +#define ENET_RCR_BC_REJ (1 << 4) /* Bit 4: Broadcast frame reject */ +#define ENET_RCR_FCE (1 << 5) /* Bit 5: Flow control enable */ +#define ENET_RCR_RGMII_EN (1 << 6) /* Bit 6: RGMII mode enable */ + /* Bit 7: Reserved */ +#define ENET_RCR_RMII_MODE (1 << 8) /* Bit 8: RGMII mode enable */ +#define ENET_RCR_RMII_10T (1 << 9) /* Bit 9: Enables 10-Mbps mode of the RMII */ + /* Bits 10-11: Reserved */ +#define ENET_RCR_PADEN (1 << 12) /* Bit 12: Enable frame padding remove on receive */ +#define ENET_RCR_PAUFWD (1 << 13) /* Bit 13: Terminate/forward pause frames */ +#define ENET_RCR_CRCFWD (1 << 14) /* Bit 14: Terminate/forward received CRC */ +#define ENET_RCR_CFEN (1 << 15) /* Bit 15: MAC control frame enable */ +#define ENET_RCR_MAX_FL_SHIFT (16) /* Bits 16-29: Maximum frame length */ +#define ENET_RCR_MAX_FL_MASK (0x3fff << ENET_RCR_MAX_FL_SHIFT) +#define ENET_RCR_NLC (1 << 30) /* Bit 30: Payload length check disable */ +#define ENET_RCR_GRS (1 << 31) /* Bit 31: Graceful receive stopped */ + +/* Transmit Control Register */ + +#define ENET_TCR_GTS (1 << 0) /* Bit 0: Graceful transmit stop */ + /* Bit 1: Reserved */ +#define ENET_TCR_FDEN (1 << 2) /* Bit 2: Full duplex enable */ +#define ENET_TCR_TFC_PAUSE (1 << 3) /* Bit 3: Transmit frame control pause */ +#define ENET_TCR_RFC_PAUSE (1 << 4) /* Bit 4: Receive frame control pause */ +#define ENET_TCR_ADDSEL_SHIFT (5) /* Bits 5-7: Source MAC address select on transmit */ +#define ENET_TCR_ADDSEL_MASK (0x7 << ENET_TCR_ADDSEL_SHIFT) +#define ENET_TCR_ADDSEL_PADDR12 (0 << ENET_TCR_ADDSEL_SHIFT) +#define ENET_TCR_ADDINS (1 << 8) /* Bit 8: Set MAC address on transmit */ +#define ENET_TCR_CRCFWD (1 << 9) /* Bit 9: Forward frame from application with CRC */ + /* Bits 10-31: Reserved, 10 must be written to 0 */ + +/* Physical Address Lower/Upper Register (32-bits of 48-address) */ + +/* Physical Address Upper Register */ + +#define ENET_PAUR_TYPE_SHIFT (0) /* Bits 0-15: Type field in PAUSE frame */ +#define ENET_PAUR_TYPE_MASK (0xffff << ENET_PAUR_TYPE_MASK) +#define ENET_PAUR_PADDR2_SHIFT (16) /* Bits 16-31: Bytes 4 and 5 of the 6-byte address */ +#define ENET_PAUR_PADDR2_MASK (0xffff << ENET_PAUR_PADDR2_SHIFT) + +/* Opcode/Pause Duration Register */ + +#define ENET_OPD_PAUSE_DUR_SHIFT (0) /* Bits 0-15: Pause duration */ +#define ENET_OPD_PAUSE_DUR_MASK (0xffff << ENET_OPD_PAUSE_DUR_SHIFT) +#define ENET_OPD_OPCODE_SHIFT (16) /* Bits 16-31: Opcode field in PAUSE frames */ +#define ENET_OPD_OPCODE_MASK (0xffff << ENET_OPD_OPCODE_SHIFT) + +/* Descriptor Individual Upper/Lower Address Register + * (64-bit address in two 32-bit registers) + */ + +/* Descriptor Group Upper/Lower Address Register + * (64-bit address in two 32-bit registers) + */ + +/* Transmit Interrupt Coalescing Register */ + +#define ENET_TXIC_ICTT_SHIFT (0) /* Bits 0-15: Interrupt coalescing timer threshold */ +#define ENET_TXIC_ICTT_SHIFT_MASK (0xffff << ENET_TXIC_ICTT_SHIFT) + /* Bits 16-19: Reserved */ +#define ENET_TXIC_ICFT_SHIFT (20) /* Bits 0-15: Interrupt coalescing timer threshold */ +#define ENET_TXIC_ICFT_SHIFT_MASK (0xff << ENET_TXIC_ICFT_SHIFT) +#define ENET_TXIC_ICTT_ICCS (1 << 30) /* Bit 30: Interrupt Coalescing Timer Clock Source Select */ +#define ENET_TXIC_ICTT_ICEN (1 << 31) /* Bit 31: Eable/disabel Interrupt Coalescing */ + +/* Receive Interrupt Coalescing Register */ + +#define ENET_RXIC_ICTT_SHIFT (0) /* Bits 0-15: Interrupt coalescing timer threshold */ +#define ENET_RXIC_ICTT_SHIFT_MASK (0xffff << ENET_TXIC_ICTT_SHIFT) + /* Bits 16-19: Reserved */ +#define ENET_RXIC_ICFT_SHIFT (20) /* Bits 0-15: Interrupt coalescing timer threshold */ +#define ENET_RXIC_ICFT_SHIFT_MASK (0xff << ENET_TXIC_ICFT_SHIFT) +#define ENET_RXIC_ICTT_ICCS (1 << 30) /* Bit 30: Interrupt Coalescing Timer Clock Source Select */ +#define ENET_RXIC_ICTT_ICEN (1 << 31) /* Bit 31: Eable/disabel Interrupt Coalescing */ + +/* Transmit FIFO Watermark Register */ + +#define ENET_TFWR_TFWR_SHIFT (0) /* Bits 0-5: Transmit FIFO write */ + /* Bits 6-7: Reserved */ +#define ENET_TFWR_TFWR_MASK (0x3f << ENET_TFWR_TFWR_SHIFT) +#define ENET_TFWR_STRFWD (1 << 8) /* Bit 8: Store and forward enable */ + /* Bits 9-31: Reserved */ + +/* Receive Descriptor Ring Start Register */ + + /* Bits 0-2: Reserved */ +#define ENET_RDSR_SHIFT (3) /* Bits 3-31: Start of the receive buffer descriptor queue */ +#define ENET_RDSR_MASK (0xfffffff8) + +/* Transmit Buffer Descriptor Ring Start Register */ + + /* Bits 0-2: Reserved */ +#define ENET_TDSR_SHIFT (3) /* Bits 3-31: Start of the transmit buffer descriptor queue */ +#define ENET_TDSR_MASK (0xfffffff8) + +/* Maximum Receive Buffer Size Register */ + + /* Bits 14-31: Reserved */ +#define ENET_MRBR_SHIFT (4) /* Bits 4-13: Receive buffer size in bytes */ +#define ENET_MRBR_MASK (0x3ff << ENET_MRBR_SHIFT) + /* Bits 0-3: Reserved */ + +/* Receive FIFO Section Full Threshold */ + + /* Bits 10-31: Reserved */ +#define ENET_RSFL_SHIFT (0) /* Bits 0-9: Value of receive FIFO section full threshold */ +#define ENET_RSFL_MASK (0x3ff << ENET_RSFL_SHIFT) + +/* Receive FIFO Section Empty Threshold */ + +#define ENET_RSEM_RX_EMPTY_SHIFT (0) /* Bits 0-9: Value of the receive FIFO section empty threshold */ +#define ENET_RSEM_RX_EMPTY_MASK (0x3ff << ENET_RSEM_RX_EMPTY_SHIFT) + /* Bits 10-15: Reserved */ +#define ENET_RSEM_SEC_EMPTY_SHIFT (16) /* Bits 16-20: RX Status FIFO Section Empty Threshold */ +#define ENET_RSEM_SEC_EMPTY_MASK (0x1f << ENET_RSEM_SEC_EMPTY_SHIFT) + +/* Receive FIFO Almost Empty Threshold */ + +#define ENET_RAEM_SHIFT (0) /* Bits 0-9: Value of the receive FIFO almost empty threshold */ +#define ENET_RAEM_MASK (0x3ff << ENET_RAEM_SHIFT) + /* Bits 10-31: Reserved */ + +/* Receive FIFO Almost Full Threshold */ + +#define ENET_RAFL_SHIFT (0) /* Bits 0-9: Value of the receive FIFO almost full threshold */ +#define ENET_RAFL_MASK (0x3ff << ENET_RAFL_SHIFT) + /* Bits 10-31: Reserved */ + +/* Transmit FIFO Section Empty Threshold */ + +#define ENET_TSEM_SHIFT (0) /* Bits 0-9: Value of the transmit FIFO section empty threshold */ +#define ENET_TSEM_MASK (0x3ff << ENET_TSEM_SHIFT) + /* Bits 10-31: Reserved */ + +/* Transmit FIFO Almost Empty Threshold */ + +#define ENET_TAEM_SHIFT (0) /* Bits 0-9: Value of the transmit FIFO section empty threshold */ +#define ENET_TAEM_MASK (0x3ff << ENET_TAEM_SHIFT) + /* Bits 10-31: Reserved */ + +/* Transmit FIFO Almost Full Threshold */ + +#define ENET_TAFL_SHIFT (0) /* Bits 0-9: Value of the transmit FIFO section empty threshold */ +#define ENET_TAFL_MASK (0x3ff << ENET_TAFL_SHIFT) + /* Bits 10-31: Reserved */ + +/* Transmit Inter-Packet Gap */ + +#define ENET_TIPG_SHIFT (0) /* Bits 0-4: Value of the transmit FIFO section empty threshold */ +#define ENET_TIPG_MASK (0x1f << ENET_TIPG_SHIFT) + /* Bits 5-31: Reserved */ + +/* Frame Truncation Length */ + +#define ENET_FTRL_SHIFT (0) /* Bits 0-13: Value of the transmit FIFO section empty threshold */ +#define ENET_FTRL_MASK (0x3fff << ENET_FTRL_SHIFT) + /* Bits 14-31: Reserved */ + +/* Transmit Accelerator Function Configuration */ + +#define ENET_TACC_SHIFT16 (1 << 0) /* Bit 0: TX FIFO shift-16 */ + /* Bits 1-2: Reserved */ +#define ENET_TACC_IPCHK (1 << 3) /* Bit 3: Enables insertion of IP header checksum */ +#define ENET_TACC_PROCHK (1 << 4) /* Bit 4: Enables insertion of protocol checksum */ + /* Bits 5-31: Reserved */ + +/* Receive Accelerator Function Configuration */ + +#define ENET_RACC_PADREM (1 << 0) /* Bit 0: Enable padding removal for short IP frames */ +#define ENET_RACC_IPDIS (1 << 1) /* Bit 1: Enable discard of frames with wrong IPv4 header checksum */ +#define ENET_RACC_PRODIS (1 << 2) /* Bit 2: Enable discard of frames with wrong protocol checksum */ + /* Bits 3-5: Reserved */ +#define ENET_RACC_LINEDIS (1 << 6) /* Bit 6: Enable discard of frames with MAC layer errors */ +#define ENET_RACC_SHIFT16 (1 << 7) /* Bit 7: RX FIFO shift-16 */ + /* Bits 8-31: Reserved */ + +/* Timer Control Register */ + +#define ENET_ATCR_EN (1 << 0) /* Bit 0: Enable timer */ + /* Bit 1: Reserved */ +#define ENET_ATCR_OFFEN (1 << 2) /* Bit 2: Enable one-shot offset event */ +#define ENET_ATCR_OFFRST (1 << 3) /* Bit 3: Reset timer on offset event */ +#define ENET_ATCR_PEREN (1 << 4) /* Bit 4: Enable periodical event */ + /* Bits 5-6: Reserved */ +#define ENET_ATCR_PINPER (1 << 7) /* Bit 7: Enables event signal output assertion on period event */ + /* Bit 8: Reserved */ +#define ENET_ATCR_RESTART (1 << 9) /* Bit 9: Reset timer */ + /* Bit 10: Reserved */ +#define ENET_ATCR_CAPTURE (1 << 11) /* Bit 11: Capture timer value */ + /* Bit 12: Reserved */ +#define ENET_ATCR_SLAVE (1 << 13) /* Bit 13: Enable timer slave mode */ + /* Bits 14-31: Reserved */ + +/* Timer Value Register (32-bit timer value) */ + +/* Timer Offset Register (32-bit offset value) */ + +/* Timer Period Register (32-bit timer period) */ + +/* Timer Correction Register */ + +#define ENET_ATCOR_MASK (0x7fffffff) /* Bits 0-3: Correction counter wrap-around value */ + /* Bit 31: Reserved */ + +/* Time-Stamping Clock Period Register */ + +#define ENET_ATINC_INC_SHIFT (0) /* Bits 0-6: Clock period of the timestamping clock (ts_clk) in nanoseconds */ +#define ENET_ATINC_INC_MASK (0x7f << ENET_ATINC_INC_SHIFT) + /* Bit 7: Reserved */ +#define ENET_ATINC_INC_CORR_SHIFT (8) /* Bits 8-14: Correction increment value */ +#define ENET_ATINC_INC_CORR_MASK (0x7f << ENET_ATINC_INC_CORR_SHIFT) + /* Bits 15-31: Reserved */ + +/* Timestamp of Last Transmitted Frame (32-bit timestamp) */ + +/* Timer Global Status Register */ + +#define ENET_TGSR_TF0 (1 << 0) /* Bit 0: Copy of Timer Flag for channel 0 */ +#define ENET_TGSR_TF1 (1 << 1) /* Bit 1: Copy of Timer Flag for channel 1 */ +#define ENET_TGSR_TF2 (1 << 2) /* Bit 2: Copy of Timer Flag for channel 2 */ +#define ENET_TGSR_TF3 (1 << 3) /* Bit 3: Copy of Timer Flag for channel 3 */ + /* Bits 4-31: Reserved */ + +/* Timer Control Status Register n */ + +#define ENET_TCSR_TDRE (1 << 0) /* Bit 0: Timer DMA Request Enable */ + /* Bit 1: Reserved */ +#define ENET_TCSR_TMODE_SHIFT (2) /* Bits 2-5: Timer Mode */ +#define ENET_TCSR_TMODE_MASK (0xf << ENET_TCSR_TMODE_SHIFT) +# define ENET_TCSR_TMODE_DISABLED (0 << ENET_TCSR_TMODE_SHIFT) /* Disabled */ +# define ENET_TCSR_TMODE_ICRISING (1 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on rising edge */ +# define ENET_TCSR_TMODE_ICFALLLING (2 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on falling edge */ +# define ENET_TCSR_TMODE_ICBOTH (3 << ENET_TCSR_TMODE_SHIFT) /* Input Capture on both edges */ +# define ENET_TCSR_TMODE_OCSW (4 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, S/W only */ +# define ENET_TCSR_TMODE_OCTOGGLE (5 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, toggle on compare */ +# define ENET_TCSR_TMODE_OCCLR (6 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, clear on compare */ +# define ENET_TCSR_TMODE_OCSET (7 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, set on compare */ +# define ENET_TCSR_TMODE_OCSETCLR (9 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, set on compare, clear on overflow */ +# define ENET_TCSR_TMODE_OCCLRSET (10 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, clear on compare, set on overflow */ +# define ENET_TCSR_TMODE_PCPULSEL (14 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, pulse low on compare */ +# define ENET_TCSR_TMODE_PCPULSEH (15 << ENET_TCSR_TMODE_SHIFT) /* Output Compare, pulse high on compare */ + +#define ENET_TCSR_TIE (1 << 6) /* Bit 6: Timer interrupt enable */ +#define ENET_TCSR_TF (1 << 7) /* Bit 7: Timer Flag */ + /* Bits 8-31: Reserved */ + +/* Timer Compare Capture Register (32-bit compare value) */ + +/* Buffer Descriptors *******************************************************/ + +/* Endian-independent descriptor offsets */ + +#define DESC_STATUS1_OFFSET (0) +#define DESC_LENGTH_OFFSET (2) +#define DESC_DATAPTR_OFFSET (4) +#define DESC_LEGACY_LEN (8) + +#define DESC_STATUS2_OFFSET (8) +#define DESC_LENPROTO_OFFSET (12) +#define DESC_CHECKSUM_OFFSET (14) +#define DESC_BDU_OFFSET (16) +#define DESC_TIMESTAMP_OFFSET (20) +#define DESC_ENHANCED_LEN (32) + +/* Legacy/Common TX Buffer Descriptor Bit Definitions. */ + +#define IMX9_USE_DBSWAP + +# define TXDESC_TC (1 << 10) /* Common */ +# define TXDESC_L (1 << 11) /* Common */ +# define TXDESC_TO2 (1 << 12) /* Common */ +# define TXDESC_W (1 << 13) /* Common */ +# define TXDESC_TO1 (1 << 14) /* Common */ +# define TXDESC_R (1 << 15) /* Common */ + +/* Enhanced TX Buffer Descriptor Bit Definitions */ + +# define TXDESC_TSE (1 << 8) +# define TXDESC_OE (1 << 9) +# define TXDESC_LCE (1 << 10) +# define TXDESC_FE (1 << 11) +# define TXDESC_EE (1 << 12) +# define TXDESC_UE (1 << 13) +# define TXDESC_TXE (1 << 15) + +# define TDXESC_FTYPE_N (0 << 20) +# define TDXESC_FTYPE_A (1 << 20) +# define TDXESC_FTYPE_B (2 << 20) +# define TXDESC_UTLT (1 << 24) +# define TXDESC_IINS (1 << 27) +# define TXDESC_PINS (1 << 28) +# define TXDESC_TS (1 << 29) +# define TXDESC_INT (1 << 30) + +# define TXDESC_BDU (1 << 31) + +/* Legacy (and Common) RX Buffer Descriptor Bit Definitions */ + +# define RXDESC_TR (1 << 0) +# define RXDESC_OV (1 << 1) +# define RXDESC_CR (1 << 2) +# define RXDESC_NO (1 << 4) +# define RXDESC_LG (1 << 5) +# define RXDESC_MC (1 << 6) +# define RXDESC_BC (1 << 7) +# define RXDESC_M (1 << 8) +# define RXDESC_L (1 << 11) +# define RXDESC_R02 (1 << 12) +# define RXDESC_W (1 << 13) +# define RXDESC_R01 (1 << 14) +# define RXDESC_E (1 << 15) + +/* Enhanced (only) RX Buffer Descriptor Bit Definitions */ + +# define RXDESC_FRAG (1 << 0) +# define RXDESC_IPV6 (1 << 1) +# define RXDESC_VLAN (1 << 2) +# define RXDESC_PCR (1 << 4) +# define RXDESC_ICE (1 << 5) +# define RXDESC_INT (1 << 23) +# define RXDESC_UC (1 << 24) +# define RXDESC_CE (1 << 25) +# define RXDESC_PE (1 << 26) +# define RXDESC_ME (1 << 31) + +# define RXDESC_BDU (1 << 31) + +#define RXDESC_STATUS1_ERRORS (RXDESC_TR | RXDESC_OV | RXDESC_CR | RXDESC_NO | RXDESC_LG) +#define RXDESC_STATUS2_ERRORS (RXDESC_CE | RXDESC_PE | RXDESC_ME) + +#define TXDESC_STATUS2_ERRORS (TXDESC_TSE | TXDESC_OE | TXDESC_LCE | TXDESC_FE | TXDESC_EE | TXDESC_UE | TXDESC_TXE) + +/* From ref manual TDSR/RDSR description + * For optimal performance the pointer should be 512-bit aligned, that is, + * evenly divisible by 64. NOTE: This is also cache-line size + */ + +#define ENET_ALIGN 64 +#define ENET_ALIGN_MASK (ENET_ALIGN - 1) +#define ENET_ALIGN_UP(n) (((n) + ENET_ALIGN_MASK) & ~ENET_ALIGN_MASK) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Buffer Descriptors *******************************************************/ + +/* Little endian descriptor order, with ECR[DBSWP] = 1 */ + +struct enet_desc_s +{ + uint16_t length; /* Data length */ + uint16_t status1; /* Control and status */ + uint32_t data; /* Buffer address */ + uint32_t status2; /* Extended status */ + uint16_t checksum; /* Payload checksum */ + uint16_t lenproto; /* Header length + Protocol type */ + uint32_t bdu; /* BDU */ + uint32_t timestamp; /* Time stamp */ + uint32_t reserved1; /* unused */ + uint32_t reserved2; /* unused */ +}; + +/* This is a 64-byte descriptor pair used for TX. Two descriptors are used + * for each TX transmission to match descriptors used for a single + * transmission on a a single cache line + */ + +struct enet_txdesc_s +{ + struct enet_desc_s d1; + struct enet_desc_s d2; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#endif /* __ARCH_ARM_SRC_IMX9_HARDWARE_IMX9_ENET_H */ diff --git a/arch/arm64/src/imx9/imx9_enet.c b/arch/arm64/src/imx9/imx9_enet.c new file mode 100644 index 0000000000000..b6a501362bfc5 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_enet.c @@ -0,0 +1,3215 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_enet.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 +#include +#include + +#ifdef CONFIG_NET_PKT +# include +#endif + +#include + +#include "arm64_internal.h" +#include "chip.h" +#include "hardware/imx9_enet.h" +#include "imx9_enet.h" + +#include "imx9_ccm.h" +#include "imx9_iomuxc.h" +#include "hardware/imx9_ccm.h" +#include "hardware/imx9_pinmux.h" + +#ifdef CONFIG_IMX9_ENET + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* If processing is not done at the interrupt level, then work queue support + * is required. + */ + +#if !defined(CONFIG_SCHED_LPWORK) +# error LPWORK queue support is required +#endif + +#define ETHWORK LPWORK + +/* We need at least two TX buffers for reliable operation */ + +#if CONFIG_IMX9_ENET_NTXBUFFERS < 1 +#define IMX9_ENET_NTXBUFFERS 1 +#else +#define IMX9_ENET_NTXBUFFERS CONFIG_IMX9_ENET_NTXBUFFERS +#endif + +/* We need an even number of RX buffers, since RX descriptors are + * freed for the DMA in pairs due to two descriptors always fitting + * in one cache line (cahce line size is 64, descriptor size is 32) + */ + +#if CONFIG_IMX9_ENET_NRXBUFFERS < 2 +#define IMX9_ENET_NRXBUFFERS 2 +#elif CONFIG_IMX9_ENET_NRXBUFFERS & 1 +#define IMX9_ENET_NRXBUFFERS (CONFIG_IMX9_ENET_NRXBUFFERS + 1) +#else +#define IMX9_ENET_NRXBUFFERS CONFIG_IMX9_ENET_NRXBUFFERS +#endif + +#define nitems(_a) (sizeof(_a) / sizeof(0[(_a)])) + +#define ALIGNED_BUFSIZE ENET_ALIGN_UP(CONFIG_NET_ETH_PKTSIZE + \ + CONFIG_NET_GUARDSIZE) + +/* TX timeout = 1 second */ + +#define IMX9_TXTIMEOUT (CLK_TCK) +#define MII_MAXPOLLS (0x1ffff) +#define LINK_WAITUS (100 * 1000) +#define LINK_NLOOPS (50) + +/* PHY reset tim in loop counts */ + +#define PHY_RESET_WAIT_COUNT (10) + +/* Estimate the MII_SPEED in order to get an MDC close to 2.5MHz, + * based on the internal module (ENET) clock: + + * MII clock frequency = 133 MHz / ((26 + 1) x 2) = 2.5 MHz + * + * TODO: This is hard-coded for now, could be properly calculated + */ + +#define IMX9_MII_SPEED 26 + +/* Interrupt groups */ + +#define RX_INTERRUPTS (ENET_INT_RXF | ENET_INT_RXB) +#define TX_INTERRUPTS ENET_INT_TXF +#define ERROR_INTERRUPTS (ENET_INT_UN | ENET_INT_RL | ENET_INT_LC | \ + ENET_INT_EBERR | ENET_INT_BABT | ENET_INT_BABR) + +/* The subset of errors that require us to reset the hardware - this list + * may need to be revisited if it's found that some error above leads to a + * locking up of the Ethernet interface. + */ + +#define CRITICAL_ERROR (ENET_INT_UN | ENET_INT_RL | ENET_INT_EBERR) + +/* This is a helper pointer for accessing + * the contents of the Ethernet header + */ + +#define BUF ((struct eth_hdr_s *)priv->dev.d_buf) + +#define IMX93_OCOTP_UID_OFFSET 0xc0 + +#define MMD1 1 +#define MMD1_PMA_STATUS1 1 +#define MMD1_PS1_RECEIVE_LINK_STATUS (1 << 2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum phy_type_t +{ + PHY_NONE = 0, + PHY_RMII = 1, + PHY_RGMII = 2, +}; + +/* The imx9_driver_s encapsulates all state information for + * a single hardware interface + */ + +struct imx9_driver_s +{ + struct net_driver_s dev; /* Interface understood by the network */ + const uint32_t base; /* Base address of ENET controller */ + const int clk_gate; /* Enet clock gate */ + const int irq; /* Enet interrupt */ + const struct phy_desc_s * phy_list; /* Supported PHYs for this IF */ + const int n_phys; /* Number of supported PHYs */ + struct enet_txdesc_s * const txdesc; /* A pointer to the list of TX descriptor */ + struct enet_desc_s * const rxdesc; /* A pointer to the list of RX descriptors */ + const uintptr_t buffer_pool; /* DMA buffer pool */ +#ifdef CONFIG_IMX9_ENET_USE_OTP_MAC + const off_t otp_mac_off; /* MAC address offset in OTP */ +#endif + const bool promiscuous; /* Set promiscuous mode */ + const enum phy_type_t phy_type; /* PHY type */ + const bool autoneg; /* Phy autonegotiation enabled */ + const bool force_speed; /* Disable autonegotiation and force speed */ + bool full_duplex; /* Manually set to full duplex mode */ + bool s_10mbps; /* Manually set to 10 MBPS */ + bool s_100mbps; /* Manually set to 100 M0BPS */ + bool s_1000mbps; /* Manually set to 1GBPS */ + const struct phy_desc_s * cur_phy; /* Currently selected phy */ + bool bifup; /* true:ifup false:ifdown */ + uint8_t txhead; /* The next TX descriptor to use */ + uint8_t rxtail; /* The next RX descriptor to use */ + uint8_t phyaddr; /* Selected PHY address */ + struct wdog_s txtimeout; /* TX timeout timer */ + uint32_t ints; /* Enabled interrupts */ + struct work_s irqwork; /* For deferring interrupt work to the work queue */ + struct work_s pollwork; /* For deferring poll work to the work queue */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Utility functions */ + +static inline uint32_t imx9_enet_getreg32(struct imx9_driver_s *priv, + uint32_t offset); +static inline void imx9_enet_putreg32(struct imx9_driver_s *priv, + uint32_t value, uint32_t offset); + +static inline void imx9_enet_modifyreg32(struct imx9_driver_s *priv, + unsigned int offset, + uint32_t clearbits, + uint32_t setbits); + +/* Common TX logic */ + +static bool imx9_txringfull(struct imx9_driver_s *priv); +static int imx9_transmit(struct imx9_driver_s *priv, + uint32_t *buf_swap); +static int imx9_txpoll(struct net_driver_s *dev); + +/* Interrupt handling */ + +static void imx9_dispatch(struct imx9_driver_s *priv); +static void imx9_receive(struct imx9_driver_s *priv); +static void imx9_txdone(struct imx9_driver_s *priv); + +static void imx9_enet_interrupt_work(void *arg); +static int imx9_enet_interrupt(int irq, void *context, void *arg); + +/* Watchdog timer expirations */ + +static void imx9_txtimeout_work(void *arg); +static void imx9_txtimeout_expiry(wdparm_t arg); + +/* NuttX callback functions */ + +static int imx9_ifup(struct net_driver_s *dev); +static int imx9_ifdown(struct net_driver_s *dev); + +static void imx9_txavail_work(void *arg); +static int imx9_txavail(struct net_driver_s *dev); + +/* Internal ifup function that allows phy reset to be optional */ + +static int imx9_ifup_action(struct net_driver_s *dev, bool resetphy); + +#ifdef CONFIG_NET_MCASTGROUP +static int imx9_addmac(struct net_driver_s *dev, const uint8_t *mac); +static int imx9_rmmac(struct net_driver_s *dev, const uint8_t *mac); +#endif + +#ifdef CONFIG_NETDEV_IOCTL +static int imx9_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/* PHY/MII support */ + +static int imx9_phy_is(struct imx9_driver_s *priv, const char *name); +static int imx9_determine_phy(struct imx9_driver_s *priv); + +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int imx9_phyintenable(struct imx9_driver_s *priv); +#endif +static inline void imx9_initmii(struct imx9_driver_s *priv); +static int imx9_writemii(struct imx9_driver_s *priv, uint8_t regaddr, + uint16_t data); +static int imx9_readmii(struct imx9_driver_s *priv, uint8_t regaddr, + uint16_t *data); +static int imx9_initphy(struct imx9_driver_s *priv, bool renogphy); + +static int imx9_readmmd(struct imx9_driver_s *priv, uint8_t mmd, + uint16_t regaddr, uint16_t *data); +#if 0 +static int imx9_writemmd(struct imx9_driver_s *priv, uint8_t mmd, + uint16_t regaddr, uint16_t data); +#endif + +/* Initialization */ + +static void imx9_initbuffers(struct imx9_driver_s *priv); +static void imx9_reset(struct imx9_driver_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_IMX9_ENET1 + +/* If the board didn't provide a list of known PHYs, we can still work with + * autonegotiation disabled and setting the speed manually + */ + +#ifndef BOARD_ENET1_PHY_LIST +#define BOARD_ENET1_PHY_LIST {} +#endif + +static const struct phy_desc_s g_enet1_phy_list[] = BOARD_ENET1_PHY_LIST; + +/* The DMA descriptors */ + +static struct enet_txdesc_s g_enet1_tx_desc_pool[IMX9_ENET_NTXBUFFERS] + aligned_data(ENET_ALIGN); +static struct enet_desc_s g_enet1_rx_desc_pool[IMX9_ENET_NRXBUFFERS] + aligned_data(ENET_ALIGN); + +/* The DMA buffers */ + +static uint8_t g_enet_1_buffer_pool + [IMX9_ENET_NTXBUFFERS + IMX9_ENET_NRXBUFFERS][ALIGNED_BUFSIZE] + aligned_data(ENET_ALIGN); +#endif + +static struct imx9_driver_s g_enet[] = +{ +#ifdef CONFIG_IMX9_ENET1 + { + .dev = + { + .d_ifup = imx9_ifup, + .d_ifdown = imx9_ifdown, + .d_txavail = imx9_txavail, + +# ifdef CONFIG_NET_MCASTGROUP + .d_addmac = imx9_addmac, + .d_rmmac = imx9_rmmac, +# endif + +# ifdef CONFIG_NETDEV_IOCTL + .d_ioctl = imx9_ioctl, +# endif + }, + .base = IMX9_ENET_BASE, + .clk_gate = CCM_LPCG_ENET1, + .irq = IMX9_IRQ_ENET, + .phy_list = g_enet1_phy_list, + .n_phys = nitems(g_enet1_phy_list), + .txdesc = g_enet1_tx_desc_pool, + .rxdesc = g_enet1_rx_desc_pool, + .buffer_pool = (const uintptr_t)g_enet_1_buffer_pool, + +# ifdef CONFIG_IMX9_ENET_USE_OTP_MAC + .otp_mac_off = CONFIG_IMX9_ENET1_OTP_MAC_ADDR, +# endif + +# ifdef CONFIG_IMX9_ENET1_PROMISCUOUS + .promiscuous = trued +# endif + +# ifdef CONFIG_IMX9_ENET1_RGMII + .phy_type = PHY_RGMII, +# elif defined(CONFIG_IMX9_ENET1_RMII) + .phy_type = PHY_RMII, +# else +# error PHY must be RGMII or RMII +# endif + + /* Duplex: default to FD */ + +# if defined(CONFIG_IMX9_ENET1_PHY_FD) || defined(CONFIG_IMX9_ENET1_PHY_AUTONEG) + .full_duplex = true, +# endif + + /* 10 mbps, default to false */ + +# ifdef CONFIG_IMX9_ENET1_PHY_10MBPS + .s_10mbps = true, +# endif + + /* 100 mbps, default to true */ + +# if defined(CONFIG_IMX9_ENET1_PHY_100MBPS) || defined(CONFIG_IMX9_ENET1_PHY_AUTONEG) + .s_100mbps = true, +# endif + + /* 1000 mbps, default to false */ + +# ifdef CONFIG_IMX9_ENET1_PHY_1000MBPS + .s_1000mbps = true, +# endif + +# ifdef CONFIG_IMX9_ENET1_PHY_AUTONEG + .autoneg = true, +# else +# ifdef CONFIG_IMX9_ENET1_PHY_FORCE_SPEED + .force_speed = true, +# endif +# endif + }, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_enet_getreg32 + * + * Description: + * Get the contents of the ENET register at offset + * + * Input Parameters: + * priv - private ENET device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t imx9_enet_getreg32(struct imx9_driver_s *priv, + uint32_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: imx9_enet_putreg32 + * + * Description: + * Atomically modify the specified bits in a memory mapped register + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * clearbits - the 32-bit value to be written as 0s + * setbits - the 32-bit value to be written as 1s + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imx9_enet_modifyreg32(struct imx9_driver_s *priv, + unsigned int offset, + uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: imx9_enet_putreg32 + * + * Description: + * Write a 16-bit value to the ENET register at offset + * + * Input Parameters: + * priv - private SPI device structure + * value - the 32-bit value to be written + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline void imx9_enet_putreg32(struct imx9_driver_s *priv, + uint32_t value, uint32_t offset) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Function: dump_descriptor + * + * Description: + * Can be used for debugging; dumps the content of a DMA descriptor + * + * Input Parameters: + * desc - Pointer to DMA descriptor + * + * Returned Value: + * None + * + ****************************************************************************/ + +inline static void dump_descriptor(struct enet_desc_s *desc) +{ + _alert("length %d\n", desc->length); + _alert("status1 0x%04x\n", desc->status1); + _alert("data 0x%08x\n", desc->data); + _alert("status2 0x%08x\n", desc->status2); + _alert("checksum 0x%08x\n", desc->checksum); + _alert("lenproto 0x%08x\n", desc->lenproto); + _alert("bdu 0x%08x\n", desc->bdu); + _alert("timestamp 0x%08x\n", desc->timestamp); +} + +/**************************************************************************** + * Function: imx9_txringfull + * + * Description: + * Check if all of the TX descriptors are in use. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * true is the TX ring is full; false if there are free slots at the + * head index. + * + ****************************************************************************/ + +static bool imx9_txringfull(struct imx9_driver_s *priv) +{ + struct enet_desc_s *txdesc = &priv->txdesc[priv->txhead].d1; + struct enet_desc_s *txdesc2 = &priv->txdesc[priv->txhead].d2; + + up_invalidate_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct enet_txdesc_s)); + + return (txdesc2->status1 & TXDESC_R) != 0; +} + +/**************************************************************************** + * Function: imx9_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int imx9_transmit(struct imx9_driver_s *priv, uint32_t *buf_swap) +{ + struct enet_desc_s *txdesc = &priv->txdesc[priv->txhead].d1; + struct enet_desc_s *txdesc2 = &priv->txdesc[priv->txhead].d2; + int split; + uint32_t buf = (uintptr_t)priv->dev.d_buf; + int len = priv->dev.d_len; + + DEBUGASSERT(len > 0 && buf != 0); + DEBUGASSERT((buf & ENET_ALIGN_MASK) == 0); + + if (imx9_txringfull(priv)) + { + /* Ring is full; this can only happen if transmit is called directly + * from the receive path. The buffer is lost. + */ + + nerr("TX ring full, packet lost\n"); + NETDEV_TXERRORS(&priv->dev); + return -EBUSY; + } + + if (len > ALIGNED_BUFSIZE) + { + nerr("TX frame too large %d, max %d\n", len, + ALIGNED_BUFSIZE); + } + + /* We are done with the provided buffer after transmit */ + + priv->dev.d_buf = NULL; + priv->dev.d_len = 0; + + /* Increment statistics */ + + NETDEV_TXPACKETS(&priv->dev); + + /* Optimize the two-descriptor usage; if possible, align the second part + * on 64-byte boundary. Note that 0-length buffers are not accepted by + * the DMA, so we must put some data to both descriptors. + */ + + split = len > 64 ? 64 : 1; + + txdesc->length = split; + txdesc->status2 = TXDESC_TS /* | TXDESC_IINS | TXDESC_PINS */; + txdesc->bdu = 0x00000000; + + txdesc2->length = len - split; + txdesc2->status2 = TXDESC_TS | TXDESC_INT /* | TXDESC_IINS | TXDESC_PINS */; + txdesc2->bdu = 0x00000000; + + if (buf_swap) + { + /* Data was written into the RX buffer, so swap the TX and RX buffers */ + + DEBUGASSERT(*buf_swap == buf); + *buf_swap = txdesc->data; + txdesc->data = buf; + } + else + { + DEBUGASSERT(txdesc->data == buf); + } + + txdesc2->data = buf + split; + + ARM64_DSB(); + + /* Make sure the buffer data is in memory */ + + up_clean_dcache(buf, buf + len); + + /* Descriptors & buffer data are ready to send */ + + txdesc2->status1 = (txdesc2->status1 & TXDESC_W) | + (TXDESC_TC | TXDESC_L | TXDESC_R); + + /* Proceed to next descriptors */ + + priv->txhead++; + if (priv->txhead >= IMX9_ENET_NTXBUFFERS) + { + priv->txhead = 0; + } + + /* If all TX descriptors are in-flight, then we have to disable receive + * interrupts too. This is because receive events can trigger more un- + * stoppable transmit events. + */ + + if (imx9_txringfull(priv)) + { + priv->ints &= ~RX_INTERRUPTS; + } + + /* Enable TX interrupts */ + + priv->ints |= TX_INTERRUPTS; + imx9_enet_putreg32(priv, priv->ints, IMX9_ENET_EIMR_OFFSET); + + /* The latter descriptor was update first. This ensures that the DMA + * won't start before all the descriptor data has been updated and it + * is safe to clean the cache + */ + + ARM64_DMB(); + txdesc->status1 = TXDESC_R; + ARM64_DSB(); + + /* Make sure the descriptors are written from cache to memory */ + + up_clean_dcache((uintptr_t)txdesc, + (uintptr_t)txdesc + sizeof(struct enet_txdesc_s)); + + /* Start the TX transfer (if it was not already waiting for buffers) */ + + imx9_enet_putreg32(priv, ENET_TDAR, IMX9_ENET_TDAR_OFFSET); + + /* Setup the TX timeout watchdog (perhaps restarting the timer) */ + + wd_start(&priv->txtimeout, IMX9_TXTIMEOUT, + imx9_txtimeout_expiry, (wdparm_t)priv); + + return OK; +} + +/**************************************************************************** + * Function: imx9_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 imx9_txpoll(struct net_driver_s *dev) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + + /* Send the packet */ + + imx9_transmit(priv, NULL); + + /* Check if the next TX descriptor is owned by the Ethernet DMA or + * CPU. We cannot perform the TX poll if we are unable to accept + * another packet for transmission. + */ + + if (imx9_txringfull(priv)) + { + return -EBUSY; + } + + /* Return 0 to continue polling */ + + return 0; +} + +/**************************************************************************** + * Function: imx9_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 (stm32_txdone), + * 2. When new TX data is available (stm32_txavail_process), and + * 3. After a TX timeout to restart the sending process + * (stm32_txtimeout_process). + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void imx9_dopoll(struct imx9_driver_s *priv) +{ + struct net_driver_s *dev = &priv->dev; + struct enet_desc_s *txdesc; + struct enet_desc_s *txdesc2; + + /* Check if the next TX descriptor is owned by the Ethernet DMA or + * CPU. We cannot perform the TX poll if we are unable to accept + * another packet for transmission. + */ + + if (!imx9_txringfull(priv)) + { + DEBUGASSERT(dev->d_len == 0 && dev->d_buf == NULL); + + txdesc = &priv->txdesc[priv->txhead].d1; + txdesc2 = &priv->txdesc[priv->txhead].d2; + + /* Debug: check for any errors in the previously sent descriptors. + * Note: cache line was invalidated in the imx9_txringfull already + */ + + if ((txdesc->status2 & TXDESC_STATUS2_ERRORS) != 0) + { + nerr("d1 status1 %x, status2 %x\n", txdesc->status1 & TXDESC_TXE, + txdesc->status2 & TXDESC_STATUS2_ERRORS); + } + + if ((txdesc2->status2 & TXDESC_STATUS2_ERRORS) != 0) + { + nerr("d2 status1 %x, status2 %x\n", txdesc2->status1 & TXDESC_TXE, + txdesc2->status2 & TXDESC_STATUS2_ERRORS); + } + + /* Poll for new data */ + + dev->d_buf = (uint8_t *)(uintptr_t)txdesc->data; + devif_poll(dev, imx9_txpoll); + dev->d_buf = NULL; + dev->d_len = 0; + } + else + { + nerr("TX ring full\n"); + } +} + +/**************************************************************************** + * Function: imx9_dispatch + * + * Description: + * A new Rx packet was received; dispatch that packet to the network layer + * as necessary. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static inline void imx9_dispatch(struct imx9_driver_s *priv) +{ + /* Update statistics */ + + NETDEV_RXPACKETS(&priv->dev); + +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the tap */ + + pkt_input(&priv->dev); +#endif + +#ifdef CONFIG_NET_IPv4 + /* Check for an IPv4 packet */ + + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + NETDEV_RXIPV4(&priv->dev); + + /* Receive an IPv4 packet from the network device */ + + ipv4_input(&priv->dev); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + /* Check for an IPv6 packet */ + + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + NETDEV_RXIPV6(&priv->dev); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->dev); + } + else +#endif +#ifdef CONFIG_NET_ARP + /* Check for an ARP packet */ + + if (BUF->type == HTONS(ETHTYPE_ARP)) + { + NETDEV_RXARP(&priv->dev); + arp_input(&priv->dev); + } +#endif + else + { + priv->dev.d_buf = NULL; + priv->dev.d_len = 0; + NETDEV_RXDROPPED(&priv->dev); + } +} + +inline static bool imx9_rxdesc_full(struct enet_desc_s *rxdesc) +{ + up_invalidate_dcache((uintptr_t)rxdesc, + (uintptr_t)rxdesc + sizeof(struct enet_desc_s)); + + /* Check if the data buffer associated with the descriptor has + * been filled or reception terminated for errors + */ + + return (rxdesc->status1 & RXDESC_E) == 0; +} + +/**************************************************************************** + * Function: imx9_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void imx9_receive(struct imx9_driver_s *priv) +{ + static uint32_t swap_data[2]; + int tail = priv->rxtail; + int swap_d_n; + struct enet_desc_s *rxdesc; + bool received; + + /* Loop while there are received packets to be processed */ + + do + { + rxdesc = &priv->rxdesc[tail]; + received = imx9_rxdesc_full(rxdesc); + if (received) + { + /* Copy the buffer pointer to priv->dev.d_buf. Set amount of data + * in priv->dev.d_len + */ + + DEBUGASSERT(priv->dev.d_buf == NULL); + + if ((rxdesc->status1 & RXDESC_STATUS1_ERRORS) != 0 || + (rxdesc->status2 & RXDESC_STATUS2_ERRORS) != 0) + { + nerr("status1 %x, status2 %x", + rxdesc->status1 & RXDESC_STATUS1_ERRORS, + rxdesc->status2 & RXDESC_STATUS2_ERRORS); + } + + DEBUGASSERT(rxdesc->length > 0 && + ((uintptr_t)rxdesc->data & ENET_ALIGN_MASK) == 0); + + priv->dev.d_len = rxdesc->length; + priv->dev.d_buf = (uint8_t *)(uintptr_t)rxdesc->data; + + /* Invalidate the buffer so that the correct packet will be re-read + * from memory when the packet content is accessed. + */ + + up_invalidate_dcache((uintptr_t)priv->dev.d_buf, + (uintptr_t)priv->dev.d_buf + priv->dev.d_len); + + /* Dispatch (or drop) the newly received packet */ + + imx9_dispatch(priv); + + /* If the dispatch resulted in data that should + * be sent out on the network, the field d_len will set to a + * value > 0. In this case imx9_transmit will just directly use + * the provided buffer to transmit, and swap the rx / tx buffers + */ + + swap_d_n = tail & 1; + swap_data[swap_d_n] = rxdesc->data; + if (priv->dev.d_len > 0) + { + /* And send the packet */ + + imx9_transmit(priv, &swap_data[swap_d_n]); + + /* Assume that the upper levels didn't write to the tx buffer + * beyond the d_len, so the transmit buffer cache is clean. + * If this wouldn't be the case, we'd have to invalidate here! + * up_invalidate_dcache(swap_data[swap_d_n], + * swap_data[swap_d_n] + ALIGNED_BUFSIZE); + */ + } + + /* We are done with the buffers - let's not leave the pointers + * laying around + */ + + priv->dev.d_buf = NULL; + priv->dev.d_len = 0; + + /* RX descriptor size is 32 bytes, but the cache line size is 64. + * This means that we can only free the rx descriptors in pairs. + * If we are the second descriptor of the pair, we update both + */ + + if (swap_d_n == 1) + { + /* First update the second descriptor - RX DMA may not start + * before both are updated + */ + + rxdesc->data = swap_data[1]; + rxdesc->length = 0; + rxdesc->status2 = RXDESC_INT; + rxdesc->bdu = 0x00000000; + rxdesc->status1 = (rxdesc->status1 & RXDESC_W) | RXDESC_E; + + /* Now update the first descriptor of the pair */ + + rxdesc -= 1; + rxdesc->data = swap_data[0]; + rxdesc->length = 0; + rxdesc->status2 = RXDESC_INT; + rxdesc->bdu = 0x00000000; + + /* Make sure both descriptors are fully updated before updating + * the first descriptor's status1; this allows DMA to proceed + * to this descriptor pair. + */ + + ARM64_DMB(); + rxdesc->status1 = RXDESC_E; + ARM64_DSB(); + + up_clean_dcache((uintptr_t)&rxdesc[(-1)], + (uintptr_t)&rxdesc[(-1)] + + 2 * sizeof(rxdesc[0])); + + /* Indicate that we produced empty receive buffers */ + + imx9_enet_putreg32(priv, ENET_RDAR, IMX9_ENET_RDAR_OFFSET); + } + + tail++; + if (tail >= IMX9_ENET_NRXBUFFERS) + { + tail = 0; + } + } + } + while (received); + + /* Update the index to the next empty descriptor */ + + priv->rxtail = tail; +} + +/**************************************************************************** + * Function: imx9_txdone + * + * Description: + * An interrupt was received indicating that the last TX packet(s) is done + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by the watchdog logic. + * The network is locked. + * + ****************************************************************************/ + +static void imx9_txdone(struct imx9_driver_s *priv) +{ + DEBUGASSERT(priv->dev.d_len == 0 && priv->dev.d_buf == NULL); + + /* Cancel the timeout watchdog */ + + wd_cancel(&priv->txtimeout); + + /* Update statistics */ + + NETDEV_TXDONE(&priv->dev); + + priv->ints |= RX_INTERRUPTS; + imx9_enet_putreg32(priv, priv->ints, IMX9_ENET_EIMR_OFFSET); + + /* Poll the network for new XMIT data */ + + imx9_dopoll(priv); +} + +/**************************************************************************** + * Function: imx9_enet_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: + * The network is locked. + * + ****************************************************************************/ + +static void imx9_enet_interrupt_work(void *arg) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)arg; + uint32_t pending; +#ifdef CONFIG_NET_MCASTGROUP + uint32_t gaurstore; + uint32_t galrstore; +#endif + + /* Process pending Ethernet interrupts */ + + net_lock(); + + /* Get the set of unmasked, pending interrupt. */ + + pending = imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & priv->ints; + + /* Clear the pending interrupts */ + + imx9_enet_putreg32(priv, pending, IMX9_ENET_EIR_OFFSET); + + /* Check for errors */ + + if (pending & ERROR_INTERRUPTS) + { + /* An error has occurred, update statistics */ + + NETDEV_ERRORS(&priv->dev); + + nerr("pending %" PRIx32 " ints %" PRIx32 "\n", pending, priv->ints); + } + + if (pending & CRITICAL_ERROR) + { + nerr("Critical error, restarting Ethernet interface\n"); + + /* Bring the Ethernet chip down and back up but with no need to + * reset/renegotiate the phy. + */ + +#ifdef CONFIG_NET_MCASTGROUP + /* Just before we pull the rug lets make sure we retain the + * multicast hash table. + */ + + gaurstore = imx9_enet_getreg32(priv, IMX9_ENET_GAUR_OFFSET); + galrstore = imx9_enet_getreg32(priv, IMX9_ENET_GALR_OFFSET); +#endif + + imx9_ifdown(&priv->dev); + imx9_ifup_action(&priv->dev, false); + +#ifdef CONFIG_NET_MCASTGROUP + /* Now write the multicast table back */ + + imx9_enet_putreg32(priv, gaurstore, IMX9_ENET_GAUR_OFFSET); + imx9_enet_putreg32(priv, galrstore, IMX9_ENET_GALR_OFFSET); +#endif + + /* Then poll the network for new XMIT data */ + + imx9_dopoll(priv); + } + else + { + /* Check for the receipt of a packet */ + + if ((pending & ENET_INT_RXF) != 0) + { + /* A packet has been received, call imx9_receive() to handle the + * packet. + */ + + imx9_receive(priv); + } + + /* Check if a packet transmission has completed */ + + if ((pending & ENET_INT_TXF) != 0) + { + /* Call imx9_txdone to handle the end of transfer */ + + imx9_txdone(priv); + } + } + + net_unlock(); + + /* Re-enable Ethernet interrupts */ + + imx9_enet_putreg32(priv, priv->ints, IMX9_ENET_EIMR_OFFSET); +} + +/**************************************************************************** + * Function: imx9_enet_interrupt + * + * Description: + * Three interrupt sources will vector to this function: + * 1. Ethernet MAC transmit interrupt handler + * 2. Ethernet MAC receive interrupt handler + * 3. + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_enet_interrupt(int irq, void *context, void *arg) +{ + register struct imx9_driver_s *priv = (struct imx9_driver_s *)arg; + + /* Mask all the interrupts */ + + imx9_enet_putreg32(priv, 0, IMX9_ENET_EIMR_OFFSET); + + /* Schedule to perform the interrupt processing on the worker thread. */ + + work_queue(ETHWORK, &priv->irqwork, imx9_enet_interrupt_work, priv, 0); + return OK; +} + +/**************************************************************************** + * Function: imx9_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: + * + ****************************************************************************/ + +static void imx9_txtimeout_work(void *arg) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)arg; + + /* Increment statistics and dump debug info */ + + nerr("Resetting interface\n"); + + /* Take the interface down and bring it back up. That is the most + * aggressive hardware reset. + */ + + NETDEV_TXTIMEOUTS(&priv->dev); + imx9_ifdown(&priv->dev); + imx9_ifup_action(&priv->dev, false); + + /* Then poll the network for new XMIT data */ + + net_lock(); + imx9_dopoll(priv); + net_unlock(); +} + +/**************************************************************************** + * Function: imx9_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 imx9_txtimeout_expiry(wdparm_t arg) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)arg; + + /* Disable further Ethernet interrupts. This will prevent some race + * conditions with interrupt work. + */ + + imx9_enet_putreg32(priv, 0, IMX9_ENET_EIMR_OFFSET); + priv->ints = 0; + + /* Schedule to perform the TX timeout processing on the worker thread, + * canceling any pending interrupt work. + */ + + work_queue(ETHWORK, &priv->irqwork, imx9_txtimeout_work, priv, 0); +} + +/**************************************************************************** + * Function: imx9_ifup_action + * + * Description: + * Internal action routine to bring up the Ethernet interface + * which makes the resetting of the phy (which takes considerable time) + * optional. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * resetphy - Flag indicating if Phy is to be reset. If not then the + * phy configuration is just re-loaded into the ethernet + * interface + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_ifup_action(struct net_driver_s *dev, bool resetphy) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + uint8_t *mac = dev->d_mac.ether.ether_addr_octet; + uint32_t ecr; + int ret; + + ninfo("Bringing up: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); + + /* Initialize ENET buffers */ + + imx9_initbuffers(priv); + + /* Configure the MII interface */ + + imx9_initmii(priv); + + /* Take MAC out of reset */ + + ecr = ENET_ECR_EN1588 | ENET_ECR_DBSWP; + imx9_enet_putreg32(priv, ecr, IMX9_ENET_ECR_OFFSET); + + /* Enable store and forward mode */ + + imx9_enet_putreg32(priv, ENET_TFWR_STRFWD, IMX9_ENET_TFWR_OFFSET); + + /* Set the MAC address */ + + imx9_enet_putreg32(priv, (mac[0] << 24) | (mac[1] << 16) | + (mac[2] << 8) | mac[3], IMX9_ENET_PALR_OFFSET); + imx9_enet_putreg32(priv, (mac[4] << 24) | (mac[5] << 16), + IMX9_ENET_PAUR_OFFSET); + + /* Configure the PHY */ + + ret = imx9_determine_phy(priv); + if (ret < 0) + { + nwarn("Unrecognized PHY\n"); + } + + ret = imx9_initphy(priv, resetphy); + if (ret < 0) + { + nerr("ERROR: Failed to configure the PHY: %d\n", ret); + return ret; + } + + /* Set the RX buffer size */ + + imx9_enet_putreg32(priv, ALIGNED_BUFSIZE, IMX9_ENET_MRBR_OFFSET); + + /* Point to the start of the circular RX buffer descriptor queue */ + + imx9_enet_putreg32(priv, (uint32_t)(uintptr_t)priv->rxdesc, + IMX9_ENET_RDSR_OFFSET); + + /* Point to the start of the circular TX buffer descriptor queue */ + + imx9_enet_putreg32(priv, (uint32_t)(uintptr_t)priv->txdesc, + IMX9_ENET_TDSR_OFFSET); + + /* Mask and clear all ENET interrupts */ + + imx9_enet_putreg32(priv, 0, IMX9_ENET_EIMR_OFFSET); + + imx9_enet_putreg32(priv, 0xffffffff, IMX9_ENET_EIR_OFFSET); + + /* Set 1GBPS if link is set to that */ + + if (priv->s_1000mbps) + { + ecr |= ENET_ECR_SPEED; + } + + /* And enable the MAC */ + + ecr |= ENET_ECR_ETHEREN; + + imx9_enet_putreg32(priv, ecr, IMX9_ENET_ECR_OFFSET); + + /* Enable RX and error interrupts at the controller */ + + priv->ints = RX_INTERRUPTS | ERROR_INTERRUPTS; + imx9_enet_putreg32(priv, priv->ints, IMX9_ENET_EIMR_OFFSET); + + /* Mark the interface "up" and enable interrupts */ + + priv->bifup = true; + up_enable_irq(priv->irq); + + /* Indicate that there have been empty receive buffers produced */ + + imx9_enet_putreg32(priv, ENET_RDAR, IMX9_ENET_RDAR_OFFSET); + + return OK; +} + +/**************************************************************************** + * Function: imx9_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_ifup(struct net_driver_s *dev) +{ + /* The externally available ifup action includes resetting the phy */ + + return imx9_ifup_action(dev, true); +} + +/**************************************************************************** + * Function: imx9_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_ifdown(struct net_driver_s *dev) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + + ninfo("Taking down: %u.%u.%u.%u\n", + ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), + ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); + + /* Cancel the TX timeout timers */ + + wd_cancel(&priv->txtimeout); + + /* Flush and disable the Ethernet interrupts */ + + up_disable_irq(priv->irq); + + imx9_enet_putreg32(priv, 0, IMX9_ENET_EIMR_OFFSET); + priv->ints = 0; + + /* Put the EMAC in its reset, non-operational state. This should be + * a known configuration that will guarantee the imx9_ifup() always + * successfully brings the interface back up. + */ + + imx9_reset(priv); + + /* Clear any pending interrupts */ + + imx9_enet_putreg32(priv, 0xffffffff, IMX9_ENET_EIR_OFFSET); + + /* Mark the device "down" */ + + priv->bifup = false; + + return OK; +} + +/**************************************************************************** + * Function: imx9_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void imx9_txavail_work(void *arg) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)arg; + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->bifup) + { + /* Poll the network for new XMIT data */ + + imx9_dopoll(priv); + } + + net_unlock(); +} + +/**************************************************************************** + * Function: imx9_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int imx9_txavail(struct net_driver_s *dev) +{ + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + + /* 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, imx9_txavail_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Function: imx9_calcethcrc + * + * Description: + * Function to calculate the CRC used by IMX9 to check an Ethernet frame + * + * Input Parameters: + * data - the data to be checked + * length - length of the data + * + * Returned Value: + * crc32 + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static uint32_t imx9_calcethcrc(const uint8_t *data, size_t length) +{ + uint32_t crc = 0xffffffffu; + uint32_t count1 = 0; + uint32_t count2 = 0; + + /* Calculates the CRC-32 polynomial on the multicast group address. */ + + for (count1 = 0; count1 < length; count1++) + { + uint8_t c = data[count1]; + + for (count2 = 0; count2 < 0x08u; count2++) + { + if ((c ^ crc) & 1U) + { + crc >>= 1U; + c >>= 1U; + crc ^= 0xedb88320u; + } + else + { + crc >>= 1U; + c >>= 1U; + } + } + } + + return crc; +} +#endif + +/**************************************************************************** + * Function: imx9_enet_hash_index + * + * Description: + * Function to find the hash index for multicast address filter + * + * Input Parameters: + * mac - The MAC address + * + * Returned Value: + * hash index + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MCASTGROUP +static uint32_t imx9_enet_hash_index(const uint8_t *mac) +{ + uint32_t crc; + uint32_t hashindex; + + ninfo("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + crc = imx9_calcethcrc(mac, 6); + hashindex = (crc >> 26) & 0x3f; + + return hashindex; +} +#endif + +/**************************************************************************** + * Function: imx9_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 imx9_addmac(struct net_driver_s *dev, const uint8_t *mac) +{ + uint32_t hashindex; + uint32_t temp; + uint32_t registeraddress; + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + + hashindex = imx9_enet_hash_index(mac); + + /* Add the MAC address to the hardware multicast routing table */ + + if (hashindex > 31) + { + registeraddress = IMX9_ENET_GAUR_OFFSET; + hashindex -= 32; + } + else + { + registeraddress = IMX9_ENET_GALR_OFFSET; + } + + temp = imx9_enet_getreg32(priv, registeraddress); + temp |= 1 << hashindex; + imx9_enet_putreg32(priv, temp, registeraddress); + + return OK; +} +#endif + +/**************************************************************************** + * Function: imx9_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 imx9_rmmac(struct net_driver_s *dev, const uint8_t *mac) +{ + uint32_t hashindex; + uint32_t temp; + uint32_t registeraddress; + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; + + /* Remove the MAC address from the hardware multicast routing table */ + + hashindex = imx9_enet_hash_index(mac); + + if (hashindex > 31) + { + registeraddress = IMX9_ENET_GAUR_OFFSET; + hashindex -= 32; + } + else + { + registeraddress = IMX9_ENET_GALR_OFFSET; + } + + temp = imx9_enet_getreg32(priv, registeraddress); + temp &= ~(1 << hashindex); + imx9_enet_putreg32(priv, temp, registeraddress); + + return OK; +} +#endif + +/**************************************************************************** + * Function: imx9_ioctl + * + * Description: + * PHY ioctl command handler + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - ioctl command + * arg - Argument accompanying the command + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int imx9_ioctl(struct net_driver_s *dev, int cmd, unsigned long arg) +{ +#ifdef CONFIG_NETDEV_PHY_IOCTL + struct imx9_driver_s *priv = (struct imx9_driver_s *)dev; +#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 = imx9_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); + if (priv->cur_phy && priv->cur_phy->clause == 45 && + MII_MSR == req->reg_num) + { + ret = imx9_readmmd(priv, MMD1, MMD1_PMA_STATUS1, + &req->val_out); + } + else + { + ret = imx9_readmii(priv, req->reg_num, &req->val_out); + } + } + break; + + case SIOCSMIIREG: /* Set register in MII PHY */ + { + struct mii_ioctl_data_s *req = + (struct mii_ioctl_data_s *)((uintptr_t)arg); + ret = imx9_writemii(priv, req->reg_num, req->val_in); + } + break; +#endif /* CONFIG_NETDEV_PHY_IOCTL */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif /* CONFIG_NETDEV_IOCTL */ + +/**************************************************************************** + * Function: imx9_phyintenable + * + * Description: + * Enable link up/down PHY interrupts. The interrupt protocol is like this: + * + * - Interrupt status is cleared when the interrupt is enabled. + * - Interrupt occurs. Interrupt is disabled (at the processor level) when + * is received. + * - Interrupt status is cleared when the interrupt is re-enabled. + * + * Input Parameters: + * priv - A reference to the private driver state structure + * + * Returned Value: + * OK on success; Negated errno (-ETIMEDOUT) on failure. + * + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_PHY_IOCTL) && defined(CONFIG_ARCH_PHY_INTERRUPT) +static int imx9_phyintenable(struct imx9_driver_s *priv) +{ + uint16_t phyval; + int ret; + uint16_t mask; + uint8_t rreg; + uint8_t wreg; + + if (imx9_phy_is(priv, MII_YT8512_NAME)) + { + mask = MII_YT8512_IMR_LD_EN | MII_YT8512_IMR_LU_EN; + rreg = MII_YT8512_ISR; + wreg = MII_YT8512_IMR; + } + else if (imx9_phy_is(priv, MII_KSZ8051_NAME) || + imx9_phy_is(priv, MII_KSZ8061_NAME) || + imx9_phy_is(priv, MII_KSZ8081_NAME) || + imx9_phy_is(priv, MII_DP83825I_NAME)) + { + mask = MII_KSZ80X1_INT_LDEN | MII_KSZ80X1_INT_LUEN; + rreg = MII_KSZ8081_INT; + wreg = rreg; + } + else + { + return -ENOSYS; + } + + /* Read the interrupt status register in order to clear any pending + * interrupts + */ + + ret = imx9_readmii(priv, rreg, &phyval); + if (ret == OK) + { + /* Enable link up/down interrupts */ + + ret = imx9_writemii(priv, wreg, mask); + } + + return ret; +} +#endif + +/**************************************************************************** + * Function: imx9_initmii + * + * Description: + * Configure the MII interface + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void imx9_initmii(struct imx9_driver_s *priv) +{ + /* Speed is based on the peripheral (bus) clock; hold time is 2 module + * clock. This hold time value may need to be increased on some platforms + */ + + imx9_enet_putreg32(priv, ENET_MSCR_HOLDTIME_2CYCLES | + IMX9_MII_SPEED << ENET_MSCR_MII_SPEED_SHIFT, + IMX9_ENET_MSCR_OFFSET); +} + +/**************************************************************************** + * Function: imx9_writemii + * + * Description: + * Write a 16-bit value to a PHY register. + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * phyaddr - The PHY address + * regaddr - The PHY register address + * data - The data to write to the PHY register + * + * Returned Value: + * Zero on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int imx9_writemii(struct imx9_driver_s *priv, + uint8_t regaddr, uint16_t data) +{ + int timeout; + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Initiate the MII Management write */ + + imx9_enet_putreg32(priv, data | + 2 << ENET_MMFR_TA_SHIFT | + (uint32_t)regaddr << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + ENET_MMFR_OP_WRMII | + 1 << ENET_MMFR_ST_SHIFT, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + /* Check for a timeout */ + + if (timeout == MII_MAXPOLLS) + { + return -ETIMEDOUT; + } + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + return OK; +} + +/**************************************************************************** + * Function: imx9_reademii + * + * Description: + * Read a 16-bit value from a PHY register. + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * regaddr - The PHY register address + * data - A pointer to the location to return the data + * + * Returned Value: + * Zero on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int imx9_readmii(struct imx9_driver_s *priv, + uint8_t regaddr, uint16_t *data) +{ + int timeout; + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Initiate the MII Management read */ + + imx9_enet_putreg32(priv, 2 << ENET_MMFR_TA_SHIFT | + (uint32_t)regaddr << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + ENET_MMFR_OP_RDMII | + 1 << ENET_MMFR_ST_SHIFT, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + /* Check for a timeout */ + + if (timeout >= MII_MAXPOLLS) + { + nerr("ERROR: Timed out waiting for transfer to complete\n"); + return -ETIMEDOUT; + } + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* And return the MII data */ + + *data = (uint16_t)(imx9_enet_getreg32(priv, IMX9_ENET_MMFR_OFFSET) & + ENET_MMFR_DATA_MASK); + return OK; +} + +/**************************************************************************** + * Function: imx9_read_phy_status + * + * Description: + * Read the phy status from the current phy + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * 0 on success, -1 on any error + * + ****************************************************************************/ + +int imx9_read_phy_status(struct imx9_driver_s *priv) +{ + int ret; + int retries; + uint16_t page = 0; + uint16_t prev_page; + uint16_t page_reg; + uint16_t mask; + uint16_t status; + + if (priv->cur_phy == NULL) + { + /* We don't support guessing the link speed based ou our and link + * partner's capabilities. For now, user must manually set the + * speed and duplex if the phy is unknown + */ + + nerr("Unknown PHY, can't read link speed\n"); + return ERROR; + } + + /* Special handling for rtl8211f, which needs to chage page */ + + if (imx9_phy_is(priv, GMII_RTL8211F_NAME)) + { + page_reg = GMII_RTL8211F_PAGSR; + page = 0xa43; + } + + if (page) + { + /* Get current page */ + + ret = imx9_readmii(priv, page_reg, &prev_page); + + /* Set page */ + + if (ret >= 0) + { + ninfo("Changing PHY page from 0x%x to 0x%x\n", prev_page, page); + ret = imx9_writemii(priv, page_reg, page); + if (ret < 0) + { + return ERROR; + } + } + } + + retries = 0; + do + { + status = 0xffff; + ret = imx9_readmii(priv, priv->cur_phy->status, &status); + } + while ((ret < 0 || status == 0xffff) && ++retries < 3); + + if (status != 0xffff) + { + ninfo("%s: PHY status %x: %04x\n", priv->cur_phy->name, + priv->cur_phy->status, status); + + /* Set the current link information */ + + mask = priv->cur_phy->speed_mask; + + priv->full_duplex = (status & priv->cur_phy->duplex) != 0; + priv->s_10mbps = (status & mask) == priv->cur_phy->mbps10; + priv->s_100mbps = (status & mask) == priv->cur_phy->mbps100; + priv->s_1000mbps = (status & mask) == priv->cur_phy->mbps1000; + } + + if (page) + { + /* Restore original page */ + + ninfo("Restoring PHY page to 0x%x\n", prev_page); + imx9_writemii(priv, page_reg, prev_page); + } + + return OK; +} + +/**************************************************************************** + * Function: imx9_determine_phy + * + * Description: + * Uses the board.h supplied PHY list to determine which PHY + * is populated on this board. + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * Zero on success, a -ENOENT errno value on failure. + * + ****************************************************************************/ + +static int imx9_determine_phy(struct imx9_driver_s *priv) +{ + int i; + uint16_t phydata = 0xffff; + uint8_t last_phyaddr = 0; + int retries; + int ret; + + for (i = 0; i < priv->n_phys; i++) + { + priv->phyaddr = (uint8_t)priv->phy_list[i].address_lo; + last_phyaddr = priv->phy_list[i].address_high == 0xffff ? + priv->phyaddr : + (uint8_t)priv->phy_list[i].address_high; + + for (; priv->phyaddr <= last_phyaddr; priv->phyaddr++) + { + retries = 0; + do + { + nxsig_usleep(100); + phydata = 0xffff; + ret = imx9_readmii(priv, MII_PHYID1, &phydata); + ninfo("phy %s addr %d received PHYID1 %x\n", + priv->phy_list[i].name, priv->phyaddr, + phydata); + } + while ((ret < 0 || phydata == 0xffff) && ++retries < 3); + + if (retries <= 3 && ret == 0 && + phydata == priv->phy_list[i].id1) + { + do + { + nxsig_usleep(100); + phydata = 0xffff; + ret = imx9_readmii(priv, MII_PHYID2, &phydata); + ninfo("phy %s addr %d received PHYID2 %x\n", + priv->phy_list[i].name, priv->phyaddr, + phydata); + } + while ((ret < 0 || phydata == 0xffff) && ++retries < 3); + if (retries <= 3 && ret == 0 && + (phydata & 0xfff0) == + (priv->phy_list[i].id2 & 0xfff0)) + { + priv->cur_phy = & priv->phy_list[i]; + ninfo("Found phy %s addr %d\n", + priv->cur_phy->name, priv->phyaddr); + return OK; + } + } + } + } + + nerr("No PHY found\n"); + + priv->cur_phy = NULL; + return -ENOENT; +} + +/**************************************************************************** + * Function: imx9_phy_is + * + * Description: + * Compares the name with the current selected PHY's name + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * name - a pointer to comapre to. + * + * Returned Value: + * 1 on match, a 0 on no match. + * + ****************************************************************************/ + +static int imx9_phy_is(struct imx9_driver_s *priv, const char *name) +{ + return priv->cur_phy && strcmp(priv->cur_phy->name, name) == 0; +} + +#if 0 +/**************************************************************************** + * Function: imx9_writemmd + * + * Description: + * Write a 16-bit value to a the selected MMD PHY register. + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * mmd - The Selected MMD Space + * regaddr - The PHY register address + * data - The data to write to the PHY register + * + * Returned Value: + * Zero on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int imx9_writemmd(struct imx9_driver_s *priv, + uint8_t mmd, uint16_t regaddr, uint16_t data) +{ + int timeout; + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Initiate the MMD Management write - Address Phase */ + + imx9_enet_putreg32(priv, + 0 << ENET_MMFR_ST_SHIFT | + ENET_MMFR_OP_WRNOTMII | + (uint32_t)mmd << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + 2 << ENET_MMFR_TA_SHIFT | + regaddr, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Check for a timeout */ + + if (timeout == MII_MAXPOLLS) + { + return -ETIMEDOUT; + } + + /* Initiate the MMD Management write - Data Phase */ + + imx9_enet_putreg32(priv, + 0 << ENET_MMFR_ST_SHIFT | + ENET_MMFR_OP_WRMII | + (uint32_t)mmd << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + 2 << ENET_MMFR_TA_SHIFT | + data, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Check for a timeout */ + + if (timeout == MII_MAXPOLLS) + { + return -ETIMEDOUT; + } + + return OK; +} +#endif + +/**************************************************************************** + * Function: imx9_reademmd + * + * Description: + * Read a 16-bit value from a PHY register. + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * mmd - The Selected MMD Space + * regaddr - The PHY register address + * data - A pointer to the location to return the data + * + * Returned Value: + * Zero on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int imx9_readmmd(struct imx9_driver_s *priv, + uint8_t mmd, uint16_t regaddr, uint16_t *data) +{ + int timeout; + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Initiate the MMD Management read - Address Phase */ + + imx9_enet_putreg32(priv, + 0 << ENET_MMFR_ST_SHIFT | + ENET_MMFR_OP_WRNOTMII | + (uint32_t)mmd << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + 2 << ENET_MMFR_TA_SHIFT | + regaddr, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Check for a timeout */ + + if (timeout >= MII_MAXPOLLS) + { + nerr("ERROR: Timed out waiting for transfer to complete\n"); + return -ETIMEDOUT; + } + + /* Initiate the MMD Management read - Data Phase */ + + imx9_enet_putreg32(priv, + 0 << ENET_MMFR_ST_SHIFT | + ENET_MMFR_OP_RDNOTMII | + (uint32_t)mmd << ENET_MMFR_RA_SHIFT | + (uint32_t)priv->phyaddr << ENET_MMFR_PA_SHIFT | + 2 << ENET_MMFR_TA_SHIFT, + IMX9_ENET_MMFR_OFFSET); + + /* Wait for the transfer to complete */ + + for (timeout = 0; timeout < MII_MAXPOLLS; timeout++) + { + if ((imx9_enet_getreg32(priv, IMX9_ENET_EIR_OFFSET) & + ENET_INT_MII) != 0) + { + break; + } + } + + /* Clear the MII interrupt bit */ + + imx9_enet_putreg32(priv, ENET_INT_MII, IMX9_ENET_EIR_OFFSET); + + /* Check for a timeout */ + + if (timeout == MII_MAXPOLLS) + { + return -ETIMEDOUT; + } + + /* And return the MII data */ + + *data = (uint16_t)(imx9_enet_getreg32(priv, IMX9_ENET_MMFR_OFFSET) & + ENET_MMFR_DATA_MASK); + return OK; +} + +int imx9_reset_phy(struct imx9_driver_s *priv) +{ + int timeout; + int ret; + int result; + uint16_t mcr; + + /* Reset the PHY */ + + ret = imx9_writemii(priv, MII_MCR, MII_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++) + { + nxsig_usleep(100); + result = imx9_readmii(priv, MII_MCR, &mcr); + if (result < 0) + { + nerr("ERROR: Failed to read the MCR register: %d\n", ret); + ret = result; + } + else if ((mcr & MII_MCR_RESET) == 0) + { + ninfo("MII reset complete: %x\n", mcr); + ret = OK; + break; + } + else + { + nerr("MCR data %x\n", mcr); + } + } + + return ret; +} + +/**************************************************************************** + * Function: imx9_phy_set_speed + * + * Description: + * Set or start to autonegotiate the link speed + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * autonegotiate - true: autonegotiate with default advertisement + * false: autonegotiate, but disable other speeds + * Returned Value: + * Zero (OK) returned on success; a negated errno value is returned on any + * failure; + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_phy_set_speed(struct imx9_driver_s *priv, bool autonegotiate) +{ + uint16_t advertise; + uint16_t mcr; + uint16_t force_mcr; + uint16_t btcr; + int ret; + + /* Read initial MCR and take link down */ + + ret = imx9_readmii(priv, MII_MCR, &mcr); + + if (ret < 0) + { + nerr("ERROR: Failed to read MCR register: %d\n", ret); + return ret; + } + + ret = imx9_writemii(priv, MII_MCR, mcr | MII_MCR_PDOWN); + + /* If we are trying to manually set the speed, disable advertisement of the + * other ones. In case we want to force the speed setting on the PHY, + * set the speed in MCR and disable autonegotiation completely. + */ + + force_mcr = mcr; + if (!autonegotiate) + { + /* Read the Auto_negotiation Advertisement Register defaults */ + + ret = imx9_readmii(priv, MII_ADVERTISE, &advertise); + + if (ret < 0) + { + nerr("ERROR: Failed to read ADVERTISE register: %d\n", ret); + return ret; + } + + if (priv->phy_type == PHY_RGMII) + { + ret = imx9_readmii(priv, GMII_1000BTCR, &btcr); + if (ret < 0) + { + nerr("ERROR: Failed to read GMII_1000BTCR register\n"); + return ret; + } + } + + if (priv->full_duplex) + { + advertise &= ~(MII_ADVERTISE_1000XHALF | + MII_ADVERTISE_100BASETXHALF | + MII_ADVERTISE_10BASETXHALF); + btcr &= ~GMII_1000BTCR_1000BASETHALF; + force_mcr |= MII_MCR_FULLDPLX; + } + else + { + advertise &= ~(MII_ADVERTISE_1000XFULL | + MII_ADVERTISE_100BASETXFULL | + MII_ADVERTISE_10BASETXFULL); + btcr &= ~GMII_1000BTCR_1000BASETFULL; + force_mcr &= ~MII_MCR_FULLDPLX; + } + + if (priv->s_10mbps) + { + advertise &= ~(MII_ADVERTISE_100BASETXFULL | + MII_ADVERTISE_100BASETXHALF | + MII_ADVERTISE_1000XFULL | + MII_ADVERTISE_1000XHALF); + btcr &= ~(GMII_1000BTCR_1000BASETHALF | + GMII_1000BTCR_1000BASETFULL); + force_mcr &= ~(MII_MCR_SPEED100 | GMII_MCR_SPEED1000); + } + + if (priv->s_100mbps) + { + advertise &= ~(MII_ADVERTISE_10BASETXFULL | + MII_ADVERTISE_10BASETXHALF | + MII_ADVERTISE_1000XFULL | + MII_ADVERTISE_1000XHALF); + btcr &= ~(GMII_1000BTCR_1000BASETHALF | + GMII_1000BTCR_1000BASETFULL); + force_mcr &= ~GMII_MCR_SPEED1000; + force_mcr |= MII_MCR_SPEED100; + } + + if (priv->s_1000mbps) + { + advertise &= ~(MII_ADVERTISE_10BASETXFULL | + MII_ADVERTISE_10BASETXHALF | + MII_ADVERTISE_100BASETXFULL | + MII_ADVERTISE_100BASETXHALF); + force_mcr &= ~MII_MCR_SPEED100; + force_mcr |= GMII_MCR_SPEED1000; + } + + ret = imx9_writemii(priv, MII_ADVERTISE, advertise); + + if (ret < 0) + { + nerr("ERROR: Failed to write ADVERTISE register\n"); + return ret; + } + + if (priv->phy_type == PHY_RGMII) + { + ret = imx9_writemii(priv, GMII_1000BTCR, btcr); + if (ret < 0) + { + nerr("ERROR: Failed to write GMII_1000BTCR register\n"); + return ret; + } + } + } + + /* Enable autonegotiation and take link back up */ + + if (!priv->force_speed) + { + mcr |= (MII_MCR_ANENABLE | MII_MCR_ANRESTART); + } + else + { + mcr = force_mcr & (~(MII_MCR_ANENABLE | MII_MCR_ANRESTART)); + } + + ret = imx9_writemii(priv, MII_MCR, mcr); + if (ret < 0) + { + nerr("ERROR: Failed to write MCR register: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Function: imx9_phy_wait_autoneg_complete + * + * Description: + * Wait for autonegotiation to complete + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * Zero (OK) returned on success; a negated errno value is returned on any + * failure; + * + * Assumptions: + * + ****************************************************************************/ + +static int imx9_phy_wait_autoneg_complete(struct imx9_driver_s *priv) +{ + int ret; + uint16_t msr; + int timeout; + + /* Wait for autonegotiation to complete */ + + for (timeout = 0; timeout < LINK_NLOOPS; timeout++) + { + ret = imx9_readmii(priv, MII_MSR, &msr); + if (ret < 0) + { + nerr("ERROR: Failed to read MSR register: %d\n", ret); + return ret; + } + + /* Check for completion of autonegotiation */ + + if ((msr & MII_MSR_ANEGCOMPLETE) != 0) + { + /* Yes break out of the loop */ + + ninfo("Autonegotiate complete, MSR %x\n", msr); + break; + } + + nxsig_usleep(LINK_WAITUS); + } + + if (timeout == LINK_NLOOPS) + { + ninfo("Autonegotiate failed, MSR %x\n", msr); + return -ETIMEDOUT; + } + + return imx9_read_phy_status(priv); +} + +/**************************************************************************** + * Function: imx9_initphy + * + * Description: + * Configure the PHY + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * renogphy - Flag indicating if to perform negotiation of the link + * + * Returned Value: + * Zero (OK) returned on success; a negated errno value is returned on any + * failure; + * + * Assumptions: + * + ****************************************************************************/ + +static inline int imx9_initphy(struct imx9_driver_s *priv, bool renogphy) +{ + uint32_t rcr; + uint32_t tcr; + uint32_t racc; + uint16_t phydata; + int retries; + int ret; + const char *phy_name = priv->cur_phy ? priv->cur_phy->name : "Unknown"; + + if (renogphy) + { + /* Loop until we successfully communicate + * with the PHY. This is 'standard stuff' that should work for any PHY + * - we are not communicating with it's 'special' registers + * at this point. + */ + + ninfo("%s: Try phyaddr: %u\n", phy_name, priv->phyaddr); + + /* Try to read PHYID1 few times using this address */ + + retries = 0; + do + { + nxsig_usleep(LINK_WAITUS); + + ninfo("%s: Read PHYID1, retries=%d\n", phy_name, retries + 1); + + phydata = 0xffff; + ret = imx9_readmii(priv, MII_PHYID1, &phydata); + } + while ((ret < 0 || phydata == 0xffff) && ++retries < 3); + + if (retries >= 3) + { + nerr("ERROR: Failed to read %s PHYID1 at address %d\n", + phy_name, priv->phyaddr); + return -ENOENT; + } + + ninfo("%s: Using PHY address %u\n", phy_name, priv->phyaddr); + + /* Verify PHYID1. Compare OUI bits 3-18 */ + + ninfo("%s: PHYID1: %04x\n", phy_name, phydata); + if (priv->cur_phy && phydata != priv->cur_phy->id1) + { + nerr("ERROR: PHYID1=%04x incorrect for %s. Expected %04x\n", + phydata, phy_name, priv->cur_phy->id1); + return -ENXIO; + } + + /* Read PHYID2 */ + + ret = imx9_readmii(priv, MII_PHYID2, &phydata); + if (ret < 0) + { + nerr("ERROR: Failed to read %s PHYID2: %d\n", phy_name, ret); + return ret; + } + + ninfo("%s: PHYID2: %04x\n", phy_name, phydata); + + /* Verify PHYID2: Compare OUI bits 19-24 and the 6-bit model number + * (ignoring the 4-bit revision number). + */ + + if (priv->cur_phy && + (phydata & 0xfff0) != (priv->cur_phy->id2 & 0xfff0)) + { + nerr("ERROR: PHYID2=%04x incorrect for %s. Expected %04x\n", + (phydata & 0xfff0), phy_name, + (priv->cur_phy->id2 & 0xfff0)); + return -ENXIO; + } + + if (imx9_phy_is(priv, MII_LAN8720_NAME) || + imx9_phy_is(priv, MII_LAN8742A_NAME)) + { + /* Make sure that PHY comes up in correct mode when it's reset */ + + imx9_writemii(priv, MII_LAN8720_MODES, + MII_LAN8720_MODES_RESV | MII_LAN8720_MODES_ALL | + MII_LAN8720_MODES_PHYAD(priv->phyaddr)); + } + + ret = imx9_reset_phy(priv); + if (ret < 0) + { + nerr("ERROR: PHY reset failed: %d\n", ret); + return ret; + } + + ret = imx9_phy_set_speed(priv, priv->autoneg); + + if (ret < 0) + { + nerr("ERROR: PHY setting speed failed: %d\n", ret); + return ret; + } + + /* If this is an unknown phy, we can't read the current link speed. In + * that case, just set the default speed and duplex settings. + */ + + if (!priv->cur_phy || !priv->autoneg) + { + nwarn("Can't read PHY status, using default speed and duplex\n"); + imx9_phy_set_speed(priv, false); + } + else + { + ret = imx9_phy_wait_autoneg_complete(priv); + if (ret < 0) + { + return ret; + } + } + } + + /* Set up the transmit and receive control registers based on the + * configuration and the auto negotiation results. + */ + + rcr = (ENET_RCR_CRCFWD | ((CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE) + << ENET_RCR_MAX_FL_SHIFT) | + ENET_RCR_FCE | ENET_RCR_MII_MODE); + + if (priv->phy_type == PHY_RGMII) + { + rcr |= ENET_RCR_RGMII_EN; + } + else + { + rcr |= ENET_RCR_RMII_MODE; + } + + if (priv->promiscuous) + { + rcr |= ENET_RCR_PROM; + } + + tcr = 0; + + imx9_enet_putreg32(priv, tcr, IMX9_ENET_TCR_OFFSET); + + /* Enable Discard Of Frames With MAC Layer Errors. + * Enable Discard Of Frames With Wrong Protocol Checksum. + * Bit 1: Enable discard of frames with wrong IPv4 header checksum. + */ + + racc = ENET_RACC_PRODIS | ENET_RACC_LINEDIS | ENET_RACC_IPDIS; + imx9_enet_putreg32(priv, racc, IMX9_ENET_RACC_OFFSET); + + /* Setup half or full duplex */ + + if (priv->full_duplex) + { + /* Full duplex */ + + ninfo("%s: Full duplex\n", phy_name); + tcr |= ENET_TCR_FDEN; + } + else + { + /* Half duplex */ + + ninfo("%s: Half duplex\n", phy_name); + rcr |= ENET_RCR_DRT; + } + + if (priv->s_10mbps) + { + /* 10 Mbps */ + + ninfo("%s: 10 Base-T\n", phy_name); + rcr |= ENET_RCR_RMII_10T; + } + else if (priv->s_100mbps) + { + /* 100 Mbps */ + + ninfo("%s: 100 Base-T\n", phy_name); + } + else if (priv->s_1000mbps) + { + /* 1000 Mbps */ + + ninfo("%s: 1000 Base-T\n", phy_name); + } + else + { + /* This might happen if Autonegotiation did not complete(?) */ + + nerr("ERROR: No 10-, 100-, or 1000-BaseT reported: PHY STATUS=%04x\n", + phydata); + return -EIO; + } + + imx9_enet_putreg32(priv, rcr, IMX9_ENET_RCR_OFFSET); + imx9_enet_putreg32(priv, tcr, IMX9_ENET_TCR_OFFSET); + return OK; +} + +/**************************************************************************** + * Function: imx9_initbuffers + * + * Description: + * Initialize ENET buffers and descriptors + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void imx9_initbuffers(struct imx9_driver_s *priv) +{ + uintptr_t addr; + int i; + + /* Get the beginning of the first aligned buffer */ + + addr = priv->buffer_pool; + + /* Then fill in the TX descriptors */ + + memset(priv->txdesc, 0, IMX9_ENET_NTXBUFFERS * sizeof(priv->txdesc[0])); + + for (i = 0; i < IMX9_ENET_NTXBUFFERS; i++) + { + priv->txdesc[i].d1.data = addr; + priv->txdesc[i].d1.bdu = TXDESC_BDU; + + priv->txdesc[i].d2.bdu = TXDESC_BDU; + addr += ALIGNED_BUFSIZE; + } + + /* Then fill in the RX descriptors */ + + memset(priv->rxdesc, 0, IMX9_ENET_NRXBUFFERS * sizeof(priv->rxdesc[0])); + + for (i = 0; i < IMX9_ENET_NRXBUFFERS; i++) + { + priv->rxdesc[i].status1 = RXDESC_E; + priv->rxdesc[i].data = addr; + priv->rxdesc[i].status2 = RXDESC_INT; + addr += ALIGNED_BUFSIZE; + } + + /* Set the wrap bit in the last descriptors to form a ring */ + + priv->txdesc[IMX9_ENET_NTXBUFFERS - 1].d2.status1 |= TXDESC_W; + priv->rxdesc[IMX9_ENET_NRXBUFFERS - 1].status1 |= RXDESC_W; + + ARM64_DSB(); + + up_clean_dcache((uintptr_t)priv->txdesc, + (uintptr_t)priv->txdesc + + IMX9_ENET_NTXBUFFERS * sizeof(priv->txdesc[0])); + up_clean_dcache((uintptr_t)priv->rxdesc, + (uintptr_t)priv->rxdesc + + IMX9_ENET_NRXBUFFERS * sizeof(priv->rxdesc[0])); + + /* We start with RX descriptor 0 and with no TX descriptors in use */ + + priv->txhead = 0; + priv->rxtail = 0; +} + +/**************************************************************************** + * Function: imx9_reset + * + * Description: + * Put the EMAC in the non-operational, reset state + * + * Input Parameters: + * priv - Reference to the private ENET driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void imx9_reset(struct imx9_driver_s *priv) +{ + /* Set the reset bit and wait for the enable to clear */ + + imx9_enet_putreg32(priv, ENET_ECR_RESET, IMX9_ENET_ECR_OFFSET); + + while (imx9_enet_getreg32(priv, IMX9_ENET_ECR_OFFSET) & ENET_ECR_ETHEREN) + { + asm volatile ("nop"); + } +} + +/**************************************************************************** + * Function: imx9_enet_mux_io + * + * Description: + * Mux all the IO pins + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void imx9_enet_mux_io(void) +{ +#ifdef CONFIG_IMX9_ENET1 + imx9_iomux_configure(MUX_ENET1_MDIO); + imx9_iomux_configure(MUX_ENET1_MDC); + + imx9_iomux_configure(MUX_ENET1_RX_DATA00); + imx9_iomux_configure(MUX_ENET1_RX_DATA01); + + imx9_iomux_configure(MUX_ENET1_TX_DATA00); + imx9_iomux_configure(MUX_ENET1_TX_DATA01); + +# if defined(CONFIG_IMX9_ENET1_RGMII) + imx9_iomux_configure(MUX_ENET1_RX_DATA02); + imx9_iomux_configure(MUX_ENET1_RX_DATA03); + imx9_iomux_configure(MUX_ENET1_TX_DATA02); + imx9_iomux_configure(MUX_ENET1_TX_DATA03); + imx9_iomux_configure(MUX_ENET1_RXC); + imx9_iomux_configure(MUX_ENET1_TX_CTL); + imx9_iomux_configure(MUX_ENET1_RX_CTL); +# else /* RMII */ + imx9_iomux_configure(MUX_ENET1_TX_EN); + imx9_iomux_configure(MUX_ENET1_REF_CLK); + imx9_iomux_configure(MUX_ENET1_CRS_DV); +# endif + +# ifdef MUX_ENET1_RX_ER + imx9_iomux_configure(MUX_ENET1_RX_ER); +# endif +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: imx9_netinitialize + * + * Description: + * Initialize the Ethernet controller and driver + * + * 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 imx9_netinitialize(int intf) +{ + struct imx9_driver_s *priv; + uint32_t uidl; + uint32_t uidml; + uint8_t *mac; + int ret; + + /* Get the interface structure associated with this interface number. */ + + priv = &g_enet[intf]; + + /* Disable the ENET clock */ + + imx9_ccm_gate_on(priv->clk_gate, false); + + /* Enet ref to 125 MHz */ + + imx9_ccm_configure_root_clock(CCM_CR_ENETREF, SYS_PLL1PFD0DIV2, 2); + + /* Enet timer 1 to 125MHz */ + + imx9_ccm_configure_root_clock(CCM_CR_ENETTIMER1, SYS_PLL1PFD0DIV2, 2); + + /* Enet ref clock to 25 MHz */ + + imx9_ccm_configure_root_clock(CCM_CR_ENETREFPHY, SYS_PLL1PFD0DIV2, 20); + + /* Enable the ENET clock */ + + imx9_ccm_gate_on(priv->clk_gate, true); + + /* Attach the Ethernet interrupt handler */ + + if (irq_attach(priv->irq, imx9_enet_interrupt, priv)) + { + /* We could not attach the ISR to the interrupt */ + + nerr("ERROR: Failed to attach EMACTX IRQ\n"); + return -EAGAIN; + } + + /* TODO: 1588 features */ + + /* Attach the Ethernet MAC IEEE 1588 timer interrupt handler */ + +#if 0 + if (irq_attach(IMX9_IRQ_ENET_1588, imx9_enet_interrupt, priv)) + { + /* We could not attach the ISR to the interrupt */ + + nerr("ERROR: Failed to attach EMACTMR IRQ\n"); + return -EAGAIN; + } +#endif + +#ifdef CONFIG_IMX9_ENET_USE_OTP_MAC + + /* Boards like the imx93-evk have a unique (official) + * MAC address stored in OTP. + */ + + uidl = getreg32(IMX9_OCOTP_BASE + priv->otp_mac_off); + uidml = getreg32(IMX9_OCOTP_BASE + priv->otp_mac_off + 4); + mac = priv->dev.d_mac.ether.ether_addr_octet; + + mac[0] = (uidml & 0x0000ff00) >> 8; + mac[1] = (uidml & 0x000000ff) >> 0; + mac[2] = (uidl & 0xff000000) >> 24; + mac[3] = (uidl & 0x00ff0000) >> 16; + mac[4] = (uidl & 0x0000ff00) >> 8; + mac[5] = (uidl & 0x000000ff) >> 0; + +#else + + /* Determine a semi-unique MAC address from MCU UID + * We use UID Low and Mid Low registers to get 64 bits, from which we keep + * 40 bits. We then force locally administered bits in mac[0] based on + * interface number (0x2,0x6,0xa,0xe for if 0,1,2,3) + */ + + uidl = getreg32(IMX9_OCOTP_BASE + IMX93_OCOTP_UID_OFFSET); + uidml = getreg32(IMX9_OCOTP_BASE + IMX93_OCOTP_UID_OFFSET + 4); + mac = priv->dev.d_mac.ether.ether_addr_octet; + + mac[0] = (0x2 | (intf << 2)); + mac[1] = (uidml & 0x000000ff); + mac[2] = (uidl & 0xff000000) >> 24; + mac[3] = (uidl & 0x00ff0000) >> 16; + mac[4] = (uidl & 0x0000ff00) >> 8; + mac[5] = (uidl & 0x000000ff); + +#endif + +#ifdef CONFIG_IMX9_ENET_PHYINIT + /* Perform any necessary, one-time, board-specific PHY initialization */ + + ret = imx9_phy_boardinitialize(intf); + if (ret < 0) + { + nerr("ERROR: Failed to initialize the PHY: %d\n", ret); + return ret; + } +#endif + + /* Put the interface in the down state. This usually amounts to resetting + * the device and/or calling imx9_ifdown(). + */ + + imx9_ifdown(&priv->dev); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + netdev_register(&priv->dev, NET_LL_ETHERNET); + + UNUSED(ret); + return OK; +} + +/**************************************************************************** + * Name: arm_netinitialize + * + * Description: + * Initialize the first network interface. If there are more than one + * interface in the chip, then board-specific logic will have to provide + * this function to determine which, if any, Ethernet controllers should + * be initialized. + * + ****************************************************************************/ + +#if !defined(CONFIG_NETDEV_LATEINIT) +void arm64_netinitialize(void) +{ + int i; + + /* Configure all ENET/MII pins */ + + imx9_enet_mux_io(); + + /* Initialize all IFs */ + + for (i = 0; i < nitems(g_enet); i++) + { + imx9_netinitialize(i); + } +} +#endif + +#endif /* CONFIG_IMX9_ENET */ diff --git a/arch/arm64/src/imx9/imx9_enet.h b/arch/arm64/src/imx9/imx9_enet.h new file mode 100644 index 0000000000000..e37cdc6464a8a --- /dev/null +++ b/arch/arm64/src/imx9/imx9_enet.h @@ -0,0 +1,106 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_enet.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_ARM64_SRC_IMX9_IMX9_ENET_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_ENET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "hardware/imx9_enet.h" + +#ifdef CONFIG_IMX9_ENET + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Definitions for use with imx9_phy_boardinitialize */ + +#define EMAC_INTF 0 + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Function: imx9_netinitialize + * + * Description: + * Initialize the Ethernet controller and driver + * + * 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 imx9_netinitialize(int intf); + +/**************************************************************************** + * Function: imx9_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_IMX9_ENET_PHYINIT is defined in the + * configuration then the board specific logic must provide + * imx9_phyinitialize(); The i.MX RT 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. + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_ENET_PHYINIT +int imx9_phy_boardinitialize(int intf); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_IMX9_ENET */ +#endif /* __ARCH_ARM_SRC_IMX9_IMX9_ENET_H */ diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig index 9cd5129d39349..92348877f1c6a 100644 --- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig +++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig @@ -22,7 +22,10 @@ CONFIG_DEBUG_FULLOPT=y CONFIG_DEBUG_SYMBOLS=y CONFIG_DEFAULT_TASK_STACKSIZE=8192 CONFIG_DEV_ZERO=y +CONFIG_ETH0_PHY_MULTI=y CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_TCPBLASTER=y +CONFIG_EXAMPLES_UDPBLASTER=y CONFIG_EXPERIMENTAL=y CONFIG_FS_PROCFS=y CONFIG_FS_ROMFS=y @@ -35,6 +38,9 @@ CONFIG_IDLETHREAD_STACKSIZE=8192 CONFIG_IMX9_DMA_ALLOC=y CONFIG_IMX9_DMA_ALLOC_POOL_SIZE=81920 CONFIG_IMX9_EDMA=y +CONFIG_IMX9_ENET1_RGMII=y +CONFIG_IMX9_ENET=y +CONFIG_IMX9_ENET_USE_OTP_MAC=y CONFIG_IMX9_FLEXIO1_PWM=y CONFIG_IMX9_GPIO_IRQ=y CONFIG_IMX9_LPI2C1=y @@ -52,6 +58,14 @@ CONFIG_IMX9_USBDEV_USBC1=y CONFIG_INIT_ENTRYPOINT="nsh_main" CONFIG_INTELHEX_BINARY=y CONFIG_LPUART1_SERIAL_CONSOLE=y +CONFIG_NDEBUG=y +CONFIG_NET=y +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDEV_PHY_IOCTL=y +CONFIG_NET_ICMP=y +CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_TCP=y +CONFIG_NET_UDP=y CONFIG_NSH_ARCHINIT=y CONFIG_NSH_BUILTIN_APPS=y CONFIG_NSH_FILEIOSIZE=512 @@ -78,6 +92,7 @@ CONFIG_SYMTAB_ORDEREDBYNAME=y CONFIG_SYSTEM_CDCACM=y CONFIG_SYSTEM_I2CTOOL=y CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_PING=y CONFIG_SYSTEM_SPITOOL=y CONFIG_SYSTEM_SYSTEM=y CONFIG_SYSTEM_TIME64=y diff --git a/boards/arm64/imx9/imx93-evk/include/board.h b/boards/arm64/imx9/imx93-evk/include/board.h index 4632d5443e724..8382bd62ed03d 100644 --- a/boards/arm64/imx9/imx93-evk/include/board.h +++ b/boards/arm64/imx9/imx93-evk/include/board.h @@ -26,6 +26,8 @@ ****************************************************************************/ #include +#include +#include /**************************************************************************** * Pre-processor Definitions @@ -121,6 +123,85 @@ PFD_CFG(IMX9_SYSPLL_BASE, 2, PFD_PARMS(6, 2, true)), \ } +/* Ethernet configuration */ + +#define BOARD_ENET1_PHY_LIST \ +{ \ + { \ + .name = GMII_RTL8211F_NAME, \ + .id1 = GMII_PHYID1_RTL8211F, \ + .id2 = GMII_PHYID2_RTL8211F, \ + .status = GMII_RTL8211F_PHYSR_A43, \ + .address_lo = 2, \ + .address_high = 0xffff, \ + .mbps10 = GMII_RTL8211F_PHYSR_10MBPS, \ + .mbps100 = GMII_RTL8211F_PHYSR_100MBPS, \ + .duplex = GMII_RTL8211F_PHYSR_DUPLEX, \ + .clause = 22, \ + .mbps1000 = GMII_RTL8211F_PHYSR_1000MBPS, \ + .speed_mask = GMII_RTL8211F_PHYSR_SPEED_MASK, \ + }, \ +} + +#endif /* CONFIG_IMX9_ENET1 */ + +#ifdef CONFIG_IMX9_ENET1 + +#define MUX_ENET1_MDIO IOMUX_CFG(IOMUXC_PAD_ENET2_MDIO_ENET1_MDIO, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, IOMUXC_MUX_SION_ON) +#define MUX_ENET1_MDC IOMUX_CFG(IOMUXC_PAD_ENET2_MDC_ENET1_MDC, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) + +#define MUX_ENET1_RX_DATA00 IOMUX_CFG(IOMUXC_PAD_ENET2_RD0_ENET1_RGMII_RD0, 0, 0) +#define MUX_ENET1_RX_DATA01 IOMUX_CFG(IOMUXC_PAD_ENET2_RD1_ENET1_RGMII_RD1, 0, 0) + +#define MUX_ENET1_TX_DATA00 IOMUX_CFG(IOMUXC_PAD_ENET2_TD0_ENET1_RGMII_TD0, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) +#define MUX_ENET1_TX_DATA01 IOMUX_CFG(IOMUXC_PAD_ENET2_TD1_ENET1_RGMII_TD1, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) + +#if defined(CONFIG_IMX9_ENET1_RGMII) + +# define MUX_ENET1_RX_DATA02 IOMUX_CFG(IOMUXC_PAD_ENET2_RD2_ENET1_RGMII_RD2, 0, 0) +# define MUX_ENET1_RX_DATA03 IOMUX_CFG(IOMUXC_PAD_ENET2_RD3_ENET1_RGMII_RD3, 0, 0) +# define MUX_ENET1_TX_DATA02 IOMUX_CFG(IOMUXC_PAD_ENET2_TD2_ENET1_RGMII_TD2, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) +# define MUX_ENET1_TX_DATA03 IOMUX_CFG(IOMUXC_PAD_ENET2_TD3_ENET1_RGMII_TD3, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) +# define MUX_ENET1_RXC IOMUX_CFG(IOMUXC_PAD_ENET2_RXC_ENET1_RGMII_RXC, 0, 0) +# define MUX_ENET1_TX_CTL IOMUX_CFG(IOMUXC_PAD_ENET2_TX_CTL_ENET1_RGMII_TX_CTL, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) +# define MUX_ENET1_RX_CTL IOMUX_CFG(IOMUXC_PAD_ENET2_RX_CTL_ENET1_RGMII_RX_CTL, 0, 0) + +#elif defined(CONFIG_IMX9_ENET1_RMII) + +/* Same pin as TX_CTL for RGMII */ + +# define MUX_ENET1_TX_EN IOMUX_CFG(IOMUXC_PAD_ENET2_TX_CTL_ENET1_RGMII_TX_CTL, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) + +/* Same pin as TX_DATA02 for RGMII */ + +# define MUX_ENET1_REF_CLK IOMUX_CFG(IOMUXC_PAD_ENET2_TD2_ENET1_RGMII_TD2, IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_DSE_X6, 0) + +/* Same pin as RX_CTL for RGMII */ + +# define MUX_ENET1_CRS_DV IOMUX_CFG(IOMUXC_PAD_ENET2_RX_CTL_ENET1_RGMII_RX_CTL, 0, 0) + +#else +#error ENET1 supports only RMII and RGMII +#endif + +#define BOARD_ENET1_PHY_LIST \ +{ \ + { \ + GMII_RTL8211F_NAME, \ + GMII_PHYID1_RTL8211F, \ + GMII_PHYID2_RTL8211F, \ + GMII_RTL8211F_PHYSR_A43, \ + 2, \ + 0xffff, \ + GMII_RTL8211F_PHYSR_10MBPS, \ + GMII_RTL8211F_PHYSR_100MBPS, \ + GMII_RTL8211F_PHYSR_DUPLEX, \ + 22, \ + GMII_RTL8211F_PHYSR_1000MBPS, \ + GMII_RTL8211F_PHYSR_SPEED_MASK, \ + }, \ +} + /**************************************************************************** * Public Data ****************************************************************************/