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