// SPDX-License-Identifier: GPL-2.0-or-later /* * Arasan Secure Digital Host Controller Interface. * Copyright (C) 2011 - 2012 Michal Simek * Copyright (c) 2012 Wind River Systems, Inc. * Copyright (C) 2013 Pengutronix e.K. * Copyright (C) 2013 Xilinx Inc. * * Based on sdhci-of-esdhc.c * * Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * * Authors: Xiaobo Xie * Anton Vorontsov */ #include #include #include #include #include #include #include #include #include #include "cqhci.h" #include "sdhci-cqhci.h" #include "sdhci-pltfm.h" #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 #define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8 #define SDHCI_ARASAN_ITAPDLY_SEL_MASK 0xFF #define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC #define SDHCI_ARASAN_OTAPDLY_SEL_MASK 0x3F #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 #define VENDOR_ENHANCED_STROBE BIT(0) #define PHY_CLK_TOO_SLOW_HZ 400000 #define MIN_PHY_CLK_HZ 50000000 #define SDHCI_ITAPDLY_CHGWIN 0x200 #define SDHCI_ITAPDLY_ENABLE 0x100 #define SDHCI_OTAPDLY_ENABLE 0x40 #define PHY_CTRL_REG1 0x270 #define PHY_CTRL_ITAPDLY_ENA_MASK BIT(0) #define PHY_CTRL_ITAPDLY_SEL_MASK GENMASK(5, 1) #define PHY_CTRL_ITAPDLY_SEL_SHIFT 1 #define PHY_CTRL_ITAP_CHG_WIN_MASK BIT(6) #define PHY_CTRL_OTAPDLY_ENA_MASK BIT(8) #define PHY_CTRL_OTAPDLY_SEL_MASK GENMASK(15, 12) #define PHY_CTRL_OTAPDLY_SEL_SHIFT 12 #define PHY_CTRL_STRB_SEL_MASK GENMASK(23, 16) #define PHY_CTRL_STRB_SEL_SHIFT 16 #define PHY_CTRL_TEST_CTRL_MASK GENMASK(31, 24) #define PHY_CTRL_REG2 0x274 #define PHY_CTRL_EN_DLL_MASK BIT(0) #define PHY_CTRL_DLL_RDY_MASK BIT(1) #define PHY_CTRL_FREQ_SEL_MASK GENMASK(6, 4) #define PHY_CTRL_FREQ_SEL_SHIFT 4 #define PHY_CTRL_SEL_DLY_TX_MASK BIT(16) #define PHY_CTRL_SEL_DLY_RX_MASK BIT(17) #define FREQSEL_200M_170M 0x0 #define FREQSEL_170M_140M 0x1 #define FREQSEL_140M_110M 0x2 #define FREQSEL_110M_80M 0x3 #define FREQSEL_80M_50M 0x4 #define FREQSEL_275M_250M 0x5 #define FREQSEL_250M_225M 0x6 #define FREQSEL_225M_200M 0x7 #define PHY_DLL_TIMEOUT_MS 100 #define SDHCI_HW_RST_EN BIT(4) /* Default settings for ZynqMP Clock Phases */ #define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0} #define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0} #define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0} #define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0} #define VERSAL_NET_EMMC_ICLK_PHASE {0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0} #define VERSAL_NET_EMMC_OCLK_PHASE {0, 113, 0, 0, 0, 0, 0, 0, 113, 79, 45} #define VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL 0X77 /* * 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 * atomic updates of the register without locking. This macro is used on SoCs * that have that feature. */ #define HIWORD_UPDATE(val, mask, shift) \ ((val) << (shift) | (mask) << ((shift) + 16)) /** * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map * * @reg: Offset within the syscon of the register containing this field * @width: Number of bits for this field * @shift: Bit offset within @reg of this field (or -1 if not avail) */ struct sdhci_arasan_soc_ctl_field { u32 reg; u16 width; s16 shift; }; /** * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers * * @baseclkfreq: Where to find corecfg_baseclkfreq * @clockmultiplier: Where to find corecfg_clockmultiplier * @support64b: Where to find SUPPORT64B bit * @hiword_update: If true, use HIWORD_UPDATE to access the syscon * * It's up to the licensee of the Arsan IP block to make these available * somewhere if needed. Presumably these will be scattered somewhere that's * accessible via the syscon API. */ struct sdhci_arasan_soc_ctl_map { struct sdhci_arasan_soc_ctl_field baseclkfreq; struct sdhci_arasan_soc_ctl_field clockmultiplier; struct sdhci_arasan_soc_ctl_field support64b; bool hiword_update; }; /** * struct sdhci_arasan_clk_ops - Clock Operations for Arasan SD controller * * @sdcardclk_ops: The output clock related operations * @sampleclk_ops: The sample clock related operations */ struct sdhci_arasan_clk_ops { const struct clk_ops *sdcardclk_ops; const struct clk_ops *sampleclk_ops; }; /** * struct sdhci_arasan_clk_data - Arasan Controller Clock Data. * * @sdcardclk_hw: Struct for the clock we might provide to a PHY. * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @sampleclk_hw: Struct for the clock we might provide to a PHY. * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw. * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes * @set_clk_delays: Function pointer for setting Clock Delays * @clk_of_data: Platform specific runtime clock data storage pointer */ struct sdhci_arasan_clk_data { struct clk_hw sdcardclk_hw; struct clk *sdcardclk; struct clk_hw sampleclk_hw; struct clk *sampleclk; int clk_phase_in[MMC_TIMING_MMC_HS400 + 1]; int clk_phase_out[MMC_TIMING_MMC_HS400 + 1]; void (*set_clk_delays)(struct sdhci_host *host); void *clk_of_data; }; /** * struct sdhci_arasan_data - Arasan Controller Data * * @host: Pointer to the main SDHCI host structure. * @clk_ahb: Pointer to the AHB clock * @phy: Pointer to the generic phy * @is_phy_on: True if the PHY is on; false if not. * @internal_phy_reg: True if the PHY is within the Host controller. * @has_cqe: True if controller has command queuing engine. * @clk_data: Struct for the Arasan Controller Clock Data. * @clk_ops: Struct for the Arasan Controller Clock Operations. * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. * @soc_ctl_map: Map to get offsets into soc_ctl registers. * @quirks: Arasan deviations from spec. */ struct sdhci_arasan_data { struct sdhci_host *host; struct clk *clk_ahb; struct phy *phy; bool is_phy_on; bool internal_phy_reg; bool has_cqe; struct sdhci_arasan_clk_data clk_data; const struct sdhci_arasan_clk_ops *clk_ops; struct regmap *soc_ctl_base; const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; unsigned int quirks; /* Controller does not have CD wired and will not function normally without */ #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) /* 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) /* * Some of the Arasan variations might not have timing requirements * met at 25MHz for Default Speed mode, those controllers work at * 19MHz instead */ #define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2) }; struct sdhci_arasan_of_data { const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; const struct sdhci_pltfm_data *pdata; const struct sdhci_arasan_clk_ops *clk_ops; }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0}, .hiword_update = true, }; static const struct sdhci_arasan_soc_ctl_map intel_lgm_emmc_soc_ctl_map = { .baseclkfreq = { .reg = 0xa0, .width = 8, .shift = 2 }, .clockmultiplier = { .reg = 0, .width = -1, .shift = -1 }, .hiword_update = false, }; static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = { .baseclkfreq = { .reg = 0x80, .width = 8, .shift = 2 }, .clockmultiplier = { .reg = 0, .width = -1, .shift = -1 }, .hiword_update = false, }; static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = { .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 }, .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 }, .support64b = { .reg = 0x4, .width = 1, .shift = 24 }, .hiword_update = false, }; static void sdhci_arasan_phy_set_delaychain(struct sdhci_host *host, bool enable) { u32 reg; reg = readl(host->ioaddr + PHY_CTRL_REG2); if (enable) reg |= (PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK); else reg &= ~(PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK); writel(reg, host->ioaddr + PHY_CTRL_REG2); } static int sdhci_arasan_phy_set_dll(struct sdhci_host *host, bool enable) { u32 reg; reg = readl(host->ioaddr + PHY_CTRL_REG2); if (enable) reg |= PHY_CTRL_EN_DLL_MASK; else reg &= ~PHY_CTRL_EN_DLL_MASK; writel(reg, host->ioaddr + PHY_CTRL_REG2); if (!enable) return 0; return readl_relaxed_poll_timeout(host->ioaddr + PHY_CTRL_REG2, reg, (reg & PHY_CTRL_DLL_RDY_MASK), 10, 1000 * PHY_DLL_TIMEOUT_MS); } static void sdhci_arasan_phy_dll_set_freq(struct sdhci_host *host, int clock) { u32 reg, freq_sel, freq; freq = DIV_ROUND_CLOSEST(clock, 1000000); if (freq <= 200 && freq > 170) freq_sel = FREQSEL_200M_170M; else if (freq <= 170 && freq > 140) freq_sel = FREQSEL_170M_140M; else if (freq <= 140 && freq > 110) freq_sel = FREQSEL_140M_110M; else if (freq <= 110 && freq > 80) freq_sel = FREQSEL_110M_80M; else freq_sel = FREQSEL_80M_50M; reg = readl(host->ioaddr + PHY_CTRL_REG2); reg &= ~PHY_CTRL_FREQ_SEL_MASK; reg |= (freq_sel << PHY_CTRL_FREQ_SEL_SHIFT); writel(reg, host->ioaddr + PHY_CTRL_REG2); } /** * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers * * @host: The sdhci_host * @fld: The field to write to * @val: The value to write * * This function allows writing to fields in sdhci_arasan_soc_ctl_map. * Note that if a field is specified as not available (shift < 0) then * this function will silently return an error code. It will be noisy * and print errors for any other (unexpected) errors. * * Return: 0 on success and error value on error */ static int sdhci_arasan_syscon_write(struct sdhci_host *host, const struct sdhci_arasan_soc_ctl_field *fld, u32 val) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base; u32 reg = fld->reg; u16 width = fld->width; s16 shift = fld->shift; int ret; /* * Silently return errors for shift < 0 so caller doesn't have * to check for fields which are optional. For fields that * are required then caller needs to do something special * anyway. */ if (shift < 0) return -EINVAL; if (sdhci_arasan->soc_ctl_map->hiword_update) ret = regmap_write(soc_ctl_base, reg, HIWORD_UPDATE(val, GENMASK(width, 0), shift)); else ret = regmap_update_bits(soc_ctl_base, reg, GENMASK(shift + width, shift), val << shift); /* Yell about (unexpected) regmap errors */ if (ret) pr_warn("%s: Regmap write fail: %d\n", mmc_hostname(host->mmc), ret); return ret; } static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; bool ctrl_phy = false; if (!IS_ERR(sdhci_arasan->phy)) { if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) { /* * If PHY off, set clock to max speed and power PHY on. * * Although PHY docs apparently suggest power cycling * when changing the clock the PHY doesn't like to be * powered on while at low speeds like those used in ID * mode. Even worse is powering the PHY on while the * clock is off. * * To workaround the PHY limitations, the best we can * do is to power it on at a faster speed and then slam * through low speeds without power cycling. */ sdhci_set_clock(host, host->max_clk); if (phy_power_on(sdhci_arasan->phy)) { pr_err("%s: Cannot power on phy.\n", mmc_hostname(host->mmc)); return; } sdhci_arasan->is_phy_on = true; /* * We'll now fall through to the below case with * ctrl_phy = false (so we won't turn off/on). The * sdhci_set_clock() will set the real clock. */ } else if (clock > PHY_CLK_TOO_SLOW_HZ) { /* * At higher clock speeds the PHY is fine being power * cycled and docs say you _should_ power cycle when * changing clock speeds. */ ctrl_phy = true; } } if (ctrl_phy && sdhci_arasan->is_phy_on) { phy_power_off(sdhci_arasan->phy); sdhci_arasan->is_phy_on = false; } if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN) { /* * Some of the Arasan variations might not have timing * requirements met at 25MHz for Default Speed mode, * those controllers work at 19MHz instead. */ if (clock == DEFAULT_SPEED_MAX_DTR) clock = (DEFAULT_SPEED_MAX_DTR * 19) / 25; } /* Set the Input and Output Clock Phase Delays */ if (clk_data->set_clk_delays && clock > PHY_CLK_TOO_SLOW_HZ) clk_data->set_clk_delays(host); if (sdhci_arasan->internal_phy_reg && clock >= MIN_PHY_CLK_HZ) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); sdhci_arasan_phy_set_dll(host, 0); sdhci_arasan_phy_set_delaychain(host, 0); sdhci_arasan_phy_dll_set_freq(host, clock); } else if (sdhci_arasan->internal_phy_reg) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); sdhci_arasan_phy_set_delaychain(host, 1); } sdhci_set_clock(host, clock); if (sdhci_arasan->internal_phy_reg && clock >= MIN_PHY_CLK_HZ) sdhci_arasan_phy_set_dll(host, 1); if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE) /* * Some controllers immediately report SDHCI_CLOCK_INT_STABLE * after enabling the clock even though the clock is not * stable. Trying to use a clock without waiting here results * in EILSEQ while detecting some older/slower cards. The * chosen delay is the maximum delay from sdhci_set_clock. */ msleep(20); if (ctrl_phy) { if (phy_power_on(sdhci_arasan->phy)) { pr_err("%s: Cannot power on phy.\n", mmc_hostname(host->mmc)); return; } sdhci_arasan->is_phy_on = true; } } static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) { u32 vendor; struct sdhci_host *host = mmc_priv(mmc); vendor = sdhci_readl(host, SDHCI_ARASAN_VENDOR_REGISTER); if (ios->enhanced_strobe) vendor |= VENDOR_ENHANCED_STROBE; else vendor &= ~VENDOR_ENHANCED_STROBE; sdhci_writel(host, vendor, SDHCI_ARASAN_VENDOR_REGISTER); } static void sdhci_arasan_reset(struct sdhci_host *host, u8 mask) { u8 ctrl; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); sdhci_and_cqhci_reset(host, mask); if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) { ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } } static void sdhci_arasan_hw_reset(struct sdhci_host *host) { u8 reg; reg = sdhci_readb(host, SDHCI_POWER_CONTROL); reg |= SDHCI_HW_RST_EN; sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); /* As per eMMC spec, minimum 1us is required but give it 2us for good measure */ usleep_range(2, 5); reg &= ~SDHCI_HW_RST_EN; sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); /* As per eMMC spec, minimum 200us is required but give it 300us for good measure */ usleep_range(300, 500); } static int sdhci_arasan_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) { switch (ios->signal_voltage) { case MMC_SIGNAL_VOLTAGE_180: /* * Plese don't switch to 1V8 as arasan,5.1 doesn't * actually refer to this setting to indicate the * signal voltage and the state machine will be broken * actually if we force to enable 1V8. That's something * like broken quirk but we could work around here. */ return 0; case MMC_SIGNAL_VOLTAGE_330: case MMC_SIGNAL_VOLTAGE_120: /* We don't support 3V3 and 1V2 */ break; } return -EINVAL; } static const 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, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .set_power = sdhci_set_power_and_bus_voltage, .hw_reset = sdhci_arasan_hw_reset, }; static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) { int cmd_error = 0; int data_error = 0; if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) return intmask; cqhci_irq(host->mmc, intmask, cmd_error, data_error); return 0; } static void sdhci_arasan_dumpregs(struct mmc_host *mmc) { sdhci_dumpregs(mmc_priv(mmc)); } static void sdhci_arasan_cqe_enable(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); u32 reg; reg = sdhci_readl(host, SDHCI_PRESENT_STATE); while (reg & SDHCI_DATA_AVAILABLE) { sdhci_readl(host, SDHCI_BUFFER); reg = sdhci_readl(host, SDHCI_PRESENT_STATE); } sdhci_cqe_enable(mmc); } static const struct cqhci_host_ops sdhci_arasan_cqhci_ops = { .enable = sdhci_arasan_cqe_enable, .disable = sdhci_cqe_disable, .dumpregs = sdhci_arasan_dumpregs, }; static const struct sdhci_ops sdhci_arasan_cqe_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, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .set_power = sdhci_set_power_and_bus_voltage, .irq = sdhci_arasan_cqhci_irq, }; static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { .ops = &sdhci_arasan_cqe_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; #ifdef CONFIG_PM_SLEEP /** * sdhci_arasan_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_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); if (sdhci_arasan->has_cqe) { ret = cqhci_suspend(host->mmc); if (ret) return ret; } ret = sdhci_suspend_host(host); if (ret) return ret; if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) { ret = phy_power_off(sdhci_arasan->phy); if (ret) { dev_err(dev, "Cannot power off phy.\n"); if (sdhci_resume_host(host)) dev_err(dev, "Cannot resume host.\n"); return ret; } sdhci_arasan->is_phy_on = false; } clk_disable(pltfm_host->clk); clk_disable(sdhci_arasan->clk_ahb); return 0; } /** * sdhci_arasan_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_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); 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; } if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) { ret = phy_power_on(sdhci_arasan->phy); if (ret) { dev_err(dev, "Cannot power on phy.\n"); return ret; } sdhci_arasan->is_phy_on = true; } ret = sdhci_resume_host(host); if (ret) { dev_err(dev, "Cannot resume host.\n"); return ret; } if (sdhci_arasan->has_cqe) return cqhci_resume(host->mmc); return 0; } #endif /* ! CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, sdhci_arasan_resume); /** * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate * * @hw: Pointer to the hardware clock structure. * @parent_rate: The parent rate (should be rate of clk_xin). * * Return the current actual rate of the SD card clock. This can be used * to communicate with out PHY. * * Return: The card clock rate. */ static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; return host->mmc->actual_clock; } static const struct clk_ops arasan_sdcardclk_ops = { .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, }; /** * sdhci_arasan_sampleclk_recalc_rate - Return the sampling clock rate * * @hw: Pointer to the hardware clock structure. * @parent_rate: The parent rate (should be rate of clk_xin). * * Return the current actual rate of the sampling clock. This can be used * to communicate with out PHY. * * Return: The sample clock rate. */ static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; return host->mmc->actual_clock; } static const struct clk_ops arasan_sampleclk_ops = { .recalc_rate = sdhci_arasan_sampleclk_recalc_rate, }; /** * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays * * @hw: Pointer to the hardware clock structure. * @degrees: The clock phase shift between 0 - 359. * * Set the SD Output Clock Tap Delays for Output path * * Return: 0 on success and error value on error */ static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; const char *clk_name = clk_hw_get_name(hw); u32 node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1; u8 tap_delay, tap_max = 0; int ret; /* This is applicable for SDHCI_SPEC_300 and above */ if (host->version < SDHCI_SPEC_300) return 0; switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: /* For 50MHz clock, 30 Taps are available */ tap_max = 30; break; case MMC_TIMING_UHS_SDR50: /* For 100MHz clock, 15 Taps are available */ tap_max = 15; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: /* For 200MHz clock, 8 Taps are available */ tap_max = 8; break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_OUTPUT, tap_delay); if (ret) pr_err("Error setting Output Tap Delay\n"); /* Release DLL Reset */ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_RELEASE); return ret; } static const struct clk_ops zynqmp_sdcardclk_ops = { .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, .set_phase = sdhci_zynqmp_sdcardclk_set_phase, }; /** * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays * * @hw: Pointer to the hardware clock structure. * @degrees: The clock phase shift between 0 - 359. * * Set the SD Input Clock Tap Delays for Input path * * Return: 0 on success and error value on error */ static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; const char *clk_name = clk_hw_get_name(hw); u32 node_id = !strcmp(clk_name, "clk_in_sd0") ? NODE_SD_0 : NODE_SD_1; u8 tap_delay, tap_max = 0; int ret; /* This is applicable for SDHCI_SPEC_300 and above */ if (host->version < SDHCI_SPEC_300) return 0; /* Assert DLL Reset */ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT); switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: /* For 50MHz clock, 120 Taps are available */ tap_max = 120; break; case MMC_TIMING_UHS_SDR50: /* For 100MHz clock, 60 Taps are available */ tap_max = 60; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: /* For 200MHz clock, 30 Taps are available */ tap_max = 30; break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_INPUT, tap_delay); if (ret) pr_err("Error setting Input Tap Delay\n"); return ret; } static const struct clk_ops zynqmp_sampleclk_ops = { .recalc_rate = sdhci_arasan_sampleclk_recalc_rate, .set_phase = sdhci_zynqmp_sampleclk_set_phase, }; /** * sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays * * @hw: Pointer to the hardware clock structure. * @degrees: The clock phase shift between 0 - 359. * * Set the SD Output Clock Tap Delays for Output path * * Return: 0 on success and error value on error */ static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; u8 tap_delay, tap_max = 0; /* This is applicable for SDHCI_SPEC_300 and above */ if (host->version < SDHCI_SPEC_300) return 0; switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: /* For 50MHz clock, 30 Taps are available */ tap_max = 30; break; case MMC_TIMING_UHS_SDR50: /* For 100MHz clock, 15 Taps are available */ tap_max = 15; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: /* For 200MHz clock, 8 Taps are available */ tap_max = 8; break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ if (tap_delay) { u32 regval; regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER); regval |= SDHCI_OTAPDLY_ENABLE; sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER); regval &= ~SDHCI_ARASAN_OTAPDLY_SEL_MASK; regval |= tap_delay; sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER); } return 0; } static const struct clk_ops versal_sdcardclk_ops = { .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, .set_phase = sdhci_versal_sdcardclk_set_phase, }; /** * sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays * * @hw: Pointer to the hardware clock structure. * @degrees: The clock phase shift between 0 - 359. * * Set the SD Input Clock Tap Delays for Input path * * Return: 0 on success and error value on error */ static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; u8 tap_delay, tap_max = 0; /* This is applicable for SDHCI_SPEC_300 and above */ if (host->version < SDHCI_SPEC_300) return 0; switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: /* For 50MHz clock, 120 Taps are available */ tap_max = 120; break; case MMC_TIMING_UHS_SDR50: /* For 100MHz clock, 60 Taps are available */ tap_max = 60; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: /* For 200MHz clock, 30 Taps are available */ tap_max = 30; break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ if (tap_delay) { u32 regval; 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 &= ~SDHCI_ARASAN_ITAPDLY_SEL_MASK; regval |= tap_delay; sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); regval &= ~SDHCI_ITAPDLY_CHGWIN; sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER); } return 0; } static const struct clk_ops versal_sampleclk_ops = { .recalc_rate = sdhci_arasan_sampleclk_recalc_rate, .set_phase = sdhci_versal_sampleclk_set_phase, }; static int sdhci_versal_net_emmc_sdcardclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; u8 tap_delay, tap_max = 0; switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_MMC_DDR52: tap_max = 16; break; case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS400: /* For 200MHz clock, 32 Taps are available */ tap_max = 32; break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ if (tap_delay) { u32 regval; regval = sdhci_readl(host, PHY_CTRL_REG1); regval |= PHY_CTRL_OTAPDLY_ENA_MASK; sdhci_writel(host, regval, PHY_CTRL_REG1); regval &= ~PHY_CTRL_OTAPDLY_SEL_MASK; regval |= tap_delay << PHY_CTRL_OTAPDLY_SEL_SHIFT; sdhci_writel(host, regval, PHY_CTRL_REG1); } return 0; } static const struct clk_ops versal_net_sdcardclk_ops = { .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, .set_phase = sdhci_versal_net_emmc_sdcardclk_set_phase, }; static int sdhci_versal_net_emmc_sampleclk_set_phase(struct clk_hw *hw, int degrees) { struct sdhci_arasan_clk_data *clk_data = container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw); struct sdhci_arasan_data *sdhci_arasan = container_of(clk_data, struct sdhci_arasan_data, clk_data); struct sdhci_host *host = sdhci_arasan->host; u8 tap_delay, tap_max = 0; u32 regval; switch (host->timing) { case MMC_TIMING_MMC_HS: case MMC_TIMING_MMC_DDR52: tap_max = 32; break; case MMC_TIMING_MMC_HS400: /* Strobe select tap point for strb90 and strb180 */ regval = sdhci_readl(host, PHY_CTRL_REG1); regval &= ~PHY_CTRL_STRB_SEL_MASK; regval |= VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL << PHY_CTRL_STRB_SEL_SHIFT; sdhci_writel(host, regval, PHY_CTRL_REG1); break; default: break; } tap_delay = (degrees * tap_max) / 360; /* Set the Clock Phase */ if (tap_delay) { regval = sdhci_readl(host, PHY_CTRL_REG1); regval |= PHY_CTRL_ITAP_CHG_WIN_MASK; sdhci_writel(host, regval, PHY_CTRL_REG1); regval |= PHY_CTRL_ITAPDLY_ENA_MASK; sdhci_writel(host, regval, PHY_CTRL_REG1); regval &= ~PHY_CTRL_ITAPDLY_SEL_MASK; regval |= tap_delay << PHY_CTRL_ITAPDLY_SEL_SHIFT; sdhci_writel(host, regval, PHY_CTRL_REG1); regval &= ~PHY_CTRL_ITAP_CHG_WIN_MASK; sdhci_writel(host, regval, PHY_CTRL_REG1); } return 0; } static const struct clk_ops versal_net_sampleclk_ops = { .recalc_rate = sdhci_arasan_sampleclk_recalc_rate, .set_phase = sdhci_versal_net_emmc_sampleclk_set_phase, }; static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid) { u16 clk; 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_pm_sd_dll_reset(deviceid, PM_DLL_RESET_PULSE); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); sdhci_enable_clk(host, clk); } static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); struct clk_hw *hw = &sdhci_arasan->clk_data.sdcardclk_hw; const char *clk_name = clk_hw_get_name(hw); u32 device_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1; int err; /* ZynqMP SD controller does not perform auto tuning in DDR50 mode */ if (mmc->ios.timing == MMC_TIMING_UHS_DDR50) return 0; arasan_zynqmp_dll_reset(host, device_id); err = sdhci_execute_tuning(mmc, opcode); if (err) return err; arasan_zynqmp_dll_reset(host, device_id); return 0; } /** * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier * * @host: The sdhci_host * @value: The value to write * * The corecfg_clockmultiplier is supposed to contain clock multiplier * value of programmable clock generator. * * NOTES: * - Many existing devices don't seem to do this and work fine. To keep * compatibility for old hardware where the device tree doesn't provide a * register map, this function is a noop if a soc_ctl_map hasn't been provided * for this platform. * - The value of corecfg_clockmultiplier should sync with that of corresponding * value reading from sdhci_capability_register. So this function is called * once at probe time and never called again. */ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, u32 value) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = sdhci_arasan->soc_ctl_map; /* Having a map is optional */ if (!soc_ctl_map) return; /* If we have a map, we expect to have a syscon */ if (!sdhci_arasan->soc_ctl_base) { pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", mmc_hostname(host->mmc)); return; } sdhci_arasan_syscon_write(host, &soc_ctl_map->clockmultiplier, value); } /** * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq * * @host: The sdhci_host * * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This * function can be used to make that happen. * * NOTES: * - Many existing devices don't seem to do this and work fine. To keep * compatibility for old hardware where the device tree doesn't provide a * register map, this function is a noop if a soc_ctl_map hasn't been provided * for this platform. * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider * to achieve lower clock rates. That means that this function is called once * at probe time and never called again. */ static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = sdhci_arasan->soc_ctl_map; u32 mhz = DIV_ROUND_CLOSEST_ULL(clk_get_rate(pltfm_host->clk), 1000000); /* Having a map is optional */ if (!soc_ctl_map) return; /* If we have a map, we expect to have a syscon */ if (!sdhci_arasan->soc_ctl_base) { pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", mmc_hostname(host->mmc)); return; } sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); } static void sdhci_arasan_set_clk_delays(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; clk_set_phase(clk_data->sampleclk, clk_data->clk_phase_in[host->timing]); clk_set_phase(clk_data->sdcardclk, clk_data->clk_phase_out[host->timing]); } static void arasan_dt_read_clk_phase(struct device *dev, struct sdhci_arasan_clk_data *clk_data, unsigned int timing, const char *prop) { struct device_node *np = dev->of_node; u32 clk_phase[2] = {0}; 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_variable_u32_array(np, prop, &clk_phase[0], 2, 0); if (ret < 0) { dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n", prop, clk_data->clk_phase_in[timing], clk_data->clk_phase_out[timing]); return; } /* The values read are Input and Output Clock Delays in order */ clk_data->clk_phase_in[timing] = clk_phase[0]; clk_data->clk_phase_out[timing] = clk_phase[1]; } /** * arasan_dt_parse_clk_phases - Read Clock Delay values from DT * * @dev: Pointer to our struct device. * @clk_data: Pointer to the Clock Data structure * * Called at initialization to parse the values of Clock Delays. */ static void arasan_dt_parse_clk_phases(struct device *dev, struct sdhci_arasan_clk_data *clk_data) { u32 mio_bank = 0; int i; /* * This has been kept as a pointer and is assigned a function here. * So that different controller variants can assign their own handling * function. */ clk_data->set_clk_delays = sdhci_arasan_set_clk_delays; if (of_device_is_compatible(dev->of_node, "xlnx,zynqmp-8.9a")) { u32 zynqmp_iclk_phase[MMC_TIMING_MMC_HS400 + 1] = ZYNQMP_ICLK_PHASE; u32 zynqmp_oclk_phase[MMC_TIMING_MMC_HS400 + 1] = ZYNQMP_OCLK_PHASE; of_property_read_u32(dev->of_node, "xlnx,mio-bank", &mio_bank); if (mio_bank == 2) { zynqmp_oclk_phase[MMC_TIMING_UHS_SDR104] = 90; zynqmp_oclk_phase[MMC_TIMING_MMC_HS200] = 90; } for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i]; clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i]; } } if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) { u32 versal_iclk_phase[MMC_TIMING_MMC_HS400 + 1] = VERSAL_ICLK_PHASE; u32 versal_oclk_phase[MMC_TIMING_MMC_HS400 + 1] = VERSAL_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { clk_data->clk_phase_in[i] = versal_iclk_phase[i]; clk_data->clk_phase_out[i] = versal_oclk_phase[i]; } } if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) { u32 versal_net_iclk_phase[MMC_TIMING_MMC_HS400 + 1] = VERSAL_NET_EMMC_ICLK_PHASE; u32 versal_net_oclk_phase[MMC_TIMING_MMC_HS400 + 1] = VERSAL_NET_EMMC_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { clk_data->clk_phase_in[i] = versal_net_iclk_phase[i]; clk_data->clk_phase_out[i] = versal_net_oclk_phase[i]; } } arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY, "clk-phase-legacy"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS, "clk-phase-mmc-hs"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS, "clk-phase-sd-hs"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12, "clk-phase-uhs-sdr12"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25, "clk-phase-uhs-sdr25"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50, "clk-phase-uhs-sdr50"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104, "clk-phase-uhs-sdr104"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50, "clk-phase-uhs-ddr50"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52, "clk-phase-mmc-ddr52"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200, "clk-phase-mmc-hs200"); arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400, "clk-phase-mmc-hs400"); } 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, }; static const struct sdhci_arasan_clk_ops arasan_clk_ops = { .sdcardclk_ops = &arasan_sdcardclk_ops, .sampleclk_ops = &arasan_sampleclk_ops, }; static struct sdhci_arasan_of_data sdhci_arasan_generic_data = { .pdata = &sdhci_arasan_pdata, .clk_ops = &arasan_clk_ops, }; static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = { .ops = &sdhci_arasan_cqe_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_NO_LED | SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | SDHCI_QUIRK2_STOP_WITH_TC | SDHCI_QUIRK2_BROKEN_64_BIT_DMA, }; static const struct sdhci_pltfm_data sdhci_keembay_sd_pdata = { .ops = &sdhci_arasan_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_NO_LED | SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_STOP_WITH_TC | SDHCI_QUIRK2_BROKEN_64_BIT_DMA, }; static const struct sdhci_pltfm_data sdhci_keembay_sdio_pdata = { .ops = &sdhci_arasan_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_NO_LED | SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_BROKEN_64_BIT_DMA, }; static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = { .soc_ctl_map = &rk3399_soc_ctl_map, .pdata = &sdhci_arasan_cqe_pdata, .clk_ops = &arasan_clk_ops, }; static struct sdhci_arasan_of_data intel_lgm_emmc_data = { .soc_ctl_map = &intel_lgm_emmc_soc_ctl_map, .pdata = &sdhci_arasan_cqe_pdata, .clk_ops = &arasan_clk_ops, }; static struct sdhci_arasan_of_data intel_lgm_sdxc_data = { .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map, .pdata = &sdhci_arasan_cqe_pdata, .clk_ops = &arasan_clk_ops, }; static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = { .ops = &sdhci_arasan_ops, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, }; static const struct sdhci_pltfm_data sdhci_arasan_versal_net_pdata = { .ops = &sdhci_arasan_ops, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, }; static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = { .sdcardclk_ops = &zynqmp_sdcardclk_ops, .sampleclk_ops = &zynqmp_sampleclk_ops, }; static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = { .pdata = &sdhci_arasan_zynqmp_pdata, .clk_ops = &zynqmp_clk_ops, }; static const struct sdhci_arasan_clk_ops versal_clk_ops = { .sdcardclk_ops = &versal_sdcardclk_ops, .sampleclk_ops = &versal_sampleclk_ops, }; static struct sdhci_arasan_of_data sdhci_arasan_versal_data = { .pdata = &sdhci_arasan_zynqmp_pdata, .clk_ops = &versal_clk_ops, }; static const struct sdhci_arasan_clk_ops versal_net_clk_ops = { .sdcardclk_ops = &versal_net_sdcardclk_ops, .sampleclk_ops = &versal_net_sampleclk_ops, }; static struct sdhci_arasan_of_data sdhci_arasan_versal_net_data = { .pdata = &sdhci_arasan_versal_net_pdata, .clk_ops = &versal_net_clk_ops, }; static struct sdhci_arasan_of_data intel_keembay_emmc_data = { .soc_ctl_map = &intel_keembay_soc_ctl_map, .pdata = &sdhci_keembay_emmc_pdata, .clk_ops = &arasan_clk_ops, }; static struct sdhci_arasan_of_data intel_keembay_sd_data = { .soc_ctl_map = &intel_keembay_soc_ctl_map, .pdata = &sdhci_keembay_sd_pdata, .clk_ops = &arasan_clk_ops, }; static struct sdhci_arasan_of_data intel_keembay_sdio_data = { .soc_ctl_map = &intel_keembay_soc_ctl_map, .pdata = &sdhci_keembay_sdio_pdata, .clk_ops = &arasan_clk_ops, }; static const struct of_device_id sdhci_arasan_of_match[] = { /* SoC-specific compatible strings w/ soc_ctl_map */ { .compatible = "rockchip,rk3399-sdhci-5.1", .data = &sdhci_arasan_rk3399_data, }, { .compatible = "intel,lgm-sdhci-5.1-emmc", .data = &intel_lgm_emmc_data, }, { .compatible = "intel,lgm-sdhci-5.1-sdxc", .data = &intel_lgm_sdxc_data, }, { .compatible = "intel,keembay-sdhci-5.1-emmc", .data = &intel_keembay_emmc_data, }, { .compatible = "intel,keembay-sdhci-5.1-sd", .data = &intel_keembay_sd_data, }, { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", .data = &sdhci_arasan_generic_data, }, { .compatible = "arasan,sdhci-5.1", .data = &sdhci_arasan_generic_data, }, { .compatible = "arasan,sdhci-4.9a", .data = &sdhci_arasan_generic_data, }, { .compatible = "xlnx,zynqmp-8.9a", .data = &sdhci_arasan_zynqmp_data, }, { .compatible = "xlnx,versal-8.9a", .data = &sdhci_arasan_versal_data, }, { .compatible = "xlnx,versal-net-emmc", .data = &sdhci_arasan_versal_net_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); /** * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use * * @sdhci_arasan: Our private data structure. * @clk_xin: Pointer to the functional clock * @dev: Pointer to our struct device. * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. * * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan, struct clk *clk_xin, struct device *dev) { struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; struct device_node *np = dev->of_node; struct clk_init_data sdcardclk_init; const char *parent_clk_name; int ret; ret = of_property_read_string_index(np, "clock-output-names", 0, &sdcardclk_init.name); if (ret) { dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); return ret; } parent_clk_name = __clk_get_name(clk_xin); sdcardclk_init.parent_names = &parent_clk_name; sdcardclk_init.num_parents = 1; sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops; clk_data->sdcardclk_hw.init = &sdcardclk_init; clk_data->sdcardclk = devm_clk_register(dev, &clk_data->sdcardclk_hw); if (IS_ERR(clk_data->sdcardclk)) return PTR_ERR(clk_data->sdcardclk); clk_data->sdcardclk_hw.init = NULL; ret = of_clk_add_provider(np, of_clk_src_simple_get, clk_data->sdcardclk); if (ret) dev_err(dev, "Failed to add sdcard clock provider\n"); return ret; } /** * sdhci_arasan_register_sampleclk - Register the sampleclk for a PHY to use * * @sdhci_arasan: Our private data structure. * @clk_xin: Pointer to the functional clock * @dev: Pointer to our struct device. * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. * * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan, struct clk *clk_xin, struct device *dev) { struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; struct device_node *np = dev->of_node; struct clk_init_data sampleclk_init; const char *parent_clk_name; int ret; ret = of_property_read_string_index(np, "clock-output-names", 1, &sampleclk_init.name); if (ret) { dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); return ret; } parent_clk_name = __clk_get_name(clk_xin); sampleclk_init.parent_names = &parent_clk_name; sampleclk_init.num_parents = 1; sampleclk_init.flags = CLK_GET_RATE_NOCACHE; sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops; clk_data->sampleclk_hw.init = &sampleclk_init; clk_data->sampleclk = devm_clk_register(dev, &clk_data->sampleclk_hw); if (IS_ERR(clk_data->sampleclk)) return PTR_ERR(clk_data->sampleclk); clk_data->sampleclk_hw.init = NULL; ret = of_clk_add_provider(np, of_clk_src_simple_get, clk_data->sampleclk); if (ret) dev_err(dev, "Failed to add sample clock provider\n"); return ret; } /** * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() * * @dev: Pointer to our struct device. * * Should be called any time we're exiting and sdhci_arasan_register_sdclk() * returned success. */ static void sdhci_arasan_unregister_sdclk(struct device *dev) { struct device_node *np = dev->of_node; if (!of_property_present(np, "#clock-cells")) return; of_clk_del_provider(dev->of_node); } /** * sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support) * @host: The sdhci_host * @value: The value to write * * This should be set based on the System Address Bus. * 0: the Core supports only 32-bit System Address Bus. * 1: the Core supports 64-bit System Address Bus. * * NOTE: * For Keem Bay, it is required to clear this bit. Its default value is 1'b1. * Keem Bay does not support 64-bit access. */ static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; /* Having a map is optional */ soc_ctl_map = sdhci_arasan->soc_ctl_map; if (!soc_ctl_map) return; /* If we have a map, we expect to have a syscon */ if (!sdhci_arasan->soc_ctl_base) { pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", mmc_hostname(host->mmc)); return; } sdhci_arasan_syscon_write(host, &soc_ctl_map->support64b, value); } /** * sdhci_arasan_register_sdclk - Register the sdcardclk for a PHY to use * * @sdhci_arasan: Our private data structure. * @clk_xin: Pointer to the functional clock * @dev: Pointer to our struct device. * * Some PHY devices need to know what the actual card clock is. In order for * them to find out, we'll provide a clock through the common clock framework * for them to query. * * Note: without seriously re-architecting SDHCI's clock code and testing on * all platforms, there's no way to create a totally beautiful clock here * with all clock ops implemented. Instead, we'll just create a clock that can * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock * framework that we're doing things behind its back. This should be sufficient * to create nice clean device tree bindings and later (if needed) we can try * re-architecting SDHCI if we see some benefit to it. * * Return: 0 on success and error value on error */ static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, struct clk *clk_xin, struct device *dev) { struct device_node *np = dev->of_node; u32 num_clks = 0; int ret; /* Providing a clock to the PHY is optional; no error if missing */ if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0) return 0; ret = sdhci_arasan_register_sdcardclk(sdhci_arasan, clk_xin, dev); if (ret) return ret; if (num_clks) { ret = sdhci_arasan_register_sampleclk(sdhci_arasan, clk_xin, dev); if (ret) { sdhci_arasan_unregister_sdclk(dev); return ret; } } return 0; } static int sdhci_zynqmp_set_dynamic_config(struct device *dev, struct sdhci_arasan_data *sdhci_arasan) { struct sdhci_host *host = sdhci_arasan->host; struct clk_hw *hw = &sdhci_arasan->clk_data.sdcardclk_hw; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); const char *clk_name = clk_hw_get_name(hw); u32 mhz, node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1; struct reset_control *rstc; int ret; /* Obtain SDHC reset control */ rstc = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(rstc)) { dev_err(dev, "Cannot get SDHC reset.\n"); return PTR_ERR(rstc); } ret = reset_control_assert(rstc); if (ret) return ret; ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_FIXED, 0); if (ret) return ret; ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_EMMC_SEL, !!(host->mmc->caps & MMC_CAP_NONREMOVABLE)); if (ret) return ret; mhz = DIV_ROUND_CLOSEST_ULL(clk_get_rate(pltfm_host->clk), 1000000); if (mhz > 100 && mhz <= 200) mhz = 200; else if (mhz > 50 && mhz <= 100) mhz = 100; else if (mhz > 25 && mhz <= 50) mhz = 50; else mhz = 25; ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz); if (ret) return ret; ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_8BIT, !!(host->mmc->caps & MMC_CAP_8_BIT_DATA)); if (ret) return ret; ret = reset_control_deassert(rstc); if (ret) return ret; usleep_range(1000, 1500); return 0; } static int sdhci_arasan_add_host(struct sdhci_arasan_data *sdhci_arasan) { struct sdhci_host *host = sdhci_arasan->host; struct cqhci_host *cq_host; bool dma64; int ret; if (!sdhci_arasan->has_cqe) return sdhci_add_host(host); ret = sdhci_setup_host(host); if (ret) return ret; cq_host = devm_kzalloc(host->mmc->parent, sizeof(*cq_host), GFP_KERNEL); if (!cq_host) { ret = -ENOMEM; goto cleanup; } cq_host->mmio = host->ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR; cq_host->ops = &sdhci_arasan_cqhci_ops; dma64 = host->flags & SDHCI_USE_64_BIT_DMA; if (dma64) cq_host->caps |= CQHCI_TASK_DESC_SZ_128; ret = cqhci_init(cq_host, host->mmc, dma64); if (ret) goto cleanup; ret = __sdhci_add_host(host); if (ret) goto cleanup; return 0; cleanup: sdhci_cleanup_host(host); return ret; } static int sdhci_arasan_probe(struct platform_device *pdev) { int ret; struct device_node *node; struct clk *clk_xin; struct clk *clk_dll; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct sdhci_arasan_data *sdhci_arasan; const struct sdhci_arasan_of_data *data; data = of_device_get_match_data(dev); if (!data) return -EINVAL; host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); sdhci_arasan = sdhci_pltfm_priv(pltfm_host); sdhci_arasan->host = host; sdhci_arasan->soc_ctl_map = data->soc_ctl_map; sdhci_arasan->clk_ops = data->clk_ops; node = of_parse_phandle(np, "arasan,soc-ctl-syscon", 0); if (node) { sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); of_node_put(node); if (IS_ERR(sdhci_arasan->soc_ctl_base)) { ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->soc_ctl_base), "Can't get syscon\n"); goto err_pltfm_free; } } sdhci_get_of_property(pdev); sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb"); if (IS_ERR(sdhci_arasan->clk_ahb)) { ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), "clk_ahb clock not found.\n"); goto err_pltfm_free; } clk_xin = devm_clk_get(dev, "clk_xin"); if (IS_ERR(clk_xin)) { ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); goto err_pltfm_free; } ret = clk_prepare_enable(sdhci_arasan->clk_ahb); if (ret) { dev_err(dev, "Unable to enable AHB clock.\n"); goto err_pltfm_free; } /* If clock-frequency property is set, use the provided value */ if (pltfm_host->clock && pltfm_host->clock != clk_get_rate(clk_xin)) { ret = clk_set_rate(clk_xin, pltfm_host->clock); if (ret) { dev_err(&pdev->dev, "Failed to set SD clock rate\n"); goto clk_dis_ahb; } } ret = clk_prepare_enable(clk_xin); if (ret) { dev_err(dev, "Unable to enable SD clock.\n"); goto clk_dis_ahb; } clk_dll = devm_clk_get_optional_enabled(dev, "gate"); if (IS_ERR(clk_dll)) { ret = dev_err_probe(dev, PTR_ERR(clk_dll), "failed to get dll clk\n"); goto clk_disable_all; } if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; if (of_property_read_bool(np, "xlnx,int-clock-stable-broken")) sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE; pltfm_host->clk = clk_xin; if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1")) sdhci_arasan_update_clockmultiplier(host, 0x0); if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") || of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") || of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) { sdhci_arasan_update_clockmultiplier(host, 0x0); sdhci_arasan_update_support64b(host, 0x0); host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; } sdhci_arasan_update_baseclkfreq(host); ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, dev); if (ret) goto clk_disable_all; if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) { host->mmc_host_ops.execute_tuning = arasan_zynqmp_execute_tuning; sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN; host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; } arasan_dt_parse_clk_phases(dev, &sdhci_arasan->clk_data); ret = mmc_of_parse(host->mmc); if (ret) { ret = dev_err_probe(dev, ret, "parsing dt failed.\n"); goto unreg_clk; } if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) { ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_SD_CONFIG); if (!ret) { ret = sdhci_zynqmp_set_dynamic_config(dev, sdhci_arasan); if (ret) goto unreg_clk; } } sdhci_arasan->phy = ERR_PTR(-ENODEV); if (of_device_is_compatible(np, "arasan,sdhci-5.1")) { sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan"); if (IS_ERR(sdhci_arasan->phy)) { ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->phy), "No phy for arasan,sdhci-5.1.\n"); goto unreg_clk; } ret = phy_init(sdhci_arasan->phy); if (ret < 0) { dev_err(dev, "phy_init err.\n"); goto unreg_clk; } host->mmc_host_ops.hs400_enhanced_strobe = sdhci_arasan_hs400_enhanced_strobe; host->mmc_host_ops.start_signal_voltage_switch = sdhci_arasan_voltage_switch; sdhci_arasan->has_cqe = true; host->mmc->caps2 |= MMC_CAP2_CQE; if (!of_property_read_bool(np, "disable-cqe-dcmd")) host->mmc->caps2 |= MMC_CAP2_CQE_DCMD; } if (of_device_is_compatible(np, "xlnx,versal-net-emmc")) sdhci_arasan->internal_phy_reg = true; ret = sdhci_arasan_add_host(sdhci_arasan); if (ret) goto err_add_host; return 0; err_add_host: if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); unreg_clk: sdhci_arasan_unregister_sdclk(dev); clk_disable_all: clk_disable_unprepare(clk_xin); clk_dis_ahb: clk_disable_unprepare(sdhci_arasan->clk_ahb); err_pltfm_free: sdhci_pltfm_free(pdev); return ret; } static void sdhci_arasan_remove(struct platform_device *pdev) { 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 clk *clk_ahb = sdhci_arasan->clk_ahb; struct clk *clk_xin = pltfm_host->clk; if (!IS_ERR(sdhci_arasan->phy)) { if (sdhci_arasan->is_phy_on) phy_power_off(sdhci_arasan->phy); phy_exit(sdhci_arasan->phy); } sdhci_arasan_unregister_sdclk(&pdev->dev); sdhci_pltfm_remove(pdev); clk_disable_unprepare(clk_xin); clk_disable_unprepare(clk_ahb); } static struct platform_driver sdhci_arasan_driver = { .driver = { .name = "sdhci-arasan", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_arasan_of_match, .pm = &sdhci_arasan_dev_pm_ops, }, .probe = sdhci_arasan_probe, .remove = sdhci_arasan_remove, }; module_platform_driver(sdhci_arasan_driver); MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); MODULE_AUTHOR("Soeren Brinkmann "); MODULE_LICENSE("GPL");