diff options
Diffstat (limited to 'drivers/mmc/core/mmc.c')
-rw-r--r-- | drivers/mmc/core/mmc.c | 132 |
1 files changed, 128 insertions, 4 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 06084dbf1277..ff6e15125fa9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -121,7 +121,7 @@ static int mmc_decode_csd(struct mmc_card *card) * v1.2 has extra information in bits 15, 11 and 10. */ csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { + if (csd_struct < 1 || csd_struct > 3) { printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", mmc_hostname(card->host), csd_struct); return -EINVAL; @@ -208,7 +208,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) } ext_csd_struct = ext_csd[EXT_CSD_REV]; - if (ext_csd_struct > 3) { + if (ext_csd_struct > 5) { printk(KERN_ERR "%s: unrecognised EXT_CSD structure " "version %d\n", mmc_hostname(card->host), ext_csd_struct); @@ -226,7 +226,16 @@ 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]) { + case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 + | EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + break; case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: card->ext_csd.hs_max_dtr = 52000000; break; @@ -238,6 +247,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) printk(KERN_WARNING "%s: card is mmc v4 but doesn't " "support any high-speed modes.\n", mmc_hostname(card->host)); + printk(KERN_WARNING "%s: card type is 0x%x\n", + mmc_hostname(card->host), ext_csd[EXT_CSD_CARD_TYPE]); goto out; } @@ -247,6 +258,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], @@ -258,6 +365,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, @@ -269,6 +380,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, }; @@ -434,10 +547,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Activate wide bus (if supported). */ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { + (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA + | MMC_CAP_DATA_DDR))) { unsigned ext_csd_bit, bus_width; - if (host->caps & MMC_CAP_8_BIT_DATA) { + if ((host->caps & MMC_CAP_8_BIT_DATA) && + (host->caps & MMC_CAP_DATA_DDR) && + (card->ext_csd.card_type & MMC_DDR_MODE_MASK)) { + ext_csd_bit = EXT_CSD_BUS_WIDTH_8_DDR; + bus_width = MMC_BUS_WIDTH_8 | MMC_BUS_WIDTH_DDR; + } else if ((host->caps & MMC_CAP_4_BIT_DATA) && + (host->caps & MMC_CAP_DATA_DDR) && + (card->ext_csd.card_type & MMC_DDR_MODE_MASK)) { + ext_csd_bit = EXT_CSD_BUS_WIDTH_4_DDR; + bus_width = MMC_BUS_WIDTH_4 | MMC_BUS_WIDTH_DDR; + } else if (host->caps & MMC_CAP_8_BIT_DATA) { ext_csd_bit = EXT_CSD_BUS_WIDTH_8; bus_width = MMC_BUS_WIDTH_8; } else { |