]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
d0ebbb8d JC |
2 | /* |
3 | * (C) Copyright 2012 SAMSUNG Electronics | |
4 | * Jaehoon Chung <[email protected]> | |
d0ebbb8d JC |
5 | */ |
6 | ||
7 | #include <common.h> | |
d0ebbb8d | 8 | #include <dwmmc.h> |
a082a2dd | 9 | #include <fdtdec.h> |
401d1c4f | 10 | #include <asm/global_data.h> |
b08c8c48 | 11 | #include <linux/libfdt.h> |
a082a2dd | 12 | #include <malloc.h> |
ccd60a85 | 13 | #include <errno.h> |
d0ebbb8d JC |
14 | #include <asm/arch/dwmmc.h> |
15 | #include <asm/arch/clk.h> | |
a082a2dd | 16 | #include <asm/arch/pinmux.h> |
64029f7a | 17 | #include <asm/arch/power.h> |
959198f7 | 18 | #include <asm/gpio.h> |
d0ebbb8d | 19 | |
a082a2dd A |
20 | #define DWMMC_MAX_CH_NUM 4 |
21 | #define DWMMC_MAX_FREQ 52000000 | |
22 | #define DWMMC_MIN_FREQ 400000 | |
5dab81ce JC |
23 | #define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001 |
24 | #define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001 | |
25 | ||
3537ee87 JC |
26 | #ifdef CONFIG_DM_MMC |
27 | #include <dm.h> | |
28 | DECLARE_GLOBAL_DATA_PTR; | |
29 | ||
30 | struct exynos_mmc_plat { | |
31 | struct mmc_config cfg; | |
32 | struct mmc mmc; | |
33 | }; | |
34 | #endif | |
35 | ||
5dab81ce JC |
36 | /* Exynos implmentation specific drver private data */ |
37 | struct dwmci_exynos_priv_data { | |
3537ee87 JC |
38 | #ifdef CONFIG_DM_MMC |
39 | struct dwmci_host host; | |
40 | #endif | |
5dab81ce JC |
41 | u32 sdr_timing; |
42 | }; | |
d0ebbb8d | 43 | |
a082a2dd A |
44 | /* |
45 | * Function used as callback function to initialise the | |
46 | * CLKSEL register for every mmc channel. | |
47 | */ | |
d456dfba | 48 | static int exynos_dwmci_clksel(struct dwmci_host *host) |
d0ebbb8d | 49 | { |
7c350a2a LM |
50 | #ifdef CONFIG_DM_MMC |
51 | struct dwmci_exynos_priv_data *priv = | |
52 | container_of(host, struct dwmci_exynos_priv_data, host); | |
53 | #else | |
5dab81ce | 54 | struct dwmci_exynos_priv_data *priv = host->priv; |
7c350a2a | 55 | #endif |
5dab81ce | 56 | dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); |
d456dfba SCL |
57 | |
58 | return 0; | |
a082a2dd | 59 | } |
d0ebbb8d | 60 | |
e3563f2e | 61 | unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) |
a082a2dd | 62 | { |
d3e016cc RS |
63 | unsigned long sclk; |
64 | int8_t clk_div; | |
65 | ||
66 | /* | |
67 | * Since SDCLKIN is divided inside controller by the DIVRATIO | |
68 | * value set in the CLKSEL register, we need to use the same output | |
69 | * clock value to calculate the CLKDIV value. | |
70 | * as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1) | |
71 | */ | |
72 | clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT) | |
73 | & DWMCI_DIVRATIO_MASK) + 1; | |
74 | sclk = get_mmc_clk(host->dev_index); | |
75 | ||
959198f7 JC |
76 | /* |
77 | * Assume to know divider value. | |
78 | * When clock unit is broken, need to set "host->div" | |
79 | */ | |
80 | return sclk / clk_div / (host->div + 1); | |
d0ebbb8d JC |
81 | } |
82 | ||
18ab6755 JC |
83 | static void exynos_dwmci_board_init(struct dwmci_host *host) |
84 | { | |
5dab81ce JC |
85 | struct dwmci_exynos_priv_data *priv = host->priv; |
86 | ||
18ab6755 JC |
87 | if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) { |
88 | dwmci_writel(host, EMMCP_MPSBEGIN0, 0); | |
89 | dwmci_writel(host, EMMCP_SEND0, 0); | |
90 | dwmci_writel(host, EMMCP_CTRL0, | |
91 | MPSCTRL_SECURE_READ_BIT | | |
92 | MPSCTRL_SECURE_WRITE_BIT | | |
93 | MPSCTRL_NON_SECURE_READ_BIT | | |
94 | MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID); | |
95 | } | |
3a33bb18 | 96 | |
5dab81ce JC |
97 | /* Set to timing value at initial time */ |
98 | if (priv->sdr_timing) | |
3a33bb18 | 99 | exynos_dwmci_clksel(host); |
18ab6755 JC |
100 | } |
101 | ||
d956a67e | 102 | static int exynos_dwmci_core_init(struct dwmci_host *host) |
d0ebbb8d | 103 | { |
a082a2dd A |
104 | unsigned int div; |
105 | unsigned long freq, sclk; | |
959198f7 JC |
106 | |
107 | if (host->bus_hz) | |
108 | freq = host->bus_hz; | |
109 | else | |
110 | freq = DWMMC_MAX_FREQ; | |
111 | ||
a082a2dd | 112 | /* request mmc clock vlaue of 52MHz. */ |
d956a67e | 113 | sclk = get_mmc_clk(host->dev_index); |
a082a2dd A |
114 | div = DIV_ROUND_UP(sclk, freq); |
115 | /* set the clock divisor for mmc */ | |
d956a67e | 116 | set_mmc_clk(host->dev_index, div); |
d0ebbb8d | 117 | |
a082a2dd | 118 | host->name = "EXYNOS DWMMC"; |
6f0b7caa RS |
119 | #ifdef CONFIG_EXYNOS5420 |
120 | host->quirks = DWMCI_QUIRK_DISABLE_SMU; | |
121 | #endif | |
18ab6755 | 122 | host->board_init = exynos_dwmci_board_init; |
a082a2dd | 123 | |
e09bd853 | 124 | host->caps = MMC_MODE_DDR_52MHz; |
d0ebbb8d | 125 | host->clksel = exynos_dwmci_clksel; |
b44fe83a | 126 | host->get_mmc_clk = exynos_dwmci_get_clk; |
3537ee87 JC |
127 | |
128 | #ifndef CONFIG_DM_MMC | |
a082a2dd A |
129 | /* Add the mmc channel to be registered with mmc core */ |
130 | if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { | |
d956a67e | 131 | printf("DWMMC%d registration failed\n", host->dev_index); |
a082a2dd A |
132 | return -1; |
133 | } | |
3537ee87 JC |
134 | #endif |
135 | ||
a082a2dd A |
136 | return 0; |
137 | } | |
138 | ||
959198f7 | 139 | static int do_dwmci_init(struct dwmci_host *host) |
a082a2dd | 140 | { |
d956a67e | 141 | int flag, err; |
a082a2dd | 142 | |
959198f7 JC |
143 | flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; |
144 | err = exynos_pinmux_config(host->dev_id, flag); | |
145 | if (err) { | |
d956a67e | 146 | printf("DWMMC%d not configure\n", host->dev_index); |
959198f7 JC |
147 | return err; |
148 | } | |
a082a2dd | 149 | |
d956a67e | 150 | return exynos_dwmci_core_init(host); |
959198f7 | 151 | } |
d0ebbb8d | 152 | |
959198f7 | 153 | static int exynos_dwmci_get_config(const void *blob, int node, |
b88c1efa LM |
154 | struct dwmci_host *host, |
155 | struct dwmci_exynos_priv_data *priv) | |
959198f7 JC |
156 | { |
157 | int err = 0; | |
5dab81ce | 158 | u32 base, timing[3]; |
d0ebbb8d | 159 | |
959198f7 JC |
160 | /* Extract device id for each mmc channel */ |
161 | host->dev_id = pinmux_decode_periph_id(blob, node); | |
a082a2dd | 162 | |
dfcb683a JC |
163 | host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id); |
164 | if (host->dev_index == host->dev_id) | |
165 | host->dev_index = host->dev_id - PERIPH_ID_SDMMC0; | |
166 | ||
ce757b18 JC |
167 | if (host->dev_index > 4) { |
168 | printf("DWMMC%d: Can't get the dev index\n", host->dev_index); | |
169 | return -EINVAL; | |
170 | } | |
171 | ||
70f6d394 JC |
172 | /* Get the bus width from the device node (Default is 4bit buswidth) */ |
173 | host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4); | |
a082a2dd | 174 | |
959198f7 JC |
175 | /* Set the base address from the device node */ |
176 | base = fdtdec_get_addr(blob, node, "reg"); | |
177 | if (!base) { | |
dfcb683a | 178 | printf("DWMMC%d: Can't get base address\n", host->dev_index); |
959198f7 JC |
179 | return -EINVAL; |
180 | } | |
181 | host->ioaddr = (void *)base; | |
182 | ||
183 | /* Extract the timing info from the node */ | |
184 | err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3); | |
185 | if (err) { | |
dfcb683a JC |
186 | printf("DWMMC%d: Can't get sdr-timings for devider\n", |
187 | host->dev_index); | |
959198f7 JC |
188 | return -EINVAL; |
189 | } | |
190 | ||
5dab81ce | 191 | priv->sdr_timing = (DWMCI_SET_SAMPLE_CLK(timing[0]) | |
959198f7 JC |
192 | DWMCI_SET_DRV_CLK(timing[1]) | |
193 | DWMCI_SET_DIV_RATIO(timing[2])); | |
5dab81ce JC |
194 | |
195 | /* sdr_timing didn't assigned anything, use the default value */ | |
196 | if (!priv->sdr_timing) { | |
197 | if (host->dev_index == 0) | |
198 | priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL; | |
199 | else if (host->dev_index == 2) | |
200 | priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL; | |
201 | } | |
959198f7 JC |
202 | |
203 | host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0); | |
204 | host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0); | |
205 | host->div = fdtdec_get_int(blob, node, "div", 0); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
3537ee87 JC |
210 | #ifdef CONFIG_DM_MMC |
211 | static int exynos_dwmmc_probe(struct udevice *dev) | |
212 | { | |
c69cda25 | 213 | struct exynos_mmc_plat *plat = dev_get_plat(dev); |
3537ee87 JC |
214 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
215 | struct dwmci_exynos_priv_data *priv = dev_get_priv(dev); | |
216 | struct dwmci_host *host = &priv->host; | |
217 | int err; | |
218 | ||
b88c1efa LM |
219 | err = exynos_dwmci_get_config(gd->fdt_blob, dev_of_offset(dev), host, |
220 | priv); | |
3537ee87 JC |
221 | if (err) |
222 | return err; | |
223 | err = do_dwmci_init(host); | |
224 | if (err) | |
225 | return err; | |
226 | ||
e5113c33 | 227 | dwmci_setup_cfg(&plat->cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ); |
3537ee87 JC |
228 | host->mmc = &plat->mmc; |
229 | host->mmc->priv = &priv->host; | |
230 | host->priv = dev; | |
231 | upriv->mmc = host->mmc; | |
232 | ||
233 | return dwmci_probe(dev); | |
234 | } | |
235 | ||
236 | static int exynos_dwmmc_bind(struct udevice *dev) | |
237 | { | |
c69cda25 | 238 | struct exynos_mmc_plat *plat = dev_get_plat(dev); |
3537ee87 | 239 | |
24f5aec3 | 240 | return dwmci_bind(dev, &plat->mmc, &plat->cfg); |
3537ee87 JC |
241 | } |
242 | ||
243 | static const struct udevice_id exynos_dwmmc_ids[] = { | |
244 | { .compatible = "samsung,exynos4412-dw-mshc" }, | |
0acdb2cc | 245 | { .compatible = "samsung,exynos-dwmmc" }, |
3537ee87 JC |
246 | { } |
247 | }; | |
248 | ||
249 | U_BOOT_DRIVER(exynos_dwmmc_drv) = { | |
250 | .name = "exynos_dwmmc", | |
251 | .id = UCLASS_MMC, | |
252 | .of_match = exynos_dwmmc_ids, | |
253 | .bind = exynos_dwmmc_bind, | |
254 | .ops = &dm_dwmci_ops, | |
255 | .probe = exynos_dwmmc_probe, | |
41575d8e | 256 | .priv_auto = sizeof(struct dwmci_exynos_priv_data), |
caa4daa2 | 257 | .plat_auto = sizeof(struct exynos_mmc_plat), |
3537ee87 JC |
258 | }; |
259 | #endif |