You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2021/09/18 15:18:14 UTC
[incubator-nuttx] 01/05: mpfs: add emmcsd driver
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 772432e7c36eff619167e17d613ff2075968346e
Author: Eero Nurkkala <ee...@offcode.fi>
AuthorDate: Mon Aug 30 07:56:18 2021 +0300
mpfs: add emmcsd driver
This adds the emmcsd driver for the Polarfire Icicle kit.
The driver has been tested with several SD-cards, such as:
- Kingston 32 GB SDS2 Canvas Select Plus
- Kingston MicroSD Canvas Select Plus
- Sandisk Extreme PRO 32 GB
- Transcend 8 GB MicroSD
The internal eMMC hasn't been tested comprehensively.
Signed-off-by: Eero Nurkkala <ee...@offcode.fi>
---
arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.h | 746 +++++++
arch/risc-v/src/mpfs/mpfs_emmcsd.c | 2966 +++++++++++++++++++++++++++
arch/risc-v/src/mpfs/mpfs_emmcsd.h | 113 +
3 files changed, 3825 insertions(+)
diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.h b/arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.h
new file mode 100755
index 0000000..6cf6cdf
--- /dev/null
+++ b/arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.h
@@ -0,0 +1,746 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.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_RISCV_SRC_MPFS_HARDWARE_MPFS_EMMCSD_H
+#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_EMMCSD_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MPFS_EMMCSD_HRS00_OFFSET 0x00
+#define MPFS_EMMCSD_HRS01_OFFSET 0x04
+#define MPFS_EMMCSD_HRS02_OFFSET 0x08
+#define MPFS_EMMCSD_HRS03_OFFSET 0x0c
+#define MPFS_EMMCSD_HRS04_OFFSET 0x10
+#define MPFS_EMMCSD_HRS06_OFFSET 0x18
+#define MPFS_EMMCSD_HRS07_OFFSET 0x1c
+#define MPFS_EMMCSD_HRS30_OFFSET 0x78
+#define MPFS_EMMCSD_HRS31_OFFSET 0x7c
+#define MPFS_EMMCSD_HRS32_OFFSET 0x80
+#define MPFS_EMMCSD_HRS33_OFFSET 0x84
+#define MPFS_EMMCSD_HRS34_OFFSET 0x88
+#define MPFS_EMMCSD_HRS35_OFFSET 0x8c
+#define MPFS_EMMCSD_HRS36_OFFSET 0x90
+#define MPFS_EMMCSD_HRS37_OFFSET 0x94
+#define MPFS_EMMCSD_HRS38_OFFSET 0x98
+
+#define MPFS_EMMCSD_CRS63_OFFSET 0xfc
+
+#define MPFS_EMMCSD_SRS00_OFFSET 0x200
+#define MPFS_EMMCSD_SRS01_OFFSET 0x204
+#define MPFS_EMMCSD_SRS02_OFFSET 0x208
+#define MPFS_EMMCSD_SRS03_OFFSET 0x20c
+#define MPFS_EMMCSD_SRS04_OFFSET 0x210
+#define MPFS_EMMCSD_SRS05_OFFSET 0x214
+#define MPFS_EMMCSD_SRS06_OFFSET 0x218
+#define MPFS_EMMCSD_SRS07_OFFSET 0x21c
+#define MPFS_EMMCSD_SRS08_OFFSET 0x220
+#define MPFS_EMMCSD_SRS09_OFFSET 0x224
+#define MPFS_EMMCSD_SRS10_OFFSET 0x228
+#define MPFS_EMMCSD_SRS11_OFFSET 0x22c
+#define MPFS_EMMCSD_SRS12_OFFSET 0x230
+#define MPFS_EMMCSD_SRS13_OFFSET 0x234
+#define MPFS_EMMCSD_SRS14_OFFSET 0x238
+#define MPFS_EMMCSD_SRS15_OFFSET 0x23c
+#define MPFS_EMMCSD_SRS16_OFFSET 0x240
+#define MPFS_EMMCSD_SRS17_OFFSET 0x244
+#define MPFS_EMMCSD_SRS18_OFFSET 0x248
+#define MPFS_EMMCSD_SRS19_OFFSET 0x24c
+#define MPFS_EMMCSD_SRS20_OFFSET 0x250
+#define MPFS_EMMCSD_SRS21_OFFSET 0x254
+#define MPFS_EMMCSD_SRS22_OFFSET 0x258
+#define MPFS_EMMCSD_SRS23_OFFSET 0x25c
+
+#define MPFS_EMMCSD_SRS24_OFFSET 0x260
+#define MPFS_EMMCSD_SRS25_OFFSET 0x264
+#define MPFS_EMMCSD_SRS26_OFFSET 0x268
+#define MPFS_EMMCSD_SRS27_OFFSET 0x26c
+#define MPFS_EMMCSD_SRS29_OFFSET 0x274
+
+#define MPFS_EMMCSD_CQRS00_OFFSET 0x400
+#define MPFS_EMMCSD_CQRS01_OFFSET 0x404
+#define MPFS_EMMCSD_CQRS02_OFFSET 0x408
+#define MPFS_EMMCSD_CQRS03_OFFSET 0x40c
+#define MPFS_EMMCSD_CQRS04_OFFSET 0x410
+#define MPFS_EMMCSD_CQRS05_OFFSET 0x414
+#define MPFS_EMMCSD_CQRS06_OFFSET 0x418
+#define MPFS_EMMCSD_CQRS07_OFFSET 0x41c
+#define MPFS_EMMCSD_CQRS08_OFFSET 0x420
+#define MPFS_EMMCSD_CQRS09_OFFSET 0x424
+#define MPFS_EMMCSD_CQRS10_OFFSET 0x428
+#define MPFS_EMMCSD_CQRS11_OFFSET 0x42c
+#define MPFS_EMMCSD_CQRS12_OFFSET 0x430
+#define MPFS_EMMCSD_CQRS13_OFFSET 0x434
+#define MPFS_EMMCSD_CQRS14_OFFSET 0x438
+#define MPFS_EMMCSD_CQRS16_OFFSET 0x440
+#define MPFS_EMMCSD_CQRS17_OFFSET 0x444
+#define MPFS_EMMCSD_CQRS18_OFFSET 0x448
+#define MPFS_EMMCSD_CQRS20_OFFSET 0x450
+#define MPFS_EMMCSD_CQRS21_OFFSET 0x454
+#define MPFS_EMMCSD_CQRS22_OFFSET 0x458
+#define MPFS_EMMCSD_CQRS23_OFFSET 0x45c
+
+/* HRS00 register */
+
+#define MPFS_EMMCSD_HRS00_HWINIT1 (0xff << 24)
+#define MPFS_EMMCSD_HRS00_SAV (0xff << 16)
+#define MPFS_EMMCSD_HRS00_HWINIT0 (0x7fff << 1)
+#define MPFS_EMMCSD_HRS00_SWR (1 << 0)
+
+/* HRS01 register */
+
+#define MPFS_EMMCSD_HRS01_HWINIT0 (0xff << 24)
+#define MPFS_EMMCSD_HRS01_DP (0xffffff << 0)
+
+/* HRS02 register */
+
+#define MPFS_EMMCSD_HRS02_HWINIT1 (0x3fff << 18)
+#define MPFS_EMMCSD_FRS02_OTN (0x3 << 16)
+#define MPFS_EMMCSD_HRS02_HWINIT0 (0xfff << 4)
+#define MPFS_EMMCSD_HRS02_PBL (0xf << 0)
+
+/* HRS03 register */
+
+#define MPFS_EMMCSD_HRS03_AER_IEBS (1 << 19)
+#define MPFS_EMMCSD_HRS03_AER_IEBD (1 << 18)
+#define MPFS_EMMCSD_HRS03_AER_IERS (1 << 17)
+#define MPFS_EMMCSD_HRS03_AER_IERD (1 << 16)
+#define MPFS_EMMCSD_HRS03_AER_SENBS (1 << 11)
+#define MPFS_EMMCSD_HRS03_AER_SENBD (1 << 10)
+#define MPFS_EMMCSD_HRS03_AER_SENRS (1 << 9)
+#define MPFS_EMMCSD_HRS03_AER_SENRD (1 << 8)
+#define MPFS_EMMCSD_HRS03_AER_BS (1 << 3)
+#define MPFS_EMMCSD_HRS03_AER_BD (1 << 2)
+#define MPFS_EMMCSD_HRS03_AER_RS (1 << 1)
+#define MPFS_EMMCSD_HRS03_AER_RD (1 << 0)
+
+/* HRS04 register */
+
+#define MPFS_EMMCSD_HRS04_UIS_ACK (1 << 26)
+#define MPFS_EMMCSD_HRS04_UIS_RD (1 << 25)
+#define MPFS_EMMCSD_HRS04_UIS_WR (1 << 24)
+#define MPFS_EMMCSD_HRS04_UIS_RDATA (0xff << 16)
+#define MPFS_EMMCSD_HRS04_UIS_WDATA (0xff << 8)
+#define MPFS_EMMCSD_HRS04_UIS_ADDR (0x3f << 0)
+
+/* HRS06 register */
+
+#define MPFS_EMMCSD_HRS06_ETR (1 << 15)
+#define MPFS_EMMCSD_HRS06_ETV (0x3f << 8)
+#define MPFS_EMMCSD_HRS06_EMM (0x3 << 0)
+
+/* HRS07 register */
+
+#define MPFS_EMMCSD_HRS07_ODELAY_VAL (0x1f << 16)
+#define MPFS_EMMCSD_HRS07_IDELAY_VAL (0x1f << 0)
+
+/* HRS30 register */
+
+#define MPFS_EMMCSD_HRS30_HS400ESSUP (1 << 1)
+#define MPFS_EMMCSD_HRS30_CQSUP (1 << 0)
+
+/* HRS31 register */
+
+#define MPFS_EMMCSD_HRS31_HOSTCTRLVER (0xfff << 16)
+#define MPFS_EMMCSD_HRS31_HOSTFIXVER (0xff << 0)
+
+/* HRS32 register */
+
+#define MPFS_EMMCSD_HRS32_LOAD (1 << 31)
+#define MPFS_EMMCSD_HRS32_ADDR (0x7fff << 16)
+#define MPFS_EMMCSD_HRS32_DATA (0xffff << 0)
+
+/* HRS33 register */
+
+/* #define MPFS_EMMCSD_HRS33_STAT0 */
+
+/* HRS34 register */
+
+#define MPFS_EMMCSD_HRS33_STAT1 (0xff << 0)
+
+/* HRS35 register */
+
+#define MPFS_EMMCSD_HRS35_TFR (1 << 31)
+#define MPFS_EMMCSD_HRS35_TFV (0x3f << 16)
+#define MPFS_EMMCSD_HRS35_TVAL (0x3f << 0)
+
+/* HRS36 register */
+
+#define MPFS_EMMCSD_HRS36_BOOT_EDE (1 << 5)
+#define MPFS_EMMCSD_HRS36_BOOT_EDC (1 << 4)
+#define MPFS_EMMCSD_HRS36_BOOT_EDT (1 << 3)
+#define MPFS_EMMCSD_HRS36_BOOT_EAI (1 << 2)
+#define MPFS_EMMCSD_HRS36_BOOT_EAT (1 << 1)
+#define MPFS_EMMCSD_HRS36_BOOT_ACT (1 << 0)
+
+/* HRS37 register */
+
+#define MPFS_EMMCSD_HRS37_RGB_COEFF_IFM (0x3f << 0)
+
+/* HRS38 register */
+
+#define MPFS_EMMCSD_HRS38_RGB_COEFF (0x0f << 0)
+
+/* CRS63 register */
+
+#define MPFS_EMMCSD_CRS63_HWINIT1 (0xff << 24)
+#define MPFS_EMMCSD_CRS63_SVN (0xff << 16)
+#define MPFS_EMMCSD_CRS63_HWINIT0 (0xff << 8)
+#define MPFS_EMMCSD_CRS63_ISES (0xff << 0)
+
+/* SRS01 register */
+
+#define MPFS_EMMCSD_SRS01_BCCT (0xff << 16)
+#define MPFS_EMMCSD_SRS01_SDMABB (0x7 << 12)
+#define MPFS_EMMCSD_SRS01_TBS (0xfff << 0)
+
+#define MPFS_EMMCSD_SRS01_DMA_SZ_512KB 0x00007000
+
+/* SRS03 register */
+
+#define MPFS_EMMCSD_SRS03_CIDX (0x3f << 24)
+#define MPFS_EMMCSD_SRS03_CT (0x03 << 22)
+#define MPFS_EMMCSD_SRS03_DPS (1 << 21)
+#define MPFS_EMMCSD_SRS03_CICE (1 << 20)
+#define MPFS_EMMCSD_SRS03_CRCCE (1 << 19)
+#define MPFS_EMMCSD_SRS03_RTS (0x3 << 16)
+#define MPFS_EMMCSD_SRS03_RID (1 << 8)
+#define MPFS_EMMCSD_SRS03_RECE (1 << 7)
+#define MPFS_EMMCSD_SRS03_RECT (1 << 6)
+#define MPFS_EMMCSD_SRS03_MSBS (1 << 5)
+#define MPFS_EMMCSD_SRS03_DTDS (1 << 4)
+#define MPFS_EMMCSD_SRS03_ACE (0x3 << 2)
+#define MPFS_EMMCSD_SRS03_BCE (1 << 1)
+#define MPFS_EMMCSD_SRS03_DMAE (1 << 0)
+
+#define MPFS_EMMCSD_SRS03_NO_RESPONSE (0 << 16)
+#define MPFS_EMMCSD_SRS03_RESP_L136 (0x1 << 16)
+#define MPFS_EMMCSD_SRS03_RESP_L48 (0x2 << 16)
+#define MPFS_EMMCSD_SRS03_RESP_L48B (0x3 << 16)
+
+/* SRS09 register */
+
+#define MPFS_EMMCSD_SRS09_CMDSL (1 << 24)
+#define MPFS_EMMCSD_SRS09_DATSL1 (0xf << 20)
+#define MPFS_EMMCSD_SRS09_WPSL (1 << 19)
+#define MPFS_EMMCSD_SRS09_CDSL (1 << 18)
+#define MPFS_EMMCSD_SRS09_CSS (1 << 17)
+#define MPFS_EMMCSD_SRS09_CI (1 << 16)
+#define MPFS_EMMCSD_SRS09_BRE (1 << 11)
+#define MPFS_EMMCSD_SRS09_BWE (1 << 10)
+#define MPFS_EMMCSD_SRS09_RTA (1 << 9)
+#define MPFS_EMMCSD_SRS09_WTA (1 << 8)
+#define MPFS_EMMCSD_SRS09_DATSL2 (0xf << 4)
+#define MPFS_EMMCSD_SRS09_DLA (1 << 2)
+#define MPFS_EMMCSD_SRS09_CIDAT (1 << 1)
+#define MPFS_EMMCSD_SRS09_CICMD (1 << 0)
+
+/* SRS10 register */
+
+#define MPFS_EMMCSD_SRS10_WORM (1 << 26)
+#define MPFS_EMMCSD_SRS10_WOIS (1 << 25)
+#define MPFS_EMMCSD_SRS10_WOIQ (1 << 24)
+#define MPFS_EMMCSD_SRS10_IBG (1 << 19)
+#define MPFS_EMMCSD_SRS10_RWC (1 << 18)
+#define MPFS_EMMCSD_SRS10_CREQ (1 << 17)
+#define MPFS_EMMCSD_SRS10_SBGR (1 << 16)
+#define MPFS_EMMCSD_SRS10_BVS2 (0x7 << 13)
+#define MPFS_EMMCSD_SRS10_BP2 (1 << 12)
+#define MPFS_EMMCSD_SRS10_BVS (0x7 << 9)
+#define MPFS_EMMCSD_SRS10_BP (1 << 8)
+#define MPFS_EMMCSD_SRS10_CDSS (1 << 7)
+#define MPFS_EMMCSD_SRS10_CDTL (1 << 6)
+#define MPFS_EMMCSD_SRS10_EDTW (1 << 5)
+#define MPFS_EMMCSD_SRS10_DMASEL (0x3 << 3)
+#define MPFS_EMMCSD_SRS10_HSE (1 << 2)
+#define MPFS_EMMCSD_SRS10_DTW (1 << 1)
+#define MPFS_EMMCSD_SRS10_LEDC (1 << 0)
+
+/* SRS11 register */
+
+#define MPFS_EMMCSD_SRS11_SRDAT (1 << 26)
+#define MPFS_EMMCSD_SRS11_SRCMD (1 << 25)
+#define MPFS_EMMCSD_SRS11_SRFA (1 << 24)
+#define MPFS_EMMCSD_SRS11_DTCV (0xf << 16)
+#define MPFS_EMMCSD_SRS11_SDCFSL (0xff << 8)
+#define MPFS_EMMCSD_SRS11_SDCFSH (0x3 << 6)
+#define MPFS_EMMCSD_SRS11_CLKGENSEL (1 << 5) /* Not documented! */
+#define MPFS_EMMCSD_SRS11_SDCE (1 << 2)
+#define MPFS_EMMCSD_SRS11_ICS (1 << 1)
+#define MPFS_EMMCSD_SRS11_ICE (1 << 0)
+
+/* SRS12 register */
+
+#define MPFS_EMMCSD_SRS12_ERSP (1 << 27)
+#define MPFS_EMMCSD_SRS12_EADMA (1 << 25)
+#define MPFS_EMMCSD_SRS12_EAC (1 << 24)
+#define MPFS_EMMCSD_SRS12_ECL (1 << 23)
+#define MPFS_EMMCSD_SRS12_EDEB (1 << 22)
+#define MPFS_EMMCSD_SRS12_EDCRC (1 << 21)
+#define MPFS_EMMCSD_SRS12_EDT (1 << 20)
+#define MPFS_EMMCSD_SRS12_ECI (1 << 19)
+#define MPFS_EMMCSD_SRS12_ECEB (1 << 18)
+#define MPFS_EMMCSD_SRS12_ECCRC (1 << 17)
+#define MPFS_EMMCSD_SRS12_ECT (1 << 16)
+#define MPFS_EMMCSD_SRS12_EINT (1 << 15)
+#define MPFS_EMMCSD_SRS12_CQINT (1 << 14)
+#define MPFS_EMMCSD_SRS12_CINT (1 << 8)
+#define MPFS_EMMCSD_SRS12_CR (1 << 7)
+#define MPFS_EMMCSD_SRS12_CIN (1 << 6)
+#define MPFS_EMMCSD_SRS12_BRR (1 << 5)
+#define MPFS_EMMCSD_SRS12_BWR (1 << 4)
+#define MPFS_EMMCSD_SRS12_DMAINT (1 << 3)
+#define MPFS_EMMCSD_SRS12_BGE (1 << 2)
+#define MPFS_EMMCSD_SRS12_TC (1 << 1)
+#define MPFS_EMMCSD_SRS12_CC (1 << 0)
+
+#define MPFS_EMMCSD_SRS12_ESTAT_MASK (0xFFFF8000u)
+#define MPFS_EMMCSD_SRS12_STAT_CLEAR 0xFFFFFFFFu
+
+/* SRS13 register */
+
+#define MPFS_EMMCSD_SRS13_ERSP_SE (1 << 27)
+#define MPFS_EMMCSD_SRS13_TUNING_ERR_SE (1 << 26)
+#define MPFS_EMMCSD_SRS13_EADMA_SE (1 << 25)
+#define MPFS_EMMCSD_SRS13_EAC_SE (1 << 24)
+#define MPFS_EMMCSD_SRS13_ECL_SE (1 << 23)
+#define MPFS_EMMCSD_SRS13_EDEB_SE (1 << 22)
+#define MPFS_EMMCSD_SRS13_EDCRC_SE (1 << 21)
+#define MPFS_EMMCSD_SRS13_EDT_SE (1 << 20)
+#define MPFS_EMMCSD_SRS13_ECI_SE (1 << 19)
+#define MPFS_EMMCSD_SRS13_ECEB_SE (1 << 18)
+#define MPFS_EMMCSD_SRS13_ECCRC_SE (1 << 17)
+#define MPFS_EMMCSD_SRS13_ECT_SE (1 << 16)
+#define MPFS_EMMCSD_SRS13_CQINT_SE (1 << 14)
+#define MPFS_EMMCSD_SRS13_RT_SE (1 << 12) /* Undocumented */
+#define MPFS_EMMCSD_SRS13_INT_ON_C_SE (1 << 11) /* Undocumented */
+#define MPFS_EMMCSD_SRS13_INT_ON_B_SE (1 << 10) /* Undocumented */
+#define MPFS_EMMCSD_SRS13_INT_ON_A_SE (1 << 9) /* Undocumented */
+#define MPFS_EMMCSD_SRS13_CINT_SE (1 << 8)
+#define MPFS_EMMCSD_SRS13_CR_SE (1 << 7)
+#define MPFS_EMMCSD_SRS13_CIN_SE (1 << 6)
+#define MPFS_EMMCSD_SRS13_BRR_SE (1 << 5)
+#define MPFS_EMMCSD_SRS13_BWR_SE (1 << 4)
+#define MPFS_EMMCSD_SRS13_DMAINT_SE (1 << 3)
+#define MPFS_EMMCSD_SRS13_BGE_SE (1 << 2)
+#define MPFS_EMMCSD_SRS13_TC_SE (1 << 1)
+#define MPFS_EMMCSD_SRS13_CC_SE (1 << 0)
+
+#define MPFS_EMMCSD_SRS13_STATUS_EN (MPFS_EMMCSD_SRS13_ERSP_SE | \
+ MPFS_EMMCSD_SRS13_TUNING_ERR_SE | \
+ MPFS_EMMCSD_SRS13_EADMA_SE | \
+ MPFS_EMMCSD_SRS13_EAC_SE | \
+ MPFS_EMMCSD_SRS13_ECL_SE | \
+ MPFS_EMMCSD_SRS13_EDEB_SE | \
+ MPFS_EMMCSD_SRS13_EDCRC_SE | \
+ MPFS_EMMCSD_SRS13_EDT_SE | \
+ MPFS_EMMCSD_SRS13_ECI_SE | \
+ MPFS_EMMCSD_SRS13_ECEB_SE | \
+ MPFS_EMMCSD_SRS13_ECCRC_SE | \
+ MPFS_EMMCSD_SRS13_ECT_SE | \
+ MPFS_EMMCSD_SRS13_CQINT_SE | \
+ MPFS_EMMCSD_SRS13_RT_SE | \
+ MPFS_EMMCSD_SRS13_INT_ON_C_SE | \
+ MPFS_EMMCSD_SRS13_INT_ON_B_SE | \
+ MPFS_EMMCSD_SRS13_INT_ON_A_SE | \
+ MPFS_EMMCSD_SRS13_CR_SE | \
+ MPFS_EMMCSD_SRS13_CIN_SE | \
+ MPFS_EMMCSD_SRS13_BRR_SE | \
+ MPFS_EMMCSD_SRS13_BWR_SE | \
+ MPFS_EMMCSD_SRS13_DMAINT_SE | \
+ MPFS_EMMCSD_SRS13_BGE_SE | \
+ MPFS_EMMCSD_SRS13_TC_SE | \
+ MPFS_EMMCSD_SRS13_CC_SE)
+
+/* SRS14 register */
+
+#define MPFS_EMMCSD_SRS14_ERSP_IE (1 << 27)
+#define MPFS_EMMCSD_SRS14_EADMA_IE (1 << 25)
+#define MPFS_EMMCSD_SRS14_EAC_IE (1 << 24)
+#define MPFS_EMMCSD_SRS14_ECL_IE (1 << 23)
+#define MPFS_EMMCSD_SRS14_EDEB_IE (1 << 22)
+#define MPFS_EMMCSD_SRS14_EDCRC_IE (1 << 21)
+#define MPFS_EMMCSD_SRS14_EDT_IE (1 << 20)
+#define MPFS_EMMCSD_SRS14_ECI_IE (1 << 19)
+#define MPFS_EMMCSD_SRS14_ECEB_IE (1 << 18)
+#define MPFS_EMMCSD_SRS14_ECCRC_IE (1 << 17)
+#define MPFS_EMMCSD_SRS14_ECT_IE (1 << 16)
+#define MPFS_EMMCSD_SRS14_CQINT_IE (1 << 14)
+#define MPFS_EMMCSD_SRS14_CINT_IE (1 << 8)
+#define MPFS_EMMCSD_SRS14_CR_IE (1 << 7)
+#define MPFS_EMMCSD_SRS14_CIN_IE (1 << 6)
+#define MPFS_EMMCSD_SRS14_BRR_IE (1 << 5)
+#define MPFS_EMMCSD_SRS14_BWR_IE (1 << 4)
+#define MPFS_EMMCSD_SRS14_DMAINT_IE (1 << 3)
+#define MPFS_EMMCSD_SRS14_BGE_IE (1 << 2)
+#define MPFS_EMMCSD_SRS14_TC_IE (1 << 1)
+#define MPFS_EMMCSD_SRS14_CC_IE (1 << 0)
+
+/* SRS15 register */
+
+#define MPFS_EMMCSD_SRS15_PVE (1 << 31)
+#define MPFS_EMMCSD_SRS15_A64B (1 << 29)
+#define MPFS_EMMCSD_SRS15_HV4E (1 << 28)
+#define MPFS_EMMCSD_SRS15_SCS (1 << 23)
+#define MPFS_EMMCSD_SRS15_EXTNG (1 << 22)
+#define MPFS_EMMCSD_SRS15_DSS (0x3 << 20)
+#define MPFS_EMMCSD_SRS15_V18SE (1 << 19)
+#define MPFS_EMMCSD_SRS15_UMS (0x7 << 16)
+#define MPFS_EMMCSD_SRS15_CNIACE (1 << 7)
+#define MPFS_EMMCSD_SRS15_ACRE (1 << 5)
+#define MPFS_EMMCSD_SRS15_ACIE (1 << 4)
+#define MPFS_EMMCSD_SRS15_ACEBE (1 << 3)
+#define MPFS_EMMCSD_SRS15_ACCE (1 << 2)
+#define MPFS_EMMCSD_SRS15_ACTE (1 << 1)
+#define MPFS_EMMCSD_SRS15_ACNE (1 << 0)
+
+/* SRS16 register */
+
+#define MPFS_EMMCSD_SRS16_SLT (0x3 << 30)
+#define MPFS_EMMCSD_SRS16_AIS (1 << 29)
+#define MPFS_EMMCSD_SRS16_A64S (1 << 28)
+#define MPFS_EMMCSD_SRS16_HWINIT1 (1 << 27)
+#define MPFS_EMMCSD_SRS16_VS18 (1 << 26)
+#define MPFS_EMMCSD_SRS16_VS30 (1 << 25)
+#define MPFS_EMMCSD_SRS16_VS33 (1 << 24)
+#define MPFS_EMMCSD_SRS16_SRS (1 << 23)
+#define MPFS_EMMCSD_SRS16_DMAS (1 << 22)
+#define MPFS_EMMCSD_SRS16_HSS (1 << 21)
+#define MPFS_EMMCSD_SRS16_ADMA1S (1 << 20)
+#define MPFS_EMMCSD_SRS16_ADMA2S (1 << 19)
+#define MPFS_EMMCSD_SRS16_EDS8 (1 << 18)
+#define MPFS_EMMCSD_SRS16_MBL (0x3 << 16)
+#define MPFS_EMMCSD_SRS16_BCSDCLK (0xff << 8)
+#define MPFS_EMMCSD_SRS16_TCU (1 << 7)
+#define MPFS_EMMCSD_SRS16_HWINIT0 (1 << 6)
+#define MPFS_EMMCSD_SRS16_TCF (0x3f << 0)
+
+/* SRS17 register */
+
+#define MPFS_EMMCSD_SRS17_HWINIT3 (0x7 << 29)
+#define MPFS_EMMCSD_SRS17_VDD2S (1 << 28)
+#define MPFS_EMMCSD_SRS17_HWINIT2 (0xf << 24)
+#define MPFS_EMMCSD_SRS17_CLKMPR (0xff << 16)
+#define MPFS_EMMCSD_SRS17_RTNGM (0x3 << 14)
+#define MPFS_EMMCSD_SRS17_UTSM50 (1 << 13)
+#define MPFS_EMMCSD_SRS17_HWINIT1 (1 << 12)
+#define MPFS_EMMCSD_SRS17_RTNGCNT (0xf << 8)
+#define MPFS_EMMCSD_SRS17_HWINIT0 (1 << 7)
+#define MPFS_EMMCSD_SRS17_DRVD (1 << 6)
+#define MPFS_EMMCSD_SRS17_DRVC (1 << 5)
+#define MPFS_EMMCSD_SRS17_DRVA (1 << 4)
+#define MPFS_EMMCSD_SRS17_UHSII (1 << 3)
+#define MPFS_EMMCSD_SRS17_DDR50 (1 << 2)
+#define MPFS_EMMCSD_SRS17_SDR104 (1 << 1)
+#define MPFS_EMMCSD_SRS17_SDR50 (1 << 0)
+
+/* SRS18 register */
+
+#define MPFS_EMMCSD_SRS18_HWINIT0 (0xff << 24)
+#define MPFS_EMMCSD_SRS18_MC18 (0xff << 16)
+#define MPFS_EMMCSD_SRS18_MC30 (0xff << 8)
+#define MPFS_EMMCSD_SRS18_MC33 (0xff << 0)
+
+/* SRS19 register */
+
+#define MPFS_EMMCSD_SRS19_HWINIT0 (0xffffff << 8)
+#define MPFS_EMMCSD_SRS19_MC18V2 (0xff << 0)
+
+/* SRS20 register */
+
+#define MPFS_EMMCSD_SRS20_ERESP_FE (1 << 27)
+#define MPFS_EMMCSD_SRS20_ETUNE_FE (1 << 26)
+#define MPFS_EMMCSD_SRS20_EADMA_FE (1 << 25)
+#define MPFS_EMMCSD_SRS20_EAC_FE (1 << 24)
+#define MPFS_EMMCSD_SRS20_ECL_FE (1 << 23)
+#define MPFS_EMMCSD_SRS20_EDEB_FE (1 << 22)
+#define MPFS_EMMCSD_SRS20_EDCRC_FE (1 << 21)
+#define MPFS_EMMCSD_SRS20_EDT_FE (1 << 20)
+#define MPFS_EMMCSD_SRS20_ECI_FE (1 << 19)
+#define MPFS_EMMCSD_SRS20_ECEB_FE (1 << 18)
+#define MPFS_EMMCSD_SRS20_ECCRC_FE (1 << 17)
+#define MPFS_EMMCSD_SRS20_ECT_FE (1 << 16)
+#define MPFS_EMMCSD_SRS20_CNIACE_FE (1 << 7)
+#define MPFS_EMMCSD_SRS20_ACIE_FE (1 << 4)
+#define MPFS_EMMCSD_SRS20_ACEBE_FE (1 << 3)
+#define MPFS_EMMCSD_SRS20_ACCE_FE (1 << 2)
+#define MPFS_EMMCSD_SRS20_ACTE_FE (1 << 1)
+#define MPFS_EMMCSD_SRS20_ACNE_FE (1 << 0)
+
+/* SRS21 register */
+
+#define MPFS_EMMCSD_SRS21_EADMAL (1 << 2)
+#define MPFS_EMMCSD_SRS21_EADMAS (0x3 << 0)
+
+/* SRS24 register */
+
+#define MPFS_EMMCSD_SRS24_DSSPV_31_30 (0x3 << 30)
+#define MPFS_EMMCSD_SRS24_HWINIT1 (0xf << 26)
+#define MPFS_EMMCSD_SRS24_SDCFSPV_25_16 (0x3ff << 16)
+#define MPFS_EMMCSD_SRS24_HWINIT0 (0xffff << 0)
+
+/* SRS25 register */
+
+#define MPFS_EMMCSD_SRS25_DSSPV_31_30 (0x3 << 30)
+#define MPFS_EMMCSD_SRS25_HWINIT1 (0xf << 26)
+#define MPFS_EMMCSD_SRS25_SDCFSPV_25_16 (0x3ff << 16)
+#define MPFS_EMMCSD_SRS25_DSSPV_15_14 (0x3 << 14)
+#define MPFS_EMMCSD_SRS25_HWINIT0 (0xf << 10)
+#define MPFS_EMMCSD_SRS25_SDCFSPV_09_00 (0x3ff << 0)
+
+/* SRS26 register */
+
+#define MPFS_EMMCSD_SRS26_DSSPV_31_30 (0x3 << 30)
+#define MPFS_EMMCSD_SRS26_HWINIT1 (0xf << 26)
+#define MPFS_EMMCSD_SRS26_SDCFSPV_25_16 (0x3ff << 16)
+#define MPFS_EMMCSD_SRS26_DSSPV_15_14 (0x3 << 14)
+#define MPFS_EMMCSD_SRS26_HWINIT0 (0x7 << 11)
+#define MPFS_EMMCSD_SRS26_CGSPV_10 (1 << 10)
+#define MPFS_EMMCSD_SRS26_SDCFSPV_09_00 (0x3ff << 0)
+
+/* SRS27 register */
+
+#define MPFS_EMMCSD_SRS27_DSSPV_31_30 (0x3 << 30)
+#define MPFS_EMMCSD_SRS27_HWINIT1 (0xf << 26)
+#define MPFS_EMMCSD_SRS27_SDCFSPV_25_16 (0x3ff << 16)
+#define MPFS_EMMCSD_SRS27_DSSPV_15_14 (0x3 << 14)
+#define MPFS_EMMCSD_SRS27_HWINIT0 (0xf << 10)
+#define MPFS_EMMCSD_SRS27_SDCFSPV_09_00 (0x3ff << 0)
+
+/* SRS29 register */
+
+#define MPFS_EMMCSD_SRS29_HWINIT1 (0xffff << 16)
+#define MPFS_EMMCSD_SRS29_DSSPV_15_14 (0x3 << 14)
+#define MPFS_EMMCSD_SRS29_HWINIT0 (0xf << 10)
+#define MPFS_EMMCSD_SRS29_SDCFSPV_09_00 (0x3ff << 0)
+
+/* CQRS00 register */
+
+#define MPFS_EMMCSD_CQRS00_CQVN1 (0xf << 8)
+#define MPFS_EMMCSD_CQRS00_CQVN2 (0xf << 4)
+#define MPFS_EMMCSD_CQRS00_CQVN3 (0xf << 0)
+
+/* CQRS01 register */
+
+#define MPFS_EMMCSD_CQRS01_ITCFMUL (0xf << 12)
+#define MPFS_EMMCSD_CQRS01_ITCFVAL (0x3ff << 0)
+
+/* CQRS02 register */
+
+#define MPFS_EMMCSD_CQRS02_CQDCE (1 << 12)
+#define MPFS_EMMCSD_CQRS02_CQTDS (1 << 8)
+#define MPFS_EMMCSD_CQRS02_CQE (1 << 0)
+
+/* CQRS03 register */
+
+#define MPFS_EMMCSD_CQRS03_CQCAT (1 << 8)
+#define MPFS_EMMCSD_CQRS03_CQHLT (1 << 0)
+
+/* CQRS04 register */
+
+#define MPFS_EMMCSD_CQRS04_CQTCL (1 << 3)
+#define MPFS_EMMCSD_CQRS04_CQREDI (1 << 2)
+#define MPFS_EMMCSD_CQRS04_CQTCC (1 << 1)
+#define MPFS_EMMCSD_CQRS04_CQHAC (1 << 0)
+
+/* CQRS05 register */
+
+#define MPFS_EMMCSD_CQRS05_CQTCLST (1 << 3)
+#define MPFS_EMMCSD_CQRS05_CQREDST (1 << 2)
+#define MPFS_EMMCSD_CQRS05_CQTCCST (1 << 1)
+#define MPFS_EMMCSD_CQRS05_CQHACST (1 << 0)
+
+/* CQRS06 register */
+
+#define MPFS_EMMCSD_CQRS06_CQTCLSI (1 << 3)
+#define MPFS_EMMCSD_CQRS06_CQREDSI (1 << 2)
+#define MPFS_EMMCSD_CQRS06_CQTCCSI (1 << 1)
+#define MPFS_EMMCSD_CQRS06_CQHACSI (1 << 0)
+
+/* CQRS07 register */
+
+#define MPFS_EMMCSD_CQRS07_CQICED (1 << 31)
+#define MPFS_EMMCSD_CQRS07_CQICSB (1 << 20)
+#define MPFS_EMMCSD_CQRS07_CQICCTR (1 << 16)
+#define MPFS_EMMCSD_CQRS07_CQICCTHWEN (1 << 15)
+#define MPFS_EMMCSD_CQRS07_CQICCTH (0x1f << 8)
+#define MPFS_EMMCSD_CQRS07_CQICTOVALEN (1 << 7)
+#define MPFS_EMMCSD_CQRS07_CQICTOVAL (0x7f << 0)
+
+/* CQRS10 register */
+
+#define MPFS_EMMCSD_CQRS10_CQTD31 (1 << 31)
+#define MPFS_EMMCSD_CQRS10_CQTD30 (1 << 30)
+#define MPFS_EMMCSD_CQRS10_CQTD29 (1 << 29)
+#define MPFS_EMMCSD_CQRS10_CQTD28 (1 << 28)
+#define MPFS_EMMCSD_CQRS10_CQTD27 (1 << 27)
+#define MPFS_EMMCSD_CQRS10_CQTD26 (1 << 26)
+#define MPFS_EMMCSD_CQRS10_CQTD25 (1 << 25)
+#define MPFS_EMMCSD_CQRS10_CQTD24 (1 << 24)
+#define MPFS_EMMCSD_CQRS10_CQTD23 (1 << 23)
+#define MPFS_EMMCSD_CQRS10_CQTD22 (1 << 22)
+#define MPFS_EMMCSD_CQRS10_CQTD21 (1 << 21)
+#define MPFS_EMMCSD_CQRS10_CQTD20 (1 << 20)
+#define MPFS_EMMCSD_CQRS10_CQTD19 (1 << 19)
+#define MPFS_EMMCSD_CQRS10_CQTD18 (1 << 18)
+#define MPFS_EMMCSD_CQRS10_CQTD17 (1 << 17)
+#define MPFS_EMMCSD_CQRS10_CQTD16 (1 << 16)
+#define MPFS_EMMCSD_CQRS10_CQTD15 (1 << 15)
+#define MPFS_EMMCSD_CQRS10_CQTD14 (1 << 14)
+#define MPFS_EMMCSD_CQRS10_CQTD13 (1 << 13)
+#define MPFS_EMMCSD_CQRS10_CQTD12 (1 << 12)
+#define MPFS_EMMCSD_CQRS10_CQTD11 (1 << 11)
+#define MPFS_EMMCSD_CQRS10_CQTD10 (1 << 10)
+#define MPFS_EMMCSD_CQRS10_CQTD09 (1 << 9)
+#define MPFS_EMMCSD_CQRS10_CQTD08 (1 << 8)
+#define MPFS_EMMCSD_CQRS10_CQTD07 (1 << 7)
+#define MPFS_EMMCSD_CQRS10_CQTD06 (1 << 6)
+#define MPFS_EMMCSD_CQRS10_CQTD05 (1 << 5)
+#define MPFS_EMMCSD_CQRS10_CQTD04 (1 << 4)
+#define MPFS_EMMCSD_CQRS10_CQTD03 (1 << 3)
+#define MPFS_EMMCSD_CQRS10_CQTD02 (1 << 2)
+#define MPFS_EMMCSD_CQRS10_CQTD01 (1 << 1)
+#define MPFS_EMMCSD_CQRS10_CQTD00 (1 << 0)
+
+/* CQRS11 register */
+
+#define MPFS_EMMCSD_CQRS11_CQTCN31 (1 << 31)
+#define MPFS_EMMCSD_CQRS11_CQTCN30 (1 << 30)
+#define MPFS_EMMCSD_CQRS11_CQTCN29 (1 << 29)
+#define MPFS_EMMCSD_CQRS11_CQTCN28 (1 << 28)
+#define MPFS_EMMCSD_CQRS11_CQTCN27 (1 << 27)
+#define MPFS_EMMCSD_CQRS11_CQTCN26 (1 << 26)
+#define MPFS_EMMCSD_CQRS11_CQTCN25 (1 << 25)
+#define MPFS_EMMCSD_CQRS11_CQTCN24 (1 << 24)
+#define MPFS_EMMCSD_CQRS11_CQTCN23 (1 << 23)
+#define MPFS_EMMCSD_CQRS11_CQTCN22 (1 << 22)
+#define MPFS_EMMCSD_CQRS11_CQTCN21 (1 << 21)
+#define MPFS_EMMCSD_CQRS11_CQTCN20 (1 << 20)
+#define MPFS_EMMCSD_CQRS11_CQTCN19 (1 << 19)
+#define MPFS_EMMCSD_CQRS11_CQTCN18 (1 << 18)
+#define MPFS_EMMCSD_CQRS11_CQTCN17 (1 << 17)
+#define MPFS_EMMCSD_CQRS11_CQTCN16 (1 << 16)
+#define MPFS_EMMCSD_CQRS11_CQTCN15 (1 << 15)
+#define MPFS_EMMCSD_CQRS11_CQTCN14 (1 << 14)
+#define MPFS_EMMCSD_CQRS11_CQTCN13 (1 << 13)
+#define MPFS_EMMCSD_CQRS11_CQTCN12 (1 << 12)
+#define MPFS_EMMCSD_CQRS11_CQTCN11 (1 << 11)
+#define MPFS_EMMCSD_CQRS11_CQTCN10 (1 << 10)
+#define MPFS_EMMCSD_CQRS11_CQTCN09 (1 << 9)
+#define MPFS_EMMCSD_CQRS11_CQTCN08 (1 << 8)
+#define MPFS_EMMCSD_CQRS11_CQTCN07 (1 << 7)
+#define MPFS_EMMCSD_CQRS11_CQTCN06 (1 << 6)
+#define MPFS_EMMCSD_CQRS11_CQTCN05 (1 << 5)
+#define MPFS_EMMCSD_CQRS11_CQTCN04 (1 << 4)
+#define MPFS_EMMCSD_CQRS11_CQTCN03 (1 << 3)
+#define MPFS_EMMCSD_CQRS11_CQTCN02 (1 << 2)
+#define MPFS_EMMCSD_CQRS11_CQTCN01 (1 << 1)
+#define MPFS_EMMCSD_CQRS11_CQTCN00 (1 << 0)
+
+/* CQRS13 register */
+
+#define MPFS_EMMCSD_CQRS13_CQDPT31 (1 << 31)
+#define MPFS_EMMCSD_CQRS13_CQDPT30 (1 << 30)
+#define MPFS_EMMCSD_CQRS13_CQDPT29 (1 << 29)
+#define MPFS_EMMCSD_CQRS13_CQDPT28 (1 << 28)
+#define MPFS_EMMCSD_CQRS13_CQDPT27 (1 << 27)
+#define MPFS_EMMCSD_CQRS13_CQDPT26 (1 << 26)
+#define MPFS_EMMCSD_CQRS13_CQDPT25 (1 << 25)
+#define MPFS_EMMCSD_CQRS13_CQDPT24 (1 << 24)
+#define MPFS_EMMCSD_CQRS13_CQDPT23 (1 << 23)
+#define MPFS_EMMCSD_CQRS13_CQDPT22 (1 << 22)
+#define MPFS_EMMCSD_CQRS13_CQDPT21 (1 << 21)
+#define MPFS_EMMCSD_CQRS13_CQDPT20 (1 << 20)
+#define MPFS_EMMCSD_CQRS13_CQDPT19 (1 << 19)
+#define MPFS_EMMCSD_CQRS13_CQDPT18 (1 << 18)
+#define MPFS_EMMCSD_CQRS13_CQDPT17 (1 << 17)
+#define MPFS_EMMCSD_CQRS13_CQDPT16 (1 << 16)
+#define MPFS_EMMCSD_CQRS13_CQDPT15 (1 << 15)
+#define MPFS_EMMCSD_CQRS13_CQDPT14 (1 << 14)
+#define MPFS_EMMCSD_CQRS13_CQDPT13 (1 << 13)
+#define MPFS_EMMCSD_CQRS13_CQDPT12 (1 << 12)
+#define MPFS_EMMCSD_CQRS13_CQDPT11 (1 << 11)
+#define MPFS_EMMCSD_CQRS13_CQDPT10 (1 << 10)
+#define MPFS_EMMCSD_CQRS13_CQDPT09 (1 << 9)
+#define MPFS_EMMCSD_CQRS13_CQDPT08 (1 << 8)
+#define MPFS_EMMCSD_CQRS13_CQDPT07 (1 << 7)
+#define MPFS_EMMCSD_CQRS13_CQDPT06 (1 << 6)
+#define MPFS_EMMCSD_CQRS13_CQDPT05 (1 << 5)
+#define MPFS_EMMCSD_CQRS13_CQDPT04 (1 << 4)
+#define MPFS_EMMCSD_CQRS13_CQDPT03 (1 << 3)
+#define MPFS_EMMCSD_CQRS13_CQDPT02 (1 << 2)
+#define MPFS_EMMCSD_CQRS13_CQDPT01 (1 << 1)
+#define MPFS_EMMCSD_CQRS13_CQDPT00 (1 << 0)
+
+/* CQRS14 register */
+
+#define MPFS_EMMCSD_CQRS14_CQTC31 (1 << 31)
+#define MPFS_EMMCSD_CQRS14_CQTC30 (1 << 30)
+#define MPFS_EMMCSD_CQRS14_CQTC29 (1 << 29)
+#define MPFS_EMMCSD_CQRS14_CQTC28 (1 << 28)
+#define MPFS_EMMCSD_CQRS14_CQTC27 (1 << 27)
+#define MPFS_EMMCSD_CQRS14_CQTC26 (1 << 26)
+#define MPFS_EMMCSD_CQRS14_CQTC25 (1 << 25)
+#define MPFS_EMMCSD_CQRS14_CQTC24 (1 << 24)
+#define MPFS_EMMCSD_CQRS14_CQTC23 (1 << 23)
+#define MPFS_EMMCSD_CQRS14_CQTC22 (1 << 22)
+#define MPFS_EMMCSD_CQRS14_CQTC21 (1 << 21)
+#define MPFS_EMMCSD_CQRS14_CQTC20 (1 << 20)
+#define MPFS_EMMCSD_CQRS14_CQTC19 (1 << 19)
+#define MPFS_EMMCSD_CQRS14_CQTC18 (1 << 18)
+#define MPFS_EMMCSD_CQRS14_CQTC17 (1 << 17)
+#define MPFS_EMMCSD_CQRS14_CQTC16 (1 << 16)
+#define MPFS_EMMCSD_CQRS14_CQTC15 (1 << 15)
+#define MPFS_EMMCSD_CQRS14_CQTC14 (1 << 14)
+#define MPFS_EMMCSD_CQRS14_CQTC13 (1 << 13)
+#define MPFS_EMMCSD_CQRS14_CQTC12 (1 << 12)
+#define MPFS_EMMCSD_CQRS14_CQTC11 (1 << 11)
+#define MPFS_EMMCSD_CQRS14_CQTC10 (1 << 10)
+#define MPFS_EMMCSD_CQRS14_CQTC09 (1 << 9)
+#define MPFS_EMMCSD_CQRS14_CQTC08 (1 << 8)
+#define MPFS_EMMCSD_CQRS14_CQTC07 (1 << 7)
+#define MPFS_EMMCSD_CQRS14_CQTC06 (1 << 6)
+#define MPFS_EMMCSD_CQRS14_CQTC05 (1 << 5)
+#define MPFS_EMMCSD_CQRS14_CQTC04 (1 << 4)
+#define MPFS_EMMCSD_CQRS14_CQTC03 (1 << 3)
+#define MPFS_EMMCSD_CQRS14_CQTC02 (1 << 2)
+#define MPFS_EMMCSD_CQRS14_CQTC01 (1 << 1)
+#define MPFS_EMMCSD_CQRS14_CQTC00 (1 << 0)
+
+/* CQRS16 register */
+
+#define MPFS_EMMCSD_CQRS16_CQSSCBC (0xf << 16)
+#define MPFS_EMMCSD_CQRS16_CQSSCIT (0xffff << 0)
+
+/* CQRS21 register */
+
+#define MPFS_EMMCSD_CQRS21_CQDTEFV (1 << 31)
+#define MPFS_EMMCSD_CQRS21_CQDTETID (0x1f << 24)
+#define MPFS_EMMCSD_CQRS21_CQDTECI (0x3f << 16)
+#define MPFS_EMMCSD_CQRS21_CQRMEFV (1 << 15)
+#define MPFS_EMMCSD_CQRS21_CQRMETID (0x1f << 8)
+#define MPFS_EMMCSD_CQRS21_CQRMECI (0x3f << 0)
+
+/* CQRS22 register */
+
+#define MPFS_EMMCSD_CQRS22_CQLCRI (0x3f << 0)
+
+#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_EMMCSD_H */
diff --git a/arch/risc-v/src/mpfs/mpfs_emmcsd.c b/arch/risc-v/src/mpfs/mpfs_emmcsd.c
new file mode 100755
index 0000000..82a182a
--- /dev/null
+++ b/arch/risc-v/src/mpfs/mpfs_emmcsd.c
@@ -0,0 +1,2966 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_emmcsd.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 <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/wdog.h>
+#include <nuttx/clock.h>
+#include <nuttx/sdio.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/signal.h>
+#include <nuttx/mmcsd.h>
+#include <nuttx/irq.h>
+#include <nuttx/cache.h>
+
+#include <arch/board/board.h>
+
+#include "mpfs_emmcsd.h"
+#include "riscv_arch.h"
+#include "hardware/mpfs_emmcsd.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MPFS_EMMCSD_HRS00 (priv->hw_base + MPFS_EMMCSD_HRS00_OFFSET)
+#define MPFS_EMMCSD_HRS01 (priv->hw_base + MPFS_EMMCSD_HRS01_OFFSET)
+#define MPFS_EMMCSD_HRS04 (priv->hw_base + MPFS_EMMCSD_HRS04_OFFSET)
+#define MPFS_EMMCSD_HRS06 (priv->hw_base + MPFS_EMMCSD_HRS06_OFFSET)
+
+#define MPFS_EMMCSD_SRS01 (priv->hw_base + MPFS_EMMCSD_SRS01_OFFSET)
+#define MPFS_EMMCSD_SRS02 (priv->hw_base + MPFS_EMMCSD_SRS02_OFFSET)
+#define MPFS_EMMCSD_SRS03 (priv->hw_base + MPFS_EMMCSD_SRS03_OFFSET)
+#define MPFS_EMMCSD_SRS04 (priv->hw_base + MPFS_EMMCSD_SRS04_OFFSET)
+#define MPFS_EMMCSD_SRS05 (priv->hw_base + MPFS_EMMCSD_SRS05_OFFSET)
+#define MPFS_EMMCSD_SRS06 (priv->hw_base + MPFS_EMMCSD_SRS06_OFFSET)
+#define MPFS_EMMCSD_SRS07 (priv->hw_base + MPFS_EMMCSD_SRS07_OFFSET)
+#define MPFS_EMMCSD_SRS08 (priv->hw_base + MPFS_EMMCSD_SRS08_OFFSET)
+#define MPFS_EMMCSD_SRS09 (priv->hw_base + MPFS_EMMCSD_SRS09_OFFSET)
+#define MPFS_EMMCSD_SRS10 (priv->hw_base + MPFS_EMMCSD_SRS10_OFFSET)
+#define MPFS_EMMCSD_SRS11 (priv->hw_base + MPFS_EMMCSD_SRS11_OFFSET)
+#define MPFS_EMMCSD_SRS12 (priv->hw_base + MPFS_EMMCSD_SRS12_OFFSET)
+#define MPFS_EMMCSD_SRS13 (priv->hw_base + MPFS_EMMCSD_SRS13_OFFSET)
+#define MPFS_EMMCSD_SRS14 (priv->hw_base + MPFS_EMMCSD_SRS14_OFFSET)
+#define MPFS_EMMCSD_SRS15 (priv->hw_base + MPFS_EMMCSD_SRS15_OFFSET)
+#define MPFS_EMMCSD_SRS16 (priv->hw_base + MPFS_EMMCSD_SRS16_OFFSET)
+#define MPFS_EMMCSD_SRS21 (priv->hw_base + MPFS_EMMCSD_SRS21_OFFSET)
+#define MPFS_EMMCSD_SRS22 (priv->hw_base + MPFS_EMMCSD_SRS22_OFFSET)
+#define MPFS_EMMCSD_SRS23 (priv->hw_base + MPFS_EMMCSD_SRS23_OFFSET)
+
+#define MPFS_SYSREG_SOFT_RESET_CR (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_SOFT_RESET_CR_OFFSET)
+#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET)
+
+#define MPFS_PMPCFG_MMC_0 (MPFS_MPUCFG_BASE + 0x700)
+#define MPFS_PMPCFG_MMC_1 (MPFS_MPUCFG_BASE + 0x708)
+#define MPFS_PMPCFG_MMC_2 (MPFS_MPUCFG_BASE + 0x710)
+#define MPFS_PMPCFG_MMC_3 (MPFS_MPUCFG_BASE + 0x718)
+
+#define MPFS_MMC_CLOCK_400KHZ 400u
+#define MPFS_MMC_CLOCK_12_5MHZ 12500u
+#define MPFS_MMC_CLOCK_25MHZ 25000u
+#define MPFS_MMC_CLOCK_26MHZ 26000u
+#define MPFS_MMC_CLOCK_50MHZ 50000u
+#define MPFS_MMC_CLOCK_100MHZ 100000u
+#define MPFS_MMC_CLOCK_200MHZ 200000u
+
+#define MPFS_EMMCSD_DEBOUNCE_TIME 0x300000u
+#define MPFS_EMMCSD_MODE_LEGACY 0x7u
+
+#define MPFS_EMMCSD_DATA_TIMEOUT 500000
+
+#define MPFS_EMMCSD_SRS10_3_3V_BUS_VOLTAGE (0x7 << 9)
+#define MPFS_EMMCSD_SRS10_3_0V_BUS_VOLTAGE (0x6 << 9)
+#define MPFS_EMMCSD_SRS10_1_8V_BUS_VOLTAGE (0x5 << 9)
+
+#define MPFS_EMMCSD_1_8V_BUS_VOLTAGE 18
+#define MPFS_EMMCSD_3_3V_BUS_VOLTAGE 33
+
+#define MPFS_EMMCSD_INITIALIZED 0x00
+#define MPFS_EMMCSD_NOT_INITIALIZED 0x01
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# error "Callback support requires CONFIG_SCHED_WORKQUEUE"
+#endif
+
+/* High-speed single data rate supports clock frequency up to 52 MHz and data
+ * bus width of 1 bit, 4 bits, and 8 bits.
+ */
+
+#define MPFS_EMMCSD_MODE_SDR 0x2u
+
+/* High speed double data rate supports clock frequency up to 52 MHz and data
+ * bus width of 4 bits and 8 bits.
+ */
+
+#define MPFS_EMMCSD_MODE_DDR 0x3u
+
+/* SDR data sampling supports clock frequency up to 200 MHz and data bus
+ * width of 4 bits and 8 bits.
+ */
+
+#define MPFS_EMMCSD_MODE_HS200 0x4u
+
+/* DDR data sampling supports clock frequency up to 200 MHz and data bus
+ * width of 8 bits.
+ */
+
+#define MPFS_EMMCSD_MODE_HS400 0x5u
+
+/* HS400 mode with Enhanced Strobe */
+
+#define MPFS_EMMCSD_MODE_HS400_ES 0x6u
+
+/* Define the Hardware FIFO size */
+
+#define FIFO_SIZE_IN_BYTES 64
+
+/* Timing */
+
+#define EMMCSD_CMDTIMEOUT (100000)
+#define EMMCSD_LONGTIMEOUT (100000000)
+
+/* Event waiting interrupt mask bits */
+
+#define MPFS_EMMCSD_CARD_INTS (MPFS_EMMCSD_SRS14_CINT_IE | \
+ MPFS_EMMCSD_SRS14_CIN_IE | \
+ MPFS_EMMCSD_SRS14_CR_IE)
+
+#define MPFS_EMMCSD_CMDDONE_STA (MPFS_EMMCSD_SRS12_CC)
+
+#define MPFS_EMMCSD_RESPDONE_STA (MPFS_EMMCSD_SRS12_ECT | \
+ MPFS_EMMCSD_SRS12_ECCRC)
+
+#define MPFS_EMMCSD_XFRDONE_STA (MPFS_EMMCSD_SRS12_TC)
+
+#define MPFS_EMMCSD_CMDDONE_MASK (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_CC_IE)
+
+#define MPFS_EMMCSD_RESPDONE_MASK (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_ECCRC_IE | \
+ MPFS_EMMCSD_SRS14_ECT_IE)
+
+#define MPFS_EMMCSD_XFRDONE_MASK (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_TC_IE)
+
+#define MPFS_EMMCSD_CMDDONE_ICR (MPFS_EMMCSD_SRS12_CC)
+
+#define MPFS_EMMCSD_RESPDONE_ICR (MPFS_EMMCSD_SRS12_ECT | \
+ MPFS_EMMCSD_SRS12_ECCRC)
+
+#define MPFS_EMMCSD_XFRDONE_ICR (MPFS_EMMCSD_SRS12_EDCRC | \
+ MPFS_EMMCSD_SRS12_EDT | \
+ MPFS_EMMCSD_SRS12_TC)
+
+#define MPFS_EMMCSD_WAITALL_ICR (MPFS_EMMCSD_CMDDONE_ICR | \
+ MPFS_EMMCSD_RESPDONE_ICR | \
+ MPFS_EMMCSD_XFRDONE_ICR)
+
+#define MPFS_EMMCSD_RECV_MASKDMA (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_EADMA_IE | \
+ MPFS_EMMCSD_SRS14_DMAINT_IE | \
+ MPFS_EMMCSD_SRS14_EDT_IE | \
+ MPFS_EMMCSD_SRS14_ECT_IE | \
+ MPFS_EMMCSD_SRS14_BRR_IE | \
+ MPFS_EMMCSD_SRS14_TC_IE)
+
+#define MPFS_EMMCSD_RECV_MASK (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_EDT_IE | \
+ MPFS_EMMCSD_SRS14_ECT_IE | \
+ MPFS_EMMCSD_SRS14_BRR_IE | \
+ MPFS_EMMCSD_SRS14_TC_IE)
+
+#define MPFS_EMMCSD_SEND_MASKDMA (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_EADMA_IE | \
+ MPFS_EMMCSD_SRS14_DMAINT_IE | \
+ MPFS_EMMCSD_SRS14_EDT_IE | \
+ MPFS_EMMCSD_SRS14_ECT_IE | \
+ MPFS_EMMCSD_SRS14_BWR_IE | \
+ MPFS_EMMCSD_SRS14_TC_IE)
+
+#define MPFS_EMMCSD_SEND_MASK (MPFS_EMMCSD_CARD_INTS | \
+ MPFS_EMMCSD_SRS14_EDT_IE | \
+ MPFS_EMMCSD_SRS14_ECT_IE | \
+ MPFS_EMMCSD_SRS14_BWR_IE | \
+ MPFS_EMMCSD_SRS14_TC_IE)
+
+/* SD-Card IOMUX */
+
+#define LIBERO_SETTING_IOMUX1_CR_SD 0x00000000UL
+#define LIBERO_SETTING_IOMUX2_CR_SD 0x00000000UL
+#define LIBERO_SETTING_IOMUX6_CR_SD 0x0000001DUL
+#define LIBERO_SETTING_MSSIO_BANK4_CFG_CR_SD 0x00080907UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_0_1_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_2_3_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_4_5_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_6_7_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_8_9_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_10_11_CR_SD 0x08290829UL
+#define LIBERO_SETTING_MSSIO_BANK4_IO_CFG_12_13_CR_SD 0x08290829UL
+
+/* eMMC / SD switch address */
+
+#define SDIO_REGISTER_ADDRESS 0x4f000000
+
+#define MPFS_SYSREG_IOMUX1 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_IOMUX1_CR_OFFSET)
+#define MPFS_SYSREG_IOMUX2 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_IOMUX2_CR_OFFSET)
+#define MPFS_SYSREG_IOMUX6 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_IOMUX6_CR_OFFSET)
+#define MPFS_SYSREG_B4_CFG (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_CFG_CR)
+#define MPFS_SYSREG_B4_0_1 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_0_1_CR_OFFSET)
+#define MPFS_SYSREG_B4_2_3 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_2_3_CR_OFFSET)
+#define MPFS_SYSREG_B4_4_5 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_4_5_CR_OFFSET)
+#define MPFS_SYSREG_B4_6_7 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_6_7_CR_OFFSET)
+#define MPFS_SYSREG_B4_8_9 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_8_9_CR_OFFSET)
+#define MPFS_SYSREG_B4_10_11 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_10_11_CR_OFFSET)
+#define MPFS_SYSREG_4_12_13 (MPFS_SYSREG_BASE + \
+ MPFS_SYSREG_MSSIO_BANK4_IO_CFG_12_13_CR_OFFSET)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure defines the state of the MPFS eMMCSD interface */
+
+struct mpfs_dev_s
+{
+ struct sdio_dev_s dev; /* Standard, base SDIO interface */
+
+ uintptr_t hw_base; /* Base address */
+ int plic_irq; /* PLIC interrupt */
+ bool clk_enabled; /* Clk state */
+
+ /* eMMC / SD and HW parameters */
+
+ bool emmc; /* eMMC or SD */
+ int bus_voltage; /* Bus voltage */
+ int bus_speed; /* Bus speed */
+ bool jumpers_3v3; /* Jumper settings: 1v8 or 3v3 */
+
+ /* Event support */
+
+ sem_t waitsem; /* Implements event waiting */
+ sdio_eventset_t waitevents; /* Set of events to be waited for */
+ uint32_t waitmask; /* Interrupt enables for event waiting */
+ volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
+ struct wdog_s 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 */
+ size_t receivecnt; /* Real count to receive */
+ uint32_t xfrmask; /* Interrupt enables for data transfer */
+
+ bool widebus; /* Required for DMA support */
+ bool onebit; /* true: Only 1-bit transfers are supported */
+
+ /* DMA data transfer support */
+
+ bool polltransfer; /* Indicate a poll transfer, no DMA */
+
+ /* Misc */
+
+ uint32_t blocksize; /* Current block size */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Low-level helper ********************************************************/
+
+#define mpfs_givesem(priv) (nxsem_post(&priv->waitsem))
+
+/* Mutual exclusion */
+
+#if defined(CONFIG_SDIO_MUXBUS)
+static int mpfs_lock(FAR struct sdio_dev_s *dev, bool lock);
+#endif
+
+/* Initialization/setup */
+
+static void mpfs_reset(FAR struct sdio_dev_s *dev);
+static sdio_capset_t mpfs_capabilities(FAR struct sdio_dev_s *dev);
+static sdio_statset_t mpfs_status(FAR struct sdio_dev_s *dev);
+static void mpfs_widebus(FAR struct sdio_dev_s *dev, bool enable);
+static void mpfs_clock(FAR struct sdio_dev_s *dev,
+ enum sdio_clock_e rate);
+static int mpfs_attach(FAR struct sdio_dev_s *dev);
+
+/* Command / Status / Data Transfer */
+
+static int mpfs_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t arg);
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void mpfs_blocksetup(FAR struct sdio_dev_s *dev,
+ unsigned int blocksize, unsigned int nblocks);
+#endif
+static int mpfs_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+ size_t nbytes);
+static int mpfs_sendsetup(FAR struct sdio_dev_s *dev,
+ FAR const uint8_t *buffer, size_t nbytes);
+static int mpfs_cancel(FAR struct sdio_dev_s *dev);
+
+static int mpfs_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
+static int mpfs_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort);
+static int mpfs_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t rlong[4]);
+static int mpfs_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort);
+
+/* Event handler */
+
+static void mpfs_waitenable(FAR struct sdio_dev_s *dev,
+ sdio_eventset_t eventset, uint32_t timeout);
+static sdio_eventset_t mpfs_eventwait(FAR struct sdio_dev_s *dev);
+static void mpfs_callbackenable(FAR struct sdio_dev_s *dev,
+ sdio_eventset_t eventset);
+static int mpfs_registercallback(FAR struct sdio_dev_s *dev,
+ worker_t callback, void *arg);
+
+/* DMA */
+
+#if defined(CONFIG_SDIO_DMA)
+static int mpfs_dmarecvsetup(FAR struct sdio_dev_s *dev,
+ FAR uint8_t *buffer, size_t buflen);
+static int mpfs_dmasendsetup(FAR struct sdio_dev_s *dev,
+ FAR const uint8_t *buffer, size_t buflen);
+#endif
+
+/* Initialization/uninitialization/reset ************************************/
+
+static void mpfs_callback(void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+struct mpfs_dev_s g_emmcsd_dev =
+{
+ .dev =
+ {
+#if defined(CONFIG_SDIO_MUXBUS)
+ .lock = mpfs_lock,
+#endif
+ .reset = mpfs_reset,
+ .capabilities = mpfs_capabilities,
+ .status = mpfs_status,
+ .widebus = mpfs_widebus,
+ .clock = mpfs_clock,
+ .attach = mpfs_attach,
+ .sendcmd = mpfs_sendcmd,
+#ifdef CONFIG_SDIO_BLOCKSETUP
+ .blocksetup = mpfs_blocksetup,
+#endif
+ .recvsetup = mpfs_recvsetup,
+ .sendsetup = mpfs_sendsetup,
+ .cancel = mpfs_cancel,
+ .waitresponse = mpfs_waitresponse,
+ .recv_r1 = mpfs_recvshortcrc,
+ .recv_r2 = mpfs_recvlong,
+ .recv_r3 = mpfs_recvshort,
+ .recv_r4 = mpfs_recvshort,
+ .recv_r5 = mpfs_recvshortcrc,
+ .recv_r6 = mpfs_recvshortcrc,
+ .recv_r7 = mpfs_recvshort,
+ .waitenable = mpfs_waitenable,
+ .eventwait = mpfs_eventwait,
+ .callbackenable = mpfs_callbackenable,
+ .registercallback = mpfs_registercallback,
+#if defined(CONFIG_SDIO_DMA)
+ .dmarecvsetup = mpfs_dmarecvsetup,
+ .dmasendsetup = mpfs_dmasendsetup,
+#endif
+ },
+ .hw_base = MPFS_EMMC_SD_BASE,
+ .plic_irq = MPFS_IRQ_MMC_MAIN,
+ .emmc = false,
+ .bus_voltage = MPFS_EMMCSD_3_3V_BUS_VOLTAGE,
+ .bus_speed = MPFS_EMMCSD_MODE_SDR,
+ .jumpers_3v3 = true,
+ .blocksize = 512,
+ .onebit = false,
+ .polltransfer = true,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mpfs_takesem
+ *
+ * Description:
+ * Take the wait semaphore (handling false alarm wakeups due to the receipt
+ * of signals).
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * Normally OK, but may return -ECANCELED in the rare event that the task
+ * has been canceled.
+ *
+ ****************************************************************************/
+
+static int mpfs_takesem(struct mpfs_dev_s *priv)
+{
+ return nxsem_wait_uninterruptible(&priv->waitsem);
+}
+
+/****************************************************************************
+ * Name: mpfs_reset_lines
+ *
+ * Description:
+ * Resets the DAT and CMD lines and waits until they are cleared.
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_reset_lines(struct mpfs_dev_s *priv)
+{
+ uint32_t retries = EMMCSD_CMDTIMEOUT;
+ uint32_t status;
+
+ /* Software Reset For DAT Line and CMD Line */
+
+ modifyreg32(MPFS_EMMCSD_SRS11, 0, MPFS_EMMCSD_SRS11_SRDAT |
+ MPFS_EMMCSD_SRS11_SRCMD);
+
+ do
+ {
+ status = getreg32(MPFS_EMMCSD_SRS11);
+ }
+ while ((status & (MPFS_EMMCSD_SRS11_SRDAT | MPFS_EMMCSD_SRS11_SRCMD)) &&
+ --retries);
+
+ if (retries == 0)
+ {
+ mcerr("Timeout waiting line resets!\n");
+ }
+}
+
+/****************************************************************************
+ * Name: mpfs_check_lines_busy
+ *
+ * Description:
+ * Verifies the DAT and CMD lines are available, not busy.
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * true if busy, false if available
+ *
+ ****************************************************************************/
+
+static bool mpfs_check_lines_busy(struct mpfs_dev_s *priv)
+{
+ uint32_t retries = EMMCSD_LONGTIMEOUT;
+ uint32_t srs9;
+
+ do
+ {
+ srs9 = getreg32(MPFS_EMMCSD_SRS09);
+ }
+ while (srs9 & (MPFS_EMMCSD_SRS09_CICMD | MPFS_EMMCSD_SRS09_CIDAT) &&
+ --retries);
+
+ if (retries == 0)
+ {
+ mcerr("Lines are still busy!\n");
+ return true;
+ }
+
+ return false;
+}
+
+/****************************************************************************
+ * Name: mpfs_setclkrate
+ *
+ * Description:
+ * Set the clock rate. Disables the SD clock for the time changing the
+ * settings, if already enabled.
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ * clkcr - New clock rate.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_setclkrate(struct mpfs_dev_s *priv, uint32_t clkrate)
+{
+ uint32_t baseclk;
+ uint32_t settings;
+ uint32_t divider;
+ uint32_t clkstable;
+ uint32_t retries = EMMCSD_CMDTIMEOUT;
+ int i;
+
+ mcinfo("clkrate: %08" PRIx32 "\n", clkrate);
+
+ if (clkrate == 0)
+ {
+ modifyreg32(MPFS_EMMCSD_SRS11, MPFS_EMMCSD_SRS11_SDCE, 0);
+ priv->clk_enabled = false;
+ return;
+ }
+
+ baseclk = (getreg32(MPFS_EMMCSD_SRS16) & 0xff00) >> 8;
+ baseclk *= 1000;
+
+ DEBUGASSERT(baseclk != 0);
+
+ /* 10-bit divider, search for match (N*2) */
+
+ for (i = 1; i < 2046; i++)
+ {
+ if (((baseclk / i) < clkrate) || (((baseclk / i) == clkrate) &&
+ ((baseclk % i) == 0u)))
+ {
+ break;
+ }
+ }
+
+ divider = ((i / 2) << 8);
+
+ /* Set SDCLK Frequency Select and Internal Clock Enable */
+
+ settings = (divider & 0xff00u) | ((divider & 0x30000u) >> 10) |
+ MPFS_EMMCSD_SRS11_ICE;
+
+ /* Disable SD clock if enabled */
+
+ if (priv->clk_enabled)
+ {
+ modifyreg32(MPFS_EMMCSD_SRS11, MPFS_EMMCSD_SRS11_SDCE, 0);
+ }
+
+ /* Apply new settings */
+
+ modifyreg32(MPFS_EMMCSD_SRS11, MPFS_EMMCSD_SRS11_SDCFSL |
+ MPFS_EMMCSD_SRS11_SDCFSH | MPFS_EMMCSD_SRS11_ICE,
+ settings);
+
+ /* Wait for stable clock */
+
+ clkstable = getreg32(MPFS_EMMCSD_SRS11);
+ while (!(clkstable & MPFS_EMMCSD_SRS11_ICS) && --retries)
+ {
+ clkstable = getreg32(MPFS_EMMCSD_SRS11);
+ }
+
+ if (retries == 0)
+ {
+ mcwarn("Clock didn't get stable!\n");
+ DEBUGPANIC();
+ }
+
+ /* HSE bit enabled if clk greater than 25 Mhz */
+
+ if (clkrate > MPFS_MMC_CLOCK_25MHZ)
+ {
+ modifyreg32(MPFS_EMMCSD_SRS10, 0, MPFS_EMMCSD_SRS10_HSE);
+ }
+
+ priv->clk_enabled = true;
+ modifyreg32(MPFS_EMMCSD_SRS11, 0, MPFS_EMMCSD_SRS11_SDCE);
+
+ mcinfo("SRS11 now: %08" PRIx32 "\n", getreg32(MPFS_EMMCSD_SRS11));
+}
+
+/****************************************************************************
+ * Name: mpfs_configwaitints
+ *
+ * Description:
+ * Enable/disable SDIO interrupts needed to support the wait function
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ * waitmask - The set of bits in the SDIO MASK register to set
+ * waitevents - Waited for events
+ * wkupevent - Wake-up events
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_configwaitints(struct mpfs_dev_s *priv, uint32_t waitmask,
+ 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->waitmask = waitmask;
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: mpfs_configxfrints
+ *
+ * Description:
+ * Enable SDIO interrupts needed to support the data transfer event
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ * xfrmask - The set of bits in the SDIO MASK register to set
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_configxfrints(struct mpfs_dev_s *priv, uint32_t xfrmask)
+{
+ irqstate_t flags;
+
+ flags = enter_critical_section();
+ priv->xfrmask = xfrmask;
+
+ mcinfo("Mask: %08" PRIx32 "\n", priv->xfrmask | priv->waitmask);
+
+ putreg32(priv->xfrmask | priv->waitmask, MPFS_EMMCSD_SRS14);
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: mpfs_sendfifo
+ *
+ * Description:
+ * Send SDIO data in interrupt mode
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_sendfifo(struct mpfs_dev_s *priv)
+{
+ union
+ {
+ uint32_t w;
+ uint8_t b[4];
+ } data;
+
+ /* Disable Buffer Write Ready interrupt */
+
+ modifyreg32(MPFS_EMMCSD_SRS14, MPFS_EMMCSD_SRS14_BWR_IE, 0);
+
+ putreg32(MPFS_EMMCSD_SRS12_TC, MPFS_EMMCSD_SRS12);
+
+ /* We might get extra interrupts */
+
+ if (priv->remaining == 0)
+ {
+ return;
+ }
+
+ /* Loop while there is more data to be sent and the TX FIFO is not full */
+
+ while (priv->remaining > 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 < (int)priv->remaining; i++)
+ {
+ data.b[i] = *ptr++;
+ }
+
+ /* Now the transfer is finished */
+
+ priv->remaining = 0;
+ }
+
+ /* Put the word in the FIFO */
+
+ putreg32(data.w, MPFS_EMMCSD_SRS08);
+
+ /* Multi-block writes may hit the wall - stop for now,
+ * continue later. Enable BWR interrupt, clear status and
+ * come back when we're good to write again.
+ */
+
+ if (priv->remaining && (!(getreg32(MPFS_EMMCSD_SRS09) &
+ MPFS_EMMCSD_SRS09_BWE)))
+ {
+ modifyreg32(MPFS_EMMCSD_SRS14, 0, MPFS_EMMCSD_SRS14_BWR_IE);
+ putreg32(MPFS_EMMCSD_SRS12_BWR, MPFS_EMMCSD_SRS12);
+ return;
+ }
+ }
+
+ mcinfo("Wrote all\n");
+}
+
+/****************************************************************************
+ * Name: mpfs_recvfifo
+ *
+ * Description:
+ * Receive SDIO data in interrupt mode
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_recvfifo(struct mpfs_dev_s *priv)
+{
+ union
+ {
+ uint32_t w;
+ uint8_t b[4];
+ } data;
+
+ mcinfo("Reading: %lu bytes\n", priv->remaining);
+
+ /* Disable Buffer Read Ready interrupt */
+
+ modifyreg32(MPFS_EMMCSD_SRS14, MPFS_EMMCSD_SRS14_BRR_IE, 0);
+
+ if (!priv->remaining)
+ {
+ return;
+ }
+
+ /* Loop while there is space to store the data and there is more
+ * data available in the RX FIFO.
+ */
+
+ while (priv->remaining > 0)
+ {
+ /* Multi-block reads may hit the wall - stop for now,
+ * continue later. Enable BRR interrupt, clear status and
+ * come back when we're good to read more.
+ */
+
+ if (!(getreg32(MPFS_EMMCSD_SRS09) & MPFS_EMMCSD_SRS09_BRE))
+ {
+ modifyreg32(MPFS_EMMCSD_SRS14, 0, MPFS_EMMCSD_SRS14_BRR_IE);
+ putreg32(MPFS_EMMCSD_SRS12_BRR, MPFS_EMMCSD_SRS12);
+ return;
+ }
+
+ /* Read the next word from the RX FIFO */
+
+ data.w = getreg32(MPFS_EMMCSD_SRS08);
+ 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 < (int)priv->remaining; i++)
+ {
+ *ptr++ = data.b[i];
+ }
+
+ /* Now the transfer is finished */
+
+ priv->remaining = 0;
+ }
+ }
+
+ mcinfo("Read all\n");
+}
+
+/****************************************************************************
+ * Name: mpfs_endwait
+ *
+ * Description:
+ * Wake up a waiting thread if the waited-for event has occurred.
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ * wkupevent - The event that caused the wait to end
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void mpfs_endwait(struct mpfs_dev_s *priv,
+ sdio_eventset_t wkupevent)
+{
+ mcinfo("wkupevent: %u\n", wkupevent);
+
+ /* Cancel the watchdog timeout */
+
+ wd_cancel(&priv->waitwdog);
+
+ /* Disable event-related interrupts */
+
+ mpfs_configwaitints(priv, 0, 0, wkupevent);
+
+ /* Wake up the waiting thread */
+
+ mpfs_givesem(priv);
+}
+
+/****************************************************************************
+ * Name: mpfs_eventtimeout
+ *
+ * Description:
+ * The watchdog timeout setup when the event wait start has expired without
+ * any other waited-for event occurring.
+ *
+ * Input Parameters:
+ * arg - The argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void mpfs_eventtimeout(wdparm_t arg)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)arg;
+
+ /* There is always race conditions with timer expirations. */
+
+ DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 ||
+ priv->wkupevent != 0);
+
+ mcinfo("sta: %08" PRIx32 " enabled irq: %08" PRIx32 "\n",
+ getreg32(MPFS_EMMCSD_SRS12),
+ getreg32(MPFS_EMMCSD_SRS13));
+
+ /* Is a data transfer complete event expected? */
+
+ if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+ {
+ /* Yes.. wake up any waiting threads */
+
+ mpfs_endwait(priv, SDIOWAIT_TIMEOUT);
+ mcerr("Timeout: remaining: %lu\n", priv->remaining);
+ }
+}
+
+/****************************************************************************
+ * Name: mpfs_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 - Instance of the EMMCSD private state structure.
+ * wkupevent - The event that caused the transfer to end
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Always called from the interrupt level with interrupts disabled.
+ *
+ ****************************************************************************/
+
+static void mpfs_endtransfer(struct mpfs_dev_s *priv,
+ sdio_eventset_t wkupevent)
+{
+ /* Disable all transfer related interrupts */
+
+ mpfs_configxfrints(priv, 0);
+
+ /* If there were errors, reset lines */
+
+ if ((wkupevent & (~SDIOWAIT_TRANSFERDONE)) != 0)
+ {
+ mpfs_reset_lines(priv);
+ }
+
+ /* Clear Buffer Read Ready (BRR), BWR and DMA statuses */
+
+ putreg32(MPFS_EMMCSD_SRS12, MPFS_EMMCSD_SRS12_BRR |
+ MPFS_EMMCSD_SRS12_BWR | MPFS_EMMCSD_SRS12_DMAINT);
+
+ /* Mark the transfer finished */
+
+ priv->remaining = 0;
+
+ /* Is a thread wait for these data transfer complete events? */
+
+ if ((priv->waitevents & wkupevent) != 0)
+ {
+ /* Yes.. wake up any waiting threads */
+
+ mpfs_endwait(priv, wkupevent);
+ }
+}
+
+/****************************************************************************
+ * Name: mpfs_emmcsd_interrupt
+ *
+ * Description:
+ * eMMCSD interrupt handler
+ *
+ * Input Parameters:
+ * priv - Instance of the eMMCSD private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int mpfs_emmcsd_interrupt(int irq, void *context, void *arg)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)arg;
+ uint32_t status;
+
+ DEBUGASSERT(priv != NULL);
+
+ status = getreg32(MPFS_EMMCSD_SRS12);
+
+ mcinfo("status: %08" PRIx32 "\n", status);
+
+ if (status & MPFS_EMMCSD_SRS12_EINT)
+ {
+ if (status & MPFS_EMMCSD_SRS12_EDCRC)
+ {
+ /* Handle data block send/receive CRC failure */
+
+ mcerr("ERROR: Data block CRC failure, remaining: %lu\n",
+ priv->remaining);
+
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE |
+ SDIOWAIT_ERROR);
+ }
+ else if (status & MPFS_EMMCSD_SRS12_EDT)
+ {
+ /* Handle data timeout error */
+
+ mcerr("ERROR: Data timeout: %08" PRIx32 " remaining: %lu\n",
+ status, priv->remaining);
+
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE |
+ SDIOWAIT_TIMEOUT);
+ }
+ else if (status & MPFS_EMMCSD_SRS12_EADMA)
+ {
+ /* Handle DMA error */
+
+ mcerr("ERROR: DMA error: %08" PRIx32 " SRS21: %08" PRIx32 "\n",
+ status, getreg32(MPFS_EMMCSD_SRS21));
+
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE |
+ SDIOWAIT_ERROR);
+ }
+ else
+ {
+ /* Generic error, not specified above */
+
+ mcerr("ERROR: %08" PRIx32 "\n", status);
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE |
+ SDIOWAIT_ERROR);
+ }
+ }
+ else
+ {
+ /* Handle wait events */
+
+ if (status & MPFS_EMMCSD_SRS12_DMAINT)
+ {
+ /* Very large transfers may end up here.
+ * They are not tested at all.
+ */
+
+ mcerr("DMAINT not expected, TC instead!\n");
+
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ else if (status & MPFS_EMMCSD_SRS12_BRR)
+ {
+ if (priv->polltransfer)
+ {
+ mpfs_recvfifo(priv);
+ if (!priv->remaining)
+ {
+ if (status & MPFS_EMMCSD_SRS12_TC)
+ {
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ }
+ }
+ else
+ {
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ }
+ else if (status & MPFS_EMMCSD_SRS12_BWR)
+ {
+ if (priv->polltransfer)
+ {
+ mpfs_sendfifo(priv);
+ if (status & MPFS_EMMCSD_SRS12_TC)
+ {
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ }
+ }
+ else if (status & MPFS_EMMCSD_SRS12_TC)
+ {
+ putreg32(MPFS_EMMCSD_SRS12_TC, MPFS_EMMCSD_SRS12);
+ if (priv->polltransfer && priv->receivecnt)
+ {
+ mcerr("Unexpected Transfer Complete!\n");
+ }
+ else
+ {
+ mpfs_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+ }
+ }
+ else if (status & MPFS_EMMCSD_SRS12_CC)
+ {
+ /* We don't handle Command Completes here! */
+
+ DEBUGPANIC();
+ }
+ else if (status & MPFS_EMMCSD_SRS12_CIN)
+ {
+ mcinfo("Card inserted!\n");
+
+ sdio_mediachange((struct sdio_dev_s *)priv, true);
+ putreg32(MPFS_EMMCSD_SRS12_CIN, MPFS_EMMCSD_SRS12);
+ }
+ else if (status & MPFS_EMMCSD_SRS12_CR)
+ {
+ mcinfo("Card removed!\n");
+
+ sdio_mediachange((struct sdio_dev_s *)priv, false);
+ putreg32(MPFS_EMMCSD_SRS12_CR, MPFS_EMMCSD_SRS12);
+ }
+ else
+ {
+ mcerr("Status not handled: %08" PRIx32 "\n", status);
+ }
+ }
+
+ mcinfo("done\n");
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_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
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_SDIO_MUXBUS)
+static int mpfs_lock(FAR struct sdio_dev_s *dev, bool lock)
+{
+ /* The multiplex bus is part of board support package. */
+
+ mpfs_muxbus_sdio_lock(dev, lock);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: mpfs_set_data_timeout
+ *
+ * Description:
+ * Sets the cycles for determining the data line timeout.
+ *
+ * Input Parameters:
+ * priv - Instance of the eMMCSD private state structure.
+ * timeout_us - Requested timeout in microseconds
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_set_data_timeout(struct mpfs_dev_s *priv,
+ uint32_t timeout_us)
+{
+ uint32_t timeout_interval;
+ uint32_t sdmclk_khz;
+ uint32_t sdmclk_mhz;
+ uint32_t sdmclk;
+ uint32_t timeout;
+ uint32_t temp;
+ uint8_t j;
+
+ temp = getreg32(MPFS_EMMCSD_SRS16);
+
+ /* 0x4u; - 0x4 is dummy -> 50Mhz * 4 = 200Mhz */
+
+ sdmclk_khz = (temp & 0x0000003fu);
+
+ DEBUGASSERT(sdmclk_khz);
+
+ if (!(temp & MPFS_EMMCSD_SRS16_TCU))
+ {
+ DEBUGASSERT(timeout_us >= 1000);
+ }
+ else
+ {
+ sdmclk_khz *= 1000;
+ }
+
+ sdmclk_mhz = sdmclk_khz / 1000;
+
+ if (sdmclk_mhz == 0)
+ {
+ sdmclk = sdmclk_khz;
+ timeout = timeout_us / 1000u;
+ }
+ else
+ {
+ sdmclk = sdmclk_mhz;
+ timeout = timeout_us;
+ }
+
+ /* calculate data timeout counter value */
+
+ timeout_interval = 8192; /* 2^13 */
+ for (j = 0; j < 15; j++)
+ {
+ if (timeout < (timeout_interval / sdmclk))
+ {
+ break;
+ }
+
+ timeout_interval *= 2;
+ }
+
+ timeout_interval = (uint32_t)j << 16;
+
+ mcinfo("Data timeout: %08" PRIx32 "\n", timeout_interval);
+
+ modifyreg32(MPFS_EMMCSD_SRS11, MPFS_EMMCSD_SRS11_DTCV, timeout_interval);
+}
+
+/****************************************************************************
+ * Name: mpfs_set_sdhost_power
+ *
+ * Description:
+ * Sets the requested SD bus voltage.
+ *
+ * Input Parameters:
+ * priv - Instance of the eMMCSD private state structure.
+ * voltage - Requested voltage: 0v, 3v3, 3v0 or 1v8
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_set_sdhost_power(struct mpfs_dev_s *priv, uint32_t voltage)
+{
+ uint32_t srs16;
+
+ /* Disable SD bus power */
+
+ modifyreg32(MPFS_EMMCSD_SRS10, MPFS_EMMCSD_SRS10_BP, 0);
+
+ /* Clear current voltage settings */
+
+ modifyreg32(MPFS_EMMCSD_SRS10, MPFS_EMMCSD_SRS10_BVS, 0);
+
+ if (!voltage)
+ {
+ return;
+ }
+
+ srs16 = getreg32(MPFS_EMMCSD_SRS16);
+
+ switch (voltage)
+ {
+ case MPFS_EMMCSD_SRS10_3_3V_BUS_VOLTAGE:
+ DEBUGASSERT(srs16 & MPFS_EMMCSD_SRS16_VS33);
+ modifyreg32(MPFS_EMMCSD_SRS10,
+ MPFS_EMMCSD_SRS10_BVS,
+ MPFS_EMMCSD_SRS10_3_3V_BUS_VOLTAGE |
+ MPFS_EMMCSD_SRS10_BP);
+ break;
+ case MPFS_EMMCSD_SRS10_3_0V_BUS_VOLTAGE:
+ DEBUGASSERT(srs16 & MPFS_EMMCSD_SRS16_VS30);
+ modifyreg32(MPFS_EMMCSD_SRS10,
+ MPFS_EMMCSD_SRS10_BVS,
+ MPFS_EMMCSD_SRS10_3_0V_BUS_VOLTAGE |
+ MPFS_EMMCSD_SRS10_BP);
+ break;
+ case MPFS_EMMCSD_SRS10_1_8V_BUS_VOLTAGE:
+ DEBUGASSERT(srs16 & MPFS_EMMCSD_SRS16_VS18);
+ modifyreg32(MPFS_EMMCSD_SRS10,
+ MPFS_EMMCSD_SRS10_BVS,
+ MPFS_EMMCSD_SRS10_1_8V_BUS_VOLTAGE |
+ MPFS_EMMCSD_SRS10_BP);
+ break;
+ default:
+ DEBUGPANIC();
+ }
+
+ nxsig_usleep(1000);
+}
+
+/****************************************************************************
+ * Name: mpfs_sdcard_init
+ *
+ * Description:
+ * Switches to use to SD-card instead of eMMC. Call only if the SD-card
+ * is used, not eMMC. SDIO_REGISTER_ADDRESS is the switch itself: 0
+ * means eMMC and 1 is for SD. Also the IOMUX settings are applied
+ * properly.
+ *
+ * Input Parameters:
+ * priv - Instance of the eMMCSD private state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_sdcard_init(struct mpfs_dev_s *priv)
+{
+ mcinfo("Init SD-card. Old FPGA will crash here!\n");
+
+ putreg32(LIBERO_SETTING_IOMUX1_CR_SD, MPFS_SYSREG_IOMUX1);
+ putreg32(LIBERO_SETTING_IOMUX2_CR_SD, MPFS_SYSREG_IOMUX2);
+ putreg32(LIBERO_SETTING_IOMUX6_CR_SD, MPFS_SYSREG_IOMUX6);
+
+ /* With 3.3v we exit from here */
+
+ if (priv->jumpers_3v3)
+ {
+ putreg32(1, SDIO_REGISTER_ADDRESS);
+ return;
+ }
+
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_CFG_CR_SD, MPFS_SYSREG_B4_CFG);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_0_1_CR_SD, MPFS_SYSREG_B4_0_1);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_2_3_CR_SD, MPFS_SYSREG_B4_2_3);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_4_5_CR_SD, MPFS_SYSREG_B4_4_5);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_6_7_CR_SD, MPFS_SYSREG_B4_6_7);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_8_9_CR_SD, MPFS_SYSREG_B4_8_9);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_10_11_CR_SD,
+ MPFS_SYSREG_B4_10_11);
+ putreg32(LIBERO_SETTING_MSSIO_BANK4_IO_CFG_12_13_CR_SD,
+ MPFS_SYSREG_4_12_13);
+
+ putreg32(1, SDIO_REGISTER_ADDRESS);
+}
+
+/****************************************************************************
+ * Name: mpfs_device_reset
+ *
+ * Description:
+ * Reset the SDIO controller. Undo all setup and initialization.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * true on success, false otherwise
+ *
+ ****************************************************************************/
+
+static bool mpfs_device_reset(FAR struct sdio_dev_s *dev)
+{
+ FAR struct mpfs_dev_s *priv = (FAR struct mpfs_dev_s *)dev;
+ irqstate_t flags;
+ uint32_t regval;
+ uint32_t cap;
+ uint32_t srs09;
+ bool retval = true;
+ int status = MPFS_EMMCSD_INITIALIZED;
+
+ flags = enter_critical_section();
+
+ up_disable_irq(priv->plic_irq);
+
+ if (!priv->emmc)
+ {
+ /* SD card needs FPGA out of reset and FIC3 clks for the eMMC / SD
+ * switch. It's OK if these are already out of reset or clk applied.
+ */
+
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ SYSREG_SOFT_RESET_CR_FPGA |
+ SYSREG_SOFT_RESET_CR_FIC3, 0);
+
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
+ SYSREG_SUBBLK_CLOCK_CR_FIC3);
+
+ mpfs_sdcard_init(priv);
+ }
+
+ /* Perform system-level reset */
+
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
+ SYSREG_SUBBLK_CLOCK_CR_MMC);
+
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0,
+ SYSREG_SOFT_RESET_CR_MMC);
+
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ SYSREG_SOFT_RESET_CR_MMC, 0);
+
+ nxsig_sleep(1);
+
+ /* Perform module-level reset */
+
+ modifyreg32(MPFS_EMMCSD_HRS00, 0, MPFS_EMMCSD_HRS00_SWR);
+
+ nxsig_usleep(1000);
+
+ do
+ {
+ regval = getreg32(MPFS_EMMCSD_HRS00);
+ }
+ while (regval & MPFS_EMMCSD_HRS00_SWR);
+
+ putreg32(MPFS_EMMCSD_DEBOUNCE_TIME, MPFS_EMMCSD_HRS01);
+
+ modifyreg32(MPFS_EMMCSD_HRS06, MPFS_EMMCSD_HRS06_EMM, 0);
+
+ /* eMMC, not SD */
+
+ if (priv->emmc)
+ {
+ modifyreg32(MPFS_EMMCSD_HRS06, 0, MPFS_EMMCSD_MODE_LEGACY);
+ }
+
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+
+ cap = getreg32(MPFS_EMMCSD_SRS16);
+
+ /* DMA 64 bit support? */
+
+ if (cap & MPFS_EMMCSD_SRS16_A64S)
+ {
+ modifyreg32(MPFS_EMMCSD_SRS15, 0, MPFS_EMMCSD_SRS15_A64B |
+ MPFS_EMMCSD_SRS15_HV4E);
+ }
+
+#ifdef CONFIG_SDIO_DMA
+ /* Check if SDMA is supported */
+
+ if (!(cap & MPFS_EMMCSD_SRS16_DMAS))
+ {
+ mcerr("DMA not supported!\n");
+ DEBUGPANIC();
+ }
+
+ uint64_t pmpcfg_mmc_x;
+
+ /* DMA will not work with the power-on default PMPCFG values.
+ * Check that the HSS or envm bootloader has applied the
+ * following values below, provide info if not.
+ */
+
+ pmpcfg_mmc_x = getreg64(MPFS_PMPCFG_MMC_0);
+ if ((pmpcfg_mmc_x & 0x1ffffff000000000) != 0x1f00000000000000)
+ {
+ mcinfo("Please check PMPCFG_MMC0 register.\n");
+ }
+
+ pmpcfg_mmc_x = getreg64(MPFS_PMPCFG_MMC_1);
+ if ((pmpcfg_mmc_x & 0x1ffffff000000000) != 0x1f00000000000000)
+ {
+ mcinfo("Please check PMPCFG_MMC1 register.\n");
+ }
+
+ pmpcfg_mmc_x = getreg64(MPFS_PMPCFG_MMC_2);
+ if ((pmpcfg_mmc_x & 0x1ffffff000000000) != 0x1f00000000000000)
+ {
+ mcinfo("Please check PMPCFG_MMC2 register.\n");
+ }
+
+ pmpcfg_mmc_x = getreg64(MPFS_PMPCFG_MMC_3);
+ if ((pmpcfg_mmc_x & 0x1ffffff000000000) != 0x1f00000000000000)
+ {
+ mcinfo("Please check PMPCFG_MMC3 register.\n");
+ }
+
+#endif
+
+ /* Clear interrupt status and disable interrupts */
+
+ putreg32(MPFS_EMMCSD_SRS13_STATUS_EN, MPFS_EMMCSD_SRS13);
+ putreg32(0, MPFS_EMMCSD_SRS14);
+
+ mpfs_set_data_timeout(priv, MPFS_EMMCSD_DATA_TIMEOUT);
+
+ /* Turn off host controller power */
+
+ mpfs_set_sdhost_power(priv, 0);
+
+ /* Card state stable */
+
+ srs09 = getreg32(MPFS_EMMCSD_SRS09);
+ DEBUGASSERT(srs09 & MPFS_EMMCSD_SRS09_CSS);
+
+ if (!priv->emmc)
+ {
+ if (!(srs09 & MPFS_EMMCSD_SRS09_CI))
+ {
+ mcerr("Please insert the SD card!\n");
+
+ /* No card detected, cannot communicate with it */
+
+ retval = false;
+ }
+ }
+
+ /* Set 1-bit bus mode */
+
+ modifyreg32(MPFS_EMMCSD_SRS10,
+ MPFS_EMMCSD_SRS10_DTW | MPFS_EMMCSD_SRS10_EDTW,
+ 0);
+
+ if (priv->emmc)
+ {
+ switch (priv->bus_voltage)
+ {
+ case MPFS_EMMCSD_1_8V_BUS_VOLTAGE:
+ mpfs_set_sdhost_power(priv, MPFS_EMMCSD_SRS10_1_8V_BUS_VOLTAGE);
+ break;
+
+ case MPFS_EMMCSD_3_3V_BUS_VOLTAGE:
+ if ((priv->bus_speed != MPFS_EMMCSD_MODE_HS200) &&
+ (priv->bus_speed != MPFS_EMMCSD_MODE_HS400_ES) &&
+ (priv->bus_speed != MPFS_EMMCSD_MODE_HS400))
+ {
+ mpfs_set_sdhost_power(priv,
+ MPFS_EMMCSD_SRS10_3_3V_BUS_VOLTAGE);
+ }
+ else
+ {
+ status = MPFS_EMMCSD_NOT_INITIALIZED;
+ mcerr("Voltage / mode combination not supported!\n");
+ }
+ break;
+
+ default:
+ DEBUGPANIC();
+ }
+ }
+ else
+ {
+ /* SD-card support currently only 3.3v */
+
+ mpfs_set_sdhost_power(priv, MPFS_EMMCSD_SRS10_3_3V_BUS_VOLTAGE);
+ }
+
+ if (status == MPFS_EMMCSD_INITIALIZED)
+ {
+ mpfs_setclkrate(priv, MPFS_MMC_CLOCK_400KHZ);
+ }
+
+ nxsig_usleep(1000);
+
+ /* Reset data */
+
+ priv->waitevents = 0; /* Set of events to be waited for */
+ priv->waitmask = 0; /* Interrupt enables for event waiting */
+ priv->wkupevent = 0; /* The event that caused the wakeup */
+
+ wd_cancel(&priv->waitwdog); /* Cancel any timeouts */
+
+ /* Interrupt mode data transfer support */
+
+ priv->buffer = 0; /* Address of current R/W buffer */
+ priv->remaining = 0; /* Number of bytes remaining in the transfer */
+ priv->xfrmask = 0; /* Interrupt enables for data transfer */
+
+ priv->widebus = false;
+
+ mpfs_reset_lines(priv);
+
+ leave_critical_section(flags);
+
+ return retval;
+}
+
+/****************************************************************************
+ * Name: mpfs_reset
+ *
+ * Description:
+ * Reset the SDIO controller via mpfs_device_reset. This is a wrapper
+ * function only.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_reset(FAR struct sdio_dev_s *dev)
+{
+ mpfs_device_reset(dev);
+}
+
+/****************************************************************************
+ * Name: mpfs_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 mpfs_capabilities(FAR struct sdio_dev_s *dev)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ sdio_capset_t caps = 0;
+
+ if (priv->onebit)
+ {
+ caps |= SDIO_CAPS_1BIT_ONLY;
+ }
+
+ /* This reverses the logic for mmcsd_writesingle(): setup first, then
+ * the command is sent. Normally the command is sent first and the setup
+ * via SDIO_SENDSETUP().
+ */
+
+ caps |= SDIO_CAPS_DMABEFOREWRITE;
+
+#ifdef CONFIG_SDIO_DMA
+ caps |= SDIO_CAPS_DMASUPPORTED;
+#endif
+
+ return caps;
+}
+
+/****************************************************************************
+ * Name: mpfs_status
+ *
+ * Description:
+ * Get SDIO status.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ *
+ * Returned Value:
+ * Returns a bitset of status values (see mpfs_status_* defines)
+ *
+ ****************************************************************************/
+
+static sdio_statset_t mpfs_status(FAR struct sdio_dev_s *dev)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ return priv->cdstatus;
+}
+
+/****************************************************************************
+ * Name: mpfs_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 mpfs_widebus(FAR struct sdio_dev_s *dev, bool wide)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ priv->widebus = wide;
+
+ mcinfo("wide: %d\n", wide);
+
+ if (wide)
+ {
+ modifyreg32(MPFS_EMMCSD_SRS10, 0, MPFS_EMMCSD_SRS10_DTW);
+ }
+ else
+ {
+ modifyreg32(MPFS_EMMCSD_SRS10, MPFS_EMMCSD_SRS10_DTW, 0);
+ }
+}
+
+/****************************************************************************
+ * Name: mpfs_clock
+ *
+ * Description:
+ * Enable/disable SDIO clocking. Only up to 25 Mhz is supported now. 50 Mhz
+ * may work with some cards.
+ *
+ * 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 mpfs_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ uint32_t clckr;
+
+ switch (rate)
+ {
+ /* Disable clock */
+
+ default:
+ case CLOCK_SDIO_DISABLED:
+ clckr = 0;
+ break;
+
+ /* Enable in initial ID mode clocking (400KHz) */
+
+ case CLOCK_IDMODE:
+ clckr = MPFS_MMC_CLOCK_400KHZ;
+ break;
+
+ /* Enable normal MMC operation clocking */
+
+ case CLOCK_MMC_TRANSFER:
+ clckr = MPFS_MMC_CLOCK_25MHZ;
+ break;
+
+ /* SD normal operation clocking (wide 4-bit mode) */
+
+ case CLOCK_SD_TRANSFER_4BIT:
+ clckr = MPFS_MMC_CLOCK_25MHZ;
+ break;
+
+ /* SD normal operation clocking (narrow 1-bit mode) */
+
+ case CLOCK_SD_TRANSFER_1BIT:
+ clckr = MPFS_MMC_CLOCK_25MHZ;
+ break;
+ }
+
+ /* Set the new clock frequency */
+
+ mpfs_setclkrate(priv, clckr);
+}
+
+/****************************************************************************
+ * Name: mpfs_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 mpfs_attach(FAR struct sdio_dev_s *dev)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ int ret;
+
+ /* Attach the SDIO interrupt handler */
+
+ ret = irq_attach(priv->plic_irq, mpfs_emmcsd_interrupt, priv);
+ if (ret == OK)
+ {
+ /* Disable all interrupts at the SDIO controller and clear
+ * interrupt flags, except current limit error, card interrupt,
+ * card removal and card insertion
+ */
+
+ modifyreg32(MPFS_EMMCSD_SRS12, MPFS_EMMCSD_SRS12_ECL |
+ MPFS_EMMCSD_SRS12_CINT |
+ MPFS_EMMCSD_SRS12_CR |
+ MPFS_EMMCSD_SRS12_CIN,
+ 0);
+
+ /* Enable SDIO interrupts. They can now be enabled at the
+ * SDIO controller as needed.
+ */
+
+ up_enable_irq(priv->plic_irq);
+
+ /* Enable card insertion and removal interrupts */
+
+ putreg32(MPFS_EMMCSD_CARD_INTS, MPFS_EMMCSD_SRS14);
+ }
+
+ mcinfo("attach: %d\n", ret);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_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:
+ * OK if no errors, an error otherwise
+ *
+ ****************************************************************************/
+
+static int mpfs_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t arg)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ uint32_t command_information;
+ uint32_t cmdidx;
+
+ /* Check if command / data lines are busy */
+
+ if (mpfs_check_lines_busy(priv))
+ {
+ mcerr("Busy!\n");
+ return -EBUSY;
+ }
+
+ /* Clear all status interrupts */
+
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+
+ /* Check response type */
+
+ switch (cmd & MMCSD_RESPONSE_MASK)
+ {
+ case MMCSD_R2_RESPONSE:
+ command_information = MPFS_EMMCSD_SRS03_RESP_L136 |
+ MPFS_EMMCSD_SRS03_CRCCE;
+ break;
+ case MMCSD_R3_RESPONSE:
+ case MMCSD_R4_RESPONSE:
+ command_information = MPFS_EMMCSD_SRS03_RESP_L48;
+ break;
+ case MMCSD_R1_RESPONSE:
+ case MMCSD_R5_RESPONSE:
+ case MMCSD_R6_RESPONSE:
+ case MMCSD_R7_RESPONSE:
+ command_information = MPFS_EMMCSD_SRS03_RESP_L48 |
+ MPFS_EMMCSD_SRS03_CRCCE |
+ MPFS_EMMCSD_SRS03_CICE;
+ break;
+ case MMCSD_R1B_RESPONSE:
+ command_information = MPFS_EMMCSD_SRS03_RESP_L48B |
+ MPFS_EMMCSD_SRS03_CRCCE |
+ MPFS_EMMCSD_SRS03_CICE;
+ break;
+ case MMCSD_NO_RESPONSE:
+ command_information = MPFS_EMMCSD_SRS03_NO_RESPONSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ putreg32(arg, MPFS_EMMCSD_SRS02);
+
+ cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
+
+ if (cmd & MMCSD_DATAXFR_MASK)
+ {
+ command_information |= MPFS_EMMCSD_SRS03_DPS |
+ MPFS_EMMCSD_SRS03_BCE |
+ MPFS_EMMCSD_SRS03_RECE;
+
+#ifdef CONFIG_SDIO_DMA
+ if (!priv->polltransfer)
+ {
+ command_information |= MPFS_EMMCSD_SRS03_DMAE;
+ }
+#endif
+
+ if ((cmd & MMCSD_DATAXFR_MASK) == MMCSD_RDDATAXFR)
+ {
+ command_information |= MPFS_EMMCSD_SRS03_DTDS;
+ mcinfo("cmd & MMCSD_RDDATAXFR\n");
+ }
+
+ if (cmd & MMCSD_MULTIBLOCK)
+ {
+ command_information |= MPFS_EMMCSD_SRS03_MSBS;
+ }
+
+ mcinfo("cmd & MMCSD_DATAXFR_MASK\n");
+ }
+
+ putreg32((((cmdidx << 24) & MPFS_EMMCSD_SRS03_CIDX) | command_information),
+ MPFS_EMMCSD_SRS03);
+
+ mcinfo("sendcmd: %08" PRIx32 " cmd: %08" PRIx32 " arg: %08" PRIx32
+ " cmdidx: %08" PRIx32 "\n",
+ command_information, cmd, arg, cmdidx);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_blocksetup
+ *
+ * Description:
+ * Configure block size and the number of blocks for next transfer.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface.
+ * blocksize - The selected block size.
+ * nblocks - The number of blocks to transfer.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void mpfs_blocksetup(FAR struct sdio_dev_s *dev,
+ unsigned int blocksize, unsigned int nblocks)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+
+ priv->blocksize = blocksize;
+
+ putreg32(priv->blocksize | (nblocks << 16) |
+ MPFS_EMMCSD_SRS01_DMA_SZ_512KB, MPFS_EMMCSD_SRS01);
+}
+#endif
+
+/****************************************************************************
+ * Name: mpfs_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,
+ * EMMCSD_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
+ *
+ ****************************************************************************/
+
+static int mpfs_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+ size_t nbytes)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+
+ mcinfo("Receive: %zu bytes\n", nbytes);
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+ DEBUGASSERT(((uintptr_t)buffer & 3) == 0);
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = nbytes;
+ priv->receivecnt = nbytes;
+ priv->polltransfer = true;
+
+ mpfs_check_lines_busy(priv);
+
+ /* Set up the SDIO data path, reset DAT and CMD lines */
+
+ mpfs_reset_lines(priv);
+
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ uint32_t blockcount = ((nbytes - 1) / priv->blocksize) + 1;
+ putreg32(priv->blocksize | (blockcount << 16), MPFS_EMMCSD_SRS01);
+#endif
+
+ putreg32(MPFS_EMMCSD_SRS13_STATUS_EN, MPFS_EMMCSD_SRS13);
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+
+ /* Enable interrupts */
+
+ mpfs_configxfrints(priv, MPFS_EMMCSD_RECV_MASK);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_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 BEFORE sending CMD24 (WRITE_BLOCK), CMD25
+ * (WRITE_MULTIPLE_BLOCK).
+ *
+ * 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
+ *
+ ****************************************************************************/
+
+static int mpfs_sendsetup(FAR struct sdio_dev_s *dev, FAR const
+ uint8_t *buffer, size_t nbytes)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+
+ mcinfo("Send: %zu bytes\n", nbytes);
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0);
+ DEBUGASSERT(((uintptr_t)buffer & 3) == 0);
+
+ mpfs_check_lines_busy(priv);
+
+ /* Save the source buffer information for use by the interrupt handler */
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = nbytes;
+ priv->receivecnt = 0;
+ priv->polltransfer = true;
+
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ uint32_t blockcount = ((nbytes - 1) / priv->blocksize) + 1;
+ putreg32(priv->blocksize | (blockcount << 16), MPFS_EMMCSD_SRS01);
+#endif
+
+ putreg32(MPFS_EMMCSD_SRS13_STATUS_EN, MPFS_EMMCSD_SRS13);
+ putreg32(~(MPFS_EMMCSD_SRS12_BWR), MPFS_EMMCSD_SRS12);
+
+ /* Enable interrupts */
+
+ mpfs_configxfrints(priv, MPFS_EMMCSD_SEND_MASK);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_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. No cache support is currently implemented.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - The memory to DMA from
+ * buflen - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_DMA
+static int mpfs_dmarecvsetup(FAR struct sdio_dev_s *dev,
+ FAR uint8_t *buffer, size_t buflen)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ uint32_t blockcount;
+#endif
+
+ mcinfo("Receive: %zu bytes\n", buflen);
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
+ DEBUGASSERT(((uintptr_t)buffer & 3) == 0);
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = buflen;
+ priv->receivecnt = buflen;
+ priv->polltransfer = false;
+
+ /* Set up the SDIO data path, reset DAT and CMD lines */
+
+ mpfs_reset_lines(priv);
+
+ modifyreg32(MPFS_EMMCSD_SRS10, MPFS_EMMCSD_SRS10_DMASEL, 0);
+
+ putreg32((uintptr_t)buffer, MPFS_EMMCSD_SRS22);
+ putreg32((uintptr_t)((uint64_t)buffer >> 32), MPFS_EMMCSD_SRS23);
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ blockcount = ((buflen - 1) / priv->blocksize) + 1;
+
+ putreg32((priv->blocksize | (blockcount << 16) |
+ MPFS_EMMCSD_SRS01_DMA_SZ_512KB), MPFS_EMMCSD_SRS01);
+#endif
+
+ /* Clear interrupt status */
+
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+
+ mpfs_configxfrints(priv, MPFS_EMMCSD_RECV_MASKDMA);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: mpfs_dmasendsetup
+ *
+ * Description:
+ * Setup to perform a write DMA. No cache support is currently implemented.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * buffer - The memory to DMA into
+ * buflen - The size of the DMA transfer in bytes
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_DMA
+static int mpfs_dmasendsetup(FAR struct sdio_dev_s *dev,
+ FAR const uint8_t *buffer, size_t buflen)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ uint32_t blockcount;
+#endif
+
+ mcinfo("Send: %zu bytes\n", buflen);
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
+ DEBUGASSERT(((uintptr_t)buffer & 3) == 0);
+
+ /* DMA send doesn't work in 0x08xxxxxxx address range. Default to IRQ mode
+ * in this special case.
+ */
+
+ if (((uintptr_t)buffer & 0xff000000) == 0x08000000)
+ {
+ return mpfs_sendsetup(dev, buffer, buflen);
+ }
+
+ /* Save the source buffer information for use by the interrupt handler */
+
+ priv->buffer = (uint32_t *)buffer;
+ priv->remaining = buflen;
+ priv->receivecnt = 0;
+ priv->polltransfer = false;
+
+ modifyreg32(MPFS_EMMCSD_SRS10, MPFS_EMMCSD_SRS10_DMASEL, 0);
+
+ putreg32((uintptr_t)buffer, MPFS_EMMCSD_SRS22);
+ putreg32((uintptr_t)((uint64_t)buffer >> 32), MPFS_EMMCSD_SRS23);
+
+#ifndef CONFIG_SDIO_BLOCKSETUP
+ blockcount = ((buflen - 1) / priv->blocksize) + 1;
+
+ putreg32((priv->blocksize | (blockcount << 16) |
+ MPFS_EMMCSD_SRS01_DMA_SZ_512KB),
+ MPFS_EMMCSD_SRS01);
+#endif
+
+ /* Clear interrupt status */
+
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+
+ mpfs_configxfrints(priv, MPFS_EMMCSD_SEND_MASKDMA);
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: mpfs_cancel
+ *
+ * Description:
+ * Cancel the data transfer setup of EMMCSD_RECVSETUP, EMMCSD_SENDSETUP,
+ * EMMCSD_DMARECVSETUP or EMMCSD_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 mpfs_cancel(FAR struct sdio_dev_s *dev)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+
+ /* Disable all transfer- and event- related interrupts */
+
+ mpfs_configxfrints(priv, 0);
+ mpfs_configwaitints(priv, 0, 0, 0);
+
+ /* If this was a DMA transfer, make sure that DMA is stopped */
+
+ modifyreg32(MPFS_EMMCSD_SRS03, MPFS_EMMCSD_SRS03_DMAE, 0);
+
+ /* Clearing pending interrupt status on all transfer- and event- related
+ * interrupts
+ */
+
+ putreg32(MPFS_EMMCSD_WAITALL_ICR, MPFS_EMMCSD_SRS12);
+
+ /* Cancel any watchdog timeout */
+
+ wd_cancel(&priv->waitwdog);
+
+ /* Mark no transfer in progress */
+
+ priv->remaining = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_waitresponse
+ *
+ * Description:
+ * Poll-wait for the response to the last command to be ready.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - The command that was sent.
+ *
+ * Returned Value:
+ * OK is success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int mpfs_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ uint32_t status;
+ int32_t timeout;
+ uint32_t waitbits;
+
+ mcinfo("cmd: %08" PRIx32 "\n", cmd);
+
+ switch (cmd & MMCSD_RESPONSE_MASK)
+ {
+ case MMCSD_NO_RESPONSE:
+ timeout = EMMCSD_CMDTIMEOUT;
+ 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 = EMMCSD_LONGTIMEOUT;
+ break;
+
+ case MMCSD_R3_RESPONSE:
+ case MMCSD_R7_RESPONSE:
+ timeout = EMMCSD_CMDTIMEOUT;
+ break;
+
+ default:
+ mcerr("Unknown command\n");
+ return -EINVAL;
+ }
+
+ /* Then wait for the response (or timeout) */
+
+ if (cmd & MMCSD_DATAXFR_MASK)
+ {
+ waitbits = MPFS_EMMCSD_SRS12_CC;
+ }
+
+ do
+ {
+ status = getreg32(MPFS_EMMCSD_SRS12);
+ }
+ while (!(status & (waitbits | MPFS_EMMCSD_SRS12_EINT))
+ && --timeout);
+
+ if (timeout == 0 || (status & MPFS_EMMCSD_SRS12_ECT))
+ {
+ mcerr("ERROR: Timeout cmd: %08" PRIx32 " stat: %08" PRIx32 " wb: %08"
+ PRIx32 "\n", cmd, status, waitbits);
+ return -EBUSY;
+ }
+
+ mcinfo("status: %08" PRIx32 "\n", status);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mpfs_check_recverror
+ *
+ * Description:
+ * Receive response to SDIO command.
+ *
+ * Input Parameters:
+ * priv - Instance of the EMMCSD private state structure.
+ *
+ * Returned Value:
+ * OK on success; a negated errno if error detected.
+ *
+ ****************************************************************************/
+
+static int mpfs_check_recverror(struct mpfs_dev_s *priv)
+{
+ uint32_t regval;
+ int ret = OK;
+
+ regval = getreg32(MPFS_EMMCSD_SRS12);
+
+ if (regval & MPFS_EMMCSD_SRS12_EINT)
+ {
+ if (regval & (MPFS_EMMCSD_SRS12_ECT | MPFS_EMMCSD_SRS12_EDT))
+ {
+ mcerr("ERROR: Command timeout: %08" PRIx32 "\n", regval);
+ ret = -ETIMEDOUT;
+ }
+ else if (regval & MPFS_EMMCSD_SRS12_EDCRC)
+ {
+ mcerr("ERROR: CRC failure: %08" PRIx32 "\n", regval);
+ ret = -EIO;
+ }
+ else
+ {
+ mcerr("ERROR: %08" PRIx32 "\n", regval);
+ ret = -EIO;
+ }
+ }
+ else if (!(regval & MPFS_EMMCSD_SRS12_CC))
+ {
+ mcerr("ERROR: Command not completed: %08" PRIx32 "\n", regval);
+ ret = -EIO;
+ }
+
+ if (ret)
+ {
+ /* With an error, reset DAT and CMD lines. Otherwise the next command
+ * will fail as well.
+ */
+
+ mpfs_reset_lines(priv);
+
+ /* Clear all status interrupts */
+
+ putreg32(MPFS_EMMCSD_SRS12_STAT_CLEAR, MPFS_EMMCSD_SRS12);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_recvshortcrc
+ *
+ * Description:
+ * Receive response to SDIO command.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - Command
+ * rshort - Buffer for reveiving the response
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ int ret = OK;
+
+ /* Check if a timeout or CRC error occurred */
+
+ if (!(cmd & MMCSD_DATAXFR_MASK)) /* TBD: Fix this bypass! */
+ {
+ ret = mpfs_check_recverror(priv);
+ }
+
+ if (rshort)
+ {
+ *rshort = getreg32(MPFS_EMMCSD_SRS04);
+ mcinfo("recv: %08" PRIx32 "\n", *rshort);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_recvshort
+ *
+ * Description:
+ * Receive response to SDIO command.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - Command
+ * rshort - Buffer for reveiving the response
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t *rshort)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ int ret = OK;
+
+ if (!(cmd & MMCSD_DATAXFR_MASK))
+ {
+ ret = mpfs_check_recverror(priv);
+ }
+
+ if (rshort)
+ {
+ *rshort = getreg32(MPFS_EMMCSD_SRS04);
+ mcinfo("recv: %08" PRIx32 "\n", *rshort);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_recvlong
+ *
+ * Description:
+ * Receive response to SDIO command.
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO device interface
+ * cmd - Command
+ * rlong - Buffer for reveiving the response
+ *
+ * Returned Value:
+ * OK on success; a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int mpfs_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd,
+ uint32_t rlong[4])
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ int ret;
+
+ ret = mpfs_check_recverror(priv);
+
+ /* Return the long response */
+
+ if (rlong)
+ {
+ /* Last 8-bits are missing, see SRS04 documemntation, RESP3[23:0]
+ * has only 24 bits unlike RESP2, RESP1 and RESP0 that have 32 bits.
+ * We have to shift left 8 bits to match the proper long response.
+ */
+
+ rlong[3] = getreg32(MPFS_EMMCSD_SRS04) << 8;
+ rlong[2] = getreg32(MPFS_EMMCSD_SRS05) << 8;
+ rlong[1] = getreg32(MPFS_EMMCSD_SRS06) << 8;
+ rlong[0] = getreg32(MPFS_EMMCSD_SRS07) << 8;
+
+ mcinfo("recv: %08" PRIx32 " %08" PRIx32 " %08" PRIx32 " %08" \
+ PRIx32"\n", rlong[0], rlong[1], rlong[2], rlong[3]);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mpfs_waitenable
+ *
+ * Description:
+ * Enable/disable of a set of SDIO wait events. This is part of the
+ * the EMMCSD_WAITEVENT sequence. The set of to-be-waited-for events is
+ * configured before calling mpfs_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) EMMCSD_WAITENABLE is called
+ * again specifying a different set of wait events, or (2) EMMCSD_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.
+ * timeout - SDIO timeout
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void mpfs_waitenable(FAR struct sdio_dev_s *dev,
+ sdio_eventset_t eventset, uint32_t timeout)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ uint32_t waitmask = 0;
+
+ mcinfo("eventset: %02" PRIx8 "\n", eventset);
+
+ DEBUGASSERT(priv != NULL);
+
+ /* Disable event-related interrupts */
+
+ mpfs_configwaitints(priv, 0, 0, 0);
+
+ /* Select the interrupt mask that will give us the appropriate wakeup
+ * interrupts.
+ */
+
+ if ((eventset & SDIOWAIT_CMDDONE) != 0)
+ {
+ waitmask |= MPFS_EMMCSD_CMDDONE_MASK;
+ }
+
+ if ((eventset & SDIOWAIT_RESPONSEDONE) != 0)
+ {
+ waitmask |= MPFS_EMMCSD_RESPDONE_MASK;
+ }
+
+ if ((eventset & SDIOWAIT_TRANSFERDONE) != 0)
+ {
+ }
+
+ /* Enable event-related interrupts */
+
+ mpfs_configwaitints(priv, waitmask, eventset, 0);
+
+ /* Check if the timeout event is specified in the event set */
+
+ if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0)
+ {
+ int delay;
+ int ret;
+
+ /* Yes.. Handle a cornercase: The user request a timeout event but
+ * with timeout == 0?
+ */
+
+ if (!timeout)
+ {
+ priv->wkupevent = SDIOWAIT_TIMEOUT;
+ return;
+ }
+
+ /* Start the watchdog timer */
+
+ delay = MSEC2TICK(timeout);
+ ret = wd_start(&priv->waitwdog, delay,
+ mpfs_eventtimeout, (wdparm_t)priv);
+ if (ret < OK)
+ {
+ mcerr("ERROR: wd_start failed: %d\n", ret);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: mpfs_eventwait
+ *
+ * Description:
+ * Wait for one of the enabled events to occur (or a timeout). Note that
+ * all events enabled by EMMCSD_WAITEVENTS are disabled when mpfs_eventwait
+ * returns. EMMCSD_WAITEVENTS must be called again before mpfs_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 mpfs_eventwait(FAR struct sdio_dev_s *dev)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ sdio_eventset_t wkupevent = 0;
+ irqstate_t flags;
+ int ret;
+
+ mcinfo("wait\n");
+
+ /* There is a race condition here... the event may have completed before
+ * we get here. In this case waitevents will be zero, but wkupevents will
+ * be non-zero (and, hopefully, the semaphore count will also be non-zero.
+ */
+
+ flags = enter_critical_section();
+
+ DEBUGASSERT(priv->waitevents != 0 || priv->wkupevent != 0);
+
+ /* Loop until the event (or the timeout occurs). Race conditions are
+ * avoided by calling mpfs_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.
+ */
+
+ ret = mpfs_takesem(priv);
+ if (ret < 0)
+ {
+ /* Task canceled. Cancel the wdog (assuming it was started) and
+ * return an SDIO error.
+ */
+
+ wd_cancel(&priv->waitwdog);
+ wkupevent = SDIOWAIT_ERROR;
+ goto errout_with_waitints;
+ }
+
+ wkupevent = priv->wkupevent;
+
+ /* Check if the event has occurred. When the event has occurred, then
+ * evenset will be set to 0 and wkupevent will be set to a nonzero
+ * value.
+ */
+
+ if (wkupevent != 0)
+ {
+ /* Yes... break out of the loop with wkupevent non-zero */
+
+ break;
+ }
+ }
+
+ /* Disable event-related interrupts */
+
+errout_with_waitints:
+
+ mpfs_configwaitints(priv, 0, 0, 0);
+
+ leave_critical_section(flags);
+ return wkupevent;
+}
+
+/****************************************************************************
+ * Name: mpfs_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 mpfs_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 mpfs_callbackenable(FAR struct sdio_dev_s *dev,
+ sdio_eventset_t eventset)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+
+ mcinfo("eventset: %02" PRIx8 "\n", eventset);
+ DEBUGASSERT(priv != NULL);
+
+ priv->cbevents = eventset;
+ mpfs_callback(priv);
+}
+
+/****************************************************************************
+ * Name: mpfs_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 EMMCSD_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 mpfs_registercallback(FAR struct sdio_dev_s *dev,
+ worker_t callback, void *arg)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_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: mpfs_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 mpfs_callback(void *arg)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)arg;
+
+ /* Is a callback registered? */
+
+ DEBUGASSERT(priv != NULL);
+
+ mcinfo("Callback %p(%p) cbevents: %02" PRIx8 " cdstatus: %02" PRIx8 "\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);
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sdio_initialize
+ *
+ * Description:
+ * Initialize SDIO for operation.
+ *
+ * Input Parameters:
+ * slotno - Not used.
+ *
+ * Returned Values:
+ * A reference to an SDIO interface structure. NULL is returned on
+ * failures.
+ *
+ ****************************************************************************/
+
+FAR struct sdio_dev_s *sdio_initialize(int slotno)
+{
+ struct mpfs_dev_s *priv = NULL;
+ priv = &g_emmcsd_dev;
+
+ /* 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);
+
+ /* Reset the card and assure that it is in the initial, unconfigured
+ * state.
+ */
+
+ if (!mpfs_device_reset(&priv->dev))
+ {
+ return NULL;
+ }
+
+ return &priv->dev;
+}
+
+/****************************************************************************
+ * Name: sdio_mediachange
+ *
+ * Description:
+ * Called by board-specific logic -- possible from an interrupt handler --
+ * in order to signal to the driver that a card has been inserted or
+ * removed from the slot
+ *
+ * Input Parameters:
+ * dev - An instance of the SDIO driver device state structure.
+ * cardinslot - true is a card has been detected in the slot; false if a
+ * card has been removed from the slot. Only transitions
+ * (inserted->removed or removed->inserted should be reported)
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ sdio_statset_t cdstatus;
+ irqstate_t flags;
+
+ /* Update card status */
+
+ flags = enter_critical_section();
+ cdstatus = priv->cdstatus;
+ if (cardinslot)
+ {
+ priv->cdstatus |= SDIO_STATUS_PRESENT;
+ }
+ else
+ {
+ priv->cdstatus &= ~SDIO_STATUS_PRESENT;
+ }
+
+ leave_critical_section(flags);
+
+ mcinfo("cdstatus OLD: %02" PRIx8 " NEW: %02" PRIx8 "\n",
+ cdstatus, priv->cdstatus);
+
+ /* Perform any requested callback if the status has changed */
+
+ if (cdstatus != priv->cdstatus)
+ {
+ mpfs_callback(priv);
+ }
+}
+
+/****************************************************************************
+ * 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)
+{
+ struct mpfs_dev_s *priv = (struct mpfs_dev_s *)dev;
+ irqstate_t flags;
+
+ /* Update card status */
+
+ flags = enter_critical_section();
+ if (wrprotect)
+ {
+ priv->cdstatus |= SDIO_STATUS_WRPROTECTED;
+ }
+ else
+ {
+ priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED;
+ }
+
+ mcinfo("cdstatus: %02" PRIx8 "\n", priv->cdstatus);
+
+ leave_critical_section(flags);
+}
diff --git a/arch/risc-v/src/mpfs/mpfs_emmcsd.h b/arch/risc-v/src/mpfs/mpfs_emmcsd.h
new file mode 100755
index 0000000..747dba6
--- /dev/null
+++ b/arch/risc-v/src/mpfs/mpfs_emmcsd.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_emmcsd.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_RISCV_SRC_MPFS_MPFS_EMMCSD_H
+#define __ARCH_RISCV_SRC_MPFS_MPFS_EMMCSD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdbool.h>
+
+#include "chip.h"
+#include "hardware/mpfs_emmcsd.h"
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: sdio_initialize
+ *
+ * Description:
+ * Initialize SDIO for operation.
+ *
+ * Input Parameters:
+ * slotno - Not used.
+ *
+ * Returned Values:
+ * A reference to an SDIO interface structure. NULL is returned on
+ * failures.
+ *
+ ****************************************************************************/
+
+struct sdio_dev_s; /* See include/nuttx/sdio.h */
+FAR struct sdio_dev_s *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 Values:
+ * 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 Values:
+ * None
+ *
+ ****************************************************************************/
+
+void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_MPFS_MPFS_EMMCSD_H */