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