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> 1903d2bfc8SOlof Johansson #include <linux/platform_device.h> 2003d2bfc8SOlof Johansson #include <linux/clk.h> 2103d2bfc8SOlof Johansson #include <linux/io.h> 2255cd65e4SStephen Warren #include <linux/of.h> 233e44a1a7SStephen Warren #include <linux/of_device.h> 2403d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2503d2bfc8SOlof Johansson #include <linux/mmc/host.h> 26c3c2384cSLucas Stach #include <linux/mmc/mmc.h> 270aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 282391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 2903d2bfc8SOlof Johansson 3003d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 3103d2bfc8SOlof Johansson 32ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 3374cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 34c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 35c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 36c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 3874cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 3974cd42bcSLucas Stach 40ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 413145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 423145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 43ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 443145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 45ca5879d3SPavan Kunapuli 46e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 47e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START BIT(31) 48e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE BIT(29) 49e5c63d91SLucas Stach 503e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 513e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 52ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 537ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3) 547ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4) 557ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5) 56e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB BIT(6) 573e44a1a7SStephen Warren 583e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 591db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 603e44a1a7SStephen Warren u32 nvquirks; 613e44a1a7SStephen Warren }; 623e44a1a7SStephen Warren 633e44a1a7SStephen Warren struct sdhci_tegra { 643e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 652391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 66a8e326a9SLucas Stach bool ddr_signaling; 67e5c63d91SLucas Stach bool pad_calib_required; 683e44a1a7SStephen Warren }; 693e44a1a7SStephen Warren 7003d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 7103d2bfc8SOlof Johansson { 723e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 730734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 743e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 753e44a1a7SStephen Warren 763e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 773e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 7803d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 7903d2bfc8SOlof Johansson return SDHCI_SPEC_200; 8003d2bfc8SOlof Johansson } 8103d2bfc8SOlof Johansson 8203d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 8303d2bfc8SOlof Johansson } 8403d2bfc8SOlof Johansson 85352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 86352ee868SPavan Kunapuli { 87352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 88352ee868SPavan Kunapuli 89352ee868SPavan Kunapuli switch (reg) { 90352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 91352ee868SPavan Kunapuli /* 92352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 93352ee868SPavan Kunapuli * command write that is down below. 94352ee868SPavan Kunapuli */ 95352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 96352ee868SPavan Kunapuli return; 97352ee868SPavan Kunapuli case SDHCI_COMMAND: 98352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 99352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 100352ee868SPavan Kunapuli return; 101352ee868SPavan Kunapuli } 102352ee868SPavan Kunapuli 103352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 104352ee868SPavan Kunapuli } 105352ee868SPavan Kunapuli 10603d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 10703d2bfc8SOlof Johansson { 1083e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1090734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1103e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1113e44a1a7SStephen Warren 11203d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 11303d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 11403d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 11503d2bfc8SOlof Johansson */ 11603d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 11703d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 11803d2bfc8SOlof Johansson 11903d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 12003d2bfc8SOlof Johansson 1213e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 1223e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 12303d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 12403d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 12503d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 12603d2bfc8SOlof Johansson gap_ctrl |= 0x8; 12703d2bfc8SOlof Johansson else 12803d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 12903d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 13003d2bfc8SOlof Johansson } 13103d2bfc8SOlof Johansson } 13203d2bfc8SOlof Johansson 1333e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 13403d2bfc8SOlof Johansson { 1350aacd23fSJoseph Lo return mmc_gpio_get_ro(host->mmc); 13603d2bfc8SOlof Johansson } 13703d2bfc8SOlof Johansson 13803231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 139ca5879d3SPavan Kunapuli { 140ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1410734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 142ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 14374cd42bcSLucas Stach u32 misc_ctrl, clk_ctrl; 144ca5879d3SPavan Kunapuli 14503231f9bSRussell King sdhci_reset(host, mask); 14603231f9bSRussell King 147ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 148ca5879d3SPavan Kunapuli return; 149ca5879d3SPavan Kunapuli 1501b84def8SLucas Stach misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 1514f6aa326SJon Hunter clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 1524f6aa326SJon Hunter 1534f6aa326SJon Hunter misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | 1544f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR50 | 1554f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_DDR50 | 1564f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR104); 1574f6aa326SJon Hunter 1584f6aa326SJon Hunter clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; 1594f6aa326SJon Hunter 1604f6aa326SJon Hunter /* 1614f6aa326SJon Hunter * If the board does not define a regulator for the SDHCI 1624f6aa326SJon Hunter * IO voltage, then don't advertise support for UHS modes 1634f6aa326SJon Hunter * even if the device supports it because the IO voltage 1644f6aa326SJon Hunter * cannot be configured. 1654f6aa326SJon Hunter */ 1664f6aa326SJon Hunter if (!IS_ERR(host->mmc->supply.vqmmc)) { 167ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 1683145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 169ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 1707ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */ 1717ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 1727ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 1737ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 1747ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 1757ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 1767ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 1777ad2ed1dSLucas Stach if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) 178c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 1794f6aa326SJon Hunter } 1804f6aa326SJon Hunter 1814f6aa326SJon Hunter sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 18274cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 18374cd42bcSLucas Stach 184e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 185e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 186e5c63d91SLucas Stach 187a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 188ca5879d3SPavan Kunapuli } 189ca5879d3SPavan Kunapuli 1902317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) 19103d2bfc8SOlof Johansson { 19203d2bfc8SOlof Johansson u32 ctrl; 19303d2bfc8SOlof Johansson 19403d2bfc8SOlof Johansson ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 1950aacd23fSJoseph Lo if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && 1960aacd23fSJoseph Lo (bus_width == MMC_BUS_WIDTH_8)) { 19703d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 19803d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_8BITBUS; 19903d2bfc8SOlof Johansson } else { 20003d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_8BITBUS; 20103d2bfc8SOlof Johansson if (bus_width == MMC_BUS_WIDTH_4) 20203d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_4BITBUS; 20303d2bfc8SOlof Johansson else 20403d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 20503d2bfc8SOlof Johansson } 20603d2bfc8SOlof Johansson sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 20703d2bfc8SOlof Johansson } 20803d2bfc8SOlof Johansson 209e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) 210e5c63d91SLucas Stach { 211e5c63d91SLucas Stach u32 val; 212e5c63d91SLucas Stach 213e5c63d91SLucas Stach mdelay(1); 214e5c63d91SLucas Stach 215e5c63d91SLucas Stach val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 216e5c63d91SLucas Stach val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; 217e5c63d91SLucas Stach sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG); 218e5c63d91SLucas Stach } 219e5c63d91SLucas Stach 220a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 221a8e326a9SLucas Stach { 222a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2230734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 224a8e326a9SLucas Stach unsigned long host_clk; 225a8e326a9SLucas Stach 226a8e326a9SLucas Stach if (!clock) 2273491b690SLucas Stach return sdhci_set_clock(host, clock); 228a8e326a9SLucas Stach 229a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 230a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 231a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 232a8e326a9SLucas Stach 233e5c63d91SLucas Stach sdhci_set_clock(host, clock); 234e5c63d91SLucas Stach 235e5c63d91SLucas Stach if (tegra_host->pad_calib_required) { 236e5c63d91SLucas Stach tegra_sdhci_pad_autocalib(host); 237e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 238e5c63d91SLucas Stach } 239a8e326a9SLucas Stach } 240a8e326a9SLucas Stach 241a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 242a8e326a9SLucas Stach unsigned timing) 243a8e326a9SLucas Stach { 244a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2450734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 246a8e326a9SLucas Stach 247a8e326a9SLucas Stach if (timing == MMC_TIMING_UHS_DDR50) 248a8e326a9SLucas Stach tegra_host->ddr_signaling = true; 249a8e326a9SLucas Stach 250a8e326a9SLucas Stach return sdhci_set_uhs_signaling(host, timing); 251a8e326a9SLucas Stach } 252a8e326a9SLucas Stach 253a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 254a8e326a9SLucas Stach { 255a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 256a8e326a9SLucas Stach 257a8e326a9SLucas Stach /* 258a8e326a9SLucas Stach * DDR modes require the host to run at double the card frequency, so 259a8e326a9SLucas Stach * the maximum rate we can support is half of the module input clock. 260a8e326a9SLucas Stach */ 261a8e326a9SLucas Stach return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; 262a8e326a9SLucas Stach } 263a8e326a9SLucas Stach 264c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 265c3c2384cSLucas Stach { 266c3c2384cSLucas Stach u32 reg; 267c3c2384cSLucas Stach 268c3c2384cSLucas Stach reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 269c3c2384cSLucas Stach reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 270c3c2384cSLucas Stach reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 271c3c2384cSLucas Stach sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 272c3c2384cSLucas Stach } 273c3c2384cSLucas Stach 274c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 275c3c2384cSLucas Stach { 276c3c2384cSLucas Stach unsigned int min, max; 277c3c2384cSLucas Stach 278c3c2384cSLucas Stach /* 279c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 280c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 281c3c2384cSLucas Stach * according to the TRM. 282c3c2384cSLucas Stach */ 283c3c2384cSLucas Stach min = 10; 284c3c2384cSLucas Stach while (min < 255) { 285c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 286c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 287c3c2384cSLucas Stach break; 288c3c2384cSLucas Stach min++; 289c3c2384cSLucas Stach } 290c3c2384cSLucas Stach 291c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 292c3c2384cSLucas Stach max = min + 1; 293c3c2384cSLucas Stach while (max < 255) { 294c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 295c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 296c3c2384cSLucas Stach max--; 297c3c2384cSLucas Stach break; 298c3c2384cSLucas Stach } 299c3c2384cSLucas Stach max++; 300c3c2384cSLucas Stach } 301c3c2384cSLucas Stach 302c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 303c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 304c3c2384cSLucas Stach 305c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 306c3c2384cSLucas Stach } 307c3c2384cSLucas Stach 308e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host) 309e5c63d91SLucas Stach { 310e5c63d91SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 311e5c63d91SLucas Stach struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 312e5c63d91SLucas Stach const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 313e5c63d91SLucas Stach 314e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 315e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 316e5c63d91SLucas Stach } 317e5c63d91SLucas Stach 318c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 31985d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 32085d6509dSShawn Guo .read_w = tegra_sdhci_readw, 32185d6509dSShawn Guo .write_l = tegra_sdhci_writel, 322a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 3232317f56cSRussell King .set_bus_width = tegra_sdhci_set_bus_width, 32403231f9bSRussell King .reset = tegra_sdhci_reset, 325c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 326a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 327e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 328a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 32985d6509dSShawn Guo }; 33003d2bfc8SOlof Johansson 3311db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 33285d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 33385d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 33485d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 335f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 336f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 33785d6509dSShawn Guo .ops = &tegra_sdhci_ops, 33885d6509dSShawn Guo }; 33985d6509dSShawn Guo 340d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 3413e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 3423e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 3433e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 3443e44a1a7SStephen Warren }; 3453e44a1a7SStephen Warren 3461db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 3473e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 3483e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3493e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 3503e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 351f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 352f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 353a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 3543e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 3553e44a1a7SStephen Warren }; 3563e44a1a7SStephen Warren 357d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 3583e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 3593145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 3607ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 361e5c63d91SLucas Stach NVQUIRK_ENABLE_SDR104 | 362e5c63d91SLucas Stach NVQUIRK_HAS_PADCALIB, 3633e44a1a7SStephen Warren }; 3643e44a1a7SStephen Warren 36501df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 36601df7ecdSRhyland Klein .get_ro = tegra_sdhci_get_ro, 36701df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 36801df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 36901df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 370a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 37101df7ecdSRhyland Klein .set_bus_width = tegra_sdhci_set_bus_width, 37201df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 373c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 374a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 375e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 376a8e326a9SLucas Stach .get_max_clock = tegra_sdhci_get_max_clock, 37701df7ecdSRhyland Klein }; 37801df7ecdSRhyland Klein 3791db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 3805ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 3815ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 3825ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 3835ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 384f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 385f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 386a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 38701df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 3885ebf2552SRhyland Klein }; 3895ebf2552SRhyland Klein 390d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 3915ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 3927bf037d6SJon Hunter }; 3937bf037d6SJon Hunter 394b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 395b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 396b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 397b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 398b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 399a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 400a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 401a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 402b5a84ecfSThierry Reding .ops = &tegra114_sdhci_ops, 403b5a84ecfSThierry Reding }; 404b5a84ecfSThierry Reding 405b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 406b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 407b5a84ecfSThierry Reding }; 408b5a84ecfSThierry Reding 409498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 410b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 41170ad7f7eSJon Hunter { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, 4125ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 4133e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 4143e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 415275173b2SGrant Likely {} 416275173b2SGrant Likely }; 417e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 418275173b2SGrant Likely 419c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 42003d2bfc8SOlof Johansson { 4213e44a1a7SStephen Warren const struct of_device_id *match; 4223e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 4233e44a1a7SStephen Warren struct sdhci_host *host; 42485d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 4253e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 42603d2bfc8SOlof Johansson struct clk *clk; 42703d2bfc8SOlof Johansson int rc; 42803d2bfc8SOlof Johansson 4293e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 430b37f9d98SJoseph Lo if (!match) 431b37f9d98SJoseph Lo return -EINVAL; 4323e44a1a7SStephen Warren soc_data = match->data; 4333e44a1a7SStephen Warren 4340734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 43585d6509dSShawn Guo if (IS_ERR(host)) 43685d6509dSShawn Guo return PTR_ERR(host); 43785d6509dSShawn Guo pltfm_host = sdhci_priv(host); 43885d6509dSShawn Guo 4390734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host); 440a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 441e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 4423e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 443275173b2SGrant Likely 4442391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 44547caa84fSSimon Baatz if (rc) 44647caa84fSSimon Baatz goto err_parse_dt; 4470e786102SStephen Warren 4487ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 449c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 450c3c2384cSLucas Stach 4512391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 4522391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 4532391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 4542391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 45585d6509dSShawn Guo goto err_power_req; 45603d2bfc8SOlof Johansson } 45703d2bfc8SOlof Johansson 458e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 45903d2bfc8SOlof Johansson if (IS_ERR(clk)) { 46003d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 46103d2bfc8SOlof Johansson rc = PTR_ERR(clk); 46285d6509dSShawn Guo goto err_clk_get; 46303d2bfc8SOlof Johansson } 4641e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 46503d2bfc8SOlof Johansson pltfm_host->clk = clk; 46603d2bfc8SOlof Johansson 46785d6509dSShawn Guo rc = sdhci_add_host(host); 46885d6509dSShawn Guo if (rc) 46985d6509dSShawn Guo goto err_add_host; 47085d6509dSShawn Guo 47103d2bfc8SOlof Johansson return 0; 47203d2bfc8SOlof Johansson 47385d6509dSShawn Guo err_add_host: 4741e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 47585d6509dSShawn Guo err_clk_get: 47685d6509dSShawn Guo err_power_req: 47747caa84fSSimon Baatz err_parse_dt: 47885d6509dSShawn Guo sdhci_pltfm_free(pdev); 47903d2bfc8SOlof Johansson return rc; 48003d2bfc8SOlof Johansson } 48103d2bfc8SOlof Johansson 48285d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 48385d6509dSShawn Guo .driver = { 48485d6509dSShawn Guo .name = "sdhci-tegra", 485275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 48629495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 48785d6509dSShawn Guo }, 48885d6509dSShawn Guo .probe = sdhci_tegra_probe, 489caebcae9SKevin Hao .remove = sdhci_pltfm_unregister, 49003d2bfc8SOlof Johansson }; 49103d2bfc8SOlof Johansson 492d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 49385d6509dSShawn Guo 49485d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 49585d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 49685d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 497