Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/****************************************************************************
* 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_hsmci.h"
#if CONFIG_SAM3U_HSMCI
/****************************************************************************
* Pre-Processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_SAM3U_DMA
# warning "HSMCI driver requires CONFIG_SAM3U_DMA"
#endif
#ifndef CONFIG_SCHED_WORKQUEUE
# error "Callback support requires CONFIG_SCHED_WORKQUEUE"
#endif
#ifndef CONFIG_SDIO_BLOCKSETUP
# error "This driver requires CONFIG_SDIO_BLOCKSETUP"
#endif
#ifndef CONFIG_HSMCI_PRI
# define CONFIG_HSMCI_PRI NVIC_SYSH_PRIORITY_DEFAULT
#endif
#if !defined(CONFIG_DEBUG_FS) || !defined(CONFIG_DEBUG_VERBOSE)
#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
/* Timing */
#define HSMCI_CMDTIMEOUT (100000)
#define HSMCI_LONGTIMEOUT (0x7fffffff)
/* Big DTIMER setting */
#define HSMCI_DTIMER_DATATIMEOUT (0x000fffff)
(DMACH_FLAG_FIFO_8BYTES | DMACH_FLAG_FIFOCFG_LARGEST | \
(DMACHAN_PID_MCI0 << DMACH_FLAG_PERIPHPID_SHIFT) | \
DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | DMACH_FLAG_MEMCHUNKSIZE_4)
/* 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
*/
#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 | \
#define HSMCI_RESPONSE_NOCRC_ERRORS \
( HSMCI_INT_CSTOE | HSMCI_INT_RTOE | HSMCI_INT_RENDE | HSMCI_INT_RDIRE | \
HSMCI_INT_RINDE )
#define HSMCI_RESPONSE_TIMEOUT_ERRORS \
( HSMCI_INT_CSTOE | HSMCI_INT_RTOE )
/* 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
*/
#define HSMCI_DATA_ERRORS \
( HSMCI_INT_UNRE | HSMCI_INT_OVRE | HSMCI_INT_BLKOVRE | HSMCI_INT_CSTOE | \
HSMCI_INT_DTOE | HSMCI_INT_DCRCE )
#define HSMCI_DATA_TIMEOUT_ERRORS \
( HSMCI_INT_CSTOE | HSMCI_INT_DTOE )
#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.
*/
( HSMCI_DATA_DMARECV_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
( HSMCI_DATA_DMASEND_ERRORS | HSMCI_INT_XFRDONE /* | HSMCI_INT_DMADONE */ )
/* 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
*/
#define HSMCI_CMDRESP_INTS \
( HSMCI_RESPONSE_ERRORS | HSMCI_INT_CMDRDY )
#define HSMCI_CMDRESP_NOCRC_INTS \
( HSMCI_RESPONSE_NOCRC_ERRORS | HSMCI_INT_CMDRDY )
/* Register logging support */
#ifdef CONFIG_HSMCI_XFRDEBUG
# 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
# else
# define SAMPLENDX_BEFORE_SETUP 0
# define SAMPLENDX_AFTER_SETUP 1
# define SAMPLENDX_END_TRANSFER 2
#ifdef CONFIG_HSMCI_CMDDEBUG
# define SAMPLENDX_AFTER_CMDR 0
# define SAMPLENDX_AT_WAKEUP 1
# define DEBUG_NCMDSAMPLES 2
#endif
/****************************************************************************
* 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 */
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)
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 */
#ifdef CONFIG_HSMCI_XFRDEBUG
struct sam3u_xfrregs_s
struct sam3u_hsmciregs_s hsmci;
#ifdef CONFIG_DEBUG_DMA
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);
static inline void sam3u_disable(void);
static inline void sam3u_enable(void);
/* 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
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);
# define sam3u_cmdsampleinit()
# define sam3u_cmdsample1(index)
# define sam3u_cmdsample2(index,sr)
# define sam3u_cmddump()
/* DMA Helpers **************************************************************/
static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result);
/* 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);
/* 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,
static void sam3u_blocksetup(FAR struct sdio_dev_s *dev, unsigned int blocklen,
unsigned int nblocks);
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,
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 */
static bool sam3u_dmasupported(FAR struct sdio_dev_s *dev);
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,
.recvsetup = sam3u_dmarecvsetup,
.sendsetup = sam3u_dmasendsetup,
.cancel = sam3u_cancel,
.waitresponse = sam3u_waitresponse,
.recvR2 = sam3u_recvlong,
.recvR3 = sam3u_recvshort,
.recvR4 = sam3u_recvnotimpl,
.recvR5 = sam3u_recvnotimpl,
.recvR7 = sam3u_recvshort,
.waitenable = sam3u_waitenable,
.eventwait = sam3u_eventwait,
.callbackenable = sam3u_callbackenable,
.registercallback = sam3u_registercallback,
.dmasupported = sam3u_dmasupported,
.dmarecvsetup = sam3u_dmarecvsetup,
.dmasendsetup = sam3u_dmasendsetup,
},
};
/* 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;
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
#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);
}
/****************************************************************************
* Name: sam3u_disable
*
* Description:
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
*
****************************************************************************/
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);
}
/****************************************************************************
****************************************************************************/
/****************************************************************************
*
****************************************************************************/
#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
static void sam3u_hsmcisample(struct sam3u_hsmciregs_s *regs)
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);
}
#endif
/****************************************************************************
*
****************************************************************************/
#if defined(CONFIG_HSMCI_XFRDEBUG) || defined(CONFIG_HSMCI_CMDDEBUG)
static void sam3u_hsmcidump(struct sam3u_hsmciregs_s *regs, const char *msg)
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);
}
#endif
/****************************************************************************
*
* Description:
* Sample HSMCI/DMA registers
*
****************************************************************************/
#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrsample(struct sam3u_dev_s *priv, int index)
struct sam3u_xfrregs_s *regs = &g_xfrsamples[index];
}
#endif
/****************************************************************************
* Setup prior to collecting transfer samples
*
****************************************************************************/
#ifdef CONFIG_HSMCI_XFRDEBUG
memset(g_xfrsamples, 0xff, DEBUG_NDMASAMPLES * sizeof(struct sam3u_xfrregs_s));
#ifdef CONFIG_HSMCI_CMDDEBUG
g_xfrinitialized = true;
#endif
}
#endif
/****************************************************************************
*
****************************************************************************/
#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrdumpone(struct sam3u_dev_s *priv,
struct sam3u_xfrregs_s *regs, const char *msg)
}
#endif
/****************************************************************************
* Dump all transfer-related, sampled register data
*
****************************************************************************/
#ifdef CONFIG_HSMCI_XFRDEBUG
static void sam3u_xfrdump(struct sam3u_dev_s *priv)
#ifdef CONFIG_HSMCI_CMDDEBUG
if (g_xfrinitialized)
#endif
{
sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_BEFORE_SETUP], "Before setup");
sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_BEFORE_ENABLE], "Before DMA enable");
sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_AFTER_SETUP], "After setup");
sam3u_xfrdumpone(priv, &g_xfrsamples[SAMPLENDX_END_TRANSFER], "End of transfer");
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
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;
/****************************************************************************
* DMA Helpers
****************************************************************************/
/****************************************************************************
* Name: sam3u_dmacallback
*
* Description:
* Called when HSMCI DMA completes
*
****************************************************************************/
static void sam3u_dmacallback(DMA_HANDLE handle, void *arg, int result)
{
/* 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);
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
}
/****************************************************************************
* 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);
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
}
}
/****************************************************************************
* 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.