summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Makefile1
-rw-r--r--drivers/mmc/mmc.c87
-rw-r--r--drivers/mmc/msm_sdhci.c11
-rw-r--r--drivers/mmc/sdhci-cadence.c63
-rw-r--r--drivers/mmc/sdhci-cadence.h69
-rw-r--r--drivers/mmc/sdhci-cadence6.c293
-rw-r--r--drivers/mmc/sdhci.c52
-rw-r--r--drivers/mmc/snps_dw_mmc.c9
8 files changed, 478 insertions, 107 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 72c3fb66ce0..235c477c2e0 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_ATMEL) += atmel_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
+obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence6.o
obj-$(CONFIG_MMC_SDHCI_CV1800B) += cv1800b_sdhci.o
obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += iproc_sdhci.o
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index cf8277cbed8..7e702c3ae85 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -30,6 +30,41 @@
#define DEFAULT_CMD6_TIMEOUT_MS 500
+/**
+ * names of emmc BOOT_PARTITION_ENABLE values
+ *
+ * Boot Area Partitions - name consistent with Linux
+ */
+const char *emmc_boot_part_names[] = {
+ "default", /* EMMC_BOOT_PART_DEFAULT */
+ "boot0", /* EMMC_BOOT_PART_BOOT1 */
+ "boot1", /* EMMC_BOOT_PART_BOOT2 */
+ "",
+ "",
+ "",
+ "",
+ "user", /* EMMC_BOOT_PART_USER */
+};
+
+/**
+ * names of emmc 'hardware partitions' consistent with:
+ * - value used in mmc_switch()
+ * - value used by PARTITION_CONFIG PARTITION_ACCESS field
+ *
+ * Boot Area Partitions - name consistent with Linux
+ * General Perpose Partitions - name consistent with 'mmc hwpartition' usage
+ */
+const char *emmc_hwpart_names[] = {
+ "user", /* EMMC_HWPART_DEFAULT */
+ "boot0", /* EMMC_HWPART_BOOT1 */
+ "boot1", /* EMMC_HWPART_BOOT2 */
+ "rpmb", /* EMMC_HWPART_RPMB */
+ "gp1", /* EMMC_HWPART_GP1 */
+ "gp2", /* EMMC_HWPART_GP2 */
+ "gp3", /* EMMC_HWPART_GP3 */
+ "gp4", /* EMMC_HWPART_GP4 */
+};
+
static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
#if !CONFIG_IS_ENABLED(DM_MMC)
@@ -294,7 +329,7 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
if (status & MMC_STATUS_MASK) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("Status Error: 0x%08x\n", status);
+ log_err("Status Error: %#08x\n", status);
#endif
return -ECOMM;
}
@@ -307,7 +342,7 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
if (timeout_ms <= 0) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("Timeout waiting card ready\n");
+ log_err("Timeout waiting card ready\n");
#endif
return -ETIMEDOUT;
}
@@ -449,7 +484,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
if (blkcnt > 1) {
if (mmc_send_stop_transmission(mmc, false)) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("mmc fail to send stop cmd\n");
+ log_err("mmc fail to send stop cmd\n");
#endif
return 0;
}
@@ -500,8 +535,8 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
if ((start + blkcnt) > block_dev->lba) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
- start + blkcnt, block_dev->lba);
+ log_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
+ start + blkcnt, block_dev->lba);
#endif
return 0;
}
@@ -962,8 +997,8 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode,
* Extended CSD. Reconfigure the controller to run at HS mode.
*/
if (hsdowngrade) {
- mmc_select_mode(mmc, MMC_HS);
- mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), false);
+ mmc_select_mode(mmc, MMC_HS_52);
+ mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS_52), false);
}
#endif
@@ -996,7 +1031,7 @@ static int mmc_get_capabilities(struct mmc *mmc)
return 0;
if (!ext_csd) {
- pr_err("No ext_csd found!\n"); /* this should enver happen */
+ log_err("No ext_csd found!\n"); /* this should never happen */
return -ENOTSUPP;
}
@@ -1108,17 +1143,17 @@ int mmc_hwpart_config(struct mmc *mmc,
return -EINVAL;
if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
- pr_err("eMMC >= 4.4 required for enhanced user data area\n");
+ log_err("eMMC >= 4.4 required for enhanced user data area\n");
return -EMEDIUMTYPE;
}
if (!(mmc->part_support & PART_SUPPORT)) {
- pr_err("Card does not support partitioning\n");
+ log_err("Card does not support partitioning\n");
return -EMEDIUMTYPE;
}
if (!mmc->hc_wp_grp_size) {
- pr_err("Card does not define HC WP group size\n");
+ log_err("Card does not define HC WP group size\n");
return -EMEDIUMTYPE;
}
@@ -1126,8 +1161,7 @@ int mmc_hwpart_config(struct mmc *mmc,
if (conf->user.enh_size) {
if (conf->user.enh_size % mmc->hc_wp_grp_size ||
conf->user.enh_start % mmc->hc_wp_grp_size) {
- pr_err("User data enhanced area not HC WP group "
- "size aligned\n");
+ log_err("User data enhanced area not HC WP group size aligned\n");
return -EINVAL;
}
part_attrs |= EXT_CSD_ENH_USR;
@@ -1145,8 +1179,8 @@ int mmc_hwpart_config(struct mmc *mmc,
for (pidx = 0; pidx < 4; pidx++) {
if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
- pr_err("GP%i partition not HC WP group size "
- "aligned\n", pidx+1);
+ log_err("GP%i partition not HC WP group-size aligned\n",
+ pidx + 1);
return -EINVAL;
}
gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
@@ -1157,7 +1191,7 @@ int mmc_hwpart_config(struct mmc *mmc,
}
if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
- pr_err("Card does not support enhanced attribute\n");
+ log_err("Card does not support enhanced attribute\n");
return -EMEDIUMTYPE;
}
@@ -1170,8 +1204,8 @@ int mmc_hwpart_config(struct mmc *mmc,
(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
if (tot_enh_size_mult > max_enh_size_mult) {
- pr_err("Total enhanced size exceeds maximum (%u > %u)\n",
- tot_enh_size_mult, max_enh_size_mult);
+ log_err("Total enhanced size exceeds maximum (%#x > %#x)\n",
+ tot_enh_size_mult, max_enh_size_mult);
return -EMEDIUMTYPE;
}
@@ -1204,7 +1238,7 @@ int mmc_hwpart_config(struct mmc *mmc,
if (ext_csd[EXT_CSD_PARTITION_SETTING] &
EXT_CSD_PARTITION_SETTING_COMPLETED) {
- pr_err("Card already partitioned\n");
+ log_err("Card already partitioned\n");
return -EPERM;
}
@@ -1875,7 +1909,7 @@ error:
}
}
- pr_err("unable to select a mode\n");
+ log_err("unable to select a mode\n");
return -ENOTSUPP;
}
@@ -2043,7 +2077,7 @@ static int mmc_select_hs400(struct mmc *mmc)
}
/* Set back to HS */
- mmc_set_card_speed(mmc, MMC_HS, true);
+ mmc_set_card_speed(mmc, MMC_HS_52, true);
err = mmc_hs400_prepare_ddr(mmc);
if (err)
@@ -2253,7 +2287,7 @@ error:
}
}
- pr_err("unable to select a mode : %d\n", err);
+ log_err("unable to select a mode: %d\n", err);
return -ENOTSUPP;
}
@@ -2921,7 +2955,8 @@ retry:
if (err) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
if (!quiet)
- pr_err("Card did not respond to voltage select! : %d\n", err);
+ log_err("Card did not respond to voltage select! : %d\n",
+ err);
#endif
return -EOPNOTSUPP;
}
@@ -2954,7 +2989,7 @@ int mmc_start_init(struct mmc *mmc)
| MMC_CAP(MMC_LEGACY) |
MMC_MODE_1BIT);
} else {
- pr_err("bus_mode requested is not supported\n");
+ log_err("bus_mode requested is not supported\n");
return -EINVAL;
}
}
@@ -2974,7 +3009,7 @@ int mmc_start_init(struct mmc *mmc)
if (no_card) {
mmc->has_init = 0;
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("MMC: no card present\n");
+ log_err("MMC: no card present\n");
#endif
return -ENOMEDIUM;
}
@@ -3103,7 +3138,7 @@ static int mmc_probe(struct bd_info *bis)
uclass_foreach_dev(dev, uc) {
ret = device_probe(dev);
if (ret)
- pr_err("%s - probe failed: %d\n", dev->name, ret);
+ log_err("%s - probe failed: %d\n", dev->name, ret);
}
return 0;
diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c
index f5e9930c799..4e5c932c071 100644
--- a/drivers/mmc/msm_sdhci.c
+++ b/drivers/mmc/msm_sdhci.c
@@ -32,6 +32,8 @@
#define SDCC_MCI_STATUS2_MCI_ACT 0x1
#define SDCC_MCI_HC_MODE 0x78
+#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
+
struct msm_sdhc_plat {
struct mmc_config cfg;
struct mmc mmc;
@@ -46,6 +48,7 @@ struct msm_sdhc {
struct msm_sdhc_variant_info {
bool mci_removed;
+ u32 core_vendor_spec;
u32 core_vendor_spec_capabilities0;
};
@@ -54,11 +57,14 @@ DECLARE_GLOBAL_DATA_PTR;
static int msm_sdc_clk_init(struct udevice *dev)
{
struct msm_sdhc *prv = dev_get_priv(dev);
+ const struct msm_sdhc_variant_info *var_info;
ofnode node = dev_ofnode(dev);
ulong clk_rate;
int ret, i = 0, n_clks;
const char *clk_name;
+ var_info = (void *)dev_get_driver_data(dev);
+
ret = ofnode_read_u32(node, "clock-frequency", (uint *)(&clk_rate));
if (ret)
clk_rate = 201500000;
@@ -105,6 +111,9 @@ static int msm_sdc_clk_init(struct udevice *dev)
return -EINVAL;
}
+ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
+ prv->host.ioaddr + var_info->core_vendor_spec);
+
return 0;
}
@@ -254,12 +263,14 @@ static int msm_sdc_bind(struct udevice *dev)
static const struct msm_sdhc_variant_info msm_sdhc_mci_var = {
.mci_removed = false,
+ .core_vendor_spec = 0x10c,
.core_vendor_spec_capabilities0 = 0x11c,
};
static const struct msm_sdhc_variant_info msm_sdhc_v5_var = {
.mci_removed = true,
+ .core_vendor_spec = 0x20c,
.core_vendor_spec_capabilities0 = 0x21c,
};
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 07ec35a0463..7d169efa476 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -16,56 +16,7 @@
#include <linux/libfdt.h>
#include <mmc.h>
#include <sdhci.h>
-
-/* HRS - Host Register Set (specific to Cadence) */
-#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
-#define SDHCI_CDNS_HRS04_ACK BIT(26)
-#define SDHCI_CDNS_HRS04_RD BIT(25)
-#define SDHCI_CDNS_HRS04_WR BIT(24)
-#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
-#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
-#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
-
-#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
-#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
-#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
-#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
-#define SDHCI_CDNS_HRS06_MODE_SD 0x0
-#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
-#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
-
-/* SRS - Slot Register Set (SDHCI-compatible) */
-#define SDHCI_CDNS_SRS_BASE 0x200
-
-/* PHY */
-#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
-#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
-#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
-#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
-#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
-#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
-#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
-#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
-#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
-
-/*
- * The tuned val register is 6 bit-wide, but not the whole of the range is
- * available. The range 0-42 seems to be available (then 43 wraps around to 0)
- * but I am not quite sure if it is official. Use only 0 to 39 for safety.
- */
-#define SDHCI_CDNS_MAX_TUNING_LOOP 40
-
-struct sdhci_cdns_plat {
- struct mmc_config cfg;
- struct mmc mmc;
- void __iomem *hrs_addr;
-};
+#include "sdhci-cadence.h"
struct sdhci_cdns_phy_cfg {
const char *property;
@@ -162,6 +113,9 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
tmp &= ~SDHCI_CDNS_HRS06_MODE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06);
+
+ if (device_is_compatible(mmc->dev, "cdns,sd6hc"))
+ sdhci_cdns6_phy_adj(mmc->dev, plat, mode);
}
static const struct sdhci_ops sdhci_cdns_ops = {
@@ -175,6 +129,9 @@ static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
u32 tmp;
int i, ret;
+ if (device_is_compatible(plat->mmc.dev, "cdns,sd6hc"))
+ return sdhci_cdns6_set_tune_val(plat, val);
+
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
return -EINVAL;
@@ -281,7 +238,10 @@ static int sdhci_cdns_probe(struct udevice *dev)
if (ret)
return ret;
- ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
+ if (device_is_compatible(dev, "cdns,sd6hc"))
+ ret = sdhci_cdns6_phy_init(dev, plat);
+ else
+ ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
if (ret)
return ret;
@@ -300,6 +260,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
static const struct udevice_id sdhci_cdns_match[] = {
{ .compatible = "socionext,uniphier-sd4hc" },
{ .compatible = "cdns,sd4hc" },
+ { .compatible = "cdns,sd6hc" },
{ /* sentinel */ }
};
diff --git a/drivers/mmc/sdhci-cadence.h b/drivers/mmc/sdhci-cadence.h
new file mode 100644
index 00000000000..7101f00b75b
--- /dev/null
+++ b/drivers/mmc/sdhci-cadence.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#ifndef SDHCI_CADENCE_H_
+#define SDHCI_CADENCE_H_
+
+/* HRS - Host Register Set (specific to Cadence) */
+/* PHY access port */
+#define SDHCI_CDNS_HRS04 0x10
+/* Cadence V4 HRS04 Description*/
+#define SDHCI_CDNS_HRS04_ACK BIT(26)
+#define SDHCI_CDNS_HRS04_RD BIT(25)
+#define SDHCI_CDNS_HRS04_WR BIT(24)
+#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
+#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
+#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
+
+#define SDHCI_CDNS_HRS05 0x14
+
+/* eMMC control */
+#define SDHCI_CDNS_HRS06 0x18
+#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
+#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
+#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
+#define SDHCI_CDNS_HRS06_MODE_SD 0x0
+#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
+#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
+
+/* SRS - Slot Register Set (SDHCI-compatible) */
+#define SDHCI_CDNS_SRS_BASE 0x200
+
+/* Cadence V4 PHY Setting*/
+#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
+#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
+#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
+#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
+#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
+#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
+#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
+#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
+#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
+
+/*
+ * The tuned val register is 6 bit-wide, but not the whole of the range is
+ * available. The range 0-42 seems to be available (then 43 wraps around to 0)
+ * but I am not quite sure if it is official. Use only 0 to 39 for safety.
+ */
+#define SDHCI_CDNS_MAX_TUNING_LOOP 40
+
+struct sdhci_cdns_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+ void __iomem *hrs_addr;
+};
+
+int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode);
+int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat);
+int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val);
+
+#endif
diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c
new file mode 100644
index 00000000000..a5ed87321ab
--- /dev/null
+++ b/drivers/mmc/sdhci-cadence6.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-or-platform_driver
+/*
+ * Copyright (C) 2023 Starfive.
+ * Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
+ */
+
+#include <dm.h>
+#include <asm/global_data.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/libfdt.h>
+#include <mmc.h>
+#include <sdhci.h>
+#include "sdhci-cadence.h"
+
+/* IO Delay Information */
+#define SDHCI_CDNS_HRS07 0X1C
+#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16)
+#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0)
+
+/* PHY Control and Status */
+#define SDHCI_CDNS_HRS09 0x24
+#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16)
+#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15)
+#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3)
+#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2)
+#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1)
+#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0)
+
+/* SDCLK adjustment */
+#define SDHCI_CDNS_HRS10 0x28
+#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16)
+
+/* CMD/DAT output delay */
+#define SDHCI_CDNS_HRS16 0x40
+
+/* PHY Special Function Registers */
+/* register to control the DQ related timing */
+#define PHY_DQ_TIMING_REG_ADDR 0x2000
+
+/* register to control the DQS related timing */
+#define PHY_DQS_TIMING_REG_ADDR 0x2004
+
+/* register to control the gate and loopback control related timing */
+#define PHY_GATE_LPBK_CTRL_REG_ADDR 0x2008
+
+/* register to control the Master DLL logic */
+#define PHY_DLL_MASTER_CTRL_REG_ADDR 0x200C
+
+/* register to control the Slave DLL logic */
+#define PHY_DLL_SLAVE_CTRL_REG_ADDR 0x2010
+#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY GENMASK(31, 24)
+#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY GENMASK(7, 0)
+
+#define SDHCI_CDNS6_PHY_CFG_NUM 4
+#define SDHCI_CDNS6_CTRL_CFG_NUM 4
+
+struct sdhci_cdns6_phy_cfg {
+ const char *property;
+ u32 val;
+};
+
+struct sdhci_cdns6_ctrl_cfg {
+ const char *property;
+ u32 val;
+};
+
+static struct sdhci_cdns6_phy_cfg sd_ds_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-sd-ds", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-sd-ds", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-sd-ds", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-sd-ds", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_sdr_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-semmc-sdr", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-sdr", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-sdr", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-emmc-sdr", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_ddr_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-ddr", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-ddr", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-ddr", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-emmc-ddr", 0x10000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_hs200_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-hs200", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs200", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-hs200", 0x00DADA00, },
+ { "cdns,phy-dq-timing-delay-emmc-hs200", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_hs400_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-hs400", 0x00280004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs400", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-hs400", 0x00DAD800, },
+ { "cdns,phy-dq-timing-delay-emmc-hs400", 0x00000001, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg sd_ds_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-sd-ds", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-sd-ds", 0x00020000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-sd-ds", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-sd-ds", 0x00080000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_sdr_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-sdr", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-sdr", 0x00030000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-sdr", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-sdr", 0x00080000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_ddr_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-ddr", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-ddr", 0x00020000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-ddr", 0x11000001, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-ddr", 0x00090001, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_hs200_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-hs200", 0x00018000, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs200", 0x00080000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs200", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-hs200", 0x00090000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_hs400_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-hs400", 0x00018000, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs400", 0x00080000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs400", 0x11000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-hs400", 0x00080000, },
+};
+
+static u32 sdhci_cdns6_read_phy_reg(struct sdhci_cdns_plat *plat, u32 addr)
+{
+ writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
+ return readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u32 val)
+{
+ writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
+ writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
+{
+ void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
+ u32 tmp;
+ int ret;
+
+ tmp = readl(reg);
+ tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+
+ /* Switch On DLL Reset */
+ if (reset)
+ tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 0);
+ else
+ tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 1);
+
+ writel(tmp, reg);
+
+ /* After reset, wait until HRS09.PHY_INIT_COMPLETE is set to 1 within 3000us*/
+ if (!reset) {
+ ret = readl_poll_timeout(reg, tmp, (tmp & SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
+ 3000);
+ }
+
+ return ret;
+}
+
+int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ struct sdhci_cdns6_phy_cfg *sdhci_cdns6_phy_cfgs;
+ struct sdhci_cdns6_ctrl_cfg *sdhci_cdns6_ctrl_cfgs;
+ const fdt32_t *prop;
+ u32 tmp;
+ int i, ret;
+
+ switch (mode) {
+ case SDHCI_CDNS_HRS06_MODE_SD:
+ sdhci_cdns6_phy_cfgs = sd_ds_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = sd_ds_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_SDR:
+ sdhci_cdns6_phy_cfgs = emmc_sdr_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_sdr_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_DDR:
+ sdhci_cdns6_phy_cfgs = emmc_ddr_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_ddr_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_HS200:
+ sdhci_cdns6_phy_cfgs = emmc_hs200_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_hs200_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_HS400:
+ sdhci_cdns6_phy_cfgs = emmc_hs400_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_hs400_ctrl_cfgs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDHCI_CDNS6_PHY_CFG_NUM; i++) {
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ sdhci_cdns6_phy_cfgs[i].property, NULL);
+ if (prop)
+ sdhci_cdns6_phy_cfgs[i].val = *prop;
+ }
+
+ for (i = 0; i < SDHCI_CDNS6_CTRL_CFG_NUM; i++) {
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ sdhci_cdns6_ctrl_cfgs[i].property, NULL);
+ if (prop)
+ sdhci_cdns6_ctrl_cfgs[i].val = *prop;
+ }
+
+ /* Switch On the DLL Reset */
+ sdhci_cdns6_reset_phy_dll(plat, true);
+
+ sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
+ sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
+
+ /* Switch Off the DLL Reset */
+ ret = sdhci_cdns6_reset_phy_dll(plat, false);
+ if (ret) {
+ printf("sdhci_cdns6_reset_phy is not completed\n");
+ return ret;
+ }
+
+ /* Set PHY DQ TIMING control register */
+ sdhci_cdns6_write_phy_reg(plat, PHY_DQ_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[3].val);
+
+ /* Set HRS09 register */
+ tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS09);
+ tmp &= ~(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
+ SDHCI_CDNS_HRS09_EXTENDED_RD_MODE |
+ SDHCI_CDNS_HRS09_RDDATA_EN |
+ SDHCI_CDNS_HRS09_RDCMD_EN);
+ tmp |= sdhci_cdns6_ctrl_cfgs[0].val;
+ writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS09);
+
+ /* Set HRS10 register */
+ tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS10);
+ tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
+ tmp |= sdhci_cdns6_ctrl_cfgs[1].val;
+ writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS10);
+
+ /* Set HRS16 register */
+ writel(sdhci_cdns6_ctrl_cfgs[2].val, plat->hrs_addr + SDHCI_CDNS_HRS16);
+
+ /* Set HRS07 register */
+ writel(sdhci_cdns6_ctrl_cfgs[3].val, plat->hrs_addr + SDHCI_CDNS_HRS07);
+
+ return 0;
+}
+
+int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat)
+{
+ return sdhci_cdns6_phy_adj(dev, plat, SDHCI_CDNS_HRS06_MODE_SD);
+}
+
+int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
+{
+ u32 tmp, tuneval;
+
+ tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP;
+
+ tmp = sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
+ tmp &= ~(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY |
+ PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY);
+ tmp |= FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) |
+ FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY, tuneval);
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
+
+ return 0;
+}
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 560b7e889c7..4833b5158c7 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -32,8 +32,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
- printf("%s: Reset 0x%x never completed.\n",
- __func__, (int)mask);
+ log_warning("Reset %#x never completed\n", mask);
return;
}
timeout--;
@@ -139,8 +138,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data)
do {
stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR) {
- pr_debug("%s: Error detected in status(0x%X)!\n",
- __func__, stat);
+ log_debug("Error detected in status(%#x)!\n", stat);
return -EIO;
}
if (!transfer_done && (stat & rdy)) {
@@ -173,7 +171,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data)
if (timeout-- > 0)
udelay(10);
else {
- printf("%s: Transfer data timeout\n", __func__);
+ log_err("Transfer data timeout\n");
return -ETIMEDOUT;
}
} while (!(stat & SDHCI_INT_DATA_END));
@@ -232,13 +230,13 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (time >= cmd_timeout) {
- printf("%s: MMC: %d busy ", __func__, mmc_dev);
+ log_warning("mmc%d busy ", mmc_dev);
if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) {
cmd_timeout += cmd_timeout;
- printf("timeout increasing to: %u ms.\n",
- cmd_timeout);
+ log_warning("timeout increasing to: %u ms\n",
+ cmd_timeout);
} else {
- puts("timeout.\n");
+ log_warning("timeout\n");
return -ECOMM;
}
}
@@ -316,8 +314,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
}
if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) {
- printf("%s: Timeout for status update: %08x %08x\n",
- __func__, stat, mask);
+ log_warning("Timeout for status update: %08x %08x\n",
+ stat, mask);
return -ETIMEDOUT;
}
} while ((stat & mask) != mask);
@@ -358,7 +356,7 @@ static int sdhci_execute_tuning(struct udevice *dev, uint opcode)
struct mmc *mmc = mmc_get_mmc_dev(dev);
struct sdhci_host *host = mmc->priv;
- debug("%s\n", __func__);
+ log_debug("sdhci tuning\n");
if (host->ops && host->ops->platform_execute_tuning) {
err = host->ops->platform_execute_tuning(mmc, opcode);
@@ -380,8 +378,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
while (sdhci_readl(host, SDHCI_PRESENT_STATE) &
(SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
if (timeout == 0) {
- printf("%s: Timeout to wait cmd & data inhibit\n",
- __func__);
+ log_err("Timeout waiting for cmd & data inhibit\n");
return -EBUSY;
}
@@ -397,7 +394,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->set_delay) {
ret = host->ops->set_delay(host);
if (ret) {
- printf("%s: Error while setting tap delay\n", __func__);
+ log_err("Error while setting tap delay\n");
return ret;
}
}
@@ -405,7 +402,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->config_dll) {
ret = host->ops->config_dll(host, clock, false);
if (ret) {
- printf("%s: Error while configuring dll\n", __func__);
+ log_err("Error configuring dll\n");
return ret;
}
}
@@ -456,7 +453,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->config_dll) {
ret = host->ops->config_dll(host, clock, true);
if (ret) {
- printf("%s: Error while configuring dll\n", __func__);
+ log_err("Error while configuring dll\n");
return ret;
}
}
@@ -472,8 +469,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
- printf("%s: Internal clock never stabilised.\n",
- __func__);
+ log_err("Internal clock never stabilised.\n");
return -EBUSY;
}
timeout--;
@@ -738,8 +734,7 @@ static int sdhci_init(struct mmc *mmc)
if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) {
host->align_buffer = memalign(8, 512 * 1024);
if (!host->align_buffer) {
- printf("%s: Aligned buffer alloc failed!!!\n",
- __func__);
+ log_err("Aligned buffer alloc failed\n");
return -ENOMEM;
}
}
@@ -881,20 +876,18 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
#else
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
#endif
- debug("%s, caps: 0x%x\n", __func__, caps);
+ log_debug("caps: %#x\n", caps);
#if CONFIG_IS_ENABLED(MMC_SDHCI_SDMA)
if ((caps & SDHCI_CAN_DO_SDMA)) {
host->flags |= USE_SDMA;
} else {
- debug("%s: Your controller doesn't support SDMA!!\n",
- __func__);
+ log_debug("Controller doesn't support SDMA\n");
}
#endif
#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
if (!(caps & SDHCI_CAN_DO_ADMA2)) {
- printf("%s: Your controller doesn't support ADMA!!\n",
- __func__);
+ log_err("Controller doesn't support ADMA\n");
return -EINVAL;
}
if (!host->adma_desc_table) {
@@ -927,7 +920,7 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
#else
caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
#endif
- debug("%s, caps_1: 0x%x\n", __func__, caps_1);
+ log_debug("caps_1: %#x\n", caps_1);
host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
SDHCI_CLOCK_MUL_SHIFT;
@@ -953,8 +946,7 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
host->max_clk *= host->clk_mul;
}
if (host->max_clk == 0) {
- printf("%s: Hardware doesn't specify base clock frequency\n",
- __func__);
+ log_err("Hardware doesn't specify base clock frequency\n");
return -EINVAL;
}
if (f_max && (f_max < host->max_clk))
@@ -1047,7 +1039,7 @@ int add_sdhci(struct sdhci_host *host, u32 f_max, u32 f_min)
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL) {
- printf("%s: mmc create fail!\n", __func__);
+ log_err("mmc create fail\n");
return -ENOMEM;
}
diff --git a/drivers/mmc/snps_dw_mmc.c b/drivers/mmc/snps_dw_mmc.c
index f30331e51f7..47ab5654bd6 100644
--- a/drivers/mmc/snps_dw_mmc.c
+++ b/drivers/mmc/snps_dw_mmc.c
@@ -12,6 +12,7 @@
#include <dwmmc.h>
#include <errno.h>
#include <fdtdec.h>
+#include <asm/gpio.h>
#include <dm/device_compat.h>
#include <linux/libfdt.h>
#include <linux/err.h>
@@ -29,6 +30,7 @@ struct snps_dwmci_plat {
struct snps_dwmci_priv_data {
struct dwmci_host host;
u32 f_max;
+ struct gpio_desc cd_gpio;
};
static int snps_dwmmc_clk_setup(struct udevice *dev)
@@ -104,6 +106,10 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev)
if (!ret && priv->f_max < CLOCK_MIN)
return -EINVAL;
+ if (CONFIG_IS_ENABLED(DM_GPIO))
+ gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
+ GPIOD_IS_IN);
+
host->fifo_mode = dev_read_bool(dev, "fifo-mode");
host->name = dev->name;
host->dev_index = 0;
@@ -117,6 +123,9 @@ int snps_dwmmc_getcd(struct udevice *dev)
struct snps_dwmci_priv_data *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
+ if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&priv->cd_gpio))
+ return dm_gpio_get_value(&priv->cd_gpio);
+
return !(dwmci_readl(host, DWMCI_CDETECT) & 1);
}