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