]>
Commit | Line | Data |
---|---|---|
23b93e1d MW |
1 | /* |
2 | * ARM PrimeCell MultiMedia Card Interface - PL180 | |
3 | * | |
4 | * Copyright (C) ST-Ericsson SA 2010 | |
5 | * | |
6 | * Author: Ulf Hansson <ulf.hansson@stericsson.com> | |
7 | * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com> | |
8 | * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org> | |
9 | * | |
1a459660 | 10 | * SPDX-License-Identifier: GPL-2.0+ |
23b93e1d MW |
11 | */ |
12 | ||
13 | /* #define DEBUG */ | |
14 | ||
23b93e1d MW |
15 | #include "common.h" |
16 | #include <errno.h> | |
3c0dbed2 | 17 | #include <malloc.h> |
23b93e1d | 18 | #include <mmc.h> |
3c0dbed2 | 19 | |
23b93e1d | 20 | #include "arm_pl180_mmci.h" |
3c0dbed2 PC |
21 | |
22 | #include <asm/io.h> | |
23 | ||
24 | #ifdef CONFIG_DM_MMC | |
25 | #include <dm.h> | |
26 | DECLARE_GLOBAL_DATA_PTR; | |
27 | ||
28 | #define MMC_CLOCK_MAX 48000000 | |
29 | #define MMC_CLOCK_MIN 400000 | |
30 | ||
31 | struct arm_pl180_mmc_plat { | |
32 | struct mmc_config cfg; | |
33 | struct mmc mmc; | |
34 | }; | |
35 | #endif | |
23b93e1d | 36 | |
23b93e1d MW |
37 | static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) |
38 | { | |
39 | u32 hoststatus, statusmask; | |
10ed93dc | 40 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
41 | |
42 | statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; | |
43 | if ((cmd->resp_type & MMC_RSP_PRESENT)) | |
44 | statusmask |= SDI_STA_CMDREND; | |
45 | else | |
46 | statusmask |= SDI_STA_CMDSENT; | |
47 | ||
48 | do | |
49 | hoststatus = readl(&host->base->status) & statusmask; | |
50 | while (!hoststatus); | |
51 | ||
52 | writel(statusmask, &host->base->status_clear); | |
53 | if (hoststatus & SDI_STA_CTIMEOUT) { | |
10ed93dc | 54 | debug("CMD%d time out\n", cmd->cmdidx); |
915ffa52 | 55 | return -ETIMEDOUT; |
23b93e1d | 56 | } else if ((hoststatus & SDI_STA_CCRCFAIL) && |
95b01c47 | 57 | (cmd->resp_type & MMC_RSP_CRC)) { |
23b93e1d MW |
58 | printf("CMD%d CRC error\n", cmd->cmdidx); |
59 | return -EILSEQ; | |
60 | } | |
61 | ||
62 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
63 | cmd->response[0] = readl(&host->base->response0); | |
64 | cmd->response[1] = readl(&host->base->response1); | |
65 | cmd->response[2] = readl(&host->base->response2); | |
66 | cmd->response[3] = readl(&host->base->response3); | |
67 | debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " | |
68 | "response[2]:0x%08X, response[3]:0x%08X\n", | |
69 | cmd->cmdidx, cmd->response[0], cmd->response[1], | |
70 | cmd->response[2], cmd->response[3]); | |
71 | } | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | /* send command to the mmc card and wait for results */ | |
77 | static int do_command(struct mmc *dev, struct mmc_cmd *cmd) | |
78 | { | |
79 | int result; | |
80 | u32 sdi_cmd = 0; | |
10ed93dc | 81 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
82 | |
83 | sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); | |
84 | ||
85 | if (cmd->resp_type) { | |
86 | sdi_cmd |= SDI_CMD_WAITRESP; | |
87 | if (cmd->resp_type & MMC_RSP_136) | |
88 | sdi_cmd |= SDI_CMD_LONGRESP; | |
89 | } | |
90 | ||
91 | writel((u32)cmd->cmdarg, &host->base->argument); | |
92 | udelay(COMMAND_REG_DELAY); | |
93 | writel(sdi_cmd, &host->base->command); | |
94 | result = wait_for_command_end(dev, cmd); | |
95 | ||
96 | /* After CMD2 set RCA to a none zero value. */ | |
97 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) | |
98 | dev->rca = 10; | |
99 | ||
100 | /* After CMD3 open drain is switched off and push pull is used. */ | |
101 | if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { | |
102 | u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; | |
103 | writel(sdi_pwr, &host->base->power); | |
104 | } | |
105 | ||
106 | return result; | |
107 | } | |
108 | ||
109 | static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) | |
110 | { | |
111 | u32 *tempbuff = dest; | |
23b93e1d | 112 | u64 xfercount = blkcount * blksize; |
10ed93dc | 113 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
114 | u32 status, status_err; |
115 | ||
116 | debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
117 | ||
118 | status = readl(&host->base->status); | |
119 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
120 | SDI_STA_RXOVERR); | |
23b93e1d MW |
121 | while ((!status_err) && (xfercount >= sizeof(u32))) { |
122 | if (status & SDI_STA_RXDAVL) { | |
123 | *(tempbuff) = readl(&host->base->fifo); | |
124 | tempbuff++; | |
125 | xfercount -= sizeof(u32); | |
126 | } | |
127 | status = readl(&host->base->status); | |
128 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | | |
129 | SDI_STA_RXOVERR); | |
130 | } | |
131 | ||
132 | status_err = status & | |
133 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
134 | SDI_STA_RXOVERR); | |
135 | while (!status_err) { | |
136 | status = readl(&host->base->status); | |
137 | status_err = status & | |
138 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | | |
139 | SDI_STA_RXOVERR); | |
140 | } | |
141 | ||
142 | if (status & SDI_STA_DTIMEOUT) { | |
143 | printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", | |
144 | xfercount, status); | |
145 | return -ETIMEDOUT; | |
146 | } else if (status & SDI_STA_DCRCFAIL) { | |
147 | printf("Read data bytes CRC error: 0x%x\n", status); | |
148 | return -EILSEQ; | |
149 | } else if (status & SDI_STA_RXOVERR) { | |
150 | printf("Read data RX overflow error\n"); | |
151 | return -EIO; | |
152 | } | |
153 | ||
154 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
155 | ||
156 | if (xfercount) { | |
157 | printf("Read data error, xfercount: %llu\n", xfercount); | |
158 | return -ENOBUFS; | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) | |
165 | { | |
166 | u32 *tempbuff = src; | |
167 | int i; | |
168 | u64 xfercount = blkcount * blksize; | |
10ed93dc | 169 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
170 | u32 status, status_err; |
171 | ||
172 | debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); | |
173 | ||
174 | status = readl(&host->base->status); | |
175 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
176 | while (!status_err && xfercount) { | |
177 | if (status & SDI_STA_TXFIFOBW) { | |
178 | if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { | |
179 | for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) | |
180 | writel(*(tempbuff + i), | |
181 | &host->base->fifo); | |
182 | tempbuff += SDI_FIFO_BURST_SIZE; | |
183 | xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); | |
184 | } else { | |
185 | while (xfercount >= sizeof(u32)) { | |
186 | writel(*(tempbuff), &host->base->fifo); | |
187 | tempbuff++; | |
188 | xfercount -= sizeof(u32); | |
189 | } | |
190 | } | |
191 | } | |
192 | status = readl(&host->base->status); | |
193 | status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); | |
194 | } | |
195 | ||
196 | status_err = status & | |
197 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
198 | while (!status_err) { | |
199 | status = readl(&host->base->status); | |
200 | status_err = status & | |
201 | (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); | |
202 | } | |
203 | ||
204 | if (status & SDI_STA_DTIMEOUT) { | |
205 | printf("Write data timed out, xfercount:%llu,status:0x%08X\n", | |
206 | xfercount, status); | |
207 | return -ETIMEDOUT; | |
208 | } else if (status & SDI_STA_DCRCFAIL) { | |
209 | printf("Write data CRC error\n"); | |
210 | return -EILSEQ; | |
211 | } | |
212 | ||
213 | writel(SDI_ICR_MASK, &host->base->status_clear); | |
214 | ||
215 | if (xfercount) { | |
216 | printf("Write data error, xfercount:%llu", xfercount); | |
217 | return -ENOBUFS; | |
218 | } | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static int do_data_transfer(struct mmc *dev, | |
224 | struct mmc_cmd *cmd, | |
225 | struct mmc_data *data) | |
226 | { | |
227 | int error = -ETIMEDOUT; | |
10ed93dc | 228 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
229 | u32 blksz = 0; |
230 | u32 data_ctrl = 0; | |
231 | u32 data_len = (u32) (data->blocks * data->blocksize); | |
232 | ||
10ed93dc JR |
233 | if (!host->version2) { |
234 | blksz = (ffs(data->blocksize) - 1); | |
235 | data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); | |
236 | } else { | |
237 | blksz = data->blocksize; | |
238 | data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); | |
239 | } | |
240 | data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE; | |
23b93e1d MW |
241 | |
242 | writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); | |
243 | writel(data_len, &host->base->datalength); | |
244 | udelay(DATA_REG_DELAY); | |
245 | ||
246 | if (data->flags & MMC_DATA_READ) { | |
247 | data_ctrl |= SDI_DCTRL_DTDIR_IN; | |
248 | writel(data_ctrl, &host->base->datactrl); | |
249 | ||
250 | error = do_command(dev, cmd); | |
251 | if (error) | |
252 | return error; | |
253 | ||
254 | error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, | |
255 | (u32)data->blocksize); | |
256 | } else if (data->flags & MMC_DATA_WRITE) { | |
257 | error = do_command(dev, cmd); | |
258 | if (error) | |
259 | return error; | |
260 | ||
261 | writel(data_ctrl, &host->base->datactrl); | |
262 | error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, | |
10ed93dc | 263 | (u32)data->blocksize); |
23b93e1d MW |
264 | } |
265 | ||
266 | return error; | |
267 | } | |
268 | ||
269 | static int host_request(struct mmc *dev, | |
270 | struct mmc_cmd *cmd, | |
271 | struct mmc_data *data) | |
272 | { | |
273 | int result; | |
274 | ||
275 | if (data) | |
276 | result = do_data_transfer(dev, cmd, data); | |
277 | else | |
278 | result = do_command(dev, cmd); | |
279 | ||
280 | return result; | |
281 | } | |
282 | ||
07b0b9c0 | 283 | static int host_set_ios(struct mmc *dev) |
23b93e1d | 284 | { |
10ed93dc | 285 | struct pl180_mmc_host *host = dev->priv; |
23b93e1d MW |
286 | u32 sdi_clkcr; |
287 | ||
288 | sdi_clkcr = readl(&host->base->clock); | |
289 | ||
290 | /* Ramp up the clock rate */ | |
291 | if (dev->clock) { | |
292 | u32 clkdiv = 0; | |
10ed93dc | 293 | u32 tmp_clock; |
23b93e1d | 294 | |
93bfd616 | 295 | if (dev->clock >= dev->cfg->f_max) { |
10ed93dc | 296 | clkdiv = 0; |
93bfd616 | 297 | dev->clock = dev->cfg->f_max; |
10ed93dc JR |
298 | } else { |
299 | clkdiv = (host->clock_in / dev->clock) - 2; | |
300 | } | |
23b93e1d | 301 | |
10ed93dc JR |
302 | tmp_clock = host->clock_in / (clkdiv + 2); |
303 | while (tmp_clock > dev->clock) { | |
304 | clkdiv++; | |
305 | tmp_clock = host->clock_in / (clkdiv + 2); | |
306 | } | |
23b93e1d MW |
307 | |
308 | if (clkdiv > SDI_CLKCR_CLKDIV_MASK) | |
309 | clkdiv = SDI_CLKCR_CLKDIV_MASK; | |
310 | ||
10ed93dc JR |
311 | tmp_clock = host->clock_in / (clkdiv + 2); |
312 | dev->clock = tmp_clock; | |
23b93e1d MW |
313 | sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); |
314 | sdi_clkcr |= clkdiv; | |
315 | } | |
316 | ||
317 | /* Set the bus width */ | |
318 | if (dev->bus_width) { | |
319 | u32 buswidth = 0; | |
320 | ||
321 | switch (dev->bus_width) { | |
322 | case 1: | |
323 | buswidth |= SDI_CLKCR_WIDBUS_1; | |
324 | break; | |
325 | case 4: | |
326 | buswidth |= SDI_CLKCR_WIDBUS_4; | |
327 | break; | |
10ed93dc JR |
328 | case 8: |
329 | buswidth |= SDI_CLKCR_WIDBUS_8; | |
330 | break; | |
23b93e1d | 331 | default: |
10ed93dc | 332 | printf("Invalid bus width: %d\n", dev->bus_width); |
23b93e1d MW |
333 | break; |
334 | } | |
335 | sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); | |
336 | sdi_clkcr |= buswidth; | |
337 | } | |
338 | ||
339 | writel(sdi_clkcr, &host->base->clock); | |
340 | udelay(CLK_CHANGE_DELAY); | |
07b0b9c0 JC |
341 | |
342 | return 0; | |
23b93e1d MW |
343 | } |
344 | ||
3c0dbed2 PC |
345 | #ifndef CONFIG_DM_MMC |
346 | /* MMC uses open drain drivers in the enumeration phase */ | |
347 | static int mmc_host_reset(struct mmc *dev) | |
348 | { | |
349 | struct pl180_mmc_host *host = dev->priv; | |
350 | ||
351 | writel(host->pwr_init, &host->base->power); | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
ab769f22 PA |
356 | static const struct mmc_ops arm_pl180_mmci_ops = { |
357 | .send_cmd = host_request, | |
358 | .set_ios = host_set_ios, | |
359 | .init = mmc_host_reset, | |
360 | }; | |
3c0dbed2 | 361 | #endif |
ab769f22 | 362 | |
23b93e1d MW |
363 | /* |
364 | * mmc_host_init - initialize the mmc controller. | |
365 | * Set initial clock and power for mmc slot. | |
366 | * Initialize mmc struct and register with mmc framework. | |
367 | */ | |
cb0060e8 | 368 | int arm_pl180_mmci_init(struct pl180_mmc_host *host, struct mmc **mmc) |
23b93e1d | 369 | { |
23b93e1d MW |
370 | u32 sdi_u32; |
371 | ||
10ed93dc JR |
372 | writel(host->pwr_init, &host->base->power); |
373 | writel(host->clkdiv_init, &host->base->clock); | |
23b93e1d MW |
374 | udelay(CLK_CHANGE_DELAY); |
375 | ||
376 | /* Disable mmc interrupts */ | |
377 | sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; | |
378 | writel(sdi_u32, &host->base->mask0); | |
93bfd616 PA |
379 | |
380 | host->cfg.name = host->name; | |
3c0dbed2 | 381 | #ifndef CONFIG_DM_MMC |
93bfd616 | 382 | host->cfg.ops = &arm_pl180_mmci_ops; |
3c0dbed2 | 383 | #endif |
93bfd616 PA |
384 | /* TODO remove the duplicates */ |
385 | host->cfg.host_caps = host->caps; | |
386 | host->cfg.voltages = host->voltages; | |
387 | host->cfg.f_min = host->clock_min; | |
388 | host->cfg.f_max = host->clock_max; | |
389 | if (host->b_max != 0) | |
390 | host->cfg.b_max = host->b_max; | |
391 | else | |
392 | host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; | |
393 | ||
cb0060e8 PC |
394 | *mmc = mmc_create(&host->cfg, host); |
395 | if (!*mmc) | |
93bfd616 PA |
396 | return -1; |
397 | ||
cb0060e8 PC |
398 | debug("registered mmc interface number is:%d\n", |
399 | (*mmc)->block_dev.devnum); | |
23b93e1d MW |
400 | |
401 | return 0; | |
402 | } | |
3c0dbed2 PC |
403 | |
404 | #ifdef CONFIG_DM_MMC | |
405 | static int arm_pl180_mmc_probe(struct udevice *dev) | |
406 | { | |
407 | struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); | |
408 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | |
409 | struct mmc *mmc = &pdata->mmc; | |
410 | struct pl180_mmc_host *host = mmc->priv; | |
411 | int ret; | |
412 | ||
413 | strcpy(host->name, "MMC"); | |
414 | host->pwr_init = INIT_PWR; | |
415 | host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN | | |
416 | SDI_CLKCR_HWFC_EN; | |
417 | host->voltages = VOLTAGE_WINDOW_SD; | |
418 | host->caps = 0; | |
419 | host->clock_in = MMC_CLOCK_MAX; | |
420 | host->clock_min = MMC_CLOCK_MIN; | |
421 | host->clock_max = dev_read_u32_default(dev, "max-frequency", | |
422 | MMC_CLOCK_MAX); | |
423 | host->version2 = dev_get_driver_data(dev); | |
424 | ret = arm_pl180_mmci_init(host, &mmc); | |
425 | if (ret) { | |
426 | dev_err(dev, "arm_pl180_mmci init failed\n"); | |
427 | return ret; | |
428 | } | |
429 | ||
430 | mmc->dev = dev; | |
431 | dev->priv = host; | |
432 | upriv->mmc = mmc; | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | static int dm_host_request(struct udevice *dev, struct mmc_cmd *cmd, | |
438 | struct mmc_data *data) | |
439 | { | |
440 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
441 | ||
442 | return host_request(mmc, cmd, data); | |
443 | } | |
444 | ||
445 | static int dm_host_set_ios(struct udevice *dev) | |
446 | { | |
447 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
448 | ||
449 | return host_set_ios(mmc); | |
450 | } | |
451 | ||
452 | static const struct dm_mmc_ops arm_pl180_dm_mmc_ops = { | |
453 | .send_cmd = dm_host_request, | |
454 | .set_ios = dm_host_set_ios, | |
455 | }; | |
456 | ||
457 | static int arm_pl180_mmc_ofdata_to_platdata(struct udevice *dev) | |
458 | { | |
459 | struct arm_pl180_mmc_plat *pdata = dev_get_platdata(dev); | |
460 | struct mmc *mmc = &pdata->mmc; | |
461 | struct pl180_mmc_host *host = mmc->priv; | |
462 | fdt_addr_t addr; | |
463 | ||
464 | addr = devfdt_get_addr(dev); | |
465 | if (addr == FDT_ADDR_T_NONE) | |
466 | return -EINVAL; | |
467 | ||
468 | host->base = (void *)addr; | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
473 | static const struct udevice_id arm_pl180_mmc_match[] = { | |
474 | { .compatible = "st,stm32f4xx-sdio", .data = VERSION1 }, | |
475 | { /* sentinel */ } | |
476 | }; | |
477 | ||
478 | U_BOOT_DRIVER(arm_pl180_mmc) = { | |
479 | .name = "arm_pl180_mmc", | |
480 | .id = UCLASS_MMC, | |
481 | .of_match = arm_pl180_mmc_match, | |
482 | .ops = &arm_pl180_dm_mmc_ops, | |
483 | .probe = arm_pl180_mmc_probe, | |
484 | .ofdata_to_platdata = arm_pl180_mmc_ofdata_to_platdata, | |
485 | .priv_auto_alloc_size = sizeof(struct pl180_mmc_host), | |
486 | .platdata_auto_alloc_size = sizeof(struct arm_pl180_mmc_plat), | |
487 | }; | |
488 | #endif |