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> 23275173b2SGrant Likely #include <linux/of_gpio.h> 2403d2bfc8SOlof Johansson #include <linux/gpio.h> 2503d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2603d2bfc8SOlof Johansson #include <linux/mmc/host.h> 2703d2bfc8SOlof Johansson 28e6b750d4SRussell King #include <asm/gpio.h> 29ea5abbd2SStephen Warren 3003d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 3103d2bfc8SOlof Johansson 32ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 33ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 34ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 35ca5879d3SPavan Kunapuli 363e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 373e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 38ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 393e44a1a7SStephen Warren 403e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 413e44a1a7SStephen Warren struct sdhci_pltfm_data *pdata; 423e44a1a7SStephen Warren u32 nvquirks; 433e44a1a7SStephen Warren }; 443e44a1a7SStephen Warren 453e44a1a7SStephen Warren struct sdhci_tegra { 463e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 470e786102SStephen Warren int cd_gpio; 480e786102SStephen Warren int wp_gpio; 490e786102SStephen Warren int power_gpio; 500e786102SStephen Warren int is_8bit; 513e44a1a7SStephen Warren }; 523e44a1a7SStephen Warren 5303d2bfc8SOlof Johansson static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) 5403d2bfc8SOlof Johansson { 5503d2bfc8SOlof Johansson u32 val; 5603d2bfc8SOlof Johansson 5703d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_PRESENT_STATE)) { 5803d2bfc8SOlof Johansson /* Use wp_gpio here instead? */ 5903d2bfc8SOlof Johansson val = readl(host->ioaddr + reg); 6003d2bfc8SOlof Johansson return val | SDHCI_WRITE_PROTECT; 6103d2bfc8SOlof Johansson } 6203d2bfc8SOlof Johansson 6303d2bfc8SOlof Johansson return readl(host->ioaddr + reg); 6403d2bfc8SOlof Johansson } 6503d2bfc8SOlof Johansson 6603d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 6703d2bfc8SOlof Johansson { 683e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 693e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 703e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 713e44a1a7SStephen Warren 723e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 733e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 7403d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 7503d2bfc8SOlof Johansson return SDHCI_SPEC_200; 7603d2bfc8SOlof Johansson } 7703d2bfc8SOlof Johansson 7803d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 7903d2bfc8SOlof Johansson } 8003d2bfc8SOlof Johansson 8103d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 8203d2bfc8SOlof Johansson { 833e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 843e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 853e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 863e44a1a7SStephen Warren 8703d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 8803d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 8903d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 9003d2bfc8SOlof Johansson */ 9103d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 9203d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 9303d2bfc8SOlof Johansson 9403d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 9503d2bfc8SOlof Johansson 963e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 973e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 9803d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 9903d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 10003d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 10103d2bfc8SOlof Johansson gap_ctrl |= 0x8; 10203d2bfc8SOlof Johansson else 10303d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 10403d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 10503d2bfc8SOlof Johansson } 10603d2bfc8SOlof Johansson } 10703d2bfc8SOlof Johansson 1083e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 10903d2bfc8SOlof Johansson { 1103e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1113e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 11203d2bfc8SOlof Johansson 1130e786102SStephen Warren if (!gpio_is_valid(tegra_host->wp_gpio)) 11403d2bfc8SOlof Johansson return -1; 11503d2bfc8SOlof Johansson 1160e786102SStephen Warren return gpio_get_value(tegra_host->wp_gpio); 11703d2bfc8SOlof Johansson } 11803d2bfc8SOlof Johansson 11903d2bfc8SOlof Johansson static irqreturn_t carddetect_irq(int irq, void *data) 12003d2bfc8SOlof Johansson { 12103d2bfc8SOlof Johansson struct sdhci_host *sdhost = (struct sdhci_host *)data; 12203d2bfc8SOlof Johansson 12303d2bfc8SOlof Johansson tasklet_schedule(&sdhost->card_tasklet); 12403d2bfc8SOlof Johansson return IRQ_HANDLED; 12503d2bfc8SOlof Johansson }; 12603d2bfc8SOlof Johansson 127ca5879d3SPavan Kunapuli static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) 128ca5879d3SPavan Kunapuli { 129ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 130ca5879d3SPavan Kunapuli struct sdhci_tegra *tegra_host = pltfm_host->priv; 131ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 132ca5879d3SPavan Kunapuli 133ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 134ca5879d3SPavan Kunapuli return; 135ca5879d3SPavan Kunapuli 136ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 137ca5879d3SPavan Kunapuli if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) { 138ca5879d3SPavan Kunapuli u32 misc_ctrl; 139ca5879d3SPavan Kunapuli 140ca5879d3SPavan Kunapuli misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 141ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 142ca5879d3SPavan Kunapuli sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 143ca5879d3SPavan Kunapuli } 144ca5879d3SPavan Kunapuli } 145ca5879d3SPavan Kunapuli 1467bc088d3SSascha Hauer static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) 14703d2bfc8SOlof Johansson { 148275173b2SGrant Likely struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1493e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 15003d2bfc8SOlof Johansson u32 ctrl; 15103d2bfc8SOlof Johansson 15203d2bfc8SOlof Johansson ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 1530e786102SStephen Warren if (tegra_host->is_8bit && bus_width == MMC_BUS_WIDTH_8) { 15403d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 15503d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_8BITBUS; 15603d2bfc8SOlof Johansson } else { 15703d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_8BITBUS; 15803d2bfc8SOlof Johansson if (bus_width == MMC_BUS_WIDTH_4) 15903d2bfc8SOlof Johansson ctrl |= SDHCI_CTRL_4BITBUS; 16003d2bfc8SOlof Johansson else 16103d2bfc8SOlof Johansson ctrl &= ~SDHCI_CTRL_4BITBUS; 16203d2bfc8SOlof Johansson } 16303d2bfc8SOlof Johansson sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 16403d2bfc8SOlof Johansson return 0; 16503d2bfc8SOlof Johansson } 16603d2bfc8SOlof Johansson 16785d6509dSShawn Guo static struct sdhci_ops tegra_sdhci_ops = { 16885d6509dSShawn Guo .get_ro = tegra_sdhci_get_ro, 16985d6509dSShawn Guo .read_l = tegra_sdhci_readl, 17085d6509dSShawn Guo .read_w = tegra_sdhci_readw, 17185d6509dSShawn Guo .write_l = tegra_sdhci_writel, 1727bc088d3SSascha Hauer .platform_bus_width = tegra_sdhci_buswidth, 173ca5879d3SPavan Kunapuli .platform_reset_exit = tegra_sdhci_reset_exit, 17485d6509dSShawn Guo }; 17503d2bfc8SOlof Johansson 1763e44a1a7SStephen Warren static struct sdhci_pltfm_data sdhci_tegra20_pdata = { 17785d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 17885d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 17985d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 18085d6509dSShawn Guo SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 18185d6509dSShawn Guo .ops = &tegra_sdhci_ops, 18285d6509dSShawn Guo }; 18385d6509dSShawn Guo 1843e44a1a7SStephen Warren static struct sdhci_tegra_soc_data soc_data_tegra20 = { 1853e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 1863e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 1873e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 1883e44a1a7SStephen Warren }; 1893e44a1a7SStephen Warren 1903e44a1a7SStephen Warren static struct sdhci_pltfm_data sdhci_tegra30_pdata = { 1913e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 1923e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 1933e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 1943e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 1953e44a1a7SStephen Warren SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 1963e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 1973e44a1a7SStephen Warren }; 1983e44a1a7SStephen Warren 1993e44a1a7SStephen Warren static struct sdhci_tegra_soc_data soc_data_tegra30 = { 2003e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 201ca5879d3SPavan Kunapuli .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300, 2023e44a1a7SStephen Warren }; 2033e44a1a7SStephen Warren 2045ebf2552SRhyland Klein static struct sdhci_pltfm_data sdhci_tegra114_pdata = { 2055ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 2065ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 2075ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 2085ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 2095ebf2552SRhyland Klein SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 2105ebf2552SRhyland Klein .ops = &tegra_sdhci_ops, 2115ebf2552SRhyland Klein }; 2125ebf2552SRhyland Klein 2135ebf2552SRhyland Klein static struct sdhci_tegra_soc_data soc_data_tegra114 = { 2145ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 2155ebf2552SRhyland Klein }; 2165ebf2552SRhyland Klein 217498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 2185ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 2193e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 2203e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 221275173b2SGrant Likely {} 222275173b2SGrant Likely }; 223275173b2SGrant Likely MODULE_DEVICE_TABLE(of, sdhci_dt_ids); 224275173b2SGrant Likely 2250e786102SStephen Warren static void sdhci_tegra_parse_dt(struct device *dev, 2260e786102SStephen Warren struct sdhci_tegra *tegra_host) 227275173b2SGrant Likely { 2280e786102SStephen Warren struct device_node *np = dev->of_node; 229c11bd557SStephen Warren u32 bus_width; 230275173b2SGrant Likely 2310e786102SStephen Warren tegra_host->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); 2320e786102SStephen Warren tegra_host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); 2330e786102SStephen Warren tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0); 234c11bd557SStephen Warren 235c11bd557SStephen Warren if (of_property_read_u32(np, "bus-width", &bus_width) == 0 && 236c11bd557SStephen Warren bus_width == 8) 2370e786102SStephen Warren tegra_host->is_8bit = 1; 238275173b2SGrant Likely } 239275173b2SGrant Likely 240c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 24103d2bfc8SOlof Johansson { 2423e44a1a7SStephen Warren const struct of_device_id *match; 2433e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 2443e44a1a7SStephen Warren struct sdhci_host *host; 24585d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 2463e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 24703d2bfc8SOlof Johansson struct clk *clk; 24803d2bfc8SOlof Johansson int rc; 24903d2bfc8SOlof Johansson 2503e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 251b37f9d98SJoseph Lo if (!match) 252b37f9d98SJoseph Lo return -EINVAL; 2533e44a1a7SStephen Warren soc_data = match->data; 2543e44a1a7SStephen Warren 2553e44a1a7SStephen Warren host = sdhci_pltfm_init(pdev, soc_data->pdata); 25685d6509dSShawn Guo if (IS_ERR(host)) 25785d6509dSShawn Guo return PTR_ERR(host); 25885d6509dSShawn Guo pltfm_host = sdhci_priv(host); 25985d6509dSShawn Guo 2603e44a1a7SStephen Warren tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); 2613e44a1a7SStephen Warren if (!tegra_host) { 2623e44a1a7SStephen Warren dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); 2633e44a1a7SStephen Warren rc = -ENOMEM; 2640e786102SStephen Warren goto err_alloc_tegra_host; 2653e44a1a7SStephen Warren } 2663e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 2673e44a1a7SStephen Warren pltfm_host->priv = tegra_host; 268275173b2SGrant Likely 2690e786102SStephen Warren sdhci_tegra_parse_dt(&pdev->dev, tegra_host); 2700e786102SStephen Warren 2710e786102SStephen Warren if (gpio_is_valid(tegra_host->power_gpio)) { 2720e786102SStephen Warren rc = gpio_request(tegra_host->power_gpio, "sdhci_power"); 27303d2bfc8SOlof Johansson if (rc) { 27403d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), 27503d2bfc8SOlof Johansson "failed to allocate power gpio\n"); 27685d6509dSShawn Guo goto err_power_req; 27703d2bfc8SOlof Johansson } 2780e786102SStephen Warren gpio_direction_output(tegra_host->power_gpio, 1); 27903d2bfc8SOlof Johansson } 28003d2bfc8SOlof Johansson 2810e786102SStephen Warren if (gpio_is_valid(tegra_host->cd_gpio)) { 2820e786102SStephen Warren rc = gpio_request(tegra_host->cd_gpio, "sdhci_cd"); 28303d2bfc8SOlof Johansson if (rc) { 28403d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), 28503d2bfc8SOlof Johansson "failed to allocate cd gpio\n"); 28685d6509dSShawn Guo goto err_cd_req; 28703d2bfc8SOlof Johansson } 2880e786102SStephen Warren gpio_direction_input(tegra_host->cd_gpio); 28903d2bfc8SOlof Johansson 2900e786102SStephen Warren rc = request_irq(gpio_to_irq(tegra_host->cd_gpio), 2910e786102SStephen Warren carddetect_irq, 29203d2bfc8SOlof Johansson IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 29303d2bfc8SOlof Johansson mmc_hostname(host->mmc), host); 29403d2bfc8SOlof Johansson 29503d2bfc8SOlof Johansson if (rc) { 29603d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "request irq error\n"); 29785d6509dSShawn Guo goto err_cd_irq_req; 29803d2bfc8SOlof Johansson } 29903d2bfc8SOlof Johansson 30003d2bfc8SOlof Johansson } 30103d2bfc8SOlof Johansson 3020e786102SStephen Warren if (gpio_is_valid(tegra_host->wp_gpio)) { 3030e786102SStephen Warren rc = gpio_request(tegra_host->wp_gpio, "sdhci_wp"); 30403d2bfc8SOlof Johansson if (rc) { 30503d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), 30603d2bfc8SOlof Johansson "failed to allocate wp gpio\n"); 30785d6509dSShawn Guo goto err_wp_req; 30803d2bfc8SOlof Johansson } 3090e786102SStephen Warren gpio_direction_input(tegra_host->wp_gpio); 31003d2bfc8SOlof Johansson } 31103d2bfc8SOlof Johansson 31203d2bfc8SOlof Johansson clk = clk_get(mmc_dev(host->mmc), NULL); 31303d2bfc8SOlof Johansson if (IS_ERR(clk)) { 31403d2bfc8SOlof Johansson dev_err(mmc_dev(host->mmc), "clk err\n"); 31503d2bfc8SOlof Johansson rc = PTR_ERR(clk); 31685d6509dSShawn Guo goto err_clk_get; 31703d2bfc8SOlof Johansson } 3181e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 31903d2bfc8SOlof Johansson pltfm_host->clk = clk; 32003d2bfc8SOlof Johansson 3210e786102SStephen Warren if (tegra_host->is_8bit) 32203d2bfc8SOlof Johansson host->mmc->caps |= MMC_CAP_8_BIT_DATA; 32303d2bfc8SOlof Johansson 32485d6509dSShawn Guo rc = sdhci_add_host(host); 32585d6509dSShawn Guo if (rc) 32685d6509dSShawn Guo goto err_add_host; 32785d6509dSShawn Guo 32803d2bfc8SOlof Johansson return 0; 32903d2bfc8SOlof Johansson 33085d6509dSShawn Guo err_add_host: 3311e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 33285d6509dSShawn Guo clk_put(pltfm_host->clk); 33385d6509dSShawn Guo err_clk_get: 3340e786102SStephen Warren if (gpio_is_valid(tegra_host->wp_gpio)) 3350e786102SStephen Warren gpio_free(tegra_host->wp_gpio); 33685d6509dSShawn Guo err_wp_req: 3370e786102SStephen Warren if (gpio_is_valid(tegra_host->cd_gpio)) 3380e786102SStephen Warren free_irq(gpio_to_irq(tegra_host->cd_gpio), host); 33985d6509dSShawn Guo err_cd_irq_req: 3400e786102SStephen Warren if (gpio_is_valid(tegra_host->cd_gpio)) 3410e786102SStephen Warren gpio_free(tegra_host->cd_gpio); 34285d6509dSShawn Guo err_cd_req: 3430e786102SStephen Warren if (gpio_is_valid(tegra_host->power_gpio)) 3440e786102SStephen Warren gpio_free(tegra_host->power_gpio); 34585d6509dSShawn Guo err_power_req: 3460e786102SStephen Warren err_alloc_tegra_host: 34785d6509dSShawn Guo sdhci_pltfm_free(pdev); 34803d2bfc8SOlof Johansson return rc; 34903d2bfc8SOlof Johansson } 35003d2bfc8SOlof Johansson 3516e0ee714SBill Pemberton static int sdhci_tegra_remove(struct platform_device *pdev) 35203d2bfc8SOlof Johansson { 35385d6509dSShawn Guo struct sdhci_host *host = platform_get_drvdata(pdev); 35403d2bfc8SOlof Johansson struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3553e44a1a7SStephen Warren struct sdhci_tegra *tegra_host = pltfm_host->priv; 35685d6509dSShawn Guo int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); 35785d6509dSShawn Guo 35885d6509dSShawn Guo sdhci_remove_host(host, dead); 35903d2bfc8SOlof Johansson 3600e786102SStephen Warren if (gpio_is_valid(tegra_host->wp_gpio)) 3610e786102SStephen Warren gpio_free(tegra_host->wp_gpio); 36203d2bfc8SOlof Johansson 3630e786102SStephen Warren if (gpio_is_valid(tegra_host->cd_gpio)) { 3640e786102SStephen Warren free_irq(gpio_to_irq(tegra_host->cd_gpio), host); 3650e786102SStephen Warren gpio_free(tegra_host->cd_gpio); 36603d2bfc8SOlof Johansson } 36703d2bfc8SOlof Johansson 3680e786102SStephen Warren if (gpio_is_valid(tegra_host->power_gpio)) 3690e786102SStephen Warren gpio_free(tegra_host->power_gpio); 37003d2bfc8SOlof Johansson 3711e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 37203d2bfc8SOlof Johansson clk_put(pltfm_host->clk); 37385d6509dSShawn Guo 37485d6509dSShawn Guo sdhci_pltfm_free(pdev); 37585d6509dSShawn Guo 37685d6509dSShawn Guo return 0; 37703d2bfc8SOlof Johansson } 37803d2bfc8SOlof Johansson 37985d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 38085d6509dSShawn Guo .driver = { 38185d6509dSShawn Guo .name = "sdhci-tegra", 38285d6509dSShawn Guo .owner = THIS_MODULE, 383275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 38429495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 38585d6509dSShawn Guo }, 38685d6509dSShawn Guo .probe = sdhci_tegra_probe, 3870433c143SBill Pemberton .remove = sdhci_tegra_remove, 38803d2bfc8SOlof Johansson }; 38903d2bfc8SOlof Johansson 390d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 39185d6509dSShawn Guo 39285d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 39385d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 39485d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 395