From 772432e7c36eff619167e17d613ff2075968346e Mon Sep 17 00:00:00 2001 From: Eero Nurkkala Date: Mon, 30 Aug 2021 07:56:18 +0300 Subject: [PATCH] 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 --- 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(+) create mode 100755 arch/risc-v/src/mpfs/hardware/mpfs_emmcsd.h create mode 100755 arch/risc-v/src/mpfs/mpfs_emmcsd.c create mode 100755 arch/risc-v/src/mpfs/mpfs_emmcsd.h 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 0000000000..6cf6cdf2e0 --- /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 0000000000..82a182a1dc --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 0000000000..747dba699b --- /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 +#include +#include + +#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 */