mmc: core: Add support for BKOPS and HPI interrupt
Shridhar Rasal [Wed, 25 Jan 2012 12:24:30 +0000 (17:24 +0530)]
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>

drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h

index 344d241..2a288e9 100644 (file)
@@ -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))
index 6952f77..69fb227 100644 (file)
@@ -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;
index 770c3d0..330b968 100644 (file)
@@ -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;
+}
index 9276946..d8f157d 100644 (file)
@@ -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
 
index b6a1fcf..9178aa4 100644 (file)
@@ -77,6 +77,12 @@ struct mmc_ext_csd {
        u8                      raw_sec_feature_support;/* 231 */
        u8                      raw_trim_mult;          /* 232 */
        u8                      raw_sectors[4];         /* 212 - 4 bytes */
+       bool                    hpi_en;                 /* HPI enablebit */
+       bool                    hpi;                    /* HPI support bit */
+       unsigned int            hpi_cmd;                /* cmd used as HPI */
+       u8                      out_of_int_time;        /* out of int time */
+       bool                    bk_ops;                 /* BK ops support bit */
+       bool                    bk_ops_en;              /* BK ops enable bit */
 };
 
 struct sd_scr {
@@ -359,6 +365,11 @@ static inline void __maybe_unused remove_quirk_sd(struct mmc_card *card,
        if (mmc_card_sd(card))
                card->quirks &= ~data;
 }
+#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_need_bkops(c) ((c)->state |= MMC_STATE_NEED_BKOPS)
+
+#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
+#define mmc_card_clr_need_bkops(c) ((c)->state &= ~MMC_STATE_NEED_BKOPS)
 
 static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
 {
index b8b1b7a..c3e55fa 100644 (file)
@@ -136,6 +136,9 @@ struct mmc_async_req;
 
 extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
                                           struct mmc_async_req *, int *);
+extern int mmc_interrupt_hpi(struct mmc_card *);
+extern int mmc_bkops_start(struct mmc_card *card, bool is_synchronous);
+
 extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
 extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
 extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
index 51128f8..8c0bf3f 100644 (file)
@@ -230,6 +230,7 @@ struct mmc_host {
 #define MMC_CAP_MAX_CURRENT_600        (1 << 28)       /* Host max current limit is 600mA */
 #define MMC_CAP_MAX_CURRENT_800        (1 << 29)       /* Host max current limit is 800mA */
 #define MMC_CAP_CMD23          (1 << 30)       /* CMD23 supported. */
+#define MMC_CAP_BKOPS          (1 << 31)       /* Host supports BKOPS */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
 
index 5a794cb..6c5fc36 100644 (file)
@@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
 #define R1_CURRENT_STATE(x)    ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
 #define R1_READY_FOR_DATA      (1 << 8)        /* sx, a */
 #define R1_SWITCH_ERROR                (1 << 7)        /* sx, c */
+#define R1_URGENT_BKOPS        (1 << 6)        /* sr, a */
 #define R1_APP_CMD             (1 << 5)        /* sr, c */
 
 #define R1_STATE_IDLE  0
@@ -151,6 +152,11 @@ static inline bool mmc_op_multi(u32 opcode)
 #define R1_STATE_DIS   8
 
 /*
+ * MMC/SD card state
+ */
+#define R1_STATE_PRG           0x7
+
+/*
  * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
  * R1 is the low order byte; R2 is the next highest byte, when present.
  */
@@ -272,6 +278,9 @@ struct _mmc_csd {
 
 #define EXT_CSD_PARTITION_ATTRIBUTE    156     /* R/W */
 #define EXT_CSD_PARTITION_SUPPORT      160     /* RO */
+#define EXT_CSD_HPI_MGMT               161     /* R/W */
+#define EXT_CSD_BKOPS_EN               163     /* R/W */
+#define EXT_CSD_BKOPS_START            164     /* R/W */
 #define EXT_CSD_WR_REL_PARAM           166     /* RO */
 #define EXT_CSD_ERASE_GROUP_DEF                175     /* R/W */
 #define EXT_CSD_PART_CONFIG            179     /* R/W */
@@ -281,6 +290,7 @@ struct _mmc_csd {
 #define EXT_CSD_REV                    192     /* RO */
 #define EXT_CSD_STRUCTURE              194     /* RO */
 #define EXT_CSD_CARD_TYPE              196     /* RO */
+#define EXT_CSD_OUT_OF_INTERRUPT_TIME  198     /* RO */
 #define EXT_CSD_PART_SWITCH_TIME        199     /* RO */
 #define EXT_CSD_SEC_CNT                        212     /* RO, 4 bytes */
 #define EXT_CSD_S_A_TIMEOUT            217     /* RO */
@@ -293,6 +303,9 @@ struct _mmc_csd {
 #define EXT_CSD_SEC_ERASE_MULT         230     /* RO */
 #define EXT_CSD_SEC_FEATURE_SUPPORT    231     /* RO */
 #define EXT_CSD_TRIM_MULT              232     /* RO */
+#define EXT_CSD_BKOPS_STATUS           246     /* RO */
+#define EXT_CSD_BKOPS_SUPPORT          502     /* RO */
+#define EXT_CSD_HPI_FEATURES           503     /* RO */
 
 /*
  * EXT_CSD field definitions