MMCSD SDIO: Add support for a new SDWAIT_WRCOMPLETE condition. The previous logic used a busy-wait loop to pool the card R1 start to determine when the card was ready for the next transfer. That busy-wait can be quite long -- hundreds of milliseconds. And alternative is to look the the SD D0 pin which will change state when the card is no longer busy.
This logic implements a change the avoids the busy-wait poll by reconfiguring the SD D0 pin as a GPIO interrupt, then waiting for the card to becom ready without taking up CPU cycles. This change is conditioned on CONFIG_MMCSD_SDIOWATI_WRCOMPLETE and is currenlty only implemented for the STM32 SDIO driver. From David Sidrane
This commit is contained in:
parent
1f10c56dd0
commit
1842525cc2
108
arch/arm/src/stm32/stm32_sdio.c
Normal file → Executable file
108
arch/arm/src/stm32/stm32_sdio.c
Normal file → Executable file
@ -400,6 +400,9 @@ static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupeven
|
||||
/* Interrupt Handling *******************************************************/
|
||||
|
||||
static int stm32_interrupt(int irq, void *context);
|
||||
#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE
|
||||
static int stm32_rdyinterrupt(int irq, void *context);
|
||||
#endif
|
||||
|
||||
/* SDIO interface methods ***************************************************/
|
||||
|
||||
@ -619,12 +622,39 @@ static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask,
|
||||
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 = irqsave();
|
||||
|
||||
#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE
|
||||
if ((waitmask & SDIOWAIT_WRCOMPLETE) != 0)
|
||||
{
|
||||
/* Do not use this in STM32_SDIO_MASK register */
|
||||
|
||||
waitmask &= !SDIOWAIT_WRCOMPLETE;
|
||||
|
||||
pinset = GPIO_SDIO_D0 & (GPIO_PORT_MASK|GPIO_PIN_MASK);
|
||||
pinset |= (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI);
|
||||
|
||||
/* Arm the SDIO_D Ready and install Isr */
|
||||
|
||||
stm32_gpiosetevent(pinset, true, false, false, stm32_rdyinterrupt);
|
||||
}
|
||||
|
||||
/* Disarm SDIO_D ready */
|
||||
|
||||
if ((wkupevent & SDIOWAIT_WRCOMPLETE) != 0)
|
||||
{
|
||||
stm32_gpiosetevent(GPIO_SDIO_D0, false, false, false , NULL);
|
||||
stm32_configgpio(GPIO_SDIO_D0);
|
||||
}
|
||||
#endif
|
||||
|
||||
priv->waitevents = waitevents;
|
||||
priv->wkupevent = wkupevent;
|
||||
priv->waitmask = waitmask;
|
||||
@ -1235,9 +1265,32 @@ static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupeven
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Interrrupt Handling
|
||||
* Interrupt Handling
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_rdyinterrupt
|
||||
*
|
||||
* Description:
|
||||
* SDIO ready interrupt handler
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - An instance of the SDIO device interface
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE
|
||||
static int stm32_rdyinterrupt(int irq, void *context)
|
||||
{
|
||||
struct stm32_dev_s *priv = &g_sdiodev;
|
||||
stm32_endwait(priv, SDIOWAIT_WRCOMPLETE);
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_interrupt
|
||||
*
|
||||
@ -2236,25 +2289,35 @@ static void stm32_waitenable(FAR struct sdio_dev_s *dev,
|
||||
* interrupts.
|
||||
*/
|
||||
|
||||
waitmask = 0;
|
||||
if ((eventset & SDIOWAIT_CMDDONE) != 0)
|
||||
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
|
||||
if ((eventset & SDIOWAIT_WRCOMPLETE) != 0)
|
||||
{
|
||||
waitmask |= SDIO_CMDDONE_MASK;
|
||||
waitmask = SDIOWAIT_WRCOMPLETE;
|
||||
}
|
||||
|
||||
if ((eventset & SDIOWAIT_RESPONSEDONE) != 0)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
waitmask |= SDIO_RESPDONE_MASK;
|
||||
}
|
||||
waitmask = 0;
|
||||
if ((eventset & SDIOWAIT_CMDDONE) != 0)
|
||||
{
|
||||
waitmask |= SDIO_CMDDONE_MASK;
|
||||
}
|
||||
|
||||
if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
|
||||
{
|
||||
waitmask |= SDIO_XFRDONE_MASK;
|
||||
}
|
||||
if ((eventset & SDIOWAIT_RESPONSEDONE) != 0)
|
||||
{
|
||||
waitmask |= SDIO_RESPDONE_MASK;
|
||||
}
|
||||
|
||||
/* Enable event-related interrupts */
|
||||
if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
|
||||
{
|
||||
waitmask |= SDIO_XFRDONE_MASK;
|
||||
}
|
||||
|
||||
/* Enable event-related interrupts */
|
||||
|
||||
putreg32(SDIO_WAITALL_ICR, STM32_SDIO_ICR);
|
||||
}
|
||||
|
||||
putreg32(SDIO_WAITALL_ICR, STM32_SDIO_ICR);
|
||||
stm32_configwaitints(priv, waitmask, eventset, 0);
|
||||
}
|
||||
|
||||
@ -2324,6 +2387,21 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
|
||||
}
|
||||
}
|
||||
|
||||
#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(GPIO_SDIO_D0) &&
|
||||
(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
|
||||
|
17
drivers/mmcsd/Kconfig
Normal file → Executable file
17
drivers/mmcsd/Kconfig
Normal file → Executable file
@ -38,6 +38,23 @@ config MMCSD_HAVECARDDETECT
|
||||
SDIO driver card detection is
|
||||
100% accurate
|
||||
|
||||
config MMCSD_SDIOWAIT_WRCOMPLETE
|
||||
bool "Use SDIO_D Busy to detect Write Complete"
|
||||
default n
|
||||
---help---
|
||||
SDIO driver will use SDIO_D Busy signalling to detect Write Complete.
|
||||
This option when selected, will enable the MMCSD driver to use the
|
||||
underlying (stm32_sdio only) drivers implementation of the SD specs
|
||||
SDIO_D Busy signalling to detect Write Complete. This will avoid
|
||||
potentially very long (600Ms+) busy waiting in the MMCSD driver.
|
||||
|
||||
To implement SDIO_D Busy signalling, the underlying driver must
|
||||
be capable of switching the GPIO_SDIO_D0 to be a rising edge sensitive
|
||||
interrupt pin. It must then, condition that pin to detect the rising edge
|
||||
on receipt of SDWAIT_WRCOMPLETE in the SDIO_WAITENABLE call and
|
||||
return it back to regular SDIO mode, when either the ISR fires or pin is
|
||||
found to be high in the SDIO_EVENTWAIT call.
|
||||
|
||||
config MMCSD_SPI
|
||||
bool "MMC/SD SPI transfer support"
|
||||
default y
|
||||
|
15
drivers/mmcsd/mmcsd_sdio.c
Normal file → Executable file
15
drivers/mmcsd/mmcsd_sdio.c
Normal file → Executable file
@ -1150,6 +1150,15 @@ static int mmcsd_transferready(FAR struct mmcsd_state_s *priv)
|
||||
* the TRANSFER state when the card completes the WRITE operation.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
|
||||
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT|SDIOWAIT_ERROR,
|
||||
MMCSD_BLOCK_WDATADELAY);
|
||||
if (ret != OK)
|
||||
{
|
||||
fdbg("ERROR: mmcsd_eventwait for transfer ready failed: %d\n", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
starttime = clock_systimer();
|
||||
do
|
||||
{
|
||||
@ -1721,6 +1730,12 @@ static ssize_t mmcsd_writesingle(FAR struct mmcsd_state_s *priv,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
|
||||
/* Arm the write complete detection with timeout */
|
||||
|
||||
SDIO_WAITENABLE(priv->dev, SDIOWAIT_WRCOMPLETE|SDIOWAIT_TIMEOUT);
|
||||
#endif
|
||||
|
||||
/* On success, return the number of blocks written */
|
||||
|
||||
return 1;
|
||||
|
22
include/nuttx/sdio.h
Normal file → Executable file
22
include/nuttx/sdio.h
Normal file → Executable file
@ -63,7 +63,27 @@
|
||||
#define SDIOWAIT_TIMEOUT (1 << 3) /* Bit 3: Timeout */
|
||||
#define SDIOWAIT_ERROR (1 << 4) /* Bit 4: Some other error occurred */
|
||||
|
||||
#define SDIOWAIT_ALLEVENTS 0x1f
|
||||
/* SDIOWAIT_WRCOMPLETE (optional) : SDIO driver will use SDIO_D Busy
|
||||
* signalling to detect Write Complete. This option when selected, will
|
||||
* enable the MMCSD driver to use the underlying (stm32_sdio only) drivers
|
||||
* implementation of the SD specs SDIO_D Busy signalling to detect Write
|
||||
* Complete. This will avoid potentially very long (600Ms+) busy waiting
|
||||
* in the MMCSD driver.
|
||||
*
|
||||
* To implement SDIO_D Busy signalling, the underlying driver must be
|
||||
* capable of switching the GPIO_SDIO_D0 to be a rising edge sensitive
|
||||
* interrupt pin. It must then, condition that pin to detect the rising
|
||||
* edge on receipt of SDWAIT_WRCOMPLETE in the SDIO_WAITENABLE call and
|
||||
* return it back to regular SDIO mode, when either the ISR fires or pin
|
||||
* is found to be high in the SDIO_EVENTWAIT call.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
|
||||
# define SDIOWAIT_WRCOMPLETE (1 << 5) /* Bit 5: Hardware Write Completion */
|
||||
# define SDIOWAIT_ALLEVENTS 0x3f
|
||||
#else
|
||||
# define SDIOWAIT_ALLEVENTS 0x1f
|
||||
#endif
|
||||
|
||||
/* Media events are used for enable/disable registered event callbacks */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user