mmc: core: support HPI send command
authorChuanxiao Dong <chuanxiao.dong@intel.com>
Mon, 24 Oct 2011 02:12:40 +0000 (10:12 +0800)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 21:21:17 +0000 (13:21 -0800)
HPI command is defined in eMMC4.41. This patch implements sending
HPI command to eMMC card if card supports.

Besides these changes, this patch also exported some sysfs which can
be used check whether the hpi is supported easily

Change-Id: I14ad85fe47a080564731228c3fe816f4f0b847eb
Signed-off-by: Chuanxiao Dong <chuanxiao.dong@intel.com>
Reviewed-on: http://android.intel.com:8080/23121
Reviewed-by: Gross, Mark <mark.gross@intel.com>
Tested-by: Gross, Mark <mark.gross@intel.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/mmc.h

index 6cfa8d7..cf1adf4 100644 (file)
@@ -333,6 +333,61 @@ 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.
+ */
+static int mmc_interrupt_hpi(struct mmc_card *card)
+{
+       int err;
+       u32 status;
+       unsigned long t1 = jiffies + 10 * HZ;
+
+       BUG_ON(!card);
+
+       if (!card->ext_csd.hpi_en)
+               return 1;
+
+       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) {
+               err = mmc_send_hpi_cmd(card, &status);
+               if (err && R1_CURRENT_STATE(status) == R1_STATE_PRG)
+                       goto out;
+       } else
+               goto out;
+
+       if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
+               do {
+                       if (time_after(jiffies, t1)) {
+                               err = -ETIMEDOUT;
+                               break;
+                       }
+                       /*
+                        * We don't know when the HPI command will finish
+                        * processing, so we need to check the card status
+                        * with SEND_STATUS.
+                        */
+                       err = mmc_send_status(card, &status);
+                       if (err)
+                               break;
+               } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
+       } else
+               return 0;
+out:
+       return err;
+}
+
+/**
  *     mmc_wait_for_cmd - start a command and wait for completion
  *     @host: MMC host to start command
  *     @cmd: MMC command to start
index 82237c5..2a46c63 100644 (file)
@@ -403,6 +403,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];
        }
@@ -500,6 +515,10 @@ MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
 MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
                card->ext_csd.enhanced_area_offset);
 MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
+MMC_DEV_ATTR(hpi_support, "%d\n", card->ext_csd.hpi);
+MMC_DEV_ATTR(hpi_enable, "%d\n", card->ext_csd.hpi_en);
+MMC_DEV_ATTR(hpi_command, "%d\n", card->ext_csd.hpi_cmd);
+MMC_DEV_ATTR(hw_reset_support, "%d\n", card->ext_csd.rst_n_function);
 
 static struct attribute *mmc_std_attrs[] = {
        &dev_attr_cid.attr,
@@ -515,6 +534,10 @@ static struct attribute *mmc_std_attrs[] = {
        &dev_attr_serial.attr,
        &dev_attr_enhanced_area_offset.attr,
        &dev_attr_enhanced_area_size.attr,
+       &dev_attr_hpi_support.attr,
+       &dev_attr_hpi_enable.attr,
+       &dev_attr_hpi_command.attr,
+       &dev_attr_hw_reset_support.attr,
        NULL,
 };
 
@@ -728,6 +751,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.
         */
        max_dtr = (unsigned int)-1;
index 770c3d0..a780d7a 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 58ffd35..9d5c7db 100644 (file)
@@ -65,10 +65,14 @@ struct mmc_ext_csd {
        unsigned long long      enhanced_area_offset;   /* Units: Byte */
        unsigned int            enhanced_area_size;     /* Units: KB */
        unsigned int            boot_size;              /* in bytes */
+       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 ed8fca8..5e636c9 100644 (file)
@@ -272,6 +272,7 @@ 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_RST_N_FUNCTION         162     /* R/W */
 #define EXT_CSD_WR_REL_PARAM           166     /* RO */
 #define EXT_CSD_ERASE_GROUP_DEF                175     /* R/W */
@@ -282,6 +283,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 */
@@ -294,6 +296,7 @@ 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_HPI_FEATURES           503     /* RO */
 
 /*
  * EXT_CSD field definitions