tegra sdhci: Enable/disable SDCLK source in set_clock host_op
[firefly-linux-kernel-4.4.55.git] / drivers / mmc / host / sdhci-tegra.c
1 /*
2  * drivers/mmc/host/sdhci-tegra.c
3  *
4  * Copyright (C) 2009 Palm, Inc.
5  * Author: Yvonne Yip <y@palm.com>
6  *
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.
10  *
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.
15  *
16  */
17
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>
23 #include <linux/io.h>
24 #include <linux/gpio.h>
25 #include <linux/mmc/card.h>
26
27 #include <mach/sdhci.h>
28
29 #include "sdhci.h"
30
31 #define DRIVER_NAME    "sdhci-tegra"
32
33 #define SDHCI_VENDOR_CLOCK_CNTRL       0x100
34
35 struct tegra_sdhci_host {
36         struct sdhci_host *sdhci;
37         struct clk *clk;
38         int clk_enabled;
39 };
40
41 static irqreturn_t carddetect_irq(int irq, void *data)
42 {
43         struct sdhci_host *sdhost = (struct sdhci_host *)data;
44
45         sdhci_card_detect_callback(sdhost);
46         return IRQ_HANDLED;
47 };
48
49 static int tegra_sdhci_enable_dma(struct sdhci_host *host)
50 {
51         return 0;
52 }
53
54 static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
55 {
56         struct tegra_sdhci_host *host = sdhci_priv(sdhci);
57         pr_info("tegra sdhci clock %s %u\n",
58                 mmc_hostname(sdhci->mmc), clock);
59
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;
68         }
69 }
70
71 static struct sdhci_ops tegra_sdhci_ops = {
72         .enable_dma = tegra_sdhci_enable_dma,
73         .set_clock = tegra_sdhci_set_clock,
74 };
75
76 static int __devinit tegra_sdhci_probe(struct platform_device *pdev)
77 {
78         int rc;
79         struct tegra_sdhci_platform_data *plat;
80         struct sdhci_host *sdhci;
81         struct tegra_sdhci_host *host;
82         struct resource *res;
83         int irq;
84         void __iomem *ioaddr;
85
86         plat = pdev->dev.platform_data;
87         if (plat == NULL)
88                 return -ENXIO;
89
90         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
91         if (res == NULL)
92                 return -ENODEV;
93
94         irq = res->start;
95
96         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
97         if (res == NULL)
98                 return -ENODEV;
99
100         ioaddr = ioremap(res->start, res->end - res->start);
101
102         sdhci = sdhci_alloc_host(&pdev->dev, sizeof(struct tegra_sdhci_host));
103         if (IS_ERR(sdhci)) {
104                 rc = PTR_ERR(sdhci);
105                 goto err_unmap;
106         }
107
108         host = sdhci_priv(sdhci);
109         host->sdhci = sdhci;
110
111         host->clk = clk_get(&pdev->dev, plat->clk_id);
112         if (IS_ERR(host->clk)) {
113                 rc = PTR_ERR(host->clk);
114                 goto err_free_host;
115         }
116
117         rc = clk_enable(host->clk);
118         if (rc != 0)
119                 goto err_clkput;
120
121         host->clk_enabled = 1;
122         sdhci->hw_name = "tegra";
123         sdhci->ops = &tegra_sdhci_ops;
124         sdhci->irq = irq;
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;
137
138         if (plat->force_hs != 0)
139                 sdhci->quirks |= SDHCI_QUIRK_FORCE_HIGH_SPEED_MODE;
140
141         rc = sdhci_add_host(sdhci);
142         if (rc)
143                 goto err_clk_disable;
144
145         platform_set_drvdata(pdev, host);
146
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);
151
152                 if (rc)
153                         goto err_remove_host;
154         }
155
156         if (plat->board_probe)
157                 plat->board_probe(pdev->id, sdhci->mmc);
158
159         printk(KERN_INFO "sdhci%d: initialized irq %d ioaddr %p\n", pdev->id,
160                         sdhci->irq, sdhci->ioaddr);
161
162         return 0;
163
164 err_remove_host:
165         sdhci_remove_host(sdhci, 1);
166 err_clk_disable:
167         clk_disable(host->clk);
168 err_clkput:
169         clk_put(host->clk);
170 err_free_host:
171         if (sdhci)
172                 sdhci_free_host(sdhci);
173 err_unmap:
174         iounmap(sdhci->ioaddr);
175
176         return rc;
177 }
178
179 static int tegra_sdhci_remove(struct platform_device *pdev)
180 {
181         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
182         if (host) {
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);
187
188                 sdhci_remove_host(host->sdhci, 0);
189                 sdhci_free_host(host->sdhci);
190         }
191         return 0;
192 }
193
194 #ifdef CONFIG_PM
195 static int tegra_sdhci_suspend(struct platform_device *pdev, pm_message_t state)
196 {
197         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
198         int ret;
199
200         ret = sdhci_suspend_host(host->sdhci, state);
201         if (ret)
202                 pr_err("%s: failed, error = %d\n", __func__, ret);
203
204         return ret;
205 }
206
207 static int tegra_sdhci_resume(struct platform_device *pdev)
208 {
209         struct tegra_sdhci_host *host = platform_get_drvdata(pdev);
210         int ret;
211
212         ret = sdhci_resume_host(host->sdhci);
213         if (ret)
214                 pr_err("%s: failed, error = %d\n", __func__, ret);
215
216         return ret;
217 }
218 #else
219 #define tegra_sdhci_suspend    NULL
220 #define tegra_sdhci_resume     NULL
221 #endif
222
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,
228         .driver = {
229                 .name = DRIVER_NAME,
230                 .owner = THIS_MODULE,
231         },
232 };
233
234 static int __init tegra_sdhci_init(void)
235 {
236         return platform_driver_register(&tegra_sdhci_driver);
237 }
238
239 static void __exit tegra_sdhci_exit(void)
240 {
241         platform_driver_unregister(&tegra_sdhci_driver);
242 }
243
244 module_init(tegra_sdhci_init);
245 module_exit(tegra_sdhci_exit);
246
247 MODULE_DESCRIPTION("Tegra SDHCI controller driver");
248 MODULE_LICENSE("GPL");