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-unsubscr...@nuttx.apache.org

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


Reply via email to