2 * SDHCI support for SiRF primaII and marco SoCs
4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
6 * Licensed under GPLv2 or later.
9 #include <linux/delay.h>
10 #include <linux/device.h>
11 #include <linux/mmc/host.h>
12 #include <linux/module.h>
14 #include <linux/of_gpio.h>
15 #include <linux/mmc/slot-gpio.h>
16 #include "sdhci-pltfm.h"
18 #define SDHCI_CLK_DELAY_SETTING 0x4C
19 #define SDHCI_SIRF_8BITBUS BIT(3)
20 #define SIRF_TUNING_COUNT 16384
22 struct sdhci_sirf_priv
{
26 static void sdhci_sirf_set_bus_width(struct sdhci_host
*host
, int width
)
30 ctrl
= sdhci_readb(host
, SDHCI_HOST_CONTROL
);
31 ctrl
&= ~(SDHCI_CTRL_4BITBUS
| SDHCI_SIRF_8BITBUS
);
34 * CSR atlas7 and prima2 SD host version is not 3.0
35 * 8bit-width enable bit of CSR SD hosts is 3,
36 * while stardard hosts use bit 5
38 if (width
== MMC_BUS_WIDTH_8
)
39 ctrl
|= SDHCI_SIRF_8BITBUS
;
40 else if (width
== MMC_BUS_WIDTH_4
)
41 ctrl
|= SDHCI_CTRL_4BITBUS
;
43 sdhci_writeb(host
, ctrl
, SDHCI_HOST_CONTROL
);
46 static u32
sdhci_sirf_readl_le(struct sdhci_host
*host
, int reg
)
48 u32 val
= readl(host
->ioaddr
+ reg
);
50 if (unlikely((reg
== SDHCI_CAPABILITIES_1
) &&
51 (host
->mmc
->caps
& MMC_CAP_UHS_SDR50
))) {
52 /* fake CAP_1 register */
53 val
= SDHCI_SUPPORT_DDR50
|
54 SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING
;
57 if (unlikely(reg
== SDHCI_SLOT_INT_STATUS
)) {
59 /* fake chips as V3.0 host conreoller */
60 prss
&= ~(0xFF << 16);
61 val
= prss
| (SDHCI_SPEC_300
<< 16);
66 static u16
sdhci_sirf_readw_le(struct sdhci_host
*host
, int reg
)
70 ret
= readw(host
->ioaddr
+ reg
);
72 if (unlikely(reg
== SDHCI_HOST_VERSION
)) {
73 ret
= readw(host
->ioaddr
+ SDHCI_HOST_VERSION
);
74 ret
|= SDHCI_SPEC_300
;
80 static int sdhci_sirf_execute_tuning(struct sdhci_host
*host
, u32 opcode
)
82 int tuning_seq_cnt
= 3;
84 u8 tuned_phase_cnt
= 0;
85 int rc
= 0, longest_range
= 0;
86 int start
= -1, end
= 0, tuning_value
= -1, range
= 0;
88 struct mmc_host
*mmc
= host
->mmc
;
90 clock_setting
= sdhci_readw(host
, SDHCI_CLK_DELAY_SETTING
);
91 clock_setting
&= ~0x3fff;
98 clock_setting
| phase
,
99 SDHCI_CLK_DELAY_SETTING
);
101 if (!mmc_send_tuning(mmc
, opcode
, NULL
)) {
102 /* Tuning is successful at this tuning point */
104 dev_dbg(mmc_dev(mmc
), "%s: Found good phase = %d\n",
105 mmc_hostname(mmc
), phase
);
110 if (phase
== (SIRF_TUNING_COUNT
- 1)
111 && range
> longest_range
)
112 tuning_value
= (start
+ end
) / 2;
114 dev_dbg(mmc_dev(mmc
), "%s: Found bad phase = %d\n",
115 mmc_hostname(mmc
), phase
);
116 if (range
> longest_range
) {
117 tuning_value
= (start
+ end
) / 2;
118 longest_range
= range
;
123 } while (++phase
< SIRF_TUNING_COUNT
);
125 if (tuned_phase_cnt
&& tuning_value
> 0) {
127 * Finally set the selected phase in delay
130 phase
= tuning_value
;
132 clock_setting
| phase
,
133 SDHCI_CLK_DELAY_SETTING
);
135 dev_dbg(mmc_dev(mmc
), "%s: Setting the tuning phase to %d\n",
136 mmc_hostname(mmc
), phase
);
138 if (--tuning_seq_cnt
)
141 dev_dbg(mmc_dev(mmc
), "%s: No tuning point found\n",
149 static struct sdhci_ops sdhci_sirf_ops
= {
150 .read_l
= sdhci_sirf_readl_le
,
151 .read_w
= sdhci_sirf_readw_le
,
152 .platform_execute_tuning
= sdhci_sirf_execute_tuning
,
153 .set_clock
= sdhci_set_clock
,
154 .get_max_clock
= sdhci_pltfm_clk_get_max_clock
,
155 .set_bus_width
= sdhci_sirf_set_bus_width
,
156 .reset
= sdhci_reset
,
157 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
160 static struct sdhci_pltfm_data sdhci_sirf_pdata
= {
161 .ops
= &sdhci_sirf_ops
,
162 .quirks
= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
|
163 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
|
164 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
|
165 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS
,
166 .quirks2
= SDHCI_QUIRK2_PRESET_VALUE_BROKEN
,
169 static int sdhci_sirf_probe(struct platform_device
*pdev
)
171 struct sdhci_host
*host
;
172 struct sdhci_pltfm_host
*pltfm_host
;
173 struct sdhci_sirf_priv
*priv
;
178 clk
= devm_clk_get(&pdev
->dev
, NULL
);
180 dev_err(&pdev
->dev
, "unable to get clock");
184 if (pdev
->dev
.of_node
)
185 gpio_cd
= of_get_named_gpio(pdev
->dev
.of_node
, "cd-gpios", 0);
189 host
= sdhci_pltfm_init(pdev
, &sdhci_sirf_pdata
, sizeof(struct sdhci_sirf_priv
));
191 return PTR_ERR(host
);
193 pltfm_host
= sdhci_priv(host
);
194 pltfm_host
->clk
= clk
;
195 priv
= sdhci_pltfm_priv(pltfm_host
);
196 priv
->gpio_cd
= gpio_cd
;
198 sdhci_get_of_property(pdev
);
200 ret
= clk_prepare_enable(pltfm_host
->clk
);
202 goto err_clk_prepare
;
204 ret
= sdhci_add_host(host
);
209 * We must request the IRQ after sdhci_add_host(), as the tasklet only
210 * gets setup in sdhci_add_host() and we oops.
212 if (gpio_is_valid(priv
->gpio_cd
)) {
213 ret
= mmc_gpio_request_cd(host
->mmc
, priv
->gpio_cd
, 0);
215 dev_err(&pdev
->dev
, "card detect irq request failed: %d\n",
219 mmc_gpiod_request_cd_irq(host
->mmc
);
225 sdhci_remove_host(host
, 0);
227 clk_disable_unprepare(pltfm_host
->clk
);
229 sdhci_pltfm_free(pdev
);
233 #ifdef CONFIG_PM_SLEEP
234 static int sdhci_sirf_suspend(struct device
*dev
)
236 struct sdhci_host
*host
= dev_get_drvdata(dev
);
237 struct sdhci_pltfm_host
*pltfm_host
= sdhci_priv(host
);
240 ret
= sdhci_suspend_host(host
);
244 clk_disable(pltfm_host
->clk
);
249 static int sdhci_sirf_resume(struct device
*dev
)
251 struct sdhci_host
*host
= dev_get_drvdata(dev
);
252 struct sdhci_pltfm_host
*pltfm_host
= sdhci_priv(host
);
255 ret
= clk_enable(pltfm_host
->clk
);
257 dev_dbg(dev
, "Resume: Error enabling clock\n");
261 return sdhci_resume_host(host
);
265 static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops
, sdhci_sirf_suspend
, sdhci_sirf_resume
);
267 static const struct of_device_id sdhci_sirf_of_match
[] = {
268 { .compatible
= "sirf,prima2-sdhc" },
271 MODULE_DEVICE_TABLE(of
, sdhci_sirf_of_match
);
273 static struct platform_driver sdhci_sirf_driver
= {
275 .name
= "sdhci-sirf",
276 .of_match_table
= sdhci_sirf_of_match
,
277 .pm
= &sdhci_sirf_pm_ops
,
279 .probe
= sdhci_sirf_probe
,
280 .remove
= sdhci_pltfm_unregister
,
283 module_platform_driver(sdhci_sirf_driver
);
285 MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
286 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
287 MODULE_LICENSE("GPL v2");