You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2024/05/08 00:48:18 UTC
(nuttx) branch master updated (c9a2e4a6c5 -> 5b3ef20629)
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
from c9a2e4a6c5 risc-v/virt: enable gradeful shutdown in kernel build
new 602bd685ec arm64/imx9: Add uSDHC driver
new c11ffaa78f arm64/imx9: Add DMA preflight support for uSDHC
new 0d3a1bc617 arm64/imx9: Fix fat_dma_ macro issue.
new 5b3ef20629 arm64/imx9: Allocate 64B granules instead of 256B
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
arch/arm64/src/imx9/Kconfig | 93 ++
arch/arm64/src/imx9/Make.defs | 8 +-
arch/arm64/src/imx9/hardware/imx9_usdhc.h | 704 ++++++++++++
arch/arm64/src/imx9/imx9_dma_alloc.c | 18 +-
arch/arm64/src/imx9/imx9_dma_alloc.h | 5 -
.../imxrt_usdhc.c => arm64/src/imx9/imx9_usdhc.c} | 1215 ++++++++++----------
.../imxrt_usdhc.h => arm64/src/imx9/imx9_usdhc.h} | 18 +-
boards/arm64/imx9/imx93-evk/configs/nsh/defconfig | 12 +
boards/arm64/imx9/imx93-evk/include/board.h | 31 +
boards/arm64/imx9/imx93-evk/src/Makefile | 4 +
boards/arm64/imx9/imx93-evk/src/imx93-evk.h | 30 +
boards/arm64/imx9/imx93-evk/src/imx9_bringup.c | 9 +
.../imx9/imx93-evk/src/imx9_usdhc.c} | 27 +-
13 files changed, 1550 insertions(+), 624 deletions(-)
create mode 100644 arch/arm64/src/imx9/hardware/imx9_usdhc.h
copy arch/{arm/src/imxrt/imxrt_usdhc.c => arm64/src/imx9/imx9_usdhc.c} (71%)
copy arch/{arm/src/imxrt/imxrt_usdhc.h => arm64/src/imx9/imx9_usdhc.h} (85%)
copy boards/{risc-v/mpfs/common/src/mpfs_emmcsd.c => arm64/imx9/imx93-evk/src/imx9_usdhc.c} (82%)
(nuttx) 02/04: arm64/imx9: Add DMA preflight support for uSDHC
Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit c11ffaa78fc3051158bcf2e2e295ad4e2350b3b0
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Mon May 6 15:52:10 2024 +0300
arm64/imx9: Add DMA preflight support for uSDHC
Signed-off-by: Jani Paalijarvi <ja...@unikie.com>
---
arch/arm64/src/imx9/Kconfig | 1 +
arch/arm64/src/imx9/imx9_usdhc.c | 83 ++++++++++++++++++++++-
boards/arm64/imx9/imx93-evk/configs/nsh/defconfig | 1 +
3 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig
index c796899f21..0d5de6cee5 100644
--- a/arch/arm64/src/imx9/Kconfig
+++ b/arch/arm64/src/imx9/Kconfig
@@ -59,6 +59,7 @@ menu "USDHC"
config IMX9_USDHC
bool
default n
+ select ARCH_HAVE_SDIO_PREFLIGHT
config IMX9_USDHC1
bool "USDHC1"
diff --git a/arch/arm64/src/imx9/imx9_usdhc.c b/arch/arm64/src/imx9/imx9_usdhc.c
index 7bb5d7a3b9..29f0501c7b 100644
--- a/arch/arm64/src/imx9/imx9_usdhc.c
+++ b/arch/arm64/src/imx9/imx9_usdhc.c
@@ -378,6 +378,10 @@ static int imx9_registercallback(struct sdio_dev_s *dev,
/* DMA */
#ifdef CONFIG_IMX9_USDHC_DMA
+# if defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
+static int imx9_dmapreflight(struct sdio_dev_s *dev,
+ const uint8_t *buffer, size_t buflen);
+# endif
static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
uint8_t *buffer, size_t buflen);
static int imx9_dmasendsetup(struct sdio_dev_s *dev,
@@ -441,6 +445,9 @@ struct imx9_dev_s g_sdhcdev[IMX9_MAX_SDHC_DEV_SLOTS] =
.registercallback = imx9_registercallback,
#ifdef CONFIG_SDIO_DMA
#ifdef CONFIG_IMX9_USDHC_DMA
+# if defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
+ .dmapreflight = imx9_dmapreflight,
+# endif
.dmarecvsetup = imx9_dmarecvsetup,
.dmasendsetup = imx9_dmasendsetup,
#else
@@ -500,6 +507,9 @@ struct imx9_dev_s g_sdhcdev[IMX9_MAX_SDHC_DEV_SLOTS] =
.registercallback = imx9_registercallback,
#ifdef CONFIG_SDIO_DMA
#ifdef CONFIG_IMX9_USDHC_DMA
+# if defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
+ .dmapreflight = imx9_dmapreflight,
+# endif
.dmarecvsetup = imx9_dmarecvsetup,
.dmasendsetup = imx9_dmasendsetup,
#else
@@ -2949,6 +2959,60 @@ static int imx9_registercallback(struct sdio_dev_s *dev,
return OK;
}
+/****************************************************************************
+ * Name: imx9_dmapreflight
+ *
+ * Description:
+ * Preflight an SDIO DMA operation. If the buffer is not well-formed for
+ * SDIO DMA transfer (alignment, size, etc.) returns an error.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - The memory to DMA to/from
+ * buflen - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ ****************************************************************************/
+
+#if defined(CONFIG_IMX9_USDHC_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
+static int imx9_dmapreflight(struct sdio_dev_s *dev,
+ const uint8_t *buffer, size_t buflen)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ DEBUGASSERT(priv != NULL && buflen > 0);
+
+ /* DMA must be possible to the buffer and it must be word (4 bytes) aligned
+ */
+
+ if (buffer != priv->rxbuffer && ((uintptr_t)buffer & 3) != 0)
+ {
+ mcerr("non word aligned buffer:%p\n", buffer);
+ return -EFAULT;
+ }
+
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ /* buffer alignment is required for DMA transfers with dcache in buffered
+ * mode (not write-through) because a) arch_invalidate_dcache could lose
+ * buffered writes and b) arch_flush_dcache could corrupt adjacent memory
+ * if the maddr and the mend+1, the next next address are not on
+ * ARMV8A_DCACHE_LINESIZE boundaries.
+ */
+
+ if (buffer != priv->rxbuffer &&
+ (((uintptr_t)buffer & (ARMV8A_DCACHE_LINESIZE - 1)) != 0 ||
+ ((uintptr_t)(buffer + buflen) & (ARMV8A_DCACHE_LINESIZE - 1)) != 0))
+ {
+ mcerr("dcache unaligned buffer:%p end:%p\n",
+ buffer, buffer + buflen - 1);
+ return -EFAULT;
+ }
+#endif
+
+ return 0;
+}
+#endif
+
/****************************************************************************
* Name: imx9_dmarecvsetup
*
@@ -2974,7 +3038,20 @@ static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
{
struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
- DEBUGASSERT(((uint64_t) buffer & 3) == 0);
+
+#if defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
+ /* Normaly imx9_dmapreflight is called prior to imx9_dmarecvsetup
+ * except for the case where the CSR read is done at initalization
+ *
+ * With a total read size of less then priv->rxbuffer we can
+ * handle the unaligned case herein, using the rxbuffer.
+ *
+ * Any other case is a fault.
+ */
+
+ DEBUGASSERT(buflen <= sizeof(priv->rxbuffer) ||
+ imx9_dmapreflight(dev, buffer, buflen) == 0);
+#endif
/* Begin sampling register values */
@@ -2985,8 +3062,8 @@ static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
if (((uintptr_t)buffer & (ARMV8A_DCACHE_LINESIZE - 1)) != 0 ||
(buflen & (ARMV8A_DCACHE_LINESIZE - 1)) != 0)
{
- /* The read buffer is not cache-line aligned. Read to an internal
- * buffer instead.
+ /* The read buffer is not cache-line aligned, but will fit in
+ * the rxbuffer. So read to an internal buffer instead.
*/
up_invalidate_dcache((uintptr_t)priv->rxbuffer,
diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
index 531a8c8eb3..e2810cc581 100644
--- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
+++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
@@ -31,6 +31,7 @@ CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_TCPBLASTER=y
CONFIG_EXAMPLES_UDPBLASTER=y
CONFIG_EXPERIMENTAL=y
+CONFIG_FAT_DMAMEMORY=y
CONFIG_FS_FAT=y
CONFIG_FS_FATTIME=y
CONFIG_FS_PROCFS=y
(nuttx) 01/04: arm64/imx9: Add uSDHC driver
Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 602bd685ec7b41e6045c6d9171406361a4b7b90a
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Thu May 2 15:54:19 2024 +0300
arm64/imx9: Add uSDHC driver
Signed-off-by: Jani Paalijarvi <ja...@unikie.com>
---
arch/arm64/src/imx9/Kconfig | 92 +
arch/arm64/src/imx9/Make.defs | 8 +-
arch/arm64/src/imx9/hardware/imx9_usdhc.h | 704 ++++
arch/arm64/src/imx9/imx9_usdhc.c | 3353 ++++++++++++++++++++
.../arm64/src/imx9/imx9_usdhc.h | 103 +-
boards/arm64/imx9/imx93-evk/configs/nsh/defconfig | 11 +
boards/arm64/imx9/imx93-evk/include/board.h | 31 +
boards/arm64/imx9/imx93-evk/src/Makefile | 4 +
boards/arm64/imx9/imx93-evk/src/imx93-evk.h | 30 +
boards/arm64/imx9/imx93-evk/src/imx9_bringup.c | 9 +
.../imx93-evk/src/{imx9_bringup.c => imx9_usdhc.c} | 80 +-
11 files changed, 4307 insertions(+), 118 deletions(-)
diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig
index 6772b2c27b..c796899f21 100644
--- a/arch/arm64/src/imx9/Kconfig
+++ b/arch/arm64/src/imx9/Kconfig
@@ -54,6 +54,98 @@ config IMX9_EDMA
default n
select ARCH_DMA
+menu "USDHC"
+
+config IMX9_USDHC
+ bool
+ default n
+
+config IMX9_USDHC1
+ bool "USDHC1"
+ default n
+ select ARCH_HAVE_SDIO
+ select IMX9_USDHC
+ ---help---
+ Support USDHC host controller 1
+
+config IMX9_USDHC2
+ bool "USDHC2"
+ default n
+ select ARCH_HAVE_SDIO
+ select IMX9_USDHC
+ ---help---
+ Support USDHC host controller 2
+
+menu "USDHC Configuration"
+ depends on IMX9_USDHC
+
+config IMX9_USDHC_DMA
+ bool "Support DMA data transfers"
+ default y
+ select SDIO_DMA
+ ---help---
+ Support DMA data transfers.
+ Enable SD card DMA data transfers. This is marginally optional.
+ For most usages, SD accesses will cause data overruns if used without
+ DMA.
+
+choice
+ prompt "Bus width for USDHC1"
+ default IMX9_USDHC1_WIDTH_D1_ONLY
+ depends on IMX9_USDHC1
+
+config IMX9_USDHC1_WIDTH_D1_ONLY
+ bool "One bit"
+
+config IMX9_USDHC1_WIDTH_D1_D4
+ bool "Four bit"
+
+config IMX9_USDHC1_WIDTH_D1_D8
+ bool "Eight bit"
+
+endchoice
+
+config IMX9_USDHC1_INVERT_CD
+ bool "Invert the USDHC1 CD"
+ default n
+ depends on IMX9_USDHC1
+ ---help---
+ If the board defines PIN_USDHC1_CD the CD_B input to the USDHC it is
+ assumed to be active low. Selecting IMX9_USDHC1_INVERT_CD will make it
+ active high.
+
+ If the board defines PIN_USDHC1_CD_GPIO it is assumed to be active low.
+ Selecting IMX9_USDHC1_INVERT_CD will make it active high.
+
+choice
+ depends on IMX9_USDHC2
+ prompt "Bus width for USDHC2"
+ default IMX9_USDHC2_WIDTH_D1_D4
+
+config IMX9_USDHC2_WIDTH_D1_ONLY
+ bool "One bit"
+
+config IMX9_USDHC2_WIDTH_D1_D4
+ bool "Four bit"
+
+endchoice
+
+config IMX9_USDHC2_INVERT_CD
+ bool "Invert the USDHC2 CD"
+ default n
+ depends on IMX9_USDHC2
+ ---help---
+ If the board defines PIN_USDHC2_CD the CD_B input to the USDHC it is
+ assumed to be active low. Selecting IMX9_USDHC_INVERT_CD will make it
+ active high.
+
+ If the board defines PIN_USDHC2_CD_GPIO it is assumed to be active low.
+ Selecting IMX9_USDHC2_INVERT_CD will make it active high.
+
+endmenu # USDHC Configuration
+
+endmenu # USDHC
+
menu "LPUART"
config IMX9_LPUART
diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs
index 1ededeea50..38466ba370 100644
--- a/arch/arm64/src/imx9/Make.defs
+++ b/arch/arm64/src/imx9/Make.defs
@@ -48,7 +48,7 @@ ifeq ($(CONFIG_IMX9_LPI2C),y)
CHIP_CSRCS += imx9_lpi2c.c
endif
-ifeq ($(CONFIG_IMX9_LPSPI), y)
+ifeq ($(CONFIG_IMX9_LPSPI),y)
CHIP_CSRCS += imx9_lpspi.c
endif
@@ -61,5 +61,9 @@ ifeq ($(CONFIG_IMX9_DMA_ALLOC),y)
endif
ifeq ($(CONFIG_IMX9_ENET),y)
-CHIP_CSRCS += imx9_enet.c
+ CHIP_CSRCS += imx9_enet.c
+endif
+
+ifeq ($(CONFIG_IMX9_USDHC),y)
+ CHIP_CSRCS += imx9_usdhc.c
endif
diff --git a/arch/arm64/src/imx9/hardware/imx9_usdhc.h b/arch/arm64/src/imx9/hardware/imx9_usdhc.h
new file mode 100644
index 0000000000..0ef608feb9
--- /dev/null
+++ b/arch/arm64/src/imx9/hardware/imx9_usdhc.h
@@ -0,0 +1,704 @@
+/****************************************************************************
+ * arch/arm64/src/imx9/hardware/imx9_usdhc.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_USDHC_H
+#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_USDHC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define IMX9_USDHC_DSADDR_OFFSET 0x0000 /* DMA System Address Register */
+#define IMX9_USDHC_BLKATTR_OFFSET 0x0004 /* Block Attributes Register */
+#define IMX9_USDHC_CMDARG_OFFSET 0x0008 /* Command Argument Register */
+#define IMX9_USDHC_XFERTYP_OFFSET 0x000c /* Transfer Type Register */
+#define IMX9_USDHC_CMDRSP0_OFFSET 0x0010 /* Command Response 0 */
+#define IMX9_USDHC_CMDRSP1_OFFSET 0x0014 /* Command Response 1 */
+#define IMX9_USDHC_CMDRSP2_OFFSET 0x0018 /* Command Response 2 */
+#define IMX9_USDHC_CMDRSP3_OFFSET 0x001c /* Command Response 3 */
+#define IMX9_USDHC_DATAPORT_OFFSET 0x0020 /* Buffer Data Port Register */
+#define IMX9_USDHC_PRSSTAT_OFFSET 0x0024 /* Present State Register */
+#define IMX9_USDHC_PROCTL_OFFSET 0x0028 /* Protocol Control Register */
+#define IMX9_USDHC_SYSCTL_OFFSET 0x002c /* System Control Register */
+#define IMX9_USDHC_IRQSTAT_OFFSET 0x0030 /* Interrupt Status Register */
+#define IMX9_USDHC_IRQSTATEN_OFFSET 0x0034 /* Interrupt Status Enable Register */
+#define IMX9_USDHC_IRQSIGEN_OFFSET 0x0038 /* Interrupt Signal Enable Register */
+#define IMX9_USDHC_AC12ERR_OFFSET 0x003c /* Auto CMD12 Error Status Register */
+#define IMX9_USDHC_HTCAPBLT_OFFSET 0x0040 /* Host Controller Capabilities */
+#define IMX9_USDHC_WML_OFFSET 0x0044 /* Watermark Level Register */
+#define IMX9_USDHC_MIX_OFFSET 0x0048 /* Mixer Control Register */
+#define IMX9_USDHC_FEVT_OFFSET 0x0050 /* Force Event Register */
+#define IMX9_USDHC_ADMAES_OFFSET 0x0054 /* ADMA Error Status Register */
+#define IMX9_USDHC_ADSADDR_OFFSET 0x0058 /* ADMA System Address Register */
+#define IMX9_USDHC_DLL_CONTROL_OFFSET 0x0060 /* DLL Control Register */
+#define IMX9_USDHC_DLL_STATUS_OFFSET 0x0064 /* DLL Status Register */
+#define IMX9_USDHC_CLK_TUNE_CTRL_OFFSET 0x0068 /* Clock turing control Register */
+#define IMX9_USDHC_VENDOR_OFFSET 0x00c0 /* Vendor Specific Register */
+#define IMX9_USDHC_MMCBOOT_OFFSET 0x00c4 /* MMC Boot Register */
+#define IMX9_USDHC_VENDOR2_OFFSET 0x00c8 /* Vendor 2 Register */
+#define IMX9_USDHC_TC_OFFSET 0x00cc /* Tuning Control Register */
+#define IMX9_USDHC_CQVER_OFFSET 0x0100 /* Command Queuing Version */
+#define IMX9_USDHC_CQCAP_OFFSET 0x0104 /* Command Queuing Capabilities */
+#define IMX9_USDHC_CQCFG_OFFSET 0x0108 /* Command Queuing Configuration */
+#define IMX9_USDHC_CQCTL_OFFSET 0x010c /* Command Queuing Control */
+#define IMX9_USDHC_CQIS_OFFSET 0x0110 /* Command Queuing Interrupt Status */
+#define IMX9_USDHC_CQISTE_OFFSET 0x0114 /* Command Queuing Interrupt Status Enable */
+#define IMX9_USDHC_CQISGE_OFFSET 0x0118 /* Command Queuing Interrupt Signal Enable */
+#define IMX9_USDHC_CQIC_OFFSET 0x011c /* Command Queuing Interrupt Coalescing */
+#define IMX9_USDHC_CQTDLBA_OFFSET 0x0120 /* Command Queuing Task Descriptor List Base Address */
+#define IMX9_USDHC_CQTDLBAU_OFFSET 0x0124 /* Command Queuing Task Descriptor List Base Address Upper 32 Bits */
+#define IMX9_USDHC_CQTDBR_OFFSET 0x0128 /* Command Queuing Task Doorbell */
+#define IMX9_USDHC_CQTCN_OFFSET 0x012c /* Command Queuing Task Completion Notification */
+#define IMX9_USDHC_CQDQS_OFFSET 0x0130 /* Command Queuing Device Queue Status */
+#define IMX9_USDHC_CQDPT_OFFSET 0x0134 /* Command Queuing Device Pending Tasks */
+#define IMX9_USDHC_CQTCLR_OFFSET 0x0138 /* Command Queuing Task Clear */
+#define IMX9_USDHC_CQSSC1_OFFSET 0x0140 /* Command Queuing Send Status Configuration 1 */
+#define IMX9_USDHC_CQSSC2_OFFSET 0x0144 /* Command Queuing Send Status Configuration 2 */
+#define IMX9_USDHC_CQCRDCT_OFFSET 0x0148 /* Command Queuing Command Response for Direct-Command Task */
+#define IMX9_USDHC_CQRMEM_OFFSET 0x0150 /* Command Queuing Response Mode Error Mask */
+#define IMX9_USDHC_CQTERRI_OFFSET 0x0154 /* Command Queuing Task Error Information */
+#define IMX9_USDHC_CQCRI_OFFSET 0x0158 /* Command Queuing Command Response Index */
+#define IMX9_USDHC_CQCRA_OFFSET 0x015c /* Command Queuing Command Response Argument */
+
+/* Register Addresses *******************************************************/
+
+/* For USDHC1 ... */
+
+#define IMX9_USDHC1_DSADDR (IMX9_USDHC1_BASE + IMX9_USDHC_DSADDR_OFFSET)
+#define IMX9_USDHC1_BLKATTR (IMX9_USDHC1_BASE + IMX9_USDHC_BLKATTR_OFFSET)
+#define IMX9_USDHC1_CMDARG (IMX9_USDHC1_BASE + IMX9_USDHC_CMDARG_OFFSET)
+#define IMX9_USDHC1_XFERTYP (IMX9_USDHC1_BASE + IMX9_USDHC_XFERTYP_OFFSET)
+#define IMX9_USDHC1_CMDRSP0 (IMX9_USDHC1_BASE + IMX9_USDHC_CMDRSP0_OFFSET)
+#define IMX9_USDHC1_CMDRSP1 (IMX9_USDHC1_BASE + IMX9_USDHC_CMDRSP1_OFFSET)
+#define IMX9_USDHC1_CMDRSP2 (IMX9_USDHC1_BASE + IMX9_USDHC_CMDRSP2_OFFSET)
+#define IMX9_USDHC1_CMDRSP3 (IMX9_USDHC1_BASE + IMX9_USDHC_CMDRSP3_OFFSET)
+#define IMX9_USDHC1_DATAPORT (IMX9_USDHC1_BASE + IMX9_USDHC_DATAPORT_OFFSET)
+#define IMX9_USDHC1_PRSSTAT (IMX9_USDHC1_BASE + IMX9_USDHC_PRSSTAT_OFFSET)
+#define IMX9_USDHC1_PROCTL (IMX9_USDHC1_BASE + IMX9_USDHC_PROCTL_OFFSET)
+#define IMX9_USDHC1_SYSCTL (IMX9_USDHC1_BASE + IMX9_USDHC_SYSCTL_OFFSET)
+#define IMX9_USDHC1_IRQSTAT (IMX9_USDHC1_BASE + IMX9_USDHC_IRQSTAT_OFFSET)
+#define IMX9_USDHC1_IRQSTATEN (IMX9_USDHC1_BASE + IMX9_USDHC_IRQSTATEN_OFFSET)
+#define IMX9_USDHC1_IRQSIGEN (IMX9_USDHC1_BASE + IMX9_USDHC_IRQSIGEN_OFFSET)
+#define IMX9_USDHC1_AC12ERR (IMX9_USDHC1_BASE + IMX9_USDHC_AC12ERR_OFFSET)
+#define IMX9_USDHC1_HTCAPBLT (IMX9_USDHC1_BASE + IMX9_USDHC_HTCAPBLT_OFFSET)
+#define IMX9_USDHC1_WML (IMX9_USDHC1_BASE + IMX9_USDHC_WML_OFFSET)
+#define IMX9_USDHC1_MIX (IMX9_USDHC1_BASE + IMX9_USDHC_MIX_OFFSET)
+#define IMX9_USDHC1_FEVT (IMX9_USDHC1_BASE + IMX9_USDHC_FEVT_OFFSET)
+#define IMX9_USDHC1_ADMAES (IMX9_USDHC1_BASE + IMX9_USDHC_ADMAES_OFFSET)
+#define IMX9_USDHC1_ADSADDR (IMX9_USDHC1_BASE + IMX9_USDHC_ADSADDR_OFFSET)
+#define IMX9_USDHC1_DLL_CONTROL (IMX9_USDHC1_BASE + IMX9_USDHC_DLL_CONTROL_OFFSET)
+#define IMX9_USDHC1_DLL_STATUS (IMX9_USDHC1_BASE + IMX9_USDHC_DLL_STATUS)
+#define IMX9_USDHC1_CLK_TUNE_CTRL (IMX9_USDHC1_BASE + IMX9_USDHC_CLK_TUNE_CTRL)
+#define IMX9_USDHC1_VENDOR (IMX9_USDHC1_BASE + IMX9_USDHC_VENDOR_OFFSET)
+#define IMX9_USDHC1_MMCBOOT (IMX9_USDHC1_BASE + IMX9_USDHC_MMCBOOT_OFFSET)
+#define IMX9_USDHC1_VENDOR2 (IMX9_USDHC1_BASE + IMX9_USDHC_VENDOR2_OFFSET)
+#define IMX9_USDHC1_TC (IMX9_USDHC1_BASE + IMX9_USDHC_TC_OFFSET)
+#define IMX9_USDHC1_CQVER (IMX9_USDHC1_BASE + IMX9_USDHC_CQVER_OFFSET)
+#define IMX9_USDHC1_CQCAP (IMX9_USDHC1_BASE + IMX9_USDHC_CQCAP_OFFSET)
+#define IMX9_USDHC1_CQCFG (IMX9_USDHC1_BASE + IMX9_USDHC_CQCFG_OFFSET)
+#define IMX9_USDHC1_CQCTL (IMX9_USDHC1_BASE + IMX9_USDHC_CQCTL_OFFSET)
+#define IMX9_USDHC1_CQIS (IMX9_USDHC1_BASE + IMX9_USDHC_CQIS_OFFSET)
+#define IMX9_USDHC1_CQISTE (IMX9_USDHC1_BASE + IMX9_USDHC_CQISTE_OFFSET)
+#define IMX9_USDHC1_CQISGE (IMX9_USDHC1_BASE + IMX9_USDHC_CQISGE_OFFSET)
+#define IMX9_USDHC1_CQIC (IMX9_USDHC1_BASE + IMX9_USDHC_CQIC_OFFSET)
+#define IMX9_USDHC1_CQTDLBA (IMX9_USDHC1_BASE + IMX9_USDHC_CQTDLBA_OFFSET)
+#define IMX9_USDHC1_CQTDLBAU (IMX9_USDHC1_BASE + IMX9_USDHC_CQTDLBAU_OFFSET)
+#define IMX9_USDHC1_CQTDBR (IMX9_USDHC1_BASE + IMX9_USDHC_CQTDBR_OFFSET)
+#define IMX9_USDHC1_CQTCN (IMX9_USDHC1_BASE + IMX9_USDHC_CQTCN_OFFSET)
+#define IMX9_USDHC1_CQDQS (IMX9_USDHC1_BASE + IMX9_USDHC_CQDQS_OFFSET)
+#define IMX9_USDHC1_CQDPT (IMX9_USDHC1_BASE + IMX9_USDHC_CQDPT_OFFSET)
+#define IMX9_USDHC1_CQTCL (IMX9_USDHC1_BASE + IMX9_USDHC_CQTCLR_OFFSET)
+#define IMX9_USDHC1_CQSSC1 (IMX9_USDHC1_BASE + IMX9_USDHC_CQSSC1_OFFSET)
+#define IMX9_USDHC1_CQSSC2 (IMX9_USDHC1_BASE + IMX9_USDHC_CQSSC2_OFFSET)
+#define IMX9_USDHC1_CQCRDCT (IMX9_USDHC1_BASE + IMX9_USDHC_CQCRDCT_OFFSET)
+#define IMX9_USDHC1_CQRMEM (IMX9_USDHC1_BASE + IMX9_USDHC_CQRMEM_OFFSET)
+#define IMX9_USDHC1_CQTERRI (IMX9_USDHC1_BASE + IMX9_USDHC_CQTERRI_OFFSET)
+#define IMX9_USDHC1_CQCRI (IMX9_USDHC1_BASE + IMX9_USDHC_CQCRI_OFFSET)
+#define IMX9_USDHC1_CQCRA (IMX9_USDHC1_BASE + IMX9_USDHC_CQCRA_OFFSET)
+
+/* For USDHC2 ... */
+
+#define IMX9_USDHC2_DSADDR (IMX9_USDHC2_BASE + IMX9_USDHC_DSADDR_OFFSET)
+#define IMX9_USDHC2_BLKATTR (IMX9_USDHC2_BASE + IMX9_USDHC_BLKATTR_OFFSET)
+#define IMX9_USDHC2_CMDARG (IMX9_USDHC2_BASE + IMX9_USDHC_CMDARG_OFFSET)
+#define IMX9_USDHC2_XFERTYP (IMX9_USDHC2_BASE + IMX9_USDHC_XFERTYP_OFFSET)
+#define IMX9_USDHC2_CMDRSP0 (IMX9_USDHC2_BASE + IMX9_USDHC_CMDRSP0_OFFSET)
+#define IMX9_USDHC2_CMDRSP1 (IMX9_USDHC2_BASE + IMX9_USDHC_CMDRSP1_OFFSET)
+#define IMX9_USDHC2_CMDRSP2 (IMX9_USDHC2_BASE + IMX9_USDHC_CMDRSP2_OFFSET)
+#define IMX9_USDHC2_CMDRSP3 (IMX9_USDHC2_BASE + IMX9_USDHC_CMDRSP3_OFFSET)
+#define IMX9_USDHC2_DATAPORT (IMX9_USDHC2_BASE + IMX9_USDHC_DATPORT_OFFSET)
+#define IMX9_USDHC2_PRSSTAT (IMX9_USDHC2_BASE + IMX9_USDHC_PRSSTAT_OFFSET)
+#define IMX9_USDHC2_PROCTL (IMX9_USDHC2_BASE + IMX9_USDHC_PROCTL_OFFSET)
+#define IMX9_USDHC2_SYSCTL (IMX9_USDHC2_BASE + IMX9_USDHC_SYSCTL_OFFSET)
+#define IMX9_USDHC2_IRQSTAT (IMX9_USDHC2_BASE + IMX9_USDHC_IRQSTAT_OFFSET)
+#define IMX9_USDHC2_IRQSTATEN (IMX9_USDHC2_BASE + IMX9_USDHC_IRQSTATEN_OFFSET)
+#define IMX9_USDHC2_IRQSIGEN (IMX9_USDHC2_BASE + IMX9_USDHC_IRQSIGEN_OFFSET)
+#define IMX9_USDHC2_AC12ERR (IMX9_USDHC2_BASE + IMX9_USDHC_AC12ERR_OFFSET)
+#define IMX9_USDHC2_HTCAPBLT (IMX9_USDHC2_BASE + IMX9_USDHC_HTCAPBLT_OFFSET)
+#define IMX9_USDHC2_WML (IMX9_USDHC2_BASE + IMX9_USDHC_WML_OFFSET)
+#define IMX9_USDHC2_MIX (IMX9_USDHC2_BASE + IMX9_USDHC_MIX_OFFSET)
+#define IMX9_USDHC2_FEVT (IMX9_USDHC2_BASE + IMX9_USDHC_FEVT_OFFSET)
+#define IMX9_USDHC2_ADMAES (IMX9_USDHC2_BASE + IMX9_USDHC_ADMAES_OFFSET)
+#define IMX9_USDHC2_ADSADDR (IMX9_USDHC2_BASE + IMX9_USDHC_ADSADDR_OFFSET)
+#define IMX9_USDHC2_DLL_CONTROL (IMX9_USDHC2_BASE + IMX9_USDHC_DLL_CONTROL_OFFSET)
+#define IMX9_USDHC2_DLL_STATUS (IMX9_USDHC2_BASE + IMX9_USDHC_DLL_STATUS)
+#define IMX9_USDHC2_CLK_TUNE_CTRL (IMX9_USDHC2_BASE + IMX9_USDHC_CLK_TUNE_CTRL)
+#define IMX9_USDHC2_VENDOR (IMX9_USDHC2_BASE + IMX9_USDHC_VENDOR_OFFSET)
+#define IMX9_USDHC2_MMCBOOT (IMX9_USDHC2_BASE + IMX9_USDHC_MMCBOOT_OFFSET)
+#define IMX9_USDHC2_VENDOR2 (IMX9_USDHC2_BASE + IMX9_USDHC_VENDOR2_OFFSET)
+#define IMX9_USDHC2_TC (IMX9_USDHC2_BASE + IMX9_USDHC_TC_OFFSET)
+#define IMX9_USDHC2_CQVER (IMX9_USDHC2_BASE + IMX9_USDHC_CQVER_OFFSET)
+#define IMX9_USDHC2_CQCAP (IMX9_USDHC2_BASE + IMX9_USDHC_CQCAP_OFFSET)
+#define IMX9_USDHC2_CQCFG (IMX9_USDHC2_BASE + IMX9_USDHC_CQCFG_OFFSET)
+#define IMX9_USDHC2_CQCTL (IMX9_USDHC2_BASE + IMX9_USDHC_CQCTL_OFFSET)
+#define IMX9_USDHC2_CQIS (IMX9_USDHC2_BASE + IMX9_USDHC_CQIS_OFFSET)
+#define IMX9_USDHC2_CQISTE (IMX9_USDHC2_BASE + IMX9_USDHC_CQISTE_OFFSET)
+#define IMX9_USDHC2_CQISGE (IMX9_USDHC2_BASE + IMX9_USDHC_CQISGE_OFFSET)
+#define IMX9_USDHC2_CQIC (IMX9_USDHC2_BASE + IMX9_USDHC_CQIC_OFFSET)
+#define IMX9_USDHC2_CQTDLBA (IMX9_USDHC2_BASE + IMX9_USDHC_CQTDLBA_OFFSET)
+#define IMX9_USDHC2_CQTDLBAU (IMX9_USDHC2_BASE + IMX9_USDHC_CQTDLBAU_OFFSET)
+#define IMX9_USDHC2_CQTDBR (IMX9_USDHC2_BASE + IMX9_USDHC_CQTDBR_OFFSET)
+#define IMX9_USDHC2_CQTCN (IMX9_USDHC2_BASE + IMX9_USDHC_CQTCN_OFFSET)
+#define IMX9_USDHC2_CQDQS (IMX9_USDHC2_BASE + IMX9_USDHC_CQDQS_OFFSET)
+#define IMX9_USDHC2_CQDPT (IMX9_USDHC2_BASE + IMX9_USDHC_CQDPT_OFFSET)
+#define IMX9_USDHC2_CQTCL (IMX9_USDHC2_BASE + IMX9_USDHC_CQTCLR_OFFSET)
+#define IMX9_USDHC2_CQSSC1 (IMX9_USDHC2_BASE + IMX9_USDHC_CQSSC1_OFFSET)
+#define IMX9_USDHC2_CQSSC2 (IMX9_USDHC2_BASE + IMX9_USDHC_CQSSC2_OFFSET)
+#define IMX9_USDHC2_CQCRDCT (IMX9_USDHC2_BASE + IMX9_USDHC_CQCRDCT_OFFSET)
+#define IMX9_USDHC2_CQRMEM (IMX9_USDHC2_BASE + IMX9_USDHC_CQRMEM_OFFSET)
+#define IMX9_USDHC2_CQTERRI (IMX9_USDHC2_BASE + IMX9_USDHC_CQTERRI_OFFSET)
+#define IMX9_USDHC2_CQCRI (IMX9_USDHC2_BASE + IMX9_USDHC_CQCRI_OFFSET)
+#define IMX9_USDHC2_CQCRA (IMX9_USDHC2_BASE + IMX9_USDHC_CQCRA_OFFSET)
+
+/* Register Bit Definitions *************************************************/
+
+/* DMA System Address Register */
+
+#define USDHC_DSADDR_SHIFT (0) /* Bits 2-31: DMA System Address */
+#define USDHC_DSADDR_MASK (0xfffffffc) /* Bits 0-1: 32 bit aligned, low bits Reserved */
+
+/* Block Attributes Register */
+
+#define USDHC_BLKATTR_SIZE_SHIFT (0) /* Bits 0-12: Transfer Block Size */
+#define USDHC_BLKATTR_SIZE_MASK (0x1fff << USDHC_BLKATTR_SIZE_SHIFT)
+# define USDHC_BLKATTR_SIZE(n) ((n) << USDHC_BLKATTR_SIZE_SHIFT)
+ /* Bits 13-15: Reserved */
+#define USDHC_BLKATTR_CNT_SHIFT (16) /* Bits 16-31: Blocks Count For Current Transfer */
+#define USDHC_BLKATTR_CNT_MASK (0xffff << USDHC_BLKATTR_CNT_SHIFT)
+# define USDHC_BLKATTR_CNT(n) ((n) << USDHC_BLKATTR_CNT_SHIFT)
+
+/* Command Argument Register (32-bit cmd/arg data) */
+
+/* Transfer Type Register */
+
+#define USDHC_XFERTYP_DMAEN (1 << 0) /* Bit 0: Enable DMA functionality */
+#define USDHC_XFERTYP_BCEN (1 << 1) /* Bit 1: Enable the Block Count register */
+#define USDHC_XFERTYP_AC12EN (1 << 2) /* Bit 2: Enable automatic CMD12 */
+#define USDHC_XFERTYP_DDREN (1 << 3) /* Bit 3: Dual data rate mode selection */
+#define USDHC_XFERTYP_DTDSEL (1 << 4) /* Bit 4: The direction of DATA line data transfers */
+#define USDHC_XFERTYP_MSBSEL (1 << 5) /* Bit 5: Enable multiple block DATA line data transfers */
+#define USDHC_XFERTYP_NIBBLE_POS (1 << 6) /* Bit 6: The nibble position */
+#define USDHC_XFERTYP_AC23EN (1 << 7) /* Bit 7: Enable automatic CMD23 */
+ /* Bits 8-15: Reserved */
+#define USDHC_XFERTYP_RSPTYP_SHIFT (16) /* Bits 16-17: Response Type Select */
+#define USDHC_XFERTYP_RSPTYP_MASK (3 << USDHC_XFERTYP_RSPTYP_SHIFT)
+# define USDHC_XFERTYP_RSPTYP_NONE (0 << USDHC_XFERTYP_RSPTYP_SHIFT) /* No response */
+# define USDHC_XFERTYP_RSPTYP_LEN136 (1 << USDHC_XFERTYP_RSPTYP_SHIFT) /* Response length 136 */
+# define USDHC_XFERTYP_RSPTYP_LEN48 (2 << USDHC_XFERTYP_RSPTYP_SHIFT) /* Response length 48 */
+# define USDHC_XFERTYP_RSPTYP_LEN48BSY (3 << USDHC_XFERTYP_RSPTYP_SHIFT) /* Response length 48, check busy */
+
+ /* Bit 18: Reserved */
+#define USDHC_XFERTYP_CCCEN (1 << 19) /* Bit 19: Command CRC Check Enable */
+#define USDHC_XFERTYP_CICEN (1 << 20) /* Bit 20: Command Index Check Enable */
+#define USDHC_XFERTYP_DPSEL (1 << 21) /* Bit 21: Data Present Select */
+#define USDHC_XFERTYP_CMDTYP_SHIFT (22) /* Bits 22-23: Command Type */
+#define USDHC_XFERTYP_CMDTYP_MASK (3 << USDHC_XFERTYP_CMDTYP_SHIFT)
+# define USDHC_XFERTYP_CMDTYP_NORMAL (0 << USDHC_XFERTYP_CMDTYP_SHIFT) /* Normal other commands */
+# define USDHC_XFERTYP_CMDTYP_SUSPEND (1 << USDHC_XFERTYP_CMDTYP_SHIFT) /* Suspend CMD52 for writing bus suspend in CCCR */
+# define USDHC_XFERTYP_CMDTYP_RESUME (2 << USDHC_XFERTYP_CMDTYP_SHIFT) /* Resume CMD52 for writing function select in CCCR */
+# define USDHC_XFERTYP_CMDTYP_ABORT (3 << USDHC_XFERTYP_CMDTYP_SHIFT) /* Abort CMD12, CMD52 for writing I/O abort in CCCR */
+
+#define USDHC_XFERTYP_CMDINX_SHIFT (24) /* Bits 24-29: Command Index */
+#define USDHC_XFERTYP_CMDINX_MASK (0x3f << USDHC_XFERTYP_CMDINX_SHIFT)
+ /* Bits 30-31: Reserved */
+
+/* Command Response 0-3 (32-bit response data) */
+
+/* Buffer Data Port Register (32-bit data content) */
+
+/* Present State Register */
+
+#define USDHC_PRSSTAT_CIHB (1 << 0) /* Bit 0: Command Inhibit (CMD) */
+#define USDHC_PRSSTAT_CDIHB (1 << 1) /* Bit 1: Command Inhibit (DAT) */
+#define USDHC_PRSSTAT_DLA (1 << 2) /* Bit 2: Data Line Active */
+#define USDHC_PRSSTAT_SDSTB (1 << 3) /* Bit 3: SD Clock Stable */
+ /* Bits 4-7: Reserved */
+#define USDHC_PRSSTAT_WTA (1 << 8) /* Bit 8: Write Transfer Active */
+#define USDHC_PRSSTAT_RTA (1 << 9) /* Bit 9: Read Transfer Active */
+#define USDHC_PRSSTAT_BWEN (1 << 10) /* Bit 10: Buffer Write Enable */
+#define USDHC_PRSSTAT_BREN (1 << 11) /* Bit 11: Buffer Read Enable */
+#define USDHC_PRSSTAT_RTR (1 << 12) /* Bit 12: Retuning request */
+ /* Bits 13-14: Reserved */
+#define USDHC_PRSSTAT_TSCD (1 << 15) /* Bit 15: Tape Select Change Done */
+#define USDHC_PRSSTAT_CINS (1 << 16) /* Bit 16: Card Inserted */
+ /* Bit 17: Reserved */
+#define USDHC_PRSSTAT_CDPL (1 << 18) /* Bit 18: Card Detect Pin Level */
+#define USDHC_PRSSTAT_WPSPL (1 << 19) /* Bit 19: Write Protect Switch Pin Level */
+ /* Bits 20-22: Reserved */
+#define USDHC_PRSSTAT_CLSL (1 << 23) /* Bit 23: CMD Line Signal Level */
+#define USDHC_PRSSTAT_DLSL_SHIFT (24) /* Bits 24-31: DAT Line Signal Level */
+#define USDHC_PRSSTAT_DLSL_MASK (0xff << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT0 (0x01 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT1 (0x02 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT2 (0x04 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT3 (0x08 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT4 (0x10 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT5 (0x20 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT6 (0x40 << USDHC_PRSSTAT_DLSL_SHIFT)
+# define USDHC_PRSSTAT_DLSL_DAT7 (0x80 << USDHC_PRSSTAT_DLSL_SHIFT)
+
+/* Protocol Control Register */
+
+ /* Bit 0: Reserved */
+#define USDHC_PROCTL_DTW_SHIFT (1) /* Bits 1-2: Data Transfer Width */
+#define USDHC_PROCTL_DTW_MASK (3 << USDHC_PROCTL_DTW_SHIFT)
+# define USDHC_PROCTL_DTW_1BIT (0 << USDHC_PROCTL_DTW_SHIFT) /* 1-bit mode */
+# define USDHC_PROCTL_DTW_4BIT (1 << USDHC_PROCTL_DTW_SHIFT) /* 4-bit mode */
+# define USDHC_PROCTL_DTW_8BIT (2 << USDHC_PROCTL_DTW_SHIFT) /* 8-bit mode */
+
+#define USDHC_PROCTL_D3CD (1 << 3) /* Bit 3: DAT3 as Card Detection Pin */
+#define USDHC_PROCTL_EMODE_SHIFT (4) /* Bits 4-5: Endian mode */
+#define USDHC_PROCTL_EMODE_MASK (3 << USDHC_PROCTL_EMODE_SHIFT)
+# define USDHC_PROCTL_EMODE_BE (0 << USDHC_PROCTL_EMODE_SHIFT)/* Big endian mode */
+# define USDHC_PROCTL_EMODE_HWBE (1 << USDHC_PROCTL_EMODE_SHIFT)/* Half word big endian mode */
+# define USDHC_PROCTL_EMODE_LE (2 << USDHC_PROCTL_EMODE_SHIFT)/* Little endian mode */
+ /* Bits 6-7: Reserved */
+#define USDHC_PROCTL_DMAS_SHIFT (8) /* Bits 8-9: DMA Select */
+#define USDHC_PROCTL_DMAS_MASK (3 << USDHC_PROCTL_DMAS_SHIFT)
+# define USDHC_PROCTL_DMAS_NODMA (0 << USDHC_PROCTL_DMAS_SHIFT) /* No DMA or simple DMA is selected */
+# define USDHC_PROCTL_DMAS_ADMA1 (1 << USDHC_PROCTL_DMAS_SHIFT) /* ADMA1 is selected */
+# define USDHC_PROCTL_DMAS_ADMA2 (2 << USDHC_PROCTL_DMAS_SHIFT) /* ADMA2 is selected */
+
+ /* Bits 10-25: Reserved */
+#define USDHC_PROCTL_SABGREQ (1 << 16) /* Bit 16: Stop At Block Gap Request */
+#define USDHC_PROCTL_CREQ (1 << 17) /* Bit 17: Continue Request */
+#define USDHC_PROCTL_RWCTL (1 << 18) /* Bit 18: Read Wait Control */
+#define USDHC_PROCTL_IABG (1 << 19) /* Bit 19: Interrupt At Block Gap */
+#define USDHC_PROCTL_RDDONENO8CLK (1 << 20) /* Bit 20: Read done to 8 clock */
+#define USDHC_PROCTL_RESV2023 (4 << 21) /* Bits 21-23: Reserved, write as 0x100 */
+#define USDHC_PROCTL_WECINT (1 << 24) /* Bit 24: Wakeup Event Enable On Card Interrupt */
+#define USDHC_PROCTL_WECINS (1 << 25) /* Bit 25: Wakeup Event Enable On SD Card Insertion */
+#define USDHC_PROCTL_WECRM (1 << 26) /* Bit 26: Wakeup Event Enable On SD Card Removal */
+ /* Bits 27-29: Reserved */
+#define USDHC_PROTCTL_NEBLKRD (1 << 30) /* Bit 30: Non-exect block read */
+ /* Bit 31: Reserved */
+
+/* System Control Register */
+
+#define USDHC_SYSCTL_RES0 (0x0F << 0) /* Bit 0-3: Reserved, set to 1 */
+#define USDHC_SYSCTL_DVS_SHIFT (4) /* Bits 4-7: Divisor */
+#define USDHC_SYSCTL_DVS_MASK (0x0f << USDHC_SYSCTL_DVS_SHIFT)
+# define USDHC_SYSCTL_DVS_DIV(n) (((n) - 1) << USDHC_SYSCTL_DVS_SHIFT) /* Divide by n, n=1,2,15,16 */
+
+#define USDHC_SYSCTL_SDCLKFS_SHIFT (8) /* Bits 8-15: SDCLK Frequency Select */
+#define USDHC_SYSCTL_SDCLKFS_MASK (0xff << USDHC_SYSCTL_SDCLKFS_SHIFT)
+# define USDHC_SYSCTL_SDCLKFS_BYPASS (0x00 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Bypass the prescaler */
+# define USDHC_SYSCTL_SDCLKFS_DIV2 (0x01 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 2 */
+# define USDHC_SYSCTL_SDCLKFS_DIV4 (0x02 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 4 */
+# define USDHC_SYSCTL_SDCLKFS_DIV8 (0x04 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 8 */
+# define USDHC_SYSCTL_SDCLKFS_DIV16 (0x08 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 16 */
+# define USDHC_SYSCTL_SDCLKFS_DIV32 (0x10 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 32 */
+# define USDHC_SYSCTL_SDCLKFS_DIV64 (0x20 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 64 */
+# define USDHC_SYSCTL_SDCLKFS_DIV128 (0x40 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 128 */
+# define USDHC_SYSCTL_SDCLKFS_DIV256 (0x80 << USDHC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 256 */
+
+#define USDHC_SYSCTL_DTOCV_SHIFT (16) /* Bits 16-19: Data Timeout Counter Value */
+#define USDHC_SYSCTL_DTOCV_MASK (0xf << USDHC_SYSCTL_DTOCV_SHIFT)
+# define USDHC_SYSCTL_DTOCV_PWR32 (0x0 << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^32 */
+# define USDHC_SYSCTL_DTOCV_PWR33 (0x1 << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^33 */
+# define USDHC_SYSCTL_DTOCV_PWR18 (0x2 << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^18 */
+# define USDHC_SYSCTL_DTOCV_PWR19 (0x3 << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^19 */
+# define USDHC_SYSCTL_DTOCV_PWR29 (0xd << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^29 */
+# define USDHC_SYSCTL_DTOCV_PWR30 (0xe << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^30 */
+# define USDHC_SYSCTL_DTOCV_PWR31 (0xf << USDHC_SYSCTL_DTOCV_SHIFT) /* SDCLK x 2^31 */
+
+ /* Bits 20-21: Reserved */
+#define USDHC_SYSCTL_RSTF (1 << 22) /* Bit 24: Reset the async FIFO */
+#define USDHC_SYSCTL_IPPRSTN (1 << 23) /* Bit 23: Card /reset (default 1) */
+#define USDHC_SYSCTL_RSTA (1 << 24) /* Bit 24: Software Reset For ALL */
+#define USDHC_SYSCTL_RSTC (1 << 25) /* Bit 25: Software Reset For CMD Line */
+#define USDHC_SYSCTL_RSTD (1 << 26) /* Bit 26: Software Reset For DAT Line */
+#define USDHC_SYSCTL_INITA (1 << 27) /* Bit 27: Initialization Active */
+#define USDHC_SYSCTL_RSTT (1 << 28) /* Bit 28: Reset tuning */
+ /* Bits 29-31: Reserved */
+
+/* Interrupt Status Register, Interrupt Status Enable Register and
+ * Interrupt Signal Enable Register
+ * Common interrupt bit definitions
+ */
+
+#define USDHC_INT_CC (1 << 0) /* Bit 0: Command Complete */
+#define USDHC_INT_TC (1 << 1) /* Bit 1: Transfer Complete */
+#define USDHC_INT_BGE (1 << 2) /* Bit 2: Block Gap Event */
+#define USDHC_INT_DINT (1 << 3) /* Bit 3: DMA Interrupt */
+#define USDHC_INT_BWR (1 << 4) /* Bit 4: Buffer Write Ready */
+#define USDHC_INT_BRR (1 << 5) /* Bit 5: Buffer Read Ready */
+#define USDHC_INT_CINS (1 << 6) /* Bit 6: Card Insertion */
+#define USDHC_INT_CRM (1 << 7) /* Bit 7: Card Removal */
+#define USDHC_INT_CINT (1 << 8) /* Bit 8: Card Interrupt */
+ /* Bits 9-11: Reserved */
+#define USDHC_INT_RTR (1 << 12) /* Bit 12: Re-tuning event */
+#define USDHC_INT_TP (1 << 13) /* Bit 13: Tuning pass */
+#define USDHC_INT_CQI (1 << 14) /* Bit 14: Command queuing interrupt */
+#define USDHC_INT_STATUS (1 << 15) /* Bit 15: Error Interrupt Status / Reserved */
+#define USDHC_INT_CTOE (1 << 16) /* Bit 16: Command Timeout Error */
+#define USDHC_INT_CCE (1 << 17) /* Bit 17: Command CRC Error */
+#define USDHC_INT_CEBE (1 << 18) /* Bit 18: Command End Bit Error */
+#define USDHC_INT_CIE (1 << 19) /* Bit 19: Command Index Error */
+#define USDHC_INT_DTOE (1 << 20) /* Bit 20: Data Timeout Error */
+#define USDHC_INT_DCE (1 << 21) /* Bit 21: Data CRC Error */
+#define USDHC_INT_DEBE (1 << 22) /* Bit 22: Data End Bit Error */
+ /* Bit 23: Reserved */
+#define USDHC_INT_AC12E (1 << 24) /* Bit 24: Auto CMD12 Error */
+ /* Bit 25: Reserved */
+#define USDHC_INT_TNE (1 << 26) /* Bit 26: Tuning error */
+ /* Bit 27: Reserved */
+#define USDHC_INT_DMAE (1 << 28) /* Bit 28: DMA Error */
+ /* Bits 29-31: Reserved */
+#define USDHC_INT_ALL 0x117f01ff
+
+/* Auto CMD12 Error Status Register */
+
+#define USDHC_AC12ERR_NE (1 << 0) /* Bit 0: Auto CMD12 Not Executed */
+#define USDHC_AC12ERR_TOE (1 << 1) /* Bit 1: Auto CMD12/23 Timeout Error */
+#define USDHC_AC12ERR_CE (1 << 2) /* Bit 2: Auto CMD12/23 CRC Error */
+#define USDHC_AC12ERR_EBE (1 << 3) /* Bit 3: Auto CMD12/23 End Bit Error */
+#define USDHC_AC12ERR_IE (1 << 4) /* Bit 4: Auto CMD12/23 Index Error */
+ /* Bits 5-6: Reserved */
+#define USDHC_AC12ERR_CNI (1 << 7) /* Bit 7: Command Not Issued By Auto CMD12 Error */
+ /* Bits 8-21: Reserved */
+#define USDHC_AC12ERR_EXECUTE_TUNING (1 << 22) /* Bit 22: Execute Tuning */
+#define USDHC_AC12ERR_SMP_CLK_SEL (1 << 23) /* Bit 23: Sample clock sel */
+ /* Bits 24-31: Reserved */
+
+/* Host Controller Capabilities */
+
+#define USDHC_HTCAPBLT_SDR50 (1 << 0) /* Bit 0: SDR50 support indication */
+#define USDHC_HTCAPBLT_SDR104 (1 << 1) /* Bit 1: SDR104 support indication */
+#define USDHC_HTCAPBLT_DDR50 (1 << 2) /* Bit 2: DDR50 support indication */
+ /* Bits 3-7: Reserved */
+ /* Bits 8-12: Reserved */
+#define USDHC_HTCAPBLT_USE_TUNING_SDR50 (1 << 13) /* Bit 13: Use tuning for SDR50 */
+#define USDHC_HTCAPBLT_MBL_SHIFT (16) /* Bits 16-18: Max Block Length */
+#define USDHC_HTCAPBLT_MBL_MASK (7 << USDHC_HTCAPBLT_MBL_SHIFT)
+# define USDHC_HTCAPBLT_MBL_512BYTES (0 << USDHC_HTCAPBLT_MBL_SHIFT)
+# define USDHC_HTCAPBLT_MBL_1KB (1 << USDHC_HTCAPBLT_MBL_SHIFT)
+# define USDHC_HTCAPBLT_MBL_2KB (2 << USDHC_HTCAPBLT_MBL_SHIFT)
+# define USDHC_HTCAPBLT_MBL_4KB (3 << USDHC_HTCAPBLT_MBL_SHIFT)
+ /* Bit 19: Reserved */
+#define USDHC_HTCAPBLT_ADMAS (1 << 20) /* Bit 20: ADMA Support */
+#define USDHC_HTCAPBLT_HSS (1 << 21) /* Bit 21: High Speed Support */
+#define USDHC_HTCAPBLT_DMAS (1 << 22) /* Bit 22: DMA Support */
+#define USDHC_HTCAPBLT_SRS (1 << 23) /* Bit 23: Suspend/Resume Support */
+#define USDHC_HTCAPBLT_VS33 (1 << 24) /* Bit 24: Voltage Support 3.3 V */
+#define USDHC_HTCAPBLT_VS30 (1 << 25) /* Bit 25: Voltage Support 3.0 V */
+ /* Bits 26-31: Reserved */
+
+/* Watermark Level Register */
+
+#define USDHC_WML_RD_SHIFT (0) /* Bits 0-7: Read Watermark Level */
+#define USDHC_WML_RD_MASK (0xff << USDHC_WML_RDWML_SHIFT)
+# define USDHC_WML_RD(n) ((n) << SDHC_WML_RDWML_SHIFT)
+ /* Bits 8-15: Reserved */
+#define USDHC_WML_WR_SHIFT (16) /* Bits 16-23: Write Watermark Level */
+#define USDHC_WML_WR_MASK (0xff << USDHC_WML_WRWML_SHIFT)
+# define USDHC_WML_WR(n) ((n) << SDHC_WML_WRWML_SHIFT)
+ /* Bits 24-31: Reserved */
+
+/* Mixer Control Register */
+
+#define USDHC_MC_DEFAULTVAL (0x80000000) /* Bit 31 is always set */
+#define USDHC_MC_DMAEN (1 << 0) /* Bit 0: DMA Enable */
+#define USDHC_MC_BCEN (1 << 1) /* Bit 1: Block Count Enable */
+#define USDHC_MC_AC12EN (1 << 2) /* Bit 2: Auto CMD12 Enable */
+#define USDHC_MC_DDR_EN (1 << 3) /* Bit 3: DDR mode enable */
+#define USDHC_MC_DTDSEL (1 << 4) /* Bit 4: Data Transfer direction select */
+#define USDHC_MC_MSBSEL (1 << 5) /* Bit 5: Multi/single block select */
+#define USDHC_MC_NIBBLE_POS (1 << 6) /* Bit 6: Nibble position for DDR 4 bit */
+#define USDHC_MC_AC23EN (1 << 7) /* Bit 7: Auto CMD23 Enable */
+ /* Bits 8-21: Reserved */
+#define USDHC_MC_EXE_TUNE (1 << 22) /* Bit 22: Execute Tuning */
+#define USDHC_MC_SMP_CLK_SEL (1 << 23) /* Bit 23: SMP Clock Sel */
+#define USDHC_MC_AUTO_TUNE_EN (1 << 24) /* Bit 24: Auto tune enable */
+#define USDHC_MC_FBCLK_SEL (1 << 25) /* Bit 25: Feedback clock source selection */
+#define USDHC_MC_HS400EN (1 << 26) /* Bit 26: Enable HS400 mode */
+#define USDHC_MC_EHS400EN (1 << 27) /* Bit 27: Enable enhance HS400 mode */
+ /* Bits 28-31: reserved */
+
+/* Force Event Register */
+
+#define USDHC_FEVT_AC12NE (1 << 0) /* Bit 0: Force Event Auto Command 12 Not Executed */
+#define USDHC_FEVT_AC12TOE (1 << 1) /* Bit 1: Force Event Auto Command 12 Time Out Error */
+#define USDHC_FEVT_AC12CE (1 << 2) /* Bit 2: Force Event Auto Command 12 CRC Error */
+#define USDHC_FEVT_AC12EBE (1 << 3) /* Bit 3: Force Event Auto Command 12 End Bit Error */
+#define USDHC_FEVT_AC12IE (1 << 4) /* Bit 4: Force Event Auto Command 12 Index Error */
+ /* Bits 5-6: Reserved */
+#define USDHC_FEVT_CNIBAC12E (1 << 7) /* Bit 7: Force Event Command Not Executed By Auto Command 12 Error */
+ /* Bits 8-15: Reserved */
+#define USDHC_FEVT_CTOE (1 << 16) /* Bit 16: Force Event Command Time Out Error */
+#define USDHC_FEVT_CCE (1 << 17) /* Bit 17: Force Event Command CRC Error */
+#define USDHC_FEVT_CEBE (1 << 18) /* Bit 18: Force Event Command End Bit Error */
+#define USDHC_FEVT_CIE (1 << 19) /* Bit 19: Force Event Command Index Error */
+#define USDHC_FEVT_DTOE (1 << 20) /* Bit 20: Force Event Data Time Out Error */
+#define USDHC_FEVT_DCE (1 << 21) /* Bit 21: Force Event Data CRC Error */
+#define USDHC_FEVT_DEBE (1 << 22) /* Bit 22: Force Event Data End Bit Error */
+ /* Bit 23: Reserved */
+#define USDHC_FEVT_AC12E (1 << 24) /* Bit 24: Force Event Auto Command 12 Error */
+ /* Bit 25: Reserved */
+#define USDHC_FEVT_TTNE (1 << 26) /* Bit 26: Force tuning error */
+ /* Bit 27: reserved */
+#define USDHC_FEVT_DMAE (1 << 28) /* Bit 28: Force Event DMA Error */
+ /* Bits 29-30: Reserved */
+#define USDHC_FEVT_CINT (1 << 31) /* Bit 31: Force Event Card Interrupt */
+
+/* ADMA Error Status Register */
+
+#define USDHC_ADMAES_SHIFT (0) /* Bits 0-1: ADMA Error State (when ADMA Error is occurred) */
+#define USDHC_ADMAES_MASK (3 << USDHC_ADMAES_ADMAES_SHIFT)
+# define USDHC_ADMAES_STOP (0 << USDHC_ADMAES_ADMAES_SHIFT) /* Stop DMA */
+# define USDHC_ADMAES_FDS (1 << USDHC_ADMAES_ADMAES_SHIFT) /* Fetch descriptor */
+# define USDHC_ADMAES_CADR (2 << USDHC_ADMAES_ADMAES_SHIFT) /* Change address */
+# define USDHC_ADMAES_TFR (3 << USDHC_ADMAES_ADMAES_SHIFT) /* Transfer data */
+
+#define USDHC_ADMAES_LME (1 << 2) /* Bit 2: ADMA Length Mismatch Error */
+#define USDHC_ADMAES_DCE (1 << 3) /* Bit 3: ADMA Descriptor Error */
+ /* Bits 4-31: Reserved */
+
+/* ADMA System Address Register */
+
+#define USDHC_ADSADDR_SHIFT (0) /* Bits 1-31: ADMA System Address */
+#define USDHC_ADSADDR_MASK (0xfffffffc) /* Bits 0-1: Reserved */
+
+/* Delay Line Control */
+
+#define USDHC_DL_CTRL_EN (1 << 0) /* Bit 0: Delay Line enable */
+#define USDHC_DL_CTRL_RST (1 << 1) /* Bit 1: Delay line reset */
+#define USDHC_DL_CTRL_SLV_FORCE_UP (1 << 2) /* Bit 2: SLV Force update */
+#define USDHC_DL_SLV_DLY_TGT0_SHIFT (3) /* Bits 3-6: Delay Target 0 */
+#define USDHC_DL_SLV_DLY_TGT0_MASK (0xf << USDHC_DL_SLV_DLY_TGT0_SHIFT)
+# define USDHC_DL_SLV_DLY_TGT0(n) ((n) << USDHC_DL_SLV_DLY_TGT0_SHIFT)
+#define USDHC_DL_CTRL_SLV_UPD (1 << 7) /* Bit 7: Delay Control Gate update */
+#define USDHC_DL_CTRL_SLV_OVR (1 << 8) /* Bit 8: Delay Control Gate override */
+#define USDHC_DL_CTRL_OVR_VAL_SHIFT (9) /* Bits 9-15: Override Value */
+#define USDHC_DL_CTRL_OVR_VAL_MASK (0x7f << USDHC_DL_CTRL_OVR_VAL_SHIFT)
+# define USDHC_DL_CTRL_OVR_VAL(n) ((n) << USDHC_DL_CTRL_OVR_VAL_SHIFT)
+#define USDHC_DL_SLV_DLY_TGT1_SHIFT (16) /* Bits 16-18: Delay Target 1 */
+#define USDHC_DL_SLV_DLY_TGT1_MASK (0x7 << USDHC_DL_SLV_DLY_TGT1_SHIFT)
+# define USDHC_DL_SLV_DLY_TGT1(n) ((n) << USDHC_DL_SLV_DLY_TGT1_SHIFT)
+ /* Bit 19: Reserved */
+#define USDHC_DL_CTRL_SLV_UPDINT_SHIFT (20) /* Bits 20-27: DLL Control SLV Update Interval */
+#define USDHC_DL_CTRL_SLV_UPDINT_MASK (0xff << USDHC_DL_CTRL_SLV_UPDINT_SHIFT)
+# define USDHC_DL_CTRL_SLV_UPDINT(n) ((n) << USDHC_DL_CTRL_SLV_UPDINT_SHIFT)
+#define USDHC_DL_CTRL_REF_UPDINT_SHIFT (28) /* Bits 28-31: DLL Control Reference Update Interval */
+#define USDHC_DL_CTRL_REF_UPDINT_MASK (0xf << USDHC_DL_CTRL_REF_UPDINT_SHIFT)
+# define USDHC_DL_CTRL_REF_UPDINT(n) ((n)<< USDHC_DL_CTRL_REF_UPDINT_SHIFT)
+
+/* Delay Line Status */
+
+#define USDHC_DL_STAT_SLV_LOCK (1 << 0) /* Bit 0: Slave delay-line lock status */
+#define USDHC_DL_STAT_REF_LOCK (1 << 1) /* Bit 1: Reference delay-line lock status */
+#define USDHC_DL_STAT_SLV_SEL_SHIFT (2) /* Bits 2-8: Slave delay line select status */
+#define USDHC_DL_STAT_SLV_SEL_MASK (0x7f << USDHC_DL_STAT_SLV_SEL_SHIFT)
+# define USDHC_DL_STAT_SLV_SEL(n) ((n) << USDHC_DL_STAT_SLV_SEL_SHIFT)
+#define USDHC_DL_STAT_REF_SEL_SHIFT (9) /* Bits 9-15: Reference delay line select taps */
+#define USDHC_DL_STAT_REF_SEL_MASK (0x7f << USDHC_DL_STAT_REF_SEL_SHIFT)
+# define USDHC_DL_STAT_REF_SEL(n) ((n) << USDHC_DL_STAT_REF_SEL_SHIFT)
+ /* Bits 16-31: Reserved */
+
+/* Clk tuning control and status */
+
+#define USDHC_CLKTUNE_CS_DCS_POST_SHIFT (0) /* Bits 0-3: Set delay cells between CLK_OUT and CLK_POST*/
+#define USDHC_CLKTUNE_CS_DCS_POST_MASK (0xf << USDHC_CLKTUNE_CS_DCS_POST_SHIFT)
+# define USDHC_CLKTUNE_CS_DCS_POST(n) ((n) << USDHC_CLKTUNE_CS_DCS_POST_SHIFT)
+#define USDHC_CLKTUNE_CS_DCS_OUT_SHIFT (4) /* Bits 4-7: Set delay cells between CLK_PRE and CLK_OUT */
+#define USDHC_CLKTUNE_CS_DCS_OUT_MASK (0xf << USDHC_CLKTUNE_CS_DCS_OUT_SHIFT)
+# define USDHC_CLKTUNE_CS_DCS_OUT(n) ((n) << USDHC_CLKTUNE_CS_DCS_OUT_SHIFT)
+#define USDHC_CLKTUNE_CS_DCS_PRE_SHIFT (8) /* Bits 8-14: Set celay cells between the feedback clock and CLK_PRE */
+#define USDHC_CLKTUNE_CS_DCS_PRE_MASK (0x7f << USDHC_CLKTUNE_CS_DCS_PRE_SHIFT)
+# define USDHC_CLKTUNE_CS_DCS_PRE(n) ((n) << USDHC_CLKTUNE_CS_DCS_PRE_SHIFT)
+#define USDHC_CLKTUNE_CS_NXT_ERR (1 << 15) /* Bit 15: NXT error */
+#define USDHC_CLKTUNE_CS_TS_POST_SHIFT (16) /* Bits 16-19: Delay cells between CLK_OUT and CLK_POST */
+#define USDHC_CLKTUNE_CS_TS_POST_MASK (0xf << USDHC_CLKTUNE_CS_TS_POST_SHIFT)
+# define USDHC_CLKTUNE_CS_TS_POST(n) ((n) << USDHC_CLKTUNE_CS_TS_POST_SHIFT)
+#define USDHC_CLKTUNE_CS_TS_OUT_SHIFT (20) /* Bits 20-23: Delay cells between CLK_PRE and CLK_OUT */
+#define USDHC_CLKTUNE_CS_TS_OUT_MASK (0xf << USDHC_CLKTUNE_CS_TS_OUT_SHIFT)
+# define USDHC_CLKTUNE_CS_TS_OUT(n) ((n) << USDHC_CLKTUNE_CS_TS_OUT_SHIFT)
+#define USDHC_CLKTUNE_CS_TS_PRE_SHIFT (24) /* Bits 24-30: Delay cells between the feedback clock and CLK_PRE */
+#define USDHC_CLKTUNE_CS_TS_PRE_MASK (0x7f << USDHC_CLKTUNE_CS_TS_PRE_SHIFT)
+# define USDHC_CLKTUNE_CS_TS_PRE_OUT(n) ((n) << USDHC_CLKTUNE_CS_TS_PRE_SHIFT)
+#define USDHC_CLKTUNE_CS_PRE_ERR (1 << 31) /* Bit 31: PRE error */
+
+/* Strobe DLL control */
+
+/* Strobe DLL status */
+
+/* Vendor Specific Register */
+
+ /* Bit 0: Reserved */
+#define USHDC_VENDOR_VSELECT (1 << 1) /* Bit 1: Voltage selection */
+ /* Bit 2: Reserved */
+#define USDHC_VENDOR_CHKBUSY_ON (1 << 3) /* Bit 3: Enable Check busy */
+ /* Bit 4-7: Reserved */
+#define USDHC_VENDOR_FRC_SDCLK_ON (1 << 8) /* Bit 8: Force clock active */
+ /* Bits 9-14: Reserved */
+#define USDHC_VENDOR_CRC_CHECK_OFF (1 << 15) /* Bit 15: Switch off CRC checking */
+ /* Bits 16:30 Reserved */
+#define USHDC_VENDOR_DEFAULTVAL (0x20000000) /* Bit 29 is always set */
+#define USDHC_VENDOR_CMDBYTEACC_ON (1 << 31) /* Bit 31: Enable command byte access */
+
+/* MMC Boot Register */
+
+#define USDHC_MMCBOOT_DTOCVACK_SHIFT (0) /* Bits 0-3: Boot ACK time out counter value */
+#define USDHC_MMCBOOT_DTOCVACK_MASK (0xf << USDHC_MMCBOOT_DTOCVACK_SHIFT)
+# define USDHC_MMCBOOT_DTOCVACK_PWR32 (0x0 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^32 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR33 (0x1 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^33 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR18 (0x2 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^18 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR19 (0x3 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^19 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR20 (0x4 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^20 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR21 (0x5 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^21 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR22 (0x6 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^22 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR23 (0x7 << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^23 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR30 (0xe << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^30 */
+# define USDHC_MMCBOOT_DTOCVACK_PWR31 (0xf << USDHC_MMCBOOT_DTOCVACK_SHIFT) /* SDCLK x 2^31 */
+
+#define USDHC_MMCBOOT_BOOTACK (1 << 4) /* Bit 4: Boot ack mode select */
+#define USDHC_MMCBOOT_BOOTMODE (1 << 5) /* Bit 5: Boot mode select */
+#define USDHC_MMCBOOT_BOOTEN (1 << 6) /* Bit 6: Boot mode enable */
+#define USDHC_MMCBOOT_AUTOSABGEN (1 << 7) /* Bit 7: Enable auto stop at block gap function */
+#define USDHC_MMCBOOT_DISABLETO (1 << 8) /* Bit 8: Timeout enable */
+ /* Bits 9-15: Reserved */
+#define USDHC_MMCBOOT_BOOTBLKCNT_SHIFT (16) /* Bits 16-31: Stop at block gap value of automatic mode */
+#define USDHC_MMCBOOT_BOOTBLKCNT_MASK (0xffff << USDHC_MMCBOOT_BOOTBLKCNT_SHIFT)
+#define USDHC_MMCBOOT_BOOTBLKCNT(n) ((n) << USDHC_MMCBOOT_BOOTBLKCNT_SHIFT)
+
+/* Vendor specific register 2 */
+
+ /* Bits 0-2: Reserved */
+#define USDHC_VS2_CARDINTD3 (1 << 3) /* Bit 3: Card interrupt detection test */
+#define USDHC_VS2_TUNINGBITEN_SHIFT (4) /* Bits 4-5: Tuning bit enable */
+#define USDHC_VS2_TUNINGBITEN_MASK (0x3 << USDHC_VS2_TUNINGBITEN_SHIFT)
+# define USDHC_VS2_TUNINGBITEN_30 (0 << USDHC_VS2_TUNINGBITEN_SHIFT) /* Enable Tuning circuit for DATA[3:0] */
+# define USDHC_VS2_TUNINGBITEN_70 (1 << USDHC_VS2_TUNINGBITEN_SHIFT) /* Enable Tuning circuit for DATA[7:0] */
+# define USDHC_VS2_TUNINGBITEN_0 (2 << USDHC_VS2_TUNINGBITEN_SHIFT) /* Enable Tuning circuit for DATA[0] */
+
+#define USDHC_VS2_TUNINGCMDEN (1 << 6) /* Bit 6: Tuning CMD enable */
+ /* Bits 7-9: Reserved */
+#define USDHC_VS2_HS400WRCLKSEN (1 << 10) /* Bit 10: HS400 write clock stop enable */
+#define USDHC_VS2_HS400RDCLKSEN (1 << 11) /* Bit 11: HS400 read clock stop enable */
+#define USDHC_VS2_ACMD23ARGU2 (1 << 12) /* Bit 12: Argument 2 register enable for ACMD23 */
+ /* Bit 13: Reserved */
+#define USDHC_VS2_BUSRESET (1 << 14) /* Bit 14: Bus reset (undocumented!) */
+#define USDHC_VS2_32KCLKEN (1 << 15) /* Bit 15: Use low power clock for card detection */
+#define USDHC_VS2_FBCLK_TAP_SEL_SHIFT (16) /* Bits 16-31: Enable extra delay on internal feedback clock */
+#define USDHC_VS2_FBCLK_TAP_SEL_MASK (0xffff << USDHC_VS2_FBCLK_TAP_SEL_SHIFT)
+#define USDHC_VS2_FBCLK_TAP_SEL(n) ((n) << USDHC_VS2_FBCLK_TAP_SEL_SHIFT)
+
+/* Tuning Control Register */
+
+#define USDHC_TC_STARTTAP_SHIFT (0) /* Bits 0-6: Start TAP for CMD19 tuning */
+#define USDHC_TC_STARTTAP_MASK (0x7f << USDHC_TC_STARTTAP_SHIFT)
+# define USDHC_TC_STARTTAP(n) ((n) << USDHC_TC_STARTTAP_SHIFT)
+#define USDHC_TC_DIS_CMD (1 << 7) /* Bit 7: Disable command check for standard tuning */
+#define USDHC_TC_COUNT_SHIFT (8) /* Bits 8-15: Count for CMD19 tuning */
+#define USDHC_TC_COUNT_MASK (0xff << USDHC_TC_COUNT_SHIFT)
+# define USDHC_TC_COUNT(n) ((n) << USDHC_TC_COUNT_SHIFT)
+ /* Bit 19: Reserved */
+#define USDHC_TC_WINDOW_SHIFT (20) /* Bits 20-22: Tuning window */
+#define USDHC_TC_WINDOW_MASK (0x7 << USDHC_TC_WINDOWS_SHIFT)
+# define USDHC_TC_WINDOW(n) ((n) << USDHC_TC_WINDOW_SHIFT)
+ /* Bit 23: Reserved */
+#define USDHC_TC_TUNINGEN (1 << 24) /* Bit 24: Tuning enable */
+ /* Bits 25-31: Reserved */
+
+/* Command Queuing Version */
+
+/* Command Queuing Capabilities */
+
+/* Command Queuing Configuration */
+
+/* Command Queuing Control */
+
+/* Command Queuing Interrupt Status */
+
+/* Command Queuing Interrupt Status Enable */
+
+/* Command Queuing Interrupt Signal Enable */
+
+/* Command Queuing Interrupt Coalescing */
+
+/* Command Queuing Task Descriptor List Base Address */
+
+/* Command Queuing Task Descriptor List Base Address Upper 32 Bits */
+
+/* Command Queuing Task Doorbell */
+
+/* Command Queuing Task Completion Notification */
+
+/* Command Queuing Device Queue Status */
+
+/* Command Queuing Device Pending Tasks */
+
+/* Command Queuing Task Clear */
+
+/* Command Queuing Send Status Configuration 1 */
+
+/* Command Queuing Send Status Configuration 2 */
+
+/* Command Queuing Command Response for Direct-Command Task */
+
+/* Command Queuing Response Mode Error Mask */
+
+/* Command Queuing Task Error Information */
+
+/* Command Queuing Command Response Index */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_USDHC_H */
diff --git a/arch/arm64/src/imx9/imx9_usdhc.c b/arch/arm64/src/imx9/imx9_usdhc.c
new file mode 100644
index 0000000000..7bb5d7a3b9
--- /dev/null
+++ b/arch/arm64/src/imx9/imx9_usdhc.c
@@ -0,0 +1,3353 @@
+/****************************************************************************
+ * arch/arm64/src/imx9/imx9_usdhc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/wdog.h>
+#include <nuttx/clock.h>
+#include <nuttx/arch.h>
+#include <nuttx/sdio.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/mmcsd.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm64_internal.h"
+#include "imx9_gpio.h"
+#include "hardware/imx9_pinmux.h"
+#include "hardware/imx9_ccm.h"
+#include "imx9_iomuxc.h"
+#include "imx9_ccm.h"
+#include "imx9_clockconfig.h"
+#include "imx9_usdhc.h"
+
+#ifdef CONFIG_IMX9_USDHC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if !defined(ARMV8A_DCACHE_LINESIZE) || ARMV8A_DCACHE_LINESIZE == 0
+# undef ARMV8A_DCACHE_LINESIZE
+# define ARMV8A_DCACHE_LINESIZE 64
+#endif
+
+#define DCACHE_LINEMASK (ARMV8A_DCACHE_LINESIZE - 1)
+
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+# define cache_aligned_alloc(s) kmm_memalign(ARMV8A_DCACHE_LINESIZE,(s))
+# define CACHE_ALIGNED_DATA aligned_data(ARMV8A_DCACHE_LINESIZE)
+#else
+# define cache_aligned_alloc kmm_malloc
+# define CACHE_ALIGNED_DATA
+#endif
+
+/* Configuration ************************************************************/
+
+#if ((defined(CONFIG_IMX9_USDHC1) && !defined(CONFIG_IMX9_USDHC2)) || \
+ (defined(CONFIG_IMX9_USDHC2) && !defined(CONFIG_IMX9_USDHC1)))
+# define IMX9_MAX_SDHC_DEV_SLOTS 1
+#elif (defined(CONFIG_IMX9_USDHC1) && defined(CONFIG_IMX9_USDHC2))
+# define IMX9_MAX_SDHC_DEV_SLOTS 2
+#else
+#error Unrecognised number of SDHC slots
+#endif
+
+#if !defined(CONFIG_IMX9_USDHC_DMA)
+# warning "Large Non-DMA transfer may result in RX overrun failures"
+#elif !defined(CONFIG_SDIO_DMA)
+# warning CONFIG_SDIO_DMA should be defined with CONFIG_IMX9_USDHC_DMA
+#endif
+
+#if !defined(CONFIG_SCHED_WORKQUEUE) || !defined(CONFIG_SCHED_HPWORK)
+# error "Callback support requires CONFIG_SCHED_WORKQUEUE and CONFIG_SCHED_HPWORK"
+#endif
+
+#if !defined(CONFIG_SDIO_BLOCKSETUP)
+# error "CONFIG_SDIO_BLOCKSETUP is mandatory for this driver"
+#endif
+
+#ifndef CONFIG_DEBUG_MEMCARD_INFO
+# undef CONFIG_SDIO_XFRDEBUG
+#endif
+
+/* Timing in ms for commands wait response */
+
+#define USDHC_CMDTIMEOUT MSEC2TICK(100)
+#define USDHC_LONGTIMEOUT MSEC2TICK(500)
+
+/* Big DTOCV setting.
+ * 1101 - SDCLK x 2 29,
+ * recommend to use for supported speed modes
+ * except HS200, HS400, SDR104 mode
+ *
+ * 1110 - SDCLK x 2 30, recommend to use for HS200 and SDR104 mode
+ */
+
+#define USDHC_DTOCV_MAXTIMEOUT (13)
+
+/* Maximum watermark value */
+
+#define USDHC_MAX_WATERMARK 128
+
+/* Block size for multi-block transfers */
+
+#define SDMMC_MAX_BLOCK_SIZE (512)
+
+/* Data transfer / Event waiting interrupt mask bits */
+
+#define USDHC_RESPERR_INTS (USDHC_INT_CCE | USDHC_INT_CTOE | \
+ USDHC_INT_CEBE | USDHC_INT_CIE)
+#define USDHC_RESPDONE_INTS (USDHC_RESPERR_INTS | USDHC_INT_CC)
+
+#define USDHC_XFRERR_INTS (USDHC_INT_DCE | USDHC_INT_DTOE | \
+ USDHC_INT_DEBE)
+#define USDHC_RCVDONE_INTS (USDHC_XFRERR_INTS | USDHC_INT_BRR | \
+ USDHC_INT_TC)
+#define USDHC_SNDDONE_INTS (USDHC_XFRERR_INTS | USDHC_INT_BWR | \
+ USDHC_INT_TC)
+#define USDHC_XFRDONE_INTS (USDHC_XFRERR_INTS | USDHC_INT_BRR | \
+ USDHC_INT_BWR | USDHC_INT_TC)
+
+/* CD Detect Types */
+
+/* For DMA operations DINT is not interesting TC will indicate completions */
+
+#define USDHC_DMAERR_INTS (USDHC_XFRERR_INTS | USDHC_INT_DMAE)
+#define USDHC_DMADONE_INTS (USDHC_DMAERR_INTS | USDHC_INT_TC)
+
+#define USDHC_WAITALL_INTS (USDHC_RESPDONE_INTS | \
+ USDHC_XFRDONE_INTS | \
+ USDHC_DMADONE_INTS)
+
+/* Register logging support */
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+# if defined(CONFIG_IMX9_USDHC1)
+# define DBG_BASE_ADDR IMX9_USDHC1_BASE
+# else
+# define DBG_BASE_ADDR IMX9_USDHC2_BASE
+# endif
+# define SAMPLENDX_BEFORE_SETUP 0
+# define SAMPLENDX_AFTER_SETUP 1
+# define SAMPLENDX_END_TRANSFER 2
+# define DEBUG_NSAMPLES 3
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure defines the state of the Imx9 SDIO interface */
+
+struct imx9_dev_s
+{
+ struct sdio_dev_s dev; /* Standard, base SDIO interface */
+
+ /* Imx9-specific extensions */
+
+ /* Event support */
+
+ sem_t waitsem; /* Implements event waiting */
+ sdio_eventset_t waitevents; /* Set of events to be waited for */
+ uint32_t waitints; /* Interrupt enables for event waiting */
+ volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
+ struct wdog_s waitwdog; /* Watchdog that handles event timeouts */
+
+ /* Callback support */
+
+ sdio_statset_t cdstatus; /* Card status */
+ sdio_eventset_t cbevents; /* Set of events to be cause callbacks */
+ worker_t callback; /* Registered callback function */
+ void *cbarg; /* Registered callback argument */
+ struct work_s cbwork; /* Callback work queue structure */
+
+ /* Interrupt mode data transfer support */
+
+ uint32_t *buffer; /* Address of current R/W buffer */
+ size_t remaining; /* Number of bytes remaining in the
+ * transfer */
+ uint32_t xfrints; /* Interrupt enables for data transfer */
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+ /* DMA data transfer support */
+
+ volatile uint8_t xfrflags; /* Used to synchronize SDIO and DMA
+ * completion */
+ /* DMA buffer for unaligned transfers */
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ uint32_t blocksize; /* Current block size */
+ uint8_t rxbuffer[SDMMC_MAX_BLOCK_SIZE]
+ __attribute__((aligned(ARMV8A_DCACHE_LINESIZE)));
+ bool unaligned_rx; /* buffer is not cache-line aligned */
+#endif
+#endif
+
+ /* Card interrupt support for SDIO */
+
+ uint32_t cintints; /* Interrupt enables for card ints */
+ int (*do_sdio_card)(void *); /* SDIO card ISR */
+ void *do_sdio_arg; /* arg for SDIO card ISR */
+
+ uint32_t addr; /* Base address of this instances */
+ uint32_t sw_cd_gpio; /* If a non USDHCx CD pin is used,
+ * this is its GPIO */
+ uint32_t cd_invert; /* If true invert the CD pin */
+};
+
+/* Register logging support */
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+struct imx9_sdhcregs_s
+{
+ /* All read-able USDHC registers */
+
+ uint32_t dsaddr; /* DMA System Address Register */
+ uint32_t blkattr; /* Block Attributes Register */
+ uint32_t cmdarg; /* Command Argument Register */
+ uint32_t xferty; /* Transfer Type Register */
+ uint32_t cmdrsp0; /* Command Response 0 */
+ uint32_t cmdrsp1; /* Command Response 1 */
+ uint32_t cmdrsp2; /* Command Response 2 */
+ uint32_t cmdrsp3; /* Command Response 3 */
+ uint32_t dbap; /* Data buffer access port */
+ uint32_t prsstat; /* Present State Register */
+ uint32_t proctl; /* Protocol Control Register */
+ uint32_t sysctl; /* System Control Register */
+ uint32_t irqstat; /* Interrupt Status Register */
+ uint32_t irqstaten; /* Interrupt Status Enable Register */
+ uint32_t irqsigen; /* Interrupt Signal Enable Register */
+ uint32_t ac12err; /* Auto CMD12 Error Status Register */
+ uint32_t htcapblt; /* Host Controller Capabilities */
+ uint32_t wml; /* Watermark Level Register */
+ uint32_t mixctrl; /* Mixer Control */
+ uint32_t fevent; /* Force Event */
+ uint32_t admaes; /* ADMA Error Status Register */
+ uint32_t adsaddr; /* ADMA System Address Register */
+ uint32_t dllctrl; /* Delay line control */
+ uint32_t dllstat; /* Delay line status */
+ uint32_t clktune; /* Clock tune and control */
+ uint32_t vendor; /* Vendor Specific Register */
+ uint32_t mmcboot; /* MMC Boot Register */
+ uint32_t vendor2; /* Vendor Specific Register 2 */
+ uint32_t tuningctrl; /* Tuning Control */
+};
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level helpers ********************************************************/
+
+static void imx9_configwaitints(struct imx9_dev_s *priv, uint32_t waitints,
+ sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void imx9_configxfrints(struct imx9_dev_s *priv, uint32_t xfrints);
+
+/* DMA Helpers **************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_sampleinit(void);
+static void imx9_sdhcsample(struct imx9_sdhcregs_s *regs);
+static void imx9_sample(struct imx9_dev_s *priv, int index);
+static void imx9_dumpsample(struct imx9_dev_s *priv,
+ struct imx9_sdhcregs_s *regs, const char *msg);
+static void imx9_dumpsamples(struct imx9_dev_s *priv);
+static void imx9_showregs(struct imx9_dev_s *priv, const char *msg);
+
+#else
+# define imx9_sampleinit()
+# define imx9_sample(priv, index)
+# define imx9_dumpsamples(priv)
+# define imx9_showregs(priv, msg)
+#endif
+
+/* Data Transfer Helpers ****************************************************/
+
+static void imx9_dataconfig(struct imx9_dev_s *priv, bool bwrite,
+ unsigned int datalen, unsigned int timeout);
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static void imx9_transmit(struct imx9_dev_s *priv);
+static void imx9_receive(struct imx9_dev_s *priv);
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+static void imx9_recvdma(struct imx9_dev_s *priv);
+#endif
+#endif
+
+static void imx9_eventtimeout(wdparm_t arg);
+static void imx9_endwait(struct imx9_dev_s *priv,
+ sdio_eventset_t wkupevent);
+static void imx9_endtransfer(struct imx9_dev_s *priv,
+ sdio_eventset_t wkupevent);
+
+/* Interrupt Handling *******************************************************/
+
+static int imx9_interrupt(int irq, void *context, void *arg);
+
+/* SDIO interface methods ***************************************************/
+
+/* Mutual exclusion */
+
+#ifdef CONFIG_SDIO_MUXBUS
+static int imx9_lock(struct sdio_dev_s *dev, bool lock);
+#endif
+
+/* Initialization/setup */
+
+static void imx9_reset(struct sdio_dev_s *dev);
+static sdio_capset_t imx9_capabilities(struct sdio_dev_s *dev);
+static sdio_statset_t imx9_status(struct sdio_dev_s *dev);
+static void imx9_widebus(struct sdio_dev_s *dev, bool enable);
+
+#ifdef CONFIG_IMX9_USDHC_ABSFREQ
+static void imx9_frequency(struct sdio_dev_s *dev, uint32_t frequency);
+#endif
+
+static void imx9_clock(struct sdio_dev_s *dev, enum sdio_clock_e rate);
+static int imx9_attach(struct sdio_dev_s *dev);
+
+/* Command/Status/Data Transfer */
+
+static int imx9_sendcmd(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t arg);
+
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void imx9_blocksetup(struct sdio_dev_s *dev,
+ unsigned int blocklen, unsigned int nblocks);
+#endif
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static int imx9_recvsetup(struct sdio_dev_s *dev, uint8_t *buffer,
+ size_t nbytes);
+static int imx9_sendsetup(struct sdio_dev_s *dev,
+ const uint8_t *buffer, size_t nbytes);
+#endif
+
+static int imx9_cancel(struct sdio_dev_s *dev);
+static int imx9_waitresponse(struct sdio_dev_s *dev, uint32_t cmd);
+static int imx9_recvshortcrc(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort);
+static int imx9_recvlong(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t rlong[4]);
+static int imx9_recvshort(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort);
+
+/* EVENT handler */
+
+static void imx9_waitenable(struct sdio_dev_s *dev,
+ sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t imx9_eventwait(struct sdio_dev_s *dev);
+static void imx9_callbackenable(struct sdio_dev_s *dev,
+ sdio_eventset_t eventset);
+static int imx9_registercallback(struct sdio_dev_s *dev,
+ worker_t callback, void *arg);
+
+/* DMA */
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
+ uint8_t *buffer, size_t buflen);
+static int imx9_dmasendsetup(struct sdio_dev_s *dev,
+ const uint8_t *buffer, size_t buflen);
+#endif
+
+/* Initialization/uninitialization/reset ************************************/
+
+static void imx9_callback(void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+struct imx9_dev_s g_sdhcdev[IMX9_MAX_SDHC_DEV_SLOTS] =
+{
+#ifdef CONFIG_IMX9_USDHC1
+ {
+ .addr = IMX9_USDHC1_BASE,
+#if defined(PIN_USDHC1_CD_GPIO)
+ .sw_cd_gpio = PIN_USDHC1_CD_GPIO,
+#endif
+#if defined(CONFIG_IMX9_USDHC1_INVERT_CD)
+ .cd_invert = true,
+#endif
+ .dev =
+ {
+#ifdef CONFIG_SDIO_MUXBUS
+ .lock = imx9_lock,
+#endif
+ .reset = imx9_reset,
+ .capabilities = imx9_capabilities,
+ .status = imx9_status,
+ .widebus = imx9_widebus,
+ .clock = imx9_clock,
+ .attach = imx9_attach,
+ .sendcmd = imx9_sendcmd,
+#ifdef CONFIG_SDIO_BLOCKSETUP
+ .blocksetup = imx9_blocksetup,
+#endif
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+ .recvsetup = imx9_recvsetup,
+ .sendsetup = imx9_sendsetup,
+#else
+ .recvsetup = imx9_dmarecvsetup,
+ .sendsetup = imx9_dmasendsetup,
+#endif
+ .cancel = imx9_cancel,
+ .waitresponse = imx9_waitresponse,
+ .recv_r1 = imx9_recvshortcrc,
+ .recv_r2 = imx9_recvlong,
+ .recv_r3 = imx9_recvshort,
+ .recv_r4 = imx9_recvshort,
+ .recv_r5 = imx9_recvshortcrc,
+ .recv_r6 = imx9_recvshortcrc,
+ .recv_r7 = imx9_recvshort,
+ .waitenable = imx9_waitenable,
+ .eventwait = imx9_eventwait,
+ .callbackenable = imx9_callbackenable,
+ .registercallback = imx9_registercallback,
+#ifdef CONFIG_SDIO_DMA
+#ifdef CONFIG_IMX9_USDHC_DMA
+ .dmarecvsetup = imx9_dmarecvsetup,
+ .dmasendsetup = imx9_dmasendsetup,
+#else
+ .dmarecvsetup = imx9_recvsetup,
+ .dmasendsetup = imx9_sendsetup,
+#endif
+#endif
+ },
+ .waitsem = SEM_INITIALIZER(0),
+ },
+#endif
+
+#ifdef CONFIG_IMX9_USDHC2
+ {
+ .addr = IMX9_USDHC2_BASE,
+#if defined(PIN_USDHC2_CD_GPIO)
+ .sw_cd_gpio = PIN_USDHC2_CD_GPIO,
+#endif
+#if defined(CONFIG_IMX9_USDHC2_INVERT_CD)
+ .cd_invert = true,
+#endif
+ .dev =
+ {
+#ifdef CONFIG_SDIO_MUXBUS
+ .lock = imx9_lock,
+#endif
+ .reset = imx9_reset,
+ .capabilities = imx9_capabilities,
+ .status = imx9_status,
+ .widebus = imx9_widebus,
+ .clock = imx9_clock,
+ .attach = imx9_attach,
+ .sendcmd = imx9_sendcmd,
+#ifdef CONFIG_SDIO_BLOCKSETUP
+ .blocksetup = imx9_blocksetup,
+#endif
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+ .recvsetup = imx9_recvsetup,
+ .sendsetup = imx9_sendsetup,
+#else
+ .recvsetup = imx9_dmarecvsetup,
+ .sendsetup = imx9_dmasendsetup,
+#endif
+ .cancel = imx9_cancel,
+ .waitresponse = imx9_waitresponse,
+ .recv_r1 = imx9_recvshortcrc,
+ .recv_r2 = imx9_recvlong,
+ .recv_r3 = imx9_recvshort,
+ .recv_r4 = imx9_recvshort,
+ .recv_r5 = imx9_recvshortcrc,
+ .recv_r6 = imx9_recvshortcrc,
+ .recv_r7 = imx9_recvshort,
+ .waitenable = imx9_waitenable,
+ .eventwait = imx9_eventwait,
+ .callbackenable = imx9_callbackenable,
+ .registercallback = imx9_registercallback,
+#ifdef CONFIG_SDIO_DMA
+#ifdef CONFIG_IMX9_USDHC_DMA
+ .dmarecvsetup = imx9_dmarecvsetup,
+ .dmasendsetup = imx9_dmasendsetup,
+#else
+ .dmarecvsetup = imx9_recvsetup,
+ .dmasendsetup = imx9_sendsetup,
+#endif
+#endif
+ },
+ .waitsem = SEM_INITIALIZER(0),
+ }
+#endif
+};
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+/* Register logging support */
+
+static struct imx9_sdhcregs_s g_sampleregs[DEBUG_NSAMPLES];
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_configwaitints
+ *
+ * Description:
+ * Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ * priv - A reference to the SDIO device state structure
+ * waitints - The set of bits in the SDIO MASK register to set
+ * waitevents - Waited for events
+ * wkupevent - Wake-up events
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_configwaitints(struct imx9_dev_s *priv, uint32_t waitints,
+ sdio_eventset_t waitevents,
+ sdio_eventset_t wkupevent)
+{
+ irqstate_t flags;
+
+ /* Save all of the data and set the new interrupt mask in one, atomic
+ * operation.
+ */
+
+ flags = enter_critical_section();
+ priv->waitevents = waitevents;
+ priv->wkupevent = wkupevent;
+ priv->waitints = waitints;
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+ priv->xfrflags = 0;
+#endif
+ putreg32(priv->xfrints | priv->waitints | priv->cintints,
+ priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: imx9_configxfrints
+ *
+ * Description:
+ * Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ * priv - A reference to the SDIO device state structure
+ * xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_configxfrints(struct imx9_dev_s *priv, uint32_t xfrints)
+{
+ irqstate_t flags;
+
+ flags = enter_critical_section();
+ priv->xfrints = xfrints;
+ putreg32(priv->xfrints | priv->waitints | priv->cintints,
+ priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: imx9_sampleinit
+ *
+ * Description:
+ * Setup prior to collecting DMA samples
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_sampleinit(void)
+{
+ memset(g_sampleregs, 0xff,
+ DEBUG_NSAMPLES * sizeof(struct imx9_sdhcregs_s));
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_sdhcsample
+ *
+ * Description:
+ * Sample SDIO registers
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_sdhcsample(struct imx9_sdhcregs_s *regs)
+{
+ regs->dsaddr = getreg32(DBG_BASE_ADDR + IMX9_USDHC_DSADDR_OFFSET);
+ regs->blkattr = getreg32(DBG_BASE_ADDR + IMX9_USDHC_BLKATTR_OFFSET);
+ regs->cmdarg = getreg32(DBG_BASE_ADDR + IMX9_USDHC_CMDARG_OFFSET);
+ regs->xferty = getreg32(DBG_BASE_ADDR + IMX9_USDHC_XFERTYP_OFFSET);
+ regs->cmdrsp0 = getreg32(DBG_BASE_ADDR + IMX9_USDHC_CMDRSP0_OFFSET);
+ regs->cmdrsp1 = getreg32(DBG_BASE_ADDR + IMX9_USDHC_CMDRSP1_OFFSET);
+ regs->cmdrsp2 = getreg32(DBG_BASE_ADDR + IMX9_USDHC_CMDRSP2_OFFSET);
+ regs->cmdrsp3 = getreg32(DBG_BASE_ADDR + IMX9_USDHC_CMDRSP3_OFFSET);
+ regs->prsstat = getreg32(DBG_BASE_ADDR + IMX9_USDHC_PRSSTAT_OFFSET);
+ regs->proctl = getreg32(DBG_BASE_ADDR + IMX9_USDHC_PROCTL_OFFSET);
+ regs->sysctl = getreg32(DBG_BASE_ADDR + IMX9_USDHC_SYSCTL_OFFSET);
+ regs->irqstat = getreg32(DBG_BASE_ADDR + IMX9_USDHC_IRQSTAT_OFFSET);
+ regs->irqstaten = getreg32(DBG_BASE_ADDR + IMX9_USDHC_IRQSTATEN_OFFSET);
+ regs->irqsigen = getreg32(DBG_BASE_ADDR + IMX9_USDHC_IRQSIGEN_OFFSET);
+ regs->ac12err = getreg32(DBG_BASE_ADDR + IMX9_USDHC_AC12ERR_OFFSET);
+ regs->htcapblt = getreg32(DBG_BASE_ADDR + IMX9_USDHC_HTCAPBLT_OFFSET);
+ regs->wml = getreg32(DBG_BASE_ADDR + IMX9_USDHC_WML_OFFSET);
+ regs->admaes = getreg32(DBG_BASE_ADDR + IMX9_USDHC_ADMAES_OFFSET);
+ regs->adsaddr = getreg32(DBG_BASE_ADDR + IMX9_USDHC_ADSADDR_OFFSET);
+ regs->vendor = getreg32(DBG_BASE_ADDR + IMX9_USDHC_VENDOR_OFFSET);
+ regs->vendor2 = getreg32(DBG_BASE_ADDR + IMX9_USDHC_VENDOR2_OFFSET);
+ regs->mmcboot = getreg32(DBG_BASE_ADDR + IMX9_USDHC_MMCBOOT_OFFSET);
+ regs->mixctrl = getreg32(DBG_BASE_ADDR + IMX9_USDHC_MIX_OFFSET);
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_sample
+ *
+ * Description:
+ * Sample SDIO/DMA registers
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_sample(struct imx9_dev_s *priv, int index)
+{
+ if (priv->addr == DBG_BASE_ADDR)
+ {
+ imx9_sdhcsample(&g_sampleregs[index]);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_dumpsample
+ *
+ * Description:
+ * Dump one register sample
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_dumpsample(struct imx9_dev_s *priv,
+ struct imx9_sdhcregs_s *regs, const char *msg)
+{
+ mcinfo("USDHC Registers: %s\n", msg);
+ mcinfo(" DSADDR[%08x]: %08x\n",
+ IMX9_USDHC_DSADDR_OFFSET, regs->dsaddr);
+ mcinfo(" BLKATTR[%08x]: %08x\n",
+ IMX9_USDHC_BLKATTR_OFFSET, regs->blkattr);
+ mcinfo(" CMDARG[%08x]: %08x\n",
+ IMX9_USDHC_CMDARG_OFFSET, regs->cmdarg);
+ mcinfo(" XFERTY[%08x]: %08x\n",
+ IMX9_USDHC_XFERTYP_OFFSET, regs->xferty);
+ mcinfo(" CMDRSP0[%08x]: %08x\n",
+ IMX9_USDHC_CMDRSP0_OFFSET, regs->cmdrsp0);
+ mcinfo(" CMDRSP1[%08x]: %08x\n",
+ IMX9_USDHC_CMDRSP1_OFFSET, regs->cmdrsp1);
+ mcinfo(" CMDRSP2[%08x]: %08x\n",
+ IMX9_USDHC_CMDRSP2_OFFSET, regs->cmdrsp2);
+ mcinfo(" CMDRSP3[%08x]: %08x\n",
+ IMX9_USDHC_CMDRSP3_OFFSET, regs->cmdrsp3);
+ mcinfo(" PRSSTAT[%08x]: %08x\n",
+ IMX9_USDHC_PRSSTAT_OFFSET, regs->prsstat);
+ mcinfo(" PROCTL[%08x]: %08x\n",
+ IMX9_USDHC_PROCTL_OFFSET, regs->proctl);
+ mcinfo(" SYSCTL[%08x]: %08x\n",
+ IMX9_USDHC_SYSCTL_OFFSET, regs->sysctl);
+ mcinfo(" IRQSTAT[%08x]: %08x\n",
+ IMX9_USDHC_IRQSTAT_OFFSET, regs->irqstat);
+ mcinfo("IRQSTATEN[%08x]: %08x\n",
+ IMX9_USDHC_IRQSTATEN_OFFSET, regs->irqstaten);
+ mcinfo(" IRQSIGEN[%08x]: %08x\n",
+ IMX9_USDHC_IRQSIGEN_OFFSET, regs->irqsigen);
+ mcinfo(" AC12ERR[%08x]: %08x\n",
+ IMX9_USDHC_AC12ERR_OFFSET, regs->ac12err);
+ mcinfo(" HTCAPBLT[%08x]: %08x\n",
+ IMX9_USDHC_HTCAPBLT_OFFSET, regs->htcapblt);
+ mcinfo(" WML[%08x]: %08x\n",
+ IMX9_USDHC_WML_OFFSET, regs->wml);
+ mcinfo(" MIX[%08x]: %08x\n",
+ IMX9_USDHC_MIX_OFFSET, regs->mixctrl);
+ mcinfo(" ADMAES[%08x]: %08x\n",
+ IMX9_USDHC_ADMAES_OFFSET, regs->admaes);
+ mcinfo(" ADSADDR[%08x]: %08x\n",
+ IMX9_USDHC_ADSADDR_OFFSET, regs->adsaddr);
+ mcinfo(" VENDOR[%08x]: %08x\n",
+ IMX9_USDHC_VENDOR_OFFSET, regs->vendor);
+ mcinfo(" VENDOR2[%08x]: %08x\n",
+ IMX9_USDHC_VENDOR2_OFFSET, regs->vendor2);
+ mcinfo(" MMCBOOT[%08x]: %08x\n",
+ IMX9_USDHC_MMCBOOT_OFFSET, regs->mmcboot);
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_dumpsamples
+ *
+ * Description:
+ * Dump all sampled register data
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_dumpsamples(struct imx9_dev_s *priv)
+{
+ imx9_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP],
+ "Before setup");
+ imx9_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP],
+ "After setup");
+ imx9_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER],
+ "End of transfer");
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_showregs
+ *
+ * Description:
+ * Dump the current state of all registers
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_XFRDEBUG
+static void imx9_showregs(struct imx9_dev_s *priv, const char *msg)
+{
+ struct imx9_sdhcregs_s regs;
+
+ imx9_sdhcsample(®s);
+ imx9_dumpsample(priv, ®s, msg);
+}
+#endif
+
+/****************************************************************************
+ * Data Transfer Helpers
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_dataconfig
+ *
+ * Description:
+ * Configure the SDIO data path for the next data transfer
+ *
+ ****************************************************************************/
+
+static void imx9_dataconfig(struct imx9_dev_s *priv, bool bwrite,
+ unsigned int datalen, unsigned int timeout)
+{
+ unsigned int watermark;
+ uint32_t regval = 0;
+
+ /* Set the data timeout value in the USDHC_SYSCTL field to the selected
+ * value.
+ */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+ regval &= ~USDHC_SYSCTL_DTOCV_MASK;
+ regval |= timeout << USDHC_SYSCTL_DTOCV_SHIFT;
+ putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+
+#if defined(CONFIG_IMX9_USDHC_DMA) && !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ /* If cache is enabled, and this is an unaligned receive,
+ * receive one block at a time to the internal buffer
+ */
+
+ if (!bwrite && priv->unaligned_rx)
+ {
+ DEBUGASSERT(priv->blocksize <= sizeof(priv->rxbuffer));
+ datalen = priv->blocksize;
+ }
+#endif
+
+ /* Set the watermark level */
+
+ /* Set the Read Watermark Level to the datalen to be read (limited to half
+ * of the maximum watermark value). BRR will be set when the number of
+ * queued words is greater than or equal to this value.
+ */
+
+ watermark = (datalen + 3) >> 2;
+ if (watermark > (USDHC_MAX_WATERMARK / 2))
+ {
+ watermark = (USDHC_MAX_WATERMARK / 2);
+ }
+
+ /* When the watermark level requirement is met in data transfer, and the
+ * internal DMA is enabled, the data buffer block sends a DMA request to
+ * the crossbar switch interface.
+ */
+
+ if (bwrite)
+ {
+ /* The USDHC will not start data transmission until the number of
+ * words set in the WML register can be held in the buffer. If the
+ * buffer is empty and the host system does not write data in time,
+ * the USDHC will stop the SD_CLK to avoid the data buffer under-run
+ * situation.
+ */
+
+ putreg32(watermark << USDHC_WML_WR_SHIFT,
+ priv->addr + IMX9_USDHC_WML_OFFSET);
+ }
+ else
+ {
+ /* The USDHC will not start data transmission until the number of words
+ * set in the WML register are in the buffer. If the buffer is full and
+ * the Host System does not read data in time, the USDHC will stop the
+ * USDHC_DCLK to avoid the data buffer over-run situation.
+ */
+
+ putreg32(watermark << USDHC_WML_RD_SHIFT,
+ priv->addr + IMX9_USDHC_WML_OFFSET);
+ }
+}
+
+/****************************************************************************
+ * Name: imx9_transmit
+ *
+ * Description:
+ * Send SDIO data in interrupt mode
+ *
+ * Input Parameters:
+ * priv - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static void imx9_transmit(struct imx9_dev_s *priv)
+{
+ union
+ {
+ uint32_t w;
+ uint8_t b[4];
+ } data;
+
+ /* Loop while there is more data to be sent, while buffer write enable
+ * (PRSSTAT.BWEN)
+ */
+
+ mcinfo("Entry: remaining: %lu IRQSTAT: %08x\n", priv->remaining,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET));
+
+ while (priv->remaining > 0 &&
+ (getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET) &
+ USDHC_PRSSTAT_BWEN) != 0)
+ {
+ /* Is there a full word remaining in the user buffer? */
+
+ if (priv->remaining >= sizeof(uint32_t))
+ {
+ /* Yes, transfer the word to the TX FIFO */
+
+ data.w = *priv->buffer++;
+ priv->remaining -= sizeof(uint32_t);
+ }
+ else
+ {
+ /* No.. transfer just the bytes remaining in the user buffer,
+ * padding with zero as necessary to extend to a full word.
+ */
+
+ uint8_t *ptr = (uint8_t *)priv->remaining;
+ int i;
+
+ data.w = 0;
+ for (i = 0; i < priv->remaining; i++)
+ {
+ data.b[i] = *ptr++;
+ }
+
+ /* Now the transfer is finished */
+
+ priv->remaining = 0;
+ }
+
+ /* Put the word in the FIFO */
+
+ putreg32(data.w, priv->addr + IMX9_USDHC_DATAPORT_OFFSET);
+ }
+
+ /* Clear BWR. If there is more data in the buffer, writing to the buffer
+ * should reset BWR.
+ */
+
+ putreg32(USDHC_INT_BWR, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ mcinfo("Exit: remaining: %lu IRQSTAT: %08x\n", priv->remaining,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_receive
+ *
+ * Description:
+ * Receive SDIO data in interrupt mode
+ *
+ * Input Parameters:
+ * priv - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static void imx9_receive(struct imx9_dev_s *priv)
+{
+ unsigned int watermark;
+ union
+ {
+ uint32_t w;
+ uint8_t b[4];
+ } data;
+
+ /* Set the Read Watermark Level to 1: BRR will be set when the number of
+ * queued words is greater than or equal to 1.
+ */
+
+ putreg32(1 << USDHC_WML_RD_SHIFT, priv->addr + IMX9_USDHC_WML_OFFSET);
+
+ /* Loop while there is space to store the data, waiting for buffer
+ * read ready (BRR)
+ */
+
+ mcinfo("Entry: remaining: %lu IRQSTAT: %08x\n", priv->remaining,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET));
+
+ while (priv->remaining > 0 &&
+ (getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET) &
+ USDHC_INT_BRR) != 0)
+ {
+ /* Clear BRR. If there is more data in the buffer, reading from the
+ * buffer should reset BRR.
+ */
+
+ putreg32(USDHC_INT_BRR, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ /* Read the next word from the RX buffer */
+
+ data.w = getreg32(priv->addr + IMX9_USDHC_DATAPORT_OFFSET);
+ if (priv->remaining >= sizeof(uint32_t))
+ {
+ /* Transfer the whole word to the user buffer */
+
+ *priv->buffer++ = data.w;
+ priv->remaining -= sizeof(uint32_t);
+ }
+ else
+ {
+ /* Transfer any trailing fractional word */
+
+ uint8_t *ptr = (uint8_t *)priv->buffer;
+ int i;
+
+ for (i = 0; i < priv->remaining; i++)
+ {
+ *ptr++ = data.b[i];
+ }
+
+ /* Now the transfer is finished */
+
+ priv->remaining = 0;
+ }
+ }
+
+ /* Set the Read Watermark Level either the number of remaining words to be
+ * read (limited to half of the maximum watermark value)
+ */
+
+ watermark = ((priv->remaining + 3) >> 2);
+ if (watermark > (USDHC_MAX_WATERMARK / 2))
+ {
+ watermark = (USDHC_MAX_WATERMARK / 2);
+ }
+
+ putreg32(watermark << USDHC_WML_RD_SHIFT,
+ priv->addr + IMX9_USDHC_WML_OFFSET);
+
+ mcinfo("Exit: remaining: %lu IRQSTAT: %08x WML: %08x\n", priv->remaining,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET),
+ getreg32(priv->addr + IMX9_USDHC_WML_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_recvdma
+ *
+ * Description:
+ * Receive SDIO data in dma mode
+ *
+ * Input Parameters:
+ * priv - Instance of the SDMMC private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_IMX9_USDHC_DMA) && !defined(CONFIG_ARM64_DCACHE_DISABLE)
+static void imx9_recvdma(struct imx9_dev_s *priv)
+{
+ unsigned int watermark;
+
+ if (priv->unaligned_rx)
+ {
+ /* If we are receiving multiple blocks to an unaligned buffers,
+ * we receive them one-by-one
+ */
+
+ /* Invalidate the cache before receiving next block */
+
+ up_invalidate_dcache((uintptr_t)priv->rxbuffer,
+ (uintptr_t)priv->rxbuffer + priv->blocksize);
+
+ /* Copy the received data to client buffer */
+
+ memcpy(priv->buffer, priv->rxbuffer, priv->blocksize);
+
+ /* Update how much there is left to receive */
+
+ priv->remaining -= priv->blocksize;
+ }
+ else
+ {
+ /* In an aligned case, we have always received all blocks */
+
+ priv->remaining = 0;
+ }
+
+ if (priv->remaining == 0)
+ {
+ /* no data remaining, end the transfer */
+
+ imx9_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ else
+ {
+ /* We end up here only in unaligned rx-buffers case, and are receiving
+ * the data one block at a time
+ */
+
+ /* Update where to receive the following block */
+
+ priv->buffer = (uint32_t *)((uintptr_t)priv->buffer + priv->blocksize);
+
+ watermark = (priv->blocksize + 3) >> 2;
+ if (watermark > (USDHC_MAX_WATERMARK / 2))
+ {
+ watermark = (USDHC_MAX_WATERMARK / 2);
+ }
+
+ /* Re-enable datapath and wait for next block */
+
+ putreg32(watermark << USDHC_WML_RD_SHIFT,
+ priv->addr + IMX9_USDHC_WML_OFFSET);
+ }
+}
+
+#endif
+/****************************************************************************
+ * Name: imx9_eventtimeout
+ *
+ * Description:
+ * The watchdog timeout setup when the event wait start has expired without
+ * any other waited-for event occurring.
+ *
+ * Input Parameters:
+ * arg - The argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void imx9_eventtimeout(wdparm_t arg)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)arg;
+
+ DEBUGASSERT(priv != NULL);
+ DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0);
+
+ /* Is a data transfer complete event expected? */
+
+ if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+ {
+ /* Yes.. Sample registers at the time of the timeout */
+
+ imx9_sample(priv, SAMPLENDX_END_TRANSFER);
+
+ /* Wake up any waiting threads */
+
+ imx9_endwait(priv, SDIOWAIT_TIMEOUT);
+ mcerr("ERROR: Timeout: remaining: %lu\n", priv->remaining);
+ }
+}
+
+/****************************************************************************
+ * Name: imx9_endwait
+ *
+ * Description:
+ * Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ * priv - An instance of the SDIO device interface
+ * wkupevent - The event that caused the wait to end
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void imx9_endwait(struct imx9_dev_s *priv,
+ sdio_eventset_t wkupevent)
+{
+ /* Cancel the watchdog timeout */
+
+ wd_cancel(&priv->waitwdog);
+
+ /* Disable event-related interrupts */
+
+ imx9_configwaitints(priv, 0, 0, wkupevent);
+
+ /* Wake up the waiting thread */
+
+ nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: imx9_endtransfer
+ *
+ * Description:
+ * Terminate a transfer with the provided status. This function is called
+ * only from the SDIO interrupt handler when end-of-transfer conditions
+ * are detected.
+ *
+ * Input Parameters:
+ * priv - An instance of the SDIO device interface
+ * wkupevent - The event that caused the transfer to end
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void imx9_endtransfer(struct imx9_dev_s *priv,
+ sdio_eventset_t wkupevent)
+{
+ /* Disable all transfer related interrupts */
+
+ imx9_configxfrints(priv, 0);
+
+ /* Clearing pending interrupt status on all transfer related interrupts */
+
+ putreg32(USDHC_XFRDONE_INTS | USDHC_DMADONE_INTS,
+ priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ /* Mark the transfer finished */
+
+ priv->remaining = 0;
+
+ /* Debug instrumentation */
+
+ imx9_sample(priv, SAMPLENDX_END_TRANSFER);
+
+ /* Is a thread wait for these data transfer complete events? */
+
+ if ((priv->waitevents & wkupevent) != 0)
+ {
+ /* Yes.. wake up any waiting threads */
+
+ imx9_endwait(priv, wkupevent);
+ }
+}
+
+/****************************************************************************
+ * Name: imx9_interrupt
+ *
+ * Description:
+ * SDIO interrupt handler
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int imx9_interrupt(int irq, void *context, void *arg)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)arg;
+ uint32_t enabled;
+ uint32_t pending;
+ uint32_t regval;
+
+ /* Check the USDHC IRQSTAT register. Mask out all bits that don't
+ * correspond to enabled interrupts. (This depends on the fact that bits
+ * are ordered the same in both the IRQSTAT and IRQSIGEN registers).
+ * If there are non-zero bits remaining, then we have work to do here.
+ */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ enabled = getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET) & regval;
+
+ mcinfo("IRQSTAT: %08" PRIx32 " IRQSIGEN %08" PRIx32
+ " enabled: %08" PRIx32 "\n",
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET), regval, enabled);
+
+ /* Clear all pending interrupts */
+
+ putreg32(enabled, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ /* Handle in progress, interrupt driven data transfers ********************/
+
+ pending = enabled & priv->xfrints;
+ if (pending != 0)
+ {
+#ifndef CONFIG_IMX9_USDHC_DMA
+ /* Is the RX buffer read ready? Is so then we must be processing a
+ * non-DMA receive transaction.
+ */
+
+ if ((pending & USDHC_INT_BRR) != 0)
+ {
+ /* Receive data from the RX buffer */
+
+ imx9_receive(priv);
+ }
+
+ /* Otherwise, Is the TX buffer write ready? If so we must be processing
+ * non-DMA send transaction. NOTE: We can't be processing both!
+ */
+
+ else if ((pending & USDHC_INT_BWR) != 0)
+ {
+ /* Send data via the TX FIFO */
+
+ imx9_transmit(priv);
+ }
+#endif
+
+ /* ... transfer complete events */
+
+ if ((pending & USDHC_INT_TC) != 0)
+ {
+ /* Terminate the transfer */
+#if defined(CONFIG_IMX9_USDHC_DMA) && !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ imx9_recvdma(priv);
+#else
+ imx9_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+#endif
+ }
+
+ /* ... data block send/receive CRC failure */
+
+ else if ((pending & USDHC_INT_DCE) != 0)
+ {
+ /* Terminate the transfer with an error */
+
+ mcerr("ERROR: Data block CRC failure, remaining: %lu\n",
+ priv->remaining);
+ imx9_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
+ }
+
+ /* ... data timeout error */
+
+ else if ((pending & USDHC_INT_DTOE) != 0)
+ {
+ /* Terminate the transfer with an error */
+
+ mcerr("ERROR: Data timeout, remaining: %lu\n", priv->remaining);
+ imx9_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT);
+ }
+ }
+
+ /* Handle Card interrupt events *******************************************/
+
+ pending = enabled & priv->cintints;
+ if ((pending & USDHC_INT_CINT) != 0)
+ {
+ if (priv->do_sdio_card)
+ {
+ (priv->do_sdio_card)(priv->do_sdio_arg);
+ }
+
+ /* We don't want any more ints now, so switch it off */
+
+ regval &= ~USDHC_INT_CINT;
+ priv->cintints = regval;
+ putreg32(regval, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ }
+
+ if ((pending & USDHC_INT_CINS) != 0 || (pending & USDHC_INT_CRM) != 0)
+ {
+ if (up_interrupt_context())
+ {
+ /* Yes.. queue it */
+
+ mcinfo("Queuing callback to %p(%p)\n",
+ priv->callback, priv->cbarg);
+
+ work_queue(HPWORK, &priv->cbwork, priv->callback,
+ priv->cbarg, 0);
+ }
+ else
+ {
+ /* No.. then just call the callback here */
+
+ mcinfo("Callback to %p(%p)\n", priv->callback, priv->cbarg);
+
+ priv->callback(priv->cbarg);
+ }
+ }
+
+ /* Handle wait events *****************************************************/
+
+ pending = enabled & priv->waitints;
+ if (pending != 0)
+ {
+ /* Is this a response completion event? */
+
+ if ((pending & USDHC_RESPDONE_INTS) != 0)
+ {
+ /* Yes.. Is there a thread waiting for response done? */
+
+ if ((priv->waitevents &
+ (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+ {
+ /* Yes.. mask further interrupts and wake the thread up */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ regval &= ~USDHC_RESPDONE_INTS;
+ putreg32(regval, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+
+ imx9_endwait(priv, SDIOWAIT_RESPONSEDONE);
+ }
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * SDIO Interface Methods
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_lock
+ *
+ * Description:
+ * Locks the bus. Function calls low-level multiplexed bus routines to
+ * resolve bus requests and acknowledgment issues.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * lock - TRUE to lock, FALSE to unlock.
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_MUXBUS
+static int imx9_lock(struct sdio_dev_s *dev, bool lock)
+{
+ /* The multiplex bus is part of board support package. */
+
+ /* FIXME: Implement the below function to support bus share:
+ *
+ * imx9_muxbus_sdio_lock((dev - g_sdhcdev) /
+ * sizeof(struct imx9_dev_s), lock);
+ */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_reset
+ *
+ * Description:
+ * Reset the SDIO controller. Undo all setup and initialization.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_reset(struct sdio_dev_s *dev)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ /* Disable all interrupts so that nothing interferes with the following. */
+
+ putreg32(0, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+
+ /* Reset the USDHC block, putting registers in their default, reset state.
+ * Initiate the reset by setting the RSTA bit in the SYSCTL register.
+ * USDHC_VS2_BUSRESET has not been documented in i.MX93 ref manual.
+ */
+
+ modifyreg32(priv->addr + IMX9_USDHC_VENDOR2_OFFSET, 0, USDHC_VS2_BUSRESET);
+ modifyreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET, 0, USDHC_SYSCTL_RSTA);
+ modifyreg32(priv->addr + IMX9_USDHC_VENDOR2_OFFSET,
+ USDHC_VS2_BUSRESET | USDHC_VS2_ACMD23ARGU2, 0);
+
+ /* The USDHC will reset the RSTA bit to 0 when the capabilities registers
+ * are valid and the host driver can read them.
+ */
+
+ while ((getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET) &
+ USDHC_SYSCTL_RSTA) != 0)
+ {
+ }
+
+ mcinfo("Reset complete\n");
+
+ /* Make sure that all clocking is disabled */
+
+ imx9_clock(dev, CLOCK_SDIO_DISABLED);
+
+ /* Enable all status bits (these could not all be potential sources of
+ * interrupts.
+ */
+
+ putreg32(USDHC_INT_ALL, priv->addr + IMX9_USDHC_IRQSTATEN_OFFSET);
+
+ mcinfo("SYSCTL: %08" PRIx32 " PRSSTAT: %08" PRIx32
+ " IRQSTATEN: %08" PRIx32 "\n",
+ getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET),
+ getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET),
+ getreg32(priv->addr + IMX9_USDHC_IRQSTATEN_OFFSET));
+
+ /* The next phase of the hardware reset would be to set the SYSCTRL INITA
+ * bit to send 80 clock ticks for card to power up and then reset the card
+ * with CMD0. This is done elsewhere.
+ */
+
+ /* Reset state data */
+
+ priv->waitevents = 0; /* Set of events to be waited for */
+ priv->waitints = 0; /* Interrupt enables for event waiting */
+ priv->wkupevent = 0; /* The event that caused the wakeup */
+#ifdef CONFIG_IMX9_USDHC_DMA
+ priv->xfrflags = 0; /* Used to synchronize SDIO and DMA completion */
+#endif
+
+ wd_cancel(&priv->waitwdog); /* Cancel any timeouts */
+
+ /* Interrupt mode data transfer support */
+
+ priv->buffer = 0; /* Address of current R/W buffer */
+ priv->remaining = 0; /* Number of bytes remaining in the transfer */
+ priv->xfrints = 0; /* Interrupt enables for data transfer */
+}
+
+/****************************************************************************
+ * Name: imx9_capabilities
+ *
+ * Description:
+ * Get capabilities (and limitations) of the SDIO driver (optional)
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ *
+ * Returned Value:
+ * Returns a bitset of status values (see SDIO_CAPS_* defines)
+ *
+ ****************************************************************************/
+
+static sdio_capset_t imx9_capabilities(struct sdio_dev_s *dev)
+{
+ sdio_capset_t caps = 0;
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ switch (priv->addr)
+ {
+ case IMX9_USDHC1_BASE:
+#ifdef CONFIG_IMX9_USDHC1_WIDTH_D1_ONLY
+ caps |= SDIO_CAPS_1BIT_ONLY;
+#endif
+#ifdef CONFIG_IMX9_USDHC1_WIDTH_D1_D4
+ caps |= SDIO_CAPS_4BIT;
+#endif
+#ifdef CONFIG_IMX9_USDHC1_WIDTH_D1_D8
+ caps |= SDIO_CAPS_8BIT;
+#endif
+ break;
+
+ case IMX9_USDHC2_BASE:
+#ifdef CONFIG_IMX9_USDHC2_WIDTH_D1_ONLY
+ caps |= SDIO_CAPS_1BIT_ONLY;
+#endif
+#ifdef CONFIG_IMX9_USDHC2_WIDTH_D1_D4
+ caps |= SDIO_CAPS_4BIT;
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+ caps |= SDIO_CAPS_DMASUPPORTED;
+#endif
+ caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+ return caps;
+}
+
+/****************************************************************************
+ * Name: imx9_status
+ *
+ * Description:
+ * Get SDIO status.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ *
+ * Returned Value:
+ * Returns a bitset of status values (see imx9_status_* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t imx9_status(struct sdio_dev_s *dev)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ bool present = false;
+
+ /* Board did not use one of the GPIO_USDHCn_CD pins
+ * but instead a GPIO was used and defined in board.h
+ * as PIN_USDHCx_CD_GPIO
+ */
+
+ if (priv->sw_cd_gpio != 0)
+ {
+ present = priv->cd_invert ^ !imx9_gpio_read(priv->sw_cd_gpio);
+ }
+ else
+ {
+ /* This register reflects the state of CD no matter if it's a separate pin
+ * or DAT3
+ */
+
+ present = ((getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET) &
+ USDHC_PRSSTAT_CINS) != 0) ^ priv->cd_invert;
+ }
+
+ if (present)
+ {
+ priv->cdstatus |= SDIO_STATUS_PRESENT;
+ }
+ else
+ {
+ priv->cdstatus &= ~SDIO_STATUS_PRESENT;
+ }
+
+ mcinfo("cdstatus=%02x\n", priv->cdstatus);
+
+ return priv->cdstatus;
+}
+
+/****************************************************************************
+ * Name: imx9_widebus
+ *
+ * Description:
+ * Called after change in Bus width has been selected (via ACMD6). Most
+ * controllers will need to perform some special operations to work
+ * correctly in the new bus mode.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_widebus(struct sdio_dev_s *dev, bool wide)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t regval;
+
+ /* Set the Data Transfer Width (DTW) field in the PROCTL register. */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_PROCTL_OFFSET);
+ regval &= ~USDHC_PROCTL_DTW_MASK;
+ if (wide)
+ {
+ regval |= USDHC_PROCTL_DTW_4BIT;
+ }
+ else
+ {
+ regval |= USDHC_PROCTL_DTW_1BIT;
+ }
+
+ putreg32(regval, priv->addr + IMX9_USDHC_PROCTL_OFFSET);
+}
+
+/****************************************************************************
+ * Name: imx9_frequency
+ *
+ * Description:
+ * Set the SD clock frequency
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * frequency - The frequency to use
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IMX9_USDHC_ABSFREQ
+static void imx9_frequency(struct sdio_dev_s *dev, uint32_t frequency)
+{
+ uint32_t sdclkfs;
+ uint32_t prescaled;
+ uint32_t regval;
+ unsigned int prescaler;
+ unsigned int divisor;
+
+ /* The SDCLK frequency is determined by
+ * (1) the frequency of the base clock that was selected as the
+ * input clock, and
+ * (2) by a prescaler and a divisor that are selected here:
+ *
+ * SDCLK frequency = (base clock) / (prescaler * divisor)
+ *
+ * The prescaler is available only for the values: 2, 4, 8, 16, 32,
+ * 64, 128, and 256. Pick the smallest value of SDCLKFS that would
+ * result in an in-range frequency. For example, if the base clock
+ * frequency is 96 MHz, and the target frequency is 25 MHz, the
+ * following logic will select prescaler:
+ *
+ * NOTE: USDHC_SYSCTL_SDCLKFS_DIVs are for Single Data Rate mode.
+ * See Reference manual for further details.
+ *
+ * 96MHz / 2 <= 25MHz <= 96MHz / 2 /16 -- YES, prescaler == 2
+ *
+ * If the target frequency is 400 kHz, the following logic will
+ * select prescaler:
+ *
+ * 96MHz / 2 <= 400KHz <= 96MHz / 2 / 16 -- NO
+ * 96MHz / 4 <= 400KHz <= 96MHz / 4 / 16 -- NO
+ * 96MHz / 8 <= 400KHz <= 96MHz / 8 / 16 -- NO
+ * 96MHz / 16 <=400KHz <= 96MHz / 16 / 16 -- YES, prescaler == 16
+ */
+
+ if (/* frequency >= (BOARD_CORECLK_FREQ / 2) && */
+ frequency <= (BOARD_CORECLK_FREQ / 2 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV2;
+ prescaler = 2;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 4) &&
+ frequency <= (BOARD_CORECLK_FREQ / 4 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV4;
+ prescaler = 4;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 8) &&
+ frequency <= (BOARD_CORECLK_FREQ / 8 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV8;
+ prescaler = 8;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 16) &&
+ frequency <= (BOARD_CORECLK_FREQ / 16 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV16;
+ prescaler = 16;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 32) &&
+ frequency <= (BOARD_CORECLK_FREQ / 32 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV32;
+ prescaler = 32;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 64) &&
+ frequency <= (BOARD_CORECLK_FREQ / 64 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV64;
+ prescaler = 64;
+ }
+ else if (frequency >= (BOARD_CORECLK_FREQ / 128) &&
+ frequency <= (BOARD_CORECLK_FREQ / 128 / 16))
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV128;
+ prescaler = 128;
+ }
+ else
+ {
+ sdclkfs = USDHC_SYSCTL_SDCLKFS_DIV256;
+ prescaler = 256;
+ }
+
+ /* The optimal divider can than be calculated. For example, if the base
+ * clock frequency is 96 MHz, the target frequency is 25 MHz, and the
+ * selected prescaler value is 2, then
+ *
+ * prescaled = 96MHz / 2 = 48MHz
+ * divisor = (48MHz + 12.5HMz/ 25MHz = 2
+ *
+ * And the resulting frequency will be 24MHz. Or, for example, if the
+ * target frequency is 400 kHz and the selected prescaler is 16, the
+ * following logic will select prescaler:
+ *
+ * prescaled = 96MHz / 16 = 6MHz
+ * divisor = (6MHz + 200KHz) / 400KHz = 15
+ *
+ * And the resulting frequency will be exactly 400KHz.
+ */
+
+ prescaled = frequency / prescaler;
+ divisor = (prescaled + (frequency >> 1)) / frequency;
+
+ /* Set the new divisor information and enable all clocks in the SYSCTRL
+ * register. TODO: Investigate using the automatically gated clocks to
+ * reduce power consumption.
+ */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+ regval &= ~(USDHC_SYSCTL_SDCLKFS_MASK | USDHC_SYSCTL_DVS_MASK);
+ regval |= (sdclkfs | USDHC_SYSCTL_DVS_DIV(divisor));
+ regval |= (USDHC_SYSCTL_SDCLKEN | USDHC_SYSCTL_PEREN |
+ USDHC_SYSCTL_HCKEN | USDHC_SYSCTL_IPGEN);
+
+ putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+
+ mcinfo("SYSCTRL: %08x\n",
+ getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_clock
+ *
+ * Description:
+ * Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_clock(struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t regval;
+
+ /* Clear the old prescaler and divisor values so that new ones can be
+ * ORed in.
+ */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+ regval &= ~(USDHC_SYSCTL_SDCLKFS_MASK | USDHC_SYSCTL_DVS_MASK);
+
+ /* Select the new prescaler and divisor values based on the requested
+ * mode and the settings from the board.h file. Clocks are automatically
+ * gated by the driver when not needed.
+ */
+
+ switch (rate)
+ {
+ default:
+ case CLOCK_SDIO_DISABLED: /* Clock is disabled */
+ {
+ /* Clear the prescaler and divisor settings */
+
+ putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+ mcinfo("DISABLED, SYSCTRL: %08" PRIx32 "\n",
+ getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET)); return;
+ }
+ break;
+
+ case CLOCK_IDMODE:
+ {
+ /* Initial ID mode clocking (<400KHz) */
+
+ mcinfo("IDMODE\n");
+
+ /* Put out an additional 80 clocks in case this is a power-up
+ * sequence.
+ */
+
+ regval |= (BOARD_USDHC_IDMODE_PRESCALER |
+ BOARD_USDHC_IDMODE_DIVISOR |
+ USDHC_SYSCTL_INITA);
+ }
+ break;
+
+ case CLOCK_MMC_TRANSFER:
+ {
+ /* MMC normal operation clocking */
+
+ mcinfo("MMCTRANSFER\n");
+ regval |= (BOARD_USDHC_MMCMODE_PRESCALER |
+ BOARD_USDHC_MMCMODE_DIVISOR);
+ }
+ break;
+
+ case CLOCK_SD_TRANSFER_1BIT:
+ {
+ /* SD normal operation clocking (narrow 1-bit mode) */
+
+ mcinfo("1BITTRANSFER\n");
+ regval |= (BOARD_USDHC_SD1MODE_PRESCALER |
+ BOARD_USDHC_SD1MODE_DIVISOR);
+ }
+ break;
+
+ case CLOCK_SD_TRANSFER_4BIT:
+ {
+ /* SD normal operation clocking (wide 4-bit mode) */
+
+ mcinfo("4BITTRANSFER\n");
+ regval |= (BOARD_USDHC_SD4MODE_PRESCALER |
+ BOARD_USDHC_SD4MODE_DIVISOR);
+ }
+ break;
+ }
+
+ putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+
+ /* Wait for clock to become stable */
+
+ while ((getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET) &
+ USDHC_PRSSTAT_SDSTB) == 0)
+ {
+ }
+
+ mcinfo("SYSCTRL: %08" PRIx32 "\n",
+ getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET));
+}
+
+/****************************************************************************
+ * Name: imx9_attach
+ *
+ * Description:
+ * Attach and prepare interrupts
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int imx9_attach(struct sdio_dev_s *dev)
+{
+ int ret;
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ int usdhc2_dev = (IMX9_MAX_SDHC_DEV_SLOTS > 1) ? 1 : 0;
+
+ /* Attach the SDIO interrupt handler */
+
+ if (priv->addr == IMX9_USDHC1_BASE)
+ {
+ ret = irq_attach(IMX9_IRQ_USDHC1, imx9_interrupt, &g_sdhcdev[0]);
+ }
+ else
+ {
+ if (priv->addr == IMX9_USDHC2_BASE)
+ {
+ ret = irq_attach(IMX9_IRQ_USDHC2, imx9_interrupt,
+ &g_sdhcdev[usdhc2_dev]);
+ }
+ else
+ {
+ PANIC();
+ }
+ }
+
+ if (ret == OK)
+ {
+ /* Disable all interrupts at the SDIO controller and clear all pending
+ * interrupts.
+ */
+
+ putreg32(0, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ putreg32(USDHC_INT_ALL, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ /* Enable SDIO interrupts at the NVIC. They can now be enabled at the
+ * SDIO controller as needed.
+ */
+
+ if (priv->addr == IMX9_USDHC1_BASE)
+ {
+ up_enable_irq(IMX9_IRQ_USDHC1);
+ }
+ else if (priv->addr == IMX9_USDHC2_BASE)
+ {
+ up_enable_irq(IMX9_IRQ_USDHC2);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: imx9_sendcmd
+ *
+ * Description:
+ * Send the SDIO command
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - The command to send (32-bits, encoded)
+ * arg - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int imx9_sendcmd(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t arg)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ clock_t timeout;
+ clock_t start;
+ clock_t elapsed;
+ uint32_t regval;
+ uint32_t mcrregval;
+ uint32_t cmdidx;
+
+ /* Initialize the command index */
+
+ cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+ regval = cmdidx << USDHC_XFERTYP_CMDINX_SHIFT;
+ mcrregval = USDHC_MC_DEFAULTVAL;
+
+ mcinfo("cmdidx: %d\n", cmdidx);
+
+ if (cmdidx == SD_ACMDIDX53)
+ {
+ /* Dynamically set parameters for ACMD53 because it can accommodate
+ * different transmission characteristics (single and multi-block,
+ * rx & tx).
+ */
+
+ if (arg & (1 << 31))
+ {
+ /* Transmit mode */
+
+ cmd |= MMCSD_WRDATAXFR;
+ }
+ else
+ {
+ /* Receive mode */
+
+ cmd |= MMCSD_RDDATAXFR;
+ }
+
+ if (arg & (1 << 27))
+ {
+ /* In block mode */
+
+ cmd |= SDIO_MULTIBLOCK;
+ }
+ }
+
+ /* Check if a data transfer accompanies the command */
+
+ switch (cmd & MMCSD_DATAXFR_MASK)
+ {
+ default:
+ case MMCSD_NODATAXFR:
+ {
+ /* No.. no data transfer */
+ }
+ break;
+
+ /* The following two cases are probably missing some setup logic */
+
+ case MMCSD_RDSTREAM:
+ {
+ /* Yes.. streaming read data transfer */
+
+ regval |= USDHC_XFERTYP_DPSEL;
+ mcrregval |= USDHC_MC_DTDSEL;
+ }
+ break;
+
+ case MMCSD_WRSTREAM:
+ {
+ /* Yes.. streaming write data transfer */
+
+ regval |= USDHC_XFERTYP_DPSEL;
+ }
+ break;
+
+ case MMCSD_RDDATAXFR:
+ {
+ /* Yes.. normal read data transfer */
+
+ regval |= USDHC_XFERTYP_DPSEL;
+ mcrregval |= USDHC_MC_DTDSEL;
+ }
+ break;
+
+ case MMCSD_WRDATAXFR:
+ {
+ /* Yes.. normal write data transfer */
+
+ regval |= USDHC_XFERTYP_DPSEL;
+ }
+ break;
+ }
+
+ /* Is it a multi-block transfer? */
+
+ if ((cmd & (MMCSD_MULTIBLOCK | SDIO_MULTIBLOCK)) != 0)
+ {
+ mcrregval |= USDHC_MC_MSBSEL;
+
+ /* Yes.. should the transfer be stopped with ACMD12? */
+
+ if (((cmd & MMCSD_MULTIBLOCK) != 0) &&
+ ((cmd & MMCSD_STOPXFR) != 0))
+ {
+ /* Yes.. Indefinite block transfer (not SDIO) */
+
+ mcrregval |= USDHC_MC_AC12EN;
+ }
+ else
+ {
+ /* No.. Fixed block transfer */
+
+ mcrregval |= USDHC_MC_BCEN;
+ }
+ }
+
+ /* Configure response type bits */
+
+ switch (cmd & MMCSD_RESPONSE_MASK)
+ {
+ case MMCSD_NO_RESPONSE:
+ {
+ /* No response */
+
+ regval |= USDHC_XFERTYP_RSPTYP_NONE;
+ }
+ break;
+
+ case MMCSD_R1B_RESPONSE:
+ {
+ /* Response length 48, check busy & cmdindex */
+
+ regval |=
+ (USDHC_XFERTYP_RSPTYP_LEN48BSY | USDHC_XFERTYP_CICEN |
+ USDHC_XFERTYP_CCCEN);
+ }
+ break;
+
+ case MMCSD_R1_RESPONSE: /* Response length 48, check cmdindex */
+ case MMCSD_R5_RESPONSE:
+ case MMCSD_R6_RESPONSE:
+ {
+ regval |=
+ (USDHC_XFERTYP_RSPTYP_LEN48 | USDHC_XFERTYP_CICEN |
+ USDHC_XFERTYP_CCCEN);
+ }
+ break;
+
+ case MMCSD_R2_RESPONSE:
+ {
+ /* Response length 136, check CRC */
+
+ regval |= (USDHC_XFERTYP_RSPTYP_LEN136 | USDHC_XFERTYP_CCCEN);
+ }
+ break;
+
+ case MMCSD_R3_RESPONSE: /* Response length 48 */
+ case MMCSD_R4_RESPONSE:
+ case MMCSD_R7_RESPONSE:
+ {
+ regval |= USDHC_XFERTYP_RSPTYP_LEN48;
+ }
+ break;
+ }
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+ /* Enable DMA */
+
+ /* Internal DMA is used */
+
+ mcrregval |= USDHC_MC_DMAEN;
+#endif
+
+ /* Check for abort. TODO: Check Suspend/Resume bits too in
+ * XFR_TYP::CMDTYP.
+ */
+
+ if (cmd & MMCSD_STOPXFR)
+ {
+ regval |= USDHC_XFERTYP_CMDTYP_ABORT;
+ }
+
+ mcinfo("cmd: %08" PRIx32 " arg: %08" PRIx32
+ " regval: %08" PRIx32 " mcrval: %08" PRIx32 "\n", cmd, arg,
+ regval, mcrregval);
+
+ /* If there has been a response error then perform a reset and wait for it
+ * to complete.
+ */
+
+ if ((getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET) &
+ USDHC_RESPERR_INTS) != 0)
+ {
+ modifyreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET, 0,
+ USDHC_SYSCTL_RSTC);
+ while ((getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET) &
+ USDHC_SYSCTL_RSTC) != 0)
+ {
+ }
+ }
+
+ /* The Command Inhibit (CIHB) bit is set in the PRSSTAT bit immediately
+ * after the transfer type register is written. This bit is cleared
+ * when the command response is received. If this status bit is 0, it
+ * indicates that the CMD line is not in use and the USDHC can issue a
+ * SD/MMC Command using the CMD line. CIHB should always be clear before
+ * this function is called, but this check is performed here to provide
+ * overlap and maximum performance.
+ */
+
+ timeout = USDHC_CMDTIMEOUT;
+ start = clock_systime_ticks();
+ while ((getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET) &
+ USDHC_PRSSTAT_CIHB) != 0)
+ {
+ /* Calculate the elapsed time */
+
+ elapsed = clock_systime_ticks() - start;
+ if (elapsed >= timeout)
+ {
+ mcerr("ERROR: Timeout (waiting CIHB) cmd: %08" PRIx32
+ " PRSSTAT: %08" PRIx32 "\n",
+ cmd, getreg32(priv->addr + IMX9_USDHC_PRSSTAT_OFFSET));
+ return -EBUSY;
+ }
+ }
+
+ /* Set the USDHC Argument value */
+
+ putreg32(arg, priv->addr + IMX9_USDHC_CMDARG_OFFSET);
+
+ /* Clear interrupt status and write the USDHC CMD */
+
+ putreg32(USDHC_RESPDONE_INTS, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+ putreg32(mcrregval, priv->addr + IMX9_USDHC_MIX_OFFSET);
+ putreg32(regval, priv->addr + IMX9_USDHC_XFERTYP_OFFSET);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: imx9_blocksetup
+ *
+ * Description:
+ * Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * blocklen - The selected block size.
+ * nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void imx9_blocksetup(struct sdio_dev_s *dev,
+ unsigned int blocklen,
+ unsigned int nblocks)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+ blocklen * nblocks, nblocks);
+
+ /* Configure block size for next transfer */
+
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ priv->blocksize = blocklen;
+#endif
+
+ putreg32(USDHC_BLKATTR_SIZE(blocklen) | USDHC_BLKATTR_CNT(nblocks),
+ priv->addr + IMX9_USDHC_BLKATTR_OFFSET);
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_recvsetup
+ *
+ * Description:
+ * Setup hardware in preparation for data transfer from the card in non-
+ * DMA (interrupt driven mode). This method will do whatever controller
+ * setup is necessary. This would be called for SD memory just BEFORE
+ * sending CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18
+ * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally,
+ * SDIO_WAITEVENT will be called to receive the indication that the
+ * transfer is complete.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - Address of the buffer in which to receive the data
+ * nbytes - The number of bytes in the transfer
+ *
+ * Returned Value:
+ * Number of bytes sent on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static int imx9_recvsetup(struct sdio_dev_s *dev, uint8_t *buffer,
+ size_t nbytes)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+ DEBUGASSERT(((uint64_t) buffer & 3) == 0);
+
+ /* Reset the DPSM configuration */
+
+ imx9_sampleinit();
+ imx9_sample(priv, SAMPLENDX_BEFORE_SETUP);
+
+ /* Save the destination buffer information for use by the interrupt
+ * handler and DMA memory invalidation.
+ */
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = nbytes;
+
+ /* Then set up the SDIO data path */
+
+ imx9_dataconfig(priv, false, nbytes, USDHC_DTOCV_MAXTIMEOUT);
+
+ /* And enable interrupts */
+
+ imx9_configxfrints(priv, USDHC_RCVDONE_INTS);
+ imx9_sample(priv, SAMPLENDX_AFTER_SETUP); return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_sendsetup
+ *
+ * Description:
+ * Setup hardware in preparation for data transfer from the card. This
+ * method will do whatever controller setup is necessary. This would be
+ * called for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25
+ * (WRITE_MULTIPLE_BLOCK), ... and before SDIO_SENDDATA is called.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - Address of the buffer containing the data to send
+ * nbytes - The number of bytes in the transfer
+ *
+ * Returned Value:
+ * Number of bytes sent on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_IMX9_USDHC_DMA
+static int imx9_sendsetup(struct sdio_dev_s *dev,
+ const uint8_t *buffer,
+ size_t nbytes)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+ DEBUGASSERT(((uint64_t) buffer & 3) == 0);
+
+ /* Reset the DPSM configuration */
+
+ imx9_sampleinit();
+ imx9_sample(priv, SAMPLENDX_BEFORE_SETUP);
+
+ /* Save the source buffer information for use by the interrupt handler */
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = nbytes;
+
+ /* Then set up the SDIO data path */
+
+ imx9_dataconfig(priv, true, nbytes, USDHC_DTOCV_MAXTIMEOUT);
+
+ /* Enable TX interrupts */
+
+ imx9_configxfrints(priv, USDHC_SNDDONE_INTS);
+ imx9_sample(priv, SAMPLENDX_AFTER_SETUP); return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_cancel
+ *
+ * Description:
+ * Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ * SDIO_DMARECVSETUP or SDIO_DMASENDSETUP. This must be called to cancel
+ * the data transfer setup if, for some reason, you cannot perform the
+ * transfer.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int imx9_cancel(struct sdio_dev_s *dev)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+ uint32_t regval;
+#endif
+
+ /* Disable all transfer- and event- related interrupts */
+
+ imx9_configxfrints(priv, 0); imx9_configwaitints(priv, 0, 0, 0);
+
+ /* Clearing pending interrupt status on all transfer- and event- related
+ * interrupts
+ */
+
+ putreg32(USDHC_WAITALL_INTS, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
+ /* Cancel any watchdog timeout */
+
+ wd_cancel(&priv->waitwdog);
+
+ /* If this was a DMA transfer, make sure that DMA is stopped */
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+
+ /* Stop the DMA by resetting the data path */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+ regval |= USDHC_SYSCTL_RSTD;
+ putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
+#endif
+
+ /* Mark no transfer in progress */
+
+ priv->remaining = 0;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: imx9_waitresponse
+ *
+ * Description:
+ * Poll-wait for the response to the last command to be ready. This
+ * function should be called even after sending commands that have no
+ * response (such as CMD0) to make sure that the hardware is ready to
+ * receive the next command.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - The command that was sent. See 32-bit command definitions above.
+ *
+ * Returned Value:
+ * OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int imx9_waitresponse(struct sdio_dev_s *dev, uint32_t cmd)
+{
+ clock_t timeout;
+ clock_t start;
+ clock_t elapsed;
+ uint32_t errors;
+ uint32_t enerrors;
+
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ int ret = OK;
+
+ switch (cmd & MMCSD_RESPONSE_MASK)
+ {
+ case MMCSD_NO_RESPONSE:
+ timeout = USDHC_CMDTIMEOUT;
+ errors = 0;
+ break;
+
+ case MMCSD_R1_RESPONSE:
+ case MMCSD_R1B_RESPONSE:
+ case MMCSD_R2_RESPONSE:
+ case MMCSD_R4_RESPONSE:
+ case MMCSD_R5_RESPONSE:
+ case MMCSD_R6_RESPONSE:
+ {
+ timeout = USDHC_LONGTIMEOUT;
+ errors = USDHC_RESPERR_INTS;
+ }
+ break;
+
+ case MMCSD_R3_RESPONSE:
+ case MMCSD_R7_RESPONSE:
+ {
+ timeout = USDHC_CMDTIMEOUT;
+ errors = USDHC_RESPERR_INTS;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Then wait for the Command Complete (CC) indication (or timeout). The
+ * CC bit is set when the end bit of the command response is received
+ * (except Auto CMD12).
+ */
+
+ start = clock_systime_ticks();
+ while ((getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET) &
+ USDHC_INT_CC) == 0)
+ {
+ /* Calculate the elapsed time */
+
+ elapsed = clock_systime_ticks() - start;
+ if (elapsed >= timeout)
+ {
+ mcerr("ERROR: Timeout cmd: %08" PRIx32
+ " IRQSTAT: %08" PRIx32 "\n", cmd,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET));
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ /* Check for hardware detected errors */
+
+ enerrors = getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET) & errors;
+ if (enerrors != 0)
+ {
+ mcerr("ERROR: cmd: %08" PRIx32 " errors: %08" PRIx32 ", "
+ "fired %08" PRIx32 " IRQSTAT: %08" PRIx32 "\n",
+ cmd, errors, enerrors,
+ getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET));
+
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: imx9_recv*
+ *
+ * Description:
+ * Receive response to SDIO command. Only the critical payload is
+ * returned -- that is 32 bits for 48 bit status and 128 bits for 136 bit
+ * status. The driver implementation should verify the correctness of
+ * the remaining, non-returned bits (CRCs, CMD index, etc.).
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * Rx - Buffer in which to receive the response
+ *
+ * Returned Value:
+ * Number of bytes sent on success; a negated errno on failure. Here a
+ * failure means only a failure to obtain the requested response (due to
+ * transport problem -- timeout, CRC, etc.). The implementation only
+ * assures that the response is returned intact and does not check errors
+ * within the response itself.
+ *
+ ****************************************************************************/
+
+static int imx9_recvshortcrc(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t regval;
+ int ret = OK;
+
+ /* R1 Command response (48-bit)
+ * 47 0 Start bit
+ * 46 0 Transmission bit (0=from card)
+ * 45:40 bit5 - bit0 Command index (0-63)
+ * 39:8 bit31- bit0 32-bit card status
+ * 7:1 bit6 - bit0 CRC7
+ * 0 1 End bit
+ *
+ * R1b Identical to R1 with the additional busy signalling via the data
+ * line.
+ * R6 Published RCA Response (48-bit, SD card only)
+ * 47 0 Start bit
+ * 46 0 Transmission bit (0=from card)
+ * 45:40 bit5 - bit0 Command index (0-63)
+ * 39:8 bit31 - bit0 32-bit Argument Field, consisting of:
+ * [31:16] New published RCA of card
+ * [15:0] Card status bits
+ * {23,22,19,12:0}
+ * 7:1 bit6 - bit0 CRC7
+ * 0 1 End bit
+ */
+
+#ifdef CONFIG_DEBUG_FEATURES
+ if (!rshort)
+ {
+ mcerr("ERROR: rshort=NULL\n");
+ ret = -EINVAL;
+ }
+
+ /* Check that this is the correct response to this command */
+
+ else if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+ (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+ (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+ (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE)
+ {
+ mcerr("ERROR: Wrong response CMD=%08" PRIx32 "\n", cmd);
+ ret = -EINVAL;
+ }
+ else
+#endif
+ {
+ /* Check if a timeout or CRC error occurred */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+ if ((regval & USDHC_INT_CTOE) != 0)
+ {
+ mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+ ret = -ETIMEDOUT;
+ }
+ else if ((regval & USDHC_INT_CCE) != 0)
+ {
+ mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval); ret = -EIO;
+ }
+ }
+
+ /* Return the R1/R1b/R6 response. These responses are returned in
+ * CDMRSP0. NOTE: This is not true for R1b (Auto CMD12 response) which is
+ * returned in CMDRSP3.
+ */
+
+ *rshort = getreg32(priv->addr + IMX9_USDHC_CMDRSP0_OFFSET);
+ return ret;
+}
+
+static int imx9_recvlong(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t rlong[4])
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t regval;
+ int ret = OK;
+
+ /* R2 CID, CSD register (136-bit)
+ * 135 0 Start bit
+ * 134 0 Transmission bit (0=from card)
+ * 133:128 bit5 - bit0 Reserved
+ * 127:1 bit127 - bit1 127-bit CID or CSD register (including int. CRC)
+ * 0 1 End bit
+ */
+
+#ifdef CONFIG_DEBUG_FEATURES
+ /* Check that R1 is the correct response to this command */
+
+ if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+ {
+ mcerr("ERROR: Wrong response CMD=%08" PRIx32 "\n", cmd);
+ ret = -EINVAL;
+ }
+ else
+#endif
+ {
+ /* Check if a timeout or CRC error occurred */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+ if (regval & USDHC_INT_CTOE)
+ {
+ mcerr("ERROR: Timeout IRQSTAT: %08" PRIx32 "\n", regval);
+ ret = -ETIMEDOUT;
+ }
+ else if (regval & USDHC_INT_CCE)
+ {
+ mcerr("ERROR: CRC fail IRQSTAT: %08" PRIx32 "\n", regval);
+ ret = -EIO;
+ }
+ }
+
+ /* Return the long response in CMDRSP3..0 */
+
+ if (rlong)
+ {
+ uint32_t rsp3 = getreg32(priv->addr + IMX9_USDHC_CMDRSP3_OFFSET);
+ uint32_t rsp2 = getreg32(priv->addr + IMX9_USDHC_CMDRSP2_OFFSET);
+ uint32_t rsp1 = getreg32(priv->addr + IMX9_USDHC_CMDRSP1_OFFSET);
+ uint32_t rsp0 = getreg32(priv->addr + IMX9_USDHC_CMDRSP0_OFFSET);
+
+ rlong[0] = rsp3 << 8 | rsp2 >> 24;
+ rlong[1] = rsp2 << 8 | rsp1 >> 24;
+ rlong[2] = rsp1 << 8 | rsp0 >> 24; rlong[3] = rsp0 << 8;
+ }
+
+ return ret;
+}
+
+static int imx9_recvshort(struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t regval;
+ int ret = OK;
+
+ /* R3 OCR (48-bit)
+ * 47 0 Start bit
+ * 46 0 Transmission bit (0=from card)
+ * 45:40 bit5 - bit0 Reserved
+ * 39:8 bit31- bit0 32-bit OCR register
+ * 7:1 bit6 - bit0 Reserved
+ * 0 1 End bit
+ */
+
+ /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_FEATURES
+ if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+ (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+ (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+ {
+ mcerr("ERROR: Wrong response CMD=%08" PRIx32 "\n", cmd);
+ ret = -EINVAL;
+ }
+ else
+#endif
+ {
+ /* Check if a timeout occurred (Apparently a CRC error can terminate a
+ * good response)
+ */
+
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+ if (regval & USDHC_INT_CTOE)
+ {
+ mcerr("ERROR: Timeout IRQSTAT: %08" PRIx32 "\n", regval);
+ ret = -ETIMEDOUT;
+ }
+ }
+
+ /* Return the short response in CMDRSP0 */
+
+ if (rshort)
+ {
+ *rshort = getreg32(priv->addr + IMX9_USDHC_CMDRSP0_OFFSET);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: imx9_waitenable
+ *
+ * Description:
+ * Enable/disable of a set of SDIO wait events. This is part of the
+ * the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is
+ * configured before calling imx9_eventwait. This is done in this way
+ * to help the driver to eliminate race conditions between the command
+ * setup and the subsequent events.
+ *
+ * The enabled events persist until either (1) SDIO_WAITENABLE is called
+ * again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ * returns.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ * definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_waitenable(struct sdio_dev_s *dev,
+ sdio_eventset_t eventset, uint32_t timeout)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ uint32_t waitints;
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Disable event-related interrupts */
+
+ imx9_configwaitints(priv, 0, 0, 0);
+
+ /* Select the interrupt mask that will give us the appropriate wakeup
+ * interrupts.
+ */
+
+ waitints = 0;
+ if ((eventset & (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+ {
+ waitints |= USDHC_RESPDONE_INTS;
+ }
+
+ if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
+ {
+#ifdef CONFIG_IMX9_USDHC_DMA
+ waitints |= USDHC_DMADONE_INTS;
+#else
+ waitints |= USDHC_XFRDONE_INTS;
+#endif
+ }
+
+ /* Enable event-related interrupts */
+
+ imx9_configwaitints(priv, waitints, eventset, 0);
+
+ /* Check if the timeout event is specified in the event set */
+
+ if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+ {
+ int delay;
+ int ret;
+
+ /* Yes.. Handle a corner case */
+
+ if (!timeout)
+ {
+ priv->wkupevent = SDIOWAIT_TIMEOUT;
+ return;
+ }
+
+ /* Start the watchdog timer */
+
+ delay = MSEC2TICK(timeout);
+ ret = wd_start(&priv->waitwdog, delay,
+ imx9_eventtimeout, (wdparm_t)priv);
+
+ if (ret < 0)
+ {
+ mcerr("ERROR: wd_start failed: %d\n", ret);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: imx9_eventwait
+ *
+ * Description:
+ * Wait for one of the enabled events to occur (or a timeout). Note that
+ * all events enabled by SDIO_WAITEVENTS are disabled when imx9_eventwait
+ * returns. SDIO_WAITEVENTS must be called again before imx9_eventwait
+ * can be used again.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * timeout - Maximum time in milliseconds to wait. Zero means immediate
+ * timeout with no wait. The timeout value is ignored if
+ * SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ * Event set containing the event(s) that ended the wait. Should always
+ * be non-zero. All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t imx9_eventwait(struct sdio_dev_s *dev)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ sdio_eventset_t wkupevent = 0; int ret;
+
+ /* There is a race condition here... the event may have completed before
+ * we get here. In this case waitevents will be zero, but wkupevents
+ * will be non-zero (and, hopefully, the semaphore count will also be
+ * non-zero.
+ */
+
+ DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
+ (priv->waitevents == 0 && priv->wkupevent != 0));
+
+ /* Loop until the event (or the timeout occurs). Race conditions are
+ * avoided by calling imx9_waitenable prior to triggering the logic
+ * that will cause the wait to terminate. Under certain race
+ * conditions, the waited-for may have already occurred before this
+ * function was called!
+ */
+
+ for (; ; )
+ {
+ /* Wait for an event in event set to occur. If this the event has
+ * already occurred, then the semaphore will already have been
+ * incremented and there will be no wait.
+ */
+
+ ret = nxsem_wait_uninterruptible(&priv->waitsem);
+ if (ret < 0)
+ {
+ /* Task canceled. Cancel the wdog (assuming it was started) and
+ * return an SDIO error.
+ */
+
+ wd_cancel(&priv->waitwdog);
+ return SDIOWAIT_ERROR;
+ }
+
+ wkupevent = priv->wkupevent;
+
+ /* Check if the event has occurred. When the event has occurred, then
+ * evenset will be set to 0 and wkupevent will be set to a non-zero
+ * value.
+ */
+
+ if (wkupevent != 0)
+ {
+ /* Yes... break out of the loop with wkupevent non-zero */
+
+ break;
+ }
+ }
+
+ /* Disable event-related interrupts */
+
+ imx9_configwaitints(priv, 0, 0, 0);
+#ifdef CONFIG_IMX9_USDHC_DMA
+ priv->xfrflags = 0;
+#endif
+ imx9_dumpsamples(priv);
+ return wkupevent;
+}
+
+/****************************************************************************
+ * Name: imx9_callbackenable
+ *
+ * Description:
+ * Enable/disable of a set of SDIO callback events. This is part of the
+ * the SDIO callback sequence. The set of events is configured to enabled
+ * callbacks to the function provided in imx9_registercallback.
+ *
+ * Events are automatically disabled once the callback is performed and no
+ * further callback events will occur until they are again enabled by
+ * calling this method.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
+ * definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void imx9_callbackenable(struct sdio_dev_s *dev,
+ sdio_eventset_t eventset)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ mcinfo("eventset: %02x\n", eventset);
+ DEBUGASSERT(priv != NULL);
+
+ priv->cbevents = eventset;
+ imx9_callback(priv);
+}
+
+/****************************************************************************
+ * Name: imx9_registercallback
+ *
+ * Description:
+ * Register a callback that that will be invoked on any media status
+ * change. Callbacks should not be made from interrupt handlers, rather
+ * interrupt level events should be handled by calling back on the work
+ * thread.
+ *
+ * When this method is called, all callbacks should be disabled until they
+ * are enabled via a call to SDIO_CALLBACKENABLE
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * callback - The function to call on the media change
+ * arg - A caller provided value to return with the callback
+ *
+ * Returned Value:
+ * 0 on success; negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int imx9_registercallback(struct sdio_dev_s *dev,
+ worker_t callback, void *arg)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ /* Disable callbacks and register this callback and is argument */
+
+ mcinfo("Register %p(%p)\n", callback, arg);
+
+ DEBUGASSERT(priv != NULL);
+ priv->cbevents = 0;
+ priv->cbarg = arg;
+ priv->callback = callback;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: imx9_dmarecvsetup
+ *
+ * Description:
+ * Setup to perform a read DMA. If the processor supports a data cache,
+ * then this method will also make sure that the contents of the DMA memory
+ * and the data cache are coherent. For read transfers this may mean
+ * invalidating the data cache.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - The memory to DMA from
+ * buflen - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
+ uint8_t *buffer, size_t buflen)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
+ DEBUGASSERT(((uint64_t) buffer & 3) == 0);
+
+ /* Begin sampling register values */
+
+ imx9_sampleinit();
+ imx9_sample(priv, SAMPLENDX_BEFORE_SETUP);
+
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ if (((uintptr_t)buffer & (ARMV8A_DCACHE_LINESIZE - 1)) != 0 ||
+ (buflen & (ARMV8A_DCACHE_LINESIZE - 1)) != 0)
+ {
+ /* The read buffer is not cache-line aligned. Read to an internal
+ * buffer instead.
+ */
+
+ up_invalidate_dcache((uintptr_t)priv->rxbuffer,
+ (uintptr_t)priv->rxbuffer + priv->blocksize);
+
+ priv->unaligned_rx = true;
+ }
+ else
+ {
+ up_invalidate_dcache((uintptr_t)buffer,
+ (uintptr_t)buffer + buflen);
+
+ priv->unaligned_rx = false;
+ }
+#endif
+
+ /* Save the destination buffer information for use by the interrupt
+ * handler
+ */
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = buflen;
+
+ /* Then set up the SDIO data path */
+
+ imx9_dataconfig(priv, false, buflen, USDHC_DTOCV_MAXTIMEOUT);
+
+ /* Configure the RX DMA */
+
+ imx9_configxfrints(priv, USDHC_DMADONE_INTS);
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ if (priv->unaligned_rx)
+ {
+ putreg32((uint64_t) priv->rxbuffer,
+ priv->addr + IMX9_USDHC_DSADDR_OFFSET);
+ }
+ else
+#endif
+ {
+ putreg32((uint64_t) priv->buffer,
+ priv->addr + IMX9_USDHC_DSADDR_OFFSET);
+ }
+
+ /* Sample the register state */
+
+ imx9_sample(priv, SAMPLENDX_AFTER_SETUP);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_dmasendsetup
+ *
+ * Description:
+ * Setup to perform a write DMA. If the processor supports a data cache,
+ * then this method will also make sure that the contents of the DMA memory
+ * and the data cache are coherent. For write transfers, this may mean
+ * flushing the data cache.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - The memory to DMA into
+ * buflen - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IMX9_USDHC_DMA
+static int imx9_dmasendsetup(struct sdio_dev_s *dev,
+ const uint8_t *buffer, size_t buflen)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+ DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
+ DEBUGASSERT(((uint64_t) buffer & 3) == 0);
+
+ /* Begin sampling register values */
+
+ imx9_sampleinit();
+ imx9_sample(priv, SAMPLENDX_BEFORE_SETUP);
+
+ /* Save the source buffer information for use by the interrupt handler */
+
+#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
+ priv->unaligned_rx = false;
+
+ /* Flush cache to physical memory when not in DTCM memory */
+
+ up_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+
+#endif
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = buflen;
+
+ /* Then set up the SDIO data path */
+
+ imx9_dataconfig(priv, true, buflen, USDHC_DTOCV_MAXTIMEOUT);
+
+ /* Configure the TX DMA */
+
+ putreg32((uint64_t) buffer, priv->addr + IMX9_USDHC_DSADDR_OFFSET);
+
+ /* Sample the register state */
+
+ imx9_sample(priv, SAMPLENDX_AFTER_SETUP);
+
+ /* Enable TX interrupts */
+
+ imx9_configxfrints(priv, USDHC_DMADONE_INTS);
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: imx9_callback
+ *
+ * Description:
+ * Perform callback.
+ *
+ * Assumptions:
+ * This function does not execute in the context of an interrupt handler.
+ * It may be invoked on any user thread or scheduled on the work thread
+ * from an interrupt handler.
+ *
+ ****************************************************************************/
+
+static void imx9_callback(void *arg)
+{
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)arg;
+
+ /* Is a callback registered? */
+
+ DEBUGASSERT(priv != NULL);
+ mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n", priv->callback,
+ priv->cbarg, priv->cbevents, priv->cdstatus);
+
+ if (priv->callback)
+ {
+ /* Yes.. Check for enabled callback events */
+
+ if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0)
+ {
+ /* Media is present. Is the media inserted event enabled? */
+
+ if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0)
+ {
+ /* No... return without performing the callback */
+
+ return;
+ }
+ }
+ else
+ {
+ /* Media is not present. Is the media eject event enabled? */
+
+ if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0)
+ {
+ /* No... return without performing the callback */
+
+ return;
+ }
+ }
+
+ /* Perform the callback, disabling further callbacks. Of course, the
+ * the callback can (and probably should) re-enable callbacks.
+ */
+
+ priv->cbevents = 0;
+
+ /* Callbacks cannot be performed in the context of an interrupt
+ * handler. If we are in an interrupt handler, then queue the
+ * callback to be performed later on the work thread.
+ */
+
+ if (up_interrupt_context())
+ {
+ /* Yes.. queue it */
+
+ mcinfo("Queuing callback to %p(%p)\n",
+ priv->callback, priv->cbarg);
+
+ work_queue(HPWORK, &priv->cbwork, priv->callback,
+ priv->cbarg, 0);
+ }
+ else
+ {
+ /* No.. then just call the callback here */
+
+ mcinfo("Callback to %p(%p)\n",
+ priv->callback, priv->cbarg);
+
+ priv->callback(priv->cbarg);
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_usdhc_set_sdio_card_isr
+ *
+ * Description:
+ * SDIO card generates interrupt via SDIO_DATA_1 pin.
+ * Called by board-specific logic to register an ISR for SDIO card.
+ *
+ * Input Parameters:
+ * func - callback function.
+ * arg - arg to be passed to the function.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void imx9_usdhc_set_sdio_card_isr(struct sdio_dev_s *dev,
+ int (*func)(void *), void *arg)
+{
+ irqstate_t flags;
+ uint32_t regval;
+ struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
+
+ priv->do_sdio_card = func;
+ priv->do_sdio_arg = arg;
+
+ if (priv->do_sdio_card != NULL)
+ {
+ priv->cintints = USDHC_INT_CINT;
+ }
+ else
+ {
+ priv->cintints = 0;
+ }
+
+#if defined(CONFIG_MMCSD_HAVE_CARDDETECT)
+ if (priv->sw_cd_gpio == 0)
+ {
+ priv->cintints |= USDHC_INT_CINS | USDHC_INT_CRM;
+ }
+#endif
+
+ flags = enter_critical_section();
+ regval = getreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ regval = (regval & ~USDHC_INT_CINT) | priv->cintints;
+ putreg32(regval, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: imx9_sdhc_initialize
+ *
+ * Description:
+ * Initialize SDIO for operation.
+ *
+ * Input Parameters:
+ * slotno - Slot to be used
+ *
+ * Returned Value:
+ * A reference to an SDIO interface structure.
+ * NULL is returned on failures.
+ *
+ ****************************************************************************/
+
+struct sdio_dev_s *imx9_usdhc_initialize(int slotno)
+{
+ DEBUGASSERT(slotno < IMX9_MAX_SDHC_DEV_SLOTS);
+ struct imx9_dev_s *priv = &g_sdhcdev[slotno];
+
+ /* Initialize the USDHC slot structure data structure */
+
+ switch (priv->addr)
+ {
+#if defined(CONFIG_IMX9_USDHC1)
+ case IMX9_USDHC1_BASE:
+
+# if defined(CONFIG_IMX9_USDHC1_WIDTH_D1_D4) || defined(CONFIG_IMX9_USDHC1_WIDTH_D1_D8)
+ imx9_iomux_configure(PIN_USDHC1_D1_MUX);
+ imx9_iomux_configure(PIN_USDHC1_D2_MUX);
+ imx9_iomux_configure(PIN_USDHC1_D3_MUX);
+# endif
+
+# if defined(CONFIG_IMX9_USDHC1_WIDTH_D1_D8)
+ imx9_iomux_configure(PIN_USDHC1_D4_MUX);
+ imx9_iomux_configure(PIN_USDHC1_D5_MUX);
+ imx9_iomux_configure(PIN_USDHC1_D6_MUX);
+ imx9_iomux_configure(PIN_USDHC1_D7_MUX);
+# endif
+
+ /* Clocking and CMD pins (all data widths) */
+
+ imx9_iomux_configure(PIN_USDHC1_D0_MUX);
+ imx9_iomux_configure(PIN_USDHC1_DCLK_MUX);
+ imx9_iomux_configure(PIN_USDHC1_CMD_MUX);
+
+# if defined(CONFIG_MMCSD_HAVE_CARDDETECT)
+# if defined(PIN_USDHC1_CD)
+ imx9_iomux_configure(PIN_USDHC1_CD_MUX);
+# elif defined (PIN_USDHC2_CD_GPIO)
+ if (priv->sw_cd_gpio != 0)
+ {
+ imx9_config_gpio(priv->sw_cd_gpio);
+ imx9_iomux_configure(PIN_USDHC1_CD_GPIO_MUX);
+ }
+# endif
+# endif
+
+ /* Enable clocks */
+
+ imx9_ccm_configure_root_clock(CCM_CR_USDHC2, SYS_PLL1PFD1, 4);
+
+ imx9_ccm_gate_on(CCM_LPCG_USDHC1, true);
+
+ break;
+#endif
+
+#if defined(CONFIG_IMX9_USDHC2)
+ case IMX9_USDHC2_BASE:
+# if defined(CONFIG_IMX9_USDHC2_WIDTH_D1_D4)
+ imx9_iomux_configure(PIN_USDHC2_D1_MUX);
+ imx9_iomux_configure(PIN_USDHC2_D2_MUX);
+ imx9_iomux_configure(PIN_USDHC2_D3_MUX);
+# endif
+
+ imx9_iomux_configure(PIN_USDHC2_D0_MUX);
+ imx9_iomux_configure(PIN_USDHC2_DCLK_MUX);
+ imx9_iomux_configure(PIN_USDHC2_CMD_MUX);
+
+# if defined(CONFIG_MMCSD_HAVE_CARDDETECT)
+# if defined(PIN_USDHC2_CD)
+ imx9_iomux_configure(PIN_USDHC2_CD_MUX);
+# elif defined (PIN_USDHC2_CD_GPIO)
+ if (priv->sw_cd_gpio != 0)
+ {
+ imx9_config_gpio(priv->sw_cd_gpio);
+ imx9_iomux_configure(PIN_USDHC2_CD_GPIO_MUX);
+ }
+# endif
+# endif
+
+ imx9_iomux_configure(PIN_USDHC2_VSELECT_MUX);
+
+ /* Enable clocks */
+
+ imx9_ccm_configure_root_clock(CCM_CR_USDHC2, SYS_PLL1PFD1, 4);
+
+ imx9_ccm_gate_on(CCM_LPCG_USDHC2, true);
+
+ mcinfo("Enabled clocks\n");
+
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ imx9_reset(&priv->dev);
+ imx9_showregs(priv, "After reset");
+
+ return &g_sdhcdev[slotno].dev;
+}
+
+#endif /* CONFIG_IMX9_USDHC */
diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c b/arch/arm64/src/imx9/imx9_usdhc.h
similarity index 52%
copy from boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
copy to arch/arm64/src/imx9/imx9_usdhc.h
index fc27bc3c95..0b830914e2 100644
--- a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
+++ b/arch/arm64/src/imx9/imx9_usdhc.h
@@ -1,5 +1,5 @@
/****************************************************************************
- * boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
+ * arch/arm64/src/imx9/imx9_usdhc.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -18,87 +18,58 @@
*
****************************************************************************/
+#ifndef __ARCH_ARM64_SRC_IMX9_IMX9_USDHC_H
+#define __ARCH_ARM64_SRC_IMX9_IMX9_USDHC_H
+
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#include <nuttx/fs/fs.h>
-
-#include "imx9_dma_alloc.h"
-
-#include "imx93-evk.h"
+#include "chip.h"
+#include "hardware/imx9_usdhc.h"
/****************************************************************************
- * Public Functions
+ * Public Function Prototypes
****************************************************************************/
/****************************************************************************
- * Name: imx_bringup
+ * Name: imx9_usdhc_set_sdio_card_isr
*
* Description:
- * Bring up board features
+ * SDIO card generates interrupt via SDIO_DATA_1 pin.
+ * Called by board-specific logic to register an ISR for SDIO card.
+ *
+ * Input Parameters:
+ * func - callback function.
+ * arg - arg to be passed to the function.
+ *
+ * Returned Value:
+ * None
*
****************************************************************************/
-int imx9_bringup(void)
-{
- int ret;
-
-#ifdef CONFIG_FS_PROCFS
- /* Mount the procfs file system */
-
- ret = nx_mount(NULL, "/proc", "procfs", 0, NULL);
- if (ret < 0)
- {
- syslog(LOG_ERR, "ERROR: Failed to mount procfs at /proc: %d\n", ret);
- }
-#endif
-
-#ifdef CONFIG_IMX9_DMA_ALLOC
- /* Initialize the DMA memory allocator */
-
- ret = imx9_dma_alloc_init();
- if (ret < 0)
- {
- syslog(LOG_ERR, "ERROR: Failed initialize DMA allocator: %d\n", ret);
- }
-#endif
-
-#ifdef CONFIG_PWM
- /* Configure PWM outputs */
+void imx9_usdhc_set_sdio_card_isr(struct sdio_dev_s *dev,
+ int (*func)(void *), void *arg);
- ret = imx9_pwm_setup();
- if (ret < 0)
- {
- syslog(LOG_ERR, "ERROR: Failed initialize PWM outputs: %d\n", ret);
- }
-#endif
-
-#if defined(CONFIG_I2C_DRIVER)
- /* Configure I2C peripheral interfaces */
-
- ret = imx9_i2c_initialize();
- if (ret < 0)
- {
- syslog(LOG_ERR, "Failed to initialize I2C driver: %d\n", ret);
- }
-#endif
-
-#if defined(CONFIG_SPI_DRIVER)
- /* Configure SPI peripheral interfaces */
+/****************************************************************************
+ * Name: imx9_usdhc_initialize
+ *
+ * Description:
+ * Initialize USDHC for operation.
+ *
+ * Input Parameters:
+ * slotno - Not used.
+ *
+ * Returned Value:
+ * A reference to an USDIO interface structure. NULL is returned on
+ * failures.
+ *
+ ****************************************************************************/
- ret = imx9_spi_initialize();
- if (ret < 0)
- {
- syslog(LOG_ERR, "Failed to initialize SPI driver: %d\n", ret);
- }
-#endif
+struct sdio_dev_s *imx9_usdhc_initialize(int slotno);
- UNUSED(ret);
- return OK;
-}
+#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_USDHC_H */
diff --git a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
index 92348877f1..531a8c8eb3 100644
--- a/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
+++ b/boards/arm64/imx9/imx93-evk/configs/nsh/defconfig
@@ -5,6 +5,10 @@
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
+# CONFIG_MMCSD_HAVE_WRITEPROTECT is not set
+# CONFIG_MMCSD_IOCSUPPORT is not set
+# CONFIG_MMCSD_MMCSUPPORT is not set
+# CONFIG_MMCSD_SPI is not set
CONFIG_ARCH="arm64"
CONFIG_ARCH_ARM64=y
CONFIG_ARCH_BOARD="imx93-evk"
@@ -27,6 +31,8 @@ CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_TCPBLASTER=y
CONFIG_EXAMPLES_UDPBLASTER=y
CONFIG_EXPERIMENTAL=y
+CONFIG_FS_FAT=y
+CONFIG_FS_FATTIME=y
CONFIG_FS_PROCFS=y
CONFIG_FS_ROMFS=y
CONFIG_GRAN=y
@@ -55,9 +61,13 @@ CONFIG_IMX9_LPUART1=y
CONFIG_IMX9_TPM3_PWM=y
CONFIG_IMX9_TPM3_PWM_CHMUX=0x00000003
CONFIG_IMX9_USBDEV_USBC1=y
+CONFIG_IMX9_USDHC2=y
+CONFIG_IMX9_USDHC2_INVERT_CD=y
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LPUART1_SERIAL_CONSOLE=y
+CONFIG_MMCSD=y
+CONFIG_MMCSD_SDIO=y
CONFIG_NDEBUG=y
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
@@ -83,6 +93,7 @@ CONFIG_SCHED_HPWORK=y
CONFIG_SCHED_HPWORKPRIORITY=192
CONFIG_SCHED_LPWORK=y
CONFIG_SCHED_LPWORKPRIORITY=50
+CONFIG_SDIO_BLOCKSETUP=y
CONFIG_SPI=y
CONFIG_SPINLOCK=y
CONFIG_STACK_COLORATION=y
diff --git a/boards/arm64/imx9/imx93-evk/include/board.h b/boards/arm64/imx9/imx93-evk/include/board.h
index 8382bd62ed..1d93aef9c7 100644
--- a/boards/arm64/imx9/imx93-evk/include/board.h
+++ b/boards/arm64/imx9/imx93-evk/include/board.h
@@ -90,6 +90,37 @@
#define MUX_LPSPI6_CS IOMUX_CFG(IOMUXC_PAD_GPIO_IO00_GPIO2_IO00, IOMUX_GPIO_DEFAULT, IOMUXC_MUX_SION_ON)
#define GPIO_LPSPI6_CS (GPIO_PORT2 | GPIO_PIN0 | GPIO_OUTPUT | GPIO_OUTPUT_ONE)
+/* USDHC */
+
+/* Note: Need to set the SION for cmd and data pads (ERR052021) */
+
+#define PIN_USDHC2_D0_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA0_USDHC2_DATA0, IOMUXC_PAD_DSE_X1 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PU_ON | IOMUXC_PAD_HYS_ST_ON, IOMUXC_MUX_SION_ON)
+#define PIN_USDHC2_D1_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA1_USDHC2_DATA1, IOMUXC_PAD_DSE_X1 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PU_ON | IOMUXC_PAD_HYS_ST_ON, IOMUXC_MUX_SION_ON)
+#define PIN_USDHC2_D2_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA2_USDHC2_DATA2, IOMUXC_PAD_DSE_X1 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PU_ON | IOMUXC_PAD_HYS_ST_ON, IOMUXC_MUX_SION_ON)
+#define PIN_USDHC2_D3_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA3_USDHC2_DATA3, IOMUXC_PAD_DSE_X1 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PU_ON | IOMUXC_PAD_HYS_ST_ON, IOMUXC_MUX_SION_ON)
+#define PIN_USDHC2_DCLK_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA1_USDHC2_DATA1, IOMUXC_PAD_DSE_X6 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PD_ON | IOMUXC_PAD_HYS_ST_ON, 0)
+#define PIN_USDHC2_CMD_MUX IOMUX_CFG(IOMUXC_PAD_SD2_CMD_USDHC2_CMD, IOMUXC_PAD_DSE_X1 | IOMUXC_PAD_FSEL_FAST | IOMUXC_PAD_PU_ON | IOMUXC_PAD_HYS_ST_ON, IOMUXC_MUX_SION_ON)
+#define PIN_USDHC2_CD_MUX IOMUX_CFG(IOMUXC_PAD_SD2_DATA3_USDHC2_DATA3, IOMUXC_PAD_DSE_X4 | IOMUXC_PAD_FSEL_FAST, 0)
+#define PIN_USDHC2_VSELECT_MUX IOMUX_CFG(IOMUXC_PAD_SD2_VSELECT_USDHC2_VSELECT, IOMUXC_PAD_DSE_X5 | IOMUXC_PAD_FSEL_SFAST | IOMUXC_PAD_PD_ON, 0)
+
+/* 390 KHz for initial inquiry stuff */
+
+#define BOARD_USDHC_IDMODE_PRESCALER USDHC_SYSCTL_SDCLKFS_DIV256
+#define BOARD_USDHC_IDMODE_DIVISOR USDHC_SYSCTL_DVS_DIV(2)
+
+/* 25MHz for 1-bit wide bus */
+
+#define BOARD_USDHC_MMCMODE_PRESCALER USDHC_SYSCTL_SDCLKFS_DIV8
+#define BOARD_USDHC_MMCMODE_DIVISOR USDHC_SYSCTL_DVS_DIV(1)
+
+#define BOARD_USDHC_SD1MODE_PRESCALER USDHC_SYSCTL_SDCLKFS_DIV8
+#define BOARD_USDHC_SD1MODE_DIVISOR USDHC_SYSCTL_DVS_DIV(1)
+
+/* 50MHz for 4-bit wide bus */
+
+#define BOARD_USDHC_SD4MODE_PRESCALER USDHC_SYSCTL_SDCLKFS_DIV4
+#define BOARD_USDHC_SD4MODE_DIVISOR USDHC_SYSCTL_DVS_DIV(1)
+
/* Set the PLL clocks as follows:
*
* - OSC24M : 24 MHz
diff --git a/boards/arm64/imx9/imx93-evk/src/Makefile b/boards/arm64/imx9/imx93-evk/src/Makefile
index 7849be43dc..6a9b3eebfa 100644
--- a/boards/arm64/imx9/imx93-evk/src/Makefile
+++ b/boards/arm64/imx9/imx93-evk/src/Makefile
@@ -38,4 +38,8 @@ ifeq ($(CONFIG_IMX9_LPSPI),y)
CSRCS += imx9_spi.c
endif
+ifeq ($(CONFIG_IMX9_USDHC),y)
+CSRCS += imx9_usdhc.c
+endif
+
include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h
index be6bceb339..f8125289f4 100644
--- a/boards/arm64/imx9/imx93-evk/src/imx93-evk.h
+++ b/boards/arm64/imx9/imx93-evk/src/imx93-evk.h
@@ -29,6 +29,24 @@
#include <stdint.h>
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Checking needed by MMC/SDCard */
+
+#ifdef CONFIG_NSH_MMCSDSLOTNO
+# define SDIO_SLOTNO CONFIG_NSH_MMCSDSLOTNO
+#else
+# define SDIO_SLOTNO 0
+#endif
+
+#ifdef CONFIG_NSH_MMCSDMINOR
+# define SDIO_MINOR CONFIG_NSH_MMCSDMINOR
+#else
+# define SDIO_MINOR 0
+#endif
+
/****************************************************************************
* Public Types
****************************************************************************/
@@ -91,5 +109,17 @@ int imx9_i2c_initialize(void);
int imx9_spi_initialize(void);
#endif
+/****************************************************************************
+ * Name: imx9_usdhc_init
+ *
+ * Description:
+ * Initialize uSDHC driver
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_MMCSD)
+int imx9_usdhc_init(void);
+#endif
+
#endif /* __ASSEMBLY__ */
#endif /* __BOARDS_ARM64_IMX9_IMX93_EVK_SRC_IMX93_EVK_H */
diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
index fc27bc3c95..57d2887c98 100644
--- a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
+++ b/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
@@ -99,6 +99,15 @@ int imx9_bringup(void)
}
#endif
+#ifdef CONFIG_MMCSD
+ ret = imx9_usdhc_init();
+
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Failed to init MMCSD driver: %d\n", ret);
+ }
+#endif
+
UNUSED(ret);
return OK;
}
diff --git a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c b/boards/arm64/imx9/imx93-evk/src/imx9_usdhc.c
similarity index 56%
copy from boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
copy to boards/arm64/imx9/imx93-evk/src/imx9_usdhc.c
index fc27bc3c95..54116fa795 100644
--- a/boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
+++ b/boards/arm64/imx9/imx93-evk/src/imx9_usdhc.c
@@ -1,5 +1,5 @@
/****************************************************************************
- * boards/arm64/imx9/imx93-evk/src/imx9_bringup.c
+ * boards/arm64/imx9/imx93-evk/src/imx9_usdhc.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -24,81 +24,61 @@
#include <nuttx/config.h>
-#include <sys/types.h>
-#include <syslog.h>
+#include <debug.h>
+#include <errno.h>
+#include <nuttx/mmcsd.h>
-#include <nuttx/fs/fs.h>
+#include "imx9_usdhc.h"
+#include "imx93-evk.h"
-#include "imx9_dma_alloc.h"
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
-#include "imx93-evk.h"
+static struct sdio_dev_s *g_sdio_dev;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: imx_bringup
+ * Name: imx9_usdhc_init
*
* Description:
- * Bring up board features
+ * Configure the uSDHC driver.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned
+ * to indicate the nature of any failure.
*
****************************************************************************/
-int imx9_bringup(void)
+int imx9_usdhc_init(void)
{
int ret;
-#ifdef CONFIG_FS_PROCFS
- /* Mount the procfs file system */
-
- ret = nx_mount(NULL, "/proc", "procfs", 0, NULL);
- if (ret < 0)
- {
- syslog(LOG_ERR, "ERROR: Failed to mount procfs at /proc: %d\n", ret);
- }
-#endif
-
-#ifdef CONFIG_IMX9_DMA_ALLOC
- /* Initialize the DMA memory allocator */
-
- ret = imx9_dma_alloc_init();
- if (ret < 0)
- {
- syslog(LOG_ERR, "ERROR: Failed initialize DMA allocator: %d\n", ret);
- }
-#endif
+ /* First, get an instance of the SDIO interface */
-#ifdef CONFIG_PWM
- /* Configure PWM outputs */
+ finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO);
- ret = imx9_pwm_setup();
- if (ret < 0)
+ g_sdio_dev = imx9_usdhc_initialize(SDIO_SLOTNO);
+ if (!g_sdio_dev)
{
- syslog(LOG_ERR, "ERROR: Failed initialize PWM outputs: %d\n", ret);
+ ferr("ERROR: Failed to initialize SDIO slot %d\n", SDIO_SLOTNO);
+ return -ENODEV;
}
-#endif
-#if defined(CONFIG_I2C_DRIVER)
- /* Configure I2C peripheral interfaces */
+ /* Now bind the SDIO interface to the MMC/SD driver */
- ret = imx9_i2c_initialize();
- if (ret < 0)
- {
- syslog(LOG_ERR, "Failed to initialize I2C driver: %d\n", ret);
- }
-#endif
+ finfo("Bind SDIO to the MMC/SD driver, minor=%d\n", SDIO_MINOR);
-#if defined(CONFIG_SPI_DRIVER)
- /* Configure SPI peripheral interfaces */
-
- ret = imx9_spi_initialize();
- if (ret < 0)
+ ret = mmcsd_slotinitialize(SDIO_MINOR, g_sdio_dev);
+ if (ret != OK)
{
- syslog(LOG_ERR, "Failed to initialize SPI driver: %d\n", ret);
+ ferr("ERROR: Failed to bind SDIO to the MMC/SD driver: %d\n", ret);
+ return ret;
}
-#endif
- UNUSED(ret);
return OK;
}
+
(nuttx) 03/04: arm64/imx9: Fix fat_dma_ macro issue.
Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 0d3a1bc6175b3445633174fafbdc92dba1bf18d2
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Tue May 7 14:07:02 2024 +0300
arm64/imx9: Fix fat_dma_ macro issue.
Defining fat_dma_alloc(s) as imx9_dma_alloc(s) causes compiler errors.
Signed-off-by: Jani Paalijarvi <ja...@unikie.com>
---
arch/arm64/src/imx9/imx9_dma_alloc.c | 12 ++++++++++++
arch/arm64/src/imx9/imx9_dma_alloc.h | 5 -----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/src/imx9/imx9_dma_alloc.c b/arch/arm64/src/imx9/imx9_dma_alloc.c
index b397f91127..172e2276db 100644
--- a/arch/arm64/src/imx9/imx9_dma_alloc.c
+++ b/arch/arm64/src/imx9/imx9_dma_alloc.c
@@ -142,4 +142,16 @@ void imx9_dma_free(void *memory, size_t size)
gran_free(dma_allocator, memory, size);
}
+#ifdef CONFIG_FAT_DMAMEMORY
+FAR void *fat_dma_alloc(size_t size)
+{
+ return imx9_dma_alloc(size);
+}
+
+void fat_dma_free(FAR void *memory, size_t size)
+{
+ imx9_dma_free(memory, size);
+}
+#endif
+
#endif /* CONFIG_IMX9_DMA_ALLOC */
diff --git a/arch/arm64/src/imx9/imx9_dma_alloc.h b/arch/arm64/src/imx9/imx9_dma_alloc.h
index dd98bdf369..d0c78ec451 100644
--- a/arch/arm64/src/imx9/imx9_dma_alloc.h
+++ b/arch/arm64/src/imx9/imx9_dma_alloc.h
@@ -80,9 +80,4 @@ void *imx9_dma_alloc(size_t size);
void imx9_dma_free(void *memory, size_t size);
-#ifdef CONFIG_FAT_DMAMEMORY
-# define fat_dma_alloc(s) imx9_dma_alloc(s)
-# define fat_dma_free(m,s) imx9_dma_free(m,s)
-#endif
-
#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_DMA_ALLOC_H */
(nuttx) 04/04: arm64/imx9: Allocate 64B granules instead of 256B
Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 5b3ef206299c801b0451848b0c5f50de3963be5a
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Tue May 7 15:14:13 2024 +0300
arm64/imx9: Allocate 64B granules instead of 256B
Optimal size of granule is 64B (the dcache line size).
We can use it now as we don't have max. 32 granules limitation anymore.
Signed-off-by: Jani Paalijarvi <ja...@unikie.com>
---
arch/arm64/src/imx9/imx9_dma_alloc.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/arch/arm64/src/imx9/imx9_dma_alloc.c b/arch/arm64/src/imx9/imx9_dma_alloc.c
index 172e2276db..1708e1ae69 100644
--- a/arch/arm64/src/imx9/imx9_dma_alloc.c
+++ b/arch/arm64/src/imx9/imx9_dma_alloc.c
@@ -89,11 +89,7 @@ int imx9_dma_alloc_init(void)
{
/* Allocate 64B granules with 64B alignment */
- /* REVISIT: Use 256B granule size to get 8K maximum allocation. This is a
- * limitation in the granule allocator itself.
- */
-
- dma_allocator = gran_initialize(g_dma_heap, sizeof(g_dma_heap), 8, 6);
+ dma_allocator = gran_initialize(g_dma_heap, sizeof(g_dma_heap), 6, 6);
if (dma_allocator == NULL)
{