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); 660734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 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); 1020734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 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); 1340734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 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 1431b84def8SLucas Stach misc_ctrl = sdhci_readl(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; 1507bf037d6SJon Hunter else 1517bf037d6SJon Hunter misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; 1527ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 1537ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 1547bf037d6SJon Hunter else 1557bf037d6SJon Hunter misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; 1567ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 1577ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 1587bf037d6SJon Hunter else 1597bf037d6SJon Hunter misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; 1601b84def8SLucas Stach sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 161a8e326a9SLucas Stach 16274cd42bcSLucas Stach clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 16374cd42bcSLucas Stach clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; 1647ad2ed1dSLucas Stach if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) 165c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 16674cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 16774cd42bcSLucas Stach 168a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 169ca5879d3SPavan Kunapuli } 170ca5879d3SPavan Kunapuli 1712317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) 17203d2bfc8SOlof Johansson { 17303d2bfc8SOlof Johansson u32 ctrl; 17403d2bfc8SOlof Johansson 17503d2bfc8SOlof Johansson ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 1760aacd23fSJoseph Lo if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && 1770aacd23fSJoseph Lo (bus_width == MMC_BUS_WIDTH_8)) { 17803d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 17903d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_8BITBUS; 18003d2bfc8SOlof Johansson } else { 18103d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_8BITBUS; 18203d2bfc8SOlof Johansson if (bus_width == MMC_BUS_WIDTH_4) 18303d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_4BITBUS; 18403d2bfc8SOlof Johansson else 18503d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 18603d2bfc8SOlof Johansson } 18703d2bfc8SOlof Johansson sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 18803d2bfc8SOlof Johansson } 18903d2bfc8SOlof Johansson 190a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 191a8e326a9SLucas Stach { 192a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1930734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 194a8e326a9SLucas Stach unsigned long host_clk; 195a8e326a9SLucas Stach 196a8e326a9SLucas Stach if (!clock) 197a8e326a9SLucas Stach return; 198a8e326a9SLucas Stach 199a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 200a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 201a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 202a8e326a9SLucas Stach 203a8e326a9SLucas Stach return sdhci_set_clock(host, clock); 204a8e326a9SLucas Stach } 205a8e326a9SLucas Stach 206a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 207a8e326a9SLucas Stach unsigned timing) 208a8e326a9SLucas Stach { 209a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2100734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 211a8e326a9SLucas Stach 212a8e326a9SLucas Stach if (timing == MMC_TIMING_UHS_DDR50) 213a8e326a9SLucas Stach tegra_host->ddr_signaling = true; 214a8e326a9SLucas Stach 215a8e326a9SLucas Stach return sdhci_set_uhs_signaling(host, timing); 216a8e326a9SLucas Stach } 217a8e326a9SLucas Stach 218a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 219a8e326a9SLucas Stach { 220a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 221a8e326a9SLucas Stach 222a8e326a9SLucas Stach /* 223a8e326a9SLucas Stach * DDR modes require the host to run at double the card frequency, so 224a8e326a9SLucas Stach * the maximum rate we can support is half of the module input clock. 225a8e326a9SLucas Stach */ 226a8e326a9SLucas Stach return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; 227a8e326a9SLucas Stach } 228a8e326a9SLucas Stach 229c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 230c3c2384cSLucas Stach { 231c3c2384cSLucas Stach u32 reg; 232c3c2384cSLucas Stach 233c3c2384cSLucas Stach reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 234c3c2384cSLucas Stach reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 235c3c2384cSLucas Stach reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 236c3c2384cSLucas Stach sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 237c3c2384cSLucas Stach } 238c3c2384cSLucas Stach 239c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 240c3c2384cSLucas Stach { 241c3c2384cSLucas Stach unsigned int min, max; 242c3c2384cSLucas Stach 243c3c2384cSLucas Stach /* 244c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 245c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 246c3c2384cSLucas Stach * according to the TRM. 247c3c2384cSLucas Stach */ 248c3c2384cSLucas Stach min = 10; 249c3c2384cSLucas Stach while (min < 255) { 250c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 251c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 252c3c2384cSLucas Stach break; 253c3c2384cSLucas Stach min++; 254c3c2384cSLucas Stach } 255c3c2384cSLucas Stach 256c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 257c3c2384cSLucas Stach max = min + 1; 258c3c2384cSLucas Stach while (max < 255) { 259c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 260c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 261c3c2384cSLucas Stach max--; 262c3c2384cSLucas Stach break; 263c3c2384cSLucas Stach } 264c3c2384cSLucas Stach max++; 265c3c2384cSLucas Stach } 266c3c2384cSLucas Stach 267c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 268c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 269c3c2384cSLucas Stach 270c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 271c3c2384cSLucas Stach } 272c3c2384cSLucas Stach 273c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 27485d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 27585d6509dSShawn Guo .read_w = tegra_sdhci_readw, 27685d6509dSShawn Guo .write_l = tegra_sdhci_writel, 277a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 2782317f56cSRussell King .set_bus_width = tegra_sdhci_set_bus_width, 27903231f9bSRussell King .reset = tegra_sdhci_reset, 280c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 281a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 282a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 28385d6509dSShawn Guo }; 28403d2bfc8SOlof Johansson 2851db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 28685d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 28785d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 28885d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 289f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 290f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 29185d6509dSShawn Guo .ops = &tegra_sdhci_ops, 29285d6509dSShawn Guo }; 29385d6509dSShawn Guo 294d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 2953e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 2963e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 2973e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 2983e44a1a7SStephen Warren }; 2993e44a1a7SStephen Warren 3001db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 3013e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 3023e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3033e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 3043e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 305f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 306f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 307a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 3083e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 3093e44a1a7SStephen Warren }; 3103e44a1a7SStephen Warren 311d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 3123e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 3133145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 3147ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 3157ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR104, 3163e44a1a7SStephen Warren }; 3173e44a1a7SStephen Warren 31801df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 31901df7ecdSRhyland Klein .get_ro = tegra_sdhci_get_ro, 32001df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 32101df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 32201df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 323a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 32401df7ecdSRhyland Klein .set_bus_width = tegra_sdhci_set_bus_width, 32501df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 326c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 327a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 328a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 32901df7ecdSRhyland Klein }; 33001df7ecdSRhyland Klein 3311db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 3325ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 3335ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3345ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 3355ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 336f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 337f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 338a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 33901df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 3405ebf2552SRhyland Klein }; 3415ebf2552SRhyland Klein 342d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 3435ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 3447bf037d6SJon Hunter }; 3457bf037d6SJon Hunter 3467bf037d6SJon Hunter static const struct sdhci_tegra_soc_data soc_data_tegra124 = { 3477bf037d6SJon Hunter .pdata = &sdhci_tegra114_pdata, 3487ad2ed1dSLucas Stach .nvquirks = NVQUIRK_ENABLE_SDR50 | 3497ad2ed1dSLucas Stach NVQUIRK_ENABLE_DDR50 | 3507ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR104, 3515ebf2552SRhyland Klein }; 3525ebf2552SRhyland Klein 353b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 354b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 355b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 356b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 357b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 358a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 359a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 360a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 361b5a84ecfSThierry Reding .ops = &tegra114_sdhci_ops, 362b5a84ecfSThierry Reding }; 363b5a84ecfSThierry Reding 364b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 365b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 366b5a84ecfSThierry Reding }; 367b5a84ecfSThierry Reding 368498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 369b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 3707bf037d6SJon Hunter { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, 3715ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 3723e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 3733e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 374275173b2SGrant Likely {} 375275173b2SGrant Likely }; 376e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 377275173b2SGrant Likely 378c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 37903d2bfc8SOlof Johansson { 3803e44a1a7SStephen Warren const struct of_device_id *match; 3813e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 3823e44a1a7SStephen Warren struct sdhci_host *host; 38385d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 3843e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 38503d2bfc8SOlof Johansson struct clk *clk; 38603d2bfc8SOlof Johansson int rc; 38703d2bfc8SOlof Johansson 3883e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 389b37f9d98SJoseph Lo if (!match) 390b37f9d98SJoseph Lo return -EINVAL; 3913e44a1a7SStephen Warren soc_data = match->data; 3923e44a1a7SStephen Warren 3930734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 39485d6509dSShawn Guo if (IS_ERR(host)) 39585d6509dSShawn Guo return PTR_ERR(host); 39685d6509dSShawn Guo pltfm_host = sdhci_priv(host); 39785d6509dSShawn Guo 3980734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host); 399a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 4003e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 401275173b2SGrant Likely 4022391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 40347caa84fSSimon Baatz if (rc) 40447caa84fSSimon Baatz goto err_parse_dt; 4050e786102SStephen Warren 4067ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 407c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 408c3c2384cSLucas Stach 4092391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 4102391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 4112391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 4122391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 41385d6509dSShawn Guo goto err_power_req; 41403d2bfc8SOlof Johansson } 41503d2bfc8SOlof Johansson 416e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 41703d2bfc8SOlof Johansson if (IS_ERR(clk)) { 41803d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 41903d2bfc8SOlof Johansson rc = PTR_ERR(clk); 42085d6509dSShawn Guo goto err_clk_get; 42103d2bfc8SOlof Johansson } 4221e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 42303d2bfc8SOlof Johansson pltfm_host->clk = clk; 42403d2bfc8SOlof Johansson 42585d6509dSShawn Guo rc = sdhci_add_host(host); 42685d6509dSShawn Guo if (rc) 42785d6509dSShawn Guo goto err_add_host; 42885d6509dSShawn Guo 42903d2bfc8SOlof Johansson return 0; 43003d2bfc8SOlof Johansson 43185d6509dSShawn Guo err_add_host: 4321e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 43385d6509dSShawn Guo err_clk_get: 43485d6509dSShawn Guo err_power_req: 43547caa84fSSimon Baatz err_parse_dt: 43685d6509dSShawn Guo sdhci_pltfm_free(pdev); 43703d2bfc8SOlof Johansson return rc; 43803d2bfc8SOlof Johansson } 43903d2bfc8SOlof Johansson 44085d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 44185d6509dSShawn Guo .driver = { 44285d6509dSShawn Guo .name = "sdhci-tegra", 443275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 44429495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 44585d6509dSShawn Guo }, 44685d6509dSShawn Guo .probe = sdhci_tegra_probe, 447caebcae9SKevin Hao .remove = sdhci_pltfm_unregister, 44803d2bfc8SOlof Johansson }; 44903d2bfc8SOlof Johansson 450d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 45185d6509dSShawn Guo 45285d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 45385d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 45485d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 455