diff --git a/arch/arm/src/lpc43xx/Kconfig b/arch/arm/src/lpc43xx/Kconfig index efc4f8dc3c8..59c927604b4 100644 --- a/arch/arm/src/lpc43xx/Kconfig +++ b/arch/arm/src/lpc43xx/Kconfig @@ -254,6 +254,8 @@ config LPC43_SCT config LPC43_SDMMC bool "SD/MMC" default n + select ARCH_HAVE_SDIO + depends on EXPERIMENTAL config LPC43_SPI bool "SPI" @@ -338,19 +340,6 @@ config LPC43_WWDT endmenu # LPC43xx Peripheral Support -menu "ADC driver options" - depends on LPC43_ADC0 || LPC43_ADC1 - -config ADC0_MASK - hex "ADC0 mask" - default 0x01 - -config ADC0_FREQ - int "ADC0 frequency" - default 4500000 - -endmenu # ADC driver options - config LPC43_GPIO_IRQ bool "GPIO interrupt support" default n @@ -509,6 +498,32 @@ endif # LCP43_EXTSDRAM3 endmenu # External Memory Configuration +menu "SD/MMC Configuration" + depends on LPC43_SDMMC + +config LPC43_SDMMC_PWRCTRL + bool "Power-enable pin" + default n + ---help--- + Select if the board supports a power-enable pin that must be selected + to provide power to the SD card. + +config LPC43_SDMMC_DMA + bool "Support DMA data transfers" + default y + select SDIO_DMA + ---help--- + Support DMA data transfers. + +config LPC43_SDMMC_REGDEBUG + bool "Register level debug" + default n + depends on DEBUG_MEMCARD_INFO + ---help--- + Output detailed register-level SD/MMC debug information. + +endmenu # SD/MMC Configuration + menu "Ethernet MAC configuration" depends on LPC43_ETHERNET @@ -672,6 +687,19 @@ config LPC43_ETHERNET_REGDEBUG endmenu # Ethernet MAC configuration +menu "ADC driver options" + depends on LPC43_ADC0 || LPC43_ADC1 + +config ADC0_MASK + hex "ADC0 mask" + default 0x01 + +config ADC0_FREQ + int "ADC0 frequency" + default 4500000 + +endmenu # ADC driver options + menu "RS-485 Configuration" if LPC43_USART0 diff --git a/arch/arm/src/lpc43xx/Make.defs b/arch/arm/src/lpc43xx/Make.defs index 9c17f16cece..d5bd8912372 100644 --- a/arch/arm/src/lpc43xx/Make.defs +++ b/arch/arm/src/lpc43xx/Make.defs @@ -116,6 +116,10 @@ ifeq ($(CONFIG_LPC43_WWDT),y) CHIP_CSRCS += lpc43_wwdt.c endif +ifeq ($(CONFIG_LPC43_SDMMC),y) +CHIP_CSRCS += lpc43_sdmmc.c +endif + ifeq ($(CONFIG_LPC43_ETHERNET),y) CHIP_CSRCS += lpc43_ethernet.c endif diff --git a/arch/arm/src/lpc43xx/chip/lpc43_cgu.h b/arch/arm/src/lpc43xx/chip/lpc43_cgu.h index bd9682c8fd4..7f60f38836f 100644 --- a/arch/arm/src/lpc43xx/chip/lpc43_cgu.h +++ b/arch/arm/src/lpc43xx/chip/lpc43_cgu.h @@ -643,6 +643,31 @@ # define BASE_VADC_CLKSEL_IDIVD (15 << BASE_VADC_CLK_CLKSEL_SHIFT) /* IDIVD */ # define BASE_VADC_CLKSEL_IDIVE (16 << BASE_VADC_CLK_CLKSEL_SHIFT) /* IDIVE */ /* Bits 29-31: Reserved */ + +/* Output stage 13 control register (BASE_SDIO_CLK) */ +/* NOTE: Clocks 4-19 are identical */ + +#define BASE_SDIO_CLK_PD (1 << 0) /* Bit 0: Output stage power down */ + /* Bits 1-10: Reserved */ +#define BASE_SDIO_CLK_AUTOBLOCK (1 << 11) /* Bit 11: Block clock during frequency change */ + /* Bits 12-23: Reserved */ +#define BASE_SDIO_CLK_CLKSEL_SHIFT (24) /* Bits 24-28: Clock source selection */ +#define BASE_SDIO_CLK_CLKSEL_MASK (31 << BASE_SDIO_CLK_CLKSEL_SHIFT) +# define BASE_SDIO_CLKSEL_32KHZOSC (0 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* 32 kHz oscillator */ +# define BASE_SDIO_CLKSEL_IRC (1 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IRC (default) */ +# define BASE_SDIO_CLKSEL_ENET_RXCLK (2 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* ENET_RX_CLK */ +# define BASE_SDIO_CLKSEL_ENET_TXCLK (3 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* ENET_TX_CLK */ +# define BASE_SDIO_CLKSEL_GPCLKIN (4 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* GP_CLKIN */ +# define BASE_SDIO_CLKSEL_XTAL (6 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* Crystal oscillator */ +# define BASE_SDIO_CLKSEL_PLL0AUDIO (8 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* PLL0AUDIO */ +# define BASE_SDIO_CLKSEL_PLL1 (9 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* PLL1 */ +# define BASE_SDIO_CLKSEL_IDIVA (12 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IDIVA */ +# define BASE_SDIO_CLKSEL_IDIVB (13 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IDIVB */ +# define BASE_SDIO_CLKSEL_IDIVC (14 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IDIVC */ +# define BASE_SDIO_CLKSEL_IDIVD (15 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IDIVD */ +# define BASE_SDIO_CLKSEL_IDIVE (16 << BASE_SDIO_CLK_CLKSEL_SHIFT) /* IDIVE */ + /* Bits 29-31: Reserved */ + /* Output stage 14 control register (BASE_SSP0_CLK) */ /* NOTE: Clocks 4-19 are identical */ diff --git a/arch/arm/src/lpc43xx/chip/lpc43_sdmmc.h b/arch/arm/src/lpc43xx/chip/lpc43_sdmmc.h index 6151a015f56..3ac448a92d1 100644 --- a/arch/arm/src/lpc43xx/chip/lpc43_sdmmc.h +++ b/arch/arm/src/lpc43xx/chip/lpc43_sdmmc.h @@ -46,6 +46,11 @@ * Pre-processor Definitions ************************************************************************************************/ +#define LPC43_TXFIFO_DEPTH 32 +#define LPC43_TXFIFO_WIDTH 4 +#define LPC43_RXFIFO_DEPTH 32 +#define LPC43_RXFIFO_WIDTH 4 + /* MCI register offsets (with respect to the MCI base) ******************************************/ #define LPC43_SDMMC_CTRL_OFFSET 0x0000 /* Control register */ @@ -156,12 +161,16 @@ #define SDMMC_CLKDIV0_SHIFT (0) /* Bits 0-7: Clock divider 0 value */ #define SDMMC_CLKDIV0_MASK (255 << SDMMC_CLKDIV0_SHIFT) +# define SDMMC_CLKDIV0(n) ((((n) + 1) >> 1) << SDMMC_CLKDIV0_SHIFT) #define SDMMC_CLKDIV1_SHIFT (8) /* Bits 8-15: Clock divider 1 value */ #define SDMMC_CLKDIV1_MASK (255 << SDMMC_CLKDIV1_SHIFT) +# define SDMMC_CLKDIV1(n) ((((n) + 1) >> 1) << SDMMC_CLKDIV1_SHIFT) #define SDMMC_CLKDIV2_SHIFT (16) /* Bits 16-23: Clock divider 2 value */ #define SDMMC_CLKDIV2_MASK (255 << SDMMC_CLKDIV2_SHIFT) +# define SDMMC_CLKDIV2(n) ((((n) + 1) >> 1) << SDMMC_CLKDIV2_SHIFT) #define SDMMC_CLKDIV3_SHIFT (24) /* Bits 24-31: Clock divider 3 value */ #define SDMMC_CLKDIV3_MASK (255 << SDMMC_CLKDIV3_SHIFT) +# define SDMMC_CLKDIV3(n) ((((n) + 1) >> 1) << SDMMC_CLKDIV3_SHIFT) /* Clock source register CLKSRC */ @@ -217,7 +226,7 @@ #define SDMMC_INT_SBE (1 << 13) /* Bit 13: Start-bit error */ #define SDMMC_INT_ACD (1 << 14) /* Bit 14: Auto command done */ #define SDMMC_INT_EBE (1 << 15) /* Bit 15: End-bit error (read)/Write no CRC */ -#define SDMMC_INT_SDIO (1 << 16) /* Bit 16: Mask SDIO interrupt */ +#define SDMMC_INT_SDIO (1 << 16) /* Bit 16: SDIO interrupt */ /* Bits 17-31: Reserved */ #define SDMMC_INT_ALL (0x1ffff) @@ -227,6 +236,11 @@ #define SDMMC_CMD_CMDINDEX_MASK (63 << SDMMC_CMD_CMDINDEX_SHIFT) #define SDMMC_CMD_RESPONSE (1 << 6) /* Bit 6: Response expected from card */ #define SDMMC_CMD_LONGRESP (1 << 7) /* Bit 7: Long response expected from card */ +#define SDMMC_CMD_WAITRESP_SHIFT (6) /* Bits 6-7: Response expected */ +#define SDMMC_CMD_WAITRESP_MASK (3 << SDMMC_CMD_WAITRESP_SHIFT) +# define SDMMC_CMD_NORESPONSE (0 << SDMMC_CMD_WAITRESP_SHIFT) /* x0: No response */ +# define SDMMC_CMD_SHORTRESPONSE (1 << SDMMC_CMD_WAITRESP_SHIFT) /* 01: Short response */ +# define SDMMC_CMD_LONGRESPONSE (3 << SDMMC_CMD_WAITRESP_SHIFT) /* 11: Long response */ #define SDMMC_CMD_RESPCRC (1 << 8) /* Bit 8: Check response CRC */ #define SDMMC_CMD_DATAXFREXPTD (1 << 9) /* Bit 9: Data transfer expected (read/write) */ #define SDMMC_CMD_WRITE (1 << 10) /* Bit 10: Write to card */ @@ -285,9 +299,11 @@ #define SDMMC_FIFOTH_TXWMARK_SHIFT (0) /* Bits 0-11: FIFO threshold level when transmitting */ #define SDMMC_FIFOTH_TXWMARK_MASK (0xfff << SDMMC_FIFOTH_TXWMARK_SHIFT) +# define SDMMC_FIFOTH_TXWMARK(n) ((uint32_t)(n) << SDMMC_FIFOTH_TXWMARK_SHIFT) /* Bits 12-15: Reserved */ #define SDMMC_FIFOTH_RXWMARK_SHIFT (16) /* Bits 16-27: FIFO threshold level when receiving */ #define SDMMC_FIFOTH_RXWMARK_MASK (0xfff << SDMMC_FIFOTH_RXWMARK_SHIFT) +# define SDMMC_FIFOTH_RXWMARK(n) ((uint32_t)(n) << SDMMC_FIFOTH_RXWMARK_SHIFT) #define SDMMC_FIFOTH_DMABURST_SHIFT (28) /* Bits 28-30: Burst size for multiple transaction */ #define SDMMC_FIFOTH_DMABURST_MASK (7 << SDMMC_FIFOTH_DMABURST_SHIFT) # define SDMMC_FIFOTH_DMABURST_1XFR (0 << SDMMC_FIFOTH_DMABURST_SHIFT) /* 1 transfer */ @@ -323,6 +339,7 @@ #define SDMMC_BMOD_FB (1 << 1) /* Bit 1: Fixed Burst */ #define SDMMC_BMOD_DSL_SHIFT (2) /* Bits 2-6: Descriptor Skip Length */ #define SDMMC_BMOD_DSL_MASK (31 << SDMMC_BMOD_DSL_SHIFT) +# define SDMMC_BMOD_DSL(n) ((uint32_t)(n) << SDMMC_BMOD_DSL_SHIFT) #define SDMMC_BMOD_DE (1 << 7) /* Bit 7: SD/MMC DMA Enable */ #define SDMMC_BMOD_PBL_SHIFT (8) /* Bits 8-10: Programmable Burst Length */ #define SDMMC_BMOD_PBL_MASK (7 << SDMMC_BMOD_PBL_SHIFT) diff --git a/arch/arm/src/lpc43xx/lpc43_sdmmc.c b/arch/arm/src/lpc43xx/lpc43_sdmmc.c new file mode 100644 index 00000000000..075ad38cdfc --- /dev/null +++ b/arch/arm/src/lpc43xx/lpc43_sdmmc.c @@ -0,0 +1,2732 @@ +/**************************************************************************** + * arch/arm/src/lpc43xx/lpc43_sdmmc.c + * + * Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved. + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Alan Carvalho de Assis + * + * This code is based on arch/arm/src/lpc17xx/lpc17_sdcard.c: + * + * Copyright (C) 2013-2014, 2016-2017 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chip.h" +#include "up_arch.h" + +#include "chip/lpc43_pinconfig.h" +#include "lpc43_cgu.h" +#include "lpc43_ccu.h" +#include "lpc43_gpio.h" +#include "lpc43_sdmmc.h" + +#include + +#ifdef CONFIG_LPC43_SDMMC + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MCI_DMADES0_OWN (1UL << 31) +#define MCI_DMADES0_CH (1 << 4) +#define MCI_DMADES0_FS (1 << 3) +#define MCI_DMADES0_LD (1 << 2) +#define MCI_DMADES0_DIC (1 << 1) +#define MCI_DMADES1_MAXTR 4096 +#define MCI_DMADES1_BS1(x) (x) + +/* Configuration ************************************************************/ +/* Required system configuration options in the sched/Kconfig: + * + * CONFIG_SCHED_WORKQUEUE -- Callback support requires work queue support. + * + * Driver-specific configuration options in the drivers/mmcd Kdonfig: + * + * CONFIG_SDIO_MUXBUS - Setting this configuration enables some locking + * APIs to manage concurrent accesses on the SD card bus. This is not + * needed for the simple case of a single SD card slot, for example. + * CONFIG_SDIO_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_MMCSD_HAVE_CARDDETECT - Select if the SD slot supports a card + * detect pin. + * CONFIG_MMCSD_HAVE_WRITEPROTECT - Select if the SD slots supports a + * write protected pin. + * + * Driver-specific configuration options in the arch/arm/src/lpc43xx/Kconfig + * + * CONFIG_LPC43_SDMMC_PWRCTRL - Select if the board supports an output + * pin to enable power to the SD slot. + * CONFIG_LPC43_SDMMC_DMA - Enable SD card DMA. This is a marginally + * optional. For most usages, SD accesses will cause data overruns if + * used without DMA. This will also select CONFIG_SDIO_DMA. + * CONFIG_LPC43_SDMMC_REGDEBUG - Enables some very low-level debug output + * This also requires CONFIG_DEBUG_MEMCARD_INFO + */ + +#ifndef CONFIG_SCHED_WORKQUEUE +# error "Callback support requires CONFIG_SCHED_WORKQUEUE" +#endif + +/* Timing */ + +#define SDCARD_CMDTIMEOUT (10000) +#define SDCARD_LONGTIMEOUT (0x7fffffff) + +/* Type of Card Bus Size */ + +#define SDCARD_BUS_D1 0 +#define SDCARD_BUS_D4 1 +#define SDCARD_BUS_D8 0x100 + +/* FIFO size in bytes */ + +#define LPC43_TXFIFO_SIZE (LPC43_TXFIFO_DEPTH | LPC43_TXFIFO_WIDTH) +#define LPC43_RXFIFO_SIZE (LPC43_RXFIFO_DEPTH | LPC43_RXFIFO_WIDTH) + +/* Data transfer interrupt mask bits */ + +#define SDCARD_RECV_MASK (SDMMC_INT_DCRC | SDMMC_INT_RCRC | SDMMC_INT_DRTO | \ + SDMMC_INT_RTO | SDMMC_INT_EBE | SDMMC_INT_RXDR | \ + SDMMC_INT_SBE) +#define SDCARD_SEND_MASK (SDMMC_INT_DCRC | SDMMC_INT_RCRC | SDMMC_INT_DRTO | \ + SDMMC_INT_RTO | SDMMC_INT_EBE | SDMMC_INT_TXDR | \ + SDMMC_INT_DTO | SDMMC_INT_SBE) + +#define SDCARD_DMARECV_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \ + SDMMC_INT_SBE | SDMMC_INT_EBE) +#define SDCARD_DMASEND_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \ + SDMMC_INT_EBE) + +#define SDCARD_DMAERROR_MASK (SDMMC_IDINTEN_FBE | SDMMC_IDINTEN_DU | \ + SDMMC_IDINTEN_AIS) + +/* Event waiting interrupt mask bits */ + +#define SDCARD_INT_ERROR (SDMMC_INT_RE | SDMMC_INT_RCRC | SDMMC_INT_DCRC | \ + SDMMC_INT_RTO | SDMMC_INT_DRTO | SDMMC_INT_HTO | \ + SDMMC_INT_FRUN | SDMMC_INT_HLE | SDMMC_INT_SBE | \ + SDMMC_INT_EBE) + +#ifdef CONFIG_MMCSD_HAVE_CARDDETECT +# define SDCARD_INT_CDET SDMMC_INT_CDET +#else +# define SDCARD_INT_CDET 0 +#endif + +#define SDCARD_CMDDONE_STA (SDMMC_INT_CDONE) +#define SDCARD_RESPDONE_STA (0) + +#define SDCARD_CMDDONE_MASK (SDMMC_INT_CDONE) +#define SDCARD_RESPDONE_MASK (SDMMC_INT_RTO | SDMMC_INT_RCRC | SDMMC_INT_CDONE) +#define SDCARD_XFRDONE_MASK (SDMMC_INT_DTO) + +#define SDCARD_CMDDONE_ICR (SDMMC_INT_CDONE) +#define SDCARD_RESPDONE_ICR (SDMMC_INT_RTO | SDMMC_INT_RCRC | SDMMC_INT_CDONE) + +#define SDCARD_XFRDONE_ICR (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \ + SDMMC_INT_FRUN | SDMMC_INT_SBE) + +#define SDCARD_WAITALL_ICR (SDCARD_CMDDONE_ICR | SDCARD_RESPDONE_ICR | \ + SDCARD_XFRDONE_ICR) + +/* Let's wait until we have both SD card transfer complete and DMA complete. */ + +#define SDCARD_XFRDONE_FLAG (1) +#define SDCARD_DMADONE_FLAG (2) +#define SDCARD_ALLDONE (3) + +/* Card debounce time. Number of host clocks (SD_CLK) used by debounce + * filter logic for card detect. typical debounce time is 5-25 ms. + * + * Eg. Fsd = 44MHz, ticks = 660,000 + */ + +#define DEBOUNCE_TICKS (15 * BOARD_SDMMC_FREQUENCY / 1000) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sdmmc_dma_s +{ + volatile uint32_t des0; /* Control and status */ + volatile uint32_t des1; /* Buffer size(s) */ + volatile uint32_t des2; /* Buffer address pointer 1 */ + volatile uint32_t des3; /* Buffer address pointer 2 */ +}; + +/* This structure defines the state of the LPC43XX SD card interface */ + +struct lpc43_dev_s +{ + struct sdio_dev_s dev; /* Standard, base SD card interface */ + + /* LPC43XX-specific extensions */ + /* 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 */ + uint32_t xfrmask; /* Interrupt enables for data transfer */ +#ifdef CONFIG_LPC43_SDMMC_DMA + uint32_t dmamask; /* Interrupt enables for DMA transfer */ +#endif + ssize_t remaining; /* Number of bytes remaining in the transfer */ + bool wrdir; /* True: Writing False: Reading */ + + /* DMA data transfer support */ + + bool widebus; /* Required for DMA support */ +#ifdef CONFIG_LPC43_SDMMC_DMA + bool dmamode; /* true: DMA mode transfer */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_REGDEBUG +static uint32_t lpc43_getreg(uint32_t addr); +static void lpc43_putreg(uint32_t val, uint32_t addr); +static void lpc43_checksetup(void); +#else +# define lpc43_getreg(addr) getreg32(addr) +# define lpc43_putreg(val,addr) putreg32(val,addr) +# define lpc43_checksetup() +#endif + +/* Low-level helpers ********************************************************/ + +static void lpc43_takesem(struct lpc43_dev_s *priv); +#define lpc43_givesem(priv) (sem_post(&priv->waitsem)) +static inline void lpc43_setclock(uint32_t clkdiv); +static inline void lpc43_settype(uint32_t ctype); +static inline void lpc43_sdcard_clock(bool enable); +static int lpc43_ciu_sendcmd(uint32_t cmd, uint32_t arg); +static void lpc43_configwaitints(struct lpc43_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, sdio_eventset_t wkupevents); +static void lpc43_configxfrints(struct lpc43_dev_s *priv, uint32_t xfrmask); +#ifdef CONFIG_LPC43_SDMMC_DMA +static void lpc43_configdmaints(struct lpc43_dev_s *priv, uint32_t dmamask); +#endif + +/* Data Transfer Helpers ****************************************************/ + +static void lpc43_eventtimeout(int argc, uint32_t arg); +static void lpc43_endwait(struct lpc43_dev_s *priv, sdio_eventset_t wkupevent); +static void lpc43_endtransfer(struct lpc43_dev_s *priv, sdio_eventset_t wkupevent); + +/* Interrupt Handling *******************************************************/ + +static int lpc43_sdmmc_interrupt(int irq, void *context, FAR void *arg); + +/* SD Card Interface Methods ************************************************/ + +/* Mutual exclusion */ + +#ifdef CONFIG_SDIO_MUXBUS +static int lpc43_lock(FAR struct sdio_dev_s *dev, bool lock); +#endif + +/* Initialization/setup */ + +static void lpc43_reset(FAR struct sdio_dev_s *dev); +static sdio_capset_t lpc43_capabilities(FAR struct sdio_dev_s *dev); +static uint8_t lpc43_status(FAR struct sdio_dev_s *dev); +static void lpc43_widebus(FAR struct sdio_dev_s *dev, bool enable); +static void lpc43_clock(FAR struct sdio_dev_s *dev, + enum sdio_clock_e rate); +static int lpc43_attach(FAR struct sdio_dev_s *dev); + +/* Command/Status/Data Transfer */ + +static int lpc43_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg); +static int lpc43_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes); +static int lpc43_sendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, uint32_t nbytes); +static int lpc43_cancel(FAR struct sdio_dev_s *dev); + +static int lpc43_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd); +static int lpc43_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int lpc43_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]); +static int lpc43_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int lpc43_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rnotimpl); + +/* EVENT handler */ + +static void lpc43_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static sdio_eventset_t + lpc43_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout); +static void lpc43_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static void lpc43_callback(struct lpc43_dev_s *priv); +static int lpc43_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg); + +/* DMA */ + +#ifdef CONFIG_LPC43_SDMMC_DMA +static int lpc43_dmarecvsetup(FAR struct sdio_dev_s *dev, + FAR uint8_t *buffer, size_t buflen); +static int lpc43_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct lpc43_dev_s g_scard_dev = +{ + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = lpc43_lock, +#endif + .reset = lpc43_reset, + .capabilities = lpc43_capabilities, + .status = lpc43_status, + .widebus = lpc43_widebus, + .clock = lpc43_clock, + .attach = lpc43_attach, + .sendcmd = lpc43_sendcmd, + .recvsetup = lpc43_recvsetup, + .sendsetup = lpc43_sendsetup, + .cancel = lpc43_cancel, + .waitresponse = lpc43_waitresponse, + .recvR1 = lpc43_recvshortcrc, + .recvR2 = lpc43_recvlong, + .recvR3 = lpc43_recvshort, + .recvR4 = lpc43_recvnotimpl, + .recvR5 = lpc43_recvnotimpl, + .recvR6 = lpc43_recvshortcrc, + .recvR7 = lpc43_recvshort, + .waitenable = lpc43_waitenable, + .eventwait = lpc43_eventwait, + .callbackenable = lpc43_callbackenable, + .registercallback = lpc43_registercallback, +#ifdef CONFIG_LPC43_SDMMC_DMA + .dmarecvsetup = lpc43_dmarecvsetup, + .dmasendsetup = lpc43_dmasendsetup, +#endif + }, +}; + +#ifdef CONFIG_LPC43_SDMMC_DMA +static struct sdmmc_dma_s g_sdmmc_dmadd[1 + (0x10000 / MCI_DMADES1_MAXTR)]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lpc43_getreg + * + * Description: + * This function may to used to intercept an monitor all register accesses. + * Clearly this is nothing you would want to do unless you are debugging + * this driver. + * + * Input Parameters: + * addr - The register address to read + * + * Returned Value: + * The value read from the register + * + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_REGDEBUG +static uint32_t lpc43_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + mcinfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + mcinfo("[repeats %d more times]\n", count-3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + mcinfo("%08x->%08x\n", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: lpc43_putreg + * + * Description: + * This function may to used to intercept an monitor all register accesses. + * Clearly this is nothing you would want to do unless you are debugging + * this driver. + * + * Input Parameters: + * val - The value to write to the register + * addr - The register address to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_REGDEBUG +static void lpc43_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + mcinfo("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: lpc43_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * dev - Instance of the SD card device driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_takesem(struct lpc43_dev_s *priv) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_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: lpc43_setclock + * + * Description: + * Define the new clock frequency + * + * Input Parameters: + * clkdiv - A new division value to generate the needed frequency. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lpc43_setclock(uint32_t clkdiv) +{ + mcinfo("clkdiv=%08lx\n", (unsigned long)clkdiv); + + /* Disable the clock before setting frequency */ + + lpc43_sdcard_clock(false); + + /* Use the Divider0 */ + + lpc43_putreg(SDMMC_CLKSRC_CLKDIV0, LPC43_SDMMC_CLKSRC); + + /* Inform CIU */ + + lpc43_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0); + + /* Set Divider0 to desired value */ + + lpc43_putreg(clkdiv, LPC43_SDMMC_CLKDIV); + + /* Inform CIU */ + + lpc43_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0); + + /* Enable the clock */ + + lpc43_sdcard_clock(true); + + /* Inform CIU */ + + lpc43_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0); +} + +/**************************************************************************** + * Name: lpc43_settype + * + * Description: Define the Bus Size of SDCard (1, 4 or 8-bit) + * + * Input Parameters: + * ctype - A new CTYPE (Card Type Register) value + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lpc43_settype(uint32_t ctype) +{ + mcinfo("cteype=%08lx\n", (unsigned long)ctype); + lpc43_putreg(ctype, LPC43_SDMMC_CTYPE); +} + +/**************************************************************************** + * Name: lpc43_sdcard_clock + * + * Description: Enable/Disable the SDCard clock + * + * Input Parameters: + * enable - False = clock disabled; True = clock enabled. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void lpc43_sdcard_clock(bool enable) +{ + if (enable) + { + lpc43_putreg(SDMMC_CLKENA_ENABLE, LPC43_SDMMC_CLKENA); + } + else + { + lpc43_putreg(0, LPC43_SDMMC_CLKENA); + } +} + +/**************************************************************************** + * Name: lpc43_ciu_sendcmd + * + * Description: + * Function to send command to Card interface unit (CIU) + * + * Input Parameters: + * cmd - The command to be executed + * arg - The argument to use with the command. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int lpc43_ciu_sendcmd(uint32_t cmd, uint32_t arg) +{ + volatile int32_t tmo = SDCARD_CMDTIMEOUT; + + mcinfo("cmd=%04lx arg=%04lx\n", (unsigned long)cmd, (unsigned long)arg); + + /* Set command arg reg */ + + lpc43_putreg(arg, LPC43_SDMMC_CMDARG); + lpc43_putreg(SDMMC_CMD_STARTCMD | cmd, LPC43_SDMMC_CMD); + + /* Poll until command is accepted by the CIU */ + + while (--tmo > 0 && (lpc43_getreg(LPC43_SDMMC_CMD) & SDMMC_CMD_STARTCMD) != 0) + { + } + + return (tmo < 1) ? 1 : 0; +} + +/**************************************************************************** + * Name: lpc43_setupints + * + * Description: + * Enable/disable SD card interrupts per functional settings. + * + * Input Parameters: + * priv - A reference to the SD card device state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_setupints(struct lpc43_dev_s *priv) +{ + uint32_t regval; + +#ifdef CONFIG_LPC43_SDMMC_DMA + mcinfo("waitmask=%04lx xfrmask=%04lx dmamask=%04lx\n", + (unsigned long)priv->waitmask, (unsigned long)priv->xfrmask, + (unsigned long)priv->dmamask); + + /* Enable DMA-related interrupts */ + + lpc43_putreg(priv->dmamask, LPC43_SDMMC_IDINTEN); + +#else + mcinfo("waitmask=%04lx xfrmask=%04lx\n", + (unsigned long)priv->waitmask, (unsigned long)priv->xfrmask); +#endif + + /* Enable SDMMC interrupts */ + + regval = priv->xfrmask | priv->waitmask | SDCARD_INT_CDET; + lpc43_putreg(regval, LPC43_SDMMC_INTMASK); +} + +/**************************************************************************** + * Name: lpc43_configwaitints + * + * Description: + * Enable/disable SD card interrupts needed to suport the wait function + * + * Input Parameters: + * priv - A reference to the SD card device state structure + * waitmask - The set of bits in the SD card MASK register to set + * waitevents - Waited for events + * wkupevent - Wake-up events + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_configwaitints(struct lpc43_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, + sdio_eventset_t wkupevent) +{ + irqstate_t flags; + + mcinfo("waitevents=%04x wkupevent=%04x\n", + (unsigned)waitevents, (unsigned)wkupevent); + + /* Save all of the data and set the new interrupt mask in one, atomic + * operation. + */ + + flags = enter_critical_section(); + priv->waitevents = waitevents; + priv->wkupevent = wkupevent; + priv->waitmask = waitmask; + + lpc43_setupints(priv); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: lpc43_configxfrints + * + * Description: + * Enable SD card interrupts needed to support the data transfer event + * + * Input Parameters: + * priv - A reference to the SD card device state structure + * xfrmask - The set of bits in the SD card MASK register to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_configxfrints(struct lpc43_dev_s *priv, uint32_t xfrmask) +{ + irqstate_t flags; + flags = enter_critical_section(); + + priv->xfrmask = xfrmask; + lpc43_setupints(priv); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: lpc43_configdmaints + * + * Description: + * Enable DMA interrupts + * + * Input Parameters: + * priv - A reference to the SD card device state structure + * dmamask - The set of bits in the SD card MASK register to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_DMA +static void lpc43_configdmaints(struct lpc43_dev_s *priv, uint32_t dmamask) +{ + irqstate_t flags; + flags = enter_critical_section(); + + priv->dmamask = dmamask; + lpc43_setupints(priv); + + leave_critical_section(flags); +} +#endif + +/**************************************************************************** + * Name: lpc43_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 (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void lpc43_eventtimeout(int argc, uint32_t arg) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)arg; + + mcinfo("argc=%d, arg=%08lx\n", argc, (unsigned long)arg); + + /* There is always race conditions with timer expirations. */ + + DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 || priv->wkupevent != 0); + + /* Is a data transfer complete event expected? */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + /* Yes.. wake up any waiting threads */ + + lpc43_endwait(priv, SDIOWAIT_TIMEOUT); + mcerr("ERROR: Timeout: remaining: %d\n", priv->remaining); + } +} + +/**************************************************************************** + * Name: lpc43_endwait + * + * Description: + * Wake up a waiting thread if the waited-for event has occurred. + * + * Input Parameters: + * priv - An instance of the SD card device interface + * wkupevent - The event that caused the wait to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void lpc43_endwait(struct lpc43_dev_s *priv, sdio_eventset_t wkupevent) +{ + mcinfo("wkupevent=%04x\n", (unsigned)wkupevent); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* Disable event-related interrupts */ + + lpc43_configwaitints(priv, 0, 0, wkupevent); + + /* Wake up the waiting thread */ + + lpc43_givesem(priv); +} + +/**************************************************************************** + * Name: lpc43_endtransfer + * + * Description: + * Terminate a transfer with the provided status. This function is called + * only from the SD card interrupt handler when end-of-transfer conditions + * are detected. + * + * Input Parameters: + * priv - An instance of the SD card device interface + * wkupevent - The event that caused the transfer to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void lpc43_endtransfer(struct lpc43_dev_s *priv, sdio_eventset_t wkupevent) +{ + mcinfo("wkupevent=%04x\n", (unsigned)wkupevent); + + /* Disable all transfer related interrupts */ + + lpc43_configxfrints(priv, 0); + + /* Clearing pending interrupt status on all transfer related interrupts */ + + lpc43_putreg(priv->waitmask, LPC43_SDMMC_RINTSTS); + + /* 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 */ + + lpc43_endwait(priv, wkupevent); + } +} + +/**************************************************************************** + * Name: lpc43_sdmmc_interrupt + * + * Description: + * SD card interrupt handler + * + * Input Parameters: + * dev - An instance of the SD card device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int lpc43_sdmmc_interrupt(int irq, void *context, FAR void *arg) +{ + struct lpc43_dev_s *priv = &g_scard_dev; + uint32_t enabled; + uint32_t pending; + + /* Loop while there are pending interrupts. Check the SD card 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 = lpc43_getreg(LPC43_SDMMC_MINTSTS)) != 0) + { + /* Clear pending status */ + + lpc43_putreg(enabled, LPC43_SDMMC_RINTSTS); + +#ifdef CONFIG_MMCSD_HAVE_CARDDETECT + /* Handle in card detection events ************************************/ + + if ((enabled & SDMMC_INT_CDET) != 0) + { + sdio_statset_t cdstatus; + + /* Update card status */ + + cdstatus = priv->cdstatus; + if ((lpc43_getreg(LPC43_SDMMC_CDETECT) & SDMMC_CDETECT_NOTPRESENT) == 0) + { + priv->cdstatus |= SDIO_STATUS_PRESENT; + +#ifdef CONFIG_MMCSD_HAVE_WRITEPROTECT + if ((lpc43_getreg(LPC43_SDMMC_WRTPRT) & SDMMC_WRTPRT_PROTECTED) != 0) + { + priv->cdstatus |= SDIO_STATUS_WRPROTECTED; + } + else +#endif + { + priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED; + } + +#ifdef CONFIG_LPC43_SDMMC_PWRCTRL + /* Enable/ power to the SD card */ + + lpc43_putreg(SDMMC_PWREN, LPC43_SDMMC_PWREN); +#endif + + } + else + { + priv->cdstatus &= ~(SDIO_STATUS_PRESENT | SDIO_STATUS_WRPROTECTED); + +#ifdef CONFIG_LPC43_SDMMC_PWRCTRL + /* Disable power to the SD card */ + + lpc43_putreg(0, LPC43_SDMMC_PWREN); +#endif + } + + mcinfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus); + + /* Perform any requested callback if the status has changed */ + + if (cdstatus != priv->cdstatus) + { + lpc43_callback(priv); + } + } +#endif + + /* Handle in progress, interrupt driven data transfers ****************/ + + pending = enabled & priv->xfrmask; + if (pending != 0) + { + /* Handle data request events */ + + if ((pending & SDMMC_INT_TXDR) != 0) + { + uint32_t status; + + /* Transfer data to the TX FIFO */ + + mcinfo("Write FIFO\n"); + DEBUGASSERT(priv->wrdir); + + for (status = lpc43_getreg(LPC43_SDMMC_STATUS); + (status & SDMMC_STATUS_FIFOFULL) == 0 && + priv->remaining > 0; + status = lpc43_getreg(LPC43_SDMMC_STATUS)) + { + lpc43_putreg(*priv->buffer, LPC43_SDMMC_DATA); + priv->buffer++; + priv->remaining -= 4; + } + + /* If all of the data has been transferred to the FIFO, then + * disable further TX data requests and wait for the data end + * event. + */ + + if (priv->remaining <= 0) + { + uint32_t intmask = lpc43_getreg(LPC43_SDMMC_INTMASK); + intmask &= ~SDMMC_INT_TXDR; + lpc43_putreg(intmask, LPC43_SDMMC_INTMASK); + + priv->xfrmask &= ~SDMMC_INT_TXDR; + } + } + else if ((pending & SDMMC_INT_RXDR) != 0) + { + uint32_t status; + + /* Transfer data from the RX FIFO */ + + mcinfo("Read from FIFO\n"); + DEBUGASSERT(!priv->wrdir); + + for (status = lpc43_getreg(LPC43_SDMMC_STATUS); + (status & SDMMC_STATUS_FIFOEMPTY) == 0 && + priv->remaining > 0; + status = lpc43_getreg(LPC43_SDMMC_STATUS)) + { + *priv->buffer = lpc43_getreg(LPC43_SDMMC_DATA); + priv->buffer++; + priv->remaining -= 4; + } + + /* If all of the data has been transferred to the FIFO, then + * just force DTO event processing (the DTO interrupt is not + * actually even enabled in this use case). + */ + + if (priv->remaining <= 0) + { + /* Force the DTO event */ + + pending |= SDMMC_INT_DTO; + } + } + + /* Handle data block send/receive CRC failure */ + + if ((pending & SDMMC_INT_DCRC) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data block CRC failure, remaining: %d\n", priv->remaining); + lpc43_putreg(SDMMC_INT_DCRC, LPC43_SDMMC_RINTSTS); + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle data timeout error */ + + else if ((pending & SDMMC_INT_DRTO) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data timeout, remaining: %d\n", priv->remaining); + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); + } + + /* Handle RX FIFO overrun error */ + + else if ((pending & SDMMC_INT_FRUN) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: RX FIFO overrun, remaining: %d\n", priv->remaining); + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle TX FIFO underrun error */ + + else if ((pending & SDMMC_INT_FRUN) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: TX FIFO underrun, remaining: %d\n", priv->remaining); + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle start bit error */ + + else if ((pending & SDMMC_INT_SBE) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Start bit, remaining: %d\n", priv->remaining); + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle data end events. Note that RXDR may accompany DTO, DTO + * will be set on received while there is still data in the FIFO. + * So for the case of receiving, we don't actually even enable the + * DTO interrupt. + */ + + else if ((pending & SDMMC_INT_DTO) != 0) + { + /* Finish the transfer */ + + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + + } + + /* Handle wait events *************************************************/ + + pending = enabled & priv->waitmask; + if (pending != 0) + { + /* Is this a response completion event? */ + + if ((pending & SDMMC_INT_DTO) != 0) + { + /* Yes.. Is their a thread waiting for response done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, + LPC43_SDMMC_RINTSTS); + lpc43_endwait(priv, SDIOWAIT_RESPONSEDONE); + } + } + + /* Is this a command completion event? */ + + if ((pending & SDMMC_INT_CDONE) != 0) + { + /* Yes.. Is their a thread waiting for command done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + lpc43_putreg(SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + lpc43_endwait(priv, SDIOWAIT_CMDDONE); + } + } + } + } + +#ifdef CONFIG_LPC43_SDMMC_DMA + /* DMA error events *******************************************************/ + + pending = lpc43_getreg(LPC43_SDMMC_IDSTS); + if ((pending & priv->dmamask) != 0) + { + mcerr("ERROR: IDTS=%08lx\n", (unsigned long)pending); + + /* Clear the pending interrupts */ + + lpc43_putreg(pending, LPC43_SDMMC_IDSTS); + + /* Abort the transfer */ + + lpc43_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: lpc43_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 SD card 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 lpc43_lock(FAR struct sdio_dev_s *dev, bool lock) +{ + /* Single SD card instance so there is only one possibility. The multiplex + * bus is part of board support package. + */ + + lpc43_muxbus_sdio_lock(lock); + return OK; +} +#endif + +/**************************************************************************** + * Name: lpc43_reset + * + * Description: + * Reset the SD card controller. Undo all setup and initialization. + * + * Input Parameters: + * dev - An instance of the SD card device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_reset(FAR struct sdio_dev_s *dev) +{ + FAR struct lpc43_dev_s *priv = (FAR struct lpc43_dev_s *)dev; + irqstate_t flags; + uint32_t regval; + + mcinfo("Resetting...\n"); + + flags = enter_critical_section(); + + /* Reset DMA controller internal registers. */ + + lpc43_putreg(SDMMC_BMOD_SWR, LPC43_SDMMC_BMOD); + + /* Software Reset */ + + lpc43_putreg(SDMMC_BMOD_SWR, LPC43_SDMMC_BMOD); + + /* Reset all blocks */ + + lpc43_putreg(SDMMC_CTRL_CNTLRRESET | SDMMC_CTRL_FIFORESET | + SDMMC_CTRL_DMARESET, LPC43_SDMMC_CTRL); + + while ((regval = lpc43_getreg(LPC43_SDMMC_CTRL)) & + (SDMMC_CTRL_CNTLRRESET | SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET)); + + /* 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 */ + + 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 */ +#ifdef CONFIG_LPC43_SDMMC_DMA + priv->dmamask = 0; /* Interrupt enables for DMA transfer */ +#endif + + /* DMA data transfer support */ + + priv->widebus = true; /* Required for DMA support */ + priv->cdstatus = 0; /* Card status is unknown */ + + regval = 0; + +#ifdef CONFIG_LPC43_SDMMC_DMA + priv->dmamode = false; /* true: DMA mode transfer */ + + /* Use the Internal DMA */ + + regval = SDMMC_CTRL_INTDMA; +#endif + + /* Enable interrupts */ + + regval |= SDMMC_CTRL_INTENABLE; + lpc43_putreg(regval, LPC43_SDMMC_CTRL); + + /* Disable Interrupts except for card detection. */ + + lpc43_putreg(SDCARD_INT_CDET, LPC43_SDMMC_INTMASK); + +#ifdef CONFIG_MMCSD_HAVE_CARDDETECT + /* Set the card debounce time. Number of host clocks (SD_CLK) used by + * debounce filter logic for card detect. typical debounce time is 5-25 + * ms. + */ + + lpc43_putreg(DEBOUNCE_TICKS, LPC43_SDMMC_DEBNCE); +#endif + + /* Clear to Interrupts */ + + lpc43_putreg(0xffffffff, LPC43_SDMMC_RINTSTS); + + /* Define MAX Timeout */ + + lpc43_putreg(SDCARD_LONGTIMEOUT, LPC43_SDMMC_TMOUT); + + /* Disable clock to CIU (needs latch) */ + + lpc43_putreg(0, LPC43_SDMMC_CLKENA); + leave_critical_section(flags); + +#if defined(CONFIG_LPC43_SDMMC_PWRCTRL) && !defined(CONFIG_MMCSD_HAVE_CARDDETECT) + /* Enable power to the SD card */ + + lpc43_putreg(SDMMC_PWREN, LPC43_SDMMC_PWREN); +#endif +} + +/**************************************************************************** + * Name: lpc43_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 lpc43_capabilities(FAR struct sdio_dev_s *dev) +{ + sdio_capset_t caps = 0; + + caps |= SDIO_CAPS_DMABEFOREWRITE; + +#ifdef CONFIG_SDIO_WIDTH_D1_ONLY + caps |= SDIO_CAPS_1BIT_ONLY; +#endif +#ifdef CONFIG_LPC43_SDMMC_DMA + caps |= SDIO_CAPS_DMASUPPORTED; +#endif + + return caps; +} + +/**************************************************************************** + * Name: lpc43_status + * + * Description: + * Get SD card status. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Returns a bitset of status values (see lpc43_status_* defines) + * + ****************************************************************************/ + +static sdio_statset_t lpc43_status(FAR struct sdio_dev_s *dev) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("cdstatus=%02x\n", priv->cdstatus); + + return priv->cdstatus; +} + +/**************************************************************************** + * Name: lpc43_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 SD card device interface + * wide - true: wide bus (4-bit) bus mode enabled + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_widebus(FAR struct sdio_dev_s *dev, bool wide) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("wide=%d\n", wide); + + priv->widebus = wide; +} + +/**************************************************************************** + * Name: lpc43_clock + * + * Description: + * Enable/disable SD card clocking + * + * Input Parameters: + * dev - An instance of the SD card device interface + * rate - Specifies the clocking to use (see enum sdio_clock_e) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate) +{ + uint8_t clkdiv; + uint8_t ctype; + bool enabled = false; + + switch (rate) + { + /* Disable clocking (with default ID mode divisor) */ + + default: + case CLOCK_SDIO_DISABLED: + clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_INIT); + ctype = SDCARD_BUS_D1; + enabled = false; + return; + break; + + /* Enable in initial ID mode clocking (<400KHz) */ + + case CLOCK_IDMODE: + clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_INIT); + ctype = SDCARD_BUS_D1; + enabled = true; + break; + + /* Enable in MMC normal operation clocking */ + + case CLOCK_MMC_TRANSFER: + clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_MMCXFR); + ctype = SDCARD_BUS_D1; + enabled = true; + break; + + /* SD normal operation clocking (wide 4-bit mode) */ + + case CLOCK_SD_TRANSFER_4BIT: +#ifndef CONFIG_SDIO_WIDTH_D1_ONLY + clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_SDWIDEXFR); + ctype = SDCARD_BUS_D4; + enabled = true; + break; +#endif + + /* SD normal operation clocking (narrow 1-bit mode) */ + + case CLOCK_SD_TRANSFER_1BIT: + clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_SDXFR); + ctype = SDCARD_BUS_D1; + enabled = true; + break; + } + + /* Setup the type of card bus wide */ + + lpc43_settype(ctype); + + /* Set the new clock frequency division */ + + lpc43_setclock(clkdiv); + + /* Enable the new clock */ + + lpc43_sdcard_clock(enabled); +} + +/**************************************************************************** + * Name: lpc43_attach + * + * Description: + * Attach and prepare interrupts + * + * Input Parameters: + * dev - An instance of the SD card device interface + * + * Returned Value: + * OK on success; A negated errno on failure. + * + ****************************************************************************/ + +static int lpc43_attach(FAR struct sdio_dev_s *dev) +{ + int ret; + uint32_t regval; + + mcinfo("Attaching..\n"); + + /* Attach the SD card interrupt handler */ + + ret = irq_attach(LPC43M4_IRQ_SDIO, lpc43_sdmmc_interrupt, NULL); + if (ret == OK) + { + /* Disable all interrupts at the SD card controller and clear static + * interrupt flags + */ + + lpc43_putreg(0, LPC43_SDMMC_INTMASK); + lpc43_putreg(SDMMC_INT_ALL , LPC43_SDMMC_RINTSTS); + + /* Enable Interrupts to happen when the INTMASK is activated */ + + regval = lpc43_getreg(LPC43_SDMMC_CTRL); + regval |= SDMMC_CTRL_INTENABLE; + lpc43_putreg(regval, LPC43_SDMMC_CTRL); + + /* Enable card detection interrupts */ + + lpc43_putreg(SDCARD_INT_CDET, LPC43_SDMMC_INTMASK); + + /* Enable SD card interrupts at the NVIC. They can now be enabled at + * the SD card controller as needed. + */ + + up_enable_irq(LPC43M4_IRQ_SDIO); + } + + return ret; +} + +/**************************************************************************** + * Name: lpc43_sendcmd + * + * Description: + * Send the SD card command + * + * Input Parameters: + * dev - An instance of the SD card device interface + * cmd - The command to send (32-bits, encoded) + * arg - 32-bit argument required with some commands + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int lpc43_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg) +{ + uint32_t regval = 0; + uint32_t cmdidx; + + mcinfo("cmd=%04x arg=%04x\n", cmd, arg); + + /* The CMD0 needs the SENDINIT CMD */ + + if (cmd == 0) + { + regval |= SDMMC_CMD_SENDINIT; + } + + /* Is this a Read/Write Transfer Command ? */ + + if ((cmd & MMCSD_WRDATAXFR) == MMCSD_WRDATAXFR) + { + regval |= SDMMC_CMD_DATAXFREXPTD | SDMMC_CMD_WRITE | SDMMC_CMD_WAITPREV; + } + else if ((cmd & MMCSD_RDDATAXFR) == MMCSD_RDDATAXFR) + { + regval |= SDMMC_CMD_DATAXFREXPTD | SDMMC_CMD_WAITPREV; + } + + /* Set WAITRESP bits */ + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + regval |= SDMMC_CMD_NORESPONSE; + break; + + case MMCSD_R1B_RESPONSE: + regval |= SDMMC_CMD_WAITPREV; + case MMCSD_R1_RESPONSE: + case MMCSD_R3_RESPONSE: + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + case MMCSD_R6_RESPONSE: + case MMCSD_R7_RESPONSE: + regval |= SDMMC_CMD_SHORTRESPONSE; + break; + + case MMCSD_R2_RESPONSE: + regval |= SDMMC_CMD_LONGRESPONSE; + break; + } + + /* Set the command index */ + + cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT; + regval |= cmdidx; + + mcinfo("cmd: %04x arg: %04x regval: %08x\n", cmd, arg, regval); + + /* Write the SD card CMD */ + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, + LPC43_SDMMC_RINTSTS); + lpc43_ciu_sendcmd(regval, arg); + + return OK; +} + +/**************************************************************************** + * Name: lpc43_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, SDCARD_WAITEVENT + * will be called to receive the indication that the transfer is complete. + * + * Input Parameters: + * dev - An instance of the SD card 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 lpc43_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + uint32_t blocksize; + uint32_t bytecnt; + + mcinfo("nbytes=%ld\n", (long) nbytes); + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Save the destination buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; + priv->wrdir = false; +#ifdef CONFIG_LPC43_SDMMC_DMA + priv->dmamode = false; +#endif + + /* Then set up the SD card data path */ + + if (nbytes < 64) + { + blocksize = nbytes; + bytecnt = nbytes; + } + else + { + blocksize = 64; + bytecnt = nbytes; + DEBUGASSERT((nbytes & 0x3f) == 0); + } + + lpc43_putreg(blocksize, LPC43_SDMMC_BLKSIZ); + lpc43_putreg(bytecnt, LPC43_SDMMC_BYTCNT); + + /* Configure the FIFO so that we will receive the RXDR interrupt whenever + * there are more than 1 words (at least 8 bytes) in the RX FIFO. + */ + + lpc43_putreg(SDMMC_FIFOTH_RXWMARK(1), LPC43_SDMMC_FIFOTH); + + /* Make sure that internal DMA is disabled */ + + lpc43_putreg(0, LPC43_SDMMC_BMOD); + + /* Configure the transfer interrupts */ + + lpc43_configxfrints(priv, SDCARD_RECV_MASK); + return OK; +} + +/**************************************************************************** + * Name: lpc43_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 SDCARD_SENDDATA is called. + * + * Input Parameters: + * dev - An instance of the SD card 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 lpc43_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer, + size_t nbytes) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("nbytes=%ld\n", (long)nbytes); + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; + priv->wrdir = true; +#ifdef CONFIG_LPC43_SDMMC_DMA + priv->dmamode = false; +#endif + + /* Configure the FIFO so that we will receive the TXDR interrupt whenever + * there the TX FIFO is at least half empty. + */ + + lpc43_putreg(SDMMC_FIFOTH_TXWMARK(LPC43_TXFIFO_DEPTH / 2), + LPC43_SDMMC_FIFOTH); + + /* Make sure that internal DMA is disabled */ + + lpc43_putreg(0, LPC43_SDMMC_BMOD); + + /* Configure the transfer interrupts */ + + lpc43_configxfrints(priv, SDCARD_SEND_MASK); + return OK; +} + +/**************************************************************************** + * Name: lpc43_cancel + * + * Description: + * Cancel the data transfer setup of SDCARD_RECVSETUP, SDCARD_SENDSETUP, + * SDCARD_DMARECVSETUP or SDCARD_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 SD card device interface + * + * Returned Value: + * OK is success; a negated errno on failure + * + ****************************************************************************/ + +static int lpc43_cancel(FAR struct sdio_dev_s *dev) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("Cancelling..\n"); + + /* Disable all transfer- and event- related interrupts */ + + lpc43_configxfrints(priv, 0); + lpc43_configwaitints(priv, 0, 0, 0); + + /* Clearing pending interrupt status on all transfer- and event- related + * interrupts + */ + + lpc43_putreg(SDCARD_WAITALL_ICR, LPC43_SDMMC_RINTSTS); + + /* Cancel any watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* Mark no transfer in progress */ + + priv->remaining = 0; + return OK; +} + +/**************************************************************************** + * Name: lpc43_waitresponse + * + * Description: + * Poll-wait for the response to the last command to be ready. + * + * Input Parameters: + * dev - An instance of the SD card 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 lpc43_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd) +{ + volatile int32_t timeout; + uint32_t events; + + mcinfo("cmd=%04x\n", cmd); + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + events = SDCARD_CMDDONE_STA; + timeout = SDCARD_CMDTIMEOUT; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R2_RESPONSE: + case MMCSD_R6_RESPONSE: + events = SDCARD_RESPDONE_STA; + timeout = SDCARD_LONGTIMEOUT; + break; + + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + return -ENOSYS; + + case MMCSD_R3_RESPONSE: + case MMCSD_R7_RESPONSE: + events = SDCARD_RESPDONE_STA; + timeout = SDCARD_CMDTIMEOUT; + break; + + default: + return -EINVAL; + } + + events |= SDCARD_CMDDONE_STA; + + mcinfo("cmd: %04x events: %04x STATUS: %08x RINTSTS: %08x\n", + cmd, events, lpc43_getreg(LPC43_SDMMC_STATUS), + lpc43_getreg(LPC43_SDMMC_RINTSTS)); + + /* Then wait for the response (or timeout or error) */ + + while ((lpc43_getreg(LPC43_SDMMC_RINTSTS) & events) != events) + { + if (--timeout <= 0) + { + mcerr("ERROR: Timeout cmd: %04x events: %04x STA: %08x RINTSTS: %08x\n", + cmd, events, lpc43_getreg(LPC43_SDMMC_STATUS), + lpc43_getreg(LPC43_SDMMC_RINTSTS)); + return -ETIMEDOUT; + } + else if ((lpc43_getreg(LPC43_SDMMC_RINTSTS) & SDCARD_INT_ERROR) != 0) + { + mcerr("ERROR: SDMMC failure cmd: %04x events: %04x STA: %08x RINTSTS: %08x\n", + cmd, events, lpc43_getreg(LPC43_SDMMC_STATUS), + lpc43_getreg(LPC43_SDMMC_RINTSTS)); + return -EIO; + } + } + + lpc43_putreg(SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + return OK; +} + +/**************************************************************************** + * Name: lpc43_recvRx + * + * Description: + * Receive response to SD card 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 SD card 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 reponse (due to + * transport problem -- timeout, CRC, etc.). The implementation only + * assures that the response is returned intacta and does not check errors + * within the response itself. + * + ****************************************************************************/ + +static int lpc43_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort) +{ + uint32_t regval; + + int ret = OK; + + mcinfo("cmd=%04x\n", cmd); + + /* 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=%04x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = lpc43_getreg(LPC43_SDMMC_RINTSTS); + if ((regval & SDMMC_INT_RTO) != 0) + { + mcerr("ERROR: Command timeout: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if ((regval & SDMMC_INT_RCRC) != 0) + { + mcerr("ERROR: CRC failure: %08x\n", regval); + ret = -EIO; + } + } + + /* Clear all pending message completion events and return the R1/R6 response */ + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + *rshort = lpc43_getreg(LPC43_SDMMC_RESP0); + mcinfo("CRC = %04x\n", *rshort); + + return ret; +} + +static int lpc43_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t rlong[4]) +{ + uint32_t regval; + int ret = OK; + + mcinfo("cmd=%04x\n", cmd); + + /* 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=%04x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = lpc43_getreg(LPC43_SDMMC_RINTSTS); + if (regval & SDMMC_INT_RTO) + { + mcerr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if (regval & SDMMC_INT_RCRC) + { + mcerr("ERROR: CRC fail STA: %08x\n", regval); + ret = -EIO; + } + } + + /* Return the long response */ + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + if (rlong) + { + rlong[0] = lpc43_getreg(LPC43_SDMMC_RESP3); + rlong[1] = lpc43_getreg(LPC43_SDMMC_RESP2); + rlong[2] = lpc43_getreg(LPC43_SDMMC_RESP1); + rlong[3] = lpc43_getreg(LPC43_SDMMC_RESP0); + } + + return ret; +} + +static int lpc43_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rshort) +{ + uint32_t regval; + int ret = OK; + + mcinfo("cmd=%04x\n", cmd); + + /* 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=%04x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout occurred (Apparently a CRC error can terminate + * a good response) + */ + + regval = lpc43_getreg(LPC43_SDMMC_RINTSTS); + if (regval & SDMMC_INT_RTO) + { + mcerr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + } + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + if (rshort) + { + *rshort = lpc43_getreg(LPC43_SDMMC_RESP0); + } + + return ret; +} + +/* MMC responses not supported */ + +static int lpc43_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rnotimpl) +{ + mcinfo("cmd=%04x\n", cmd); + + lpc43_putreg(SDCARD_RESPDONE_ICR | SDCARD_CMDDONE_ICR, LPC43_SDMMC_RINTSTS); + return -ENOSYS; +} + +/**************************************************************************** + * Name: lpc43_waitenable + * + * Description: + * Enable/disable of a set of SD card wait events. This is part of the + * the SDCARD_WAITEVENT sequence. The set of to-be-waited-for events is + * configured before calling lpc43_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) SDCARD_WAITENABLE is called + * again specifying a different set of wait events, or (2) SDCARD_EVENTWAIT + * returns. + * + * Input Parameters: + * dev - An instance of the SD card device interface + * eventset - A bitset of events to enable or disable (see SDIOWAIT_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + uint32_t waitmask; + + mcinfo("eventset=%04x\n", (unsigned int)eventset); + DEBUGASSERT(priv != NULL); + + /* Disable event-related interrupts */ + + lpc43_configwaitints(priv, 0, 0, 0); + + /* Select the interrupt mask that will give us the appropriate wakeup + * interrupts. + */ + + waitmask = 0; + if ((eventset & SDIOWAIT_CMDDONE) != 0) + { + waitmask |= SDCARD_CMDDONE_MASK; + } + + if ((eventset & SDIOWAIT_RESPONSEDONE) != 0) + { + waitmask |= SDCARD_RESPDONE_MASK; + } + + if ((eventset & SDIOWAIT_TRANSFERDONE) != 0) + { + waitmask |= SDCARD_XFRDONE_MASK; + } + + /* Enable event-related interrupts */ + + lpc43_configwaitints(priv, waitmask, eventset, 0); +} + +/**************************************************************************** + * Name: lpc43_eventwait + * + * Description: + * Wait for one of the enabled events to occur (or a timeout). Note that + * all events enabled by SDCARD_WAITEVENTS are disabled when lpc43_eventwait + * returns. SDCARD_WAITEVENTS must be called again before lpc43_eventwait + * can be used again. + * + * Input Parameters: + * dev - An instance of the SD card 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 lpc43_eventwait(FAR struct sdio_dev_s *dev, + uint32_t timeout) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + sdio_eventset_t wkupevent = 0; + irqstate_t flags; + int ret; + + mcinfo("timeout=%lu\n", (unsigned long)timeout); + + /* 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)lpc43_eventtimeout, + 1, (uint32_t)priv); + if (ret != OK) + { + mcerr("ERROR: wd_start failed: %d\n", ret); + } + } + + /* Loop until the event (or the timeout occurs). Race conditions are avoided + * by calling lpc43_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. + */ + + lpc43_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 */ + + lpc43_configwaitints(priv, 0, 0, 0); + +errout: + leave_critical_section(flags); + return wkupevent; +} + +/**************************************************************************** + * Name: lpc43_callbackenable + * + * Description: + * Enable/disable of a set of SD card callback events. This is part of the + * the SD card callback sequence. The set of events is configured to enabled + * callbacks to the function provided in lpc43_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 SD card device interface + * eventset - A bitset of events to enable or disable (see SDIOMEDIA_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lpc43_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("eventset: %02x\n", eventset); + DEBUGASSERT(priv != NULL); + + priv->cbevents = eventset; + lpc43_callback(priv); +} + +/**************************************************************************** + * Name: lpc43_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 SDCARD_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 lpc43_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + + mcinfo("callback=%p arg=%p\n", callback, arg); + + /* Disable callbacks and register this callback and its argument */ + + mcinfo("Register %p(%p)\n", callback, arg); + DEBUGASSERT(priv != NULL); + + priv->cbevents = 0; + priv->cbarg = arg; + priv->callback = callback; + return OK; +} + +/**************************************************************************** + * Name: lpc43_dmarecvsetup + * + * Description: + * Setup to perform a read DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For read transfers this may mean + * invalidating the data cache. + * + * Input Parameters: + * dev - An instance of the SD card device interface + * buffer - The memory to DMA from + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_DMA +static int lpc43_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t buflen) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + uint32_t regval; + uint32_t ctrl; + uint32_t maxs; + int i; + + /* Don't bother with DMA if the entire transfer will fit in the RX FIFO or + * if we do not have a 4-bit wide bus. + */ + + DEBUGASSERT(priv != NULL); + + if (buflen <= LPC43_RXFIFO_SIZE || !priv->widebus) + { + return lpc43_recvsetup(dev, buffer, buflen); + } + + mcinfo("buflen=%lu\n", (unsigned long)buflen); + DEBUGASSERT(buffer != NULL && buflen > 0 && ((uint32_t)buffer & 3) == 0); + + /* Reset DMA controller internal registers. The SWR bit automatically + * clears in one clock cycle. + */ + + lpc43_putreg(SDMMC_BMOD_SWR, LPC43_SDMMC_BMOD); + + /* Save the destination buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = buflen; + priv->wrdir = false; + priv->dmamode = true; + + /* Reset DMA */ + + regval = lpc43_getreg(LPC43_SDMMC_CTRL); + regval |= SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET; + lpc43_putreg(regval, LPC43_SDMMC_CTRL); + + while (lpc43_getreg(LPC43_SDMMC_CTRL) & SDMMC_CTRL_DMARESET) + { + } + + /* Configure the FIFO so that we will receive the DMA/FIFO requests whenever + * there more than than (FIFO_DEPTH/2) - 1 words in the FIFO. + */ + + regval = SDMMC_FIFOTH_RXWMARK(LPC43_RXFIFO_DEPTH / 2 - 1) | + SDMMC_FIFOTH_DMABURST_4XFRS; + lpc43_putreg(regval, LPC43_SDMMC_FIFOTH); + + /* Setup DMA list */ + + i = 0; + while (buflen > 0) + { + /* Limit size of the transfer to maximum buffer size */ + + maxs = buflen; + + if (maxs > MCI_DMADES1_MAXTR) + { + maxs = MCI_DMADES1_MAXTR; + } + + buflen -= maxs; + + /* Set buffer size */ + + g_sdmmc_dmadd[i].des1 = MCI_DMADES1_BS1(maxs); + + /* Setup buffer address (chained) */ + + g_sdmmc_dmadd[i].des2 = (uint32_t)priv->buffer + (i * MCI_DMADES1_MAXTR); + + /* Setup basic control */ + + ctrl = MCI_DMADES0_OWN | MCI_DMADES0_CH; + + if (i == 0) + { + ctrl |= MCI_DMADES0_FS; /* First DMA buffer */ + } + + /* No more data? Then this is the last descriptor */ + + if (buflen == 0) + { + ctrl |= MCI_DMADES0_LD; + } + else + { + ctrl |= MCI_DMADES0_DIC; + } + + /* Another descriptor is needed */ + + g_sdmmc_dmadd[i].des0 = ctrl; + g_sdmmc_dmadd[i].des3 = (uint32_t) &g_sdmmc_dmadd[i + 1]; + i++; + } + + lpc43_putreg((uint32_t) &g_sdmmc_dmadd[0], LPC43_SDMMC_DBADDR); + + /* Enable internal DMA, burst size of 4, fixed burst */ + + regval = SDMMC_BMOD_DE | SDMMC_BMOD_PBL_4XFRS | SDMMC_BMOD_DSL(4); + lpc43_putreg(regval, LPC43_SDMMC_BMOD); + + /* Setup DMA error interrupts */ + + lpc43_configxfrints(priv, SDCARD_DMARECV_MASK); + lpc43_configdmaints(priv, SDCARD_DMAERROR_MASK); + return OK; +} +#endif + +/**************************************************************************** + * Name: lpc43_dmasendsetup + * + * Description: + * Setup to perform a write DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For write transfers, this may mean + * flushing the data cache. + * + * Input Parameters: + * dev - An instance of the SD card device interface + * buffer - The memory to DMA into + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_LPC43_SDMMC_DMA +static int lpc43_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen) +{ + struct lpc43_dev_s *priv = (struct lpc43_dev_s *)dev; + uint32_t regval; + + /* Don't bother with DMA if the entire transfer will fit in the TX FIFO or + * if we do not have a 4-bit wide bus. + */ + + DEBUGASSERT(priv != NULL); + + if (buflen <= LPC43_TXFIFO_SIZE || !priv->widebus) + { + return lpc43_sendsetup(dev, buffer, buflen); + } + + mcinfo("buflen=%lu\n", (unsigned long)buflen); + DEBUGASSERT(buffer != NULL && buflen > 0 && ((uint32_t)buffer & 3) == 0); + + /* Reset DMA controller internal registers. The SWR bit automatically + * clears in one clock cycle. + */ + + lpc43_putreg(SDMMC_BMOD_SWR, LPC43_SDMMC_BMOD); + + /* Save the destination buffer information for use by the interrupt + * handler. + */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = buflen; + priv->wrdir = true; + priv->dmamode = true; + + /* Reset DMA */ + + regval = lpc43_getreg(LPC43_SDMMC_CTRL); + regval |= SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET; + lpc43_putreg(regval, LPC43_SDMMC_CTRL); + while (lpc43_getreg(LPC43_SDMMC_CTRL) & SDMMC_CTRL_DMARESET) + { + } + + /* Configure the FIFO so that we will receive the DMA/FIFO requests whenever + * there are FIFO_DEPTH/2 or fewer words in the FIFO. + */ + + regval = SDMMC_FIFOTH_TXWMARK(LPC43_TXFIFO_DEPTH / 2) | + SDMMC_FIFOTH_DMABURST_4XFRS; + lpc43_putreg(regval, LPC43_SDMMC_FIFOTH); + + /* Setup DMA descriptor list */ + + g_sdmmc_dmadd[0].des0 = MCI_DMADES0_OWN | MCI_DMADES0_CH | MCI_DMADES0_LD; + g_sdmmc_dmadd[0].des1 = 512; + g_sdmmc_dmadd[0].des2 = (uint32_t)priv->buffer; + g_sdmmc_dmadd[0].des3 = (uint32_t)&g_sdmmc_dmadd[1]; + + lpc43_putreg((uint32_t) &g_sdmmc_dmadd[0], LPC43_SDMMC_DBADDR); + + /* Enable internal DMA, burst size of 4, fixed burst */ + + regval = SDMMC_BMOD_DE | SDMMC_BMOD_PBL_4XFRS | SDMMC_BMOD_DSL(4); + lpc43_putreg(regval, LPC43_SDMMC_BMOD); + + /* Setup DMA error interrupts */ + + lpc43_configxfrints(priv, SDCARD_DMASEND_MASK); + lpc43_configdmaints(priv, SDCARD_DMAERROR_MASK); + return OK; +} +#endif + +/**************************************************************************** + * Name: lpc43_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 lpc43_callback(struct lpc43_dev_s *priv) +{ + /* Is a callback registered? */ + + 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); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lpc43_sdmmc_initialize + * + * Description: + * Initialize the SD/MMC peripheral for normal operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SD card interface structure. NULL is returned on failures. + * + ****************************************************************************/ + +FAR struct sdio_dev_s *lpc43_sdmmc_initialize(int slotno) +{ + struct lpc43_dev_s *priv = &g_scard_dev; + irqstate_t flags; + uint32_t regval; + + mcinfo("slotno=%d\n", slotno); + flags = enter_critical_section(); + +/* Configure clocking */ + + regval = getreg32(LPC43_BASE_SDIO_CLK); + regval &= ~BASE_SDIO_CLK_CLKSEL_MASK; + regval |= (BOARD_SDIO_CLKSRC | BASE_SDIO_CLK_AUTOBLOCK); + putreg32(regval, LPC43_BASE_SDIO_CLK); + + /* Enable clocking to the SDIO block */ + + regval = lpc43_getreg(LPC43_CCU1_M4_SDIO_CFG); + regval |= CCU_CLK_CFG_RUN; + regval |= CCU_CLK_CFG_AUTO; + regval |= CCU_CLK_CFG_WAKEUP; + lpc43_putreg(regval, LPC43_CCU1_M4_SDIO_CFG); + + /* REVISIT: The delay values on the sample and drive inputs and outputs + * can be adjusted using the SDIOCLKCTRL register in the SYSCON block. + */ + + /* Initialize semaphores */ + + sem_init(&priv->waitsem, 0, 0); + + /* The waitsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + sem_setprotocol(&priv->waitsem, SEM_PRIO_NONE); + + /* Create a watchdog timer */ + + priv->waitwdog = wd_create(); + DEBUGASSERT(priv->waitwdog != NULL); + + /* Configure GPIOs for 4-bit, wide-bus operation */ + + lpc43_pin_config(GPIO_SD_D0); +#ifndef CONFIG_SDIO_WIDTH_D1_ONLY + lpc43_pin_config(GPIO_SD_D1); + lpc43_pin_config(GPIO_SD_D2); + lpc43_pin_config(GPIO_SD_D3); +#endif +#ifdef CONFIG_MMCSD_HAVE_CARDDETECT + lpc43_pin_config(GPIO_SD_CARD_DET_N); +#endif + lpc43_pin_config(GPIO_SD_CLK); + lpc43_pin_config(GPIO_SD_CMD); +#ifdef CONFIG_LPC43_SDMMC_PWRCTRL + lpc43_pin_config(GPIO_SD_POW_EN); +#endif +#ifdef CONFIG_MMCSD_HAVE_WRITEPROTECT + lpc43_pin_config(GPIO_SD_WR_PRT); +#endif + + /* Reset the card and assure that it is in the initial, unconfigured + * state. + */ + + lpc43_reset(&priv->dev); + + leave_critical_section(flags); + return &g_scard_dev.dev; +} + +#endif /* CONFIG_LPC43_SDMMC */ diff --git a/arch/arm/src/lpc43xx/lpc43_sdmmc.h b/arch/arm/src/lpc43xx/lpc43_sdmmc.h new file mode 100644 index 00000000000..7a27088415c --- /dev/null +++ b/arch/arm/src/lpc43xx/lpc43_sdmmc.h @@ -0,0 +1,88 @@ +/************************************************************************************ + * arch/arm/src/lpc43xx/lpc43_sdmmc.h + * + * Copyright (C) 2017 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_LPC43XX_LPC43_SDMMC_H +#define __ARCH_ARM_SRC_LPC43XX_LPC43_SDMMC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "chip/lpc43_sdmmc.h" + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: lpc43_sdmmc_initialize + * + * Description: + * Initialize the SD/MMC peripheral for normal operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on failures. + * + ****************************************************************************/ + +struct lpc43_sdmmc_dev_s; /* See include/nuttx/sdio.h */ +FAR struct sdio_dev_s *lpc43_sdmmc_initialize(int slotno); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_LPC43XX_LPC43_SDMMC_H */ diff --git a/arch/arm/src/lpc54xx/lpc54_sdmmc.c b/arch/arm/src/lpc54xx/lpc54_sdmmc.c index 6e929b5924b..ee7b142bc94 100644 --- a/arch/arm/src/lpc54xx/lpc54_sdmmc.c +++ b/arch/arm/src/lpc54xx/lpc54_sdmmc.c @@ -1062,22 +1062,9 @@ static int lpc54_sdmmc_interrupt(int irq, void *context, FAR void *arg) } } - /* Handle data end events. Note that RXDR may accompany DTO, DTO - * will be set on received while there is still data in the FIFO. - * So for the case of receiving, we don't actually even enable the - * DTO interrupt. - */ - - if ((pending & SDMMC_INT_DTO) != 0) - { - /* Finish the transfer */ - - lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE); - } - /* Handle data block send/receive CRC failure */ - else if ((pending & SDMMC_INT_DCRC) != 0) + if ((pending & SDMMC_INT_DCRC) != 0) { /* Terminate the transfer with an error */ @@ -1125,6 +1112,20 @@ static int lpc54_sdmmc_interrupt(int irq, void *context, FAR void *arg) mcerr("ERROR: Start bit, remaining: %d\n", priv->remaining); lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); } + + /* Handle data end events. Note that RXDR may accompany DTO, DTO + * will be set on received while there is still data in the FIFO. + * So for the case of receiving, we don't actually even enable the + * DTO interrupt. + */ + + else if ((pending & SDMMC_INT_DTO) != 0) + { + /* Finish the transfer */ + + lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + } /* Handle wait events *************************************************/ diff --git a/arch/arm/src/lpc54xx/lpc54_sdmmc.h b/arch/arm/src/lpc54xx/lpc54_sdmmc.h index c4c6bdc14be..7eb00403c55 100644 --- a/arch/arm/src/lpc54xx/lpc54_sdmmc.h +++ b/arch/arm/src/lpc54xx/lpc54_sdmmc.h @@ -79,5 +79,10 @@ extern "C" struct lpc54_sdmmc_dev_s; /* See include/nuttx/sdio.h */ FAR struct sdio_dev_s *lpc54_sdmmc_initialize(int slotno); +#undef EXTERN +#if defined(__cplusplus) +} +#endif + #endif /* __ASSEMBLY__ */ #endif /* __ARCH_ARM_SRC_LPC54XX_LPC54_SDMMC_H */