mmc: core: support HPI send command
authorJaehoon Chung <jh80.chung@samsung.com>
Tue, 18 Oct 2011 05:26:42 +0000 (01:26 -0400)
committerChris Ball <cjb@laptop.org>
Wed, 26 Oct 2011 20:32:29 +0000 (16:32 -0400)
HPI command is defined in eMMC4.41.
This feature is important for eMMC4.5 devices.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
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/mmc.h

index 235bb6a..fe65bb3 100644 (file)
@@ -379,6 +379,63 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 }
 EXPORT_SYMBOL(mmc_wait_for_req);
 
+/**
+ *     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;
+
+       BUG_ON(!card);
+
+       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:
+       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
index de5900a..fb5bf01 100644 (file)
@@ -448,6 +448,21 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
        }
 
        if (card->ext_csd.rev >= 5) {
+               /* 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;
+               }
+
                card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
                card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
        }
@@ -895,6 +910,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                }
        }
 
+       /*
+        * Enable HPI feature (if supported)
+        */
+       if (card->ext_csd.hpi) {
+               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;
+       }
+
        /*
         * Compute bus speed.
         */
index 4e11d56..007863e 100644 (file)
@@ -547,3 +547,34 @@ 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;
+       if (opcode == MMC_STOP_TRANSMISSION)
+               flags = MMC_RSP_R1 | MMC_CMD_AC;
+       else if (opcode == MMC_SEND_STATUS)
+               flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+       cmd.opcode = opcode;
+       cmd.arg = card->rca << 16 | 1;
+       cmd.flags = flags;
+       cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
+
+       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;
+}
index 9276946..3dd8941 100644 (file)
@@ -26,6 +26,7 @@ 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);
 
 #endif
 
index 32492b7..1684d92 100644 (file)
@@ -69,10 +69,14 @@ struct mmc_ext_csd {
        unsigned long long      enhanced_area_offset;   /* Units: Byte */
        unsigned int            enhanced_area_size;     /* Units: KB */
        unsigned int            cache_size;             /* Units: KB */
+       bool                    hpi_en;                 /* HPI enablebit */
+       bool                    hpi;                    /* HPI support bit */
+       unsigned int            hpi_cmd;                /* cmd used as HPI */
        u8                      raw_partition_support;  /* 160 */
        u8                      raw_erased_mem_count;   /* 181 */
        u8                      raw_ext_csd_structure;  /* 194 */
        u8                      raw_card_type;          /* 196 */
+       u8                      out_of_int_time;        /* 198 */
        u8                      raw_s_a_timeout;                /* 217 */
        u8                      raw_hc_erase_gap_size;  /* 221 */
        u8                      raw_erase_timeout_mult; /* 223 */
index 67bac37..174a844 100644 (file)
@@ -136,6 +136,7 @@ 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 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 160e4c5..0e71356 100644 (file)
@@ -276,6 +276,7 @@ struct _mmc_csd {
 #define EXT_CSD_GP_SIZE_MULT           143     /* R/W */
 #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_RST_N_FUNCTION         162     /* R/W */
 #define EXT_CSD_SANITIZE_START         165     /* W */
 #define EXT_CSD_WR_REL_PARAM           166     /* RO */
@@ -288,6 +289,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_PWR_CL_52_195          200     /* RO */
 #define EXT_CSD_PWR_CL_26_195          201     /* RO */
@@ -311,6 +313,7 @@ struct _mmc_csd {
 #define EXT_CSD_POWER_OFF_LONG_TIME    247     /* RO */
 #define EXT_CSD_GENERIC_CMD6_TIME      248     /* RO */
 #define EXT_CSD_CACHE_SIZE             249     /* RO, 4 bytes */
+#define EXT_CSD_HPI_FEATURES           503     /* RO */
 
 /*
  * EXT_CSD field definitions