103d2bfc8SOlof Johansson /* 203d2bfc8SOlof Johansson * Copyright (C) 2010 Google, Inc. 303d2bfc8SOlof Johansson * 403d2bfc8SOlof Johansson * This software is licensed under the terms of the GNU General Public 503d2bfc8SOlof Johansson * License version 2, as published by the Free Software Foundation, and 603d2bfc8SOlof Johansson * may be copied, distributed, and modified under those terms. 703d2bfc8SOlof Johansson * 803d2bfc8SOlof Johansson * This program is distributed in the hope that it will be useful, 903d2bfc8SOlof Johansson * but WITHOUT ANY WARRANTY; without even the implied warranty of 1003d2bfc8SOlof Johansson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1103d2bfc8SOlof Johansson * GNU General Public License for more details. 1203d2bfc8SOlof Johansson * 1303d2bfc8SOlof Johansson */ 1403d2bfc8SOlof Johansson 15e5c63d91SLucas Stach #include <linux/delay.h> 1603d2bfc8SOlof Johansson #include <linux/err.h> 1796547f5dSPaul Gortmaker #include <linux/module.h> 1803d2bfc8SOlof Johansson #include <linux/init.h> 19e7c07148SAapo Vienamo #include <linux/iopoll.h> 2003d2bfc8SOlof Johansson #include <linux/platform_device.h> 2103d2bfc8SOlof Johansson #include <linux/clk.h> 2203d2bfc8SOlof Johansson #include <linux/io.h> 2355cd65e4SStephen Warren #include <linux/of.h> 243e44a1a7SStephen Warren #include <linux/of_device.h> 2586ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h> 2686ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h> 2720567be9SThierry Reding #include <linux/reset.h> 2803d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2903d2bfc8SOlof Johansson #include <linux/mmc/host.h> 30c3c2384cSLucas Stach #include <linux/mmc/mmc.h> 310aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 322391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 3303d2bfc8SOlof Johansson 3403d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 3503d2bfc8SOlof Johansson 36ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 3774cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 38c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 39c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 40c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 4174cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 4274cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 4374cd42bcSLucas Stach 44ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 453145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 463145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 47ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 483145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 49ca5879d3SPavan Kunapuli 50d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 51d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 52d4501d8eSAapo Vienamo 53e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 54e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START BIT(31) 55e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE BIT(29) 5651b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff 57e5c63d91SLucas Stach 589d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0 599d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f 609d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7 61212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31) 629d548f11SAapo Vienamo 63e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec 64e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) 65e7c07148SAapo Vienamo 663e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 673e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 68ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 697ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3) 707ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4) 717ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5) 72e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB BIT(6) 7386ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) 74d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) 753e44a1a7SStephen Warren 763e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 771db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 783e44a1a7SStephen Warren u32 nvquirks; 793e44a1a7SStephen Warren }; 803e44a1a7SStephen Warren 8151b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */ 8251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets { 8351b77c8eSAapo Vienamo u32 pull_up_3v3; 8451b77c8eSAapo Vienamo u32 pull_down_3v3; 8551b77c8eSAapo Vienamo u32 pull_up_3v3_timeout; 8651b77c8eSAapo Vienamo u32 pull_down_3v3_timeout; 8751b77c8eSAapo Vienamo u32 pull_up_1v8; 8851b77c8eSAapo Vienamo u32 pull_down_1v8; 8951b77c8eSAapo Vienamo u32 pull_up_1v8_timeout; 9051b77c8eSAapo Vienamo u32 pull_down_1v8_timeout; 9151b77c8eSAapo Vienamo u32 pull_up_sdr104; 9251b77c8eSAapo Vienamo u32 pull_down_sdr104; 9351b77c8eSAapo Vienamo u32 pull_up_hs400; 9451b77c8eSAapo Vienamo u32 pull_down_hs400; 9551b77c8eSAapo Vienamo }; 9651b77c8eSAapo Vienamo 973e44a1a7SStephen Warren struct sdhci_tegra { 983e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 992391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 100a8e326a9SLucas Stach bool ddr_signaling; 101e5c63d91SLucas Stach bool pad_calib_required; 10286ac2f8bSAapo Vienamo bool pad_control_available; 10320567be9SThierry Reding 10420567be9SThierry Reding struct reset_control *rst; 10586ac2f8bSAapo Vienamo struct pinctrl *pinctrl_sdmmc; 10686ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_3v3; 10786ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_1v8; 10851b77c8eSAapo Vienamo 10951b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets autocal_offsets; 1103e44a1a7SStephen Warren }; 1113e44a1a7SStephen Warren 11203d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 11303d2bfc8SOlof Johansson { 1143e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1150734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1163e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1173e44a1a7SStephen Warren 1183e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 1193e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 12003d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 12103d2bfc8SOlof Johansson return SDHCI_SPEC_200; 12203d2bfc8SOlof Johansson } 12303d2bfc8SOlof Johansson 12403d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 12503d2bfc8SOlof Johansson } 12603d2bfc8SOlof Johansson 127352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 128352ee868SPavan Kunapuli { 129352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 130352ee868SPavan Kunapuli 131352ee868SPavan Kunapuli switch (reg) { 132352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 133352ee868SPavan Kunapuli /* 134352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 135352ee868SPavan Kunapuli * command write that is down below. 136352ee868SPavan Kunapuli */ 137352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 138352ee868SPavan Kunapuli return; 139352ee868SPavan Kunapuli case SDHCI_COMMAND: 140352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 141352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 142352ee868SPavan Kunapuli return; 143352ee868SPavan Kunapuli } 144352ee868SPavan Kunapuli 145352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 146352ee868SPavan Kunapuli } 147352ee868SPavan Kunapuli 14803d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 14903d2bfc8SOlof Johansson { 1503e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1510734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1523e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1533e44a1a7SStephen Warren 15403d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 15503d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 15603d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 15703d2bfc8SOlof Johansson */ 15803d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 15903d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 16003d2bfc8SOlof Johansson 16103d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 16203d2bfc8SOlof Johansson 1633e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 1643e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 16503d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 16603d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 16703d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 16803d2bfc8SOlof Johansson gap_ctrl |= 0x8; 16903d2bfc8SOlof Johansson else 17003d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 17103d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 17203d2bfc8SOlof Johansson } 17303d2bfc8SOlof Johansson } 17403d2bfc8SOlof Johansson 1753e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 17603d2bfc8SOlof Johansson { 1770aacd23fSJoseph Lo return mmc_gpio_get_ro(host->mmc); 17803d2bfc8SOlof Johansson } 17903d2bfc8SOlof Johansson 18086ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host) 18186ac2f8bSAapo Vienamo { 18286ac2f8bSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 18386ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 18486ac2f8bSAapo Vienamo int has_1v8, has_3v3; 18586ac2f8bSAapo Vienamo 18686ac2f8bSAapo Vienamo /* 18786ac2f8bSAapo Vienamo * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad 18886ac2f8bSAapo Vienamo * voltage configuration in order to perform voltage switching. This 18986ac2f8bSAapo Vienamo * means that valid pinctrl info is required on SDHCI instances capable 19086ac2f8bSAapo Vienamo * of performing voltage switching. Whether or not an SDHCI instance is 19186ac2f8bSAapo Vienamo * capable of voltage switching is determined based on the regulator. 19286ac2f8bSAapo Vienamo */ 19386ac2f8bSAapo Vienamo 19486ac2f8bSAapo Vienamo if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 19586ac2f8bSAapo Vienamo return true; 19686ac2f8bSAapo Vienamo 19786ac2f8bSAapo Vienamo if (IS_ERR(host->mmc->supply.vqmmc)) 19886ac2f8bSAapo Vienamo return false; 19986ac2f8bSAapo Vienamo 20086ac2f8bSAapo Vienamo has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 20186ac2f8bSAapo Vienamo 1700000, 1950000); 20286ac2f8bSAapo Vienamo 20386ac2f8bSAapo Vienamo has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 20486ac2f8bSAapo Vienamo 2700000, 3600000); 20586ac2f8bSAapo Vienamo 20686ac2f8bSAapo Vienamo if (has_1v8 == 1 && has_3v3 == 1) 20786ac2f8bSAapo Vienamo return tegra_host->pad_control_available; 20886ac2f8bSAapo Vienamo 20986ac2f8bSAapo Vienamo /* Fixed voltage, no pad control required. */ 21086ac2f8bSAapo Vienamo return true; 21186ac2f8bSAapo Vienamo } 21286ac2f8bSAapo Vienamo 21303231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 214ca5879d3SPavan Kunapuli { 215ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2160734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 217ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 2189d548f11SAapo Vienamo u32 misc_ctrl, clk_ctrl, pad_ctrl; 219ca5879d3SPavan Kunapuli 22003231f9bSRussell King sdhci_reset(host, mask); 22103231f9bSRussell King 222ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 223ca5879d3SPavan Kunapuli return; 224ca5879d3SPavan Kunapuli 2251b84def8SLucas Stach misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 2264f6aa326SJon Hunter clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 2274f6aa326SJon Hunter 2284f6aa326SJon Hunter misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | 2294f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR50 | 2304f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_DDR50 | 2314f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR104); 2324f6aa326SJon Hunter 2334f6aa326SJon Hunter clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; 2344f6aa326SJon Hunter 23586ac2f8bSAapo Vienamo if (tegra_sdhci_is_pad_and_regulator_valid(host)) { 236ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 2373145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 238ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 2397ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */ 2407ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 2417ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 2427ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 2437ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 2447ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 2457ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 2467ad2ed1dSLucas Stach if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) 247c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 2484f6aa326SJon Hunter } 2494f6aa326SJon Hunter 2504f6aa326SJon Hunter sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 25174cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 25274cd42bcSLucas Stach 2539d548f11SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) { 2549d548f11SAapo Vienamo pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 2559d548f11SAapo Vienamo pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK; 2569d548f11SAapo Vienamo pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL; 2579d548f11SAapo Vienamo sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 2589d548f11SAapo Vienamo 259e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 2609d548f11SAapo Vienamo } 261e5c63d91SLucas Stach 262a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 263ca5879d3SPavan Kunapuli } 264ca5879d3SPavan Kunapuli 265212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable) 266212b0cf1SAapo Vienamo { 267212b0cf1SAapo Vienamo u32 val; 268212b0cf1SAapo Vienamo 269212b0cf1SAapo Vienamo /* 270212b0cf1SAapo Vienamo * Enable or disable the additional I/O pad used by the drive strength 271212b0cf1SAapo Vienamo * calibration process. 272212b0cf1SAapo Vienamo */ 273212b0cf1SAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 274212b0cf1SAapo Vienamo 275212b0cf1SAapo Vienamo if (enable) 276212b0cf1SAapo Vienamo val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 277212b0cf1SAapo Vienamo else 278212b0cf1SAapo Vienamo val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 279212b0cf1SAapo Vienamo 280212b0cf1SAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 281212b0cf1SAapo Vienamo 282212b0cf1SAapo Vienamo if (enable) 283212b0cf1SAapo Vienamo usleep_range(1, 2); 284212b0cf1SAapo Vienamo } 285212b0cf1SAapo Vienamo 286887bda8fSAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable) 287887bda8fSAapo Vienamo { 288887bda8fSAapo Vienamo bool status; 289887bda8fSAapo Vienamo u32 reg; 290887bda8fSAapo Vienamo 291887bda8fSAapo Vienamo reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 292887bda8fSAapo Vienamo status = !!(reg & SDHCI_CLOCK_CARD_EN); 293887bda8fSAapo Vienamo 294887bda8fSAapo Vienamo if (status == enable) 295887bda8fSAapo Vienamo return status; 296887bda8fSAapo Vienamo 297887bda8fSAapo Vienamo if (enable) 298887bda8fSAapo Vienamo reg |= SDHCI_CLOCK_CARD_EN; 299887bda8fSAapo Vienamo else 300887bda8fSAapo Vienamo reg &= ~SDHCI_CLOCK_CARD_EN; 301887bda8fSAapo Vienamo 302887bda8fSAapo Vienamo sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); 303887bda8fSAapo Vienamo 304887bda8fSAapo Vienamo return status; 305887bda8fSAapo Vienamo } 306887bda8fSAapo Vienamo 30751b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host, 30851b77c8eSAapo Vienamo u16 pdpu) 30951b77c8eSAapo Vienamo { 31051b77c8eSAapo Vienamo u32 reg; 31151b77c8eSAapo Vienamo 31251b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 31351b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK; 31451b77c8eSAapo Vienamo reg |= pdpu; 31551b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 31651b77c8eSAapo Vienamo } 31751b77c8eSAapo Vienamo 318e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) 319e5c63d91SLucas Stach { 32051b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 32151b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 32251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets offsets = 32351b77c8eSAapo Vienamo tegra_host->autocal_offsets; 32451b77c8eSAapo Vienamo struct mmc_ios *ios = &host->mmc->ios; 325887bda8fSAapo Vienamo bool card_clk_enabled; 32651b77c8eSAapo Vienamo u16 pdpu; 327e7c07148SAapo Vienamo u32 reg; 328e7c07148SAapo Vienamo int ret; 329e5c63d91SLucas Stach 33051b77c8eSAapo Vienamo switch (ios->timing) { 33151b77c8eSAapo Vienamo case MMC_TIMING_UHS_SDR104: 33251b77c8eSAapo Vienamo pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104; 33351b77c8eSAapo Vienamo break; 33451b77c8eSAapo Vienamo case MMC_TIMING_MMC_HS400: 33551b77c8eSAapo Vienamo pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400; 33651b77c8eSAapo Vienamo break; 33751b77c8eSAapo Vienamo default: 33851b77c8eSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) 33951b77c8eSAapo Vienamo pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8; 34051b77c8eSAapo Vienamo else 34151b77c8eSAapo Vienamo pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3; 34251b77c8eSAapo Vienamo } 34351b77c8eSAapo Vienamo 34451b77c8eSAapo Vienamo tegra_sdhci_set_pad_autocal_offset(host, pdpu); 34551b77c8eSAapo Vienamo 346887bda8fSAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 347887bda8fSAapo Vienamo 348212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, true); 349212b0cf1SAapo Vienamo 350e7c07148SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 351e7c07148SAapo Vienamo reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; 352e7c07148SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 353e5c63d91SLucas Stach 354e7c07148SAapo Vienamo usleep_range(1, 2); 355e7c07148SAapo Vienamo /* 10 ms timeout */ 356e7c07148SAapo Vienamo ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS, 357e7c07148SAapo Vienamo reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE), 358e7c07148SAapo Vienamo 1000, 10000); 359e7c07148SAapo Vienamo 360212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, false); 361212b0cf1SAapo Vienamo 362887bda8fSAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 363887bda8fSAapo Vienamo 36451b77c8eSAapo Vienamo if (ret) { 365e7c07148SAapo Vienamo dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n"); 36651b77c8eSAapo Vienamo 36751b77c8eSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) 36851b77c8eSAapo Vienamo pdpu = offsets.pull_down_1v8_timeout << 8 | 36951b77c8eSAapo Vienamo offsets.pull_up_1v8_timeout; 37051b77c8eSAapo Vienamo else 37151b77c8eSAapo Vienamo pdpu = offsets.pull_down_3v3_timeout << 8 | 37251b77c8eSAapo Vienamo offsets.pull_up_3v3_timeout; 37351b77c8eSAapo Vienamo 37451b77c8eSAapo Vienamo /* Disable automatic calibration and use fixed offsets */ 37551b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 37651b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_ENABLE; 37751b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 37851b77c8eSAapo Vienamo 37951b77c8eSAapo Vienamo tegra_sdhci_set_pad_autocal_offset(host, pdpu); 38051b77c8eSAapo Vienamo } 38151b77c8eSAapo Vienamo } 38251b77c8eSAapo Vienamo 38351b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) 38451b77c8eSAapo Vienamo { 38551b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 38651b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 38751b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets *autocal = 38851b77c8eSAapo Vienamo &tegra_host->autocal_offsets; 38951b77c8eSAapo Vienamo int err; 39051b77c8eSAapo Vienamo 39151b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 39251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3", 39351b77c8eSAapo Vienamo &autocal->pull_up_3v3); 39451b77c8eSAapo Vienamo if (err) 39551b77c8eSAapo Vienamo autocal->pull_up_3v3 = 0; 39651b77c8eSAapo Vienamo 39751b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 39851b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3", 39951b77c8eSAapo Vienamo &autocal->pull_down_3v3); 40051b77c8eSAapo Vienamo if (err) 40151b77c8eSAapo Vienamo autocal->pull_down_3v3 = 0; 40251b77c8eSAapo Vienamo 40351b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 40451b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8", 40551b77c8eSAapo Vienamo &autocal->pull_up_1v8); 40651b77c8eSAapo Vienamo if (err) 40751b77c8eSAapo Vienamo autocal->pull_up_1v8 = 0; 40851b77c8eSAapo Vienamo 40951b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 41051b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8", 41151b77c8eSAapo Vienamo &autocal->pull_down_1v8); 41251b77c8eSAapo Vienamo if (err) 41351b77c8eSAapo Vienamo autocal->pull_down_1v8 = 0; 41451b77c8eSAapo Vienamo 41551b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 41651b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3-timeout", 41751b77c8eSAapo Vienamo &autocal->pull_up_3v3); 41851b77c8eSAapo Vienamo if (err) 41951b77c8eSAapo Vienamo autocal->pull_up_3v3_timeout = 0; 42051b77c8eSAapo Vienamo 42151b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 42251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3-timeout", 42351b77c8eSAapo Vienamo &autocal->pull_down_3v3); 42451b77c8eSAapo Vienamo if (err) 42551b77c8eSAapo Vienamo autocal->pull_down_3v3_timeout = 0; 42651b77c8eSAapo Vienamo 42751b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 42851b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8-timeout", 42951b77c8eSAapo Vienamo &autocal->pull_up_1v8); 43051b77c8eSAapo Vienamo if (err) 43151b77c8eSAapo Vienamo autocal->pull_up_1v8_timeout = 0; 43251b77c8eSAapo Vienamo 43351b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 43451b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8-timeout", 43551b77c8eSAapo Vienamo &autocal->pull_down_1v8); 43651b77c8eSAapo Vienamo if (err) 43751b77c8eSAapo Vienamo autocal->pull_down_1v8_timeout = 0; 43851b77c8eSAapo Vienamo 43951b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 44051b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-sdr104", 44151b77c8eSAapo Vienamo &autocal->pull_up_sdr104); 44251b77c8eSAapo Vienamo if (err) 44351b77c8eSAapo Vienamo autocal->pull_up_sdr104 = autocal->pull_up_1v8; 44451b77c8eSAapo Vienamo 44551b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 44651b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-sdr104", 44751b77c8eSAapo Vienamo &autocal->pull_down_sdr104); 44851b77c8eSAapo Vienamo if (err) 44951b77c8eSAapo Vienamo autocal->pull_down_sdr104 = autocal->pull_down_1v8; 45051b77c8eSAapo Vienamo 45151b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 45251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-hs400", 45351b77c8eSAapo Vienamo &autocal->pull_up_hs400); 45451b77c8eSAapo Vienamo if (err) 45551b77c8eSAapo Vienamo autocal->pull_up_hs400 = autocal->pull_up_1v8; 45651b77c8eSAapo Vienamo 45751b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 45851b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-hs400", 45951b77c8eSAapo Vienamo &autocal->pull_down_hs400); 46051b77c8eSAapo Vienamo if (err) 46151b77c8eSAapo Vienamo autocal->pull_down_hs400 = autocal->pull_down_1v8; 462e5c63d91SLucas Stach } 463e5c63d91SLucas Stach 464a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 465a8e326a9SLucas Stach { 466a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4670734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 468a8e326a9SLucas Stach unsigned long host_clk; 469a8e326a9SLucas Stach 470a8e326a9SLucas Stach if (!clock) 4713491b690SLucas Stach return sdhci_set_clock(host, clock); 472a8e326a9SLucas Stach 47357d1654eSAapo Vienamo /* 47457d1654eSAapo Vienamo * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI 47557d1654eSAapo Vienamo * divider to be configured to divided the host clock by two. The SDHCI 47657d1654eSAapo Vienamo * clock divider is calculated as part of sdhci_set_clock() by 47757d1654eSAapo Vienamo * sdhci_calc_clk(). The divider is calculated from host->max_clk and 47857d1654eSAapo Vienamo * the requested clock rate. 47957d1654eSAapo Vienamo * 48057d1654eSAapo Vienamo * By setting the host->max_clk to clock * 2 the divider calculation 48157d1654eSAapo Vienamo * will always result in the correct value for DDR50/52 modes, 48257d1654eSAapo Vienamo * regardless of clock rate rounding, which may happen if the value 48357d1654eSAapo Vienamo * from clk_get_rate() is used. 48457d1654eSAapo Vienamo */ 485a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 486a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 48757d1654eSAapo Vienamo if (tegra_host->ddr_signaling) 48857d1654eSAapo Vienamo host->max_clk = host_clk; 48957d1654eSAapo Vienamo else 490a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 491a8e326a9SLucas Stach 492e5c63d91SLucas Stach sdhci_set_clock(host, clock); 493e5c63d91SLucas Stach 494e5c63d91SLucas Stach if (tegra_host->pad_calib_required) { 495e5c63d91SLucas Stach tegra_sdhci_pad_autocalib(host); 496e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 497e5c63d91SLucas Stach } 498a8e326a9SLucas Stach } 499a8e326a9SLucas Stach 500a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 501a8e326a9SLucas Stach unsigned timing) 502a8e326a9SLucas Stach { 503a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 5040734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 505a8e326a9SLucas Stach 506e300149eSStefan Agner if (timing == MMC_TIMING_UHS_DDR50 || 507e300149eSStefan Agner timing == MMC_TIMING_MMC_DDR52) 508a8e326a9SLucas Stach tegra_host->ddr_signaling = true; 509a8e326a9SLucas Stach 510cf56c819SAapo Vienamo sdhci_set_uhs_signaling(host, timing); 511a8e326a9SLucas Stach } 512a8e326a9SLucas Stach 51344350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 51444350993SAapo Vienamo { 51544350993SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 51644350993SAapo Vienamo 51744350993SAapo Vienamo return clk_round_rate(pltfm_host->clk, UINT_MAX); 51844350993SAapo Vienamo } 51944350993SAapo Vienamo 520c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 521c3c2384cSLucas Stach { 522d4501d8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 523d4501d8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 524d4501d8eSAapo Vienamo const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 525d4501d8eSAapo Vienamo bool card_clk_enabled = false; 526c3c2384cSLucas Stach u32 reg; 527c3c2384cSLucas Stach 528d4501d8eSAapo Vienamo /* 529d4501d8eSAapo Vienamo * Touching the tap values is a bit tricky on some SoC generations. 530d4501d8eSAapo Vienamo * The quirk enables a workaround for a glitch that sometimes occurs if 531d4501d8eSAapo Vienamo * the tap values are changed. 532d4501d8eSAapo Vienamo */ 533d4501d8eSAapo Vienamo 534d4501d8eSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) 535d4501d8eSAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 536d4501d8eSAapo Vienamo 537c3c2384cSLucas Stach reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 538c3c2384cSLucas Stach reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 539c3c2384cSLucas Stach reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 540c3c2384cSLucas Stach sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 541d4501d8eSAapo Vienamo 542d4501d8eSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP && 543d4501d8eSAapo Vienamo card_clk_enabled) { 544d4501d8eSAapo Vienamo usleep_range(1, 2); 545d4501d8eSAapo Vienamo sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 546d4501d8eSAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 547d4501d8eSAapo Vienamo } 548c3c2384cSLucas Stach } 549c3c2384cSLucas Stach 550c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 551c3c2384cSLucas Stach { 552c3c2384cSLucas Stach unsigned int min, max; 553c3c2384cSLucas Stach 554c3c2384cSLucas Stach /* 555c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 556c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 557c3c2384cSLucas Stach * according to the TRM. 558c3c2384cSLucas Stach */ 559c3c2384cSLucas Stach min = 10; 560c3c2384cSLucas Stach while (min < 255) { 561c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 562c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 563c3c2384cSLucas Stach break; 564c3c2384cSLucas Stach min++; 565c3c2384cSLucas Stach } 566c3c2384cSLucas Stach 567c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 568c3c2384cSLucas Stach max = min + 1; 569c3c2384cSLucas Stach while (max < 255) { 570c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 571c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 572c3c2384cSLucas Stach max--; 573c3c2384cSLucas Stach break; 574c3c2384cSLucas Stach } 575c3c2384cSLucas Stach max++; 576c3c2384cSLucas Stach } 577c3c2384cSLucas Stach 578c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 579c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 580c3c2384cSLucas Stach 581c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 582c3c2384cSLucas Stach } 583c3c2384cSLucas Stach 58486ac2f8bSAapo Vienamo static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage) 58586ac2f8bSAapo Vienamo { 58686ac2f8bSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 58786ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 58886ac2f8bSAapo Vienamo int ret; 58986ac2f8bSAapo Vienamo 59086ac2f8bSAapo Vienamo if (!tegra_host->pad_control_available) 59186ac2f8bSAapo Vienamo return 0; 59286ac2f8bSAapo Vienamo 59386ac2f8bSAapo Vienamo if (voltage == MMC_SIGNAL_VOLTAGE_180) { 59486ac2f8bSAapo Vienamo ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 59586ac2f8bSAapo Vienamo tegra_host->pinctrl_state_1v8); 59686ac2f8bSAapo Vienamo if (ret < 0) 59786ac2f8bSAapo Vienamo dev_err(mmc_dev(host->mmc), 59886ac2f8bSAapo Vienamo "setting 1.8V failed, ret: %d\n", ret); 59986ac2f8bSAapo Vienamo } else { 60086ac2f8bSAapo Vienamo ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 60186ac2f8bSAapo Vienamo tegra_host->pinctrl_state_3v3); 60286ac2f8bSAapo Vienamo if (ret < 0) 60386ac2f8bSAapo Vienamo dev_err(mmc_dev(host->mmc), 60486ac2f8bSAapo Vienamo "setting 3.3V failed, ret: %d\n", ret); 60586ac2f8bSAapo Vienamo } 60686ac2f8bSAapo Vienamo 60786ac2f8bSAapo Vienamo return ret; 60886ac2f8bSAapo Vienamo } 60986ac2f8bSAapo Vienamo 61086ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc, 61186ac2f8bSAapo Vienamo struct mmc_ios *ios) 61286ac2f8bSAapo Vienamo { 61386ac2f8bSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 61444babea2SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 61544babea2SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 61686ac2f8bSAapo Vienamo int ret = 0; 61786ac2f8bSAapo Vienamo 61886ac2f8bSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { 61986ac2f8bSAapo Vienamo ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage); 62086ac2f8bSAapo Vienamo if (ret < 0) 62186ac2f8bSAapo Vienamo return ret; 62286ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 62386ac2f8bSAapo Vienamo } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { 62486ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 62586ac2f8bSAapo Vienamo if (ret < 0) 62686ac2f8bSAapo Vienamo return ret; 62786ac2f8bSAapo Vienamo ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage); 62886ac2f8bSAapo Vienamo } 62986ac2f8bSAapo Vienamo 63044babea2SAapo Vienamo if (tegra_host->pad_calib_required) 63144babea2SAapo Vienamo tegra_sdhci_pad_autocalib(host); 63244babea2SAapo Vienamo 63386ac2f8bSAapo Vienamo return ret; 63486ac2f8bSAapo Vienamo } 63586ac2f8bSAapo Vienamo 63686ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev, 63786ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host) 63886ac2f8bSAapo Vienamo { 63986ac2f8bSAapo Vienamo tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev); 64086ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_sdmmc)) { 64186ac2f8bSAapo Vienamo dev_dbg(dev, "No pinctrl info, err: %ld\n", 64286ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_sdmmc)); 64386ac2f8bSAapo Vienamo return -1; 64486ac2f8bSAapo Vienamo } 64586ac2f8bSAapo Vienamo 64686ac2f8bSAapo Vienamo tegra_host->pinctrl_state_3v3 = 64786ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3"); 64886ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_3v3)) { 64986ac2f8bSAapo Vienamo dev_warn(dev, "Missing 3.3V pad state, err: %ld\n", 65086ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_state_3v3)); 65186ac2f8bSAapo Vienamo return -1; 65286ac2f8bSAapo Vienamo } 65386ac2f8bSAapo Vienamo 65486ac2f8bSAapo Vienamo tegra_host->pinctrl_state_1v8 = 65586ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8"); 65686ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_1v8)) { 65786ac2f8bSAapo Vienamo dev_warn(dev, "Missing 1.8V pad state, err: %ld\n", 65886ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_state_3v3)); 65986ac2f8bSAapo Vienamo return -1; 66086ac2f8bSAapo Vienamo } 66186ac2f8bSAapo Vienamo 66286ac2f8bSAapo Vienamo tegra_host->pad_control_available = true; 66386ac2f8bSAapo Vienamo 66486ac2f8bSAapo Vienamo return 0; 66586ac2f8bSAapo Vienamo } 66686ac2f8bSAapo Vienamo 667e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host) 668e5c63d91SLucas Stach { 669e5c63d91SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 670e5c63d91SLucas Stach struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 671e5c63d91SLucas Stach const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 672e5c63d91SLucas Stach 673e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 674e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 675e5c63d91SLucas Stach } 676e5c63d91SLucas Stach 677c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 67885d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 67985d6509dSShawn Guo .read_w = tegra_sdhci_readw, 68085d6509dSShawn Guo .write_l = tegra_sdhci_writel, 681a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 68214b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 68303231f9bSRussell King .reset = tegra_sdhci_reset, 684c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 685a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 686e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 68744350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 68885d6509dSShawn Guo }; 68903d2bfc8SOlof Johansson 6901db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 69185d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 69285d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 69385d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 694f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 695f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 69685d6509dSShawn Guo .ops = &tegra_sdhci_ops, 69785d6509dSShawn Guo }; 69885d6509dSShawn Guo 699d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 7003e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 7013e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 7023e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 7033e44a1a7SStephen Warren }; 7043e44a1a7SStephen Warren 7051db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 7063e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 7073e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 7083e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 7093e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 710f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 711f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 712127407e3SStefan Agner .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 713726df1d5SStefan Agner SDHCI_QUIRK2_BROKEN_HS200 | 714726df1d5SStefan Agner /* 715726df1d5SStefan Agner * Auto-CMD23 leads to "Got command interrupt 0x00010000 even 716726df1d5SStefan Agner * though no command operation was in progress." 717726df1d5SStefan Agner * 718726df1d5SStefan Agner * The exact reason is unknown, as the same hardware seems 719726df1d5SStefan Agner * to support Auto CMD23 on a downstream 3.1 kernel. 720726df1d5SStefan Agner */ 721726df1d5SStefan Agner SDHCI_QUIRK2_ACMD23_BROKEN, 7223e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 7233e44a1a7SStephen Warren }; 7243e44a1a7SStephen Warren 725d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 7263e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 7273145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 7287ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 729e5c63d91SLucas Stach NVQUIRK_ENABLE_SDR104 | 730e5c63d91SLucas Stach NVQUIRK_HAS_PADCALIB, 7313e44a1a7SStephen Warren }; 7323e44a1a7SStephen Warren 73301df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 73401df7ecdSRhyland Klein .get_ro = tegra_sdhci_get_ro, 73501df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 73601df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 73701df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 738a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 73914b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 74001df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 741c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 742a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 743e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 74444350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 74501df7ecdSRhyland Klein }; 74601df7ecdSRhyland Klein 7471db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 7485ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 7495ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 7505ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 7515ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 752f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 753f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 754a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 75501df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 7565ebf2552SRhyland Klein }; 7575ebf2552SRhyland Klein 758d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 7595ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 7607bf037d6SJon Hunter }; 7617bf037d6SJon Hunter 7624ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = { 7634ae12588SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 7644ae12588SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 7654ae12588SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 7664ae12588SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 7674ae12588SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 7684ae12588SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 7694ae12588SThierry Reding .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 7704ae12588SThierry Reding /* 7714ae12588SThierry Reding * The TRM states that the SD/MMC controller found on 7724ae12588SThierry Reding * Tegra124 can address 34 bits (the maximum supported by 7734ae12588SThierry Reding * the Tegra memory controller), but tests show that DMA 7744ae12588SThierry Reding * to or from above 4 GiB doesn't work. This is possibly 7754ae12588SThierry Reding * caused by missing programming, though it's not obvious 7764ae12588SThierry Reding * what sequence is required. Mark 64-bit DMA broken for 7774ae12588SThierry Reding * now to fix this for existing users (e.g. Nyan boards). 7784ae12588SThierry Reding */ 7794ae12588SThierry Reding SDHCI_QUIRK2_BROKEN_64_BIT_DMA, 7804ae12588SThierry Reding .ops = &tegra114_sdhci_ops, 7814ae12588SThierry Reding }; 7824ae12588SThierry Reding 7834ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = { 7844ae12588SThierry Reding .pdata = &sdhci_tegra124_pdata, 7854ae12588SThierry Reding }; 7864ae12588SThierry Reding 787b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 788b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 789b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 790b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 791b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 792a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 793a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 794a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 795b5a84ecfSThierry Reding .ops = &tegra114_sdhci_ops, 796b5a84ecfSThierry Reding }; 797b5a84ecfSThierry Reding 798b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 799b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 800d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 801d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 802d4501d8eSAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP, 803b5a84ecfSThierry Reding }; 804b5a84ecfSThierry Reding 8054346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { 8064346b7c7SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 8074346b7c7SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 8084346b7c7SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 8094346b7c7SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 8104346b7c7SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 8114346b7c7SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 81268481a7eSKrishna Reddy .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 81368481a7eSKrishna Reddy /* SDHCI controllers on Tegra186 support 40-bit addressing. 81468481a7eSKrishna Reddy * IOVA addresses are 48-bit wide on Tegra186. 81568481a7eSKrishna Reddy * With 64-bit dma mask used for SDHCI, accesses can 81668481a7eSKrishna Reddy * be broken. Disable 64-bit dma, which would fall back 81768481a7eSKrishna Reddy * to 32-bit dma mask. Ideally 40-bit dma mask would work, 81868481a7eSKrishna Reddy * But it is not supported as of now. 81968481a7eSKrishna Reddy */ 82068481a7eSKrishna Reddy SDHCI_QUIRK2_BROKEN_64_BIT_DMA, 8214346b7c7SThierry Reding .ops = &tegra114_sdhci_ops, 8224346b7c7SThierry Reding }; 8234346b7c7SThierry Reding 8244346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = { 8254346b7c7SThierry Reding .pdata = &sdhci_tegra186_pdata, 826d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 827d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 828d4501d8eSAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP, 8294346b7c7SThierry Reding }; 8304346b7c7SThierry Reding 831498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 8324346b7c7SThierry Reding { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, 833b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 8344ae12588SThierry Reding { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, 8355ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 8363e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 8373e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 838275173b2SGrant Likely {} 839275173b2SGrant Likely }; 840e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 841275173b2SGrant Likely 842c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 84303d2bfc8SOlof Johansson { 8443e44a1a7SStephen Warren const struct of_device_id *match; 8453e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 8463e44a1a7SStephen Warren struct sdhci_host *host; 84785d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 8483e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 84903d2bfc8SOlof Johansson struct clk *clk; 85003d2bfc8SOlof Johansson int rc; 85103d2bfc8SOlof Johansson 8523e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 853b37f9d98SJoseph Lo if (!match) 854b37f9d98SJoseph Lo return -EINVAL; 8553e44a1a7SStephen Warren soc_data = match->data; 8563e44a1a7SStephen Warren 8570734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 85885d6509dSShawn Guo if (IS_ERR(host)) 85985d6509dSShawn Guo return PTR_ERR(host); 86085d6509dSShawn Guo pltfm_host = sdhci_priv(host); 86185d6509dSShawn Guo 8620734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host); 863a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 864e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 86586ac2f8bSAapo Vienamo tegra_host->pad_control_available = false; 8663e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 867275173b2SGrant Likely 86886ac2f8bSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) { 86986ac2f8bSAapo Vienamo rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); 87086ac2f8bSAapo Vienamo if (rc == 0) 87186ac2f8bSAapo Vienamo host->mmc_host_ops.start_signal_voltage_switch = 87286ac2f8bSAapo Vienamo sdhci_tegra_start_signal_voltage_switch; 87386ac2f8bSAapo Vienamo } 87486ac2f8bSAapo Vienamo 8752391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 87647caa84fSSimon Baatz if (rc) 87747caa84fSSimon Baatz goto err_parse_dt; 8780e786102SStephen Warren 8797ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 880c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 881c3c2384cSLucas Stach 88251b77c8eSAapo Vienamo tegra_sdhci_parse_pad_autocal_dt(host); 88351b77c8eSAapo Vienamo 8842391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 8852391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 8862391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 8872391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 88885d6509dSShawn Guo goto err_power_req; 88903d2bfc8SOlof Johansson } 89003d2bfc8SOlof Johansson 891e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 89203d2bfc8SOlof Johansson if (IS_ERR(clk)) { 89303d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 89403d2bfc8SOlof Johansson rc = PTR_ERR(clk); 89585d6509dSShawn Guo goto err_clk_get; 89603d2bfc8SOlof Johansson } 8971e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 89803d2bfc8SOlof Johansson pltfm_host->clk = clk; 89903d2bfc8SOlof Johansson 9002cd6c49dSPhilipp Zabel tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, 9012cd6c49dSPhilipp Zabel "sdhci"); 90220567be9SThierry Reding if (IS_ERR(tegra_host->rst)) { 90320567be9SThierry Reding rc = PTR_ERR(tegra_host->rst); 90420567be9SThierry Reding dev_err(&pdev->dev, "failed to get reset control: %d\n", rc); 90520567be9SThierry Reding goto err_rst_get; 90620567be9SThierry Reding } 90720567be9SThierry Reding 90820567be9SThierry Reding rc = reset_control_assert(tegra_host->rst); 90920567be9SThierry Reding if (rc) 91020567be9SThierry Reding goto err_rst_get; 91120567be9SThierry Reding 91220567be9SThierry Reding usleep_range(2000, 4000); 91320567be9SThierry Reding 91420567be9SThierry Reding rc = reset_control_deassert(tegra_host->rst); 91520567be9SThierry Reding if (rc) 91620567be9SThierry Reding goto err_rst_get; 91720567be9SThierry Reding 91820567be9SThierry Reding usleep_range(2000, 4000); 91920567be9SThierry Reding 92085d6509dSShawn Guo rc = sdhci_add_host(host); 92185d6509dSShawn Guo if (rc) 92285d6509dSShawn Guo goto err_add_host; 92385d6509dSShawn Guo 92403d2bfc8SOlof Johansson return 0; 92503d2bfc8SOlof Johansson 92685d6509dSShawn Guo err_add_host: 92720567be9SThierry Reding reset_control_assert(tegra_host->rst); 92820567be9SThierry Reding err_rst_get: 9291e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 93085d6509dSShawn Guo err_clk_get: 93185d6509dSShawn Guo err_power_req: 93247caa84fSSimon Baatz err_parse_dt: 93385d6509dSShawn Guo sdhci_pltfm_free(pdev); 93403d2bfc8SOlof Johansson return rc; 93503d2bfc8SOlof Johansson } 93603d2bfc8SOlof Johansson 93720567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev) 93820567be9SThierry Reding { 93920567be9SThierry Reding struct sdhci_host *host = platform_get_drvdata(pdev); 94020567be9SThierry Reding struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 94120567be9SThierry Reding struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 94220567be9SThierry Reding 94320567be9SThierry Reding sdhci_remove_host(host, 0); 94420567be9SThierry Reding 94520567be9SThierry Reding reset_control_assert(tegra_host->rst); 94620567be9SThierry Reding usleep_range(2000, 4000); 94720567be9SThierry Reding clk_disable_unprepare(pltfm_host->clk); 94820567be9SThierry Reding 94920567be9SThierry Reding sdhci_pltfm_free(pdev); 95020567be9SThierry Reding 95120567be9SThierry Reding return 0; 95220567be9SThierry Reding } 95320567be9SThierry Reding 95485d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 95585d6509dSShawn Guo .driver = { 95685d6509dSShawn Guo .name = "sdhci-tegra", 957275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 958fa243f64SUlf Hansson .pm = &sdhci_pltfm_pmops, 95985d6509dSShawn Guo }, 96085d6509dSShawn Guo .probe = sdhci_tegra_probe, 96120567be9SThierry Reding .remove = sdhci_tegra_remove, 96203d2bfc8SOlof Johansson }; 96303d2bfc8SOlof Johansson 964d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 96585d6509dSShawn Guo 96685d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 96785d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 96885d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 969