2794 lines
81 KiB
C
2794 lines
81 KiB
C
/****************************************************************************
|
|
* arch/arm/src/lpc54xx/lpc54_sdmmc.c
|
|
*
|
|
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* This code is based on arch/arm/src/lpc43xx/lpc43_sdmmc.c:
|
|
*
|
|
* Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved.
|
|
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
|
|
* Author: Alan Carvalho de Assis <acassis@gmail.com>
|
|
*
|
|
* which was itself based on arch/arm/src/lpc17xx/lpc17_sdcard.c:
|
|
*
|
|
* Copyright (C) 2013-2014, 2016-2017 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/semaphore.h>
|
|
#include <nuttx/mmcsd.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "chip/lpc54_pinmux.h"
|
|
#include "chip/lpc54_syscon.h"
|
|
#include "lpc54_enableclk.h"
|
|
#include "lpc54_gpio.h"
|
|
#include "lpc54_sdmmc.h"
|
|
|
|
#include <arch/board/board.h>
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define MCI_DMADES0_OWN (1UL << 31)
|
|
#define MCI_DMADES0_CH (1 << 4)
|
|
#define MCI_DMADES0_FS (1 << 3)
|
|
#define MCI_DMADES0_LD (1 << 2)
|
|
#define MCI_DMADES0_DIC (1 << 1)
|
|
#define MCI_DMADES1_MAXTR 4096
|
|
#define MCI_DMADES1_BS1(x) (x)
|
|
|
|
/* Configuration ************************************************************/
|
|
/* Required system configuration options in the sched/Kconfig:
|
|
*
|
|
* CONFIG_SCHED_WORKQUEUE -- Callback support requires work queue support.
|
|
*
|
|
* Driver-specific configuration options in the drivers/mmcd Kdonfig:
|
|
*
|
|
* CONFIG_SDIO_MUXBUS - Setting this configuration enables some locking
|
|
* APIs to manage concurrent accesses on the SD card bus. This is not
|
|
* needed for the simple case of a single SD card slot, for example.
|
|
* CONFIG_SDIO_WIDTH_D1_ONLY - This may be selected to force the driver
|
|
* operate with only a single data line (the default is to use all
|
|
* 4 SD data lines).
|
|
* CONFIG_MMCSD_HAVE_CARDDETECT - Select if the SD slot supports a card
|
|
* detect pin.
|
|
* CONFIG_MMCSD_HAVE_WRITEPROTECT - Select if the SD slots supports a
|
|
* write protected pin.
|
|
*
|
|
* Driver-specific configuration options in the arch/arm/src/lpc54xx/Kconfig
|
|
*
|
|
* CONFIG_LPC54_SDMMC_PWRCTRL - Select if the board supports an output
|
|
* pin to enable power to the SD slot.
|
|
* CONFIG_LPC54_SDMMC_DMA - Enable SD card DMA. This is a marginally
|
|
* optional. For most usages, SD accesses will cause data overruns if
|
|
* used without DMA. This will also select CONFIG_SDIO_DMA.
|
|
* CONFIG_LPC54_SDMMC_REGDEBUG - Enables some very low-level debug output
|
|
* This also requires CONFIG_DEBUG_MEMCARD_INFO
|
|
*/
|
|
|
|
#ifndef CONFIG_SCHED_WORKQUEUE
|
|
# error "Callback support requires CONFIG_SCHED_WORKQUEUE"
|
|
#endif
|
|
|
|
/* Timing */
|
|
|
|
#define SDCARD_CMDTIMEOUT (10000)
|
|
#define SDCARD_LONGTIMEOUT (0x7fffffff)
|
|
|
|
/* Type of Card Bus Size */
|
|
|
|
#define SDCARD_BUS_D1 0
|
|
#define SDCARD_BUS_D4 1
|
|
#define SDCARD_BUS_D8 0x100
|
|
|
|
/* FIFO size in bytes */
|
|
|
|
#define LPC54_TXFIFO_SIZE (LPC54_TXFIFO_DEPTH | LPC54_TXFIFO_WIDTH)
|
|
#define LPC54_RXFIFO_SIZE (LPC54_RXFIFO_DEPTH | LPC54_RXFIFO_WIDTH)
|
|
|
|
/* Data transfer interrupt mask bits */
|
|
|
|
#define SDCARD_RECV_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \
|
|
SDMMC_INT_EBE | SDMMC_INT_RXDR | SDMMC_INT_SBE)
|
|
#define SDCARD_SEND_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \
|
|
SDMMC_INT_EBE | SDMMC_INT_TXDR | SDMMC_INT_SBE)
|
|
|
|
#define SDCARD_DMARECV_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \
|
|
SDMMC_INT_SBE | SDMMC_INT_EBE)
|
|
#define SDCARD_DMASEND_MASK (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \
|
|
SDMMC_INT_EBE)
|
|
|
|
#define SDCARD_DMAERROR_MASK (SDMMC_IDINTEN_FBE | SDMMC_IDINTEN_DU | \
|
|
SDMMC_IDINTEN_AIS)
|
|
|
|
#define SDCARD_TRANSFER_ALL (SDMMC_INT_DTO | SDMMC_INT_DCRC | SDMMC_INT_DRTO | \
|
|
SDMMC_INT_EBE | SDMMC_INT_TXDR | SDMMC_INT_RXDR | \
|
|
SDMMC_INT_SBE)
|
|
|
|
/* Event waiting interrupt mask bits */
|
|
|
|
#define SDCARD_INT_RESPERR (SDMMC_INT_RE | SDMMC_INT_RCRC | SDMMC_INT_RTO)
|
|
|
|
#ifdef CONFIG_MMCSD_HAVE_CARDDETECT
|
|
# define SDCARD_INT_CDET SDMMC_INT_CDET
|
|
#else
|
|
# define SDCARD_INT_CDET 0
|
|
#endif
|
|
|
|
#define SDCARD_CMDDONE_STA (SDMMC_INT_CDONE)
|
|
#define SDCARD_RESPDONE_STA (0)
|
|
|
|
#define SDCARD_CMDDONE_MASK (SDMMC_INT_CDONE)
|
|
#define SDCARD_RESPDONE_MASK (SDMMC_INT_CDONE | SDCARD_INT_RESPERR)
|
|
#define SDCARD_XFRDONE_MASK (0) /* Handled by transfer masks */
|
|
|
|
#define SDCARD_CMDDONE_CLEAR (SDMMC_INT_CDONE)
|
|
#define SDCARD_RESPDONE_CLEAR (SDMMC_INT_CDONE | SDCARD_INT_RESPERR)
|
|
|
|
#define SDCARD_XFRDONE_CLEAR (SDCARD_TRANSFER_ALL)
|
|
|
|
#define SDCARD_WAITALL_CLEAR (SDCARD_CMDDONE_CLEAR | SDCARD_RESPDONE_CLEAR | \
|
|
SDCARD_XFRDONE_CLEAR)
|
|
|
|
/* Let's wait until we have both SD card transfer complete and DMA complete. */
|
|
|
|
#define SDCARD_XFRDONE_FLAG (1)
|
|
#define SDCARD_DMADONE_FLAG (2)
|
|
#define SDCARD_ALLDONE (3)
|
|
|
|
/* Card debounce time. Number of host clocks (SD_CLK) used by debounce
|
|
* filter logic for card detect. typical debounce time is 5-25 ms.
|
|
*
|
|
* Eg. Fsd = 44MHz, ticks = 660,000
|
|
*/
|
|
|
|
#define DEBOUNCE_TICKS (15 * BOARD_SDMMC_FREQUENCY / 1000)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct sdmmc_dma_s
|
|
{
|
|
volatile uint32_t des0; /* Control and status */
|
|
volatile uint32_t des1; /* Buffer size(s) */
|
|
volatile uint32_t des2; /* Buffer address pointer 1 */
|
|
volatile uint32_t des3; /* Buffer address pointer 2 */
|
|
};
|
|
|
|
/* This structure defines the state of the LPC54XX SD card interface */
|
|
|
|
struct lpc54_dev_s
|
|
{
|
|
struct sdio_dev_s dev; /* Standard, base SD card interface */
|
|
|
|
/* LPC54XX-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 */
|
|
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
|
|
WDOG_ID waitwdog; /* Watchdog that handles event timeouts */
|
|
|
|
/* Callback support */
|
|
|
|
sdio_statset_t cdstatus; /* Card status */
|
|
sdio_eventset_t cbevents; /* Set of events to be cause callbacks */
|
|
worker_t callback; /* Registered callback function */
|
|
void *cbarg; /* Registered callback argument */
|
|
struct work_s cbwork; /* Callback work queue structure */
|
|
|
|
/* Interrupt mode data transfer support */
|
|
|
|
uint32_t *buffer; /* Address of current R/W buffer */
|
|
uint32_t xfrmask; /* Interrupt enables for data transfer */
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
uint32_t dmamask; /* Interrupt enables for DMA transfer */
|
|
#endif
|
|
ssize_t remaining; /* Number of bytes remaining in the transfer */
|
|
bool wrdir; /* True: Writing False: Reading */
|
|
|
|
/* DMA data transfer support */
|
|
|
|
bool widebus; /* Required for DMA support */
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
bool dmamode; /* true: DMA mode transfer */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_REGDEBUG
|
|
static uint32_t lpc54_getreg(uint32_t addr);
|
|
static void lpc54_putreg(uint32_t val, uint32_t addr);
|
|
static void lpc54_checksetup(void);
|
|
#else
|
|
# define lpc54_getreg(addr) getreg32(addr)
|
|
# define lpc54_putreg(val,addr) putreg32(val,addr)
|
|
# define lpc54_checksetup()
|
|
#endif
|
|
|
|
/* Low-level helpers ********************************************************/
|
|
|
|
static void lpc54_takesem(struct lpc54_dev_s *priv);
|
|
#define lpc54_givesem(priv) (sem_post(&priv->waitsem))
|
|
static inline void lpc54_setclock(uint32_t clkdiv);
|
|
static inline void lpc54_sdcard_clock(bool enable);
|
|
static int lpc54_ciu_sendcmd(uint32_t cmd, uint32_t arg);
|
|
static void lpc54_enable_ints(struct lpc54_dev_s *priv);
|
|
static void lpc54_disable_allints(struct lpc54_dev_s *priv);
|
|
static void lpc54_config_waitints(struct lpc54_dev_s *priv, uint32_t waitmask,
|
|
sdio_eventset_t waitevents, sdio_eventset_t wkupevents);
|
|
static void lpc54_config_xfrints(struct lpc54_dev_s *priv, uint32_t xfrmask);
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
static void lpc54_config_dmaints(struct lpc54_dev_s *priv, uint32_t xfrmask,
|
|
uint32_t dmamask);
|
|
#endif
|
|
|
|
/* Data Transfer Helpers ****************************************************/
|
|
|
|
static void lpc54_eventtimeout(int argc, uint32_t arg);
|
|
static void lpc54_endwait(struct lpc54_dev_s *priv, sdio_eventset_t wkupevent);
|
|
static void lpc54_endtransfer(struct lpc54_dev_s *priv, sdio_eventset_t wkupevent);
|
|
|
|
/* Interrupt Handling *******************************************************/
|
|
|
|
static int lpc54_sdmmc_interrupt(int irq, void *context, FAR void *arg);
|
|
|
|
/* SD Card Interface Methods ************************************************/
|
|
|
|
/* Mutual exclusion */
|
|
|
|
#ifdef CONFIG_SDIO_MUXBUS
|
|
static int lpc54_lock(FAR struct sdio_dev_s *dev, bool lock);
|
|
#endif
|
|
|
|
/* Initialization/setup */
|
|
|
|
static void lpc54_reset(FAR struct sdio_dev_s *dev);
|
|
static sdio_capset_t lpc54_capabilities(FAR struct sdio_dev_s *dev);
|
|
static uint8_t lpc54_status(FAR struct sdio_dev_s *dev);
|
|
static void lpc54_widebus(FAR struct sdio_dev_s *dev, bool enable);
|
|
static void lpc54_clock(FAR struct sdio_dev_s *dev,
|
|
enum sdio_clock_e rate);
|
|
static int lpc54_attach(FAR struct sdio_dev_s *dev);
|
|
|
|
/* Command/Status/Data Transfer */
|
|
|
|
static int lpc54_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t arg);
|
|
static int lpc54_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
|
|
size_t nbytes);
|
|
static int lpc54_sendsetup(FAR struct sdio_dev_s *dev,
|
|
FAR const uint8_t *buffer, uint32_t nbytes);
|
|
static int lpc54_cancel(FAR struct sdio_dev_s *dev);
|
|
|
|
static int lpc54_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
|
|
static int lpc54_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t *rshort);
|
|
static int lpc54_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t rlong[4]);
|
|
static int lpc54_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t *rshort);
|
|
static int lpc54_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t *rnotimpl);
|
|
|
|
/* EVENT handler */
|
|
|
|
static void lpc54_waitenable(FAR struct sdio_dev_s *dev,
|
|
sdio_eventset_t eventset);
|
|
static sdio_eventset_t
|
|
lpc54_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout);
|
|
static void lpc54_callbackenable(FAR struct sdio_dev_s *dev,
|
|
sdio_eventset_t eventset);
|
|
static void lpc54_callback(struct lpc54_dev_s *priv);
|
|
static int lpc54_registercallback(FAR struct sdio_dev_s *dev,
|
|
worker_t callback, void *arg);
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
/* DMA */
|
|
|
|
static int lpc54_dmarecvsetup(FAR struct sdio_dev_s *dev,
|
|
FAR uint8_t *buffer, size_t buflen);
|
|
static int lpc54_dmasendsetup(FAR struct sdio_dev_s *dev,
|
|
FAR const uint8_t *buffer, size_t buflen);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
struct lpc54_dev_s g_scard_dev =
|
|
{
|
|
.dev =
|
|
{
|
|
#ifdef CONFIG_SDIO_MUXBUS
|
|
.lock = lpc54_lock,
|
|
#endif
|
|
.reset = lpc54_reset,
|
|
.capabilities = lpc54_capabilities,
|
|
.status = lpc54_status,
|
|
.widebus = lpc54_widebus,
|
|
.clock = lpc54_clock,
|
|
.attach = lpc54_attach,
|
|
.sendcmd = lpc54_sendcmd,
|
|
.recvsetup = lpc54_recvsetup,
|
|
.sendsetup = lpc54_sendsetup,
|
|
.cancel = lpc54_cancel,
|
|
.waitresponse = lpc54_waitresponse,
|
|
.recvR1 = lpc54_recvshortcrc,
|
|
.recvR2 = lpc54_recvlong,
|
|
.recvR3 = lpc54_recvshort,
|
|
.recvR4 = lpc54_recvnotimpl,
|
|
.recvR5 = lpc54_recvnotimpl,
|
|
.recvR6 = lpc54_recvshortcrc,
|
|
.recvR7 = lpc54_recvshort,
|
|
.waitenable = lpc54_waitenable,
|
|
.eventwait = lpc54_eventwait,
|
|
.callbackenable = lpc54_callbackenable,
|
|
.registercallback = lpc54_registercallback,
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
.dmarecvsetup = lpc54_dmarecvsetup,
|
|
.dmasendsetup = lpc54_dmasendsetup,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
static struct sdmmc_dma_s g_sdmmc_dmadd[1 + (0x10000 / MCI_DMADES1_MAXTR)];
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_getreg
|
|
*
|
|
* Description:
|
|
* This function may to used to intercept an monitor all register accesses.
|
|
* Clearly this is nothing you would want to do unless you are debugging
|
|
* this driver.
|
|
*
|
|
* Input Parameters:
|
|
* addr - The register address to read
|
|
*
|
|
* Returned Value:
|
|
* The value read from the register
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_REGDEBUG
|
|
static uint32_t lpc54_getreg(uint32_t addr)
|
|
{
|
|
static uint32_t prevaddr = 0;
|
|
static uint32_t preval = 0;
|
|
static uint32_t count = 0;
|
|
|
|
/* Read the value from the register */
|
|
|
|
uint32_t val = getreg32(addr);
|
|
|
|
/* Is this the same value that we read from the same register last time?
|
|
* Are we polling the register? If so, suppress some of the output.
|
|
*/
|
|
|
|
if (addr == prevaddr && val == preval)
|
|
{
|
|
if (count == 0xffffffff || ++count > 3)
|
|
{
|
|
if (count == 4)
|
|
{
|
|
mcinfo("...\n");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
}
|
|
|
|
/* No this is a new address or value */
|
|
|
|
else
|
|
{
|
|
/* Did we print "..." for the previous value? */
|
|
|
|
if (count > 3)
|
|
{
|
|
/* Yes.. then show how many times the value repeated */
|
|
|
|
mcinfo("[repeats %d more times]\n", count-3);
|
|
}
|
|
|
|
/* Save the new address, value, and count */
|
|
|
|
prevaddr = addr;
|
|
preval = val;
|
|
count = 1;
|
|
}
|
|
|
|
/* Show the register value read */
|
|
|
|
mcinfo("%08x->%08x\n", addr, val);
|
|
return val;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_putreg
|
|
*
|
|
* Description:
|
|
* This function may to used to intercept an monitor all register accesses.
|
|
* Clearly this is nothing you would want to do unless you are debugging
|
|
* this driver.
|
|
*
|
|
* Input Parameters:
|
|
* val - The value to write to the register
|
|
* addr - The register address to read
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_REGDEBUG
|
|
static void lpc54_putreg(uint32_t val, uint32_t addr)
|
|
{
|
|
/* Show the register value being written */
|
|
|
|
mcinfo("%08x<-%08x\n", addr, val);
|
|
|
|
/* Write the value */
|
|
|
|
putreg32(val, addr);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_takesem
|
|
*
|
|
* Description:
|
|
* Take the wait semaphore (handling false alarm wakeups due to the receipt
|
|
* of signals).
|
|
*
|
|
* Input Parameters:
|
|
* dev - Instance of the SD card device driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_takesem(struct lpc54_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.
|
|
*/
|
|
|
|
DEBUGASSERT(errno == EINTR);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_setclock
|
|
*
|
|
* Description:
|
|
* Define the new clock frequency
|
|
*
|
|
* Input Parameters:
|
|
* clkdiv - A new division value to generate the needed frequency.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lpc54_setclock(uint32_t clkdiv)
|
|
{
|
|
mcinfo("clkdiv=%08lx\n", (unsigned long)clkdiv);
|
|
|
|
/* Disable the clock before setting frequency */
|
|
|
|
lpc54_sdcard_clock(false);
|
|
|
|
/* Inform CIU */
|
|
|
|
lpc54_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0);
|
|
|
|
/* Set Divider0 to desired value */
|
|
|
|
lpc54_putreg(clkdiv, LPC54_SDMMC_CLKDIV);
|
|
|
|
/* Inform CIU */
|
|
|
|
lpc54_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0);
|
|
|
|
/* Enable the clock */
|
|
|
|
lpc54_sdcard_clock(true);
|
|
|
|
/* Inform CIU */
|
|
|
|
lpc54_ciu_sendcmd(SDMMC_CMD_UPDCLOCK | SDMMC_CMD_WAITPREV, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_sdcard_clock
|
|
*
|
|
* Description: Enable/Disable the SDCard clock
|
|
*
|
|
* Input Parameters:
|
|
* enable - False = clock disabled; True = clock enabled.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline void lpc54_sdcard_clock(bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
lpc54_putreg(SDMMC_CLKENA_ENABLE, LPC54_SDMMC_CLKENA);
|
|
}
|
|
else
|
|
{
|
|
lpc54_putreg(0, LPC54_SDMMC_CLKENA);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_ciu_sendcmd
|
|
*
|
|
* Description:
|
|
* Function to send command to Card interface unit (CIU)
|
|
*
|
|
* Input Parameters:
|
|
* cmd - The command to be executed
|
|
* arg - The argument to use with the command.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_ciu_sendcmd(uint32_t cmd, uint32_t arg)
|
|
{
|
|
volatile int32_t tmo = SDCARD_CMDTIMEOUT;
|
|
|
|
mcinfo("cmd=%04lx arg=%04lx\n", (unsigned long)cmd, (unsigned long)arg);
|
|
|
|
/* Set command arg reg */
|
|
|
|
lpc54_putreg(arg, LPC54_SDMMC_CMDARG);
|
|
lpc54_putreg(SDMMC_CMD_STARTCMD | cmd, LPC54_SDMMC_CMD);
|
|
|
|
/* Poll until command is accepted by the CIU */
|
|
|
|
while (--tmo > 0 && (lpc54_getreg(LPC54_SDMMC_CMD) & SDMMC_CMD_STARTCMD) != 0)
|
|
{
|
|
}
|
|
|
|
return (tmo < 1) ? 1 : 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_enable_ints
|
|
*
|
|
* Description:
|
|
* Enable/disable SD card interrupts per functional settings.
|
|
*
|
|
* Input Parameters:
|
|
* priv - A reference to the SD card device state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_enable_ints(struct lpc54_dev_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
mcinfo("waitmask=%04lx xfrmask=%04lx dmamask=%04lx RINTSTS=%08lx\n",
|
|
(unsigned long)priv->waitmask, (unsigned long)priv->xfrmask,
|
|
(unsigned long)priv->dmamask,
|
|
(unsigned long)lpc54_getreg(LPC54_SDMMC_RINTSTS));
|
|
|
|
/* Enable DMA-related interrupts */
|
|
|
|
lpc54_putreg(priv->dmamask, LPC54_SDMMC_IDINTEN);
|
|
|
|
#else
|
|
mcinfo("waitmask=%04lx xfrmask=%04lx RINTSTS=%08lx\n",
|
|
(unsigned long)priv->waitmask, (unsigned long)priv->xfrmask,
|
|
(unsigned long)lpc54_getreg(LPC54_SDMMC_RINTSTS));
|
|
#endif
|
|
|
|
/* Enable SDMMC interrupts */
|
|
|
|
regval = priv->xfrmask | priv->waitmask | SDCARD_INT_CDET;
|
|
lpc54_putreg(regval, LPC54_SDMMC_INTMASK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_disable_allints
|
|
*
|
|
* Description:
|
|
* Disable all SD card interrupts.
|
|
*
|
|
* Input Parameters:
|
|
* priv - A reference to the SD card device state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_disable_allints(struct lpc54_dev_s *priv)
|
|
{
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
/* Disable DMA-related interrupts */
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_IDINTEN);
|
|
priv->dmamask = 0;
|
|
#endif
|
|
|
|
/* Disable all SDMMC interrupts (except card detect) */
|
|
|
|
lpc54_putreg(SDCARD_INT_CDET, LPC54_SDMMC_INTMASK);
|
|
priv->waitmask = 0;
|
|
priv->xfrmask = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_config_waitints
|
|
*
|
|
* Description:
|
|
* Enable/disable SD card interrupts needed to suport the wait function
|
|
*
|
|
* Input Parameters:
|
|
* priv - A reference to the SD card device state structure
|
|
* waitmask - The set of bits in the SD card INTMASK register to set
|
|
* waitevents - Waited for events
|
|
* wkupevent - Wake-up events
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_config_waitints(struct lpc54_dev_s *priv, uint32_t waitmask,
|
|
sdio_eventset_t waitevents,
|
|
sdio_eventset_t wkupevent)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
mcinfo("waitevents=%04x wkupevent=%04x\n",
|
|
(unsigned)waitevents, (unsigned)wkupevent);
|
|
|
|
/* 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->waitmask = waitmask;
|
|
|
|
lpc54_enable_ints(priv);
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_config_xfrints
|
|
*
|
|
* Description:
|
|
* Enable SD card interrupts needed to support the data transfer event
|
|
*
|
|
* Input Parameters:
|
|
* priv - A reference to the SD card device state structure
|
|
* xfrmask - The set of bits in the SD card MASK register to set
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_config_xfrints(struct lpc54_dev_s *priv, uint32_t xfrmask)
|
|
{
|
|
irqstate_t flags;
|
|
flags = enter_critical_section();
|
|
|
|
priv->xfrmask = xfrmask;
|
|
lpc54_enable_ints(priv);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_config_dmaints
|
|
*
|
|
* Description:
|
|
* Enable DMA transfer interrupts
|
|
*
|
|
* Input Parameters:
|
|
* priv - A reference to the SD card device state structure
|
|
* dmamask - The set of bits in the SD card MASK register to set
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
static void lpc54_config_dmaints(struct lpc54_dev_s *priv, uint32_t xfrmask,
|
|
uint32_t dmamask)
|
|
{
|
|
irqstate_t flags;
|
|
flags = enter_critical_section();
|
|
|
|
priv->xfrmask = xfrmask;
|
|
priv->dmamask = dmamask;
|
|
lpc54_enable_ints(priv);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 lpc54_eventtimeout(int argc, uint32_t arg)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)arg;
|
|
|
|
mcinfo("argc=%d, arg=%08lx\n", argc, (unsigned long)arg);
|
|
|
|
/* There is always race conditions with timer expirations. */
|
|
|
|
DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 || priv->wkupevent != 0);
|
|
|
|
/* Is a data transfer complete event expected? */
|
|
|
|
if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
|
|
{
|
|
/* Yes.. wake up any waiting threads */
|
|
|
|
lpc54_endwait(priv, SDIOWAIT_TIMEOUT);
|
|
mcerr("ERROR: Timeout: remaining: %d\n", priv->remaining);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_endwait
|
|
*
|
|
* Description:
|
|
* Wake up a waiting thread if the waited-for event has occurred.
|
|
*
|
|
* Input Parameters:
|
|
* priv - An instance of the SD card 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 lpc54_endwait(struct lpc54_dev_s *priv, sdio_eventset_t wkupevent)
|
|
{
|
|
mcinfo("wkupevent=%04x\n", (unsigned)wkupevent);
|
|
|
|
/* Cancel the watchdog timeout */
|
|
|
|
(void)wd_cancel(priv->waitwdog);
|
|
|
|
/* Disable event-related interrupts */
|
|
|
|
lpc54_config_waitints(priv, 0, 0, wkupevent);
|
|
|
|
/* Wake up the waiting thread */
|
|
|
|
lpc54_givesem(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_endtransfer
|
|
*
|
|
* Description:
|
|
* Terminate a transfer with the provided status. This function is called
|
|
* only from the SD card interrupt handler when end-of-transfer conditions
|
|
* are detected.
|
|
*
|
|
* Input Parameters:
|
|
* priv - An instance of the SD card 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 lpc54_endtransfer(struct lpc54_dev_s *priv, sdio_eventset_t wkupevent)
|
|
{
|
|
mcinfo("wkupevent=%04x\n", (unsigned)wkupevent);
|
|
|
|
/* Disable all transfer related interrupts */
|
|
|
|
lpc54_config_xfrints(priv, 0);
|
|
|
|
/* Clearing pending interrupt status on all transfer related interrupts */
|
|
|
|
lpc54_putreg(priv->waitmask, LPC54_SDMMC_RINTSTS);
|
|
|
|
/* Mark the transfer finished */
|
|
|
|
priv->remaining = 0;
|
|
|
|
/* Is a thread wait for these data transfer complete events? */
|
|
|
|
if ((priv->waitevents & wkupevent) != 0)
|
|
{
|
|
/* Yes.. wake up any waiting threads */
|
|
|
|
lpc54_endwait(priv, wkupevent);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_sdmmc_interrupt
|
|
*
|
|
* Description:
|
|
* SD card interrupt handler
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_sdmmc_interrupt(int irq, void *context, FAR void *arg)
|
|
{
|
|
struct lpc54_dev_s *priv = &g_scard_dev;
|
|
uint32_t enabled;
|
|
uint32_t pending;
|
|
|
|
/* Loop while there are pending interrupts. Check the SD card status
|
|
* 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 STA and MASK register). If there are non-zero
|
|
* bits remaining, then we have work to do here.
|
|
*/
|
|
|
|
while ((enabled = lpc54_getreg(LPC54_SDMMC_MINTSTS)) != 0)
|
|
{
|
|
/* Clear pending status */
|
|
|
|
lpc54_putreg(enabled, LPC54_SDMMC_RINTSTS);
|
|
|
|
#ifdef CONFIG_MMCSD_HAVE_CARDDETECT
|
|
/* Handle in card detection events ************************************/
|
|
|
|
if ((enabled & SDMMC_INT_CDET) != 0)
|
|
{
|
|
sdio_statset_t cdstatus;
|
|
|
|
/* Update card status */
|
|
|
|
cdstatus = priv->cdstatus;
|
|
if ((lpc54_getreg(LPC54_SDMMC_CDETECT) & SDMMC_CDETECT_NOTPRESENT) == 0)
|
|
{
|
|
priv->cdstatus |= SDIO_STATUS_PRESENT;
|
|
|
|
#ifdef CONFIG_MMCSD_HAVE_WRITEPROTECT
|
|
if ((lpc54_getreg(LPC54_SDMMC_WRTPRT) & SDMMC_WRTPRT_PROTECTED) != 0)
|
|
{
|
|
priv->cdstatus |= SDIO_STATUS_WRPROTECTED;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED;
|
|
}
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_PWRCTRL
|
|
/* Enable/ power to the SD card */
|
|
|
|
lpc54_putreg(SDMMC_PWREN, LPC54_SDMMC_PWREN);
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
priv->cdstatus &= ~(SDIO_STATUS_PRESENT | SDIO_STATUS_WRPROTECTED);
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_PWRCTRL
|
|
/* Disable power to the SD card */
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_PWREN);
|
|
#endif
|
|
}
|
|
|
|
mcinfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus);
|
|
|
|
/* Perform any requested callback if the status has changed */
|
|
|
|
if (cdstatus != priv->cdstatus)
|
|
{
|
|
lpc54_callback(priv);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Handle idata transfer events ***************************************/
|
|
|
|
pending = enabled & priv->xfrmask;
|
|
if (pending != 0)
|
|
{
|
|
/* Handle data request events */
|
|
|
|
if ((pending & SDMMC_INT_TXDR) != 0)
|
|
{
|
|
uint32_t status;
|
|
|
|
/* Transfer data to the TX FIFO */
|
|
|
|
mcinfo("Write FIFO\n");
|
|
DEBUGASSERT(priv->wrdir);
|
|
|
|
for (status = lpc54_getreg(LPC54_SDMMC_STATUS);
|
|
(status & SDMMC_STATUS_FIFOFULL) == 0 &&
|
|
priv->remaining > 0;
|
|
status = lpc54_getreg(LPC54_SDMMC_STATUS))
|
|
{
|
|
lpc54_putreg(*priv->buffer, LPC54_SDMMC_DATA);
|
|
priv->buffer++;
|
|
priv->remaining -= 4;
|
|
}
|
|
|
|
/* If all of the data has been transferred to the FIFO, then
|
|
* disable further TX data requests and wait for the data end
|
|
* event.
|
|
*/
|
|
|
|
if (priv->remaining <= 0)
|
|
{
|
|
uint32_t intmask = lpc54_getreg(LPC54_SDMMC_INTMASK);
|
|
intmask &= ~SDMMC_INT_TXDR;
|
|
lpc54_putreg(intmask, LPC54_SDMMC_INTMASK);
|
|
|
|
priv->xfrmask &= ~SDMMC_INT_TXDR;
|
|
}
|
|
}
|
|
else if ((pending & SDMMC_INT_RXDR) != 0)
|
|
{
|
|
uint32_t status;
|
|
|
|
/* Transfer data from the RX FIFO */
|
|
|
|
mcinfo("Read from FIFO\n");
|
|
DEBUGASSERT(!priv->wrdir);
|
|
|
|
for (status = lpc54_getreg(LPC54_SDMMC_STATUS);
|
|
(status & SDMMC_STATUS_FIFOEMPTY) == 0 &&
|
|
priv->remaining > 0;
|
|
status = lpc54_getreg(LPC54_SDMMC_STATUS))
|
|
{
|
|
*priv->buffer = lpc54_getreg(LPC54_SDMMC_DATA);
|
|
priv->buffer++;
|
|
priv->remaining -= 4;
|
|
}
|
|
|
|
/* If all of the data has been transferred to the FIFO, then
|
|
* just force DTO event processing (the DTO interrupt is not
|
|
* actually even enabled in this use case).
|
|
*/
|
|
|
|
if (priv->remaining <= 0)
|
|
{
|
|
/* Force the DTO event */
|
|
|
|
pending |= SDMMC_INT_DTO;
|
|
}
|
|
}
|
|
|
|
/* Check for transfer errors */
|
|
/* Handle data block send/receive CRC failure */
|
|
|
|
if ((pending & SDMMC_INT_DCRC) != 0)
|
|
{
|
|
/* Terminate the transfer with an error */
|
|
|
|
mcerr("ERROR: Data CRC failure, pending=%08x remaining: %d\n",
|
|
pending, priv->remaining);
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
|
|
}
|
|
|
|
/* Handle data timeout error */
|
|
|
|
else if ((pending & SDMMC_INT_DRTO) != 0)
|
|
{
|
|
/* Terminate the transfer with an error */
|
|
|
|
mcerr("ERROR: Data timeout, pending=%08x remaining: %d\n",
|
|
pending, priv->remaining);
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT);
|
|
}
|
|
|
|
/* Handle RX FIFO overrun error */
|
|
|
|
else if ((pending & SDMMC_INT_FRUN) != 0)
|
|
{
|
|
/* Terminate the transfer with an error */
|
|
|
|
mcerr("ERROR: RX FIFO overrun, pending=%08x remaining: %d\n",
|
|
pending, priv->remaining);
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
|
|
}
|
|
|
|
/* Handle TX FIFO underrun error */
|
|
|
|
else if ((pending & SDMMC_INT_FRUN) != 0)
|
|
{
|
|
/* Terminate the transfer with an error */
|
|
|
|
mcerr("ERROR: TX FIFO underrun, pending=%08x remaining: %d\n",
|
|
pending, priv->remaining);
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
|
|
}
|
|
|
|
/* Handle start bit error */
|
|
|
|
else if ((pending & SDMMC_INT_SBE) != 0)
|
|
{
|
|
/* Terminate the transfer with an error */
|
|
|
|
mcerr("ERROR: Start bit, pending=%08x remaining: %d\n",
|
|
pending, priv->remaining);
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
|
|
}
|
|
|
|
/* Handle data end events. Note that RXDR may accompany DTO, DTO
|
|
* will be set on received while there is still data in the FIFO.
|
|
* So for the case of receiving, we don't actually even enable the
|
|
* DTO interrupt.
|
|
*/
|
|
|
|
else if ((pending & SDMMC_INT_DTO) != 0)
|
|
{
|
|
/* Finish the transfer */
|
|
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
|
|
}
|
|
}
|
|
|
|
/* Handle wait events *************************************************/
|
|
|
|
pending = enabled & priv->waitmask;
|
|
if (pending != 0)
|
|
{
|
|
/* Is this a response error event? */
|
|
|
|
if ((pending & SDCARD_INT_RESPERR) != 0)
|
|
{
|
|
/* If response errors are enabled, then we must certainly be
|
|
* waiting for a response.
|
|
*/
|
|
|
|
DEBUGASSERT((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0);
|
|
|
|
/* Wake the thread up */
|
|
|
|
mcerr("ERROR: Response error, pending=%08x\n", pending);
|
|
lpc54_endwait(priv, SDIOWAIT_RESPONSEDONE | SDIOWAIT_ERROR);
|
|
}
|
|
|
|
/* Is this a command (plus response) completion event? */
|
|
|
|
else if ((pending & SDMMC_INT_CDONE) != 0)
|
|
{
|
|
/* Yes.. Is their a thread waiting for response done? */
|
|
|
|
if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0)
|
|
{
|
|
/* Yes.. wake the thread up */
|
|
|
|
lpc54_endwait(priv, SDIOWAIT_RESPONSEDONE);
|
|
}
|
|
|
|
/* NO.. Is their a thread waiting for command done? */
|
|
|
|
else if ((priv->waitevents & SDIOWAIT_CMDDONE) != 0)
|
|
{
|
|
/* Yes.. wake the thread up */
|
|
|
|
lpc54_endwait(priv, SDIOWAIT_CMDDONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
/* DMA error events *******************************************************/
|
|
|
|
pending = lpc54_getreg(LPC54_SDMMC_IDSTS);
|
|
if ((pending & priv->dmamask) != 0)
|
|
{
|
|
mcerr("ERROR: IDTS=%08lx\n", (unsigned long)pending);
|
|
|
|
/* Clear the pending interrupts */
|
|
|
|
lpc54_putreg(pending, LPC54_SDMMC_IDSTS);
|
|
|
|
/* Abort the transfer */
|
|
|
|
lpc54_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
|
|
}
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_lock
|
|
*
|
|
* Description:
|
|
* Locks the bus. Function calls low-level multiplexed bus routines to
|
|
* resolve bus requests and acknowledgment issues.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card 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 lpc54_lock(FAR struct sdio_dev_s *dev, bool lock)
|
|
{
|
|
/* Single SD card instance so there is only one possibility. The multiplex
|
|
* bus is part of board support package.
|
|
*/
|
|
|
|
lpc54_muxbus_sdio_lock(lock);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_reset
|
|
*
|
|
* Description:
|
|
* Reset the SD card controller. Undo all setup and initialization.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_reset(FAR struct sdio_dev_s *dev)
|
|
{
|
|
FAR struct lpc54_dev_s *priv = (FAR struct lpc54_dev_s *)dev;
|
|
irqstate_t flags;
|
|
uint32_t regval;
|
|
|
|
mcinfo("Resetting...\n");
|
|
|
|
flags = enter_critical_section();
|
|
|
|
/* Reset DMA controller internal registers. */
|
|
|
|
lpc54_putreg(SDMMC_BMOD_SWR, LPC54_SDMMC_BMOD);
|
|
|
|
/* Reset all blocks */
|
|
|
|
lpc54_putreg(SDMMC_CTRL_CNTLRRESET | SDMMC_CTRL_FIFORESET |
|
|
SDMMC_CTRL_DMARESET, LPC54_SDMMC_CTRL);
|
|
|
|
while ((lpc54_getreg(LPC54_SDMMC_CTRL) &
|
|
(SDMMC_CTRL_CNTLRRESET | SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET)) != 0)
|
|
{
|
|
}
|
|
|
|
/* Reset data */
|
|
|
|
priv->waitevents = 0; /* Set of events to be waited for */
|
|
priv->waitmask = 0; /* Interrupt enables for event waiting */
|
|
priv->wkupevent = 0; /* The event that caused the wakeup */
|
|
|
|
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->xfrmask = 0; /* Interrupt enables for data transfer */
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
priv->dmamask = 0; /* Interrupt enables for DMA transfer */
|
|
#endif
|
|
|
|
/* DMA data transfer support */
|
|
|
|
priv->widebus = true; /* Required for DMA support */
|
|
priv->cdstatus = 0; /* Card status is unknown */
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
priv->dmamode = false; /* true: DMA mode transfer */
|
|
#endif
|
|
|
|
/* Select 1-bit wide bus */
|
|
|
|
lpc54_putreg(SDMMC_CTYPE_WIDTH1, LPC54_SDMMC_CTYPE);
|
|
|
|
/* Enable interrupts */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_INTENABLE;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
/* Disable Interrupts except for card detection. */
|
|
|
|
lpc54_putreg(SDCARD_INT_CDET, LPC54_SDMMC_INTMASK);
|
|
|
|
#ifdef CONFIG_MMCSD_HAVE_CARDDETECT
|
|
/* Set the card debounce time. Number of host clocks (SD_CLK) used by
|
|
* debounce filter logic for card detect. typical debounce time is 5-25
|
|
* ms.
|
|
*/
|
|
|
|
lpc54_putreg(DEBOUNCE_TICKS, LPC54_SDMMC_DEBNCE);
|
|
#endif
|
|
|
|
/* Clear to Interrupts */
|
|
|
|
lpc54_putreg(0xffffffff, LPC54_SDMMC_RINTSTS);
|
|
|
|
/* Define MAX Timeout */
|
|
|
|
lpc54_putreg(SDCARD_LONGTIMEOUT, LPC54_SDMMC_TMOUT);
|
|
|
|
/* Disable clock to CIU (needs latch) */
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_CLKENA);
|
|
leave_critical_section(flags);
|
|
|
|
#if defined(CONFIG_LPC54_SDMMC_PWRCTRL) && !defined(CONFIG_MMCSD_HAVE_CARDDETECT)
|
|
/* Enable power to the SD card */
|
|
|
|
lpc54_putreg(SDMMC_PWREN, LPC54_SDMMC_PWREN);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_capabilities
|
|
*
|
|
* Description:
|
|
* Get capabilities (and limitations) of the SDIO driver (optional)
|
|
*
|
|
* Input Parameters:
|
|
* dev - Device-specific state data
|
|
*
|
|
* Returned Value:
|
|
* Returns a bitset of status values (see SDIO_CAPS_* defines)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static sdio_capset_t lpc54_capabilities(FAR struct sdio_dev_s *dev)
|
|
{
|
|
sdio_capset_t caps = 0;
|
|
|
|
caps |= SDIO_CAPS_DMABEFOREWRITE;
|
|
|
|
#ifdef CONFIG_SDIO_WIDTH_D1_ONLY
|
|
caps |= SDIO_CAPS_1BIT_ONLY;
|
|
#endif
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
caps |= SDIO_CAPS_DMASUPPORTED;
|
|
#endif
|
|
|
|
return caps;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_status
|
|
*
|
|
* Description:
|
|
* Get SD card status.
|
|
*
|
|
* Input Parameters:
|
|
* dev - Device-specific state data
|
|
*
|
|
* Returned Value:
|
|
* Returns a bitset of status values (see lpc54_status_* defines)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static sdio_statset_t lpc54_status(FAR struct sdio_dev_s *dev)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
|
|
mcinfo("cdstatus=%02x\n", priv->cdstatus);
|
|
|
|
return priv->cdstatus;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 SD card device interface
|
|
* wide - true: wide bus (4-bit) bus mode enabled
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_widebus(FAR struct sdio_dev_s *dev, bool wide)
|
|
{
|
|
mcinfo("wide=%d\n", wide);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_clock
|
|
*
|
|
* Description:
|
|
* Enable/disable SD card clocking
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
* rate - Specifies the clocking to use (see enum sdio_clock_e)
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
uint8_t clkdiv;
|
|
uint8_t ctype;
|
|
bool enabled = false;
|
|
bool widebus = false;
|
|
|
|
switch (rate)
|
|
{
|
|
/* Disable clocking (with default ID mode divisor) */
|
|
|
|
default:
|
|
case CLOCK_SDIO_DISABLED:
|
|
clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_INIT);
|
|
ctype = SDCARD_BUS_D1;
|
|
enabled = false;
|
|
widebus = false;
|
|
return;
|
|
break;
|
|
|
|
/* Enable in initial ID mode clocking (<400KHz) */
|
|
|
|
case CLOCK_IDMODE:
|
|
clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_INIT);
|
|
ctype = SDCARD_BUS_D1;
|
|
enabled = true;
|
|
widebus = false;
|
|
break;
|
|
|
|
/* Enable in MMC normal operation clocking */
|
|
|
|
case CLOCK_MMC_TRANSFER:
|
|
clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_MMCXFR);
|
|
ctype = SDCARD_BUS_D1;
|
|
enabled = true;
|
|
widebus = false;
|
|
break;
|
|
|
|
/* SD normal operation clocking (wide 4-bit mode) */
|
|
|
|
case CLOCK_SD_TRANSFER_4BIT:
|
|
#ifndef CONFIG_SDIO_WIDTH_D1_ONLY
|
|
clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_SDWIDEXFR);
|
|
ctype = SDCARD_BUS_D4;
|
|
enabled = true;
|
|
widebus = true;
|
|
break;
|
|
#endif
|
|
|
|
/* SD normal operation clocking (narrow 1-bit mode) */
|
|
|
|
case CLOCK_SD_TRANSFER_1BIT:
|
|
clkdiv = SDMMC_CLKDIV0(BOARD_CLKDIV_SDXFR);
|
|
ctype = SDCARD_BUS_D1;
|
|
enabled = true;
|
|
widebus = false;
|
|
break;
|
|
}
|
|
|
|
/* Setup the card bus width */
|
|
|
|
mcinfo("widebus=%d\n", widebus);
|
|
|
|
priv->widebus = widebus;
|
|
lpc54_putreg(ctype, LPC54_SDMMC_CTYPE);
|
|
|
|
/* Set the new clock frequency division */
|
|
|
|
lpc54_setclock(clkdiv);
|
|
|
|
/* Enable the new clock */
|
|
|
|
lpc54_sdcard_clock(enabled);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_attach
|
|
*
|
|
* Description:
|
|
* Attach and prepare interrupts
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
*
|
|
* Returned Value:
|
|
* OK on success; A negated errno on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_attach(FAR struct sdio_dev_s *dev)
|
|
{
|
|
int ret;
|
|
uint32_t regval;
|
|
|
|
mcinfo("Attaching..\n");
|
|
|
|
/* Attach the SD card interrupt handler */
|
|
|
|
ret = irq_attach(LPC54_IRQ_SDMMC, lpc54_sdmmc_interrupt, NULL);
|
|
if (ret == OK)
|
|
{
|
|
/* Disable all interrupts at the SD card controller and clear static
|
|
* interrupt flags
|
|
*/
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_INTMASK);
|
|
lpc54_putreg(SDMMC_INT_ALL, LPC54_SDMMC_RINTSTS);
|
|
|
|
/* Enable Interrupts to happen when the INTMASK is activated */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_INTENABLE;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
/* Enable card detection interrupts */
|
|
|
|
lpc54_putreg(SDCARD_INT_CDET, LPC54_SDMMC_INTMASK);
|
|
|
|
/* Enable SD card interrupts at the NVIC. They can now be enabled at
|
|
* the SD card controller as needed.
|
|
*/
|
|
|
|
up_enable_irq(LPC54_IRQ_SDMMC);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_sendcmd
|
|
*
|
|
* Description:
|
|
* Send the SD card command
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
* cmd - The command to send (32-bits, encoded)
|
|
* arg - 32-bit argument required with some commands
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t arg)
|
|
{
|
|
uint32_t regval = 0;
|
|
uint32_t cmdidx;
|
|
|
|
mcinfo("cmd=%04x arg=%04x\n", cmd, arg);
|
|
|
|
/* The CMD0 needs the SENDINIT CMD */
|
|
|
|
if (cmd == 0)
|
|
{
|
|
regval |= SDMMC_CMD_SENDINIT;
|
|
}
|
|
|
|
/* Is this a Read/Write Transfer Command ? */
|
|
|
|
if ((cmd & MMCSD_WRDATAXFR) == MMCSD_WRDATAXFR)
|
|
{
|
|
regval |= SDMMC_CMD_DATAXFREXPTD | SDMMC_CMD_WRITE | SDMMC_CMD_WAITPREV;
|
|
}
|
|
else if ((cmd & MMCSD_RDDATAXFR) == MMCSD_RDDATAXFR)
|
|
{
|
|
regval |= SDMMC_CMD_DATAXFREXPTD | SDMMC_CMD_WAITPREV;
|
|
}
|
|
|
|
/* Set WAITRESP bits */
|
|
|
|
switch (cmd & MMCSD_RESPONSE_MASK)
|
|
{
|
|
case MMCSD_NO_RESPONSE:
|
|
regval |= SDMMC_CMD_NORESPONSE;
|
|
break;
|
|
|
|
case MMCSD_R1B_RESPONSE:
|
|
regval |= SDMMC_CMD_WAITPREV;
|
|
case MMCSD_R1_RESPONSE:
|
|
case MMCSD_R3_RESPONSE:
|
|
case MMCSD_R4_RESPONSE:
|
|
case MMCSD_R5_RESPONSE:
|
|
case MMCSD_R6_RESPONSE:
|
|
case MMCSD_R7_RESPONSE:
|
|
regval |= SDMMC_CMD_SHORTRESPONSE;
|
|
break;
|
|
|
|
case MMCSD_R2_RESPONSE:
|
|
regval |= SDMMC_CMD_LONGRESPONSE;
|
|
break;
|
|
}
|
|
|
|
/* Set the command index */
|
|
|
|
cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
|
|
regval |= cmdidx;
|
|
|
|
mcinfo("cmd: %04x arg: %04x regval: %08x\n", cmd, arg, regval);
|
|
|
|
/* Write the SD card CMD */
|
|
|
|
lpc54_putreg(SDCARD_RESPDONE_CLEAR | SDCARD_CMDDONE_CLEAR,
|
|
LPC54_SDMMC_RINTSTS);
|
|
lpc54_ciu_sendcmd(regval, arg);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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, SDCARD_WAITEVENT
|
|
* will be called to receive the indication that the transfer is complete.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card 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
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
|
|
size_t nbytes)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
uint32_t blocksize;
|
|
uint32_t bytecnt;
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
uint32_t regval;
|
|
#endif
|
|
|
|
mcinfo("nbytes=%ld\n", (long) nbytes);
|
|
|
|
DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
|
|
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
|
|
|
|
/* Save the destination buffer information for use by the interrupt handler */
|
|
|
|
priv->buffer = (uint32_t *)buffer;
|
|
priv->remaining = nbytes;
|
|
priv->wrdir = false;
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
priv->dmamode = false;
|
|
#endif
|
|
|
|
/* Then set up the SD card data path */
|
|
|
|
if (nbytes < 64)
|
|
{
|
|
blocksize = nbytes;
|
|
bytecnt = nbytes;
|
|
}
|
|
else
|
|
{
|
|
blocksize = 64;
|
|
bytecnt = nbytes;
|
|
DEBUGASSERT((nbytes & 0x3f) == 0);
|
|
}
|
|
|
|
lpc54_putreg(blocksize, LPC54_SDMMC_BLKSIZ);
|
|
lpc54_putreg(bytecnt, LPC54_SDMMC_BYTCNT);
|
|
|
|
/* Configure the FIFO so that we will receive the RXDR interrupt whenever
|
|
* there are more than 1 words (at least 8 bytes) in the RX FIFO.
|
|
*/
|
|
|
|
lpc54_putreg(SDMMC_FIFOTH_RXWMARK(1), LPC54_SDMMC_FIFOTH);
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
/* Make sure that internal DMA is disabled */
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_BMOD);
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval &= ~SDMMC_CTRL_INTDMA;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
#endif
|
|
|
|
/* Configure the transfer interrupts */
|
|
|
|
lpc54_config_xfrints(priv, SDCARD_RECV_MASK);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 SDCARD_SENDDATA is called.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card 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
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
|
|
size_t nbytes)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
uint32_t regval;
|
|
#endif
|
|
|
|
mcinfo("nbytes=%ld\n", (long)nbytes);
|
|
|
|
DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
|
|
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
|
|
|
|
/* Save the source buffer information for use by the interrupt handler */
|
|
|
|
priv->buffer = (uint32_t *)buffer;
|
|
priv->remaining = nbytes;
|
|
priv->wrdir = true;
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
priv->dmamode = false;
|
|
#endif
|
|
|
|
/* Configure the FIFO so that we will receive the TXDR interrupt whenever
|
|
* there the TX FIFO is at least half empty.
|
|
*/
|
|
|
|
lpc54_putreg(SDMMC_FIFOTH_TXWMARK(LPC54_TXFIFO_DEPTH / 2),
|
|
LPC54_SDMMC_FIFOTH);
|
|
|
|
#ifdef CONFIG_LPC54_SDMMC_DMA
|
|
/* Make sure that internal DMA is disabled */
|
|
|
|
lpc54_putreg(0, LPC54_SDMMC_BMOD);
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval &= ~SDMMC_CTRL_INTDMA;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
#endif
|
|
|
|
/* Configure the transfer interrupts */
|
|
|
|
lpc54_config_xfrints(priv, SDCARD_SEND_MASK);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_cancel
|
|
*
|
|
* Description:
|
|
* Cancel the data transfer setup of SDCARD_RECVSETUP, SDCARD_SENDSETUP,
|
|
* SDCARD_DMARECVSETUP or SDCARD_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 SD card device interface
|
|
*
|
|
* Returned Value:
|
|
* OK is success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int lpc54_cancel(FAR struct sdio_dev_s *dev)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
|
|
mcinfo("Cancelling..\n");
|
|
|
|
/* Disable all transfer- and event- related interrupts */
|
|
|
|
lpc54_disable_allints(priv);
|
|
|
|
/* Clearing pending interrupt status on all transfer- and event- related
|
|
* interrupts
|
|
*/
|
|
|
|
lpc54_putreg(SDCARD_WAITALL_CLEAR, LPC54_SDMMC_RINTSTS);
|
|
|
|
/* Cancel any watchdog timeout */
|
|
|
|
(void)wd_cancel(priv->waitwdog);
|
|
|
|
/* Mark no transfer in progress */
|
|
|
|
priv->remaining = 0;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_waitresponse
|
|
*
|
|
* Description:
|
|
* Poll-wait for the response to the last command to be ready.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card 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 lpc54_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
|
|
{
|
|
volatile int32_t timeout;
|
|
uint32_t events;
|
|
|
|
mcinfo("cmd=%04x\n", cmd);
|
|
|
|
switch (cmd & MMCSD_RESPONSE_MASK)
|
|
{
|
|
case MMCSD_NO_RESPONSE:
|
|
events = SDCARD_CMDDONE_STA;
|
|
timeout = SDCARD_CMDTIMEOUT;
|
|
break;
|
|
|
|
case MMCSD_R1_RESPONSE:
|
|
case MMCSD_R1B_RESPONSE:
|
|
case MMCSD_R2_RESPONSE:
|
|
case MMCSD_R6_RESPONSE:
|
|
events = (SDCARD_CMDDONE_STA | SDCARD_RESPDONE_STA);
|
|
timeout = SDCARD_LONGTIMEOUT;
|
|
break;
|
|
|
|
case MMCSD_R4_RESPONSE:
|
|
case MMCSD_R5_RESPONSE:
|
|
return -ENOSYS;
|
|
|
|
case MMCSD_R3_RESPONSE:
|
|
case MMCSD_R7_RESPONSE:
|
|
events = (SDCARD_CMDDONE_STA | SDCARD_RESPDONE_STA);
|
|
timeout = SDCARD_CMDTIMEOUT;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
mcinfo("cmd: %04x events: %04x STATUS: %08x RINTSTS: %08x\n",
|
|
cmd, events, lpc54_getreg(LPC54_SDMMC_STATUS),
|
|
lpc54_getreg(LPC54_SDMMC_RINTSTS));
|
|
|
|
/* Then wait for the response (or timeout or error) */
|
|
|
|
while ((lpc54_getreg(LPC54_SDMMC_RINTSTS) & events) != events)
|
|
{
|
|
if (--timeout <= 0)
|
|
{
|
|
mcerr("ERROR: Timeout cmd: %04x events: %04x STA: %08x RINTSTS: %08x\n",
|
|
cmd, events, lpc54_getreg(LPC54_SDMMC_STATUS),
|
|
lpc54_getreg(LPC54_SDMMC_RINTSTS));
|
|
return -ETIMEDOUT;
|
|
}
|
|
else if ((lpc54_getreg(LPC54_SDMMC_RINTSTS) & SDCARD_INT_RESPERR) != 0)
|
|
{
|
|
mcerr("ERROR: SDMMC failure cmd: %04x events: %04x STA: %08x RINTSTS: %08x\n",
|
|
cmd, events, lpc54_getreg(LPC54_SDMMC_STATUS),
|
|
lpc54_getreg(LPC54_SDMMC_RINTSTS));
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
lpc54_putreg(SDCARD_CMDDONE_CLEAR, LPC54_SDMMC_RINTSTS);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_recvRx
|
|
*
|
|
* Description:
|
|
* Receive response to SD card 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 SD card 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 lpc54_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t *rshort)
|
|
{
|
|
uint32_t regval;
|
|
|
|
int ret = OK;
|
|
|
|
mcinfo("cmd=%04x\n", cmd);
|
|
|
|
/* 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 signaling 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=%04x\n", cmd);
|
|
ret = -EINVAL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check if a timeout or CRC error occurred */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_RINTSTS);
|
|
if ((regval & SDMMC_INT_RTO) != 0)
|
|
{
|
|
mcerr("ERROR: Command timeout: %08x\n", regval);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
else if ((regval & SDMMC_INT_RCRC) != 0)
|
|
{
|
|
mcerr("ERROR: CRC failure: %08x\n", regval);
|
|
ret = -EIO;
|
|
}
|
|
}
|
|
|
|
/* Clear all pending message completion events and return the R1/R6
|
|
* response.
|
|
*/
|
|
|
|
lpc54_putreg(SDCARD_RESPDONE_CLEAR | SDCARD_CMDDONE_CLEAR,
|
|
LPC54_SDMMC_RINTSTS);
|
|
*rshort = lpc54_getreg(LPC54_SDMMC_RESP0);
|
|
mcinfo("CRC=%04x\n", *rshort);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lpc54_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t rlong[4])
|
|
{
|
|
uint32_t regval;
|
|
int ret = OK;
|
|
|
|
mcinfo("cmd=%04x\n", cmd);
|
|
|
|
/* 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=%04x\n", cmd);
|
|
ret = -EINVAL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check if a timeout or CRC error occurred */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_RINTSTS);
|
|
if (regval & SDMMC_INT_RTO)
|
|
{
|
|
mcerr("ERROR: Timeout STA: %08x\n", regval);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
else if (regval & SDMMC_INT_RCRC)
|
|
{
|
|
mcerr("ERROR: CRC fail STA: %08x\n", regval);
|
|
ret = -EIO;
|
|
}
|
|
}
|
|
|
|
/* Return the long response */
|
|
|
|
lpc54_putreg(SDCARD_RESPDONE_CLEAR | SDCARD_CMDDONE_CLEAR,
|
|
LPC54_SDMMC_RINTSTS);
|
|
if (rlong)
|
|
{
|
|
rlong[0] = lpc54_getreg(LPC54_SDMMC_RESP3);
|
|
rlong[1] = lpc54_getreg(LPC54_SDMMC_RESP2);
|
|
rlong[2] = lpc54_getreg(LPC54_SDMMC_RESP1);
|
|
rlong[3] = lpc54_getreg(LPC54_SDMMC_RESP0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lpc54_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rshort)
|
|
{
|
|
uint32_t regval;
|
|
int ret = OK;
|
|
|
|
mcinfo("cmd=%04x\n", cmd);
|
|
|
|
/* 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=%04x\n", cmd);
|
|
ret = -EINVAL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check if a timeout occurred (Apparently a CRC error can terminate
|
|
* a good response)
|
|
*/
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_RINTSTS);
|
|
if (regval & SDMMC_INT_RTO)
|
|
{
|
|
mcerr("ERROR: Timeout STA: %08x\n", regval);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
lpc54_putreg(SDCARD_RESPDONE_CLEAR | SDCARD_CMDDONE_CLEAR,
|
|
LPC54_SDMMC_RINTSTS);
|
|
if (rshort)
|
|
{
|
|
*rshort = lpc54_getreg(LPC54_SDMMC_RESP0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* MMC responses not supported */
|
|
|
|
static int lpc54_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd,
|
|
uint32_t *rnotimpl)
|
|
{
|
|
mcinfo("cmd=%04x\n", cmd);
|
|
|
|
lpc54_putreg(SDCARD_RESPDONE_CLEAR | SDCARD_CMDDONE_CLEAR,
|
|
LPC54_SDMMC_RINTSTS);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_waitenable
|
|
*
|
|
* Description:
|
|
* Enable/disable of a set of SD card wait events. This is part of the
|
|
* the SDCARD_WAITEVENT sequence. The set of to-be-waited-for events is
|
|
* configured before calling lpc54_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) SDCARD_WAITENABLE is called
|
|
* again specifying a different set of wait events, or (2) SDCARD_EVENTWAIT
|
|
* returns.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
* eventset - A bitset of events to enable or disable (see SDIOWAIT_*
|
|
* definitions). 0=disable; 1=enable.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_waitenable(FAR struct sdio_dev_s *dev,
|
|
sdio_eventset_t eventset)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
uint32_t waitmask;
|
|
|
|
mcinfo("eventset=%04x\n", (unsigned int)eventset);
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
/* Disable event-related interrupts */
|
|
|
|
lpc54_config_waitints(priv, 0, 0, 0);
|
|
|
|
/* Select the interrupt mask that will give us the appropriate wakeup
|
|
* interrupts.
|
|
*/
|
|
|
|
waitmask = 0;
|
|
if ((eventset & SDIOWAIT_CMDDONE) != 0)
|
|
{
|
|
waitmask |= SDCARD_CMDDONE_MASK;
|
|
}
|
|
|
|
if ((eventset & SDIOWAIT_RESPONSEDONE) != 0)
|
|
{
|
|
waitmask |= SDCARD_RESPDONE_MASK;
|
|
}
|
|
|
|
if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
|
|
{
|
|
waitmask |= SDCARD_XFRDONE_MASK;
|
|
}
|
|
|
|
/* Enable event-related interrupts */
|
|
|
|
lpc54_config_waitints(priv, waitmask, eventset, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_eventwait
|
|
*
|
|
* Description:
|
|
* Wait for one of the enabled events to occur (or a timeout). Note that
|
|
* all events enabled by SDCARD_WAITEVENTS are disabled when lpc54_eventwait
|
|
* returns. SDCARD_WAITEVENTS must be called again before lpc54_eventwait
|
|
* can be used again.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card 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 lpc54_eventwait(FAR struct sdio_dev_s *dev,
|
|
uint32_t timeout)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
sdio_eventset_t wkupevent = 0;
|
|
irqstate_t flags;
|
|
int ret;
|
|
|
|
mcinfo("timeout=%lu\n", (unsigned long)timeout);
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
flags = enter_critical_section();
|
|
DEBUGASSERT(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 cornercase: The user request a timeout event but
|
|
* with timeout == 0?
|
|
*/
|
|
|
|
if (!timeout)
|
|
{
|
|
/* Then just tell the caller that we already timed out */
|
|
|
|
wkupevent = SDIOWAIT_TIMEOUT;
|
|
goto errout;
|
|
}
|
|
|
|
/* Start the watchdog timer */
|
|
|
|
delay = MSEC2TICK(timeout);
|
|
ret = wd_start(priv->waitwdog, delay, (wdentry_t)lpc54_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 lpc54_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.
|
|
*/
|
|
|
|
lpc54_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 nonzero value.
|
|
*/
|
|
|
|
if (wkupevent != 0)
|
|
{
|
|
/* Yes... break out of the loop with wkupevent non-zero */
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Disable all transfer- and event- related interrupts */
|
|
|
|
lpc54_disable_allints(priv);
|
|
|
|
errout:
|
|
leave_critical_section(flags);
|
|
mcinfo("wkupevent=%04x\n", wkupevent);
|
|
return wkupevent;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_callbackenable
|
|
*
|
|
* Description:
|
|
* Enable/disable of a set of SD card callback events. This is part of the
|
|
* the SD card callback sequence. The set of events is configured to enabled
|
|
* callbacks to the function provided in lpc54_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 methos.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the SD card device interface
|
|
* eventset - A bitset of events to enable or disable (see SDIOMEDIA_*
|
|
* definitions). 0=disable; 1=enable.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void lpc54_callbackenable(FAR struct sdio_dev_s *dev,
|
|
sdio_eventset_t eventset)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
|
|
mcinfo("eventset: %02x\n", eventset);
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
priv->cbevents = eventset;
|
|
lpc54_callback(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 SDCARD_CALLBACKENABLE
|
|
*
|
|
* Input Parameters:
|
|
* dev - Device-specific state data
|
|
* callback - The funtion 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 lpc54_registercallback(FAR struct sdio_dev_s *dev,
|
|
worker_t callback, void *arg)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
|
|
mcinfo("callback=%p arg=%p\n", callback, arg);
|
|
|
|
/* Disable callbacks and register this callback and its argument */
|
|
|
|
mcinfo("Register %p(%p)\n", callback, arg);
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
priv->cbevents = 0;
|
|
priv->cbarg = arg;
|
|
priv->callback = callback;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 SD card 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_LPC54_SDMMC_DMA
|
|
static int lpc54_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
|
|
size_t buflen)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
uint32_t regval;
|
|
uint32_t ctrl;
|
|
uint32_t maxs;
|
|
int i;
|
|
|
|
/* Don't bother with DMA if the entire transfer will fit in the RX FIFO or
|
|
* if we do not have a 4-bit wide bus.
|
|
*/
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
if (buflen <= LPC54_RXFIFO_SIZE || !priv->widebus)
|
|
{
|
|
return lpc54_recvsetup(dev, buffer, buflen);
|
|
}
|
|
|
|
mcinfo("buflen=%lu\n", (unsigned long)buflen);
|
|
DEBUGASSERT(buffer != NULL && buflen > 0 && ((uint32_t)buffer & 3) == 0);
|
|
|
|
/* Reset DMA controller internal registers. The SWR bit automatically
|
|
* clears in one clock cycle.
|
|
*/
|
|
|
|
lpc54_putreg(SDMMC_BMOD_SWR, LPC54_SDMMC_BMOD);
|
|
|
|
/* Save the destination buffer information for use by the interrupt handler */
|
|
|
|
priv->buffer = (uint32_t *)buffer;
|
|
priv->remaining = buflen;
|
|
priv->wrdir = false;
|
|
priv->dmamode = true;
|
|
|
|
/* Reset the FIFO and DMA */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
while ((lpc54_getreg(LPC54_SDMMC_CTRL) & SDMMC_CTRL_DMARESET) != 0)
|
|
{
|
|
}
|
|
|
|
/* Configure the FIFO so that we will receive the DMA/FIFO requests whenever
|
|
* there more than than (FIFO_DEPTH/2) - 1 words in the FIFO.
|
|
*/
|
|
|
|
regval = SDMMC_FIFOTH_RXWMARK(LPC54_RXFIFO_DEPTH / 2 - 1) |
|
|
SDMMC_FIFOTH_DMABURST_4XFRS;
|
|
lpc54_putreg(regval, LPC54_SDMMC_FIFOTH);
|
|
|
|
/* Setup DMA list */
|
|
|
|
i = 0;
|
|
while (buflen > 0)
|
|
{
|
|
/* Limit size of the transfer to maximum buffer size */
|
|
|
|
maxs = buflen;
|
|
|
|
if (maxs > MCI_DMADES1_MAXTR)
|
|
{
|
|
maxs = MCI_DMADES1_MAXTR;
|
|
}
|
|
|
|
buflen -= maxs;
|
|
|
|
/* Set buffer size */
|
|
|
|
g_sdmmc_dmadd[i].des1 = MCI_DMADES1_BS1(maxs);
|
|
|
|
/* Setup buffer address (chained) */
|
|
|
|
g_sdmmc_dmadd[i].des2 = (uint32_t)priv->buffer + (i * MCI_DMADES1_MAXTR);
|
|
|
|
/* Setup basic control */
|
|
|
|
ctrl = MCI_DMADES0_OWN | MCI_DMADES0_CH;
|
|
|
|
if (i == 0)
|
|
{
|
|
ctrl |= MCI_DMADES0_FS; /* First DMA buffer */
|
|
}
|
|
|
|
/* No more data? Then this is the last descriptor */
|
|
|
|
if (buflen == 0)
|
|
{
|
|
ctrl |= MCI_DMADES0_LD;
|
|
}
|
|
else
|
|
{
|
|
ctrl |= MCI_DMADES0_DIC;
|
|
}
|
|
|
|
/* Another descriptor is needed */
|
|
|
|
g_sdmmc_dmadd[i].des0 = ctrl;
|
|
g_sdmmc_dmadd[i].des3 = (uint32_t) &g_sdmmc_dmadd[i + 1];
|
|
i++;
|
|
}
|
|
|
|
lpc54_putreg((uint32_t)&g_sdmmc_dmadd[0], LPC54_SDMMC_DBADDR);
|
|
|
|
/* Enable internal DMA, burst size of 4, fixed burst */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_INTDMA;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
regval = SDMMC_BMOD_DE | SDMMC_BMOD_PBL_4XFRS | SDMMC_BMOD_DSL(4);
|
|
lpc54_putreg(regval, LPC54_SDMMC_BMOD);
|
|
|
|
/* Setup DMA error interrupts */
|
|
|
|
lpc54_config_dmaints(priv, SDCARD_DMARECV_MASK, SDCARD_DMAERROR_MASK);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 SD card 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_LPC54_SDMMC_DMA
|
|
static int lpc54_dmasendsetup(FAR struct sdio_dev_s *dev,
|
|
FAR const uint8_t *buffer, size_t buflen)
|
|
{
|
|
struct lpc54_dev_s *priv = (struct lpc54_dev_s *)dev;
|
|
uint32_t regval;
|
|
|
|
/* Don't bother with DMA if the entire transfer will fit in the TX FIFO or
|
|
* if we do not have a 4-bit wide bus.
|
|
*/
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
if (buflen <= LPC54_TXFIFO_SIZE || !priv->widebus)
|
|
{
|
|
return lpc54_sendsetup(dev, buffer, buflen);
|
|
}
|
|
|
|
mcinfo("buflen=%lu\n", (unsigned long)buflen);
|
|
DEBUGASSERT(buffer != NULL && buflen > 0 && ((uint32_t)buffer & 3) == 0);
|
|
|
|
/* Reset DMA controller internal registers. The SWR bit automatically
|
|
* clears in one clock cycle.
|
|
*/
|
|
|
|
lpc54_putreg(SDMMC_BMOD_SWR, LPC54_SDMMC_BMOD);
|
|
|
|
/* Save the destination buffer information for use by the interrupt
|
|
* handler.
|
|
*/
|
|
|
|
priv->buffer = (uint32_t *)buffer;
|
|
priv->remaining = buflen;
|
|
priv->wrdir = true;
|
|
priv->dmamode = true;
|
|
|
|
/* Reset the FIFO and DMA */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_FIFORESET | SDMMC_CTRL_DMARESET;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
while ((lpc54_getreg(LPC54_SDMMC_CTRL) & SDMMC_CTRL_DMARESET) != 0)
|
|
{
|
|
}
|
|
|
|
/* Configure the FIFO so that we will receive the DMA/FIFO requests whenever
|
|
* there are FIFO_DEPTH/2 or fewer words in the FIFO.
|
|
*/
|
|
|
|
regval = SDMMC_FIFOTH_TXWMARK(LPC54_TXFIFO_DEPTH / 2) |
|
|
SDMMC_FIFOTH_DMABURST_4XFRS;
|
|
lpc54_putreg(regval, LPC54_SDMMC_FIFOTH);
|
|
|
|
/* Setup DMA descriptor list */
|
|
|
|
g_sdmmc_dmadd[0].des0 = MCI_DMADES0_OWN | MCI_DMADES0_CH | MCI_DMADES0_LD;
|
|
g_sdmmc_dmadd[0].des1 = 512;
|
|
g_sdmmc_dmadd[0].des2 = (uint32_t)priv->buffer;
|
|
g_sdmmc_dmadd[0].des3 = (uint32_t)&g_sdmmc_dmadd[1];
|
|
|
|
lpc54_putreg((uint32_t) &g_sdmmc_dmadd[0], LPC54_SDMMC_DBADDR);
|
|
|
|
/* Enable internal DMA, burst size of 4, fixed burst */
|
|
|
|
regval = lpc54_getreg(LPC54_SDMMC_CTRL);
|
|
regval |= SDMMC_CTRL_INTDMA;
|
|
lpc54_putreg(regval, LPC54_SDMMC_CTRL);
|
|
|
|
regval = SDMMC_BMOD_DE | SDMMC_BMOD_PBL_4XFRS | SDMMC_BMOD_DSL(4);
|
|
lpc54_putreg(regval, LPC54_SDMMC_BMOD);
|
|
|
|
/* Setup DMA error interrupts */
|
|
|
|
lpc54_config_dmaints(priv, SDCARD_DMASEND_MASK, SDCARD_DMAERROR_MASK);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: lpc54_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 lpc54_callback(struct lpc54_dev_s *priv)
|
|
{
|
|
/* Is a callback registered? */
|
|
|
|
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: lpc54_sdmmc_initialize
|
|
*
|
|
* Description:
|
|
* Initialize the SD/MMC peripheral for normal operation.
|
|
*
|
|
* Input Parameters:
|
|
* slotno - Not used.
|
|
*
|
|
* Returned Values:
|
|
* A reference to an SD card interface structure. NULL is returned on failures.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct sdio_dev_s *lpc54_sdmmc_initialize(int slotno)
|
|
{
|
|
struct lpc54_dev_s *priv = &g_scard_dev;
|
|
irqstate_t flags;
|
|
uint32_t regval;
|
|
|
|
mcinfo("slotno=%d\n", slotno);
|
|
flags = enter_critical_section();
|
|
|
|
/* Set the SD/MMMC clock source */
|
|
|
|
lpc54_putreg(BOARD_SDMMC_CLKSRC, LPC54_SYSCON_SDIOCLKSEL);
|
|
|
|
/* Set up the clock divider to obtain the desired clock rate.
|
|
* NOTE: The SDIO function clock to the interface can be up to 50 MHZ.
|
|
*/
|
|
|
|
regval = SYSCON_SDIOCLKDIV_DIV(BOARD_SDMMC_CLKDIV);
|
|
lpc54_putreg(regval, LPC54_SYSCON_SDIOCLKDIV);
|
|
lpc54_putreg(regval | SYSCON_SDIOCLKDIV_REQFLAG, LPC54_SYSCON_SDIOCLKDIV);
|
|
|
|
/* Enable clocking to the SD/MMC peripheral */
|
|
|
|
lpc54_sdmmc_enableclk();
|
|
|
|
/* REVISIT: The delay values on the sample and drive inputs and outputs
|
|
* can be adjusted using the SDIOCLKCTRL register in the SYSCON block.
|
|
*/
|
|
|
|
/* Initialize semaphores */
|
|
|
|
sem_init(&priv->waitsem, 0, 0);
|
|
|
|
/* The waitsem semaphore is used for signaling and, hence, should not have
|
|
* priority inheritance enabled.
|
|
*/
|
|
|
|
sem_setprotocol(&priv->waitsem, SEM_PRIO_NONE);
|
|
|
|
/* Create a watchdog timer */
|
|
|
|
priv->waitwdog = wd_create();
|
|
DEBUGASSERT(priv->waitwdog != NULL);
|
|
|
|
/* Configure GPIOs for 4-bit, wide-bus operation */
|
|
|
|
lpc54_gpio_config(GPIO_SD_D0);
|
|
#ifndef CONFIG_SDIO_WIDTH_D1_ONLY
|
|
lpc54_gpio_config(GPIO_SD_D1);
|
|
lpc54_gpio_config(GPIO_SD_D2);
|
|
lpc54_gpio_config(GPIO_SD_D3);
|
|
#endif
|
|
#ifdef CONFIG_MMCSD_HAVE_CARDDETECT
|
|
lpc54_gpio_config(GPIO_SD_CARD_DET_N);
|
|
#endif
|
|
lpc54_gpio_config(GPIO_SD_CLK);
|
|
lpc54_gpio_config(GPIO_SD_CMD);
|
|
#ifdef CONFIG_LPC54_SDMMC_PWRCTRL
|
|
lpc54_gpio_config(GPIO_SD_POW_EN);
|
|
#endif
|
|
#ifdef CONFIG_MMCSD_HAVE_WRITEPROTECT
|
|
lpc54_gpio_config(GPIO_SD_WR_PRT);
|
|
#endif
|
|
|
|
/* Reset the card and assure that it is in the initial, unconfigured
|
|
* state.
|
|
*/
|
|
|
|
lpc54_reset(&priv->dev);
|
|
|
|
leave_critical_section(flags);
|
|
return &g_scard_dev.dev;
|
|
}
|
|
|
|
#endif /* CONFIG_LPC54_SDMMC */
|