summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Zhu <r65037@freescale.com>2010-08-12 16:34:23 +0800
committerFrank Li <Frank.Li@freescale.com>2011-04-01 11:33:11 +0800
commite359812d5a3cdac1ea1ad93f46915000b5bba99e (patch)
tree8494a91a7cec535ee97a87d83e037e2af98b3887
parent88e349106bf8abeaef4873af6e33a9b98697ff4d (diff)
ENGR00126228 eMMC: Configure boot_partition_enable
Enable the configurations of the boot enable on the eMMC cards. Add the interface that used to configure the boot_bus_width In order to make sure that the re-read the ext-csd of card can be completed successfully, add the method to wait for the finish of the busy state. NOTE: The following are the valid inputs when configure the boot bus width of the eMMC cards. +--------------------------------------------------------------------+ | Bit7 Bit6 Bit5 | Bit4 Bit3 | Bit2 | Bit1 Bit0 | |----------------|----------------------------------|----------------| | X | BOOT_MODE | RESET_BOOT_BUS_WIDTH | BOOT_BUS_WIDTH | +--------------------------------------------------------------------+ Bit [4:3] : BOOT_MODE (non-volatile) 0x0 : Use single data rate + backward compatible timings in boot operation (default) 0x1 : Use single data rate + high speed timings in boot operation mode 0x2 : Use dual data rate in boot operation 0x3 : Reserved Bit [2]: RESET_BOOT_BUS_WIDTH (non-volatile) 0x0 : Reset bus width to x1, single data rate and backward compatible timings after boot operation (default) 0x1 : Retain boot bus width and boot mode after boot operation Bit[1:0] : BOOT_BUS_WIDTH (non-volatile) 0x0 : x1 (sdr) or x4 (ddr) bus width in boot operation mode (default) 0x1 : x4 (sdr/ddr) bus width in boot operation mode 0x2 : x8 (sdr/ddr) bus width in boot operation mode 0x3 : Reserved The following are the valid inputs when configure the boot partitions of the eMMC cards. +------------------------------------------------------------+ | Bit7 | Bit6 | Bit5 Bit4 Bit3 | Bit2 Bit1 Bit0 | |------|----------|-----------------------|------------------| | X | BOOT_ACK | BOOT_PARTITION_ENABLE | PARTITION_ACCESS | +------------------------------------------------------------+ Bit7: Reserved Bit6: always set to vaule '1' when boot_part is enabled Bit[5:3]: 0x0 : Device not boot enabled (default) 0x1 : Boot partition 1 enabled for boot 0x2 : Boot partition 2 enabled for boot 0x7 : User area enabled for boot Bit[2:0]: 0x0 : No access to boot partition (default) 0x1 : R/W boot partition 1 0x2 : R/W boot partition 2 So only the '0, 1, 2; 8, 9, 10; 16, 17, 18; 56, 57, 58' are valid parameters when configure the boot_partiton. Signed-off-by: Richard Zhu <r65037@freescale.com>
-rw-r--r--drivers/mmc/core/mmc.c233
-rw-r--r--include/linux/mmc/card.h1
-rw-r--r--include/linux/mmc/mmc.h25
3 files changed, 232 insertions, 27 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ea57760ae1a9..5d304072c0bd 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -260,6 +260,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
card->ext_csd.boot_info = ext_csd[EXT_CSD_BOOT_INFO];
card->ext_csd.boot_size_mult = ext_csd[EXT_CSD_BOOT_SIZE_MULT];
card->ext_csd.boot_config = ext_csd[EXT_CSD_BOOT_CONFIG];
+ card->ext_csd.boot_bus_width = ext_csd[EXT_CSD_BOOT_BUS_WIDTH];
card->ext_csd.card_type = ext_csd[EXT_CSD_CARD_TYPE];
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
@@ -328,34 +329,21 @@ out:
return err;
}
-/* switch the partitions */
+/* configure the boot partitions */
static ssize_t
-switch_partitions(struct device *dev, struct device_attribute *attr,
+setup_boot_partitions(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- int err;
+ int err, busy = 0;
u32 part, new_part;
u8 *ext_csd, boot_config;
+ struct mmc_command cmd;
struct mmc_card *card = container_of(dev, struct mmc_card, dev);
- struct mmc_host *host = card->host;
BUG_ON(!card);
- mmc_claim_host(card->host);
sscanf(buf, "%d\n", &part);
- /* partition must be -
- * 0 - user area
- * 1 - boot partition 1
- * 2 - boot partition 2
- */
- if (part > 2) {
- printk(KERN_ERR "%s: wrong partition id"
- " 0 (user area), 1 (boot1), 2 (boot2)\n",
- mmc_hostname(card->host));
- return -EINVAL;
- }
-
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) {
printk(KERN_ERR "%s: invalid mmc version"
" mmc version is below version 4!)\n",
@@ -378,6 +366,7 @@ switch_partitions(struct device *dev, struct device_attribute *attr,
return -ENOMEM;
}
+ mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
if (err) {
printk(KERN_ERR "%s: unable to read EXT_CSD.\n",
@@ -385,9 +374,62 @@ switch_partitions(struct device *dev, struct device_attribute *attr,
goto err_rtn;
}
+ /* enable the boot partition in boot mode */
+ /* boot enable be -
+ * 0x00 - disable boot enable.
+ * 0x08 - boot partition 1 is enabled for boot.
+ * 0x10 - boot partition 2 is enabled for boot.
+ * 0x38 - User area is enabled for boot.
+ */
+ switch (part & EXT_CSD_BOOT_PARTITION_ENABLE_MASK) {
+ case 0:
+ boot_config = (ext_csd[EXT_CSD_BOOT_CONFIG]
+ & ~EXT_CSD_BOOT_PARTITION_ENABLE_MASK
+ & ~EXT_CSD_BOOT_ACK_ENABLE);
+ break;
+ case EXT_CSD_BOOT_PARTITION_PART1:
+ boot_config = ((ext_csd[EXT_CSD_BOOT_CONFIG]
+ & ~EXT_CSD_BOOT_PARTITION_ENABLE_MASK)
+ | EXT_CSD_BOOT_PARTITION_PART1
+ | EXT_CSD_BOOT_ACK_ENABLE);
+ break;
+ case EXT_CSD_BOOT_PARTITION_PART2:
+ boot_config = ((ext_csd[EXT_CSD_BOOT_CONFIG]
+ & ~EXT_CSD_BOOT_PARTITION_ENABLE_MASK)
+ | EXT_CSD_BOOT_PARTITION_PART2
+ | EXT_CSD_BOOT_ACK_ENABLE);
+ break;
+ case EXT_CSD_BOOT_PARTITION_ENABLE_MASK:
+ boot_config = ((ext_csd[EXT_CSD_BOOT_CONFIG]
+ | EXT_CSD_BOOT_PARTITION_ENABLE_MASK)
+ & ~EXT_CSD_BOOT_ACK_ENABLE);
+ break;
+ default:
+ printk(KERN_ERR "%s: wrong boot config parameter"
+ " 00 (disable boot), 08 (enable boot1),"
+ "16 (enable boot2), 56 (User area)\n",
+ mmc_hostname(card->host));
+ err = -EINVAL;
+ goto err_rtn;
+ }
+
+ /* switch the partitions that used to be accessed in OS layer */
+ /* partition must be -
+ * 0 - user area
+ * 1 - boot partition 1
+ * 2 - boot partition 2
+ */
+ if ((part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK) > 2) {
+ printk(KERN_ERR "%s: wrong partition id"
+ " 0 (user area), 1 (boot1), 2 (boot2)\n",
+ mmc_hostname(card->host));
+ err = -EINVAL;
+ goto err_rtn;
+ }
+
/* Send SWITCH command to change partition for access */
- boot_config = (ext_csd[EXT_CSD_BOOT_CONFIG] &
- ~EXT_CSD_BOOT_PARTITION_ACCESS_MASK) | part;
+ boot_config &= ~EXT_CSD_BOOT_PARTITION_ACCESS_MASK;
+ boot_config |= (part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BOOT_CONFIG, boot_config);
if (err) {
@@ -397,17 +439,41 @@ switch_partitions(struct device *dev, struct device_attribute *attr,
goto err_rtn;
}
+ /* waiting for the card to finish the busy state */
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err || busy > 100) {
+ printk(KERN_ERR "%s: failed to wait for"
+ "the busy state to end.\n",
+ mmc_hostname(card->host));
+ break;
+ }
+
+ if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+ printk(KERN_INFO "%s: card is in busy state"
+ "pls wait for busy state to end.\n",
+ mmc_hostname(card->host));
+ }
+ busy++;
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
/* Now check whether it works */
err = mmc_send_ext_csd(card, ext_csd);
if (err) {
- printk(KERN_ERR "%s: %d unable to read EXT_CSD.\n",
+ printk(KERN_ERR "%s: %d unable to re-read EXT_CSD.\n",
mmc_hostname(card->host), err);
goto err_rtn;
}
new_part = ext_csd[EXT_CSD_BOOT_CONFIG] &
EXT_CSD_BOOT_PARTITION_ACCESS_MASK;
- if (part != new_part) {
+ if ((part & EXT_CSD_BOOT_PARTITION_ACCESS_MASK) != new_part) {
printk(KERN_ERR "%s: after SWITCH, current part id %d is not"
" same as requested partition %d!\n",
mmc_hostname(card->host), new_part, part);
@@ -424,6 +490,120 @@ err_rtn:
return count;
}
+/* configure the boot bus */
+static ssize_t
+setup_boot_bus(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err, busy = 0;
+ u32 boot_bus, new_bus;
+ u8 *ext_csd;
+ struct mmc_command cmd;
+ struct mmc_card *card = container_of(dev, struct mmc_card, dev);
+
+ BUG_ON(!card);
+
+ sscanf(buf, "%d\n", &boot_bus);
+
+ if (card->csd.mmca_vsn < CSD_SPEC_VER_4) {
+ printk(KERN_ERR "%s: invalid mmc version"
+ " mmc version is below version 4!)\n",
+ mmc_hostname(card->host));
+ return -EINVAL;
+ }
+
+ /* it's a normal SD/MMC but user request to configure boot bus */
+ if (card->ext_csd.boot_size_mult <= 0) {
+ printk(KERN_ERR "%s: this is a normal SD/MMC card"
+ " but you request to configure boot bus !\n",
+ mmc_hostname(card->host));
+ return -EINVAL;
+ }
+
+ ext_csd = kmalloc(512, GFP_KERNEL);
+ if (!ext_csd) {
+ printk(KERN_ERR "%s: could not allocate a buffer to "
+ "receive the ext_csd.\n", mmc_hostname(card->host));
+ return -ENOMEM;
+ }
+
+ mmc_claim_host(card->host);
+ err = mmc_send_ext_csd(card, ext_csd);
+ if (err) {
+ printk(KERN_ERR "%s: unable to read EXT_CSD.\n",
+ mmc_hostname(card->host));
+ goto err_rtn;
+ }
+
+ /* Configure the boot bus width when boot partition is enabled */
+ if (((boot_bus & EXT_CSD_BOOT_BUS_WIDTH_MODE_MASK) >> 3) > 2
+ || (boot_bus & EXT_CSD_BOOT_BUS_WIDTH_WIDTH_MASK) > 2
+ || (boot_bus & ~EXT_CSD_BOOT_BUS_WIDTH_MASK) > 0) {
+ printk(KERN_ERR "%s: Invalid inputs!\n",
+ mmc_hostname(card->host));
+ err = -EINVAL;
+ goto err_rtn;
+ }
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BOOT_BUS_WIDTH, boot_bus);
+ if (err) {
+ printk(KERN_ERR "%s: fail to send SWITCH command to "
+ "card to swich partition for access!\n",
+ mmc_hostname(card->host));
+ goto err_rtn;
+ }
+
+ /* waiting for the card to finish the busy state */
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err || busy > 100) {
+ printk(KERN_ERR "%s: failed to wait for"
+ "the busy state to end.\n",
+ mmc_hostname(card->host));
+ break;
+ }
+
+ if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+ printk(KERN_INFO "%s: card is in busy state"
+ "pls wait for busy state to end.\n",
+ mmc_hostname(card->host));
+ }
+ busy++;
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
+ /* Now check whether it works */
+ err = mmc_send_ext_csd(card, ext_csd);
+ if (err) {
+ printk(KERN_ERR "%s: %d unable to re-read EXT_CSD.\n",
+ mmc_hostname(card->host), err);
+ goto err_rtn;
+ }
+
+ new_bus = ext_csd[EXT_CSD_BOOT_BUS_WIDTH];
+ if (boot_bus != new_bus) {
+ printk(KERN_ERR "%s: after SWITCH, current boot bus mode %d"
+ " is not same as requested bus mode %d!\n",
+ mmc_hostname(card->host), new_bus, boot_bus);
+ goto err_rtn;
+ }
+ card->ext_csd.boot_bus_width = ext_csd[EXT_CSD_BOOT_BUS_WIDTH];
+
+err_rtn:
+ mmc_release_host(card->host);
+ kfree(ext_csd);
+ if (err)
+ return err;
+ else
+ return count;
+}
+
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
@@ -437,10 +617,12 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
-MMC_DEV_ATTR(boot_info, "Info:0x%02x;Size:0x%02xMB;Part:0x%02x\n",
- card->ext_csd.boot_info, card->ext_csd.boot_size_mult / 8,
- card->ext_csd.boot_config);
-DEVICE_ATTR(boot_config, S_IWUGO, NULL, switch_partitions);
+MMC_DEV_ATTR(boot_info, "boot_info:0x%02x; boot_size:%04dKB;"
+ " boot_partition:0x%02x; boot_bus:0x%02x\n",
+ card->ext_csd.boot_info, card->ext_csd.boot_size_mult * 128,
+ card->ext_csd.boot_config, card->ext_csd.boot_bus_width);
+DEVICE_ATTR(boot_config, S_IWUGO, NULL, setup_boot_partitions);
+DEVICE_ATTR(boot_bus_config, S_IWUGO, NULL, setup_boot_bus);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
@@ -456,6 +638,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_serial.attr,
&dev_attr_boot_info.attr,
&dev_attr_boot_config.attr,
+ &dev_attr_boot_bus_config.attr,
NULL,
};
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 40a22ea81c3b..43dea1dde6bf 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -58,6 +58,7 @@ struct mmc_ext_csd {
unsigned char boot_info;
unsigned char boot_size_mult;
unsigned char boot_config;
+ unsigned char boot_bus_width;
};
struct sd_scr {
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index e57b7e5069d0..7725f8ac14a7 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -271,7 +271,7 @@ struct _mmc_csd {
#define EXT_CSD_BOOT_CONFIG 179 /* R/W */
#define EXT_CSD_BOOT_SIZE_MULT 226 /* RO, 1 bytes */
#define EXT_CSD_BOOT_INFO 228 /* RO, 1 bytes */
-
+#define EXT_CSD_BOOT_BUS_WIDTH 177
/*
* EXT_CSD field definitions
*/
@@ -300,7 +300,28 @@ struct _mmc_csd {
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
#define EXT_CSD_SEC_GB_CL_EN BIT(4)
-#define EXT_CSD_BOOT_PARTITION_ACCESS_MASK (0x3)
+#define EXT_CSD_BOOT_BUS_WIDTH_MASK (0x1F)
+#define EXT_CSD_BOOT_BUS_WIDTH_MODE_MASK (0x3 << 3)
+#define EXT_CSD_BOOT_BUS_WIDTH_MODE_SDR_NORMAL (0x0)
+#define EXT_CSD_BOOT_BUS_WIDTH_MODE_SDR_HIGH (0x1)
+#define EXT_CSD_BOOT_BUS_WIDTH_MODE_DDR (0x2)
+#define EXT_CSD_BOOT_BUS_WIDTH_RST_WIDTH (1 << 2)
+#define EXT_CSD_BOOT_BUS_WIDTH_WIDTH_MASK (0x3)
+#define EXT_CSD_BOOT_BUS_WIDTH_1_SDR_4_DDR (0x0)
+#define EXT_CSD_BOOT_BUS_WIDTH_4_SDR_4_DDR (0x1)
+#define EXT_CSD_BOOT_BUS_WIDTH_8_SDR_8_DDR (0x2)
+
+#define EXT_CSD_BOOT_ACK_ENABLE (0x1 << 6)
+#define EXT_CSD_BOOT_PARTITION_ENABLE_MASK (0x7 << 3)
+#define EXT_CSD_BOOT_PARTITION_DISABLE (0x0)
+#define EXT_CSD_BOOT_PARTITION_PART1 (0x1 << 3)
+#define EXT_CSD_BOOT_PARTITION_PART2 (0x2 << 3)
+#define EXT_CSD_BOOT_PARTITION_USER (0x7 << 3)
+
+#define EXT_CSD_BOOT_PARTITION_ACCESS_MASK (0x7)
+#define EXT_CSD_BOOT_PARTITION_ACCESS_DISABLE (0x0)
+#define EXT_CSD_BOOT_PARTITION_ACCESS_PART1 (0x1)
+#define EXT_CSD_BOOT_PARTITION_ACCESS_PART2 (0x2)
/*
* MMC_SWITCH access modes