1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
4 * Vincent Yang <vincent.yang@tw.fujitsu.com>
5 * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org>
6 * Copyright (C) 2019 Socionext Inc.
7 * Takao Orito <orito.takao@socionext.com>
10 #include <linux/bits.h>
11 #include <linux/clk.h>
12 #include <linux/delay.h>
13 #include <linux/err.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/module.h>
17 #include <linux/property.h>
19 #include "sdhci-pltfm.h"
20 #include "sdhci_f_sdh30.h"
22 /* milbeaut bridge controller register */
23 #define MLB_SOFT_RESET 0x0200
24 #define MLB_SOFT_RESET_RSTX BIT(0)
26 #define MLB_WP_CD_LED_SET 0x0210
27 #define MLB_WP_CD_LED_SET_LED_INV BIT(2)
29 #define MLB_CR_SET 0x0220
30 #define MLB_CR_SET_CR_TOCLKUNIT BIT(24)
31 #define MLB_CR_SET_CR_TOCLKFREQ_SFT (16)
32 #define MLB_CR_SET_CR_TOCLKFREQ_MASK (0x3F << MLB_CR_SET_CR_TOCLKFREQ_SFT)
33 #define MLB_CR_SET_CR_BCLKFREQ_SFT (8)
34 #define MLB_CR_SET_CR_BCLKFREQ_MASK (0xFF << MLB_CR_SET_CR_BCLKFREQ_SFT)
35 #define MLB_CR_SET_CR_RTUNTIMER_SFT (4)
36 #define MLB_CR_SET_CR_RTUNTIMER_MASK (0xF << MLB_CR_SET_CR_RTUNTIMER_SFT)
38 #define MLB_SD_TOCLK_I_DIV 16
39 #define MLB_TOCLKFREQ_UNIT_THRES 16000000
40 #define MLB_CAL_TOCLKFREQ_MHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000000)
41 #define MLB_CAL_TOCLKFREQ_KHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000)
42 #define MLB_TOCLKFREQ_MAX 63
43 #define MLB_TOCLKFREQ_MIN 1
45 #define MLB_SD_BCLK_I_DIV 4
46 #define MLB_CAL_BCLKFREQ(rate) (rate / MLB_SD_BCLK_I_DIV / 1000000)
47 #define MLB_BCLKFREQ_MAX 255
48 #define MLB_BCLKFREQ_MIN 1
50 #define MLB_CDR_SET 0x0230
51 #define MLB_CDR_SET_CLK2POW16 3
53 struct f_sdhost_priv
{
54 struct clk
*clk_iface
;
57 bool enable_cmd_dat_delay
;
60 static void sdhci_milbeaut_soft_voltage_switch(struct sdhci_host
*host
)
64 usleep_range(2500, 3000);
65 ctrl
= sdhci_readl(host
, F_SDH30_IO_CONTROL2
);
66 ctrl
|= F_SDH30_CRES_O_DN
;
67 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
68 ctrl
|= F_SDH30_MSEL_O_1_8
;
69 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
71 ctrl
&= ~F_SDH30_CRES_O_DN
;
72 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
73 usleep_range(2500, 3000);
75 ctrl
= sdhci_readl(host
, F_SDH30_TUNING_SETTING
);
76 ctrl
|= F_SDH30_CMD_CHK_DIS
;
77 sdhci_writel(host
, ctrl
, F_SDH30_TUNING_SETTING
);
80 static unsigned int sdhci_milbeaut_get_min_clock(struct sdhci_host
*host
)
82 return F_SDH30_MIN_CLOCK
;
85 static void sdhci_milbeaut_reset(struct sdhci_host
*host
, u8 mask
)
87 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
92 clk
= sdhci_readw(host
, SDHCI_CLOCK_CONTROL
);
93 clk
= (clk
& ~SDHCI_CLOCK_CARD_EN
) | SDHCI_CLOCK_INT_EN
;
94 sdhci_writew(host
, clk
, SDHCI_CLOCK_CONTROL
);
96 sdhci_reset(host
, mask
);
98 clk
|= SDHCI_CLOCK_CARD_EN
;
99 sdhci_writew(host
, clk
, SDHCI_CLOCK_CONTROL
);
101 timeout
= ktime_add_ms(ktime_get(), 10);
103 bool timedout
= ktime_after(ktime_get(), timeout
);
105 clk
= sdhci_readw(host
, SDHCI_CLOCK_CONTROL
);
106 if (clk
& SDHCI_CLOCK_INT_STABLE
)
109 pr_err("%s: Internal clock never stabilised.\n",
110 mmc_hostname(host
->mmc
));
111 sdhci_dumpregs(host
);
117 if (priv
->enable_cmd_dat_delay
) {
118 ctl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
119 ctl
|= F_SDH30_CMD_DAT_DELAY
;
120 sdhci_writel(host
, ctl
, F_SDH30_ESD_CONTROL
);
124 static const struct sdhci_ops sdhci_milbeaut_ops
= {
125 .voltage_switch
= sdhci_milbeaut_soft_voltage_switch
,
126 .get_min_clock
= sdhci_milbeaut_get_min_clock
,
127 .reset
= sdhci_milbeaut_reset
,
128 .set_clock
= sdhci_set_clock
,
129 .set_bus_width
= sdhci_set_bus_width
,
130 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
131 .set_power
= sdhci_set_power_and_bus_voltage
,
134 static void sdhci_milbeaut_bridge_reset(struct sdhci_host
*host
,
138 sdhci_writel(host
, 0, MLB_SOFT_RESET
);
140 sdhci_writel(host
, MLB_SOFT_RESET_RSTX
, MLB_SOFT_RESET
);
143 static void sdhci_milbeaut_bridge_init(struct sdhci_host
*host
,
148 /* IO_SDIO_CR_SET should be set while reset */
149 val
= sdhci_readl(host
, MLB_CR_SET
);
150 val
&= ~(MLB_CR_SET_CR_TOCLKFREQ_MASK
| MLB_CR_SET_CR_TOCLKUNIT
|
151 MLB_CR_SET_CR_BCLKFREQ_MASK
);
152 if (rate
>= MLB_TOCLKFREQ_UNIT_THRES
) {
153 clk
= MLB_CAL_TOCLKFREQ_MHZ(rate
);
154 clk
= min_t(u32
, MLB_TOCLKFREQ_MAX
, clk
);
155 val
|= MLB_CR_SET_CR_TOCLKUNIT
|
156 (clk
<< MLB_CR_SET_CR_TOCLKFREQ_SFT
);
158 clk
= MLB_CAL_TOCLKFREQ_KHZ(rate
);
159 clk
= min_t(u32
, MLB_TOCLKFREQ_MAX
, clk
);
160 clk
= max_t(u32
, MLB_TOCLKFREQ_MIN
, clk
);
161 val
|= clk
<< MLB_CR_SET_CR_TOCLKFREQ_SFT
;
164 clk
= MLB_CAL_BCLKFREQ(rate
);
165 clk
= min_t(u32
, MLB_BCLKFREQ_MAX
, clk
);
166 clk
= max_t(u32
, MLB_BCLKFREQ_MIN
, clk
);
167 val
|= clk
<< MLB_CR_SET_CR_BCLKFREQ_SFT
;
168 val
&= ~MLB_CR_SET_CR_RTUNTIMER_MASK
;
169 sdhci_writel(host
, val
, MLB_CR_SET
);
171 sdhci_writel(host
, MLB_CDR_SET_CLK2POW16
, MLB_CDR_SET
);
173 sdhci_writel(host
, MLB_WP_CD_LED_SET_LED_INV
, MLB_WP_CD_LED_SET
);
176 static void sdhci_milbeaut_vendor_init(struct sdhci_host
*host
)
178 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
181 ctl
= sdhci_readl(host
, F_SDH30_IO_CONTROL2
);
182 ctl
|= F_SDH30_CRES_O_DN
;
183 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
184 ctl
&= ~F_SDH30_MSEL_O_1_8
;
185 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
186 ctl
&= ~F_SDH30_CRES_O_DN
;
187 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
189 ctl
= sdhci_readw(host
, F_SDH30_AHB_CONFIG
);
190 ctl
|= F_SDH30_SIN
| F_SDH30_AHB_INCR_16
| F_SDH30_AHB_INCR_8
|
192 ctl
&= ~(F_SDH30_AHB_BIGED
| F_SDH30_BUSLOCK_EN
);
193 sdhci_writew(host
, ctl
, F_SDH30_AHB_CONFIG
);
195 if (priv
->enable_cmd_dat_delay
) {
196 ctl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
197 ctl
|= F_SDH30_CMD_DAT_DELAY
;
198 sdhci_writel(host
, ctl
, F_SDH30_ESD_CONTROL
);
202 static const struct of_device_id mlb_dt_ids
[] = {
204 .compatible
= "socionext,milbeaut-m10v-sdhci-3.0",
208 MODULE_DEVICE_TABLE(of
, mlb_dt_ids
);
210 static void sdhci_milbeaut_init(struct sdhci_host
*host
)
212 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
213 int rate
= clk_get_rate(priv
->clk
);
216 sdhci_milbeaut_bridge_reset(host
, 0);
218 ctl
= sdhci_readw(host
, SDHCI_CLOCK_CONTROL
);
219 ctl
&= ~(SDHCI_CLOCK_CARD_EN
| SDHCI_CLOCK_INT_EN
);
220 sdhci_writew(host
, ctl
, SDHCI_CLOCK_CONTROL
);
222 sdhci_milbeaut_bridge_reset(host
, 1);
224 sdhci_milbeaut_bridge_init(host
, rate
);
225 sdhci_milbeaut_bridge_reset(host
, 0);
227 sdhci_milbeaut_vendor_init(host
);
230 static int sdhci_milbeaut_probe(struct platform_device
*pdev
)
232 struct sdhci_host
*host
;
233 struct device
*dev
= &pdev
->dev
;
235 struct f_sdhost_priv
*priv
;
237 irq
= platform_get_irq(pdev
, 0);
241 host
= sdhci_alloc_host(dev
, sizeof(struct f_sdhost_priv
));
243 return PTR_ERR(host
);
245 priv
= sdhci_priv(host
);
248 host
->quirks
= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
249 SDHCI_QUIRK_INVERTED_WRITE_PROTECT
|
250 SDHCI_QUIRK_CLOCK_BEFORE_RESET
|
251 SDHCI_QUIRK_DELAY_AFTER_POWER
;
252 host
->quirks2
= SDHCI_QUIRK2_SUPPORT_SINGLE
|
253 SDHCI_QUIRK2_TUNING_WORK_AROUND
|
254 SDHCI_QUIRK2_PRESET_VALUE_BROKEN
;
256 priv
->enable_cmd_dat_delay
= device_property_read_bool(dev
,
257 "fujitsu,cmd-dat-delay-select");
259 ret
= mmc_of_parse(host
->mmc
);
263 platform_set_drvdata(pdev
, host
);
265 host
->hw_name
= "f_sdh30";
266 host
->ops
= &sdhci_milbeaut_ops
;
269 host
->ioaddr
= devm_platform_ioremap_resource(pdev
, 0);
270 if (IS_ERR(host
->ioaddr
)) {
271 ret
= PTR_ERR(host
->ioaddr
);
275 if (dev_of_node(dev
)) {
276 sdhci_get_of_property(pdev
);
278 priv
->clk_iface
= devm_clk_get(&pdev
->dev
, "iface");
279 if (IS_ERR(priv
->clk_iface
)) {
280 ret
= PTR_ERR(priv
->clk_iface
);
284 ret
= clk_prepare_enable(priv
->clk_iface
);
288 priv
->clk
= devm_clk_get(&pdev
->dev
, "core");
289 if (IS_ERR(priv
->clk
)) {
290 ret
= PTR_ERR(priv
->clk
);
294 ret
= clk_prepare_enable(priv
->clk
);
299 sdhci_milbeaut_init(host
);
301 ret
= sdhci_add_host(host
);
308 clk_disable_unprepare(priv
->clk
);
310 clk_disable_unprepare(priv
->clk_iface
);
312 sdhci_free_host(host
);
316 static int sdhci_milbeaut_remove(struct platform_device
*pdev
)
318 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
319 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
321 sdhci_remove_host(host
, readl(host
->ioaddr
+ SDHCI_INT_STATUS
) ==
324 clk_disable_unprepare(priv
->clk_iface
);
325 clk_disable_unprepare(priv
->clk
);
327 sdhci_free_host(host
);
328 platform_set_drvdata(pdev
, NULL
);
333 static struct platform_driver sdhci_milbeaut_driver
= {
335 .name
= "sdhci-milbeaut",
336 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
337 .of_match_table
= of_match_ptr(mlb_dt_ids
),
339 .probe
= sdhci_milbeaut_probe
,
340 .remove
= sdhci_milbeaut_remove
,
343 module_platform_driver(sdhci_milbeaut_driver
);
345 MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver");
346 MODULE_AUTHOR("Takao Orito <orito.takao@socionext.com>");
347 MODULE_LICENSE("GPL v2");
348 MODULE_ALIAS("platform:sdhci-milbeaut");