summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorShridhar Rasal <srasal@nvidia.com>2012-01-25 17:54:30 +0530
committerSimone Willett <swillett@nvidia.com>2012-02-10 13:12:17 -0800
commit506e63fcfb563581c4a0417d424e36a75cf51acf (patch)
treee9bf67c45323f25b73c60418c8a2b68b7355182d /drivers
parent9c5ce68dd28bb7d750c47181ebcca6ac6c071eab (diff)
mmc: core: Add support for BKOPS and HPI interrupt
Added support for starting BKOPS and issuing HPI commands which are supported by eMMC v4.41 and eMMC v4.5 cards. Enable BKOPS and HPI interrupt if the host and card support it. Originally reviewed on: http://git-master/r/69778 Bug 919232 Change-Id: I09b33ddc18013e2eeb505fdb28dd8357fa75b569 Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Signed-off-by: Shridhar Rasal <srasal@nvidia.com> Reviewed-on: http://git-master/r/77319 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sachin Nikam <snikam@nvidia.com> Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/core/core.c109
-rw-r--r--drivers/mmc/core/mmc.c58
-rw-r--r--drivers/mmc/core/mmc_ops.c70
-rw-r--r--drivers/mmc/core/mmc_ops.h3
4 files changed, 238 insertions, 2 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 344d2414f05c..2a288e936a84 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -5,6 +5,7 @@
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -325,6 +326,110 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
EXPORT_SYMBOL(mmc_wait_for_req);
/**
+ * mmc_bkops_start - Issue start for mmc background ops
+ * @card: the MMC card associated with bkops
+ * @is_synchronous: is the backops synchronous
+ *
+ * Issued background ops without the busy wait.
+ */
+int mmc_bkops_start(struct mmc_card *card, bool is_synchronous)
+{
+ int err;
+ unsigned long flags;
+
+ BUG_ON(!card);
+
+ if (!card->ext_csd.bk_ops_en || mmc_card_doing_bkops(card))
+ return 1;
+
+ mmc_claim_host(card->host);
+ err = mmc_send_bk_ops_cmd(card, is_synchronous);
+ if (err)
+ pr_err("%s: abort bk ops (%d error)\n",
+ mmc_hostname(card->host), err);
+
+ /*
+ * Incase of asynchronous backops, set card state
+ * to doing bk ops to ensure that HPI is issued before
+ * handling any new request in the queue.
+ */
+ spin_lock_irqsave(&card->host->lock, flags);
+ mmc_card_clr_need_bkops(card);
+ if (!is_synchronous)
+ mmc_card_set_doing_bkops(card);
+ spin_unlock_irqrestore(&card->host->lock, flags);
+
+ mmc_release_host(card->host);
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_bkops_start);
+
+/**
+ * mmc_interrupt_hpi - Issue for High priority Interrupt
+ * @card: the MMC card associated with the HPI transfer
+ *
+ * Issued High Priority Interrupt, and check for card status
+ * util out-of prg-state.
+ */
+int mmc_interrupt_hpi(struct mmc_card *card)
+{
+ int err;
+ u32 status;
+ unsigned long flags;
+
+ BUG_ON(!card);
+
+ if (!mmc_card_mmc(card))
+ return 1;
+
+ if (!card->ext_csd.hpi_en) {
+ pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host));
+ return 1;
+ }
+
+ mmc_claim_host(card->host);
+ err = mmc_send_status(card, &status);
+ if (err) {
+ pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
+ goto out;
+ }
+
+ /*
+ * If the card status is in PRG-state, we can send the HPI command.
+ */
+ if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
+ do {
+ /*
+ * We don't know when the HPI command will finish
+ * processing, so we need to resend HPI until out
+ * of prg-state, and keep checking the card status
+ * with SEND_STATUS. If a timeout error occurs when
+ * sending the HPI command, we are already out of
+ * prg-state.
+ */
+ err = mmc_send_hpi_cmd(card, &status);
+ if (err)
+ pr_debug("%s: abort HPI (%d error)\n",
+ mmc_hostname(card->host), err);
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ break;
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
+ } else
+ pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
+
+out:
+ spin_lock_irqsave(&card->host->lock, flags);
+ mmc_card_clr_doing_bkops(card);
+ spin_unlock_irqrestore(&card->host->lock, flags);
+ mmc_release_host(card->host);
+ return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_hpi);
+
+/**
* mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command
* @cmd: MMC command to start
@@ -2010,6 +2115,10 @@ int mmc_suspend_host(struct mmc_host *host)
if (mmc_bus_needs_resume(host))
return 0;
+ if (mmc_card_mmc(host->card) && mmc_card_doing_bkops(host->card))
+ mmc_interrupt_hpi(host->card);
+ mmc_card_clr_need_bkops(host->card);
+
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
if (cancel_delayed_work(&host->detect))
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6952f778c294..69fb2275845c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -4,6 +4,7 @@
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -403,10 +404,29 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_TRIM_MULT];
}
- if (card->ext_csd.rev >= 5)
+ card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
+ if (card->ext_csd.rev >= 5) {
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
+ /* check whether the eMMC card supports HPI */
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
+ card->ext_csd.hpi = 1;
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
+ card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
+ else
+ card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
+ /*
+ * Indicate the maximum timeout to close
+ * a command interrupted by HPI
+ */
+ card->ext_csd.out_of_int_time =
+ ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
+ }
+
+ /* Check whether the eMMC card supports background ops */
+ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)
+ card->ext_csd.bk_ops = 1;
+ }
- card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
card->erased_byte = 0xFF;
else
@@ -728,6 +748,40 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Enable HPI feature (if supported)
+ */
+ if (card->ext_csd.hpi && (card->host->caps & MMC_CAP_BKOPS)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HPI_MGMT, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling HPI failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else {
+ card->ext_csd.hpi_en = 1;
+ }
+ }
+
+ /*
+ * Enable Background ops feature (if supported)
+ */
+ if (card->ext_csd.bk_ops && (card->host->caps & MMC_CAP_BKOPS)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BKOPS_EN, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling BK ops failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else {
+ card->ext_csd.bk_ops_en = 1;
+ }
+ }
+
+ /*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 770c3d06f5dc..330b968393d6 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -547,3 +547,73 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
return err;
}
+
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
+{
+ struct mmc_command cmd = {0};
+ unsigned int opcode;
+ unsigned int flags;
+ int err;
+
+ opcode = card->ext_csd.hpi_cmd;
+ flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ cmd.opcode = opcode;
+ cmd.arg = card->rca << 16 | 1;
+ cmd.flags = flags;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ pr_warn("%s: error %d interrupting operation. "
+ "HPI command response %#x\n", mmc_hostname(card->host),
+ err, cmd.resp[0]);
+ return err;
+ }
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
+
+int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous)
+{
+ int err;
+ struct mmc_command cmd;
+ u32 status;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_BKOPS_EN << 16) |
+ (1 << 8) |
+ EXT_CSD_CMD_SET_NORMAL;
+ if (is_synchronous)
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ else
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ /* Must check status to be sure of no errors */
+ do {
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+ if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ break;
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ if (status & 0xFDFFA000)
+ printk(KERN_ERR "%s: unexpected status %#x after "
+ "switch", mmc_hostname(card->host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+
+ return 0;
+}
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 9276946fa5b7..d8f157dee147 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -2,6 +2,7 @@
* linux/drivers/mmc/core/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +27,8 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
+int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous);
#endif