aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c629
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c3
-rw-r--r--drivers/mmc/host/sdhci.c3
-rw-r--r--drivers/mmc/host/sdhci.h2
4 files changed, 632 insertions, 5 deletions
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index b12abf9b15f2..c72382d9ea9d 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -18,20 +18,63 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/phy/phy.h>
+#include <linux/mmc/mmc.h>
+#include <linux/soc/xilinx/zynqmp/tap_delays.h>
+#include <linux/soc/xilinx/zynqmp/fw.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <linux/of.h>
+#include <linux/slab.h>
#include "cqhci.h"
#include "sdhci-pltfm.h"
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
+#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
+#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
+
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define VENDOR_ENHANCED_STROBE BIT(0)
+#define CLK_CTRL_TIMEOUT_SHIFT 16
+#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
+#define CLK_CTRL_TIMEOUT_MIN_EXP 13
+#define SD_CLK_25_MHZ 25000000
+#define SD_CLK_19_MHZ 19000000
+#define MAX_TUNING_LOOP 40
#define PHY_CLK_TOO_SLOW_HZ 400000
+#define SDHCI_ITAPDLY_CHGWIN 0x200
+#define SDHCI_ITAPDLY_ENABLE 0x100
+#define SDHCI_OTAPDLY_ENABLE 0x40
+
+#define SDHCI_ITAPDLYSEL_SD_HSD 0x15
+#define SDHCI_ITAPDLYSEL_SDR25 0x15
+#define SDHCI_ITAPDLYSEL_SDR50 0x0
+#define SDHCI_ITAPDLYSEL_SDR104_B2 0x0
+#define SDHCI_ITAPDLYSEL_SDR104_B0 0x0
+#define SDHCI_ITAPDLYSEL_MMC_HSD 0x15
+#define SDHCI_ITAPDLYSEL_SD_DDR50 0x3D
+#define SDHCI_ITAPDLYSEL_MMC_DDR52 0x12
+#define SDHCI_ITAPDLYSEL_MMC_HS200_B2 0x0
+#define SDHCI_ITAPDLYSEL_MMC_HS200_B0 0x0
+#define SDHCI_OTAPDLYSEL_SD_HSD 0x05
+#define SDHCI_OTAPDLYSEL_SDR25 0x05
+#define SDHCI_OTAPDLYSEL_SDR50 0x03
+#define SDHCI_OTAPDLYSEL_SDR104_B0 0x03
+#define SDHCI_OTAPDLYSEL_SDR104_B2 0x02
+#define SDHCI_OTAPDLYSEL_MMC_HSD 0x06
+#define SDHCI_OTAPDLYSEL_SD_DDR50 0x04
+#define SDHCI_OTAPDLYSEL_MMC_DDR52 0x06
+#define SDHCI_OTAPDLYSEL_MMC_HS200_B0 0x03
+#define SDHCI_OTAPDLYSEL_MMC_HS200_B2 0x02
+
+#define MMC_BANK2 0x2
+
/*
* On some SoCs the syscon area has a feature where the upper 16-bits of
* each 32-bit register act as a write mask for the lower 16-bits. This allows
@@ -86,6 +129,10 @@ struct sdhci_arasan_data {
struct sdhci_host *host;
struct clk *clk_ahb;
struct phy *phy;
+ u32 mio_bank;
+ u32 device_id;
+ u32 itapdly[MMC_TIMING_MMC_HS400 + 1];
+ u32 otapdly[MMC_TIMING_MMC_HS400 + 1];
bool is_phy_on;
bool has_cqe;
@@ -93,6 +140,8 @@ struct sdhci_arasan_data {
struct clk *sdcardclk;
struct regmap *soc_ctl_base;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
unsigned int quirks; /* Arasan deviations from spec */
@@ -101,6 +150,8 @@ struct sdhci_arasan_data {
/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
* internal clock even when the clock isn't stable */
#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1)
+/* Controller has tap delay setting registers in it's local reg space */
+#define SDHCI_ARASAN_TAPDELAY_REG_LOCAL BIT(2)
};
struct sdhci_arasan_of_data {
@@ -164,6 +215,236 @@ static int sdhci_arasan_syscon_write(struct sdhci_host *host,
return ret;
}
+static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 deviceid)
+{
+ u16 clk;
+ unsigned long timeout;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Issue DLL Reset */
+ zynqmp_dll_reset(deviceid);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 20 ms */
+ timeout = 20;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ dev_err(mmc_dev(host->mmc),
+ ": Internal clock never stabilised.\n");
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+static int arasan_zynqmp_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_host *mmc = host->mmc;
+ u16 ctrl;
+ int tuning_loop_counter = MAX_TUNING_LOOP;
+ int err = 0;
+ unsigned long flags;
+ unsigned int tuning_count = 0;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = host->tuning_count;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND)
+ ctrl |= SDHCI_CTRL_TUNED_CLK;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ mdelay(1);
+
+ arasan_zynqmp_dll_reset(host, sdhci_arasan->device_id);
+
+ /*
+ * As per the Host Controller spec v3.00, tuning command
+ * generates Buffer Read Ready interrupt, so enable that.
+ *
+ * Note: The spec clearly says that when tuning sequence
+ * is being performed, the controller does not generate
+ * interrupts other than Buffer Read Ready interrupt. But
+ * to make sure we don't hit a controller bug, we _only_
+ * enable Buffer Read Ready interrupt here.
+ */
+ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
+
+ /*
+ * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches 40 times or a timeout of 150ms occurs.
+ */
+ do {
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {NULL};
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.retries = 0;
+ cmd.data = NULL;
+ cmd.mrq = &mrq;
+ cmd.error = 0;
+
+ if (tuning_loop_counter-- == 0)
+ break;
+
+ mrq.cmd = &cmd;
+
+ /*
+ * In response to CMD19, the card sends 64 bytes of tuning
+ * block to the Host Controller. So we set the block size
+ * to 64 here.
+ */
+ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+ SDHCI_BLOCK_SIZE);
+ } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ }
+ } else {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ }
+
+ /*
+ * The tuning block is sent by the card to the host controller.
+ * So we set the TRNS_READ bit in the Transfer Mode register.
+ * This also takes care of setting DMA Enable and Multi Block
+ * Select in the same register to 0.
+ */
+ sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
+
+ sdhci_send_command(host, &cmd);
+
+ host->cmd = NULL;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ /* Wait for Buffer Read Ready interrupt */
+ wait_event_interruptible_timeout(host->buf_ready_int,
+ (host->tuning_done == 1),
+ msecs_to_jiffies(50));
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->tuning_done) {
+ dev_warn(mmc_dev(host->mmc),
+ ": Timeout for Buffer Read Ready interrupt, back to fixed sampling clock\n");
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ err = -EIO;
+ goto out;
+ }
+
+ host->tuning_done = 0;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ /* eMMC spec does not require a delay between tuning cycles */
+ if (opcode == MMC_SEND_TUNING_BLOCK)
+ mdelay(1);
+ } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
+
+ /*
+ * The Host Driver has exhausted the maximum number of loops allowed,
+ * so use fixed sampling frequency.
+ */
+ if (tuning_loop_counter < 0) {
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ }
+ if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
+ dev_warn(mmc_dev(host->mmc),
+ ": Tuning failed, back to fixed sampling clock\n");
+ err = -EIO;
+ } else {
+ arasan_zynqmp_dll_reset(host, sdhci_arasan->device_id);
+ }
+
+out:
+ /*
+ * In case tuning fails, host controllers which support
+ * re-tuning can try tuning again at a later time, when the
+ * re-tuning timer expires. So for these controllers, we
+ * return 0. Since there might be other controllers who do not
+ * have this capability, we return error for them.
+ */
+ if (tuning_count)
+ err = 0;
+
+ host->mmc->retune_period = err ? 0 : tuning_count;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return err;
+}
+
+static void __arasan_set_tap_delay(struct sdhci_host *host, u8 itap_delay,
+ u8 otap_delay)
+{
+ u32 regval;
+
+ if (itap_delay) {
+ regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= SDHCI_ITAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval |= itap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ regval &= ~SDHCI_ITAPDLY_CHGWIN;
+ sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+ }
+
+ if (otap_delay) {
+ regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= SDHCI_OTAPDLY_ENABLE;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ regval |= otap_delay;
+ sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+ }
+}
+
+static void arasan_set_tap_delay(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ u8 itap_delay;
+ u8 otap_delay;
+
+ itap_delay = sdhci_arasan->itapdly[host->timing];
+ otap_delay = sdhci_arasan->otapdly[host->timing];
+
+ if (sdhci_arasan->quirks & SDHCI_ARASAN_TAPDELAY_REG_LOCAL)
+ __arasan_set_tap_delay(host, itap_delay, otap_delay);
+ else
+ arasan_zynqmp_set_tap_delay(sdhci_arasan->device_id,
+ itap_delay, otap_delay);
+}
+
static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -204,6 +485,17 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+ /* Set the Input and Output Tap Delays */
+ if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_STANDARD_25_BROKEN) &&
+ (host->version >= SDHCI_SPEC_300)) {
+ if (clock == SD_CLK_25_MHZ)
+ clock = SD_CLK_19_MHZ;
+ if ((host->timing != MMC_TIMING_LEGACY) &&
+ (host->timing != MMC_TIMING_UHS_SDR12)) {
+ arasan_set_tap_delay(host);
+ }
+ }
+
if (ctrl_phy && sdhci_arasan->is_phy_on) {
phy_power_off(sdhci_arasan->phy);
sdhci_arasan->is_phy_on = false;
@@ -290,7 +582,7 @@ static void sdhci_arasan_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power_noreg(host, mode, vdd);
}
-static const struct sdhci_ops sdhci_arasan_ops = {
+static struct sdhci_ops sdhci_arasan_ops = {
.set_clock = sdhci_arasan_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
@@ -302,7 +594,6 @@ static const struct sdhci_ops sdhci_arasan_ops = {
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
.ops = &sdhci_arasan_ops,
- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
@@ -373,6 +664,75 @@ static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
.pdata = &sdhci_arasan_cqe_pdata,
};
+#ifdef CONFIG_PM
+/**
+ * sdhci_arasan_runtime_suspend - Suspend method for the driver
+ * @dev: Address of the device structure
+ * Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_arasan_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ ret = sdhci_runtime_suspend_host(host);
+ if (ret)
+ return ret;
+
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+ mmc_retune_needed(host->mmc);
+
+ clk_disable(pltfm_host->clk);
+ clk_disable(sdhci_arasan->clk_ahb);
+
+ return 0;
+}
+
+/**
+ * sdhci_arasan_runtime_resume - Resume method for the driver
+ * @dev: Address of the device structure
+ * Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_arasan_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ ret = clk_enable(sdhci_arasan->clk_ahb);
+ if (ret) {
+ dev_err(dev, "Cannot enable AHB clock.\n");
+ return ret;
+ }
+
+ ret = clk_enable(pltfm_host->clk);
+ if (ret) {
+ dev_err(dev, "Cannot enable SD clock.\n");
+ return ret;
+ }
+
+ ret = sdhci_runtime_resume_host(host, 0);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ clk_disable(pltfm_host->clk);
+ clk_disable(sdhci_arasan->clk_ahb);
+
+ return ret;
+}
+#endif /* ! CONFIG_PM */
+
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
@@ -465,8 +825,11 @@ static int sdhci_arasan_resume(struct device *dev)
}
#endif /* ! CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
- sdhci_arasan_resume);
+static const struct dev_pm_ops sdhci_arasan_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_arasan_suspend, sdhci_arasan_resume)
+ SET_RUNTIME_PM_OPS(sdhci_arasan_runtime_suspend,
+ sdhci_arasan_runtime_resume, NULL)
+};
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
@@ -487,6 +850,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
.compatible = "arasan,sdhci-4.9a",
.data = &sdhci_arasan_data,
},
+ {
+ .compatible = "xlnx,zynqmp-8.9a",
+ .data = &sdhci_arasan_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
@@ -714,6 +1081,181 @@ cleanup:
return ret;
}
+/**
+ * arasan_zynqmp_dt_parse_tap_delays - Read Tap Delay values from DT
+ *
+ * Called at initialization to parse the values of Tap Delays.
+ *
+ * @dev: Pointer to our struct device.
+ */
+static void arasan_zynqmp_dt_parse_tap_delays(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ struct device_node *np = dev->of_node;
+ u32 *itapdly = sdhci_arasan->itapdly;
+ u32 *otapdly = sdhci_arasan->otapdly;
+ int ret;
+
+ /*
+ * Read Tap Delay values from DT, if the DT does not contain the
+ * Tap Values then use the pre-defined values
+ */
+ ret = of_property_read_u32(np, "xlnx,itap-delay-sd-hsd",
+ &itapdly[MMC_TIMING_SD_HS]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_SD_HS\n");
+ itapdly[MMC_TIMING_SD_HS] = SDHCI_ITAPDLYSEL_SD_HSD;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-sd-hsd",
+ &otapdly[MMC_TIMING_SD_HS]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_SD_HS\n");
+ otapdly[MMC_TIMING_SD_HS] = SDHCI_OTAPDLYSEL_SD_HSD;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-sdr25",
+ &itapdly[MMC_TIMING_UHS_SDR25]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_UHS_SDR25\n");
+ itapdly[MMC_TIMING_UHS_SDR25] = SDHCI_ITAPDLYSEL_SDR25;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-sdr25",
+ &otapdly[MMC_TIMING_UHS_SDR25]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_UHS_SDR25\n");
+ otapdly[MMC_TIMING_UHS_SDR25] = SDHCI_OTAPDLYSEL_SDR25;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-sdr50",
+ &itapdly[MMC_TIMING_UHS_SDR50]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_UHS_SDR50\n");
+ itapdly[MMC_TIMING_UHS_SDR50] = SDHCI_ITAPDLYSEL_SDR50;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-sdr50",
+ &otapdly[MMC_TIMING_UHS_SDR50]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_UHS_SDR50\n");
+ otapdly[MMC_TIMING_UHS_SDR50] = SDHCI_OTAPDLYSEL_SDR50;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-sd-ddr50",
+ &itapdly[MMC_TIMING_UHS_DDR50]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_UHS_DDR50\n");
+ itapdly[MMC_TIMING_UHS_DDR50] = SDHCI_ITAPDLYSEL_SD_DDR50;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-sd-ddr50",
+ &otapdly[MMC_TIMING_UHS_DDR50]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_UHS_DDR50\n");
+ otapdly[MMC_TIMING_UHS_DDR50] = SDHCI_OTAPDLYSEL_SD_DDR50;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-mmc-hsd",
+ &itapdly[MMC_TIMING_MMC_HS]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_MMC_HS\n");
+ itapdly[MMC_TIMING_MMC_HS] = SDHCI_ITAPDLYSEL_MMC_HSD;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-mmc-hsd",
+ &otapdly[MMC_TIMING_MMC_HS]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_MMC_HS\n");
+ otapdly[MMC_TIMING_MMC_HS] = SDHCI_OTAPDLYSEL_MMC_HSD;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-mmc-ddr52",
+ &itapdly[MMC_TIMING_MMC_DDR52]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_MMC_DDR52\n");
+ itapdly[MMC_TIMING_MMC_DDR52] = SDHCI_ITAPDLYSEL_MMC_DDR52;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-mmc-ddr52",
+ &otapdly[MMC_TIMING_MMC_DDR52]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_MMC_DDR52\n");
+ otapdly[MMC_TIMING_MMC_DDR52] = SDHCI_OTAPDLYSEL_MMC_DDR52;
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-sdr104",
+ &itapdly[MMC_TIMING_UHS_SDR104]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_UHS_SDR104\n");
+ if (sdhci_arasan->mio_bank == MMC_BANK2) {
+ itapdly[MMC_TIMING_UHS_SDR104] =
+ SDHCI_ITAPDLYSEL_SDR104_B2;
+ } else {
+ itapdly[MMC_TIMING_UHS_SDR104] =
+ SDHCI_ITAPDLYSEL_SDR104_B0;
+ }
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-sdr104",
+ &otapdly[MMC_TIMING_UHS_SDR104]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_UHS_SDR104\n");
+ if (sdhci_arasan->mio_bank == MMC_BANK2) {
+ otapdly[MMC_TIMING_UHS_SDR104] =
+ SDHCI_OTAPDLYSEL_SDR104_B2;
+ } else {
+ otapdly[MMC_TIMING_UHS_SDR104] =
+ SDHCI_OTAPDLYSEL_SDR104_B0;
+ }
+ }
+
+ ret = of_property_read_u32(np, "xlnx,itap-delay-mmc-hs200",
+ &itapdly[MMC_TIMING_MMC_HS200]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined itapdly for MMC_TIMING_MMC_HS200\n");
+ if (sdhci_arasan->mio_bank == MMC_BANK2) {
+ itapdly[MMC_TIMING_MMC_HS200] =
+ SDHCI_ITAPDLYSEL_MMC_HS200_B2;
+ } else {
+ itapdly[MMC_TIMING_MMC_HS200] =
+ SDHCI_ITAPDLYSEL_MMC_HS200_B0;
+ }
+ }
+
+ ret = of_property_read_u32(np, "xlnx,otap-delay-mmc-hs200",
+ &otapdly[MMC_TIMING_MMC_HS200]);
+ if (ret) {
+ dev_dbg(dev,
+ "Using predefined otapdly for MMC_TIMING_MMC_HS200\n");
+ if (sdhci_arasan->mio_bank == MMC_BANK2) {
+ otapdly[MMC_TIMING_MMC_HS200] =
+ SDHCI_OTAPDLYSEL_MMC_HS200_B2;
+ } else {
+ otapdly[MMC_TIMING_MMC_HS200] =
+ SDHCI_OTAPDLYSEL_MMC_HS200_B0;
+ }
+ }
+}
+
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
@@ -724,10 +1266,35 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_arasan_data *sdhci_arasan;
struct device_node *np = pdev->dev.of_node;
+ unsigned int host_quirks2 = 0;
const struct sdhci_arasan_of_data *data;
match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
data = match->data;
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a")) {
+ char *soc_rev;
+
+ /* read Silicon version using nvmem driver */
+ soc_rev = zynqmp_nvmem_get_silicon_version(&pdev->dev,
+ "soc_revision");
+ if (PTR_ERR(soc_rev) == -EPROBE_DEFER)
+ /* Do a deferred probe */
+ return -EPROBE_DEFER;
+ else if (IS_ERR(soc_rev))
+ dev_dbg(&pdev->dev, "Error getting silicon version\n");
+
+ /* Set host quirk if the silicon version is v1.0 */
+ if (!IS_ERR(soc_rev) && (*soc_rev == ZYNQMP_SILICON_V1))
+ host_quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+
+ /* Clean soc_rev if got a valid pointer from nvmem driver
+ * else we may end up in kernel panic
+ */
+ if (!IS_ERR(soc_rev))
+ kfree(soc_rev);
+ }
+
host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
if (IS_ERR(host))
@@ -739,6 +1306,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
+ host->quirks2 |= host_quirks2;
+
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
if (node) {
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
@@ -787,6 +1356,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (of_property_read_bool(np, "xlnx,int-clock-stable-broken"))
sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE;
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-8.9a"))
+ sdhci_arasan->quirks |= SDHCI_ARASAN_TAPDELAY_REG_LOCAL;
+
pltfm_host->clk = clk_xin;
if (of_device_is_compatible(pdev->dev.of_node,
@@ -806,6 +1378,48 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
goto unreg_clk;
}
+ if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-8.9a")) {
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ host->quirks2 |= SDHCI_QUIRK2_CLOCK_STANDARD_25_BROKEN;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "xlnx,zynqmp-8.9a") ||
+ of_device_is_compatible(pdev->dev.of_node, "xlnx,versal-8.9a")) {
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,mio_bank",
+ &sdhci_arasan->mio_bank);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "\"xlnx,mio_bank \" property is missing.\n");
+ goto clk_disable_all;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,device_id",
+ &sdhci_arasan->device_id);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "\"xlnx,device_id \" property is missing.\n");
+ goto clk_disable_all;
+ }
+
+ arasan_zynqmp_dt_parse_tap_delays(&pdev->dev);
+
+ sdhci_arasan_ops.platform_execute_tuning =
+ arasan_zynqmp_execute_tuning;
+ }
+
+ sdhci_arasan->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!IS_ERR(sdhci_arasan->pinctrl)) {
+ sdhci_arasan->pins_default = pinctrl_lookup_state(
+ sdhci_arasan->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(sdhci_arasan->pins_default)) {
+ dev_err(&pdev->dev, "Missing default pinctrl config\n");
+ return IS_ERR(sdhci_arasan->pins_default);
+ }
+
+ pinctrl_select_state(sdhci_arasan->pinctrl,
+ sdhci_arasan->pins_default);
+ }
+
sdhci_arasan->phy = ERR_PTR(-ENODEV);
if (of_device_is_compatible(pdev->dev.of_node,
"arasan,sdhci-5.1")) {
@@ -838,6 +1452,13 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (ret)
goto err_add_host;
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_forbid(&pdev->dev);
+
return 0;
err_add_host:
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index d268b3b8850a..b7833fa4f0a2 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -102,6 +102,9 @@ void sdhci_get_property(struct platform_device *pdev)
sdhci_get_compatibility(pdev);
+ if (device_property_present(dev, "broken-mmc-highspeed"))
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+
device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock);
if (device_property_present(dev, "keep-power-in-suspend"))
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 5a8d97a8f1e1..30df594d267a 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3965,7 +3965,8 @@ int sdhci_setup_host(struct sdhci_host *host)
if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
mmc->caps &= ~MMC_CAP_CMD23;
- if (host->caps & SDHCI_CAN_DO_HISPD)
+ if ((host->caps & SDHCI_CAN_DO_HISPD) &&
+ !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index f69bebe51520..187ef75307e1 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -483,6 +483,8 @@ struct sdhci_host {
* block count.
*/
#define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18)
+/* Broken Clock between 19MHz-25MHz */
+#define SDHCI_QUIRK2_CLOCK_STANDARD_25_BROKEN (1<<19)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */