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;
38 struct tegra_sdhci_platform_data *plat;
42 static irqreturn_t carddetect_irq(int irq, void *data)
44 struct sdhci_host *sdhost = (struct sdhci_host *)data;
46 sdhci_card_detect_callback(sdhost);
50 static void sdhci_status_notify_cb(int card_present, void *dev_id)
52 struct sdhci_host *host = (struct sdhci_host *)dev_id;
53 struct tegra_sdhci_host *tegra_host = sdhci_priv(host);
54 unsigned int status, oldstat;
56 pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
59 if (!tegra_host->plat->mmc_data.status) {
60 mmc_detect_change(host->mmc, 0);
64 status = tegra_host->plat->mmc_data.status(mmc_dev(host->mmc));
66 oldstat = host->card_present;
67 host->card_present = status;
68 if (status ^ oldstat) {
69 pr_debug("%s: Slot status change detected (%d -> %d)\n",
70 mmc_hostname(host->mmc), oldstat, status);
71 if (status && !tegra_host->plat->mmc_data.built_in)
72 mmc_detect_change(host->mmc, (5 * HZ) / 2);
74 mmc_detect_change(host->mmc, 0);
78 static int tegra_sdhci_enable_dma(struct sdhci_host *host)
83 static void tegra_sdhci_enable_clock(struct tegra_sdhci_host *host, int enable)
85 if (enable && !host->clk_enabled) {
86 clk_enable(host->clk);
87 sdhci_writeb(host->sdhci, 1, SDHCI_VENDOR_CLOCK_CNTRL);
88 host->clk_enabled = 1;
89 } else if (!enable && host->clk_enabled) {
90 sdhci_writeb(host->sdhci, 0, SDHCI_VENDOR_CLOCK_CNTRL);
91 clk_disable(host->clk);
92 host->clk_enabled = 0;
96 static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
98 struct tegra_sdhci_host *host = sdhci_priv(sdhci);
99 pr_debug("tegra sdhci clock %s %u enabled=%d\n",
100 mmc_hostname(sdhci->mmc), clock, host->clk_enabled);
102 tegra_sdhci_enable_clock(host, clock);
105 static struct sdhci_ops tegra_sdhci_ops = {
106 .enable_dma = tegra_sdhci_enable_dma,
107 .set_clock = tegra_sdhci_set_clock,
110 static int __devinit tegra_sdhci_probe(struct platform_device *pdev)
113 struct tegra_sdhci_platform_data *plat;
114 struct sdhci_host *sdhci;
115 struct tegra_sdhci_host *host;
116 struct resource *res;
118 void __iomem *ioaddr;
120 plat = pdev->dev.platform_data;
124 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
130 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
134 ioaddr = ioremap(res->start, res->end - res->start);
136 sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host));
142 host = sdhci_priv(sdhci);
146 #ifdef CONFIG_MMC_EMBEDDED_SDIO
147 if (plat->mmc_data.embedded_sdio)
148 mmc_set_embedded_sdio_data(sdhci->mmc,
149 &plat->mmc_data.embedded_sdio->cis,
150 &plat->mmc_data.embedded_sdio->cccr,
151 plat->mmc_data.embedded_sdio->funcs,
152 plat->mmc_data.embedded_sdio->num_funcs);
155 host->clk = clk_get(&pdev->dev, plat->clk_id);
156 if (IS_ERR(host->clk)) {
157 rc = PTR_ERR(host->clk);
161 rc = clk_enable(host->clk);
165 host->clk_enabled = 1;
166 sdhci->hw_name = "tegra";
167 sdhci->ops = &tegra_sdhci_ops;
169 sdhci->ioaddr = ioaddr;
170 sdhci->version = SDHCI_SPEC_200;
171 sdhci->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
172 SDHCI_QUIRK_SINGLE_POWER_WRITE |
173 SDHCI_QUIRK_ENABLE_INTERRUPT_AT_BLOCK_GAP |
174 SDHCI_QUIRK_BROKEN_WRITE_PROTECT |
175 SDHCI_QUIRK_BROKEN_CTRL_HISPD |
176 SDHCI_QUIRK_NO_HISPD_BIT |
177 SDHCI_QUIRK_8_BIT_DATA |
178 SDHCI_QUIRK_NO_VERSION_REG |
179 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
180 SDHCI_QUIRK_NO_SDIO_IRQ;
182 if (plat->force_hs != 0)
183 sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE;
185 sdhci->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
186 if (plat->mmc_data.built_in)
187 sdhci->mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
189 if (plat->rt_disable != 0)
190 sdhci->quirks |= SDHCI_QUIRK_RUNTIME_DISABLE;
192 rc = sdhci_add_host(sdhci);
194 goto err_clk_disable;
196 platform_set_drvdata(pdev, host);
198 if (plat->cd_gpio != -1) {
199 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
200 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
201 mmc_hostname(sdhci->mmc), sdhci);
204 goto err_remove_host;
205 } else if (plat->mmc_data.register_status_notify) {
206 plat->mmc_data.register_status_notify(sdhci_status_notify_cb, sdhci);
209 if (plat->mmc_data.status) {
210 sdhci->card_present = host->plat->mmc_data.status(mmc_dev(sdhci->mmc));
213 if (plat->board_probe)
214 plat->board_probe(pdev->id, sdhci->mmc);
216 printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id,
217 sdhci->irq, sdhci->ioaddr);
222 sdhci_remove_host(sdhci, 1);
224 clk_disable(host->clk);
229 sdhci_free_host(sdhci);
231 iounmap(sdhci->ioaddr);
236 static int tegra_sdhci_remove(struct platform_device *pdev)
238 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
240 struct tegra_sdhci_platform_data *plat;
241 plat = pdev->dev.platform_data;
242 if (plat && plat->board_probe)
243 plat->board_probe(pdev->id, host->sdhci->mmc);
245 sdhci_remove_host(host->sdhci, 0);
246 sdhci_free_host(host->sdhci);
252 static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state)
254 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
255 struct mmc_host *mmc = host->sdhci->mmc;
258 if (host->plat->mmc_data.built_in)
259 mmc->pm_flags |= MMC_PM_KEEP_POWER;
261 ret = sdhci_suspend_host(host->sdhci, state);
263 pr_err("%s: failed, error = %d\n", __func__, ret);
265 tegra_sdhci_enable_clock(host, 0);
269 static int tegra_sdhci_resume(struct platform_device *pdev)
271 struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
274 tegra_sdhci_enable_clock(host, 1);
275 ret = sdhci_resume_host(host->sdhci);
277 pr_err("%s: failed, error = %d\n", __func__, ret);
282 #define tegra_sdhci_suspend NULL
283 #define tegra_sdhci_resume NULL
286 static struct platform_driver tegra_sdhci_driver = {
287 .probe = tegra_sdhci_probe,
288 .remove = tegra_sdhci_remove,
289 .suspend = tegra_sdhci_suspend,
290 .resume = tegra_sdhci_resume,
293 .owner = THIS_MODULE,
297 static int __init tegra_sdhci_init(void)
299 return platform_driver_register(&tegra_sdhci_driver);
302 static void __exit tegra_sdhci_exit(void)
304 platform_driver_unregister(&tegra_sdhci_driver);
307 module_init(tegra_sdhci_init);
308 module_exit(tegra_sdhci_exit);
310 MODULE_DESCRIPTION("Tegra SDHCI controller driver");
311 MODULE_LICENSE("GPL");