From 89752e99934d63be8452354d673b82216dd2926d Mon Sep 17 00:00:00 2001 From: Ville Juven Date: Fri, 5 Apr 2024 12:59:22 +0300 Subject: [PATCH] arm64/imx9: Add eDMA driver This driver supports both eDMA3 and eDMA4 (also referred to as DMA0 / DMA1 in some contexts..) The IP blocks are almost identical, with sufficiently minor differences to use them via a unified driver. The price is a great amount of code obfuscation in the hardware description layer. --- arch/arm64/include/imx9/chip.h | 5 + arch/arm64/src/imx9/Kconfig | 90 +- arch/arm64/src/imx9/Make.defs | 4 + .../arm64/src/imx9/hardware/imx93/imx93_ccm.h | 4 +- .../src/imx9/hardware/imx93/imx93_dmamux.h | 209 +++ .../src/imx9/hardware/imx93/imx93_edma.h | 436 +++++ arch/arm64/src/imx9/hardware/imx9_dmamux.h | 36 + arch/arm64/src/imx9/hardware/imx9_edma.h | 36 + arch/arm64/src/imx9/imx9_edma.c | 1509 +++++++++++++++++ arch/arm64/src/imx9/imx9_edma.h | 482 ++++++ 10 files changed, 2807 insertions(+), 4 deletions(-) create mode 100644 arch/arm64/src/imx9/hardware/imx93/imx93_dmamux.h create mode 100644 arch/arm64/src/imx9/hardware/imx93/imx93_edma.h create mode 100644 arch/arm64/src/imx9/hardware/imx9_dmamux.h create mode 100644 arch/arm64/src/imx9/hardware/imx9_edma.h create mode 100644 arch/arm64/src/imx9/imx9_edma.c create mode 100644 arch/arm64/src/imx9/imx9_edma.h diff --git a/arch/arm64/include/imx9/chip.h b/arch/arm64/include/imx9/chip.h index 28553a96e2..ddb9a58e7e 100644 --- a/arch/arm64/include/imx9/chip.h +++ b/arch/arm64/include/imx9/chip.h @@ -31,6 +31,11 @@ * Pre-processor Definitions ****************************************************************************/ +/* Cache line sizes (in bytes)for the i.MX9 (Cortex-A55) */ + +#define ARMV8A_DCACHE_LINESIZE 64 /* 64 bytes (16 words) */ +#define ARMV8A_ICACHE_LINESIZE 64 /* 64 bytes (16 words) */ + /* Number of bytes in x kibibytes/mebibytes/gibibytes */ #define KB(x) ((x) << 10) diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index cebadac8dd..4ad6caf585 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -31,6 +31,11 @@ config IMX9_FLEXIO_PWM menu "i.MX9 Peripheral Selection" +config IMX9_EDMA + bool "eDMA" + default n + select ARCH_DMA + menu "LPUART" config IMX9_LPUART @@ -293,8 +298,6 @@ endmenu # USB device controller driver (DCD) options endif # IMX9_USBDEV -endmenu # iMX Peripheral Selection - config IMX9_GPIO_IRQ bool "GPIO Interrupt Support" default n @@ -547,6 +550,87 @@ menuconfig IMX9_LPSPI8 endmenu # LPSPI Peripherals +menu "eDMA Configuration" + depends on IMX9_EDMA + +config IMX9_EDMA_NTCD + int "Number of transfer descriptors" + default 0 + ---help--- + Number of pre-allocated transfer descriptors. Needed for scatter- + gather DMA. Make to be set to zero to disable in-memory TCDs in + which case only the TCD channel registers will be used and scatter- + will not be supported. + +config IMX9_EDMA_ELINK + bool "Channeling Linking" + default n + ---help--- + This option enables optional minor or major loop channel linking: + + Minor loop channel linking: As the channel completes the minor + loop, this flag enables linking to another channel. The link target + channel initiates a channel service request via an internal + mechanism that sets the TCDn_CSR[START] bit of the specified + channel. + + If minor loop channel linking is disabled, this link mechanism is + suppressed in favor of the major loop channel linking. + + Major loop channel linking: As the channel completes the minor + loop, this option enables the linking to another channel. The link + target channel initiates a channel service request via an internal + mechanism that sets the TCDn_CSR[START] bit of the linked channel. + +config IMX9_EDMA_ERCA + bool "Round Robin Channel Arbitration" + default n + ---help--- + Normally, a fixed priority arbitration is used for channel + selection. If this option is selected, round robin arbitration is + used for channel selection. + +config IMX9_EDMA_HOE + bool "Halt On Error" + default y + ---help--- + Any error causes the HALT bit to set. Subsequently, all service + requests are ignored until the HALT bit is cleared. + +config IMX9_EDMA_CLM + bool "Continuous Link Mode" + default n + ---help--- + By default, A minor loop channel link made to itself goes through + channel arbitration before being activated again. If this option is + selected, a minor loop channel link made to itself does not go + through channel arbitration before being activated again. Upon minor + loop completion, the channel activates again if that channel has a + minor loop channel link enabled and the link channel is itself. This + effectively applies the minor loop offsets and restarts the next + minor loop. + +config IMX9_EDMA_EMLIM + bool "Minor Loop Mapping" + default n + ---help--- + Normally TCD word 2 is a 32-bit NBYTES field. When this option is + enabled, TCD word 2 is redefined to include individual enable fields, + an offset field, and the NBYTES field. The individual enable fields + allow the minor loop offset to be applied to the source address, the + destination address, or both. The NBYTES field is reduced when either + offset is enabled. + +config IMX9_EDMA_EDBG + bool "Enable Debug" + default n + ---help--- + When in debug mode, the DMA stalls the start of a new channel. Executing + channels are allowed to complete. Channel execution resumes when the + system exits debug mode or the EDBG bit is cleared + +endmenu # eDMA Global Configuration + menu "LPI2C Configuration" depends on IMX9_LPI2C @@ -678,4 +762,6 @@ config IMX9_LPSPI8_DMA endmenu # LPSPI Configuration +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 c420afd939..dcca949e0f 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -51,3 +51,7 @@ endif ifeq ($(CONFIG_IMX9_LPSPI), y) CHIP_CSRCS += imx9_lpspi.c endif + +ifeq ($(CONFIG_IMX9_EDMA), y) + CHIP_CSRCS += imx9_edma.c +endif diff --git a/arch/arm64/src/imx9/hardware/imx93/imx93_ccm.h b/arch/arm64/src/imx9/hardware/imx93/imx93_ccm.h index ae46d228ff..6b43fb7edc 100644 --- a/arch/arm64/src/imx9/hardware/imx93/imx93_ccm.h +++ b/arch/arm64/src/imx9/hardware/imx93/imx93_ccm.h @@ -454,8 +454,8 @@ #define CCM_LPCG_SEMA2 18 #define CCM_LPCG_MU_A 19 #define CCM_LPCG_MU_B 20 -#define CCM_LPCG_EDMA1 21 -#define CCM_LPCG_EDMA2 22 +#define CCM_LPCG_EDMA3 21 +#define CCM_LPCG_EDMA4 22 #define CCM_LPCG_ROMCP_A55 23 #define CCM_LPCG_ROMCP_M33 24 #define CCM_LPCG_FLEXSPI1 25 diff --git a/arch/arm64/src/imx9/hardware/imx93/imx93_dmamux.h b/arch/arm64/src/imx9/hardware/imx93/imx93_dmamux.h new file mode 100644 index 0000000000..6301856ba5 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx93/imx93_dmamux.h @@ -0,0 +1,209 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx93/imx93_dmamux.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_IMX93_IMX93_DMAMUX_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX93_IMX93_DMAMUX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "imx93_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Identify channel MUX from 9th bit */ + +#define EDMA3_MUX_ID 0x0000 +#define EDMA4_MUX_ID 0x0100 +#define EDMA_MUX_ID_MASK 0xff00 +#define EDMA_MUX_MASK 0x00ff + +/* eDMA3 MUXs */ + +#define DMA_REQUEST_DISABLED (0) /**< Channel disabled */ +#define DMA_REQUEST_MUXCAN1 (1 | EDMA3_MUX_ID) /**< CAN1 */ +#define DMA_REQUEST_MUXGPIO1_0 (3 | EDMA3_MUX_ID) /**< GPIO1 channel 0 */ +#define DMA_REQUEST_MUXGPIO1_1 (4 | EDMA3_MUX_ID) /**< GPIO1 channel 1 */ +#define DMA_REQUEST_MUXI3C1TOBUS (5 | EDMA3_MUX_ID) /**< I3C1 To-bus Request */ +#define DMA_REQUEST_MUXI3C1FROMBUS (6 | EDMA3_MUX_ID) /**< I3C1 From-bus Request */ +#define DMA_REQUEST_MUXLPI2C1TX (7 | EDMA3_MUX_ID) /**< LPI2C1 */ +#define DMA_REQUEST_MUXLPI2C1RX (8 | EDMA3_MUX_ID) /**< LPI2C1 */ +#define DMA_REQUEST_MUXLPI2C2TX (9 | EDMA3_MUX_ID) /**< LPI2C2 */ +#define DMA_REQUEST_MUXLPI2C2RX (10 | EDMA3_MUX_ID) /**< LPI2C2 */ +#define DMA_REQUEST_MUXLPSPI1TX (11 | EDMA3_MUX_ID) /**< LPSPI1 Transmit */ +#define DMA_REQUEST_MUXLPSPI1RX (12 | EDMA3_MUX_ID) /**< LPSPI1 Receive */ +#define DMA_REQUEST_MUXLPSPI2TX (13 | EDMA3_MUX_ID) /**< LPSPI2 Transmit */ +#define DMA_REQUEST_MUXLPSPI2RX (14 | EDMA3_MUX_ID) /**< LPSPI2 Receive */ +#define DMA_REQUEST_MUXLPTMR1 (15 | EDMA3_MUX_ID) /**< LPTMR1 Request */ +#define DMA_REQUEST_MUXLPUART1TX (16 | EDMA3_MUX_ID) /**< LPUART1 Transmit */ +#define DMA_REQUEST_MUXLPUART1RX (17 | EDMA3_MUX_ID) /**< LPUART1 Receive */ +#define DMA_REQUEST_MUXLPUART2TX (18 | EDMA3_MUX_ID) /**< LPUART2 Transmit */ +#define DMA_REQUEST_MUXLPUART2RX (19 | EDMA3_MUX_ID) /**< LPUART2 Receive */ +#define DMA_REQUEST_MUXEDGELOCK (20 | EDMA3_MUX_ID) /**< Edgelock enclave DMA Request */ +#define DMA_REQUEST_MUXSAI1TX (21 | EDMA3_MUX_ID) /**< SAI1 Transmit */ +#define DMA_REQUEST_MUXSAI1RX (22 | EDMA3_MUX_ID) /**< SAI1 Receive */ +#define DMA_REQUEST_MUXTPM1_0_2 (23 | EDMA3_MUX_ID) /**< TPM1 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM1_1_3 (24 | EDMA3_MUX_ID) /**< TPM1 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM1OVERFLOW (25 | EDMA3_MUX_ID) /**< TPM1 Overflow request */ +#define DMA_REQUEST_MUXTPM2_0_2 (26 | EDMA3_MUX_ID) /**< TPM2 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM2_1_3 (27 | EDMA3_MUX_ID) /**< TPM2 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM2OVERFLOW (28 | EDMA3_MUX_ID) /**< TPM2 Overflow request */ +#define DMA_REQUEST_MUXPDM (29 | EDMA3_MUX_ID) /**< PDM */ +#define DMA_REQUEST_MUXADC1 (30 | EDMA3_MUX_ID) /**< ADC1 */ + +#define DMA3_REQUEST_MUX_COUNT (31) + +/* eDMA4 MUXs */ + +#define DMA_REQUEST_MUXCAN2 (1 | EDMA4_MUX_ID) /**< CAN2 */ +#define DMA_REQUEST_MUXGPIO2_0 (2 | EDMA4_MUX_ID) /**< GPIO2 channel 0 */ +#define DMA_REQUEST_MUXGPIO2_1 (3 | EDMA4_MUX_ID) /**< GPIO2 channel 1 */ +#define DMA_REQUEST_MUXGPIO3_0 (4 | EDMA4_MUX_ID) /**< GPIO3 channel 0 */ +#define DMA_REQUEST_MUXGPIO3_1 (5 | EDMA4_MUX_ID) /**< GPIO3 channel 1 */ +#define DMA_REQUEST_MUXI3C2TOBUS (6 | EDMA4_MUX_ID) /**< I3C2 To-bus Request */ +#define DMA_REQUEST_MUXI3C2FROMBUS (7 | EDMA4_MUX_ID) /**< I3C2 From-bus Request */ +#define DMA_REQUEST_MUXLPI2C3TX (8 | EDMA4_MUX_ID) /**< LPI2C3 */ +#define DMA_REQUEST_MUXLPI2C3RX (9 | EDMA4_MUX_ID) /**< LPI2C3 */ +#define DMA_REQUEST_MUXLPI2C4TX (10 | EDMA4_MUX_ID) /**< LPI2C4 */ +#define DMA_REQUEST_MUXLPI2C4RX (11 | EDMA4_MUX_ID) /**< LPI2C4 */ +#define DMA_REQUEST_MUXLPSPI3TX (12 | EDMA4_MUX_ID) /**< LPSPI3 Transmit */ +#define DMA_REQUEST_MUXLPSPI3RX (13 | EDMA4_MUX_ID) /**< LPSPI3 Receive */ +#define DMA_REQUEST_MUXLPSPI4TX (14 | EDMA4_MUX_ID) /**< LPSPI4 Transmit */ +#define DMA_REQUEST_MUXLPSPI4RX (15 | EDMA4_MUX_ID) /**< LPSPI4 Receive */ +#define DMA_REQUEST_MUXLPTMR2 (16 | EDMA4_MUX_ID) /**< LPTMR2 Request */ +#define DMA_REQUEST_MUXLPUART3TX (17 | EDMA4_MUX_ID) /**< LPUART3 Transmit */ +#define DMA_REQUEST_MUXLPUART3RX (18 | EDMA4_MUX_ID) /**< LPUART3 Receive */ +#define DMA_REQUEST_MUXLPUART4TX (19 | EDMA4_MUX_ID) /**< LPUART4 Transmit */ +#define DMA_REQUEST_MUXLPUART4RX (20 | EDMA4_MUX_ID) /**< LPUART4 Receive */ +#define DMA_REQUEST_MUXLPUART5TX (21 | EDMA4_MUX_ID) /**< LPUART5 Transmit */ +#define DMA_REQUEST_MUXLPUART5RX (22 | EDMA4_MUX_ID) /**< LPUART5 Receive */ +#define DMA_REQUEST_MUXLPUART6TX (23 | EDMA4_MUX_ID) /**< LPUART6 Transmit */ +#define DMA_REQUEST_MUXLPUART6RX (24 | EDMA4_MUX_ID) /**< LPUART6 Receive */ +#define DMA_REQUEST_MUXTPM3_0_2 (25 | EDMA4_MUX_ID) /**< TPM3 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM3_1_3 (26 | EDMA4_MUX_ID) /**< TPM3 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM3OVERFLOW (27 | EDMA4_MUX_ID) /**< TPM3 Overflow request */ +#define DMA_REQUEST_MUXTPM4_0_2 (28 | EDMA4_MUX_ID) /**< TPM4 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM4_1_3 (29 | EDMA4_MUX_ID) /**< TPM4 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM4OVERFLOW (30 | EDMA4_MUX_ID) /**< TPM4 Overflow request */ +#define DMA_REQUEST_MUXTPM5_0_2 (31 | EDMA4_MUX_ID) /**< TPM5 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM5_1_3 (32 | EDMA4_MUX_ID) /**< TPM5 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM5OVERFLOW (33 | EDMA4_MUX_ID) /**< TPM5 Overflow request */ +#define DMA_REQUEST_MUXTPM6_0_2 (34 | EDMA4_MUX_ID) /**< TPM6 request 0 and request 2 */ +#define DMA_REQUEST_MUXTPM6_1_3 (35 | EDMA4_MUX_ID) /**< TPM6 request 1 and request 3 */ +#define DMA_REQUEST_MUXTPM6OVERFLOW (36 | EDMA4_MUX_ID) /**< TPM6 Overflow request */ +#define DMA_REQUEST_MUXFLEXIO1_0 (37 | EDMA4_MUX_ID) /**< FlexIO1 Request0 */ +#define DMA_REQUEST_MUXFLEXIO1_1 (38 | EDMA4_MUX_ID) /**< FlexIO1 Request1 */ +#define DMA_REQUEST_MUXFLEXIO1_2 (39 | EDMA4_MUX_ID) /**< FlexIO1 Request2 */ +#define DMA_REQUEST_MUXFLEXIO1_3 (40 | EDMA4_MUX_ID) /**< FlexIO1 Request3 */ +#define DMA_REQUEST_MUXFLEXIO1_4 (41 | EDMA4_MUX_ID) /**< FlexIO1 Request4 */ +#define DMA_REQUEST_MUXFLEXIO1_5 (42 | EDMA4_MUX_ID) /**< FlexIO1 Request5 */ +#define DMA_REQUEST_MUXFLEXIO1_6 (43 | EDMA4_MUX_ID) /**< FlexIO1 Request6 */ +#define DMA_REQUEST_MUXFLEXIO1_7 (44 | EDMA4_MUX_ID) /**< FlexIO1 Request7 */ +#define DMA_REQUEST_MUXFLEXIO2_0 (45 | EDMA4_MUX_ID) /**< FlexIO2 Request0 */ +#define DMA_REQUEST_MUXFLEXIO2_1 (46 | EDMA4_MUX_ID) /**< FlexIO2 Request1 */ +#define DMA_REQUEST_MUXFLEXIO2_2 (47 | EDMA4_MUX_ID) /**< FlexIO2 Request2 */ +#define DMA_REQUEST_MUXFLEXIO2_3 (48 | EDMA4_MUX_ID) /**< FlexIO2 Request3 */ +#define DMA_REQUEST_MUXFLEXIO2_4 (49 | EDMA4_MUX_ID) /**< FlexIO2 Request4 */ +#define DMA_REQUEST_MUXFLEXIO2_5 (50 | EDMA4_MUX_ID) /**< FlexIO2 Request5 */ +#define DMA_REQUEST_MUXFLEXIO2_6 (51 | EDMA4_MUX_ID) /**< FlexIO2 Request6 */ +#define DMA_REQUEST_MUXFLEXIO2_7 (52 | EDMA4_MUX_ID) /**< FlexIO2 Request7 */ +#define DMA_REQUEST_MUXFLEXSPI1TX (53 | EDMA4_MUX_ID) /**< FlexSPI1 Transmit */ +#define DMA_REQUEST_MUXFLEXSPI1RX (54 | EDMA4_MUX_ID) /**< FlexSPI1 Receive */ +#define DMA_REQUEST_MUXSAI2TX (58 | EDMA4_MUX_ID) /**< SAI2 Transmit */ +#define DMA_REQUEST_MUXSAI2RX (59 | EDMA4_MUX_ID) /**< SAI2 Receive */ +#define DMA_REQUEST_MUXSAI3TX (60 | EDMA4_MUX_ID) /**< SAI3 Transmit */ +#define DMA_REQUEST_MUXSAI3RX (61 | EDMA4_MUX_ID) /**< SAI3 Receive */ +#define DMA_REQUEST_MUXGPIO4_0 (62 | EDMA4_MUX_ID) /**< GPIO4 channel 0 */ +#define DMA_REQUEST_MUXGPIO4_1 (63 | EDMA4_MUX_ID) /**< GPIO4 channel 1 */ +#define DMA_REQUEST_MUXSPDIF (65 | EDMA4_MUX_ID) /**< SPDIF */ +#define DMA_REQUEST_MUXSPDIF_1 (66 | EDMA4_MUX_ID) /**< SPDIF */ +#define DMA_REQUEST_MUXENET (67 | EDMA4_MUX_ID) /**< ENET */ +#define DMA_REQUEST_MUXENET_1 (68 | EDMA4_MUX_ID) /**< ENET */ +#define DMA_REQUEST_MUXENET_2 (69 | EDMA4_MUX_ID) /**< ENET */ +#define DMA_REQUEST_MUXENET_3 (70 | EDMA4_MUX_ID) /**< ENET */ +#define DMA_REQUEST_MUXLPI2C5TX (71 | EDMA4_MUX_ID) /**< LPI2C5 */ +#define DMA_REQUEST_MUXLPI2C5RX (72 | EDMA4_MUX_ID) /**< LPI2C5 */ +#define DMA_REQUEST_MUXLPI2C6TX (73 | EDMA4_MUX_ID) /**< LPI2C6 */ +#define DMA_REQUEST_MUXLPI2C6RX (74 | EDMA4_MUX_ID) /**< LPI2C6 */ +#define DMA_REQUEST_MUXLPI2C7TX (75 | EDMA4_MUX_ID) /**< LPI2C7 */ +#define DMA_REQUEST_MUXLPI2C7RX (76 | EDMA4_MUX_ID) /**< LPI2C7 */ +#define DMA_REQUEST_MUXLPI2C8TX (77 | EDMA4_MUX_ID) /**< LPI2C8 */ +#define DMA_REQUEST_MUXLPI2C8RX (78 | EDMA4_MUX_ID) /**< LPI2C8 */ +#define DMA_REQUEST_MUXLPSPI5TX (79 | EDMA4_MUX_ID) /**< LPSPI5 Transmit */ +#define DMA_REQUEST_MUXLPSPI5RX (80 | EDMA4_MUX_ID) /**< LPSPI5 Receive */ +#define DMA_REQUEST_MUXLPSPI6TX (81 | EDMA4_MUX_ID) /**< LPSPI6 Transmit */ +#define DMA_REQUEST_MUXLPSPI6RX (82 | EDMA4_MUX_ID) /**< LPSPI6 Receive */ +#define DMA_REQUEST_MUXLPSPI7TX (83 | EDMA4_MUX_ID) /**< LPSPI7 Transmit */ +#define DMA_REQUEST_MUXLPSPI7RX (84 | EDMA4_MUX_ID) /**< LPSPI7 Receive */ +#define DMA_REQUEST_MUXLPSPI8TX (85 | EDMA4_MUX_ID) /**< LPSPI8 Transmit */ +#define DMA_REQUEST_MUXLPSPI8RX (86 | EDMA4_MUX_ID) /**< LPSPI8 Receive */ +#define DMA_REQUEST_MUXLPUART7TX (87 | EDMA4_MUX_ID) /**< LPUART7 Transmit */ +#define DMA_REQUEST_MUXLPUART7RX (88 | EDMA4_MUX_ID) /**< LPUART7 Receive */ +#define DMA_REQUEST_MUXLPUART8TX (89 | EDMA4_MUX_ID) /**< LPUART8 Transmit */ +#define DMA_REQUEST_MUXLPUART8RX (90 | EDMA4_MUX_ID) /**< LPUART8 Receive */ +#define DMA_REQUEST_MUXENET_QOS (91 | EDMA4_MUX_ID) /**< ENET_QOS */ +#define DMA_REQUEST_MUXENET_QOS_1 (92 | EDMA4_MUX_ID) /**< ENET_QOS */ +#define DMA_REQUEST_MUXENET_QOS_2 (93 | EDMA4_MUX_ID) /**< ENET_QOS */ +#define DMA_REQUEST_MUXENET_QOS_3 (94 | EDMA4_MUX_ID) /**< ENET_QOS */ + +#define DMA4_REQUEST_MUX_COUNT (95) + +/* Combined MUX count (eDMA3 and eDMA4) */ + +#define DMA_REQUEST_MUX_COUNT (DMA3_REQUEST_MUX_COUNT + DMA4_REQUEST_MUX_COUNT) + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_dmamux_get_dmabase + * + * Description: + * Get DMA engine base address from MUX identifier. + * + * Input Parameters: + * dmamux - The DMA MUX identifier. + * + * Returned Value: + * Base address of the associated DMA engine. + * + ****************************************************************************/ + +static inline uintptr_t imx9_dmamux_get_dmabase(uint16_t dmamux) +{ + if ((dmamux & EDMA_MUX_ID_MASK) == EDMA3_MUX_ID) + { + return IMX9_DMA3_BASE; + } + else + { + return IMX9_DMA4_BASE; + } +} + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX93_IMX93_DMAMUX_H */ diff --git a/arch/arm64/src/imx9/hardware/imx93/imx93_edma.h b/arch/arm64/src/imx9/hardware/imx93/imx93_edma.h new file mode 100644 index 0000000000..968b6e09db --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx93/imx93_edma.h @@ -0,0 +1,436 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx93/imx93_edma.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_IMX93_IMX93_EDMA_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX93_IMX93_EDMA_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "imx93_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* eDMA3 / eDMA4 Register Offsets */ + +#define IMX9_EDMA_CSR_OFFSET (0x000000) /* Management Page Control Register (CSR) */ +#define IMX9_EDMA_ES_OFFSET (0x000004) /* Management Page Error Status Register (ES) */ +#define IMX9_EDMA_CH_GRPRI_OFFSET(n) (0x000100 + ((n) << 2)) /* Channel n Arbitration Group Register (CHn_GRPRI) */ + +/* eDMA3 only */ + +#define IMX9_EDMA_INT_OFFSET (0x000008) /* Management Page Interrupt Request Status Register (INT) */ +#define IMX9_EDMA_HRS_OFFSET (0x00000c) /* Management Page Hardware Request Status Register (HRS) */ + +/* eDMA4 only */ + +#define IMX9_EDMA_INT_LOW_OFFSET (0x000008) /* Management Page Interrupt Request Status Register (INT_LOW) */ +#define IMX9_EDMA_INT_HIGH_OFFSET (0x00000c) /* Management Page Interrupt Request Status Register (INT_HIGH) */ +#define IMX9_EDMA_HRS_LOW_OFFSET (0x000010) /* Management Page Hardware Request Status Register (HRS_LOW) */ +#define IMX9_EDMA_HRS_HIGH_OFFSET (0x000014) /* Management Page Hardware Request Status Register (HRS_HIGH) */ + +/* eDMA3 / eDMA4 Register Addresses */ + +#define IMX9_EDMA_CSR(n) ((n) + IMX9_EDMA_CSR_OFFSET) +#define IMX9_EDMA_ES(n) ((n) + IMX9_EDMA_ES_OFFSET) +#define IMX9_EDMA_CH_GRPRI(n,c) ((n) + IMX9_EDMA_CH_GRPRI_OFFSET(n)) + +/* eDMA3 only */ + +#define IMX9_EDMA_INT (IMX9_DMA3_BASE + IMX9_EDMA_INT_OFFSET) +#define IMX9_EDMA_HRS (IMX9_DMA3_BASE + IMX9_EDMA_HRS_OFFSET) + +/* eDMA4 only */ + +#define IMX9_EDMA_INT_LOW (IMX9_DMA4_BASE + IMX9_EDMA_INT_LOW_OFFSET) +#define IMX9_EDMA_INT_HIGH (IMX9_DMA4_BASE + IMX9_EDMA_INT_HIGH_OFFSET) +#define IMX9_EDMA_HRS_LOW (IMX9_DMA4_BASE + IMX9_EDMA_HRS_LOW_OFFSET) +#define IMX9_EDMA_HRS_HIGH (IMX9_DMA4_BASE + IMX9_EDMA_HRS_HIGH_OFFSET) + +/* eDMA Transfer Control Descriptor (TCD) Register Offsets */ + +#define IMX9_EDMA_CH_CSR_OFFSET (0x000000) /* Channel Control and Status Register (CH0_CSR) */ +#define IMX9_EDMA_CH_ES_OFFSET (0x000004) /* Channel Error Status Register (CH0_ES) */ +#define IMX9_EDMA_CH_INT_OFFSET (0x000008) /* Channel Interrupt Status Register (CH0_INT) */ +#define IMX9_EDMA_CH_SBR_OFFSET (0x00000c) /* Channel System Bus Register (CH0_SBR) */ +#define IMX9_EDMA_CH_PRI_OFFSET (0x000010) /* Channel Priority Register (CH0_PRI) */ +#define IMX9_EDMA_CH_MUX_OFFSET (0x000014) /* Channel Multiplexor Configuration (CH0_MUX) (eDMA4 only) */ +#define IMX9_EDMA_CH_MATTR_OFFSET (0x000018) /* Memory Attributes Register (CH0_MATTR) (eDMA4 only) */ +#define IMX9_EDMA_TCD_SADDR_OFFSET (0x000020) /* TCD Source Address Register (TCD0_SADDR) */ +#define IMX9_EDMA_TCD_SOFF_OFFSET (0x000024) /* TCD Signed Source Address Offset Register (TCD0_SOFF) */ +#define IMX9_EDMA_TCD_ATTR_OFFSET (0x000026) /* TCD Transfer Attributes (TCD0_ATTR) */ +#define IMX9_EDMA_TCD_NBYTES_OFFSET (0x000028) /* TCD Transfer Size (TCD0_NBYTES) */ +#define IMX9_EDMA_TCD_SLAST_SDA_OFFSET (0x00002c) /* TCD Last Source Address Adjustment / Store DADDR Address Register (TCD0_SLAST_SDA) */ +#define IMX9_EDMA_TCD_DADDR_OFFSET (0x000030) /* TCD Destination Address Register (TCD0_DADDR) */ +#define IMX9_EDMA_TCD_DOFF_OFFSET (0x000034) /* TCD Signed Destination Address Offset Register (TCD0_DOFF) */ +#define IMX9_EDMA_TCD_CITER_OFFSET (0x000036) /* TCD Current Major Loop Count Register (TCD0_CITER) */ +#define IMX9_EDMA_TCD_DLAST_SGA_OFFSET (0x000038) /* TCD Last Destination Address Adjustment / Scatter Gather Address Register (TCD0_DLAST_SGA)*/ +#define IMX9_EDMA_TCD_CSR_OFFSET (0x00003c) /* TCD Control and Status Register (TCD0_CSR) */ +#define IMX9_EDMA_TCD_BITER_OFFSET (0x00003e) /* TCD Beginning Major Loop Count Register (TCD0_BITER) */ + +/* eDMA 3 and eDMA 4 have TCD instance offsets, but same base offset */ + +#define IMX9_EDMA_TCD_BASE_OFFSET (0x10000) /* Offset to TCD for both eDMA3/4 */ +#define IMX9_EDMA3_TCD_INST_OFFSET (0x10000) /* Per instance TCD offset for eDMA3 */ +#define IMX9_EDMA4_TCD_INST_OFFSET (0x8000) /* Per instance TCD offset for eDMA4 */ +#define IMX9_EDMA_TCD_BASE(n) ((n) + IMX9_EDMA_TCD_BASE_OFFSET) +#define IMX9_EDMA_TCD_INST_OFFSET(n) ((n) == IMX9_DMA3_BASE ? IMX9_EDMA3_TCD_INST_OFFSET : IMX9_EDMA4_TCD_INST_OFFSET) +#define IMX9_EDMA_TCD(n,t) (IMX9_EDMA_TCD_BASE(n) + (t) * IMX9_EDMA_TCD_INST_OFFSET(n)) + +/* eDMA Transfer Control Descriptor (TCD) Register Addresses ****************/ + +#define IMX9_EDMA_CH_CSR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_CSR_OFFSET) +#define IMX9_EDMA_CH_ES(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_ES_OFFSET) +#define IMX9_EDMA_CH_INT(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_INT_OFFSET) +#define IMX9_EDMA_CH_SBR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_SBR_OFFSET) +#define IMX9_EDMA_CH_PRI(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_PRI_OFFSET) +#define IMX9_EDMA_CH_MUX(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_CH_MUX_OFFSET) +#define IMX9_EDMA_TCD_SADDR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_SADDR_OFFSET) +#define IMX9_EDMA_TCD_SOFF(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_SOFF_OFFSET) +#define IMX9_EDMA_TCD_ATTR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_ATTR_OFFSET) +#define IMX9_EDMA_TCD_NBYTES(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_NBYTES_OFFSET) +#define IMX9_EDMA_TCD_SLAST_SDA(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_SLAST_SDA_OFFSET) +#define IMX9_EDMA_TCD_DADDR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_DADDR_OFFSET) +#define IMX9_EDMA_TCD_DOFF(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_DOFF_OFFSET) +#define IMX9_EDMA_TCD_CITER(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_CITER_OFFSET) +#define IMX9_EDMA_TCD_DLAST_SGA(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_DLAST_SGA_OFFSET) +#define IMX9_EDMA_TCD_CSR(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_CSR_OFFSET) +#define IMX9_EDMA_TCD_BITER(n,t) (IMX9_EDMA_TCD(n,t) + IMX9_EDMA_TCD_BITER_OFFSET) + +/* eDMA Register Bitfield Definitions ***************************************/ + +/* Management Page Control Register (CSR) */ + + /* Bit 0: Reserved */ +#define EDMA_CSR_EDBG (1 << 1) /* Bit 1: Enable Debug (EDBG) */ +#define EDMA_CSR_ERCA (1 << 2) /* Bit 2: Enable Round Robin Channel Arbitration (ERCA) */ + /* Bit 3: Reserved */ +#define EDMA_CSR_HAE (1 << 4) /* Bit 4: Halt After Error (HAE) */ +#define EDMA_CSR_HALT (1 << 5) /* Bit 5: Halt DMA Operations (HALT) */ +#define EDMA_CSR_GCLC (1 << 6) /* Bit 6: Global Channel Linking Control (GCLC) */ +#define EDMA_CSR_GMRC (1 << 7) /* Bit 7: Global Master ID Replication Control (GMRC) */ +#define EDMA_CSR_ECX (1 << 8) /* Bit 8: Cancel Transfer With Error (ECX) */ +#define EDMA_CSR_CX (1 << 9) /* Bit 9: Cancel Transfer (CX) */ + /* Bits 10-23: Reserved */ +#define EDMA_CSR_ACTIVE_ID_SHIFT (24) /* Bits 24-28: Active Channel ID (ACTIVE_ID) */ +#define EDMA_CSR_ACTIVE_ID_MASK (0x1f << EDMA_CSR_ACTIVE_ID_SHIFT) + /* Bits 29-30: Reserved */ +#define EDMA_CSR_ACTIVE (1 << 31) /* Bit 31: DMA Active Status (ACTIVE) */ + +/* Management Page Error Status Register (ES) */ + +#define EDMA_ES_DBE (1 << 0) /* Bit 0: Destination Bus Error (DBE) */ +#define EDMA_ES_SBE (1 << 1) /* Bit 1: Source Bus Error (SBE) */ +#define EDMA_ES_SGE (1 << 2) /* Bit 2: Scatter/Gather Configuration Error (SGE) */ +#define EDMA_ES_NCE (1 << 3) /* Bit 3: NBYTES/CITER Configuration Error (NCE) */ +#define EDMA_ES_DOE (1 << 4) /* Bit 4: Destination Offset Error (DOE) */ +#define EDMA_ES_DAE (1 << 5) /* Bit 5: Destination Address Error (DAE) */ +#define EDMA_ES_SOE (1 << 6) /* Bit 6: Source Offset Error (SOE) */ +#define EDMA_ES_SAE (1 << 7) /* Bit 7: Source Address Error (SAE) */ +#define EDMA_ES_ECX (1 << 8) /* Bit 8: Transfer Canceled (ECX) */ + /* Bits 9-23: Reserved */ +#define EDMA_ES_ERRCHN_SHIFT (24) /* Bits 24-28: Error Channel Number or Canceled Channel Number (ERRCHN) */ +#define EDMA_ES_ERRCHN_MASK (0x1f << EDMA_ES_ERRCHN_SHIFT) + /* Bits 29-30: Reserved */ +#define EDMA_ES_VLD (1 << 31) /* Bit 31: Logical OR of all ERR status fields (VALID) */ + +/* Management Page Interrupt Request Status Register (INT) */ + +#define EDMA_INT(n) (1 << (n)) /* Bit n: Interrupt Request Status (INT) */ + +/* Management Page Hardware Request Status Register (HRS) */ + +#define EDMA_HRS(n) (1 << (n)) /* Bit n: Hardware Request Status (HRS) */ + +/* Channel n Arbitration Group Register (CHn_GRPRI) */ + +#define EDMA_CH_GRPRI_SHIFT (0) /* Bits 0-4: Arbitration Group For Channel n (GRPRI) */ +#define EDMA_CH_GRPRI_MASK (0x1f << EDMA_CH_GRPRI_SHIFT) + /* Bits 5-31: Reserved */ + +/* eDMA Transfer Control Descriptor (TCD) Bitfield Definitions **************/ + +/* Channel n Control and Status Register (CHn_CSR) */ + +#define EDMA_CH_CSR_ERQ (1 << 0) /* Bit 0: Enable DMA Request (ERQ) */ +#define EDMA_CH_CSR_EARQ (1 << 1) /* Bit 1: Enable Asynchronous DMA Request in Stop Mode for Channel (EARQ) */ +#define EDMA_CH_CSR_EEI (1 << 2) /* Bit 2: Enable Error Interrupt (EEI) */ +#define EDMA_CH_CSR_EBW (1 << 3) /* Bit 3: Enable Buffered Writes (EBW) */ + /* Bit 4-29: Reserved */ +#define EDMA_CH_CSR_DONE (1 << 30) /* Bit 30: Channel Done (DONE) */ +#define EDMA_CH_CSR_ACTIVE (1 << 31) /* Bit 31: CHannel Active (ACTIVE) */ + +/* Channel n Error Status Register (CHn_ES) */ + +#define EDMA_CH_ES_DBE (1 << 0) /* Bit 0: Destination Bus Error (DBE) */ +#define EDMA_CH_ES_SBE (1 << 1) /* Bit 1: Source Bus Error (SBE) */ +#define EDMA_CH_ES_SGE (1 << 2) /* Bit 2: Scatter/Gather Configuration Error (SGE) */ +#define EDMA_CH_ES_NCE (1 << 3) /* Bit 3: NBYTES/CITER Configuration Error (NCE) */ +#define EDMA_CH_ES_DOE (1 << 4) /* Bit 4: Destination Offset Error (DOE) */ +#define EDMA_CH_ES_DAE (1 << 5) /* Bit 5: Destination Address Error (DAE) */ +#define EDMA_CH_ES_SOE (1 << 6) /* Bit 6: Source Offset Error (SOE) */ +#define EDMA_CH_ES_SAE (1 << 7) /* Bit 7: Source Address Error (SAE) */ + /* Bit 8-30: Reserved */ +#define EDMA_CH_ES_ERR (1 << 31) /* Bit 31: Error in this channel (ERR) */ + +/* Channel n Interrupt Status Register (CHn_INT) */ + +#define EDMA_CH_INT (1 << 0) /* Bit 0: Interrupt Request (INT) */ + /* Bits 1-31: Reserved */ + +/* Channel n System Bus Register (CHn_SBR) */ + +#define EDMA_CH_SBR_MID_SHIFT (0) /* Bits 0-3: Master ID (MID) */ +#define EDMA_CH_SBR_MID_MASK (0x0f << EDMA_CH_SBR_MID_SHIFT) + /* Bits 4-13: Reserved */ +#define EDMA_CH_SBR_SEC (1 << 14) /* Bit 14: Security Level (SEC) */ +#define EDMA_CH_SBR_PAL (1 << 15) /* Bit 15: Privileged Access Level (PAL) */ +#define EDMA_CH_SBR_EMI (1 << 16) /* Bit 16: Enable Master ID Replication (EMI) */ +#define EDMA_CH_SBR_ATTR_SHIFT (17) /* Bits 17-19: Attribute Output (ATTR) */ +#define EDMA_CH_SBR_ATTR_MASK (0x07 << EDMA_CH_SBR_ATTR_SHIFT) + /* Bits 20-31: Reserved */ + +/* Channel n Priority Register (CHn_PRI) */ + +#define EDMA_CH_PRI_APL_SHIFT (0) /* Bits 0-2: Arbitration Priority Level (APL) */ +#define EDMA_CH_PRI_APL_MASK (0x07 << EDMA_CH_PRI_APL_SHIFT) + /* Bits 3-29: Reserved */ +#define EDMA_CH_PRI_DPA (1 << 30) /* Bit 30: Disable Preempt Ability (DPA) */ +#define EDMA_CH_PRI_ECP (1 << 31) /* Bit 31: Enable Channel Preemption (ECP) */ + +/* Channel Multiplexor Configuration (CHn_MUX) */ + +#define EDMA_CH_SRC_SHIFT (0) /* Bits 0-6: Service Request Source */ +#define EDMA_CH_SRC_MASK (0x7f << EDMA_CH_SRC_SHIFT) + +/* TCDn Source Address Register (TCDn_SADDR) */ + +#define EDMA_TCD_SADDR_SHIFT (0) /* Bits 0-31: Source Address (SADDR) */ +#define EDMA_TCD_SADDR_MASK (0xffffffff << EDMA_TCD_SADDR_SHIFT) + +/* TCDn Signed Source Address Offset Register (TCDn_SOFF) */ + +#define EDMA_TCD_SOFF_SHIFT (0) /* Bits 0-31: Source Address Signed Offset (SOFF) */ +#define EDMA_TCD_SOFF_MASK (0xffffffff << EDMA_TCD_SOFF_SHIFT) + +/* TCDn Transfer Attributes (TCDn_ATTR) */ + +#define EDMA_TCD_ATTR_DSIZE_SHIFT (0) /* Bits 0-2: Destination Data Transfer Size (DSIZE) */ +#define EDMA_TCD_ATTR_DSIZE_MASK (0x07 << EDMA_TCD_ATTR_DSIZE_SHIFT) +#define EDMA_TCD_ATTR_DSIZE(n) (((n) << EDMA_TCD_ATTR_DSIZE_SHIFT) & EDMA_TCD_ATTR_DSIZE_MASK) +#define EDMA_TCD_ATTR_DMOD_SHIFT (3) /* Bits 3-7: Destination Address Modulo (DMOD) */ +#define EDMA_TCD_ATTR_DMOD_MASK (0x1f << EDMA_TCD_ATTR_DMOD_SHIFT) +#define EDMA_TCD_ATTR_DMOD(n) (((n) << EDMA_TCD_ATTR_DMOD_SHIFT) & EDMA_TCD_ATTR_DMOD_MASK) +#define EDMA_TCD_ATTR_SSIZE_SHIFT (8) /* Bits 8-10: Source Data Transfer Size (SSIZE) */ +#define EDMA_TCD_ATTR_SSIZE_MASK (0x07 << EDMA_TCD_ATTR_SSIZE_SHIFT) +#define EDMA_TCD_ATTR_SSIZE(n) (((n) << EDMA_TCD_ATTR_SSIZE_SHIFT) & EDMA_TCD_ATTR_SSIZE_MASK) +# define EDMA_TCD_ATTR_SSIZE_8BIT (0x00 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 8-bit */ +# define EDMA_TCD_ATTR_SSIZE_16BIT (0x01 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 16-bit */ +# define EDMA_TCD_ATTR_SSIZE_32BIT (0x02 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 32-bit */ +# define EDMA_TCD_ATTR_SSIZE_64BIT (0x03 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 64-bit */ +# define EDMA_TCD_ATTR_SSIZE_16BYTE (0x04 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 16-byte */ +# define EDMA_TCD_ATTR_SSIZE_32BYTE (0x05 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 32-byte */ +# define EDMA_TCD_ATTR_SSIZE_64BYTE (0x06 << EDMA_TCD_ATTR_SSIZE_SHIFT) /* 64-byte */ + +#define EDMA_TCD_ATTR_SMOD_SHIFT (11) /* Bits 11-15: Source Address Modulo (SMOD) */ +#define EDMA_TCD_ATTR_SMOD_MASK (0x1f << EDMA_TCD_ATTR_SMOD_SHIFT) +#define EDMA_TCD_ATTR_SMOD(n) (((n) << EDMA_TCD_ATTR_SMOD_SHIFT) & EDMA_TCD_ATTR_SMOD_MASK) + +/* TCDn Transfer Size (TCDn_NBYTES) */ + +#define EDMA_TCD_NBYTES_SHIFT (0) /* Bits 0-29: Number of Bytes to Transfer per Service Request (NBYTES) */ +#define EDMA_TCD_NBYTES_MASK (0x3fffffff << EDMA_TCD_NBYTES_SHIFT) +#define EDMA_TCD_NBYTES_MASK_MLOFF (0x03ff << EDMA_TCD_NBYTES_SHIFT) +#define EDMA_TCD_NBYTES_MLOFF_SHIFT (10) /* Bits 10-29: Minor Loop Offset (MLOFF) */ +#define EDMA_TCD_NBYTES_MLOFF_MASK (0x0fffff << EDMA_TCD_NBYTES_MLOFF_SHIFT) +#define EDMA_TCD_NBYTES_DMLOE (1 << 30) /* Bit 30: Destination Minor Loop Offset Enable (DMLOE) */ +#define EDMA_TCD_NBYTES_SMLOE (1 << 31) /* Bit 31: Source Minor Loop Offset Enable (SMLOE) */ + +/* TCDn Last Source Address Adjustment / Store DADDR Address Register + * (TCDn_SLAST_SDA) + */ + +#define EDMA_TCD_SLAST_SDA_SHIFT (0) /* Bits 0-31: Last Source Address Adjustment / Store DADDR Address (SLAST_SDA) */ +#define EDMA_TCD_SLAST_SDA_MASK (0xffffffff << EDMA_TCD_SLAST_SDA_SHIFT) + +/* TCDn Destination Address Register (TCDn_DADDR) */ + +#define EDMA_TCD_DADDR_SHIFT (0) /* Bits 0-31: Destination Address (DADDR) */ +#define EDMA_TCD_DADDR_MASK (0xffffffff << EDMA_TCD_DADDR_SHIFT) + +/* TCDn Signed Destination Address Offset Register (TCDn_DOFF) */ + +#define EDMA_TCD_DOFF_SHIFT (0) /* Bits 0-15: Destination Address Signed Offset (DOFF) */ +#define EDMA_TCD_DOFF_MASK (0xffff << EDMA_TCD_DOFF_SHIFT) + +/* TCDn Current Major Loop Count Register (TCDn_CITER) */ + +#define EDMA_TCD_CITER_SHIFT (0) /* Bits 0-14: Current Major Iteration Count (CITER) */ +#define EDMA_TCD_CITER_MASK (0x7fff << EDMA_TCD_CITER_SHIFT) +#define EDMA_TCD_CITER_MASK_ELINK (0x01ff << EDMA_TCD_CITER_SHIFT) +#define EDMA_TCD_CITER_LINKCH_SHIFT (9) /* Bits 9-13: Minor Loop Link Channel Number (LINKCH) */ +#define EDMA_TCD_CITER_LINKCH_MASK (0x1f << EDMA_TCD_CITER_LINKCH_SHIFT) +#define EDMA_TCD_CITER_LINKCH(n) (((n) << EDMA_TCD_CITER_LINKCH_SHIFT) & EDMA_TCD_CITER_LINKCH_SHIFT) +#define EDMA_TCD_CITER_ELINK (1 << 15) /* Bit 15: Enable Link (ELINK) */ + +/* TCDn Last Destination Address Adjustment / Scatter Gather Address Register + * (TCDn_DLAST_SGA) + */ + +#define EDMA_TCD_DLAST_SGA_SHIFT (0) /* Bits 0-31: Last Destination Address Adjustment / Scatter Gather Address (DLAST_SGA) */ +#define EDMA_TCD_DLAST_SGA_MASK (0xffffffff << EDMA_TCD_DLAST_SGA_SHIFT) + +/* TCDn Control and Status Register (TCDn_CSR) */ + +#define EDMA_TCD_CSR_START (1 << 0) /* Bit 0: Channel Start (START) */ +#define EDMA_TCD_CSR_INTMAJOR (1 << 1) /* Bit 1: Enable Interrupt if Major count complete (INTMAJOR) */ +#define EDMA_TCD_CSR_INTHALF (1 << 2) /* Bit 2: Enable Interrupt if Major Count Half-complete (INTHALF) */ +#define EDMA_TCD_CSR_DREQ (1 << 3) /* Bit 3: Disable Request (DREQ) */ +#define EDMA_TCD_CSR_ESG (1 << 4) /* Bit 4: Enable Scatter/Gather Processing (ESG) */ +#define EDMA_TCD_CSR_MAJORELINK (1 << 5) /* Bit 5: Enable Link When Major Loop Complete (MAJORELINK) */ +#define EDMA_TCD_CSR_EEOP (1 << 6) /* Bit 6: Enable End-Of-Packet Processing (EEOP) */ +#define EDMA_TCD_CSR_ESDA (1 << 7) /* Bit 7: Enable Store Destination Address (ESDA) */ +#define EDMA_TCD_CSR_MAJORLINKCH_SHIFT (8) /* Bits 8-12: Major Loop Link Channel Number (MAJORLINKCH) */ +#define EDMA_TCD_CSR_MAJORLINKCH_MASK (0x1f << EDMA_TCD_CSR_MAJORLINKCH_SHIFT) +#define EDMA_TCD_CSR_MAJORLINKCH(n) (((n) << EDMA_TCD_CSR_MAJORLINKCH_SHIFT) & EDMA_TCD_CSR_MAJORLINKCH_MASK) + /* Bit 13: Reserved */ +#define EDMA_TCD_CSR_BWC_SHIFT (14) /* Bits 14-15: Bandwidth Control (BWC) */ +#define EDMA_TCD_CSR_BWC_MASK (0x03 << EDMA_TCD_CSR_BWC_SHIFT) +# define EDMA_TCD_CSR_BWC_NOSTALL (0x00 << EDMA_TCD_CSR_BWC_SHIFT) /* No eDMA engine stalls */ +# define EDMA_TCD_CSR_BWC_HPE (0x01 << EDMA_TCD_CSR_BWC_SHIFT) /* Enable eDMA master high-priority elevation (HPE) mode */ +# define EDMA_TCD_CSR_BWC_4CYCLES (0x02 << EDMA_TCD_CSR_BWC_SHIFT) /* eDMA engine stalls for 4 cycles after each R/W */ +# define EDMA_TCD_CSR_BWC_8CYCLES (0x03 << EDMA_TCD_CSR_BWC_SHIFT) /* eDMA engine stalls for 8 cycles after each R/W */ + +/* TCDn Beginning Major Loop Count Register (TCDn_BITER) */ + +#define EDMA_TCD_BITER_SHIFT (0) /* Bits 0-14: Starting Major Iteration Count (BITER) */ +#define EDMA_TCD_BITER_MASK (0x7fff << EDMA_TCD_BITER_SHIFT) +#define EDMA_TCD_BITER_MASK_ELINK (0x01ff << EDMA_TCD_BITER_SHIFT) +#define EDMA_TCD_BITER_LINKCH_SHIFT (9) /* Bits 9-13: Link Channel Number (LINKCH) */ +#define EDMA_TCD_BITER_LINKCH_MASK (0x1f << EDMA_TCD_BITER_LINKCH_SHIFT) +#define EDMA_TCD_BITER_LINKCH(n) (((n) << EDMA_TCD_BITER_LINKCH_SHIFT) & EDMA_TCD_BITER_LINKCH_MASK) +#define EDMA_TCD_BITER_ELINK (1 << 15) /* Bit 15: Enable Link (ELINK) */ + +/* Amount of channels */ + +#define DMA3_CHANNEL_COUNT (31) +#define DMA4_CHANNEL_COUNT (64) +#define IMX9_EDMA_NCHANNELS (DMA3_CHANNEL_COUNT + DMA4_CHANNEL_COUNT) + +/* Amount of interrupt sources */ + +#define DMA3_IRQ_COUNT (32) /* Error interrupt not counted */ +#define DMA4_IRQ_COUNT (32) /* Error interrupt not counted */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* In-memory representation of the 32-byte Transfer Control Descriptor + * (TCD) + */ + +struct imx9_edmatcd_s +{ + uint32_t saddr; /* Offset: 0x0000 TCD Source Address */ + uint16_t soff; /* Offset: 0x0004 TCD Signed Source Address Offset */ + uint16_t attr; /* Offset: 0x0006 TCD Transfer Attributes */ + uint32_t nbytes; /* Offset: 0x0008 TCD Signed Minor Loop Offset / Byte Count */ + uint32_t slast; /* Offset: 0x000c TCD Last Source Address Adjustment */ + uint32_t daddr; /* Offset: 0x0010 TCD Destination Address */ + uint16_t doff; /* Offset: 0x0014 TCD Signed Destination Address Offset */ + uint16_t citer; /* Offset: 0x0016 TCD Current Minor Loop Link, Major Loop Count */ + uint32_t dlastsga; /* Offset: 0x0018 TCD Last Destination Address Adjustment/Scatter Gather Address */ + uint16_t csr; /* Offset: 0x001c TCD Control and Status */ + uint16_t biter; /* Offset: 0x001e TCD Beginning Minor Loop Link, Major Loop Count */ +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_edma_tcdhasmux + * + * Description: + * Check if DMA TCD has TCD.MUX register. + * + * Input Parameters: + * dmabase - The eDMA base. + * + * Returned Value: + * true if TCD.MUX exists; false if not. + * + ****************************************************************************/ + +static inline bool imx9_edma_tcdhasmux(uintptr_t dmabase) +{ + /* Only eDMA4 has TCD.MUX register */ + + return dmabase == IMX9_DMA4_BASE ? true : false; +} + +/**************************************************************************** + * Name: imx9_edma_choffset + * + * Description: + * Channel offset in global channel list for dma base. + * + * Input Parameters: + * base - The eDMA base. + * + * Returned Value: + * Channel offset. + * + ****************************************************************************/ + +static inline uint32_t imx9_edma_choffset(uintptr_t base) +{ + return base == IMX9_DMA3_BASE ? 0 : DMA3_CHANNEL_COUNT; +} + +/**************************************************************************** + * Name: imx9_edma_chmax + * + * Description: + * Max channel in global channel list for dma base. + * + * Input Parameters: + * base - The eDMA base. + * + * Returned Value: + * Channel max. + * + ****************************************************************************/ + +static inline uint32_t imx9_edma_chmax(uintptr_t base) +{ + return base == IMX9_DMA3_BASE ? DMA3_CHANNEL_COUNT : IMX9_EDMA_NCHANNELS; +} + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX93_IMX93_EDMA_H */ diff --git a/arch/arm64/src/imx9/hardware/imx9_dmamux.h b/arch/arm64/src/imx9/hardware/imx9_dmamux.h new file mode 100644 index 0000000000..c9a7e6bbc9 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_dmamux.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_dmamux.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_DMAMUX_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_DMAMUX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_ARCH_CHIP_IMX93) +# include "hardware/imx93/imx93_dmamux.h" +#else +# error Unrecognized i.MX9 architecture +#endif + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_DMAMUX_H */ diff --git a/arch/arm64/src/imx9/hardware/imx9_edma.h b/arch/arm64/src/imx9/hardware/imx9_edma.h new file mode 100644 index 0000000000..d08610e7b5 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_edma.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_edma.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_EDMA_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_EDMA_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_ARCH_CHIP_IMX93) +# include "hardware/imx93/imx93_edma.h" +#else +# error Unrecognized i.MX9 architecture +#endif + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_EDMA_H */ diff --git a/arch/arm64/src/imx9/imx9_edma.c b/arch/arm64/src/imx9/imx9_edma.c new file mode 100644 index 0000000000..67a2cd3ef3 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_edma.c @@ -0,0 +1,1509 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_edma.c + * + * Copyright (C) 2019, 2021, 2023 Gregory Nutt. All rights reserved. + * Copyright 2022 NXP + * Authors: Gregory Nutt + * David Sidrane + * Peter van der Perk + * + * This file was leveraged from the NuttX S32K3 port. Portions of that eDMA + * logic derived from NXP sample code which has a compatible BSD 3-clause + * license: + * + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "arm64_internal.h" +#include "sched/sched.h" + +#include "chip.h" +#include "imx9_edma.h" +#include "imx9_ccm.h" + +#include "hardware/imx9_ccm.h" +#include "hardware/imx9_edma.h" +#include "hardware/imx9_dmamux.h" + +#ifdef CONFIG_IMX9_EDMA + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* TCD Alignment. + * + * eDMA TCDs must be aligned with the D-Cache line boundaries to facilitate + * cache operations on the TCDs when the D-Cache is enabled. + * + * NOTE: The TCDs are 32-bytes in length. We implicitly assume that the + * D-Cache line size is also 32-bits. Otherwise, padding would be required + * at the ends of the TCDS and buffers to protect data after the end of from + * invalidation. + */ + +#define EDMA_ALIGN ARMV8A_DCACHE_LINESIZE +#define EDMA_ALIGN_MASK (EDMA_ALIGN - 1) +#define EDMA_ALIGN_UP(n) (((n) + EDMA_ALIGN_MASK) & ~EDMA_ALIGN_MASK) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* State of a DMA channel */ + +enum imx9_dmastate_e +{ + IMX9_DMA_IDLE = 0, /* No DMA in progress */ + IMX9_DMA_CONFIGURED, /* DMA configured, but not yet started */ + IMX9_DMA_ACTIVE /* DMA has been started and is in progress */ +}; + +/* This structure describes one DMA channel */ + +struct imx9_dmach_s +{ + uintptr_t base; /* DMA engine base address */ + uint32_t flags; /* DMA channel flags */ + bool inuse; /* true: The DMA channel is in use */ + uint8_t dmamux; /* DMAMUX channel number */ + uint8_t chan; /* DMA channel number (either eDMA3 or eDMA4) */ + uint8_t state; /* Channel state. See enum imx9_dmastate_e */ + edma_callback_t callback; /* Callback invoked when the DMA completes */ + void *arg; /* Argument passed to callback function */ +#if CONFIG_IMX9_EDMA_NTCD > 0 + /* That TCD list is linked through the DLAST SGA field. The first transfer + * to be performed is at the head of the list. Subsequent TCDs are added + * at the tail of the list. + */ + + struct imx9_edmatcd_s *head; /* First TCD in the list */ + struct imx9_edmatcd_s *tail; /* Last TCD in the list */ +#endif +}; + +/* This structure describes the state of the eDMA controller */ + +struct imx9_edma_s +{ + /* These mutex protect the DMA channel and descriptor tables */ + + mutex_t chlock; /* Protects channel table */ +#if CONFIG_IMX9_EDMA_NTCD > 0 + sem_t dsem; /* Supports wait for free descriptors */ +#endif + + /* This array describes each DMA channel */ + + struct imx9_dmach_s dmach[IMX9_EDMA_NCHANNELS]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The state of the eDMA */ + +static struct imx9_edma_s g_edma = +{ + .chlock = NXMUTEX_INITIALIZER, +#if CONFIG_IMX9_EDMA_NTCD > 0 + .dsem = SEM_INITIALIZER(CONFIG_IMX9_EDMA_NTCD), +#endif +}; + +#if CONFIG_IMX9_EDMA_NTCD > 0 +/* This is a singly-linked list of free TCDs */ + +static sq_queue_t g_tcd_free; + +/* This is a pool of pre-allocated TCDs */ + +static struct imx9_edmatcd_s g_tcd_pool[CONFIG_IMX9_EDMA_NTCD] + aligned_data(EDMA_ALIGN); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_tcd_alloc + * + * Description: + * Allocate an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMX9_EDMA_NTCD > 0 +static struct imx9_edmatcd_s *imx9_tcd_alloc(void) +{ + struct imx9_edmatcd_s *tcd; + irqstate_t flags; + + /* Take the 'dsem'. When we hold the the 'dsem', then we know that one + * TCD is reserved for us in the free list. + * + * NOTE: We use a critical section here because we may block waiting for + * the 'dsem'. The critical section will be suspended while we are + * waiting. + */ + + flags = enter_critical_section(); + nxsem_wait_uninterruptible(&g_edma.dsem); + + /* Now there should be a TCD in the free list reserved just for us */ + + tcd = (struct imx9_edmatcd_s *)sq_remfirst(&g_tcd_free); + DEBUGASSERT(tcd != NULL); + + leave_critical_section(flags); + return tcd; +} +#endif + +/**************************************************************************** + * Name: imx9_tcd_free + * + * Description: + * Free an in-memory, TCD + * + ****************************************************************************/ + +#if CONFIG_IMX9_EDMA_NTCD > 0 +static void imx9_tcd_free(struct imx9_edmatcd_s *tcd) +{ + irqstate_t flags; + + /* Add the the TCD to the end of the free list and post the 'dsem', + * possibly waking up another thread that might be waiting for + * a TCD. + */ + + flags = spin_lock_irqsave(NULL); + sq_addlast((sq_entry_t *)tcd, &g_tcd_free); + nxsem_post(&g_edma.dsem); + spin_unlock_irqrestore(NULL, flags); +} +#endif + +/**************************************************************************** + * Name: imx9_tcd_initialize() + * + * Description: + * Initialize the TCD free list from the pool of pre-allocated TCDs. + * + * Assumptions: + * Called early in the initialization sequence so no special protection is + * necessary. + * + ****************************************************************************/ + +#if CONFIG_IMX9_EDMA_NTCD > 0 +static inline void imx9_tcd_initialize(void) +{ + sq_entry_t *tcd; + int i; + + /* Add each pre-allocated TCD to the tail of the TCD free list */ + + sq_init(&g_tcd_free); + for (i = 0; i < CONFIG_IMX9_EDMA_NTCD; i++) + { + tcd = (sq_entry_t *)&g_tcd_pool[i]; + sq_addlast(tcd, &g_tcd_free); + } +} +#endif + +/**************************************************************************** + * Name: imx9_tcd_chanlink + * + * Description: + * This function configures either a minor link or a major link. The minor + * link means the channel link is triggered every time CITER decreases by 1 + * The major link means that the channel link is triggered when the CITER + * is exhausted. + * + * NOTE: Users should ensure that DONE flag is cleared before calling this + * interface, or the configuration is invalid. + * + * Input Parameters: + * tcd - Point to the TCD structure. + * type - Channel link type. + * chan - The linked channel number. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_EDMA_ELINK +static inline void imx9_tcd_chanlink(uint8_t flags, + struct imx9_dmach_s *linkch, + struct imx9_edmatcd_s *tcd) +{ + uint16_t regval16; + + flags &= EDMA_CONFIG_LINKTYPE_MASK; + + if (linkch == NULL || flags == EDMA_CONFIG_LINKTYPE_LINKNONE) + { + /* No link or no link channel provided */ + + /* Disable minor links */ + + /* Disable major link */ + + tcd->csr &= ~EDMA_TCD_CSR_MAJORELINK; + } + else if (flags == EDMA_CONFIG_LINKTYPE_MINORLINK) /* Minor link config */ + { + /* Enable minor link */ + + tcd->citer |= EDMA_TCD_CITER_ELINK; + tcd->biter |= EDMA_TCD_BITER_ELINK; + + /* Set linked channel */ + + regval16 = tcd->citer; + regval16 &= ~EDMA_TCD_CITER_LINKCH_MASK; + regval16 |= EDMA_TCD_CITER_LINKCH(linkch->chan); + tcd->citer = regval16; + + regval16 = tcd->biter; + regval16 &= ~EDMA_TCD_BITER_LINKCH_MASK; + regval16 |= EDMA_TCD_BITER_LINKCH(linkch->chan); + tcd->biter = regval16; + } + else /* if (flags == EDMA_CONFIG_LINKTYPE_MAJORLINK) Major link config */ + { + /* Enable major link */ + + regval16 = tcd->csr; + regval16 |= EDMA_TCD_CSR_MAJORELINK; + tcd->csr = regval16; + + /* Set major linked channel */ + + regval16 &= ~EDMA_TCD_CSR_MAJORLINKCH_MASK; + regval16 |= EDMA_TCD_CSR_MAJORLINKCH(linkch->chan); + tcd->csr = regval16; + } +} +#endif + +/**************************************************************************** + * Name: imx9_tcd_configure + * + * Description: + * Configure all TCD registers to the specified values. 'tcd' is an + * 'overlay' that may refer either to either the TCD register set or to an + * in-memory TCD structure. + * + ****************************************************************************/ + +static inline void imx9_tcd_configure(struct imx9_edmatcd_s *tcd, + const struct imx9_edma_xfrconfig_s *config) +{ + tcd->saddr = config->saddr; + tcd->soff = config->soff; + tcd->attr = EDMA_TCD_ATTR_SSIZE(config->ssize) | /* Transfer Attributes */ + EDMA_TCD_ATTR_DSIZE(config->dsize); +#ifdef CONFIG_IMX9_EDMA_MOD + tcd->attr |= EDMA_TCD_ATTR_SMOD(config->smod) | /* Transfer Attributes */ + EDMA_TCD_ATTR_DMOD(config->dmod); +#endif + tcd->nbytes = config->nbytes; + tcd->slast = config->flags & EDMA_CONFIG_LOOPSRC ? + -(config->iter * config->nbytes) : 0; + + tcd->daddr = config->daddr; + tcd->doff = config->doff; + tcd->citer = config->iter & EDMA_TCD_CITER_MASK; + tcd->biter = config->iter & EDMA_TCD_BITER_MASK; + tcd->csr = config->flags & EDMA_CONFIG_LOOP_MASK ? + 0 : EDMA_TCD_CSR_DREQ; + tcd->csr |= config->flags & EDMA_CONFIG_INTHALF ? + EDMA_TCD_CSR_INTHALF : 0; + tcd->dlastsga = config->flags & EDMA_CONFIG_LOOPDEST ? + -(config->iter * config->nbytes) : 0; + + /* And special case flags */ + +#ifdef CONFIG_IMX9_EDMA_ELINK + /* Configure major/minor link mapping */ + + imx9_tcd_chanlink(config->flags, (struct imx9_dmach_s *)config->linkch, + tcd); +#endif +} + +/**************************************************************************** + * Name: imx9_tcd_instantiate + * + * Description: + * Copy an in-memory TCD into eDMA channel TCD registers + * + ****************************************************************************/ + +#if CONFIG_IMX9_EDMA_NTCD > 0 +static void imx9_tcd_instantiate(struct imx9_dmach_s *dmach, + const struct imx9_edmatcd_s *tcd) +{ + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + + /* Push tcd into hardware TCD register */ + + /* Clear DONE bit first, otherwise ESG cannot be set */ + + putreg16(0, base + IMX9_EDMA_TCD_CSR_OFFSET); + + putreg32(tcd->saddr, base + IMX9_EDMA_TCD_SADDR_OFFSET); + putreg16(tcd->soff, base + IMX9_EDMA_TCD_SOFF_OFFSET); + putreg16(tcd->attr, base + IMX9_EDMA_TCD_ATTR_OFFSET); + putreg32(tcd->nbytes, base + IMX9_EDMA_TCD_NBYTES_OFFSET); + putreg32(tcd->slast, base + IMX9_EDMA_TCD_SLAST_SDA_OFFSET); + putreg32(tcd->daddr, base + IMX9_EDMA_TCD_DADDR_OFFSET); + putreg16(tcd->doff, base + IMX9_EDMA_TCD_DOFF_OFFSET); + putreg16(tcd->citer, base + IMX9_EDMA_TCD_CITER_OFFSET); + putreg32(tcd->dlastsga, base + IMX9_EDMA_TCD_DLAST_SGA_OFFSET); + + putreg16(tcd->csr, base + IMX9_EDMA_TCD_CSR_OFFSET); + + putreg16(tcd->biter, base + IMX9_EDMA_TCD_BITER_OFFSET); +} +#endif + +/**************************************************************************** + * Name: imx9_dmaterminate + * + * Description: + * Terminate the DMA transfer and disable the DMA channel + * + ****************************************************************************/ + +static void imx9_dmaterminate(struct imx9_dmach_s *dmach, int result) +{ + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); +#if CONFIG_IMX9_EDMA_NTCD > 0 + struct imx9_edmatcd_s *tcd; + struct imx9_edmatcd_s *next; +#endif + edma_callback_t callback; + void *arg; + + /* Disable channel IRQ requests */ + + putreg32(EDMA_CH_INT, base + IMX9_EDMA_CH_INT_OFFSET); + + /* Clear CSR to disable channel. Because if the given channel started, + * transfer CSR will be not zero. Because if it is the last transfer, DREQ + * will be set. If not, ESG will be set. + */ + + putreg32(0, base + IMX9_EDMA_CH_CSR_OFFSET); + + putreg16(0, base + IMX9_EDMA_TCD_CSR_OFFSET); + + /* Cancel next TCD transfer. */ + + putreg32(0, base + IMX9_EDMA_TCD_DLAST_SGA_OFFSET); + +#if CONFIG_IMX9_EDMA_NTCD > 0 + /* Return all allocated TCDs to the free list */ + + for (tcd = dmach->head; tcd != NULL; tcd = next) + { + /* If channel looped to itself we are done + * if not continue to free tcds in chain + */ + + next = dmach->flags & EDMA_CONFIG_LOOPDEST ? + NULL : (struct imx9_edmatcd_s *)((uintptr_t)tcd->dlastsga); + + imx9_tcd_free(tcd); + } + + dmach->head = NULL; + dmach->tail = NULL; +#endif + + /* Perform the DMA complete callback */ + + callback = dmach->callback; + arg = dmach->arg; + + dmach->callback = NULL; + dmach->arg = NULL; + dmach->state = IMX9_DMA_IDLE; + + if (callback) + { + callback((DMACH_HANDLE)dmach, arg, true, result); + } +} + +/**************************************************************************** + * Name: imx9_edma_intstatus + * + * Description: + * DMA interrupt status per eDMA engine and channel. + * + ****************************************************************************/ + +static inline uint32_t imx9_edma_intstatus(uintptr_t base, uint8_t chan) +{ + /* The status register varies depending on eDMA instance and channel */ + +#ifdef IMX9_DMA3_BASE + /* eDMA3 uses the normal INT register */ + + if (base == IMX9_DMA3_BASE) + { + return getreg32(IMX9_EDMA_INT); + } +#endif + +#ifdef IMX9_DMA4_BASE + /* eDMA4 has two INT status registers, holding 32 statuses each */ + + if (chan > 31) + { + return getreg32(IMX9_EDMA_INT_HIGH); + } + + return getreg32(IMX9_EDMA_INT_LOW); +#endif +} + +/**************************************************************************** + * Name: imx9_edma_isr + * + * Description: + * DMA interrupt service routine. The vector handler calls this with the + * appropriate parameters. + * + ****************************************************************************/ + +static int imx9_edma_isr(int irq, void *context, void *arg) +{ + struct imx9_dmach_s *dmach; + uintptr_t base; + uint32_t regval32; + uint32_t errval32; + uint8_t chan; + int result; + + /* 'arg' should the DMA channel instance. */ + + dmach = (struct imx9_dmach_s *)arg; + DEBUGASSERT(dmach != NULL); + + chan = dmach->chan; + base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + + /* Get the eDMA Error Status register value. */ + + errval32 = getreg32(base + IMX9_EDMA_CH_ES_OFFSET); + + if (errval32 & EDMA_CH_ES_ERR) + { + DEBUGASSERT(dmach->state == IMX9_DMA_ACTIVE); + + /* Clear the error */ + + putreg32(EDMA_CH_ES_ERR, base + IMX9_EDMA_CH_ES_OFFSET); + + /* Clear the pending eDMA channel interrupt */ + + putreg32(EDMA_CH_INT, base + IMX9_EDMA_CH_INT_OFFSET); + + imx9_dmaterminate(dmach, -EIO); + return OK; + } + + /* Check for an eDMA pending interrupt on this channel */ + + regval32 = imx9_edma_intstatus(dmach->base, dmach->chan); + if ((regval32 & EDMA_INT(chan % 31)) != 0) + { + /* An interrupt is pending. + * This should only happen if the channel is active. + */ + + DEBUGASSERT(dmach->state == IMX9_DMA_ACTIVE); + + /* Clear the pending eDMA channel interrupt */ + + putreg32(EDMA_CH_INT, base + IMX9_EDMA_CH_INT_OFFSET); + + /* Get the eDMA TCD Control and Status register value. */ + + regval32 = getreg32(base + IMX9_EDMA_CH_CSR_OFFSET); + + /* Check if transfer has finished. */ + + if ((regval32 & EDMA_CH_CSR_DONE) != 0) + { + /* Clear the pending DONE interrupt status. */ + + regval32 |= EDMA_CH_CSR_DONE; + putreg32(regval32, base + IMX9_EDMA_CH_CSR_OFFSET); + result = OK; + } + else + { + /* Perform the half or end-of-major-cycle DMA callback */ + + if (dmach->callback != NULL) + { + dmach->callback((DMACH_HANDLE)dmach, dmach->arg, false, OK); + } + + return OK; + } + + /* Terminate the transfer when it is done. */ + + if ((dmach->flags & EDMA_CONFIG_LOOP_MASK) == 0) + { + imx9_dmaterminate(dmach, result); + } + else if (dmach->callback != NULL) + { + dmach->callback((DMACH_HANDLE)dmach, dmach->arg, true, result); + } + } + + return OK; +} + +/**************************************************************************** + * Name: imx9_edma_interrupt + * + * Description: + * DMA interrupt handler. This function clears the channel major + * interrupt flag and calls the callback function if it is not NULL. + * + * NOTE: For the case using TCD queue, when the major iteration count is + * exhausted, additional operations are performed. These include the + * final address adjustments and reloading of the BITER field into the + * CITER. Assertion of an optional interrupt request also occurs at this + * time, as does a possible fetch of a new TCD from memory using the + * scatter/gather address pointer included in the descriptor (if scatter/ + * gather is enabled). + * + ****************************************************************************/ + +static int imx9_edma_interrupt(int irq, void *context, void *arg) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)arg; + +#ifdef IMX9_DMA3_BASE + if ((irq >= IMX9_IRQ_DMA3_0) && (irq <= IMX9_IRQ_DMA3_30)) + { + /* eDMA3 interrupt has a single source */ + + imx9_edma_isr(irq, context, dmach); + } +#endif + +#ifdef IMX9_DMA4_BASE + if ((irq >= IMX9_IRQ_DMA4_0_1) && (irq <= IMX9_IRQ_DMA4_62_63)) + { + /* eDMA4 interrupt has two sources */ + + imx9_edma_isr(irq, context, dmach); + imx9_edma_isr(irq, context, dmach + 1); + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: imx9_edma_configure + * + * Description: + * Configure eDMA instance. + * + ****************************************************************************/ + +static void imx9_edma_configure(uintptr_t base) +{ + uint32_t regval; + + /* Configure the eDMA controllers */ + + regval = getreg32(IMX9_EDMA_CSR(base)); + regval &= ~(EDMA_CSR_EDBG | EDMA_CSR_ERCA | EDMA_CSR_HAE | EDMA_CSR_GCLC | + EDMA_CSR_GMRC); + +#ifdef CONFIG_IMX9_EDMA_EDBG + regval |= EDMA_CSR_EDBG; /* Enable Debug */ +#endif +#ifdef CONFIG_IMX9_EDMA_ERCA + regval |= EDMA_CSR_ERCA; /* Enable Round Robin Channel Arbitration */ +#endif +#ifdef CONFIG_IMX9_EDMA_ERGA + regval |= EDMA_CSR_ERGA; /* Enable Round Robin Group Arbitration */ +#endif +#ifdef CONFIG_IMX9_EDMA_HOE + regval |= EDMA_CSR_HAE; /* Halt On Error */ +#endif +#ifdef CONFIG_IMX9_EDMA_CLM + regval |= EDMA_CSR_GCLC; /* Continuous Link Mode / Global Channel Linking Control */ +#endif +#ifdef CONFIG_IMX9_EDMA_EMLIM + regval |= EDMA_CSR_GMRC; /* Enable Minor Loop Mapping / Global Master ID Replication Control */ +#endif + + putreg32(regval, IMX9_EDMA_CSR(base)); +} + +/**************************************************************************** + * Name: imx9_find_free_ch + * + * Description: + * Configure eDMA instance. + * + ****************************************************************************/ + +static struct imx9_dmach_s * imx9_find_free_ch(uint16_t dmamux) +{ + struct imx9_dmach_s *candidate; + uintptr_t base; + unsigned int chndx; + + /* eDMA base for MUX */ + + base = imx9_dmamux_get_dmabase(dmamux); + +#ifdef IMX9_DMA3_BASE + /* For eDMA3 the channel must match the MUX number */ + + if (base == IMX9_DMA3_BASE) + { + chndx = dmamux & EDMA_MUX_MASK; + candidate = &g_edma.dmach[chndx]; + if (!candidate->inuse) + { + return candidate; + } + } +#endif + +#ifdef IMX9_DMA4_BASE + /* For eDMA4 any free channel is good */ + + if (base == IMX9_DMA4_BASE) + { + unsigned int offset; + unsigned int max; + + /* Iterate relevant channel range from the global LUT */ + + offset = imx9_edma_choffset(base); + max = imx9_edma_chmax(base); + + for (chndx = offset; chndx < max; chndx++) + { + candidate = &g_edma.dmach[chndx]; + if (!candidate->inuse) + { + return candidate; + } + } + } +#endif + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_dma_initialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function arm64_dma_initialize(void) +{ + struct imx9_dmach_s *dmach; + uintptr_t base; + int chan; + int i; + + dmainfo("Initialize eDMA\n"); + + /* Enable root clock */ + + imx9_ccm_configure_root_clock(CCM_CR_WAKEUPAXI, SYS_PLL1PFD0, 4); + + /* Configure the instances */ + + dmach = &g_edma.dmach[0]; + +#ifdef IMX9_DMA3_BASE + /* Enable peripheral clock */ + + imx9_ccm_gate_on(CCM_LPCG_EDMA3, true); + + imx9_edma_configure(IMX9_DMA3_BASE); + + /* Initialize the channel */ + + for (i = 0; i < DMA3_CHANNEL_COUNT; i++, dmach++) + { + dmach->base = IMX9_DMA3_BASE; + dmach->chan = i; + + irq_attach(IMX9_IRQ_DMA3_0 + i, imx9_edma_interrupt, dmach); + } +#endif + +#ifdef IMX9_DMA4_BASE + /* Enable peripheral clock */ + + imx9_ccm_gate_on(CCM_LPCG_EDMA4, true); + + imx9_edma_configure(IMX9_DMA4_BASE); + + /* Initialize the channel */ + + for (i = 0; i < DMA4_CHANNEL_COUNT; i++, dmach++) + { + dmach->base = IMX9_DMA4_BASE; + dmach->chan = i; + + /* Attach interrupt for every second channel */ + + if ((i & 0x01) == 0) + { + irq_attach(IMX9_IRQ_DMA4_0_1 + (i >> 1), imx9_edma_interrupt, + dmach); + } + } +#endif + +#if CONFIG_IMX9_EDMA_NTCD > 0 + /* Initialize the list of free TCDs from the pool of pre-allocated TCDs. */ + + imx9_tcd_initialize(); +#endif + + /* Disable all DMA channel interrupts at the eDMA controller */ + + for (i = 0; i < IMX9_EDMA_NCHANNELS; i++) + { + /* DMA engine base and TCD channel */ + + base = g_edma.dmach[i].base; + chan = g_edma.dmach[i].chan; + + /* Disable all DMA channels and DMA channel interrupts */ + + putreg32(0, IMX9_EDMA_TCD(base, chan) + IMX9_EDMA_CH_CSR_OFFSET); + + /* Set all TCD CSR, biter and citer entries to 0 so that + * will be 0 when DONE is not set so that imx9_dmach_getcount + * reports 0. + */ + + putreg16(0, IMX9_EDMA_TCD(base, chan) + IMX9_EDMA_TCD_CSR_OFFSET); + putreg16(0, IMX9_EDMA_TCD(base, chan) + IMX9_EDMA_TCD_CITER_OFFSET); + putreg16(0, IMX9_EDMA_TCD(base, chan) + IMX9_EDMA_TCD_BITER_OFFSET); + } + +#ifdef IMX9_DMA3_BASE + /* Clear all pending DMA channel interrupts */ + + putreg32(0xffffffff, IMX9_EDMA_INT); + + /* Enable the channel interrupts at the NVIC (still disabled at the eDMA + * controller). + */ + + for (i = 0; i < DMA3_IRQ_COUNT; i++) + { + up_enable_irq(IMX9_IRQ_DMA3_0 + i); + } +#endif + +#ifdef IMX9_DMA4_BASE + /* Clear all pending DMA channel interrupts */ + + putreg32(0xffffffff, IMX9_EDMA_INT_LOW); + putreg32(0xffffffff, IMX9_EDMA_INT_HIGH); + + /* Enable the channel interrupts at the NVIC (still disabled at the eDMA + * controller). + */ + + for (i = 0; i < DMA4_IRQ_COUNT; i++) + { + up_enable_irq(IMX9_IRQ_DMA4_0_1 + i); + } +#endif +} + +/**************************************************************************** + * Name: imx9_dmach_alloc + * + * Allocate a DMA channel. This function sets aside a DMA channel, + * initializes the DMAMUX for the channel, then gives the caller exclusive + * access to the DMA channel. + * + * Input Parameters: + * + * dmamux - DMAMUX configuration see DMAMUX channel configuration register + * bit-field definitions in hardware/imx9_dmamux.h. + * Settings include: + * + * DMAMUX_CHCFG_SOURCE Chip-specific DMA source (required) + * DMAMUX_CHCFG_TRIG DMA Channel Trigger Enable (optional) + * DMAMUX_CHCFG_ENBL DMA Mux Channel Enable (required) + * + * A value of zero will disable the DMAMUX channel. + * dchpri - DCHPRI channel priority configuration. See DCHPRI channel + * configuration register bit-field definitions in + * hardware/imx9_edma.h. Meaningful settings include: + * + * EDMA_DCHPRI_CHPRI Channel Arbitration Priority + * DCHPRI_DPA Disable Preempt Ability + * DCHPRI_ECP Enable Channel Preemption + * + * The power-on default, 0x05, is a reasonable choice. + * + * Returned Value: + * If a DMA channel is available, this function returns a non-NULL, void* + * DMA channel handle. NULL is returned on any failure. + * + ****************************************************************************/ + +DMACH_HANDLE imx9_dmach_alloc(uint16_t dmamux, uint8_t dchpri) +{ + struct imx9_dmach_s *dmach; + uintptr_t base; + int ret; + + /* Search for an available DMA channel */ + + dmach = NULL; + ret = nxmutex_lock(&g_edma.chlock); + if (ret < 0) + { + return NULL; + } + + /* Find channel for this DMA MUX */ + + dmach = imx9_find_free_ch(dmamux); + if (dmach) + { + dmach->inuse = true; + dmach->state = IMX9_DMA_IDLE; + dmach->dmamux = dmamux & EDMA_MUX_MASK; + + /* TCD register base */ + + base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + + /* Clear any pending interrupts on the channel */ + + putreg32(0, base + IMX9_EDMA_CH_CSR_OFFSET); + + /* Make sure that the channel is disabled. */ + + putreg32(EDMA_CH_INT, base + IMX9_EDMA_CH_INT_OFFSET); + + /* Set the DMAMUX source */ + + if (imx9_edma_tcdhasmux(dmach->base)) + { + dmainfo("CH%d: MUX:%u->%p\n", dmach->chan, dmach->dmamux, + (void *)(base + IMX9_EDMA_CH_MUX_OFFSET)); + putreg8(dmach->dmamux, base + IMX9_EDMA_CH_MUX_OFFSET); + } + } + + nxmutex_unlock(&g_edma.chlock); + + /* Show the result of the allocation */ + + if (dmach != NULL) + { + dmainfo("CH%d: returning dmach: %p\n", dmach->chan, dmach); + } + else + { + dmaerr("ERROR: Failed allocate eDMA channel\n"); + } + + return (DMACH_HANDLE)dmach; +} + +/**************************************************************************** + * Name: imx9_dmach_free + * + * Description: + * Release a DMA channel. NOTE: The 'handle' used in this argument must + * NEVER be used again until imx9_dmach_alloc() is called again to + * re-gain a valid handle. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void imx9_dmach_free(DMACH_HANDLE handle) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + + dmainfo("dmach: %p\n", dmach); + DEBUGASSERT(dmach != NULL && dmach->inuse && + dmach->state != IMX9_DMA_ACTIVE); + + /* Mark the channel no longer in use. Clearing the inuse flag is an atomic + * operation and so should be safe. + */ + + dmach->flags = 0; + dmach->inuse = false; /* No longer in use */ + dmach->state = IMX9_DMA_IDLE; /* Better not be active! */ + + /* Make sure that the channel is disabled. */ + + putreg32(EDMA_CH_INT, base + IMX9_EDMA_CH_INT_OFFSET); + + /* Disable the associated DMAMUX */ + + if (imx9_edma_tcdhasmux(dmach->base)) + { + putreg8(0, base + IMX9_EDMA_CH_MUX_OFFSET); + } +} + +/**************************************************************************** + * Name: imx9_dmach_xfrsetup + * + * Description: + * This function adds the eDMA transfer to the DMA sequence. The request + * is setup according to the content of the transfer configuration + * structure. For "normal" DMA, imx9_dmach_xfrsetup is called only once. + * Scatter/gather DMA is accomplished by calling this function repeatedly, + * once for each transfer in the sequence. Scatter/gather DMA processing + * is enabled automatically when the second transfer configuration is + * received. + * + * This function may be called multiple times to handle multiple, + * discontinuous transfers (scatter-gather) + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * config - A DMA transfer configuration instance, populated by the + * The content of 'config' describes the transfer + * + * Returned Value + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int imx9_dmach_xfrsetup(DMACH_HANDLE *handle, + const struct imx9_edma_xfrconfig_s *config) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); +#if CONFIG_IMX9_EDMA_NTCD > 0 + struct imx9_edmatcd_s *tcd; + struct imx9_edmatcd_s *prev; + uint16_t mask = config->flags & EDMA_CONFIG_INTMAJOR ? 0 : + EDMA_TCD_CSR_INTMAJOR; + uint16_t regval16; +#else + uint32_t regval32; +#endif + + DEBUGASSERT(dmach != NULL); + dmainfo("dmach%u: %p config: %p\n", dmach->chan, dmach, config); + + dmach->flags = config->flags; + +#if CONFIG_IMX9_EDMA_NTCD > 0 + /* Scatter/gather DMA is supported */ + + /* Allocate a TCD, waiting if necessary */ + + tcd = imx9_tcd_alloc(); + + /* Configure current TCD block transfer. */ + + imx9_tcd_configure(tcd, config); + + /* Enable the interrupt when the major iteration count completes for this + * TCD. For "normal" DMAs, this will correspond to the DMA DONE + * interrupt; for scatter gather DMAs, multiple interrupts will be + * generated with the final being the DONE interrupt. + */ + + tcd->csr |= EDMA_TCD_CSR_INTMAJOR; + + /* Is this the first descriptor in the list? */ + + if (dmach->head == NULL) + { + /* Yes.. add it to the list */ + + dmach->head = tcd; + dmach->tail = tcd; + + /* And instantiate the first TCD in the DMA channel TCD registers. */ + + imx9_tcd_instantiate(dmach, tcd); + } + else + { + /* Cannot mix transfer types */ + + if (dmach->flags & EDMA_CONFIG_LOOP_MASK) + { + imx9_tcd_free(tcd); + return -EINVAL; + } + + /* Chain from previous descriptor in the list. */ + + /* Enable scatter/gather feature in the previous TCD. */ + + prev = dmach->tail; + regval16 = prev->csr; + regval16 &= ~(EDMA_TCD_CSR_DREQ | mask); + regval16 |= EDMA_TCD_CSR_ESG; + prev->csr = regval16; + + prev->dlastsga = (uint32_t)((uintptr_t)tcd); + dmach->tail = tcd; + + /* Clean cache associated with the previous TCD memory */ + + up_clean_dcache((uintptr_t)prev, + (uintptr_t)prev + sizeof(struct imx9_edmatcd_s)); + + /* Check if the TCD block in the DMA channel registers is the same as + * the previous previous TCD. This can happen if the previous TCD was + * the first TCD and has already be loaded into the TCD registers. + */ + + if (dmach->head == prev) + { + /* Enable scatter/gather also in the TCD registers. */ + + regval16 = getreg16(base + IMX9_EDMA_TCD_CSR_OFFSET); + regval16 &= ~(EDMA_TCD_CSR_DREQ | mask); + regval16 |= EDMA_TCD_CSR_ESG; + putreg16(regval16, base + IMX9_EDMA_TCD_CSR_OFFSET); + + putreg32((uint32_t)((uintptr_t)tcd), + base + IMX9_EDMA_TCD_DLAST_SGA_OFFSET); + } + } + + /* Clean cache associated with the TCD memory */ + + up_clean_dcache((uintptr_t)tcd, + (uintptr_t)tcd + sizeof(struct imx9_edmatcd_s)); +#else + + /* Scatter/gather DMA is NOT supported */ + + /* Check if eDMA is busy: if the channel has started transfer, CSR will be + * non-zero. + */ + + regval32 = getreg32(base + IMX9_EDMA_CH_CSR_OFFSET); + + if (regval32 != 0 && (regval32 & EDMA_CH_CSR_DONE) == 0) + { + return -EBUSY; + } + + /* Configure channel TCD registers to the values specified in config. */ + + imx9_tcd_configure((struct imx9_edmatcd_s *) + (base + IMX9_EDMA_TCD_SADDR_OFFSET), config); + + /* Enable the DONE interrupt when the major iteration count completes. */ + + modifyreg16(base + IMX9_EDMA_TCD_CSR_OFFSET, 0, EDMA_TCD_CSR_INTMAJOR); +#endif + + dmach->state = IMX9_DMA_CONFIGURED; + return OK; +} + +/**************************************************************************** + * Name: imx9_dmach_start + * + * Description: + * Start the DMA transfer. This function should be called after the final + * call to imx9_dmach_xfrsetup() in order to avoid race conditions. + * + * At the conclusion of each major DMA loop, a callback to the user + * provided function is made: |For "normal" DMAs, this will correspond to + * the DMA DONE interrupt; for scatter gather DMAs, + * this will be generated with the final TCD. + * + * At the conclusion of the DMA, the DMA channel is reset, all TCDs are + * freed, and the callback function is called with the the success/fail + * result of the DMA. + * + * NOTE: On Rx DMAs (peripheral-to-memory or memory-to-memory), it is + * necessary to invalidate the destination memory. That is not done + * automatically by the DMA module. Invalidation of the destination memory + * regions is the responsibility of the caller. + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * callback - The callback to be invoked when the DMA is completes or is + * aborted. + * arg - An argument that accompanies the callback + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int imx9_dmach_start(DMACH_HANDLE handle, edma_callback_t callback, + void *arg) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + irqstate_t flags; + uint32_t regval; + uint8_t chan; + + DEBUGASSERT(dmach != NULL && dmach->state == IMX9_DMA_CONFIGURED); + chan = dmach->chan; + dmainfo("dmach%u: %p callback: %p arg: %p\n", chan, dmach, callback, arg); + + /* Save the callback info. This will be invoked when the DMA completes */ + + flags = spin_lock_irqsave(NULL); + dmach->callback = callback; + dmach->arg = arg; + +#if CONFIG_IMX9_EDMA_NTCD > 0 + /* Although it is not recommended, it might be possible to call this + * function multiple times while adding TCDs on the fly. + */ + + if (dmach->state != IMX9_DMA_ACTIVE) +#endif + { + dmach->state = IMX9_DMA_ACTIVE; + + regval = getreg32(base + IMX9_EDMA_CH_CSR_OFFSET); + regval |= EDMA_CH_CSR_ERQ | EDMA_CH_CSR_EEI; + putreg32(regval, base + IMX9_EDMA_CH_CSR_OFFSET); + } + + spin_unlock_irqrestore(NULL, flags); + return OK; +} + +/**************************************************************************** + * Name: imx9_dmach_stop + * + * Description: + * Cancel the DMA. After imx9_dmach_stop() is called, the DMA channel + * is reset, all TCDs are freed, and imx9_dmarx/txsetup() must be called + * before imx9_dmach_start() can be called again. + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void imx9_dmach_stop(DMACH_HANDLE handle) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + irqstate_t flags; + + dmainfo("dmach: %p\n", dmach); + DEBUGASSERT(dmach != NULL); + + flags = spin_lock_irqsave(NULL); + imx9_dmaterminate(dmach, -EINTR); + spin_unlock_irqrestore(NULL, flags); +} + +/**************************************************************************** + * Name: imx9_dmach_getcount + * + * Description: + * This function checks the TCD (Task Control Descriptor) status for a + * specified eDMA channel and returns the the number of major loop counts + * that have not finished. + * + * NOTES: + * 1. This function can only be used to get unfinished major loop count of + * transfer without the next TCD, or it might be inaccuracy. + * 2. The unfinished/remaining transfer bytes cannot be obtained directly + * from registers while the channel is running. + * + * Because to calculate the remaining bytes, the initial NBYTES configured + * in DMA_TCDn_NBYTES_MLNO register is needed while the eDMA IP does not + * support getting it while a channel is active. In another words, the + * NBYTES value reading is always the actual (decrementing) NBYTES value + * the dma_engine is working with while a channel is running. + * Consequently, to get the remaining transfer bytes, a software-saved + * initial value of NBYTES (for example copied before enabling the channel) + * is needed. The formula to calculate it is shown below: + * + * RemainingBytes = RemainingMajorLoopCount * + * NBYTES(initially configured) + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * + * Returned Value: + * Major loop count which has not been transferred yet for the current TCD. + * + ****************************************************************************/ + +unsigned int imx9_dmach_getcount(DMACH_HANDLE *handle) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + unsigned int remaining = 0; + uintptr_t regval32; + uint16_t regval16; + + DEBUGASSERT(dmach != NULL); + + /* If the DMA is done, then the remaining count is zero */ + + regval32 = getreg32(base + IMX9_EDMA_CH_CSR_OFFSET); + + if ((regval32 & EDMA_CH_CSR_DONE) == 0) + { + /* Calculate the unfinished bytes */ + + regval16 = getreg16(base + IMX9_EDMA_TCD_CITER_OFFSET); + + if ((regval16 & EDMA_TCD_CITER_ELINK) != 0) + { + remaining = (regval16 & EDMA_TCD_CITER_MASK_ELINK) >> + EDMA_TCD_CITER_SHIFT; + } + else + { + remaining = (regval16 & EDMA_TCD_CITER_MASK) >> + EDMA_TCD_CITER_SHIFT; + } + } + + return remaining; +} + +/**************************************************************************** + * Name: imx9_dmach_idle + * + * Description: + * This function checks if the dma is idle + * + * Returned Value: + * 0 - if idle + * !0 - not + * + ****************************************************************************/ + +unsigned int imx9_dmach_idle(DMACH_HANDLE handle) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + return dmach->state == IMX9_DMA_IDLE ? 0 : -1; +} + +/**************************************************************************** + * Name: imx9_dmasample + * + * Description: + * Sample DMA register contents + * + * Assumptions: + * - DMA handle allocated by imx9_dmach_alloc() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void imx9_dmasample(DMACH_HANDLE handle, struct imx9_dmaregs_s *regs) +{ + struct imx9_dmach_s *dmach = (struct imx9_dmach_s *)handle; + unsigned int chan; + irqstate_t flags; + uintptr_t base = IMX9_EDMA_TCD(dmach->base, dmach->chan); + + DEBUGASSERT(dmach != NULL && regs != NULL); + chan = dmach->chan; + regs->chan = chan; + + /* eDMA Global Registers */ + + flags = spin_lock_irqsave(NULL); + + /* REVISIT: eDMA4 does not show INT_HIGH / HRS_HIGH values correctly */ + + regs->cr = getreg32(IMX9_EDMA_CSR(base)); /* Control */ + regs->es = getreg32(IMX9_EDMA_ES(base)); /* Error Status */ + regs->req = getreg32(IMX9_EDMA_INT); /* Interrupt Request */ + regs->hrs = getreg32(IMX9_EDMA_HRS); /* Hardware Request Status */ + + /* eDMA TCD */ + + regs->saddr = getreg32(base + IMX9_EDMA_TCD_SADDR_OFFSET); + regs->soff = getreg16(base + IMX9_EDMA_TCD_SOFF_OFFSET); + regs->attr = getreg16(base + IMX9_EDMA_TCD_ATTR_OFFSET); + regs->nbml = getreg32(base + IMX9_EDMA_TCD_NBYTES_OFFSET); + regs->slast = getreg32(base + IMX9_EDMA_TCD_SLAST_SDA_OFFSET); + regs->daddr = getreg32(base + IMX9_EDMA_TCD_DADDR_OFFSET); + regs->doff = getreg16(base + IMX9_EDMA_TCD_DOFF_OFFSET); + regs->citer = getreg16(base + IMX9_EDMA_TCD_CITER_OFFSET); + regs->dlastsga = getreg32(base + IMX9_EDMA_TCD_DLAST_SGA_OFFSET); + regs->csr = getreg16(base + IMX9_EDMA_TCD_CSR_OFFSET); + regs->biter = getreg16(base + IMX9_EDMA_TCD_BITER_OFFSET); + + /* DMAMUX registers */ + + if (imx9_edma_tcdhasmux(dmach->base)) + { + regs->dmamux = getreg32(base + IMX9_EDMA_CH_MUX_OFFSET); + } + else + { + regs->dmamux = 0; + } + + spin_unlock_irqrestore(NULL, flags); +} +#endif /* CONFIG_DEBUG_DMA */ + +/**************************************************************************** + * Name: imx9_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by imx9_dmach_alloc() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void imx9_dmadump(const struct imx9_dmaregs_s *regs, const char *msg) +{ + unsigned int chan; + + DEBUGASSERT(regs != NULL && msg != NULL); + + chan = regs->chan; + DEBUGASSERT(chan < IMX9_EDMA_NCHANNELS); + + dmainfo("%s\n", msg); + dmainfo(" eDMA Global Registers:\n"); + dmainfo(" CR: %08x\n", (unsigned int)regs->cr); + dmainfo(" ES: %08x\n", (unsigned int)regs->es); + dmainfo(" INT: %08x\n", (unsigned int)regs->req); + dmainfo(" EARS: %08x\n", (unsigned int)regs->hrs); + + /* eDMA Channel registers */ + + dmainfo(" eDMA Channel %u Registers:\n", chan); + dmainfo(" DCHPRI: %02x\n", regs->dchpri); + + /* eDMA TCD */ + + dmainfo(" eDMA Channel %u TCD Registers:\n", chan); + dmainfo(" SADDR: %08x\n", (unsigned int)regs->saddr); + dmainfo(" SOFF: %04x\n", (unsigned int)regs->soff); + dmainfo(" ATTR: %04x\n", (unsigned int)regs->attr); + dmainfo(" NBML: %05x\n", (unsigned int)regs->nbml); + dmainfo(" SLAST: %05x\n", (unsigned int)regs->slast); + dmainfo(" DADDR: %05x\n", (unsigned int)regs->daddr); + dmainfo(" DOFF: %04x\n", (unsigned int)regs->doff); + dmainfo(" CITER: %04x\n", (unsigned int)regs->citer); + dmainfo(" DLASTSGA: %08x\n", (unsigned int)regs->dlastsga); + dmainfo(" CSR: %04x\n", (unsigned int)regs->csr); + dmainfo(" BITER: %04x\n", (unsigned int)regs->biter); + + /* DMAMUX registers */ + + dmainfo(" DMAMUX Channel %u Registers:\n", chan); + dmainfo(" DMAMUX: %08x\n", (unsigned int)regs->dmamux); +} +#endif /* CONFIG_DEBUG_DMA */ +#endif /* CONFIG_IMX9_EDMA */ diff --git a/arch/arm64/src/imx9/imx9_edma.h b/arch/arm64/src/imx9/imx9_edma.h new file mode 100644 index 0000000000..4f3a4ac73e --- /dev/null +++ b/arch/arm64/src/imx9/imx9_edma.h @@ -0,0 +1,482 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_edma.h + * + * Copyright (C) 2019, 2021, 2023 Gregory Nutt. All rights reserved. + * Copyright 2022 NXP + * Authors: Gregory Nutt + * David Sidrane + * Peter van der Perk + * + * This file was leveraged from the NuttX S32K1 port. Portions of that eDMA + * logic derived from NXP sample code which has a compatible BSD 3-clause + * license: + * + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM64_SRC_IMX9_IMX9_EDMA_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_EDMA_H + +/* General Usage: + * + * 1. Allocate a DMA channel + * + * DMACH_HANDLE handle; + * handle = edma_dmach_alloc(dmamux, dchpri); + * + * Where 'dmamux' is the channel DMAMUX configuration register setting and + * 'dchpri' is the channel DCHPRIO priority register setting. + * + * 2. Create the transfer configuration: + * + * struct imx9_edma_xfrconfig_s config; + * config.saddr = ..; + * config.daddr = ..; + * etc. + * + * 3. Setup the transfer in hardware: + * + * int ret; + * ret = imx9_dmach_xfrsetup(handle, &config); + * + * 4. If you are setting up a scatter gather DMA + * (with CONFIG_IMX9_EDMA_NTCD > 0), then repeat steps 2 and 3 for + * each segment of the transfer. + * + * 5. Start the DMA: + * + * ret = imx9_dmach_start(handle, my_callback_func, priv); + * + * Where my_callback_func() is called when the DMA completes or an error + * occurs. 'priv' represents some internal driver state that will be + * provided with the callback. + * + * 6. If you need to stop the DMA and free resources (such as if a timeout + * occurs), then: + * + * i mxrt_dmach_stop(handle); + * + * 7. The callback will be received when the DMA completes (or an error + * occurs). After that, you may free the DMA channel, or re-use it on + * subsequent DMAs. + * + * imx9_dmach_free(handle); + * + * Almost non-invasive debug instrumentation is available. You may call + * imx9_dmasample() to save the current state of the eDMA registers at + * any given point in time. At some later, postmortem analysis, you can + * dump the content of the buffered registers with imx9_dmadump(). + * imx9_dmasample() is also available for monitoring DMA progress. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration flags. + * + * REVISIT: Many missing options that should be represented as flags: + * 1. Bandwidth + * 2. Source/Destination modulo + */ + +#define EDMA_CONFIG_LINKTYPE_SHIFT (0) /* Bits 0-1: Link type */ +#define EDMA_CONFIG_LINKTYPE_MASK (3 << EDMA_CONFIG_LINKTYPE_SHIFT) +# define EDMA_CONFIG_LINKTYPE_LINKNONE (0 << EDMA_CONFIG_LINKTYPE_SHIFT) /* No channel link */ +# define EDMA_CONFIG_LINKTYPE_MINORLINK (1 << EDMA_CONFIG_LINKTYPE_SHIFT) /* Channel link after each minor loop */ +# define EDMA_CONFIG_LINKTYPE_MAJORLINK (2 << EDMA_CONFIG_LINKTYPE_SHIFT) /* Channel link when major loop count exhausted */ + +#define EDMA_CONFIG_LOOP_SHIFT (2) /* Bits 2: Loop type */ +#define EDMA_CONFIG_LOOP_MASK (3 << EDMA_CONFIG_LOOP_SHIFT) +# define EDMA_CONFIG_LOOPNONE (0 << EDMA_CONFIG_LOOP_SHIFT) /* No looping */ +# define EDMA_CONFIG_LOOPSRC (1 << EDMA_CONFIG_LOOP_SHIFT) /* Source looping */ +# define EDMA_CONFIG_LOOPDEST (2 << EDMA_CONFIG_LOOP_SHIFT) /* Dest looping */ + +#define EDMA_CONFIG_INTHALF (1 << 4) /* Bits 4: Int on HALF */ +#define EDMA_CONFIG_INTMAJOR (1 << 5) /* Bits 5: Int on all Major completion + * Default is only on last completion + * if using scatter gather + */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void *DMACH_HANDLE; +typedef void (*edma_callback_t)(DMACH_HANDLE handle, + void *arg, bool done, int result); + +/* eDMA transfer type */ + +enum imx9_edma_xfrtype_e +{ + EDMA_MEM2MEM = 0, /* Transfer from memory to memory */ + EDMA_PERIPH2MEM, /* Transfer from peripheral to memory */ + EDMA_MEM2PERIPH, /* Transfer from memory to peripheral */ +}; + +/* eDMA transfer sises */ + +enum imx9_edma_sizes_e +{ + EDMA_8BIT = 0, /* Transfer data size 8 */ + EDMA_16BIT = 1, /* Transfer data size 16 */ + EDMA_32BIT = 2, /* Transfer data size 32 */ + EDMA_64BIT = 3, /* Transfer data size 64 */ + EDMA_16BYTE = 4, /* Transfer data size 16-byte */ + EDMA_32BYTE = 5, /* Transfer data size 32-byte */ + EDMA_64BYTE = 6, /* Transfer data size 64-byte */ +}; + +/* This structure holds the source/destination transfer attribute + * configuration. + */ + +struct imx9_edma_xfrconfig_s +{ + uintptr_t saddr; /* Source data address. */ + uintptr_t daddr; /* Destination data address. */ + int16_t soff; /* Sign-extended offset for current source address. */ + int16_t doff; /* Sign-extended offset for current destination address. */ + uint16_t iter; /* Major loop iteration count. */ + uint8_t flags; /* See EDMA_CONFIG_* definitions */ + uint8_t ssize; /* Source data transfer size (see TCD_ATTR_SIZE_* definitions in rdware/. */ + uint8_t dsize; /* Destination data transfer size. */ +#ifdef CONFIG_IMX9_EDMA_EMLIM + uint16_t nbytes; /* Bytes to transfer in a minor loop */ +#else + uint32_t nbytes; /* Bytes to transfer in a minor loop */ +#endif +#ifdef CONFIG_IMX9_EDMA_MOD + uint8_t smod; + uint8_t dmod; +#endif +#ifdef CONFIG_IMX9_EDMA_BWC + uint8_t bwc; +#endif +#ifdef CONFIG_IMX9_EDMA_ELINK + DMACH_HANDLE linkch; /* Link channel (With EDMA_CONFIG_LINKTYPE_* flags) */ +#endif +}; + +/* The following is used for sampling DMA registers when CONFIG DEBUG_DMA + * is selected + */ + +#ifdef CONFIG_DEBUG_DMA +struct imx9_dmaregs_s +{ + uint8_t chan; /* Sampled channel */ + + /* eDMA Global Registers */ + + uint32_t cr; /* Control */ + uint32_t es; /* Error Status */ + uint32_t req; /* Interrupt Request */ + uint32_t hrs; /* Hardware Request Status */ + + /* eDMA Channel registers */ + + uint8_t dchpri; /* Channel priority */ + + /* eDMA TCD */ + + uint32_t saddr; /* TCD Source Address */ + uint16_t soff; /* TCD Signed Source Address Offset */ + uint16_t attr; /* TCD Transfer Attributes */ + uint32_t nbml; /* TCD Signed Minor Loop Offset / Byte Count */ + uint32_t slast; /* TCD Last Source Address Adjustment */ + uint32_t daddr; /* TCD Destination Address */ + uint16_t doff; /* TCD Signed Destination Address Offset */ + uint16_t citer; /* TCD Current Minor Loop Link, Major Loop Count */ + uint32_t dlastsga; /* TCD Last Destination Address Adjustment/Scatter Gather Address */ + uint16_t csr; /* TCD Control and Status */ + uint16_t biter; /* TCD Beginning Minor Loop Link, Major Loop Count */ + + /* DMAMUX registers */ + + uint32_t dmamux; /* Channel configuration */ +}; +#endif /* CONFIG_DEBUG_DMA */ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_dmach_alloc + * + * Allocate a DMA channel. This function sets aside a DMA channel, + * initializes the DMAMUX for the channel, then gives the caller exclusive + * access to the DMA channel. + * + * Input Parameters: + * dmamux - DMAMUX configuration see DMAMUX channel configuration register + * bit-field definitions in hardware/imx9_dmamux.h. + * Settings include: + * + * DMAMUX_CHCFG_SOURCE Chip-specific DMA source (required) + * DMAMUX_CHCFG_TRIG DMA Channel Trigger Enable (optional) + * DMAMUX_CHCFG_ENBL DMA Mux Channel Enable (required) + * + * A value of zero will disable the DMAMUX channel. + * dchpri - DCHPRI channel priority configuration. See DCHPRI channel + * configuration register bit-field definitions in + * hardware/imx9_edma.h. Meaningful settings include: + * + * EDMA_DCHPRI_CHPRI Channel Arbitration Priority + * DCHPRI_DPA Disable Preempt Ability + * DCHPRI_ECP Enable Channel Preemption + * + * The power-on default, 0x05, is a reasonable choice. + * + * Returned Value: + * If a DMA channel is available, this function returns a non-NULL, void* + * DMA channel handle. NULL is returned on any failure. + * + ****************************************************************************/ + +DMACH_HANDLE imx9_dmach_alloc(uint16_t dmamux, uint8_t dchpri); + +/**************************************************************************** + * Name: imx9_dmach_free + * + * Description: + * Release a DMA channel. + * NOTE: The 'handle' used in this argument must NEVER be used again + * until imx9_dmach_alloc() is called again to re-gain a valid handle. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void imx9_dmach_free(DMACH_HANDLE handle); + +/**************************************************************************** + * Name: imx9_dmach_xfrsetup + * + * Description: + * This function adds the eDMA transfer to the DMA sequence. The request + * is setup according to the content of the transfer configuration + * structure. For "normal" DMA, imx9_dmach_xfrsetup is called only + * once. + * Scatter/gather DMA is accomplished by calling this function repeatedly, + * once for each transfer in the sequence. Scatter/gather DMA processing + * is enabled automatically when the second transfer configuration is + * received. + * + * This function may be called multiple times to handle multiple, + * discontinuous transfers (scatter-gather) + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * config - A DMA transfer configuration instance, populated by the + * The content of 'config' describes the transfer + * + * Returned Value + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int imx9_dmach_xfrsetup(DMACH_HANDLE *handle, + const struct imx9_edma_xfrconfig_s *config); + +/**************************************************************************** + * Name: imx9_dmach_start + * + * Description: + * Start the DMA transfer by enabling the channel DMA request. + * This function should be called after the final call to + * imx9_dmasetup() in order to avoid race conditions. + * + * At the conclusion of each major DMA loop, a callback to the + * user-provided function is made: |For "normal" DMAs, this will + * correspond to the DMA DONE interrupt; for scatter gather DMAs, multiple + * interrupts will be generated with the final being the DONE interrupt. + * + * At the conclusion of the DMA, the DMA channel is reset, all TCDs are + * freed, and the callback function is called with the the success/fail + * result of the DMA. + * + * NOTE: + * On Rx DMAs (peripheral-to-memory or memory-to-memory), it is necessary + * to invalidate the destination memory. That is not done automatically + * by the DMA module. Invalidation of the destination memory regions is + * the responsibility of the caller. + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * callback - The callback to be invoked when the DMA is completes or is + * aborted. + * arg - An argument that accompanies the callback + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int imx9_dmach_start(DMACH_HANDLE handle, edma_callback_t callback, + void *arg); + +/**************************************************************************** + * Name: imx9_dmach_stop + * + * Description: + * Cancel the DMA. After imx9_dmach_stop() is called, the DMA channel + * is reset, all TCDs are freed, and imx9_dmarx/txsetup() must be called + * before imx9_dmach_start() can be called again + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void imx9_dmach_stop(DMACH_HANDLE handle); + +/**************************************************************************** + * Name: imx9_dmach_getcount + * + * Description: + * This function checks the TCD (Task Control Descriptor) status for a + * specified eDMA channel and returns the the number of major loop counts + * that have not finished. + * + * NOTES: + * 1. This function can only be used to get unfinished major loop count of + * transfer without the next TCD, or it might be inaccuracy. + * 2. The unfinished/remaining transfer bytes cannot be obtained directly + * from registers while the channel is running. + * + * Because to calculate the remaining bytes, the initial NBYTES configured + * in DMA_TCDn_NBYTES_MLNO register is needed while the eDMA IP does not + * support getting it while a channel is active. In another words, the + * NBYTES value reading is always the actual (decrementing) NBYTES value + * the dma_engine is working with while a channel is running. + * Consequently, to get the remaining transfer bytes, a software-saved + * initial value of NBYTES (for example copied before enabling the channel) + * is needed. The formula to calculate it is shown below: + * + * RemainingBytes = RemainingMajorLoopCount * NBYTES(initially configured) + * + * Input Parameters: + * handle - DMA channel handle created by imx9_dmach_alloc() + * + * Returned Value: + * Major loop count which has not been transferred yet for the current TCD. + * + ****************************************************************************/ + +unsigned int imx9_dmach_getcount(DMACH_HANDLE *handle); + +/**************************************************************************** + * Name: imx9_dmach_idle + * + * Description: + * This function checks if the dma is idle + * + * Returned Value: + * 0 - if idle + * !0 - not + * + ****************************************************************************/ + +unsigned int imx9_dmach_idle(DMACH_HANDLE handle); + +/**************************************************************************** + * Name: imx9_dmasample + * + * Description: + * Sample DMA register contents + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void imx9_dmasample(DMACH_HANDLE handle, struct imx9_dmaregs_s *regs); +#else +# define imx9_dmasample(handle,regs) +#endif + +/**************************************************************************** + * Name: imx9_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void imx9_dmadump(const struct imx9_dmaregs_s *regs, const char *msg); +#else +# define imx9_dmadump(handle,regs,msg) +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_EDMA_H */