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