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> 25c3c2384cSLucas Stach #include <linux/mmc/mmc.h> 260aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 272391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 2803d2bfc8SOlof Johansson 2903d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 3003d2bfc8SOlof Johansson 31ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 3274cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 33c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 34c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 35c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 3674cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 3874cd42bcSLucas Stach 39ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 403145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 413145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 42ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 433145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 44ca5879d3SPavan Kunapuli 453e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 463e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 47ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 487ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3) 497ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4) 507ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5) 513e44a1a7SStephen Warren 523e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 531db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 543e44a1a7SStephen Warren u32 nvquirks; 553e44a1a7SStephen Warren }; 563e44a1a7SStephen Warren 573e44a1a7SStephen Warren struct sdhci_tegra { 583e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 592391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 60a8e326a9SLucas Stach bool ddr_signaling; 613e44a1a7SStephen Warren }; 623e44a1a7SStephen Warren 6303d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 6403d2bfc8SOlof Johansson { 653e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 663e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 673e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 683e44a1a7SStephen Warren 693e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 703e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 7103d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 7203d2bfc8SOlof Johansson return SDHCI_SPEC_200; 7303d2bfc8SOlof Johansson } 7403d2bfc8SOlof Johansson 7503d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 7603d2bfc8SOlof Johansson } 7703d2bfc8SOlof Johansson 78352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 79352ee868SPavan Kunapuli { 80352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 81352ee868SPavan Kunapuli 82352ee868SPavan Kunapuli switch (reg) { 83352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 84352ee868SPavan Kunapuli /* 85352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 86352ee868SPavan Kunapuli * command write that is down below. 87352ee868SPavan Kunapuli */ 88352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 89352ee868SPavan Kunapuli return; 90352ee868SPavan Kunapuli case SDHCI_COMMAND: 91352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 92352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 93352ee868SPavan Kunapuli return; 94352ee868SPavan Kunapuli } 95352ee868SPavan Kunapuli 96352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 97352ee868SPavan Kunapuli } 98352ee868SPavan Kunapuli 9903d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 10003d2bfc8SOlof Johansson { 1013e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1023e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 1033e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1043e44a1a7SStephen Warren 10503d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 10603d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 10703d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 10803d2bfc8SOlof Johansson */ 10903d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 11003d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 11103d2bfc8SOlof Johansson 11203d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 11303d2bfc8SOlof Johansson 1143e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 1153e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 11603d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 11703d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 11803d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 11903d2bfc8SOlof Johansson gap_ctrl |= 0x8; 12003d2bfc8SOlof Johansson else 12103d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 12203d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 12303d2bfc8SOlof Johansson } 12403d2bfc8SOlof Johansson } 12503d2bfc8SOlof Johansson 1263e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 12703d2bfc8SOlof Johansson { 1280aacd23fSJoseph Lo return mmc_gpio_get_ro(host->mmc); 12903d2bfc8SOlof Johansson } 13003d2bfc8SOlof Johansson 13103231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 132ca5879d3SPavan Kunapuli { 133ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 134ca5879d3SPavan Kunapuli struct sdhci_tegra *tegra_host = pltfm_host->priv; 135ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 13674cd42bcSLucas Stach u32 misc_ctrl, clk_ctrl; 137ca5879d3SPavan Kunapuli 13803231f9bSRussell King sdhci_reset(host, mask); 13903231f9bSRussell King 140ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 141ca5879d3SPavan Kunapuli return; 142ca5879d3SPavan Kunapuli 1433145351aSAndrew Bresticker misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 144ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 1453145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 146ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 1477ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */ 1487ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 1497ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 1507ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 1517ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 1527ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 1537ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 1543145351aSAndrew Bresticker sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 155a8e326a9SLucas Stach 15674cd42bcSLucas Stach clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 15774cd42bcSLucas Stach clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; 1587ad2ed1dSLucas Stach if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) 159c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 16074cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 16174cd42bcSLucas Stach 162a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 163ca5879d3SPavan Kunapuli } 164ca5879d3SPavan Kunapuli 1652317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) 16603d2bfc8SOlof Johansson { 16703d2bfc8SOlof Johansson u32 ctrl; 16803d2bfc8SOlof Johansson 16903d2bfc8SOlof Johansson ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 1700aacd23fSJoseph Lo if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && 1710aacd23fSJoseph Lo (bus_width == MMC_BUS_WIDTH_8)) { 17203d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 17303d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_8BITBUS; 17403d2bfc8SOlof Johansson } else { 17503d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_8BITBUS; 17603d2bfc8SOlof Johansson if (bus_width == MMC_BUS_WIDTH_4) 17703d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_4BITBUS; 17803d2bfc8SOlof Johansson else 17903d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 18003d2bfc8SOlof Johansson } 18103d2bfc8SOlof Johansson sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 18203d2bfc8SOlof Johansson } 18303d2bfc8SOlof Johansson 184a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 185a8e326a9SLucas Stach { 186a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 187a8e326a9SLucas Stach struct sdhci_tegra *tegra_host = pltfm_host->priv; 188a8e326a9SLucas Stach unsigned long host_clk; 189a8e326a9SLucas Stach 190a8e326a9SLucas Stach if (!clock) 191a8e326a9SLucas Stach return; 192a8e326a9SLucas Stach 193a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 194a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 195a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 196a8e326a9SLucas Stach 197a8e326a9SLucas Stach return sdhci_set_clock(host, clock); 198a8e326a9SLucas Stach } 199a8e326a9SLucas Stach 200a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 201a8e326a9SLucas Stach unsigned timing) 202a8e326a9SLucas Stach { 203a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 204a8e326a9SLucas Stach struct sdhci_tegra *tegra_host = pltfm_host->priv; 205a8e326a9SLucas Stach 206a8e326a9SLucas Stach if (timing == MMC_TIMING_UHS_DDR50) 207a8e326a9SLucas Stach tegra_host->ddr_signaling = true; 208a8e326a9SLucas Stach 209a8e326a9SLucas Stach return sdhci_set_uhs_signaling(host, timing); 210a8e326a9SLucas Stach } 211a8e326a9SLucas Stach 212a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 213a8e326a9SLucas Stach { 214a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 215a8e326a9SLucas Stach 216a8e326a9SLucas Stach /* 217a8e326a9SLucas Stach * DDR modes require the host to run at double the card frequency, so 218a8e326a9SLucas Stach * the maximum rate we can support is half of the module input clock. 219a8e326a9SLucas Stach */ 220a8e326a9SLucas Stach return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; 221a8e326a9SLucas Stach } 222a8e326a9SLucas Stach 223c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 224c3c2384cSLucas Stach { 225c3c2384cSLucas Stach u32 reg; 226c3c2384cSLucas Stach 227c3c2384cSLucas Stach reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 228c3c2384cSLucas Stach reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 229c3c2384cSLucas Stach reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 230c3c2384cSLucas Stach sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 231c3c2384cSLucas Stach } 232c3c2384cSLucas Stach 233c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 234c3c2384cSLucas Stach { 235c3c2384cSLucas Stach unsigned int min, max; 236c3c2384cSLucas Stach 237c3c2384cSLucas Stach /* 238c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 239c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 240c3c2384cSLucas Stach * according to the TRM. 241c3c2384cSLucas Stach */ 242c3c2384cSLucas Stach min = 10; 243c3c2384cSLucas Stach while (min < 255) { 244c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 245c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 246c3c2384cSLucas Stach break; 247c3c2384cSLucas Stach min++; 248c3c2384cSLucas Stach } 249c3c2384cSLucas Stach 250c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 251c3c2384cSLucas Stach max = min + 1; 252c3c2384cSLucas Stach while (max < 255) { 253c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 254c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 255c3c2384cSLucas Stach max--; 256c3c2384cSLucas Stach break; 257c3c2384cSLucas Stach } 258c3c2384cSLucas Stach max++; 259c3c2384cSLucas Stach } 260c3c2384cSLucas Stach 261c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 262c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 263c3c2384cSLucas Stach 264c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 265c3c2384cSLucas Stach } 266c3c2384cSLucas Stach 267c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 26885d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 26985d6509dSShawn Guo .read_w = tegra_sdhci_readw, 27085d6509dSShawn Guo .write_l = tegra_sdhci_writel, 271a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 2722317f56cSRussell King .set_bus_width = tegra_sdhci_set_bus_width, 27303231f9bSRussell King .reset = tegra_sdhci_reset, 274c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 275a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 276a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 27785d6509dSShawn Guo }; 27803d2bfc8SOlof Johansson 2791db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 28085d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 28185d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 28285d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 283f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 284f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 28585d6509dSShawn Guo .ops = &tegra_sdhci_ops, 28685d6509dSShawn Guo }; 28785d6509dSShawn Guo 288d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 2893e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 2903e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 2913e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 2923e44a1a7SStephen Warren }; 2933e44a1a7SStephen Warren 2941db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 2953e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 2963e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 2973e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 2983e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 299f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 300f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 301a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 3023e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 3033e44a1a7SStephen Warren }; 3043e44a1a7SStephen Warren 305d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 3063e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 3073145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 3087ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 3097ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR104, 3103e44a1a7SStephen Warren }; 3113e44a1a7SStephen Warren 31201df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 31301df7ecdSRhyland Klein .get_ro = tegra_sdhci_get_ro, 31401df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 31501df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 31601df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 317a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 31801df7ecdSRhyland Klein .set_bus_width = tegra_sdhci_set_bus_width, 31901df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 320c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 321a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 322a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 32301df7ecdSRhyland Klein }; 32401df7ecdSRhyland Klein 3251db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 3265ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 3275ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3285ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 3295ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 330f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 331f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 332a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 33301df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 3345ebf2552SRhyland Klein }; 3355ebf2552SRhyland Klein 336d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 3375ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 3387ad2ed1dSLucas Stach .nvquirks = NVQUIRK_ENABLE_SDR50 | 3397ad2ed1dSLucas Stach NVQUIRK_ENABLE_DDR50 | 3407ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR104, 3415ebf2552SRhyland Klein }; 3425ebf2552SRhyland Klein 343b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 344b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 345b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 346b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 347b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 348a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 349a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 350a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 351b5a84ecfSThierry Reding .ops = &tegra114_sdhci_ops, 352b5a84ecfSThierry Reding }; 353b5a84ecfSThierry Reding 354b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 355b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 356b5a84ecfSThierry Reding }; 357b5a84ecfSThierry Reding 358498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 359b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 36067debea3SStephen Warren { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, 3615ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 3623e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 3633e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 364275173b2SGrant Likely {} 365275173b2SGrant Likely }; 366e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 367275173b2SGrant Likely 368c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 36903d2bfc8SOlof Johansson { 3703e44a1a7SStephen Warren const struct of_device_id *match; 3713e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 3723e44a1a7SStephen Warren struct sdhci_host *host; 37385d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 3743e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 37503d2bfc8SOlof Johansson struct clk *clk; 37603d2bfc8SOlof Johansson int rc; 37703d2bfc8SOlof Johansson 3783e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 379b37f9d98SJoseph Lo if (!match) 380b37f9d98SJoseph Lo return -EINVAL; 3813e44a1a7SStephen Warren soc_data = match->data; 3823e44a1a7SStephen Warren 3830e748234SChristian Daudt host = sdhci_pltfm_init(pdev, soc_data->pdata, 0); 38485d6509dSShawn Guo if (IS_ERR(host)) 38585d6509dSShawn Guo return PTR_ERR(host); 38685d6509dSShawn Guo pltfm_host = sdhci_priv(host); 38785d6509dSShawn Guo 3883e44a1a7SStephen Warren tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); 3893e44a1a7SStephen Warren if (!tegra_host) { 3903e44a1a7SStephen Warren dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); 3913e44a1a7SStephen Warren rc = -ENOMEM; 3920e786102SStephen Warren goto err_alloc_tegra_host; 3933e44a1a7SStephen Warren } 394a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 3953e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 3963e44a1a7SStephen Warren pltfm_host->priv = tegra_host; 397275173b2SGrant Likely 3982391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 39947caa84fSSimon Baatz if (rc) 40047caa84fSSimon Baatz goto err_parse_dt; 4010e786102SStephen Warren 4027ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 403c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 404c3c2384cSLucas Stach 4052391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 4062391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 4072391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 4082391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 40985d6509dSShawn Guo goto err_power_req; 41003d2bfc8SOlof Johansson } 41103d2bfc8SOlof Johansson 412e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 41303d2bfc8SOlof Johansson if (IS_ERR(clk)) { 41403d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 41503d2bfc8SOlof Johansson rc = PTR_ERR(clk); 41685d6509dSShawn Guo goto err_clk_get; 41703d2bfc8SOlof Johansson } 4181e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 41903d2bfc8SOlof Johansson pltfm_host->clk = clk; 42003d2bfc8SOlof Johansson 42185d6509dSShawn Guo rc = sdhci_add_host(host); 42285d6509dSShawn Guo if (rc) 42385d6509dSShawn Guo goto err_add_host; 42485d6509dSShawn Guo 42503d2bfc8SOlof Johansson return 0; 42603d2bfc8SOlof Johansson 42785d6509dSShawn Guo err_add_host: 4281e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 42985d6509dSShawn Guo err_clk_get: 43085d6509dSShawn Guo err_power_req: 43147caa84fSSimon Baatz err_parse_dt: 4320e786102SStephen Warren err_alloc_tegra_host: 43385d6509dSShawn Guo sdhci_pltfm_free(pdev); 43403d2bfc8SOlof Johansson return rc; 43503d2bfc8SOlof Johansson } 43603d2bfc8SOlof Johansson 43785d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 43885d6509dSShawn Guo .driver = { 43985d6509dSShawn Guo .name = "sdhci-tegra", 440275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 44129495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 44285d6509dSShawn Guo }, 44385d6509dSShawn Guo .probe = sdhci_tegra_probe, 444caebcae9SKevin Hao .remove = sdhci_pltfm_unregister, 44503d2bfc8SOlof Johansson }; 44603d2bfc8SOlof Johansson 447d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 44885d6509dSShawn Guo 44985d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 45085d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 45185d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 452