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 1503d2bfc8SOlof Johansson #include <linux/err.h> 1696547f5dSPaul Gortmaker #include <linux/module.h> 1703d2bfc8SOlof Johansson #include <linux/init.h> 1803d2bfc8SOlof Johansson #include <linux/platform_device.h> 1903d2bfc8SOlof Johansson #include <linux/clk.h> 2003d2bfc8SOlof Johansson #include <linux/io.h> 2155cd65e4SStephen Warren #include <linux/of.h> 223e44a1a7SStephen Warren #include <linux/of_device.h> 2303d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2403d2bfc8SOlof Johansson #include <linux/mmc/host.h> 250aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 262391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 2703d2bfc8SOlof Johansson 2803d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 2903d2bfc8SOlof Johansson 30ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 31ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 323145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 333145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 34ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 353145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 36ca5879d3SPavan Kunapuli 373e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 383e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 39ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 403145351aSAndrew Bresticker #define NVQUIRK_DISABLE_SDR50 BIT(3) 413145351aSAndrew Bresticker #define NVQUIRK_DISABLE_SDR104 BIT(4) 423145351aSAndrew Bresticker #define NVQUIRK_DISABLE_DDR50 BIT(5) 433e44a1a7SStephen Warren 443e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 451db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 463e44a1a7SStephen Warren u32 nvquirks; 473e44a1a7SStephen Warren }; 483e44a1a7SStephen Warren 493e44a1a7SStephen Warren struct sdhci_tegra { 503e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 512391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 52a8e326a9SLucas Stach bool ddr_signaling; 533e44a1a7SStephen Warren }; 543e44a1a7SStephen Warren 5503d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 5603d2bfc8SOlof Johansson { 573e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 583e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 593e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 603e44a1a7SStephen Warren 613e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 623e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 6303d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 6403d2bfc8SOlof Johansson return SDHCI_SPEC_200; 6503d2bfc8SOlof Johansson } 6603d2bfc8SOlof Johansson 6703d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 6803d2bfc8SOlof Johansson } 6903d2bfc8SOlof Johansson 70352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 71352ee868SPavan Kunapuli { 72352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 73352ee868SPavan Kunapuli 74352ee868SPavan Kunapuli switch (reg) { 75352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 76352ee868SPavan Kunapuli /* 77352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 78352ee868SPavan Kunapuli * command write that is down below. 79352ee868SPavan Kunapuli */ 80352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 81352ee868SPavan Kunapuli return; 82352ee868SPavan Kunapuli case SDHCI_COMMAND: 83352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 84352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 85352ee868SPavan Kunapuli return; 86352ee868SPavan Kunapuli } 87352ee868SPavan Kunapuli 88352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 89352ee868SPavan Kunapuli } 90352ee868SPavan Kunapuli 9103d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 9203d2bfc8SOlof Johansson { 933e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 943e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 953e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 963e44a1a7SStephen Warren 9703d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 9803d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 9903d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 10003d2bfc8SOlof Johansson */ 10103d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 10203d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 10303d2bfc8SOlof Johansson 10403d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 10503d2bfc8SOlof Johansson 1063e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 1073e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 10803d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 10903d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 11003d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 11103d2bfc8SOlof Johansson gap_ctrl |= 0x8; 11203d2bfc8SOlof Johansson else 11303d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 11403d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 11503d2bfc8SOlof Johansson } 11603d2bfc8SOlof Johansson } 11703d2bfc8SOlof Johansson 1183e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 11903d2bfc8SOlof Johansson { 1200aacd23fSJoseph Lo return mmc_gpio_get_ro(host->mmc); 12103d2bfc8SOlof Johansson } 12203d2bfc8SOlof Johansson 12303231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 124ca5879d3SPavan Kunapuli { 125ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 126ca5879d3SPavan Kunapuli struct sdhci_tegra *tegra_host = pltfm_host->priv; 127ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1283145351aSAndrew Bresticker u32 misc_ctrl; 129ca5879d3SPavan Kunapuli 13003231f9bSRussell King sdhci_reset(host, mask); 13103231f9bSRussell King 132ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 133ca5879d3SPavan Kunapuli return; 134ca5879d3SPavan Kunapuli 1353145351aSAndrew Bresticker misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 136ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 1373145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 138ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 1393145351aSAndrew Bresticker /* Don't advertise UHS modes which aren't supported yet */ 1403145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50) 1413145351aSAndrew Bresticker misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; 1423145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50) 1433145351aSAndrew Bresticker misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; 1443145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104) 1453145351aSAndrew Bresticker misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; 1463145351aSAndrew Bresticker sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 147a8e326a9SLucas Stach 148a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 149ca5879d3SPavan Kunapuli } 150ca5879d3SPavan Kunapuli 1512317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) 15203d2bfc8SOlof Johansson { 15303d2bfc8SOlof Johansson u32 ctrl; 15403d2bfc8SOlof Johansson 15503d2bfc8SOlof Johansson ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 1560aacd23fSJoseph Lo if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && 1570aacd23fSJoseph Lo (bus_width == MMC_BUS_WIDTH_8)) { 15803d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 15903d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_8BITBUS; 16003d2bfc8SOlof Johansson } else { 16103d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_8BITBUS; 16203d2bfc8SOlof Johansson if (bus_width == MMC_BUS_WIDTH_4) 16303d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_4BITBUS; 16403d2bfc8SOlof Johansson else 16503d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 16603d2bfc8SOlof Johansson } 16703d2bfc8SOlof Johansson sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 16803d2bfc8SOlof Johansson } 16903d2bfc8SOlof Johansson 170a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 171a8e326a9SLucas Stach { 172a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 173a8e326a9SLucas Stach struct sdhci_tegra *tegra_host = pltfm_host->priv; 174a8e326a9SLucas Stach unsigned long host_clk; 175a8e326a9SLucas Stach 176a8e326a9SLucas Stach if (!clock) 177a8e326a9SLucas Stach return; 178a8e326a9SLucas Stach 179a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 180a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 181a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 182a8e326a9SLucas Stach 183a8e326a9SLucas Stach return sdhci_set_clock(host, clock); 184a8e326a9SLucas Stach } 185a8e326a9SLucas Stach 186a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 187a8e326a9SLucas Stach unsigned timing) 188a8e326a9SLucas Stach { 189a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 190a8e326a9SLucas Stach struct sdhci_tegra *tegra_host = pltfm_host->priv; 191a8e326a9SLucas Stach 192a8e326a9SLucas Stach if (timing == MMC_TIMING_UHS_DDR50) 193a8e326a9SLucas Stach tegra_host->ddr_signaling = true; 194a8e326a9SLucas Stach 195a8e326a9SLucas Stach return sdhci_set_uhs_signaling(host, timing); 196a8e326a9SLucas Stach } 197a8e326a9SLucas Stach 198a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 199a8e326a9SLucas Stach { 200a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 201a8e326a9SLucas Stach 202a8e326a9SLucas Stach /* 203a8e326a9SLucas Stach * DDR modes require the host to run at double the card frequency, so 204a8e326a9SLucas Stach * the maximum rate we can support is half of the module input clock. 205a8e326a9SLucas Stach */ 206a8e326a9SLucas Stach return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; 207a8e326a9SLucas Stach } 208a8e326a9SLucas Stach 209c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 21085d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 21185d6509dSShawn Guo .read_w = tegra_sdhci_readw, 21285d6509dSShawn Guo .write_l = tegra_sdhci_writel, 213a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 2142317f56cSRussell King .set_bus_width = tegra_sdhci_set_bus_width, 21503231f9bSRussell King .reset = tegra_sdhci_reset, 216a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 217a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 21885d6509dSShawn Guo }; 21903d2bfc8SOlof Johansson 2201db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 22185d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 22285d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 22385d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 224f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 225f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 22685d6509dSShawn Guo .ops = &tegra_sdhci_ops, 22785d6509dSShawn Guo }; 22885d6509dSShawn Guo 229d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 2303e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 2313e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 2323e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 2333e44a1a7SStephen Warren }; 2343e44a1a7SStephen Warren 2351db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 2363e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 2373e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 2383e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 2393e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 240f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 241f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 242a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 2433e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 2443e44a1a7SStephen Warren }; 2453e44a1a7SStephen Warren 246d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 2473e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 2483145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 2493145351aSAndrew Bresticker NVQUIRK_DISABLE_SDR50 | 2503145351aSAndrew Bresticker NVQUIRK_DISABLE_SDR104, 2513e44a1a7SStephen Warren }; 2523e44a1a7SStephen Warren 25301df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 25401df7ecdSRhyland Klein .get_ro = tegra_sdhci_get_ro, 25501df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 25601df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 25701df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 258a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 25901df7ecdSRhyland Klein .set_bus_width = tegra_sdhci_set_bus_width, 26001df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 261a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 262a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 26301df7ecdSRhyland Klein }; 26401df7ecdSRhyland Klein 2651db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 2665ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 2675ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 2685ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 2695ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 270f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 271f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 272a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 27301df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 2745ebf2552SRhyland Klein }; 2755ebf2552SRhyland Klein 276d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 2775ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 2783145351aSAndrew Bresticker .nvquirks = NVQUIRK_DISABLE_SDR50 | 2793145351aSAndrew Bresticker NVQUIRK_DISABLE_DDR50 | 28001df7ecdSRhyland Klein NVQUIRK_DISABLE_SDR104, 2815ebf2552SRhyland Klein }; 2825ebf2552SRhyland Klein 283b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 284b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 285b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 286b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 287b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 288a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 289a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 290a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 291b5a84ecfSThierry Reding .ops = &tegra114_sdhci_ops, 292b5a84ecfSThierry Reding }; 293b5a84ecfSThierry Reding 294b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 295b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 296b5a84ecfSThierry Reding .nvquirks = NVQUIRK_DISABLE_SDR50 | 297b5a84ecfSThierry Reding NVQUIRK_DISABLE_DDR50 | 298b5a84ecfSThierry Reding NVQUIRK_DISABLE_SDR104, 299b5a84ecfSThierry Reding }; 300b5a84ecfSThierry Reding 301498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 302b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 30367debea3SStephen Warren { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, 3045ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 3053e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 3063e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 307275173b2SGrant Likely {} 308275173b2SGrant Likely }; 309e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 310275173b2SGrant Likely 311c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 31203d2bfc8SOlof Johansson { 3133e44a1a7SStephen Warren const struct of_device_id *match; 3143e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 3153e44a1a7SStephen Warren struct sdhci_host *host; 31685d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 3173e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 31803d2bfc8SOlof Johansson struct clk *clk; 31903d2bfc8SOlof Johansson int rc; 32003d2bfc8SOlof Johansson 3213e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 322b37f9d98SJoseph Lo if (!match) 323b37f9d98SJoseph Lo return -EINVAL; 3243e44a1a7SStephen Warren soc_data = match->data; 3253e44a1a7SStephen Warren 3260e748234SChristian Daudt host = sdhci_pltfm_init(pdev, soc_data->pdata, 0); 32785d6509dSShawn Guo if (IS_ERR(host)) 32885d6509dSShawn Guo return PTR_ERR(host); 32985d6509dSShawn Guo pltfm_host = sdhci_priv(host); 33085d6509dSShawn Guo 3313e44a1a7SStephen Warren tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); 3323e44a1a7SStephen Warren if (!tegra_host) { 3333e44a1a7SStephen Warren dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); 3343e44a1a7SStephen Warren rc = -ENOMEM; 3350e786102SStephen Warren goto err_alloc_tegra_host; 3363e44a1a7SStephen Warren } 337a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 3383e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 3393e44a1a7SStephen Warren pltfm_host->priv = tegra_host; 340275173b2SGrant Likely 3412391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 34247caa84fSSimon Baatz if (rc) 34347caa84fSSimon Baatz goto err_parse_dt; 3440e786102SStephen Warren 3452391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 3462391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 3472391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 3482391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 34985d6509dSShawn Guo goto err_power_req; 35003d2bfc8SOlof Johansson } 35103d2bfc8SOlof Johansson 352e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 35303d2bfc8SOlof Johansson if (IS_ERR(clk)) { 35403d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 35503d2bfc8SOlof Johansson rc = PTR_ERR(clk); 35685d6509dSShawn Guo goto err_clk_get; 35703d2bfc8SOlof Johansson } 3581e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 35903d2bfc8SOlof Johansson pltfm_host->clk = clk; 36003d2bfc8SOlof Johansson 36185d6509dSShawn Guo rc = sdhci_add_host(host); 36285d6509dSShawn Guo if (rc) 36385d6509dSShawn Guo goto err_add_host; 36485d6509dSShawn Guo 36503d2bfc8SOlof Johansson return 0; 36603d2bfc8SOlof Johansson 36785d6509dSShawn Guo err_add_host: 3681e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 36985d6509dSShawn Guo err_clk_get: 37085d6509dSShawn Guo err_power_req: 37147caa84fSSimon Baatz err_parse_dt: 3720e786102SStephen Warren err_alloc_tegra_host: 37385d6509dSShawn Guo sdhci_pltfm_free(pdev); 37403d2bfc8SOlof Johansson return rc; 37503d2bfc8SOlof Johansson } 37603d2bfc8SOlof Johansson 37785d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 37885d6509dSShawn Guo .driver = { 37985d6509dSShawn Guo .name = "sdhci-tegra", 380275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 38129495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 38285d6509dSShawn Guo }, 38385d6509dSShawn Guo .probe = sdhci_tegra_probe, 384caebcae9SKevin Hao .remove = sdhci_pltfm_unregister, 38503d2bfc8SOlof Johansson }; 38603d2bfc8SOlof Johansson 387d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 38885d6509dSShawn Guo 38985d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 39085d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 39185d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 392