Merge branch 'for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu
[linux.git] / drivers / mmc / core / mmc_ops.c
index 78f75f00efc5b44673adafaf757195106480af76..5f7c5920231a8b8684541f50d0070c2e6e433f1f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mmc/mmc.h>
 
 #include "core.h"
+#include "card.h"
 #include "host.h"
 #include "mmc_ops.h"
 
@@ -54,7 +55,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
        0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
 };
 
-int mmc_send_status(struct mmc_card *card, u32 *status)
+int __mmc_send_status(struct mmc_card *card, u32 *status, unsigned int retries)
 {
        int err;
        struct mmc_command cmd = {};
@@ -64,7 +65,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
                cmd.arg = card->rca << 16;
        cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
 
-       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+       err = mmc_wait_for_cmd(card->host, &cmd, retries);
        if (err)
                return err;
 
@@ -76,6 +77,12 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(__mmc_send_status);
+
+int mmc_send_status(struct mmc_card *card, u32 *status)
+{
+       return __mmc_send_status(card, status, MMC_CMD_RETRIES);
+}
 
 static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
 {
@@ -200,24 +207,6 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
        return err;
 }
 
-int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
-{
-       int err;
-       struct mmc_command cmd = {};
-
-       cmd.opcode = MMC_ALL_SEND_CID;
-       cmd.arg = 0;
-       cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
-
-       err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
-       if (err)
-               return err;
-
-       memcpy(cid, cmd.resp, sizeof(u32) * 4);
-
-       return 0;
-}
-
 int mmc_set_relative_addr(struct mmc_card *card)
 {
        struct mmc_command cmd = {};
@@ -302,15 +291,11 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
        return 0;
 }
 
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
+static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd)
 {
        int ret, i;
        __be32 *csd_tmp;
 
-       if (!mmc_host_is_spi(card->host))
-               return mmc_send_cxd_native(card->host, card->rca << 16,
-                               csd, MMC_SEND_CSD);
-
        csd_tmp = kzalloc(16, GFP_KERNEL);
        if (!csd_tmp)
                return -ENOMEM;
@@ -327,18 +312,20 @@ err:
        return ret;
 }
 
-int mmc_send_cid(struct mmc_host *host, u32 *cid)
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+       if (mmc_host_is_spi(card->host))
+               return mmc_spi_send_csd(card, csd);
+
+       return mmc_send_cxd_native(card->host, card->rca << 16, csd,
+                               MMC_SEND_CSD);
+}
+
+static int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
 {
        int ret, i;
        __be32 *cid_tmp;
 
-       if (!mmc_host_is_spi(host)) {
-               if (!host->card)
-                       return -EINVAL;
-               return mmc_send_cxd_native(host, host->card->rca << 16,
-                               cid, MMC_SEND_CID);
-       }
-
        cid_tmp = kzalloc(16, GFP_KERNEL);
        if (!cid_tmp)
                return -ENOMEM;
@@ -355,6 +342,14 @@ err:
        return ret;
 }
 
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
+{
+       if (mmc_host_is_spi(host))
+               return mmc_spi_send_cid(host, cid);
+
+       return mmc_send_cxd_native(host, 0, cid, MMC_ALL_SEND_CID);
+}
+
 int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
 {
        int err;
@@ -800,7 +795,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
        return mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
 }
 
-int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
+static int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
 {
        struct mmc_command cmd = {};
        unsigned int opcode;
@@ -834,11 +829,208 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
        return 0;
 }
 
+/**
+ *     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
+ *     until out-of prg-state.
+ */
+int mmc_interrupt_hpi(struct mmc_card *card)
+{
+       int err;
+       u32 status;
+       unsigned long prg_wait;
+
+       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;
+       }
+
+       switch (R1_CURRENT_STATE(status)) {
+       case R1_STATE_IDLE:
+       case R1_STATE_READY:
+       case R1_STATE_STBY:
+       case R1_STATE_TRAN:
+               /*
+                * In idle and transfer states, HPI is not needed and the caller
+                * can issue the next intended command immediately
+                */
+               goto out;
+       case R1_STATE_PRG:
+               break;
+       default:
+               /* In all other states, it's illegal to issue HPI */
+               pr_debug("%s: HPI cannot be sent. Card state=%d\n",
+                       mmc_hostname(card->host), R1_CURRENT_STATE(status));
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = mmc_send_hpi_cmd(card, &status);
+       if (err)
+               goto out;
+
+       prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
+       do {
+               err = mmc_send_status(card, &status);
+
+               if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
+                       break;
+               if (time_after(jiffies, prg_wait))
+                       err = -ETIMEDOUT;
+       } while (!err);
+
+out:
+       mmc_release_host(card->host);
+       return err;
+}
+
 int mmc_can_ext_csd(struct mmc_card *card)
 {
        return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
 }
 
+/**
+ *     mmc_stop_bkops - stop ongoing BKOPS
+ *     @card: MMC card to check BKOPS
+ *
+ *     Send HPI command to stop ongoing background operations to
+ *     allow rapid servicing of foreground operations, e.g. read/
+ *     writes. Wait until the card comes out of the programming state
+ *     to avoid errors in servicing read/write requests.
+ */
+int mmc_stop_bkops(struct mmc_card *card)
+{
+       int err = 0;
+
+       err = mmc_interrupt_hpi(card);
+
+       /*
+        * If err is EINVAL, we can't issue an HPI.
+        * It should complete the BKOPS.
+        */
+       if (!err || (err == -EINVAL)) {
+               mmc_card_clr_doing_bkops(card);
+               mmc_retune_release(card->host);
+               err = 0;
+       }
+
+       return err;
+}
+
+static int mmc_read_bkops_status(struct mmc_card *card)
+{
+       int err;
+       u8 *ext_csd;
+
+       mmc_claim_host(card->host);
+       err = mmc_get_ext_csd(card, &ext_csd);
+       mmc_release_host(card->host);
+       if (err)
+               return err;
+
+       card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
+       card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
+       kfree(ext_csd);
+       return 0;
+}
+
+/**
+ *     mmc_start_bkops - start BKOPS for supported cards
+ *     @card: MMC card to start BKOPS
+ *     @form_exception: A flag to indicate if this function was
+ *                      called due to an exception raised by the card
+ *
+ *     Start background operations whenever requested.
+ *     When the urgent BKOPS bit is set in a R1 command response
+ *     then background operations should be started immediately.
+*/
+void mmc_start_bkops(struct mmc_card *card, bool from_exception)
+{
+       int err;
+       int timeout;
+       bool use_busy_signal;
+
+       if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
+               return;
+
+       err = mmc_read_bkops_status(card);
+       if (err) {
+               pr_err("%s: Failed to read bkops status: %d\n",
+                      mmc_hostname(card->host), err);
+               return;
+       }
+
+       if (!card->ext_csd.raw_bkops_status)
+               return;
+
+       if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
+           from_exception)
+               return;
+
+       mmc_claim_host(card->host);
+       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
+               timeout = MMC_OPS_TIMEOUT_MS;
+               use_busy_signal = true;
+       } else {
+               timeout = 0;
+               use_busy_signal = false;
+       }
+
+       mmc_retune_hold(card->host);
+
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                       EXT_CSD_BKOPS_START, 1, timeout, 0,
+                       use_busy_signal, true, false);
+       if (err) {
+               pr_warn("%s: Error %d starting bkops\n",
+                       mmc_hostname(card->host), err);
+               mmc_retune_release(card->host);
+               goto out;
+       }
+
+       /*
+        * For urgent bkops status (LEVEL_2 and more)
+        * bkops executed synchronously, otherwise
+        * the operation is in progress
+        */
+       if (!use_busy_signal)
+               mmc_card_set_doing_bkops(card);
+       else
+               mmc_retune_release(card->host);
+out:
+       mmc_release_host(card->host);
+}
+
+/*
+ * Flush the cache to the non-volatile storage.
+ */
+int mmc_flush_cache(struct mmc_card *card)
+{
+       int err = 0;
+
+       if (mmc_card_mmc(card) &&
+                       (card->ext_csd.cache_size > 0) &&
+                       (card->ext_csd.cache_ctrl & 1)) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                               EXT_CSD_FLUSH_CACHE, 1, 0);
+               if (err)
+                       pr_err("%s: cache flush error %d\n",
+                                       mmc_hostname(card->host), err);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(mmc_flush_cache);
+
 static int mmc_cmdq_switch(struct mmc_card *card, bool enable)
 {
        u8 val = enable ? EXT_CSD_CMDQ_MODE_ENABLED : 0;
This page took 0.035556 seconds and 4 git commands to generate.