Skip to content

Commit

Permalink
arch/arm64/imx9: Improve imx9 usdhc reliability
Browse files Browse the repository at this point in the history
Force clock on during wait and convert some status
variables to "volatile"

Signed-off-by: Jouni Ukkonen <[email protected]>
  • Loading branch information
joukkone committed Dec 4, 2024
1 parent 7aed9c3 commit 3b2d1e7
Showing 1 changed file with 42 additions and 33 deletions.
75 changes: 42 additions & 33 deletions arch/arm64/src/imx9/imx9_usdhc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#endif

#define DCACHE_LINEMASK (ARMV8A_DCACHE_LINESIZE - 1)
#define DCACHE_ALIGN_UP(a) (((a) + DCACHE_LINEMASK) & ~DCACHE_LINEMASK)

#if !defined(CONFIG_ARM64_DCACHE_DISABLE)
# define cache_aligned_alloc(s) kmm_memalign(ARMV8A_DCACHE_LINESIZE,(s))
Expand Down Expand Up @@ -126,6 +127,10 @@

#define SDMMC_MAX_BLOCK_SIZE (512)

/* Vendor register default value */

#define USDHC_VENDOR_REG_DEFAULT 0x20007809

/* Data transfer / Event waiting interrupt mask bits */

#define USDHC_RESPERR_INTS (USDHC_INT_CCE | USDHC_INT_CTOE | \
Expand Down Expand Up @@ -180,11 +185,11 @@ struct imx9_dev_s

/* Event support */

sem_t waitsem; /* Implements event waiting */
sdio_eventset_t waitevents; /* Set of events to be waited for */
uint32_t waitints; /* Interrupt enables for event waiting */
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
struct wdog_s waitwdog; /* Watchdog that handles event timeouts */
sem_t waitsem; /* Implements event waiting */
volatile sdio_eventset_t waitevents; /* Set of events to be waited for */
volatile uint32_t waitints; /* Interrupt enables for event waiting */
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
struct wdog_s waitwdog; /* Watchdog that handles event timeouts */

/* Callback support */

Expand All @@ -197,7 +202,7 @@ struct imx9_dev_s
/* Interrupt mode data transfer support */

uint32_t *buffer; /* Address of current R/W buffer */
size_t remaining; /* Number of bytes remaining in the
volatile size_t remaining; /* Number of bytes remaining in the
* transfer */
uint32_t xfrints; /* Interrupt enables for data transfer */

Expand Down Expand Up @@ -786,16 +791,13 @@ static void imx9_dataconfig(struct imx9_dev_s *priv, bool bwrite,
unsigned int datalen, unsigned int timeout)
{
unsigned int watermark;
uint32_t regval = 0;

/* Set the data timeout value in the USDHC_SYSCTL field to the selected
* value.
*/

regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
regval &= ~USDHC_SYSCTL_DTOCV_MASK;
regval |= timeout << USDHC_SYSCTL_DTOCV_SHIFT;
putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
modifyreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET,
USDHC_SYSCTL_DTOCV_MASK, timeout << USDHC_SYSCTL_DTOCV_SHIFT);

#if defined(CONFIG_IMX9_USDHC_DMA) && !defined(CONFIG_ARM64_DCACHE_DISABLE)
/* If cache is enabled, and this is an unaligned receive,
Expand Down Expand Up @@ -1165,6 +1167,11 @@ static void imx9_endwait(struct imx9_dev_s *priv,

wd_cancel(&priv->waitwdog);

/* disable force clock */

modifyreg32(priv->addr + IMX9_USDHC_VENDOR_OFFSET,
USDHC_VENDOR_FRC_SDCLK_ON, 0);

/* Disable event-related interrupts */

imx9_configwaitints(priv, 0, 0, wkupevent);
Expand Down Expand Up @@ -1380,9 +1387,8 @@ static int imx9_interrupt(int irq, void *context, void *arg)
{
/* Yes.. mask further interrupts and wake the thread up */

regval = getreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
regval &= ~USDHC_RESPDONE_INTS;
putreg32(regval, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
modifyreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET,
USDHC_RESPDONE_INTS, 0);

imx9_endwait(priv, SDIOWAIT_RESPONSEDONE);
}
Expand Down Expand Up @@ -1470,6 +1476,16 @@ static void imx9_reset(struct sdio_dev_s *dev)

mcinfo("Reset complete\n");

/* Set VENDOR to default value and reset registers
* that are not resetted by RSTA
*/

putreg32(0x20007809, priv->addr + IMX9_USDHC_VENDOR_OFFSET);
putreg32(0, priv->addr + IMX9_USDHC_MMCBOOT_OFFSET);
putreg32(0, priv->addr + IMX9_USDHC_MIX_OFFSET);
putreg32(0, priv->addr + IMX9_USDHC_CLK_TUNE_CTRL_OFFSET);
putreg32(0, priv->addr + IMX9_USDHC_DLL_CONTROL_OFFSET);

/* Make sure that all clocking is disabled */

imx9_clock(dev, CLOCK_SDIO_DISABLED);
Expand Down Expand Up @@ -1839,7 +1855,7 @@ static void imx9_clock(struct sdio_dev_s *dev, enum sdio_clock_e rate)
{
/* Initial ID mode clocking (<400KHz) */

mcinfo("IDMODE\n");
mcerr("IDMODE\n");

/* Put out an additional 80 clocks in case this is a power-up
* sequence.
Expand Down Expand Up @@ -2378,10 +2394,6 @@ static int imx9_cancel(struct sdio_dev_s *dev)
{
struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;

#ifdef CONFIG_IMX9_USDHC_DMA
uint32_t regval;
#endif

/* Disable all transfer- and event- related interrupts */

imx9_configxfrints(priv, 0); imx9_configwaitints(priv, 0, 0, 0);
Expand All @@ -2402,9 +2414,8 @@ static int imx9_cancel(struct sdio_dev_s *dev)

/* Stop the DMA by resetting the data path */

regval = getreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
regval |= USDHC_SYSCTL_RSTD;
putreg32(regval, priv->addr + IMX9_USDHC_SYSCTL_OFFSET);
modifyreg32(priv->addr + IMX9_USDHC_SYSCTL_OFFSET, 0, USDHC_SYSCTL_RSTD);

#endif

/* Mark no transfer in progress */
Expand Down Expand Up @@ -2792,6 +2803,12 @@ static void imx9_waitenable(struct sdio_dev_s *dev,
/* Start the watchdog timer */

delay = MSEC2TICK(timeout);

/* Force clock */

modifyreg32(priv->addr + IMX9_USDHC_VENDOR_OFFSET,
0, USDHC_VENDOR_FRC_SDCLK_ON);

ret = wd_start(&priv->waitwdog, delay,
imx9_eventtimeout, (wdparm_t)priv);

Expand Down Expand Up @@ -2834,9 +2851,6 @@ static sdio_eventset_t imx9_eventwait(struct sdio_dev_s *dev)
* non-zero.
*/

DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
(priv->waitevents == 0 && priv->wkupevent != 0));

/* Loop until the event (or the timeout occurs). Race conditions are
* avoided by calling imx9_waitenable prior to triggering the logic
* that will cause the wait to terminate. Under certain race
Expand Down Expand Up @@ -3075,7 +3089,7 @@ static int imx9_dmarecvsetup(struct sdio_dev_s *dev,
else
{
up_invalidate_dcache((uintptr_t)buffer,
(uintptr_t)buffer + buflen);
(uintptr_t)buffer + DCACHE_ALIGN_UP(buflen));

priv->unaligned_rx = false;
}
Expand Down Expand Up @@ -3285,8 +3299,6 @@ static void imx9_callback(void *arg)
void imx9_usdhc_set_sdio_card_isr(struct sdio_dev_s *dev,
int (*func)(void *), void *arg)
{
irqstate_t flags;
uint32_t regval;
struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;

priv->do_sdio_card = func;
Expand All @@ -3308,11 +3320,8 @@ void imx9_usdhc_set_sdio_card_isr(struct sdio_dev_s *dev,
}
#endif

flags = enter_critical_section();
regval = getreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
regval = (regval & ~USDHC_INT_CINT) | priv->cintints;
putreg32(regval, priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET);
leave_critical_section(flags);
modifyreg32(priv->addr + IMX9_USDHC_IRQSIGEN_OFFSET,
USDHC_INT_CINT, priv->cintints);
}

/****************************************************************************
Expand Down

0 comments on commit 3b2d1e7

Please sign in to comment.