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