diff options
author | Jaehoon Chung <jh80.chung@samsung.com> | 2012-09-17 04:42:02 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-10-03 10:05:12 -0400 |
commit | 950d56acce5d401f477b91d0177605b543d63d07 (patch) | |
tree | 205505f3976d02c6ef2fa9d6c911407f0e0f6c80 /drivers/mmc | |
parent | bec9d4e5939987053169a9bb48fc58b6a2d3e237 (diff) |
mmc: support BKOPS feature for eMMC
Enable eMMC background operations (BKOPS) feature.
If URGENT_BKOPS is set after a response, note that BKOPS are required.
Immediately run BKOPS if required. Read/write operations should be
requested during BKOPS(LEVEL-1), then issue HPI to interrupt the
ongoing BKOPS and service the foreground operation.
(This patch only controls the LEVEL2/3.)
When repeating the writing 1GB data, at a certain time, performance is
decreased. At that time, card triggers the Level-3 or Level-2. After
running bkops, performance is recovered.
Future considerations:
* Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
* Interrupt ongoing BKOPS before powering off the card.
* How do we get BKOPS_STATUS value (periodically send ext_csd command)?
* If using periodic bkops, also consider runtime_pm control.
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
Reviewed-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 160 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 11 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 26 |
3 files changed, 189 insertions, 8 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index af2c4d2fd69e..044cd016320e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/suspend.h> | 26 | #include <linux/suspend.h> |
27 | #include <linux/fault-inject.h> | 27 | #include <linux/fault-inject.h> |
28 | #include <linux/random.h> | 28 | #include <linux/random.h> |
29 | #include <linux/slab.h> | ||
29 | 30 | ||
30 | #include <linux/mmc/card.h> | 31 | #include <linux/mmc/card.h> |
31 | #include <linux/mmc/host.h> | 32 | #include <linux/mmc/host.h> |
@@ -41,6 +42,12 @@ | |||
41 | #include "sd_ops.h" | 42 | #include "sd_ops.h" |
42 | #include "sdio_ops.h" | 43 | #include "sdio_ops.h" |
43 | 44 | ||
45 | /* | ||
46 | * Background operations can take a long time, depending on the housekeeping | ||
47 | * operations the card has to perform. | ||
48 | */ | ||
49 | #define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */ | ||
50 | |||
44 | static struct workqueue_struct *workqueue; | 51 | static struct workqueue_struct *workqueue; |
45 | static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; | 52 | static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; |
46 | 53 | ||
@@ -245,6 +252,70 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) | |||
245 | host->ops->request(host, mrq); | 252 | host->ops->request(host, mrq); |
246 | } | 253 | } |
247 | 254 | ||
255 | /** | ||
256 | * mmc_start_bkops - start BKOPS for supported cards | ||
257 | * @card: MMC card to start BKOPS | ||
258 | * @form_exception: A flag to indicate if this function was | ||
259 | * called due to an exception raised by the card | ||
260 | * | ||
261 | * Start background operations whenever requested. | ||
262 | * When the urgent BKOPS bit is set in a R1 command response | ||
263 | * then background operations should be started immediately. | ||
264 | */ | ||
265 | void mmc_start_bkops(struct mmc_card *card, bool from_exception) | ||
266 | { | ||
267 | int err; | ||
268 | int timeout; | ||
269 | bool use_busy_signal; | ||
270 | |||
271 | BUG_ON(!card); | ||
272 | |||
273 | if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card)) | ||
274 | return; | ||
275 | |||
276 | err = mmc_read_bkops_status(card); | ||
277 | if (err) { | ||
278 | pr_err("%s: Failed to read bkops status: %d\n", | ||
279 | mmc_hostname(card->host), err); | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | if (!card->ext_csd.raw_bkops_status) | ||
284 | return; | ||
285 | |||
286 | if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 && | ||
287 | from_exception) | ||
288 | return; | ||
289 | |||
290 | mmc_claim_host(card->host); | ||
291 | if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) { | ||
292 | timeout = MMC_BKOPS_MAX_TIMEOUT; | ||
293 | use_busy_signal = true; | ||
294 | } else { | ||
295 | timeout = 0; | ||
296 | use_busy_signal = false; | ||
297 | } | ||
298 | |||
299 | err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||
300 | EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal); | ||
301 | if (err) { | ||
302 | pr_warn("%s: Error %d starting bkops\n", | ||
303 | mmc_hostname(card->host), err); | ||
304 | goto out; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * For urgent bkops status (LEVEL_2 and more) | ||
309 | * bkops executed synchronously, otherwise | ||
310 | * the operation is in progress | ||
311 | */ | ||
312 | if (!use_busy_signal) | ||
313 | mmc_card_set_doing_bkops(card); | ||
314 | out: | ||
315 | mmc_release_host(card->host); | ||
316 | } | ||
317 | EXPORT_SYMBOL(mmc_start_bkops); | ||
318 | |||
248 | static void mmc_wait_done(struct mmc_request *mrq) | 319 | static void mmc_wait_done(struct mmc_request *mrq) |
249 | { | 320 | { |
250 | complete(&mrq->completion); | 321 | complete(&mrq->completion); |
@@ -354,6 +425,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, | |||
354 | if (host->areq) { | 425 | if (host->areq) { |
355 | mmc_wait_for_req_done(host, host->areq->mrq); | 426 | mmc_wait_for_req_done(host, host->areq->mrq); |
356 | err = host->areq->err_check(host->card, host->areq); | 427 | err = host->areq->err_check(host->card, host->areq); |
428 | /* | ||
429 | * Check BKOPS urgency for each R1 response | ||
430 | */ | ||
431 | if (host->card && mmc_card_mmc(host->card) && | ||
432 | ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) || | ||
433 | (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) && | ||
434 | (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) | ||
435 | mmc_start_bkops(host->card, true); | ||
357 | } | 436 | } |
358 | 437 | ||
359 | if (!err && areq) | 438 | if (!err && areq) |
@@ -398,7 +477,7 @@ EXPORT_SYMBOL(mmc_wait_for_req); | |||
398 | * @card: the MMC card associated with the HPI transfer | 477 | * @card: the MMC card associated with the HPI transfer |
399 | * | 478 | * |
400 | * Issued High Priority Interrupt, and check for card status | 479 | * Issued High Priority Interrupt, and check for card status |
401 | * util out-of prg-state. | 480 | * until out-of prg-state. |
402 | */ | 481 | */ |
403 | int mmc_interrupt_hpi(struct mmc_card *card) | 482 | int mmc_interrupt_hpi(struct mmc_card *card) |
404 | { | 483 | { |
@@ -490,6 +569,64 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries | |||
490 | EXPORT_SYMBOL(mmc_wait_for_cmd); | 569 | EXPORT_SYMBOL(mmc_wait_for_cmd); |
491 | 570 | ||
492 | /** | 571 | /** |
572 | * mmc_stop_bkops - stop ongoing BKOPS | ||
573 | * @card: MMC card to check BKOPS | ||
574 | * | ||
575 | * Send HPI command to stop ongoing background operations to | ||
576 | * allow rapid servicing of foreground operations, e.g. read/ | ||
577 | * writes. Wait until the card comes out of the programming state | ||
578 | * to avoid errors in servicing read/write requests. | ||
579 | */ | ||
580 | int mmc_stop_bkops(struct mmc_card *card) | ||
581 | { | ||
582 | int err = 0; | ||
583 | |||
584 | BUG_ON(!card); | ||
585 | err = mmc_interrupt_hpi(card); | ||
586 | |||
587 | /* | ||
588 | * If err is EINVAL, we can't issue an HPI. | ||
589 | * It should complete the BKOPS. | ||
590 | */ | ||
591 | if (!err || (err == -EINVAL)) { | ||
592 | mmc_card_clr_doing_bkops(card); | ||
593 | err = 0; | ||
594 | } | ||
595 | |||
596 | return err; | ||
597 | } | ||
598 | EXPORT_SYMBOL(mmc_stop_bkops); | ||
599 | |||
600 | int mmc_read_bkops_status(struct mmc_card *card) | ||
601 | { | ||
602 | int err; | ||
603 | u8 *ext_csd; | ||
604 | |||
605 | /* | ||
606 | * In future work, we should consider storing the entire ext_csd. | ||
607 | */ | ||
608 | ext_csd = kmalloc(512, GFP_KERNEL); | ||
609 | if (!ext_csd) { | ||
610 | pr_err("%s: could not allocate buffer to receive the ext_csd.\n", | ||
611 | mmc_hostname(card->host)); | ||
612 | return -ENOMEM; | ||
613 | } | ||
614 | |||
615 | mmc_claim_host(card->host); | ||
616 | err = mmc_send_ext_csd(card, ext_csd); | ||
617 | mmc_release_host(card->host); | ||
618 | if (err) | ||
619 | goto out; | ||
620 | |||
621 | card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; | ||
622 | card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS]; | ||
623 | out: | ||
624 | kfree(ext_csd); | ||
625 | return err; | ||
626 | } | ||
627 | EXPORT_SYMBOL(mmc_read_bkops_status); | ||
628 | |||
629 | /** | ||
493 | * mmc_set_data_timeout - set the timeout for a data command | 630 | * mmc_set_data_timeout - set the timeout for a data command |
494 | * @data: data phase for command | 631 | * @data: data phase for command |
495 | * @card: the MMC card associated with the data transfer | 632 | * @card: the MMC card associated with the data transfer |
@@ -2333,9 +2470,14 @@ int mmc_suspend_host(struct mmc_host *host) | |||
2333 | 2470 | ||
2334 | mmc_bus_get(host); | 2471 | mmc_bus_get(host); |
2335 | if (host->bus_ops && !host->bus_dead) { | 2472 | if (host->bus_ops && !host->bus_dead) { |
2336 | 2473 | if (host->bus_ops->suspend) { | |
2337 | if (host->bus_ops->suspend) | 2474 | if (mmc_card_doing_bkops(host->card)) { |
2475 | err = mmc_stop_bkops(host->card); | ||
2476 | if (err) | ||
2477 | goto out; | ||
2478 | } | ||
2338 | err = host->bus_ops->suspend(host); | 2479 | err = host->bus_ops->suspend(host); |
2480 | } | ||
2339 | 2481 | ||
2340 | if (err == -ENOSYS || !host->bus_ops->resume) { | 2482 | if (err == -ENOSYS || !host->bus_ops->resume) { |
2341 | /* | 2483 | /* |
@@ -2417,11 +2559,21 @@ int mmc_pm_notify(struct notifier_block *notify_block, | |||
2417 | struct mmc_host *host = container_of( | 2559 | struct mmc_host *host = container_of( |
2418 | notify_block, struct mmc_host, pm_notify); | 2560 | notify_block, struct mmc_host, pm_notify); |
2419 | unsigned long flags; | 2561 | unsigned long flags; |
2420 | 2562 | int err = 0; | |
2421 | 2563 | ||
2422 | switch (mode) { | 2564 | switch (mode) { |
2423 | case PM_HIBERNATION_PREPARE: | 2565 | case PM_HIBERNATION_PREPARE: |
2424 | case PM_SUSPEND_PREPARE: | 2566 | case PM_SUSPEND_PREPARE: |
2567 | if (host->card && mmc_card_mmc(host->card) && | ||
2568 | mmc_card_doing_bkops(host->card)) { | ||
2569 | err = mmc_stop_bkops(host->card); | ||
2570 | if (err) { | ||
2571 | pr_err("%s: didn't stop bkops\n", | ||
2572 | mmc_hostname(host)); | ||
2573 | return err; | ||
2574 | } | ||
2575 | mmc_card_clr_doing_bkops(host->card); | ||
2576 | } | ||
2425 | 2577 | ||
2426 | spin_lock_irqsave(&host->lock, flags); | 2578 | spin_lock_irqsave(&host->lock, flags); |
2427 | host->rescan_disable = 1; | 2579 | host->rescan_disable = 1; |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 396b25891bb9..7509de14aa78 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -463,6 +463,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
463 | } | 463 | } |
464 | 464 | ||
465 | if (card->ext_csd.rev >= 5) { | 465 | if (card->ext_csd.rev >= 5) { |
466 | /* check whether the eMMC card supports BKOPS */ | ||
467 | if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { | ||
468 | card->ext_csd.bkops = 1; | ||
469 | card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN]; | ||
470 | card->ext_csd.raw_bkops_status = | ||
471 | ext_csd[EXT_CSD_BKOPS_STATUS]; | ||
472 | if (!card->ext_csd.bkops_en) | ||
473 | pr_info("%s: BKOPS_EN bit is not set\n", | ||
474 | mmc_hostname(card->host)); | ||
475 | } | ||
476 | |||
466 | /* check whether the eMMC card supports HPI */ | 477 | /* check whether the eMMC card supports HPI */ |
467 | if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { | 478 | if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { |
468 | card->ext_csd.hpi = 1; | 479 | card->ext_csd.hpi = 1; |
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 225371a28861..a0e172042e65 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c | |||
@@ -393,18 +393,19 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) | |||
393 | } | 393 | } |
394 | 394 | ||
395 | /** | 395 | /** |
396 | * mmc_switch - modify EXT_CSD register | 396 | * __mmc_switch - modify EXT_CSD register |
397 | * @card: the MMC card associated with the data transfer | 397 | * @card: the MMC card associated with the data transfer |
398 | * @set: cmd set values | 398 | * @set: cmd set values |
399 | * @index: EXT_CSD register index | 399 | * @index: EXT_CSD register index |
400 | * @value: value to program into EXT_CSD register | 400 | * @value: value to program into EXT_CSD register |
401 | * @timeout_ms: timeout (ms) for operation performed by register write, | 401 | * @timeout_ms: timeout (ms) for operation performed by register write, |
402 | * timeout of zero implies maximum possible timeout | 402 | * timeout of zero implies maximum possible timeout |
403 | * @use_busy_signal: use the busy signal as response type | ||
403 | * | 404 | * |
404 | * Modifies the EXT_CSD register for selected card. | 405 | * Modifies the EXT_CSD register for selected card. |
405 | */ | 406 | */ |
406 | int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, | 407 | int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, |
407 | unsigned int timeout_ms) | 408 | unsigned int timeout_ms, bool use_busy_signal) |
408 | { | 409 | { |
409 | int err; | 410 | int err; |
410 | struct mmc_command cmd = {0}; | 411 | struct mmc_command cmd = {0}; |
@@ -418,13 +419,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, | |||
418 | (index << 16) | | 419 | (index << 16) | |
419 | (value << 8) | | 420 | (value << 8) | |
420 | set; | 421 | set; |
421 | cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; | 422 | cmd.flags = MMC_CMD_AC; |
423 | if (use_busy_signal) | ||
424 | cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B; | ||
425 | else | ||
426 | cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; | ||
427 | |||
428 | |||
422 | cmd.cmd_timeout_ms = timeout_ms; | 429 | cmd.cmd_timeout_ms = timeout_ms; |
423 | 430 | ||
424 | err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); | 431 | err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); |
425 | if (err) | 432 | if (err) |
426 | return err; | 433 | return err; |
427 | 434 | ||
435 | /* No need to check card status in case of unblocking command */ | ||
436 | if (!use_busy_signal) | ||
437 | return 0; | ||
438 | |||
428 | /* Must check status to be sure of no errors */ | 439 | /* Must check status to be sure of no errors */ |
429 | do { | 440 | do { |
430 | err = mmc_send_status(card, &status); | 441 | err = mmc_send_status(card, &status); |
@@ -449,6 +460,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, | |||
449 | 460 | ||
450 | return 0; | 461 | return 0; |
451 | } | 462 | } |
463 | EXPORT_SYMBOL_GPL(__mmc_switch); | ||
464 | |||
465 | int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, | ||
466 | unsigned int timeout_ms) | ||
467 | { | ||
468 | return __mmc_switch(card, set, index, value, timeout_ms, true); | ||
469 | } | ||
452 | EXPORT_SYMBOL_GPL(mmc_switch); | 470 | EXPORT_SYMBOL_GPL(mmc_switch); |
453 | 471 | ||
454 | int mmc_send_status(struct mmc_card *card, u32 *status) | 472 | int mmc_send_status(struct mmc_card *card, u32 *status) |