diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 79cede6bb5..2405f9e332 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -195,6 +195,20 @@ config STM32H7_USBHOST_PKTDUMP default n depends on USBHOST +config STM32H7_SDMMC1 + bool "SDMMC1" + default n + select STM32H7_SDMMC + select ARCH_HAVE_SDIO + select ARCH_HAVE_SDIOWAIT_WRCOMPLETE + +config STM32H7_SDMMC2 + bool "SDMMC2" + default n + select STM32H7_SDMMC + select ARCH_HAVE_SDIO + select ARCH_HAVE_SDIOWAIT_WRCOMPLETE + menu "STM32H7 I2C Selection" config STM32H7_I2C1 @@ -763,6 +777,83 @@ config STM32H7_PM_SERIAL_ACTIVITY endif # PM endmenu # U[S]ART Configuration +menu "SD/MMC Configuration" + depends on STM32H7_SDMMC + +config STM32H7_SDMMC_XFRDEBUG + bool "SDMMC transfer debug" + depends on DEBUG_FS_INFO + default n + ---help--- + Enable special debug instrumentation analyze SDMMC data transfers. + This logic is as non-invasive as possible: It samples SDMMC + registers at key points in the data transfer and then dumps all of + the registers at the end of the transfer. If DEBUG_DMA is also + enabled, then DMA register will be collected as well. Requires also + DEBUG_FS and CONFIG_DEBUG_INFO. + +config STM32H7_SDMMC_IDMA + bool "Support IDMA data transfers" + default y + select STM32H7_SDMMC_DMA + depends on STM32H7_DMA + ---help--- + Support IDMA data transfers. + +menu "SDMMC1 Configuration" + depends on STM32H7_SDMMC1 + +config SDMMC1_WIDTH_D1_ONLY + bool "Use D1 only on SDMMC1" + default n + ---help--- + Select 1-bit transfer mode. Default: 4-bit transfer mode. + +config SDMMC1_SDIO_MODE + bool "SDIO Card Support" + default n + ---help--- + Build in additional support needed only for SDIO cards (vs. SD + memory cards) + +config SDMMC1_SDIO_PULLUP + bool "Enable internal Pull-Ups" + default n + ---help--- + If you are using an external SDCard module that does not have the + pull-up resistors for the SDIO interface (like the Gadgeteer SD Card + Module) then enable this option to activate the internal pull-up + resistors. + +endmenu # "SDMMC1 Configuration" + +menu "SDMMC2 Configuration" + depends on STM32H7_SDMMC2 + +config SDMMC2_WIDTH_D1_ONLY + bool "Use D1 only on SDMMC2" + default n + ---help--- + Select 1-bit transfer mode. Default: 4-bit transfer mode. + +config SDMMC2_SDIO_MODE + bool "SDIO Card Support" + default n + ---help--- + Build in additional support needed only for SDIO cards (vs. SD + memory cards) + +config SDMMC2_SDIO_PULLUP + bool "Enable internal Pull-Ups" + default n + ---help--- + If you are using an external SDCard module that does not have the + pull-up resistors for the SDIO interface (like the Gadgeteer SD Card + Module) then enable this option to activate the internal pull-up + resistors. + +endmenu # "SDMMC2 Configuration" +endmenu # "SD/MMC Configuration" config STM32H7_CUSTOM_CLOCKCONFIG bool "Custom clock configuration" default n diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index 45c1cccb84..6d7984ff25 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -114,6 +114,10 @@ ifeq ($(CONFIG_STM32H7_SPI),y) CHIP_CSRCS += stm32_spi.c endif +ifeq ($(CONFIG_STM32H7_SDMMC),y) +CHIP_CSRCS += stm32_sdmmc.c +endif + ifeq ($(CONFIG_USBDEV),y) CHIP_CSRCS += stm32_otgdev.c endif diff --git a/arch/arm/src/stm32h7/chip/stm32_sdmmc.h b/arch/arm/src/stm32h7/chip/stm32_sdmmc.h new file mode 100644 index 0000000000..e258ac9d7c --- /dev/null +++ b/arch/arm/src/stm32h7/chip/stm32_sdmmc.h @@ -0,0 +1,54 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/chip/stm32_sdmmc.h + * + * Copyright (C) 2016, 2019 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * Jukka Laitinen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_CHIP_STM32_SDMMC_H +#define __ARCH_ARM_SRC_STM32H7_CHIP_STM32_SDMMC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip.h" + +#if defined(CONFIG_STM32H7_STM32H7X3XX) +# include "stm32h7x3xx_sdmmc.h" +#else +# error "Unsupported STM32 H7 part" +#endif + +#endif /* __ARCH_ARM_SRC_STM32H7_CHIP_STM32_SDMMC_H */ diff --git a/arch/arm/src/stm32h7/chip/stm32h7x3xx_sdmmc.h b/arch/arm/src/stm32h7/chip/stm32h7x3xx_sdmmc.h new file mode 100644 index 0000000000..99fc5adcef --- /dev/null +++ b/arch/arm/src/stm32h7/chip/stm32h7x3xx_sdmmc.h @@ -0,0 +1,246 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/chip/stm32h7x3xx_sdmmc.h + * + * Copyright (C) 2009, 2011-2016, 2019 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * Jukka Laitinen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_CHIP_STM32H7X3XX_SDMMC_H +#define __ARCH_ARM_SRC_STM32H7_CHIP_STM32H7X3XX_SDMMC_H + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define STM32_SDMMC_POWER_OFFSET 0x0000 /* SDMMC power control register */ +#define STM32_SDMMC_CLKCR_OFFSET 0x0004 /* SDMMC clock control register */ +#define STM32_SDMMC_ARG_OFFSET 0x0008 /* SDMMC argument register */ +#define STM32_SDMMC_CMD_OFFSET 0x000c /* SDMMC command register */ +#define STM32_SDMMC_RESPCMD_OFFSET 0x0010 /* SDMMC command response register */ +#define STM32_SDMMC_RESP_OFFSET(n) (0x0010+4*(n)) +#define STM32_SDMMC_RESP1_OFFSET 0x0014 /* SDMMC response 1 register */ +#define STM32_SDMMC_RESP2_OFFSET 0x0018 /* SDMMC response 2 register */ +#define STM32_SDMMC_RESP3_OFFSET 0x001c /* SDMMC response 3 register */ +#define STM32_SDMMC_RESP4_OFFSET 0x0020 /* SDMMC response 4 register */ +#define STM32_SDMMC_DTIMER_OFFSET 0x0024 /* SDMMC data timer register */ +#define STM32_SDMMC_DLEN_OFFSET 0x0028 /* SDMMC data length register */ +#define STM32_SDMMC_DCTRL_OFFSET 0x002c /* SDMMC data control register */ +#define STM32_SDMMC_DCOUNT_OFFSET 0x0030 /* SDMMC data counter register */ +#define STM32_SDMMC_STA_OFFSET 0x0034 /* SDMMC status register */ +#define STM32_SDMMC_ICR_OFFSET 0x0038 /* SDMMC interrupt clear register */ +#define STM32_SDMMC_MASK_OFFSET 0x003c /* SDMMC mask register */ +#define STM32_SDMMC_IDMACTRLR_OFFSET 0x0050 /* SDMMC DMA control register */ +#define STM32_SDMMC_IDMABASE0R_OFFSET 0x0058 /* SDMMC IDMA buffer 0 base register */ +#define STM32_SDMMC_FIFO_OFFSET 0x0080 /* SDMMC data FIFO register */ + +/* Register Bitfield Definitions ****************************************************/ + +#define STM32_SDMMC_POWER_PWRCTRL_SHIFT (0) /* Bits 0-1: Power supply control bits */ +#define STM32_SDMMC_POWER_PWRCTRL_MASK (3 << STM32_SDMMC_POWER_PWRCTRL_SHIFT) +# define STM32_SDMMC_POWER_PWRCTRL_OFF (0 << STM32_SDMMC_POWER_PWRCTRL_SHIFT) /* 00: Power-off: card clock stopped */ +# define STM32_SDMMC_POWER_PWRCTRL_CYCLE (2 << STM32_SDMMC_POWER_PWRCTRL_SHIFT) /* 10: Reserved power-up */ +# define STM32_SDMMC_POWER_PWRCTRL_ON (3 << STM32_SDMMC_POWER_PWRCTRL_SHIFT) /* 11: Power-on: card is clocked */ + +#define STM32_SDMMC_POWER_RESET (0) /* Reset value */ + +#define STM32_SDMMC_CLKCR_CLKDIV_SHIFT (0) /* Bits 9-0: Clock divide factor */ +#define STM32_SDMMC_CLKCR_CLKDIV_MASK (0x3ff << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) +#define STM32_SDMMC_CLKCR_PWRSAV (1 << 12) /* Bit 12: Power saving configuration bit */ +#define STM32_SDMMC_CLKCR_WIDBUS_SHIFT (14) /* Bits 12-11: Wide bus mode enable bits */ +#define STM32_SDMMC_CLKCR_WIDBUS_MASK (3 << STM32_SDMMC_CLKCR_WIDBUS_SHIFT) +# define STM32_SDMMC_CLKCR_WIDBUS_D1 (0 << STM32_SDMMC_CLKCR_WIDBUS_SHIFT) /* 00: Default (STM32_SDMMC_D0) */ +# define STM32_SDMMC_CLKCR_WIDBUS_D4 (1 << STM32_SDMMC_CLKCR_WIDBUS_SHIFT) /* 01: 4-wide (STM32_SDMMC_D[3:0]) */ +# define STM32_SDMMC_CLKCR_WIDBUS_D8 (2 << STM32_SDMMC_CLKCR_WIDBUS_SHIFT) /* 10: 8-wide (STM32_SDMMC_D[7:0]) */ +#define STM32_SDMMC_CLKCR_NEGEDGE (1 << 16) /* Bit 16: STM32_SDMMC_CK dephasing selection bit */ +#define STM32_SDMMC_CLKCR_HWFC_EN (1 << 17) /* Bit 17: HW Flow Control enable */ + +#define STM32_SDMMC_CLKCR_RESET (0) /* Reset value */ + +#define STM32_SDMMC_ARG_RESET (0) /* Reset value */ + +#define STM32_SDMMC_CMD_CMDINDEX_SHIFT (0) +#define STM32_SDMMC_CMD_CMDINDEX_MASK (0x3f << STM32_SDMMC_CMD_CMDINDEX_SHIFT) +#define STM32_SDMMC_CMD_CMDTRANS (1 << 6) /* Bit 6: The CPSM treats the command as a data transfer command */ +#define STM32_SDMMC_CMD_CMDSTOP (1 << 7) /* Bit 7: The CPSM treats the command as a Stop Transmission command */ +#define STM32_SDMMC_CMD_WAITRESP_SHIFT (8) /* Bits 8-9: Wait for response bits */ +#define STM32_SDMMC_CMD_WAITRESP_MASK (3 << STM32_SDMMC_CMD_WAITRESP_SHIFT) +# define STM32_SDMMC_CMD_NORESPONSE (0 << STM32_SDMMC_CMD_WAITRESP_SHIFT) /* 00/10: No response */ +# define STM32_SDMMC_CMD_SHORTRESPONSE (1 << STM32_SDMMC_CMD_WAITRESP_SHIFT) /* 01: Short response */ +# define STM32_SDMMC_CMD_SHORTRESPONSE_NOCRC (2 << STM32_SDMMC_CMD_WAITRESP_SHIFT) /* 01: Short response, no CRC */ +# define STM32_SDMMC_CMD_LONGRESPONSE (3 << STM32_SDMMC_CMD_WAITRESP_SHIFT) /* 11: Long response */ +#define STM32_SDMMC_CMD_WAITINT (1 << 10) /* Bit 10: CPSM waits for interrupt request */ +#define STM32_SDMMC_CMD_WAITPEND (1 << 11) /* Bit 11: CPSM Waits for ends of data transfer */ +#define STM32_SDMMC_CMD_CPSMEN (1 << 12) /* Bit 12: Command path state machine enable */ +#define STM32_SDMMC_CMD_DTHOLD (1 << 13) /* Bit 13: Hold new data block transmission and reception in the DPSM */ +#define STM32_SDMMC_CMD_BOOTMODE (1 << 14) /* Bit 14: Select bootmode */ +#define STM32_SDMMC_CMD_BOOTEN (1 << 15) /* Bit 15: Enable boot mode */ +#define STM32_SDMMC_CMD_SUSPEND (1 << 16) /* Bit 16: SD I/O suspend command */ + +#define STM32_SDMMC_CMD_RESET (0) /* Reset value */ + +#define STM32_SDMMC_RESPCMD_SHIFT (0) +#define STM32_SDMMC_RESPCMD_MASK (0x3f << STM32_SDMMC_RESPCMD_SHIFT) + +#define STM32_SDMMC_DTIMER_RESET (0) /* Reset value */ + +#define STM32_SDMMC_DLEN_SHIFT (0) +#define STM32_SDMMC_DLEN_MASK (0x01ffffff << STM32_SDMMC_DLEN_SHIFT) + +#define STM32_SDMMC_DLEN_RESET (0) /* Reset value */ + +#define STM32_SDMMC_DCTRL_DTEN (1 << 0) /* Bit 0: Data transfer enabled bit */ +#define STM32_SDMMC_DCTRL_DTDIR (1 << 1) /* Bit 1: Data transfer direction */ +#define STM32_SDMMC_DCTRL_DTMODE_SHIFT (2) /* Bits 2-3: Data transfer mode */ +#define STM32_SDMMC_DCTRL_DTMODE_MASK (3 << STM32_SDMMC_DCTRL_DTMODE_SHIFT) +# define STM32_SDMMC_DCTRL_DTMODE_END (0 << STM32_SDMMC_DCTRL_DTMODE_SHIFT) +# define STM32_SDMMC_DCTRL_DTMODE_SDIO (1 << STM32_SDMMC_DCTRL_DTMODE_SHIFT) +# define STM32_SDMMC_DCTRL_DTMODE_EMMC (2 << STM32_SDMMC_DCTRL_DTMODE_SHIFT) +# define STM32_SDMMC_DCTRL_DTMODE_BLOCK (3 << STM32_SDMMC_DCTRL_DTMODE_SHIFT) + +#define STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT (4) /* Bits 7-4: Data block size */ +#define STM32_SDMMC_DCTRL_DBLOCKSIZE_MASK (0xf << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_1BYTE (0 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_2BYTES (1 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_4BYTES (2 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_8BYTES (3 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_16BYTES (4 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_32BYTES (5 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_64BYTES (6 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_128BYTES (7 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_256BYTES (8 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_512BYTES (9 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_1KBYTE (10 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_2KBYTES (11 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_4KBYTES (12 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_8KBYTES (13 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +# define STM32_SDMMC_DCTRL_16KBYTES (14 << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT) +#define STM32_SDMMC_DCTRL_RWSTART (1 << 8) /* Bit 8: Read wait start */ +#define STM32_SDMMC_DCTRL_RWSTOP (1 << 9) /* Bit 9: Read wait stop */ +#define STM32_SDMMC_DCTRL_RWMOD (1 << 10) /* Bit 10: Read wait mode */ +#define STM32_SDMMC_DCTRL_SDIOEN (1 << 11) /* Bit 11: SD I/O enable functions */ +#define STM32_SDMMC_DCTRL_BOOTACKEN (1 << 12) /* Bit 12: Enable reception of boot ack */ +#define STM32_SDMMC_DCTRL_FIFORST (1 << 13) /* Bit 13: FIFO reset */ + +#define STM32_SDMMC_DCTRL_RESET (0) /* Reset value */ + +#define STM32_SDMMC_DCOUNT_SHIFT (0) +#define STM32_SDMMC_DCOUNT_MASK (0x01ffffff << STM32_SDMMC_DCOUNT_SHIFT) + +#define STM32_SDMMC_STA_CCRCFAIL (1 << 0) /* Bit 0: Command response CRC fail */ +#define STM32_SDMMC_STA_DCRCFAIL (1 << 1) /* Bit 1: Data block CRC fail */ +#define STM32_SDMMC_STA_CTIMEOUT (1 << 2) /* Bit 2: Command response timeout */ +#define STM32_SDMMC_STA_DTIMEOUT (1 << 3) /* Bit 3: Data timeout */ +#define STM32_SDMMC_STA_TXUNDERR (1 << 4) /* Bit 4: Transmit FIFO underrun error */ +#define STM32_SDMMC_STA_RXOVERR (1 << 5) /* Bit 5: Received FIFO overrun error */ +#define STM32_SDMMC_STA_CMDREND (1 << 6) /* Bit 6: Command response received */ +#define STM32_SDMMC_STA_CMDSENT (1 << 7) /* Bit 7: Command sent */ +#define STM32_SDMMC_STA_DATAEND (1 << 8) /* Bit 8: Data end */ +#define STM32_SDMMC_STA_DHOLD (1 << 9) /* Bit 9: Data transfer hold */ +#define STM32_SDMMC_STA_DBCKEND (1 << 10) /* Bit 10: Data block sent/received */ +#define STM32_SDMMC_STA_DABORT (1 << 11) /* Bit 11: Data transfer aborted by CMD12 */ +#define STM32_SDMMC_STA_DPSMACT (1 << 12) /* Bit 12: Data path state machine active */ +#define STM32_SDMMC_STA_CPSMACT (1 << 13) /* Bit 13: Command path state machine active */ +#define STM32_SDMMC_STA_TXFIFOHE (1 << 14) /* Bit 14: Transmit FIFO half empty */ +#define STM32_SDMMC_STA_RXFIFOHF (1 << 15) /* Bit 15: Receive FIFO half full */ +#define STM32_SDMMC_STA_TXFIFOF (1 << 16) /* Bit 16: Transmit FIFO full */ +#define STM32_SDMMC_STA_RXFIFOF (1 << 17) /* Bit 17: Receive FIFO full */ +#define STM32_SDMMC_STA_TXFIFOE (1 << 18) /* Bit 18: Transmit FIFO empty */ +#define STM32_SDMMC_STA_RXFIFOE (1 << 19) /* Bit 19: Receive FIFO empty */ +#define STM32_SDMMC_STA_BUSYD0 (1 << 20) /* Bit 20: Card signals busy on SDMMC_D0 */ +#define STM32_SDMMC_STA_BUSYD0END (1 << 21) /* Bit 21: card SDMMC_D0 signal changed from busy to NOT busy */ +#define STM32_SDMMC_STA_SDIOIT (1 << 22) /* Bit 22: SDIO interrupt received */ +#define STM32_SDMMC_STA_ACKFAIL (1 << 23) /* Bit 23: Boot ack check fail */ +#define STM32_SDMMC_STA_ACKTIMEOUT (1 << 24) /* Bit 24: Boot ack timeout */ +#define STM32_SDMMC_STA_VSWEND (1 << 25) /* Bit 25: Voltage switch critical timing section completion */ +#define STM32_SDMMC_STA_CKSTOP (1 << 26) /* Bit 26: SDMMC_CK stopped in Voltage switch procedure */ +#define STM32_SDMMC_STA_IDMATE (1 << 27) /* Bit 27: IDMA transfer error */ +#define STM32_SDMMC_STA_IDMABTC (1 << 28) /* Bit 28: IDMA buffer transfer complete */ + +#define STM32_SDMMC_ICR_CCRCFAILC (1 << 0) /* Bit 0: CCRCFAIL flag clear bit */ +#define STM32_SDMMC_ICR_DCRCFAILC (1 << 1) /* Bit 1: DCRCFAIL flag clear bit */ +#define STM32_SDMMC_ICR_CTIMEOUTC (1 << 2) /* Bit 2: CTIMEOUT flag clear bit */ +#define STM32_SDMMC_ICR_DTIMEOUTC (1 << 3) /* Bit 3: DTIMEOUT flag clear bit */ +#define STM32_SDMMC_ICR_TXUNDERRC (1 << 4) /* Bit 4: TXUNDERR flag clear bit */ +#define STM32_SDMMC_ICR_RXOVERRC (1 << 5) /* Bit 5: RXOVERR flag clear bit */ +#define STM32_SDMMC_ICR_CMDRENDC (1 << 6) /* Bit 6: CMDREND flag clear bit */ +#define STM32_SDMMC_ICR_CMDSENTC (1 << 7) /* Bit 7: CMDSENT flag clear bit */ +#define STM32_SDMMC_ICR_DATAENDC (1 << 8) /* Bit 8: DATAEND flag clear bit */ +#define STM32_SDMMC_ICR_DHOLDC (1 << 9) /* Bit 9: DHOLD flag clear bit */ +#define STM32_SDMMC_ICR_DBCKENDC (1 << 10) /* Bit 10: DBCKEND flag clear bit */ +#define STM32_SDMMC_ICR_DABORTC (1 << 11) /* Bit 11: DABORT flag clear bit */ +#define STM32_SDMMC_ICR_BUSYD0ENDC (1 << 21) /* Bit 21: BUSYD0END flag clear bit */ +#define STM32_SDMMC_ICR_SDIOITC (1 << 22) /* Bit 22: SDIOIT flag clear bit */ +#define STM32_SDMMC_ICR_ACKFAILC (1 << 23) /* Bit 23: ACKFAIL flag clear bit */ +#define STM32_SDMMC_ICR_ACKTIMEOUTC (1 << 24) /* Bit 24: ACKTIMEOUT flag clear bit */ +#define STM32_SDMMC_ICR_VSWENDC (1 << 25) /* Bit 25: VSWEND flag clear bit */ +#define STM32_SDMMC_ICR_CKSTOPC (1 << 26) /* Bit 26: CKSTOP flag clear bit */ +#define STM32_SDMMC_ICR_IDMATEC (1 << 27) /* Bit 27: IDMA transfer error clear bit */ +#define STM32_SDMMC_ICR_IDMABTCC (1 << 28) /* Bit 28: IDMA buffer transfer complete clear bit */ + +#define STM32_SDMMC_ICR_RESET 0x00000000 +#define STM32_SDMMC_ICR_ALLFLAGS 0x1fe00fff + +#define STM32_SDMMC_MASK_CCRCFAILIE (1 << 0) /* Bit 0: Command CRC fail interrupt enable */ +#define STM32_SDMMC_MASK_DCRCFAILIE (1 << 1) /* Bit 1: Data CRC fail interrupt enable */ +#define STM32_SDMMC_MASK_CTIMEOUTIE (1 << 2) /* Bit 2: Command timeout interrupt enable */ +#define STM32_SDMMC_MASK_DTIMEOUTIE (1 << 3) /* Bit 3: Data timeout interrupt enable */ +#define STM32_SDMMC_MASK_TXUNDERRIE (1 << 4) /* Bit 4: Tx FIFO underrun error interrupt enable */ +#define STM32_SDMMC_MASK_RXOVERRIE (1 << 5) /* Bit 5: Rx FIFO overrun error interrupt enable */ +#define STM32_SDMMC_MASK_CMDRENDIE (1 << 6) /* Bit 6: Command response received interrupt enable */ +#define STM32_SDMMC_MASK_CMDSENTIE (1 << 7) /* Bit 7: Command sent interrupt enable */ +#define STM32_SDMMC_MASK_DATAENDIE (1 << 8) /* Bit 8: Data end interrupt enable */ +#define STM32_SDMMC_MASK_DHOLDIE (1 << 9) /* Bit 9: Data hold interrupt enable */ +#define STM32_SDMMC_MASK_DBCKENDIE (1 << 10) /* Bit 10: Data block end interrupt enable */ +#define STM32_SDMMC_MASK_DABOTRTIE (1 << 11) /* Bit 11: Data transfer aborted interrupt enable */ +#define STM32_SDMMC_MASK_TXFIFOHEIE (1 << 14) /* Bit 14: Tx FIFO half empty interrupt enable */ +#define STM32_SDMMC_MASK_RXFIFOHFIE (1 << 15) /* Bit 15: Rx FIFO half full interrupt enable */ +#define STM32_SDMMC_MASK_RXFIFOFIE (1 << 17) /* Bit 17: Rx FIFO full interrupt enable */ +#define STM32_SDMMC_MASK_TXFIFOFIE (1 << 18) /* Bit 18: Tx FIFO full interrupt enable */ +#define STM32_SDMMC_MASK_BUSYD0ENDIE (1 << 21) /* Bit 21: BUSYD0END interrupt enable */ +#define STM32_SDMMC_MASK_SDIOITIE (1 << 22) /* Bit 22: SDIO mode interrupt received interrupt enable */ +#define STM32_SDMMC_MASK_ACKFAILIE (1 << 23) /* Bit 23: Acknowledgment Fail interrupt enable */ +#define STM32_SDMMC_MASK_ACKTIMEOUTIE (1 << 24) /* Bit 24: Acknowledgment timeout interrupt enable */ +#define STM32_SDMMC_MASK_VSWENDIE (1 << 25) /* Bit 25: Voltage switch critical timing section completion interrupt enable */ +#define STM32_SDMMC_MASK_CKSTOPIE (1 << 26) /* Bit 26: Voltage Switch clock stopped interrupt enable */ +#define STM32_SDMMC_MASK_IDMABTCIE (1 << 28) /* Bit 28: IDMA buffer transfer complete interrupt enable */ + +#define STM32_SDMMC_MASK_RESET (0) + +#define STM32_SDMMC_IDMACTRLR_IDMAEN (1 << 0) + +#define STM32_SDMMC_FIFOCNT_SHIFT (0) +#define STM32_SDMMC_FIFOCNT_MASK (0x0ffffff << STM32_SDMMC_FIFOCNT_SHIFT) + +#endif /* __ARCH_ARM_SRC_STM32F7_CHIP_STM32H7X3XX_SDMMC_H */ diff --git a/arch/arm/src/stm32h7/stm32_sdmmc.c b/arch/arm/src/stm32h7/stm32_sdmmc.c new file mode 100644 index 0000000000..7d711a8fbf --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_sdmmc.c @@ -0,0 +1,2995 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_sdmmc.c + * + * Copyright (C) 2009, 2011-2017, 2019 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * David Sidrane + * Jukka Laitinen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "chip.h" +#include "up_arch.h" + +#include "stm32_dma.h" +#include "stm32_gpio.h" +#include "stm32_rcc.h" +#include "stm32_sdmmc.h" + +#if defined(CONFIG_STM32H7_SDMMC1) || defined(CONFIG_STM32H7_SDMMC2) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Required system configuration options: + * + * CONFIG_ARCH_DMA - Enable architecture-specific DMA subsystem + * initialization. Required if CONFIG_SDMMC[1|2]_DMA is enabled. + * CONFIG_SCHED_WORKQUEUE -- Callback support requires work queue support. + * + * Driver-specific configuration options: + * + * CONFIG_SDIO_MUXBUS - Setting this configuration enables some locking + * APIs to manage concurrent accesses on the SDMMC bus. This is not + * needed for the simple case of a single SD card, for example. + * CONFIG_STM32H7_SDMMC_IDMA - Enable SDMMC IDMA. + * CONFIG_STM32H7_SDMMC_MDMA - Enable SDMMC MDMA. + * DMA support for SDMMC. These are alternative to each other. If both are + * disabled, the SDMMC will work in interrupt mode + * CONFIG_SDMMC1/2_WIDTH_D1_ONLY - This may be selected to force the driver + * operate with only a single data line (the default is to use all + * 4 SD data lines). + * CONFIG_SDMMC_PRI - SDMMC interrupt priority. This setting is not very + * important since interrupt nesting is not currently supported. + * CONFIG_CONFIG_STM32H7_SDMMC_XFRDEBUG - Enables some very low-level debug + * output This also requires CONFIG_DEBUG_FS and CONFIG_DEBUG_INFO + */ + +#ifndef CONFIG_STM32H7_SDMMC_IDMA +# error "Currently only IDMA support implemented" +#endif + +#if !defined(CONFIG_STM32H7_SDMMC_IDMA) && !defined (CONFIG_STM32H7_SDMMC_MDMA) +# warning "Large Non-DMA transfer may result in RX overrun failures" +#else +# ifndef CONFIG_STM32H7_SDMMC_DMA +# define CONFIG_STM32H7_SDMMC_DMA +# endif +#endif + +#ifndef CONFIG_SCHED_WORKQUEUE +# error "Callback support requires CONFIG_SCHED_WORKQUEUE" +#endif + +#ifdef CONFIG_STM32H7_SDMMC1 +# if defined(CONFIG_ARCH_IRQPRIO) && !defined(CONFIG_SDMMC1_PRI) +# define CONFIG_SDMMC1_PRI NVIC_SYSH_PRIORITY_DEFAULT +# endif +#endif + +#ifdef CONFIG_STM32H7_SDMMC2 +# if defined(CONFIG_ARCH_IRQPRIO) && !defined(CONFIG_SDMMC2_PRI) +# define CONFIG_SDMMC2_PRI NVIC_SYSH_PRIORITY_DEFAULT +# endif +#endif + +#if !defined(CONFIG_DEBUG_FS) || !defined(CONFIG_DEBUG_FEATURES) +# undef CONFIG_CONFIG_STM32H7_SDMMC_XFRDEBUG +#endif + +/* Friendly CLKCR bit re-definitions ****************************************/ + +#define STM32_CLKCR_RISINGEDGE (0) +#define STM32_CLKCR_FALLINGEDGE STM32_SDMMC_CLKCR_NEGEDGE + +/* Use the default of the rising edge but allow a configuration, + * that does not have the errata, to override the edge the SDIO + * command and data is changed on. + */ + +#if !defined(STM32_SDMMC_CLKCR_EDGE) +# define STM32_SDMMC_CLKCR_EDGE STM32_CLKCR_RISINGEDGE +#endif + +/* Mode dependent settings. These depend on clock divisor settings that must + * be defined in the board-specific board.h header file: STM32_SDMMC_INIT_CLKDIV, + * STM32_SDMMC_MMCXFR_CLKDIV, and STM32_SDMMC_SDXFR_CLKDIV. + */ + +#define STM32_CLCKCR_INIT (STM32_SDMMC_INIT_CLKDIV | \ + STM32_SDMMC_CLKCR_EDGE | \ + STM32_SDMMC_CLKCR_WIDBUS_D1) +#define STM32_SDMMC_CLKCR_MMCXFR (STM32_SDMMC_MMCXFR_CLKDIV | \ + STM32_SDMMC_CLKCR_EDGE | \ + STM32_SDMMC_CLKCR_PWRSAV | \ + STM32_SDMMC_CLKCR_WIDBUS_D1) +#define STM32_SDMMC_CLCKR_SDXFR (STM32_SDMMC_SDXFR_CLKDIV | \ + STM32_SDMMC_CLKCR_EDGE | \ + STM32_SDMMC_CLKCR_PWRSAV | \ + STM32_SDMMC_CLKCR_WIDBUS_D1) +#define STM32_SDMMC_CLCKR_SDWIDEXFR (STM32_SDMMC_SDXFR_CLKDIV | \ + STM32_SDMMC_CLKCR_EDGE | \ + STM32_SDMMC_CLKCR_PWRSAV | \ + STM32_SDMMC_CLKCR_WIDBUS_D4) + +/* Timing */ + +#define SDMMC_CMDTIMEOUT (100000) +#define SDMMC_LONGTIMEOUT (0x7fffffff) + +/* Big DTIMER setting */ + +#define SDMMC_DTIMER_DATATIMEOUT (0x000fffff) + +/* FIFO sizes */ + +#define SDMMC_HALFFIFO_WORDS (8) +#define SDMMC_HALFFIFO_BYTES (8*4) + +/* Data transfer interrupt mask bits */ + +#if defined(CONFIG_STM32H7_SDMMC_DMA) +/* DMA interrupts */ + +#define STM32_SDMMC_RECV_MASK (STM32_SDMMC_MASK_DCRCFAILIE | \ + STM32_SDMMC_MASK_DTIMEOUTIE | \ + STM32_SDMMC_MASK_DATAENDIE | \ + STM32_SDMMC_MASK_RXOVERRIE) + +#define STM32_SDMMC_SEND_MASK (STM32_SDMMC_MASK_DCRCFAILIE | \ + STM32_SDMMC_MASK_DTIMEOUTIE | \ + STM32_SDMMC_MASK_DATAENDIE | \ + STM32_SDMMC_MASK_TXUNDERRIE) +#define STM32_SDMMC_XFRDONE_MASK (0) +#else +/* Interrupt mode */ + +#define STM32_SDMMC_RECV_MASK (STM32_SDMMC_MASK_DCRCFAILIE | \ + STM32_SDMMC_MASK_DTIMEOUTIE | \ + STM32_SDMMC_MASK_DATAENDIE | \ + STM32_SDMMC_MASK_RXOVERRIE | \ + STM32_SDMMC_MASK_RXFIFOHFIE) + +#define STM32_SDMMC_SEND_MASK (STM32_SDMMC_MASK_DCRCFAILIE | \ + STM32_SDMMC_MASK_DTIMEOUTIE | \ + STM32_SDMMC_MASK_DATAENDIE | \ + STM32_SDMMC_MASK_TXFIFOHEIE | \ + STM32_SDMMC_MASK_TXUNDERRIE) + +#endif + +/* Event waiting interrupt mask bits */ + +#define STM32_SDMMC_CMDDONE_STA (STM32_SDMMC_STA_CMDSENT) + +#define STM32_SDMMC_RESPDONE_STA (STM32_SDMMC_STA_CTIMEOUT | \ + STM32_SDMMC_STA_CCRCFAIL | \ + STM32_SDMMC_STA_CMDREND) + +#define STM32_SDMMC_CMDDONE_MASK (STM32_SDMMC_MASK_CMDSENTIE) + +#define STM32_SDMMC_RESPDONE_MASK (STM32_SDMMC_MASK_CCRCFAILIE | \ + STM32_SDMMC_MASK_CTIMEOUTIE | \ + STM32_SDMMC_MASK_CMDRENDIE) + +#define STM32_SDMMC_XFRDONE_MASK (0) + +#define STM32_SDMMC_CMDDONE_ICR (STM32_SDMMC_ICR_CMDSENTC | \ + STM32_SDMMC_ICR_DBCKENDC) + +#define STM32_SDMMC_RESPDONE_ICR (STM32_SDMMC_ICR_CTIMEOUTC | \ + STM32_SDMMC_ICR_CCRCFAILC | \ + STM32_SDMMC_ICR_CMDRENDC | \ + STM32_SDMMC_ICR_DBCKENDC) + +#define STM32_SDMMC_XFRDONE_ICR (STM32_SDMMC_ICR_DATAENDC | \ + STM32_SDMMC_ICR_DCRCFAILC | \ + STM32_SDMMC_ICR_DTIMEOUTC | \ + STM32_SDMMC_ICR_RXOVERRC | \ + STM32_SDMMC_ICR_TXUNDERRC | \ + STM32_SDMMC_ICR_DBCKENDC) + +#define STM32_SDMMC_WAITALL_ICR (STM32_SDMMC_CMDDONE_ICR | \ + STM32_SDMMC_RESPDONE_ICR | \ + STM32_SDMMC_XFRDONE_ICR | \ + STM32_SDMMC_ICR_DBCKENDC) + +/* Let's wait until we have both SDIO transfer complete and DMA complete. */ + +#define SDMMC_XFRDONE_FLAG (1) +#define SDMMC_DMADONE_FLAG (2) +#define SDMMC_ALLDONE (3) + +/* Register logging support */ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +# ifdef CONFIG_STM32H7_SDMMC_MDMA +# define SAMPLENDX_BEFORE_SETUP 0 +# define SAMPLENDX_BEFORE_ENABLE 1 +# define SAMPLENDX_AFTER_SETUP 2 +# define SAMPLENDX_END_TRANSFER 3 +# define SAMPLENDX_DMA_CALLBACK 4 +# define DEBUG_NSAMPLES 5 +# else +# define SAMPLENDX_BEFORE_SETUP 0 +# define SAMPLENDX_AFTER_SETUP 1 +# define SAMPLENDX_END_TRANSFER 2 +# define DEBUG_NSAMPLES 3 +# endif +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure defines the state of the STM32 SDMMC interface */ + +struct stm32_dev_s +{ + struct sdio_dev_s dev; /* Standard, base SDIO interface */ + + /* STM32-specific extensions */ + + uint32_t base; + int nirq; +#ifdef CONFIG_ARCH_IRQPRIO + int irqprio; +#endif +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + uint32_t d0_gpio; +#endif + + /* Event support */ + + sem_t waitsem; /* Implements event waiting */ + sdio_eventset_t waitevents; /* Set of events to be waited for */ + uint32_t waitmask; /* Interrupt enables for event waiting */ + volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */ + WDOG_ID 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 xfrmask; /* Interrupt enables for data transfer */ + + bool widebus; + bool onebit; /* true: Only 1-bit transfers are supported */ + + bool receive; /* true: data receive transfer */ + + /* MDMA data transfer support */ +}; + +/* Register logging support */ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +struct stm32_sdioregs_s +{ + uint8_t power; + uint16_t clkcr; + uint16_t dctrl; + uint32_t dtimer; + uint32_t dlen; + uint32_t dcount; + uint32_t sta; + uint32_t mask; + uint32_t fifocnt; +}; + +struct stm32_sampleregs_s +{ + struct stm32_sdioregs_s sdio; +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level helpers ********************************************************/ + +static inline void sdmmc_putreg32(struct stm32_dev_s *priv, uint32_t value, + int offset); +static inline uint32_t sdmmc_getreg32(struct stm32_dev_s *priv, int offset); +static void stm32_takesem(struct stm32_dev_s *priv); +#define stm32_givesem(priv) (nxsem_post(&priv->waitsem)) +static inline void stm32_setclkcr(struct stm32_dev_s *priv, uint32_t clkcr); +static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, + sdio_eventset_t wkupevents); +static void stm32_configxfrints(struct stm32_dev_s *priv, uint32_t xfrmask); +static void stm32_setpwrctrl(struct stm32_dev_s *priv, uint32_t pwrctrl); +static inline uint32_t stm32_getpwrctrl(struct stm32_dev_s *priv); + +/* DMA Helpers **************************************************************/ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static void stm32_sampleinit(void); +static void stm32_sdiosample(struct stm32_dev_s *priv, + struct stm32_sdioregs_s *regs); +static void stm32_sample(struct stm32_dev_s *priv, int index); +static void stm32_sdiodump(struct stm32_sdioregs_s *regs, const char *msg); +static void stm32_dumpsample(struct stm32_dev_s *priv, + struct stm32_sampleregs_s *regs, + const char *msg); +static void stm32_dumpsamples(struct stm32_dev_s *priv); +#else +# define stm32_sampleinit() +# define stm32_sample(priv,index) +# define stm32_dumpsamples(priv) +#endif + +/* Data Transfer Helpers ****************************************************/ + +static uint8_t stm32_log2(uint16_t value); +static void stm32_dataconfig(struct stm32_dev_s *priv, uint32_t timeout, + uint32_t dlen, uint32_t dctrl); +static void stm32_datadisable(struct stm32_dev_s *priv); +#ifndef CONFIG_STM32H7_SDMMC_IDMA +static void stm32_sendfifo(struct stm32_dev_s *priv); +static void stm32_recvfifo(struct stm32_dev_s *priv); +#endif +static void stm32_eventtimeout(int argc, uint32_t arg); +static void stm32_endwait(struct stm32_dev_s *priv, + sdio_eventset_t wkupevent); +static void stm32_endtransfer(struct stm32_dev_s *priv, + sdio_eventset_t wkupevent); + +/* Interrupt Handling *******************************************************/ + +static int stm32_sdmmc_interrupt(int irq, void *context, void *arg); +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE +static int stm32_sdmmc_rdyinterrupt(int irq, void *context, void *arg); +#endif + +/* SDIO interface methods ***************************************************/ + +/* Mutual exclusion */ + +#ifdef CONFIG_SDIO_MUXBUS +static int stm32_lock(FAR struct sdio_dev_s *dev, bool lock); +#endif + +/* Initialization/setup */ + +static void stm32_reset(FAR struct sdio_dev_s *dev); +static sdio_capset_t stm32_capabilities(FAR struct sdio_dev_s *dev); +static sdio_statset_t stm32_status(FAR struct sdio_dev_s *dev); +static void stm32_widebus(FAR struct sdio_dev_s *dev, bool enable); +static void stm32_clock(FAR struct sdio_dev_s *dev, + enum sdio_clock_e rate); +static int stm32_attach(FAR struct sdio_dev_s *dev); + +/* Command/Status/Data Transfer */ + +static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg); +static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes); +static int stm32_sendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, uint32_t nbytes); +static int stm32_cancel(FAR struct sdio_dev_s *dev); + +static int stm32_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd); +static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]); +static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int stm32_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rnotimpl); + +/* EVENT handler */ + +static void stm32_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static sdio_eventset_t +stm32_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout); +static void stm32_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static int stm32_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg); + +/* DMA */ + +#ifdef CONFIG_SDIO_DMA +#ifdef CONFIG_SDIO_PREFLIGHT +static int stm32_dmapreflight(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif +static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, + FAR uint8_t *buffer, size_t buflen); +static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif + +/* Initialization/uninitialization/reset ************************************/ + +static void stm32_callback(void *arg); +static void stm32_default(struct stm32_dev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +#ifdef CONFIG_STM32H7_SDMMC1 +struct stm32_dev_s g_sdmmcdev1 = +{ + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = stm32_lock, +#endif + .reset = stm32_reset, + .capabilities = stm32_capabilities, + .status = stm32_status, + .widebus = stm32_widebus, + .clock = stm32_clock, + .attach = stm32_attach, + .sendcmd = stm32_sendcmd, +#ifdef CONFIG_SDIO_BLOCKSETUP + .blocksetup = stm32_blocksetup, /* Not implemented yet */ +#endif + .recvsetup = stm32_recvsetup, + .sendsetup = stm32_sendsetup, + .cancel = stm32_cancel, + .waitresponse = stm32_waitresponse, + .recvR1 = stm32_recvshortcrc, + .recvR2 = stm32_recvlong, + .recvR3 = stm32_recvshort, + .recvR4 = stm32_recvnotimpl, + .recvR5 = stm32_recvnotimpl, + .recvR6 = stm32_recvshortcrc, + .recvR7 = stm32_recvshort, + .waitenable = stm32_waitenable, + .eventwait = stm32_eventwait, + .callbackenable = stm32_callbackenable, + .registercallback = stm32_registercallback, +#ifdef CONFIG_SDIO_DMA +#ifdef CONFIG_SDIO_PREFLIGHT + .dmapreflight = stm32_dmapreflight, +#endif + .dmarecvsetup = stm32_dmarecvsetup, + .dmasendsetup = stm32_dmasendsetup, +#endif + }, + .base = STM32_SDMMC1_BASE, + .nirq = STM32_IRQ_SDMMC1, +#ifdef CONFIG_SDMMC1_PRI + .irqprio = CONFIG_SDMMC1_PRI, +#endif +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + .d0_gpio = GPIO_SDMMC1_D0, +#endif +}; +#endif +#ifdef CONFIG_STM32H7_SDMMC2 +struct stm32_dev_s g_sdmmcdev2 = +{ + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = stm32_lock, +#endif + .reset = stm32_reset, + .capabilities = stm32_capabilities, + .status = stm32_status, + .widebus = stm32_widebus, + .clock = stm32_clock, + .attach = stm32_attach, + .sendcmd = stm32_sendcmd, +#ifdef CONFIG_SDIO_BLOCKSETUP + .blocksetup = stm32_blocksetup, /* Not implemented yet */ +#endif + .recvsetup = stm32_recvsetup, + .sendsetup = stm32_sendsetup, + .cancel = stm32_cancel, + .waitresponse = stm32_waitresponse, + .recvR1 = stm32_recvshortcrc, + .recvR2 = stm32_recvlong, + .recvR3 = stm32_recvshort, + .recvR4 = stm32_recvnotimpl, + .recvR5 = stm32_recvnotimpl, + .recvR6 = stm32_recvshortcrc, + .recvR7 = stm32_recvshort, + .waitenable = stm32_waitenable, + .eventwait = stm32_eventwait, + .callbackenable = stm32_callbackenable, + .registercallback = stm32_registercallback, +#ifdef CONFIG_SDIO_DMA +#ifdef CONFIG_SDIO_PREFLIGHT + .dmapreflight = stm32_dmapreflight, +#endif + .dmarecvsetup = stm32_dmarecvsetup, + .dmasendsetup = stm32_dmasendsetup, +#endif + }, + .base = STM32_SDMMC2_BASE, + .nirq = STM32_IRQ_SDMMC2, +#ifdef CONFIG_SDMMC2_PRI + .irqprio = CONFIG_SDMMC2_PRI, +#endif +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + .d0_gpio = GPIO_SDMMC2_D0, +#endif +}; +#endif +/* Register logging support */ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static struct stm32_sampleregs_s g_sampleregs[DEBUG_NSAMPLES]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sdmmc_putreg32 + ****************************************************************************/ + +static inline void sdmmc_putreg32(struct stm32_dev_s *priv, uint32_t value, + int offset) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: sdmmc_gettreg32 + ****************************************************************************/ + +static inline uint32_t sdmmc_getreg32(struct stm32_dev_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: sdmmc_modifyreg32 + ****************************************************************************/ + +static inline void sdmmc_modifyreg32(struct stm32_dev_s *priv, int offset, + uint32_t clearbits, uint32_t setbits) +{ + irqstate_t flags; + int32_t regval; + + flags = enter_critical_section(); + regval = getreg32(priv->base + offset); + regval &= ~clearbits; + regval |= setbits; + putreg32(regval, priv->base + offset); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_takesem(struct stm32_dev_s *priv) +{ + /* Take the semaphore (perhaps waiting) */ + + while (nxsem_wait(&priv->waitsem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: stm32_setclkcr + * + * Description: + * Modify oft-changed bits in the CLKCR register. Only the following bit- + * fields are changed: + * + * CLKDIV, PWRSAV, BYPASS, WIDBUS, NEGEDGE, and HWFC_EN + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * clkcr - A new CLKCR setting for the above mentions bits (other bits + * are ignored. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void stm32_setclkcr(struct stm32_dev_s *priv, uint32_t clkcr) +{ + uint32_t regval = sdmmc_getreg32(priv, STM32_SDMMC_CLKCR_OFFSET); + + /* Clear CLKDIV, PWRSAV, WIDBUS, NEGEDGE, HWFC_EN bits */ + + regval &= ~(STM32_SDMMC_CLKCR_CLKDIV_MASK | STM32_SDMMC_CLKCR_PWRSAV | + STM32_SDMMC_CLKCR_WIDBUS_MASK | + STM32_SDMMC_CLKCR_NEGEDGE | STM32_SDMMC_CLKCR_HWFC_EN); + + /* Replace with user provided settings */ + + clkcr &= (STM32_SDMMC_CLKCR_CLKDIV_MASK | STM32_SDMMC_CLKCR_PWRSAV | + STM32_SDMMC_CLKCR_WIDBUS_MASK | + STM32_SDMMC_CLKCR_NEGEDGE | STM32_SDMMC_CLKCR_HWFC_EN); + + regval |= clkcr; + regval |= STM32_SDMMC_CLKCR_HWFC_EN; + + sdmmc_putreg32(priv, regval, STM32_SDMMC_CLKCR_OFFSET); + + mcinfo("CLKCR: %08x PWR: %08x\n", + sdmmc_getreg32(priv, STM32_SDMMC_CLKCR_OFFSET), + sdmmc_getreg32(priv, STM32_SDMMC_POWER_OFFSET)); +} + +/**************************************************************************** + * Name: stm32_configwaitints + * + * Description: + * Enable/disable SDIO interrupts needed to suport the wait function + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * waitmask - The set of bits in the SDIO MASK register to set + * waitevents - Waited for events + * wkupevent - Wake-up events + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, + sdio_eventset_t wkupevent) +{ + irqstate_t flags; +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + int pinset; +#endif + + /* Save all of the data and set the new interrupt mask in one, atomic + * operation. + */ + flags = enter_critical_section(); + +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + if ((waitmask & SDIOWAIT_WRCOMPLETE) != 0) + { + /* Do not use this in STM32_SDMMC_MASK register */ + + waitmask &= !SDIOWAIT_WRCOMPLETE; + + pinset = priv->d0_gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK); + pinset |= (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI); + + /* Arm the SDMMC_D Ready and install Isr */ + + (void)stm32_gpiosetevent(pinset, true, false, false, + stm32_sdmmc_rdyinterrupt, priv); + } + + /* Disarm SDMMC_D ready */ + + if ((wkupevent & SDIOWAIT_WRCOMPLETE) != 0) + { + (void)stm32_gpiosetevent(priv->d0_gpio, false, false, false, + NULL, NULL); + stm32_configgpio(priv->d0_gpio); + } +#endif + + priv->waitevents = waitevents; + priv->wkupevent = wkupevent; + priv->waitmask = waitmask; +#ifdef CONFIG_STM32H7_SDMMC_MDMA + priv->xfrflags = 0; +#endif + sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, + STM32_SDMMC_MASK_OFFSET); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_configxfrints + * + * Description: + * Enable SDIO interrupts needed to support the data transfer event + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * xfrmask - The set of bits in the SDIO MASK register to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_configxfrints(struct stm32_dev_s *priv, uint32_t xfrmask) +{ + irqstate_t flags; + + flags = enter_critical_section(); + priv->xfrmask = xfrmask; + sdmmc_putreg32(priv, priv->xfrmask | priv->waitmask, + STM32_SDMMC_MASK_OFFSET); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_setpwrctrl + * + * Description: + * Change the PWRCTRL field of the SDIO POWER register to turn the SDIO + * ON or OFF + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * clkcr - A new PWRCTRL setting + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_setpwrctrl(struct stm32_dev_s *priv, uint32_t pwrctrl) +{ + uint32_t regval; + + regval = sdmmc_getreg32(priv, STM32_SDMMC_POWER_OFFSET); + regval &= ~STM32_SDMMC_POWER_PWRCTRL_MASK; + regval |= pwrctrl; + sdmmc_putreg32(priv, regval, STM32_SDMMC_POWER_OFFSET); +} + +/**************************************************************************** + * Name: stm32_getpwrctrl + * + * Description: + * Return the current value of the the PWRCTRL field of the SDIO POWER + * register. This function can be used to see if the SDIO is powered ON + * or OFF + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * + * Returned Value: + * The current value of the the PWRCTRL field of the SDIO POWER register. + * + ****************************************************************************/ + +static inline uint32_t stm32_getpwrctrl(struct stm32_dev_s *priv) +{ + return sdmmc_getreg32(priv, STM32_SDMMC_POWER_OFFSET) & + STM32_SDMMC_POWER_PWRCTRL_MASK; +} + +/**************************************************************************** + * Name: stm32_sample + * + * Description: + * Sample SDIO/DMA registers + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static void stm32_sample(struct stm32_dev_s *priv, int index) +{ + struct stm32_sampleregs_s *regs = &g_sampleregs[index]; + + stm32_sdiosample(priv, ®s->sdio); +} +#endif + +/**************************************************************************** + * Name: stm32_sdiodump + * + * Description: + * Dump one register sample + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static void stm32_sdiodump(struct stm32_sdioregs_s *regs, const char *msg) +{ + mcinfo("SDIO Registers: %s\n", msg); + mcinfo(" POWER[%08x]: %08x\n", STM32_SDMMC_POWER_OFFSET, regs->power); + mcinfo(" CLKCR[%08x]: %08x\n", STM32_SDMMC_CLKCR_OFFSET, regs->clkcr); + mcinfo(" DCTRL[%08x]: %08x\n", STM32_SDMMC_DCTRL_OFFSET, regs->dctrl); + mcinfo(" DTIMER[%08x]: %08x\n", STM32_SDMMC_DTIMER_OFFSET, regs->dtimer); + mcinfo(" DLEN[%08x]: %08x\n", STM32_SDMMC_DLEN_OFFSET, regs->dlen); + mcinfo(" DCOUNT[%08x]: %08x\n", STM32_SDMMC_DCOUNT_OFFSET, regs->dcount); + mcinfo(" STA[%08x]: %08x\n", STM32_SDMMC_STA_OFFSET, regs->sta); + mcinfo(" MASK[%08x]: %08x\n", STM32_SDMMC_MASK_OFFSET, regs->mask); + mcinfo("FIFOCNT[%08x]: %08x\n", STM32_SDMMC_FIFOCNT_OFFSET, regs->fifocnt); +} +#endif + +/**************************************************************************** + * Name: stm32_dumpsample + * + * Description: + * Dump one register sample + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static void stm32_dumpsample(struct stm32_dev_s *priv, + struct stm32_sampleregs_s *regs, + const char *msg) +{ + stm32_sdiodump(®s->sdio, msg); +} +#endif + +/**************************************************************************** + * Name: stm32_dumpsamples + * + * Description: + * Dump all sampled register data + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_SDMMC_XFRDEBUG +static void stm32_dumpsamples(struct stm32_dev_s *priv) +{ + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP], + "Before setup"); + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP], + "After setup"); + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER], + "End of transfer"); +} +#endif + +/**************************************************************************** + * Name: stm32_log2 + * + * Description: + * Take (approximate) log base 2 of the provided number (Only works if the + * provided number is a power of 2). + * + ****************************************************************************/ + +static uint8_t stm32_log2(uint16_t value) +{ + uint8_t log2 = 0; + + /* 0000 0000 0000 0001 -> return 0, + * 0000 0000 0000 001x -> return 1, + * 0000 0000 0000 01xx -> return 2, + * 0000 0000 0000 1xxx -> return 3, + * ... + * 1xxx xxxx xxxx xxxx -> return 15, + */ + + DEBUGASSERT(value > 0); + while (value != 1) + { + value >>= 1; + log2++; + } + + return log2; +} + +/**************************************************************************** + * Name: stm32_dataconfig + * + * Description: + * Configure the SDIO data path for the next data transfer + * + ****************************************************************************/ + +static void stm32_dataconfig(struct stm32_dev_s *priv, uint32_t timeout, + uint32_t dlen, uint32_t dctrl) +{ + /* Enable data path */ + + sdmmc_putreg32(priv, timeout, STM32_SDMMC_DTIMER_OFFSET); /* Set DTIMER */ + sdmmc_putreg32(priv, dlen, STM32_SDMMC_DLEN_OFFSET); /* Set DLEN */ + + /* Mask off any extra bits */ + + dctrl &= (STM32_SDMMC_DCTRL_DBLOCKSIZE_MASK | + STM32_SDMMC_DCTRL_DTDIR); + + dctrl |= (dctrl | STM32_SDMMC_DCTRL_DTMODE_SDIO | + STM32_SDMMC_DCTRL_SDIOEN); + +#ifdef CONFIG_STM32H7_SDMMC_IDMA + sdmmc_putreg32(priv, (uintptr_t)priv->buffer, + STM32_SDMMC_IDMABASE0R_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_IDMACTRLR_IDMAEN, + STM32_SDMMC_IDMACTRLR_OFFSET); +#else +#if 0 + dctrl |= STM32_SDMMC_DCTRL_FIFORST; +#endif +#endif + + sdmmc_putreg32(priv, dctrl, STM32_SDMMC_DCTRL_OFFSET); +} + +/**************************************************************************** + * Name: stm32_datadisable + * + * Description: + * Disable the SDIO data path setup by stm32_dataconfig() and + * disable DMA. + * + ****************************************************************************/ + +static void stm32_datadisable(struct stm32_dev_s *priv) +{ + /* Disable the data path */ + + /* Reset DTIMER */ + + sdmmc_putreg32(priv, SDMMC_DTIMER_DATATIMEOUT, STM32_SDMMC_DTIMER_OFFSET); + sdmmc_putreg32(priv, 0, STM32_SDMMC_DLEN_OFFSET); /* Reset DLEN */ + + /* Reset DCTRL */ + + sdmmc_putreg32(priv, 0, STM32_SDMMC_DCTRL_OFFSET); +} + +#ifndef CONFIG_STM32H7_SDMMC_IDMA +/**************************************************************************** + * Name: stm32_sendfifo + * + * Description: + * Send SDIO data in interrupt mode + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_sendfifo(struct stm32_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is more data to be sent and the RX FIFO is not full */ + + while (priv->remaining > 0 && + (sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET) & + STM32_SDMMC_STA_TXFIFOF) == 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 < (int)priv->remaining; i++) + { + data.b[i] = *ptr++; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + + /* Put the word in the FIFO */ + + sdmmc_putreg32(priv, data.w, STM32_SDMMC_FIFO_OFFSET); + } +} + +/**************************************************************************** + * Name: stm32_recvfifo + * + * Description: + * Receive SDIO data in interrupt mode + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_recvfifo(struct stm32_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is space to store the data and there is more + * data available in the RX FIFO. + */ + + while (priv->remaining > 0 && + (sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET) & + STM32_SDMMC_STA_RXFIFOE) == 0) + { + /* Read the next word from the RX FIFO */ + + data.w = sdmmc_getreg32(priv, STM32_SDMMC_FIFO_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 < (int)priv->remaining; i++) + { + *ptr++ = data.b[i]; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + } +} +#endif + +/**************************************************************************** + * Name: stm32_eventtimeout + * + * Description: + * The watchdog timeout setup when the event wait start has expired without + * any other waited-for event occurring. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (the SDMMC private state structure reference cast + * to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_eventtimeout(int argc, uint32_t arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; + + /* There is always race conditions with timer expirations. */ + + DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 || + priv->wkupevent != 0); + + mcinfo("sta: %08x enabled irq: %08x\n", + sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET), + sdmmc_getreg32(priv, STM32_SDMMC_MASK_OFFSET)); + + /* Is a data transfer complete event expected? */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + /* Yes.. wake up any waiting threads */ + + stm32_endwait(priv, SDIOWAIT_TIMEOUT); + mcerr("Timeout: remaining: %d\n", priv->remaining); + } +} + +/**************************************************************************** + * Name: stm32_endwait + * + * Description: + * Wake up a waiting thread if the waited-for event has occurred. + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * wkupevent - The event that caused the wait to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_endwait(struct stm32_dev_s *priv, sdio_eventset_t wkupevent) +{ + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, wkupevent); + + /* Wake up the waiting thread */ + + stm32_givesem(priv); +} + +/**************************************************************************** + * Name: stm32_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 - Instance of the SDMMC private state structure. + * wkupevent - The event that caused the transfer to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_endtransfer(struct stm32_dev_s *priv, + sdio_eventset_t wkupevent) +{ + /* Disable all transfer related interrupts */ + + stm32_configxfrints(priv, 0); + + /* If there were errors, send a stop command to DPSM */ + + if ((wkupevent & (~SDIOWAIT_TRANSFERDONE)) != 0) + { + stm32_sendcmd((FAR struct sdio_dev_s *)priv, + STM32_SDMMC_CMD_CMDSTOP, 0); + } + + /* Clearing pending interrupt status on all transfer related interrupts */ + + sdmmc_putreg32(priv, STM32_SDMMC_XFRDONE_ICR, STM32_SDMMC_ICR_OFFSET); + + /* invalidate dcache in case of DMA receive. */ + + if (priv->receive) + { + up_invalidate_dcache((uintptr_t)priv->buffer, + (uintptr_t)priv->buffer + priv->remaining); + } + + /* Mark the transfer finished */ + + priv->remaining = 0; + + /* Is a thread wait for these data transfer complete events? */ + + if ((priv->waitevents & wkupevent) != 0) + { + /* Yes.. wake up any waiting threads */ + + stm32_endwait(priv, wkupevent); + } +} + +/**************************************************************************** + * Name: stm32_sdmmc[1|2]_rdyinterrupt + * + * Description: + * SDMMC ready interrupt handler + * + * Input Parameters: + * irq - not used + * context - not used + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE +static int stm32_sdmmc_rdyinterrupt(int irq, void *context, void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; + stm32_endwait(priv, SDIOWAIT_WRCOMPLETE); + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_sdmmc_interrupt + * + * Description: + * SDMMC interrupt handler + * + * Input Parameters: + * priv - Instance of the SDMMC private state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int stm32_sdmmc_interrupt(int irq, void *context, void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; + uint32_t enabled; + uint32_t pending; + + DEBUGASSERT(priv != NULL); + + /* Loop while there are pending interrupts. Check the SDIO status + * 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 STA and MASK register). If there are non-zero + * bits remaining, then we have work to do here. + */ + + while ((enabled = sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET) & + sdmmc_getreg32(priv, STM32_SDMMC_MASK_OFFSET)) != 0) + { + /* Handle in progress, interrupt driven data transfers ****************/ + + pending = enabled & priv->xfrmask; + if (pending != 0) + { +#ifndef CONFIG_STM32H7_SDMMC_IDMA +#ifdef CONFIG_STM32H7_SDMMC_MDMA + if (!priv->dmamode) +#endif + { + /* Is the RX FIFO half full or more? Is so then we must be + * processing a receive transaction. + */ + + if ((pending & STM32_SDMMC_STA_RXFIFOHF) != 0) + { + /* Receive data from the RX FIFO */ + + stm32_recvfifo(priv); + } + + /* Otherwise, Is the transmit FIFO half empty or less? If so + * we must be processing a send transaction. NOTE: We can't + * be processing both! + */ + + else if ((pending & STM32_SDMMC_STA_TXFIFOHE) != 0) + { + /* Send data via the TX FIFO */ + + stm32_sendfifo(priv); + } + } +#endif + /* Handle data end events */ + + if ((pending & STM32_SDMMC_STA_DATAEND) != 0) + { + /* Handle any data remaining the RX FIFO. If the RX FIFO is + * less than half full at the end of the transfer, then no + * half-full interrupt will be received. + */ + +#ifdef CONFIG_STM32H7_SDMMC_IDMA + /* Just end transfer */ + + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE); +#else +#ifdef CONFIG_STM32H7_SDMMC_MDMA + /* Was this transfer performed in DMA mode? */ + + if (priv->dmamode) + { + /* Yes.. Terminate the transfers only if the DMA has also + * finished. + */ + + priv->xfrflags |= SDMMC_XFRDONE_FLAG; + if (priv->xfrflags == SDMMC_ALLDONE) + { + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + + /* Otherwise, just disable futher transfer interrupts and + * wait for the DMA complete event. + */ + + else + { + stm32_configxfrints(priv, 0); + } + } + else +#endif + { + /* Receive data from the RX FIFO */ + + stm32_recvfifo(priv); + + /* Then terminate the transfer */ + + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } +#endif + } + + /* Handle data block send/receive CRC failure */ + + else if ((pending & STM32_SDMMC_STA_DCRCFAIL) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data block CRC failure, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle data timeout error */ + + else if ((pending & STM32_SDMMC_STA_DTIMEOUT) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data timeout, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | + SDIOWAIT_TIMEOUT); + } + + /* Handle RX FIFO overrun error */ + + else if ((pending & STM32_SDMMC_STA_RXOVERR) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: RX FIFO overrun, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | + SDIOWAIT_ERROR); + } + + /* Handle TX FIFO underrun error */ + + else if ((pending & STM32_SDMMC_STA_TXUNDERR) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: TX FIFO underrun, remaining: %d\n", + priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | + SDIOWAIT_ERROR); + } + } + + /* Handle wait events *************************************************/ + + pending = enabled & priv->waitmask; + if (pending != 0) + { + /* Is this a response completion event? */ + + if ((pending & STM32_SDMMC_RESPDONE_STA) != 0) + { + sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | + STM32_SDMMC_CMDDONE_ICR, STM32_SDMMC_ICR_OFFSET); + + /* Yes.. Is their a thread waiting for response done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + stm32_endwait(priv, SDIOWAIT_RESPONSEDONE); + } + } + + /* Is this a command completion event? */ + + if ((pending & STM32_SDMMC_CMDDONE_STA) != 0) + { + sdmmc_putreg32(priv, STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); + + /* Yes.. Is their a thread waiting for command done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + stm32_endwait(priv, SDIOWAIT_CMDDONE); + } + } + } + } + + return OK; +} + +/**************************************************************************** + * SDIO Interface Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_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 stm32_lock(FAR struct sdio_dev_s *dev, bool lock) +{ + /* The multiplex bus is part of board support package. */ + + stm32_muxbus_sdio_lock(dev, lock); + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_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 stm32_reset(FAR struct sdio_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + irqstate_t flags; + + /* Disable clocking */ + + flags = enter_critical_section(); + + uint32_t regval; +#ifdef CONFIG_STM32H7_SDMMC1 + regval = getreg32(STM32_RCC_AHB3RSTR); + putreg32(regval | RCC_AHB3RSTR_SDMMC1RST, STM32_RCC_AHB3RSTR); +#elif defined CONFIG_STM32H7_SDMMC2 + regval = getreg32(STM32_RCC_AHB2RSTR); + putreg32(regval | RCC_AHB2RSTR_SDMMC2RST, STM32_RCC_AHB2RSTR); +#else +# error CONFIG_STM32H7_SDMMC1 or CONFIG_STM32H7_SDMMC2 not defined +#endif + + usleep(2); + +#ifdef CONFIG_STM32H7_SDMMC1 + putreg32(regval & ~RCC_AHB3RSTR_SDMMC1RST, STM32_RCC_AHB3RSTR); +#elif defined CONFIG_STM32H7_SDMMC2 + putreg32(regval & ~RCC_AHB2RSTR_SDMMC2RST, STM32_RCC_AHB2RSTR); +#endif + + stm32_setpwrctrl(priv, STM32_SDMMC_POWER_PWRCTRL_CYCLE); + usleep(1000); + + /* Put SDIO registers in their default, reset state */ + + stm32_default(priv); + + /* Reset data */ + + priv->waitevents = 0; /* Set of events to be waited for */ + priv->waitmask = 0; /* Interrupt enables for event waiting */ + priv->wkupevent = 0; /* The event that caused the wakeup */ +#ifdef CONFIG_STM32H7_SDMMC_MDMA + priv->xfrflags = 0; /* Used to synchronize SDIO and DMA + * completion events */ +#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->xfrmask = 0; /* Interrupt enables for data transfer */ + + priv->widebus = false; + +#ifdef CONFIG_STM32H7_SDMMC_MDMA + /* MDMA data transfer support */ + + priv->dmamode = false; /* true: DMA mode transfer */ +#endif + + /* Configure the SDIO peripheral */ + + stm32_setpwrctrl(priv, STM32_SDMMC_POWER_PWRCTRL_OFF); + usleep(1000); + stm32_setpwrctrl(priv, STM32_SDMMC_POWER_PWRCTRL_ON); + + stm32_setclkcr(priv, STM32_CLCKCR_INIT); + + leave_critical_section(flags); + + mcinfo("CLCKR: %08x POWER: %08x\n", + sdmmc_getreg32(priv, STM32_SDMMC_CLKCR_OFFSET), + sdmmc_getreg32(priv, STM32_SDMMC_POWER_OFFSET)); +} + +/**************************************************************************** + * Name: stm32_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 stm32_capabilities(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + sdio_capset_t caps = 0; + + if (priv->onebit) + { + caps |= SDIO_CAPS_1BIT_ONLY; + } + + caps |= SDIO_CAPS_DMABEFOREWRITE; + +#ifdef CONFIG_STM32H7_SDMMC_MDMA + caps |= SDIO_CAPS_DMASUPPORTED; +#endif + + return caps; +} + +/**************************************************************************** + * Name: stm32_status + * + * Description: + * Get SDIO status. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Returns a bitset of status values (see stm32_status_* defines) + * + ****************************************************************************/ + +static sdio_statset_t stm32_status(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + return priv->cdstatus; +} + +/**************************************************************************** + * Name: stm32_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 stm32_widebus(FAR struct sdio_dev_s *dev, bool wide) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + priv->widebus = wide; +} + +/**************************************************************************** + * Name: stm32_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 stm32_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t clckr; + + switch (rate) + { + /* Disable clocking (with default ID mode divisor) */ + + default: + case CLOCK_SDIO_DISABLED: + clckr = STM32_CLCKCR_INIT; + return; + + /* Enable in initial ID mode clocking (<400KHz) */ + + case CLOCK_IDMODE: + clckr = (STM32_CLCKCR_INIT); + break; + + /* Enable in MMC normal operation clocking */ + + case CLOCK_MMC_TRANSFER: + clckr = (STM32_SDMMC_CLKCR_MMCXFR); + break; + + /* SD normal operation clocking (wide 4-bit mode) */ + + case CLOCK_SD_TRANSFER_4BIT: + if (!priv->onebit) + { + clckr = (STM32_SDMMC_CLCKR_SDWIDEXFR); + break; + } + + /* SD normal operation clocking (narrow 1-bit mode) */ + + case CLOCK_SD_TRANSFER_1BIT: + clckr = (STM32_SDMMC_CLCKR_SDXFR); + break; + } + + /* Set the new clock frequency along with the clock enable/disable bit */ + + stm32_setclkcr(priv, clckr); +} + +/**************************************************************************** + * Name: stm32_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 stm32_attach(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + int ret; + + /* Attach the SDIO interrupt handler */ + + ret = irq_attach(priv->nirq, stm32_sdmmc_interrupt, priv); + if (ret == OK) + { + /* Disable all interrupts at the SDIO controller and clear + * interrupt flags + */ + + sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET, STM32_SDMMC_MASK_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_ICR_ALLFLAGS, STM32_SDMMC_ICR_OFFSET); + + /* Enable SDIO interrupts at the NVIC. They can now be enabled at + * the SDIO controller as needed. + */ + + up_enable_irq(priv->nirq); + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_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 stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t regval; + uint32_t cmdidx; + + /* Clear CMDINDEX, CMDTRANS, WAITRESP, WAITINT, WAITPEND, and CPSMEN bits */ + + regval = sdmmc_getreg32(priv, STM32_SDMMC_CMD_OFFSET); + + /* Make sure the register is clear */ + + if (regval & STM32_SDMMC_CMD_CPSMEN) + sdmmc_putreg32(priv, STM32_SDMMC_CMD_OFFSET, 0); + + regval &= ~(STM32_SDMMC_CMD_CMDINDEX_MASK | STM32_SDMMC_CMD_CMDTRANS | + STM32_SDMMC_CMD_WAITRESP_MASK | STM32_SDMMC_CMD_WAITINT | + STM32_SDMMC_CMD_WAITPEND | STM32_SDMMC_CMD_CPSMEN); + + /* Set WAITRESP bits */ + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + regval |= STM32_SDMMC_CMD_NORESPONSE; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R3_RESPONSE: + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + case MMCSD_R6_RESPONSE: + case MMCSD_R7_RESPONSE: + regval |= STM32_SDMMC_CMD_SHORTRESPONSE; + break; + + case MMCSD_R2_RESPONSE: + regval |= STM32_SDMMC_CMD_LONGRESPONSE; + break; + } + + /* Set CPSMEN and the command index */ + + cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT; + regval |= cmdidx | STM32_SDMMC_CMD_CPSMEN; + + /* Clear interrupts */ + + uint32_t icr_mask = STM32_SDMMC_CMDDONE_ICR | STM32_SDMMC_RESPDONE_ICR; + sdmmc_putreg32(priv, icr_mask, STM32_SDMMC_ICR_OFFSET); + + if (cmd & MMCSD_DATAXFR_MASK) + { + regval |= STM32_SDMMC_CMD_CMDTRANS; + + /* Enable receive/send interrupts */ + + if (priv->receive) + { + stm32_configxfrints(priv, STM32_SDMMC_RECV_MASK); + } + else + { + stm32_configxfrints(priv, STM32_SDMMC_SEND_MASK); + } + } + + mcinfo("cmd: %08x arg: %08x regval: %08x enabled irq: %08x\n", + cmd, arg, regval, sdmmc_getreg32(priv, STM32_SDMMC_MASK_OFFSET)); + + /* Set the SDIO Argument value */ + + sdmmc_putreg32(priv, arg, STM32_SDMMC_ARG_OFFSET); + + /* Write the SDIO CMD */ + + sdmmc_putreg32(priv, regval, STM32_SDMMC_CMD_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: stm32_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, + * SDMMC_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 + * + ****************************************************************************/ + +static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + stm32_datadisable(priv); + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the destination buffer information for use by the interrupt + * handler + */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; +#ifdef CONFIG_STM32H7_SDMMC_MDMA + priv->dmamode = false; +#endif + priv->receive = true; + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(nbytes) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, dblocksize | + STM32_SDMMC_DCTRL_DTDIR); + + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + + return OK; +} + +/**************************************************************************** + * Name: stm32_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 SDMMC_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 + * + ****************************************************************************/ + +static int stm32_sendsetup(FAR struct sdio_dev_s *dev, FAR const + uint8_t *buffer, size_t nbytes) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + stm32_datadisable(priv); + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; +#ifdef CONFIG_STM32H7_SDMMC_MDMA + priv->dmamode = false; +#endif + priv->receive = false; + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(nbytes) << STM32_SDMMC_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(priv, SDMMC_DTIMER_DATATIMEOUT, nbytes, + dblocksize); + + /* Flush dcache if enabled */ + + up_clean_dcache((uintptr_t)priv->buffer, (uintptr_t)priv->buffer + nbytes); + + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} + +/**************************************************************************** + * Name: stm32_cancel + * + * Description: + * Cancel the data transfer setup of SDMMC_RECVSETUP, SDMMC_SENDSETUP, + * SDMMC_DMARECVSETUP or SDMMC_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 stm32_cancel(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + /* Disable all transfer- and event- related interrupts */ + + stm32_configxfrints(priv, 0); + stm32_configwaitints(priv, 0, 0, 0); + + /* Send a stop command to DPSM */ + + stm32_sendcmd(dev, STM32_SDMMC_CMD_CMDSTOP, 0); + + /* If this was a DMA transfer, make sure that DMA is stopped */ + + sdmmc_putreg32(priv, 0, STM32_SDMMC_IDMACTRLR_OFFSET); + + /* Clearing pending interrupt status on all transfer- and event- related + * interrupts + */ + + sdmmc_putreg32(priv, STM32_SDMMC_WAITALL_ICR, STM32_SDMMC_ICR_OFFSET); + + /* Cancel any watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* Mark no transfer in progress */ + + priv->remaining = 0; + return OK; +} + +/**************************************************************************** + * Name: stm32_waitresponse + * + * Description: + * Poll-wait for the response to the last command to be ready. + * + * 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 stm32_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + int32_t timeout = SDMMC_CMDTIMEOUT; + uint32_t events; + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + events = STM32_SDMMC_CMDDONE_STA; + timeout = SDMMC_CMDTIMEOUT; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R2_RESPONSE: + case MMCSD_R6_RESPONSE: + events = STM32_SDMMC_RESPDONE_STA; + timeout = SDMMC_LONGTIMEOUT; + break; + + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + return -ENOSYS; + + case MMCSD_R3_RESPONSE: + case MMCSD_R7_RESPONSE: + events = STM32_SDMMC_RESPDONE_STA; + timeout = SDMMC_CMDTIMEOUT; + break; + + default: + return -EINVAL; + } + + /* Then wait for the response (or timeout) */ + + while ((sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET) & events) == 0) + { + if (--timeout <= 0) + { + mcerr("ERROR: Timeout cmd: %08x events: %08x STA: %08x\n", + cmd, events, sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET)); + + return -ETIMEDOUT; + } + } + + sdmmc_putreg32(priv, STM32_SDMMC_CMDDONE_ICR, STM32_SDMMC_ICR_OFFSET); + return OK; +} + +/**************************************************************************** + * Name: stm32_recvRx + * + * 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 faiure 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 stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; +#ifdef CONFIG_DEBUG_FEATURES + uint32_t respcmd; +#endif + 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 signaling 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_R6_RESPONSE) + { + mcerr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET); + if ((regval & STM32_SDMMC_STA_CTIMEOUT) != 0) + { + mcerr("ERROR: Command timeout: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if ((regval & STM32_SDMMC_STA_CCRCFAIL) != 0) + { + mcerr("ERROR: CRC failure: %08x\n", regval); + ret = -EIO; + } +#ifdef CONFIG_DEBUG_FEATURES + else + { + /* Check response received is of desired command */ + + respcmd = sdmmc_getreg32(priv, STM32_SDMMC_RESPCMD_OFFSET); + if ((uint8_t)(respcmd & STM32_SDMMC_RESPCMD_MASK) != + (cmd & MMCSD_CMDIDX_MASK)) + { + mcerr("ERROR: RESCMD=%02x CMD=%08x\n", respcmd, cmd); + ret = -EINVAL; + } + } +#endif + } + + /* Clear all pending message completion events and return the R1/R6 response */ + + sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); + *rshort = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET); + return ret; +} + +static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]) +{ + struct stm32_dev_s *priv = (struct stm32_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 internal 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=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET); + if (regval & STM32_SDMMC_STA_CTIMEOUT) + { + mcerr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if (regval & STM32_SDMMC_STA_CCRCFAIL) + { + mcerr("ERROR: CRC fail STA: %08x\n", regval); + ret = -EIO; + } + } + + /* Return the long response */ + + sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); + if (rlong) + { + rlong[0] = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET); + rlong[1] = sdmmc_getreg32(priv, STM32_SDMMC_RESP2_OFFSET); + rlong[2] = sdmmc_getreg32(priv, STM32_SDMMC_RESP3_OFFSET); + rlong[3] = sdmmc_getreg32(priv, STM32_SDMMC_RESP4_OFFSET); + } + + return ret; +} + +static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort) +{ + struct stm32_dev_s *priv = (struct stm32_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_R7_RESPONSE) + { + mcerr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout occurred (Apparently a CRC error can terminate + * a good response) + */ + + regval = sdmmc_getreg32(priv, STM32_SDMMC_STA_OFFSET); + if (regval & STM32_SDMMC_STA_CTIMEOUT) + { + mcerr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + } + + sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); + if (rshort) + { + *rshort = sdmmc_getreg32(priv, STM32_SDMMC_RESP1_OFFSET); + } + + return ret; +} + +/* MMC responses not supported */ + +static int stm32_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rnotimpl) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + sdmmc_putreg32(priv, STM32_SDMMC_RESPDONE_ICR | STM32_SDMMC_CMDDONE_ICR, + STM32_SDMMC_ICR_OFFSET); + return -ENOSYS; +} + +/**************************************************************************** + * Name: stm32_waitenable + * + * Description: + * Enable/disable of a set of SDIO wait events. This is part of the + * the SDMMC_WAITEVENT sequence. The set of to-be-waited-for events is + * configured before calling stm32_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) SDMMC_WAITENABLE is called + * again specifying a different set of wait events, or (2) SDMMC_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 stm32_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t waitmask; + + DEBUGASSERT(priv != NULL); + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, 0); + + /* Select the interrupt mask that will give us the appropriate wakeup + * interrupts. + */ + +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) + if ((eventset & SDIOWAIT_WRCOMPLETE) != 0) + { + waitmask = SDIOWAIT_WRCOMPLETE; + } + else +#endif + { + waitmask = 0; + if ((eventset & SDIOWAIT_CMDDONE) != 0) + { + waitmask |= STM32_SDMMC_CMDDONE_MASK; + } + + if ((eventset & SDIOWAIT_RESPONSEDONE) != 0) + { + waitmask |= STM32_SDMMC_RESPDONE_MASK; + } + + if ((eventset & SDIOWAIT_TRANSFERDONE) != 0) + { + waitmask |= STM32_SDMMC_XFRDONE_MASK; + } + + /* Enable event-related interrupts */ + + sdmmc_putreg32(priv, STM32_SDMMC_WAITALL_ICR, STM32_SDMMC_ICR_OFFSET); + } + + stm32_configwaitints(priv, waitmask, eventset, 0); +} + +/**************************************************************************** + * Name: stm32_eventwait + * + * Description: + * Wait for one of the enabled events to occur (or a timeout). Note that + * all events enabled by SDMMC_WAITEVENTS are disabled when stm32_eventwait + * returns. SDMMC_WAITEVENTS must be called again before stm32_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 stm32_eventwait(FAR struct sdio_dev_s *dev, + uint32_t timeout) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + sdio_eventset_t wkupevent = 0; + irqstate_t flags; + 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. + */ + + flags = enter_critical_section(); + DEBUGASSERT(priv->waitevents != 0 || priv->wkupevent != 0); + + /* Check if the timeout event is specified in the event set */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + int delay; + + /* Yes.. Handle a cornercase: The user request a timeout event but + * with timeout == 0? + */ + + if (!timeout) + { + /* Then just tell the caller that we already timed out */ + + wkupevent = SDIOWAIT_TIMEOUT; + goto errout; + } + + /* Start the watchdog timer */ + + delay = MSEC2TICK(timeout); + ret = wd_start(priv->waitwdog, delay, (wdentry_t)stm32_eventtimeout, + 1, (uint32_t)priv); + if (ret != OK) + { + mcerr("ERROR: wd_start failed: %d\n", ret); + } + } + +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) + if ((priv->waitevents & SDIOWAIT_WRCOMPLETE) != 0) + { + /* Atomically read pin to see if ready (true) and determine if ISR fired + * If Pin is ready and if ISR did NOT fire end the wait here + */ + + if (stm32_gpioread(priv->d0_gpio) && + (priv->wkupevent & SDIOWAIT_WRCOMPLETE) == 0) + { + stm32_endwait(priv, SDIOWAIT_WRCOMPLETE); + } + } +#endif + + /* Loop until the event (or the timeout occurs). Race conditions are avoided + * by calling stm32_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. + */ + + stm32_takesem(priv); + 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 nonzero value. + */ + + if (wkupevent != 0) + { + /* Yes... break out of the loop with wkupevent non-zero */ + + break; + } + } + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, 0); +#ifdef CONFIG_STM32H7_SDMMC_MDMA + priv->xfrflags = 0; +#endif + +errout: + leave_critical_section(flags); + stm32_dumpsamples(priv); + return wkupevent; +} + +/**************************************************************************** + * Name: stm32_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 stm32_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 methos. + * + * 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 stm32_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + mcinfo("eventset: %02x\n", eventset); + DEBUGASSERT(priv != NULL); + + priv->cbevents = eventset; + stm32_callback(priv); +} + +/**************************************************************************** + * Name: stm32_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 SDMMC_CALLBACKENABLE + * + * Input Parameters: + * dev - Device-specific state data + * callback - The funtion 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 stm32_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_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: stm32_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_STM32H7_SDMMC_DMA) && defined(CONFIG_SDIO_PREFLIGHT) +static int stm32_dmapreflight(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); + + /* DMA must be possible to the buffer */ + /* TODO */ + + if (1) + { + return -EFAULT; + } + + return 0; +} +#endif + +/**************************************************************************** + * Name: stm32_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 stm32_callback(void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_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); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)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); + } + } +} + +/**************************************************************************** + * Name: stm32_default + * + * Description: + * Restore SDIO registers to their default, reset values + * + ****************************************************************************/ + +static void stm32_default(struct stm32_dev_s *priv) +{ + sdmmc_putreg32(priv, STM32_SDMMC_POWER_RESET, STM32_SDMMC_POWER_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_CLKCR_RESET, STM32_SDMMC_CLKCR_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_ARG_RESET, STM32_SDMMC_ARG_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_CMD_RESET, STM32_SDMMC_CMD_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_DTIMER_RESET, STM32_SDMMC_DTIMER_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_DLEN_RESET, STM32_SDMMC_DLEN_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_DCTRL_RESET, STM32_SDMMC_DCTRL_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_ICR_RESET, STM32_SDMMC_ICR_OFFSET); + sdmmc_putreg32(priv, STM32_SDMMC_MASK_RESET, STM32_SDMMC_MASK_OFFSET); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sdio_initialize + * + * Description: + * Initialize SDIO for operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on + * failures. + * + ****************************************************************************/ + +FAR struct sdio_dev_s *sdio_initialize(int slotno) +{ + struct stm32_dev_s *priv = NULL; +#ifdef CONFIG_STM32H7_SDMMC_MDMA + unsigned int dmachan; +#endif + +#ifdef CONFIG_STM32H7_SDMMC1 + if (slotno == 0) + { + /* Select SDMMC 1 */ + + priv = &g_sdmmcdev1; + +#ifdef CONFIG_STM32H7_SDMMC_MDMA + dmachan = SDMMC1_DMACHAN; +#endif + +#ifdef CONFIG_SDMMC1_WIDTH_D1_ONLY + priv->onebit = true; +#else + priv->onebit = false; +#endif + + /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable + * of 8-bit wide bus operation but D4-D7 are not configured). + * + * If bus is multiplexed then there is a custom bus configuration + * utility in the scope of the board support package. + */ + +#ifndef CONFIG_SDIO_MUXBUS + stm32_configgpio(GPIO_SDMMC1_D0); +#ifndef CONFIG_SDMMC1_WIDTH_D1_ONLY + stm32_configgpio(GPIO_SDMMC1_D1); + stm32_configgpio(GPIO_SDMMC1_D2); + stm32_configgpio(GPIO_SDMMC1_D3); +#endif + stm32_configgpio(GPIO_SDMMC1_CK); + stm32_configgpio(GPIO_SDMMC1_CMD); +#endif + } + else +#endif +#ifdef CONFIG_STM32H7_SDMMC2 + if (slotno == 1) + { + /* Select SDMMC 2 */ + + priv = &g_sdmmcdev2; + +#ifdef CONFIG_STM32H7_SDMMC_DMA + dmachan = SDMMC2_DMACHAN; +#endif + +#ifdef CONFIG_SDMMC2_WIDTH_D1_ONLY + priv->onebit = true; +#else + priv->onebit = false; +#endif + + /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable + * of 8-bit wide bus operation but D4-D7 are not configured). + * + * If bus is multiplexed then there is a custom bus configuration + * utilityin the scope of the board support package. + */ + +#ifndef CONFIG_SDIO_MUXBUS + stm32_configgpio(GPIO_SDMMC2_D0); +#ifndef CONFIG_SDMMC2_WIDTH_D1_ONLY + stm32_configgpio(GPIO_SDMMC2_D1); + stm32_configgpio(GPIO_SDMMC2_D2); + stm32_configgpio(GPIO_SDMMC2_D3); +#endif + stm32_configgpio(GPIO_SDMMC2_CK); + stm32_configgpio(GPIO_SDMMC2_CMD); +#endif + } + else +#endif + { + mcerr("ERROR: Unsupported SDMMC slot: %d\n", slotno); + return NULL; + } + + /* Initialize the SDIO slot structure */ + /* Initialize semaphores */ + + nxsem_init(&priv->waitsem, 0, 0); + + /* The waitsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_setprotocol(&priv->waitsem, SEM_PRIO_NONE); + + /* Create a watchdog timer */ + + priv->waitwdog = wd_create(); + DEBUGASSERT(priv->waitwdog); + +#ifdef CONFIG_STM32H7_SDMMC_MDMA + /* Allocate a DMA channel */ + + priv->dma = stm32_dmachannel(dmachan); + DEBUGASSERT(priv->dma); +#endif + + /* Reset the card and assure that it is in the initial, unconfigured + * state. + */ + + stm32_reset(&priv->dev); + return &priv->dev; +} + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- possible from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + sdio_statset_t cdstatus; + irqstate_t flags; + + /* Update card status */ + + flags = enter_critical_section(); + cdstatus = priv->cdstatus; + if (cardinslot) + { + priv->cdstatus |= SDIO_STATUS_PRESENT; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_PRESENT; + } + + leave_critical_section(flags); + + mcinfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus); + + /* Perform any requested callback if the status has changed */ + + if (cdstatus != priv->cdstatus) + { + stm32_callback(priv); + } +} + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is writeprotected. + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + irqstate_t flags; + + /* Update card status */ + + flags = enter_critical_section(); + if (wrprotect) + { + priv->cdstatus |= SDIO_STATUS_WRPROTECTED; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED; + } + + mcinfo("cdstatus: %02x\n", priv->cdstatus); + leave_critical_section(flags); +} + +#endif /* CONFIG_STM32H7_SDMMC1 || CONFIG_STM32H7_SDMMC2 */ diff --git a/arch/arm/src/stm32h7/stm32_sdmmc.h b/arch/arm/src/stm32h7/stm32_sdmmc.h new file mode 100644 index 0000000000..32a35a6bac --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_sdmmc.h @@ -0,0 +1,128 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_sdmmc.h + * + * Copyright (C) 2009, 2011, 2016, 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_STM32_SDMMC_H +#define __ARCH_ARM_SRC_STM32H7_STM32_SDMMC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "chip/stm32_sdmmc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: sdio_initialize + * + * Description: + * Initialize SDIO for operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on + * failures. + * + ****************************************************************************/ + +struct sdio_dev_s; /* See include/nuttx/sdio.h */ +FAR struct sdio_dev_s *sdio_initialize(int slotno); + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- posssible from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot); + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is writeprotected. + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32H7_STM32_SDMMC_H */ diff --git a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c index ded312dbb9..72ae5baa2c 100644 --- a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c +++ b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c @@ -220,6 +220,12 @@ static inline void rcc_enableahb3(void) regval |= RCC_AHB3ENR_MDMAEN; #endif +#ifdef CONFIG_STM32H7_SDMMC1 + /* SDMMC clock enable */ + + regval |= RCC_AHB3ENR_SDMMC1EN; +#endif + // TODO: ... putreg32(regval, STM32_RCC_AHB3ENR); /* Enable peripherals */ @@ -396,7 +402,11 @@ static inline void rcc_enableapb2(void) regval |= RCC_APB2ENR_SPI5EN; #endif - // TODO: ... +#ifdef CONFIG_STM32H7_SDMMC2 + /* SDMMC2 clock enable */ + + regval |= RCC_APB2ENR_SDMMC2EN; +#endif putreg32(regval, STM32_RCC_APB2ENR); /* Enable peripherals */ }