2 * drivers/mmc/host/sdhci-tegra.c
4 * Copyright (C) 2009 Palm, Inc.
5 * Author: Yvonne Yip <y@palm.com>
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 #include <linux/err.h>
19 #include <linux/init.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/clk.h>
24 #include <linux/gpio.h>
25 #include <linux/mmc/card.h>
27 #include <mach/sdhci.h>
31 #define DRIVER_NAME "sdhci-tegra"
33 #define SDHCI_VENDOR_CLOCK_CNTRL 0x100
35 struct tegra_sdhci_host {
36 struct sdhci_host *sdhci;
41 static irqreturn_t carddetect_irq(int irq, void *data)
43 struct sdhci_host *sdhost = (struct sdhci_host *)data;
45 sdhci_card_detect_callback(sdhost);
49 static int tegra_sdhci_enable_dma(struct sdhci_host *host)
54 static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
56 struct tegra_sdhci_host *host = sdhci_priv(sdhci);
57 pr_info("tegra sdhci clock %s %u\n",
58 mmc_hostname(sdhci->mmc), clock);
60 if (clock && !host->clk_enabled) {
61 clk_enable(host->clk);
62 sdhci_writeb(sdhci, 1, SDHCI_VENDOR_CLOCK_CNTRL);
63 host->clk_enabled = 1;
64 } else if (!clock && host->clk_enabled) {
65 sdhci_writeb(sdhci, 0, SDHCI_VENDOR_CLOCK_CNTRL);
66 clk_disable(host->clk);
67 host->clk_enabled = 0;
71 static struct sdhci_ops tegra_sdhci_ops = {
72 .enable_dma = tegra_sdhci_enable_dma,
73 .set_clock = tegra_sdhci_set_clock,
76 static int __devinit tegra_sdhci_probe(struct platform_device *pdev)
79 struct tegra_sdhci_platform_data *plat;
80 struct sdhci_host *sdhci;
81 struct tegra_sdhci_host *host;
86 plat = pdev->dev.platform_data;
90 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
96 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
100 ioaddr = ioremap(res->start, res->end - res->start);
102 sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host));
108 host = sdhci_priv(sdhci);
111 host->clk = clk_get(&pdev->dev, plat->clk_id);
112 if (IS_ERR(host->clk)) {
113 rc = PTR_ERR(host->clk);
117 rc = clk_enable(host->clk);
121 host->clk_enabled = 1;
122 sdhci->hw_name = "tegra";
123 sdhci->ops = &tegra_sdhci_ops;
125 sdhci->ioaddr = ioaddr;
126 sdhci->version = SDHCI_SPEC_200;
127 sdhci->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
128 SDHCI_QUIRK_SINGLE_POWER_WRITE |
129 SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP |
130 SDHCI_QUIRK_BROKEN_WRITE_PROTECT |
131 SDHCI_QUIRK_BROKEN_CTRL_HISPD |
132 SDHCI_QUIRK_NO_HISPD_BIT |
133 SDHCI_QUIRK_8_BIT_DATA |
134 SDHCI_QUIRK_NO_VERSION_REG |
135 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
136 SDHCI_QUIRK_NO_SDIO_IRQ;
138 if (plat->force_hs != 0)
139 sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE;
141 rc = sdhci_add_host(sdhci);
143 goto err_clk_disable;
145 platform_set_drvdata(pdev, host);
147 if (plat->cd_gpio != -1) {
148 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
149 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
150 mmc_hostname(sdhci->mmc), sdhci);
153 goto err_remove_host;
156 if (plat->board_probe)
157 plat->board_probe(pdev->id, sdhci->mmc);
159 printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id,
160 sdhci->irq, sdhci->ioaddr);
165 sdhci_remove_host(sdhci, 1);
167 clk_disable(host->clk);
172 sdhci_free_host(sdhci);
174 iounmap(sdhci->ioaddr);
179 static int tegra_sdhci_remove(struct platform_device *pdev)
181 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
183 struct tegra_sdhci_platform_data *plat;
184 plat = pdev->dev.platform_data;
185 if (plat && plat->board_probe)
186 plat->board_probe(pdev->id, host->sdhci->mmc);
188 sdhci_remove_host(host->sdhci, 0);
189 sdhci_free_host(host->sdhci);
195 static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state)
197 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
200 ret = sdhci_suspend_host(host->sdhci, state);
202 pr_err("%s: failed, error = %d\n", __func__, ret);
207 static int tegra_sdhci_resume(struct platform_device *pdev)
209 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
212 ret = sdhci_resume_host(host->sdhci);
214 pr_err("%s: failed, error = %d\n", __func__, ret);
219 #define tegra_sdhci_suspend NULL
220 #define tegra_sdhci_resume NULL
223 static struct platform_driver tegra_sdhci_driver = {
224 .probe = tegra_sdhci_probe,
225 .remove = tegra_sdhci_remove,
226 .suspend = tegra_sdhci_suspend,
227 .resume = tegra_sdhci_resume,
230 .owner = THIS_MODULE,
234 static int __init tegra_sdhci_init(void)
236 return platform_driver_register(&tegra_sdhci_driver);
239 static void __exit tegra_sdhci_exit(void)
241 platform_driver_unregister(&tegra_sdhci_driver);
244 module_init(tegra_sdhci_init);
245 module_exit(tegra_sdhci_exit);
247 MODULE_DESCRIPTION("Tegra SDHCI controller driver");
248 MODULE_LICENSE("GPL");