]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
e94cad93 MV |
2 | /* |
3 | * Copyright (C) 2018 Marek Vasut <[email protected]> | |
e94cad93 MV |
4 | */ |
5 | ||
6 | #include <common.h> | |
d2661d8e | 7 | #include <bouncebuf.h> |
e94cad93 MV |
8 | #include <clk.h> |
9 | #include <fdtdec.h> | |
f7ae49fc | 10 | #include <log.h> |
336d4615 | 11 | #include <malloc.h> |
e94cad93 MV |
12 | #include <mmc.h> |
13 | #include <dm.h> | |
336d4615 | 14 | #include <dm/device_compat.h> |
e94cad93 MV |
15 | #include <linux/compat.h> |
16 | #include <linux/dma-direction.h> | |
17 | #include <linux/io.h> | |
18 | #include <linux/sizes.h> | |
19 | #include <power/regulator.h> | |
20 | #include <asm/unaligned.h> | |
21 | ||
cb0b6b03 | 22 | #include "tmio-common.h" |
e94cad93 | 23 | |
50aa1d99 MV |
24 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ |
25 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
26 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
f63968ba MV |
27 | |
28 | /* SCC registers */ | |
29 | #define RENESAS_SDHI_SCC_DTCNTL 0x800 | |
1bac2b6b MV |
30 | #define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) |
31 | #define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 | |
32 | #define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff | |
f63968ba MV |
33 | #define RENESAS_SDHI_SCC_TAPSET 0x804 |
34 | #define RENESAS_SDHI_SCC_DT2FF 0x808 | |
35 | #define RENESAS_SDHI_SCC_CKSEL 0x80c | |
1bac2b6b MV |
36 | #define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) |
37 | #define RENESAS_SDHI_SCC_RVSCNTL 0x810 | |
38 | #define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) | |
f63968ba | 39 | #define RENESAS_SDHI_SCC_RVSREQ 0x814 |
1bac2b6b | 40 | #define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) |
6900066c MV |
41 | #define RENESAS_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) |
42 | #define RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN BIT(0) | |
f63968ba | 43 | #define RENESAS_SDHI_SCC_SMPCMP 0x818 |
6900066c MV |
44 | #define RENESAS_SDHI_SCC_SMPCMP_CMD_ERR (BIT(24) | BIT(8)) |
45 | #define RENESAS_SDHI_SCC_SMPCMP_CMD_REQUP BIT(24) | |
46 | #define RENESAS_SDHI_SCC_SMPCMP_CMD_REQDOWN BIT(8) | |
1bac2b6b MV |
47 | #define RENESAS_SDHI_SCC_TMPPORT2 0x81c |
48 | #define RENESAS_SDHI_SCC_TMPPORT2_HS400EN BIT(31) | |
49 | #define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) | |
b5900a58 MV |
50 | #define RENESAS_SDHI_SCC_TMPPORT3 0x828 |
51 | #define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_0 3 | |
52 | #define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_1 2 | |
53 | #define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_2 1 | |
54 | #define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_3 0 | |
55 | #define RENESAS_SDHI_SCC_TMPPORT3_OFFSET_MASK 0x3 | |
56 | #define RENESAS_SDHI_SCC_TMPPORT4 0x82c | |
57 | #define RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0) | |
58 | #define RENESAS_SDHI_SCC_TMPPORT5 0x830 | |
59 | #define RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R BIT(8) | |
60 | #define RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W (0 << 8) | |
61 | #define RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK 0x3F | |
62 | #define RENESAS_SDHI_SCC_TMPPORT6 0x834 | |
63 | #define RENESAS_SDHI_SCC_TMPPORT7 0x838 | |
64 | #define RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE 0xa5000000 | |
65 | #define RENESAS_SDHI_SCC_TMPPORT_CALIB_CODE_MASK 0x1f | |
66 | #define RENESAS_SDHI_SCC_TMPPORT_MANUAL_MODE BIT(7) | |
f63968ba MV |
67 | |
68 | #define RENESAS_SDHI_MAX_TAP 3 | |
69 | ||
56b0bb96 MV |
70 | #define CALIB_TABLE_MAX (RENESAS_SDHI_SCC_TMPPORT_CALIB_CODE_MASK + 1) |
71 | ||
72 | static const u8 r8a7795_calib_table[2][CALIB_TABLE_MAX] = { | |
73 | { 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 5, 6, 6, 7, 11, | |
74 | 15, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 19, 20, 21, 21 }, | |
75 | { 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 12, 15, | |
76 | 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 19, 20, 21, 22, 22 } | |
77 | }; | |
78 | ||
79 | static const u8 r8a7796_rev1_calib_table[2][CALIB_TABLE_MAX] = { | |
80 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 9, | |
81 | 15, 15, 15, 16, 16, 16, 16, 16, 17, 18, 19, 20, 21, 21, 22, 22 }, | |
82 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, | |
83 | 2, 9, 16, 17, 17, 17, 18, 18, 18, 18, 19, 20, 21, 22, 23, 24} | |
84 | }; | |
85 | ||
86 | static const u8 r8a7796_rev3_calib_table[2][CALIB_TABLE_MAX] = { | |
87 | { 0, 0, 0, 0, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 9, 10, | |
88 | 11, 12, 13, 15, 16, 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23 }, | |
89 | { 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 12, | |
90 | 13, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, 24, 24 } | |
91 | }; | |
92 | ||
93 | static const u8 r8a77965_calib_table[2][CALIB_TABLE_MAX] = { | |
94 | { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, | |
95 | 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 29 }, | |
96 | { 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 15, | |
97 | 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 31 } | |
98 | }; | |
99 | ||
100 | static const u8 r8a77990_calib_table[2][CALIB_TABLE_MAX] = { | |
101 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
102 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | |
103 | { 0, 0, 1, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7, 8, 10, 11, | |
104 | 12, 13, 14, 16, 17, 18, 18, 18, 19, 19, 20, 24, 26, 26, 26, 26 } | |
105 | }; | |
106 | ||
107 | static int rmobile_is_gen3_mmc0(struct tmio_sd_priv *priv) | |
108 | { | |
109 | /* On R-Car Gen3, MMC0 is at 0xee140000 */ | |
110 | return (uintptr_t)(priv->regbase) == 0xee140000; | |
111 | } | |
112 | ||
b5900a58 MV |
113 | static u32 sd_scc_tmpport_read32(struct tmio_sd_priv *priv, u32 addr) |
114 | { | |
115 | /* read mode */ | |
116 | tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R | | |
117 | (RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr), | |
118 | RENESAS_SDHI_SCC_TMPPORT5); | |
119 | ||
120 | /* access start and stop */ | |
121 | tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START, | |
122 | RENESAS_SDHI_SCC_TMPPORT4); | |
123 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT4); | |
124 | ||
125 | return tmio_sd_readl(priv, RENESAS_SDHI_SCC_TMPPORT7); | |
126 | } | |
127 | ||
128 | static void sd_scc_tmpport_write32(struct tmio_sd_priv *priv, u32 addr, u32 val) | |
129 | { | |
130 | /* write mode */ | |
131 | tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W | | |
132 | (RENESAS_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr), | |
133 | RENESAS_SDHI_SCC_TMPPORT5); | |
134 | tmio_sd_writel(priv, val, RENESAS_SDHI_SCC_TMPPORT6); | |
135 | ||
136 | /* access start and stop */ | |
137 | tmio_sd_writel(priv, RENESAS_SDHI_SCC_TMPPORT4_DLL_ACC_START, | |
138 | RENESAS_SDHI_SCC_TMPPORT4); | |
139 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT4); | |
140 | } | |
141 | ||
6900066c MV |
142 | static bool renesas_sdhi_check_scc_error(struct udevice *dev) |
143 | { | |
144 | struct tmio_sd_priv *priv = dev_get_priv(dev); | |
145 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
146 | unsigned long new_tap = priv->tap_set; | |
1bdcb83d | 147 | unsigned long error_tap = priv->tap_set; |
6900066c MV |
148 | u32 reg, smpcmp; |
149 | ||
150 | if ((priv->caps & TMIO_SD_CAP_RCAR_UHS) && | |
151 | (mmc->selected_mode != UHS_SDR104) && | |
152 | (mmc->selected_mode != MMC_HS_200) && | |
153 | (mmc->selected_mode != MMC_HS_400) && | |
154 | (priv->nrtaps != 4)) | |
155 | return false; | |
156 | ||
157 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); | |
158 | /* Handle automatic tuning correction */ | |
159 | if (reg & RENESAS_SDHI_SCC_RVSCNTL_RVSEN) { | |
160 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSREQ); | |
161 | if (reg & RENESAS_SDHI_SCC_RVSREQ_RVSERR) { | |
162 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); | |
163 | return true; | |
164 | } | |
165 | ||
166 | return false; | |
167 | } | |
168 | ||
169 | /* Handle manual tuning correction */ | |
170 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSREQ); | |
171 | if (!reg) /* No error */ | |
172 | return false; | |
173 | ||
174 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); | |
175 | ||
176 | if (mmc->selected_mode == MMC_HS_400) { | |
177 | /* | |
178 | * Correction Error Status contains CMD and DAT signal status. | |
179 | * In HS400, DAT signal based on DS signal, not CLK. | |
180 | * Therefore, use only CMD status. | |
181 | */ | |
182 | smpcmp = tmio_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP) & | |
183 | RENESAS_SDHI_SCC_SMPCMP_CMD_ERR; | |
184 | ||
185 | switch (smpcmp) { | |
186 | case 0: | |
187 | return false; /* No error in CMD signal */ | |
188 | case RENESAS_SDHI_SCC_SMPCMP_CMD_REQUP: | |
189 | new_tap = (priv->tap_set + | |
190 | priv->tap_num + 1) % priv->tap_num; | |
1bdcb83d MV |
191 | error_tap = (priv->tap_set + |
192 | priv->tap_num - 1) % priv->tap_num; | |
6900066c MV |
193 | break; |
194 | case RENESAS_SDHI_SCC_SMPCMP_CMD_REQDOWN: | |
195 | new_tap = (priv->tap_set + | |
196 | priv->tap_num - 1) % priv->tap_num; | |
1bdcb83d MV |
197 | error_tap = (priv->tap_set + |
198 | priv->tap_num + 1) % priv->tap_num; | |
6900066c MV |
199 | break; |
200 | default: | |
201 | return true; /* Need re-tune */ | |
202 | } | |
203 | ||
1bdcb83d MV |
204 | if (priv->hs400_bad_tap & BIT(new_tap)) { |
205 | /* | |
206 | * New tap is bad tap (cannot change). | |
207 | * Compare with HS200 tuning result. | |
208 | * In HS200 tuning, when smpcmp[error_tap] | |
209 | * is OK, retune is executed. | |
210 | */ | |
211 | if (priv->smpcmp & BIT(error_tap)) | |
212 | return true; /* Need retune */ | |
213 | ||
214 | return false; /* cannot change */ | |
215 | } | |
216 | ||
6900066c MV |
217 | priv->tap_set = new_tap; |
218 | } else { | |
219 | if (reg & RENESAS_SDHI_SCC_RVSREQ_RVSERR) | |
220 | return true; /* Need re-tune */ | |
221 | else if (reg & RENESAS_SDHI_SCC_RVSREQ_REQTAPUP) | |
222 | priv->tap_set = (priv->tap_set + | |
223 | priv->tap_num + 1) % priv->tap_num; | |
224 | else if (reg & RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN) | |
225 | priv->tap_set = (priv->tap_set + | |
226 | priv->tap_num - 1) % priv->tap_num; | |
227 | else | |
228 | return false; | |
229 | } | |
230 | ||
231 | /* Set TAP position */ | |
232 | tmio_sd_writel(priv, priv->tap_set >> ((priv->nrtaps == 4) ? 1 : 0), | |
233 | RENESAS_SDHI_SCC_TAPSET); | |
234 | ||
235 | return false; | |
236 | } | |
237 | ||
b5900a58 MV |
238 | static void renesas_sdhi_adjust_hs400_mode_enable(struct tmio_sd_priv *priv) |
239 | { | |
240 | u32 calib_code; | |
241 | ||
242 | if (!priv->adjust_hs400_enable) | |
243 | return; | |
244 | ||
245 | if (!priv->needs_adjust_hs400) | |
246 | return; | |
247 | ||
56b0bb96 MV |
248 | if (!priv->adjust_hs400_calib_table) |
249 | return; | |
250 | ||
b5900a58 MV |
251 | /* |
252 | * Enabled Manual adjust HS400 mode | |
253 | * | |
254 | * 1) Disabled Write Protect | |
255 | * W(addr=0x00, WP_DISABLE_CODE) | |
56b0bb96 MV |
256 | * |
257 | * 2) Read Calibration code | |
258 | * read_value = R(addr=0x26) | |
259 | * 3) Refer to calibration table | |
260 | * Calibration code = table[read_value] | |
261 | * 4) Enabled Manual Calibration | |
b5900a58 | 262 | * W(addr=0x22, manual mode | Calibration code) |
56b0bb96 | 263 | * 5) Set Offset value to TMPPORT3 Reg |
b5900a58 MV |
264 | */ |
265 | sd_scc_tmpport_write32(priv, 0x00, | |
266 | RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE); | |
267 | calib_code = sd_scc_tmpport_read32(priv, 0x26); | |
268 | calib_code &= RENESAS_SDHI_SCC_TMPPORT_CALIB_CODE_MASK; | |
b5900a58 MV |
269 | sd_scc_tmpport_write32(priv, 0x22, |
270 | RENESAS_SDHI_SCC_TMPPORT_MANUAL_MODE | | |
56b0bb96 | 271 | priv->adjust_hs400_calib_table[calib_code]); |
b5900a58 MV |
272 | tmio_sd_writel(priv, priv->adjust_hs400_offset, |
273 | RENESAS_SDHI_SCC_TMPPORT3); | |
274 | ||
275 | /* Clear flag */ | |
276 | priv->needs_adjust_hs400 = false; | |
277 | } | |
278 | ||
279 | static void renesas_sdhi_adjust_hs400_mode_disable(struct tmio_sd_priv *priv) | |
280 | { | |
281 | ||
282 | /* Disabled Manual adjust HS400 mode | |
283 | * | |
284 | * 1) Disabled Write Protect | |
285 | * W(addr=0x00, WP_DISABLE_CODE) | |
286 | * 2) Disabled Manual Calibration | |
287 | * W(addr=0x22, 0) | |
288 | * 3) Clear offset value to TMPPORT3 Reg | |
289 | */ | |
290 | sd_scc_tmpport_write32(priv, 0x00, | |
291 | RENESAS_SDHI_SCC_TMPPORT_DISABLE_WP_CODE); | |
292 | sd_scc_tmpport_write32(priv, 0x22, 0); | |
293 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT3); | |
294 | } | |
295 | ||
cb0b6b03 | 296 | static unsigned int renesas_sdhi_init_tuning(struct tmio_sd_priv *priv) |
f63968ba MV |
297 | { |
298 | u32 reg; | |
299 | ||
300 | /* Initialize SCC */ | |
cb0b6b03 | 301 | tmio_sd_writel(priv, 0, TMIO_SD_INFO1); |
f63968ba | 302 | |
cb0b6b03 MV |
303 | reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); |
304 | reg &= ~TMIO_SD_CLKCTL_SCLKEN; | |
305 | tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); | |
f63968ba MV |
306 | |
307 | /* Set sampling clock selection range */ | |
a376dde1 MV |
308 | tmio_sd_writel(priv, (0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) | |
309 | RENESAS_SDHI_SCC_DTCNTL_TAPEN, | |
310 | RENESAS_SDHI_SCC_DTCNTL); | |
f63968ba | 311 | |
cb0b6b03 | 312 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); |
f63968ba | 313 | reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; |
cb0b6b03 | 314 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); |
f63968ba | 315 | |
cb0b6b03 | 316 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 317 | reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; |
cb0b6b03 | 318 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 319 | |
cb0b6b03 | 320 | tmio_sd_writel(priv, 0x300 /* scc_tappos */, |
f63968ba MV |
321 | RENESAS_SDHI_SCC_DT2FF); |
322 | ||
cb0b6b03 MV |
323 | reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); |
324 | reg |= TMIO_SD_CLKCTL_SCLKEN; | |
325 | tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); | |
f63968ba MV |
326 | |
327 | /* Read TAPNUM */ | |
cb0b6b03 | 328 | return (tmio_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL) >> |
f63968ba MV |
329 | RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & |
330 | RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK; | |
331 | } | |
332 | ||
cb0b6b03 | 333 | static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) |
f63968ba MV |
334 | { |
335 | u32 reg; | |
336 | ||
337 | /* Reset SCC */ | |
cb0b6b03 MV |
338 | reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); |
339 | reg &= ~TMIO_SD_CLKCTL_SCLKEN; | |
340 | tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); | |
f63968ba | 341 | |
cb0b6b03 | 342 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); |
f63968ba | 343 | reg &= ~RENESAS_SDHI_SCC_CKSEL_DTSEL; |
cb0b6b03 | 344 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); |
f63968ba | 345 | |
dc1488f1 MV |
346 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_TMPPORT2); |
347 | reg &= ~(RENESAS_SDHI_SCC_TMPPORT2_HS400EN | | |
348 | RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); | |
349 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); | |
350 | ||
b5900a58 MV |
351 | /* Disable HS400 mode adjustment */ |
352 | renesas_sdhi_adjust_hs400_mode_disable(priv); | |
353 | ||
cb0b6b03 MV |
354 | reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); |
355 | reg |= TMIO_SD_CLKCTL_SCLKEN; | |
356 | tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); | |
f63968ba | 357 | |
cb0b6b03 | 358 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 359 | reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; |
cb0b6b03 | 360 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 361 | |
cb0b6b03 | 362 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 363 | reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; |
cb0b6b03 | 364 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba MV |
365 | } |
366 | ||
50aa1d99 MV |
367 | static int renesas_sdhi_hs400(struct udevice *dev) |
368 | { | |
369 | struct tmio_sd_priv *priv = dev_get_priv(dev); | |
370 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
371 | bool hs400 = (mmc->selected_mode == MMC_HS_400); | |
372 | int ret, taps = hs400 ? priv->nrtaps : 8; | |
1bdcb83d | 373 | unsigned long new_tap; |
50aa1d99 MV |
374 | u32 reg; |
375 | ||
376 | if (taps == 4) /* HS400 on 4tap SoC needs different clock */ | |
377 | ret = clk_set_rate(&priv->clk, 400000000); | |
378 | else | |
379 | ret = clk_set_rate(&priv->clk, 200000000); | |
380 | if (ret < 0) | |
381 | return ret; | |
382 | ||
8f39b030 MV |
383 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); |
384 | reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; | |
385 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); | |
50aa1d99 MV |
386 | |
387 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_TMPPORT2); | |
388 | if (hs400) { | |
389 | reg |= RENESAS_SDHI_SCC_TMPPORT2_HS400EN | | |
390 | RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL; | |
391 | } else { | |
392 | reg &= ~(RENESAS_SDHI_SCC_TMPPORT2_HS400EN | | |
393 | RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); | |
394 | } | |
395 | ||
396 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); | |
397 | ||
b5900a58 MV |
398 | /* Disable HS400 mode adjustment */ |
399 | if (!hs400) | |
400 | renesas_sdhi_adjust_hs400_mode_disable(priv); | |
401 | ||
ba41c45e | 402 | tmio_sd_writel(priv, (0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) | |
50aa1d99 MV |
403 | RENESAS_SDHI_SCC_DTCNTL_TAPEN, |
404 | RENESAS_SDHI_SCC_DTCNTL); | |
405 | ||
1bdcb83d MV |
406 | /* Avoid bad TAP */ |
407 | if (priv->hs400_bad_tap & BIT(priv->tap_set)) { | |
408 | new_tap = (priv->tap_set + | |
409 | priv->tap_num + 1) % priv->tap_num; | |
410 | ||
411 | if (priv->hs400_bad_tap & BIT(new_tap)) | |
412 | new_tap = (priv->tap_set + | |
413 | priv->tap_num - 1) % priv->tap_num; | |
414 | ||
415 | if (priv->hs400_bad_tap & BIT(new_tap)) { | |
416 | new_tap = priv->tap_set; | |
417 | debug("Three consecutive bad tap is prohibited\n"); | |
418 | } | |
419 | ||
420 | priv->tap_set = new_tap; | |
421 | tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); | |
422 | } | |
423 | ||
50aa1d99 MV |
424 | if (taps == 4) { |
425 | tmio_sd_writel(priv, priv->tap_set >> 1, | |
426 | RENESAS_SDHI_SCC_TAPSET); | |
dc419fc6 MV |
427 | tmio_sd_writel(priv, hs400 ? 0x100 : 0x300, |
428 | RENESAS_SDHI_SCC_DT2FF); | |
50aa1d99 MV |
429 | } else { |
430 | tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); | |
dc419fc6 | 431 | tmio_sd_writel(priv, 0x300, RENESAS_SDHI_SCC_DT2FF); |
50aa1d99 MV |
432 | } |
433 | ||
434 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); | |
435 | reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; | |
436 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); | |
437 | ||
b5900a58 MV |
438 | /* Execute adjust hs400 offset after setting to HS400 mode */ |
439 | if (hs400) | |
440 | priv->needs_adjust_hs400 = true; | |
441 | ||
50aa1d99 MV |
442 | return 0; |
443 | } | |
444 | ||
cb0b6b03 | 445 | static void renesas_sdhi_prepare_tuning(struct tmio_sd_priv *priv, |
f63968ba MV |
446 | unsigned long tap) |
447 | { | |
448 | /* Set sampling clock position */ | |
cb0b6b03 | 449 | tmio_sd_writel(priv, tap, RENESAS_SDHI_SCC_TAPSET); |
f63968ba MV |
450 | } |
451 | ||
cb0b6b03 | 452 | static unsigned int renesas_sdhi_compare_scc_data(struct tmio_sd_priv *priv) |
f63968ba MV |
453 | { |
454 | /* Get comparison of sampling data */ | |
cb0b6b03 | 455 | return tmio_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP); |
f63968ba MV |
456 | } |
457 | ||
cb0b6b03 | 458 | static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, |
37c39906 | 459 | unsigned int taps) |
f63968ba MV |
460 | { |
461 | unsigned long tap_cnt; /* counter of tuning success */ | |
f63968ba MV |
462 | unsigned long tap_start;/* start position of tuning success */ |
463 | unsigned long tap_end; /* end position of tuning success */ | |
464 | unsigned long ntap; /* temporary counter of tuning success */ | |
465 | unsigned long match_cnt;/* counter of matching data */ | |
466 | unsigned long i; | |
467 | bool select = false; | |
468 | u32 reg; | |
469 | ||
b5900a58 MV |
470 | priv->needs_adjust_hs400 = false; |
471 | ||
f63968ba | 472 | /* Clear SCC_RVSREQ */ |
cb0b6b03 | 473 | tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); |
f63968ba MV |
474 | |
475 | /* Merge the results */ | |
0196a58a | 476 | for (i = 0; i < priv->tap_num * 2; i++) { |
f63968ba | 477 | if (!(taps & BIT(i))) { |
0196a58a MV |
478 | taps &= ~BIT(i % priv->tap_num); |
479 | taps &= ~BIT((i % priv->tap_num) + priv->tap_num); | |
f63968ba | 480 | } |
37c39906 MV |
481 | if (!(priv->smpcmp & BIT(i))) { |
482 | priv->smpcmp &= ~BIT(i % priv->tap_num); | |
483 | priv->smpcmp &= ~BIT((i % priv->tap_num) + priv->tap_num); | |
f63968ba MV |
484 | } |
485 | } | |
486 | ||
487 | /* | |
488 | * Find the longest consecutive run of successful probes. If that | |
489 | * is more than RENESAS_SDHI_MAX_TAP probes long then use the | |
490 | * center index as the tap. | |
491 | */ | |
492 | tap_cnt = 0; | |
493 | ntap = 0; | |
494 | tap_start = 0; | |
495 | tap_end = 0; | |
0196a58a | 496 | for (i = 0; i < priv->tap_num * 2; i++) { |
f63968ba MV |
497 | if (taps & BIT(i)) |
498 | ntap++; | |
499 | else { | |
500 | if (ntap > tap_cnt) { | |
501 | tap_start = i - ntap; | |
502 | tap_end = i - 1; | |
503 | tap_cnt = ntap; | |
504 | } | |
505 | ntap = 0; | |
506 | } | |
507 | } | |
508 | ||
509 | if (ntap > tap_cnt) { | |
510 | tap_start = i - ntap; | |
511 | tap_end = i - 1; | |
512 | tap_cnt = ntap; | |
513 | } | |
514 | ||
515 | /* | |
516 | * If all of the TAP is OK, the sampling clock position is selected by | |
517 | * identifying the change point of data. | |
518 | */ | |
0196a58a | 519 | if (tap_cnt == priv->tap_num * 2) { |
f63968ba MV |
520 | match_cnt = 0; |
521 | ntap = 0; | |
522 | tap_start = 0; | |
523 | tap_end = 0; | |
0196a58a | 524 | for (i = 0; i < priv->tap_num * 2; i++) { |
37c39906 | 525 | if (priv->smpcmp & BIT(i)) |
f63968ba MV |
526 | ntap++; |
527 | else { | |
528 | if (ntap > match_cnt) { | |
529 | tap_start = i - ntap; | |
530 | tap_end = i - 1; | |
531 | match_cnt = ntap; | |
532 | } | |
533 | ntap = 0; | |
534 | } | |
535 | } | |
536 | if (ntap > match_cnt) { | |
537 | tap_start = i - ntap; | |
538 | tap_end = i - 1; | |
539 | match_cnt = ntap; | |
540 | } | |
541 | if (match_cnt) | |
542 | select = true; | |
543 | } else if (tap_cnt >= RENESAS_SDHI_MAX_TAP) | |
544 | select = true; | |
545 | ||
546 | if (select) | |
0196a58a | 547 | priv->tap_set = ((tap_start + tap_end) / 2) % priv->tap_num; |
f63968ba MV |
548 | else |
549 | return -EIO; | |
550 | ||
551 | /* Set SCC */ | |
95ead3d9 | 552 | tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); |
f63968ba MV |
553 | |
554 | /* Enable auto re-tuning */ | |
cb0b6b03 | 555 | reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba | 556 | reg |= RENESAS_SDHI_SCC_RVSCNTL_RVSEN; |
cb0b6b03 | 557 | tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); |
f63968ba MV |
558 | |
559 | return 0; | |
560 | } | |
561 | ||
562 | int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) | |
563 | { | |
cb0b6b03 | 564 | struct tmio_sd_priv *priv = dev_get_priv(dev); |
f63968ba MV |
565 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
566 | struct mmc *mmc = upriv->mmc; | |
567 | unsigned int tap_num; | |
37c39906 | 568 | unsigned int taps = 0; |
f63968ba MV |
569 | int i, ret = 0; |
570 | u32 caps; | |
571 | ||
572 | /* Only supported on Renesas RCar */ | |
cb0b6b03 | 573 | if (!(priv->caps & TMIO_SD_CAP_RCAR_UHS)) |
f63968ba MV |
574 | return -EINVAL; |
575 | ||
576 | /* clock tuning is not needed for upto 52MHz */ | |
577 | if (!((mmc->selected_mode == MMC_HS_200) || | |
50aa1d99 | 578 | (mmc->selected_mode == MMC_HS_400) || |
f63968ba MV |
579 | (mmc->selected_mode == UHS_SDR104) || |
580 | (mmc->selected_mode == UHS_SDR50))) | |
581 | return 0; | |
582 | ||
583 | tap_num = renesas_sdhi_init_tuning(priv); | |
584 | if (!tap_num) | |
585 | /* Tuning is not supported */ | |
586 | goto out; | |
587 | ||
0196a58a MV |
588 | priv->tap_num = tap_num; |
589 | ||
590 | if (priv->tap_num * 2 >= sizeof(taps) * 8) { | |
f63968ba MV |
591 | dev_err(dev, |
592 | "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); | |
593 | goto out; | |
594 | } | |
595 | ||
37c39906 MV |
596 | priv->smpcmp = 0; |
597 | ||
f63968ba | 598 | /* Issue CMD19 twice for each tap */ |
0196a58a MV |
599 | for (i = 0; i < 2 * priv->tap_num; i++) { |
600 | renesas_sdhi_prepare_tuning(priv, i % priv->tap_num); | |
f63968ba MV |
601 | |
602 | /* Force PIO for the tuning */ | |
603 | caps = priv->caps; | |
cb0b6b03 | 604 | priv->caps &= ~TMIO_SD_CAP_DMA_INTERNAL; |
f63968ba MV |
605 | |
606 | ret = mmc_send_tuning(mmc, opcode, NULL); | |
607 | ||
608 | priv->caps = caps; | |
609 | ||
610 | if (ret == 0) | |
611 | taps |= BIT(i); | |
612 | ||
613 | ret = renesas_sdhi_compare_scc_data(priv); | |
614 | if (ret == 0) | |
37c39906 | 615 | priv->smpcmp |= BIT(i); |
f63968ba MV |
616 | |
617 | mdelay(1); | |
618 | } | |
619 | ||
37c39906 | 620 | ret = renesas_sdhi_select_tuning(priv, taps); |
f63968ba MV |
621 | |
622 | out: | |
623 | if (ret < 0) { | |
624 | dev_warn(dev, "Tuning procedure failed\n"); | |
625 | renesas_sdhi_reset_tuning(priv); | |
626 | } | |
627 | ||
628 | return ret; | |
629 | } | |
50aa1d99 MV |
630 | #else |
631 | static int renesas_sdhi_hs400(struct udevice *dev) | |
632 | { | |
633 | return 0; | |
634 | } | |
f63968ba MV |
635 | #endif |
636 | ||
637 | static int renesas_sdhi_set_ios(struct udevice *dev) | |
638 | { | |
50aa1d99 MV |
639 | struct tmio_sd_priv *priv = dev_get_priv(dev); |
640 | u32 tmp; | |
641 | int ret; | |
cf39f3f3 | 642 | |
50aa1d99 MV |
643 | /* Stop the clock before changing its rate to avoid a glitch signal */ |
644 | tmp = tmio_sd_readl(priv, TMIO_SD_CLKCTL); | |
645 | tmp &= ~TMIO_SD_CLKCTL_SCLKEN; | |
646 | tmio_sd_writel(priv, tmp, TMIO_SD_CLKCTL); | |
cf39f3f3 | 647 | |
50aa1d99 MV |
648 | ret = renesas_sdhi_hs400(dev); |
649 | if (ret) | |
650 | return ret; | |
651 | ||
652 | ret = tmio_sd_set_ios(dev); | |
f63968ba | 653 | |
50aa1d99 MV |
654 | mdelay(10); |
655 | ||
656 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ | |
657 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
658 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
659 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
660 | if ((priv->caps & TMIO_SD_CAP_RCAR_UHS) && | |
661 | (mmc->selected_mode != UHS_SDR104) && | |
662 | (mmc->selected_mode != MMC_HS_200) && | |
663 | (mmc->selected_mode != MMC_HS_400)) { | |
52e17968 | 664 | renesas_sdhi_reset_tuning(priv); |
50aa1d99 | 665 | } |
f63968ba MV |
666 | #endif |
667 | ||
668 | return ret; | |
669 | } | |
670 | ||
2fc10754 | 671 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) |
6cf8a903 SP |
672 | static int renesas_sdhi_wait_dat0(struct udevice *dev, int state, |
673 | int timeout_us) | |
2fc10754 MV |
674 | { |
675 | int ret = -ETIMEDOUT; | |
676 | bool dat0_high; | |
677 | bool target_dat0_high = !!state; | |
678 | struct tmio_sd_priv *priv = dev_get_priv(dev); | |
679 | ||
6cf8a903 SP |
680 | timeout_us = DIV_ROUND_UP(timeout_us, 10); /* check every 10 us. */ |
681 | while (timeout_us--) { | |
2fc10754 MV |
682 | dat0_high = !!(tmio_sd_readl(priv, TMIO_SD_INFO2) & TMIO_SD_INFO2_DAT0); |
683 | if (dat0_high == target_dat0_high) { | |
684 | ret = 0; | |
685 | break; | |
686 | } | |
687 | udelay(10); | |
688 | } | |
689 | ||
690 | return ret; | |
691 | } | |
692 | #endif | |
693 | ||
d2661d8e MV |
694 | #define RENESAS_SDHI_DMA_ALIGNMENT 128 |
695 | ||
4a66d4ee MV |
696 | static int renesas_sdhi_addr_aligned_gen(uintptr_t ubuf, |
697 | size_t len, size_t len_aligned) | |
d2661d8e | 698 | { |
d2661d8e MV |
699 | /* Check if start is aligned */ |
700 | if (!IS_ALIGNED(ubuf, RENESAS_SDHI_DMA_ALIGNMENT)) { | |
4a66d4ee | 701 | debug("Unaligned buffer address %lx\n", ubuf); |
d2661d8e MV |
702 | return 0; |
703 | } | |
704 | ||
705 | /* Check if length is aligned */ | |
4a66d4ee MV |
706 | if (len != len_aligned) { |
707 | debug("Unaligned buffer length %zu\n", len); | |
d2661d8e MV |
708 | return 0; |
709 | } | |
710 | ||
711 | #ifdef CONFIG_PHYS_64BIT | |
712 | /* Check if below 32bit boundary */ | |
4a66d4ee MV |
713 | if ((ubuf >> 32) || (ubuf + len_aligned) >> 32) { |
714 | debug("Buffer above 32bit boundary %lx-%lx\n", | |
715 | ubuf, ubuf + len_aligned); | |
d2661d8e MV |
716 | return 0; |
717 | } | |
718 | #endif | |
719 | ||
720 | /* Aligned */ | |
721 | return 1; | |
722 | } | |
723 | ||
4a66d4ee MV |
724 | static int renesas_sdhi_addr_aligned(struct bounce_buffer *state) |
725 | { | |
726 | uintptr_t ubuf = (uintptr_t)state->user_buffer; | |
727 | ||
728 | return renesas_sdhi_addr_aligned_gen(ubuf, state->len, | |
729 | state->len_aligned); | |
730 | } | |
731 | ||
b5900a58 MV |
732 | static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, |
733 | struct mmc_data *data) | |
734 | { | |
d2661d8e MV |
735 | struct bounce_buffer bbstate; |
736 | unsigned int bbflags; | |
737 | bool bbok = false; | |
738 | size_t len; | |
739 | void *buf; | |
b5900a58 MV |
740 | int ret; |
741 | ||
d2661d8e MV |
742 | if (data) { |
743 | if (data->flags & MMC_DATA_READ) { | |
744 | buf = data->dest; | |
745 | bbflags = GEN_BB_WRITE; | |
746 | } else { | |
747 | buf = (void *)data->src; | |
748 | bbflags = GEN_BB_READ; | |
749 | } | |
750 | len = data->blocks * data->blocksize; | |
751 | ||
752 | ret = bounce_buffer_start_extalign(&bbstate, buf, len, bbflags, | |
753 | RENESAS_SDHI_DMA_ALIGNMENT, | |
754 | renesas_sdhi_addr_aligned); | |
755 | /* | |
756 | * If the amount of data to transfer is too large, we can get | |
757 | * -ENOMEM when starting the bounce buffer. If that happens, | |
758 | * fall back to PIO as it was before, otherwise use the BB. | |
759 | */ | |
760 | if (!ret) { | |
761 | bbok = true; | |
762 | if (data->flags & MMC_DATA_READ) | |
763 | data->dest = bbstate.bounce_buffer; | |
764 | else | |
765 | data->src = bbstate.bounce_buffer; | |
766 | } | |
767 | } | |
768 | ||
b5900a58 | 769 | ret = tmio_sd_send_cmd(dev, cmd, data); |
d2661d8e MV |
770 | |
771 | if (data && bbok) { | |
772 | buf = bbstate.user_buffer; | |
773 | ||
774 | bounce_buffer_stop(&bbstate); | |
775 | ||
776 | if (data->flags & MMC_DATA_READ) | |
777 | data->dest = buf; | |
778 | else | |
779 | data->src = buf; | |
780 | } | |
781 | ||
b5900a58 MV |
782 | if (ret) |
783 | return ret; | |
784 | ||
785 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ | |
786 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
787 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
788 | struct tmio_sd_priv *priv = dev_get_priv(dev); | |
789 | ||
6900066c MV |
790 | renesas_sdhi_check_scc_error(dev); |
791 | ||
b5900a58 MV |
792 | if (cmd->cmdidx == MMC_CMD_SEND_STATUS) |
793 | renesas_sdhi_adjust_hs400_mode_enable(priv); | |
794 | #endif | |
795 | ||
796 | return 0; | |
797 | } | |
798 | ||
4a66d4ee MV |
799 | int renesas_sdhi_get_b_max(struct udevice *dev, void *dst, lbaint_t blkcnt) |
800 | { | |
801 | struct tmio_sd_priv *priv = dev_get_priv(dev); | |
802 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | |
803 | struct mmc *mmc = upriv->mmc; | |
804 | size_t len = blkcnt * mmc->read_bl_len; | |
805 | size_t len_align = roundup(len, RENESAS_SDHI_DMA_ALIGNMENT); | |
806 | ||
807 | if (renesas_sdhi_addr_aligned_gen((uintptr_t)dst, len, len_align)) { | |
808 | if (priv->quirks & TMIO_SD_CAP_16BIT) | |
809 | return U16_MAX; | |
810 | else | |
811 | return U32_MAX; | |
812 | } else { | |
813 | return (CONFIG_SYS_MALLOC_LEN / 4) / mmc->read_bl_len; | |
814 | } | |
815 | } | |
816 | ||
e94cad93 | 817 | static const struct dm_mmc_ops renesas_sdhi_ops = { |
b5900a58 | 818 | .send_cmd = renesas_sdhi_send_cmd, |
f63968ba | 819 | .set_ios = renesas_sdhi_set_ios, |
cb0b6b03 | 820 | .get_cd = tmio_sd_get_cd, |
50aa1d99 MV |
821 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ |
822 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
823 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
f63968ba MV |
824 | .execute_tuning = renesas_sdhi_execute_tuning, |
825 | #endif | |
2fc10754 MV |
826 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) |
827 | .wait_dat0 = renesas_sdhi_wait_dat0, | |
828 | #endif | |
4a66d4ee | 829 | .get_b_max = renesas_sdhi_get_b_max, |
e94cad93 MV |
830 | }; |
831 | ||
cb0b6b03 | 832 | #define RENESAS_GEN2_QUIRKS TMIO_SD_CAP_RCAR_GEN2 |
f98833db | 833 | #define RENESAS_GEN3_QUIRKS \ |
cb0b6b03 | 834 | TMIO_SD_CAP_64BIT | TMIO_SD_CAP_RCAR_GEN3 | TMIO_SD_CAP_RCAR_UHS |
f98833db | 835 | |
e94cad93 | 836 | static const struct udevice_id renesas_sdhi_match[] = { |
f98833db MV |
837 | { .compatible = "renesas,sdhi-r8a7790", .data = RENESAS_GEN2_QUIRKS }, |
838 | { .compatible = "renesas,sdhi-r8a7791", .data = RENESAS_GEN2_QUIRKS }, | |
839 | { .compatible = "renesas,sdhi-r8a7792", .data = RENESAS_GEN2_QUIRKS }, | |
840 | { .compatible = "renesas,sdhi-r8a7793", .data = RENESAS_GEN2_QUIRKS }, | |
841 | { .compatible = "renesas,sdhi-r8a7794", .data = RENESAS_GEN2_QUIRKS }, | |
842 | { .compatible = "renesas,sdhi-r8a7795", .data = RENESAS_GEN3_QUIRKS }, | |
843 | { .compatible = "renesas,sdhi-r8a7796", .data = RENESAS_GEN3_QUIRKS }, | |
844 | { .compatible = "renesas,sdhi-r8a77965", .data = RENESAS_GEN3_QUIRKS }, | |
845 | { .compatible = "renesas,sdhi-r8a77970", .data = RENESAS_GEN3_QUIRKS }, | |
d629152a | 846 | { .compatible = "renesas,sdhi-r8a77990", .data = RENESAS_GEN3_QUIRKS }, |
f98833db | 847 | { .compatible = "renesas,sdhi-r8a77995", .data = RENESAS_GEN3_QUIRKS }, |
e94cad93 MV |
848 | { /* sentinel */ } |
849 | }; | |
850 | ||
8ec6a04b MV |
851 | static ulong renesas_sdhi_clk_get_rate(struct tmio_sd_priv *priv) |
852 | { | |
853 | return clk_get_rate(&priv->clk); | |
854 | } | |
855 | ||
d34bd2de MV |
856 | static void renesas_sdhi_filter_caps(struct udevice *dev) |
857 | { | |
d34bd2de MV |
858 | struct tmio_sd_priv *priv = dev_get_priv(dev); |
859 | ||
860 | if (!(priv->caps & TMIO_SD_CAP_RCAR_GEN3)) | |
861 | return; | |
862 | ||
56b0bb96 MV |
863 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ |
864 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
865 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
866 | struct tmio_sd_plat *plat = dev_get_platdata(dev); | |
867 | ||
868 | /* HS400 is not supported on H3 ES1.x and M3W ES1.0, ES1.1 */ | |
d34bd2de MV |
869 | if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && |
870 | (rmobile_get_cpu_rev_integer() <= 1)) || | |
871 | ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && | |
872 | (rmobile_get_cpu_rev_integer() == 1) && | |
56b0bb96 | 873 | (rmobile_get_cpu_rev_fraction() < 2))) |
d34bd2de | 874 | plat->cfg.host_caps &= ~MMC_MODE_HS400; |
50aa1d99 | 875 | |
1bdcb83d MV |
876 | /* H3 ES2.0, ES3.0 and M3W ES1.2 and M3N bad taps */ |
877 | if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && | |
878 | (rmobile_get_cpu_rev_integer() >= 2)) || | |
879 | ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && | |
880 | (rmobile_get_cpu_rev_integer() == 1) && | |
881 | (rmobile_get_cpu_rev_fraction() == 2)) || | |
882 | (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77965)) | |
883 | priv->hs400_bad_tap = BIT(2) | BIT(3) | BIT(6) | BIT(7); | |
884 | ||
56b0bb96 MV |
885 | /* H3 ES3.0 can use HS400 with manual adjustment */ |
886 | if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && | |
887 | (rmobile_get_cpu_rev_integer() >= 3)) { | |
888 | priv->adjust_hs400_enable = true; | |
889 | priv->adjust_hs400_offset = 0; | |
890 | priv->adjust_hs400_calib_table = | |
891 | r8a7795_calib_table[!rmobile_is_gen3_mmc0(priv)]; | |
892 | } | |
893 | ||
894 | /* M3W ES1.2 can use HS400 with manual adjustment */ | |
895 | if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && | |
896 | (rmobile_get_cpu_rev_integer() == 1) && | |
897 | (rmobile_get_cpu_rev_fraction() == 2)) { | |
898 | priv->adjust_hs400_enable = true; | |
899 | priv->adjust_hs400_offset = 3; | |
900 | priv->adjust_hs400_calib_table = | |
901 | r8a7796_rev1_calib_table[!rmobile_is_gen3_mmc0(priv)]; | |
902 | } | |
903 | ||
1bdcb83d | 904 | /* M3W ES1.x for x>2 can use HS400 with manual adjustment and taps */ |
b5900a58 MV |
905 | if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && |
906 | (rmobile_get_cpu_rev_integer() == 1) && | |
907 | (rmobile_get_cpu_rev_fraction() > 2)) { | |
908 | priv->adjust_hs400_enable = true; | |
56b0bb96 | 909 | priv->adjust_hs400_offset = 0; |
1bdcb83d | 910 | priv->hs400_bad_tap = BIT(1) | BIT(3) | BIT(5) | BIT(7); |
56b0bb96 MV |
911 | priv->adjust_hs400_calib_table = |
912 | r8a7796_rev3_calib_table[!rmobile_is_gen3_mmc0(priv)]; | |
b5900a58 MV |
913 | } |
914 | ||
915 | /* M3N can use HS400 with manual adjustment */ | |
916 | if (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77965) { | |
917 | priv->adjust_hs400_enable = true; | |
e5d3f3d9 | 918 | priv->adjust_hs400_offset = 3; |
56b0bb96 MV |
919 | priv->adjust_hs400_calib_table = |
920 | r8a77965_calib_table[!rmobile_is_gen3_mmc0(priv)]; | |
b5900a58 MV |
921 | } |
922 | ||
923 | /* E3 can use HS400 with manual adjustment */ | |
924 | if (rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A77990) { | |
925 | priv->adjust_hs400_enable = true; | |
e5d3f3d9 | 926 | priv->adjust_hs400_offset = 3; |
56b0bb96 MV |
927 | priv->adjust_hs400_calib_table = |
928 | r8a77990_calib_table[!rmobile_is_gen3_mmc0(priv)]; | |
b5900a58 MV |
929 | } |
930 | ||
81099887 MV |
931 | /* H3 ES1.x, ES2.0 and M3W ES1.0, ES1.1, ES1.2 uses 4 tuning taps */ |
932 | if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && | |
933 | (rmobile_get_cpu_rev_integer() <= 2)) || | |
934 | ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && | |
935 | (rmobile_get_cpu_rev_integer() == 1) && | |
936 | (rmobile_get_cpu_rev_fraction() <= 2))) | |
50aa1d99 MV |
937 | priv->nrtaps = 4; |
938 | else | |
939 | priv->nrtaps = 8; | |
56b0bb96 | 940 | #endif |
992bcf4f MV |
941 | /* H3 ES1.x and M3W ES1.0 uses bit 17 for DTRAEND */ |
942 | if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && | |
943 | (rmobile_get_cpu_rev_integer() <= 1)) || | |
944 | ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && | |
945 | (rmobile_get_cpu_rev_integer() == 1) && | |
946 | (rmobile_get_cpu_rev_fraction() == 0))) | |
947 | priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD; | |
948 | else | |
949 | priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD2; | |
d34bd2de MV |
950 | } |
951 | ||
c769e609 MV |
952 | static int renesas_sdhi_probe(struct udevice *dev) |
953 | { | |
30b5d9aa | 954 | struct tmio_sd_priv *priv = dev_get_priv(dev); |
c769e609 | 955 | u32 quirks = dev_get_driver_data(dev); |
7cf7ef81 MV |
956 | struct fdt_resource reg_res; |
957 | DECLARE_GLOBAL_DATA_PTR; | |
958 | int ret; | |
959 | ||
8ec6a04b MV |
960 | priv->clk_get_rate = renesas_sdhi_clk_get_rate; |
961 | ||
f98833db MV |
962 | if (quirks == RENESAS_GEN2_QUIRKS) { |
963 | ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), | |
964 | "reg", 0, ®_res); | |
965 | if (ret < 0) { | |
966 | dev_err(dev, "\"reg\" resource not found, ret=%i\n", | |
967 | ret); | |
968 | return ret; | |
969 | } | |
7cf7ef81 | 970 | |
f98833db | 971 | if (fdt_resource_size(®_res) == 0x100) |
cb0b6b03 | 972 | quirks |= TMIO_SD_CAP_16BIT; |
f98833db | 973 | } |
c769e609 | 974 | |
8ec6a04b | 975 | ret = clk_get_by_index(dev, 0, &priv->clk); |
30b5d9aa MY |
976 | if (ret < 0) { |
977 | dev_err(dev, "failed to get host clock\n"); | |
978 | return ret; | |
979 | } | |
980 | ||
981 | /* set to max rate */ | |
8ec6a04b MV |
982 | ret = clk_set_rate(&priv->clk, 200000000); |
983 | if (ret < 0) { | |
30b5d9aa | 984 | dev_err(dev, "failed to set rate for host clock\n"); |
8ec6a04b MV |
985 | clk_free(&priv->clk); |
986 | return ret; | |
30b5d9aa MY |
987 | } |
988 | ||
8ec6a04b | 989 | ret = clk_enable(&priv->clk); |
30b5d9aa MY |
990 | if (ret) { |
991 | dev_err(dev, "failed to enable host clock\n"); | |
992 | return ret; | |
993 | } | |
994 | ||
4a66d4ee | 995 | priv->quirks = quirks; |
cb0b6b03 | 996 | ret = tmio_sd_probe(dev, quirks); |
d34bd2de MV |
997 | |
998 | renesas_sdhi_filter_caps(dev); | |
999 | ||
50aa1d99 MV |
1000 | #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ |
1001 | CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ | |
1002 | CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) | |
52e17968 | 1003 | if (!ret && (priv->caps & TMIO_SD_CAP_RCAR_UHS)) |
65186977 | 1004 | renesas_sdhi_reset_tuning(priv); |
f63968ba MV |
1005 | #endif |
1006 | return ret; | |
c769e609 MV |
1007 | } |
1008 | ||
e94cad93 MV |
1009 | U_BOOT_DRIVER(renesas_sdhi) = { |
1010 | .name = "renesas-sdhi", | |
1011 | .id = UCLASS_MMC, | |
1012 | .of_match = renesas_sdhi_match, | |
cb0b6b03 | 1013 | .bind = tmio_sd_bind, |
c769e609 | 1014 | .probe = renesas_sdhi_probe, |
cb0b6b03 MV |
1015 | .priv_auto_alloc_size = sizeof(struct tmio_sd_priv), |
1016 | .platdata_auto_alloc_size = sizeof(struct tmio_sd_plat), | |
e94cad93 MV |
1017 | .ops = &renesas_sdhi_ops, |
1018 | }; |