diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 8b47944b60f..1e91f4c5902 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -121,6 +121,10 @@ config SAMA5_HAVE_EMAC1 bool default n +config SAMA5_HAVE_SDMMC + bool + default n + config SAMA5_HAVE_HSMCI2 bool default n @@ -285,6 +289,8 @@ config ARCH_CHIP_ATSAMA5D27 select SAMA5_HAVE_CAN0 select SAMA5_HAVE_CAN1 select SAMA5_HAVE_DDR32 + select SAMA5_HAVE_SDMMC + select SAMA5_HAVE_GMAC config ARCH_CHIP_ATSAMA5D28 bool "Atmel ATSAMA5D28" @@ -545,6 +551,12 @@ config SAMA5_TWI3 default n depends on SAMA5_HAVE_TWI3 +config SAMA5_SDMMC + bool "Secure Digital Multimedia Card Interface (SDMMC)" + default n + depends on SAMA5_HAVE_SDMMC + select ARCH_HAVE_SDIO + config SAMA5_HSMCI0 bool "High Speed Multimedia Card Interface 0 (HSMCI0)" default n @@ -5056,4 +5068,32 @@ config SAMA5_EBICS3_HEAP_SIZE endif #SAMA5_EBICS3_HEAP endmenu # Heap Configuration + +config SAMA5_SDMMC + bool "enable SDMMC controller" + default y if SAMA5_HAVE_SDMMC + select SDIO_DMA + select SCHED_WORKQUEUE + select SCHED_HPWORK + select MMCSD + select MMCSD_SDIO + select SDIO_BLOCKSETUP + select ARCH_HAVE_SDIO + select SAMA5_SDMMC_DMA + ---help--- + Enable SD Card interface SDMMC. Selects SAMA5_SDMMC SAMA5_SDMMC_DMA SDIO_DMA SCHED_WORKQUEUE SCHED_HPWORK SDIO_BLOCKSETUP + +choice + prompt "SDMMC maximum bus speed" + default SAMA5_SDMMC_50MHZ + depends on SAMA5_SDMMC + +config SAMA5_SDMMC_25MHZ + bool "SDMMC 25Mhz" + +config SAMA5_SDMMC_50MHZ + bool "SDMMC 50Mhz" + +endchoice # SDMMC bus speed + endif # ARCH_CHIP_SAMA5 diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index dcb6336eab3..b5f9e5b719e 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -213,6 +213,14 @@ endif endif endif +ifeq ($(CONFIG_SAMA5_SDMMC0),y) +CHIP_CSRCS += sam_sdmmc.c +else +ifeq ($(CONFIG_SAMA5_SDMMC1),y) +CHIP_CSRCS += sam_sdmmc.c +endif +endif + ifeq ($(CONFIG_SAMA5_HSMCI0),y) CHIP_CSRCS += sam_hsmci.c sam_hsmci_clkdiv.c else diff --git a/arch/arm/src/sama5/hardware/_sama5d2x_memorymap.h b/arch/arm/src/sama5/hardware/_sama5d2x_memorymap.h index 17052bd07d5..81924b86499 100644 --- a/arch/arm/src/sama5/hardware/_sama5d2x_memorymap.h +++ b/arch/arm/src/sama5/hardware/_sama5d2x_memorymap.h @@ -317,6 +317,8 @@ #define SAM_AXIMX_MMUFLAGS MMU_IOFLAGS #define SAM_DAP_MMUFLAGS MMU_IOFLAGS #define SAM_L2CC_MMUFLAGS MMU_IOFLAGS +#define SAM_SDMMC0_MMUFLAGS MMU_IOFLAGS +#define SAM_SDMMC1_MMUFLAGS MMU_IOFLAGS /* If the NFC is not being used, the NFC SRAM can be used as general purpose * SRAM (cached). If the NFC is used, then the NFC SRAM should be treated @@ -429,8 +431,8 @@ #define SAM_EBICS1_VSECTION 0x60000000 /* 0x60000000-0x6fffffff: EBI Chip Select 1 */ #define SAM_EBICS2_VSECTION 0x70000000 /* 0x70000000-0x7fffffff: EBI Chip Select 2 */ #define SAM_EBICS3_VSECTION 0x80000000 /* 0x80000000-0x8fffffff: EBI Chip Select 3 */ -#define SAM_QSPI0AES_VSECTION 0x90000000 /* 0x90000000-0x9fffffff: QSPI0 AES Memory */ -#define SAM_QSPI1AES_VSECTION 0xa0000000 /* 0xa0000000-0xafffffff: QSPI1 AES Memory */ +#define SAM_QSPI0AES_VSECTION 0x90000000 /* 0x90000000-0x97ffffff: QSPI0 AES Memory */ +#define SAM_QSPI1AES_VSECTION 0x98000000 /* 0x98000000-0x9fffffff: QSPI1 AES Memory */ #define SAM_SDMMC0_VSECTION 0xa0000000 /* 0xa0000000-0xafffffff: SDMMC0 */ #define SAM_SDMMC1_VSECTION 0xb0000000 /* 0xb0000000-0xbfffffff: SDMMC1 */ #define SAM_NFCCR_VSECTION 0xc0000000 /* 0xc0000000-0xcfffffff: NFC Command Registers */ @@ -461,6 +463,8 @@ /* Peripheral virtual base addresses */ +#define SAM_SDMMC0_VBASE (SAM_SDMMC0_VSECTION) +#define SAM_SDMMC1_VBASE (SAM_SDMMC1_VSECTION) #define SAM_LCDC_VBASE (SAM_PERIPHA_VSECTION+SAM_LCDC_OFFSET) #define SAM_XDMAC1_VBASE (SAM_PERIPHA_VSECTION+SAM_XDMAC1_OFFSET) #define SAM_ISC_VBASE (SAM_PERIPHA_VSECTION+SAM_ISC_OFFSET) @@ -640,7 +644,7 @@ * 1) If CONFIG_ARCH_ROMPGTABLE, then the page table resides in ROM and we * will not use any page table in RAM. * 2) We are executing out of SRAM. In this case, vectors will reside at - * the bottom of SRAM, following by .text, .data, .bss, and heep. The + * the bottom of SRAM, following by .text, .data, .bss, and heap. The * page table will be squeezed into the end of internal SRAM in this * case. * diff --git a/arch/arm/src/sama5/hardware/_sama5d2x_pinmap.h b/arch/arm/src/sama5/hardware/_sama5d2x_pinmap.h index 1c9d4d59928..a7e82020808 100644 --- a/arch/arm/src/sama5/hardware/_sama5d2x_pinmap.h +++ b/arch/arm/src/sama5/hardware/_sama5d2x_pinmap.h @@ -1,4 +1,4 @@ -/************************************************************************************************************ +/**************************************************************************** * arch/arm/src/sama5/hardware/_sama5d2x_pinmap.h * * Copyright (C) 2015 Gregory Nutt. All rights reserved. @@ -31,40 +31,42 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ************************************************************************************************************/ + ****************************************************************************/ #ifndef __ARCH_ARM_SRC_SAMA5_HARDWARE__SAMA5D2X_PINMAP_H #define __ARCH_ARM_SRC_SAMA5_HARDWARE__SAMA5D2X_PINMAP_H -/************************************************************************************************************ +/**************************************************************************** * Included Files - ************************************************************************************************************/ + ****************************************************************************/ #include #include "chip.h" #include "sam_pio.h" -/************************************************************************************************************ +/**************************************************************************** * Pre-processor Definitions - ************************************************************************************************************/ + ****************************************************************************/ -/* PIO pin definitions **************************************************************************************/ -/* Alternate Pin Functions. +/* PIO pin definitions ****************************************************** + * Alternate Pin Functions. * - * Alternative pin selections are provided with a numeric suffix like _1, _2, etc. Drivers, however, will - * use the pin selection without the numeric suffix. Additional definitions are required in the board.h - * file. For example, if we wanted the PCK0 on PB26, then the following definition should appear in the - * board.h header file for that board: + * Alternative pin selections are provided with a numeric suffix like _1, _2, + * etc. Drivers, however, will use the pin selection without the numeric + * suffix. Additional definitions are required in the board.h file. For + * example, if we wanted the PCK0 on PB26, then the following definition + * should appear in the board.h header file for that board: * * #define PIO_PMC_PCK0 PIO_PMC_PCK0_1 * * The PCK logic will then automatically configure PB26 as the PCK0 pin. */ -/* WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! - * Additional effort is required to select specific PIO options such as frequency, open-drain/push-pull, - * and pull-up/down! Just the basics are defined for most pins in this file at the present time. +/* WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! + * Additional effort is required to select specific PIO options such as + * frequency, open-drain/push-pull, and pull-up/down! Just the basics are + * defined for most pins in this file at the present time. */ /* Analog-to-Digital Converter - ADC */ @@ -537,7 +539,6 @@ #define PIO_PDMIC_DAT_1 (PIO_PERIPHD | PIO_CFG_DEFAULT | PIO_PORT_PIOB | PIO_PIN11) #define PIO_PDMIC_DAT_2 (PIO_PERIPHD | PIO_CFG_DEFAULT | PIO_PORT_PIOB | PIO_PIN26) - /* Pulse Width Modulation Controller - PWM */ #define PIO_PWM0_EXTRG0 (PIO_PERIPHD | PIO_CFG_DEFAULT | PIO_PORT_PIOB | PIO_PIN3) @@ -601,28 +602,28 @@ /* Secure Data Memory Card - SDMMC */ -#define PIO_SDMMC0_CD (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN13) +#define PIO_SDMMC0_CD (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN13 | PIO_CFG_DEGLITCH | PIO_INT_BOTHEDGES | PIO_INPUT ) #define PIO_SDMMC0_CK (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN0) -#define PIO_SDMMC0_CMD (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN1) -#define PIO_SDMMC0_DAT0 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN2) -#define PIO_SDMMC0_DAT1 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN3) -#define PIO_SDMMC0_DAT2 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN4) -#define PIO_SDMMC0_DAT3 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN5) -#define PIO_SDMMC0_DAT4 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN6) -#define PIO_SDMMC0_DAT5 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN7) -#define PIO_SDMMC0_DAT6 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN8) -#define PIO_SDMMC0_DAT7 (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN9) +#define PIO_SDMMC0_CMD (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN1) +#define PIO_SDMMC0_DAT0 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN2) +#define PIO_SDMMC0_DAT1 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN3) +#define PIO_SDMMC0_DAT2 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN4) +#define PIO_SDMMC0_DAT3 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN5) +#define PIO_SDMMC0_DAT4 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN6) +#define PIO_SDMMC0_DAT5 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN7) +#define PIO_SDMMC0_DAT6 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN8) +#define PIO_SDMMC0_DAT7 (PIO_PERIPHA | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN9) #define PIO_SDMMC0_RSTN (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN10) #define PIO_SDMMC0_VDDSEL (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN11) #define PIO_SDMMC0_WP (PIO_PERIPHA | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN12) #define PIO_SDMMC1_CD (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN30) #define PIO_SDMMC1_CK (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN22) -#define PIO_SDMMC1_CMD (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN28) -#define PIO_SDMMC1_DAT0 (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN18) -#define PIO_SDMMC1_DAT1 (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN19) -#define PIO_SDMMC1_DAT2 (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN20) -#define PIO_SDMMC1_DAT3 (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN21) +#define PIO_SDMMC1_CMD (PIO_PERIPHE | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN28) +#define PIO_SDMMC1_DAT0 (PIO_PERIPHE | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN18) +#define PIO_SDMMC1_DAT1 (PIO_PERIPHE | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN19) +#define PIO_SDMMC1_DAT2 (PIO_PERIPHE | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN20) +#define PIO_SDMMC1_DAT3 (PIO_PERIPHE | PIO_CFG_PULLUP | PIO_PORT_PIOA | PIO_PIN21) #define PIO_SDMMC1_RSTN (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN27) #define PIO_SDMMC1_WP (PIO_PERIPHE | PIO_CFG_DEFAULT | PIO_PORT_PIOA | PIO_PIN29) diff --git a/arch/arm/src/sama5/hardware/sam_sdmmc.h b/arch/arm/src/sama5/hardware/sam_sdmmc.h new file mode 100644 index 00000000000..0df9658215f --- /dev/null +++ b/arch/arm/src/sama5/hardware/sam_sdmmc.h @@ -0,0 +1,603 @@ +/**************************************************************************** + * arch/arm/src/sama5/hardware/sam_sdmmc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_HARDWARE_SAMA5_SDMMC_H +#define __ARCH_ARM_SRC_SAMA5_HARDWARE_SAMA5_SDMMC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define SAMA5_SDMMC_DSADDR_OFFSET 0x0000 /* DMA System Address Register */ +#define SAMA5_SDMMC_BLKATTR_OFFSET 0x0004 /* Block Size Register */ +#define SAMA5_SDMMC_BSR_OFFSET 0x0004 /* Block Size Register */ +#define SAMA5_SDMMC_BCR_OFFSET 0x0006 /* Block Count Register */ +#define SAMA5_SDMMC_CMDARG_OFFSET 0x0008 /* Command Argument Register */ +#define SAMA5_SDMMC_XFERTYP_OFFSET 0x000c /* Transfer Type Register SAMA5: Transfer Mode Register */ +#define SAMA5_SDMMC_CMDRSP0_OFFSET 0x0010 /* Command Response 0 */ +#define SAMA5_SDMMC_CMDRSP1_OFFSET 0x0014 /* Command Response 1 */ +#define SAMA5_SDMMC_CMDRSP2_OFFSET 0x0018 /* Command Response 2 */ +#define SAMA5_SDMMC_CMDRSP3_OFFSET 0x001c /* Command Response 3 */ +#define SAMA5_SDMMC_DATAPORT_OFFSET 0x0020 /* Buffer Data Port Register */ +#define SAMA5_SDMMC_PRSSTAT_OFFSET 0x0024 /* Present State Register */ +#define SAMA5_SDMMC_PROCTL_OFFSET 0x0028 /* Protocol Control Register */ +#define SAMA5_SDMMC_PWRCTL_OFFSET 0x0029 /* Power Control Control Register */ +#define SAMA5_SDMMC_SYSCTL_OFFSET 0x002c /* System Control Register, or Clock Control Register/Timout Control Register */ +#define SAMA5_SDMMC_TCR_OFFSET 0x002e /* Timout Control Register (8 bit) */ +#define SAMA5_SDMMC_SRR_OFFSET 0x002f /* Software Reset Register (8 bit) */ +#define SAMA5_SDMMC_IRQSTAT_OFFSET 0x0030 /* Interrupt Status Register */ +#define SAMA5_SDMMC_IRQSTATEN_OFFSET 0x0034 /* Interrupt Status Enable Register */ +#define SAMA5_SDMMC_EISTER_OFFSET 0x0036 /* SAMA5: Error Interrupt Status Register */ +#define SAMA5_SDMMC_IRQSIGEN_OFFSET 0x0038 /* Interrupt Signal Enable Register */ +#define SAMA5_SDMMC_AC12ERR_OFFSET 0x003c /* Auto CMD12 Error Status Register */ +#define SAMA5_SDMMC_H2CR_OFFSET 0x003e /* Host Control 2 Register */ +#define SAMA5_SDMMC_HTCAPBLT0_OFFSET 0x0040 /* Host Controller Capabilities 0 Register */ +#define SAMA5_SDMMC_HTCAPBLT1_OFFSET 0x0044 /* Host Controller Capabilities 1 Register */ +#define SAMA5_SDMMC_MIX_OFFSET 0x0048 /* Mixer Control Register SAMA5: Maximum Current Capabililities Register */ +#define SAMA5_SDMMC_FEVT_OFFSET 0x0050 /* Force Event Register */ +#define SAMA5_SDMMC_ADMAES_OFFSET 0x0054 /* ADMA Error Status Register */ +#define SAMA5_SDMMC_ADSADDR_OFFSET 0x0058 /* ADMA System Address Register */ +#define SAMA5_SDMMC_DLL_CONTROL_OFFSET 0x0060 /* DLL Control Register */ +#define SAMA5_SDMMC_DLL_STATUS_OFFSET 0x0064 /* DLL Status Register */ +#define SAMA5_SDMMC_CLK_TUNE_CTRL_OFFSET 0x0068 /* Clock tuning control Register */ + /* 0x0070 – 0x00f8: reserved */ +#define SAMA5_SDMMC_HOST_VERSION_OFFSET 0x00fe /* Host Controller Version Register */ +#define SAMA5_SDMMC_APSR_OFFSET 0x0200 /* Additional Present State Register */ +#define SAMA5_SDMMC_MC1R_OFFSET 0x0204 /* e.MMC Control 1 Register */ +#define SAMA5_SDMMC_MC2R_OFFSET 0x0205 /* e.MMC Control 2 Register */ +#define SAMA5_SDMMC_ACR_OFFSET 0x0208 /* AHB Control Register */ +#define SAMA5_SDMMC_CC2R_OFFSET 0x020C /* Clock Control 2 Register */ +#define SAMA5_SDMMC_RTC1R_OFFSET 0x0210 /* Retuning Timer Control 1 Register */ +#define SAMA5_SDMMC_RTC2R_OFFSET 0x0211 /* Retuning Timer Control 2 Register */ +#define SAMA5_SDMMC_RTCVR_OFFSET 0x0214 /* Retuning Timer Counter Value Register */ +#define SAMA5_SDMMC_RTISTER_OFFSET 0x0218 /* Retuning Timer Interrupt Status Enable Register */ +#define SAMA5_SDMMC_RTISIER_OFFSET 0x0219 /* Retuning Timer Interrupt Signal Enable Register */ +#define SAMA5_SDMMC_RTISTR_OFFSET 0x021C /* Retuning Timer Interrupt Status Register */ +#define SAMA5_SDMMC_RTSSR_OFFSET 0x021D /* Retuning Timer Status Slots Register */ +#define SAMA5_SDMMC_TUNCR_OFFSET 0x0220 /* Tuning Control Register */ + /* 0x0224 – 0x022C Reserved */ +#define SAMA5_SDMMC_CACR_OFFSET 0x0230 /* Capabilities Control Register */ + /* 0x0234 – 0x023C Reserved */ +#define SAMA5_SDMMC_CALCR_OFFSET 0x0240 /* Calibration Control Register */ + +/* Register Addresses *******************************************************/ + +/* For SDMMC0... */ + +#define SAMA5_SDMMC0_DSADDR (SAM_SDMMC0_VBASE + SAMA5_SDMMC_DSADDR_OFFSET) +#define SAMA5_SDMMC0_BLKATTR (SAM_SDMMC0_VBASE + SAMA5_SDMMC_BLKATTR_OFFSET) +#define SAMA5_SDMMC0_CMDARG (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CMDARG_OFFSET) +#define SAMA5_SDMMC0_XFERTYP (SAM_SDMMC0_VBASE + SAMA5_SDMMC_XFERTYP_OFFSET) +#define SAMA5_SDMMC0_CMDRSP0 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CMDRSP0_OFFSET) +#define SAMA5_SDMMC0_CMDRSP1 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CMDRSP1_OFFSET) +#define SAMA5_SDMMC0_CMDRSP2 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CMDRSP2_OFFSET) +#define SAMA5_SDMMC0_CMDRSP3 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CMDRSP3_OFFSET) +#define SAMA5_SDMMC0_DATAPORT (SAM_SDMMC0_VBASE + SAMA5_SDMMC_DATAPORT_OFFSET) +#define SAMA5_SDMMC0_PRSSTAT (SAM_SDMMC0_VBASE + SAMA5_SDMMC_PRSSTAT_OFFSET) +#define SAMA5_SDMMC0_PROCTL (SAM_SDMMC0_VBASE + SAMA5_SDMMC_PROCTL_OFFSET) +#define SAMA5_SDMMC0_PWRCTL (SAM_SDMMC0_VBASE + SAMA5_SDMMC_PWRCTL_OFFSET) +#define SAMA5_SDMMC0_SYSCTL (SAM_SDMMC0_VBASE + SAMA5_SDMMC_SYSCTL_OFFSET) +#define SAMA5_SDMMC0_IRQSTAT (SAM_SDMMC0_VBASE + SAMA5_SDMMC_IRQSTAT_OFFSET) +#define SAMA5_SDMMC0_IRQSTATEN (SAM_SDMMC0_VBASE + SAMA5_SDMMC_IRQSTATEN_OFFSET) +#define SAMA5_SDMMC0_IRQSIGEN (SAM_SDMMC0_VBASE + SAMA5_SDMMC_IRQSIGEN_OFFSET) +#define SAMA5_SDMMC0_AC12ERR (SAM_SDMMC0_VBASE + SAMA5_SDMMC_AC12ERR_OFFSET) +#define SAMA5_SDMMC0_HTCAPBLT0 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_HTCAPBLT0_OFFSET) +#define SAMA5_SDMMC0_HTCAPBLT1 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_HTCAPBLT1_OFFSET) +#define SAMA5_SDMMC0_MIX (SAM_SDMMC0_VBASE + SAMA5_SDMMC_MIX_OFFSET) +#define SAMA5_SDMMC0_FEVT (SAM_SDMMC0_VBASE + SAMA5_SDMMC_FEVT_OFFSET) +#define SAMA5_SDMMC0_ADMAES (SAM_SDMMC0_VBASE + SAMA5_SDMMC_ADMAES_OFFSET) +#define SAMA5_SDMMC0_ADSADDR (SAM_SDMMC0_VBASE + SAMA5_SDMMC_ADSADDR_OFFSET) +#define SAMA5_SDMMC_DLL_CONTROL (SAM_SDMMC0_VBASE + SAMA5_SDMMC_DLL_CONTROL_OFFSET) +#define SAMA5_SDMMC_DLL_STATUS (SAM_SDMMC0_VBASE + SAMA5_SDMMC_DLL_STATUS) +#define SAMA5_SDMMC_CLK_TUNE_CTRL (SAM_SDMMC0_VBASE + SAMA5_SDMMC_CLK_TUNE_CTRL) +#define SAMA5_SDMMC0_VENDOR (SAM_SDMMC0_VBASE + SAMA5_SDMMC_VENDOR_OFFSET) +#define SAMA5_SDMMC0_MMCBOOT (SAM_SDMMC0_VBASE + SAMA5_SDMMC_MMCBOOT_OFFSET) +#define SAMA5_SDMMC0_VENDOR2 (SAM_SDMMC0_VBASE + SAMA5_SDMMC_VENDOR2_OFFSET) +#define SAMA5_SDMMC0_TC (SAM_SDMMC0_VBASE + SAMA5_SDMMC_TC_OFFSET) + +/* For SDMMC1... */ + +#define SAMA5_SDMMC1_DSADDR (SAM_SDMMC1_VBASE + SAMA5_SDMMC_DSADDR_OFFSET) +#define SAMA5_SDMMC1_BLKATTR (SAM_SDMMC1_VBASE + SAMA5_SDMMC_BLKATTR_OFFSET) +#define SAMA5_SDMMC1_CMDARG (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CMDARG_OFFSET) +#define SAMA5_SDMMC1_XFERTYP (SAM_SDMMC1_VBASE + SAMA5_SDMMC_XFERTYP_OFFSET) +#define SAMA5_SDMMC1_CMDRSP0 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CMDRSP0_OFFSET) +#define SAMA5_SDMMC1_CMDRSP1 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CMDRSP1_OFFSET) +#define SAMA5_SDMMC1_CMDRSP2 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CMDRSP2_OFFSET) +#define SAMA5_SDMMC1_CMDRSP3 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CMDRSP3_OFFSET) +#define SAMA5_SDMMC1_DATAPORT (SAM_SDMMC1_VBASE + SAMA5_SDMMC_DATPORT_OFFSET) +#define SAMA5_SDMMC1_PRSSTAT (SAM_SDMMC1_VBASE + SAMA5_SDMMC_PRSSTAT_OFFSET) +#define SAMA5_SDMMC1_PROCTL (SAM_SDMMC1_VBASE + SAMA5_SDMMC_PROCTL_OFFSET) +#define SAMA5_SDMMC1_PWRCTL (SAM_SDMMC1_VBASE + SAMA5_SDMMC_PWRCTL_OFFSET) +#define SAMA5_SDMMC1_SYSCTL (SAM_SDMMC1_VBASE + SAMA5_SDMMC_SYSCTL_OFFSET) +#define SAMA5_SDMMC1_IRQSTAT (SAM_SDMMC1_VBASE + SAMA5_SDMMC_IRQSTAT_OFFSET) +#define SAMA5_SDMMC1_IRQSTATEN (SAM_SDMMC1_VBASE + SAMA5_SDMMC_IRQSTATEN_OFFSET) +#define SAMA5_SDMMC1_IRQSIGEN (SAM_SDMMC1_VBASE + SAMA5_SDMMC_IRQSIGEN_OFFSET) +#define SAMA5_SDMMC1_AC12ERR (SAM_SDMMC1_VBASE + SAMA5_SDMMC_AC12ERR_OFFSET) +#define SAMA5_SDMMC1_HTCAPBLT0 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_HTCAPBLT0_OFFSET) +#define SAMA5_SDMMC1_HTCAPBLT1 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_HTCAPBLT1_OFFSET) +#define SAMA5_SDMMC1_FEVT (SAM_SDMMC1_VBASE + SAMA5_SDMMC_FEVT_OFFSET) +#define SAMA5_SDMMC1_ADMAES (SAM_SDMMC1_VBASE + SAMA5_SDMMC_ADMAES_OFFSET) +#define SAMA5_SDMMC1_ADSADDR (SAM_SDMMC1_VBASE + SAMA5_SDMMC_ADSADDR_OFFSET) +#define SAMA5_SDMMC1_DLL_CONTROL (SAM_SDMMC1_VBASE + SAMA5_SDMMC_DLL_CONTROL_OFFSET) +#define SAMA5_SDMMC1_DLL_STATUS (SAM_SDMMC1_VBASE + SAMA5_SDMMC_DLL_STATUS) +#define SAMA5_USSDHC_CLK_TUNE_CTRL (SAM_SDMMC1_VBASE + SAMA5_SDMMC_CLK_TUNE_CTRL) +#define SAMA5_SDMMC1_VENDOR (SAM_SDMMC1_VBASE + SAMA5_SDMMC_VENDOR_OFFSET) +#define SAMA5_SDMMC1_MMCBOOT (SAM_SDMMC1_VBASE + SAMA5_SDMMC_MMCBOOT_OFFSET) +#define SAMA5_SDMMC1_VENDOR2 (SAM_SDMMC1_VBASE + SAMA5_SDMMC_VENDOR2_OFFSET) +#define SAMA5_SDMMC1_TC (SAM_SDMMC1_VBASE + SAMA5_SDMMC_TC_OFFSET) + +/* Register Bit Definitions *************************************************/ + +/* DMA System Address Register */ + +#define SDMMC_DSADDR_SHIFT (0) /* Bits 2-31: DMA System Address */ +#define SDMMC_DSADDR_MASK (0xfffffffc) /* Bits 0-1: 32 bit aligned, low bits Reserved */ + +/* Block Attributes Register */ + +#define SDMMC_BSR_BLKSIZE_SHIFT (0) /* Bits 0-9: Transfer Block Size */ + /* Bits 10-11: Reserved */ +#define SDMMC_BSR_BOUNDARY_SHIFT (12) /* Bits 12-14: Boundary */ + /* Bit 15: Reserved */ + +/* Block Count Register (16-bit block count) */ + +/* Command Argument Register (32-bit cmd/arg data) */ + +/* Transfer Type Register */ + +#define SDMMC_XFERTYP_DMAEN (1 << 0) /* Bit 0: DMA Enable */ +#define SDMMC_XFERTYP_BCEN (1 << 1) /* Bit 1: Block Count Enable */ +#define SDMMC_XFERTYP_AC12EN (1 << 2) /* Bit 2: Auto CMD12 Enable */ + /* Bit 3: Reserved */ + +#define SDMMC_XFERTYP_DTDSEL (1 << 4) /* Bit 4: Data Transfer Direction Select */ +#define SDMMC_XFERTYP_MSBSEL (1 << 5) /* Bit 5: Multi/Single Block Select */ + /* Bits 6-15: Reserved */ + +#define SDMMC_XFERTYP_RSPTYP_SHIFT (16) /* Bits 16-17: Response Type Select */ +#define SDMMC_XFERTYP_RSPTYP_MASK (3 << SDMMC_XFERTYP_RSPTYP_SHIFT) +# define SDMMC_XFERTYP_RSPTYP_NONE (0 << SDMMC_XFERTYP_RSPTYP_SHIFT) /* No response */ +# define SDMMC_XFERTYP_RSPTYP_LEN136 (1 << SDMMC_XFERTYP_RSPTYP_SHIFT) /* Response length 136 */ +# define SDMMC_XFERTYP_RSPTYP_LEN48 (2 << SDMMC_XFERTYP_RSPTYP_SHIFT) /* Response length 48 */ +# define SDMMC_XFERTYP_RSPTYP_LEN48BSY (3 << SDMMC_XFERTYP_RSPTYP_SHIFT) /* Response length 48, check busy */ + /* Bit 18: Reserved */ +#define SDMMC_XFERTYP_CCCEN (1 << 19) /* Bit 19: Command CRC Check Enable */ +#define SDMMC_XFERTYP_CICEN (1 << 20) /* Bit 20: Command Index Check Enable */ +#define SDMMC_XFERTYP_DPSEL (1 << 21) /* Bit 21: Data Present Select */ +#define SDMMC_XFERTYP_CMDTYP_SHIFT (22) /* Bits 22-23: Command Type */ +#define SDMMC_XFERTYP_CMDTYP_MASK (3 << SDMMC_XFERTYP_CMDTYP_SHIFT) +# define SDMMC_XFERTYP_CMDTYP_NORMAL (0 << SDMMC_XFERTYP_CMDTYP_SHIFT) /* Normal other commands */ +# define SDMMC_XFERTYP_CMDTYP_SUSPEND (1 << SDMMC_XFERTYP_CMDTYP_SHIFT) /* Suspend CMD52 for writing bus suspend in CCCR */ +# define SDMMC_XFERTYP_CMDTYP_RESUME (2 << SDMMC_XFERTYP_CMDTYP_SHIFT) /* Resume CMD52 for writing function select in CCCR */ +# define SDMMC_XFERTYP_CMDTYP_ABORT (3 << SDMMC_XFERTYP_CMDTYP_SHIFT) /* Abort CMD12, CMD52 for writing I/O abort in CCCR */ +#define SDMMC_XFERTYP_CMDINX_SHIFT (24) /* Bits 24-29: Command Index */ +#define SDMMC_XFERTYP_CMDINX_MASK (0x3f << SDMMC_XFERTYP_CMDINX_SHIFT) + /* Bits 30-31: Reserved */ + +/* Command Response 0-3 (32-bit response data) */ + +/* Buffer Data Port Register (32-bit data content) */ + +/* Present State Register */ + +#define SDMMC_PRSSTAT_CIHB (1 << 0) /* Bit 0: Command Inhibit (CMD) */ +#define SDMMC_PRSSTAT_CDIHB (1 << 1) /* Bit 1: Command Inhibit (DAT) */ +#define SDMMC_PRSSTAT_DLA (1 << 2) /* Bit 2: Data Line Active */ +#define SDMMC_PRSSTAT_SDSTB (1 << 3) /* Bit 3: Unused in SAMA5 */ +#define SDMMC_PRSSTAT_IPGOFF (1 << 4) /* Bit 4: Unused */ +#define SDMMC_PRSSTAT_HCKOFF (1 << 5) /* Bit 5: Unused */ +#define SDMMC_PRSSTAT_PEROFF (1 << 6) /* Bit 6: Unused */ +#define SDMMC_PRSSTAT_SDOFF (1 << 7) /* Bit 7: Unused */ +#define SDMMC_PRSSTAT_WTA (1 << 8) /* Bit 8: Write Transfer Active */ +#define SDMMC_PRSSTAT_RTA (1 << 9) /* Bit 9: Read Transfer Active */ +#define SDMMC_PRSSTAT_BWEN (1 << 10) /* Bit 10: Buffer Write Enable */ +#define SDMMC_PRSSTAT_BREN (1 << 11) /* Bit 11: Buffer Read Enable */ +#define SDMMC_PRSSTAT_RTR (1 << 12) /* Bit 12: Retuning request */ + /* Bits 13-14: Reserved */ +#define SDMMC_PRSSTAT_TSCD (1 << 15) /* Bit 15: Tape Select Change Done */ +#define SDMMC_PRSSTAT_CINS (1 << 16) /* Bit 16: Card Inserted */ + /* Bit 17: Reserved */ +#define SDMMC_PRSSTAT_CDPL (1 << 18) /* Bit 18: Card Detect Pin Level */ +#define SDMMC_PRSSTAT_WPSPL (1 << 19) /* Bit 19: Write Protect Switch Pin Level */ +#define SDMMC_PRSSTAT_CLSL (1 << 24) /* Bit 24: CMD Line Signal Level */ + /* Bits 25-31: Reserved */ +#define SDMMC_PRSSTAT_DLSL_SHIFT (20) /* Bits 20-23: DAT Line Signal Level */ +#define SDMMC_PRSSTAT_DLSL_MASK (0x0f << SDMMC_PRSSTAT_DLSL_SHIFT) +# define SDMMC_PRSSTAT_DLSL_DAT0 (0x01 << SDMMC_PRSSTAT_DLSL_SHIFT) +# define SDMMC_PRSSTAT_DLSL_DAT1 (0x02 << SDMMC_PRSSTAT_DLSL_SHIFT) +# define SDMMC_PRSSTAT_DLSL_DAT2 (0x04 << SDMMC_PRSSTAT_DLSL_SHIFT) +# define SDMMC_PRSSTAT_DLSL_DAT3 (0x08 << SDMMC_PRSSTAT_DLSL_SHIFT) + +/* Protocol Control Register */ + +#define SDMMC_PROCTL_LCTL (1 << 0) /* Bit 0: LED Control */ +#define SDMMC_PROCTL_DTW_SHIFT (1) /* Bit 1: Data Transfer Width */ +#define SDMMC_PROCTL_DTW_MASK (1 << SDMMC_PROCTL_DTW_SHIFT) +# define SDMMC_PROCTL_DTW_1BIT (0 << SDMMC_PROCTL_DTW_SHIFT) /* 1-bit mode */ +# define SDMMC_PROCTL_DTW_4BIT (1 << SDMMC_PROCTL_DTW_SHIFT) /* 4-bit mode */ +#define SDMMC_PROCTL_HSEN (1 << 2) /* Bit 2: High Speed Enable */ +#define SDMMC_PROCTL_DMAS_SHIFT (3) /* Bits 3-4: DMA Select */ +#define SDMMC_PROCTL_DMAS_MASK (3 << SDMMC_PROCTL_DMAS_SHIFT) +# define SDMMC_PROCTL_DMAS_SDMA (0 << SDMMC_PROCTL_DMAS_SHIFT) /* No DMA or simple DMA is selected */ +# define SDMMC_PROCTL_DMAS_RES1 (1 << SDMMC_PROCTL_DMAS_SHIFT) /* Reserved */ +# define SDMMC_PROCTL_DMAS_ADMA (2 << SDMMC_PROCTL_DMAS_SHIFT) /* ADMA is selected */ +# define SDMMC_PROCTL_DMAS_RES2 (1 << SDMMC_PROCTL_DMAS_SHIFT) /* Reserved */ +#define SDMMC_PROCTL_EXTDW_SHIFT (5) /* Bits 3-4: DMA Select */ +#define SDMMC_PROCTL_EXTDW_MASK (1 << SDMMC_PROCTL_EXTDW_SHIFT) + /* Bits 6-7: Reserved */ +#define SDMMC_PROCTL_SDBPWR_SHIFT (9) /* Bits 3-4: SD Bus Power */ +#define SDMMC_PROCTL_SDBPWR_MASK (1 << SDMMC_PROCTL_SDBPWR_SHIFT) + /* Bits 10-15: Reserved */ +#define SDMMC_PROCTL_SABGREQ (1 << 16) /* Bit 16: Stop At Block Gap Request */ +#define SDMMC_PROCTL_CREQ (1 << 17) /* Bit 17: Continue Request */ +#define SDMMC_PROCTL_RWCTL (1 << 18) /* Bit 18: Read Wait Control */ +#define SDMMC_PROCTL_IABG (1 << 19) /* Bit 19: Interrupt At Block Gap */ +#define SDMMC_PROCTL_RDDONENO8CLK (1 << 20) /* Bit 20: Read done to 8 clock */ +#define SDMMC_PROCTL_RESV2023 (4 << 21) /* Bits 21-23: Reserved, write as 0x100 */ +#define SDMMC_PROCTL_WECINT (1 << 24) /* Bit 24: Wakeup Event Enable On Card Interrupt */ +#define SDMMC_PROCTL_WECINS (1 << 25) /* Bit 25: Wakeup Event Enable On SD Card Insertion */ +#define SDMMC_PROCTL_WECRM (1 << 26) /* Bit 26: Wakeup Event Enable On SD Card Removal */ +#define SDMMC_PROCTL_BURST_SHIFT (27) /* Bits 27-29: Burst Length */ +#define SDMMC_PROCTL_BURST_MASK (7 << SDMMC_PROCTL_BUSRT_SHIFT) +# define SDMMC_PROCTL_BURST_INCR (1 << SDMMC_PROCTL_BURST_SHIFT) /* Burst for Incr */ +# define SDMMC_PROCTL_BURST_4816 (2 << SDMMC_PROCTL_BURST_SHIFT) /* Burst for 4/8/16 */ +# define SDMMC_PROCTL_BURST_4W8W16W (4 << SDMMC_PROCTL_BURST_SHIFT) /* Burst for 4w/8w/16w */ +#define SDMMC_PROTCTL_NEBLKRD (1 << 30) /* Bit 30: Non-exect block read */ + /* Bit 31: Reserved */ + +/* Power Control Register */ + +#define SDMMC_POWER_ON (0x01) +#define SDMMC_POWER_180 (0x0a) +#define SDMMC_POWER_300 (0x0c) +#define SDMMC_POWER_330 (0x0e) + +/* System Control Register */ + +#define SDMMC_SYSCTL_INTCLKEN (1 << 0) /* Bit 0: Internal Clock Enable */ +#define SDMMC_SYSCTL_INTCLKS (1 << 1) /* Bit 1: Internal Clock Stable */ +#define SDMMC_SYSCTL_SDCLKEN (1 << 2) /* Bit 2: SD Clock Enable */ +#define SDMMC_SYSCTL_CLKGSEL (1 << 5) /* Bit 5: Clock Generator Select */ +#define SDMMC_SYSCTL_USDCLKFSEL (3 << 6) /* Bit 0: Upper bits of SDCLK Frequency Select */ +#define SDMMC_SYSCTL_CLKFSEL (0xf0) /* Bit 0: SDCLK Frequency Select */ +#define SDMMC_CLOCK_MUL_MASK (0x00ff0000) +#define SDMMC_CLOCK_MUL_SHIFT (16) + +#define SDMMC_SYSCTL_DVS_SHIFT (4) /* Bits 4-7: Divisor */ +#define SDMMC_SYSCTL_DVS_MASK (0x0f << SDMMC_SYSCTL_DVS_SHIFT) +# define SDMMC_SYSCTL_DVS_DIV(n) (((n) - 1) << SDMMC_SYSCTL_DVS_SHIFT) /* Divide by n, n=1..16 */ +#define SDMMC_SYSCTL_SDCLKFS_SHIFT (8) /* Bits 8-15: SDCLK Frequency Select */ +#define SDMMC_SYSCTL_SDCLKFS_MASK (0xff << SDMMC_SYSCTL_SDCLKFS_SHIFT) +# define SDMMC_SYSCTL_SDCLKFS_BYPASS (0x00 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Bypass the prescaler */ +# define SDMMC_SYSCTL_SDCLKFS_DIV2 (0x01 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 2 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV4 (0x02 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 4 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV8 (0x04 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 8 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV16 (0x08 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 16 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV32 (0x10 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 32 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV64 (0x20 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 64 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV128 (0x40 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 128 */ +# define SDMMC_SYSCTL_SDCLKFS_DIV256 (0x80 << SDMMC_SYSCTL_SDCLKFS_SHIFT) /* Base clock / 256 */ +#define SDMMC_SYSCTL_DTOCV_SHIFT (16) /* Bits 16-19: Data Timeout Counter Value */ +#define SDMMC_SYSCTL_DTOCV_MASK (0x0f << SDMMC_SYSCTL_DTOCV_SHIFT) +# define SDMMC_SYSCTL_DTOCV_MUL(n) (((n) - 213) << SDMMC_SYSCTL_DTOCV_SHIFT) /* SDCLK x n, n=213..227 */ + /* Bits 20-22: Reserved */ +#define SDMMC_SYSCTL_IPPRSTN (1 << 23) /* Bit 23: Card /reset (default 1) */ +#define SDMMC_SYSCTL_RSTA (1 << 24) /* Bit 24: Software Reset For ALL */ +#define SDMMC_SYSCTL_RSTC (1 << 25) /* Bit 25: Software Reset For CMD Line */ +#define SDMMC_SYSCTL_RSTD (1 << 26) /* Bit 26: Software Reset For DAT Line */ +#define SDMMC_SYSCTL_INITA (1 << 27) /* Bit 27: Initialization Active (SAMA5: unused) */ +#define SDMMC_SYSCTL_RSTT (1 << 28) /* Bit 28: Reset tuning (SAMA5: unused) */ + /* Bits 29-31: Reserved */ +#define SDMMC_DIVIDER_SHIFT (8) +#define SDMMC_DIV_MASK (0xff) +#define SDMMC_DIVIDER_HI_SHIFT (6) +#define SDMMC_DIV_MASK_LEN (8) +#define SDMMC_DIV_HI_MASK (0x300) +#define SDMMC_CLOCK_BASE_MASK (0x00003f00) +#define SDMMC_CLOCK_V3_BASE_MASK (0x0000ff00) +#define SDMMC_CLOCK_BASE_SHIFT (8) + +/* Timeout Control Register */ + +#define SDMMC_TCR_MASK (0x0f) /* Bits 0-3 */ + +/* Interrupt Status Register, Interrupt Status Enable Register and Interrupt + * Signal Enable Register Common interrupt bit definitions + */ + +#define SDMMC_INT_CC (1 << 0) /* Bit 0: Command Complete */ +#define SDMMC_INT_TC (1 << 1) /* Bit 1: Transfer Complete */ +#define SDMMC_INT_BGE (1 << 2) /* Bit 2: Block Gap Event */ +#define SDMMC_INT_DINT (1 << 3) /* Bit 3: DMA Interrupt - indicates SDMA Boundary Pause */ +#define SDMMC_INT_BWR (1 << 4) /* Bit 4: Buffer Write Ready */ +#define SDMMC_INT_BRR (1 << 5) /* Bit 5: Buffer Read Ready */ +#define SDMMC_INT_CINS (1 << 6) /* Bit 6: Card Insertion */ +#define SDMMC_INT_CRM (1 << 7) /* Bit 7: Card Removal */ +#define SDMMC_INT_CINT (1 << 8) /* Bit 8: Card Interrupt */ + /* Bits 9-11: Reserved */ +#define SDMMC_INT_RTR (1 << 12) /* Bit 12: Re-tuning event */ + /* Bit 13: Reserved */ +#define SDMMC_INT_TP (1 << 14) /* Bit 14: Tuning pass */ + /* Bit 15: Reserved */ +#define SDMMC_INT_CTOE (1 << 16) /* Bit 16: Command Timeout Error */ +#define SDMMC_INT_CCE (1 << 17) /* Bit 17: Command CRC Error */ +#define SDMMC_INT_CEBE (1 << 18) /* Bit 18: Command End Bit Error */ +#define SDMMC_INT_CIE (1 << 19) /* Bit 19: Command Index Error */ +#define SDMMC_INT_DTOE (1 << 20) /* Bit 20: Data Timeout Error */ +#define SDMMC_INT_DCE (1 << 21) /* Bit 21: Data CRC Error */ +#define SDMMC_INT_DEBE (1 << 22) /* Bit 22: Data End Bit Error */ +#define SDMMC_INT_CURLIM (1 << 23) /* Bit 23: Current Limit Error */ +#define SDMMC_INT_AC12E (1 << 24) /* Bit 24: Auto CMD12 Error */ +#define SDMMC_INT_ADMAE (1 << 25) /* Bit 25: ADMA error */ + /* Bits 26-31: Reserved */ +#define SDMMC_INT_ALL 0x117f01ff + +/* Auto CMD12 Error Status Register */ + +#define SDMMC_AC12ERR_NE (1 << 0) /* Bit 0: Auto CMD12 Not Executed */ +#define SDMMC_AC12ERR_TOE (1 << 1) /* Bit 1: Auto CMD12 Timeout Error */ +#define SDMMC_AC12ERR_EBE (1 << 2) /* Bit 2: Auto CMD12 End Bit Error */ +#define SDMMC_AC12ERR_CE (1 << 3) /* Bit 3: Auto CMD12 CRC Error */ +#define SDMMC_AC12ERR_IE (1 << 4) /* Bit 4: Auto CMD12 Index Error */ + /* Bits 5-6: Reserved */ +#define SDMMC_AC12ERR_CNI (1 << 7) /* Bit 7: Command Not Issued By Auto CMD12 Error */ + /* Bits 8-21: Reserved */ +#define SDMMC_AC12ERR_EXECUTE_TUNING (1 << 22) /* Bit 22: Execute Tuning */ +#define SDMMC_AC12ERR_SMP_CLK_SEL (1 << 23) /* Bit 23: Sample clock sel */ + /* Bits 24-31: Reserved */ + +/* Host Controller Capabilities */ + +#define SDMMC_HTCAPBLT_SDR50 (1 << 0) /* Bit 0: SDR50 support indication */ +#define SDMMC_HTCAPBLT_SDR104 (1 << 1) /* Bit 1: SDR104 support indication */ +#define SDMMC_HTCAPBLT_DDR50 (1 << 2) /* Bit 2: DDR50 support indication */ + /* Bits 3-7: Reserved */ +#define SDMMC_HTCAPBLT_TCR_SHIFT (8) /* Bits 8-11: Time count retuning */ +#define SDMMC_HTCAPBLT_TCR_MASK (0xF << SDMMC_HTCAPBLT_TCR_SHIFT) +# define SDMMC_HTCAPBLT_TCR(n) ((n) << SDMMC_HTCAPBLT_TCR_SHIFT) +#define SDMMC_HTCAPBLT_USE_TUNING_SDR50 (1 << 13) /* Bit 13: Use tuning for SDR50 */ +#define SDMMC_HTCAPBLT_RET_MODE_SHIFT (14) /* bit 14-15: Retuning mode */ +#define SDMMC_HTCAPBLT_RET_MODE_MASK (3< & Contributors + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_HARDWARE_SAMA5D2_SDMMC_H +#define __ARCH_ARM_SRC_SAMA5_HARDWARE_SAMA5D2_SDMMC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define SAMA5D2_SDMMC_SSAR_OFFSET 0x0000 /* SDMA System Address / Argument 2 Register */ +#define SAMA5D2_SDMMC_BSR_OFFSET 0x0004 /* Block Size Register */ +#define SAMA5D2_SDMMC_BCR_OFFSET 0x0006 /* Block Count Register */ +#define SAMA5D2_SDMMC_ARG1R_OFFSET 0x0008 /* Argument 1 Register */ +#define SAMA5D2_SDMMC_TMR_OFFSET 0x000c /* Transfer Mode Register */ +#define SAMA5D2_SDMMC_CR_OFFSET 0x000e /* Command Register */ +#define SAMA5D2_SDMMC_RR0_OFFSET 0x0010 /* Response Register 0 */ +#define SAMA5D2_SDMMC_RR1_OFFSET 0x0014 /* Response Register 1 */ +#define SAMA5D2_SDMMC_RR2_OFFSET 0x0018 /* Response Register 2 */ +#define SAMA5D2_SDMMC_RR3_OFFSET 0x001c /* Response Register 3 */ +#define SAMA5D2_SDMMC_BDPR_OFFSET 0x0020 /* Buffer Data Port Register */ +#define SAMA5D2_SDMMC_PSR_OFFSET 0x0024 /* Present State Register */ +#define SAMA5D2_SDMMC_HCR1_OFFSET 0x0028 /* Host Control 1 Register */ +#define SAMA5D2_SDMMC_PR_OFFSET 0x0029 /* Power Control Register */ +#define SAMA5D2_SDMMC_BGCR_OFFSET 0x002a /* Block Gap Control Register */ +#define SAMA5D2_SDMMC_WCR_OFFSET 0x002b /* Wakeup Control Register */ +#define SAMA5D2_SDMMC_CCR_OFFSET 0x002c /* Clock Control Register */ +#define SAMA5D2_SDMMC_TCR_OFFSET 0x002e /* Timeout Control Register */ +#define SAMA5D2_SDMMC_SRR_OFFSET 0x002f /* Software Reset Register */ +#define SAMA5D2_SDMMC_NISTR_OFFSET 0x0030 /* Normal Interrupt Status Register */ +#define SAMA5D2_SDMMC_EISTR_OFFSET 0x0032 /* Error Interrupt Status Register */ +#define SAMA5D2_SDMMC_NISTER_OFFSET 0x0034 /* Normal Interrupt Status Enable Register */ +#define SAMA5D2_SDMMC_EISTER_OFFSET 0x0036 /* Error Interrupt Status Enable Register */ +#define SAMA5D2_SDMMC_NISIER_OFFSET 0x0038 /* Normal Interrupt Signal Enable Register */ +#define SAMA5D2_SDMMC_EISIER_OFFSET 0x003a /* Error Interrupt Signal Enable Register */ +#define SAMA5D2_SDMMC_ACESR_OFFSET 0x003c /* Auto CMD Error Status Register */ +#define SAMA5D2_SDMMC_HC2R_OFFSET 0x003e /* Host Control 2 Register */ +#define SAMA5D2_SDMMC_CA0R_OFFSET 0x0040 /* Capabilities 0 Register */ +#define SAMA5D2_SDMMC_CA1R_OFFSET 0x0044 /* Capabilities 1 Register */ +#define SAMA5D2_SDMMC_MCCAR_OFFSET 0x0048 /* Maximum Current Capabilities Register */ +#define SAMA5D2_SDMMC_FERACES_OFFSET 0x0050 /* Force Event Register for Auto CMD Error Status */ +#define SAMA5D2_SDMMC_FEREIS_OFFSET 0x0052 /* Force Event Register for Error Interrupt Status */ +#define SAMA5D2_SDMMC_AESR_OFFSET 0x0054 /* ADMA Error Status Register */ +#define SAMA5D2_SDMMC_ASAR0_OFFSET 0x0054 /* ADMA System Address Register 0 */ +#define SAMA5D2_SDMMC_PVRX_OFFSET(x) (0x60 + x * 0x02) /* Preset Value Register */ +#define SAMA5D2_SDMMC_PVR0_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(0) /* Initialization */ +#define SAMA5D2_SDMMC_PVR1_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(1) /* Default Speed */ +#define SAMA5D2_SDMMC_PVR2_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(2) /* High Speed */ +#define SAMA5D2_SDMMC_PVR3_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(3) /* SDR12 */ +#define SAMA5D2_SDMMC_PVR4_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(4) /* SDR25 */ +#define SAMA5D2_SDMMC_PVR5_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(5) /* SDR50 */ +#define SAMA5D2_SDMMC_PVR6_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(6) /* SDR104/HS200 */ +#define SAMA5D2_SDMMC_PVR7_OFFSET SAMA5D2_SDMMC_PVRX_OFFSET(7) /* DDR50 */ +#define SAMA5D2_SDMMC_SISR_OFFSET 0x00fc /* Slot Interrupt Status Register */ +#define SAMA5D2_SDMMC_HCVR_OFFSET 0x00fe /* Host Controller Version Register */ +#define SAMA5D2_SDMMC_APSR_OFFSET 0x0200 /* Additional Present State Register */ +#define SAMA5D2_SDMMC_MC1R_OFFSET 0x0204 /* e.MMC Control 1 Register */ +#define SAMA5D2_SDMMC_MC2R_OFFSET 0x0204 /* e.MMC Control 2 Register */ +#define SAMA5D2_SDMMC_ACR_OFFSET 0x0208 /* AHB Control Register */ +#define SAMA5D2_SDMMC_CC2R_OFFSET 0x020c /* Clock Control 2 Register */ +#define SAMA5D2_SDMMC_RTC1R_OFFSET 0x0210 /* Retuning Control 1 Register */ +#define SAMA5D2_SDMMC_RTC2R_OFFSET 0x0211 /* Retuning Control 2 Register */ +#define SAMA5D2_SDMMC_RTCVR_OFFSET 0x0214 /* Retuning Counter Value Register */ +#define SAMA5D2_SDMMC_RTISTER_OFFSET 0x0218 /* Retuning Interrupt Status Enable Register */ +#define SAMA5D2_SDMMC_RTISIER_OFFSET 0x0219 /* Retuning Interrupt Signal Enable Register */ +#define SAMA5D2_SDMMC_RTISTR_OFFSET 0x021c /* Retuning Interrupt Status Register */ +#define SAMA5D2_SDMMC_RTSSR_OFFSET 0x021d /* Retuning Status Slots Register */ +#define SAMA5D2_SDMMC_TUNCR_OFFSET 0x0220 /* Tuning Control Register */ +#define SAMA5D2_SDMMC_CACR_OFFSET 0x0230 /* Capabilities Control Register */ +#define SAMA5D2_SDMMC_CALCR_OFFSET 0x0240 /* Calibration Control Register */ + +/* Register Addresses ***************************************************************/ + +/* For SDMMC0 ... */ + +#define SAMA5D2_SDMMC0_SSAR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_SSAR_OFFSET) +#define SAMA5D2_SDMMC0_BSR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_BSR_OFFSET) +#define SAMA5D2_SDMMC0_BCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_BCR_OFFSET) +#define SAMA5D2_SDMMC0_ARG1R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_ARG1R_OFFSET) +#define SAMA5D2_SDMMC0_TMR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_TMR_OFFSET) +#define SAMA5D2_SDMMC0_CR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CR_OFFSET) +#define SAMA5D2_SDMMC0_RR0 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RR0_OFFSET) +#define SAMA5D2_SDMMC0_RR1 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RR1_OFFSET) +#define SAMA5D2_SDMMC0_RR2 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RR2_OFFSET) +#define SAMA5D2_SDMMC0_RR3 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RR3_OFFSET) +#define SAMA5D2_SDMMC0_BDPR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_BDPR_OFFSET) +#define SAMA5D2_SDMMC0_PSR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PSR_OFFSET) +#define SAMA5D2_SDMMC0_HCR1 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_HCR1_OFFSET) +#define SAMA5D2_SDMMC0_PR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PR_OFFSET) +#define SAMA5D2_SDMMC0_BGCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_BGCR_OFFSET) +#define SAMA5D2_SDMMC0_WCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_WCR_OFFSET) +#define SAMA5D2_SDMMC0_CCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CCR_OFFSET) +#define SAMA5D2_SDMMC0_TCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_TCR_OFFSET) +#define SAMA5D2_SDMMC0_SRR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_SRR_OFFSET) +#define SAMA5D2_SDMMC0_NISTR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_NISTR_OFFSET) +#define SAMA5D2_SDMMC0_EISTR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_EISTR_OFFSET) +#define SAMA5D2_SDMMC0_NISTER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_NISTER_OFFSET) +#define SAMA5D2_SDMMC0_EISTER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_EISTER_OFFSET) +#define SAMA5D2_SDMMC0_NISIER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_NISIER_OFFSET) +#define SAMA5D2_SDMMC0_EISIER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_EISIER_OFFSET) +#define SAMA5D2_SDMMC0_ACESR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_ACESR_OFFSET) +#define SAMA5D2_SDMMC0_HC2R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_HC2R_OFFSET) +#define SAMA5D2_SDMMC0_CA0R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CA0R_OFFSET) +#define SAMA5D2_SDMMC0_CA1R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CA1R_OFFSET) +#define SAMA5D2_SDMMC0_MCCAR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_MCCAR_OFFSET) +#define SAMA5D2_SDMMC0_FERACES (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_FERACES_OFFSET) +#define SAMA5D2_SDMMC0_FEREIS (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_FEREIS_OFFSET) +#define SAMA5D2_SDMMC0_AESR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_AESR_OFFSET) +#define SAMA5D2_SDMMC0_ASAR0 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_ASAR0_OFFSET) +#define SAMA5D2_SDMMC0_PVRX(x) (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVRX_OFFSET(x)) +#define SAMA5D2_SDMMC0_PVR0 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR0_OFFSET) +#define SAMA5D2_SDMMC0_PVR1 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR1_OFFSET) +#define SAMA5D2_SDMMC0_PVR2 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR2_OFFSET) +#define SAMA5D2_SDMMC0_PVR3 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR3_OFFSET) +#define SAMA5D2_SDMMC0_PVR4 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR4_OFFSET) +#define SAMA5D2_SDMMC0_PVR5 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR5_OFFSET) +#define SAMA5D2_SDMMC0_PVR6 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR6_OFFSET) +#define SAMA5D2_SDMMC0_PVR7 (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_PVR7_OFFSET) +#define SAMA5D2_SDMMC0_SISR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_SISR_OFFSET) +#define SAMA5D2_SDMMC0_HCVR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_HCVR_OFFSET) +#define SAMA5D2_SDMMC0_APSR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_APSR_OFFSET) +#define SAMA5D2_SDMMC0_MC1R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_MC1R_OFFSET) +#define SAMA5D2_SDMMC0_MC2R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_MC2R_OFFSET) +#define SAMA5D2_SDMMC0_ACR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_ACR_OFFSET) +#define SAMA5D2_SDMMC0_CC2R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CC2R_OFFSET) +#define SAMA5D2_SDMMC0_RTC1R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTC1R_OFFSET) +#define SAMA5D2_SDMMC0_RTC2R (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTC2R_OFFSET) +#define SAMA5D2_SDMMC0_RTCVR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTCVR_OFFSET) +#define SAMA5D2_SDMMC0_RTISTER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTISTER_OFFSET) +#define SAMA5D2_SDMMC0_RTISIER (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTISIER_OFFSET) +#define SAMA5D2_SDMMC0_RTISTR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTISTR_OFFSET) +#define SAMA5D2_SDMMC0_RTSSR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_RTSSR_OFFSET) +#define SAMA5D2_SDMMC0_TUNCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_TUNCR_OFFSET) +#define SAMA5D2_SDMMC0_CACR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CACR_OFFSET) +#define SAMA5D2_SDMMC0_CALCR (SAM_SDMMC0_VBASE + SAMA5D2_SDMMC_CALCR_OFFSET) + +/* For SDMMC1 ... */ + +#define SAMA5D2_SDMMC1_SSAR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_SSAR_OFFSET) +#define SAMA5D2_SDMMC1_BSR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_BSR_OFFSET) +#define SAMA5D2_SDMMC1_BCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_BCR_OFFSET) +#define SAMA5D2_SDMMC1_ARG1R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_ARG1R_OFFSET) +#define SAMA5D2_SDMMC1_TMR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_TMR_OFFSET) +#define SAMA5D2_SDMMC1_CR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CR_OFFSET) +#define SAMA5D2_SDMMC1_RR0 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RR0_OFFSET) +#define SAMA5D2_SDMMC1_RR1 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RR1_OFFSET) +#define SAMA5D2_SDMMC1_RR2 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RR2_OFFSET) +#define SAMA5D2_SDMMC1_RR3 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RR3_OFFSET) +#define SAMA5D2_SDMMC1_BDPR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_BDPR_OFFSET) +#define SAMA5D2_SDMMC1_PSR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PSR_OFFSET) +#define SAMA5D2_SDMMC1_HCR1 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_HCR1_OFFSET) +#define SAMA5D2_SDMMC1_PR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PR_OFFSET) +#define SAMA5D2_SDMMC1_BGCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_BGCR_OFFSET) +#define SAMA5D2_SDMMC1_WCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_WCR_OFFSET) +#define SAMA5D2_SDMMC1_CCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CCR_OFFSET) +#define SAMA5D2_SDMMC1_TCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_TCR_OFFSET) +#define SAMA5D2_SDMMC1_SRR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_SRR_OFFSET) +#define SAMA5D2_SDMMC1_NISTR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_NISTR_OFFSET) +#define SAMA5D2_SDMMC1_EISTR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_EISTR_OFFSET) +#define SAMA5D2_SDMMC1_NISTER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_NISTER_OFFSET) +#define SAMA5D2_SDMMC1_EISTER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_EISTER_OFFSET) +#define SAMA5D2_SDMMC1_NISIER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_NISIER_OFFSET) +#define SAMA5D2_SDMMC1_EISIER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_EISIER_OFFSET) +#define SAMA5D2_SDMMC1_ACESR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_ACESR_OFFSET) +#define SAMA5D2_SDMMC1_HC2R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_HC2R_OFFSET) +#define SAMA5D2_SDMMC1_CA0R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CA0R_OFFSET) +#define SAMA5D2_SDMMC1_CA1R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CA1R_OFFSET) +#define SAMA5D2_SDMMC1_MCCAR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_MCCAR_OFFSET) +#define SAMA5D2_SDMMC1_FERACES (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_FERACES_OFFSET) +#define SAMA5D2_SDMMC1_FEREIS (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_FEREIS_OFFSET) +#define SAMA5D2_SDMMC1_AESR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_AESR_OFFSET) +#define SAMA5D2_SDMMC1_ASAR0 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_ASAR0_OFFSET) +#define SAMA5D2_SDMMC1_PVRX(x) (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVRX_OFFSET(x)) +#define SAMA5D2_SDMMC1_PVR0 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR0_OFFSET) +#define SAMA5D2_SDMMC1_PVR1 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR1_OFFSET) +#define SAMA5D2_SDMMC1_PVR2 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR2_OFFSET) +#define SAMA5D2_SDMMC1_PVR3 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR3_OFFSET) +#define SAMA5D2_SDMMC1_PVR4 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR4_OFFSET) +#define SAMA5D2_SDMMC1_PVR5 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR5_OFFSET) +#define SAMA5D2_SDMMC1_PVR6 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR6_OFFSET) +#define SAMA5D2_SDMMC1_PVR7 (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_PVR7_OFFSET) +#define SAMA5D2_SDMMC1_SISR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_SISR_OFFSET) +#define SAMA5D2_SDMMC1_HCVR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_HCVR_OFFSET) +#define SAMA5D2_SDMMC1_APSR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_APSR_OFFSET) +#define SAMA5D2_SDMMC1_MC1R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_MC1R_OFFSET) +#define SAMA5D2_SDMMC1_MC2R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_MC2R_OFFSET) +#define SAMA5D2_SDMMC1_ACR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_ACR_OFFSET) +#define SAMA5D2_SDMMC1_CC2R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CC2R_OFFSET) +#define SAMA5D2_SDMMC1_RTC1R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTC1R_OFFSET) +#define SAMA5D2_SDMMC1_RTC2R (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTC2R_OFFSET) +#define SAMA5D2_SDMMC1_RTCVR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTCVR_OFFSET) +#define SAMA5D2_SDMMC1_RTISTER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTISTER_OFFSET) +#define SAMA5D2_SDMMC1_RTISIER (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTISIER_OFFSET) +#define SAMA5D2_SDMMC1_RTISTR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTISTR_OFFSET) +#define SAMA5D2_SDMMC1_RTSSR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_RTSSR_OFFSET) +#define SAMA5D2_SDMMC1_TUNCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_TUNCR_OFFSET) +#define SAMA5D2_SDMMC1_CACR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CACR_OFFSET) +#define SAMA5D2_SDMMC1_CALCR (SAM_SDMMC1_VBASE + SAMA5D2_SDMMC_CALCR_OFFSET) + +/* Register Bit Definitions *********************************************************/ + +/* Block Size Register (0x04) */ + +#define SDMMC_BSR_BLKSIZE_SHIFT (0) /* Bits 0-9: Transfer Block Size */ +#define SDMMC_BSR_BLKSIZE_MASK (0x3ff << SDMMC_BSR_SIZE_SHIFT) +# define SDMMC_BSR_BLKSIZE(n) ((n & SDMMC_BSR_BLKSIZE_MASK) << SDMMC_BSR_BLKSIZE_SHIFT) + +#define SDMMC_BSR_BOUNDARY_SHIFT (12) /* Bits 12-14: Buffer Boundary */ +# define SDMMC_BSR_BOUNDARY_4K (0 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_8K (1 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_16K (2 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_32K (3 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_64K (4 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_128K (5 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_256K (6 << SDMMC_BSR_BOUNDARY_SHIFT) +# define SDMMC_BSR_BOUNDARY_512K (7 << SDMMC_BSR_BOUNDARY_SHIFT) + +/* Block Count Register (0x06) */ + +#define SDMMC_BCR_BLKCNT_SHIFT (0) /* Bits 0-15: Blocks Count For Current Transfer */ +#define SDMMC_BCR_BLKCNT_MASK (0xffff << SDMMC_BCR_BLKCNT_SHIFT) +# define SDMMC_BCR_BLKCNT(n) ((n) << SDMMC_BCR_BLKCNT_SHIFT) + +/* Transfer Mode Register (0x0c) */ + +#define SDMMC_TMR_MSBSEL (1 << 5) /* Bit 5: Multi/Single Block Selection */ +#define SDMMC_TMR_DTDSEL (1 << 4) /* Bit 4: Data Transfer Direction Selection */ +#define SDMMC_TMR_ACMDEN_SHIFT (2) +# define SDMMC_TMR_ACMDEN_DISABLED (0 << SDMMC_TMR_ACMDEN_SHIFT) +# define SDMMC_TMR_ACMDEN_CMD12 (1 << SDMMC_TMR_ACMDEN_SHIFT) +# define SDMMC_TMR_ACMDEN_CMD23 (2 << SDMMC_TMR_ACMDEN_SHIFT) +#define SDMMC_TMR_BCEN (1 << 1) /* Bit 1: Block Count Enable */ +#define SDMMC_TMR_DMAEN (1 << 0) /* Bit 0: DMA Enable */ +#define SDMMC_TMR_SINGLE (0) +#define SDMMC_TMR_INFINITE (SDMMC_TMR_MSBSEL) +#define SDMMC_TMR_MULTIPLE (SDMMC_TMR_MSBSEL | SDMMC_TMR_BCEN) + +/* Command Register (0x0e) */ + +#define SDMMC_CR_RSPTYP_SHIFT (0) /* Bits 0-1: Response Type Select */ +#define SDMMC_CR_RSPTYP_MASK (3 << SDMMC_CR_RSPTYP_SHIFT) +# define SDMMC_CR_RSPTYP_NORESP (0 << SDMMC_CR_RSPTYP_SHIFT) /* No response */ +# define SDMMC_CR_RSPTYP_RL136 (1 << SDMMC_CR_RSPTYP_SHIFT) /* Response length 136 */ +# define SDMMC_CR_RSPTYP_RL48 (2 << SDMMC_CR_RSPTYP_SHIFT) /* Response length 48 */ +# define SDMMC_CR_RSPTYP_RL48BUSY (3 << SDMMC_CR_RSPTYP_SHIFT) /* Response length 48 with Busy */ + +#define SDMMC_CR_CMDCCCEN (1 << 3) /* Bit 3: Command CRC Check Enable */ +#define SDMMC_CR_CMDCICEN (1 << 4) /* Bit 4: Command Index Check Enable */ +#define SDMMC_CR_DPSEL (1 << 5) /* Bit 5: Data Present Select */ +#define SDMMC_CR_CMDTYP_SHIFT (6) /* Bits 6-7: Command Type */ +#define SDMMC_CR_CMDTYP_MASK (3 << SDMMC_CR_CMDTYP_SHIFT) +# define SDMMC_CR_CMDTYP_NORMAL (0 << SDMMC_CR_CMDTYP_SHIFT) /* Other commands */ +# define SDMMC_CR_CMDTYP_SUSPEND (1 << SDMMC_CR_CMDTYP_SHIFT) /* CMD52 to write "Bus Suspend" in CCCR */ +# define SDMMC_CR_CMDTYP_RESUME (2 << SDMMC_CR_CMDTYP_SHIFT) /* CMD52 to write "Function Select" in CCCR */ +# define SDMMC_CR_CMDTYP_ABORT (3 << SDMMC_CR_CMDTYP_SHIFT) /* CMD12, CMD52 to write "I/O Abort" in CCCR */ +#define SDMMC_CR_CMDINX_SHIFT (8) /* Bits 8-13: Command Index */ +#define SDMMC_CR_CMDINX_MASK (0x3f << SDMMC_CR_CMDINX_SHIFT) + +/* Present State Register (0x24) */ + +#define SDMMC_PSR_CMDINHC (1 << 0) /* Bit 0: Command Inhibit (CMD) */ +#define SDMMC_PSR_CMDINHD (1 << 1) /* Bit 1: Command Inhibit (DAT) */ +#define SDMMC_PSR_DLACT (1 << 2) /* Bit 2: Data Line Active */ + /* Bits 3-7: Reserved */ +#define SDMMC_PSR_WTACT (1 << 8) /* Bit 8: Write Transfer Active */ +#define SDMMC_PSR_RTACT (1 << 9) /* Bit 9: Read Transfer Active */ +#define SDMMC_PSR_BUFWREN (1 << 10) /* Bit 10: Buffer Write Enable */ +#define SDMMC_PSR_BUFRDEN (1 << 11) /* Bit 11: Buffer Read Enable */ + /* Bits 12-15: Reserved */ +#define SDMMC_PSR_CARDINS (1 << 16) /* Bit 16: Card Inserted */ +#define SDMMC_PSR_CARDSS (1 << 17) /* Bit 17: Card State Stable */ +#define SDMMC_PSR_CARDDPL (1 << 18) /* Bit 18: Card Detect Pin Level */ +#define SDMMC_PSR_WRPPL (1 << 19) /* Bit 19: Write Protect Pin Level */ +#define SDMMC_PSR_DATLL_SHIFT (20) +#define SDMMC_PSR_DATLL_MASK (0x0f << SDMMC_PSR_DATLL_SHIFT) +# define SDMMC_PSR_DATLL_DAT0 (0x01 << SDMMC_PSR_DATLL_SHIFT) +# define SDMMC_PSR_DATLL_DAT1 (0x02 << SDMMC_PSR_DATLL_SHIFT) +# define SDMMC_PSR_DATLL_DAT2 (0x04 << SDMMC_PSR_DATLL_SHIFT) +# define SDMMC_PSR_DATLL_DAT3 (0x08 << SDMMC_PSR_DATLL_SHIFT) +#define SDMMC_PSR_CMDLL (1 << 24) /* Bit 24: CMD Line Level */ + +/* Host Control 1 Register (0x28) */ + +#define SDMMC_HC1R_LEDCTRL (1 << 0) /* Bit 0: LED Control */ +#define SDMMC_HC1R_DW_SHIFT (1) /* Bit 1: Data Width */ +#define SDMMC_HC1R_DW_MASK (1 << SDMMC_HC1R_DW_SHIFT) +# define SDMMC_HC1R_DW_1BIT (0 << SDMMC_HC1R_DW_SHIFT) /* 1-bit mode */ +# define SDMMC_HC1R_DW_4BIT (1 << SDMMC_HC1R_DW_SHIFT) /* 4-bit mode */ +#define SDMMC_H1CR_HSEN (1 << 2) /* Bit 2: High Speed Enable */ +#define SDMMC_H1CR_DMASEL_SHIFT (3) /* Bits 3-4: DMA Select */ +#define SDMMC_H1CR_DMASEL_MASK (3 << SDMMC_DMASEL_SHIFT) +# define SDMMC_H1CR_DMASEL_SDMA (0 << SDMMC_DMASEL_SHIFT) /* SDMA is selected */ +# define SDMMC_H1CR_DMASEL_ADMA32 (2 << SDMMC_DMASEL_SHIFT) /* 32-bit Address ADMA2 is selected */ +#define SDMMC_H1CR_EXTDW (1 << 5) /* Bit 5: Extended Data Width (e.MMC) */ +#define SDMMC_H1CR_CARDDTL (1 << 6) /* Bit 6: Card Detect Test Level */ +#define SDMMC_H1CR_CARDDSEL (1 << 7) /* Bit 7: Card Detect Signal Selection */ + +/* Power Control Register (0x29) */ + +#define SDMMC_PCR_SDBPWR (1 << 0) /* Bit 0: SD Bus Power */ + +/* Block Gap Control Register (0x2a) */ + +#define SDMMC_BGCR_STPBGR (1 << 0) /* Bit 0: Stop At Block Gap Request */ +#define SDMMC_BGCR_CONTR (1 << 1) /* Bit 1: Continue Request */ +#define SDMMC_BGCR_RWCTRL (1 << 2) /* Bit 2: Read Wait Control */ +#define SDMMC_BGCR_INTBG (1 << 3) /* Bit 3: Interrupt at Block Gap */ + +/* Wakeup Control Register (0x2b) */ + +#define SDMMC_WCR_WKENCINT (1 << 0) /* Bit 0: Wakeup Event Enable on Card Interrupt */ +#define SDMMC_WCR_WKENCINS (1 << 1) /* Bit 1: Wakeup Event Enable on Card Insertion */ +#define SDMMC_WCR_WKENCREM (1 << 2) /* Bit 2: Wakeup Event Enable on Card Removal */ + +/* Clock Control Register (0x2c) */ + +#define SDMMC_CCR_INTCLKEN (1 << 0) /* Bit 0: Internal Clock Enable */ +#define SDMMC_CCR_INTCLKS (1 << 1) /* Bit 1: Internal Clock Stable */ +#define SDMMC_CCR_SDCLKEN (1 << 2) /* Bit 2: SD Clock Enable */ +#define SDMMC_CCR_CLKGSEL (1 << 5) /* Bit 5: Clock Generator Select */ +#define SDMMC_CCR_USDCLKFSEL_SHIFT (6) /* Bits 6-7: Upper Bits of SDCLK Frequency Select */ +#define SDMMC_CCR_USDCLKFSEL_MASK (3 << SDMMC_CCR_USDCLKFSEL_SHIFT) +#define SDMMC_CCR_SDCLKFSEL_SHIFT (8) /* Bits 8-15: SDCLK Frequency Select */ +#define SDMMC_CCR_SDCLKFSEL_MASK (0xff << SDMMC_CCR_SDCLKFSEL_SHIFT) +# define SDMMC_CCR_SDCLKFSEL(n) (((n & 0xff) << SDMMC_CCR_SDCLKFSEL_SHIFT) | \ + ((n & 0x300) >> 2)) +# define SDMMC_CCR_SDCLKFSEL_VAL(reg) (((reg & 0xff00) >> SDMMC_CCR_SDCLKFSEL_SHIFT) | \ + ((reg & 0x0030) << 2)) + +/* Timeout Control Register (0x2e) */ + +#define SDMMC_TCR_DTCVAL_SHIFT (0) /* Bits 0-3: Data Timeout Counter Value */ +#define SDMMC_TCR_DTCVAL_MASK (0x0f << SDMMC_TCR_DTCVAL_SHIFT) +# define SDMMC_TCR_DTCVAL(n) ((n & SDMMC_TCR_DTCVAL_MASK) << SDMMC_TCR_DTCVAL_SHIFT) + +/* Software Reset Register (0x2f) */ + +#define SDMMC_SRR_SWRSTALL (1 << 0) /* Bit 0: Software Reset for All */ +#define SDMMC_SRR_SWRSTCMD (1 << 1) /* Bit 1: Software Reset for CMD Line */ +#define SDMMC_SRR_SWRSTDAT (1 << 2) /* Bit 2: Software Reset for DAT Line */ + +/* Normal Interrupt Status Register (0x30) */ + +#define SDMMC_NISTR_CMDC (1 << 0) /* Bit 0: Command Complete */ +#define SDMMC_NISTR_TRFC (1 << 1) /* Bit 1: Transfer Complete */ +#define SDMMC_NISTR_BLKGE (1 << 2) /* Bit 2: Block Gap Event */ +#define SDMMC_NISTR_DMAINT (1 << 3) /* Bit 3: DMA Interrupt */ +#define SDMMC_NISTR_BWRRDY (1 << 4) /* Bit 4: Buffer Write Ready */ +#define SDMMC_NISTR_BRDRDY (1 << 5) /* Bit 5: Buffer Read Ready */ +#define SDMMC_NISTR_CINS (1 << 6) /* Bit 6: Card Insertion */ +#define SDMMC_NISTR_CREM (1 << 7) /* Bit 7: Card Removal */ +#define SDMMC_NISTR_CINT (1 << 8) /* Bit 8: Card Interrupt */ +#define SDMMC_NISTR_BOOTAR (1 << 14) /* Bit 14: Boot Acknowledge Received (e.MMC) */ +#define SDMMC_NISTR_ERRINT (1 << 15) /* Bit 15: Error Interrupt */ + +/* Error Interrupt Status Register (0x32) */ + +#define SDMMC_EISTR_CMDTEO (1 << 0) /* Bit 0: Command TImeout Error */ +#define SDMMC_EISTR_CMDCRC (1 << 1) /* Bit 1: Command CRC Error */ +#define SDMMC_EISTR_CMDEND (1 << 2) /* Bit 2: Command End Bit Error */ +#define SDMMC_EISTR_CMDIDX (1 << 3) /* Bit 3: Command Index Error */ +#define SDMMC_EISTR_DATTEO (1 << 4) /* Bit 4: Data Timeout Error */ +#define SDMMC_EISTR_DATCRC (1 << 5) /* Bit 5: Data CRC Error */ +#define SDMMC_EISTR_DATEND (1 << 6) /* Bit 6: Data End Bit Error */ +#define SDMMC_EISTR_CURLIM (1 << 7) /* Bit 7: Current Limit Error */ +#define SDMMC_EISTR_ACMD (1 << 8) /* Bit 8: Auto CMD Error */ +#define SDMMC_EISTR_ADMA (1 << 9) /* Bit 9: ADMA Error */ +#define SDMMC_EISTR_BOOTAE (1 << 12) /* Bit 12: Boot Acknowledge Error (e.MMC) */ + +/* Normal Interrupt Status Enable Register (0x34) */ + +#define SDMMC_NISTER_CMDC (1 << 0) /* Bit 0: Command Complete Status Enable */ +#define SDMMC_NISTER_TRFC (1 << 1) /* Bit 1: Transfer Complete Status Enable */ +#define SDMMC_NISTER_BLKGE (1 << 2) /* Bit 2: Block Gap Event Status Enable */ +#define SDMMC_NISTER_DMAINT (1 << 3) /* Bit 3: DMA Interrupt Status Enable */ +#define SDMMC_NISTER_BWRRDY (1 << 4) /* Bit 4: Buffer Write Ready Status Enable */ +#define SDMMC_NISTER_BRDRDY (1 << 5) /* Bit 5: Buffer Read Ready Status Enable */ +#define SDMMC_NISTER_CINS (1 << 6) /* Bit 6: Card Insertion Status Enable */ +#define SDMMC_NISTER_CREM (1 << 7) /* Bit 7: Card Removal Status Enable */ +#define SDMMC_NISTER_CINT (1 << 8) /* Bit 8: Card Interrupt Status Enable */ +#define SDMMC_NISTER_BOOTAR (1 << 14) /* Bit 14: Boot Acknowledge Received Status Enable (e.MMC) */ + +/* Error Interrupt Status Enable Register (0x36) */ + +#define SDMMC_EISTER_CMDTEO (1 << 0) /* Bit 0: Command TImeout Error Status Enable */ +#define SDMMC_EISTER_CMDCRC (1 << 1) /* Bit 1: Command CRC Error Status Enable */ +#define SDMMC_EISTER_CMDEND (1 << 2) /* Bit 2: Command End Bit Error Status Enable */ +#define SDMMC_EISTER_CMDIDX (1 << 3) /* Bit 3: Command Index Error Status Enable */ +#define SDMMC_EISTER_DATTEO (1 << 4) /* Bit 4: Data Timeout Error Status Enable */ +#define SDMMC_EISTER_DATCRC (1 << 5) /* Bit 5: Data CRC Error Status Enable */ +#define SDMMC_EISTER_DATEND (1 << 6) /* Bit 6: Data End Bit Error Status Enable */ +#define SDMMC_EISTER_CURLIM (1 << 7) /* Bit 7: Current Limit Error Status Enable */ +#define SDMMC_EISTER_ACMD (1 << 8) /* Bit 8: Auto CMD Error Status Enable */ +#define SDMMC_EISTER_ADMA (1 << 9) /* Bit 9: ADMA Error Status Enable */ +#define SDMMC_EISTER_BOOTAE (1 << 12) /* Bit 12: Boot Acknowledge Error Status Enable (e.MMC) */ + +/* Normal Interrupt Signal Enable Register (0x38) */ + +#define SDMMC_NISIER_CMDC (1 << 0) /* Bit 0: Command Complete Signal Enable */ +#define SDMMC_NISIER_TRFC (1 << 1) /* Bit 1: Transfer Complete Signal Enable */ +#define SDMMC_NISIER_BLKGE (1 << 2) /* Bit 2: Block Gap Event Signal Enable */ +#define SDMMC_NISIER_DMAINT (1 << 3) /* Bit 3: DMA Interrupt Signal Enable */ +#define SDMMC_NISIER_BWRRDY (1 << 4) /* Bit 4: Buffer Write Ready Signal Enable */ +#define SDMMC_NISIER_BRDRDY (1 << 5) /* Bit 5: Buffer Read Ready Signal Enable */ +#define SDMMC_NISIER_CINS (1 << 6) /* Bit 6: Card Insertion Signal Enable */ +#define SDMMC_NISIER_CREM (1 << 7) /* Bit 7: Card Removal Signal Enable */ +#define SDMMC_NISIER_CINT (1 << 8) /* Bit 8: Card Interrupt Signal Enable */ +#define SDMMC_NISIER_BOOTAR (1 << 14) /* Bit 14: Boot Acknowledge Received Signal Enable (e.MMC) */ + +/* Error Interrupt Signal Enable Register (0x3a) */ + +#define SDMMC_EISIER_CMDTEO (1 << 0) /* Bit 0: Command TImeout Error Signal Enable */ +#define SDMMC_EISIER_CMDCRC (1 << 1) /* Bit 1: Command CRC Error Signal Enable */ +#define SDMMC_EISIER_CMDEND (1 << 2) /* Bit 2: Command End Bit Error Signal Enable */ +#define SDMMC_EISIER_CMDIDX (1 << 3) /* Bit 3: Command Index Error Signal Enable */ +#define SDMMC_EISIER_DATTEO (1 << 4) /* Bit 4: Data Timeout Error Signal Enable */ +#define SDMMC_EISIER_DATCRC (1 << 5) /* Bit 5: Data CRC Error Signal Enable */ +#define SDMMC_EISIER_DATEND (1 << 6) /* Bit 6: Data End Bit Error Signal Enable */ +#define SDMMC_EISIER_CURLIM (1 << 7) /* Bit 7: Current Limit Error Signal Enable */ +#define SDMMC_EISIER_ACMD (1 << 8) /* Bit 8: Auto CMD Error Signal Enable */ +#define SDMMC_EISIER_ADMA (1 << 9) /* Bit 9: ADMA Error Signal Enable */ +#define SDMMC_EISIER_BOOTAE (1 << 12) /* Bit 12: Boot Acknowledge Error Signal Enable (e.MMC) */ + +/* Auto CMD Error Status Register (0x3c) */ +#define SDMMC_ACESR_ACMD12NE (1 << 0) /* Bit 0: Auto CMD12 Not Executed */ +#define SDMMC_ACESR_ACMDTER (1 << 1) /* Bit 1: Auto Command Timeout Error */ +#define SDMMC_ACESR_ACMDCRC (1 << 2) /* Bit 2: Auto CMD CRC Error */ +#define SDMMC_ACESR_ACMDEND (1 << 3) /* Bit 3: Auto CMD End Bit Error */ +#define SDMMC_ACESR_ACMDIDX (1 << 4) /* Bit 4: Auto CMD Index Error */ +#define SDMMC_ACESR_CMDNI (1 << 7) /* Bit 7: Command Not Issued by Auto CMD12 Error */ + +/* Host Control 2 Register (0x3e) */ +#define SDMMC_HC2R_UHSMS_SHIFT (0) /* Bits 0-2: UHS Mode Select */ +#define SDMMC_HC2R_UHSMS_MASK (0x07 << SDMMC_HC2R_UHSMS_SHIFT) +# define SDMMC_HC2R_UHSMS_SDR12 (0 << SDMMC_HC2R_UHSMS_SHIFT) /* UHS SDR12 Mode */ +# define SDMMC_HC2R_UHSMS_SDR25 (1 << SDMMC_HC2R_UHSMS_SHIFT) /* UHS SDR25 Mode */ +# define SDMMC_HC2R_UHSMS_SDR50 (2 << SDMMC_HC2R_UHSMS_SHIFT) /* UHS SDR50 Mode */ +# define SDMMC_HC2R_UHSMS_SDR104 (3 << SDMMC_HC2R_UHSMS_SHIFT) /* UHS SDR104 Mode */ +# define SDMMC_HC2R_UHSMS_DDR50 (4 << SDMMC_HC2R_UHSMS_SHIFT) /* UHS DDR50 Mode */ +#define SDMMC_HC2R_HS200EN_SHIFT (0) /* Bits 0-3: HS200 Mode Enable (e.MMC) */ +#define SDMMC_HC2R_HS200EN_MASK (0x0f << SDMMC_HC2R_HS200EN_SHIFT) +# define SDMMC_HC2R_HS200EN_ENABLE (0x0b << SDMMC_HC2R_HS200EN_SHIFT) /* HS200 mode is enabled */ +#define SDMMC_HC2R_VS18EN (1 << 3) /* Bit 3: 1.8V Signaling Enable */ +#define SDMMC_HC2R_DRVSEL_SHIFT (4) /* Bits 4-5: Driver Strength Select */ +#define SDMMC_HC2R_DRVSEL_MASK (3 << SDMMC_HC2R_DRVSEL_SHIFT) +# define SDMMC_HC2R_DRVSEL_TYPEB (0 << SDMMC_HC2R_DRVSEL_SHIFT) /* Driver Type B is selected (Default) */ +# define SDMMC_HC2R_DRVSEL_TYPEA (1 << SDMMC_HC2R_DRVSEL_SHIFT) /* Driver Type A is selected */ +# define SDMMC_HC2R_DRVSEL_TYPEC (2 << SDMMC_HC2R_DRVSEL_SHIFT) /* Driver Type C is selected */ +# define SDMMC_HC2R_DRVSEL_TYPED (3 << SDMMC_HC2R_DRVSEL_SHIFT) /* Driver Type D is selected */ +#define SDMMC_HC2R_EXTUN (1 << 6) /* Bit 6: Execute Tuning */ +#define SDMMC_HC2R_SCLKSEL (1 << 7) /* Bit 7: Sampling Clock Select */ +#define SDMMC_HC2R_ASINTEN (1 << 14) /* Bit 14: Asynchronous Interrupt Enable */ +#define SDMMC_HC2R_PVALEN (1 << 15) /* Bit 15: Preset Value Enable */ + +/* Capabilities 0 Register (0x40) */ + +#define SDMMC_CA0R_TEOCLKF_SHIFT (0) /* Bits 0-5: Timeout Clock Frequency */ +#define SDMMC_CA0R_TEOCLKF_MASK (0x3f << SDMMC_CA0R_TEOCLKF_SHIFT) +#define SDMMC_CA0R_TEOCLKF(n) ((n &0x3f) << SDMMC_CA0R_TEOCLKF_SHIFT) +#define SDMMC_CA0R_TEOCLKU (1 << 7) /* Bit 7: Timeout Clock Unit */ +#define SDMMC_CA0R_TEOCLKU_KHz (0 << 7) /* KHz */ +#define SDMMC_CA0R_TEOCLKU_MHz (1 << 7) /* MHz */ +#define SDMMC_CA0R_BASECLKF_SHIFT (8) /* Bits 8-15: Base Clock Frequency */ +#define SDMMC_CA0R_BASECLKF_MASK (0xf << SDMMC_BASECLKF_SHIFT) +#define SDMMC_CA0R_BASECLKF(n) ((n & 0xf) << SDMMC_BASECLKF_SHIFT) +#define SDMMC_CA0R_MAXBLKL_SHIFT (16) /* Bits 16-17: Max Block Length */ +#define SDMMC_CA0R_MAXBLKL_MASK (3 << SDMMC_CA0R_MAXBLKL_SHIFT) +#define SDMMC_CA0R_MAXBLKL_512 (0 << SDMMC_CA0R_MAXBLKL_SHIFT) /* 512 bytes */ +#define SDMMC_CA0R_MAXBLKL_1024 (1 << SDMMC_CA0R_MAXBLKL_SHIFT) /* 1024 bytes */ +#define SDMMC_CA0R_MAXBLKL_2048 (2 << SDMMC_CA0R_MAXBLKL_SHIFT) /* 2048 bytes */ +#define SDMMC_CA0R_MAXBLKL_NONE (3 << SDMMC_CA0R_MAXBLKL_SHIFT) /* Reserved */ +#define SDMMC_CA0R_ED8SUP (1 << 18) /* Bit 18: 8-Bit Support for Embedded Device */ +#define SDMMC_CA0R_ADMA2SUP (1 << 19) /* Bit 19: ADMA2 Support */ +#define SDMMC_CA0R_HSSUP (1 << 21) /* Bit 21: High Speed Support */ +#define SDMMC_CA0R_SDMASUP (1 << 22) /* Bit 22: SDMA Support */ +#define SDMMC_CA0R_SRSUP (1 << 23) /* Bit 23: Suspend/Resume Support */ +#define SDMMC_CA0R_V33VSUP (1 << 24) /* Bit 24: Voltable Support 3.3V */ +#define SDMMC_CA0R_V30VSUP (1 << 25) /* Bit 25: Voltable Support 3.0V */ +#define SDMMC_CA0R_V18VSUP (1 << 26) /* Bit 26: Voltable Support 1.8V */ +#define SDMMC_CA0R_SB64SUP (1 << 28) /* Bit 28: 64-Bit System Bus Support */ +#define SDMMC_CA0R_ASINTSUP (1 << 29) /* Bit 29: Asynchronous Interrupt Support */ +#define SDMMC_CA0R_SLTYPE_SHIFT (30) /* Bits 30-31: Slot Type */ +#define SDMMC_CA0R_SLTYPE_MASK (3 << SDMMC_CA0R_SLTYPE_SHIFT) +#define SDMMC_CA0R_SLTYPE_REMOVABLE (0 << SDMMC_CA0R_SLTYPE_SHIFT) /* Removable Card Slot */ +#define SDMMC_CA0R_SLTYPE_EMBEDDED (1 << SDMMC_CA0R_SLTYPE_SHIFT) /* Embedded Slot for One Device */ + +/* Capabilities 1 Register (0x44) */ + +#define SDMMC_CA1R_SDR50SUP (1 << 0) /* Bit 0: SDR50 Support */ +#define SDMMC_CA1R_SDR104SUP (1 << 1) /* Bit 1: SDR104 Support */ +#define SDMMC_CA1R_DDR50SUP (1 << 2) /* Bit 2: DDR50 Support */ +#define SDMMC_CA1R_DRVASUP (1 << 4) /* Bit 4: Driver Type A Support */ +#define SDMMC_CA1R_DRVCSUP (1 << 5) /* Bit 5: Driver Type C Support */ +#define SDMMC_CA1R_DRVDSUP (1 << 6) /* Bit 6: Driver Type D Support */ +#define SDMMC_CA1R_TCNTRT_SHIFT (8) /* Bits 8-11: Timer Count For Retuning */ +#define SDMMC_CA1R_TCNTRT_MASK (0x0f << SDMMC_CA1R_TCNTRT_SHIFT) +#define SDMMC_CA1R_TCNTRT(n) ((n & 0x0f) << SDMMC_CA1R_TCNTRT_SHIFT) +#define SDMMC_CA1R_TSDR50 (1 << 13) /* Bit 13: Use Tuning for SDR50 */ +#define SDMMC_CA1R_RTMOD_SHIFT (14) /* Bits 14-15: Retuning Modes */ +#define SDMMC_CA1R_RTMOD_MASK (3 << SDMMC_CA1R_RTMOD_SHIFT) +#define SDMMC_CA1R_RTMOD_MODE1 (0 << SDMMC_CA1R_RTMOD_SHIFT) /* MODE1: Timer */ +#define SDMMC_CA1R_RTMOD_MODE2 (1 << SDMMC_CA1R_RTMOD_SHIFT) /* MODE2: Timer and Retuning Request */ +#define SDMMC_CA1R_RTMOD_MODE3 (2 << SDMMC_CA1R_RTMOD_SHIFT) /* MODE3: Auto Retuning Timer and Retuning Request */ +#define SDMMC_CA1R_CLKMULT_SHIFT (16) /* Bits 16-23: Clock Multiplier */ +#define SDMMC_CA1R_CLKMULT_MASK (0xf << SDMMC_CA1R_CLKMULT_SHIFT) +#define SDMMC_CA1R_CLKMULT(n) ((n & 0xf) << SDMMC_CA1R_CLKMULT_SHIFT) + +/* Preset Value Register (0x60 + x * 0x02 [x=0..7]) */ + +#define SDMMC_PVRx_SDCLKFSEL_SHIFT (0) /* Bits 0-9: SDCLK Frequency Select */ +#define SDMMC_PVRx_SDCLKFSEL_MASK (0x3f << SDMMC_PVRx_SDCLKFSEL_SHIFT) +#define SDMMC_PVRx_SDCLKFSEL(n) ((n & 0x3f) << SDMMC_PVRx_SDCLKFSEL_SHIFT) +#define SDMMC_PVRx_CLKGSEL (1 << 10) /* Bit 10: Clock Generator Select */ +#define SDMMC_PVRx_DRVSEL_SHIFT (14) /* Bits 14-15: Driver Strength Select */ +#define SDMMC_PVRx_DRVSEL_MASK (3 << SDMMC_PVRx_DRVSEL_SHIFT) +#define SDMMC_PVRx_DRVSEL(n) ((n & 3) << SDMMC_PVRx_DRVSEL_SHIFT) + +/* Slot Interrupt Status Register (0xfc) */ + +#define SDMMC_SISR_INTSSL0 (1 << 0) /* Bit 0: Interrupt Signal for Slot 0 */ +#define SDMMC_SISR_INTSSL1 (1 << 1) /* Bit 1: Interrupt Signal for Slot 1 */ + +/* Host Controller Version Register (0xfe) */ + +#define SDMMC_HCVR_SVER_SHIFT (0) /* Bits 0-7: Specification Version Number */ +#define SDMMC_HCVR_SVER_MASK (0xf << SDMMC_HCVR_SVER_SHIFT) +#define SDMMC_HCVR_SVER(reg) ((reg & SDMMC_HCVR_SVER_MASK) >> SDMMC_HCVR_SVER_SHIFT) +#define SDMMC_HCVR_VVER_SHIFT (8) /* Bits 8-15: Vendor Version Number */ +#define SDMMC_HCVR_VVER_MASK (0xf << SDMMC_HCVR_VVER_SHIFT) +#define SDMMC_HCVR_VVER(reg) ((reg & SDMMC_HCVR_VVER_MASK) >> SDMMC_HCVR_VVER_SHIFT) + +/* Additional Present State Register (0x200) */ + +#define SDMMC_APSR_HDATLL_SHIFT (0) /* Bits 0-3: DAT[7:4] High Line Level */ +#define SDMMC_APSR_HDATLL_MASK (0xf << SDMMC_APSR_HDATLL_SHIFT) +#define SDMMC_APSR_HDATLL(reg) ((reg & SDMMC_APSR_HDATLL_MASK) >> SDMMC_APSR_HDATLL_SHIFT) + +/* e.MMC Control 1 Register (0x204) */ + +#define SDMMC_MC1R_CMDTYPE_SHIFT (0) /* Bits 0-1: e.MMC Command Type */ +#define SDMMC_MC1R_CMDTYPE_MASK (3 << SDMMC_MC1R_CMDTYPE_SHIFT) +#define SDMMC_MC1R_CMDTYPE_NORMAL (0 << SDMMC_MC1R_CMDTYPE_SHIFT) +#define SDMMC_MC1R_CMDTYPE_WAITIRQ (1 << SDMMC_MC1R_CMDTYPE_SHIFT) +#define SDMMC_MC1R_CMDTYPE_STREAM (2 << SDMMC_MC1R_CMDTYPE_SHIFT) +#define SDMMC_MC1R_CMDTYPE_BOOT (3 << SDMMC_MC1R_CMDTYPE_SHIFT) +#define SDMMC_MC1R_DDR (1 << 3) /* Bit 3: e.MMC HSDDR Mode */ +#define SDMMC_MC1R_OPD (1 << 4) /* Bit 4: e.MMC Open Drain Mode */ +#define SDMMC_MC1R_BOOTA (1 << 5) /* Bit 5: e.MMC Boot Acknowledge Mode */ +#define SDMMC_MC1R_RSTN (1 << 6) /* Bit 6: e.MMC Reset Signal */ +#define SDMMC_MC1R_FCD (1 << 7) /* Bit 7: e.MMC Force Card Detect */ + +/* e.MMC Control 2 Register (0x205) */ + +#define SDMMC_MC2R_SRESP (1 << 0) /* Bit 0: e.MMC Abort Wait IRQ */ +#define SDMMC_MC2R_ABOOT (1 << 1) /* Bit 1: e.MMC Abort Boot */ + +/* Capabilities Control Register (0x230) */ + +#define SDMMC_CACR_CAPWREN (1 << 0) /* Bit 0: Capabilities Write Enable */ +#define SDMMC_CACR_KEY_SHIFT (8) /* Bits 8-15: Key */ +#define SDMMC_CACR_KEY_MASK (0xff << SDMMC_CACR_KEY_SHIFT) +#define SDMMC_CACR_KEY_ENABLE (0x46 << SDMMC_CACR_KEY_SHIFT) +#define SDMMC_CACR_KEY_DISABLE (0x00 << SDMMC_CACR_KEY_SHIFT) + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +#endif /* __ARCH_ARM_SRC_SAMA5_HARDWARE_SAMA5D2_SDMMC_H */ diff --git a/arch/arm/src/sama5/sam_hsmci_clkdiv.c b/arch/arm/src/sama5/sam_hsmci_clkdiv.c index b6fa5e84f02..a6877f1f80d 100644 --- a/arch/arm/src/sama5/sam_hsmci_clkdiv.c +++ b/arch/arm/src/sama5/sam_hsmci_clkdiv.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/sama5/sam_pmc.c + * arch/arm/src/sama5/sam_hsmci_clkdiv.c * * Copyright (C) 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt diff --git a/arch/arm/src/sama5/sam_pioirq.c b/arch/arm/src/sama5/sam_pioirq.c index 59689b37699..d2d48603fe7 100644 --- a/arch/arm/src/sama5/sam_pioirq.c +++ b/arch/arm/src/sama5/sam_pioirq.c @@ -386,8 +386,10 @@ void sam_pioirq(pio_pinset_t pinset) #if defined(SAM_PIO_ISLR_OFFSET) uint32_t regval; #endif +#if defined(SAM_PIO_ISLR_OFFSET) || defined(_PIO_INT_AIM) uint32_t base = sam_piobase(pinset); int pin = sam_piopin(pinset); +#endif #if defined(SAM_PIO_ISLR_OFFSET) /* Enable writing to PIO registers. The following registers are protected: diff --git a/arch/arm/src/sama5/sam_sdmmc.c b/arch/arm/src/sama5/sam_sdmmc.c new file mode 100644 index 00000000000..4dc6f7f9a73 --- /dev/null +++ b/arch/arm/src/sama5/sam_sdmmc.c @@ -0,0 +1,3859 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_sdmmc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "arm_internal.h" +#include "arm_arch.h" + +#include "sam_config.h" +#include "sam_pio.h" +#include "sam_periphclks.h" +#include "sam_sdmmc.h" +#include "hardware/sam_sdmmc.h" +#include "hardware/sam_pinmap.h" +#include "hardware/sam_pio.h" + +#ifdef CONFIG_SAMA5_SDMMC + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#if ((defined(CONFIG_SAMA5_SDMMC0) && !defined(CONFIG_SAMA5_SDMMC1)) || \ + (defined(CONFIG_SAMA5_SDMMC1) && !defined(CONFIG_SAMA5_SDMMC0))) +# define SAM_MAX_SDMMC_DEV_SLOTS 1 +#elif (defined(CONFIG_SAMA5_SDMMC0) && defined(CONFIG_SAMA5_SDMMC1)) +# define SAM_MAX_SDMMC_DEV_SLOTS 2 +#else +#error Unrecognised number of SDMMC slots +#endif + +#if !defined(CONFIG_SAMA5_SDMMC_DMA) +# warning "Large Non-DMA transfer may result in RX overrun failures" +#elif !defined(CONFIG_SDIO_DMA) +# warning CONFIG_SDIO_DMA should be defined with CONFIG_SAMA5_SDMMC_DMA +#endif + +#if !defined(CONFIG_SCHED_WORKQUEUE) || !defined(CONFIG_SCHED_HPWORK) +# error "Callback support requires CONFIG_SCHED_WORKQUEUE and CONFIG_SCHED_HPWORK" +#endif + +#if !defined(CONFIG_SDIO_BLOCKSETUP) +# error "CONFIG_SDIO_BLOCKSETUP is mandatory for this driver" +#endif + +#ifndef CONFIG_DEBUG_MEMCARD_INFO +# undef CONFIG_SDIO_XFRDEBUG +#endif + +/* Timing in ms for commands wait response */ + +#define SDMMC_CMDTIMEOUT MSEC2TICK(100) +#define SDMMC_LONGTIMEOUT MSEC2TICK(500) + +/* Big DTOCV setting. Range is 0 = SDCLK * 2^13 through 15 = SDCLK * 2^29 */ + +#define SDMMC_DTOCV_MAXTIMEOUT (15) + +/* Data transfer / Event waiting interrupt mask bits */ + +#define SDMMC_RESPERR_INTS (SDMMC_INT_CCE | SDMMC_INT_CTOE | \ + SDMMC_INT_CEBE | SDMMC_INT_CIE) +#define SDMMC_RESPDONE_INTS (SDMMC_RESPERR_INTS | SDMMC_INT_CC) + +#define SDMMC_XFRERR_INTS (SDMMC_INT_DCE | SDMMC_INT_DTOE | \ + SDMMC_INT_DEBE) +#define SDMMC_RCVDONE_INTS (SDMMC_XFRERR_INTS | SDMMC_INT_BRR | \ + SDMMC_INT_TC) +#define SDMMC_SNDDONE_INTS (SDMMC_XFRERR_INTS | SDMMC_INT_BWR | \ + SDMMC_INT_TC) +#define SDMMC_XFRDONE_INTS (SDMMC_XFRERR_INTS | SDMMC_INT_BRR | \ + SDMMC_INT_BWR | SDMMC_INT_TC) + +/* CD Detect Types */ + +#define SDMMC_DMAERR_INTS (SDMMC_XFRERR_INTS) +#define SDMMC_DMADONE_INTS (SDMMC_DMAERR_INTS | SDMMC_INT_TC) + +#define SDMMC_WAITALL_INTS (SDMMC_RESPDONE_INTS | \ + SDMMC_XFRDONE_INTS | \ + SDMMC_DMADONE_INTS) + +#define SDMMC_INT_CMD_MASK (SDMMC_INT_CC | SDMMC_INT_CTOE | \ + SDMMC_INT_CCE | SDMMC_INT_CEBE | \ + SDMMC_INT_CIE) + +#define SDMMC_INT_DATA_MASK (SDMMC_INT_TC | SDMMC_INT_DINT | \ + SDMMC_INT_BRR | SDMMC_INT_BWR | \ + SDMMC_INT_CTOE | SDMMC_INT_CCE | \ + SDMMC_INT_DEBE | SDMMC_INT_ADMAE) + +/* Register logging support */ + +#ifdef CONFIG_SDIO_XFRDEBUG +# define DBG_BASE_ADDR SAM_SDMMC1_VBASE +# define SAMPLENDX_BEFORE_SETUP 0 +# define SAMPLENDX_AFTER_SETUP 1 +# define SAMPLENDX_END_TRANSFER 2 +# define DEBUG_NSAMPLES 3 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure defines the state of the SAMA5 SDIO interface */ + +struct sam_dev_s +{ + struct sdio_dev_s dev; /* Standard, base SDIO interface */ + + /* SAMA5-specific extensions */ + + /* Event support */ + + uint32_t base; /* SDMMC register base address */ + sem_t waitsem; /* Implements event waiting */ + sdio_eventset_t waitevents; /* Set of events to be waited for */ + uint32_t waitints; /* Interrupt enables for event waiting */ + volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */ + WDOG_ID waitwdog; /* Watchdog that handles event timeouts */ + + /* Callback support */ + + 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 */ + size_t remaining; /* Number of bytes remaining in the transfer */ + uint32_t xfrints; /* Interrupt enables for data transfer */ + +#ifdef CONFIG_SAMA5_SDMMC_DMA + /* DMA data transfer support */ + + volatile uint8_t xfrflags; /* Used to synchronize SDIO and DMA completion */ + uint32_t *bufferend; /* Far end of R/W buffer for cache invalidation */ +#endif + + /* Card interrupt support for SDIO */ + + uint32_t cintints; /* Interrupt enables for card ints */ + int (*do_sdio_card)(void *); /* SDIO card ISR */ + void *do_sdio_arg; /* arg for SDIO card ISR */ + + uint32_t addr; /* Base address of this instances */ + uint32_t sw_cd_gpio; /* If a non SDMMCx CD pin is used, this is its GPIO */ + uint32_t cd_invert; /* If true invert the CD pin */ + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + bool wrlast; /* Last was a write */ + uint32_t addrlast; /* Last address */ + uint32_t vallast; /* Last value */ + int ntimes; /* Number of times */ +#endif +}; + +/* Register logging support */ + +#ifdef CONFIG_SDIO_XFRDEBUG +struct sam_sdmmcregs_s +{ + /* All read-able SDMMC registers */ + + uint32_t dsaddr; /* DMA System Address Register */ + uint32_t blkattr; /* Block Attributes Register */ + uint32_t cmdarg; /* Command Argument Register */ + uint32_t xferty; /* Transfer Type Register */ + uint32_t cmdrsp0; /* Command Response 0 */ + uint32_t cmdrsp1; /* Command Response 1 */ + uint32_t cmdrsp2; /* Command Response 2 */ + uint32_t cmdrsp3; /* Command Response 3 */ + uint32_t dbap; /* Data buffer access port */ + uint32_t prsstat; /* Present State Register */ + uint32_t proctl; /* Protocol Control Register */ + uint32_t sysctl; /* System Control Register */ + uint32_t irqstat; /* Interrupt Status Register */ + uint32_t irqstaten; /* Interrupt Status Enable Register */ + uint32_t irqsigen; /* Interrupt Signal Enable Register */ + uint32_t ac12err; /* Auto CMD12 Error Status Register */ + uint32_t htcapblt0; /* Host Controller Capabilities 2 Register */ + uint32_t htcapblt1; /* Host Controller Capabilities 1 Register */ + uint32_t mixctrl; /* Mixer Control */ + uint32_t fevent; /* Force Event */ + uint32_t admaes; /* ADMA Error Status Register */ + uint32_t adsaddr; /* ADMA System Address Register */ + uint32_t dllctrl; /* Delay line control */ + uint32_t dllstat; /* Delay line status */ + uint32_t clktune; /* Clock tune and control */ + uint32_t tuningctrl; /* Tuning Control */ +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level helpers ********************************************************/ + +static void sam_takesem(struct sam_dev_s *priv); +#define sam_givesem(priv) (nxsem_post(&priv->waitsem)) +static void sam_configwaitints(struct sam_dev_s *priv, uint32_t waitints, + sdio_eventset_t waitevents, sdio_eventset_t wkupevents); +static void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrints); + +/* DMA Helpers **************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_sampleinit(void); +static void sam_sdmmcsample(struct sam_dev_s *priv, + struct sam_sdmmcregs_s *regs); +static void sam_sample(struct sam_dev_s *priv, int index); +static void sam_dumpsample(struct sam_dev_s *priv, + struct sam_sdmmcregs_s *regs, const char *msg); +static void sam_dumpsamples(struct sam_dev_s *priv); +static void sam_showregs(struct sam_dev_s *priv, const char *msg); + +#else +# define sam_sampleinit() +# define sam_sample(priv, index) +# define sam_dumpsamples(priv) +# define sam_showregs(priv, msg) +#endif + +/* Data Transfer Helpers ****************************************************/ + +static void sam_dataconfig(struct sam_dev_s *priv, bool bwrite, + unsigned int datalen, unsigned int timeout); + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static void sam_transmit(struct sam_dev_s *priv); +static void sam_receive(struct sam_dev_s *priv); +#endif + +static void sam_eventtimeout(int argc, uint32_t arg); +static void sam_endwait(struct sam_dev_s *priv, + sdio_eventset_t wkupevent); +static void sam_endtransfer(struct sam_dev_s *priv, + sdio_eventset_t wkupevent); + +/* Interrupt Handling *******************************************************/ + +static int sam_interrupt(int irq, void *context, FAR void *arg); + +/* SDIO interface methods ***************************************************/ + +/* Mutual exclusion */ + +#ifdef CONFIG_SDIO_MUXBUS +static int sam_lock(FAR struct sdio_dev_s *dev, bool lock); +#endif + +/* Initialization/setup */ + +static void sam_reset(FAR struct sdio_dev_s *dev); +static sdio_capset_t sam_capabilities(FAR struct sdio_dev_s *dev); +static sdio_statset_t sam_status(FAR struct sdio_dev_s *dev); +static void sam_widebus(FAR struct sdio_dev_s *dev, bool enable); + +#ifdef CONFIG_SAM_SDMMC_ABSFREQ +static void sam_frequency(FAR struct sdio_dev_s *dev, uint32_t frequency); +#endif + +static void sam_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate); +static void sam_power(FAR struct sam_dev_s *priv); +static int sam_attach(FAR struct sdio_dev_s *dev); + +/* Command/Status/Data Transfer */ + +static int sam_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg); + +#ifdef CONFIG_SDIO_BLOCKSETUP +static void sam_blocksetup(FAR struct sdio_dev_s *dev, + unsigned int blocklen, unsigned int nblocks); +#endif + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static int sam_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes); +static int sam_sendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, uint32_t nbytes); +#endif + +static int sam_cancel(FAR struct sdio_dev_s *dev); +static int sam_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd); +static int sam_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int sam_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]); +static int sam_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); + +/* EVENT handler */ + +static void sam_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev, + uint32_t timeout); +static void sam_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static int sam_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg); + +/* DMA */ + +#ifdef CONFIG_SAMA5_SDMMC_DMA +static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, + FAR uint8_t *buffer, size_t buflen); +static int sam_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif + +/* Initialization/uninitialization/reset ************************************/ + +static void sam_callback(void *arg); +void sam_set_uhs_timing(FAR struct sam_dev_s *priv, + enum bus_mode selected_mode); +static int sam_set_clock(FAR struct sam_dev_s *priv, uint32_t clock); +static void sam_power(FAR struct sam_dev_s *priv); +static int sam_set_interrupts(FAR struct sam_dev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct sam_dev_s g_sdmmcdev[SAM_MAX_SDMMC_DEV_SLOTS] = +{ +#ifdef CONFIG_SAMA5_SDMMC0 + { + .addr = SAM_SDMMC0_VBASE, +#if defined(PIN_SDMMC0_CD_GPIO) + .sw_cd_gpio = PIN_SDMMC0_CD_GPIO, +#endif +#if defined(CONFIG_SAMA5_SDMMC0_INVERT_CD) + .cd_invert = true, +#endif + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = sam_lock, +#endif + .reset = sam_reset, + .capabilities = sam_capabilities, + .status = sam_status, + .widebus = sam_widebus, + .clock = sam_clock, + .attach = sam_attach, + .sendcmd = sam_sendcmd, +#ifdef CONFIG_SDIO_BLOCKSETUP + .blocksetup = sam_blocksetup, +#endif + +#ifndef CONFIG_SAMA5_SDMMC_DMA + .recvsetup = sam_recvsetup, + .sendsetup = sam_sendsetup, +#else + .recvsetup = sam_dmarecvsetup, + .sendsetup = sam_dmasendsetup, +#endif + .cancel = sam_cancel, + .waitresponse = sam_waitresponse, + .recv_r1 = sam_recvshortcrc, + .recv_r2 = sam_recvlong, + .recv_r3 = sam_recvshort, + .recv_r4 = sam_recvshort, + .recv_r5 = sam_recvshortcrc, + .recv_r6 = sam_recvshortcrc, + .recv_r7 = sam_recvshort, + .waitenable = sam_waitenable, + .eventwait = sam_eventwait, + .callbackenable = sam_callbackenable, + .registercallback = sam_registercallback, +#ifdef CONFIG_SDIO_DMA +#ifdef CONFIG_SAMA5_SDMMC_DMA + .dmarecvsetup = sam_dmarecvsetup, + .dmasendsetup = sam_dmasendsetup, +#else + .dmarecvsetup = sam_recvsetup, + .dmasendsetup = sam_sendsetup, +#endif +#endif + } + }, +#endif + +#ifdef CONFIG_SAMA5_SDMMC1 + { + .addr = SAM_SDMMC1_VBASE, +#if defined(PIN_SDMMC1_CD_GPIO) + .sw_cd_gpio = PIN_SDMMC1_CD_GPIO, +#endif +#if defined(CONFIG_SAMA5_SDMMC1_INVERT_CD) + .cd_invert = true, +#endif + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = sam_lock, +#endif + .reset = sam_reset, + .capabilities = sam_capabilities, + .status = sam_status, + .widebus = sam_widebus, + .clock = sam_clock, + .attach = sam_attach, + .sendcmd = sam_sendcmd, +#ifdef CONFIG_SDIO_BLOCKSETUP + .blocksetup = sam_blocksetup, +#endif + +#ifndef CONFIG_SAMA5_SDMMC_DMA + .recvsetup = sam_recvsetup, + .sendsetup = sam_sendsetup, +#else + .recvsetup = sam_dmarecvsetup, + .sendsetup = sam_dmasendsetup, +#endif + .cancel = sam_cancel, + .waitresponse = sam_waitresponse, + .recv_r1 = sam_recvshortcrc, + .recv_r2 = sam_recvlong, + .recv_r3 = sam_recvshort, + .recv_r4 = sam_recvshort, + .recv_r5 = sam_recvshortcrc, + .recv_r6 = sam_recvshortcrc, + .recv_r7 = sam_recvshort, + .waitenable = sam_waitenable, + .eventwait = sam_eventwait, + .callbackenable = sam_callbackenable, + .registercallback = sam_registercallback, +#ifdef CONFIG_SDIO_DMA +#ifdef CONFIG_SAMA5_SDMMC_DMA + .dmarecvsetup = sam_dmarecvsetup, + .dmasendsetup = sam_dmasendsetup, +#else + .dmarecvsetup = sam_recvsetup, + .dmasendsetup = sam_sendsetup, +#endif + } + } +#endif +#endif +}; + +#ifdef CONFIG_SDIO_XFRDEBUG +/* Register logging support */ + +static struct sam_sdmmcregs_s g_sampleregs[DEBUG_NSAMPLES]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Low-level Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_checkreg + * + * Description: + * Check if the current register access is a duplicate of the preceding. + * + * Input Parameters: + * value - The value to be written + * address - The address of the register to write to + * + * Returned Value: + * true: This is the first register access of this type. + * flase: This is the same as the preceding register access. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG +static bool sam_checkreg(struct sam_dev_s *priv, bool wr, uint32_t value, + uint32_t address) +{ + if (wr == priv->wrlast && /* Same kind of access? */ + value == priv->vallast && /* Same value? */ + address == priv->addrlast) /* Same address? */ + { + /* Yes, then just keep a count of the number of times we did this. */ + + priv->ntimes++; + return false; + } + else + { + /* Did we do the previous operation more than once? */ + + if (priv->ntimes > 0) + { + /* Yes... show how many times we did it */ + + mcinfo("...[Repeats %d times]...\n", priv->ntimes); + } + + /* Save information about the new access */ + + priv->wrlast = wr; + priv->vallast = value; + priv->addrlast = address; + priv->ntimes = 0; + } + + /* Return true if this is the first time that we have done this operation */ + + return true; +} + +#endif + +/**************************************************************************** + * Name: sam_getreg8 + * + * Description: + * Read an 8-bit SDMMC register + * + ****************************************************************************/ + +static inline uint32_t sam_getreg8(struct sam_dev_s *priv, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + uint32_t value = getreg8(address); + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, false, value, address)) + { + mcinfo("%08x->%08x\n", address, value); + } +#endif + + return value; +} + +/**************************************************************************** + * Name: sam_getreg16 + * + * Description: + * Read a 16-bit SDMMC register + * + ****************************************************************************/ + +static inline uint32_t sam_getreg16(struct sam_dev_s *priv, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + uint32_t value = getreg16(address); + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, false, value, address)) + { + mcinfo("%08x->%08x\n", address, value); + } +#endif + + return value; +} + +/**************************************************************************** + * Name: sam_getreg32 + * + * Description: + * Read a 32-bit SDMMC register + * + ****************************************************************************/ + +static inline uint32_t sam_getreg32(struct sam_dev_s *priv, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + uint32_t value = getreg32(address); + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, false, value, address)) + { + mcinfo("%08x->%08x\n", address, value); + } +#endif + + return value; +} + +/**************************************************************************** + * Name: sam_getreg + * + * Description: + * Read a 32-bit SDMMC register + * + ****************************************************************************/ + +static inline uint32_t sam_getreg(struct sam_dev_s *priv, + unsigned int offset) +{ + return sam_getreg32(priv, offset); +} + +/**************************************************************************** + * Name: sam_putreg8 + * + * Description: + * Write an 8-bit value to an SDMMC register + * + ****************************************************************************/ + +static inline void sam_putreg8(struct sam_dev_s *priv, uint32_t value, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, true, value, address)) + { + mcinfo("%08x<-%08x\n", address, value); + } +#endif + + putreg8(value, address); +} + +/**************************************************************************** + * Name: sam_putreg16 + * + * Description: + * Write a 16-bit value to an SDMMC register + * + ****************************************************************************/ + +static inline void sam_putreg16(struct sam_dev_s *priv, uint32_t value, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, true, value, address)) + { + mcinfo("%08x<-%08x\n", address, value); + } +#endif + + putreg16(value, address); +} + +/**************************************************************************** + * Name: sam_putreg32 + * + * Description: + * Write a value to an SDMMC register + * + ****************************************************************************/ + +static inline void sam_putreg32(struct sam_dev_s *priv, uint32_t value, + unsigned int offset) +{ + uint32_t address = priv->base + offset; + +#ifdef CONFIG_SAMA5_SDMMC_REGDEBUG + if (sam_checkreg(priv, true, value, address)) + { + mcinfo("%08x<-%08x\n", address, value); + } +#endif + + putreg32(value, address); +} + +/**************************************************************************** + * Name: sam_putreg + * + * Description: + * Write a 32-bit value to an SDMMC register + * + ****************************************************************************/ + +static inline void sam_putreg(struct sam_dev_s *priv, uint32_t value, + unsigned int offset) +{ + return sam_putreg32(priv, value, offset); +} + +/**************************************************************************** + * Name: sam_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 sam_takesem(struct sam_dev_s *priv) +{ + nxsem_wait_uninterruptible(&priv->waitsem); +} + +/**************************************************************************** + * Name: sam_configwaitints + * + * Description: + * Enable/disable SDIO interrupts needed to support the wait function + * + * Input Parameters: + * priv - A reference to the SDIO device state structure + * waitints - The set of bits in the SDIO MASK register to set + * waitevents - Waited for events + * wkupevent - Wake-up events + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_configwaitints(struct sam_dev_s *priv, uint32_t waitints, + sdio_eventset_t waitevents, + sdio_eventset_t wkupevent) +{ + irqstate_t flags; + + /* Save all of the data and set the new interrupt mask in one, atomic + * operation. + */ + + flags = enter_critical_section(); + priv->waitevents = waitevents; + priv->wkupevent = wkupevent; + priv->waitints = waitints; + +#ifdef CONFIG_SAMA5_SDMMC_DMA + priv->xfrflags = 0; +#endif + sam_putreg(priv, priv->xfrints | priv->waitints | priv->cintints | \ + SDMMC_INT_DINT, + SAMA5_SDMMC_IRQSIGEN_OFFSET); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_configxfrints + * + * Description: + * Enable SDIO interrupts needed to support the data transfer event + * + * Input Parameters: + * priv - A reference to the SDIO device state structure + * xfrints - The set of bits in the SDIO MASK register to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_configxfrints(struct sam_dev_s *priv, uint32_t xfrints) +{ + irqstate_t flags; + + flags = enter_critical_section(); + priv->xfrints = xfrints; + sam_putreg(priv, priv->xfrints | priv->waitints | priv->cintints, + SAMA5_SDMMC_IRQSIGEN_OFFSET); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_sampleinit + * + * Description: + * Setup prior to collecting DMA samples + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_sampleinit(void) +{ + memset(g_sampleregs, 0xff, + DEBUG_NSAMPLES * sizeof(struct sam_sdmmcregs_s)); +} +#endif + +/**************************************************************************** + * Name: sam_sdmmcsample + * + * Description: + * Sample SDIO registers + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_sdmmcsample(struct sam_dev_s *priv, + struct sam_sdmmcregs_s *regs) +{ + regs->dsaddr = sam_getreg32(priv, SAMA5_SDMMC_DSADDR_OFFSET); + regs->blkattr = sam_getreg32(priv, SAMA5_SDMMC_BLKATTR_OFFSET); + regs->cmdarg = sam_getreg32(priv, SAMA5_SDMMC_CMDARG_OFFSET); + regs->xferty = sam_getreg32(priv, SAMA5_SDMMC_XFERTYP_OFFSET); + regs->cmdrsp0 = sam_getreg32(priv, SAMA5_SDMMC_CMDRSP0_OFFSET); + regs->cmdrsp1 = sam_getreg32(priv, SAMA5_SDMMC_CMDRSP1_OFFSET); + regs->cmdrsp2 = sam_getreg32(priv, SAMA5_SDMMC_CMDRSP2_OFFSET); + regs->cmdrsp3 = sam_getreg32(priv, SAMA5_SDMMC_CMDRSP3_OFFSET); + regs->prsstat = sam_getreg32(priv, SAMA5_SDMMC_PRSSTAT_OFFSET); + regs->proctl = sam_getreg32(priv, SAMA5_SDMMC_PROCTL_OFFSET); + regs->sysctl = sam_getreg32(priv, SAMA5_SDMMC_SYSCTL_OFFSET); + regs->irqstat = sam_getreg32(priv, SAMA5_SDMMC_IRQSTAT_OFFSET); + regs->irqstaten = sam_getreg32(priv, SAMA5_SDMMC_IRQSTATEN_OFFSET); + regs->irqsigen = sam_getreg32(priv, SAMA5_SDMMC_IRQSIGEN_OFFSET); + regs->ac12err = sam_getreg32(priv, SAMA5_SDMMC_AC12ERR_OFFSET); + regs->htcapblt0 = sam_getreg32(priv, SAMA5_SDMMC_HTCAPBLT0_OFFSET); + regs->htcapblt1 = sam_getreg32(priv, SAMA5_SDMMC_HTCAPBLT1_OFFSET); + regs->admaes = sam_getreg32(priv, SAMA5_SDMMC_ADMAES_OFFSET); + regs->adsaddr = sam_getreg32(priv, SAMA5_SDMMC_ADSADDR_OFFSET); +} +#endif + +/**************************************************************************** + * Name: sam_sample + * + * Description: + * Sample SDIO/DMA registers + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_sample(struct sam_dev_s *priv, int index) +{ + if (priv->addr == DBG_BASE_ADDR) + { + sam_sdmmcsample(priv, &g_sampleregs[index]); + } +} +#endif + +/**************************************************************************** + * Name: sam_dumpsample + * + * Description: + * Dump one register sample + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_dumpsample(struct sam_dev_s *priv, + struct sam_sdmmcregs_s *regs, const char *msg) +{ + mcinfo("SDMMC Registers: %s\n", msg); + mcinfo(" DSADDR[%08x]: %08x\n", + SAMA5_SDMMC_DSADDR_OFFSET, regs->dsaddr); + mcinfo(" BLKATTR[%08x]: %08x\n", + SAMA5_SDMMC_BLKATTR_OFFSET, regs->blkattr); + mcinfo(" CMDARG[%08x]: %08x\n", + SAMA5_SDMMC_CMDARG_OFFSET, regs->cmdarg); + mcinfo(" XFERTY[%08x]: %08x\n", + SAMA5_SDMMC_XFERTYP_OFFSET, regs->xferty); + mcinfo(" CMDRSP0[%08x]: %08x\n", + SAMA5_SDMMC_CMDRSP0_OFFSET, regs->cmdrsp0); + mcinfo(" CMDRSP1[%08x]: %08x\n", + SAMA5_SDMMC_CMDRSP1_OFFSET, regs->cmdrsp1); + mcinfo(" CMDRSP2[%08x]: %08x\n", + SAMA5_SDMMC_CMDRSP2_OFFSET, regs->cmdrsp2); + mcinfo(" CMDRSP3[%08x]: %08x\n", + SAMA5_SDMMC_CMDRSP3_OFFSET, regs->cmdrsp3); + mcinfo(" PRSSTAT[%08x]: %08x\n", + SAMA5_SDMMC_PRSSTAT_OFFSET, regs->prsstat); + mcinfo(" PROCTL[%08x]: %08x\n", + SAMA5_SDMMC_PROCTL_OFFSET, regs->proctl); + mcinfo(" SYSCTL[%08x]: %08x\n", + SAMA5_SDMMC_SYSCTL_OFFSET, regs->sysctl); + mcinfo(" IRQSTAT[%08x]: %08x\n", + SAMA5_SDMMC_IRQSTAT_OFFSET, regs->irqstat); + mcinfo("IRQSTATEN[%08x]: %08x\n", + SAMA5_SDMMC_IRQSTATEN_OFFSET, regs->irqstaten); + mcinfo(" IRQSIGEN[%08x]: %08x\n", + SAMA5_SDMMC_IRQSIGEN_OFFSET, regs->irqsigen); + mcinfo(" AC12ERR[%08x]: %08x\n", + SAMA5_SDMMC_AC12ERR_OFFSET, regs->ac12err); + mcinfo("HTCAPBLT0[%08x]: %08x\n", + SAMA5_SDMMC_HTCAPBLT0_OFFSET, regs->htcapblt0); + mcinfo("HTCAPBLT1[%08x]: %08x\n", + SAMA5_SDMMC_HTCAPBLT1_OFFSET, regs->htcapblt1); + mcinfo(" ADMAES[%08x]: %08x\n", + SAMA5_SDMMC_ADMAES_OFFSET, regs->admaes); + mcinfo(" ADSADDR[%08x]: %08x\n", + SAMA5_SDMMC_ADSADDR_OFFSET, regs->adsaddr); +} +#endif + +/**************************************************************************** + * Name: sam_dumpsamples + * + * Description: + * Dump all sampled register data + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_dumpsamples(struct sam_dev_s *priv) +{ + sam_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP], + "Before setup"); + sam_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP], + "After setup"); + sam_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER], + "End of transfer"); +} +#endif + +/**************************************************************************** + * Name: sam_showregs + * + * Description: + * Dump the current state of all registers + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void sam_showregs(struct sam_dev_s *priv, const char *msg) +{ + struct sam_sdmmcregs_s regs; + + sam_sdmmcsample(priv, ®s); + sam_dumpsample(priv, ®s, msg); +} +#endif + +/**************************************************************************** + * Data Transfer Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_dataconfig + * + * Description: + * Configure the SDIO data path for the next data transfer + * + ****************************************************************************/ + +static inline void sam_dataconfig(struct sam_dev_s *priv, bool bwrite, + unsigned int datalen, unsigned int timeout) +{ + sam_sample(priv, SAMPLENDX_BEFORE_SETUP); + sam_putreg8(priv, timeout & SDMMC_TCR_MASK, SAMA5_SDMMC_TCR_OFFSET); +} + +/**************************************************************************** + * Name: sam_transmit + * + * Description: + * Send SDIO data in interrupt mode + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static void sam_transmit(struct sam_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is more data to be sent, while buffer write enable + * (PRSSTAT.BWEN) + */ + + mcinfo("Entry: remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + + while (priv->remaining > 0 && + (sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET) & + SDMMC_PRSSTAT_BWEN) != 0) + { + /* Is there a full word remaining in the user buffer? */ + + if (priv->remaining >= sizeof(uint32_t)) + { + /* Yes, transfer the word to the TX FIFO */ + + data.w = *priv->buffer++; + priv->remaining -= sizeof(uint32_t); + } + else + { + /* No.. transfer just the bytes remaining in the user buffer, + * padding with zero as necessary to extend to a full word. + */ + + uint8_t *ptr = (uint8_t *)priv->remaining; + int i; + + data.w = 0; + for (i = 0; i < priv->remaining; i++) + { + data.b[i] = *ptr++; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + + /* Put the word in the FIFO */ + + sam_putreg(priv, data.w, SAMA5_SDMMC_DATAPORT_OFFSET); + } + + /* Clear BWR. If there is more data in the buffer, writing to the buffer + * should reset BWR. + */ + + sam_putreg(priv, SDMMC_INT_BWR, SAMA5_SDMMC_IRQSTAT_OFFSET); + + mcinfo("Exit: remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg16(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: sam_receive + * + * Description: + * Receive SDIO data in interrupt mode + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static void sam_receive(struct sam_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is space to store the data, waiting for buffer + * read ready (BRR) + */ + + mcinfo("Entry: remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + + /* Clear BRR. It only fires on the first block of data. */ + + sam_putreg(priv, SDMMC_INT_BRR, SAMA5_SDMMC_IRQSTAT_OFFSET); + + while (priv->remaining > 0 && + (sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET) & + SDMMC_PRSSTAT_BREN) != 0) + { + /* Clear BREN. If there is more data in the buffer, reading from the + * buffer should reset BREN. + */ + + sam_putreg(priv, SDMMC_PRSSTAT_BREN, SAMA5_SDMMC_PRSSTAT_OFFSET); + + /* Read the next word from the RX buffer */ + + data.w = sam_getreg32(priv, SAMA5_SDMMC_DATAPORT_OFFSET); + if (priv->remaining >= sizeof(uint32_t)) + { + /* Transfer the whole word to the user buffer */ + + *priv->buffer++ = data.w; + priv->remaining -= sizeof(uint32_t); + } + else + { + /* Transfer any trailing fractional word */ + + uint8_t *ptr = (uint8_t *) priv->buffer; + int i; + + for (i = 0; i < priv->remaining; i++) + { + *ptr++ = data.b[i]; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + } + + mcinfo("Exit: remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: sam_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 sam_eventtimeout(int argc, uint32_t arg) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)arg; + + DEBUGASSERT(argc == 1 && priv != NULL); + DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0); + + /* Is a data transfer complete event expected? */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + /* Yes.. Sample registers at the time of the timeout */ + + sam_sample(priv, SAMPLENDX_END_TRANSFER); + + /* Wake up any waiting threads */ + + sam_endwait(priv, SDIOWAIT_TIMEOUT); + mcerr("ERROR: Timeout: remaining: %d\n", priv->remaining); + sam_dumpsamples(priv); + } +} + +/**************************************************************************** + * Name: sam_endwait + * + * Description: + * Wake up a waiting thread if the waited-for event has occurred. + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * wkupevent - The event that caused the wait to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void sam_endwait(struct sam_dev_s *priv, sdio_eventset_t wkupevent) +{ + /* Cancel the watchdog timeout */ + + wd_cancel(priv->waitwdog); + + /* Disable event-related interrupts */ + + sam_configwaitints(priv, 0, 0, wkupevent); + + /* Wake up the waiting thread */ + + sam_givesem(priv); +} + +/**************************************************************************** + * Name: sam_endtransfer + * + * Description: + * Terminate a transfer with the provided status. This function is called + * only from the SDIO interrupt handler when end-of-transfer conditions + * are detected. + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * wkupevent - The event that caused the transfer to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void sam_endtransfer(struct sam_dev_s *priv, + sdio_eventset_t wkupevent) +{ + /* Disable all transfer related interrupts */ + + sam_configxfrints(priv, 0); + + /* Clearing pending interrupt status on all transfer related interrupts */ + + sam_putreg(priv, SDMMC_XFRDONE_INTS | SDMMC_DMADONE_INTS, + SAMA5_SDMMC_IRQSTAT_OFFSET); + + /* Mark the transfer finished */ + + priv->remaining = 0; + +#ifdef CONFIG_SAMA5_SDMMC_DMA + /* DMA modified the buffer, so we need to flush its cache lines. */ + + up_invalidate_dcache((uintptr_t) priv->buffer, + (uintptr_t) priv->bufferend); +#endif + + /* Debug instrumentation */ + + sam_sample(priv, SAMPLENDX_END_TRANSFER); + + /* Is a thread wait for these data transfer complete events? */ + + if ((priv->waitevents & wkupevent) != 0) + { + /* Yes.. wake up any waiting threads */ + + sam_endwait(priv, wkupevent); + } +} + +/**************************************************************************** + * Name: sam_interrupt + * + * Description: + * SDIO interrupt handler + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int sam_interrupt(int irq, void *context, FAR void *arg) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)arg; + uint32_t enabled; + uint32_t pending; + uint32_t irqsigen; + uint32_t irqstat; +#ifdef CONFIG_SAMA5_SDMMC_DMA + uint32_t dma_start_addr; +#endif + + /* Check the SDMMC IRQSTAT register. Mask out all bits that don't + * correspond to enabled interrupts. (This depends on the fact that bits + * are ordered the same in both the IRQSTAT and IRQSIGEN registers). + * If there are non-zero bits remaining, then we have work to do here. + */ + + irqsigen = sam_getreg(priv, SAMA5_SDMMC_IRQSIGEN_OFFSET); + irqstat = sam_getreg32(priv, SAMA5_SDMMC_IRQSTAT_OFFSET); + enabled = irqstat & irqsigen; + pending = enabled & priv->xfrints; + + mcinfo("IRQSTAT: %08x IRQSIGEN %08x enabled: %08x pending: %08x " + "xfrints:%08x\n", + irqstat, irqsigen, enabled, pending, priv->xfrints); + + /* Handle in progress, interrupt driven data transfers ********************/ + + if (pending != 0) + { +#ifndef CONFIG_SAMA5_SDMMC_DMA + /* Is the RX buffer read ready? Is so then we must be processing a + * non-DMA receive transaction. + */ + + if ((pending & SDMMC_INT_BRR) != 0) + { + /* Receive data from the RX buffer */ + + sam_receive(priv); + } + + /* Otherwise, Is the TX buffer write ready? If so we must be processing + * non-DMA send transaction. NOTE: We can't be processing both! + */ + + else if ((pending & SDMMC_INT_BWR) != 0) + { + /* Send data via the TX FIFO */ + + sam_transmit(priv); + } + +#endif +#ifdef CONFIG_SAMA5_SDMMC_DMA + /* SDMA Buffer Boundary pause... update the DMA System Address Register + * to restart the transfer. See SAMA5D27 datasheet p1771. + */ + + if (((pending & SDMMC_INT_DINT) != 0) && + ((pending & SDMMC_INT_TC) == 0)) + { + /* clear interrupt */ + + sam_putreg(priv, SDMMC_INT_DINT, SAMA5_SDMMC_IRQSTAT_OFFSET); + + /* set SDMA start address to the next 4KB buffer */ + + dma_start_addr = sam_getreg(priv, SAMA5_SDMMC_DSADDR_OFFSET); + dma_start_addr &= ~(SDMMC_DEFAULT_BOUNDARY_SIZE - 1); + dma_start_addr += SDMMC_DEFAULT_BOUNDARY_SIZE; + sam_putreg(priv, dma_start_addr, SAMA5_SDMMC_DSADDR_OFFSET); + } +#endif + + /* ... transfer complete events */ + + if ((pending & SDMMC_INT_TC) != 0) + { + /* Terminate the transfer */ + + sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + + /* Data block send/receive CRC failure */ + + else if ((pending & SDMMC_INT_DCE) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data block CRC failure, remaining: %d\n", + priv->remaining); + sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* ... data timeout error */ + + else if ((pending & SDMMC_INT_DTOE) != 0) + { + /* Terminate the transfer with an error */ + + mcerr("ERROR: Data timeout, remaining: %d\n", priv->remaining); + sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); + } + } + + /* Handle Card interrupt events *******************************************/ + + pending = enabled & priv->cintints; + if ((pending & SDMMC_INT_CINT) != 0) + { + if (priv->do_sdio_card) + { + (priv->do_sdio_card)(priv->do_sdio_arg); + } + + /* We don't want any more ints now, so switch it off */ + + irqsigen &= ~SDMMC_INT_CINT; + priv->cintints = irqsigen; + sam_putreg(priv, irqsigen, SAMA5_SDMMC_IRQSIGEN_OFFSET); + } + + if ((pending & SDMMC_INT_CINS) != 0 || (pending & SDMMC_INT_CRM) != 0) + { + 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); + } + } + + /* Handle wait events *****************************************************/ + + pending = enabled & priv->waitints; + if (pending != 0) + { + /* Is this a response completion event? */ + + if ((pending & SDMMC_RESPDONE_INTS) != 0) + { + /* Yes.. Is there a thread waiting for response done? */ + + if ((priv->waitevents & + (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0) + { + /* Yes.. mask further interrupts and wake the thread up */ + + irqsigen = sam_getreg(priv, SAMA5_SDMMC_IRQSIGEN_OFFSET); + irqsigen &= ~SDMMC_RESPDONE_INTS; + sam_putreg(priv, irqsigen, SAMA5_SDMMC_IRQSIGEN_OFFSET); + + sam_endwait(priv, SDIOWAIT_RESPONSEDONE); + } + } + } + + sam_dumpsamples(priv); + return OK; +} + +/**************************************************************************** + * SDIO Interface Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_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 SDIO device interface + * lock - TRUE to lock, FALSE to unlock. + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_MUXBUS +static int sam_lock(FAR struct sdio_dev_s *dev, bool lock) +{ + /* The multiplex bus is part of board support package. */ + + sam_muxbus_sdio_lock((dev - g_sdmmcdev) / + sizeof(struct sam_dev_s), lock); + return OK; +} +#endif + +/**************************************************************************** + * Name: sam_reset + * + * Description: + * Reset the SDIO controller. Undo all setup and initialization. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_reset(FAR struct sdio_dev_s *dev) +{ + unsigned long timeout_ms; + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + + /* Turn SDMMC peripheral off and then on */ + + sam_sdmmc1_disableclk(); + sam_sdmmc1_enableclk(); + + /* Disable all interrupts so that nothing interferes with the following. */ + + sam_putreg(priv, 0, SAMA5_SDMMC_IRQSIGEN_OFFSET); + + /* Reset the SDMMC block, putting registers in their default, reset state. + * Initiate the reset by setting the RSTA bit in the SYSCTL register. + */ + + modifyreg32(priv->addr + SAMA5_SDMMC_SYSCTL_OFFSET, 0, SDMMC_SYSCTL_RSTA); + + /* The SDMMC will reset the RSTA bit to 0 when the capabilities registers + * are valid and the host driver can read them. + */ + + while ((sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET) & + SDMMC_SYSCTL_RSTA) != 0) + { + } + + /* Make sure that all clocking is disabled */ + + sam_clock(dev, CLOCK_SDIO_DISABLED); + + /* Enable all status bits (these could not all be potential sources of + * interrupts. + */ + + sam_putreg(priv, SDMMC_INT_ALL, SAMA5_SDMMC_IRQSTATEN_OFFSET); + + mcinfo("SYSCTL: %08x PRSSTAT: %08x IRQSTATEN: %08x\n", + sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET), + sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET), + sam_getreg(priv, SAMA5_SDMMC_IRQSTATEN_OFFSET)); + + /* The next phase of the hardware reset would be to send at least 80 clock + * ticks for card to power up and then reset the card with CMD0. This is + * done elsewhere. + */ + + /* Reset state data */ + + priv->waitevents = 0; /* Set of events to be waited for */ + priv->waitints = 0; /* Interrupt enables for event waiting */ + priv->wkupevent = 0; /* The event that caused the wakeup */ +#ifdef CONFIG_SAMA5_SDMMC_DMA + priv->xfrflags = 0; /* Used to synchronize SDIO and DMA completion */ +#endif + + wd_cancel(priv->waitwdog); /* Cancel any timeouts */ + + /* Interrupt mode data transfer support */ + + priv->buffer = 0; /* Address of current R/W buffer */ + priv->remaining = 0; /* Number of bytes remaining in the transfer */ + priv->xfrints = 0; /* Interrupt enables for data transfer */ + + /* SDMMC software reset - wait for a maximum of 100 ms */ + + sam_putreg8(priv, SDMMC_RESET_ALL, SAMA5_SDMMC_SRR_OFFSET); + timeout_ms = 1000; + while (sam_getreg8(priv, SAMA5_SDMMC_SRR_OFFSET) & SDMMC_RESET_ALL) + { + if (timeout_ms == 0) + { + mcinfo("%s: Reset 0x%x never completed.\n", + __func__, (int)SDMMC_RESET_ALL); + return; + } + + timeout_ms--; + usleep(100); + } + + mcinfo("Reset complete\n"); +} + +/**************************************************************************** + * Name: sam_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 sam_capabilities(FAR struct sdio_dev_s *dev) +{ + sdio_capset_t caps = 0; + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + + switch (priv->addr) + { + case SAM_SDMMC0_VBASE: +#ifdef CONFIG_SAMA5_SDMMC0_WIDTH_D1_ONLY + caps |= SDIO_CAPS_1BIT_ONLY; +#endif +#ifdef CONFIG_SAMA5_SDMMC0_WIDTH_D1_D4 + caps |= SDIO_CAPS_4BIT; +#endif + break; + + case SAM_SDMMC1_VBASE: +#ifdef CONFIG_SAMA5_SDMMC1_WIDTH_D1_ONLY + caps |= SDIO_CAPS_1BIT_ONLY; +#endif +#ifdef CONFIG_SAMA5_SDMMC1_WIDTH_D1_D4 + caps |= SDIO_CAPS_4BIT; +#endif +#ifdef CONFIG_SAMA5_SDMMC1_WIDTH_D1_D8 + caps |= SDIO_CAPS_8BIT; +#endif + break; + + default: + break; + } + +#ifdef CONFIG_SAMA5_SDMMC_DMA + caps |= SDIO_CAPS_DMASUPPORTED; +#endif + caps |= SDIO_CAPS_DMABEFOREWRITE; + + return caps; +} + +/**************************************************************************** + * Name: sam_status + * + * Description: + * Get SDIO status. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Returns a bitset of status values (see sam_status_* defines) + * + ****************************************************************************/ + +static sdio_statset_t sam_status(FAR struct sdio_dev_s *dev) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + bool present = false; + + /* Board did not use one of the GPIO_SDMMCn_CD pins + * but instead a GPIO was used and defined in board.h + * as PIN_SDMMCx_CD_GPIO + */ + + if (priv->sw_cd_gpio != 0) + { + present = priv->cd_invert ^ !sam_pioread(priv->sw_cd_gpio); + } + else + { + /* This register reflects the state of CD no matter if it's a separate pin + * or DAT3 + */ + + present = ((sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET) & + SDMMC_PRSSTAT_CINS) != 0) ^ priv->cd_invert; + } + + if (present) + { + priv->cdstatus |= SDIO_STATUS_PRESENT; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_PRESENT; + } + + mcinfo("cdstatus=%02x\n", priv->cdstatus); + + return priv->cdstatus; +} + +/**************************************************************************** + * Name: sam_widebus + * + * Description: + * Called after change in Bus width has been selected (via ACMD6). Most + * controllers will need to perform some special operations to work + * correctly in the new bus mode. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * wide - true: wide bus (4-bit) bus mode enabled + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_widebus(FAR struct sdio_dev_s *dev, bool wide) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + uint32_t regval; + + /* Set the Data Transfer Width (DTW) field in the PROCTL register. */ + + regval = sam_getreg(priv, SAMA5_SDMMC_PROCTL_OFFSET); + regval &= ~SDMMC_PROCTL_DTW_MASK; + if (wide) + { + regval |= SDMMC_PROCTL_DTW_4BIT; + } + else + { + regval |= SDMMC_PROCTL_DTW_1BIT; + } + + sam_putreg(priv, regval, SAMA5_SDMMC_PROCTL_OFFSET); +} + +/**************************************************************************** + * Name: sam_frequency + * + * Description: + * Set the SD clock frequency + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * frequency - The frequency to use + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SDMMC_ABSFREQ +static void sam_frequency(FAR struct sdio_dev_s *dev, uint32_t frequency) +{ + uint32_t sdclkfs; + uint32_t prescaled; + uint32_t regval; + unsigned int prescaler; + unsigned int divisor; + + /* The SDCLK frequency is determined by + * (1) the frequency of the base clock that was selected as the + * input clock, and + * (2) by a prescaler and a divisor that are selected here: + * + * SDCLK frequency = (base clock) / (prescaler * divisor) + * + * The prescaler is available only for the values: 2, 4, 8, 16, 32, + * 64, 128, and 256. Pick the smallest value of SDCLKFS that would + * result in an in-range frequency. For example, if the base clock + * frequency is 96 MHz, and the target frequency is 25 MHz, the following + * logic will select prescaler: + * + * 96MHz / 2 <= 25MHz <= 96MHz / 2 /16 -- YES, prescaler == 2 + * + * If the target frequency is 400 KHz, the following logic will + * select prescaler: + * + * 96MHz / 2 <= 400KHz <= 96MHz / 2 / 16 -- NO + * 96MHz / 4 <= 400KHz <= 96MHz / 4 / 16 -- NO + * 96MHz / 8 <= 400KHz <= 96MHz / 8 / 16 -- NO + * 96MHz / 16 <=400KHz <= 96MHz / 16 / 16 -- YES, prescaler == 16 + */ + + if (/* frequency >= (BOARD_CORECLK_FREQ / 2) && */ + frequency <= (BOARD_CORECLK_FREQ / 2 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV2; + prescaler = 2; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 4) && + frequency <= (BOARD_CORECLK_FREQ / 4 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV4; + prescaler = 4; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 8) && + frequency <= (BOARD_CORECLK_FREQ / 8 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV8; + prescaler = 8; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 16) && + frequency <= (BOARD_CORECLK_FREQ / 16 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV16; + prescaler = 16; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 32) && + frequency <= (BOARD_CORECLK_FREQ / 32 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV32; + prescaler = 32; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 64) && + frequency <= (BOARD_CORECLK_FREQ / 64 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV64; + prescaler = 64; + } + else if (frequency >= (BOARD_CORECLK_FREQ / 128) && + frequency <= (BOARD_CORECLK_FREQ / 128 / 16)) + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV128; + prescaler = 128; + } + else + { + sdclkfs = SDMMC_SYSCTL_SDCLKFS_DIV256; + prescaler = 256; + } + + /* The optimal divider can than be calculated. For example, if the base + * clock frequency is 96 MHz, the target frequency is 25 MHz, and the + * selected prescaler value is 2, then + * + * prescaled = 96MHz / 2 = 48MHz + * divisor = (48MHz + 12.5MHz/ 25MHz = 2 + * + * And the resulting frequency will be 24MHz. Or, for example, if the + * target frequency is 400 KHz and the selected prescaler is 16, the + * following logic will select prescaler: + * + * prescaled = 96MHz / 16 = 6MHz + * divisor = (6MHz + 200KHz) / 400KHz = 15 + * + * And the resulting frequency will be exactly 400KHz. + */ + + prescaled = frequency / prescaler; + divisor = (prescaled + (frequency >> 1)) / frequency; + + /* Set the new divisor information and enable all clocks in the SYSCTRL + * register. TODO: Investigate using the automatically gated clocks to + * reduce power consumption. + */ + + regval = sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET); + regval &= ~(SDMMC_SYSCTL_SDCLKFS_MASK | SDMMC_SYSCTL_DVS_MASK); + regval |= (sdclkfs | SDMMC_SYSCTL_DVS_DIV(divisor)); + regval |= (SDMMC_SYSCTL_SDCLKEN | SDMMC_SYSCTL_PEREN | + SDMMC_SYSCTL_HCKEN | SDMMC_SYSCTL_IPGEN); + + sam_putreg(priv, regval, SAMA5_SDMMC_SYSCTL_OFFSET); + mcinfo("SYSCTRL: %08x\n", sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: sam_clock + * + * Description: + * Enable/disable SDIO clocking + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * rate - Specifies the clocking to use (see enum sdio_clock_e) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *) dev; + uint32_t regval; + int wait_microseconds = 0; + + /* Clear the old prescaler and divisor values so that new ones can be + * ORed in. + */ + + regval = sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET); + regval &= ~(SDMMC_SYSCTL_SDCLKFS_MASK | SDMMC_SYSCTL_DVS_MASK); + + /* Select the new prescaler and divisor values based on the requested + * mode and the settings from the board.h file. Clocks are automatically + * gated by the driver when not needed. + */ + + switch (rate) + { + default: + case CLOCK_SDIO_DISABLED: /* Clock is disabled */ + { + /* Clear the prescaler and divisor settings */ + + sam_putreg(priv, regval, SAMA5_SDMMC_SYSCTL_OFFSET); + mcinfo("DISABLED, SYSCTRL: %08x\n", + sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET)); + sam_set_clock(priv, 0); + } + break; + + case CLOCK_IDMODE: + { + /* Initial ID mode clocking (<400KHz) */ + + mcinfo("IDMODE\n"); + + /* Wait for at least 74 SD Clock cycles, as per SD Card + * specification. The e.MMC Electrical Standard specifies + * tRSCA >= 200 usec. + */ + + regval |= (BOARD_SDMMC_IDMODE_PRESCALER | + BOARD_SDMMC_IDMODE_DIVISOR | + SDMMC_SYSCTL_INTCLKEN); + wait_microseconds = 200; + sam_set_clock(priv, SAMA5_SDMMC_BUS_SPEED_IDMODE); + } + break; + + case CLOCK_MMC_TRANSFER: + { + /* MMC normal operation clocking */ + + mcinfo("MMCTRANSFER\n"); + regval |= (BOARD_SDMMC_MMCMODE_PRESCALER | + BOARD_SDMMC_MMCMODE_DIVISOR); + sam_set_clock(priv, SAMA5_SDMMC_BUS_SPEED); + } + break; + + case CLOCK_SD_TRANSFER_1BIT: + { +#ifndef CONFIG_SAM_SDMMC_WIDTH_D1_ONLY + /* SD normal operation clocking (narrow 1-bit mode) */ + + mcinfo("1BITTRANSFER\n"); + regval |= (BOARD_SDMMC_SD1MODE_PRESCALER | + BOARD_SDMMC_SD1MODE_DIVISOR); + sam_set_clock(priv, SAMA5_SDMMC_BUS_SPEED); + } + break; +#endif + + case CLOCK_SD_TRANSFER_4BIT: + { + /* SD normal operation clocking (wide 4-bit mode) */ + + mcinfo("4BITTRANSFER\n"); + regval |= (BOARD_SDMMC_SD4MODE_PRESCALER | + BOARD_SDMMC_SD4MODE_DIVISOR); + sam_set_clock(priv, SAMA5_SDMMC_BUS_SPEED); + } + break; + } + + if (wait_microseconds > 0) + { + usleep(wait_microseconds); + } +} + +/**************************************************************************** + * Name: sam_attach + * + * Description: + * Attach and prepare interrupts + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * OK on success; A negated errno on failure. + * + ****************************************************************************/ + +static int sam_attach(FAR struct sdio_dev_s *dev) +{ + int ret; + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + + /* Attach the SDIO interrupt handler */ + + if (priv->addr == SAM_SDMMC0_VBASE) + { + ret = irq_attach(SAM_IRQ_SDMMC0, sam_interrupt, &g_sdmmcdev[0]); + } + else + { + if (priv->addr == SAM_SDMMC1_VBASE) + { + ret = irq_attach(SAM_IRQ_SDMMC1, sam_interrupt, &g_sdmmcdev[1]); + } + else + { + ASSERT(false); + } + } + + if (ret == OK) + { + /* Disable all interrupts at the SDIO controller and clear all pending + * interrupts. + */ + + sam_putreg(priv, 0, SAMA5_SDMMC_IRQSIGEN_OFFSET); + sam_putreg(priv, SDMMC_INT_ALL, SAMA5_SDMMC_IRQSTAT_OFFSET); + + /* Enable SDIO interrupts at the NVIC. They can now be enabled at the + * SDIO controller as needed. + */ + + if (priv->addr == SAM_SDMMC0_VBASE) + { + up_enable_irq(SAM_IRQ_SDMMC0); + } + else if (priv->addr == SAM_SDMMC1_VBASE) + { + up_enable_irq(SAM_IRQ_SDMMC1); + } + } + + return ret; +} + +/**************************************************************************** + * Name: sam_sendcmd + * + * Description: + * Send the SDIO command + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * cmd - The command to send (32-bits, encoded) + * arg - 32-bit argument required with some commands + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int sam_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + clock_t timeout; + clock_t start; + clock_t elapsed; + uint32_t regval; + uint32_t cmdidx; + + /* Initialize the command index */ + + cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT; + regval = cmdidx << SDMMC_XFERTYP_CMDINX_SHIFT; + + if (cmdidx == SD_ACMDIDX53) + { + /* Dynamically set parameters for ACMD53 because it can accommodate + * different transmission characteristics (single and multi-block, + * rx & tx). + */ + + if (arg & (1 << 31)) + { + /* Transmit mode */ + + cmd |= MMCSD_WRDATAXFR; + } + else + { + /* Receive mode */ + + cmd |= MMCSD_RDDATAXFR; + } + + if (arg & (1 << 27)) + { + /* In block mode */ + + cmd |= SDIO_MULTIBLOCK; + } + } + + /* Check if a data transfer accompanies the command */ + + switch (cmd & MMCSD_DATAXFR_MASK) + { + default: + case MMCSD_NODATAXFR: + { + /* No.. no data transfer */ + } + break; + + /* The following two cases are probably missing some setup logic */ + + case MMCSD_RDSTREAM: + { + /* Yes.. streaming read data transfer */ + + regval |= SDMMC_XFERTYP_DPSEL; + regval |= SDMMC_XFERTYP_DTDSEL; + } + break; + + case MMCSD_WRSTREAM: + { + /* Yes.. streaming write data transfer */ + + regval |= SDMMC_XFERTYP_DPSEL; + } + break; + + case MMCSD_RDDATAXFR: + { + /* Yes.. normal read data transfer */ + + regval |= SDMMC_XFERTYP_DPSEL; + regval |= SDMMC_XFERTYP_DTDSEL; + } + break; + + case MMCSD_WRDATAXFR: + { + /* Yes.. normal write data transfer */ + + regval |= SDMMC_XFERTYP_DPSEL; + } + break; + } + + /* Is it a multi-block transfer? */ + + if ((cmd & MMCSD_MULTIBLOCK) != 0) + { + /* Yes.. should the transfer be stopped with ACMD12? */ + + if ((cmd & MMCSD_STOPXFR) != 0) + { + /* Yes.. Indefinite block transfer */ + + regval |= (SDMMC_XFERTYP_MSBSEL | SDMMC_XFERTYP_AC12EN); + } + else + { + /* No.. Fixed block transfer */ + + regval |= (SDMMC_XFERTYP_MSBSEL | SDMMC_XFERTYP_BCEN); + } + } + + /* Configure response type bits */ + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + { + /* No response */ + + regval |= SDMMC_XFERTYP_RSPTYP_NONE; + } + break; + + case MMCSD_R1B_RESPONSE: + { + /* Response length 48, check busy & cmdindex */ + + regval |= + (SDMMC_XFERTYP_RSPTYP_LEN48BSY | SDMMC_XFERTYP_CICEN | + SDMMC_XFERTYP_CCCEN); + } + break; + + case MMCSD_R1_RESPONSE: /* Response length 48, check cmdindex */ + case MMCSD_R5_RESPONSE: + case MMCSD_R6_RESPONSE: + { + regval |= + (SDMMC_XFERTYP_RSPTYP_LEN48 | SDMMC_XFERTYP_CICEN | + SDMMC_XFERTYP_CCCEN); + } + break; + + case MMCSD_R2_RESPONSE: + { + /* Response length 136, check CRC */ + + regval |= (SDMMC_XFERTYP_RSPTYP_LEN136 | SDMMC_XFERTYP_CCCEN); + } + break; + + case MMCSD_R3_RESPONSE: /* Response length 48 */ + case MMCSD_R4_RESPONSE: + case MMCSD_R7_RESPONSE: + { + regval |= SDMMC_XFERTYP_RSPTYP_LEN48; + } + break; + } + +#ifdef CONFIG_SAMA5_SDMMC_DMA + /* Enable DMA */ + + regval |= SDMMC_XFERTYP_DMAEN; + +#endif + + /* Check for abort. */ + + /* TODO: Check Suspend/Resume bits too in XFR_TYP::CMDTYP */ + + if (cmd & MMCSD_STOPXFR) + { + regval |= SDMMC_XFERTYP_CMDTYP_ABORT; + } + + mcinfo("cmd: %08x arg: %08x regval: %08x\n", cmd, arg, regval); + + /* If there has been a response error then perform a reset and wait for it + * to complete. + */ + + if ((sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET) & + SDMMC_RESPERR_INTS) != 0) + { + modifyreg32(priv->addr + SAMA5_SDMMC_SYSCTL_OFFSET, 0, + SDMMC_SYSCTL_RSTC); + while ((sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET) & + SDMMC_SYSCTL_RSTC) != 0) + { + } + } + + /* The Command Inhibit (CIHB) bit is set in the PRSSTAT bit immediately + * after the transfer type register is written. This bit is cleared + * when the command response is received. If this status bit is 0, it + * indicates that the CMD line is not in use and the SDMMC can issue a + * SD/MMC Command using the CMD line. CIHB should always be clear before + * this function is called, but this check is performed here to provide + * overlap and maximum performance. + */ + + timeout = SDMMC_CMDTIMEOUT; + start = clock_systime_ticks(); + while ((sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET) & + SDMMC_PRSSTAT_CIHB) != 0) + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + if (elapsed >= timeout) + { + mcerr("ERROR: Timeout (waiting CIHB) cmd: %08x PRSSTAT: %08x\n", + cmd, sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET)); + return -EBUSY; + } + } + + /* Set the SDMMC Argument value */ + + sam_putreg(priv, arg, SAMA5_SDMMC_CMDARG_OFFSET); + + /* Clear interrupt status and write the SDMMC CMD */ + + sam_configxfrints(priv, SDMMC_RCVDONE_INTS | SDMMC_XFRDONE_INTS | \ + SDMMC_INT_DINT); + + sam_putreg(priv, SDMMC_RESPDONE_INTS, SAMA5_SDMMC_IRQSTAT_OFFSET); + sam_putreg(priv, regval, SAMA5_SDMMC_XFERTYP_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: sam_blocksetup + * + * Description: + * Configure block size and the number of blocks for next transfer + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * blocklen - The selected block size. + * nblocklen - The number of blocks to transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_BLOCKSETUP +static void sam_blocksetup(FAR struct sdio_dev_s *dev, + unsigned int blocklen, + unsigned int nblocks) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + + mcinfo("blocklen=%ld, total transfer=%ld (%ld blocks)\n", blocklen, + blocklen * nblocks, nblocks); + + /* Configure block size for next transfer */ + + sam_putreg16(priv, blocklen, SAMA5_SDMMC_BSR_OFFSET); + sam_putreg16(priv, nblocks, SAMA5_SDMMC_BCR_OFFSET); +} +#endif + +/**************************************************************************** + * Name: sam_recvsetup + * + * Description: + * Setup hardware in preparation for data transfer from the card in non-DMA + * (interrupt driven mode). This method will do whatever controller setup + * is necessary. This would be called for SD memory just BEFORE sending + * CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18 + * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, + * SDIO_WAITEVENT will be called to receive the indication that the + * transfer is complete. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - Address of the buffer in which to receive the data + * nbytes - The number of bytes in the transfer + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure + * + ****************************************************************************/ + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static int sam_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t) buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + sam_sampleinit(); + + mcinfo("nbytes: %d priv->remaining: %d IRQSTAT: %08x\n", nbytes, + priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + sam_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the destination buffer information for use by the interrupt + * handler and DMA memory invalidation. + */ + + priv->buffer = (uint32_t *) buffer; + priv->remaining = nbytes; + + /* Then set up the SDIO data path */ + + mcinfo("remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + sam_dataconfig(priv, false, nbytes, SDMMC_DTOCV_MAXTIMEOUT); + + /* And enable interrupts */ + + mcinfo("remaining: %d IRQSTAT: %08x\n", priv->remaining, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + sam_configxfrints(priv, SDMMC_RCVDONE_INTS | SDMMC_XFRDONE_INTS); + sam_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} +#endif + +/**************************************************************************** + * Name: sam_sendsetup + * + * Description: + * Setup hardware in preparation for data transfer from the card. This + * method will do whatever controller setup is necessary. This would be + * called for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25 + * (WRITE_MULTIPLE_BLOCK), ... and before SDIO_SENDDATA is called. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - Address of the buffer containing the data to send + * nbytes - The number of bytes in the transfer + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure + * + ****************************************************************************/ + +#ifndef CONFIG_SAMA5_SDMMC_DMA +static int sam_sendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, + size_t nbytes) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t) buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + sam_sampleinit(); + sam_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *) buffer; priv->remaining = nbytes; + + /* Then set up the SDIO data path */ + + sam_dataconfig(priv, true, nbytes, SDMMC_DTOCV_MAXTIMEOUT); + + /* Enable TX interrupts */ + + sam_configxfrints(priv, SDMMC_SNDDONE_INTS); + sam_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} +#endif + +/**************************************************************************** + * Name: sam_cancel + * + * Description: + * Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP, + * SDIO_DMARECVSETUP or SDIO_DMASENDSETUP. This must be called to cancel + * the data transfer setup if, for some reason, you cannot perform the + * transfer. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * OK is success; a negated errno on failure + * + ****************************************************************************/ + +static int sam_cancel(FAR struct sdio_dev_s *dev) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + +#ifdef CONFIG_SAMA5_SDMMC_DMA + uint32_t regval; +#endif + + /* Disable all transfer- and event- related interrupts */ + + sam_configxfrints(priv, 0); + sam_configwaitints(priv, 0, 0, 0); + + /* Clearing pending interrupt status on all transfer- and event- related + * interrupts + */ + + sam_putreg(priv, SDMMC_WAITALL_INTS, SAMA5_SDMMC_IRQSTAT_OFFSET); + + /* Cancel any watchdog timeout */ + + wd_cancel(priv->waitwdog); + + /* If this was a DMA transfer, make sure that DMA is stopped */ + +#ifdef CONFIG_SAMA5_SDMMC_DMA + + /* Stop the DMA by resetting the data path */ + + regval = sam_getreg(priv, SAMA5_SDMMC_SYSCTL_OFFSET); + regval |= SDMMC_SYSCTL_RSTD; + sam_putreg(priv, regval, SAMA5_SDMMC_SYSCTL_OFFSET); +#endif + + /* Mark no transfer in progress */ + + priv->remaining = 0; return OK; +} + +/**************************************************************************** + * Name: sam_waitresponse + * + * Description: + * Poll-wait for the response to the last command to be ready. This + * function should be called even after sending commands that have no + * response (such as CMD0) to make sure that the hardware is ready to + * receive the next command. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * cmd - The command that was sent. See 32-bit command definitions above. + * + * Returned Value: + * OK is success; a negated errno on failure + * + ****************************************************************************/ + +static int sam_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + uint32_t errors; + uint32_t enerrors; + + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + int ret = OK; + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + timeout = SDMMC_CMDTIMEOUT; + errors = 0; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R2_RESPONSE: + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + case MMCSD_R6_RESPONSE: + { + timeout = SDMMC_LONGTIMEOUT; + errors = SDMMC_RESPERR_INTS; + } + break; + + case MMCSD_R3_RESPONSE: + case MMCSD_R7_RESPONSE: + { + timeout = SDMMC_CMDTIMEOUT; + errors = SDMMC_RESPERR_INTS; + } + break; + + default: + return -EINVAL; + } + + /* Then wait for the Command Complete (CC) indication (or timeout). The + * CC bit is set when the end bit of the command response is received + * (except Auto CMD12). + */ + + start = clock_systime_ticks(); + while ((sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET) & SDMMC_INT_CC) == 0) + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + if (elapsed >= timeout) + { + mcerr("ERROR: Timeout cmd: %08x IRQSTAT: %08x\n", cmd, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + sam_showregs(priv, "After timeout"); + ret = -ETIMEDOUT; + break; + } + } + + /* Check for hardware detected errors */ + + enerrors = sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET) & errors; + if (enerrors != 0) + { + mcerr("ERROR: cmd: %08x errors: %08x, fired %08x IRQSTAT: %08x\n", + cmd, errors, enerrors, + sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET)); + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: sam_recv_rx + * + * Description: + * Receive response to SDIO command. Only the critical payload is + * returned -- that is 32 bits for 48 bit status and 128 bits for 136 bit + * status. The driver implementation should verify the correctness of + * the remaining, non-returned bits (CRCs, CMD index, etc.). + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * Rx - Buffer in which to receive the response + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure. Here a + * failure means only a failure to obtain the requested response (due to + * transport problem -- timeout, CRC, etc.). The implementation only + * assures that the response is returned intact and does not check errors + * within the response itself. + * + ****************************************************************************/ + +static int sam_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + uint32_t regval; + int ret = OK; + + /* R1 Command response (48-bit) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Command index (0-63) + * 39:8 bit31- bit0 32-bit card status + * 7:1 bit6 - bit0 CRC7 + * 0 1 End bit + * + * R1b Identical to R1 with the additional busy signalling via the data + * line. + * R6 Published RCA Response (48-bit, SD card only) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Command index (0-63) + * 39:8 bit31 - bit0 32-bit Argument Field, consisting of: + * [31:16] New published RCA of card + * [15:0] Card status bits + * {23,22,19,12:0} + * 7:1 bit6 - bit0 CRC7 + * 0 1 End bit + */ + +#ifdef CONFIG_DEBUG_FEATURES + if (!rshort) + { + mcerr("ERROR: rshort=NULL\n"); + ret = -EINVAL; + } + + /* Check that this is the correct response to this command */ + + else if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R5_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE) + { + mcerr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET); + if ((regval & SDMMC_INT_CTOE) != 0) + { + mcerr("ERROR: Command timeout: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if ((regval & SDMMC_INT_CCE) != 0) + { + mcerr("ERROR: CRC failure: %08x\n", regval); ret = -EIO; + } + } + + /* Return the R1/R1b/R6 response. These responses are returned in CDMRSP0. + * NOTE: This is not true for R1b (Auto CMD12 response) which is returned + * in CMDRSP3. + */ + + *rshort = sam_getreg(priv, SAMA5_SDMMC_CMDRSP0_OFFSET); + + /* We need a short delay here to let the SDMMC peripheral respond */ + + usleep(10); + + return ret; +} + +static int sam_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + uint32_t regval; + int ret = OK; + + /* R2 CID, CSD register (136-bit) + * 135 0 Start bit + * 134 0 Transmission bit (0=from card) + * 133:128 bit5 - bit0 Reserved + * 127:1 bit127 - bit1 127-bit CID or CSD register (including int. CRC) + * 0 1 End bit + */ + +#ifdef CONFIG_DEBUG_FEATURES + /* Check that R1 is the correct response to this command */ + + if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE) + { + mcerr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET); + if (regval & SDMMC_INT_CTOE) + { + mcerr("ERROR: Timeout IRQSTAT: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if (regval & SDMMC_INT_CCE) + { + mcerr("ERROR: CRC fail IRQSTAT: %08x\n", regval); + ret = -EIO; + } + } + + /* Return the long response in CMDRSP3..0 */ + + if (rlong) + { + uint32_t rsp3 = sam_getreg(priv, SAMA5_SDMMC_CMDRSP3_OFFSET); + uint32_t rsp2 = sam_getreg(priv, SAMA5_SDMMC_CMDRSP2_OFFSET); + uint32_t rsp1 = sam_getreg(priv, SAMA5_SDMMC_CMDRSP1_OFFSET); + uint32_t rsp0 = sam_getreg(priv, SAMA5_SDMMC_CMDRSP0_OFFSET); + + rlong[0] = rsp3 << 8 | rsp2 >> 24; + rlong[1] = rsp2 << 8 | rsp1 >> 24; + rlong[2] = rsp1 << 8 | rsp0 >> 24; rlong[3] = rsp0 << 8; + } + + return ret; +} + +static int sam_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort) +{ + FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev; + uint32_t regval; + int ret = OK; + + /* R3 OCR (48-bit) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Reserved + * 39:8 bit31- bit0 32-bit OCR register + * 7:1 bit6 - bit0 Reserved + * 0 1 End bit + */ + + /* Check that this is the correct response to this command */ + +#ifdef CONFIG_DEBUG_FEATURES + if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R4_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE) + { + mcerr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout occurred (Apparently a CRC error can terminate a + * good response) + */ + + regval = sam_getreg(priv, SAMA5_SDMMC_IRQSTAT_OFFSET); + if (regval & SDMMC_INT_CTOE) + { + mcerr("ERROR: Timeout IRQSTAT: %08x\n", regval); + ret = -ETIMEDOUT; + } + } + + /* Return the short response in CMDRSP0 */ + + if (rshort) + { + *rshort = sam_getreg(priv, SAMA5_SDMMC_CMDRSP0_OFFSET); + } + + return ret; +} + +/**************************************************************************** + * Name: sam_waitenable + * + * Description: + * Enable/disable of a set of SDIO wait events. This is part of the + * the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is + * configured before calling sam_eventwait. This is done in this way + * to help the driver to eliminate race conditions between the command + * setup and the subsequent events. + * + * The enabled events persist until either (1) SDIO_WAITENABLE is called + * again specifying a different set of wait events, or (2) SDIO_EVENTWAIT + * returns. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * eventset - A bitset of events to enable or disable (see SDIOWAIT_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + uint32_t waitints; + + DEBUGASSERT(priv != NULL); + + /* Disable event-related interrupts */ + + sam_configwaitints(priv, 0, 0, 0); + + /* Select the interrupt mask that will give us the appropriate wakeup + * interrupts. + */ + + waitints = 0; + if ((eventset & (SDIOWAIT_CMDDONE | SDIOWAIT_RESPONSEDONE)) != 0) + { + waitints |= SDMMC_RESPDONE_INTS; + } + + if ((eventset & SDIOWAIT_TRANSFERDONE) != 0) + { +#ifdef CONFIG_SAMA5_SDMMC_DMA + waitints |= SDMMC_DMADONE_INTS | SDMMC_INT_DINT; +#else + waitints |= SDMMC_XFRDONE_INTS; +#endif + } + + /* Enable event-related interrupts */ + + sam_configwaitints(priv, waitints, eventset, 0); +} + +/**************************************************************************** + * Name: sam_eventwait + * + * Description: + * Wait for one of the enabled events to occur (or a timeout). Note that + * all events enabled by SDIO_WAITEVENTS are disabled when sam_eventwait + * returns. SDIO_WAITEVENTS must be called again before sam_eventwait + * can be used again. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * timeout - Maximum time in milliseconds to wait. Zero means immediate + * timeout with no wait. The timeout value is ignored if + * SDIOWAIT_TIMEOUT is not included in the waited-for eventset. + * + * Returned Value: + * Event set containing the event(s) that ended the wait. Should always + * be non-zero. All events are disabled after the wait concludes. + * + ****************************************************************************/ + +static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev, + uint32_t timeout) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + sdio_eventset_t wkupevent = 0; int ret; + + /* There is a race condition here... the event may have completed before + * we get here. In this case waitevents will be zero, but wkupevents + * will be non-zero (and, hopefully, the semaphore count will also be + * non-zero. + */ + + DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) || + (priv->waitevents == 0 && priv->wkupevent != 0)); + + /* Check if the timeout event is specified in the event set */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + int delay; + + /* Yes.. Handle a corner case */ + + if (!timeout) + { + return SDIOWAIT_TIMEOUT; + } + + /* Start the watchdog timer */ + + delay = MSEC2TICK(timeout); + ret = wd_start(priv->waitwdog, delay, (wdentry_t) sam_eventtimeout, + 1, (uint32_t) priv); + + if (ret < 0) + { + mcerr("ERROR: wd_start failed: %d\n", ret); + } + } + + /* Loop until the event (or the timeout occurs). Race conditions are + * avoided by calling sam_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. + */ + + sam_takesem(priv); + wkupevent = priv->wkupevent; + + /* Check if the event has occurred. When the event has occurred, then + * evenset will be set to 0 and wkupevent will be set to a non-zero + * value. + */ + + if (wkupevent != 0) + { + /* Yes... break out of the loop with wkupevent non-zero */ + + break; + } + } + + /* Disable event-related interrupts */ + + sam_configwaitints(priv, 0, 0, 0); +#ifdef CONFIG_SAMA5_SDMMC_DMA + priv->xfrflags = 0; +#endif + return wkupevent; +} + +/**************************************************************************** + * Name: sam_callbackenable + * + * Description: + * Enable/disable of a set of SDIO callback events. This is part of the + * the SDIO callback sequence. The set of events is configured to enabled + * callbacks to the function provided in sam_registercallback. + * + * Events are automatically disabled once the callback is performed and no + * further callback events will occur until they are again enabled by + * calling this method. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * eventset - A bitset of events to enable or disable (see SDIOMEDIA_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + + mcinfo("eventset: %02x\n", eventset); + DEBUGASSERT(priv != NULL); + + priv->cbevents = eventset; + sam_callback(priv); +} + +/**************************************************************************** + * Name: sam_registercallback + * + * Description: + * Register a callback that that will be invoked on any media status + * change. Callbacks should not be made from interrupt handlers, rather + * interrupt level events should be handled by calling back on the work + * thread. + * + * When this method is called, all callbacks should be disabled until they + * are enabled via a call to SDIO_CALLBACKENABLE + * + * Input Parameters: + * dev - Device-specific state data + * callback - The function to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +static int sam_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + + /* Disable callbacks and register this callback and is argument */ + + mcinfo("Register %p(%p)\n", callback, arg); + + DEBUGASSERT(priv != NULL); + priv->cbevents = 0; + priv->cbarg = arg; + priv->callback = callback; + return OK; +} + +/**************************************************************************** + * Name: sam_dmarecvsetup + * + * Description: + * Setup to perform a read DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For read transfers this may mean + * invalidating the data cache. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - The memory to DMA from + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SDMMC_DMA +static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, + FAR uint8_t *buffer, size_t buflen) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); + DEBUGASSERT(((uint32_t) buffer & 3) == 0); + + /* Begin sampling register values */ + + sam_sampleinit(); + sam_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the destination buffer information for use by the interrupt + * handler + */ + + priv->buffer = (uint32_t *) buffer; + priv->remaining = buflen; + priv->bufferend = (uint32_t *)(buffer + buflen); + + /* DMA modified the buffer, so we need to flush its cache lines. */ + + up_invalidate_dcache((uintptr_t) priv->buffer, + (uintptr_t) priv->bufferend); + + /* Then set up the SDIO data path */ + + sam_dataconfig(priv, false, buflen, SDMMC_DTOCV_MAXTIMEOUT); + + /* Turn on SDMA mode */ + + uint32_t regval; + regval = sam_getreg(priv, SAMA5_SDMMC_PROCTL_OFFSET); + regval &= ~SDMMC_PROCTL_DMAS_MASK; + sam_putreg(priv, regval, SAMA5_SDMMC_PROCTL_OFFSET); + + /* Configure the RX DMA */ + + sam_configxfrints(priv, SDMMC_DMADONE_INTS | SDMMC_INT_DINT); + uint32_t irqsigen; + irqsigen = sam_getreg(priv, SAMA5_SDMMC_IRQSIGEN_OFFSET); + sam_putreg(priv, irqsigen | SDMMC_INT_DINT, SAMA5_SDMMC_IRQSIGEN_OFFSET); + + sam_putreg(priv, (uint32_t) buffer, SAMA5_SDMMC_DSADDR_OFFSET); + + /* Sample the register state */ + + sam_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} +#endif + +/**************************************************************************** + * Name: sam_dmasendsetup + * + * Description: + * Setup to perform a write DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For write transfers, this may mean + * flushing the data cache. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - The memory to DMA into + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SDMMC_DMA +static int sam_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); + DEBUGASSERT(((uint32_t) buffer & 3) == 0); + + /* Begin sampling register values */ + + sam_sampleinit(); + sam_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *) buffer; + priv->remaining = buflen; + priv->bufferend = (uint32_t *)(buffer + buflen); + + /* DMA will read from the buffer, so we need to flush the data cache to + * main memory. + */ + + up_flush_dcache((uintptr_t) priv->buffer, (uintptr_t) priv->bufferend); + + /* Then set up the SDIO data path */ + + sam_dataconfig(priv, true, buflen, SDMMC_DTOCV_MAXTIMEOUT); + + /* Turn on SDMA mode */ + + uint32_t regval; + regval = sam_getreg(priv, SAMA5_SDMMC_PROCTL_OFFSET); + regval &= ~SDMMC_PROCTL_DMAS_MASK; + sam_putreg(priv, regval, SAMA5_SDMMC_PROCTL_OFFSET); + + /* Configure the TX DMA */ + + sam_configxfrints(priv, SDMMC_DMADONE_INTS | SDMMC_INT_DINT); + sam_putreg(priv, (uint32_t) buffer, SAMA5_SDMMC_DSADDR_OFFSET); + + /* Sample the register state */ + + sam_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} +#endif + +/**************************************************************************** + * Name: sam_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 sam_callback(void *arg) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)arg; + + /* Is a callback registered? */ + + DEBUGASSERT(priv != NULL); + mcinfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n", priv->callback, + priv->cbarg, priv->cbevents, priv->cdstatus); + + if (priv->callback) + { + /* Yes.. Check for enabled callback events */ + + if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0) + { + /* Media is present. Is the media inserted event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + else + { + /* Media is not present. Is the media eject event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + + /* Perform the callback, disabling further callbacks. Of course, the + * the callback can (and probably should) re-enable callbacks. + */ + + priv->cbevents = 0; + + /* Callbacks cannot be performed in the context of an interrupt + * handler. If we are in an interrupt handler, then queue the callback + * to be performed later on the work thread. + */ + + if (up_interrupt_context()) + { + /* Yes.. queue it */ + + mcinfo("Queuing callback to %p(%p)\n", priv->callback, + priv->cbarg); + 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); + } + } +} + +/**************************************************************************** + * Name: sam_set_uhs_timing + * + * Description: + * Configures the SDMMC UHS mode, needed for speeds above 25MHz. + * See the SAMA5D27 datasheet's SDMMC chapter for more info. + * + * Input Parameters: + * selected_mode + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_set_uhs_timing(FAR struct sam_dev_s *priv, + enum bus_mode selected_mode) +{ + uint16_t reg; + + reg = sam_getreg16(priv, SAMA5_SDMMC_H2CR_OFFSET); + reg &= ~SDMMC_H2CR_UHS_MASK; + + switch (selected_mode) + { + case UHS_SDR50: + case MMC_HS_52: + reg |= SDMMC_H2CR_UHS_SDR50; + break; + case UHS_DDR50: + case MMC_DDR_52: + reg |= SDMMC_H2CR_UHS_DDR50; + break; + case UHS_SDR104: + case MMC_HS_200: + reg |= SDMMC_H2CR_UHS_SDR104; + break; + default: + reg |= SDMMC_H2CR_UHS_SDR12; + } + + sam_putreg16(priv, reg, SAMA5_SDMMC_H2CR_OFFSET); +} + +/**************************************************************************** + * Name: sam_set_clock + * + * Description: + * Called by sam_clock() to set up the SDMMC to match the capabilities + * of the SD Card. + * + * Input Parameters: + * clock - clock frequency in Hertz + * + * Returned Value: + * Error code + * + ****************************************************************************/ + +static int sam_set_clock(FAR struct sam_dev_s *priv, uint32_t clock) +{ + uint16_t timeout; + uint16_t div; + uint16_t clk = 0; + uint32_t clk_mul = 0; + uint32_t max_clk = 0; + uint32_t caps0; + uint32_t caps1; + uint32_t proctl; + + /* Wait max 20 ms */ + + timeout = 200; + while (sam_getreg(priv, SAMA5_SDMMC_PRSSTAT_OFFSET) & + (SDMMC_PRSSTAT_CIHB | SDMMC_PRSSTAT_CDIHB)) + { + if (timeout == 0) + { + mcinfo("%s: Timeout to wait cmd & data inhibit\n", __func__); + return -EBUSY; + } + + timeout--; + usleep(100); + } + + sam_putreg16(priv, 0, SAMA5_SDMMC_SYSCTL_OFFSET); + if (clock == 0) + { + return 0; + } + + /* Is this clock multiplier supported? */ + + uint16_t version = sam_getreg16(priv, SAMA5_SDMMC_HOST_VERSION_OFFSET); + + caps0 = sam_getreg(priv, SAMA5_SDMMC_HTCAPBLT0_OFFSET); + + if (version >= SDMMC_SPEC_3) + { + caps1 = sam_getreg(priv, SAMA5_SDMMC_HTCAPBLT1_OFFSET); + clk_mul = (caps1 & SDMMC_CLOCK_MUL_MASK) >> SDMMC_CLOCK_MUL_SHIFT; + } + + if (version >= SDMMC_SPEC_3) + { + max_clk = (caps0 & SDMMC_CLOCK_V3_BASE_MASK) >> SDMMC_CLOCK_BASE_SHIFT; + } + else + { + max_clk = (caps0 & SDMMC_CLOCK_BASE_MASK) >> SDMMC_CLOCK_BASE_SHIFT; + } + + max_clk *= 1000000; + if (clk_mul) + { + max_clk *= clk_mul; + } + + if (version >= SDMMC_SPEC_3) + { + /* Check if the SDMMC supports Programmable Clock Mode. */ + + if (clk_mul) + { + for (div = 1; div <= 1024; div++) + { + if ((max_clk / div) <= clock) + { + break; + } + } + + /* Set Programmable Clock Mode */ + + clk = SDMMC_SYSCTL_CLKGSEL; + div--; + } + else + { + /* Version 3 divisors must be a multiple of 2. */ + + if (max_clk <= clock) + { + div = 1; + } + else + { + for (div = 2; div < SDMMC_MAX_DIV_SPEC_3; div += 2) + { + if ((max_clk / div) <= clock) + break; + } + } + + div >>= 1; + } + } + + else + { + /* Version 2 divisors must be a power of 2. */ + + for (div = 1; div < SDMMC_MAX_DIV_SPEC_2; div *= 2) + { + if ((max_clk / div) <= clock) + { + break; + } + } + + div >>= 1; + } + + clk |= (div & SDMMC_DIV_MASK) << SDMMC_DIVIDER_SHIFT; + clk |= ((div & SDMMC_DIV_HI_MASK) >> SDMMC_DIV_MASK_LEN) + << SDMMC_DIVIDER_HI_SHIFT; + clk |= SDMMC_SYSCTL_INTCLKEN; + sam_putreg16(priv, clk, SAMA5_SDMMC_SYSCTL_OFFSET); + + /* Wait max 20 ms */ + + timeout = 200; + while (!((clk = sam_getreg16(priv, SAMA5_SDMMC_SYSCTL_OFFSET)) + & SDMMC_SYSCTL_INTCLKS)) + { + if (timeout == 0) + { + mcinfo("%s: Internal clock never stabilised.\n", __func__); + return -EBUSY; + } + + timeout--; + usleep(100); + } + + /* High Speed Mode? */ + + proctl = sam_getreg(priv, SAMA5_SDMMC_PROCTL_OFFSET); + if (clock > SAMA5_SDMMC_BUS_HIGH_SPEED_THRESHOLD) + { + proctl |= SDMMC_PROCTL_HSEN; + } + else + { + proctl &= ~SDMMC_PROCTL_HSEN; + } + + sam_putreg(priv, proctl, SAMA5_SDMMC_PROCTL_OFFSET); + + clk |= SDMMC_SYSCTL_SDCLKEN; + sam_putreg16(priv, clk, SAMA5_SDMMC_SYSCTL_OFFSET); + return 0; +} + +/**************************************************************************** + * Name: sam_power + * + * Description: + * Called by sam_sdmmc_sdio_initialize() to set up the SDMMC power handling + * to send the correct power to the SD Card. + * + * Returned Value: + * Error code + * + ****************************************************************************/ + +static void sam_power(FAR struct sam_dev_s *priv) +{ + uint8_t power = 0; + uint8_t card_power = 0; + + uint32_t voltages = 0; + uint32_t capabilities = sam_getreg(priv, + SAMA5_SDMMC_HTCAPBLT0_OFFSET); + + if (capabilities & SDMMC_HTCAPBLT_VS33) + { + voltages |= MMCSD_VDD_32_33 | MMCSD_VDD_33_34; + } + + if (capabilities & SDMMC_HTCAPBLT_VS30) + { + voltages |= MMCSD_VDD_29_30 | MMCSD_VDD_30_31; + } + + if (capabilities & SDMMC_HTCAPBLT_VS18) + { + voltages |= MMCSD_VDD_19_20; + } + + power = fls(voltages) - 1; + + uint32_t caps0 = sam_getreg(priv, SAMA5_SDMMC_HTCAPBLT0_OFFSET); + if ((voltages & MMCSD_VDD_19_20) && (caps0 & SDMMC_HTCAPBLT_DDR50)) + { + /* if DDR50 mode is available, set voltage to 1.8V + * and configure the SDMMC to use DDR50 mode. + */ + + mcinfo("1.8V and DDR50 available.\n"); + card_power = SDMMC_POWER_180; + } + else + { + switch (1 << power) + { + case MMCSD_VDD_19_20: + card_power = SDMMC_POWER_180; + break; + case MMCSD_VDD_29_30: + case MMCSD_VDD_30_31: + card_power = SDMMC_POWER_300; + break; + case MMCSD_VDD_32_33: + case MMCSD_VDD_33_34: + card_power = SDMMC_POWER_330; + break; + } + } + + if (card_power == 0) + { + sam_putreg8(priv, 0, SAMA5_SDMMC_PWRCTL_OFFSET); + return; + } + + card_power |= SDMMC_POWER_ON; + sam_putreg8(priv, card_power, SAMA5_SDMMC_PWRCTL_OFFSET); + + if (caps0 & SDMMC_HTCAPBLT_DDR50) + { + sam_set_uhs_timing(priv, UHS_DDR50); /* Double data rate, 50Mhz */ + } + else if (caps0 & SDMMC_HTCAPBLT_SDR50) + { + sam_set_uhs_timing(priv, UHS_SDR50); /* Single data rate, 50Mhz */ + } + else if (caps0 & SDMMC_HTCAPBLT_SDR104) + { + sam_set_uhs_timing(priv, UHS_SDR104); /* Single data rate, 100Mhz */ + } +} + +/**************************************************************************** + * Name: sam_set_interrupts + * + * Description: + * Called by sam_sdmmc_sdio_initialize() to set up the SDMMC initial + * interrupts. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int sam_set_interrupts(FAR struct sam_dev_s *priv) +{ + /* Only enable interrupts used by the SDMMC */ + + sam_putreg(priv, SDMMC_INT_DATA_MASK | SDMMC_INT_CMD_MASK, + SAMA5_SDMMC_IRQSTATEN_OFFSET); + + /* Mask all SDMMC interrupt sources */ + + sam_putreg(priv, 0x0, SAMA5_SDMMC_IRQSIGEN_OFFSET); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_sdmmc_sdio_initialize + * + * Description: + * Initialize SDIO for operation. + * + * Input Parameters: + * slotno - Slot to be used + * + * Returned Value: + * A reference to an SDIO interface structure. + * NULL is returned on failures. + * + ****************************************************************************/ + +FAR struct sdio_dev_s *sam_sdmmc_sdio_initialize(int slotno) +{ + mcinfo("slotno: %d\n", slotno); + DEBUGASSERT(slotno < SAM_MAX_SDMMC_DEV_SLOTS); + + struct sam_dev_s *priv = &g_sdmmcdev[slotno]; + + /* Initialize the SDMMC slot structure data structure + * Initialize semaphores + */ + + nxsem_init(&priv->waitsem, 0, 0); + + /* The waitsem semaphore is used for signaling and, hence, should not + * have priority inheritance enabled. + */ + + nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE); + + /* Create a watchdog timer */ + + priv->waitwdog = wd_create(); + DEBUGASSERT(priv->waitwdog); + + switch (priv->addr) + { + case SAM_SDMMC0_VBASE: + priv->base = SAM_SDMMC0_VBASE; + /* Configure pins for 1-bit, 4-bit, or 8-bit wide-bus operation. If + * bus is multiplexed then there is a custom bus configuration utility + * in the scope of the board support package. + */ + +#ifndef CONFIG_SDIO_MUXBUS + +# if defined(CONFIG_SAMA5_SDMMC0) + /* Clocking and CMD pins (all data widths) */ + + sam_configpio(PIO_SDMMC0_DAT0); + sam_configpio(PIO_SDMMC0_CK); + sam_configpio(PIO_SDMMC0_CMD); + +# if ( defined(CONFIG_SAMA5_SDMMC0_WIDTH_D1_D4) || \ + defined(CONFIG_SAMA5_SDMMC0_WIDTH_D1_D8) ) + sam_configpio(PIO_SDMMC0_DAT1); + sam_configpio(PIO_SDMMC0_DAT2); + sam_configpio(PIO_SDMMC0_DAT3); +# endif + +# if defined(CONFIG_SAMA5_SDMMC0_WIDTH_D1_D8) + sam_configpio(PIO_SDMMC0_DAT4); + sam_configpio(PIO_SDMMC0_DAT5); + sam_configpio(PIO_SDMMC0_DAT6); + sam_configpio(PIO_SDMMC0_DAT7); +# endif + +# if defined(CONFIG_MMCSD_HAVE_CARDDETECT) +# if defined(PIO_SDMMC0_CD) + sam_configpio(PIO_SDMMC0_CD); +# else + if (priv->sw_cd_gpio != 0) + { + sam_configpio(priv->sw_cd_gpio); + } +# endif +# endif + + sam_sdmmc0_enableclk(); + break; +# endif + + case SAM_SDMMC1_VBASE: + + /* Clocking and CMD pins (all data widths) */ + + priv->base = SAM_SDMMC1_VBASE; +# if defined(CONFIG_SAMA5_SDMMC1) + sam_configpio(PIO_SDMMC1_DAT0); + sam_configpio(PIO_SDMMC1_CK); + sam_configpio(PIO_SDMMC1_CMD); + +# if defined(CONFIG_SAMA5_SDMMC1_WIDTH_D1_D4) + sam_configpio(PIO_SDMMC1_DAT1); + sam_configpio(PIO_SDMMC1_DAT2); + sam_configpio(PIO_SDMMC1_DAT3); +# endif + +# if defined(CONFIG_MMCSD_HAVE_CARDDETECT) +# if defined(PIO_SDMMC1_CD) + sam_configpio(PIO_SDMMC1_CD); +# else + if (priv->sw_cd_gpio != 0) + { + sam_configpio(priv->sw_cd_gpio); + } +# endif +# endif +# endif + + sam_sdmmc1_enableclk(); + break; + +#endif + + default: + return NULL; + } + + sam_reset(&g_sdmmcdev[slotno].dev); + sam_clock(&g_sdmmcdev[slotno].dev, CLOCK_SDIO_DISABLED); + sam_power(priv); + sam_set_interrupts(priv); + + return &g_sdmmcdev[slotno].dev; +} + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is write protected. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + irqstate_t flags; + + /* Update card status */ + + flags = enter_critical_section(); + if (wrprotect) + { + priv->cdstatus |= SDIO_STATUS_WRPROTECTED; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED; + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_sdmmc_set_sdio_card_isr + * + * Description: + * SDIO card generates interrupt via SDIO_DATA_1 pin. + * Called by board-specific logic to register an ISR for SDIO card. + * + * Input Parameters: + * func - callback function. + * arg - arg to be passed to the function. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_sdmmc_set_sdio_card_isr(FAR struct sdio_dev_s *dev, + int (*func)(void *), void *arg) +{ + irqstate_t flags; + uint16_t regval; + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + + priv->do_sdio_card = func; + priv->do_sdio_arg = arg; + + if (priv->do_sdio_card != NULL) + { + priv->cintints = SDMMC_INT_CINT; + } + else + { + priv->cintints = 0; + } + +#if defined(CONFIG_MMCSD_HAVE_CARDDETECT) + if (priv->sw_cd_gpio == 0) + { + priv->cintints |= SDMMC_INT_CINS | SDMMC_INT_CRM; + } +#endif + + flags = enter_critical_section(); + regval = sam_getreg16(priv, SAMA5_SDMMC_IRQSIGEN_OFFSET); + regval = (regval & ~SDMMC_INT_CINT) | priv->cintints; + sam_putreg16(priv, regval, SAMA5_SDMMC_IRQSIGEN_OFFSET); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- possibly from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Value: + * None + * + * Assumptions: + * May be called from an interrupt handler. + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot) +{ + struct sam_dev_s *priv = (struct sam_dev_s *)dev; + sdio_statset_t cdstatus; + irqstate_t flags; + + /* Update card status. Interrupts are disabled here because if we are + * not called from an interrupt handler, then the following steps must + * still be atomic. + */ + + flags = enter_critical_section(); + cdstatus = priv->cdstatus; + if (cardinslot) + { + priv->cdstatus |= SDIO_STATUS_PRESENT; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_PRESENT; + } + + mcinfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus); + + /* Perform any requested callback if the status has changed */ + + if (cdstatus != priv->cdstatus) + { + sam_callback(priv); + } + + leave_critical_section(flags); +} + +#endif /* CONFIG_SAMA5_SDMMC */ diff --git a/arch/arm/src/sama5/sam_sdmmc.h b/arch/arm/src/sama5/sam_sdmmc.h new file mode 100644 index 00000000000..8f97cba4d17 --- /dev/null +++ b/arch/arm/src/sama5/sam_sdmmc.h @@ -0,0 +1,142 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_sdmmc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_SDMMC_H +#define __ARCH_ARM_SRC_SAMA5_SDMMC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "hardware/sam_sdmmc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_sdmmc_set_sdio_card_isr + * + * Description: + * SDIO card generates interrupt via SDIO_DATA_1 pin. + * Called by board-specific logic to register an ISR for SDIO card. + * + * Input Parameters: + * func - callback function. + * arg - arg to be passed to the function. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_sdmmc_set_sdio_card_isr(FAR struct sdio_dev_s *dev, + int (*func)(void *), void *arg); + +/**************************************************************************** + * Name: sam_sdmmc_sdio_initialize + * + * Description: + * Initialize SDMMC for operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Value: + * A reference to a SDIO interface structure. NULL is returned on + * failures. + * + ****************************************************************************/ + +FAR struct sdio_dev_s *sam_sdmmc_sdio_initialize(int slotno); + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- possibly from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot); + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is writeprotected. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect); + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMA5_SDMMC_H */ diff --git a/arch/arm/src/sama5/sam_tc.h b/arch/arm/src/sama5/sam_tc.h index a44e1ab6e36..b706a93c51b 100644 --- a/arch/arm/src/sama5/sam_tc.h +++ b/arch/arm/src/sama5/sam_tc.h @@ -85,6 +85,7 @@ /**************************************************************************** * Public Types ****************************************************************************/ + /* An opaque handle used to represent a timer channel */ typedef void *TC_HANDLE; @@ -216,7 +217,7 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler, * Name: sam_tc_getpending * * Description: - * Return the current contents of the interrutp status register, clearing + * Return the current contents of the interrupt status register, clearing * all pending interrupts. * * Input Parameters: diff --git a/arch/arm/src/sama5/sama5d2x_pio.c b/arch/arm/src/sama5/sama5d2x_pio.c index 4b65659b0eb..eb9405e9a57 100644 --- a/arch/arm/src/sama5/sama5d2x_pio.c +++ b/arch/arm/src/sama5/sama5d2x_pio.c @@ -14,7 +14,7 @@ * 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. @@ -110,6 +110,7 @@ const uintptr_t g_spiobase[SAM_NPIO] = /**************************************************************************** * Private Data ****************************************************************************/ + /* Maps a port number to the standard port character */ #if defined(CONFIG_DEBUG_GPIO_INFO) && SAM_NPIO > 0 @@ -131,6 +132,7 @@ static const char g_portchar[SAM_NPIO] = /**************************************************************************** * Private Function Prototypes ****************************************************************************/ + /**************************************************************************** * Name: sam_issecure * @@ -411,7 +413,9 @@ int sam_configpio(pio_pinset_t cfgset) irqstate_t flags; int ret; - /* Get the base address and pin mask associated with this pin configuration */ + /* Get the base address and pin mask associated with this pin + * configuration + */ base = sam_piobase(cfgset); if (base == 0) @@ -464,7 +468,10 @@ int sam_configpio(pio_pinset_t cfgset) { putreg32(pin, base + SAM_SPIO_SIONR_OFFSET); } - /* Set the mask register to modify only the specific pin being configured. */ + + /* Set the mask register to modify only the specific pin being + * configured. + */ putreg32(pin, base + SAM_PIO_MSKR_OFFSET); @@ -485,7 +492,9 @@ int sam_configpio(pio_pinset_t cfgset) break; case PIO_ANALOG: + /* REVISIT */ + ret = OK; break; @@ -581,26 +590,28 @@ bool sam_pioread(pio_pinset_t pinset) return 0; } -/************************************************************************************ +/**************************************************************************** * Name: sam_pio_forceclk * * Description: - * Enable PIO clocking. Needed only for SAMA5D3/D4 compatibility. For the SAMA5D2, - * there is a common clock for all PIO ports and that clock is always enabled. + * Enable PIO clocking. Needed only for SAMA5D3/D4 compatibility. For the + * SAMA5D2, there is a common clock for all PIO ports and that clock is + * always enabled. * - ************************************************************************************/ + ****************************************************************************/ void sam_pio_forceclk(pio_pinset_t pinset, bool enable) { } -/************************************************************************************ +/**************************************************************************** * Function: sam_dumppio * * Description: - * Dump all PIO registers associated with the base address of the provided pinset. + * Dump all PIO registers associated with the base address of the provided + * pinset. * - ************************************************************************************/ + ****************************************************************************/ #ifdef CONFIG_DEBUG_GPIO_INFO int sam_dumppio(uint32_t pinset, const char *msg) @@ -632,17 +643,22 @@ int sam_dumppio(uint32_t pinset, const char *msg) } gpioinfo(" MSKR: %08x CFGR: %08x PDSR: %08x LOCKSR: %08x\n", - getreg32(base + SAM_PIO_MSKR_OFFSET), getreg32(base + SAM_PIO_CFGR_OFFSET), - getreg32(base + SAM_PIO_PDSR_OFFSET), getreg32(base + SAM_PIO_LOCKSR_OFFSET)); + getreg32(base + SAM_PIO_MSKR_OFFSET), + getreg32(base + SAM_PIO_CFGR_OFFSET), + getreg32(base + SAM_PIO_PDSR_OFFSET), + getreg32(base + SAM_PIO_LOCKSR_OFFSET)); gpioinfo(" ODSR: %08x IMR: %08x ISR: %08x\n", - getreg32(base + SAM_PIO_ODSR_OFFSET), getreg32(base + SAM_PIO_IMR_OFFSET), + getreg32(base + SAM_PIO_ODSR_OFFSET), + getreg32(base + SAM_PIO_IMR_OFFSET), getreg32(base + SAM_PIO_ISR_OFFSET)); if (secure) { gpioinfo(" SCDR: %08x WPMR: %08x WPSR: %08x IOSSR: %08x\n", - getreg32(SAM_SPIO_SCDR), getreg32(SAM_SPIO_WPMR), - getreg32(SAM_SPIO_WPSR), getreg32(base + SAM_SPIO_IOSSR_OFFSET)); + getreg32(SAM_SPIO_SCDR), + getreg32(SAM_SPIO_WPMR), + getreg32(SAM_SPIO_WPSR), + getreg32(base + SAM_SPIO_IOSSR_OFFSET)); } else { diff --git a/boards/arm/sama5/sama5d2-xult/Kconfig b/boards/arm/sama5/sama5d2-xult/Kconfig index 1a079e4e870..5ce561f1e0a 100644 --- a/boards/arm/sama5/sama5d2-xult/Kconfig +++ b/boards/arm/sama5/sama5d2-xult/Kconfig @@ -34,4 +34,98 @@ config SAMA5D2XULT_USBHOST_PRIO default 100 depends on USBHOST +config SAMA5_SDMMC + bool "enable SDMMC controller" + default y if SAMA5_HAVE_SDMMC + select SDIO_DMA + select SCHED_WORKQUEUE + select SCHED_HPWORK + select MMCSD + select MMCSD_SDIO + select SDIO_BLOCKSETUP + select ARCH_HAVE_SDIO + select SAMA5_SDMMC_DMA + ---help--- + Enable SD Card interface SDMMC0. Selects SAMA5_SDMMC SAMA5_SDMMC0 SAMA5_SDMMC_DMA SDIO_DMA SCHED_WORKQUEUE SCHED_HPWORK SDIO_BLOCKSETUP + +config SAMA5_SDMMC0 + bool "Enable SDMMC0 (built-in eMMC)" + default n if SAMA5_SDMMC + depends on SAMA5_SDMMC + +config SAMA5_SDMMC0_SIZE + int "SDMMC0 size in bytes" + default 4294967296 + depends on SAMA5_SDMMC0 + ---help--- + Size of eMMC flash in bytes. Default: 4GB + +config SAMA5D27_SDMMC0_MOUNT + bool "Mount SDMMC0 at startup" + default n + depends on SAMA5_SDMMC0 + +config SAMA5D27_SDMMC0_MOUNT_BLKDEV + string "SDMMC0 block device name" + default mmc0 + depends on SAMA5_SDMMC0 + +config SAMA5D27_SDMMC0_MOUNT_MOUNTPOINT + string "SDMMC0 mountpoint" + default "/mnt/sdmmc0" + depends on SAMA5_SDMMC0 + +config SAMA5D27_SDMMC0_MOUNT_FSTYPE + string "SDMMC0 file system type" + default "vfat" + depends on SAMA5_SDMMC0 + +config CONFIG_SAMA5_SDMMC1_WIDTH_D1_D8 + bool "SDMMC0 data bus width 8 bits" + default y + depends on SAMA5_SDMMC0 + +config SAMA5_SDMMC1 + bool "Enable SDMMC1" + default y if SAMA5_SDMMC + select SAMA5_SDMMC1_WIDTH_D1_D4 + depends on SAMA5_SDMMC + +config SAMA5_SDMMC1_SIZE + int "SDMMC1 size in bytes" + default 1073741824 + depends on SAMA5_SDMMC1 + ---help--- + Size of SD Card in bytes. Default: 16GB + +config SAMA5D27_SDMMC1_MOUNT + bool "Mount SDMMC1 at startup" + default n + depends on SAMA5_SDMMC1 + +config SAMA5D27_SDMMC1_MOUNT_BLKDEV + string "SDMMC1 block device name" + default mmc1 + depends on SAMA5_SDMMC1 + +config SAMA5D27_SDMMC1_MOUNT_MOUNTPOINT + string "SDMMC1 mountpoint" + default "/mnt/sdmmc1" + depends on SAMA5_SDMMC1 + +config SAMA5D27_SDMMC1_MOUNT_FSTYPE + string "SDMMC1 file system type" + default "vfat" + depends on SAMA5_SDMMC1 + +config MMCSD_HAVE_CARDDETECT + bool "SDMMC1 card detect" + default y + depends on SAMA5_SDMMC1 + +config SAMA5_SDMMC1_WIDTH_D1_D4 + bool "SDMMC1 data bus width 4 bits" + default y + depends on SAMA5_SDMMC1 + endif # ARCH_BOARD_SAMA5D2_XULT diff --git a/boards/arm/sama5/sama5d2-xult/README.txt b/boards/arm/sama5/sama5d2-xult/README.txt index 0104d9dbcdb..d293bdd9768 100644 --- a/boards/arm/sama5/sama5d2-xult/README.txt +++ b/boards/arm/sama5/sama5d2-xult/README.txt @@ -780,8 +780,10 @@ Configurations netnsh: - This is a network enabled configuration based on the NuttShell (NSH) - REVISIT: More to be provided. + This is a network enabled configuration based on the NuttShell (NSH). + The CDC-ECM driver is enabled, so you can plug a USB cable into the + USB-Micro port (USB-A) and the board will appear as an CDC-ECM + ethernet adapter. nsh: @@ -952,3 +954,10 @@ Configurations NOTE: If you want the RTC to preserve time over power cycles, you will need to install a battery in the battery holder (J12) and close the jumper, JP13. + + sdmmcnsh: + + This is a configuration based on the NuttShell (NSH). The SDMMC + peripheral is enabled, and can read and write to a VFAT filesystem + on the SD Card. + diff --git a/boards/arm/sama5/sama5d2-xult/configs/sdmmcnsh/defconfig b/boards/arm/sama5/sama5d2-xult/configs/sdmmcnsh/defconfig new file mode 100644 index 00000000000..e20fd947f6a --- /dev/null +++ b/boards/arm/sama5/sama5d2-xult/configs/sdmmcnsh/defconfig @@ -0,0 +1,147 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_AUDIO_FORMAT_MP3 is not set +# CONFIG_DISABLE_OS_API is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +# CONFIG_NSH_CMDPARMS is not set +# CONFIG_SAMA5_UART0 is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="sama5d2-xult" +CONFIG_ARCH_BOARD_SAMA5D2_XULT=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="sama5" +CONFIG_ARCH_CHIP_ATSAMA5D27=y +CONFIG_ARCH_CHIP_SAMA5=y +CONFIG_ARCH_CHIP_SAMA5D2=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_IRQBUTTONS=y +CONFIG_ARCH_LOWVECTORS=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_AUDIO=y +CONFIG_AUDIO_NUM_BUFFERS=8 +CONFIG_BOARDCTL_ROMDISK=y +CONFIG_BOARD_LOOPSPERMSEC=65775 +CONFIG_BOOT_RUNFROMSDRAM=y +CONFIG_BUILTIN=y +CONFIG_CONSOLE_SYSLOG=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEV_LOOP=y +CONFIG_DEV_ZERO=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_TCPBLASTER=y +CONFIG_EXAMPLES_TCPECHO=y +CONFIG_EXAMPLES_WGET=y +CONFIG_EXAMPLES_WGET_URL="http://10.0.0.1/" +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FS_AUTOMOUNTER=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_EXCLUDE_VERSION=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_HIDKBD_POLLUSEC=80000 +CONFIG_I2S=y +CONFIG_INTELHEX_BINARY=y +CONFIG_IOB_NBUFFERS=72 +CONFIG_IOB_THROTTLE=16 +CONFIG_MAX_TASKS=16 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_NETDB_BUFSIZE=128 +CONFIG_NETINIT_NETLOCAL=y +CONFIG_NETINIT_NOMAC=y +CONFIG_NETUTILS_TELNETC=y +CONFIG_NETUTILS_WEBCLIENT=y +CONFIG_NET_ARP_IPIN=y +CONFIG_NET_ARP_SEND=y +CONFIG_NET_BROADCAST=y +CONFIG_NET_CDCECM=y +CONFIG_NET_ETH_PKTSIZE=1514 +CONFIG_NET_ICMP=y +CONFIG_NET_ICMP_SOCKET=y +CONFIG_NET_ROUTE=y +CONFIG_NET_STATISTICS=y +CONFIG_NET_TCP=y +CONFIG_NET_TCPBACKLOG=y +CONFIG_NET_TCP_NOTIFIER=y +CONFIG_NET_TCP_WRITE_BUFFERS=y +CONFIG_NET_UDP=y +CONFIG_NFILE_DESCRIPTORS=8 +CONFIG_NFILE_STREAMS=8 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_PROMPT_STRING="nsh> " +CONFIG_NSH_QUOTE=y +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_MQ_MSGS=4 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PREALLOC_WDOGS=16 +CONFIG_RAM_SIZE=268435456 +CONFIG_RAM_START=0x20000000 +CONFIG_RAM_VSTART=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_CMD_HISTORY_LEN=100 +CONFIG_READLINE_CMD_HISTORY_LINELEN=120 +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_RR_INTERVAL=200 +CONFIG_SAMA5D27_SDMMC0_MOUNT_BLKDEV="/dev/mmcsd0" +CONFIG_SAMA5D27_SDMMC0_MOUNT_MOUNTPOINT="/mnt/mmcsd0" +CONFIG_SAMA5D27_SDMMC1_MOUNT=y +CONFIG_SAMA5D27_SDMMC1_MOUNT_BLKDEV="/dev/mmcsd1" +CONFIG_SAMA5D27_SDMMC1_MOUNT_MOUNTPOINT="/mnt/mmcsd1" +CONFIG_SAMA5D2XULT_528MHZ=y +CONFIG_SAMA5D2XULT_USBHOST_STACKSIZE=2048 +CONFIG_SAMA5_BOOT_SDRAM=y +CONFIG_SAMA5_DDRCS_HEAP_END=0x2fa00000 +CONFIG_SAMA5_DDRCS_RESERVE=y +CONFIG_SAMA5_EHCI=y +CONFIG_SAMA5_HSMC=y +CONFIG_SAMA5_OHCI=y +CONFIG_SAMA5_PIOA_IRQ=y +CONFIG_SAMA5_PIOB_IRQ=y +CONFIG_SAMA5_PIOC_IRQ=y +CONFIG_SAMA5_PIO_IRQ=y +CONFIG_SAMA5_RTC=y +CONFIG_SAMA5_SDMMC0=y +CONFIG_SAMA5_SDMMC0_SIZE=1073741824 +CONFIG_SAMA5_SDMMC=y +CONFIG_SAMA5_TRNG=y +CONFIG_SAMA5_UART1=y +CONFIG_SAMA5_UDPHS=y +CONFIG_SAMA5_UHPHS=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SCHED_LPNTHREADS=2 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_SERIAL_TERMIOS=y +CONFIG_SIG_DEFAULT=y +CONFIG_SIG_EVTHREAD=y +CONFIG_START_DAY=31 +CONFIG_START_MONTH=7 +CONFIG_START_YEAR=2014 +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSLOG_TIMESTAMP=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_PING=y +CONFIG_SYSTEM_VI=y +CONFIG_TTY_SIGINT=y +CONFIG_TTY_SIGSTP=y +CONFIG_UART1_SERIAL_CONSOLE=y +CONFIG_USBDEV=y +CONFIG_USBDEV_DMA=y +CONFIG_USBDEV_DUALSPEED=y +CONFIG_USBDEV_TRACE=y +CONFIG_USBDEV_TRACE_NRECORDS=512 +CONFIG_USBHOST_HIDKBD=y +CONFIG_USBHOST_MSC=y +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WDOG_INTRESERVE=2 diff --git a/boards/arm/sama5/sama5d2-xult/include/board.h b/boards/arm/sama5/sama5d2-xult/include/board.h index 7ab198fa35b..6ef8978dd54 100644 --- a/boards/arm/sama5/sama5d2-xult/include/board.h +++ b/boards/arm/sama5/sama5d2-xult/include/board.h @@ -65,8 +65,8 @@ #if defined(CONFIG_SAMA5_BOOT_SDRAM) /* When booting from SDRAM, NuttX is loaded in SDRAM by an intermediate * bootloader. - * That bootloader had to have already configured the PLL and SDRAM for proper - * operation. + * That bootloader had to have already configured the PLL and SDRAM for + * proper operation. * * In this case, we don not reconfigure the clocking. * Rather, we need to query the register settings to determine the clock @@ -317,6 +317,24 @@ * ---- ------- ---- -------- */ +/* SDIO - Used for both Port 0 & 1 ******************************************/ + +/* 386 KHz for initial inquiry stuff */ + +#define BOARD_SDMMC_IDMODE_PRESCALER SDMMC_SYSCTL_SDCLKFS_DIV256 +#define BOARD_SDMMC_IDMODE_DIVISOR SDMMC_SYSCTL_DVS_DIV(2) + +/* 24.8MHz for other modes */ + +#define BOARD_SDMMC_MMCMODE_PRESCALER SDMMC_SYSCTL_SDCLKFS_DIV8 +#define BOARD_SDMMC_MMCMODE_DIVISOR SDMMC_SYSCTL_DVS_DIV(1) + +#define BOARD_SDMMC_SD1MODE_PRESCALER SDMMC_SYSCTL_SDCLKFS_DIV8 +#define BOARD_SDMMC_SD1MODE_DIVISOR SDMMC_SYSCTL_DVS_DIV(1) + +#define BOARD_SDMMC_SD4MODE_PRESCALER SDMMC_SYSCTL_SDCLKFS_DIV8 +#define BOARD_SDMMC_SD4MODE_DIVISOR SDMMC_SYSCTL_DVS_DIV(1) + /**************************************************************************** * Assembly Language Macros ****************************************************************************/ diff --git a/boards/arm/sama5/sama5d2-xult/src/Makefile b/boards/arm/sama5/sama5d2-xult/src/Makefile index dc30110684b..8ed19b5553c 100644 --- a/boards/arm/sama5/sama5d2-xult/src/Makefile +++ b/boards/arm/sama5/sama5d2-xult/src/Makefile @@ -59,6 +59,14 @@ CSRCS += sam_at25.c endif endif +ifeq ($(CONFIG_SAMA5_SDMMC0),y) +CSRCS += sam_sdmmc.c +else +ifeq ($(CONFIG_SAMA5_SDMMC1),y) +CSRCS += sam_sdmmc.c +endif + +endif ifeq ($(CONFIG_SAMA5_HSMCI0),y) CSRCS += sam_hsmci.c else diff --git a/boards/arm/sama5/sama5d2-xult/src/sam_appinit.c b/boards/arm/sama5/sama5d2-xult/src/sam_appinit.c index b9d518ef8dc..1a04c6ce507 100644 --- a/boards/arm/sama5/sama5d2-xult/src/sam_appinit.c +++ b/boards/arm/sama5/sama5d2-xult/src/sam_appinit.c @@ -23,6 +23,8 @@ ****************************************************************************/ #include +#include +#include #include @@ -68,6 +70,7 @@ int board_app_initialize(uintptr_t arg) #ifndef CONFIG_BOARD_LATE_INITIALIZE /* Perform board initialization */ + mcinfo("Entry\n"); return sam_bringup(); #else return OK; diff --git a/boards/arm/sama5/sama5d2-xult/src/sam_bringup.c b/boards/arm/sama5/sama5d2-xult/src/sam_bringup.c index e961bafa75d..ae0ea3f9e85 100644 --- a/boards/arm/sama5/sama5d2-xult/src/sam_bringup.c +++ b/boards/arm/sama5/sama5d2-xult/src/sam_bringup.c @@ -25,8 +25,10 @@ #include #include +#include #include #include +#include #include #include @@ -34,6 +36,8 @@ #include #include +#include "sama5d2-xult.h" + #ifdef CONFIG_CDCACM # include #endif @@ -51,7 +55,10 @@ # include #endif -#include "sama5d2-xult.h" +#ifdef CONFIG_MMCSD +# include +# include "sam_sdmmc.h" +#endif /**************************************************************************** * Pre-processor Definitions @@ -124,6 +131,102 @@ static void sam_i2ctool(void) # define sam_i2ctool() #endif +/**************************************************************************** + * Name: nsh_sdmmc_initialize + * + * Description: + * Initialize SDMMC drivers + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SDMMC + +static int nsh_sdmmc_initialize(void) +{ + struct sdio_dev_s *sdmmc0; + struct sdio_dev_s *sdmmc1; + int ret = 0; + + /* Get an instance of the SDIO interface */ + +#ifdef CONFIG_SAMA5_SDMMC0 + sdmmc0 = sam_sdmmc_sdio_initialize(SDMMC0_SLOTNO); + if (!sdmmc0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize SD/MMC\n"); + } + else + { + /* Bind the SDIO interface to the MMC/SD driver */ + + ret = mmcsd_slotinitialize(SDMMC0_MINOR, sdmmc0); + if (ret != OK) + { + syslog(LOG_ERR, + "ERROR: Failed to bind SDIO to the MMC/SD driver (slot 0): " + "%d\n", + ret); + } + } + +#ifdef CONFIG_SAMA5D27_SDMMC0_MOUNT + /* Mount the volume on SDMMC0 */ + + ret = mount(CONFIG_SAMA5D27_SDMMC0_MOUNT_BLKDEV, + CONFIG_SAMA5D27_SDMMC0_MOUNT_MOUNTPOINT, + CONFIG_SAMA5D27_SDMMC0_MOUNT_FSTYPE, + 0, NULL); + + if (ret < 0) + { + _err("ERROR: Failed to mount %s: %d\n", + CONFIG_SAMA5D27_SDMMC0_MOUNT_MOUNTPOINT, errno); + } +#endif +#endif + +#ifdef CONFIG_SAMA5_SDMMC1 + sdmmc1 = sam_sdmmc_sdio_initialize(SDMMC1_SLOTNO); + if (!sdmmc1) + { + syslog(LOG_ERR, "ERROR: Failed to initialize SD/MMC\n"); + } + else + { + /* Bind the SDIO interface to the MMC/SD driver */ + + ret = mmcsd_slotinitialize(SDMMC1_MINOR, sdmmc1); + if (ret != OK) + { + syslog(LOG_ERR, + "ERROR: Failed to bind SDIO to the MMC/SD driver (slot 0): " + "%d\n", + ret); + } + } + +#ifdef CONFIG_SAMA5D27_SDMMC1_MOUNT + /* Mount the volume on SDMMC1 */ + + ret = mount(CONFIG_SAMA5D27_SDMMC1_MOUNT_BLKDEV, + CONFIG_SAMA5D27_SDMMC1_MOUNT_MOUNTPOINT, + CONFIG_SAMA5D27_SDMMC1_MOUNT_FSTYPE, + 0, NULL); + + if (ret < 0) + { + _err("ERROR: Failed to mount %s: %d\n", + CONFIG_SAMA5D27_SDMMC1_MOUNT_MOUNTPOINT, errno); + } +#endif +#endif + + return OK; +} +#else +# define nsh_sdmmc_initialize() (OK) +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -144,6 +247,14 @@ int sam_bringup(void) sam_i2ctool(); +#ifdef HAVE_SDMMC +#ifdef CONFIG_SAMA5_SDMMC + /* Initialize SDMCC-based MMC/SD card support */ + + nsh_sdmmc_initialize(); +#endif +#endif + #ifdef HAVE_HSMCI #ifdef CONFIG_SAMA5_HSMCI0 /* Initialize the HSMCI0 driver */ @@ -191,7 +302,7 @@ int sam_bringup(void) #ifdef CONFIG_SAMA5D4EK_HSMCI1_MOUNT else { - /* REVISIT: A delay seems to be required here or the mount will fail. */ + /* REVISIT: A delay seems required here or the mount will fail. */ /* Mount the volume on HSMCI1 */ diff --git a/boards/arm/sama5/sama5d2-xult/src/sam_hsmci.c b/boards/arm/sama5/sama5d2-xult/src/sam_sdmmc.c similarity index 62% rename from boards/arm/sama5/sama5d2-xult/src/sam_hsmci.c rename to boards/arm/sama5/sama5d2-xult/src/sam_sdmmc.c index 4ae952f6105..c82a5aa8e00 100644 --- a/boards/arm/sama5/sama5d2-xult/src/sam_hsmci.c +++ b/boards/arm/sama5/sama5d2-xult/src/sam_sdmmc.c @@ -1,5 +1,5 @@ /**************************************************************************** - * boards/arm/sama5/sama5d2-xult/src/sam_hsmci.c + * boards/arm/sama5/sama5d2-xult/src/sam_sdmmc.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -18,38 +18,37 @@ * ****************************************************************************/ -/* The SAMA5D3-Xplained provides a two SD memory card slots: - * (1) a full size SD card slot (J10), and - * (2) a microSD memory card slot (J11). +/* The SAMA5D2-XULT provides a built-in 4GB eMMC (SDMMC0), and one SD + * full-size memory card slot at J19 (SDMMC1). * - * The full size SD card slot connects via HSMCI0. The card detect discrete - * is available on PB17 (pulled high). The write protect descrete is tied to - * ground (via PP6) and not available to software. The slot supports 8-bit - * wide transfer mode, but the NuttX driver currently uses only the 4-bit - * wide transfer mode + * SDMMC0: There is a Micron 4Gx8 eMMC managed NAND Flash (MTFC4GLDEA-0M WT) + * connected to SDMMC0. * - * PD17 MCI0_CD - * PD1 MCI0_DA0 - * PD2 MCI0_DA1 - * PD3 MCI0_DA2 - * PD4 MCI0_DA3 - * PD5 MCI0_DA4 - * PD6 MCI0_DA5 - * PD7 MCI0_DA6 - * PD8 MCI0_DA7 - * PD9 MCI0_CK - * PD0 MCI0_CDA + * PA1 SDMMC0_CMD + * PA2 SDMMC0_DAT0 + * PA3 SDMMC0_DAT1 + * PA4 SDMMC0_DAT2 + * PA5 SDMMC0_DAT3 + * PA6 SDMMC0_DAT4 + * PA7 SDMMC0_DAT5 + * PA8 SDMMC0_DAT6 + * PA9 SDMMC0_DAT7 + * PA0 SDMMC0_CK + * PA10 SDMMC0_RSTN * - * The microSD connects vi HSMCI1. The card detect discrete is available on - * PB18 (pulled high): + * SDMMC1: The full size SD card slot connects via SDMMC1. The card detect + * discrete is available on PA30 (pulled high). The write protect discrete + * is tied to ground and not available to software. The slot only supports + * 4-bit wide transfer mode. + * + * PA30 SDMMC1_CD + * PA18 SDMMC1_DAT0 + * PA19 SDMMC1_DAT1 + * PA20 SDMMC1_DAT2 + * PA21 SDMMC1_DAT3 + * PD22 SDMMC1_CK + * PA28 SDMMC1_CDA * - * PD18 MCI1_CD - * PB20 MCI1_DA0 - * PB21 MCI1_DA1 - * PB22 MCI1_DA2 - * PB23 MCI1_DA3 - * PB24 MCI1_CK - * PB19 MCI1_CDA */ /**************************************************************************** @@ -66,22 +65,26 @@ #include #include +#include "chip.h" +#include "arm_internal.h" +#include "arm_arch.h" + #include "sam_pio.h" -#include "sam_hsmci.h" +#include "sam_sdmmc.h" -#include "sama5d3-xplained.h" +#include "sama5d2-xult.h" -#ifdef HAVE_HSMCI +#ifdef HAVE_SDMMC /**************************************************************************** * Private Types ****************************************************************************/ -/* This structure holds static information unique to one HSMCI peripheral */ +/* This structure holds static information unique to one SDMMC peripheral */ -struct sam_hsmci_state_s +struct sam_sdmmc_state_s { - struct sdio_dev_s *hsmci; /* R/W device handle */ + struct sdio_dev_s *sdmmc; /* R/W device handle */ pio_pinset_t pincfg; /* Card detect PIO pin configuration */ uint8_t irq; /* Interrupt number (same as pid) */ uint8_t slotno; /* Slot number */ @@ -93,29 +96,29 @@ struct sam_hsmci_state_s * Private Data ****************************************************************************/ -/* HSCMI device state */ +/* SDMMC device state */ -#ifdef CONFIG_SAMA5_HSMCI0 -static int sam_hsmci0_cardetect(int irq, void *regs, FAR void *arg); +#ifdef CONFIG_SAMA5_SDMMC0 +static int sam_sdmmc0_cardetect(int irq, void *regs, FAR void *arg); -static struct sam_hsmci_state_s g_hsmci0 = +static struct sam_sdmmc_state_s g_sdmmc0 = { - .pincfg = PIO_MCI0_CD, - .irq = IRQ_MCI0_CD, + .pincfg = PIO_SDMMC0_CD, + .irq = -1, .slotno = 0, - .handler = sam_hsmci0_cardetect, + .handler = sam_sdmmc0_cardetect, }; #endif -#ifdef CONFIG_SAMA5_HSMCI1 -static int sam_hsmci1_cardetect(int irq, void *regs, FAR void *arg); +#ifdef CONFIG_SAMA5_SDMMC1 +static int sam_sdmmc1_cardetect(int irq, void *regs, FAR void *arg); -static struct sam_hsmci_state_s g_hsmci1 = +static struct sam_sdmmc_state_s g_sdmmc1 = { - .pincfg = PIO_MCI1_CD, - .irq = IRQ_MCI1_CD, + .pincfg = PIO_SDMMC1_CD, + .irq = IRQ_SDMMC1_CD, .slotno = 1, - .handler = sam_hsmci1_cardetect, + .handler = sam_sdmmc1_cardetect, }; #endif @@ -127,11 +130,11 @@ static struct sam_hsmci_state_s g_hsmci1 = * Name: sam_cardinserted_internal * * Description: - * Check if a card is inserted into the selected HSMCI slot + * Check if a card is inserted into the selected SDMMC slot * ****************************************************************************/ -bool sam_cardinserted_internal(struct sam_hsmci_state_s *state) +bool sam_cardinserted_internal(struct sam_sdmmc_state_s *state) { bool inserted; @@ -143,14 +146,14 @@ bool sam_cardinserted_internal(struct sam_hsmci_state_s *state) } /**************************************************************************** - * Name: sam_hsmci_cardetect, sam_hsmci0_cardetect, and sam_hsmci1_cardetect + * Name: sam_sdmmc_cardetect, sam_sdmmc0_cardetect, and sam_sdmmc1_cardetect * * Description: * Card detect interrupt handlers * ****************************************************************************/ -static int sam_hsmci_cardetect(struct sam_hsmci_state_s *state) +static int sam_sdmmc_cardetect(struct sam_sdmmc_state_s *state) { /* Get the current card insertion state */ @@ -160,59 +163,59 @@ static int sam_hsmci_cardetect(struct sam_hsmci_state_s *state) if (cd != state->cd) { - /* Yes... remember that new state and inform the HSMCI driver */ + /* Yes... remember that new state and inform the SDMMC driver */ state->cd = cd; /* Report the new state to the SDIO driver */ - sdio_mediachange(state->hsmci, cd); + sdio_mediachange(state->sdmmc, cd); } return OK; } -#ifdef CONFIG_SAMA5_HSMCI0 -static int sam_hsmci0_cardetect(int irq, void *regs, FAR void *arg) +#ifdef CONFIG_SAMA5_SDMMC0 +static int sam_sdmmc0_cardetect(int irq, void *regs, FAR void *arg) { - return sam_hsmci_cardetect(&g_hsmci0); + return sam_sdmmc_cardetect(&g_sdmmc0); } #endif -#ifdef CONFIG_SAMA5_HSMCI1 -static int sam_hsmci1_cardetect(int irq, void *regs, FAR void *arg) +#ifdef CONFIG_SAMA5_SDMMC1 +static int sam_sdmmc1_cardetect(int irq, void *regs, FAR void *arg) { - return sam_hsmci_cardetect(&g_hsmci1); + return sam_sdmmc_cardetect(&g_sdmmc1); } #endif /**************************************************************************** - * Name: sam_hsmci_state + * Name: sam_sdmmc_state * * Description: - * Initialize HSMCI PIOs. + * Initialize SDMMC PIOs. * ****************************************************************************/ -static struct sam_hsmci_state_s *sam_hsmci_state(int slotno) +static struct sam_sdmmc_state_s *sam_sdmmc_state(int slotno) { - struct sam_hsmci_state_s *state = NULL; + struct sam_sdmmc_state_s *state = NULL; -#ifdef CONFIG_SAMA5_HSMCI0 -#ifdef CONFIG_SAMA5_HSMCI1 +#ifdef CONFIG_SAMA5_SDMMC0 +#ifdef CONFIG_SAMA5_SDMMC1 if (slotno == 0) #endif { - state = &g_hsmci0; + state = &g_sdmmc0; } -#ifdef CONFIG_SAMA5_HSMCI1 +#ifdef CONFIG_SAMA5_SDMMC1 else #endif #endif -#ifdef CONFIG_SAMA5_HSMCI1 +#ifdef CONFIG_SAMA5_SDMMC1 { - state = &g_hsmci1; + state = &g_sdmmc1; } #endif @@ -224,21 +227,24 @@ static struct sam_hsmci_state_s *sam_hsmci_state(int slotno) ****************************************************************************/ /**************************************************************************** - * Name: sam_hsmci_initialize + * Name: sam_sdmmc_initialize * * Description: * Perform architecture specific initialization * ****************************************************************************/ -int sam_hsmci_initialize(int slotno, int minor) +int sam_sdmmc_initialize(int slotno, int minor) { - struct sam_hsmci_state_s *state; + struct sam_sdmmc_state_s *state; int ret; - /* Get the static HSMI description */ + mcinfo("Entry.\n"); + mcinfo("slotno: %d\n", slotno); - state = sam_hsmci_state(slotno); + /* Get the static SDMMC description */ + + state = sam_sdmmc_state(slotno); if (!state) { ferr("ERROR: No state for slotno %d\n", slotno); @@ -253,8 +259,8 @@ int sam_hsmci_initialize(int slotno, int minor) /* First, get an instance of the SDIO interface */ - state->hsmci = sdio_initialize(slotno); - if (!state->hsmci) + state->sdmmc = sam_sdmmc_sdio_initialize(slotno); + if (!state->sdmmc) { ferr("ERROR: Failed to initialize SDIO slot %d\n", slotno); return -ENODEV; @@ -262,7 +268,7 @@ int sam_hsmci_initialize(int slotno, int minor) /* Now bind the SDIO interface to the MMC/SD driver */ - ret = mmcsd_slotinitialize(minor, state->hsmci); + ret = mmcsd_slotinitialize(minor, state->sdmmc); if (ret != OK) { ferr("ERROR: Failed to bind SDIO to the MMC/SD driver: %d\n", ret); @@ -274,10 +280,10 @@ int sam_hsmci_initialize(int slotno, int minor) sam_pioirq(state->pincfg); irq_attach(state->irq, state->handler, NULL); - /* Then inform the HSMCI driver if there is or is not a card in the slot. */ + /* Then inform the SDMMC driver if there is or is not a card in the slot. */ state->cd = sam_cardinserted_internal(state); - sdio_mediachange(state->hsmci, state->cd); + sdio_mediachange(state->sdmmc, state->cd); /* Enable card detect interrupts */ @@ -289,17 +295,17 @@ int sam_hsmci_initialize(int slotno, int minor) * Name: sam_cardinserted * * Description: - * Check if a card is inserted into the selected HSMCI slot + * Check if a card is inserted into the selected SDMMC slot * ****************************************************************************/ bool sam_cardinserted(int slotno) { - struct sam_hsmci_state_s *state; + struct sam_sdmmc_state_s *state; - /* Get the HSMI description */ + /* Get the SDMMC description */ - state = sam_hsmci_state(slotno); + state = sam_sdmmc_state(slotno); if (!state) { ferr("ERROR: No state for slotno %d\n", slotno); @@ -315,7 +321,7 @@ bool sam_cardinserted(int slotno) * Name: sam_writeprotected * * Description: - * Check if a card is inserted into the selected HSMCI slot + * Check if a card is inserted into the selected SDMMC slot * ****************************************************************************/ @@ -326,4 +332,5 @@ bool sam_writeprotected(int slotno) return false; } -#endif /* HAVE_HSMCI */ +#endif /* HAVE_SDMMC */ + diff --git a/boards/arm/sama5/sama5d2-xult/src/sama5d2-xult.h b/boards/arm/sama5/sama5d2-xult/src/sama5d2-xult.h index 43da22882f4..2f9d40a7873 100644 --- a/boards/arm/sama5/sama5d2-xult/src/sama5d2-xult.h +++ b/boards/arm/sama5/sama5d2-xult/src/sama5d2-xult.h @@ -42,7 +42,7 @@ /* Configuration ************************************************************/ -#define HAVE_HSMCI 1 +#define HAVE_SDMMC 1 #define HAVE_AT25 1 #define HAVE_NAND 1 #define HAVE_USBHOST 1 @@ -50,26 +50,26 @@ #define HAVE_USBMONITOR 1 #define HAVE_NETWORK 1 -/* HSMCI */ +/* SDMMC */ /* Can't support MMC/SD if the card interface(s) are not enable */ -#if !defined(CONFIG_SAMA5_HSMCI0) && !defined(CONFIG_SAMA5_HSMCI1) -# undef HAVE_HSMCI +#if !defined(CONFIG_SAMA5_SDMMC) && !defined(CONFIG_SAMA5_SDMMC0) +# undef HAVE_SDMMC #endif /* Can't support MMC/SD features if mountpoints are disabled */ -#if defined(HAVE_HSMCI) && defined(CONFIG_DISABLE_MOUNTPOINT) +#if defined(HAVE_SDMMC) && defined(CONFIG_DISABLE_MOUNTPOINT) # warning Mountpoints disabled. No MMC/SD support -# undef HAVE_HSMCI +# undef HAVE_SDMCC #endif /* We need PIO interrupts on PIOD to support card detect interrupts */ -#if defined(HAVE_HSMCI) && !defined(CONFIG_SAMA5_PIOD_IRQ) -# warning PIOD interrupts not enabled. No MMC/SD support. -# undef HAVE_HSMCI +#if defined(HAVE_SDMMC) && !defined(CONFIG_SAMA5_PIOA_IRQ) +# warning PIOA interrupts not enabled. No MMC/SD support. +# undef HAVE_SDMMC #endif /* NAND FLASH */ @@ -95,27 +95,27 @@ #endif /* If we are going to mount the NAND, then they user must also have told - * us what to do with it by setting one of CONFIG_SAMA5D3XPLAINED_NAND_FTL or - * CONFIG_SAMA5D3XPLAINED_NAND_NXFFS. + * us what to do with it by setting one of CONFIG_SAMA5D2XULT_NAND_FTL or + * CONFIG_SAMA5D2XULT_NAND_NXFFS. */ #ifndef CONFIG_MTD -# undef CONFIG_SAMA5D3XPLAINED_NAND_NXFFS -# undef CONFIG_SAMA5D3XPLAINED_NAND_FTL +# undef CONFIG_SAMA5D2XULT_NAND_NXFFS +# undef CONFIG_SAMA5D2XULT_NAND_FTL #endif #if !defined(CONFIG_FS_NXFFS) || !defined(CONFIG_NXFFS_NAND) -# undef CONFIG_SAMA5D3XPLAINED_NAND_NXFFS +# undef CONFIG_SAMA5D2XULT_NAND_NXFFS #endif -#if !defined(CONFIG_SAMA5D3XPLAINED_NAND_FTL) && !defined(CONFIG_SAMA5D3XPLAINED_NAND_NXFFS) +#if !defined(CONFIG_SAMA5D2XULT_NAND_FTL) && !defined(CONFIG_SAMA5D2XULT_NAND_NXFFS) # undef HAVE_NAND #endif -#if defined(CONFIG_SAMA5D3XPLAINED_NAND_FTL) && defined(CONFIG_SAMA5D3XPLAINED_NAND_NXFFS) -# warning Both CONFIG_SAMA5D3XPLAINED_NAND_FTL and CONFIG_SAMA5D3XPLAINED_NAND_NXFFS are set -# warning Ignoring CONFIG_SAMA5D3XPLAINED_NAND_NXFFS -# undef CONFIG_SAMA5D3XPLAINED_NAND_NXFFS +#if defined(CONFIG_SAMA5D2XULT_NAND_FTL) && defined(CONFIG_SAMA5D2XULT_NAND_NXFFS) +# warning Both CONFIG_SAMA5D2XULT_NAND_FTL and CONFIG_SAMA5D2XULT_NAND_NXFFS are set +# warning Ignoring CONFIG_SAMA5D2XULT_NAND_NXFFS +# undef CONFIG_SAMA5DXULT_NAND_NXFFS #endif /* AT25 Serial FLASH */ @@ -142,14 +142,14 @@ # undef CONFIG_SAMA5D3XPLAINED_AT25_NXFFS #endif -#if !defined(CONFIG_SAMA5D3XPLAINED_AT25_FTL) && !defined(CONFIG_SAMA5D3XPLAINED_AT25_NXFFS) +#if !defined(CONFIG_SAMA5D2XULT_AT25_FTL) && !defined(CONFIG_SAMA5D2XULT_AT25_NXFFS) # undef HAVE_AT25 #endif -#if defined(CONFIG_SAMA5D3XPLAINED_AT25_FTL) && defined(CONFIG_SAMA5D3XPLAINED_AT25_NXFFS) -# warning Both CONFIG_SAMA5D3XPLAINED_AT25_FTL and CONFIG_SAMA5D3XPLAINED_AT25_NXFFS are set -# warning Ignoring CONFIG_SAMA5D3XPLAINED_AT25_NXFFS -# undef CONFIG_SAMA5D3XPLAINED_AT25_NXFFS +#if defined(CONFIG_SAMA5D2XULT_AT25_FTL) && defined(CONFIG_SAMA5D2XULT_AT25_NXFFS) +# warning Both CONFIG_SAMA5D2XULT_AT25_FTL and CONFIG_SAMA5D2XULT_AT25_NXFFS are set +# warning Ignoring CONFIG_SAMA5D2XULT_AT25_NXFFS +# undef CONFIG_SAMA5D2XULT_AT25_NXFFS #endif /* Assign minor device numbers. For example, if we also use MINOR number 0 @@ -178,16 +178,25 @@ # define CONFIG_NSH_MMCSDMINOR 0 #endif -#ifdef HAVE_HSMCI +#ifdef HAVE_SDMMC -# define HSMCI0_SLOTNO 0 -# define HSMCI1_SLOTNO 1 - -# ifdef CONFIG_SAMA5_HSMCI0 -# define HSMCI0_MINOR CONFIG_NSH_MMCSDMINOR -# define HSMCI1_MINOR (CONFIG_NSH_MMCSDMINOR+1) +# if ( defined(CONFIG_SAMA5_SDMMC0) && defined(CONFIG_SAMA5_SDMMC1) ) +# define SDMMC0_SLOTNO 0 +# define SDMMC1_SLOTNO 1 # else -# define HSMCI1_MINOR CONFIG_NSH_MMCSDMINOR +# if ( defined(CONFIG_SAMA5_SDMMC0) ) +# define SDMMC0_SLOTNO 0 +# endif +# if ( defined(CONFIG_SAMA5_SDMMC1) ) +# define SDMMC1_SLOTNO 0 +# endif +# endif + +# ifdef CONFIG_SAMA5_SDMMC0 +# define SDMMC0_MINOR CONFIG_NSH_MMCSDMINOR +# define SDMMC1_MINOR (CONFIG_NSH_MMCSDMINOR+1) +# else +# define SDMMC1_MINOR CONFIG_NSH_MMCSDMINOR # endif #else #endif @@ -300,33 +309,51 @@ PIO_INT_BOTHEDGES | PIO_PORT_PIOB | PIO_PIN6) #define IRQ_BTN_USER SAM_IRQ_PB6 -/* HSMCI Card Slots *********************************************************/ - -/* The SAMA5D2-XULT provides a SD memory card slots: - * a full size SD card slot (J19) +/* SDMMC clocking * - * The full size SD card slot connects via HSMCI0. The card detect discrete - * is available on PD17 (pulled high). The write protect discrete is tied to - * ground (via PP6) and not available to software. The slot supports 8-bit - * wide transfer mode, but the NuttX driver currently uses only the 4-bit - * wide transfer mode + * Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK) + * divided by (2*(CLKDIV+1)). * - * PD17 MCI0_CD - * PD1 MCI0_DA0 - * PD2 MCI0_DA1 - * PD3 MCI0_DA2 - * PD4 MCI0_DA3 - * PD5 MCI0_DA4 - * PD6 MCI0_DA5 - * PD7 MCI0_DA6 - * PD8 MCI0_DA7 - * PD9 MCI0_CK - * PD0 MCI0_CDA + * MCI_SPEED = MCK / (2*(CLKDIV+1)) + * CLKDIV = MCI / MCI_SPEED / 2 - 1 + * + * Where CLKDIV has a range of 0-255. */ -#define PIO_MCI0_CD (PIO_INPUT | PIO_CFG_DEFAULT | PIO_CFG_DEGLITCH | \ - PIO_INT_BOTHEDGES | PIO_PORT_PIOA | PIO_PIN11) -#define IRQ_MCI0_CD SAM_IRQ_PA11 +/* MCK = 96MHz, CLKDIV = 119, MCI_SPEED = 96MHz / 2 * (119+1) = 400 KHz */ + +#define SDMMC_INIT_CLKDIV (119 << SDMMC_MR_CLKDIV_SHIFT) + +/* MCK = 96MHz, CLKDIV = 3, MCI_SPEED = 96MHz / 2 * (3+1) = 12 MHz */ + +#define SDMMC_MMCXFR_CLKDIV (3 << SDMMC_MR_CLKDIV_SHIFT) + +/* MCK = 96MHz, CLKDIV = 1, MCI_SPEED = 96MHz / 2 * (1+1) = 24 MHz */ + +#define SDMMC_SDXFR_CLKDIV (1 << SDMMC_MR_CLKDIV_SHIFT) +#define SDMMC_SDWIDEXFR_CLKDIV SDMMC_SDXFR_CLKDIV + +/* SDMMC Card Slots *********************************************************/ + +/* The SAMA5D2-XULT provides a SD memory card slot: + * a full size SD card slot (J19) + * + * The full size SD card slot connects via SDMMC1. The card detect discrete + * is available on PA30 (pulled high). The write protect discrete is tied to + * ground and not available to software. The slot only supports 4-bit + * wide transfer mode, and the NuttX driver currently uses only the 4-bit + * wide transfer mode. + * + * PA30 SDMMC1_CD + * PA18 SDMMC1_DAT0 + * PA19 SDMMC1_DAT1 + * PA20 SDMMC1_DAT2 + * PA21 SDMMC1_DAT3 + * PA22 SDMMC1_CK + * PA28 SDMMC1_CDA + */ + +#define IRQ_SDMMC1_CD SAM_IRQ_PA30 /* USB Ports ****************************************************************/ @@ -414,6 +441,42 @@ int sam_bringup(void); +/**************************************************************************** + * Name: sam_sdmmc_initialize + * + * Description: + * Initialize and configure one SDMMC slot + * + ****************************************************************************/ + +#ifdef HAVE_SDMMC +int sam_sdmmc_initialize(int slotno, int minor); +#endif + +/**************************************************************************** + * Name: sam_cardinserted + * + * Description: + * Check if a card is inserted into the selected SDMMC slot + * + ****************************************************************************/ + +#ifdef HAVE_SDMMC +bool sam_cardinserted(int slotno); +#endif + +/**************************************************************************** + * Name: sam_writeprotected + * + * Description: + * Check if the card in the MMCSD slot is write protected + * + ****************************************************************************/ + +#ifdef HAVE_HSMCI +bool sam_writeprotected(int slotno); +#endif + /**************************************************************************** * Name: sam_usbinitialize * diff --git a/boards/arm/sama5/sama5d3x-ek/src/sama5d3x-ek.h b/boards/arm/sama5/sama5d3x-ek/src/sama5d3x-ek.h index fd759eddf4f..339c1fd4e12 100644 --- a/boards/arm/sama5/sama5d3x-ek/src/sama5d3x-ek.h +++ b/boards/arm/sama5/sama5d3x-ek/src/sama5d3x-ek.h @@ -171,8 +171,8 @@ /* AT24 Serial EEPROM * - * A AT24C512 Serial EEPPROM was used for tested I2C. There are other I2C/TWI - * devices on-board, but the serial EEPROM is the simplest test. + * A AT24C512 Serial EEPPROM was used for tested I2C. There are other + * I2C/TWI devices on-board, but the serial EEPROM is the simplest test. * * There is, however, no AT24 EEPROM on board the SAMA5D3x-EK: The Serial * EEPROM was mounted on an external adaptor board and connected to the @@ -243,9 +243,9 @@ # define AT24_MINOR _AT24_MINOR #endif -/* MMC/SD minor numbers: The NSH device minor extended is extended to support - * two devices. If CONFIG_NSH_MMCSDMINOR is zero, these will be: /dev/mmcsd0 - * and /dev/mmcsd1. +/* MMC/SD minor numbers: The NSH device minor extended is extended to + * support two devices. If CONFIG_NSH_MMCSDMINOR is zero, these will be: + * /dev/mmcsd0 and /dev/mmcsd1. */ #ifndef CONFIG_NSH_MMCSDMINOR @@ -450,7 +450,7 @@ * (2) a microSD memory card slot (J6 labeled MCI1). * * The full size SD card slot connects via HSMCI0. The card detect discrete - * is available on PB17 (pulled high). The write protect descrete is tied to + * is available on PB17 (pulled high). The write protect discrete is tied to * ground (via PP6) and not available to software. The slot supports 8-bit * wide transfer mode, but the NuttX driver currently uses only the 4-bit * wide transfer mode @@ -602,7 +602,7 @@ * (MICREL KSZ9021/31) operating at 10/100/1000 Mbps. * The board supports RGMII interface mode. The Ethernet interface * consists of 4 pairs of low voltage differential pair signals - * designated from GRX± and GTx± plus control signals for link + * designated from GRX and GTx plus control signals for link * activity indicators. These signals can be used to connect to a * 10/100/1000 BaseT RJ45 connector integrated on the main board. * @@ -688,7 +688,7 @@ #ifndef __ASSEMBLY__ /**************************************************************************** - * Public Functions + * Public Function Prototypes ****************************************************************************/ /**************************************************************************** diff --git a/drivers/README.txt b/drivers/README.txt index 8f22462e39f..150c2306a97 100644 --- a/drivers/README.txt +++ b/drivers/README.txt @@ -14,7 +14,7 @@ Files in this directory dev_null.c and dev_zero.c These files provide the standard /dev/null and /dev/zero devices. See - include/nuttx/drivers/driers.h for prototypes of functions that should + include/nuttx/drivers/drivers.h for prototypes of functions that should be called if you want to register these devices (devnull_register() and devzero_register()).