text: /****************************************************************************
   * arch/arm/src/kinetis/kinetis_sdhc.c
   *
   *   Copyright (C) 2011-2012, 2014, 2016 Gregory Nutt. All rights reserved.
   *   Author: Gregory Nutt <gnutt@nuttx.org>
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name NuttX nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  
  #include <stdint.h>
  #include <stdbool.h>
  #include <semaphore.h>
  #include <string.h>
  #include <assert.h>
  #include <debug.h>
  #include <errno.h>
  
  #include <nuttx/wdog.h>
  #include <nuttx/clock.h>
  #include <nuttx/arch.h>
  #include <nuttx/sdio.h>
  #include <nuttx/wqueue.h>
  #include <nuttx/mmcsd.h>
  
  #include <nuttx/irq.h>
  #include <arch/board/board.h>
  
  #include "chip.h"
  #include "up_arch.h"
  
  #include "kinetis.h"
  #include "chip/kinetis_pinmux.h"
  #include "chip/kinetis_sim.h"
  #include "chip/kinetis_sdhc.h"
  
  #ifdef CONFIG_KINETIS_SDHC
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  /* Configuration ************************************************************/
  
  #ifndef CONFIG_SDIO_DMA
  #  warning "Large Non-DMA transfer may result in RX overrun failures"
  #endif
  
  #if !defined(CONFIG_SCHED_WORKQUEUE) || !defined(CONFIG_SCHED_HPWORK)
  #  error "Callback support requires CONFIG_SCHED_WORKQUEUE and CONFIG_SCHED_HPWORK"
  #endif
  
  #ifndef CONFIG_KINETIS_SDHC_PRIO
  #  define CONFIG_KINETIS_SDHC_PRIO NVIC_SYSH_PRIORITY_DEFAULT
  #endif
  
  #ifndef CONFIG_DEBUG_MEMCARD_INFO
  #  undef CONFIG_SDIO_XFRDEBUG
  #endif
  
  /* SDCLK frequencies corresponding to various modes of operation.  These
   * values may be provided in either the NuttX configuration file or in
   * the board.h file
   *
   * NOTE:  These settings are not currently used.  Since there are only four
   * frequencies, it makes more sense to just "can" the fixed frequency prescaler
   * and divider values.
   */
  
  #ifdef CONFIG_KINETIS_SDHC_ABSFREQ
  #  ifndef CONFIG_KINETIS_IDMODE_FREQ
  #    define CONFIG_KINETIS_IDMODE_FREQ 400000    /* 400 KHz, ID mode */
  #  endif
  #  ifndef CONFIG_KINETIS_MMCXFR_FREQ
  #    define CONFIG_KINETIS_MMCXFR_FREQ 20000000  /* 20MHz MMC, normal clocking */
  #  endif
  #  ifndef CONFIG_KINETIS_SD1BIT_FREQ
  #    define CONFIG_KINETIS_SD1BIT_FREQ 20000000  /* 20MHz SD 1-bit, normal clocking */
  #  endif
  #  ifndef CONFIG_KINETIS_SD4BIT_FREQ
  #    define CONFIG_KINETIS_SD4BIT_FREQ 25000000  /* 25MHz SD 4-bit, normal clocking */
  #  endif
  #endif
  
  /* Timing */
  
  #define SDHC_CMDTIMEOUT         (100000)
  #define SDHC_LONGTIMEOUT        (0x7fffffff)
  
  /* Big DVS setting.  Range is 0=SDCLK*213 through 14=SDCLK*227 */
  
  #define SDHC_DVS_MAXTIMEOUT     (14)
  #define SDHC_DVS_DATATIMEOUT    (14)
  
  /* Maximum watermark value */
  
  #define SDHC_MAX_WATERMARK      128
  
  /* Data transfer / Event waiting interrupt mask bits */
  
  #define SDHC_RESPERR_INTS  (SDHC_INT_CCE|SDHC_INT_CTOE|SDHC_INT_CEBE|SDHC_INT_CIE)
  #define SDHC_RESPDONE_INTS (SDHC_RESPERR_INTS|SDHC_INT_CC)
  
  #define SCHC_XFRERR_INTS   (SDHC_INT_DCE|SDHC_INT_DTOE|SDHC_INT_DEBE)
  #define SDHC_RCVDONE_INTS  (SCHC_XFRERR_INTS|SDHC_INT_BRR|SDHC_INT_TC)
  #define SDHC_SNDDONE_INTS  (SCHC_XFRERR_INTS|SDHC_INT_BWR|SDHC_INT_TC)
  #define SDHC_XFRDONE_INTS  (SCHC_XFRERR_INTS|SDHC_INT_BRR|SDHC_INT_BWR|SDHC_INT_TC)
  
  #define SCHC_DMAERR_INTS   (SDHC_INT_DCE|SDHC_INT_DTOE|SDHC_INT_DEBE|SDHC_INT_DMAE)
  #define SDHC_DMADONE_INTS  (SCHC_DMAERR_INTS|SDHC_INT_DINT)
  
  #define SDHC_WAITALL_INTS  (SDHC_RESPDONE_INTS|SDHC_XFRDONE_INTS|SDHC_DMADONE_INTS)
  
  /* Register logging support */
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  #  define SAMPLENDX_BEFORE_SETUP  0
  #  define SAMPLENDX_AFTER_SETUP   1
  #  define SAMPLENDX_END_TRANSFER  2
  #  define DEBUG_NSAMPLES          3
  #endif
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  
  /* This structure defines the state of the Kinetis SDIO interface */
  
  struct kinetis_dev_s
  {
    struct sdio_dev_s  dev;        /* Standard, base SDIO interface */
  
    /* Kinetis-specific extensions */
    /* 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 */
    WDOG_ID            waitwdog;   /* Watchdog that handles event timeouts */
  
    /* Callback support */
  
    uint8_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          *buffer;     /* Address of current R/W buffer */
    size_t             remaining;  /* Number of bytes remaining in the transfer */
    uint32_t           xfrints;    /* Interrupt enables for data transfer */
  
    /* DMA data transfer support */
  
  #ifdef CONFIG_SDIO_DMA
    volatile uint8_t   xfrflags;   /* Used to synchronize SDIO and DMA completion events */
  #endif
  };
  
  /* Register logging support */
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  struct kinetis_sdhcregs_s
  {
    /* All read-able SDHC registers */
  
    uint32_t dsaddr;    /* DMA System Address Register */
    uint32_t blkattr;   /* Block Attributes Register */
    uint32_t cmdarg;    /* Command Argument Register */
    uint32_t xferty;    /* Transfer Type Register */
    uint32_t cmdrsp0;   /* Command Response 0 */
    uint32_t cmdrsp1;   /* Command Response 1 */
    uint32_t cmdrsp2;   /* Command Response 2 */
    uint32_t cmdrsp3;   /* Command Response 3 */
    uint32_t prsstat;   /* Present State Register */
    uint32_t proctl;    /* Protocol Control Register */
    uint32_t sysctl;    /* System Control Register */
    uint32_t irqstat;   /* Interrupt Status Register */
    uint32_t irqstaten; /* Interrupt Status Enable Register */
    uint32_t irqsigen;  /* Interrupt Signal Enable Register */
    uint32_t ac12err;   /* Auto CMD12 Error Status Register */
    uint32_t htcapblt;  /* Host Controller Capabilities */
    uint32_t wml;       /* Watermark Level Register */
    uint32_t admaes;    /* ADMA Error Status Register */
    uint32_t adsaddr;   /* ADMA System Address Register */
    uint32_t vendor;    /* Vendor Specific Register */
    uint32_t mmcboot;   /* MMC Boot Register */
    uint32_t hostver;   /* Host Controller Version */
  };
  #endif
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  
  /* Low-level helpers ********************************************************/
  
  static void kinetis_takesem(struct kinetis_dev_s *priv);
  #define     kinetis_givesem(priv) (sem_post(&priv->waitsem))
  static void kinetis_configwaitints(struct kinetis_dev_s *priv, uint32_t waitints,
                sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
  static void kinetis_configxfrints(struct kinetis_dev_s *priv, uint32_t xfrints);
  
  /* DMA Helpers **************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_sampleinit(void);
  static void kinetis_sdhcsample(struct kinetis_sdhcregs_s *regs);
  static void kinetis_sample(struct kinetis_dev_s *priv, int index);
  static void kinetis_dumpsample(struct kinetis_dev_s *priv,
                struct kinetis_sdhcregs_s *regs, const char *msg);
  static void kinetis_dumpsamples(struct kinetis_dev_s *priv);
  static void kinetis_showregs(struct kinetis_dev_s *priv, const char *msg);
  #else
  #  define   kinetis_sampleinit()
  #  define   kinetis_sample(priv,index)
  #  define   kinetis_dumpsamples(priv)
  #  define   kinetis_showregs(priv,msg)
  #endif
  
  /* Data Transfer Helpers ****************************************************/
  
  static void kinetis_dataconfig(struct kinetis_dev_s *priv, bool bwrite,
                                 unsigned int blocksize, unsigned int nblocks,
                                 unsigned int timeout);
  static void kinetis_datadisable(void);
  #ifndef CONFIG_SDIO_DMA
  static void kinetis_transmit(struct kinetis_dev_s *priv);
  static void kinetis_receive(struct kinetis_dev_s *priv);
  #endif
  static void kinetis_eventtimeout(int argc, uint32_t arg);
  static void kinetis_endwait(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent);
  static void kinetis_endtransfer(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent);
  
  /* Interrupt Handling *******************************************************/
  
  static int  kinetis_interrupt(int irq, void *context);
  
  /* SDIO interface methods ***************************************************/
  
  /* Mutual exclusion */
  
  #ifdef CONFIG_SDIO_MUXBUS
  static int kinetis_lock(FAR struct sdio_dev_s *dev, bool lock);
  #endif
  
  /* Initialization/setup */
  
  static void kinetis_reset(FAR struct sdio_dev_s *dev);
  static uint8_t kinetis_status(FAR struct sdio_dev_s *dev);
  static void kinetis_widebus(FAR struct sdio_dev_s *dev, bool enable);
  #ifdef CONFIG_KINETIS_SDHC_ABSFREQ
  static void kinetis_frequency(FAR struct sdio_dev_s *dev, uint32_t frequency);
  #endif
  static void kinetis_clock(FAR struct sdio_dev_s *dev,
                enum sdio_clock_e rate);
  static int  kinetis_attach(FAR struct sdio_dev_s *dev);
  
  /* Command/Status/Data Transfer */
  
  static int  kinetis_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
                uint32_t arg);
  #ifndef CONFIG_SDIO_DMA
  static int  kinetis_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
                size_t nbytes);
  static int  kinetis_sendsetup(FAR struct sdio_dev_s *dev,
                FAR const uint8_t *buffer, uint32_t nbytes);
  #endif
  static int  kinetis_cancel(FAR struct sdio_dev_s *dev);
  
  static int  kinetis_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
  static int  kinetis_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
                uint32_t *rshort);
  static int  kinetis_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
                uint32_t rlong[4]);
  static int  kinetis_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
                uint32_t *rshort);
  static int  kinetis_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd,
                uint32_t *rnotimpl);
  
  /* EVENT handler */
  
  static void kinetis_waitenable(FAR struct sdio_dev_s *dev,
                sdio_eventset_t eventset);
  static sdio_eventset_t
              kinetis_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout);
  static void kinetis_callbackenable(FAR struct sdio_dev_s *dev,
                sdio_eventset_t eventset);
  static int  kinetis_registercallback(FAR struct sdio_dev_s *dev,
                worker_t callback, void *arg);
  
  /* DMA */
  
  #ifdef CONFIG_SDIO_DMA
  static bool kinetis_dmasupported(FAR struct sdio_dev_s *dev);
  static int  kinetis_dmarecvsetup(FAR struct sdio_dev_s *dev,
                FAR uint8_t *buffer, size_t buflen);
  static int  kinetis_dmasendsetup(FAR struct sdio_dev_s *dev,
                FAR const uint8_t *buffer, size_t buflen);
  #endif
  
  /* Initialization/uninitialization/reset ************************************/
  
  static void kinetis_callback(void *arg);
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  struct kinetis_dev_s g_sdhcdev =
  {
    .dev =
    {
  #ifdef CONFIG_SDIO_MUXBUS
      .lock             = kinetis_lock,
  #endif
      .reset            = kinetis_reset,
      .status           = kinetis_status,
      .widebus          = kinetis_widebus,
      .clock            = kinetis_clock,
      .attach           = kinetis_attach,
      .sendcmd          = kinetis_sendcmd,
  #ifndef CONFIG_SDIO_DMA
      .recvsetup        = kinetis_recvsetup,
      .sendsetup        = kinetis_sendsetup,
  #else
      .recvsetup        = kinetis_dmarecvsetup,
      .sendsetup        = kinetis_dmasendsetup,
  #endif
      .cancel           = kinetis_cancel,
      .waitresponse     = kinetis_waitresponse,
      .recvR1           = kinetis_recvshortcrc,
      .recvR2           = kinetis_recvlong,
      .recvR3           = kinetis_recvshort,
      .recvR4           = kinetis_recvnotimpl,
      .recvR5           = kinetis_recvnotimpl,
      .recvR6           = kinetis_recvshortcrc,
      .recvR7           = kinetis_recvshort,
      .waitenable       = kinetis_waitenable,
      .eventwait        = kinetis_eventwait,
      .callbackenable   = kinetis_callbackenable,
      .registercallback = kinetis_registercallback,
  #ifdef CONFIG_SDIO_DMA
      .dmasupported     = kinetis_dmasupported,
      .dmarecvsetup     = kinetis_dmarecvsetup,
      .dmasendsetup     = kinetis_dmasendsetup,
  #endif
    },
  };
  
  /* Register logging support */
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static struct kinetis_sdhcregs_s g_sampleregs[DEBUG_NSAMPLES];
  #endif
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Low-level Helpers
   ****************************************************************************/
  /****************************************************************************
   * Name: kinetis_takesem
   *
   * Description:
   *   Take the wait semaphore (handling false alarm wakeups due to the receipt
   *   of signals).
   *
   * Input Parameters:
   *   dev - Instance of the SDIO device driver state structure.
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  static void kinetis_takesem(struct kinetis_dev_s *priv)
  {
    /* Take the semaphore (perhaps waiting) */
  
    while (sem_wait(&priv->waitsem) != 0)
      {
        /* The only case that an error should occr here is if the wait was
         * awakened by a signal.
         */
  
        ASSERT(errno == EINTR);
      }
  }
  
  /****************************************************************************
   * Name: kinetis_configwaitints
   *
   * Description:
   *   Enable/disable SDIO interrupts needed to suport the wait function
   *
   * Input Parameters:
   *   priv       - A reference to the SDIO device state structure
   *   waitints   - The set of bits in the SDIO MASK register to set
   *   waitevents - Waited for events
   *   wkupevent  - Wake-up events
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  static void kinetis_configwaitints(struct kinetis_dev_s *priv, uint32_t waitints,
                                   sdio_eventset_t waitevents,
                                   sdio_eventset_t wkupevent)
  {
    irqstate_t flags;
  
    /* Save all of the data and set the new interrupt mask in one, atomic
     * operation.
     */
  
    flags = enter_critical_section();
    priv->waitevents = waitevents;
    priv->wkupevent  = wkupevent;
    priv->waitints   = waitints;
  #ifdef CONFIG_SDIO_DMA
    priv->xfrflags   = 0;
  #endif
    putreg32(priv->xfrints | priv->waitints | SDHC_INT_CINT,
             KINETIS_SDHC_IRQSIGEN);
    leave_critical_section(flags);
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_configxfrints(struct kinetis_dev_s *priv, uint32_t xfrints)
  {
    irqstate_t flags;
    flags = enter_critical_section();
    priv->xfrints = xfrints;
    putreg32(priv->xfrints | priv->waitints | SDHC_INT_CINT,
             KINETIS_SDHC_IRQSIGEN);
    leave_critical_section(flags);
  }
  
  /****************************************************************************
   * DMA Helpers
   ****************************************************************************/
  
  /****************************************************************************
   * Name: kinetis_sampleinit
   *
   * Description:
   *   Setup prior to collecting DMA samples
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_sampleinit(void)
  {
    memset(g_sampleregs, 0xff, DEBUG_NSAMPLES * sizeof(struct kinetis_sdhcregs_s));
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_sdhcsample
   *
   * Description:
   *   Sample SDIO registers
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_sdhcsample(struct kinetis_sdhcregs_s *regs)
  {
    regs->dsaddr    = getreg32(KINETIS_SDHC_DSADDR);    /* DMA System Address Register */
    regs->blkattr   = getreg32(KINETIS_SDHC_BLKATTR);   /* Block Attributes Register */
    regs->cmdarg    = getreg32(KINETIS_SDHC_CMDARG);    /* Command Argument Register */
    regs->xferty    = getreg32(KINETIS_SDHC_XFERTYP);   /* Transfer Type Register */
    regs->cmdrsp0   = getreg32(KINETIS_SDHC_CMDRSP0);   /* Command Response 0 */
    regs->cmdrsp1   = getreg32(KINETIS_SDHC_CMDRSP1);   /* Command Response 1 */
    regs->cmdrsp2   = getreg32(KINETIS_SDHC_CMDRSP2);   /* Command Response 2 */
    regs->cmdrsp3   = getreg32(KINETIS_SDHC_CMDRSP3);   /* Command Response 3 */
    regs->prsstat   = getreg32(KINETIS_SDHC_PRSSTAT);   /* Present State Register */
    regs->proctl    = getreg32(KINETIS_SDHC_PROCTL);    /* Protocol Control Register */
    regs->sysctl    = getreg32(KINETIS_SDHC_SYSCTL);    /* System Control Register */
    regs->irqstat   = getreg32(KINETIS_SDHC_IRQSTAT);   /* Interrupt Status Register */
    regs->irqstaten = getreg32(KINETIS_SDHC_IRQSTATEN); /* Interrupt Status Enable Register */
    regs->irqsigen  = getreg32(KINETIS_SDHC_IRQSIGEN);  /* Interrupt Signal Enable Register */
    regs->ac12err   = getreg32(KINETIS_SDHC_AC12ERR);   /* Auto CMD12 Error Status Register */
    regs->htcapblt  = getreg32(KINETIS_SDHC_HTCAPBLT);  /* Host Controller Capabilities */
    regs->wml       = getreg32(KINETIS_SDHC_WML);       /* Watermark Level Register */
    regs->admaes    = getreg32(KINETIS_SDHC_ADMAES);    /* ADMA Error Status Register */
    regs->adsaddr   = getreg32(KINETIS_SDHC_ADSADDR);   /* ADMA System Address Register */
    regs->vendor    = getreg32(KINETIS_SDHC_VENDOR);    /* Vendor Specific Register */
    regs->mmcboot   = getreg32(KINETIS_SDHC_MMCBOOT);   /* MMC Boot Register */
    regs->hostver   = getreg32(KINETIS_SDHC_HOSTVER);   /* Host Controller Version */
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_sample
   *
   * Description:
   *   Sample SDIO/DMA registers
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_sample(struct kinetis_dev_s *priv, int index)
  {
    kinetis_sdhcsample(&g_sampleregs[index]);
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_dumpsample
   *
   * Description:
   *   Dump one register sample
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_dumpsample(struct kinetis_dev_s *priv,
                                 struct kinetis_sdhcregs_s *regs, const char *msg)
  {
    mcinfo("SDHC Registers: %s\n", msg);
    mcinfo("   DSADDR[%08x]: %08x\n", KINETIS_SDHC_DSADDR,    regs->dsaddr);
    mcinfo("  BLKATTR[%08x]: %08x\n", KINETIS_SDHC_BLKATTR,   regs->blkattr);
    mcinfo("   CMDARG[%08x]: %08x\n", KINETIS_SDHC_CMDARG,    regs->cmdarg);
    mcinfo("   XFERTY[%08x]: %08x\n", KINETIS_SDHC_XFERTYP,   regs->xferty);
    mcinfo("  CMDRSP0[%08x]: %08x\n", KINETIS_SDHC_CMDRSP0,   regs->cmdrsp0);
    mcinfo("  CMDRSP1[%08x]: %08x\n", KINETIS_SDHC_CMDRSP1,   regs->cmdrsp1);
    mcinfo("  CMDRSP2[%08x]: %08x\n", KINETIS_SDHC_CMDRSP2,   regs->cmdrsp2);
    mcinfo("  CMDRSP3[%08x]: %08x\n", KINETIS_SDHC_CMDRSP3,   regs->cmdrsp3);
    mcinfo("  PRSSTAT[%08x]: %08x\n", KINETIS_SDHC_PRSSTAT,   regs->prsstat);
    mcinfo("   PROCTL[%08x]: %08x\n", KINETIS_SDHC_PROCTL,    regs->proctl);
    mcinfo("   SYSCTL[%08x]: %08x\n", KINETIS_SDHC_SYSCTL,    regs->sysctl);
    mcinfo("  IRQSTAT[%08x]: %08x\n", KINETIS_SDHC_IRQSTAT,   regs->irqstat);
    mcinfo("IRQSTATEN[%08x]: %08x\n", KINETIS_SDHC_IRQSTATEN, regs->irqstaten);
    mcinfo(" IRQSIGEN[%08x]: %08x\n", KINETIS_SDHC_IRQSIGEN,  regs->irqsigen);
    mcinfo("  AC12ERR[%08x]: %08x\n", KINETIS_SDHC_AC12ERR,   regs->ac12err);
    mcinfo(" HTCAPBLT[%08x]: %08x\n", KINETIS_SDHC_HTCAPBLT,  regs->htcapblt);
    mcinfo("      WML[%08x]: %08x\n", KINETIS_SDHC_WML,       regs->wml);
    mcinfo("   ADMAES[%08x]: %08x\n", KINETIS_SDHC_ADMAES,    regs->admaes);
    mcinfo("  ADSADDR[%08x]: %08x\n", KINETIS_SDHC_ADSADDR,   regs->adsaddr);
    mcinfo("   VENDOR[%08x]: %08x\n", KINETIS_SDHC_VENDOR,    regs->vendor);
    mcinfo("  MMCBOOT[%08x]: %08x\n", KINETIS_SDHC_MMCBOOT,   regs->mmcboot);
    mcinfo("  HOSTVER[%08x]: %08x\n", KINETIS_SDHC_HOSTVER,   regs->hostver);
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_dumpsamples
   *
   * Description:
   *   Dump all sampled register data
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void  kinetis_dumpsamples(struct kinetis_dev_s *priv)
  {
    kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP], "Before setup");
    kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP], "After setup");
    kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER], "End of transfer");
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_showregs
   *
   * Description:
   *   Dump the current state of all registers
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_XFRDEBUG
  static void kinetis_showregs(struct kinetis_dev_s *priv, const char *msg)
  {
    struct kinetis_sdhcregs_s regs;
  
    kinetis_sdhcsample(&regs);
    kinetis_dumpsample(priv, &regs, msg);
  }
  #endif
  
  /****************************************************************************
   * Data Transfer Helpers
   ****************************************************************************/
  
  /****************************************************************************
   * Name: kinetis_dataconfig
   *
   * Description:
   *   Configure the SDIO data path for the next data transfer
   *
   ****************************************************************************/
  
  static void kinetis_dataconfig(struct kinetis_dev_s *priv, bool bwrite,
                                 unsigned int blocksize, unsigned int nblocks,
                                 unsigned int timeout)
  {
    unsigned int watermark;
    uint32_t regval = 0;
  
    /* Set the data timeout value in the SDHC_SYSCTL field to the selected value */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval &= ~SDHC_SYSCTL_DVS_MASK;
    regval |= timeout << SDHC_SYSCTL_DVS_SHIFT;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
  
    /* Set the block size and count in the SDHC_BLKATTR register.  The block
     * size is only valid for multiple block transfers.
     */
  
    regval = blocksize << SDHC_BLKATTR_SIZE_SHIFT |
             nblocks   << SDHC_BLKATTR_CNT_SHIFT;
    putreg32(regval, KINETIS_SDHC_BLKATTR);
  
    /* Set the watermark level */
  
  #ifdef CONFIG_SDIO_DMA
    /* Set the Read Watermark Level to the blocksize to be read
     * (limited to half of the maximum watermark value).  BRR will be
     * set when the number of queued words is greater than or equal
     * to this value.
     */
  
    watermark = (blocksize + 3) >> 2;
    if (watermark > (SDHC_MAX_WATERMARK / 2))
      {
        watermark = (SDHC_MAX_WATERMARK / 2);
      }
  
    /* When the watermark level requirement is met in data transfer, and the
     * internal DMA is enabled, the data buffer block sends a DMA request to
     * the crossbar switch interface.
     */
  
    if (bwrite)
      {
        /* The SDHC will not start data transmission until the number of
         * words set in the WML register can be held in the buffer. If the
         * buffer is empty and the host system does not write data in time,
         * the SDHC will stop the SD_CLK to avoid the data buffer under-run
         * situation.
         */
  
        putreg32(watermark << SDHC_WML_WR_SHIFT, KINETIS_SDHC_WML);
      }
    else
      {
        /* The SDHC will not start data transmission until the number of
         * words set in the WML register are in the buffer. If the buffer
         * is full and the Host System does not read data in time, the
         * SDHC will stop the SDHC_DCLK to avoid the data buffer over-run
         * situation.
         */
  
        putreg32(watermark << SDHC_WML_RD_SHIFT, KINETIS_SDHC_WML);
      }
  #else
    if (bwrite)
      {
        /* Write Watermark Level = 0:  BWR will be set when the number of
         * queued words is less than or equal to 0.
         */
  
        putreg32(0, KINETIS_SDHC_WML);
      }
    else
      {
        /* Set the Read Watermark Level to the blocksize to be read
         * (limited to half of the maximum watermark value).  BRR will be
         * set when the number of queued words is greater than or equal
         * to this value.
         */
  
        watermark = (blocksize + 3) >> 2;
        if (watermark > (SDHC_MAX_WATERMARK / 2))
          {
            watermark = (SDHC_MAX_WATERMARK / 2);
          }
  
        putreg32(watermark << SDHC_WML_RD_SHIFT, KINETIS_SDHC_WML);
      }
  #endif
  }
  
  /****************************************************************************
   * Name: kinetis_datadisable
   *
   * Description:
   *   Disable the SDIO data path setup by kinetis_dataconfig() and
   *   disable DMA.
   *
   ****************************************************************************/
  
  static void kinetis_datadisable(void)
  {
    uint32_t regval;
  
    /* Set the data timeout value in the SDHC_SYSCTL field to the maximum value */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval &= ~SDHC_SYSCTL_DVS_MASK;
    regval |= SDHC_DVS_MAXTIMEOUT << SDHC_SYSCTL_DVS_SHIFT;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
  
    /* Set the block size to zero (no transfer) */
  
    putreg32(0, KINETIS_SDHC_BLKATTR);
  }
  
  /****************************************************************************
   * Name: kinetis_transmit
   *
   * Description:
   *   Send SDIO data in interrupt mode
   *
   * Input Parameters:
   *   priv - An instance of the SDIO device interface
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  #ifndef CONFIG_SDIO_DMA
  static void kinetis_transmit(struct kinetis_dev_s *priv)
  {
    union
    {
      uint32_t w;
      uint8_t  b[4];
    } data;
  
    /* Loop while there is more data to be sent, waiting for buffer write
     * ready (BWR)
     */
  
    mcinfo("Entry: remaining: %d IRQSTAT: %08x\n",
           priv->remaining, getreg32(KINETIS_SDHC_IRQSTAT));
  
    while (priv->remaining > 0 &&
           (getreg32(KINETIS_SDHC_IRQSTAT) & SDHC_INT_BWR) != 0)
      {
        /* Clear BWR.  If there is more data in the buffer, writing to the
         * buffer should reset BRR.
         */
  
        putreg32(SDHC_INT_BWR, KINETIS_SDHC_IRQSTAT);
  
        /* Is there a full word remaining in the user buffer? */
  
        if (priv->remaining >= sizeof(uint32_t))
          {
            /* Yes, transfer the word to the TX FIFO */
  
            data.w           = *priv->buffer++;
            priv->remaining -= sizeof(uint32_t);
          }
        else
          {
            /* No.. transfer just the bytes remaining in the user buffer,
             * padding with zero as necessary to extend to a full word.
             */
  
            uint8_t *ptr = (uint8_t *)priv->remaining;
            int i;
  
            data.w = 0;
            for (i = 0; i < priv->remaining; i++)
              {
                 data.b[i] = *ptr++;
              }
  
            /* Now the transfer is finished */
  
            priv->remaining = 0;
          }
  
        /* Put the word in the FIFO */
  
        putreg32(data.w, KINETIS_SDHC_DATPORT);
      }
  
    mcinfo("Exit: remaining: %d IRQSTAT: %08x\n",
           priv->remaining, getreg32(KINETIS_SDHC_IRQSTAT));
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_receive
   *
   * Description:
   *   Receive SDIO data in interrupt mode
   *
   * Input Parameters:
   *   priv - An instance of the SDIO device interface
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  #ifndef CONFIG_SDIO_DMA
  static void kinetis_receive(struct kinetis_dev_s *priv)
  {
    unsigned int watermark;
    union
    {
      uint32_t w;
      uint8_t  b[4];
    } data;
  
    /* Set the Read Watermark Level to 1:  BRR will be set when the number of
     * queued words is greater than or equal to 1.
     */
  
    putreg32(1 << SDHC_WML_RD_SHIFT, KINETIS_SDHC_WML);
  
    /* Loop while there is space to store the data, waiting for buffer read
     * ready (BRR)
     */
  
    mcinfo("Entry: remaining: %d IRQSTAT: %08x\n",
           priv->remaining, getreg32(KINETIS_SDHC_IRQSTAT));
  
    while (priv->remaining > 0 &&
           (getreg32(KINETIS_SDHC_IRQSTAT) & SDHC_INT_BRR) != 0)
      {
        /* Clear BRR.  If there is more data in the buffer, reading from the
         * buffer should reset BRR.
         */
  
        putreg32(SDHC_INT_BRR, KINETIS_SDHC_IRQSTAT);
  
        /* Read the next word from the RX buffer */
  
        data.w = getreg32(KINETIS_SDHC_DATPORT);
        if (priv->remaining >= sizeof(uint32_t))
          {
            /* Transfer the whole word to the user buffer */
  
            *priv->buffer++  = data.w;
            priv->remaining -= sizeof(uint32_t);
          }
        else
          {
            /* Transfer any trailing fractional word */
  
            uint8_t *ptr = (uint8_t *)priv->buffer;
            int i;
  
            for (i = 0; i < priv->remaining; i++)
              {
                 *ptr++ = data.b[i];
              }
  
            /* Now the transfer is finished */
  
            priv->remaining = 0;
          }
      }
  
    /* Set the Read Watermark Level either the number of remaining words to be
     * read (limited to half of the maximum watermark value)
     */
  
    watermark = ((priv->remaining + 3) >> 2);
    if (watermark > (SDHC_MAX_WATERMARK / 2))
      {
        watermark = (SDHC_MAX_WATERMARK / 2);
      }
  
    putreg32(watermark << SDHC_WML_RD_SHIFT, KINETIS_SDHC_WML);
  
    mcinfo("Exit: remaining: %d IRQSTAT: %08x WML: %08x\n",
           priv->remaining, getreg32(KINETIS_SDHC_IRQSTAT),
           getreg32(KINETIS_SDHC_WML));
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_eventtimeout
   *
   * Description:
   *   The watchdog timeout setup when the event wait start has expired without
   *   any other waited-for event occurring.
   *
   * Input Parameters:
   *   argc   - The number of arguments (should be 1)
   *   arg    - The argument (state structure reference cast to uint32_t)
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Always called from the interrupt level with interrupts disabled.
   *
   ****************************************************************************/
  
  static void kinetis_eventtimeout(int argc, uint32_t arg)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)arg;
  
    DEBUGASSERT(argc == 1 && priv != NULL);
    DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0);
  
    /* Is a data transfer complete event expected? */
  
    if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
      {
        /* Yes.. Sample registers at the time of the timeout */
  
        kinetis_sample(priv, SAMPLENDX_END_TRANSFER);
  
        /* Wake up any waiting threads */
  
        kinetis_endwait(priv, SDIOWAIT_TIMEOUT);
        mcerr("ERROR: Timeout: remaining: %d\n", priv->remaining);
      }
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_endwait(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent)
  {
    /* Cancel the watchdog timeout */
  
    (void)wd_cancel(priv->waitwdog);
  
    /* Disable event-related interrupts */
  
    kinetis_configwaitints(priv, 0, 0, wkupevent);
  
    /* Wake up the waiting thread */
  
    kinetis_givesem(priv);
  }
  
  /****************************************************************************
   * Name: kinetis_endtransfer
   *
   * Description:
   *   Terminate a transfer with the provided status.  This function is called
   *   only from the SDIO interrupt handler when end-of-transfer conditions
   *   are detected.
   *
   * Input Parameters:
   *   priv   - An instance of the SDIO device interface
   *   wkupevent - The event that caused the transfer to end
   *
   * Returned Value:
   *   None
   *
   * Assumptions:
   *   Always called from the interrupt level with interrupts disabled.
   *
   ****************************************************************************/
  
  static void kinetis_endtransfer(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent)
  {
  #ifdef CONFIG_SDIO_DMA
    uint32_t regval;
  #endif
  
    /* Disable all transfer related interrupts */
  
    kinetis_configxfrints(priv, 0);
  
    /* Clearing pending interrupt status on all transfer related interrupts */
  
    putreg32(SDHC_XFRDONE_INTS, KINETIS_SDHC_IRQSTAT);
  
    /* If this was a DMA transfer, make sure that DMA is stopped */
  
  #ifdef CONFIG_SDIO_DMA
    /* Stop the DMA by resetting the data path */
  
    regval = getreg32(KINETIS_SDHC_SYSCTL);
    regval |= SDHC_SYSCTL_RSTD;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
  #endif
  
    /* Mark the transfer finished */
  
    priv->remaining = 0;
  
    /* Debug instrumentation */
  
    kinetis_sample(priv, SAMPLENDX_END_TRANSFER);
  
    /* Is a thread wait for these data transfer complete events? */
  
    if ((priv->waitevents & wkupevent) != 0)
      {
        /* Yes.. wake up any waiting threads */
  
        kinetis_endwait(priv, wkupevent);
      }
  }
  
  /****************************************************************************
   * Interrupt Handling
   ****************************************************************************/
  
  /****************************************************************************
   * Name: kinetis_interrupt
   *
   * Description:
   *   SDIO interrupt handler
   *
   * Input Parameters:
   *   dev - An instance of the SDIO device interface
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  static int kinetis_interrupt(int irq, void *context)
  {
    struct kinetis_dev_s *priv = &g_sdhcdev;
    uint32_t enabled;
    uint32_t pending;
    uint32_t regval;
  
    /* Check the SDHC IRQSTAT register.  Mask out all bits that don't
     * correspond to enabled interrupts.  (This depends on the fact that bits
     * are ordered the same in both the IRQSTAT and IRQSIGEN registers).  If
     * there are non-zero bits remaining, then we have work to do here.
     */
  
    regval  = getreg32(KINETIS_SDHC_IRQSIGEN);
    enabled = getreg32(KINETIS_SDHC_IRQSTAT) & regval;
    mcinfo("IRQSTAT: %08x IRQSIGEN %08x enabled: %08x\n",
           getreg32(KINETIS_SDHC_IRQSTAT), regval, enabled);
  
    /* Disable card interrupts to clear the card interrupt to the host system. */
  
    regval &= ~SDHC_INT_CINT;
    putreg32(regval, KINETIS_SDHC_IRQSIGEN);
  
    /* Clear all pending interrupts */
  
    putreg32(enabled, KINETIS_SDHC_IRQSTAT);
  
    /* Handle in progress, interrupt driven data transfers ********************/
  
    pending  = enabled & priv->xfrints;
    if (pending != 0)
      {
  #ifndef CONFIG_SDIO_DMA
        /* Is the RX buffer read ready?  Is so then we must be processing a
         * non-DMA receive transaction.
         */
  
        if ((pending & SDHC_INT_BRR) != 0)
          {
            /* Receive data from the RX buffer */
  
            kinetis_receive(priv);
          }
  
        /* Otherwise, Is the TX buffer write ready? If so we must
         * be processing a non-DMA send transaction.  NOTE:  We can't be
         * processing both!
         */
  
        else if ((pending & SDHC_INT_BWR) != 0)
          {
            /* Send data via the TX FIFO */
  
            kinetis_transmit(priv);
          }
  #endif
  
        /* Handle transfer complete events */
  
        if ((pending & SDHC_INT_TC) != 0)
          {
            /* Terminate the transfer */
  
            kinetis_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
          }
  
        /* Handle data block send/receive CRC failure */
  
        else if ((pending & SDHC_INT_DCE) != 0)
          {
            /* Terminate the transfer with an error */
  
            mcerr("ERROR: Data block CRC failure, remaining: %d\n", priv->remaining);
            kinetis_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
          }
  
        /* Handle data timeout error */
  
        else if ((pending & SDHC_INT_DTOE) != 0)
          {
            /* Terminate the transfer with an error */
  
            mcerr("ERROR: Data timeout, remaining: %d\n", priv->remaining);
            kinetis_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT);
          }
      }
  
    /* Handle wait events *****************************************************/
  
    pending  = enabled & priv->waitints;
    if (pending != 0)
      {
        /* Is this a response completion event? */
  
        if ((pending & SDHC_RESPDONE_INTS) != 0)
          {
            /* Yes.. Is their a thread waiting for response done? */
  
            if ((priv->waitevents & (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
              {
                /* Yes.. mask further interrupts and wake the thread up */
  
                regval = getreg32(KINETIS_SDHC_IRQSIGEN);
                regval &= ~SDHC_RESPDONE_INTS;
                putreg32(regval, KINETIS_SDHC_IRQSIGEN);
  
                kinetis_endwait(priv, SDIOWAIT_RESPONSEDONE);
              }
          }
      }
  
    /* Re-enable card interrupts */
  
    regval  = getreg32(KINETIS_SDHC_IRQSIGEN);
    regval |= SDHC_INT_CINT;
    putreg32(regval, KINETIS_SDHC_IRQSIGEN);
  
    return OK;
  }
  
  /****************************************************************************
   * SDIO Interface Methods
   ****************************************************************************/
  
  /****************************************************************************
   * Name: kinetis_lock
   *
   * Description:
   *   Locks the bus. Function calls low-level multiplexed bus routines to
   *   resolve bus requests and acknowledgement issues.
   *
   * Input Parameters:
   *   dev    - An instance of the SDIO device interface
   *   lock   - TRUE to lock, FALSE to unlock.
   *
   * Returned Value:
   *   OK on success; a negated errno on failure
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_MUXBUS
  static int kinetis_lock(FAR struct sdio_dev_s *dev, bool lock)
  {
    /* Single SDIO instance so there is only one possibility.  The multiplex
     * bus is part of board support package.
     */
  
    kinetis_muxbus_sdio_lock(lock);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_reset(FAR struct sdio_dev_s *dev)
  {
    FAR struct kinetis_dev_s *priv = (FAR struct kinetis_dev_s *)dev;
    uint32_t regval;
  
    /* Disable all interrupts so that nothing interferes with the following. */
  
    putreg32(0, KINETIS_SDHC_IRQSIGEN);
  
    /* Reset the SDHC block, putting registers in their default, reset state.
     * Initiate the reset by setting the RSTA bit in the SYSCTL register.
     */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval |= SDHC_SYSCTL_RSTA;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
  
    /* The SDHC will reset the RSTA bit to 0 when the capabilities
     * registers are valid and the host driver can read them.
     */
  
    while ((getreg32(KINETIS_SDHC_SYSCTL) & SDHC_SYSCTL_RSTA) != 0);
  
    /* Make sure that all clocking is disabled */
  
    kinetis_clock(dev, CLOCK_SDIO_DISABLED);
  
    /* Enable all status bits (these could not all be potential sources of
     * interrupts.
     */
  
    putreg32(SDHC_INT_ALL, KINETIS_SDHC_IRQSTATEN);
  
    mcinfo("SYSCTL: %08x PRSSTAT: %08x IRQSTATEN: %08x\n",
           getreg32(KINETIS_SDHC_SYSCTL), getreg32(KINETIS_SDHC_PRSSTAT),
           getreg32(KINETIS_SDHC_IRQSTATEN));
  
    /* The next phase of the hardware reset would be to set the SYSCTRL INITA
     * bit to send 80 clock ticks for card to power up and then reset the card
     * with CMD0.  This is done elsewhere.
     */
  
    /* Reset state data */
  
    priv->waitevents = 0;      /* Set of events to be waited for */
    priv->waitints   = 0;      /* Interrupt enables for event waiting */
    priv->wkupevent  = 0;      /* The event that caused the wakeup */
  #ifdef CONFIG_SDIO_DMA
    priv->xfrflags   = 0;      /* Used to synchronize SDIO and DMA completion events */
  #endif
  
    wd_cancel(priv->waitwdog); /* Cancel any timeouts */
  
    /* Interrupt mode data transfer support */
  
    priv->buffer     = 0;      /* Address of current R/W buffer */
    priv->remaining  = 0;      /* Number of bytes remaining in the transfer */
    priv->xfrints    = 0;      /* Interrupt enables for data transfer */
  }
  
  /****************************************************************************
   * Name: kinetis_status
   *
   * Description:
   *   Get SDIO status.
   *
   * Input Parameters:
   *   dev   - Device-specific state data
   *
   * Returned Value:
   *   Returns a bitset of status values (see kinetis_status_* defines)
   *
   ****************************************************************************/
  
  static uint8_t kinetis_status(FAR struct sdio_dev_s *dev)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
    return priv->cdstatus;
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_widebus(FAR struct sdio_dev_s *dev, bool wide)
  {
    uint32_t regval;
  
    /* Set the Data Transfer Width (DTW) field in the PROCTL register */
  
    regval = getreg32(KINETIS_SDHC_PROCTL);
    regval &= ~SDHC_PROCTL_DTW_MASK;
    if (wide)
      {
        regval |= SDHC_PROCTL_DTW_4BIT;
      }
    else
      {
        regval |= SDHC_PROCTL_DTW_1BIT;
      }
    putreg32(regval, KINETIS_SDHC_PROCTL);
  }
  
  /****************************************************************************
   * Name: kinetis_frequency
   *
   * Description:
   *   Set the SD clock frequency
   *
   * Input Parameters:
   *   dev       - An instance of the SDIO device interface
   *   frequency - The frequency to use
   *
   * Returned Value:
   *   None
   *
   ****************************************************************************/
  
  #ifdef CONFIG_KINETIS_SDHC_ABSFREQ
  static void kinetis_frequency(FAR struct sdio_dev_s *dev, uint32_t frequency)
  {
    uint32_t sdclkfs;
    uint32_t prescaled;
    uint32_t regval;
    unsigned int prescaler;
    unsigned int divisor;
  
    /* The SDCLK frequency is determined by (1) the frequency of the base clock
     * that was selected as the input clock, and (2) by a prescaler and a
     * divisor that are selected here:
     *
     * SDCLK  frequency = (base clock) / (prescaler * divisor)
     *
     * The prescaler is avalable only for the values:  2, 4, 8, 16, 32, 64, 128,
     * and 256.  Pick the smallest value of SDCLKFS that would result in an
     * in-range frequency.
     *
     * For example, if the base clock frequency is 96 MHz, and the target
     * frequency is 25 MHz, the following logic will select prescaler.
     *
     *   96MHz / 2  <= 25MHz <= 96MHz / 2 /16       -- YES, prescaler == 2
     *
     * If the target frequency is 400 kHz, the following logic will select
     * prescaler:
     *
     *   96MHz / 2  <= 400KHz <= 96MHz / 2 / 16     -- NO
     *   96MHz / 4  <= 400KHz <= 96MHz / 4 / 16     -- NO
     *   96MHz / 8  <= 400KHz <= 96MHz / 8 / 16     -- NO
     *   96MHz / 16 <= 400KHz <= 96MHz / 16 / 16    -- YES, prescaler == 16
     */
  
    if (/* frequency >= (BOARD_CORECLK_FREQ / 2) && */
        frequency <= (BOARD_CORECLK_FREQ / 2 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV2;
        prescaler = 2;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 4) &&
             frequency <= (BOARD_CORECLK_FREQ / 4 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV4;
        prescaler = 4;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 8) &&
             frequency <= (BOARD_CORECLK_FREQ / 8 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV8;
        prescaler = 8;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 16) &&
             frequency <= (BOARD_CORECLK_FREQ / 16 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV16;
        prescaler = 16;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 32) &&
             frequency <= (BOARD_CORECLK_FREQ / 32 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV32;
        prescaler = 32;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 64) &&
             frequency <= (BOARD_CORECLK_FREQ / 64 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV64;
        prescaler = 64;
      }
    else if (frequency >= (BOARD_CORECLK_FREQ / 128) &&
             frequency <= (BOARD_CORECLK_FREQ / 128 / 16))
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV128;
        prescaler = 128;
      }
    else /* if (frequency >= (BOARD_CORECLK_FREQ / 256) &&
          *     frequency <= (BOARD_CORECLK_FREQ / 256 / 16)) */
      {
        sdclkfs   = SDHC_SYSCTL_SDCLKFS_DIV256;
        prescaler = 256;
      }
  
    /* The optimal divider can than be calculated.
     *
     * For example, if the base clock frequency is 96 MHz, the target
     * frequency is 25 MHz, and the selected prescaler value is 2, then
     *
     *   prescaled = 96MHz / 2 = 48MHz
     *   divisor   = (48MHz + 12.5HMz/ 25MHz = 2
     *
     * And the resulting frequency will be 24MHz.
     *
     * Or, for example, if the target frequency is 400 kHz and the selected
     * prescaler is 16, the following* logic will select prescaler:
     *
     *   prescaled = 96MHz / 16 = 6MHz
     *   divisor   = (6MHz + 200KHz) / 400KHz = 15
     *
     * And the restuling frequency will be exactly 400KHz.
     */
  
    prescaled = frequency / prescaler;
    divisor   = (prescaled + (frequency >> 1)) / frequency;
  
    /* Set the new divisor information and enable all clocks in the SYSCTRL
     * register.
     *
     * TODO:  Investigate using the automatically gated clocks to reduce power
     *        consumption.
     */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval &= ~(SDHC_SYSCTL_SDCLKFS_MASK | SDHC_SYSCTL_DVS_MASK);
    regval |= (sdclkfs | SDHC_SYSCTL_DVS_DIV(divisor));
    regval |= (SDHC_SYSCTL_SDCLKEN | SDHC_SYSCTL_PEREN | SDHC_SYSCTL_HCKEN |
               SDHC_SYSCTL_IPGEN);
    putreg32(regval, KINETIS_SDHC_SYSCTL);
    mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_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
   *
   ****************************************************************************/
  
  #ifdef CONFIG_KINETIS_SDHC_ABSFREQ
  static void kinetis_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
  {
    uint32_t frequency;
    uint32_t regval;
  
    /* The SDCLK must be disabled before its frequency can be changed: "SDCLK
     * frequency can be changed when this bit is 0. Then, the host controller
     * shall maintain the same clock frequency until SDCLK is stopped (stop at
     * SDCLK = 0).
     */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval &= ~SDHC_SYSCTL_SDCLKEN;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
    mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
  
    switch (rate)
      {
        default:
        case CLOCK_SDIO_DISABLED :     /* Clock is disabled */
          {
            /* Clear the prescaler and divisor settings and other clock
             * enables as well.
             */
  
            regval &= ~(SDHC_SYSCTL_IPGEN | SDHC_SYSCTL_HCKEN | SDHC_SYSCTL_PEREN |
                        SDHC_SYSCTL_SDCLKFS_MASK | SDHC_SYSCTL_DVS_MASK);
            putreg32(regval, KINETIS_SDHC_SYSCTL);
            mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
            return;
          }
  
        case CLOCK_IDMODE :            /* Initial ID mode clocking (<400KHz) */
          frequency = CONFIG_KINETIS_IDMODE_FREQ;
          break;
  
        case CLOCK_MMC_TRANSFER :      /* MMC normal operation clocking */
          frequency = CONFIG_KINETIS_MMCXFR_FREQ;
          break;
  
        case CLOCK_SD_TRANSFER_1BIT :  /* SD normal operation clocking (narrow 1-bit mode) */
  #ifndef CONFIG_SDIO_WIDTH_D1_ONLY
          frequency = CONFIG_KINETIS_SD1BIT_FREQ;
          break;
  #endif
  
        case CLOCK_SD_TRANSFER_4BIT :  /* SD normal operation clocking (wide 4-bit mode) */
          frequency = CONFIG_KINETIS_SD4BIT_FREQ;
          break;
      }
  
    /* Then set the selected frequency */
  
    kinetis_frequency(dev, frequency);
  }
  #else
  static void kinetis_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
  {
    uint32_t regval;
  
    /* The SDCLK must be disabled before its frequency can be changed: "SDCLK
     * frequency can be changed when this bit is 0. Then, the host controller
     * shall maintain the same clock frequency until SDCLK is stopped (stop at
     * SDCLK = 0).
     */
  
    regval  = getreg32(KINETIS_SDHC_SYSCTL);
    regval &= ~SDHC_SYSCTL_SDCLKEN;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
    mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
  
    /* Clear the old prescaler and divisor values so that new ones can be ORed
     * in.
     */
  
    regval &= ~(SDHC_SYSCTL_SDCLKFS_MASK | SDHC_SYSCTL_DVS_MASK);
  
    /* Select the new prescaler and divisor values based on the requested mode
     * and the settings from the board.h file.
     *
     * TODO:  Investigate using the automatically gated clocks to reduce power
     *        consumption.
     */
  
    switch (rate)
      {
        default:
        case CLOCK_SDIO_DISABLED :     /* Clock is disabled */
          {
            /* Clear the prescaler and divisor settings and other clock
             * enables as well.
             */
  
            regval &= ~(SDHC_SYSCTL_IPGEN | SDHC_SYSCTL_HCKEN | SDHC_SYSCTL_PEREN);
            putreg32(regval, KINETIS_SDHC_SYSCTL);
            mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
            return;
          }
  
        case CLOCK_IDMODE :            /* Initial ID mode clocking (<400KHz) */
          regval |= (BOARD_SDHC_IDMODE_PRESCALER | BOARD_SDHC_IDMODE_DIVISOR |
                     SDHC_SYSCTL_SDCLKEN | SDHC_SYSCTL_PEREN | SDHC_SYSCTL_HCKEN |
                     SDHC_SYSCTL_IPGEN);
          break;
  
        case CLOCK_MMC_TRANSFER :      /* MMC normal operation clocking */
          regval |= (BOARD_SDHC_MMCMODE_PRESCALER | BOARD_SDHC_MMCMODE_DIVISOR |
                     SDHC_SYSCTL_SDCLKEN | SDHC_SYSCTL_PEREN | SDHC_SYSCTL_HCKEN |
                     SDHC_SYSCTL_IPGEN);
          break;
  
        case CLOCK_SD_TRANSFER_1BIT :  /* SD normal operation clocking (narrow
                                        * 1-bit mode) */
  #ifndef CONFIG_SDIO_WIDTH_D1_ONLY
          regval |= (BOARD_SDHC_SD1MODE_PRESCALER | BOARD_SDHC_IDMODE_DIVISOR |
                     SDHC_SYSCTL_SDCLKEN | SDHC_SYSCTL_PEREN | SDHC_SYSCTL_HCKEN |
                     SDHC_SYSCTL_IPGEN);
          break;
  #endif
  
        case CLOCK_SD_TRANSFER_4BIT :  /* SD normal operation clocking (wide
                                        * 4-bit mode) */
          regval |= (BOARD_SDHC_SD4MODE_PRESCALER | BOARD_SDHC_SD4MODE_DIVISOR |
                     SDHC_SYSCTL_SDCLKEN | SDHC_SYSCTL_PEREN | SDHC_SYSCTL_HCKEN |
                     SDHC_SYSCTL_IPGEN);
          break;
      }
  
    putreg32(regval, KINETIS_SDHC_SYSCTL);
    mcinfo("SYSCTRL: %08x\n", getreg32(KINETIS_SDHC_SYSCTL));
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_attach(FAR struct sdio_dev_s *dev)
  {
    int ret;
  
    /* Attach the SDIO interrupt handler */
  
    ret = irq_attach(KINETIS_IRQ_SDHC, kinetis_interrupt);
    if (ret == OK)
      {
  
        /* Disable all interrupts at the SDIO controller and clear all pending
         * interrupts.
         */
  
        putreg32(0,            KINETIS_SDHC_IRQSIGEN);
        putreg32(SDHC_INT_ALL, KINETIS_SDHC_IRQSTAT);
  
  #ifdef CONFIG_ARCH_IRQPRIO
        /* Set the interrupt priority */
  
        up_prioritize_irq(KINETIS_IRQ_SDHC, CONFIG_KINETIS_SDHC_PRIO);
  #endif
  
        /* Enable SDIO interrupts at the NVIC.  They can now be enabled at
         * the SDIO controller as needed.
         */
  
        up_enable_irq(KINETIS_IRQ_SDHC);
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg)
  {
    uint32_t regval;
    uint32_t cmdidx;
    int32_t  timeout;
  
    /* Initialize the command index */
  
    cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
    regval = cmdidx << SDHC_XFERTYP_CMDINX_SHIFT;
  
    /* Does a data transfer accompany the command? */
  
    if ((cmd & MMCSD_DATAXFR) != 0)
      {
        /* Yes.. Configure the data transfer */
  
        switch (cmd & MMCSD_DATAXFR_MASK)
          {
            default:
            case MMCSD_NODATAXFR : /* No.. no data transfer */
              break;
  
            /* The following two cases are probably missing some setup logic */
  
            case MMCSD_RDSTREAM :  /* Yes.. streaming read data transfer */
              regval |= (SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DTDSEL);
              break;
  
            case MMCSD_WRSTREAM :  /* Yes.. streaming write data transfer */
              regval |= SDHC_XFERTYP_DPSEL;
              break;
  
            case MMCSD_RDDATAXFR : /* Yes.. normal read data transfer */
              regval |= (SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DTDSEL);
              break;
  
            case MMCSD_WRDATAXFR : /* Yes.. normal write data transfer */
              regval |= SDHC_XFERTYP_DPSEL;
              break;
          }
  
        /* Is it a multi-block transfer? */
  
        if ((cmd & MMCSD_MULTIBLOCK) != 0)
          {
            /* Yes.. should the transfer be stopped with ACMD12? */
  
            if ((cmd & MMCSD_STOPXFR) != 0)
              {
                /* Yes.. Indefinite block transfer */
  
                regval |= (SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_AC12EN);
              }
            else
              {
                /* No.. Fixed block transfer */
  
                regval |= (SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_BCEN);
              }
          }
      }
  
    /* Configure response type bits */
  
    switch (cmd & MMCSD_RESPONSE_MASK)
      {
      case MMCSD_NO_RESPONSE:                /* No response */
        regval |= SDHC_XFERTYP_RSPTYP_NONE;
        break;
  
      case MMCSD_R1B_RESPONSE:              /* Response length 48, check busy & cmdindex */
        regval |= (SDHC_XFERTYP_RSPTYP_LEN48BSY | SDHC_XFERTYP_CICEN |
                   SDHC_XFERTYP_CCCEN);
        break;
  
      case MMCSD_R1_RESPONSE:              /* Response length 48, check cmdindex */
      case MMCSD_R5_RESPONSE:
      case MMCSD_R6_RESPONSE:
        regval |= (SDHC_XFERTYP_RSPTYP_LEN48 | SDHC_XFERTYP_CICEN |
                   SDHC_XFERTYP_CCCEN);
        break;
  
      case MMCSD_R2_RESPONSE:              /* Response length 136, check CRC */
        regval |= (SDHC_XFERTYP_RSPTYP_LEN136 | SDHC_XFERTYP_CCCEN);
        break;
  
      case MMCSD_R3_RESPONSE:              /* Response length 48 */
      case MMCSD_R4_RESPONSE:
      case MMCSD_R7_RESPONSE:
        regval |= SDHC_XFERTYP_RSPTYP_LEN48;
        break;
      }
  
    /* Enable DMA */
  
  #ifdef CONFIG_SDIO_DMA
    /* Internal DMA is used */
  
    regval |= SDHC_XFERTYP_DMAEN;
  #endif
  
    /* Other bits? What about CMDTYP? */
  
    mcinfo("cmd: %08x arg: %08x regval: %08x\n", cmd, arg, regval);
  
    /* The Command Inhibit (CIHB) bit is set in the PRSSTAT bit immediately
     * after the transfer type register is written.  This bit is cleared when
     * the command response is received.  If this status bit is 0, it
     * indicates that the CMD line is not in use and the SDHC can issue a
     * SD/MMC Command using the CMD line.
     *
     * CIHB should always be set when this function is called.
     */
  
    timeout = SDHC_CMDTIMEOUT;
    while ((getreg32(KINETIS_SDHC_PRSSTAT) & SDHC_PRSSTAT_CIHB) != 0)
      {
        if (--timeout <= 0)
          {
            mcerr("ERROR: Timeout cmd: %08x PRSSTAT: %08x\n",
                  cmd, getreg32(KINETIS_SDHC_PRSSTAT));
  
            return -EBUSY;
          }
      }
  
    /* Set the SDHC Argument value */
  
    putreg32(arg, KINETIS_SDHC_CMDARG);
  
    /* Clear interrupt status and write the SDHC CMD */
  
    putreg32(SDHC_RESPDONE_INTS, KINETIS_SDHC_IRQSTAT);
    putreg32(regval, KINETIS_SDHC_XFERTYP);
    return OK;
  }
  
  /****************************************************************************
   * Name: kinetis_recvsetup
   *
   * Description:
   *   Setup hardware in preparation for data transfer from the card in non-DMA
   *   (interrupt driven mode).  This method will do whatever controller setup
   *   is necessary.  This would be called for SD memory just BEFORE sending
   *   CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18
   *   (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc.  Normally, SDIO_WAITEVENT
   *   will be called to receive the indication that the transfer is complete.
   *
   * Input Parameters:
   *   dev    - An instance of the SDIO device interface
   *   buffer - Address of the buffer in which to receive the data
   *   nbytes - The number of bytes in the transfer
   *
   * Returned Value:
   *   Number of bytes sent on success; a negated errno on failure
   *
   ****************************************************************************/
  
  #ifndef CONFIG_SDIO_DMA
  static int kinetis_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
                               size_t nbytes)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  
    DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
    DEBUGASSERT(((uint32_t)buffer & 3) == 0);
  
    /* Reset the DPSM configuration */
  
    kinetis_datadisable();
    kinetis_sampleinit();
    kinetis_sample(priv, SAMPLENDX_BEFORE_SETUP);
  
    /* Save the destination buffer information for use by the interrupt handler */
  
    priv->buffer    = (uint32_t *)buffer;
    priv->remaining = nbytes;
  
    /* Then set up the SDIO data path */
  
    kinetis_dataconfig(priv, false, nbytes, 1, SDHC_DVS_DATATIMEOUT);
  
    /* And enable interrupts */
  
    kinetis_configxfrints(priv, SDHC_RCVDONE_INTS);
    kinetis_sample(priv, SAMPLENDX_AFTER_SETUP);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_sendsetup
   *
   * Description:
   *   Setup hardware in preparation for data transfer from the card.  This method
   *   will do whatever controller setup is necessary.  This would be called
   *   for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25
   *   (WRITE_MULTIPLE_BLOCK), ... and before SDIO_SENDDATA is called.
   *
   * Input Parameters:
   *   dev    - An instance of the SDIO device interface
   *   buffer - Address of the buffer containing the data to send
   *   nbytes - The number of bytes in the transfer
   *
   * Returned Value:
   *   Number of bytes sent on success; a negated errno on failure
   *
   ****************************************************************************/
  
  #ifndef CONFIG_SDIO_DMA
  static int kinetis_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
                             size_t nbytes)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  
    DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
    DEBUGASSERT(((uint32_t)buffer & 3) == 0);
  
    /* Reset the DPSM configuration */
  
    kinetis_datadisable();
    kinetis_sampleinit();
    kinetis_sample(priv, SAMPLENDX_BEFORE_SETUP);
  
    /* Save the source buffer information for use by the interrupt handler */
  
    priv->buffer    = (uint32_t *)buffer;
    priv->remaining = nbytes;
  
    /* Then set up the SDIO data path */
  
    kinetis_dataconfig(priv, true, nbytes, 1, SDHC_DVS_DATATIMEOUT);
  
    /* Enable TX interrupts */
  
    kinetis_configxfrints(priv, SDHC_SNDDONE_INTS);
    kinetis_sample(priv, SAMPLENDX_AFTER_SETUP);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_cancel(FAR struct sdio_dev_s *dev)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  #ifdef CONFIG_SDIO_DMA
    uint32_t regval;
  #endif
  
    /* Disable all transfer- and event- related interrupts */
  
    kinetis_configxfrints(priv, 0);
    kinetis_configwaitints(priv, 0, 0, 0);
  
    /* Clearing pending interrupt status on all transfer- and event- related
     * interrupts
     */
  
    putreg32(SDHC_WAITALL_INTS, KINETIS_SDHC_IRQSTAT);
  
    /* Cancel any watchdog timeout */
  
    (void)wd_cancel(priv->waitwdog);
  
    /* If this was a DMA transfer, make sure that DMA is stopped */
  
  #ifdef CONFIG_SDIO_DMA
    /* Stop the DMA by resetting the data path */
  
    regval = getreg32(KINETIS_SDHC_SYSCTL);
    regval |= SDHC_SYSCTL_RSTD;
    putreg32(regval, KINETIS_SDHC_SYSCTL);
  #endif
  
    /* Mark no transfer in progress */
  
    priv->remaining = 0;
    return OK;
  }
  
  /****************************************************************************
   * Name: kinetis_waitresponse
   *
   * Description:
   *   Poll-wait for the response to the last command to be ready.  This
   *   function should be called even after sending commands that have no
   *   response (such as CMD0) to make sure that the hardware is ready to
   *   receive the next command.
   *
   * 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 kinetis_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
  {
    uint32_t errors;
    int32_t  timeout;
    int      ret = OK;
  
    switch (cmd & MMCSD_RESPONSE_MASK)
      {
      case MMCSD_NO_RESPONSE:
        timeout = SDHC_CMDTIMEOUT;
        errors  = 0;
        return OK;
  
      case MMCSD_R1_RESPONSE:
      case MMCSD_R1B_RESPONSE:
      case MMCSD_R2_RESPONSE:
      case MMCSD_R6_RESPONSE:
        timeout = SDHC_LONGTIMEOUT;
        errors  = SDHC_RESPERR_INTS;
        break;
  
      case MMCSD_R4_RESPONSE:
      case MMCSD_R5_RESPONSE:
        return -ENOSYS;
  
      case MMCSD_R3_RESPONSE:
      case MMCSD_R7_RESPONSE:
        timeout = SDHC_CMDTIMEOUT;
        errors  = SDHC_RESPERR_INTS;
        break;
  
      default:
        return -EINVAL;
      }
  
    /* Then wait for the Command Complete (CC) indication (or timeout).  The
     * CC bit is set when the end bit of the command response is received
     * (except Auto CMD12).
     */
  
    while ((getreg32(KINETIS_SDHC_IRQSTAT) & SDHC_INT_CC) == 0)
      {
        if (--timeout <= 0)
          {
            mcerr("ERROR: Timeout cmd: %08x IRQSTAT: %08x\n",
                  cmd, getreg32(KINETIS_SDHC_IRQSTAT));
  
            return -ETIMEDOUT;
          }
      }
  
    /* Check for hardware detected errors */
  
    if ((getreg32(KINETIS_SDHC_IRQSTAT) & errors) != 0)
      {
        mcerr("ERROR: cmd: %08x errors: %08x IRQSTAT: %08x\n",
              cmd, errors, getreg32(KINETIS_SDHC_IRQSTAT));
        ret = -EIO;
      }
  
    /* Clear the response wait status bits */
  
    putreg32(SDHC_RESPDONE_INTS, KINETIS_SDHC_IRQSTAT);
    return ret;
  }
  
  /****************************************************************************
   * Name: kinetis_recvRx
   *
   * Description:
   *   Receive response to SDIO command.  Only the critical payload is
   *   returned -- that is 32 bits for 48 bit status and 128 bits for 136 bit
   *   status.  The driver implementation should verify the correctness of
   *   the remaining, non-returned bits (CRCs, CMD index, etc.).
   *
   * Input Parameters:
   *   dev    - An instance of the SDIO device interface
   *   Rx - Buffer in which to receive the response
   *
   * Returned Value:
   *   Number of bytes sent on success; a negated errno on failure.  Here a
   *   failure means only a faiure to obtain the requested reponse (due to
   *   transport problem -- timeout, CRC, etc.).  The implementation only
   *   assures that the response is returned intacta and does not check errors
   *   within the response itself.
   *
   ****************************************************************************/
  
  static int kinetis_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
                                  uint32_t *rshort)
  {
    uint32_t regval;
    int ret = OK;
  
    /* R1  Command response (48-bit)
     *     47        0               Start bit
     *     46        0               Transmission bit (0=from card)
     *     45:40     bit5   - bit0   Command index (0-63)
     *     39:8      bit31  - bit0   32-bit card status
     *     7:1       bit6   - bit0   CRC7
     *     0         1               End bit
     *
     * R1b Identical to R1 with the additional busy signalling via the data
     *     line.
     *
     * R6  Published RCA Response (48-bit, SD card only)
     *     47        0               Start bit
     *     46        0               Transmission bit (0=from card)
     *     45:40     bit5   - bit0   Command index (0-63)
     *     39:8      bit31  - bit0   32-bit Argument Field, consisting of:
     *                               [31:16] New published RCA of card
     *                               [15:0]  Card status bits {23,22,19,12:0}
     *     7:1       bit6   - bit0   CRC7
     *     0         1               End bit
     */
  
  
  #ifdef CONFIG_DEBUG_FEATURES
    if (!rshort)
      {
        mcerr("ERROR: rshort=NULL\n");
        ret = -EINVAL;
      }
  
    /* Check that this is the correct response to this command */
  
    else if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE &&
             (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE &&
             (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE)
      {
        mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
        ret = -EINVAL;
      }
    else
  #endif
      {
        /* Check if a timeout or CRC error occurred */
  
        regval = getreg32(KINETIS_SDHC_IRQSTAT);
        if ((regval & SDHC_INT_CTOE) != 0)
          {
            mcerr("ERROR: Command timeout: %08x\n", regval);
            ret = -ETIMEDOUT;
          }
        else if ((regval & SDHC_INT_CCE) != 0)
          {
            mcerr("ERROR: CRC failure: %08x\n", regval);
            ret = -EIO;
          }
      }
  
    /* Return the R1/R1b/R6 response.  These responses are returned in
     * CDMRSP0.  NOTE: This is not true for R1b (Auto CMD12 response) which
     * is returned in CMDRSP3.
     */
  
    *rshort = getreg32(KINETIS_SDHC_CMDRSP0);
    return ret;
  }
  
  static int kinetis_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t rlong[4])
  {
    uint32_t regval;
    int ret = OK;
  
    /* R2  CID, CSD register (136-bit)
     *     135       0               Start bit
     *     134       0               Transmission bit (0=from card)
     *     133:128   bit5   - bit0   Reserved
     *     127:1     bit127 - bit1   127-bit CID or CSD register
     *                               (including internal CRC)
     *     0         1               End bit
     */
  
  #ifdef CONFIG_DEBUG_FEATURES
    /* Check that R1 is the correct response to this command */
  
    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(KINETIS_SDHC_IRQSTAT);
        if (regval & SDHC_INT_CTOE)
          {
            mcerr("ERROR: Timeout IRQSTAT: %08x\n", regval);
            ret = -ETIMEDOUT;
          }
        else if (regval & SDHC_INT_CCE)
          {
            mcerr("ERROR: CRC fail IRQSTAT: %08x\n", regval);
            ret = -EIO;
          }
      }
  
    /* Return the long response in CMDRSP3..0 */
  
    if (rlong)
      {
        rlong[0] = getreg32(KINETIS_SDHC_CMDRSP3);
        rlong[1] = getreg32(KINETIS_SDHC_CMDRSP2);
        rlong[2] = getreg32(KINETIS_SDHC_CMDRSP1);
        rlong[3] = getreg32(KINETIS_SDHC_CMDRSP0);
      }
    return ret;
  }
  
  static int kinetis_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_FEATURES
    if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE &&
        (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE)
      {
        mcerr("ERROR: Wrong response CMD=%08x\n", cmd);
        ret = -EINVAL;
      }
    else
  #endif
      {
        /* Check if a timeout occurred (Apparently a CRC error can terminate
         * a good response)
         */
  
        regval = getreg32(KINETIS_SDHC_IRQSTAT);
        if (regval & SDHC_INT_CTOE)
          {
            mcerr("ERROR: Timeout IRQSTAT: %08x\n", regval);
            ret = -ETIMEDOUT;
          }
      }
  
    /* Return the short response in CMDRSP0 */
  
    if (rshort)
      {
        *rshort = getreg32(KINETIS_SDHC_CMDRSP0);
      }
  
    return ret;
  }
  
  /* MMC responses not supported */
  
  static int kinetis_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rnotimpl)
  {
    /* Just return an error */
  
    return -ENOSYS;
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_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 kinetis_waitenable(FAR struct sdio_dev_s *dev,
                               sdio_eventset_t eventset)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
    uint32_t waitints;
  
    DEBUGASSERT(priv != NULL);
  
    /* Disable event-related interrupts */
  
    kinetis_configwaitints(priv, 0, 0, 0);
  
    /* Select the interrupt mask that will give us the appropriate wakeup
     * interrupts.
     */
  
    waitints = 0;
    if ((eventset & (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0)
      {
        waitints |= SDHC_RESPDONE_INTS;
      }
  
    if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
      {
        waitints |= SDHC_XFRDONE_INTS;
      }
  
    /* Enable event-related interrupts */
  
    kinetis_configwaitints(priv, waitints, eventset, 0);
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_eventwait
   *   returns.  SDIO_WAITEVENTS must be called again before kinetis_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 kinetis_eventwait(FAR struct sdio_dev_s *dev,
                                         uint32_t timeout)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
    sdio_eventset_t wkupevent = 0;
    int ret;
  
    /* There is a race condition here... the event may have completed before
     * we get here.  In this case waitevents will be zero, but wkupevents will
     * be non-zero (and, hopefully, the semaphore count will also be non-zero.
     */
  
    DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
                (priv->waitevents == 0 && priv->wkupevent != 0));
  
    /* Check if the timeout event is specified in the event set */
  
    if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
      {
        int delay;
  
        /* Yes.. Handle a corner case */
  
        if (!timeout)
          {
            return SDIOWAIT_TIMEOUT;
          }
  
        /* Start the watchdog timer */
  
        delay = MSEC2TICK(timeout);
        ret   = wd_start(priv->waitwdog, delay, (wdentry_t)kinetis_eventtimeout,
                         1, (uint32_t)priv);
        if (ret != OK)
          {
            mcerr("ERROR: wd_start failed: %d\n", ret);
          }
      }
  
    /* Loop until the event (or the timeout occurs). Race conditions are avoided
     * by calling kinetis_waitenable prior to triggering the logic that will cause
     * the wait to terminate.  Under certain race conditions, the waited-for
     * may have already occurred before this function was called!
     */
  
    for (; ; )
      {
        /* Wait for an event in event set to occur.  If this the event has already
         * occurred, then the semaphore will already have been incremented and
         * there will be no wait.
         */
  
        kinetis_takesem(priv);
        wkupevent = priv->wkupevent;
  
        /* Check if the event has occurred.  When the event has occurred, then
         * evenset will be set to 0 and wkupevent will be set to a non-zero value.
         */
  
        if (wkupevent != 0)
          {
            /* Yes... break out of the loop with wkupevent non-zero */
  
            break;
          }
      }
  
    /* Disable event-related interrupts */
  
    kinetis_configwaitints(priv, 0, 0, 0);
  #ifdef CONFIG_SDIO_DMA
    priv->xfrflags   = 0;
  #endif
  
    kinetis_dumpsamples(priv);
    return wkupevent;
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_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 kinetis_callbackenable(FAR struct sdio_dev_s *dev,
                                   sdio_eventset_t eventset)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  
    mcinfo("eventset: %02x\n", eventset);
    DEBUGASSERT(priv != NULL);
  
    priv->cbevents = eventset;
    kinetis_callback(priv);
  }
  
  /****************************************************************************
   * Name: kinetis_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 kinetis_registercallback(FAR struct sdio_dev_s *dev,
                                    worker_t callback, void *arg)
  {
    struct kinetis_dev_s *priv = (struct kinetis_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: kinetis_dmasupported
   *
   * Description:
   *   Return true if the hardware can support DMA
   *
   * Input Parameters:
   *   dev - An instance of the SDIO device interface
   *
   * Returned Value:
   *   true if DMA is supported.
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_DMA
  static bool kinetis_dmasupported(FAR struct sdio_dev_s *dev)
  {
    return true;
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_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
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_DMA
  static int kinetis_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
                                size_t buflen)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  
    DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
    DEBUGASSERT(((uint32_t)buffer & 3) == 0);
  
    /* Reset the DPSM configuration */
  
    kinetis_datadisable();
  
    /* Begin sampling register values */
  
    kinetis_sampleinit();
    kinetis_sample(priv, SAMPLENDX_BEFORE_SETUP);
  
    /* Save the destination buffer information for use by the interrupt handler */
  
    priv->buffer    = (uint32_t *)buffer;
    priv->remaining = buflen;
  
    /* Then set up the SDIO data path */
  
    kinetis_dataconfig(priv, false, buflen, 1, SDHC_DVS_DATATIMEOUT);
  
    /* Configure the RX DMA */
  
    kinetis_configxfrints(priv, SDHC_DMADONE_INTS);
    putreg32((uint32_t)buffer, KINETIS_SDHC_DSADDR);
  
    /* Sample the register state */
  
    kinetis_sample(priv, SAMPLENDX_AFTER_SETUP);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: kinetis_dmasendsetup
   *
   * 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
   *   buflen - The size of the DMA transfer in bytes
   *
   * Returned Value:
   *   OK on success; a negated errno on failure
   *
   ****************************************************************************/
  
  #ifdef CONFIG_SDIO_DMA
  static int kinetis_dmasendsetup(FAR struct sdio_dev_s *dev,
                                FAR const uint8_t *buffer, size_t buflen)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
  
    DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
    DEBUGASSERT(((uint32_t)buffer & 3) == 0);
  
    /* Reset the DPSM configuration */
  
    kinetis_datadisable();
  
    /* Begin sampling register values */
  
    kinetis_sampleinit();
    kinetis_sample(priv, SAMPLENDX_BEFORE_SETUP);
  
    /* Save the source buffer information for use by the interrupt handler */
  
    priv->buffer    = (uint32_t *)buffer;
    priv->remaining = buflen;
  
    /* Then set up the SDIO data path */
  
    kinetis_dataconfig(priv, true, buflen, 1, SDHC_DVS_DATATIMEOUT);
  
    /* Configure the TX DMA */
  
    putreg32((uint32_t)buffer, KINETIS_SDHC_DSADDR);
  
    /* Sample the register state */
  
    kinetis_sample(priv, SAMPLENDX_AFTER_SETUP);
  
    /* Enable TX interrupts */
  
    kinetis_configxfrints(priv, SDHC_DMADONE_INTS);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Initialization/uninitialization/reset
   ****************************************************************************/
  /****************************************************************************
   * Name: kinetis_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 kinetis_callback(void *arg)
  {
    struct kinetis_dev_s *priv = (struct kinetis_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);
            (void)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
   ****************************************************************************/
  
  /****************************************************************************
   * Name: sdhc_initialize
   *
   * Description:
   *   Initialize SDIO for operation.
   *
   * Input Parameters:
   *   slotno - Not used.
   *
   * Returned Values:
   *   A reference to an SDIO interface structure.  NULL is returned on failures.
   *
   ****************************************************************************/
  
  FAR struct sdio_dev_s *sdhc_initialize(int slotno)
  {
    uint32_t regval;
  
    /* There is only one slot */
  
    struct kinetis_dev_s *priv = &g_sdhcdev;
    DEBUGASSERT(slotno == 0);
  
    /* Initialize the SDHC slot structure data structure */
  
    sem_init(&priv->waitsem, 0, 0);
    priv->waitwdog = wd_create();
    DEBUGASSERT(priv->waitwdog);
  
    /* Enable clocking to the SDHC module.  Clocking is still diabled in
     * the SYSCTRL register.
     */
  
    regval = getreg32(KINETIS_SIM_SCGC3);
    regval |= SIM_SCGC3_SDHC;
    putreg32(regval, KINETIS_SIM_SCGC3);
    mcinfo("SIM_SCGC3: %08x\n", regval);
  
    /* In addition to the system clock, the SDHC module needs a clock for the
     * base for the external card clock.  There are four possible sources for
     * this clock, selected by the SIM's SOPT2 register:
     *
     * - Core/system clock
     * - MCGPLLCLK/MCGFLLCLK clock
     * - OSCERCLK EXTAL clock
     * - External bypass clock from off-chip (SCHC0_CLKINB)
     */
  
    regval = getreg32(KINETIS_SIM_SOPT2);
    regval &= ~SIM_SOPT2_SDHCSRC_MASK;
    regval |= SIM_SOPT2_SDHCSRC_CORE;
    putreg32(regval, KINETIS_SIM_SOPT2);
    mcinfo("SIM_SOPT2: %08x\n", regval);
  
    /* Configure pins for 1 or 4-bit, wide-bus operation (the chip is capable
     * of 8-bit wide bus operation but D4-D7 are not configured).
     *
     * If bus is multiplexed then there is a custom bus configuration utility
     * in the scope of the board support package.
     */
  
  #ifndef CONFIG_SDIO_MUXBUS
    /* Data width 1, 4 or 8 */
  
    kinetis_pinconfig(PIN_SDHC0_D0);
  
    /* Data width 4 or 8 */
  
  #ifndef CONFIG_SDIO_WIDTH_D1_ONLY
    kinetis_pinconfig(PIN_SDHC0_D1);
    kinetis_pinconfig(PIN_SDHC0_D2);
    kinetis_pinconfig(PIN_SDHC0_D3);
  
    /* Data width 8 (not supported) */
  
  #if 0
    kinetis_pinconfig(PIN_SDHC0_D4);
    kinetis_pinconfig(PIN_SDHC0_D5);
    kinetis_pinconfig(PIN_SDHC0_D6);
    kinetis_pinconfig(PIN_SDHC0_D7);
  #endif
  #endif
  
    /* Clocking and CMD pins (all data widths) */
  
    kinetis_pinconfig(PIN_SDHC0_DCLK);
    kinetis_pinconfig(PIN_SDHC0_CMD);
  #endif
  
    /* Reset the card and assure that it is in the initial, unconfigured
     * state.
     */
  
    kinetis_reset(&priv->dev);
    kinetis_showregs(priv, "After reset");
    return &g_sdhcdev.dev;
  }
  
  /****************************************************************************
   * Name: sdhc_mediachange
   *
   * Description:
   *   Called by board-specific logic -- possible 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 Values:
   *   None
   *
   ****************************************************************************/
  
  void sdhc_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
    uint8_t cdstatus;
    irqstate_t flags;
  
    /* Update card status */
  
    flags = enter_critical_section();
    cdstatus = priv->cdstatus;
    if (cardinslot)
      {
        priv->cdstatus |= SDIO_STATUS_PRESENT;
      }
    else
      {
        priv->cdstatus &= ~SDIO_STATUS_PRESENT;
      }
  
    mcinfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus);
  
    /* Perform any requested callback if the status has changed */
  
    if (cdstatus != priv->cdstatus)
      {
        kinetis_callback(priv);
      }
  
    leave_critical_section(flags);
  }
  
  /****************************************************************************
   * Name: sdio_wrprotect
   *
   * Description:
   *   Called by board-specific logic to report if the card in the slot is
   *   mechanically write protected.
   *
   * Input Parameters:
   *   dev       - An instance of the SDIO driver device state structure.
   *   wrprotect - true is a card is writeprotected.
   *
   * Returned Values:
   *   None
   *
   ****************************************************************************/
  
  void sdhc_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect)
  {
    struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
    irqstate_t flags;
  
    /* Update card status */
  
    flags = enter_critical_section();
    if (wrprotect)
      {
        priv->cdstatus |= SDIO_STATUS_WRPROTECTED;
      }
    else
      {
        priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED;
      }
  
    mcinfo("cdstatus: %02x\n", priv->cdstatus);
    leave_critical_section(flags);
  }
  #endif /* CONFIG_KINETIS_SDHC */
  