Skip to content
Snippets Groups Projects
sam3u_hsmci.c 74.3 KiB
Newer Older
patacongo's avatar
patacongo committed
/****************************************************************************
 * arch/arm/src/sam3u/sam3u_sdio.c
 *
 *   Copyright (C) 2010 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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 <wdog.h>
#include <errno.h>

#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/sdio.h>
#include <nuttx/wqueue.h>
#include <nuttx/mmcsd.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "chip.h"
#include "up_arch.h"

#include "sam3u_internal.h"
#include "sam3u_dmac.h"
#include "sam3u_pmc.h"
patacongo's avatar
patacongo committed
#include "sam3u_hsmci.h"

#if CONFIG_SAM3U_HSMCI

/****************************************************************************
 * Pre-Processor Definitions
 ****************************************************************************/

/* Configuration ************************************************************/

#ifndef CONFIG_SAM3U_DMA
#  warning "HSMCI driver requires CONFIG_SAM3U_DMA"
patacongo's avatar
patacongo committed
#endif

#ifndef CONFIG_SCHED_WORKQUEUE
#  error "Callback support requires CONFIG_SCHED_WORKQUEUE"
#endif

patacongo's avatar
patacongo committed
#ifndef CONFIG_SDIO_BLOCKSETUP
#  error "This driver requires CONFIG_SDIO_BLOCKSETUP"
#endif

patacongo's avatar
patacongo committed
#ifndef CONFIG_HSMCI_PRI
#  define CONFIG_HSMCI_PRI        NVIC_SYSH_PRIORITY_DEFAULT
#endif

#if !defined(CONFIG_DEBUG_FS) || !defined(CONFIG_DEBUG_VERBOSE)
#  undef CONFIG_HSMCI_CMDDEBUG
patacongo's avatar
patacongo committed
#  undef CONFIG_HSMCI_XFRDEBUG
#endif

patacongo's avatar
patacongo committed
#ifdef CONFIG_SAM3U_HSMCI_RDPROOF
#  ifdef CONFIG_SAM3U_HSMCI_WRPROOF
#    define HSMCU_PROOF_BITS (HSMCI_MR_RDPROOF | HSMCI_MR_WRPROOF)
#  else
#    define HSMCU_PROOF_BITS HSMCI_MR_RDPROOF
#  endif
#else
#  ifdef CONFIG_SAM3U_HSMCI_WRPROOF
#    define HSMCU_PROOF_BITS HSMCI_MR_WRPROOF
#  else
#    define HSMCU_PROOF_BITS (0)
#  endif
#endif

patacongo's avatar
patacongo committed
/* Timing */

#define HSMCI_CMDTIMEOUT         (100000)
#define HSMCI_LONGTIMEOUT        (0x7fffffff)

/* Big DTIMER setting */

#define HSMCI_DTIMER_DATATIMEOUT (0x000fffff)

patacongo's avatar
patacongo committed
/* DMA configuration flags */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
#define DMA_FLAGS \
patacongo's avatar
patacongo committed
  (DMACH_FLAG_FIFO_8BYTES | DMACH_FLAG_FIFOCFG_LARGEST | \
  (DMACHAN_PID_MCI0 << DMACH_FLAG_PERIPHPID_SHIFT) | \
patacongo's avatar
patacongo committed
   DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH |  \
patacongo's avatar
patacongo committed
   DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
patacongo's avatar
patacongo committed
   DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_4)
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
/* Status errors:
 *
 *   HSMCI_INT_UNRE          Data transmit underrun
 *   HSMCI_INT_OVRE          Data receive overrun
 *   HSMCI_INT_BLKOVRE       DMA receive block overrun error
 *   HSMCI_INT_CSTOE         Completion signal time-out error (see HSMCI_CSTOR)
 *   HSMCI_INT_DTOE          Data time-out error (see HSMCI_DTOR)
 *   HSMCI_INT_DCRCE         Data CRC Error
 *   HSMCI_INT_RTOE          Response Time-out
 *   HSMCI_INT_RENDE         Response End Bit Error
 *   HSMCI_INT_RCRCE         Response CRC Error
 *   HSMCI_INT_RDIRE         Response Direction Error
 *   HSMCI_INT_RINDE         Response Index Error
 */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
#define HSMCI_STATUS_ERRORS \
  ( HSMCI_INT_UNRE  | HSMCI_INT_OVRE  | HSMCI_INT_BLKOVRE | HSMCI_INT_CSTOE | \
    HSMCI_INT_DTOE  | HSMCI_INT_DCRCE | HSMCI_INT_RTOE    | HSMCI_INT_RENDE | \
    HSMCI_INT_RCRCE | HSMCI_INT_RDIRE | HSMCI_INT_RINDE )  

/* Response errors:
 *
 *   HSMCI_INT_CSTOE         Completion signal time-out error (see HSMCI_CSTOR)
 *   HSMCI_INT_RTOE          Response Time-out
 *   HSMCI_INT_RENDE         Response End Bit Error
 *   HSMCI_INT_RCRCE         Response CRC Error
 *   HSMCI_INT_RDIRE         Response Direction Error
 *   HSMCI_INT_RINDE         Response Index Error
 */

#define HSMCI_RESPONSE_ERRORS \
  ( HSMCI_INT_CSTOE | HSMCI_INT_RTOE  | HSMCI_INT_RENDE   | HSMCI_INT_RCRCE | \
patacongo's avatar
patacongo committed
    HSMCI_INT_RDIRE | HSMCI_INT_RINDE ) 
#define HSMCI_RESPONSE_NOCRC_ERRORS \
  ( HSMCI_INT_CSTOE | HSMCI_INT_RTOE  | HSMCI_INT_RENDE   | HSMCI_INT_RDIRE | \
    HSMCI_INT_RINDE ) 
patacongo's avatar
patacongo committed
#define HSMCI_RESPONSE_TIMEOUT_ERRORS \
  ( HSMCI_INT_CSTOE | HSMCI_INT_RTOE  )
patacongo's avatar
patacongo committed

/* Data transfer errors:
 *
 *   HSMCI_INT_UNRE          Data transmit underrun
 *   HSMCI_INT_OVRE          Data receive overrun
 *   HSMCI_INT_BLKOVRE       DMA receive block overrun error
 *   HSMCI_INT_CSTOE         Completion signal time-out error (see HSMCI_CSTOR)
 *   HSMCI_INT_DTOE          Data time-out error (see HSMCI_DTOR)
 *   HSMCI_INT_DCRCE         Data CRC Error
 */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
#define HSMCI_DATA_ERRORS \
  ( HSMCI_INT_UNRE  | HSMCI_INT_OVRE  | HSMCI_INT_BLKOVRE | HSMCI_INT_CSTOE | \
    HSMCI_INT_DTOE  | HSMCI_INT_DCRCE )

patacongo's avatar
patacongo committed
#define HSMCI_DATA_TIMEOUT_ERRORS \
  ( HSMCI_INT_CSTOE | HSMCI_INT_DTOE )

patacongo's avatar
patacongo committed
#define HSMCI_DATA_DMARECV_ERRORS \
  ( HSMCI_INT_OVRE  | HSMCI_INT_BLKOVRE | HSMCI_INT_CSTOE | HSMCI_INT_DTOE | \
    HSMCI_INT_DCRCE )

#define HSMCI_DATA_DMASEND_ERRORS \
  ( HSMCI_INT_UNRE  | HSMCI_INT_CSTOE | HSMCI_INT_DTOE    | HSMCI_INT_DCRCE )

/* Data transfer status and interrupt mask bits.
 *
 * The XFRDONE flag in the HSMCI_SR indicates exactly when the read or
 * write sequence is finished.
 *
 *   0: A transfer is in progress.
 *   1: Command register is ready to operate and the data bus is in the idle state.
 *
 * DMADONE: DMA Transfer done
 *
 *   0: DMA buffer transfer has not completed since the last read of HSMCI_SR register.
 *   1: DMA buffer transfer has completed.
 */
patacongo's avatar
patacongo committed

#define HSMCI_DMARECV_INTS \
  ( HSMCI_DATA_DMARECV_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
patacongo's avatar
patacongo committed
#define HSMCI_DMASEND_INTS \
  ( HSMCI_DATA_DMASEND_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
patacongo's avatar
patacongo committed

/* Event waiting interrupt mask bits.
 *
 * CMDRDY (Command Ready):
 *
 *   0: A command is in progress
 *   1: The last command has been sent.  The CMDRDY flag is released 8 bits
 *     after the end of the card response. Cleared when writing in the HSMCI_CMDR
 */
patacongo's avatar
patacongo committed

patacongo's avatar
patacongo committed
#define HSMCI_CMDRESP_INTS \
  ( HSMCI_RESPONSE_ERRORS | HSMCI_INT_CMDRDY )
#define HSMCI_CMDRESP_NOCRC_INTS \
  ( HSMCI_RESPONSE_NOCRC_ERRORS | HSMCI_INT_CMDRDY )
patacongo's avatar
patacongo committed

/* Register logging support */

#ifdef CONFIG_HSMCI_XFRDEBUG
#  ifdef CONFIG_DEBUG_DMA
patacongo's avatar
patacongo committed
#    define SAMPLENDX_BEFORE_SETUP  0
#    define SAMPLENDX_BEFORE_ENABLE 1
#    define SAMPLENDX_AFTER_SETUP   2
#    define SAMPLENDX_END_TRANSFER  3
#    define SAMPLENDX_DMA_CALLBACK  4
#    define DEBUG_NDMASAMPLES       5
patacongo's avatar
patacongo committed
#  else
#    define SAMPLENDX_BEFORE_SETUP  0
#    define SAMPLENDX_AFTER_SETUP   1
#    define SAMPLENDX_END_TRANSFER  2
#    define DEBUG_NDMASAMPLES       3
patacongo's avatar
patacongo committed
#  endif
#endif

#ifdef CONFIG_HSMCI_CMDDEBUG
#  define SAMPLENDX_AFTER_CMDR      0
#  define SAMPLENDX_AT_WAKEUP       1
#  define DEBUG_NCMDSAMPLES         2
#endif

patacongo's avatar
patacongo committed
/****************************************************************************
 * Private Types
 ****************************************************************************/

/* This structure defines the state of the SAM3U HSMCI interface */

struct sam3u_dev_s
{
  struct sdio_dev_s  dev;        /* Standard, base SDIO interface */
  
  /* SAM3U-specific extensions */
  /* Event support */

  sem_t              waitsem;    /* Implements event waiting */
  sdio_eventset_t    waitevents; /* Set of events to be waited for */
  uint32_t           waitmask;   /* Interrupt enables for event waiting */
  uint32_t           cmdrmask;   /* Interrupt enables for this particular cmd/response */
patacongo's avatar
patacongo committed
  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           xfrmask;    /* Interrupt enables for data transfer */

  /* DMA data transfer support */

  bool               widebus;    /* Required for DMA support */
  DMA_HANDLE         dma;        /* Handle for DMA channel */
};

/* Register logging support */

#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
patacongo's avatar
patacongo committed
struct sam3u_hsmciregs_s
{
  uint32_t mr;    /* Mode Register */
  uint32_t dtor;  /* Data Timeout Register */
  uint32_t sdcr;  /* SD/SDIO Card Register */
  uint32_t argr;  /* Argument Register */
  uint32_t blkr;  /* Block Register */
  uint32_t cstor; /* Completion Signal Timeout Register */
  uint32_t rsp0;  /* Response Register 0 */
  uint32_t rsp1;  /* Response Register 1 */
  uint32_t rsp2;  /* Response Register 2 */
  uint32_t rsp3;  /* Response Register 3 */
  uint32_t sr;    /* Status Register */
  uint32_t imr;   /* Interrupt Mask Register */
  uint32_t dma;   /* DMA Configuration Register */
  uint32_t cfg;   /* Configuration Register */
  uint32_t wpmr;  /* Write Protection Mode Register */
  uint32_t wpsr;  /* Write Protection Status Register */
patacongo's avatar
patacongo committed
};
patacongo's avatar
patacongo committed

#ifdef CONFIG_HSMCI_XFRDEBUG
struct sam3u_xfrregs_s
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
  struct sam3u_hsmciregs_s hsmci;
#ifdef CONFIG_DEBUG_DMA
patacongo's avatar
patacongo committed
  struct sam3u_dmaregs_s  dma;
#endif
};
#endif

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Low-level helpers ********************************************************/

static void sam3u_takesem(struct sam3u_dev_s *priv);
#define     sam3u_givesem(priv) (sem_post(&priv->waitsem))
static void sam3u_enablewaitints(struct sam3u_dev_s *priv, uint32_t waitmask,
              sdio_eventset_t waitevents);
static void sam3u_disablewaitints(struct sam3u_dev_s *priv, sdio_eventset_t wkupevents);
static void sam3u_enablexfrints(struct sam3u_dev_s *priv, uint32_t xfrmask);
static void sam3u_disablexfrints(struct sam3u_dev_s *priv);
patacongo's avatar
patacongo committed
static inline void sam3u_disable(void);
static inline void sam3u_enable(void);
patacongo's avatar
patacongo committed

/* Register Sampling ********************************************************/

#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
static void sam3u_hsmcisample(struct sam3u_hsmciregs_s *regs);
static void sam3u_hsmcidump(struct sam3u_hsmciregs_s *regs, const char *msg);
#endif
patacongo's avatar
patacongo committed

#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrsampleinit(void);
static void sam3u_xfrsample(struct sam3u_dev_s *priv, int index);
static void sam3u_xfrdumpone(struct sam3u_dev_s *priv,
              struct sam3u_xfrregs_s *regs, const char *msg);
static void sam3u_xfrdump(struct sam3u_dev_s *priv);
#else
#  define   sam3u_xfrsampleinit()
#  define   sam3u_xfrsample(priv,index)
#  define   sam3u_xfrdump(priv)
#endif

#ifdef CONFIG_HSMCI_CMDDEBUG
static void sam3u_cmdsampleinit(void);
static inline void sam3u_cmdsample1(int index3);
static inline void sam3u_cmdsample2(int index, uint32_t sr);
static void sam3u_cmddump(void);
patacongo's avatar
patacongo committed
#else
#  define   sam3u_cmdsampleinit()
#  define   sam3u_cmdsample1(index)
#  define   sam3u_cmdsample2(index,sr)
#  define   sam3u_cmddump()
patacongo's avatar
patacongo committed
#endif

/* DMA Helpers **************************************************************/

patacongo's avatar
patacongo committed
static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result);
patacongo's avatar
patacongo committed

/* Data Transfer Helpers ****************************************************/

static void sam3u_eventtimeout(int argc, uint32_t arg);
static void sam3u_endwait(struct sam3u_dev_s *priv, sdio_eventset_t wkupevent);
static void sam3u_endtransfer(struct sam3u_dev_s *priv, sdio_eventset_t wkupevent);
static void sam3u_notransfer(struct sam3u_dev_s *priv);
patacongo's avatar
patacongo committed

/* Interrupt Handling *******************************************************/

static int  sam3u_interrupt(int irq, void *context);

/* SDIO interface methods ***************************************************/

/* Initialization/setup */

static void sam3u_reset(FAR struct sdio_dev_s *dev);
static uint8_t sam3u_status(FAR struct sdio_dev_s *dev);
static void sam3u_widebus(FAR struct sdio_dev_s *dev, bool enable);
static void sam3u_clock(FAR struct sdio_dev_s *dev,
              enum sdio_clock_e rate);
static int  sam3u_attach(FAR struct sdio_dev_s *dev);

/* Command/Status/Data Transfer */

static int  sam3u_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
patacongo's avatar
patacongo committed
              uint32_t arg);
patacongo's avatar
patacongo committed
static void sam3u_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
              unsigned int nblocks);
patacongo's avatar
patacongo committed
static int  sam3u_cancel(FAR struct sdio_dev_s *dev);
static int  sam3u_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
static int  sam3u_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
patacongo's avatar
patacongo committed
              uint32_t *rshort);
static int  sam3u_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
              uint32_t rlong[4]);
static int  sam3u_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd,
              uint32_t *rnotimpl);

/* EVENT handler */

static void sam3u_waitenable(FAR struct sdio_dev_s *dev,
              sdio_eventset_t eventset);
static sdio_eventset_t
            sam3u_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout);
static void sam3u_callbackenable(FAR struct sdio_dev_s *dev,
              sdio_eventset_t eventset);
static int  sam3u_registercallback(FAR struct sdio_dev_s *dev,
              worker_t callback, void *arg);

/* DMA */

#ifdef CONFIG_SDIO_DMA
patacongo's avatar
patacongo committed
static bool sam3u_dmasupported(FAR struct sdio_dev_s *dev);
#endif
patacongo's avatar
patacongo committed
static int  sam3u_dmarecvsetup(FAR struct sdio_dev_s *dev,
              FAR uint8_t *buffer, size_t buflen);
static int  sam3u_dmasendsetup(FAR struct sdio_dev_s *dev,
              FAR const uint8_t *buffer, size_t buflen);

/* Initialization/uninitialization/reset ************************************/

static void sam3u_callback(void *arg);

/****************************************************************************
 * Private Data
 ****************************************************************************/

struct sam3u_dev_s g_sdiodev =
{
  .dev =
  {
    .reset            = sam3u_reset,
    .status           = sam3u_status,
    .widebus          = sam3u_widebus,
    .clock            = sam3u_clock,
    .attach           = sam3u_attach,
    .sendcmd          = sam3u_sendcmd,
patacongo's avatar
patacongo committed
    .blocksetup       = sam3u_blocksetup,
    .recvsetup        = sam3u_dmarecvsetup,
    .sendsetup        = sam3u_dmasendsetup,
patacongo's avatar
patacongo committed
    .cancel           = sam3u_cancel,
    .waitresponse     = sam3u_waitresponse,
    .recvR1           = sam3u_recvshort,
patacongo's avatar
patacongo committed
    .recvR2           = sam3u_recvlong,
    .recvR3           = sam3u_recvshort,
    .recvR4           = sam3u_recvnotimpl,
    .recvR5           = sam3u_recvnotimpl,
    .recvR6           = sam3u_recvshort,
patacongo's avatar
patacongo committed
    .recvR7           = sam3u_recvshort,
    .waitenable       = sam3u_waitenable,
    .eventwait        = sam3u_eventwait,
    .callbackenable   = sam3u_callbackenable,
    .registercallback = sam3u_registercallback,
#ifdef CONFIG_SDIO_DMA
patacongo's avatar
patacongo committed
    .dmasupported     = sam3u_dmasupported,
    .dmarecvsetup     = sam3u_dmarecvsetup,
    .dmasendsetup     = sam3u_dmasendsetup,
#endif
patacongo's avatar
patacongo committed
  },
};

/* Register logging support */

#ifdef CONFIG_HSMCI_XFRDEBUG
static struct sam3u_xfrregs_s   g_xfrsamples[DEBUG_NDMASAMPLES];
#endif
#ifdef CONFIG_HSMCI_CMDDEBUG
static struct sam3u_hsmciregs_s g_cmdsamples[DEBUG_NCMDSAMPLES];
#endif
#if defined(CONFIG_HSMCI_XFRDEBUG) && defined(CONFIG_HSMCI_CMDDEBUG)
static bool                     g_xfrinitialized;
static bool                     g_cmdinitialized;
patacongo's avatar
patacongo committed
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Low-level Helpers
 ****************************************************************************/
/****************************************************************************
 * Name: sam3u_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 sam3u_takesem(struct sam3u_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: sam3u_enablewaitints
 *
 * Description:
 *   Enable HSMCI interrupts needed to suport the wait function
 *
 * Input Parameters:
 *   priv       - A reference to the HSMCI device state structure
 *   waitmask   - The set of bits in the HSMCI MASK register to set
 *   waitevents - Waited for events
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void sam3u_enablewaitints(struct sam3u_dev_s *priv, uint32_t waitmask,
                                 sdio_eventset_t waitevents)
{
  irqstate_t flags;

  /* Save all of the data and set the new interrupt mask in one, atomic
   * operation.
   */

  flags = irqsave();
  priv->waitevents = waitevents;
  priv->wkupevent  = 0;
  priv->waitmask   = waitmask;
  putreg32(priv->xfrmask | priv->waitmask, SAM3U_HSMCI_IER);
  irqrestore(flags);
}

/****************************************************************************
 * Name: sam3u_disablewaitints
 *
 * Description:
 *   Disable HSMCI interrupts and save wakeup event.  Called
 *
 * Input Parameters:
 *   priv       - A reference to the HSMCI device state structure
 *   wkupevent  - Wake-up event(s)
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void sam3u_disablewaitints(struct sam3u_dev_s *priv,
                                  sdio_eventset_t wkupevent)
{
  irqstate_t flags;

  /* Save all of the data and set the new interrupt mask in one, atomic
   * operation.
   */

  flags = irqsave();
  priv->waitevents = 0;
  priv->wkupevent  = wkupevent;
  priv->waitmask   = 0;
  putreg32(~priv->xfrmask, SAM3U_HSMCI_IDR);
  irqrestore(flags);
}

/****************************************************************************
 * Name: sam3u_enablexfrints
 *
 * Description:
 *   Enable HSMCI interrupts needed to support the data transfer event
 *
 * Input Parameters:
 *   priv    - A reference to the HSMCI device state structure
 *   xfrmask - The set of bits in the HSMCI MASK register to set
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void sam3u_enablexfrints(struct sam3u_dev_s *priv, uint32_t xfrmask)
{
  irqstate_t flags = irqsave();
  priv->xfrmask = xfrmask;
  putreg32(priv->xfrmask | priv->waitmask, SAM3U_HSMCI_IER);
  irqrestore(flags);
}

/****************************************************************************
 * Name: sam3u_disablexfrints
 *
 * Description:
 *   Disable HSMCI interrupts needed to support the data transfer event
 *
 * Input Parameters:
 *   priv    - A reference to the HSMCI device state structure
 *   xfrmask - The set of bits in the HSMCI MASK register to set
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void sam3u_disablexfrints(struct sam3u_dev_s *priv)
{
  irqstate_t flags = irqsave();
  priv->xfrmask = 0;
  putreg32(~priv->waitmask, SAM3U_HSMCI_IDR);
  irqrestore(flags);
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: sam3u_disable
 *
 * Description:
patacongo's avatar
patacongo committed
 *   Disable the HSMCI
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

static inline void sam3u_disable(void) 
{
  /* Disable the MCI peripheral clock */

  putreg32((1 << SAM3U_PID_HSMCI), SAM3U_PMC_PCDR);
  
  /* Disable the MCI */

  putreg32(HSMCI_CR_MCIDIS, SAM3U_HSMCI_CR);

  /* Disable all the interrupts */

  putreg32(0xffffffff, SAM3U_HSMCI_IDR);
}

/****************************************************************************
 * Name: sam3u_enable
 *
 * Description:
 *   Enable the HSMCI
 *
 ****************************************************************************/

static inline void sam3u_enable(void)
{
  /* Enable the MCI peripheral clock */

  putreg32((1 << SAM3U_PID_HSMCI), SAM3U_PMC_PCER);

  /* Enable the MCI and the Power Saving */

  putreg32(HSMCI_CR_MCIEN, SAM3U_HSMCI_CR);
}

patacongo's avatar
patacongo committed
/****************************************************************************
 * Register Sampling
patacongo's avatar
patacongo committed
 ****************************************************************************/

/****************************************************************************
 * Name: sam3u_hsmcisample
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Sample HSMCI registers
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
static void sam3u_hsmcisample(struct sam3u_hsmciregs_s *regs)
patacongo's avatar
patacongo committed
{
  regs->mr    = getreg32(SAM3U_HSMCI_MR);
  regs->dtor  = getreg32(SAM3U_HSMCI_DTOR);
  regs->sdcr  = getreg32(SAM3U_HSMCI_SDCR);
  regs->argr  = getreg32(SAM3U_HSMCI_ARGR);
  regs->blkr  = getreg32(SAM3U_HSMCI_BLKR);
  regs->cstor = getreg32(SAM3U_HSMCI_CSTOR);
  regs->rsp0  = getreg32(SAM3U_HSMCI_RSPR0);
  regs->rsp1  = getreg32(SAM3U_HSMCI_RSPR1);
  regs->rsp2  = getreg32(SAM3U_HSMCI_RSPR2);
  regs->rsp3  = getreg32(SAM3U_HSMCI_RSPR3);
  regs->sr    = getreg32(SAM3U_HSMCI_SR);
  regs->imr   = getreg32(SAM3U_HSMCI_IMR);
  regs->dma   = getreg32(SAM3U_HSMCI_DMA);
  regs->cfg   = getreg32(SAM3U_HSMCI_CFG);
  regs->wpmr  = getreg32(SAM3U_HSMCI_WPMR);
  regs->wpsr  = getreg32(SAM3U_HSMCI_WPSR);
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: sam3u_hsmcidump
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Dump one register sample
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
static void sam3u_hsmcidump(struct sam3u_hsmciregs_s *regs, const char *msg)
patacongo's avatar
patacongo committed
{
  fdbg("HSMCI Registers: %s\n", msg);
  fdbg("     MR[%08x]: %08x\n", SAM3U_HSMCI_MR,    regs->mr);
  fdbg("   DTOR[%08x]: %08x\n", SAM3U_HSMCI_DTOR,  regs->dtor);
  fdbg("   SDCR[%08x]: %08x\n", SAM3U_HSMCI_SDCR,  regs->sdcr);
  fdbg("   ARGR[%08x]: %08x\n", SAM3U_HSMCI_ARGR,  regs->argr);
  fdbg("   BLKR[%08x]: %08x\n", SAM3U_HSMCI_BLKR,  regs->blkr);
  fdbg("  CSTOR[%08x]: %08x\n", SAM3U_HSMCI_CSTOR, regs->cstor);
  fdbg("  RSPR0[%08x]: %08x\n", SAM3U_HSMCI_RSPR0, regs->rsp0);
  fdbg("  RSPR1[%08x]: %08x\n", SAM3U_HSMCI_RSPR1, regs->rsp1);
  fdbg("  RSPR2[%08x]: %08x\n", SAM3U_HSMCI_RSPR2, regs->rsp2);
  fdbg("  RSPR3[%08x]: %08x\n", SAM3U_HSMCI_RSPR3, regs->rsp3);
  fdbg("     SR[%08x]: %08x\n", SAM3U_HSMCI_SR,    regs->sr);
  fdbg("    IMR[%08x]: %08x\n", SAM3U_HSMCI_IMR,   regs->imr);
  fdbg("    DMA[%08x]: %08x\n", SAM3U_HSMCI_DMA,   regs->dma);
  fdbg("    CFG[%08x]: %08x\n", SAM3U_HSMCI_CFG,   regs->cfg);
  fdbg("   WPMR[%08x]: %08x\n", SAM3U_HSMCI_WPMR,  regs->wpmr);
  fdbg("   WPSR[%08x]: %08x\n", SAM3U_HSMCI_WPSR,  regs->wpsr);
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: sam3u_xfrsample
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Sample HSMCI/DMA registers
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrsample(struct sam3u_dev_s *priv, int index)
patacongo's avatar
patacongo committed
{
  struct sam3u_xfrregs_s *regs = &g_xfrsamples[index];
patacongo's avatar
patacongo committed
#ifdef CONFIG_DEBUG_DMA
  sam3u_dmasample(priv->dma, &regs->dma);
patacongo's avatar
patacongo committed
#endif
  sam3u_hsmcisample(&regs->hsmci);
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: sam3u_xfrsampleinit
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Setup prior to collecting transfer samples
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrsampleinit(void)
patacongo's avatar
patacongo committed
{
  memset(g_xfrsamples, 0xff, DEBUG_NDMASAMPLES * sizeof(struct sam3u_xfrregs_s));
#ifdef CONFIG_HSMCI_CMDDEBUG
  g_xfrinitialized = true;
#endif
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: sam3u_xfrdumpone
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Dump one transfer register sample
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrdumpone(struct sam3u_dev_s *priv,
                             struct sam3u_xfrregs_s *regs, const char *msg)
patacongo's avatar
patacongo committed
{
patacongo's avatar
patacongo committed
#ifdef CONFIG_DEBUG_DMA
  sam3u_dmadump(priv->dma, &regs->dma, msg);
patacongo's avatar
patacongo committed
#endif
  sam3u_hsmcidump(&regs->hsmci, msg);
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * Name: sam3u_xfrdump
patacongo's avatar
patacongo committed
 *
 * Description:
 *   Dump all transfer-related, sampled register data
patacongo's avatar
patacongo committed
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_XFRDEBUG
static void  sam3u_xfrdump(struct sam3u_dev_s *priv)
patacongo's avatar
patacongo committed
{
#ifdef CONFIG_HSMCI_CMDDEBUG
  if (g_xfrinitialized)
#endif
    {
      sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_BEFORE_SETUP], "Before setup");
patacongo's avatar
patacongo committed
#ifdef CONFIG_DEBUG_DMA
      sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_BEFORE_ENABLE], "Before DMA enable");
patacongo's avatar
patacongo committed
#endif
      sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_AFTER_SETUP], "After setup");
      sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_END_TRANSFER], "End of transfer");
patacongo's avatar
patacongo committed
#ifdef CONFIG_DEBUG_DMA
      sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_DMA_CALLBACK], "DMA Callback");
#endif
#ifdef CONFIG_HSMCI_CMDDEBUG
      g_xfrinitialized = false;
#endif
    }
}
#endif

/****************************************************************************
 * Name: sam3u_cmdsampleinit
 *
 * Description:
 *   Setup prior to collecting command/response samples
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_CMDDEBUG
static void sam3u_cmdsampleinit(void)
{
  memset(g_cmdsamples, 0xff, DEBUG_NCMDSAMPLES * sizeof(struct sam3u_hsmciregs_s));
#ifdef CONFIG_HSMCI_XFRDEBUG
  g_cmdinitialized = true;
#endif
}
#endif

/****************************************************************************
 * Name: sam3u_cmdsample1 & 2
 *
 * Description:
 *   Sample command/response registers
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_CMDDEBUG
static inline void sam3u_cmdsample1(int index)
{
  sam3u_hsmcisample(&g_cmdsamples[index]);
}

static inline void sam3u_cmdsample2(int index, uint32_t sr)
{
  sam3u_hsmcisample(&g_cmdsamples[index]);
  g_cmdsamples[index].sr = sr;
}
#endif

/****************************************************************************
 * Name: sam3u_cmddump
 *
 * Description:
 *   Dump all comand/response register data
 *
 ****************************************************************************/

#ifdef CONFIG_HSMCI_CMDDEBUG
static void sam3u_cmddump(void)
{
#ifdef CONFIG_HSMCI_XFRDEBUG
  if (g_cmdinitialized)
#endif
    {
      sam3u_hsmcidump(&g_cmdsamples[SAMPLENDX_AFTER_CMDR], "After command setup");
      sam3u_hsmcidump(&g_cmdsamples[SAMPLENDX_AT_WAKEUP],  "After wakeup");
#ifdef CONFIG_HSMCI_XFRDEBUG
      g_cmdinitialized = false;
patacongo's avatar
patacongo committed
#endif
patacongo's avatar
patacongo committed
}
#endif

/****************************************************************************
 * DMA Helpers
 ****************************************************************************/

patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: sam3u_dmacallback
 *
 * Description:
 *   Called when HSMCI DMA completes
 *
 ****************************************************************************/

patacongo's avatar
patacongo committed
static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result)
patacongo's avatar
patacongo committed
{
  /* We don't really do anything at the completion of DMA.  The termination
   * of the transfer is driven by the HSMCI interrupts.
   */

  sam3u_xfrsample((struct sam3u_dev_s*)arg, SAMPLENDX_DMA_CALLBACK);
patacongo's avatar
patacongo committed
}

/****************************************************************************
 * Data Transfer Helpers
 ****************************************************************************/

/****************************************************************************
 * Name: sam3u_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 sam3u_eventtimeout(int argc, uint32_t arg)
{
  struct sam3u_dev_s *priv = (struct sam3u_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.. wake up any waiting threads */

      sam3u_endwait(priv, SDIOWAIT_TIMEOUT);
      flldbg("Timeout\n");
patacongo's avatar
patacongo committed
    }
}

/****************************************************************************
 * Name: sam3u_endwait
 *
 * Description:
 *   Wake up a waiting thread if the waited-for event has occurred.
 *
 * Input Parameters:
 *   priv      - An instance of the HSMCI 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 sam3u_endwait(struct sam3u_dev_s *priv, sdio_eventset_t wkupevent)
{
  /* Cancel the watchdog timeout */

  (void)wd_cancel(priv->waitwdog);

  /* Disable event-related interrupts and save wakeup event */

  sam3u_disablewaitints(priv, wkupevent);

  /* Wake up the waiting thread */

  sam3u_givesem(priv);
}

/****************************************************************************
 * Name: sam3u_endtransfer
 *
 * Description:
 *   Terminate a transfer with the provided status.  This function is called
 *   only from the HSMCI interrupt handler when end-of-transfer conditions
 *   are detected.
 *
 * Input Parameters:
 *   priv   - An instance of the HSMCI device interface
 *   wkupevent - The event that caused the transfer to end
 *
 * Returned Value:
 *   None
 *
 * Assumptions:
 *   Always called from the interrupt level with interrupts disabled.