samv7: add support for SD card detection from CD/DAT3 line

Some SD card connectors do not have separate card detection pin. In that
case card detection has to be done on CD/DAT3 data line. This means
software (i.e. architecture level driver) has to take care of pin
configuration switching (pin has to be set as data pin in case of
transfer and as interrupt card detection pin when there is no action
on data line).

This commit adds CD/DAT3 line card detection support for SAMv7 MCU.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
This commit is contained in:
Michal Lenc 2023-09-07 14:27:59 +02:00 committed by Xiang Xiao
parent ee598b3e21
commit c2bc3dfb12
3 changed files with 189 additions and 5 deletions

View File

@ -2490,6 +2490,21 @@ endmenu # DAC device driver configuration
menu "HSMCI device driver options"
depends on SAMV7_HSMCI
config SAMV7_HSMCI_SW_CARDDETECT
bool "MMC/SD card detect is SW on CD/DAT3"
depends on MMCSD_HAVE_CARDDETECT
default n
---help---
Some SD card connectors do not have separate card detection pin. In that
case card detection has to be done on CD/DAT3 data line. This means software
(i.e. architecture level driver) has to take care of pin configuration switching
(pin has to be set as data pin in case of transfer and as interrupt card detection
pin when there is no action on data line). SAMv7 HSMCI driver offers this method
of SD card detection. Following macros have to be defined in include/board.h:
GPIO_HSMCI0_CD - detection pin (same as CD/DAT3 but with IRQ)
IRQ_HSMCI0_CD - IRQ number for the pin
config SAMV7_HSMCI_DMA
bool "Support DMA data transfers"
default y

View File

@ -368,6 +368,11 @@ struct sam_dev_s
int ntimes; /* Number of times */
#endif
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
uint32_t cdgpio;
uint32_t cdirq;
#endif
/* Register logging support */
#if defined(CONFIG_SAMV7_HSMCI_CMDDEBUG) && defined(CONFIG_SAMV7_HSMCI_XFRDEBUG)
@ -389,6 +394,13 @@ struct sam_dev_s
/* Low-level helpers ********************************************************/
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
static int sam_carddetect_enable(struct sam_dev_s *priv);
static int sam_carddetect_disable(struct sam_dev_s *priv);
static int sam_carddetect_handler(int irq, void *context,
void *arg);
#endif
#ifdef CONFIG_SAMV7_HSMCI_REGDEBUG
static bool sam_checkreg(struct sam_dev_s *priv, bool wr,
uint32_t value, uint32_t address);
@ -574,6 +586,10 @@ static struct sam_dev_s g_hsmci0 =
.waitsem = SEM_INITIALIZER(0),
.base = SAM_HSMCI0_BASE,
.hsmci = 0,
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
.cdgpio = GPIO_HSMCI0_CD,
.cdirq = IRQ_HSMCI0_CD,
#endif
};
#endif
@ -581,6 +597,73 @@ static struct sam_dev_s g_hsmci0 =
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sam_carddetect_enable
*
* Description:
* Enables card detection (switch CD/DAT3 to interrupt mode and enables it)
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
*
****************************************************************************/
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
static int sam_carddetect_enable(struct sam_dev_s *priv)
{
sam_configgpio(priv->cdgpio);
sam_gpioirq(priv->cdgpio);
irq_attach(priv->cdirq, sam_carddetect_handler, (void *)priv);
sam_gpioirqenable(priv->cdirq);
return OK;
}
/****************************************************************************
* Name: sam_carddetect_disable
*
* Description:
* Disables card detection (switch CD/DAT3 to data mode)
*
* Input Parameters:
* priv - A reference to the HSMCI device state structure
*
****************************************************************************/
static int sam_carddetect_disable(struct sam_dev_s *priv)
{
sam_gpioirqdisable(priv->cdirq);
irq_detach(priv->cdirq);
sam_configgpio(GPIO_MCI0_DA3);
return OK;
}
/****************************************************************************
* Name: sam_carddetect_handler
*
* Description:
* Standard interrupt handler for CD/DAT3 pin. Calls sdio_mediachange
* with current value of CD pin.
*
****************************************************************************/
static int sam_carddetect_handler(int irq, void *context,
void *arg)
{
struct sam_dev_s *priv = (struct sam_dev_s *)arg;
bool inserted;
inserted = sam_gpioread(priv->cdgpio);
mcinfo("Inserted: %s\n", inserted ? "YES" : "NO");
sdio_mediachange(&priv->dev, inserted);
return OK;
}
#endif
/****************************************************************************
* Name: sam_checkreg
*
@ -1390,6 +1473,14 @@ static void sam_notransfer(struct sam_dev_s *priv)
priv->xfrbusy = false;
priv->txbusy = false;
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Transfer ended so switch CD/DAT3 do CD mode to detect card inserion
* or remove.
*/
sam_carddetect_enable(priv);
#endif
}
/****************************************************************************
@ -1944,6 +2035,12 @@ static int sam_sendcmd(struct sdio_dev_s *dev,
uint32_t regval;
uint32_t cmdidx;
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
sam_cmdsampleinit(priv);
/* Set the HSMCI Argument value */
@ -2122,6 +2219,12 @@ static int sam_recvsetup(struct sdio_dev_s *dev, uint8_t *buffer,
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
#ifndef CONFIG_SAMV7_HSMCI_UNALIGNED
/* Default behavior is to transfer 32-bit values only */
@ -2192,6 +2295,12 @@ static int sam_sendsetup(struct sdio_dev_s *dev,
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
#ifndef CONFIG_SAMV7_HSMCI_UNALIGNED
/* Default behavior is to transfer 32-bit values only */
@ -2381,6 +2490,12 @@ static int sam_waitresponse(struct sdio_dev_s *dev, uint32_t cmd)
clock_t watchtime;
int32_t timeout;
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
switch (cmd & MMCSD_RESPONSE_MASK)
{
case MMCSD_R1_RESPONSE:
@ -2496,6 +2611,12 @@ static int sam_recvshort(struct sdio_dev_s *dev,
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
int ret = OK;
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
/* These responses could have CRC errors:
*
* R1 Command response (48-bit)
@ -2583,6 +2704,12 @@ static int sam_recvlong(struct sdio_dev_s *dev, uint32_t cmd,
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
int ret = OK;
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
/* R2 CID, CSD register (136-bit)
* 135 0 Start bit
* 134 0 Transmission bit (0=from card)
@ -2861,6 +2988,15 @@ static void sam_callbackenable(struct sdio_dev_s *dev,
mcinfo("eventset: %02x\n", eventset);
DEBUGASSERT(priv != NULL);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Calling callbackenable means that we expect the card to be inserted
* or removed, therefore we have to switch CD/DAT3 pin to detection
* mode.
*/
sam_carddetect_enable(priv);
#endif
priv->cbevents = eventset;
sam_callback(priv);
}
@ -2937,6 +3073,12 @@ static int sam_dmarecvsetup(struct sdio_dev_s *dev, uint8_t *buffer,
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
/* 32-bit buffer alignment is required for DMA transfers */
if (((uintptr_t)buffer & 3) != 0 || (buflen & 3) != 0)
@ -3052,6 +3194,12 @@ static int sam_dmasendsetup(struct sdio_dev_s *dev,
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Switch CD/DAT3 pin do data mode */
sam_carddetect_disable(priv);
#endif
/* 32-bit buffer alignment is required for DMA transfers */
if (((uintptr_t)buffer & 3) != 0 || (buflen & 3) != 0)
@ -3278,7 +3426,9 @@ struct sdio_dev_s *sdio_initialize(int slotno)
sam_configgpio(GPIO_MCI0_DA0); /* Data 0 of Slot A */
sam_configgpio(GPIO_MCI0_DA1); /* Data 1 of Slot A */
sam_configgpio(GPIO_MCI0_DA2); /* Data 2 of Slot A */
#ifndef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
sam_configgpio(GPIO_MCI0_DA3); /* Data 3 of Slot A */
#endif
sam_configgpio(GPIO_MCI0_CK); /* Common SD clock */
sam_configgpio(GPIO_MCI0_CDA); /* Command/Response of Slot A */
@ -3302,6 +3452,19 @@ struct sdio_dev_s *sdio_initialize(int slotno)
mcinfo("priv: %p base: %08" PRIx32 " hsmci: %d pid: %" PRId32 "\n",
priv, priv->base, priv->hsmci, pid);
#ifdef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* We do not have separare card detect pin on SD card slot. This means
* card detection has to be done on CD/DAT3 pin. Generally we have to
* switch between configuration of the pin (once as a simple interrupt
* GPIO and then as data pin for SDIO driver).
*
* Lets start with CD pin configuration.
*/
sam_carddetect_enable(priv);
sdio_mediachange(&priv->dev, sam_gpioread(priv->cdgpio));
#endif
/* Allocate a DMA channel */
priv->dma = sam_dmachannel(0, DMA_FLAGS(pid));

View File

@ -96,14 +96,15 @@ static bool sam_cardinserted_internal(struct sam_hsmci_state_s *state)
}
/****************************************************************************
* Name: sam_hsmci_cardetect and sam_hsmci_cardetect_handler
* Name: sam_hsmci_carddetect and sam_hsmci_carddetect_handler
*
* Description:
* Card detect interrupt handlers
*
****************************************************************************/
static int sam_hsmci_cardetect(struct sam_hsmci_state_s *state)
#ifndef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
static int sam_hsmci_carddetect(struct sam_hsmci_state_s *state)
{
/* Get the current card insertion state */
@ -125,7 +126,7 @@ static int sam_hsmci_cardetect(struct sam_hsmci_state_s *state)
return OK;
}
static int sam_hsmci_cardetect_handler(int irq, void *context,
static int sam_hsmci_carddetect_handler(int irq, void *context,
void *arg)
{
struct sam_hsmci_state_s *state = (struct sam_hsmci_state_s *)arg;
@ -136,7 +137,7 @@ static int sam_hsmci_cardetect_handler(int irq, void *context,
* device.
*/
ret = sam_hsmci_cardetect(state);
ret = sam_hsmci_carddetect(state);
#ifdef CONFIG_FS_AUTOMOUNTER
/* Let the automounter know about the insertion event */
@ -146,6 +147,7 @@ static int sam_hsmci_cardetect_handler(int irq, void *context,
return ret;
}
#endif
/****************************************************************************
* Name: sam_hsmci_state
@ -200,10 +202,12 @@ int sam_hsmci_initialize(int slotno, int minor, gpio_pinset_t cdcfg,
state->cdirq = cdirq;
state->cdinvert = cdinvert;
#ifndef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Initialize card-detect, write-protect, and power enable PIOs */
sam_configgpio(state->cdcfg);
sam_dumpgpio(state->cdcfg, "HSMCI Card Detect");
#endif
/* Mount the SDIO-based MMC/SD block driver */
@ -225,10 +229,11 @@ int sam_hsmci_initialize(int slotno, int minor, gpio_pinset_t cdcfg,
return ret;
}
#ifndef CONFIG_SAMV7_HSMCI_SW_CARDDETECT
/* Configure card detect interrupts */
sam_gpioirq(state->cdcfg);
irq_attach(state->cdirq, sam_hsmci_cardetect_handler, (void *)state);
irq_attach(state->cdirq, sam_hsmci_carddetect_handler, (void *)state);
/* Then inform the HSMCI driver if there is or is not a card in the slot. */
@ -238,6 +243,7 @@ int sam_hsmci_initialize(int slotno, int minor, gpio_pinset_t cdcfg,
/* Enable card detect interrupts */
sam_gpioirqenable(state->cdirq);
#endif
return OK;
}