1 /* 2 * Copyright (C) 2010 Google, Inc. 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15 #include <linux/err.h> 16 #include <linux/init.h> 17 #include <linux/platform_device.h> 18 #include <linux/clk.h> 19 #include <linux/io.h> 20 #include <linux/gpio.h> 21 #include <linux/mmc/card.h> 22 #include <linux/mmc/host.h> 23 24 #include <mach/gpio.h> 25 #include <mach/sdhci.h> 26 27 #include "sdhci.h" 28 #include "sdhci-pltfm.h" 29 30 static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) 31 { 32 u32 val; 33 34 if (unlikely(reg == SDHCI_PRESENT_STATE)) { 35 /* Use wp_gpio here instead? */ 36 val = readl(host->ioaddr + reg); 37 return val | SDHCI_WRITE_PROTECT; 38 } 39 40 return readl(host->ioaddr + reg); 41 } 42 43 static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 44 { 45 if (unlikely(reg == SDHCI_HOST_VERSION)) { 46 /* Erratum: Version register is invalid in HW. */ 47 return SDHCI_SPEC_200; 48 } 49 50 return readw(host->ioaddr + reg); 51 } 52 53 static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 54 { 55 /* Seems like we're getting spurious timeout and crc errors, so 56 * disable signalling of them. In case of real errors software 57 * timers should take care of eventually detecting them. 58 */ 59 if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 60 val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 61 62 writel(val, host->ioaddr + reg); 63 64 if (unlikely(reg == SDHCI_INT_ENABLE)) { 65 /* Erratum: Must enable block gap interrupt detection */ 66 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 67 if (val & SDHCI_INT_CARD_INT) 68 gap_ctrl |= 0x8; 69 else 70 gap_ctrl &= ~0x8; 71 writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 72 } 73 } 74 75 static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) 76 { 77 struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc)); 78 struct tegra_sdhci_platform_data *plat; 79 80 plat = pdev->dev.platform_data; 81 82 if (!gpio_is_valid(plat->wp_gpio)) 83 return -1; 84 85 return gpio_get_value(plat->wp_gpio); 86 } 87 88 static irqreturn_t carddetect_irq(int irq, void *data) 89 { 90 struct sdhci_host *sdhost = (struct sdhci_host *)data; 91 92 tasklet_schedule(&sdhost->card_tasklet); 93 return IRQ_HANDLED; 94 }; 95 96 static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) 97 { 98 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); 99 struct tegra_sdhci_platform_data *plat; 100 u32 ctrl; 101 102 plat = pdev->dev.platform_data; 103 104 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 105 if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) { 106 ctrl &= ~SDHCI_CTRL_4BITBUS; 107 ctrl |= SDHCI_CTRL_8BITBUS; 108 } else { 109 ctrl &= ~SDHCI_CTRL_8BITBUS; 110 if (bus_width == MMC_BUS_WIDTH_4) 111 ctrl |= SDHCI_CTRL_4BITBUS; 112 else 113 ctrl &= ~SDHCI_CTRL_4BITBUS; 114 } 115 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 116 return 0; 117 } 118 119 static struct sdhci_ops tegra_sdhci_ops = { 120 .get_ro = tegra_sdhci_get_ro, 121 .read_l = tegra_sdhci_readl, 122 .read_w = tegra_sdhci_readw, 123 .write_l = tegra_sdhci_writel, 124 .platform_8bit_width = tegra_sdhci_8bit, 125 }; 126 127 static struct sdhci_pltfm_data sdhci_tegra_pdata = { 128 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 129 SDHCI_QUIRK_SINGLE_POWER_WRITE | 130 SDHCI_QUIRK_NO_HISPD_BIT | 131 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 132 .ops = &tegra_sdhci_ops, 133 }; 134 135 static int __devinit sdhci_tegra_probe(struct platform_device *pdev) 136 { 137 struct sdhci_pltfm_host *pltfm_host; 138 struct tegra_sdhci_platform_data *plat; 139 struct sdhci_host *host; 140 struct clk *clk; 141 int rc; 142 143 host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); 144 if (IS_ERR(host)) 145 return PTR_ERR(host); 146 147 pltfm_host = sdhci_priv(host); 148 149 plat = pdev->dev.platform_data; 150 151 if (plat == NULL) { 152 dev_err(mmc_dev(host->mmc), "missing platform data\n"); 153 rc = -ENXIO; 154 goto err_no_plat; 155 } 156 157 if (gpio_is_valid(plat->power_gpio)) { 158 rc = gpio_request(plat->power_gpio, "sdhci_power"); 159 if (rc) { 160 dev_err(mmc_dev(host->mmc), 161 "failed to allocate power gpio\n"); 162 goto err_power_req; 163 } 164 tegra_gpio_enable(plat->power_gpio); 165 gpio_direction_output(plat->power_gpio, 1); 166 } 167 168 if (gpio_is_valid(plat->cd_gpio)) { 169 rc = gpio_request(plat->cd_gpio, "sdhci_cd"); 170 if (rc) { 171 dev_err(mmc_dev(host->mmc), 172 "failed to allocate cd gpio\n"); 173 goto err_cd_req; 174 } 175 tegra_gpio_enable(plat->cd_gpio); 176 gpio_direction_input(plat->cd_gpio); 177 178 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, 179 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 180 mmc_hostname(host->mmc), host); 181 182 if (rc) { 183 dev_err(mmc_dev(host->mmc), "request irq error\n"); 184 goto err_cd_irq_req; 185 } 186 187 } 188 189 if (gpio_is_valid(plat->wp_gpio)) { 190 rc = gpio_request(plat->wp_gpio, "sdhci_wp"); 191 if (rc) { 192 dev_err(mmc_dev(host->mmc), 193 "failed to allocate wp gpio\n"); 194 goto err_wp_req; 195 } 196 tegra_gpio_enable(plat->wp_gpio); 197 gpio_direction_input(plat->wp_gpio); 198 } 199 200 clk = clk_get(mmc_dev(host->mmc), NULL); 201 if (IS_ERR(clk)) { 202 dev_err(mmc_dev(host->mmc), "clk err\n"); 203 rc = PTR_ERR(clk); 204 goto err_clk_get; 205 } 206 clk_enable(clk); 207 pltfm_host->clk = clk; 208 209 host->mmc->pm_caps = plat->pm_flags; 210 211 if (plat->is_8bit) 212 host->mmc->caps |= MMC_CAP_8_BIT_DATA; 213 214 rc = sdhci_add_host(host); 215 if (rc) 216 goto err_add_host; 217 218 return 0; 219 220 err_add_host: 221 clk_disable(pltfm_host->clk); 222 clk_put(pltfm_host->clk); 223 err_clk_get: 224 if (gpio_is_valid(plat->wp_gpio)) { 225 tegra_gpio_disable(plat->wp_gpio); 226 gpio_free(plat->wp_gpio); 227 } 228 err_wp_req: 229 if (gpio_is_valid(plat->cd_gpio)) 230 free_irq(gpio_to_irq(plat->cd_gpio), host); 231 err_cd_irq_req: 232 if (gpio_is_valid(plat->cd_gpio)) { 233 tegra_gpio_disable(plat->cd_gpio); 234 gpio_free(plat->cd_gpio); 235 } 236 err_cd_req: 237 if (gpio_is_valid(plat->power_gpio)) { 238 tegra_gpio_disable(plat->power_gpio); 239 gpio_free(plat->power_gpio); 240 } 241 err_power_req: 242 err_no_plat: 243 sdhci_pltfm_free(pdev); 244 return rc; 245 } 246 247 static int __devexit sdhci_tegra_remove(struct platform_device *pdev) 248 { 249 struct sdhci_host *host = platform_get_drvdata(pdev); 250 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 251 struct tegra_sdhci_platform_data *plat; 252 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); 253 254 sdhci_remove_host(host, dead); 255 256 plat = pdev->dev.platform_data; 257 258 if (gpio_is_valid(plat->wp_gpio)) { 259 tegra_gpio_disable(plat->wp_gpio); 260 gpio_free(plat->wp_gpio); 261 } 262 263 if (gpio_is_valid(plat->cd_gpio)) { 264 free_irq(gpio_to_irq(plat->cd_gpio), host); 265 tegra_gpio_disable(plat->cd_gpio); 266 gpio_free(plat->cd_gpio); 267 } 268 269 if (gpio_is_valid(plat->power_gpio)) { 270 tegra_gpio_disable(plat->power_gpio); 271 gpio_free(plat->power_gpio); 272 } 273 274 clk_disable(pltfm_host->clk); 275 clk_put(pltfm_host->clk); 276 277 sdhci_pltfm_free(pdev); 278 279 return 0; 280 } 281 282 static struct platform_driver sdhci_tegra_driver = { 283 .driver = { 284 .name = "sdhci-tegra", 285 .owner = THIS_MODULE, 286 }, 287 .probe = sdhci_tegra_probe, 288 .remove = __devexit_p(sdhci_tegra_remove), 289 #ifdef CONFIG_PM 290 .suspend = sdhci_pltfm_suspend, 291 .resume = sdhci_pltfm_resume, 292 #endif 293 }; 294 295 static int __init sdhci_tegra_init(void) 296 { 297 return platform_driver_register(&sdhci_tegra_driver); 298 } 299 module_init(sdhci_tegra_init); 300 301 static void __exit sdhci_tegra_exit(void) 302 { 303 platform_driver_unregister(&sdhci_tegra_driver); 304 } 305 module_exit(sdhci_tegra_exit); 306 307 MODULE_DESCRIPTION("SDHCI driver for Tegra"); 308 MODULE_AUTHOR(" Google, Inc."); 309 MODULE_LICENSE("GPL v2"); 310