aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt87
-rw-r--r--drivers/mmc/host/Kconfig9
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c253
-rw-r--r--include/linux/mmc/dw_mmc.h2
5 files changed, 351 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
new file mode 100644
index 000000000000..792768953330
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
@@ -0,0 +1,87 @@
1* Samsung Exynos specific extensions to the Synopsis Designware Mobile
2 Storage Host Controller
3
4The Synopsis designware mobile storage host controller is used to interface
5a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
6differences between the core Synopsis dw mshc controller properties described
7by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific
8extensions to the Synopsis Designware Mobile Storage Host Controller.
9
10Required Properties:
11
12* compatible: should be
13 - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
14 specific extentions.
15 - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
16 specific extentions.
17 - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
18 specific extentions.
19
20* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
21 unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
22 ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
23
24* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
25 in transmit mode and CIU clock phase shift value in receive mode for single
26 data rate mode operation. Refer notes below for the order of the cells and the
27 valid values.
28
29* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
30 in transmit mode and CIU clock phase shift value in receive mode for double
31 data rate mode operation. Refer notes below for the order of the cells and the
32 valid values.
33
34 Notes for the sdr-timing and ddr-timing values:
35
36 The order of the cells should be
37 - First Cell: CIU clock phase shift value for tx mode.
38 - Second Cell: CIU clock phase shift value for rx mode.
39
40 Valid values for SDR and DDR CIU clock timing for Exynos5250:
41 - valid value for tx phase shift and rx phase shift is 0 to 7.
42 - when CIU clock divider value is set to 3, all possible 8 phase shift
43 values can be used.
44 - if CIU clock divider value is 0 (that is divide by 1), both tx and rx
45 phase shift clocks should be 0.
46
47Required properties for a slot:
48
49* gpios: specifies a list of gpios used for command, clock and data bus. The
50 first gpio is the command line and the second gpio is the clock line. The
51 rest of the gpios (depending on the bus-width property) are the data lines in
52 no particular order. The format of the gpio specifier depends on the gpio
53 controller.
54
55Example:
56
57 The MSHC controller node can be split into two portions, SoC specific and
58 board specific portions as listed below.
59
60 dwmmc0@12200000 {
61 compatible = "samsung,exynos5250-dw-mshc";
62 reg = <0x12200000 0x1000>;
63 interrupts = <0 75 0>;
64 #address-cells = <1>;
65 #size-cells = <0>;
66 };
67
68 dwmmc0@12200000 {
69 num-slots = <1>;
70 supports-highspeed;
71 broken-cd;
72 fifo-depth = <0x80>;
73 card-detect-delay = <200>;
74 samsung,dw-mshc-ciu-div = <3>;
75 samsung,dw-mshc-sdr-timing = <2 3>;
76 samsung,dw-mshc-ddr-timing = <1 2>;
77
78 slot@0 {
79 reg = <0>;
80 bus-width = <8>;
81 gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
82 <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
83 <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
84 <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
85 <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
86 };
87 };
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b32e3b2..9bf10e7bbfaf 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -540,6 +540,15 @@ config MMC_DW_PLTFM
540 540
541 If unsure, say Y. 541 If unsure, say Y.
542 542
543config MMC_DW_EXYNOS
544 tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
545 depends on MMC_DW
546 select MMC_DW_PLTFM
547 help
548 This selects support for Samsung Exynos SoC specific extensions to the
549 Synopsys DesignWare Memory Card Interface driver. Select this option
550 for platforms based on Exynos4 and Exynos5 SoC's.
551
543config MMC_DW_PCI 552config MMC_DW_PCI
544 tristate "Synopsys Designware MCI support on PCI bus" 553 tristate "Synopsys Designware MCI support on PCI bus"
545 depends on MMC_DW && PCI 554 depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06be925..17ad0a7ba40b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
39obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o 39obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
40obj-$(CONFIG_MMC_DW) += dw_mmc.o 40obj-$(CONFIG_MMC_DW) += dw_mmc.o
41obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o 41obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
42obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
42obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o 43obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
43obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o 44obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
44obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o 45obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
new file mode 100644
index 000000000000..660bbc528862
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -0,0 +1,253 @@
1/*
2 * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
3 *
4 * Copyright (C) 2012, Samsung Electronics Co., Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/clk.h>
15#include <linux/mmc/host.h>
16#include <linux/mmc/dw_mmc.h>
17#include <linux/of.h>
18#include <linux/of_gpio.h>
19
20#include "dw_mmc.h"
21#include "dw_mmc-pltfm.h"
22
23#define NUM_PINS(x) (x + 2)
24
25#define SDMMC_CLKSEL 0x09C
26#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
27#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
28#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
29#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
30#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
31 SDMMC_CLKSEL_CCLK_DRIVE(y) | \
32 SDMMC_CLKSEL_CCLK_DIVIDER(z))
33
34#define SDMMC_CMD_USE_HOLD_REG BIT(29)
35
36#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
37#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
38
39/* Variations in Exynos specific dw-mshc controller */
40enum dw_mci_exynos_type {
41 DW_MCI_TYPE_EXYNOS4210,
42 DW_MCI_TYPE_EXYNOS4412,
43 DW_MCI_TYPE_EXYNOS5250,
44};
45
46/* Exynos implementation specific driver private data */
47struct dw_mci_exynos_priv_data {
48 enum dw_mci_exynos_type ctrl_type;
49 u8 ciu_div;
50 u32 sdr_timing;
51 u32 ddr_timing;
52};
53
54static struct dw_mci_exynos_compatible {
55 char *compatible;
56 enum dw_mci_exynos_type ctrl_type;
57} exynos_compat[] = {
58 {
59 .compatible = "samsung,exynos4210-dw-mshc",
60 .ctrl_type = DW_MCI_TYPE_EXYNOS4210,
61 }, {
62 .compatible = "samsung,exynos4412-dw-mshc",
63 .ctrl_type = DW_MCI_TYPE_EXYNOS4412,
64 }, {
65 .compatible = "samsung,exynos5250-dw-mshc",
66 .ctrl_type = DW_MCI_TYPE_EXYNOS5250,
67 },
68};
69
70static int dw_mci_exynos_priv_init(struct dw_mci *host)
71{
72 struct dw_mci_exynos_priv_data *priv;
73 int idx;
74
75 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
76 if (!priv) {
77 dev_err(host->dev, "mem alloc failed for private data\n");
78 return -ENOMEM;
79 }
80
81 for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
82 if (of_device_is_compatible(host->dev->of_node,
83 exynos_compat[idx].compatible))
84 priv->ctrl_type = exynos_compat[idx].ctrl_type;
85 }
86
87 host->priv = priv;
88 return 0;
89}
90
91static int dw_mci_exynos_setup_clock(struct dw_mci *host)
92{
93 struct dw_mci_exynos_priv_data *priv = host->priv;
94
95 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250)
96 host->bus_hz /= (priv->ciu_div + 1);
97 else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
98 host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
99 else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
100 host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
101
102 return 0;
103}
104
105static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
106{
107 /*
108 * Exynos4412 and Exynos5250 extends the use of CMD register with the
109 * use of bit 29 (which is reserved on standard MSHC controllers) for
110 * optionally bypassing the HOLD register for command and data. The
111 * HOLD register should be bypassed in case there is no phase shift
112 * applied on CMD/DATA that is sent to the card.
113 */
114 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
115 *cmdr |= SDMMC_CMD_USE_HOLD_REG;
116}
117
118static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
119{
120 struct dw_mci_exynos_priv_data *priv = host->priv;
121
122 if (ios->timing == MMC_TIMING_UHS_DDR50)
123 mci_writel(host, CLKSEL, priv->ddr_timing);
124 else
125 mci_writel(host, CLKSEL, priv->sdr_timing);
126}
127
128static int dw_mci_exynos_parse_dt(struct dw_mci *host)
129{
130 struct dw_mci_exynos_priv_data *priv = host->priv;
131 struct device_node *np = host->dev->of_node;
132 u32 timing[2];
133 u32 div = 0;
134 int ret;
135
136 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
137 priv->ciu_div = div;
138
139 ret = of_property_read_u32_array(np,
140 "samsung,dw-mshc-sdr-timing", timing, 2);
141 if (ret)
142 return ret;
143
144 priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
145
146 ret = of_property_read_u32_array(np,
147 "samsung,dw-mshc-ddr-timing", timing, 2);
148 if (ret)
149 return ret;
150
151 priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
152 return 0;
153}
154
155static int dw_mci_exynos_setup_bus(struct dw_mci *host,
156 struct device_node *slot_np, u8 bus_width)
157{
158 int idx, gpio, ret;
159
160 if (!slot_np)
161 return -EINVAL;
162
163 /* cmd + clock + bus-width pins */
164 for (idx = 0; idx < NUM_PINS(bus_width); idx++) {
165 gpio = of_get_gpio(slot_np, idx);
166 if (!gpio_is_valid(gpio)) {
167 dev_err(host->dev, "invalid gpio: %d\n", gpio);
168 return -EINVAL;
169 }
170
171 ret = devm_gpio_request(host->dev, gpio, "dw-mci-bus");
172 if (ret) {
173 dev_err(host->dev, "gpio [%d] request failed\n", gpio);
174 return -EBUSY;
175 }
176 }
177
178 gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
179 if (gpio_is_valid(gpio)) {
180 if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
181 dev_info(host->dev, "gpio [%d] request failed\n",
182 gpio);
183 } else {
184 dev_info(host->dev, "wp gpio not available");
185 host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
186 }
187
188 if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
189 return 0;
190
191 gpio = of_get_named_gpio(slot_np, "samsung,cd-pinmux-gpio", 0);
192 if (gpio_is_valid(gpio)) {
193 if (devm_gpio_request(host->dev, gpio, "dw-mci-cd"))
194 dev_err(host->dev, "gpio [%d] request failed\n", gpio);
195 } else {
196 dev_info(host->dev, "cd gpio not available");
197 }
198
199 return 0;
200}
201
202/* Exynos5250 controller specific capabilities */
203static unsigned long exynos5250_dwmmc_caps[4] = {
204 MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
205 MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
206 MMC_CAP_CMD23,
207 MMC_CAP_CMD23,
208 MMC_CAP_CMD23,
209};
210
211static struct dw_mci_drv_data exynos5250_drv_data = {
212 .caps = exynos5250_dwmmc_caps,
213 .init = dw_mci_exynos_priv_init,
214 .setup_clock = dw_mci_exynos_setup_clock,
215 .prepare_command = dw_mci_exynos_prepare_command,
216 .set_ios = dw_mci_exynos_set_ios,
217 .parse_dt = dw_mci_exynos_parse_dt,
218 .setup_bus = dw_mci_exynos_setup_bus,
219};
220
221static const struct of_device_id dw_mci_exynos_match[] = {
222 { .compatible = "samsung,exynos5250-dw-mshc",
223 .data = (void *)&exynos5250_drv_data, },
224 {},
225};
226MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
227
228int dw_mci_exynos_probe(struct platform_device *pdev)
229{
230 struct dw_mci_drv_data *drv_data;
231 const struct of_device_id *match;
232
233 match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
234 drv_data = match->data;
235 return dw_mci_pltfm_register(pdev, drv_data);
236}
237
238static struct platform_driver dw_mci_exynos_pltfm_driver = {
239 .probe = dw_mci_exynos_probe,
240 .remove = __exit_p(dw_mci_pltfm_remove),
241 .driver = {
242 .name = "dwmmc_exynos",
243 .of_match_table = of_match_ptr(dw_mci_exynos_match),
244 .pm = &dw_mci_pltfm_pmops,
245 },
246};
247
248module_platform_driver(dw_mci_exynos_pltfm_driver);
249
250MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
251MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
252MODULE_LICENSE("GPL v2");
253MODULE_ALIAS("platform:dwmmc-exynos");
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 6cb043e48953..7c6a1139d8fa 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -227,7 +227,7 @@ struct dw_mci_board {
227 u32 num_slots; 227 u32 num_slots;
228 228
229 u32 quirks; /* Workaround / Quirk flags */ 229 u32 quirks; /* Workaround / Quirk flags */
230 unsigned int bus_hz; /* Bus speed */ 230 unsigned int bus_hz; /* Clock speed at the cclk_in pad */
231 231
232 unsigned int caps; /* Capabilities */ 232 unsigned int caps; /* Capabilities */
233 unsigned int caps2; /* More capabilities */ 233 unsigned int caps2; /* More capabilities */