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