]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
293eb33f | 2 | /* |
d9ae52c8 | 3 | * (C) Copyright 2013 - 2015 Xilinx, Inc. |
293eb33f MS |
4 | * |
5 | * Xilinx Zynq SD Host Controller Interface | |
293eb33f MS |
6 | */ |
7 | ||
e0f4de1a | 8 | #include <clk.h> |
293eb33f | 9 | #include <common.h> |
d9ae52c8 | 10 | #include <dm.h> |
345d3c0f | 11 | #include <fdtdec.h> |
d1f4e39d | 12 | #include "mmc_private.h" |
b08c8c48 | 13 | #include <linux/libfdt.h> |
293eb33f MS |
14 | #include <malloc.h> |
15 | #include <sdhci.h> | |
d1f4e39d | 16 | #include <zynqmp_tap_delay.h> |
293eb33f | 17 | |
61e745d1 SH |
18 | DECLARE_GLOBAL_DATA_PTR; |
19 | ||
329a449f SG |
20 | struct arasan_sdhci_plat { |
21 | struct mmc_config cfg; | |
22 | struct mmc mmc; | |
61e745d1 | 23 | unsigned int f_max; |
329a449f SG |
24 | }; |
25 | ||
d1f4e39d SDPP |
26 | struct arasan_sdhci_priv { |
27 | struct sdhci_host *host; | |
28 | u8 deviceid; | |
29 | u8 bank; | |
30 | u8 no_1p8; | |
31 | bool pwrseq; | |
32 | }; | |
33 | ||
34 | #if defined(CONFIG_ARCH_ZYNQMP) | |
35 | static const u8 mode2timing[] = { | |
36 | [UHS_SDR12] = UHS_SDR12_BUS_SPEED, | |
37 | [UHS_SDR25] = UHS_SDR25_BUS_SPEED, | |
38 | [UHS_SDR50] = UHS_SDR50_BUS_SPEED, | |
39 | [UHS_SDR104] = UHS_SDR104_BUS_SPEED, | |
40 | [UHS_DDR50] = UHS_DDR50_BUS_SPEED, | |
41 | }; | |
42 | ||
43 | #define SDHCI_HOST_CTRL2 0x3E | |
44 | #define SDHCI_CTRL2_MODE_MASK 0x7 | |
45 | #define SDHCI_18V_SIGNAL 0x8 | |
46 | #define SDHCI_CTRL_EXEC_TUNING 0x0040 | |
47 | #define SDHCI_CTRL_TUNED_CLK 0x80 | |
48 | #define SDHCI_TUNING_LOOP_COUNT 40 | |
49 | ||
50 | static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 deviceid) | |
51 | { | |
52 | u16 clk; | |
53 | unsigned long timeout; | |
54 | ||
55 | clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); | |
56 | clk &= ~(SDHCI_CLOCK_CARD_EN); | |
57 | sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); | |
58 | ||
59 | /* Issue DLL Reset */ | |
60 | zynqmp_dll_reset(deviceid); | |
61 | ||
62 | /* Wait max 20 ms */ | |
63 | timeout = 100; | |
64 | while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) | |
65 | & SDHCI_CLOCK_INT_STABLE)) { | |
66 | if (timeout == 0) { | |
67 | dev_err(mmc_dev(host->mmc), | |
68 | ": Internal clock never stabilised.\n"); | |
69 | return; | |
70 | } | |
71 | timeout--; | |
72 | udelay(1000); | |
73 | } | |
74 | ||
75 | clk |= SDHCI_CLOCK_CARD_EN; | |
76 | sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); | |
77 | } | |
78 | ||
79 | static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) | |
80 | { | |
81 | struct mmc_cmd cmd; | |
82 | struct mmc_data data; | |
83 | u32 ctrl; | |
84 | struct sdhci_host *host; | |
85 | struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev); | |
86 | u8 tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT; | |
87 | u8 deviceid; | |
88 | ||
89 | debug("%s\n", __func__); | |
90 | ||
91 | host = priv->host; | |
92 | deviceid = priv->deviceid; | |
93 | ||
94 | ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); | |
95 | ctrl |= SDHCI_CTRL_EXEC_TUNING; | |
96 | sdhci_writew(host, ctrl, SDHCI_HOST_CTRL2); | |
97 | ||
98 | mdelay(1); | |
99 | ||
100 | arasan_zynqmp_dll_reset(host, deviceid); | |
101 | ||
102 | sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); | |
103 | sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); | |
104 | ||
105 | do { | |
106 | cmd.cmdidx = opcode; | |
107 | cmd.resp_type = MMC_RSP_R1; | |
108 | cmd.cmdarg = 0; | |
109 | ||
110 | data.blocksize = 64; | |
111 | data.blocks = 1; | |
112 | data.flags = MMC_DATA_READ; | |
113 | ||
114 | if (tuning_loop_counter-- == 0) | |
115 | break; | |
116 | ||
117 | if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 && | |
118 | mmc->bus_width == 8) | |
119 | data.blocksize = 128; | |
120 | ||
121 | sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, | |
122 | data.blocksize), | |
123 | SDHCI_BLOCK_SIZE); | |
124 | sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT); | |
125 | sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); | |
126 | ||
127 | mmc_send_cmd(mmc, &cmd, NULL); | |
128 | ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); | |
129 | ||
130 | if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK) | |
131 | udelay(1); | |
132 | ||
133 | } while (ctrl & SDHCI_CTRL_EXEC_TUNING); | |
134 | ||
135 | if (tuning_loop_counter < 0) { | |
136 | ctrl &= ~SDHCI_CTRL_TUNED_CLK; | |
137 | sdhci_writel(host, ctrl, SDHCI_HOST_CTRL2); | |
138 | } | |
139 | ||
140 | if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { | |
141 | printf("%s:Tuning failed\n", __func__); | |
142 | return -1; | |
143 | } | |
144 | ||
145 | udelay(1); | |
146 | arasan_zynqmp_dll_reset(host, deviceid); | |
147 | ||
148 | /* Enable only interrupts served by the SD controller */ | |
149 | sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, | |
150 | SDHCI_INT_ENABLE); | |
151 | /* Mask all sdhci interrupt sources */ | |
152 | sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | static void arasan_sdhci_set_tapdelay(struct sdhci_host *host) | |
158 | { | |
159 | struct arasan_sdhci_priv *priv = dev_get_priv(host->mmc->dev); | |
160 | struct mmc *mmc = (struct mmc *)host->mmc; | |
161 | u8 uhsmode; | |
162 | ||
163 | if (!IS_SD(mmc)) | |
164 | return; | |
165 | ||
166 | uhsmode = mode2timing[mmc->selected_mode]; | |
167 | ||
168 | if (uhsmode >= UHS_SDR25_BUS_SPEED) | |
169 | arasan_zynqmp_set_tapdelay(priv->deviceid, uhsmode, | |
170 | priv->bank); | |
171 | } | |
172 | ||
173 | static void arasan_sdhci_set_control_reg(struct sdhci_host *host) | |
174 | { | |
175 | struct mmc *mmc = (struct mmc *)host->mmc; | |
176 | u32 reg; | |
177 | ||
178 | if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { | |
179 | reg = sdhci_readw(host, SDHCI_HOST_CTRL2); | |
180 | reg |= SDHCI_18V_SIGNAL; | |
181 | sdhci_writew(host, reg, SDHCI_HOST_CTRL2); | |
182 | } | |
183 | ||
184 | if (mmc->selected_mode > SD_HS && | |
185 | mmc->selected_mode <= UHS_DDR50) { | |
186 | reg = sdhci_readw(host, SDHCI_HOST_CTRL2); | |
187 | reg &= ~SDHCI_CTRL2_MODE_MASK; | |
188 | switch (mmc->selected_mode) { | |
189 | case UHS_SDR12: | |
190 | reg |= UHS_SDR12_BUS_SPEED; | |
191 | break; | |
192 | case UHS_SDR25: | |
193 | reg |= UHS_SDR25_BUS_SPEED; | |
194 | break; | |
195 | case UHS_SDR50: | |
196 | reg |= UHS_SDR50_BUS_SPEED; | |
197 | break; | |
198 | case UHS_SDR104: | |
199 | reg |= UHS_SDR104_BUS_SPEED; | |
200 | break; | |
201 | case UHS_DDR50: | |
202 | reg |= UHS_DDR50_BUS_SPEED; | |
203 | break; | |
204 | default: | |
205 | break; | |
206 | } | |
207 | sdhci_writew(host, reg, SDHCI_HOST_CTRL2); | |
208 | } | |
209 | } | |
210 | #endif | |
211 | ||
212 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) | |
213 | const struct sdhci_ops arasan_ops = { | |
214 | .platform_execute_tuning = &arasan_sdhci_execute_tuning, | |
215 | .set_delay = &arasan_sdhci_set_tapdelay, | |
216 | .set_control_reg = &arasan_sdhci_set_control_reg, | |
217 | }; | |
218 | #endif | |
219 | ||
d9ae52c8 | 220 | static int arasan_sdhci_probe(struct udevice *dev) |
293eb33f | 221 | { |
329a449f | 222 | struct arasan_sdhci_plat *plat = dev_get_platdata(dev); |
d9ae52c8 | 223 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
d1f4e39d SDPP |
224 | struct arasan_sdhci_priv *priv = dev_get_priv(dev); |
225 | struct sdhci_host *host; | |
e0f4de1a SH |
226 | struct clk clk; |
227 | unsigned long clock; | |
329a449f | 228 | int ret; |
293eb33f | 229 | |
d1f4e39d SDPP |
230 | host = priv->host; |
231 | ||
e0f4de1a SH |
232 | ret = clk_get_by_index(dev, 0, &clk); |
233 | if (ret < 0) { | |
234 | dev_err(dev, "failed to get clock\n"); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | clock = clk_get_rate(&clk); | |
239 | if (IS_ERR_VALUE(clock)) { | |
240 | dev_err(dev, "failed to get rate\n"); | |
241 | return clock; | |
242 | } | |
d1f4e39d | 243 | |
e0f4de1a SH |
244 | debug("%s: CLK %ld\n", __func__, clock); |
245 | ||
246 | ret = clk_enable(&clk); | |
247 | if (ret && ret != -ENOSYS) { | |
248 | dev_err(dev, "failed to enable clock\n"); | |
249 | return ret; | |
250 | } | |
251 | ||
eddabd16 | 252 | host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | |
f9ec45d1 | 253 | SDHCI_QUIRK_BROKEN_R1B; |
b2156146 SDPP |
254 | |
255 | #ifdef CONFIG_ZYNQ_HISPD_BROKEN | |
47819216 | 256 | host->quirks |= SDHCI_QUIRK_BROKEN_HISPD_MODE; |
b2156146 SDPP |
257 | #endif |
258 | ||
d1f4e39d SDPP |
259 | if (priv->no_1p8) |
260 | host->quirks |= SDHCI_QUIRK_NO_1_8_V; | |
261 | ||
e0f4de1a | 262 | host->max_clk = clock; |
6d0e34bf | 263 | |
61e745d1 | 264 | ret = sdhci_setup_cfg(&plat->cfg, host, plat->f_max, |
14bed52d | 265 | CONFIG_ZYNQ_SDHCI_MIN_FREQ); |
329a449f SG |
266 | host->mmc = &plat->mmc; |
267 | if (ret) | |
268 | return ret; | |
269 | host->mmc->priv = host; | |
cffe5d86 | 270 | host->mmc->dev = dev; |
329a449f | 271 | upriv->mmc = host->mmc; |
d9ae52c8 | 272 | |
329a449f | 273 | return sdhci_probe(dev); |
293eb33f | 274 | } |
d9ae52c8 MS |
275 | |
276 | static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev) | |
277 | { | |
61e745d1 | 278 | struct arasan_sdhci_plat *plat = dev_get_platdata(dev); |
d1f4e39d SDPP |
279 | struct arasan_sdhci_priv *priv = dev_get_priv(dev); |
280 | ||
281 | priv->host = calloc(1, sizeof(struct sdhci_host)); | |
282 | if (!priv->host) | |
283 | return -1; | |
d9ae52c8 | 284 | |
d1f4e39d SDPP |
285 | priv->host->name = dev->name; |
286 | priv->host->ioaddr = (void *)devfdt_get_addr(dev); | |
287 | ||
288 | priv->deviceid = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | |
289 | "xlnx,device_id", -1); | |
290 | priv->bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | |
291 | "xlnx,mio_bank", -1); | |
292 | if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), | |
293 | "no-1-8-v", NULL)) | |
294 | priv->no_1p8 = 1; | |
295 | else | |
296 | priv->no_1p8 = 0; | |
297 | ||
298 | #if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) | |
299 | priv->host->ops = &arasan_ops; | |
300 | #endif | |
d9ae52c8 | 301 | |
da409ccc | 302 | plat->f_max = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), |
61e745d1 SH |
303 | "max-frequency", CONFIG_ZYNQ_SDHCI_MAX_FREQ); |
304 | ||
d9ae52c8 MS |
305 | return 0; |
306 | } | |
307 | ||
329a449f SG |
308 | static int arasan_sdhci_bind(struct udevice *dev) |
309 | { | |
310 | struct arasan_sdhci_plat *plat = dev_get_platdata(dev); | |
329a449f | 311 | |
24f5aec3 | 312 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
329a449f SG |
313 | } |
314 | ||
d9ae52c8 MS |
315 | static const struct udevice_id arasan_sdhci_ids[] = { |
316 | { .compatible = "arasan,sdhci-8.9a" }, | |
317 | { } | |
318 | }; | |
319 | ||
320 | U_BOOT_DRIVER(arasan_sdhci_drv) = { | |
321 | .name = "arasan_sdhci", | |
322 | .id = UCLASS_MMC, | |
323 | .of_match = arasan_sdhci_ids, | |
324 | .ofdata_to_platdata = arasan_sdhci_ofdata_to_platdata, | |
329a449f SG |
325 | .ops = &sdhci_ops, |
326 | .bind = arasan_sdhci_bind, | |
d9ae52c8 | 327 | .probe = arasan_sdhci_probe, |
d1f4e39d | 328 | .priv_auto_alloc_size = sizeof(struct arasan_sdhci_priv), |
329a449f | 329 | .platdata_auto_alloc_size = sizeof(struct arasan_sdhci_plat), |
d9ae52c8 | 330 | }; |