2 * linux/drivers/mmc/host/sdhci_f_sdh30.c
4 * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
5 * Vincent Yang <vincent.yang@tw.fujitsu.com>
6 * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org>
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, version 2 of the License.
13 #include <linux/err.h>
14 #include <linux/delay.h>
15 #include <linux/module.h>
16 #include <linux/clk.h>
18 #include "sdhci-pltfm.h"
20 /* F_SDH30 extended Controller registers */
21 #define F_SDH30_AHB_CONFIG 0x100
22 #define F_SDH30_AHB_BIGED 0x00000040
23 #define F_SDH30_BUSLOCK_DMA 0x00000020
24 #define F_SDH30_BUSLOCK_EN 0x00000010
25 #define F_SDH30_SIN 0x00000008
26 #define F_SDH30_AHB_INCR_16 0x00000004
27 #define F_SDH30_AHB_INCR_8 0x00000002
28 #define F_SDH30_AHB_INCR_4 0x00000001
30 #define F_SDH30_TUNING_SETTING 0x108
31 #define F_SDH30_CMD_CHK_DIS 0x00010000
33 #define F_SDH30_IO_CONTROL2 0x114
34 #define F_SDH30_CRES_O_DN 0x00080000
35 #define F_SDH30_MSEL_O_1_8 0x00040000
37 #define F_SDH30_ESD_CONTROL 0x124
38 #define F_SDH30_EMMC_RST 0x00000002
39 #define F_SDH30_EMMC_HS200 0x01000000
41 #define F_SDH30_CMD_DAT_DELAY 0x200
43 #define F_SDH30_MIN_CLOCK 400000
45 struct f_sdhost_priv
{
46 struct clk
*clk_iface
;
52 static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host
*host
)
54 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
57 usleep_range(2500, 3000);
58 ctrl
= sdhci_readl(host
, F_SDH30_IO_CONTROL2
);
59 ctrl
|= F_SDH30_CRES_O_DN
;
60 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
61 ctrl
|= F_SDH30_MSEL_O_1_8
;
62 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
64 ctrl
&= ~F_SDH30_CRES_O_DN
;
65 sdhci_writel(host
, ctrl
, F_SDH30_IO_CONTROL2
);
66 usleep_range(2500, 3000);
68 if (priv
->vendor_hs200
) {
69 dev_info(priv
->dev
, "%s: setting hs200\n", __func__
);
70 ctrl
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
71 ctrl
|= priv
->vendor_hs200
;
72 sdhci_writel(host
, ctrl
, F_SDH30_ESD_CONTROL
);
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_f_sdh30_get_min_clock(struct sdhci_host
*host
)
82 return F_SDH30_MIN_CLOCK
;
85 static void sdhci_f_sdh30_reset(struct sdhci_host
*host
, u8 mask
)
87 if (sdhci_readw(host
, SDHCI_CLOCK_CONTROL
) == 0)
88 sdhci_writew(host
, 0xBC01, SDHCI_CLOCK_CONTROL
);
90 sdhci_reset(host
, mask
);
93 static const struct sdhci_ops sdhci_f_sdh30_ops
= {
94 .voltage_switch
= sdhci_f_sdh30_soft_voltage_switch
,
95 .get_min_clock
= sdhci_f_sdh30_get_min_clock
,
96 .reset
= sdhci_f_sdh30_reset
,
97 .set_clock
= sdhci_set_clock
,
98 .set_bus_width
= sdhci_set_bus_width
,
99 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
102 static int sdhci_f_sdh30_probe(struct platform_device
*pdev
)
104 struct sdhci_host
*host
;
105 struct device
*dev
= &pdev
->dev
;
106 struct resource
*res
;
107 int irq
, ctrl
= 0, ret
= 0;
108 struct f_sdhost_priv
*priv
;
111 irq
= platform_get_irq(pdev
, 0);
113 dev_err(dev
, "%s: no irq specified\n", __func__
);
117 host
= sdhci_alloc_host(dev
, sizeof(struct f_sdhost_priv
));
119 return PTR_ERR(host
);
121 priv
= sdhci_priv(host
);
124 host
->quirks
= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
|
125 SDHCI_QUIRK_INVERTED_WRITE_PROTECT
;
126 host
->quirks2
= SDHCI_QUIRK2_SUPPORT_SINGLE
|
127 SDHCI_QUIRK2_TUNING_WORK_AROUND
;
129 ret
= mmc_of_parse(host
->mmc
);
133 platform_set_drvdata(pdev
, host
);
135 sdhci_get_of_property(pdev
);
136 host
->hw_name
= "f_sdh30";
137 host
->ops
= &sdhci_f_sdh30_ops
;
140 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
141 host
->ioaddr
= devm_ioremap_resource(&pdev
->dev
, res
);
142 if (IS_ERR(host
->ioaddr
)) {
143 ret
= PTR_ERR(host
->ioaddr
);
147 priv
->clk_iface
= devm_clk_get(&pdev
->dev
, "iface");
148 if (IS_ERR(priv
->clk_iface
)) {
149 ret
= PTR_ERR(priv
->clk_iface
);
153 ret
= clk_prepare_enable(priv
->clk_iface
);
157 priv
->clk
= devm_clk_get(&pdev
->dev
, "core");
158 if (IS_ERR(priv
->clk
)) {
159 ret
= PTR_ERR(priv
->clk
);
163 ret
= clk_prepare_enable(priv
->clk
);
167 /* init vendor specific regs */
168 ctrl
= sdhci_readw(host
, F_SDH30_AHB_CONFIG
);
169 ctrl
|= F_SDH30_SIN
| F_SDH30_AHB_INCR_16
| F_SDH30_AHB_INCR_8
|
171 ctrl
&= ~(F_SDH30_AHB_BIGED
| F_SDH30_BUSLOCK_EN
);
172 sdhci_writew(host
, ctrl
, F_SDH30_AHB_CONFIG
);
174 reg
= sdhci_readl(host
, F_SDH30_ESD_CONTROL
);
175 sdhci_writel(host
, reg
& ~F_SDH30_EMMC_RST
, F_SDH30_ESD_CONTROL
);
177 sdhci_writel(host
, reg
| F_SDH30_EMMC_RST
, F_SDH30_ESD_CONTROL
);
179 reg
= sdhci_readl(host
, SDHCI_CAPABILITIES
);
180 if (reg
& SDHCI_CAN_DO_8BIT
)
181 priv
->vendor_hs200
= F_SDH30_EMMC_HS200
;
183 ret
= sdhci_add_host(host
);
190 clk_disable_unprepare(priv
->clk
);
192 clk_disable_unprepare(priv
->clk_iface
);
194 sdhci_free_host(host
);
198 static int sdhci_f_sdh30_remove(struct platform_device
*pdev
)
200 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
201 struct f_sdhost_priv
*priv
= sdhci_priv(host
);
203 sdhci_remove_host(host
, readl(host
->ioaddr
+ SDHCI_INT_STATUS
) ==
206 clk_disable_unprepare(priv
->clk_iface
);
207 clk_disable_unprepare(priv
->clk
);
209 sdhci_free_host(host
);
210 platform_set_drvdata(pdev
, NULL
);
215 static const struct of_device_id f_sdh30_dt_ids
[] = {
216 { .compatible
= "fujitsu,mb86s70-sdhci-3.0" },
219 MODULE_DEVICE_TABLE(of
, f_sdh30_dt_ids
);
221 static struct platform_driver sdhci_f_sdh30_driver
= {
224 .of_match_table
= f_sdh30_dt_ids
,
225 .pm
= &sdhci_pltfm_pmops
,
227 .probe
= sdhci_f_sdh30_probe
,
228 .remove
= sdhci_f_sdh30_remove
,
231 module_platform_driver(sdhci_f_sdh30_driver
);
233 MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
234 MODULE_LICENSE("GPL v2");
235 MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
236 MODULE_ALIAS("platform:f_sdh30");