You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/03/24 05:01:06 UTC

[GitHub] [incubator-nuttx] rtucker85 opened a new pull request #5835: LITESDCARD peripheral driver support

rtucker85 opened a new pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835


   ## Summary
   This PR adds:
   
   1. A driver for the LiteX LITESDCARD peripheral
   2. Changes to the ARTY_A7 development board to demonstrate SDCARD support
   
   ## Impact
   Isolated to ARTY_A7 development board users with LITEX_SDIO configuration turned on
   
   ## Testing
   Built and tested on the [Digilent ARTY_A7](https://digilent.com/shop/arty-a7-artix-7-fpga-development-board/) development board with the [Digilent PmodSD](https://digilent.com/shop/pmod-sd-full-sized-sd-card-slot/) adaptor plugged into PMOD JD.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835995373



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)

Review comment:
       Yes there is. Added.

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r833996279



##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * arch/risc-v/src/common/riscv_cache.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/cache.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+void up_invalidate_dcache_all(void)
+{
+  asm volatile(".word(0x500F)\n");
+}
+#endif
+
+/****************************************************************************
+ * Name: up_invalidate_icache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of I cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_ICACHE
+void up_invalidate_icache_all(void)
+{
+    asm volatile(
+      ".word(0x100F)\n"

Review comment:
       ditto

##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * arch/risc-v/src/common/riscv_cache.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/cache.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+void up_invalidate_dcache_all(void)
+{
+  asm volatile(".word(0x500F)\n");

Review comment:
       why not use the instruction directly

##########
File path: include/nuttx/sdio.h
##########
@@ -477,6 +477,7 @@
 #define SDIO_CAPS_DMABEFOREWRITE  0x04 /* Bit 2=1: Executes DMA before write command */
 #define SDIO_CAPS_4BIT            0x08 /* Bit 3=1: Supports 4 bit operation */
 #define SDIO_CAPS_8BIT            0x10 /* Bit 4=1: Supports 8 bit operation */
+#define SDIO_CAPS_4BIT_ONLY       0x20 /* Bit 5=1: Supports 4-bit only operation */

Review comment:
       why not remove SDIO_CAPS_8BIT from cap?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835995318



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r834861334



##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * arch/risc-v/src/common/riscv_cache.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/cache.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+void up_invalidate_dcache_all(void)
+{
+  asm volatile(".word(0x500F)\n");

Review comment:
       This is a custom instruction for VexRiscV - It does not appear in the RiscV standard.
   Ill make a comment in the code to document this.

##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * arch/risc-v/src/common/riscv_cache.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/cache.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+void up_invalidate_dcache_all(void)
+{
+  asm volatile(".word(0x500F)\n");
+}
+#endif
+
+/****************************************************************************
+ * Name: up_invalidate_icache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of I cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_ICACHE
+void up_invalidate_icache_all(void)
+{
+    asm volatile(
+      ".word(0x100F)\n"

Review comment:
       ditto




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835121976



##########
File path: boards/risc-v/litex/arty_a7/src/litex_sdio.c
##########
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * boards/risc-v/litex/arty_a7/src/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "litex.h"
+#include "arty_a7.h"
+#include "litex_sdio.h"
+
+#ifdef HAVE_SDMMC
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct sdio_dev_s *g_sdio_dev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_sdio_card_isr_callback
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+static int litex_sdio_card_isr_callback(void *arg)
+{
+  bool cd;
+
+  cd = litex_cardinserted(SDIO_SLOTNO);
+  finfo("Card detect: %d\n", cd);
+  sdio_mediachange(g_sdio_dev, cd);
+
+#ifdef HAVE_AUTOMOUNTER
+  /* Let the automounter know about the insertion event */
+
+  litex_automount_event(SDIO_SLOTNO, cd);
+#endif
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_cardinserted
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+bool litex_cardinserted(int slotno)
+{
+  return litex_sdio_get_card_detect();
+}
+
+/****************************************************************************
+ * Name: litex_sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO-based MMC/SD card support
+ *
+ ****************************************************************************/
+
+int litex_sdio_initialize(void)
+{
+  int ret = 0;
+
+  finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO);
+  g_sdio_dev = sdio_initialize(SDIO_SLOTNO);
+  if (!g_sdio_dev)

Review comment:
       ```suggestion
     if (g_sdio_dev == NULL)
   ```

##########
File path: boards/risc-v/litex/arty_a7/src/arty_a7.h
##########
@@ -27,6 +27,113 @@
 
 #include <nuttx/config.h>
 
+/****************************************************************************
+ * configuration
+ */
+
+#define HAVE_SDMMC          1
+#define HAVE_AUTOMOUNTER    1
+
+/****************************************************************************
+ * SDIO Configuration
+ */
+
+#define SDIO_MINOR CONFIG_NSH_MMCSDMINOR
+#define SDIO_SLOTNO CONFIG_NSH_MMCSDSLOTNO
+
+/* Can't support MMC/SD if the card interface(s) are not enable */
+
+#if !defined(CONFIG_LITEX_SDIO) && !defined(CONFIG_LITEX_SDIO1)
+#  undef HAVE_SDMMC
+#endif
+
+#if !defined(CONFIG_FS_AUTOMOUNTER)
+#  undef HAVE_AUTOMOUNTER
+#endif
+
+/****************************************************************************
+ * PROC File System Configuration
+ */
+
+#ifdef CONFIG_FS_PROCFS
+#  ifdef CONFIG_NSH_PROC_MOUNTPOINT
+#    define LITEX_PROCFS_MOUNTPOINT CONFIG_NSH_PROC_MOUNTPOINT
+#  else
+#    define LITEX_PROCFS_MOUNTPOINT "/proc"
+#  endif
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_bringup
+ *
+ * Description:
+ *   Bring up board features
+ *
+ ****************************************************************************/
+
 int litex_bringup(void);
 
+/****************************************************************************
+ * Name: litex_sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO-based MMC/SD card support
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_SDMMC
+int litex_sdio_initialize(void);
+#endif
+
+/****************************************************************************
+ * Name:  litex_automount_initialize
+ *
+ * Description:
+ *   Configure auto-mounters for each enabled MikroBus MMCSD
+ *
+ * Input Parameters:
+ *   None
+ *
+ *  Returned Value:
+ *    None
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+int litex_automount_initialize(void);
+#endif
+
+/****************************************************************************
+ * Name: litex_automount_event
+ *
+ * Description:
+ *   The MMCSD card detection logic has detected an insertion or removal
+ *   event.  It has already scheduled the MMC/SD block driver operations.
+ *   Now we need to schedule the auto-mount event which will occur with a
+ *   substantial delay to make sure that everything has settle down.
+ *
+ * Input Parameters:
+ *   slotno - Identifies the MB slot: MB1_MMCSD_SLOTNO or MB2_MMCSD_SLOTNO.
+ *   inserted - True if the card is inserted in the slot.  False otherwise.
+ *
+ *  Returned Value:
+ *    None
+ *
+ *  Assumptions:
+ *    Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+void litex_automount_event(int slotno, bool inserted);
+#endif
+
+#ifdef HAVE_SDMMC
+bool litex_cardinserted(int slotno);

Review comment:
       do we need a comment description for this function?

##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);
+
+/****************************************************************************
+ * Name: litex_sdio_set_card_isr
+ *
+ * Description:
+ *   SDIO card generates interrupt via SDIO_DATA_1 pin.
+ *   Called by board-specific logic to register an ISR for SDIO card.
+ *
+ * Input Parameters:
+ *   func      - callback function.
+ *   arg       - arg to be passed to the function.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void litex_sdio_set_card_isr(FAR struct sdio_dev_s *dev,
+                             int (*func)(void *), void *arg);
+
+/****************************************************************************
+ * Name: sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO for operation.
+ *
+ * Input Parameters:
+ *   slotno - Not used.
+ *
+ * Returned Value:
+ *   A reference to an SDIO interface structure.  NULL is returned on
+ *   failures.
+ *
+ ****************************************************************************/
+
+struct sdio_dev_s; /* See include/nuttx/sdio.h */
+FAR struct sdio_dev_s *sdio_initialize(int slotno);
+
+/****************************************************************************
+ * Name: sdio_mediachange
+ *
+ * Description:
+ *   Called by board-specific logic -- possibly from an interrupt handler --
+ *   in order to signal to the driver that a card has been inserted or
+ *   removed from the slot
+ *
+ * Input Parameters:
+ *   dev        - An instance of the SDIO driver device state structure.
+ *   cardinslot - true is a card has been detected in the slot; false if a
+ *                card has been removed from the slot.  Only transitions
+ *                (inserted->removed or removed->inserted should be reported)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H */

Review comment:
       ```suggestion
   #endif /* __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H */
   
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  sdio_eventset_t wkupevent = 0;
+  int ret;
+
+  DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
+              (priv->waitevents == 0 && priv->wkupevent != 0));
+  for (; ; )
+  {
+    ret = nxsem_wait_uninterruptible(&priv->waitsem);
+    if (ret < 0)
+    {
+      litex_configwaitints(priv, 0, 0, 0);
+      wd_cancel(&priv->waitwdog);
+      return SDIOWAIT_ERROR;
+    }
+
+    wkupevent = priv->wkupevent;
+    if (wkupevent != 0)
+    {
+      break;
+    }
+  }
+
+  litex_configwaitints(priv, 0, 0, 0);
+  return wkupevent;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO_CALLBACKENABLE
+ *
+ * Input Parameters:
+ *   dev -      Device-specific state data
+ *   callback - The function 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 litex_registercallback(FAR struct sdio_dev_s *dev,
+                                  worker_t callback, void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Disable callbacks and register this callback and is argument */
+
+  mcinfo("Register %p(%p)\n", callback, arg);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = 0;
+  priv->cbarg    = arg;
+  priv->callback = callback;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_callbackenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO callback events.  This is part of the
+ *   the SDIO callback sequence.  The set of events is configured to enabled
+ *   callbacks to the function provided in litex_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 method.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                                 sdio_eventset_t eventset)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("eventset: %02x\n", eventset);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = eventset;
+  litex_callback(priv);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_callback(void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  /* Is a callback registered? */
+
+  DEBUGASSERT(priv != NULL);
+  mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n",
+         priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus);
+
+  if (priv->callback)
+    {
+      /* Yes.. Check for enabled callback events */
+
+      if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0)
+        {
+          /* Media is present.  Is the media inserted event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+      else
+        {
+          /* Media is not present.  Is the media eject event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+
+      /* Perform the callback, disabling further callbacks.  Of course, the
+       * the callback can (and probably should) re-enable callbacks.
+       */
+
+      priv->cbevents = 0;
+
+      /* Callbacks cannot be performed in the context of an interrupt
+       * handler.  If we are in an interrupt handler, then queue the
+       * callback to be performed later on the work thread.
+       */
+
+      if (up_interrupt_context())
+        {
+          /* Yes.. queue it */
+
+           mcinfo("Queuing callback to %p(%p)\n",
+                  priv->callback, priv->cbarg);
+           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
+ ****************************************************************************/
+
+bool litex_sdio_get_card_detect(void)
+{
+  if (!getreg32(LITEX_SDPHY_CARD_DETECT))
+    {
+      return true;
+    }
+
+  return false;

Review comment:
       ```suggestion
     return getreg32(LITEX_SDPHY_CARD_DETECT) == 0;
   ```

##########
File path: boards/risc-v/litex/arty_a7/src/litex_sdio.c
##########
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * boards/risc-v/litex/arty_a7/src/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "litex.h"
+#include "arty_a7.h"
+#include "litex_sdio.h"
+
+#ifdef HAVE_SDMMC
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct sdio_dev_s *g_sdio_dev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_sdio_card_isr_callback
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+static int litex_sdio_card_isr_callback(void *arg)
+{
+  bool cd;
+
+  cd = litex_cardinserted(SDIO_SLOTNO);
+  finfo("Card detect: %d\n", cd);
+  sdio_mediachange(g_sdio_dev, cd);

Review comment:
       why not to get rid of `g_sdio_dev` and pass return of `sdio_initialize` as an `arg` to `litex_sdio_card_isr_callback`?

##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);

Review comment:
       missing function description




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836915028



##########
File path: arch/risc-v/src/litex/Make.defs
##########
@@ -30,7 +30,7 @@ CMN_CSRCS += riscv_initialize.c riscv_swint.c
 CMN_CSRCS += riscv_allocateheap.c riscv_createstack.c riscv_exit.c
 CMN_CSRCS += riscv_assert.c riscv_blocktask.c riscv_copystate.c riscv_initialstate.c
 CMN_CSRCS += riscv_interruptcontext.c riscv_modifyreg32.c riscv_puts.c riscv_mdelay.c
-CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c
+CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c riscv_copyfullstate.c

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r834786849



##########
File path: include/nuttx/sdio.h
##########
@@ -477,6 +477,7 @@
 #define SDIO_CAPS_DMABEFOREWRITE  0x04 /* Bit 2=1: Executes DMA before write command */
 #define SDIO_CAPS_4BIT            0x08 /* Bit 3=1: Supports 4 bit operation */
 #define SDIO_CAPS_8BIT            0x10 /* Bit 4=1: Supports 8 bit operation */
+#define SDIO_CAPS_4BIT_ONLY       0x20 /* Bit 5=1: Supports 4-bit only operation */

Review comment:
       @AndrewD  Is correct. This peripheral _only_ supports 4 bit mode.
   I.e. It doesnt support 1bit _or_ 8bit modes.
   
   We therefore must have a mechanism to force the mmcsd driver to put the card into 4 bit mode without checking the capabilities of the card obtained from the SCR register.
   
   Reading the SCR register becomes redundant but has been left in place for diagnostic purposes - If DEBUG_FS_INFO is enabled.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836253829



##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);

Review comment:
       not adeded

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1487 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#define LITEX_EV_CMDDONE                (1 << 0)
+#define LITEX_EV_WRERROR                (1 << 1)
+#define LITEX_EV_TIMEOUT                (1 << 2)
+#define LITEX_EV_CRCERROR               (1 << 3)
+
+#define MAX_DIVIDER                     256
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */

Review comment:
       why do we need return value here? I do not see that it is used anywhere

##########
File path: arch/risc-v/src/litex/litex_cache.S
##########
@@ -0,0 +1,82 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_cache.S
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+	.file	"litex_cache.S"
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Cache Operations
+ ****************************************************************************/
+
+	.text
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+	.globl  up_invalidate_dcache_all
+	.type	  up_invalidate_dcache_all, function
+
+up_invalidate_dcache_all:
+  .word 0x500F
+#endif
+
+/****************************************************************************
+ * Name: up_invalidate_icache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of I cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_ICACHE
+	.globl  up_invalidate_icache_all
+	.type	  up_invalidate_icache_all, function
+
+up_invalidate_dcaup_invalidate_icache_allhe_all:
+  .word 0x100F
+  nop
+  nop
+  nop
+  nop
+  nop

Review comment:
       Pleas use either TABs or spaces for indentation, but not mix

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  sdio_eventset_t wkupevent = 0;
+  int ret;
+
+  DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
+              (priv->waitevents == 0 && priv->wkupevent != 0));
+  for (; ; )
+  {
+    ret = nxsem_wait_uninterruptible(&priv->waitsem);
+    if (ret < 0)
+    {
+      litex_configwaitints(priv, 0, 0, 0);
+      wd_cancel(&priv->waitwdog);
+      return SDIOWAIT_ERROR;
+    }
+
+    wkupevent = priv->wkupevent;
+    if (wkupevent != 0)
+    {
+      break;
+    }
+  }
+
+  litex_configwaitints(priv, 0, 0, 0);
+  return wkupevent;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO_CALLBACKENABLE
+ *
+ * Input Parameters:
+ *   dev -      Device-specific state data
+ *   callback - The function 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 litex_registercallback(FAR struct sdio_dev_s *dev,
+                                  worker_t callback, void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Disable callbacks and register this callback and is argument */
+
+  mcinfo("Register %p(%p)\n", callback, arg);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = 0;
+  priv->cbarg    = arg;
+  priv->callback = callback;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_callbackenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO callback events.  This is part of the
+ *   the SDIO callback sequence.  The set of events is configured to enabled
+ *   callbacks to the function provided in litex_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 method.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                                 sdio_eventset_t eventset)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("eventset: %02x\n", eventset);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = eventset;
+  litex_callback(priv);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_callback(void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  /* Is a callback registered? */
+
+  DEBUGASSERT(priv != NULL);
+  mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n",
+         priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus);
+
+  if (priv->callback)
+    {
+      /* Yes.. Check for enabled callback events */
+
+      if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0)
+        {
+          /* Media is present.  Is the media inserted event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+      else
+        {
+          /* Media is not present.  Is the media eject event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+
+      /* Perform the callback, disabling further callbacks.  Of course, the
+       * the callback can (and probably should) re-enable callbacks.
+       */
+
+      priv->cbevents = 0;
+
+      /* Callbacks cannot be performed in the context of an interrupt
+       * handler.  If we are in an interrupt handler, then queue the
+       * callback to be performed later on the work thread.
+       */
+
+      if (up_interrupt_context())
+        {
+          /* Yes.. queue it */
+
+           mcinfo("Queuing callback to %p(%p)\n",
+                  priv->callback, priv->cbarg);
+           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
+ ****************************************************************************/
+
+bool litex_sdio_get_card_detect(void)
+{
+  if (!getreg32(LITEX_SDPHY_CARD_DETECT))
+    {
+      return true;
+    }
+
+  return false;

Review comment:
       not done, but still is optional




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836910012



##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);

Review comment:
       My mistake. This got missed. Actually added now.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836909106



##########
File path: arch/risc-v/src/litex/litex_cache.S
##########
@@ -0,0 +1,82 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_cache.S
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+	.file	"litex_cache.S"
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Cache Operations
+ ****************************************************************************/
+
+	.text
+
+/****************************************************************************
+ * Name: up_invalidate_dcache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of D cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_DCACHE
+	.globl  up_invalidate_dcache_all
+	.type	  up_invalidate_dcache_all, function
+
+up_invalidate_dcache_all:
+  .word 0x500F
+#endif
+
+/****************************************************************************
+ * Name: up_invalidate_icache_all
+ *
+ * Description:
+ *   Invalidate the entire contents of I cache.
+ *
+ * Input Parameters:
+ *   None
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ARCH_ICACHE
+	.globl  up_invalidate_icache_all
+	.type	  up_invalidate_icache_all, function
+
+up_invalidate_dcaup_invalidate_icache_allhe_all:
+  .word 0x100F
+  nop
+  nop
+  nop
+  nop
+  nop

Review comment:
       Sorry about that. That crept in by mistake. All fixed now.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835995051



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;

Review comment:
       Yep! Good idea. Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835995449



##########
File path: boards/risc-v/litex/arty_a7/src/litex_sdio.c
##########
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * boards/risc-v/litex/arty_a7/src/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "litex.h"
+#include "arty_a7.h"
+#include "litex_sdio.h"
+
+#ifdef HAVE_SDMMC
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct sdio_dev_s *g_sdio_dev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_sdio_card_isr_callback
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+static int litex_sdio_card_isr_callback(void *arg)
+{
+  bool cd;
+
+  cd = litex_cardinserted(SDIO_SLOTNO);
+  finfo("Card detect: %d\n", cd);
+  sdio_mediachange(g_sdio_dev, cd);
+
+#ifdef HAVE_AUTOMOUNTER
+  /* Let the automounter know about the insertion event */
+
+  litex_automount_event(SDIO_SLOTNO, cd);
+#endif
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_cardinserted
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+bool litex_cardinserted(int slotno)
+{
+  return litex_sdio_get_card_detect();
+}
+
+/****************************************************************************
+ * Name: litex_sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO-based MMC/SD card support
+ *
+ ****************************************************************************/
+
+int litex_sdio_initialize(void)
+{
+  int ret = 0;
+
+  finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO);
+  g_sdio_dev = sdio_initialize(SDIO_SLOTNO);
+  if (!g_sdio_dev)

Review comment:
       Done

##########
File path: boards/risc-v/litex/arty_a7/src/litex_sdio.c
##########
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * boards/risc-v/litex/arty_a7/src/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "litex.h"
+#include "arty_a7.h"
+#include "litex_sdio.h"
+
+#ifdef HAVE_SDMMC
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct sdio_dev_s *g_sdio_dev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_sdio_card_isr_callback
+ *
+ * Description:
+ *   Check if a card is inserted into the selected MMCSD slot
+ *
+ ****************************************************************************/
+
+static int litex_sdio_card_isr_callback(void *arg)
+{
+  bool cd;
+
+  cd = litex_cardinserted(SDIO_SLOTNO);
+  finfo("Card detect: %d\n", cd);
+  sdio_mediachange(g_sdio_dev, cd);

Review comment:
       Another good idea. Done.

##########
File path: boards/risc-v/litex/arty_a7/src/arty_a7.h
##########
@@ -27,6 +27,113 @@
 
 #include <nuttx/config.h>
 
+/****************************************************************************
+ * configuration
+ */
+
+#define HAVE_SDMMC          1
+#define HAVE_AUTOMOUNTER    1
+
+/****************************************************************************
+ * SDIO Configuration
+ */
+
+#define SDIO_MINOR CONFIG_NSH_MMCSDMINOR
+#define SDIO_SLOTNO CONFIG_NSH_MMCSDSLOTNO
+
+/* Can't support MMC/SD if the card interface(s) are not enable */
+
+#if !defined(CONFIG_LITEX_SDIO) && !defined(CONFIG_LITEX_SDIO1)
+#  undef HAVE_SDMMC
+#endif
+
+#if !defined(CONFIG_FS_AUTOMOUNTER)
+#  undef HAVE_AUTOMOUNTER
+#endif
+
+/****************************************************************************
+ * PROC File System Configuration
+ */
+
+#ifdef CONFIG_FS_PROCFS
+#  ifdef CONFIG_NSH_PROC_MOUNTPOINT
+#    define LITEX_PROCFS_MOUNTPOINT CONFIG_NSH_PROC_MOUNTPOINT
+#  else
+#    define LITEX_PROCFS_MOUNTPOINT "/proc"
+#  endif
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: litex_bringup
+ *
+ * Description:
+ *   Bring up board features
+ *
+ ****************************************************************************/
+
 int litex_bringup(void);
 
+/****************************************************************************
+ * Name: litex_sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO-based MMC/SD card support
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_SDMMC
+int litex_sdio_initialize(void);
+#endif
+
+/****************************************************************************
+ * Name:  litex_automount_initialize
+ *
+ * Description:
+ *   Configure auto-mounters for each enabled MikroBus MMCSD
+ *
+ * Input Parameters:
+ *   None
+ *
+ *  Returned Value:
+ *    None
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+int litex_automount_initialize(void);
+#endif
+
+/****************************************************************************
+ * Name: litex_automount_event
+ *
+ * Description:
+ *   The MMCSD card detection logic has detected an insertion or removal
+ *   event.  It has already scheduled the MMC/SD block driver operations.
+ *   Now we need to schedule the auto-mount event which will occur with a
+ *   substantial delay to make sure that everything has settle down.
+ *
+ * Input Parameters:
+ *   slotno - Identifies the MB slot: MB1_MMCSD_SLOTNO or MB2_MMCSD_SLOTNO.
+ *   inserted - True if the card is inserted in the slot.  False otherwise.
+ *
+ *  Returned Value:
+ *    None
+ *
+ *  Assumptions:
+ *    Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+void litex_automount_event(int slotno, bool inserted);
+#endif
+
+#ifdef HAVE_SDMMC
+bool litex_cardinserted(int slotno);

Review comment:
       Added

##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);
+
+/****************************************************************************
+ * Name: litex_sdio_set_card_isr
+ *
+ * Description:
+ *   SDIO card generates interrupt via SDIO_DATA_1 pin.
+ *   Called by board-specific logic to register an ISR for SDIO card.
+ *
+ * Input Parameters:
+ *   func      - callback function.
+ *   arg       - arg to be passed to the function.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void litex_sdio_set_card_isr(FAR struct sdio_dev_s *dev,
+                             int (*func)(void *), void *arg);
+
+/****************************************************************************
+ * Name: sdio_initialize
+ *
+ * Description:
+ *   Initialize SDIO for operation.
+ *
+ * Input Parameters:
+ *   slotno - Not used.
+ *
+ * Returned Value:
+ *   A reference to an SDIO interface structure.  NULL is returned on
+ *   failures.
+ *
+ ****************************************************************************/
+
+struct sdio_dev_s; /* See include/nuttx/sdio.h */
+FAR struct sdio_dev_s *sdio_initialize(int slotno);
+
+/****************************************************************************
+ * Name: sdio_mediachange
+ *
+ * Description:
+ *   Called by board-specific logic -- possibly from an interrupt handler --
+ *   in order to signal to the driver that a card has been inserted or
+ *   removed from the slot
+ *
+ * Input Parameters:
+ *   dev        - An instance of the SDIO driver device state structure.
+ *   cardinslot - true is a card has been detected in the slot; false if a
+ *                card has been removed from the slot.  Only transitions
+ *                (inserted->removed or removed->inserted should be reported)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H */

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.h
##########
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+#define __ARCH_RISCV_SRC_LITEX_LITEX_SDIO_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+bool litex_sdio_get_card_detect(void);

Review comment:
       Added

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  sdio_eventset_t wkupevent = 0;
+  int ret;
+
+  DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
+              (priv->waitevents == 0 && priv->wkupevent != 0));
+  for (; ; )
+  {
+    ret = nxsem_wait_uninterruptible(&priv->waitsem);
+    if (ret < 0)
+    {
+      litex_configwaitints(priv, 0, 0, 0);
+      wd_cancel(&priv->waitwdog);
+      return SDIOWAIT_ERROR;
+    }
+
+    wkupevent = priv->wkupevent;
+    if (wkupevent != 0)
+    {
+      break;
+    }
+  }
+
+  litex_configwaitints(priv, 0, 0, 0);
+  return wkupevent;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO_CALLBACKENABLE
+ *
+ * Input Parameters:
+ *   dev -      Device-specific state data
+ *   callback - The function 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 litex_registercallback(FAR struct sdio_dev_s *dev,
+                                  worker_t callback, void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Disable callbacks and register this callback and is argument */
+
+  mcinfo("Register %p(%p)\n", callback, arg);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = 0;
+  priv->cbarg    = arg;
+  priv->callback = callback;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_callbackenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO callback events.  This is part of the
+ *   the SDIO callback sequence.  The set of events is configured to enabled
+ *   callbacks to the function provided in litex_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 method.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                                 sdio_eventset_t eventset)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("eventset: %02x\n", eventset);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = eventset;
+  litex_callback(priv);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_callback(void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  /* Is a callback registered? */
+
+  DEBUGASSERT(priv != NULL);
+  mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n",
+         priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus);
+
+  if (priv->callback)
+    {
+      /* Yes.. Check for enabled callback events */
+
+      if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0)
+        {
+          /* Media is present.  Is the media inserted event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+      else
+        {
+          /* Media is not present.  Is the media eject event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+
+      /* Perform the callback, disabling further callbacks.  Of course, the
+       * the callback can (and probably should) re-enable callbacks.
+       */
+
+      priv->cbevents = 0;
+
+      /* Callbacks cannot be performed in the context of an interrupt
+       * handler.  If we are in an interrupt handler, then queue the
+       * callback to be performed later on the work thread.
+       */
+
+      if (up_interrupt_context())
+        {
+          /* Yes.. queue it */
+
+           mcinfo("Queuing callback to %p(%p)\n",
+                  priv->callback, priv->cbarg);
+           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
+ ****************************************************************************/
+
+bool litex_sdio_get_card_detect(void)
+{
+  if (!getreg32(LITEX_SDPHY_CARD_DETECT))
+    {
+      return true;
+    }
+
+  return false;

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] acassis commented on pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
acassis commented on pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#issuecomment-1077562013


   Hi @rtucker85 could you please fix these issues:
   ```
    ../nuttx/tools/checkpatch.sh -g c92c4af304dcc09254dc4023ca822fef11d98233..HEAD
   Error: /home/runner/work/incubator-nuttx/incubator-nuttx/nuttx/arch/risc-v/src/litex/litex_cache.c:2:1: error: Relative file path does not match actual file
   Error: /home/runner/work/incubator-nuttx/incubator-nuttx/nuttx/arch/risc-v/src/litex/litex_cache.c:76:1: error: "Private/Public Functions" not found! File will not be checked
   Error: /home/runner/work/incubator-nuttx/incubator-nuttx/nuttx/boards/risc-v/litex/arty_a7/src/arty_a7.h:30:1: error: Too many blank lines
   Error: /home/runner/work/incubator-nuttx/incubator-nuttx/nuttx/boards/risc-v/litex/arty_a7/src/litex_sdio.c:126:1: error: "Private/Public Functions" not found! File will not be checked
   Error: Process completed with exit code 1.
   ```
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836248350



##########
File path: arch/risc-v/src/litex/Make.defs
##########
@@ -30,7 +30,7 @@ CMN_CSRCS += riscv_initialize.c riscv_swint.c
 CMN_CSRCS += riscv_allocateheap.c riscv_createstack.c riscv_exit.c
 CMN_CSRCS += riscv_assert.c riscv_blocktask.c riscv_copystate.c riscv_initialstate.c
 CMN_CSRCS += riscv_interruptcontext.c riscv_modifyreg32.c riscv_puts.c riscv_mdelay.c
-CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c
+CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c riscv_copyfullstate.c

Review comment:
       ```suggestion
   CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836909191



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1487 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#define LITEX_EV_CMDDONE                (1 << 0)
+#define LITEX_EV_WRERROR                (1 << 1)
+#define LITEX_EV_TIMEOUT                (1 << 2)
+#define LITEX_EV_CRCERROR               (1 << 3)
+
+#define MAX_DIVIDER                     256
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */

Review comment:
       Good point, we dont. The return value has been removed. 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835994940



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835996502



##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_cache.c

Review comment:
       Ok. Sounds good.
   Converted to .S 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#issuecomment-1077319163


   Please fix the warning report here:
   https://github.com/apache/incubator-nuttx/runs/5671271324?check_suite_focus=true


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835006732



##########
File path: arch/risc-v/src/litex/litex_cache.c
##########
@@ -0,0 +1,88 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_cache.c

Review comment:
       Maybe we can create `arch/risc-v/src/litex/litex_cache.S` instead? I mean that there are only inline ASM calls here

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s

Review comment:
       Please equip with
   ```
   /****************************************************************************
    * Included Files
    ****************************************************************************/
   
   /****************************************************************************
    * Pre-processor Definitions
    ****************************************************************************/
   
   etc.
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)

Review comment:
       ```suggestion
     if ((pending & LITEX_INT_MEM2BLOCK) != 0)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );

Review comment:
       ```suggestion
     mcinfo("cmd: %" PRIu32 " cmdid: %" PRIu32 " arg: %08" PRIx32 " regval: %08" PRIx32 "\n",
            cmd, cmdidx, arg, regval);
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);

Review comment:
       ```suggestion
     putreg32((uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
     putreg32((uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)

Review comment:
       ```suggestion
       if (priv->do_sdio_card != NULL)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);

Review comment:
       ```suggestion
   static void litex_widebus(FAR struct sdio_dev_s *dev,
                             bool enable);
   ```
   Here and in other places

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)

Review comment:
       ```suggestion
     if ((pending & LITEX_INT_CARDDETECT) != 0)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);

Review comment:
       ```suggestion
     putreg32(priv->xfrints | priv->waitints | priv->cintints,
              LITEX_SDIRQ_ENABLE);
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);

Review comment:
       please add `{}`

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;

Review comment:
       Can we have `#define MAX_DIVIDER 256` and use it in all places?

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)

Review comment:
       ```suggestion
   static int litex_waitresponse(struct sdio_dev_s *dev, uint32_t cmd)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);

Review comment:
       ```suggestion
     putreg32((uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
     putreg32((uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;

Review comment:
       ```suggestion
     regval = (cmdidx << 8) | (transfer << 5) | resplen;
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)

Review comment:
       ```suggestion
   static int litex_sendsetup(struct sdio_dev_s *dev,
                              const uint8_t *buffer, size_t nbytes)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;

Review comment:
       Please add `{}`

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)

Review comment:
       are there any defines for those numbers?

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,

Review comment:
       ```suggestion
   static void litex_waitenable(struct sdio_dev_s *dev,
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)

Review comment:
       ```suggestion
   static sdio_eventset_t litex_eventwait(struct sdio_dev_s *dev)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)

Review comment:
       ```suggestion
     if (rshort != NULL)
   ```

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)

Review comment:
       ```suggestion
         if (timeout == 0)
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] AndrewD commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
AndrewD commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r834056629



##########
File path: include/nuttx/sdio.h
##########
@@ -477,6 +477,7 @@
 #define SDIO_CAPS_DMABEFOREWRITE  0x04 /* Bit 2=1: Executes DMA before write command */
 #define SDIO_CAPS_4BIT            0x08 /* Bit 3=1: Supports 4 bit operation */
 #define SDIO_CAPS_8BIT            0x10 /* Bit 4=1: Supports 8 bit operation */
+#define SDIO_CAPS_4BIT_ONLY       0x20 /* Bit 5=1: Supports 4-bit only operation */

Review comment:
       The issue is that the peripheral does not support 1bit mode, so 4 bit mode must be selected _before_ checking the SCR register.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r835995244



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)

Review comment:
       Done

##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] rtucker85 commented on a change in pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
rtucker85 commented on a change in pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#discussion_r836909877



##########
File path: arch/risc-v/src/litex/litex_sdio.c
##########
@@ -0,0 +1,1450 @@
+/****************************************************************************
+ * arch/risc-v/src/litex/litex_sdio.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/cache.h>
+#include <nuttx/clock.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/sdio.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/wdog.h>
+#include <nuttx/wqueue.h>
+
+#include <nuttx/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "riscv_internal.h"
+
+#include "litex_sdio.h"
+#include "litex_clockconfig.h"
+#include "hardware/litex_sdio.h"
+
+#define SD_CTL_DATA_XFER_NONE           0
+#define SD_CTL_DATA_XFER_READ           1
+#define SD_CTL_DATA_XFER_WRITE          2
+
+#define SDCARD_CTRL_RESPONSE_NONE       0
+#define SDCARD_CTRL_RESPONSE_SHORT      1
+#define SDCARD_CTRL_RESPONSE_LONG       2
+
+#define LITEX_INT_CARDDETECT            (1 << 0)
+#define LITEX_INT_BLOCK2MEM             (1 << 1)
+#define LITEX_INT_MEM2BLOCK             (1 << 2)
+#define LITEX_INT_CMDDONE               (1 << 3)
+
+#ifndef CONFIG_LITEX_IDMODE_FREQ
+#  define CONFIG_LITEX_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
+#endif
+
+#ifndef CONFIG_LITEX_MMCXFR_FREQ
+#  define CONFIG_LITEX_MMCXFR_FREQ 25000000  /* 25MHz MMC, normal clocking */
+#endif
+
+#ifndef CONFIG_LITEX_SD4BIT_FREQ
+#  define CONFIG_LITEX_SD4BIT_FREQ 50000000  /* 25MHz SD 4-bit, normal clocking */
+#endif
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
+struct litex_dev_s
+{
+  struct sdio_dev_s  dev;             /* Standard, base SDIO interface */
+
+  /* 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 */
+
+  /* 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           xfrints;         /* Interrupt enables for data transfer */
+
+  /* Card interrupt support for SDIO */
+
+  uint32_t           cintints;                /* Interrupt enables for card ints */
+  int               (*do_sdio_card)(void *);  /* SDIO card ISR */
+  void               *do_sdio_arg;            /* arg for SDIO card ISR */
+
+  /* Fixed transfer block size support */
+
+  uint8_t            block_size;
+};
+
+static int  litex_interrupt(int irq, void *context, void *arg);
+
+static void litex_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t litex_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev);
+static void litex_widebus(FAR struct sdio_dev_s *dev,
+                bool enable);
+static void litex_clock(FAR struct sdio_dev_s *dev,
+                enum sdio_clock_e rate);
+static int  litex_attach(FAR struct sdio_dev_s *dev);
+static int  litex_sendcmd(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t arg);
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                unsigned int blocklen, unsigned int nblocks);
+static int  litex_cancel(FAR struct sdio_dev_s *dev);
+static int  litex_recvsetup(FAR struct sdio_dev_s *dev,
+                FAR uint8_t *buffer, size_t nbytes);
+static int  litex_sendsetup(FAR struct sdio_dev_s *dev,
+                FAR const uint8_t *buffer, size_t buflen);
+static int  litex_waitresponse(FAR struct sdio_dev_s *dev,
+                uint32_t cmd);
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset);
+static int  litex_registercallback(FAR struct sdio_dev_s *dev,
+                worker_t callback, void *arg);
+static int  litex_recvlong(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t rlong[4]);
+static int  litex_recvshort(FAR struct sdio_dev_s *dev,
+                uint32_t cmd, uint32_t *rshort);
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev);
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitmask,
+                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
+static void litex_configxfrints(struct litex_dev_s *priv,
+                uint32_t xfrints);
+
+/* Data Transfer Helpers ****************************************************/
+
+static void litex_eventtimeout(wdparm_t arg);
+static void litex_endwait(struct litex_dev_s *priv,
+                sdio_eventset_t wkupevent);
+static void litex_callback(void *arg);
+
+struct litex_dev_s g_sdiodev =
+{
+  .dev =
+  {
+    .reset            = litex_reset,
+    .capabilities     = litex_capabilities,
+    .status           = litex_status,
+    .widebus          = litex_widebus,
+    .clock            = litex_clock,
+    .attach           = litex_attach,
+    .sendcmd          = litex_sendcmd,
+    .blocksetup       = litex_blocksetup,
+    .recvsetup        = litex_recvsetup,
+    .sendsetup        = litex_sendsetup,
+    .cancel           = litex_cancel,
+    .dmarecvsetup     = litex_recvsetup,
+    .dmasendsetup     = litex_sendsetup,
+    .waitresponse     = litex_waitresponse,
+    .recv_r1          = litex_recvshort,
+    .recv_r2          = litex_recvlong,
+    .recv_r3          = litex_recvshort,
+    .recv_r4          = litex_recvshort,
+    .recv_r5          = litex_recvshort,
+    .recv_r6          = litex_recvshort,
+    .recv_r7          = litex_recvshort,
+    .waitenable       = litex_waitenable,
+    .eventwait        = litex_eventwait,
+    .callbackenable   = litex_callbackenable,
+    .registercallback = litex_registercallback,
+  },
+};
+
+/****************************************************************************
+ * Name: litex_pow2roundup
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static inline uint32_t litex_pow2roundup(uint32_t r)
+{
+  r--;
+  r |= r >>  1;
+  r |= r >>  2;
+  r |= r >>  4;
+  r |= r >>  8;
+  r |= r >> 16;
+  r++;
+  return r;
+}
+
+/****************************************************************************
+ * Name: litex_configwaitints
+ *
+ * Description:
+ *   Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ *   priv       - A reference to the SDIO device state structure
+ *   waitmask   - The set of bits in the SDIO MASK register to set
+ *   waitevents - Waited for events
+ *   wkupevent  - Wake-up events
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configwaitints(struct litex_dev_s *priv, uint32_t waitints,
+                                 sdio_eventset_t waitevents,
+                                 sdio_eventset_t wkupevent)
+{
+  irqstate_t flags;
+
+  flags            = enter_critical_section();
+  priv->waitevents = waitevents;
+  priv->wkupevent  = wkupevent;
+  priv->waitints   = waitints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_configxfrints
+ *
+ * Description:
+ *   Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ *   priv    - A reference to the SDIO device state structure
+ *   xfrints - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_configxfrints(struct litex_dev_s *priv, uint32_t xfrints)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  priv->xfrints = xfrints;
+
+  putreg32(priv->xfrints | priv->waitints | priv->cintints,
+    LITEX_SDIRQ_ENABLE);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_interrupt
+ *
+ * Description:
+ *   SDIO interrupt handler
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_interrupt(int irq, void *context, void *arg)
+{
+  struct litex_dev_s *priv = &g_sdiodev;
+  uint32_t pending;
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  pending = getreg32(LITEX_SDIRQ_PENDING) & getreg32(LITEX_SDIRQ_ENABLE);
+  putreg32(pending, LITEX_SDIRQ_PENDING);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  /* check for card change interrupt */
+
+  if (pending & LITEX_INT_CARDDETECT)
+  {
+    mcinfo("Card Detect State: %lu\n", getreg32(LITEX_SDPHY_CARD_DETECT));
+
+    /* Perform callback */
+
+    if (priv->do_sdio_card)
+    {
+      priv->do_sdio_card(priv->do_sdio_arg);
+    }
+  }
+
+  /* check for DMA write interrupt */
+
+  if (pending & LITEX_INT_BLOCK2MEM)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+  /* check for DMA read interrupt */
+
+  if (pending & LITEX_INT_MEM2BLOCK)
+  {
+    if ((priv->waitevents & SDIOWAIT_TRANSFERDONE) != 0)
+      {
+        litex_configxfrints(priv, 0);
+
+        /* Yes.. wake up any waiting threads */
+
+        litex_endwait(priv, SDIOWAIT_TRANSFERDONE);
+      }
+  }
+
+#if 0 /* Not used */
+  /* check for command complete interrupt */
+
+  if (pending & LITEX_INT_CMDDONE)
+  {
+    if ((priv->waitevents &
+          (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
+    {
+      /* Yes.. wake the thread up */
+
+      litex_endwait(priv, SDIOWAIT_CMDDONE);
+    }
+  }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_eventtimeout
+ *
+ * Description:
+ *   The watchdog timeout setup when the event wait start has expired without
+ *   any other waited-for event occurring.
+ *
+ * Input Parameters:
+ *   arg    - The argument
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void litex_eventtimeout(wdparm_t arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+              priv->wkupevent != 0);
+
+  mcinfo("pending irq: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+         getreg32(LITEX_SDIRQ_PENDING),
+         getreg32(LITEX_SDIRQ_ENABLE));
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+  {
+    litex_endwait(priv, SDIOWAIT_TIMEOUT);
+    mcerr("Timeout\n");
+  }
+}
+
+/****************************************************************************
+ * Name: litex_endwait
+ *
+ * Description:
+ *   Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the SDIO 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 litex_endwait(struct litex_dev_s *priv,
+                          sdio_eventset_t wkupevent)
+{
+  /* Cancel the watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Disable event-related interrupts */
+
+  litex_configwaitints(priv, 0, 0, wkupevent);
+
+  /* Wake up the waiting thread */
+
+  nxsem_post(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: litex_reset
+ *
+ * Description:
+ *   Reset the SDIO controller.  Undo all setup and initialization.
+ *
+ * Input Parameters:
+ *   dev    - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_reset(FAR struct sdio_dev_s *dev)
+{
+  FAR struct litex_dev_s *priv = (FAR struct litex_dev_s *)dev;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  priv->waitevents  = 0;
+  priv->waitints    = 0;
+  priv->wkupevent   = 0;
+
+  wd_cancel(&priv->waitwdog);
+
+  priv->xfrints     = 0;
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_capabilities(FAR struct sdio_dev_s *dev)
+{
+  sdio_capset_t caps = 0;
+
+  /* LiteSDCard only supports 4-bit bus width */
+
+  caps |= SDIO_CAPS_4BIT_ONLY;
+  caps |= SDIO_CAPS_DMASUPPORTED;
+  caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+  return caps;
+}
+
+/****************************************************************************
+ * Name: litex_status
+ *
+ * Description:
+ *   Get SDIO status.
+ *
+ * Input Parameters:
+ *   dev   - Device-specific state data
+ *
+ * Returned Value:
+ *   Returns a bitset of status values (see litex_dev_s* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t litex_status(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  sdio_statset_t cd = priv->cdstatus;
+  mcinfo("CD Status: %u\n", cd);
+
+  return cd;
+}
+
+/****************************************************************************
+ * Name: litex_widebus
+ *
+ * Description:
+ *   Called after change in Bus width has been selected (via ACMD6).  Most
+ *   controllers will need to perform some special operations to work
+ *   correctly in the new bus mode.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   wide - true: wide bus (4-bit) bus mode enabled
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+  /* LiteSDCard only supports 4-bit bus width.
+   * Nothing to do here.
+   */
+}
+
+/****************************************************************************
+ * Name: litex_clock
+ *
+ * Description:
+ *   Enable/disable SDIO clocking
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   rate - Specifies the clocking to use (see enum sdio_clock_e)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+  uint32_t clk_freq;
+
+  switch (rate)
+    {
+      /* Return early - SDPHY doesnt support clock disabling */
+
+      default:
+      case CLOCK_SDIO_DISABLED:
+        return;
+
+      /* Enable in initial ID mode clocking (<400KHz) */
+
+      case CLOCK_IDMODE:
+        clk_freq = CONFIG_LITEX_IDMODE_FREQ;
+        break;
+
+      /* Enable in MMC normal operation clocking */
+
+      case CLOCK_MMC_TRANSFER:
+        clk_freq = CONFIG_LITEX_MMCXFR_FREQ;
+        break;
+
+      /* SD normal operation clocking (wide 4-bit mode) */
+
+      case CLOCK_SD_TRANSFER_4BIT:
+        clk_freq = CONFIG_LITEX_SD4BIT_FREQ;
+        break;
+    }
+
+  /* Set the new clock frequency along with the clock enable/disable bit */
+
+  uint32_t divider;
+  divider = clk_freq ? litex_get_hfclk() / clk_freq : 256;
+  divider = litex_pow2roundup(divider);
+  divider = min(max(divider, 2), 256);
+
+  /* this is the *effective* new clk_freq */
+
+  clk_freq = litex_get_hfclk() / divider;
+  if (clk_freq > 1000000)
+    mcinfo("Setting SDCard clk freq to %ld MHz\n", clk_freq / 1000000);
+  else
+    mcinfo("Setting SDCard clk freq to %ld KHz\n", clk_freq / 1000);
+
+  putreg32(divider, LITEX_SDPHY_CLOCKER_DIVIDER);
+}
+
+/****************************************************************************
+ * Name: litex_attach
+ *
+ * Description:
+ *   Attach and prepare interrupts
+ *
+ * Input Parameters:
+ *   dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK on success; A negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int litex_attach(FAR struct sdio_dev_s *dev)
+{
+  int ret;
+
+  /* Attach the SDIO interrupt handler */
+
+  ret = irq_attach(LITEX_IRQ_SDCARD, litex_interrupt, NULL);
+  if (ret == OK)
+  {
+    putreg32(0xffffffff, LITEX_SDIRQ_PENDING);
+    putreg32(0, LITEX_SDIRQ_ENABLE);
+    up_enable_irq(LITEX_IRQ_SDCARD);
+  }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_sendcmd
+ *
+ * Description:
+ *   Send the SDIO command
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command to send (32-bits, encoded)
+ *   arg  - 32-bit argument required with some commands
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int litex_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                         uint32_t arg)
+{
+  uint32_t transfer = 0;
+  uint32_t resplen = 0;
+  uint32_t regval = 0;
+  uint32_t cmdidx = 0;
+
+  irqstate_t flags;
+  flags = enter_critical_section();
+
+  /* Set WAITRESP bits */
+
+  switch (cmd & MMCSD_RESPONSE_MASK)
+    {
+    case MMCSD_NO_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_NONE;
+      break;
+
+    case MMCSD_R1_RESPONSE:
+    case MMCSD_R1B_RESPONSE:
+    case MMCSD_R3_RESPONSE:
+    case MMCSD_R4_RESPONSE:
+    case MMCSD_R5_RESPONSE:
+    case MMCSD_R6_RESPONSE:
+    case MMCSD_R7_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_SHORT;
+      break;
+
+    case MMCSD_R2_RESPONSE:
+      resplen = SDCARD_CTRL_RESPONSE_LONG;
+      break;
+    }
+
+  switch (cmd & MMCSD_DATAXFR_MASK)
+    {
+    case MMCSD_NODATAXFR:
+      transfer = SD_CTL_DATA_XFER_NONE;
+      break;
+
+    case MMCSD_RDSTREAM:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRSTREAM:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+
+    case MMCSD_RDDATAXFR:
+      transfer = SD_CTL_DATA_XFER_READ;
+      break;
+
+    case MMCSD_WRDATAXFR:
+      transfer = SD_CTL_DATA_XFER_WRITE;
+      break;
+    }
+
+  /* Write the SDIO CMD */
+
+  cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+  regval = cmdidx << 8 | transfer << 5 | resplen;
+  putreg32(arg, LITEX_SDCORE_CMD_ARGUMENT);
+  putreg32(regval, LITEX_SDCORE_CMD_COMMAND);
+  putreg32(1, LITEX_SDCORE_CMD_SEND);
+
+  mcinfo("cmd: %lu cmdid: %lu arg: %08lx regval: %08lx\n",
+    cmd,
+    cmdidx,
+    arg,
+    regval
+  );
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocklen  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocklen, unsigned int nblocks)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("blocklen=%d, total transfer=%d (%d blocks)\n", blocklen,
+         blocklen * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  priv->block_size = blocklen;
+
+  putreg32(blocklen, LITEX_SDCORE_BLOCK_LENGTH);
+  putreg32(nblocks, LITEX_SDCORE_BLOCK_COUNT);
+}
+
+/****************************************************************************
+ * Name: litex_cancel
+ *
+ * Description:
+ *   Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP,
+ *   SDIO_DMARECVSETUP or SDIO_DMASENDSETUP.  This must be called to cancel
+ *   the data transfer setup if, for some reason, you cannot perform the
+ *   transfer.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_cancel(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Cancel any watchdog timeout */
+
+  wd_cancel(&priv->waitwdog);
+
+  /* Stop DMA transfers */
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO 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
+ *
+ ****************************************************************************/
+
+static int litex_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+                           size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_BLOCK2MEM);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDBLOCK2MEM_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDBLOCK2MEM_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDBLOCK2MEM_DMA_LENGTH);
+  putreg32(1, LITEX_SDBLOCK2MEM_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_sendsetup
+ *
+ * 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 SDIO device interface
+ *   buffer - The memory to DMA into
+ *   nbytes - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_sendsetup(FAR struct sdio_dev_s *dev,
+                              FAR const uint8_t *buffer, size_t nbytes)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+  DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+  mcinfo("buffer: %p bytes: %u\n", buffer, nbytes);
+
+  litex_configxfrints(priv, LITEX_INT_MEM2BLOCK);
+
+  /* flush CPU d-cache */
+
+  up_invalidate_dcache_all();
+
+  putreg32(0, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+  putreg32((uint64_t)(uintptr_t)buffer >> 32, LITEX_SDMEM2BLOCK_DMA_BASE);
+  putreg32((uint64_t)(uintptr_t)buffer, LITEX_SDMEM2BLOCK_DMA_BASE + 0x04);
+  putreg32(nbytes, LITEX_SDMEM2BLOCK_DMA_LENGTH);
+  putreg32(1, LITEX_SDMEM2BLOCK_DMA_ENABLE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_waitresponse
+ *
+ * Description:
+ *   Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ *   dev  - An instance of the SDIO device interface
+ *   cmd  - The command that was sent.  See 32-bit command definitions above.
+ *
+ * Returned Value:
+ *   OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int litex_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+  uint32_t ev;
+
+  for (; ; )
+  {
+    if ((cmd & MMCSD_DATAXFR_MASK) == 0)
+      ev = getreg32(LITEX_SDCORE_CMD_EVENT);
+    else
+      ev = getreg32(LITEX_SDCORE_DATA_EVENT);
+
+    mcinfo("%lu\n", ev);
+
+    if (ev & 0x01)
+      break;
+
+    nxsig_usleep(10);
+  }
+
+  /* SD_WRERROR */
+
+  if (ev & 0x02)
+    return -EIO;
+
+  /* SD_TIMEOUT */
+
+  if (ev & 0x04)
+    return -ETIMEDOUT;
+
+  /* SD_CRCERROR */
+
+  if (ev & 0x08)
+    return -EILSEQ;
+
+  return OK;
+}
+
+static int litex_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                          uint32_t rlong[4])
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rlong)
+    {
+      rlong[0] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x00);
+      rlong[1] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x04);
+      rlong[2] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x08);
+      rlong[3] = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+static int litex_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                           uint32_t *rshort)
+{
+  uint32_t regval;
+  int ret = OK;
+
+  /* R3  OCR (48-bit)
+   *     47        0               Start bit
+   *     46        0               Transmission bit (0=from card)
+   *     45:40     bit5   - bit0   Reserved
+   *     39:8      bit31  - bit0   32-bit OCR register
+   *     7:1       bit6   - bit0   Reserved
+   *     0         1               End bit
+   */
+
+  /* Check that this is the correct response to this command */
+
+#ifdef CONFIG_DEBUG_MEMCARD_INFO
+  if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE &&
+      (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
+    {
+      mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
+      ret = -EINVAL;
+    }
+  else
+#endif
+    {
+      /* Check if a timeout or CRC error occurred */
+
+      regval = getreg32(LITEX_SDCORE_CMD_EVENT);
+      if ((regval & 0x04) != 0)
+      {
+        mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+        ret = -ETIMEDOUT;
+      }
+      else if ((regval & 0x08) != 0)
+      {
+        mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+        ret = -EIO;
+      }
+    }
+
+  if (rshort)
+    {
+      *rshort = getreg32(LITEX_SDCORE_CMD_RESPONSE + 0x0c);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: litex_waitenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO wait events.  This is part of the
+ *   the SDIO_WAITEVENT sequence.  The set of to-be-waited-for events is
+ *   configured before calling litex_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) SDIO_WAITENABLE is called
+ *   again specifying a different set of wait events, or (2) SDIO_EVENTWAIT
+ *   returns.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOWAIT_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_waitenable(FAR struct sdio_dev_s *dev,
+                             sdio_eventset_t eventset, uint32_t timeout)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  litex_configwaitints(priv, 0, eventset, 0);
+
+  /* Check if the timeout event is specified in the event set */
+
+  if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+    {
+      int delay;
+      int ret;
+
+      /* Yes.. Handle a cornercase: The user requested a timeout event but
+       * with timeout == 0?
+       */
+
+      if (!timeout)
+        {
+          priv->wkupevent = SDIOWAIT_TIMEOUT;
+          return;
+        }
+
+      /* Start the watchdog timer */
+
+      delay = MSEC2TICK(timeout);
+      ret   = wd_start(&priv->waitwdog, delay,
+                       litex_eventtimeout, (wdparm_t)priv);
+      if (ret < 0)
+        {
+          mcerr("ERROR: wd_start failed: %d\n", ret);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: litex_eventwait
+ *
+ * Description:
+ *   Wait for one of the enabled events to occur (or a timeout).  Note that
+ *   all events enabled by SDIO_WAITEVENTS are disabled when litex_eventwait
+ *   returns.  SDIO_WAITEVENTS must be called again before litex_eventwait
+ *   can be used again.
+ *
+ * Input Parameters:
+ *   dev     - An instance of the SDIO device interface
+ *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
+ *             timeout with no wait.  The timeout value is ignored if
+ *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
+ *
+ * Returned Value:
+ *   Event set containing the event(s) that ended the wait.  Should always
+ *   be non-zero.  All events are disabled after the wait concludes.
+ *
+ ****************************************************************************/
+
+static sdio_eventset_t litex_eventwait(FAR struct sdio_dev_s *dev)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+  sdio_eventset_t wkupevent = 0;
+  int ret;
+
+  DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
+              (priv->waitevents == 0 && priv->wkupevent != 0));
+  for (; ; )
+  {
+    ret = nxsem_wait_uninterruptible(&priv->waitsem);
+    if (ret < 0)
+    {
+      litex_configwaitints(priv, 0, 0, 0);
+      wd_cancel(&priv->waitwdog);
+      return SDIOWAIT_ERROR;
+    }
+
+    wkupevent = priv->wkupevent;
+    if (wkupevent != 0)
+    {
+      break;
+    }
+  }
+
+  litex_configwaitints(priv, 0, 0, 0);
+  return wkupevent;
+}
+
+/****************************************************************************
+ * Name: litex_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 SDIO_CALLBACKENABLE
+ *
+ * Input Parameters:
+ *   dev -      Device-specific state data
+ *   callback - The function 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 litex_registercallback(FAR struct sdio_dev_s *dev,
+                                  worker_t callback, void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  /* Disable callbacks and register this callback and is argument */
+
+  mcinfo("Register %p(%p)\n", callback, arg);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = 0;
+  priv->cbarg    = arg;
+  priv->callback = callback;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: litex_callbackenable
+ *
+ * Description:
+ *   Enable/disable of a set of SDIO callback events.  This is part of the
+ *   the SDIO callback sequence.  The set of events is configured to enabled
+ *   callbacks to the function provided in litex_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 method.
+ *
+ * Input Parameters:
+ *   dev      - An instance of the SDIO device interface
+ *   eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
+ *              definitions). 0=disable; 1=enable.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void litex_callbackenable(FAR struct sdio_dev_s *dev,
+                                 sdio_eventset_t eventset)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)dev;
+
+  mcinfo("eventset: %02x\n", eventset);
+  DEBUGASSERT(priv != NULL);
+
+  priv->cbevents = eventset;
+  litex_callback(priv);
+}
+
+/****************************************************************************
+ * Name: litex_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 litex_callback(void *arg)
+{
+  struct litex_dev_s *priv = (struct litex_dev_s *)arg;
+
+  /* Is a callback registered? */
+
+  DEBUGASSERT(priv != NULL);
+  mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n",
+         priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus);
+
+  if (priv->callback)
+    {
+      /* Yes.. Check for enabled callback events */
+
+      if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0)
+        {
+          /* Media is present.  Is the media inserted event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+      else
+        {
+          /* Media is not present.  Is the media eject event enabled? */
+
+          if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0)
+            {
+              /* No... return without performing the callback */
+
+              return;
+            }
+        }
+
+      /* Perform the callback, disabling further callbacks.  Of course, the
+       * the callback can (and probably should) re-enable callbacks.
+       */
+
+      priv->cbevents = 0;
+
+      /* Callbacks cannot be performed in the context of an interrupt
+       * handler.  If we are in an interrupt handler, then queue the
+       * callback to be performed later on the work thread.
+       */
+
+      if (up_interrupt_context())
+        {
+          /* Yes.. queue it */
+
+           mcinfo("Queuing callback to %p(%p)\n",
+                  priv->callback, priv->cbarg);
+           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
+ ****************************************************************************/
+
+bool litex_sdio_get_card_detect(void)
+{
+  if (!getreg32(LITEX_SDPHY_CARD_DETECT))
+    {
+      return true;
+    }
+
+  return false;

Review comment:
       My mistake. This got missed. Actually done now.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835#issuecomment-1080437837


   Thank you for fixing all the comments.
   Please rebase your changes on top of the latest mainline to fix build error.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #5835: LITESDCARD peripheral driver support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged pull request #5835:
URL: https://github.com/apache/incubator-nuttx/pull/5835


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org