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