1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/drivers/mmc/host/sdhci_f_sdh30.c
5 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
6 * Vincent Yang <vincent.yang@tw.fujitsu.com>
7 * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org>
8 * Copyright (C) 2019 Socionext Inc.
11 #include <linux/acpi.h>
12 #include <linux/err.h>
13 #include <linux/delay.h>
14 #include <linux/module.h>
16 #include <linux/property.h>
17 #include <linux/clk.h>
18 #include <linux/reset.h>
20 #include "sdhci-pltfm.h"
21 #include "sdhci_f_sdh30.h"
23 struct f_sdhost_priv
{
24 struct clk
*clk_iface
;
26 struct reset_control
*rst
;
29 bool enable_cmd_dat_delay
;
32 static void *sdhci_f_sdhost_priv(struct sdhci_host
*host
)
34 struct sdhci_pltfm_host
*pltfm_host
= sdhci_priv(host
);
36 return sdhci_pltfm_priv(pltfm_host
);
39 static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host
*host
)
41 struct f_sdhost_priv
*priv
= sdhci_f_sdhost_priv(host
);
44 usleep_range(2500, 3000);
45 ctrl
= sdhci_readl(host
, F_SDH30_IO_CONTROL2
);
46 ctrl
|= F_SDH30_CRES_O_DN
;
47 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
48 ctrl
|= F_SDH30_MSEL_O_1_8
;
49 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
51 ctrl
&= ~F_SDH30_CRES_O_DN
;
52 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
53 usleep_range(2500, 3000);
55 if (priv
->vendor_hs200
) {
56 dev_info(priv
->dev
, "%s: setting hs200\n", __func__
);
57 ctrl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
58 ctrl
|= priv
->vendor_hs200
;
59 sdhci_writel(host
, ctrl
, F_SDH30_ESD_CONTROL
);
62 ctrl
= sdhci_readl(host
, F_SDH30_TUNING_SETTING
);
63 ctrl
|= F_SDH30_CMD_CHK_DIS
;
64 sdhci_writel(host
, ctrl
, F_SDH30_TUNING_SETTING
);
67 static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host
*host
)
69 return F_SDH30_MIN_CLOCK
;
72 static void sdhci_f_sdh30_reset(struct sdhci_host
*host
, u8 mask
)
74 struct f_sdhost_priv
*priv
= sdhci_f_sdhost_priv(host
);
77 if (sdhci_readw(host
, SDHCI_CLOCK_CONTROL
) == 0)
78 sdhci_writew(host
, 0xBC01, SDHCI_CLOCK_CONTROL
);
80 sdhci_reset(host
, mask
);
82 if (priv
->enable_cmd_dat_delay
) {
83 ctl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
84 ctl
|= F_SDH30_CMD_DAT_DELAY
;
85 sdhci_writel(host
, ctl
, F_SDH30_ESD_CONTROL
);
88 if ((host
->mmc
->caps
& MMC_CAP_NONREMOVABLE
) &&
89 !(sdhci_readl(host
, SDHCI_PRESENT_STATE
) & SDHCI_CARD_PRESENT
)) {
90 ctl
= sdhci_readl(host
, F_SDH30_TEST
);
91 ctl
|= F_SDH30_FORCE_CARD_INSERT
;
92 sdhci_writel(host
, ctl
, F_SDH30_TEST
);
96 static const struct sdhci_ops sdhci_f_sdh30_ops
= {
97 .voltage_switch
= sdhci_f_sdh30_soft_voltage_switch
,
98 .get_min_clock
= sdhci_f_sdh30_get_min_clock
,
99 .reset
= sdhci_f_sdh30_reset
,
100 .set_clock
= sdhci_set_clock
,
101 .set_bus_width
= sdhci_set_bus_width
,
102 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
105 static const struct sdhci_pltfm_data sdhci_f_sdh30_pltfm_data
= {
106 .ops
= &sdhci_f_sdh30_ops
,
107 .quirks
= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
108 | SDHCI_QUIRK_INVERTED_WRITE_PROTECT
,
109 .quirks2
= SDHCI_QUIRK2_SUPPORT_SINGLE
110 | SDHCI_QUIRK2_TUNING_WORK_AROUND
,
113 static int sdhci_f_sdh30_probe(struct platform_device
*pdev
)
115 struct sdhci_host
*host
;
116 struct device
*dev
= &pdev
->dev
;
117 int ctrl
= 0, ret
= 0;
118 struct f_sdhost_priv
*priv
;
119 struct sdhci_pltfm_host
*pltfm_host
;
122 host
= sdhci_pltfm_init(pdev
, &sdhci_f_sdh30_pltfm_data
,
123 sizeof(struct f_sdhost_priv
));
125 return PTR_ERR(host
);
127 pltfm_host
= sdhci_priv(host
);
128 priv
= sdhci_pltfm_priv(pltfm_host
);
131 priv
->enable_cmd_dat_delay
= device_property_read_bool(dev
,
132 "fujitsu,cmd-dat-delay-select");
134 ret
= mmc_of_parse(host
->mmc
);
138 if (dev_of_node(dev
)) {
139 sdhci_get_of_property(pdev
);
141 priv
->clk_iface
= devm_clk_get(&pdev
->dev
, "iface");
142 if (IS_ERR(priv
->clk_iface
)) {
143 ret
= PTR_ERR(priv
->clk_iface
);
147 ret
= clk_prepare_enable(priv
->clk_iface
);
151 priv
->clk
= devm_clk_get(&pdev
->dev
, "core");
152 if (IS_ERR(priv
->clk
)) {
153 ret
= PTR_ERR(priv
->clk
);
157 ret
= clk_prepare_enable(priv
->clk
);
161 priv
->rst
= devm_reset_control_get_optional_shared(dev
, NULL
);
162 if (IS_ERR(priv
->rst
)) {
163 ret
= PTR_ERR(priv
->rst
);
167 ret
= reset_control_deassert(priv
->rst
);
172 /* init vendor specific regs */
173 ctrl
= sdhci_readw(host
, F_SDH30_AHB_CONFIG
);
174 ctrl
|= F_SDH30_SIN
| F_SDH30_AHB_INCR_16
| F_SDH30_AHB_INCR_8
|
176 ctrl
&= ~(F_SDH30_AHB_BIGED
| F_SDH30_BUSLOCK_EN
);
177 sdhci_writew(host
, ctrl
, F_SDH30_AHB_CONFIG
);
179 reg
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
180 sdhci_writel(host
, reg
& ~F_SDH30_EMMC_RST
, F_SDH30_ESD_CONTROL
);
182 sdhci_writel(host
, reg
| F_SDH30_EMMC_RST
, F_SDH30_ESD_CONTROL
);
184 reg
= sdhci_readl(host
, SDHCI_CAPABILITIES
);
185 if (reg
& SDHCI_CAN_DO_8BIT
)
186 priv
->vendor_hs200
= F_SDH30_EMMC_HS200
;
188 if (!(reg
& SDHCI_TIMEOUT_CLK_MASK
))
189 host
->quirks
|= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
;
191 ret
= sdhci_add_host(host
);
198 reset_control_assert(priv
->rst
);
200 clk_disable_unprepare(priv
->clk
);
202 clk_disable_unprepare(priv
->clk_iface
);
204 sdhci_pltfm_free(pdev
);
209 static void sdhci_f_sdh30_remove(struct platform_device
*pdev
)
211 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
212 struct f_sdhost_priv
*priv
= sdhci_f_sdhost_priv(host
);
213 struct clk
*clk_iface
= priv
->clk_iface
;
214 struct reset_control
*rst
= priv
->rst
;
215 struct clk
*clk
= priv
->clk
;
217 sdhci_pltfm_remove(pdev
);
219 reset_control_assert(rst
);
220 clk_disable_unprepare(clk
);
221 clk_disable_unprepare(clk_iface
);
225 static const struct of_device_id f_sdh30_dt_ids
[] = {
226 { .compatible
= "fujitsu,mb86s70-sdhci-3.0" },
227 { .compatible
= "socionext,f-sdh30-e51-mmc" },
230 MODULE_DEVICE_TABLE(of
, f_sdh30_dt_ids
);
234 static const struct acpi_device_id f_sdh30_acpi_ids
[] = {
238 MODULE_DEVICE_TABLE(acpi
, f_sdh30_acpi_ids
);
241 static struct platform_driver sdhci_f_sdh30_driver
= {
244 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
245 .of_match_table
= of_match_ptr(f_sdh30_dt_ids
),
246 .acpi_match_table
= ACPI_PTR(f_sdh30_acpi_ids
),
247 .pm
= &sdhci_pltfm_pmops
,
249 .probe
= sdhci_f_sdh30_probe
,
250 .remove
= sdhci_f_sdh30_remove
,
253 module_platform_driver(sdhci_f_sdh30_driver
);
255 MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
256 MODULE_LICENSE("GPL v2");
257 MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD., Socionext Inc.");
258 MODULE_ALIAS("platform:f_sdh30");