Blame view
drivers/mmc/sdhci.c
15.5 KB
af62a5578 MMC: add sdhci ge... |
1 2 3 4 |
/* * Copyright 2011, Marvell Semiconductor Inc. * Lei Wen <leiwen@marvell.com> * |
1a4596601 Add GPL-2.0+ SPDX... |
5 |
* SPDX-License-Identifier: GPL-2.0+ |
af62a5578 MMC: add sdhci ge... |
6 7 8 9 10 11 |
* * Back ported to the 8xx platform (from the 8260 platform) by * Murray.Jensen@cmst.csiro.au, 27-Jan-01. */ #include <common.h> |
2a809093f dm: mmc: sdhci: R... |
12 |
#include <errno.h> |
af62a5578 MMC: add sdhci ge... |
13 14 15 |
#include <malloc.h> #include <mmc.h> #include <sdhci.h> |
492d3223b mmc: sdhci.c: Add... |
16 17 18 |
#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER; #else |
af62a5578 MMC: add sdhci ge... |
19 |
void *aligned_buffer; |
492d3223b mmc: sdhci.c: Add... |
20 |
#endif |
af62a5578 MMC: add sdhci ge... |
21 22 23 24 25 26 27 28 29 30 |
static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; /* Wait max 100 ms */ timeout = 100; sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { if (timeout == 0) { |
30e6d979f mmc: Minor cleanu... |
31 32 33 |
printf("%s: Reset 0x%x never completed. ", __func__, (int)mask); |
af62a5578 MMC: add sdhci ge... |
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
return; } timeout--; udelay(1000); } } static void sdhci_cmd_done(struct sdhci_host *host, struct mmc_cmd *cmd) { int i; if (cmd->resp_type & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i = 0; i < 4; i++) { cmd->response[i] = sdhci_readl(host, SDHCI_RESPONSE + (3-i)*4) << 8; if (i != 3) cmd->response[i] |= sdhci_readb(host, SDHCI_RESPONSE + (3-i)*4-1); } } else { cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE); } } static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) { int i; char *offs; for (i = 0; i < data->blocksize; i += 4) { offs = data->dest + i; if (data->flags == MMC_DATA_READ) *(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER); else sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER); } } static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int start_addr) { |
a004abde8 mmc: sdhci: add t... |
74 |
unsigned int stat, rdy, mask, timeout, block = 0; |
7dde50d70 mmc: sdhci: Wait ... |
75 |
bool transfer_done = false; |
45a68fe26 mmc: move some SD... |
76 |
#ifdef CONFIG_MMC_SDHCI_SDMA |
804c7f422 mmc: sdhci: add t... |
77 |
unsigned char ctrl; |
2c011847c Fix wrong sdhci h... |
78 |
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); |
804c7f422 mmc: sdhci: add t... |
79 |
ctrl &= ~SDHCI_CTRL_DMA_MASK; |
2c011847c Fix wrong sdhci h... |
80 |
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); |
804c7f422 mmc: sdhci: add t... |
81 |
#endif |
af62a5578 MMC: add sdhci ge... |
82 |
|
5d48e4224 mmc: sdhci: incre... |
83 |
timeout = 1000000; |
af62a5578 MMC: add sdhci ge... |
84 85 86 87 88 |
rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE; do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) { |
61f2e5ee1 mmc: sdhci: chang... |
89 90 91 |
pr_debug("%s: Error detected in status(0x%X)! ", __func__, stat); |
2cb5d67c1 mmc: sdhci: use t... |
92 |
return -EIO; |
af62a5578 MMC: add sdhci ge... |
93 |
} |
7dde50d70 mmc: sdhci: Wait ... |
94 |
if (!transfer_done && (stat & rdy)) { |
af62a5578 MMC: add sdhci ge... |
95 96 97 98 99 |
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)) continue; sdhci_writel(host, rdy, SDHCI_INT_STATUS); sdhci_transfer_pio(host, data); data->dest += data->blocksize; |
7dde50d70 mmc: sdhci: Wait ... |
100 101 102 103 104 105 106 107 |
if (++block >= data->blocks) { /* Keep looping until the SDHCI_INT_DATA_END is * cleared, even if we finished sending all the * blocks. */ transfer_done = true; continue; } |
af62a5578 MMC: add sdhci ge... |
108 |
} |
45a68fe26 mmc: move some SD... |
109 |
#ifdef CONFIG_MMC_SDHCI_SDMA |
7dde50d70 mmc: sdhci: Wait ... |
110 |
if (!transfer_done && (stat & SDHCI_INT_DMA_END)) { |
af62a5578 MMC: add sdhci ge... |
111 |
sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); |
3e81c7724 mmc: sdhci: fix s... |
112 |
start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); |
af62a5578 MMC: add sdhci ge... |
113 114 115 116 |
start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); } #endif |
a004abde8 mmc: sdhci: add t... |
117 118 119 |
if (timeout-- > 0) udelay(10); else { |
30e6d979f mmc: Minor cleanu... |
120 121 |
printf("%s: Transfer data timeout ", __func__); |
2cb5d67c1 mmc: sdhci: use t... |
122 |
return -ETIMEDOUT; |
a004abde8 mmc: sdhci: add t... |
123 |
} |
af62a5578 MMC: add sdhci ge... |
124 125 126 |
} while (!(stat & SDHCI_INT_DATA_END)); return 0; } |
56b34bc61 mmc: sdhci: Avoid... |
127 128 129 130 131 |
/* * No command will be sent by driver if card is busy, so driver must wait * for card ready state. * Every time when card is busy after timeout then (last) timeout value will be * increased twice but only if it doesn't exceed global defined maximum. |
65a25b208 mmc: sdhci: drop ... |
132 |
* Each function call will use last timeout value. |
56b34bc61 mmc: sdhci: Avoid... |
133 |
*/ |
65a25b208 mmc: sdhci: drop ... |
134 |
#define SDHCI_CMD_MAX_TIMEOUT 3200 |
d8ce77b28 mmc: sdhci: drop ... |
135 |
#define SDHCI_CMD_DEFAULT_TIMEOUT 100 |
d90bb4393 mmc: increase MMC... |
136 |
#define SDHCI_READ_STATUS_TIMEOUT 1000 |
56b34bc61 mmc: sdhci: Avoid... |
137 |
|
e7881d85a dm: mmc: Drop CON... |
138 |
#ifdef CONFIG_DM_MMC |
ef1e4eda6 dm: mmc: sdhci: S... |
139 140 141 142 143 144 |
static int sdhci_send_command(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else |
6588c78bf sdhci: make local... |
145 |
static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, |
ef1e4eda6 dm: mmc: sdhci: S... |
146 |
struct mmc_data *data) |
af62a5578 MMC: add sdhci ge... |
147 |
{ |
ef1e4eda6 dm: mmc: sdhci: S... |
148 |
#endif |
93bfd6167 mmc: Split mmc st... |
149 |
struct sdhci_host *host = mmc->priv; |
af62a5578 MMC: add sdhci ge... |
150 151 152 153 |
unsigned int stat = 0; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; |
56b34bc61 mmc: sdhci: Avoid... |
154 |
unsigned int time = 0, start_addr = 0; |
19d2e3423 dm: mmc: Convert ... |
155 |
int mmc_dev = mmc_get_blk_desc(mmc)->devnum; |
29905a451 mmc: sdhci: Use t... |
156 |
unsigned start = get_timer(0); |
af62a5578 MMC: add sdhci ge... |
157 |
|
56b34bc61 mmc: sdhci: Avoid... |
158 |
/* Timeout unit - ms */ |
d8ce77b28 mmc: sdhci: drop ... |
159 |
static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; |
af62a5578 MMC: add sdhci ge... |
160 |
|
af62a5578 MMC: add sdhci ge... |
161 162 163 164 165 166 167 168 |
mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { |
56b34bc61 mmc: sdhci: Avoid... |
169 |
if (time >= cmd_timeout) { |
30e6d979f mmc: Minor cleanu... |
170 |
printf("%s: MMC: %d busy ", __func__, mmc_dev); |
65a25b208 mmc: sdhci: drop ... |
171 |
if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { |
56b34bc61 mmc: sdhci: Avoid... |
172 173 174 175 176 177 178 |
cmd_timeout += cmd_timeout; printf("timeout increasing to: %u ms. ", cmd_timeout); } else { puts("timeout. "); |
915ffa521 mmc: use the gene... |
179 |
return -ECOMM; |
56b34bc61 mmc: sdhci: Avoid... |
180 |
} |
af62a5578 MMC: add sdhci ge... |
181 |
} |
56b34bc61 mmc: sdhci: Avoid... |
182 |
time++; |
af62a5578 MMC: add sdhci ge... |
183 184 |
udelay(1000); } |
713e6815d mmc: sdhci: don't... |
185 |
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); |
af62a5578 MMC: add sdhci ge... |
186 187 188 189 190 191 192 |
mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) flags = SDHCI_CMD_RESP_LONG; else if (cmd->resp_type & MMC_RSP_BUSY) { flags = SDHCI_CMD_RESP_SHORT_BUSY; |
17ea3c862 mmc: sdhci: set t... |
193 194 |
if (data) mask |= SDHCI_INT_DATA_END; |
af62a5578 MMC: add sdhci ge... |
195 196 197 198 199 200 201 202 203 |
} else flags = SDHCI_CMD_RESP_SHORT; if (cmd->resp_type & MMC_RSP_CRC) flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; if (data) flags |= SDHCI_CMD_DATA; |
30e6d979f mmc: Minor cleanu... |
204 |
/* Set Transfer mode regarding to data flag */ |
bb7b4ef37 mmc: sdhci: do no... |
205 |
if (data) { |
af62a5578 MMC: add sdhci ge... |
206 207 208 209 210 211 212 213 |
sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; |
45a68fe26 mmc: move some SD... |
214 |
#ifdef CONFIG_MMC_SDHCI_SDMA |
af62a5578 MMC: add sdhci ge... |
215 |
if (data->flags == MMC_DATA_READ) |
3c1fcb770 sdhci: fix warnin... |
216 |
start_addr = (unsigned long)data->dest; |
af62a5578 MMC: add sdhci ge... |
217 |
else |
3c1fcb770 sdhci: fix warnin... |
218 |
start_addr = (unsigned long)data->src; |
af62a5578 MMC: add sdhci ge... |
219 220 221 |
if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && (start_addr & 0x7) != 0x0) { is_aligned = 0; |
3c1fcb770 sdhci: fix warnin... |
222 |
start_addr = (unsigned long)aligned_buffer; |
af62a5578 MMC: add sdhci ge... |
223 224 225 |
if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); } |
492d3223b mmc: sdhci.c: Add... |
226 227 228 229 230 231 232 233 234 235 |
#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) /* * Always use this bounce-buffer when * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined */ is_aligned = 0; start_addr = (unsigned long)aligned_buffer; if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); #endif |
af62a5578 MMC: add sdhci ge... |
236 237 238 239 240 241 242 243 |
sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); |
5e1c23cd3 mmc: sdhci: add t... |
244 245 |
} else if (cmd->resp_type & MMC_RSP_BUSY) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); |
af62a5578 MMC: add sdhci ge... |
246 247 248 |
} sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); |
45a68fe26 mmc: move some SD... |
249 |
#ifdef CONFIG_MMC_SDHCI_SDMA |
bb7b4ef37 mmc: sdhci: do no... |
250 |
if (data) { |
fa7720b21 mmc: sdhci: only ... |
251 252 253 |
trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); flush_cache(start_addr, trans_bytes); } |
af62a5578 MMC: add sdhci ge... |
254 255 |
#endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); |
29905a451 mmc: sdhci: Use t... |
256 |
start = get_timer(0); |
af62a5578 MMC: add sdhci ge... |
257 258 259 260 |
do { stat = sdhci_readl(host, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) break; |
af62a5578 MMC: add sdhci ge... |
261 |
|
bae4a1fdf mmc: sdhci: clean... |
262 263 264 265 266 267 268 |
if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) { if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) { return 0; } else { printf("%s: Timeout for status update! ", __func__); |
915ffa521 mmc: use the gene... |
269 |
return -ETIMEDOUT; |
bae4a1fdf mmc: sdhci: clean... |
270 |
} |
3a6383207 mmc: sdhci: add t... |
271 |
} |
bae4a1fdf mmc: sdhci: clean... |
272 |
} while ((stat & mask) != mask); |
3a6383207 mmc: sdhci: add t... |
273 |
|
af62a5578 MMC: add sdhci ge... |
274 275 276 277 278 279 280 281 |
if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); } else ret = -1; if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); |
13243f2ea mmc: sdhci: Add a... |
282 283 |
if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); |
af62a5578 MMC: add sdhci ge... |
284 285 286 287 288 289 290 291 292 293 294 295 |
stat = sdhci_readl(host, SDHCI_INT_STATUS); sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); if (!ret) { if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !is_aligned && (data->flags == MMC_DATA_READ)) memcpy(data->dest, aligned_buffer, trans_bytes); return 0; } sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); if (stat & SDHCI_INT_TIMEOUT) |
915ffa521 mmc: use the gene... |
296 |
return -ETIMEDOUT; |
af62a5578 MMC: add sdhci ge... |
297 |
else |
915ffa521 mmc: use the gene... |
298 |
return -ECOMM; |
af62a5578 MMC: add sdhci ge... |
299 300 301 302 |
} static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { |
93bfd6167 mmc: Split mmc st... |
303 |
struct sdhci_host *host = mmc->priv; |
899fb9e35 mmc: sdhci: Clear... |
304 |
unsigned int div, clk = 0, timeout; |
af62a5578 MMC: add sdhci ge... |
305 |
|
79667b7b7 mmc: sdhci: Fix t... |
306 307 308 309 310 311 312 313 |
/* Wait max 20 ms */ timeout = 200; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { if (timeout == 0) { printf("%s: Timeout to wait cmd & data inhibit ", __func__); |
2cb5d67c1 mmc: sdhci: use t... |
314 |
return -EBUSY; |
79667b7b7 mmc: sdhci: Fix t... |
315 316 317 318 319 |
} timeout--; udelay(100); } |
899fb9e35 mmc: sdhci: Clear... |
320 |
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); |
af62a5578 MMC: add sdhci ge... |
321 322 323 |
if (clock == 0) return 0; |
113e5dfcd mmc: sdhci: use t... |
324 |
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { |
6dffdbc3a mmc: sdhci: Add t... |
325 326 327 328 329 330 |
/* * Check if the Host Controller supports Programmable Clock * Mode. */ if (host->clk_mul) { for (div = 1; div <= 1024; div++) { |
0e0dcc191 mmc: sdhci: Fix m... |
331 |
if ((host->max_clk / div) <= clock) |
af62a5578 MMC: add sdhci ge... |
332 333 |
break; } |
6dffdbc3a mmc: sdhci: Add t... |
334 335 336 337 338 339 340 341 342 |
/* * Set Programmable Clock Mode in the Clock * Control register. */ clk = SDHCI_PROG_CLOCK_MODE; div--; } else { /* Version 3.00 divisors must be a multiple of 2. */ |
6d0e34bf4 mmc: sdhci: Disti... |
343 |
if (host->max_clk <= clock) { |
6dffdbc3a mmc: sdhci: Add t... |
344 345 346 347 348 |
div = 1; } else { for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { |
6d0e34bf4 mmc: sdhci: Disti... |
349 |
if ((host->max_clk / div) <= clock) |
6dffdbc3a mmc: sdhci: Add t... |
350 351 352 353 |
break; } } div >>= 1; |
af62a5578 MMC: add sdhci ge... |
354 355 356 357 |
} } else { /* Version 2.00 divisors must be a power of 2. */ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { |
6d0e34bf4 mmc: sdhci: Disti... |
358 |
if ((host->max_clk / div) <= clock) |
af62a5578 MMC: add sdhci ge... |
359 360 |
break; } |
6dffdbc3a mmc: sdhci: Add t... |
361 |
div >>= 1; |
af62a5578 MMC: add sdhci ge... |
362 |
} |
af62a5578 MMC: add sdhci ge... |
363 |
|
bf9c4d146 mmc: sdhci: fix N... |
364 |
if (host->ops && host->ops->set_clock) |
62226b686 mmc: sdhci: move ... |
365 |
host->ops->set_clock(host, div); |
b09ed6e4f mmc: s5p_sdhci: a... |
366 |
|
6dffdbc3a mmc: sdhci: Add t... |
367 |
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; |
af62a5578 MMC: add sdhci ge... |
368 369 370 371 372 373 374 375 376 377 |
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 20 ms */ timeout = 20; while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { |
30e6d979f mmc: Minor cleanu... |
378 379 380 |
printf("%s: Internal clock never stabilised. ", __func__); |
2cb5d67c1 mmc: sdhci: use t... |
381 |
return -EBUSY; |
af62a5578 MMC: add sdhci ge... |
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
} timeout--; udelay(1000); } clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); return 0; } static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; if (power != (unsigned short)-1) { switch (1 << power) { case MMC_VDD_165_195: pwr = SDHCI_POWER_180; break; case MMC_VDD_29_30: case MMC_VDD_30_31: pwr = SDHCI_POWER_300; break; case MMC_VDD_32_33: case MMC_VDD_33_34: pwr = SDHCI_POWER_330; break; } } if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); return; } pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); } |
e7881d85a dm: mmc: Drop CON... |
421 |
#ifdef CONFIG_DM_MMC |
ef1e4eda6 dm: mmc: sdhci: S... |
422 423 424 425 |
static int sdhci_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); #else |
07b0b9c00 mmc: change the s... |
426 |
static int sdhci_set_ios(struct mmc *mmc) |
af62a5578 MMC: add sdhci ge... |
427 |
{ |
ef1e4eda6 dm: mmc: sdhci: S... |
428 |
#endif |
af62a5578 MMC: add sdhci ge... |
429 |
u32 ctrl; |
93bfd6167 mmc: Split mmc st... |
430 |
struct sdhci_host *host = mmc->priv; |
af62a5578 MMC: add sdhci ge... |
431 |
|
bf9c4d146 mmc: sdhci: fix N... |
432 |
if (host->ops && host->ops->set_control_reg) |
62226b686 mmc: sdhci: move ... |
433 |
host->ops->set_control_reg(host); |
236bfecff mmc: add the quir... |
434 |
|
af62a5578 MMC: add sdhci ge... |
435 436 437 438 439 440 441 |
if (mmc->clock != host->clock) sdhci_set_clock(mmc, mmc->clock); /* Set bus width */ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (mmc->bus_width == 8) { ctrl &= ~SDHCI_CTRL_4BITBUS; |
113e5dfcd mmc: sdhci: use t... |
442 443 |
if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || (host->quirks & SDHCI_QUIRK_USE_WIDE8)) |
af62a5578 MMC: add sdhci ge... |
444 445 |
ctrl |= SDHCI_CTRL_8BITBUS; } else { |
f88a429f1 mmc: sdhci: fix b... |
446 447 |
if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || (host->quirks & SDHCI_QUIRK_USE_WIDE8)) |
af62a5578 MMC: add sdhci ge... |
448 449 450 451 452 453 454 455 456 457 458 |
ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; } if (mmc->clock > 26000000) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD; |
236bfecff mmc: add the quir... |
459 460 |
if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) ctrl &= ~SDHCI_CTRL_HISPD; |
af62a5578 MMC: add sdhci ge... |
461 |
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); |
07b0b9c00 mmc: change the s... |
462 |
|
210841c69 mmc: sdhci: Add s... |
463 464 465 |
/* If available, call the driver specific "post" set_ios() function */ if (host->ops && host->ops->set_ios_post) host->ops->set_ios_post(host); |
ef1e4eda6 dm: mmc: sdhci: S... |
466 |
return 0; |
af62a5578 MMC: add sdhci ge... |
467 |
} |
6588c78bf sdhci: make local... |
468 |
static int sdhci_init(struct mmc *mmc) |
af62a5578 MMC: add sdhci ge... |
469 |
{ |
93bfd6167 mmc: Split mmc st... |
470 |
struct sdhci_host *host = mmc->priv; |
af62a5578 MMC: add sdhci ge... |
471 |
|
8d549b61d mmc: sdhci: move ... |
472 |
sdhci_reset(host, SDHCI_RESET_ALL); |
af62a5578 MMC: add sdhci ge... |
473 474 475 |
if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) { aligned_buffer = memalign(8, 512*1024); if (!aligned_buffer) { |
30e6d979f mmc: Minor cleanu... |
476 477 478 |
printf("%s: Aligned buffer alloc failed!!! ", __func__); |
2cb5d67c1 mmc: sdhci: use t... |
479 |
return -ENOMEM; |
af62a5578 MMC: add sdhci ge... |
480 481 |
} } |
93bfd6167 mmc: Split mmc st... |
482 |
sdhci_set_power(host, fls(mmc->cfg->voltages) - 1); |
470dcc751 mmc: Add a SDHCI ... |
483 |
|
bf9c4d146 mmc: sdhci: fix N... |
484 |
if (host->ops && host->ops->get_cd) |
6f88a3a5d mmc: sdhci: remov... |
485 |
host->ops->get_cd(host); |
470dcc751 mmc: Add a SDHCI ... |
486 |
|
ce0c1bc13 mmc:sdhci:fix: Ch... |
487 |
/* Enable only interrupts served by the SD controller */ |
30e6d979f mmc: Minor cleanu... |
488 489 |
sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, SDHCI_INT_ENABLE); |
ce0c1bc13 mmc:sdhci:fix: Ch... |
490 491 |
/* Mask all sdhci interrupt sources */ sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); |
af62a5578 MMC: add sdhci ge... |
492 |
|
af62a5578 MMC: add sdhci ge... |
493 494 |
return 0; } |
e7881d85a dm: mmc: Drop CON... |
495 |
#ifdef CONFIG_DM_MMC |
ef1e4eda6 dm: mmc: sdhci: S... |
496 497 498 499 500 501 |
int sdhci_probe(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); return sdhci_init(mmc); } |
ab769f227 mmc: Remove ops f... |
502 |
|
ef1e4eda6 dm: mmc: sdhci: S... |
503 504 505 506 507 |
const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, }; #else |
ab769f227 mmc: Remove ops f... |
508 509 510 511 512 |
static const struct mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, .init = sdhci_init, }; |
ef1e4eda6 dm: mmc: sdhci: S... |
513 |
#endif |
ab769f227 mmc: Remove ops f... |
514 |
|
14bed52d2 mmc: sdhci: remov... |
515 |
int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, |
6d0e34bf4 mmc: sdhci: Disti... |
516 |
u32 f_max, u32 f_min) |
af62a5578 MMC: add sdhci ge... |
517 |
{ |
6dffdbc3a mmc: sdhci: Add t... |
518 |
u32 caps, caps_1; |
14bed52d2 mmc: sdhci: remov... |
519 520 |
caps = sdhci_readl(host, SDHCI_CAPABILITIES); |
15bd09959 mmc: sdhci: move ... |
521 |
|
45a68fe26 mmc: move some SD... |
522 |
#ifdef CONFIG_MMC_SDHCI_SDMA |
15bd09959 mmc: sdhci: move ... |
523 524 525 526 527 528 529 |
if (!(caps & SDHCI_CAN_DO_SDMA)) { printf("%s: Your controller doesn't support SDMA!! ", __func__); return -EINVAL; } #endif |
895549a2d mmc: sdhci: use t... |
530 531 532 533 534 |
if (host->quirks & SDHCI_QUIRK_REG32_RW) host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; else host->version = sdhci_readw(host, SDHCI_HOST_VERSION); |
14bed52d2 mmc: sdhci: remov... |
535 536 |
cfg->name = host->name; |
e7881d85a dm: mmc: Drop CON... |
537 |
#ifndef CONFIG_DM_MMC |
2a809093f dm: mmc: sdhci: R... |
538 |
cfg->ops = &sdhci_ops; |
af62a5578 MMC: add sdhci ge... |
539 |
#endif |
0e0dcc191 mmc: sdhci: Fix m... |
540 541 542 543 544 545 546 |
/* Check whether the clock multiplier is supported or not */ if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; } |
6d0e34bf4 mmc: sdhci: Disti... |
547 |
if (host->max_clk == 0) { |
14bed52d2 mmc: sdhci: remov... |
548 |
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) |
6d0e34bf4 mmc: sdhci: Disti... |
549 |
host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> |
2a809093f dm: mmc: sdhci: R... |
550 |
SDHCI_CLOCK_BASE_SHIFT; |
af62a5578 MMC: add sdhci ge... |
551 |
else |
6d0e34bf4 mmc: sdhci: Disti... |
552 |
host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> |
2a809093f dm: mmc: sdhci: R... |
553 |
SDHCI_CLOCK_BASE_SHIFT; |
6d0e34bf4 mmc: sdhci: Disti... |
554 |
host->max_clk *= 1000000; |
0e0dcc191 mmc: sdhci: Fix m... |
555 556 |
if (host->clk_mul) host->max_clk *= host->clk_mul; |
af62a5578 MMC: add sdhci ge... |
557 |
} |
6d0e34bf4 mmc: sdhci: Disti... |
558 |
if (host->max_clk == 0) { |
6c67954c9 mmc: sdhci: move ... |
559 560 561 |
printf("%s: Hardware doesn't specify base clock frequency ", __func__); |
2a809093f dm: mmc: sdhci: R... |
562 |
return -EINVAL; |
6c67954c9 mmc: sdhci: move ... |
563 |
} |
6d0e34bf4 mmc: sdhci: Disti... |
564 565 566 567 568 569 |
if (f_max && (f_max < host->max_clk)) cfg->f_max = f_max; else cfg->f_max = host->max_clk; if (f_min) cfg->f_min = f_min; |
af62a5578 MMC: add sdhci ge... |
570 |
else { |
14bed52d2 mmc: sdhci: remov... |
571 |
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) |
2a809093f dm: mmc: sdhci: R... |
572 |
cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_300; |
af62a5578 MMC: add sdhci ge... |
573 |
else |
2a809093f dm: mmc: sdhci: R... |
574 |
cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_200; |
af62a5578 MMC: add sdhci ge... |
575 |
} |
2a809093f dm: mmc: sdhci: R... |
576 |
cfg->voltages = 0; |
af62a5578 MMC: add sdhci ge... |
577 |
if (caps & SDHCI_CAN_VDD_330) |
2a809093f dm: mmc: sdhci: R... |
578 |
cfg->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; |
af62a5578 MMC: add sdhci ge... |
579 |
if (caps & SDHCI_CAN_VDD_300) |
2a809093f dm: mmc: sdhci: R... |
580 |
cfg->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; |
af62a5578 MMC: add sdhci ge... |
581 |
if (caps & SDHCI_CAN_VDD_180) |
2a809093f dm: mmc: sdhci: R... |
582 |
cfg->voltages |= MMC_VDD_165_195; |
236bfecff mmc: add the quir... |
583 |
|
3137e645e mmc: sdhci: move ... |
584 585 |
if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE) cfg->voltages |= host->voltages; |
be165fbbf mmc: sdhci: do no... |
586 |
cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; |
3fd0a9ba8 mmc: sdhci: combi... |
587 588 |
/* Since Host Controller Version3.0 */ |
14bed52d2 mmc: sdhci: remov... |
589 |
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { |
ecd7b246f mmc: sdhci: disab... |
590 591 |
if (!(caps & SDHCI_CAN_DO_8BIT)) cfg->host_caps &= ~MMC_MODE_8BIT; |
1695b29a5 mmc: sdhci: Enabl... |
592 |
} |
429790026 mmc: sdhci: Clear... |
593 |
|
14bed52d2 mmc: sdhci: remov... |
594 595 |
if (host->host_caps) cfg->host_caps |= host->host_caps; |
429790026 mmc: sdhci: Clear... |
596 |
|
2a809093f dm: mmc: sdhci: R... |
597 |
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; |
93bfd6167 mmc: Split mmc st... |
598 |
|
2a809093f dm: mmc: sdhci: R... |
599 600 |
return 0; } |
ef1e4eda6 dm: mmc: sdhci: S... |
601 602 603 604 605 606 |
#ifdef CONFIG_BLK int sdhci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) { return mmc_bind(dev, mmc, cfg); } #else |
6d0e34bf4 mmc: sdhci: Disti... |
607 |
int add_sdhci(struct sdhci_host *host, u32 f_max, u32 f_min) |
2a809093f dm: mmc: sdhci: R... |
608 |
{ |
6c67954c9 mmc: sdhci: move ... |
609 |
int ret; |
6d0e34bf4 mmc: sdhci: Disti... |
610 |
ret = sdhci_setup_cfg(&host->cfg, host, f_max, f_min); |
6c67954c9 mmc: sdhci: move ... |
611 612 |
if (ret) return ret; |
2a809093f dm: mmc: sdhci: R... |
613 |
|
93bfd6167 mmc: Split mmc st... |
614 615 616 617 |
host->mmc = mmc_create(&host->cfg, host); if (host->mmc == NULL) { printf("%s: mmc create fail! ", __func__); |
2cb5d67c1 mmc: sdhci: use t... |
618 |
return -ENOMEM; |
93bfd6167 mmc: Split mmc st... |
619 |
} |
af62a5578 MMC: add sdhci ge... |
620 621 622 |
return 0; } |
ef1e4eda6 dm: mmc: sdhci: S... |
623 |
#endif |