Commit e95504497ecac46907204b0ee3460b708a2981ac
Committed by
Andy Fleming
1 parent
5ed6f447af
Exists in
master
and in
53 other branches
mmc: Split device init to decouple OCR-polling delay
Most of time that MMC driver spends on initializing a device is polling OCR (operation conditions register). To decouple this polling loop, device init is split into two parts: The first part fires the OCR query command, and the second part polls the result. So the caller is now no longer bound to the OCR-polling delay; he may fire the query, go somewhere and then come back later for the result. To use this, call mmc_set_preinit() on any device which needs this. This can save significant amounts of time on boot (e.g. 200ms) by hiding the MMC init time behind other init. Signed-off-by: Che-Liang Chiou <clchiou@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> Acked-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Andy Fleming <afleming@freescale.com>
Showing 2 changed files with 135 additions and 32 deletions Side-by-side Diff
drivers/mmc/mmc.c
... | ... | @@ -524,49 +524,71 @@ |
524 | 524 | return 0; |
525 | 525 | } |
526 | 526 | |
527 | -static int mmc_send_op_cond(struct mmc *mmc) | |
527 | +/* We pass in the cmd since otherwise the init seems to fail */ | |
528 | +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, | |
529 | + int use_arg) | |
528 | 530 | { |
529 | - int timeout = 10000; | |
530 | - struct mmc_cmd cmd; | |
531 | 531 | int err; |
532 | 532 | |
533 | + cmd->cmdidx = MMC_CMD_SEND_OP_COND; | |
534 | + cmd->resp_type = MMC_RSP_R3; | |
535 | + cmd->cmdarg = 0; | |
536 | + if (use_arg && !mmc_host_is_spi(mmc)) { | |
537 | + cmd->cmdarg = | |
538 | + (mmc->voltages & | |
539 | + (mmc->op_cond_response & OCR_VOLTAGE_MASK)) | | |
540 | + (mmc->op_cond_response & OCR_ACCESS_MODE); | |
541 | + | |
542 | + if (mmc->host_caps & MMC_MODE_HC) | |
543 | + cmd->cmdarg |= OCR_HCS; | |
544 | + } | |
545 | + err = mmc_send_cmd(mmc, cmd, NULL); | |
546 | + if (err) | |
547 | + return err; | |
548 | + mmc->op_cond_response = cmd->response[0]; | |
549 | + return 0; | |
550 | +} | |
551 | + | |
552 | +int mmc_send_op_cond(struct mmc *mmc) | |
553 | +{ | |
554 | + struct mmc_cmd cmd; | |
555 | + int err, i; | |
556 | + | |
533 | 557 | /* Some cards seem to need this */ |
534 | 558 | mmc_go_idle(mmc); |
535 | 559 | |
536 | 560 | /* Asking to the card its capabilities */ |
537 | - cmd.cmdidx = MMC_CMD_SEND_OP_COND; | |
538 | - cmd.resp_type = MMC_RSP_R3; | |
539 | - cmd.cmdarg = 0; | |
561 | + mmc->op_cond_pending = 1; | |
562 | + for (i = 0; i < 2; i++) { | |
563 | + err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); | |
564 | + if (err) | |
565 | + return err; | |
540 | 566 | |
541 | - err = mmc_send_cmd(mmc, &cmd, NULL); | |
567 | + /* exit if not busy (flag seems to be inverted) */ | |
568 | + if (mmc->op_cond_response & OCR_BUSY) | |
569 | + return 0; | |
570 | + } | |
571 | + return IN_PROGRESS; | |
572 | +} | |
542 | 573 | |
543 | - if (err) | |
544 | - return err; | |
574 | +int mmc_complete_op_cond(struct mmc *mmc) | |
575 | +{ | |
576 | + struct mmc_cmd cmd; | |
577 | + int timeout = 1000; | |
578 | + uint start; | |
579 | + int err; | |
545 | 580 | |
546 | - udelay(1000); | |
547 | - | |
581 | + mmc->op_cond_pending = 0; | |
582 | + start = get_timer(0); | |
548 | 583 | do { |
549 | - cmd.cmdidx = MMC_CMD_SEND_OP_COND; | |
550 | - cmd.resp_type = MMC_RSP_R3; | |
551 | - cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : | |
552 | - (mmc->voltages & | |
553 | - (cmd.response[0] & OCR_VOLTAGE_MASK)) | | |
554 | - (cmd.response[0] & OCR_ACCESS_MODE)); | |
555 | - | |
556 | - if (mmc->host_caps & MMC_MODE_HC) | |
557 | - cmd.cmdarg |= OCR_HCS; | |
558 | - | |
559 | - err = mmc_send_cmd(mmc, &cmd, NULL); | |
560 | - | |
584 | + err = mmc_send_op_cond_iter(mmc, &cmd, 1); | |
561 | 585 | if (err) |
562 | 586 | return err; |
587 | + if (get_timer(start) > timeout) | |
588 | + return UNUSABLE_ERR; | |
589 | + udelay(100); | |
590 | + } while (!(mmc->op_cond_response & OCR_BUSY)); | |
563 | 591 | |
564 | - udelay(1000); | |
565 | - } while (!(cmd.response[0] & OCR_BUSY) && timeout--); | |
566 | - | |
567 | - if (timeout <= 0) | |
568 | - return UNUSABLE_ERR; | |
569 | - | |
570 | 592 | if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ |
571 | 593 | cmd.cmdidx = MMC_CMD_SPI_READ_OCR; |
572 | 594 | cmd.resp_type = MMC_RSP_R3; |
... | ... | @@ -1274,7 +1296,7 @@ |
1274 | 1296 | } |
1275 | 1297 | #endif |
1276 | 1298 | |
1277 | -int mmc_init(struct mmc *mmc) | |
1299 | +int mmc_start_init(struct mmc *mmc) | |
1278 | 1300 | { |
1279 | 1301 | int err; |
1280 | 1302 | |
1281 | 1303 | |
1282 | 1304 | |
1283 | 1305 | |
... | ... | @@ -1314,20 +1336,51 @@ |
1314 | 1336 | if (err == TIMEOUT) { |
1315 | 1337 | err = mmc_send_op_cond(mmc); |
1316 | 1338 | |
1317 | - if (err) { | |
1339 | + if (err && err != IN_PROGRESS) { | |
1318 | 1340 | printf("Card did not respond to voltage select!\n"); |
1319 | 1341 | return UNUSABLE_ERR; |
1320 | 1342 | } |
1321 | 1343 | } |
1322 | 1344 | |
1323 | - err = mmc_startup(mmc); | |
1345 | + if (err == IN_PROGRESS) | |
1346 | + mmc->init_in_progress = 1; | |
1347 | + | |
1348 | + return err; | |
1349 | +} | |
1350 | + | |
1351 | +static int mmc_complete_init(struct mmc *mmc) | |
1352 | +{ | |
1353 | + int err = 0; | |
1354 | + | |
1355 | + if (mmc->op_cond_pending) | |
1356 | + err = mmc_complete_op_cond(mmc); | |
1357 | + | |
1358 | + if (!err) | |
1359 | + err = mmc_startup(mmc); | |
1324 | 1360 | if (err) |
1325 | 1361 | mmc->has_init = 0; |
1326 | 1362 | else |
1327 | 1363 | mmc->has_init = 1; |
1364 | + mmc->init_in_progress = 0; | |
1328 | 1365 | return err; |
1329 | 1366 | } |
1330 | 1367 | |
1368 | +int mmc_init(struct mmc *mmc) | |
1369 | +{ | |
1370 | + int err = IN_PROGRESS; | |
1371 | + unsigned start = get_timer(0); | |
1372 | + | |
1373 | + if (mmc->has_init) | |
1374 | + return 0; | |
1375 | + if (!mmc->init_in_progress) | |
1376 | + err = mmc_start_init(mmc); | |
1377 | + | |
1378 | + if (!err || err == IN_PROGRESS) | |
1379 | + err = mmc_complete_init(mmc); | |
1380 | + debug("%s: %d, time %lu\n", __func__, err, get_timer(start)); | |
1381 | + return err; | |
1382 | +} | |
1383 | + | |
1331 | 1384 | /* |
1332 | 1385 | * CPU and board-specific MMC initializations. Aliased function |
1333 | 1386 | * signals caller to move on |
... | ... | @@ -1362,6 +1415,25 @@ |
1362 | 1415 | return cur_dev_num; |
1363 | 1416 | } |
1364 | 1417 | |
1418 | +void mmc_set_preinit(struct mmc *mmc, int preinit) | |
1419 | +{ | |
1420 | + mmc->preinit = preinit; | |
1421 | +} | |
1422 | + | |
1423 | +static void do_preinit(void) | |
1424 | +{ | |
1425 | + struct mmc *m; | |
1426 | + struct list_head *entry; | |
1427 | + | |
1428 | + list_for_each(entry, &mmc_devices) { | |
1429 | + m = list_entry(entry, struct mmc, link); | |
1430 | + | |
1431 | + if (m->preinit) | |
1432 | + mmc_start_init(m); | |
1433 | + } | |
1434 | +} | |
1435 | + | |
1436 | + | |
1365 | 1437 | int mmc_initialize(bd_t *bis) |
1366 | 1438 | { |
1367 | 1439 | INIT_LIST_HEAD (&mmc_devices); |
... | ... | @@ -1372,6 +1444,7 @@ |
1372 | 1444 | |
1373 | 1445 | print_mmc_devices(','); |
1374 | 1446 | |
1447 | + do_preinit(); | |
1375 | 1448 | return 0; |
1376 | 1449 | } |
include/mmc.h
... | ... | @@ -68,6 +68,7 @@ |
68 | 68 | #define UNUSABLE_ERR -17 /* Unusable Card */ |
69 | 69 | #define COMM_ERR -18 /* Communications Error */ |
70 | 70 | #define TIMEOUT -19 |
71 | +#define IN_PROGRESS -20 /* operation is in progress */ | |
71 | 72 | |
72 | 73 | #define MMC_CMD_GO_IDLE_STATE 0 |
73 | 74 | #define MMC_CMD_SEND_OP_COND 1 |
... | ... | @@ -270,6 +271,10 @@ |
270 | 271 | int (*getcd)(struct mmc *mmc); |
271 | 272 | int (*getwp)(struct mmc *mmc); |
272 | 273 | uint b_max; |
274 | + char op_cond_pending; /* 1 if we are waiting on an op_cond command */ | |
275 | + char init_in_progress; /* 1 if we have done mmc_start_init() */ | |
276 | + char preinit; /* start init as early as possible */ | |
277 | + uint op_cond_response; /* the response byte from the last op_cond */ | |
273 | 278 | }; |
274 | 279 | |
275 | 280 | int mmc_register(struct mmc *mmc); |
... | ... | @@ -286,6 +291,31 @@ |
286 | 291 | int mmc_getcd(struct mmc *mmc); |
287 | 292 | int mmc_getwp(struct mmc *mmc); |
288 | 293 | void spl_mmc_load(void) __noreturn; |
294 | + | |
295 | +/** | |
296 | + * Start device initialization and return immediately; it does not block on | |
297 | + * polling OCR (operation condition register) status. Then you should call | |
298 | + * mmc_init, which would block on polling OCR status and complete the device | |
299 | + * initializatin. | |
300 | + * | |
301 | + * @param mmc Pointer to a MMC device struct | |
302 | + * @return 0 on success, IN_PROGRESS on waiting for OCR status, <0 on error. | |
303 | + */ | |
304 | +int mmc_start_init(struct mmc *mmc); | |
305 | + | |
306 | +/** | |
307 | + * Set preinit flag of mmc device. | |
308 | + * | |
309 | + * This will cause the device to be pre-inited during mmc_initialize(), | |
310 | + * which may save boot time if the device is not accessed until later. | |
311 | + * Some eMMC devices take 200-300ms to init, but unfortunately they | |
312 | + * must be sent a series of commands to even get them to start preparing | |
313 | + * for operation. | |
314 | + * | |
315 | + * @param mmc Pointer to a MMC device struct | |
316 | + * @param preinit preinit flag value | |
317 | + */ | |
318 | +void mmc_set_preinit(struct mmc *mmc, int preinit); | |
289 | 319 | |
290 | 320 | #ifdef CONFIG_GENERIC_MMC |
291 | 321 | #define mmc_host_is_spi(mmc) ((mmc)->host_caps & MMC_MODE_SPI) |