tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / mmc / host / sdhci-sirf.c
1 /*
2  * SDHCI support for SiRF primaII and marco SoCs
3  *
4  * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
5  *
6  * Licensed under GPLv2 or later.
7  */
8
9 #include <linux/delay.h>
10 #include <linux/device.h>
11 #include <linux/mmc/host.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_gpio.h>
15 #include <linux/mmc/slot-gpio.h>
16 #include <linux/pinctrl/consumer.h>
17 #include "sdhci-pltfm.h"
18
19 struct sdhci_sirf_priv {
20         struct clk *clk;
21         int gpio_cd;
22 };
23
24 static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
25 {
26         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
27         struct sdhci_sirf_priv *priv = pltfm_host->priv;
28         return clk_get_rate(priv->clk);
29 }
30
31 static struct sdhci_ops sdhci_sirf_ops = {
32         .get_max_clock  = sdhci_sirf_get_max_clk,
33 };
34
35 static struct sdhci_pltfm_data sdhci_sirf_pdata = {
36         .ops = &sdhci_sirf_ops,
37         .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
38                 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
39                 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
40                 SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
41                 SDHCI_QUIRK_DELAY_AFTER_POWER,
42 };
43
44 static int sdhci_sirf_probe(struct platform_device *pdev)
45 {
46         struct sdhci_host *host;
47         struct sdhci_pltfm_host *pltfm_host;
48         struct sdhci_sirf_priv *priv;
49         struct pinctrl *pinctrl;
50         int ret;
51
52         pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
53         if (IS_ERR(pinctrl)) {
54                 dev_err(&pdev->dev, "unable to get pinmux");
55                 return PTR_ERR(pinctrl);
56         }
57
58         priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_sirf_priv),
59                 GFP_KERNEL);
60         if (!priv) {
61                 dev_err(&pdev->dev, "unable to allocate private data");
62                 return -ENOMEM;
63         }
64
65         priv->clk = devm_clk_get(&pdev->dev, NULL);
66         if (IS_ERR(priv->clk)) {
67                 dev_err(&pdev->dev, "unable to get clock");
68                 return PTR_ERR(priv->clk);
69         }
70
71         if (pdev->dev.of_node) {
72                 priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
73                         "cd-gpios", 0);
74         } else {
75                 priv->gpio_cd = -EINVAL;
76         }
77
78         host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata);
79         if (IS_ERR(host)) {
80                 ret = PTR_ERR(host);
81                 goto err_sdhci_pltfm_init;
82         }
83
84         pltfm_host = sdhci_priv(host);
85         pltfm_host->priv = priv;
86
87         sdhci_get_of_property(pdev);
88
89         clk_prepare_enable(priv->clk);
90
91         ret = sdhci_add_host(host);
92         if (ret)
93                 goto err_sdhci_add;
94
95         /*
96          * We must request the IRQ after sdhci_add_host(), as the tasklet only
97          * gets setup in sdhci_add_host() and we oops.
98          */
99         if (gpio_is_valid(priv->gpio_cd)) {
100                 ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd);
101                 if (ret) {
102                         dev_err(&pdev->dev, "card detect irq request failed: %d\n",
103                                 ret);
104                         goto err_request_cd;
105                 }
106         }
107
108         return 0;
109
110 err_request_cd:
111         sdhci_remove_host(host, 0);
112 err_sdhci_add:
113         clk_disable_unprepare(priv->clk);
114         sdhci_pltfm_free(pdev);
115 err_sdhci_pltfm_init:
116         return ret;
117 }
118
119 static int sdhci_sirf_remove(struct platform_device *pdev)
120 {
121         struct sdhci_host *host = platform_get_drvdata(pdev);
122         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
123         struct sdhci_sirf_priv *priv = pltfm_host->priv;
124
125         sdhci_pltfm_unregister(pdev);
126
127         if (gpio_is_valid(priv->gpio_cd))
128                 mmc_gpio_free_cd(host->mmc);
129
130         clk_disable_unprepare(priv->clk);
131         return 0;
132 }
133
134 #ifdef CONFIG_PM_SLEEP
135 static int sdhci_sirf_suspend(struct device *dev)
136 {
137         struct sdhci_host *host = dev_get_drvdata(dev);
138         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
139         struct sdhci_sirf_priv *priv = pltfm_host->priv;
140         int ret;
141
142         ret = sdhci_suspend_host(host);
143         if (ret)
144                 return ret;
145
146         clk_disable(priv->clk);
147
148         return 0;
149 }
150
151 static int sdhci_sirf_resume(struct device *dev)
152 {
153         struct sdhci_host *host = dev_get_drvdata(dev);
154         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
155         struct sdhci_sirf_priv *priv = pltfm_host->priv;
156         int ret;
157
158         ret = clk_enable(priv->clk);
159         if (ret) {
160                 dev_dbg(dev, "Resume: Error enabling clock\n");
161                 return ret;
162         }
163
164         return sdhci_resume_host(host);
165 }
166
167 static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
168 #endif
169
170 static const struct of_device_id sdhci_sirf_of_match[] = {
171         { .compatible = "sirf,prima2-sdhc" },
172         { }
173 };
174 MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
175
176 static struct platform_driver sdhci_sirf_driver = {
177         .driver         = {
178                 .name   = "sdhci-sirf",
179                 .owner  = THIS_MODULE,
180                 .of_match_table = sdhci_sirf_of_match,
181 #ifdef CONFIG_PM_SLEEP
182                 .pm     = &sdhci_sirf_pm_ops,
183 #endif
184         },
185         .probe          = sdhci_sirf_probe,
186         .remove         = sdhci_sirf_remove,
187 };
188
189 module_platform_driver(sdhci_sirf_driver);
190
191 MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
192 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
193 MODULE_LICENSE("GPL v2");