diff options
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r-- | drivers/mmc/host/sdhci-of-arasan.c | 629 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pltfm.c | 3 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 3 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 2 |
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 */ |