diff options
-rw-r--r-- | drivers/mmc/core/mmc.c | 107 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 4 | ||||
-rw-r--r-- | include/linux/mmc/mmc.h | 5 |
3 files changed, 116 insertions, 0 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 16006ef153fe..ea57760ae1a9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -257,6 +257,11 @@ static int mmc_read_ext_csd(struct mmc_card *card) mmc_card_set_blockaddr(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.card_type = ext_csd[EXT_CSD_CARD_TYPE]; + switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: @@ -323,6 +328,102 @@ out: return err; } +/* switch the partitions */ +static ssize_t +switch_partitions(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + u32 part, new_part; + u8 *ext_csd, boot_config; + 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", + mmc_hostname(card->host)); + return -EINVAL; + } + + /* it's a normal SD/MMC but user request to configure boot partition */ + if (card->ext_csd.boot_size_mult <= 0) { + printk(KERN_ERR "%s: this is a normal SD/MMC card" + " but you request to access boot partition!\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; + } + + 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; + } + + /* Send SWITCH command to change partition for access */ + boot_config = (ext_csd[EXT_CSD_BOOT_CONFIG] & + ~EXT_CSD_BOOT_PARTITION_ACCESS_MASK) | part; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_CONFIG, boot_config); + 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; + } + + /* 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", + 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) { + 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); + goto err_rtn; + } + card->ext_csd.boot_config = ext_csd[EXT_CSD_BOOT_CONFIG]; + +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], @@ -336,6 +437,10 @@ 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); static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, @@ -349,6 +454,8 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_name.attr, &dev_attr_oemid.attr, &dev_attr_serial.attr, + &dev_attr_boot_info.attr, + &dev_attr_boot_config.attr, NULL, }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8ce082781ccb..40a22ea81c3b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -54,6 +54,10 @@ struct mmc_ext_csd { unsigned int sec_trim_mult; /* Secure trim multiplier */ unsigned int sec_erase_mult; /* Secure erase multiplier */ unsigned int trim_timeout; /* In milliseconds */ +#define MMC_DDR_MODE_MASK (0x3<<2) + unsigned char boot_info; + unsigned char boot_size_mult; + unsigned char boot_config; }; struct sd_scr { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 612301f85d14..e57b7e5069d0 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -268,6 +268,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_BOOT_CONFIG 179 /* R/W */ +#define EXT_CSD_BOOT_SIZE_MULT 226 /* RO, 1 bytes */ +#define EXT_CSD_BOOT_INFO 228 /* RO, 1 bytes */ /* * EXT_CSD field definitions @@ -297,6 +300,8 @@ 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) + /* * MMC_SWITCH access modes */ |