]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
e24ea55c IC |
2 | /* |
3 | * (C) Copyright 2007-2011 | |
4 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> | |
5 | * Aaron <[email protected]> | |
6 | * | |
7 | * MMC driver for allwinner sunxi platform. | |
e24ea55c IC |
8 | */ |
9 | ||
10 | #include <common.h> | |
dd27918c | 11 | #include <dm.h> |
90641f82 | 12 | #include <errno.h> |
f7ae49fc | 13 | #include <log.h> |
e24ea55c IC |
14 | #include <malloc.h> |
15 | #include <mmc.h> | |
c57572eb AP |
16 | #include <clk.h> |
17 | #include <reset.h> | |
e24ea55c IC |
18 | #include <asm/io.h> |
19 | #include <asm/arch/clock.h> | |
20 | #include <asm/arch/cpu.h> | |
cd82113a | 21 | #include <asm/arch/gpio.h> |
e24ea55c | 22 | #include <asm/arch/mmc.h> |
cd82113a | 23 | #include <asm-generic/gpio.h> |
c05ed00a | 24 | #include <linux/delay.h> |
e24ea55c | 25 | |
e8f37f42 JT |
26 | #ifdef CONFIG_DM_MMC |
27 | struct sunxi_mmc_variant { | |
e8f37f42 JT |
28 | u16 mclk_offset; |
29 | }; | |
30 | #endif | |
31 | ||
dd27918c SG |
32 | struct sunxi_mmc_plat { |
33 | struct mmc_config cfg; | |
34 | struct mmc mmc; | |
35 | }; | |
36 | ||
e3c794e2 | 37 | struct sunxi_mmc_priv { |
e24ea55c IC |
38 | unsigned mmc_no; |
39 | uint32_t *mclkreg; | |
e24ea55c | 40 | unsigned fatal_err; |
dd27918c | 41 | struct gpio_desc cd_gpio; /* Change Detect GPIO */ |
8be4e61d | 42 | int cd_inverted; /* Inverted Card Detect */ |
e24ea55c IC |
43 | struct sunxi_mmc *reg; |
44 | struct mmc_config cfg; | |
e8f37f42 JT |
45 | #ifdef CONFIG_DM_MMC |
46 | const struct sunxi_mmc_variant *variant; | |
47 | #endif | |
e24ea55c IC |
48 | }; |
49 | ||
dd27918c | 50 | #if !CONFIG_IS_ENABLED(DM_MMC) |
e24ea55c | 51 | /* support 4 mmc hosts */ |
e3c794e2 | 52 | struct sunxi_mmc_priv mmc_host[4]; |
e24ea55c | 53 | |
967325fe HG |
54 | static int sunxi_mmc_getcd_gpio(int sdc_no) |
55 | { | |
56 | switch (sdc_no) { | |
57 | case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN); | |
58 | case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN); | |
59 | case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN); | |
60 | case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN); | |
61 | } | |
90641f82 | 62 | return -EINVAL; |
967325fe HG |
63 | } |
64 | ||
e24ea55c IC |
65 | static int mmc_resource_init(int sdc_no) |
66 | { | |
3f5af12a | 67 | struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; |
e24ea55c | 68 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
967325fe | 69 | int cd_pin, ret = 0; |
e24ea55c IC |
70 | |
71 | debug("init mmc %d resource\n", sdc_no); | |
72 | ||
73 | switch (sdc_no) { | |
74 | case 0: | |
3f5af12a SG |
75 | priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE; |
76 | priv->mclkreg = &ccm->sd0_clk_cfg; | |
e24ea55c IC |
77 | break; |
78 | case 1: | |
3f5af12a SG |
79 | priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE; |
80 | priv->mclkreg = &ccm->sd1_clk_cfg; | |
e24ea55c IC |
81 | break; |
82 | case 2: | |
3f5af12a SG |
83 | priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE; |
84 | priv->mclkreg = &ccm->sd2_clk_cfg; | |
e24ea55c | 85 | break; |
42956f1b | 86 | #ifdef SUNXI_MMC3_BASE |
e24ea55c | 87 | case 3: |
3f5af12a SG |
88 | priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE; |
89 | priv->mclkreg = &ccm->sd3_clk_cfg; | |
e24ea55c | 90 | break; |
42956f1b | 91 | #endif |
e24ea55c IC |
92 | default: |
93 | printf("Wrong mmc number %d\n", sdc_no); | |
94 | return -1; | |
95 | } | |
3f5af12a | 96 | priv->mmc_no = sdc_no; |
e24ea55c | 97 | |
967325fe | 98 | cd_pin = sunxi_mmc_getcd_gpio(sdc_no); |
90641f82 | 99 | if (cd_pin >= 0) { |
967325fe | 100 | ret = gpio_request(cd_pin, "mmc_cd"); |
1c09fa38 HG |
101 | if (!ret) { |
102 | sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); | |
b0c4ae1a | 103 | ret = gpio_direction_input(cd_pin); |
1c09fa38 | 104 | } |
b0c4ae1a | 105 | } |
967325fe HG |
106 | |
107 | return ret; | |
e24ea55c | 108 | } |
dd27918c | 109 | #endif |
e24ea55c | 110 | |
3f5af12a | 111 | static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) |
fc3a8325 HG |
112 | { |
113 | unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly; | |
0e21a2ff | 114 | bool new_mode = true; |
20940ef2 | 115 | bool calibrate = false; |
de9b1771 MR |
116 | u32 val = 0; |
117 | ||
0e21a2ff VK |
118 | if (!IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE)) |
119 | new_mode = false; | |
120 | ||
121 | /* A83T support new mode only on eMMC */ | |
122 | if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T) && priv->mmc_no != 2) | |
123 | new_mode = false; | |
de9b1771 | 124 | |
aaebb900 | 125 | #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6) |
20940ef2 VK |
126 | calibrate = true; |
127 | #endif | |
128 | ||
fc3a8325 HG |
129 | if (hz <= 24000000) { |
130 | pll = CCM_MMC_CTRL_OSCM24; | |
131 | pll_hz = 24000000; | |
132 | } else { | |
daf22636 HG |
133 | #ifdef CONFIG_MACH_SUN9I |
134 | pll = CCM_MMC_CTRL_PLL_PERIPH0; | |
135 | pll_hz = clock_get_pll4_periph0(); | |
aaebb900 | 136 | #elif defined(CONFIG_SUN50I_GEN_H6) |
42956f1b IZ |
137 | pll = CCM_MMC_CTRL_PLL6X2; |
138 | pll_hz = clock_get_pll6() * 2; | |
daf22636 | 139 | #else |
fc3a8325 HG |
140 | pll = CCM_MMC_CTRL_PLL6; |
141 | pll_hz = clock_get_pll6(); | |
daf22636 | 142 | #endif |
fc3a8325 HG |
143 | } |
144 | ||
145 | div = pll_hz / hz; | |
146 | if (pll_hz % hz) | |
147 | div++; | |
148 | ||
149 | n = 0; | |
150 | while (div > 16) { | |
151 | n++; | |
152 | div = (div + 1) / 2; | |
153 | } | |
154 | ||
155 | if (n > 3) { | |
3f5af12a SG |
156 | printf("mmc %u error cannot set clock to %u\n", priv->mmc_no, |
157 | hz); | |
fc3a8325 HG |
158 | return -1; |
159 | } | |
160 | ||
161 | /* determine delays */ | |
162 | if (hz <= 400000) { | |
163 | oclk_dly = 0; | |
be90974c | 164 | sclk_dly = 0; |
fc3a8325 HG |
165 | } else if (hz <= 25000000) { |
166 | oclk_dly = 0; | |
167 | sclk_dly = 5; | |
be90974c | 168 | #ifdef CONFIG_MACH_SUN9I |
4744d81c | 169 | } else if (hz <= 52000000) { |
be90974c HG |
170 | oclk_dly = 5; |
171 | sclk_dly = 4; | |
fc3a8325 | 172 | } else { |
4744d81c | 173 | /* hz > 52000000 */ |
fc3a8325 HG |
174 | oclk_dly = 2; |
175 | sclk_dly = 4; | |
be90974c | 176 | #else |
4744d81c | 177 | } else if (hz <= 52000000) { |
be90974c HG |
178 | oclk_dly = 3; |
179 | sclk_dly = 4; | |
180 | } else { | |
4744d81c | 181 | /* hz > 52000000 */ |
be90974c HG |
182 | oclk_dly = 1; |
183 | sclk_dly = 4; | |
184 | #endif | |
fc3a8325 HG |
185 | } |
186 | ||
de9b1771 MR |
187 | if (new_mode) { |
188 | #ifdef CONFIG_MMC_SUNXI_HAS_NEW_MODE | |
2a8882ec | 189 | #ifdef CONFIG_MMC_SUNXI_HAS_MODE_SWITCH |
de9b1771 | 190 | val = CCM_MMC_CTRL_MODE_SEL_NEW; |
2a8882ec | 191 | #endif |
8a647fc3 | 192 | setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW); |
de9b1771 | 193 | #endif |
20940ef2 VK |
194 | } else if (!calibrate) { |
195 | /* | |
196 | * Use hardcoded delay values if controller doesn't support | |
197 | * calibration | |
198 | */ | |
de9b1771 MR |
199 | val = CCM_MMC_CTRL_OCLK_DLY(oclk_dly) | |
200 | CCM_MMC_CTRL_SCLK_DLY(sclk_dly); | |
201 | } | |
202 | ||
203 | writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) | | |
204 | CCM_MMC_CTRL_M(div) | val, priv->mclkreg); | |
fc3a8325 HG |
205 | |
206 | debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n", | |
3f5af12a | 207 | priv->mmc_no, hz, pll_hz, 1u << n, div, pll_hz / (1u << n) / div); |
fc3a8325 HG |
208 | |
209 | return 0; | |
210 | } | |
211 | ||
034e226b | 212 | static int mmc_update_clk(struct sunxi_mmc_priv *priv) |
e24ea55c | 213 | { |
e24ea55c IC |
214 | unsigned int cmd; |
215 | unsigned timeout_msecs = 2000; | |
5ff8e548 | 216 | unsigned long start = get_timer(0); |
e24ea55c IC |
217 | |
218 | cmd = SUNXI_MMC_CMD_START | | |
219 | SUNXI_MMC_CMD_UPCLK_ONLY | | |
220 | SUNXI_MMC_CMD_WAIT_PRE_OVER; | |
5ff8e548 | 221 | |
3f5af12a SG |
222 | writel(cmd, &priv->reg->cmd); |
223 | while (readl(&priv->reg->cmd) & SUNXI_MMC_CMD_START) { | |
5ff8e548 | 224 | if (get_timer(start) > timeout_msecs) |
e24ea55c | 225 | return -1; |
e24ea55c IC |
226 | } |
227 | ||
228 | /* clock update sets various irq status bits, clear these */ | |
3f5af12a | 229 | writel(readl(&priv->reg->rint), &priv->reg->rint); |
e24ea55c IC |
230 | |
231 | return 0; | |
232 | } | |
233 | ||
034e226b | 234 | static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc) |
e24ea55c | 235 | { |
3f5af12a | 236 | unsigned rval = readl(&priv->reg->clkcr); |
e24ea55c IC |
237 | |
238 | /* Disable Clock */ | |
239 | rval &= ~SUNXI_MMC_CLK_ENABLE; | |
3f5af12a | 240 | writel(rval, &priv->reg->clkcr); |
034e226b | 241 | if (mmc_update_clk(priv)) |
e24ea55c IC |
242 | return -1; |
243 | ||
fc3a8325 | 244 | /* Set mod_clk to new rate */ |
3f5af12a | 245 | if (mmc_set_mod_clk(priv, mmc->clock)) |
fc3a8325 HG |
246 | return -1; |
247 | ||
248 | /* Clear internal divider */ | |
e24ea55c | 249 | rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; |
3f5af12a | 250 | writel(rval, &priv->reg->clkcr); |
fc3a8325 | 251 | |
aaebb900 | 252 | #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6) |
20940ef2 VK |
253 | /* A64 supports calibration of delays on MMC controller and we |
254 | * have to set delay of zero before starting calibration. | |
255 | * Allwinner BSP driver sets a delay only in the case of | |
256 | * using HS400 which is not supported by mainline U-Boot or | |
257 | * Linux at the moment | |
258 | */ | |
259 | writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl); | |
260 | #endif | |
261 | ||
e24ea55c IC |
262 | /* Re-enable Clock */ |
263 | rval |= SUNXI_MMC_CLK_ENABLE; | |
3f5af12a | 264 | writel(rval, &priv->reg->clkcr); |
034e226b | 265 | if (mmc_update_clk(priv)) |
e24ea55c IC |
266 | return -1; |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
034e226b SG |
271 | static int sunxi_mmc_set_ios_common(struct sunxi_mmc_priv *priv, |
272 | struct mmc *mmc) | |
e24ea55c | 273 | { |
fc3a8325 HG |
274 | debug("set ios: bus_width: %x, clock: %d\n", |
275 | mmc->bus_width, mmc->clock); | |
e24ea55c IC |
276 | |
277 | /* Change clock first */ | |
034e226b | 278 | if (mmc->clock && mmc_config_clock(priv, mmc) != 0) { |
3f5af12a | 279 | priv->fatal_err = 1; |
07b0b9c0 | 280 | return -EINVAL; |
e24ea55c IC |
281 | } |
282 | ||
283 | /* Change bus width */ | |
284 | if (mmc->bus_width == 8) | |
3f5af12a | 285 | writel(0x2, &priv->reg->width); |
e24ea55c | 286 | else if (mmc->bus_width == 4) |
3f5af12a | 287 | writel(0x1, &priv->reg->width); |
e24ea55c | 288 | else |
3f5af12a | 289 | writel(0x0, &priv->reg->width); |
07b0b9c0 JC |
290 | |
291 | return 0; | |
e24ea55c IC |
292 | } |
293 | ||
dd27918c | 294 | #if !CONFIG_IS_ENABLED(DM_MMC) |
5abdb156 | 295 | static int sunxi_mmc_core_init(struct mmc *mmc) |
e24ea55c | 296 | { |
3f5af12a | 297 | struct sunxi_mmc_priv *priv = mmc->priv; |
e24ea55c IC |
298 | |
299 | /* Reset controller */ | |
3f5af12a | 300 | writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); |
b6ae6765 | 301 | udelay(1000); |
e24ea55c IC |
302 | |
303 | return 0; | |
304 | } | |
dd27918c | 305 | #endif |
e24ea55c | 306 | |
034e226b SG |
307 | static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc, |
308 | struct mmc_data *data) | |
e24ea55c | 309 | { |
e24ea55c IC |
310 | const int reading = !!(data->flags & MMC_DATA_READ); |
311 | const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY : | |
312 | SUNXI_MMC_STATUS_FIFO_FULL; | |
313 | unsigned i; | |
e24ea55c | 314 | unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); |
28f69b9a | 315 | unsigned byte_cnt = data->blocksize * data->blocks; |
5ff8e548 PT |
316 | unsigned timeout_msecs = byte_cnt >> 8; |
317 | unsigned long start; | |
318 | ||
319 | if (timeout_msecs < 2000) | |
320 | timeout_msecs = 2000; | |
e24ea55c | 321 | |
b6ae6765 | 322 | /* Always read / write data through the CPU */ |
3f5af12a | 323 | setbits_le32(&priv->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB); |
b6ae6765 | 324 | |
5ff8e548 PT |
325 | start = get_timer(0); |
326 | ||
e24ea55c | 327 | for (i = 0; i < (byte_cnt >> 2); i++) { |
3f5af12a | 328 | while (readl(&priv->reg->status) & status_bit) { |
5ff8e548 | 329 | if (get_timer(start) > timeout_msecs) |
e24ea55c | 330 | return -1; |
e24ea55c IC |
331 | } |
332 | ||
333 | if (reading) | |
3f5af12a | 334 | buff[i] = readl(&priv->reg->fifo); |
e24ea55c | 335 | else |
3f5af12a | 336 | writel(buff[i], &priv->reg->fifo); |
e24ea55c IC |
337 | } |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
034e226b SG |
342 | static int mmc_rint_wait(struct sunxi_mmc_priv *priv, struct mmc *mmc, |
343 | uint timeout_msecs, uint done_bit, const char *what) | |
e24ea55c | 344 | { |
e24ea55c | 345 | unsigned int status; |
5ff8e548 | 346 | unsigned long start = get_timer(0); |
e24ea55c IC |
347 | |
348 | do { | |
3f5af12a | 349 | status = readl(&priv->reg->rint); |
5ff8e548 | 350 | if ((get_timer(start) > timeout_msecs) || |
e24ea55c IC |
351 | (status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) { |
352 | debug("%s timeout %x\n", what, | |
353 | status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT); | |
915ffa52 | 354 | return -ETIMEDOUT; |
e24ea55c | 355 | } |
e24ea55c IC |
356 | } while (!(status & done_bit)); |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
034e226b SG |
361 | static int sunxi_mmc_send_cmd_common(struct sunxi_mmc_priv *priv, |
362 | struct mmc *mmc, struct mmc_cmd *cmd, | |
363 | struct mmc_data *data) | |
e24ea55c | 364 | { |
e24ea55c IC |
365 | unsigned int cmdval = SUNXI_MMC_CMD_START; |
366 | unsigned int timeout_msecs; | |
367 | int error = 0; | |
368 | unsigned int status = 0; | |
e24ea55c IC |
369 | unsigned int bytecnt = 0; |
370 | ||
3f5af12a | 371 | if (priv->fatal_err) |
e24ea55c IC |
372 | return -1; |
373 | if (cmd->resp_type & MMC_RSP_BUSY) | |
374 | debug("mmc cmd %d check rsp busy\n", cmd->cmdidx); | |
375 | if (cmd->cmdidx == 12) | |
376 | return 0; | |
377 | ||
378 | if (!cmd->cmdidx) | |
379 | cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ; | |
380 | if (cmd->resp_type & MMC_RSP_PRESENT) | |
381 | cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE; | |
382 | if (cmd->resp_type & MMC_RSP_136) | |
383 | cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE; | |
384 | if (cmd->resp_type & MMC_RSP_CRC) | |
385 | cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC; | |
386 | ||
387 | if (data) { | |
0ea5a04f | 388 | if ((u32)(long)data->dest & 0x3) { |
e24ea55c IC |
389 | error = -1; |
390 | goto out; | |
391 | } | |
392 | ||
393 | cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER; | |
394 | if (data->flags & MMC_DATA_WRITE) | |
395 | cmdval |= SUNXI_MMC_CMD_WRITE; | |
396 | if (data->blocks > 1) | |
397 | cmdval |= SUNXI_MMC_CMD_AUTO_STOP; | |
3f5af12a SG |
398 | writel(data->blocksize, &priv->reg->blksz); |
399 | writel(data->blocks * data->blocksize, &priv->reg->bytecnt); | |
e24ea55c IC |
400 | } |
401 | ||
3f5af12a | 402 | debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", priv->mmc_no, |
e24ea55c | 403 | cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg); |
3f5af12a | 404 | writel(cmd->cmdarg, &priv->reg->arg); |
e24ea55c IC |
405 | |
406 | if (!data) | |
3f5af12a | 407 | writel(cmdval | cmd->cmdidx, &priv->reg->cmd); |
e24ea55c IC |
408 | |
409 | /* | |
410 | * transfer data and check status | |
411 | * STATREG[2] : FIFO empty | |
412 | * STATREG[3] : FIFO full | |
413 | */ | |
414 | if (data) { | |
415 | int ret = 0; | |
416 | ||
417 | bytecnt = data->blocksize * data->blocks; | |
418 | debug("trans data %d bytes\n", bytecnt); | |
3f5af12a | 419 | writel(cmdval | cmd->cmdidx, &priv->reg->cmd); |
034e226b | 420 | ret = mmc_trans_data_by_cpu(priv, mmc, data); |
e24ea55c | 421 | if (ret) { |
3f5af12a | 422 | error = readl(&priv->reg->rint) & |
e24ea55c | 423 | SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT; |
915ffa52 | 424 | error = -ETIMEDOUT; |
e24ea55c IC |
425 | goto out; |
426 | } | |
427 | } | |
428 | ||
034e226b SG |
429 | error = mmc_rint_wait(priv, mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, |
430 | "cmd"); | |
e24ea55c IC |
431 | if (error) |
432 | goto out; | |
433 | ||
434 | if (data) { | |
b6ae6765 | 435 | timeout_msecs = 120; |
e24ea55c | 436 | debug("cacl timeout %x msec\n", timeout_msecs); |
034e226b | 437 | error = mmc_rint_wait(priv, mmc, timeout_msecs, |
e24ea55c IC |
438 | data->blocks > 1 ? |
439 | SUNXI_MMC_RINT_AUTO_COMMAND_DONE : | |
440 | SUNXI_MMC_RINT_DATA_OVER, | |
441 | "data"); | |
442 | if (error) | |
443 | goto out; | |
444 | } | |
445 | ||
446 | if (cmd->resp_type & MMC_RSP_BUSY) { | |
5ff8e548 | 447 | unsigned long start = get_timer(0); |
e24ea55c | 448 | timeout_msecs = 2000; |
5ff8e548 | 449 | |
e24ea55c | 450 | do { |
3f5af12a | 451 | status = readl(&priv->reg->status); |
5ff8e548 | 452 | if (get_timer(start) > timeout_msecs) { |
e24ea55c | 453 | debug("busy timeout\n"); |
915ffa52 | 454 | error = -ETIMEDOUT; |
e24ea55c IC |
455 | goto out; |
456 | } | |
e24ea55c IC |
457 | } while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY); |
458 | } | |
459 | ||
460 | if (cmd->resp_type & MMC_RSP_136) { | |
3f5af12a SG |
461 | cmd->response[0] = readl(&priv->reg->resp3); |
462 | cmd->response[1] = readl(&priv->reg->resp2); | |
463 | cmd->response[2] = readl(&priv->reg->resp1); | |
464 | cmd->response[3] = readl(&priv->reg->resp0); | |
e24ea55c IC |
465 | debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n", |
466 | cmd->response[3], cmd->response[2], | |
467 | cmd->response[1], cmd->response[0]); | |
468 | } else { | |
3f5af12a | 469 | cmd->response[0] = readl(&priv->reg->resp0); |
e24ea55c IC |
470 | debug("mmc resp 0x%08x\n", cmd->response[0]); |
471 | } | |
472 | out: | |
e24ea55c | 473 | if (error < 0) { |
3f5af12a | 474 | writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); |
034e226b | 475 | mmc_update_clk(priv); |
e24ea55c | 476 | } |
3f5af12a SG |
477 | writel(0xffffffff, &priv->reg->rint); |
478 | writel(readl(&priv->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET, | |
479 | &priv->reg->gctrl); | |
e24ea55c IC |
480 | |
481 | return error; | |
482 | } | |
483 | ||
dd27918c | 484 | #if !CONFIG_IS_ENABLED(DM_MMC) |
034e226b SG |
485 | static int sunxi_mmc_set_ios_legacy(struct mmc *mmc) |
486 | { | |
487 | struct sunxi_mmc_priv *priv = mmc->priv; | |
488 | ||
489 | return sunxi_mmc_set_ios_common(priv, mmc); | |
490 | } | |
491 | ||
492 | static int sunxi_mmc_send_cmd_legacy(struct mmc *mmc, struct mmc_cmd *cmd, | |
493 | struct mmc_data *data) | |
494 | { | |
495 | struct sunxi_mmc_priv *priv = mmc->priv; | |
496 | ||
497 | return sunxi_mmc_send_cmd_common(priv, mmc, cmd, data); | |
498 | } | |
499 | ||
500 | static int sunxi_mmc_getcd_legacy(struct mmc *mmc) | |
cd82113a | 501 | { |
3f5af12a | 502 | struct sunxi_mmc_priv *priv = mmc->priv; |
967325fe | 503 | int cd_pin; |
cd82113a | 504 | |
3f5af12a | 505 | cd_pin = sunxi_mmc_getcd_gpio(priv->mmc_no); |
90641f82 | 506 | if (cd_pin < 0) |
cd82113a HG |
507 | return 1; |
508 | ||
b0c4ae1a | 509 | return !gpio_get_value(cd_pin); |
cd82113a HG |
510 | } |
511 | ||
e24ea55c | 512 | static const struct mmc_ops sunxi_mmc_ops = { |
034e226b SG |
513 | .send_cmd = sunxi_mmc_send_cmd_legacy, |
514 | .set_ios = sunxi_mmc_set_ios_legacy, | |
5abdb156 | 515 | .init = sunxi_mmc_core_init, |
034e226b | 516 | .getcd = sunxi_mmc_getcd_legacy, |
e24ea55c IC |
517 | }; |
518 | ||
e79c7c88 | 519 | struct mmc *sunxi_mmc_init(int sdc_no) |
e24ea55c | 520 | { |
ec73d960 | 521 | struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
034e226b SG |
522 | struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; |
523 | struct mmc_config *cfg = &priv->cfg; | |
ec73d960 | 524 | int ret; |
e24ea55c | 525 | |
034e226b | 526 | memset(priv, '\0', sizeof(struct sunxi_mmc_priv)); |
e24ea55c IC |
527 | |
528 | cfg->name = "SUNXI SD/MMC"; | |
529 | cfg->ops = &sunxi_mmc_ops; | |
530 | ||
531 | cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; | |
532 | cfg->host_caps = MMC_MODE_4BIT; | |
aaebb900 | 533 | #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_SUN50I_GEN_H6) |
d96ebc46 SS |
534 | if (sdc_no == 2) |
535 | cfg->host_caps = MMC_MODE_8BIT; | |
536 | #endif | |
5a20397b | 537 | cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; |
e24ea55c IC |
538 | cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; |
539 | ||
540 | cfg->f_min = 400000; | |
541 | cfg->f_max = 52000000; | |
542 | ||
967325fe HG |
543 | if (mmc_resource_init(sdc_no) != 0) |
544 | return NULL; | |
545 | ||
ec73d960 SG |
546 | /* config ahb clock */ |
547 | debug("init mmc %d clock and io\n", sdc_no); | |
aaebb900 | 548 | #if !defined(CONFIG_SUN50I_GEN_H6) |
ec73d960 SG |
549 | setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); |
550 | ||
551 | #ifdef CONFIG_SUNXI_GEN_SUN6I | |
552 | /* unassert reset */ | |
553 | setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); | |
554 | #endif | |
555 | #if defined(CONFIG_MACH_SUN9I) | |
556 | /* sun9i has a mmc-common module, also set the gate and reset there */ | |
557 | writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET, | |
558 | SUNXI_MMC_COMMON_BASE + 4 * sdc_no); | |
42956f1b | 559 | #endif |
aaebb900 | 560 | #else /* CONFIG_SUN50I_GEN_H6 */ |
42956f1b IZ |
561 | setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no); |
562 | /* unassert reset */ | |
563 | setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no)); | |
ec73d960 SG |
564 | #endif |
565 | ret = mmc_set_mod_clk(priv, 24000000); | |
566 | if (ret) | |
567 | return NULL; | |
e24ea55c | 568 | |
ead3697d | 569 | return mmc_create(cfg, priv); |
e24ea55c | 570 | } |
dd27918c SG |
571 | #else |
572 | ||
573 | static int sunxi_mmc_set_ios(struct udevice *dev) | |
574 | { | |
c69cda25 | 575 | struct sunxi_mmc_plat *plat = dev_get_plat(dev); |
dd27918c SG |
576 | struct sunxi_mmc_priv *priv = dev_get_priv(dev); |
577 | ||
578 | return sunxi_mmc_set_ios_common(priv, &plat->mmc); | |
579 | } | |
580 | ||
581 | static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, | |
582 | struct mmc_data *data) | |
583 | { | |
c69cda25 | 584 | struct sunxi_mmc_plat *plat = dev_get_plat(dev); |
dd27918c SG |
585 | struct sunxi_mmc_priv *priv = dev_get_priv(dev); |
586 | ||
587 | return sunxi_mmc_send_cmd_common(priv, &plat->mmc, cmd, data); | |
588 | } | |
589 | ||
590 | static int sunxi_mmc_getcd(struct udevice *dev) | |
591 | { | |
592 | struct sunxi_mmc_priv *priv = dev_get_priv(dev); | |
593 | ||
8be4e61d HS |
594 | if (dm_gpio_is_valid(&priv->cd_gpio)) { |
595 | int cd_state = dm_gpio_get_value(&priv->cd_gpio); | |
dd27918c | 596 | |
8be4e61d HS |
597 | return cd_state ^ priv->cd_inverted; |
598 | } | |
dd27918c SG |
599 | return 1; |
600 | } | |
601 | ||
602 | static const struct dm_mmc_ops sunxi_mmc_ops = { | |
603 | .send_cmd = sunxi_mmc_send_cmd, | |
604 | .set_ios = sunxi_mmc_set_ios, | |
605 | .get_cd = sunxi_mmc_getcd, | |
606 | }; | |
607 | ||
608 | static int sunxi_mmc_probe(struct udevice *dev) | |
609 | { | |
610 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | |
c69cda25 | 611 | struct sunxi_mmc_plat *plat = dev_get_plat(dev); |
dd27918c | 612 | struct sunxi_mmc_priv *priv = dev_get_priv(dev); |
c57572eb AP |
613 | struct reset_ctl_bulk reset_bulk; |
614 | struct clk gate_clk; | |
dd27918c SG |
615 | struct mmc_config *cfg = &plat->cfg; |
616 | struct ofnode_phandle_args args; | |
c57572eb | 617 | u32 *ccu_reg; |
dd27918c SG |
618 | int bus_width, ret; |
619 | ||
620 | cfg->name = dev->name; | |
621 | bus_width = dev_read_u32_default(dev, "bus-width", 1); | |
622 | ||
623 | cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; | |
624 | cfg->host_caps = 0; | |
625 | if (bus_width == 8) | |
626 | cfg->host_caps |= MMC_MODE_8BIT; | |
627 | if (bus_width >= 4) | |
628 | cfg->host_caps |= MMC_MODE_4BIT; | |
629 | cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; | |
630 | cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
631 | ||
632 | cfg->f_min = 400000; | |
633 | cfg->f_max = 52000000; | |
634 | ||
635 | priv->reg = (void *)dev_read_addr(dev); | |
e8f37f42 JT |
636 | priv->variant = |
637 | (const struct sunxi_mmc_variant *)dev_get_driver_data(dev); | |
dd27918c SG |
638 | |
639 | /* We don't have a sunxi clock driver so find the clock address here */ | |
640 | ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, | |
641 | 1, &args); | |
642 | if (ret) | |
643 | return ret; | |
e8f37f42 | 644 | ccu_reg = (u32 *)ofnode_get_addr(args.node); |
dd27918c | 645 | |
e8f37f42 JT |
646 | priv->mmc_no = ((uintptr_t)priv->reg - SUNXI_MMC0_BASE) / 0x1000; |
647 | priv->mclkreg = (void *)ccu_reg + | |
648 | (priv->variant->mclk_offset + (priv->mmc_no * 4)); | |
c57572eb AP |
649 | |
650 | ret = clk_get_by_name(dev, "ahb", &gate_clk); | |
651 | if (!ret) | |
652 | clk_enable(&gate_clk); | |
653 | ||
654 | ret = reset_get_bulk(dev, &reset_bulk); | |
655 | if (!ret) | |
656 | reset_deassert_bulk(&reset_bulk); | |
dd27918c SG |
657 | |
658 | ret = mmc_set_mod_clk(priv, 24000000); | |
659 | if (ret) | |
660 | return ret; | |
661 | ||
662 | /* This GPIO is optional */ | |
4233698d AP |
663 | if (!dev_read_bool(dev, "non-removable") && |
664 | !gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, | |
dd27918c SG |
665 | GPIOD_IS_IN)) { |
666 | int cd_pin = gpio_get_number(&priv->cd_gpio); | |
667 | ||
668 | sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); | |
669 | } | |
670 | ||
8be4e61d HS |
671 | /* Check if card detect is inverted */ |
672 | priv->cd_inverted = dev_read_bool(dev, "cd-inverted"); | |
673 | ||
dd27918c SG |
674 | upriv->mmc = &plat->mmc; |
675 | ||
676 | /* Reset controller */ | |
677 | writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); | |
678 | udelay(1000); | |
679 | ||
680 | return 0; | |
681 | } | |
682 | ||
683 | static int sunxi_mmc_bind(struct udevice *dev) | |
684 | { | |
c69cda25 | 685 | struct sunxi_mmc_plat *plat = dev_get_plat(dev); |
dd27918c SG |
686 | |
687 | return mmc_bind(dev, &plat->mmc, &plat->cfg); | |
688 | } | |
689 | ||
e8f37f42 | 690 | static const struct sunxi_mmc_variant sun4i_a10_variant = { |
e8f37f42 JT |
691 | .mclk_offset = 0x88, |
692 | }; | |
693 | ||
3c8c7da6 JT |
694 | static const struct sunxi_mmc_variant sun9i_a80_variant = { |
695 | .mclk_offset = 0x410, | |
696 | }; | |
697 | ||
9e233382 JT |
698 | static const struct sunxi_mmc_variant sun50i_h6_variant = { |
699 | .mclk_offset = 0x830, | |
700 | }; | |
701 | ||
dd27918c | 702 | static const struct udevice_id sunxi_mmc_ids[] = { |
e8f37f42 JT |
703 | { |
704 | .compatible = "allwinner,sun4i-a10-mmc", | |
705 | .data = (ulong)&sun4i_a10_variant, | |
706 | }, | |
707 | { | |
708 | .compatible = "allwinner,sun5i-a13-mmc", | |
709 | .data = (ulong)&sun4i_a10_variant, | |
710 | }, | |
711 | { | |
712 | .compatible = "allwinner,sun7i-a20-mmc", | |
713 | .data = (ulong)&sun4i_a10_variant, | |
714 | }, | |
a1925a64 JT |
715 | { |
716 | .compatible = "allwinner,sun8i-a83t-emmc", | |
717 | .data = (ulong)&sun4i_a10_variant, | |
718 | }, | |
3c8c7da6 JT |
719 | { |
720 | .compatible = "allwinner,sun9i-a80-mmc", | |
721 | .data = (ulong)&sun9i_a80_variant, | |
722 | }, | |
a1925a64 JT |
723 | { |
724 | .compatible = "allwinner,sun50i-a64-mmc", | |
725 | .data = (ulong)&sun4i_a10_variant, | |
726 | }, | |
727 | { | |
728 | .compatible = "allwinner,sun50i-a64-emmc", | |
729 | .data = (ulong)&sun4i_a10_variant, | |
730 | }, | |
9e233382 JT |
731 | { |
732 | .compatible = "allwinner,sun50i-h6-mmc", | |
733 | .data = (ulong)&sun50i_h6_variant, | |
734 | }, | |
735 | { | |
736 | .compatible = "allwinner,sun50i-h6-emmc", | |
737 | .data = (ulong)&sun50i_h6_variant, | |
738 | }, | |
e8f37f42 | 739 | { /* sentinel */ } |
dd27918c SG |
740 | }; |
741 | ||
742 | U_BOOT_DRIVER(sunxi_mmc_drv) = { | |
743 | .name = "sunxi_mmc", | |
744 | .id = UCLASS_MMC, | |
745 | .of_match = sunxi_mmc_ids, | |
746 | .bind = sunxi_mmc_bind, | |
747 | .probe = sunxi_mmc_probe, | |
748 | .ops = &sunxi_mmc_ops, | |
caa4daa2 | 749 | .plat_auto = sizeof(struct sunxi_mmc_plat), |
41575d8e | 750 | .priv_auto = sizeof(struct sunxi_mmc_priv), |
dd27918c SG |
751 | }; |
752 | #endif |