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 void sdhci_milbeaut_set_power(struct sdhci_host
*host
,
125 unsigned char mode
, unsigned short vdd
)
127 if (!IS_ERR(host
->mmc
->supply
.vmmc
)) {
128 struct mmc_host
*mmc
= host
->mmc
;
130 mmc_regulator_set_ocr(mmc
, mmc
->supply
.vmmc
, vdd
);
132 sdhci_set_power_noreg(host
, mode
, vdd
);
135 static const struct sdhci_ops sdhci_milbeaut_ops
= {
136 .voltage_switch
= sdhci_milbeaut_soft_voltage_switch
,
137 .get_min_clock
= sdhci_milbeaut_get_min_clock
,
138 .reset
= sdhci_milbeaut_reset
,
139 .set_clock
= sdhci_set_clock
,
140 .set_bus_width
= sdhci_set_bus_width
,
141 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
142 .set_power
= sdhci_milbeaut_set_power
,
145 static void sdhci_milbeaut_bridge_reset(struct sdhci_host
*host
,
149 sdhci_writel(host
, 0, MLB_SOFT_RESET
);
151 sdhci_writel(host
, MLB_SOFT_RESET_RSTX
, MLB_SOFT_RESET
);
154 static void sdhci_milbeaut_bridge_init(struct sdhci_host
*host
,
159 /* IO_SDIO_CR_SET should be set while reset */
160 val
= sdhci_readl(host
, MLB_CR_SET
);
161 val
&= ~(MLB_CR_SET_CR_TOCLKFREQ_MASK
| MLB_CR_SET_CR_TOCLKUNIT
|
162 MLB_CR_SET_CR_BCLKFREQ_MASK
);
163 if (rate
>= MLB_TOCLKFREQ_UNIT_THRES
) {
164 clk
= MLB_CAL_TOCLKFREQ_MHZ(rate
);
165 clk
= min_t(u32
, MLB_TOCLKFREQ_MAX
, clk
);
166 val
|= MLB_CR_SET_CR_TOCLKUNIT
|
167 (clk
<< MLB_CR_SET_CR_TOCLKFREQ_SFT
);
169 clk
= MLB_CAL_TOCLKFREQ_KHZ(rate
);
170 clk
= min_t(u32
, MLB_TOCLKFREQ_MAX
, clk
);
171 clk
= max_t(u32
, MLB_TOCLKFREQ_MIN
, clk
);
172 val
|= clk
<< MLB_CR_SET_CR_TOCLKFREQ_SFT
;
175 clk
= MLB_CAL_BCLKFREQ(rate
);
176 clk
= min_t(u32
, MLB_BCLKFREQ_MAX
, clk
);
177 clk
= max_t(u32
, MLB_BCLKFREQ_MIN
, clk
);
178 val
|= clk
<< MLB_CR_SET_CR_BCLKFREQ_SFT
;
179 val
&= ~MLB_CR_SET_CR_RTUNTIMER_MASK
;
180 sdhci_writel(host
, val
, MLB_CR_SET
);
182 sdhci_writel(host
, MLB_CDR_SET_CLK2POW16
, MLB_CDR_SET
);
184 sdhci_writel(host
, MLB_WP_CD_LED_SET_LED_INV
, MLB_WP_CD_LED_SET
);
187 static void sdhci_milbeaut_vendor_init(struct sdhci_host
*host
)
189 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
192 ctl
= sdhci_readl(host
, F_SDH30_IO_CONTROL2
);
193 ctl
|= F_SDH30_CRES_O_DN
;
194 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
195 ctl
&= ~F_SDH30_MSEL_O_1_8
;
196 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
197 ctl
&= ~F_SDH30_CRES_O_DN
;
198 sdhci_writel(host
, ctl
, F_SDH30_IO_CONTROL2
);
200 ctl
= sdhci_readw(host
, F_SDH30_AHB_CONFIG
);
201 ctl
|= F_SDH30_SIN
| F_SDH30_AHB_INCR_16
| F_SDH30_AHB_INCR_8
|
203 ctl
&= ~(F_SDH30_AHB_BIGED
| F_SDH30_BUSLOCK_EN
);
204 sdhci_writew(host
, ctl
, F_SDH30_AHB_CONFIG
);
206 if (priv
->enable_cmd_dat_delay
) {
207 ctl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
208 ctl
|= F_SDH30_CMD_DAT_DELAY
;
209 sdhci_writel(host
, ctl
, F_SDH30_ESD_CONTROL
);
213 static const struct of_device_id mlb_dt_ids
[] = {
215 .compatible
= "socionext,milbeaut-m10v-sdhci-3.0",
219 MODULE_DEVICE_TABLE(of
, mlb_dt_ids
);
221 static void sdhci_milbeaut_init(struct sdhci_host
*host
)
223 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
224 int rate
= clk_get_rate(priv
->clk
);
227 sdhci_milbeaut_bridge_reset(host
, 0);
229 ctl
= sdhci_readw(host
, SDHCI_CLOCK_CONTROL
);
230 ctl
&= ~(SDHCI_CLOCK_CARD_EN
| SDHCI_CLOCK_INT_EN
);
231 sdhci_writew(host
, ctl
, SDHCI_CLOCK_CONTROL
);
233 sdhci_milbeaut_bridge_reset(host
, 1);
235 sdhci_milbeaut_bridge_init(host
, rate
);
236 sdhci_milbeaut_bridge_reset(host
, 0);
238 sdhci_milbeaut_vendor_init(host
);
241 static int sdhci_milbeaut_probe(struct platform_device
*pdev
)
243 struct sdhci_host
*host
;
244 struct device
*dev
= &pdev
->dev
;
246 struct f_sdhost_priv
*priv
;
248 irq
= platform_get_irq(pdev
, 0);
252 host
= sdhci_alloc_host(dev
, sizeof(struct f_sdhost_priv
));
254 return PTR_ERR(host
);
256 priv
= sdhci_priv(host
);
259 host
->quirks
= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
260 SDHCI_QUIRK_INVERTED_WRITE_PROTECT
|
261 SDHCI_QUIRK_CLOCK_BEFORE_RESET
|
262 SDHCI_QUIRK_DELAY_AFTER_POWER
;
263 host
->quirks2
= SDHCI_QUIRK2_SUPPORT_SINGLE
|
264 SDHCI_QUIRK2_TUNING_WORK_AROUND
|
265 SDHCI_QUIRK2_PRESET_VALUE_BROKEN
;
267 priv
->enable_cmd_dat_delay
= device_property_read_bool(dev
,
268 "fujitsu,cmd-dat-delay-select");
270 ret
= mmc_of_parse(host
->mmc
);
274 platform_set_drvdata(pdev
, host
);
276 host
->hw_name
= "f_sdh30";
277 host
->ops
= &sdhci_milbeaut_ops
;
280 host
->ioaddr
= devm_platform_ioremap_resource(pdev
, 0);
281 if (IS_ERR(host
->ioaddr
)) {
282 ret
= PTR_ERR(host
->ioaddr
);
286 if (dev_of_node(dev
)) {
287 sdhci_get_of_property(pdev
);
289 priv
->clk_iface
= devm_clk_get(&pdev
->dev
, "iface");
290 if (IS_ERR(priv
->clk_iface
)) {
291 ret
= PTR_ERR(priv
->clk_iface
);
295 ret
= clk_prepare_enable(priv
->clk_iface
);
299 priv
->clk
= devm_clk_get(&pdev
->dev
, "core");
300 if (IS_ERR(priv
->clk
)) {
301 ret
= PTR_ERR(priv
->clk
);
305 ret
= clk_prepare_enable(priv
->clk
);
310 sdhci_milbeaut_init(host
);
312 ret
= sdhci_add_host(host
);
319 clk_disable_unprepare(priv
->clk
);
321 clk_disable_unprepare(priv
->clk_iface
);
323 sdhci_free_host(host
);
327 static int sdhci_milbeaut_remove(struct platform_device
*pdev
)
329 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
330 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
332 sdhci_remove_host(host
, readl(host
->ioaddr
+ SDHCI_INT_STATUS
) ==
335 clk_disable_unprepare(priv
->clk_iface
);
336 clk_disable_unprepare(priv
->clk
);
338 sdhci_free_host(host
);
339 platform_set_drvdata(pdev
, NULL
);
344 static struct platform_driver sdhci_milbeaut_driver
= {
346 .name
= "sdhci-milbeaut",
347 .of_match_table
= of_match_ptr(mlb_dt_ids
),
349 .probe
= sdhci_milbeaut_probe
,
350 .remove
= sdhci_milbeaut_remove
,
353 module_platform_driver(sdhci_milbeaut_driver
);
355 MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver");
356 MODULE_AUTHOR("Takao Orito <orito.takao@socionext.com>");
357 MODULE_LICENSE("GPL v2");
358 MODULE_ALIAS("platform:sdhci-milbeaut");