summaryrefslogtreecommitdiffstats
path: root/drivers/mci/mci-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci/mci-core.c')
-rw-r--r--drivers/mci/mci-core.c193
1 files changed, 151 insertions, 42 deletions
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 90ac2a3748..f238cea54a 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -388,6 +388,35 @@ static int mci_switch(struct mci *mci, unsigned set, unsigned index,
return mci_send_cmd(mci, &cmd, NULL);
}
+static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
+{
+ unsigned ret = cap >> shift;
+
+ if (ret > 0x7fffffff) {
+ pr_warn("Limiting card size due to 31 bit contraints\n");
+ return 0x7fffffff;
+ }
+
+ return (int)ret;
+}
+
+static void mci_part_add(struct mci *mci, uint64_t size,
+ unsigned int part_cfg, char *name, int idx, bool ro,
+ int area_type)
+{
+ struct mci_part *part = &mci->part[mci->nr_parts];
+
+ part->mci = mci;
+ part->size = size;
+ part->blk.cdev.name = name;
+ part->blk.blockbits = SECTOR_SHIFT;
+ part->blk.num_blocks = mci_calc_blk_cnt(size, part->blk.blockbits);
+ part->area_type = area_type;
+ part->part_cfg = part_cfg;
+
+ mci->nr_parts++;
+}
+
/**
* Change transfer frequency for an MMC card
* @param mci MCI instance
@@ -442,6 +471,26 @@ static int mmc_change_freq(struct mci *mci)
else
mci->card_caps |= MMC_MODE_HS;
+ if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
+ mci->ext_csd[EXT_CSD_REV] >= 3 && mci->ext_csd[EXT_CSD_BOOT_MULT]) {
+ int idx;
+ unsigned int part_size;
+
+ for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
+ char *name;
+ part_size = mci->ext_csd[EXT_CSD_BOOT_MULT] << 17;
+
+ name = asprintf("%s.boot%d", mci->cdevname, idx);
+ mci_part_add(mci, part_size,
+ EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
+ name, idx, true,
+ MMC_BLK_DATA_AREA_BOOT);
+ }
+
+ mci->ext_csd_part_config = mci->ext_csd[EXT_CSD_PART_CONFIG];
+ mci->bootpart = (mci->ext_csd_part_config >> 3) & 0x7;
+ }
+
return 0;
}
@@ -1063,6 +1112,10 @@ static int mci_startup(struct mci *mci)
/* we setup the blocklength only one times for all accesses to this media */
err = mci_set_blocklen(mci, mci->read_bl_len);
+ mci_part_add(mci, mci->capacity, 0,
+ mci->cdevname, 0, true,
+ MMC_BLK_DATA_AREA_MAIN);
+
return err;
}
@@ -1104,6 +1157,36 @@ static int sd_send_if_cond(struct mci *mci)
return 0;
}
+static int mci_blk_part_switch(struct mci_part *part)
+{
+ struct mci *mci = part->mci;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS))
+ return 0;
+
+ if (mci->part_curr == part)
+ return 0;
+
+ if (!IS_SD(mci)) {
+ u8 part_config = mci->ext_csd_part_config;
+
+ part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+ part_config |= part->part_cfg;
+
+ ret = mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG, part_config);
+ if (ret)
+ return ret;
+
+ mci->ext_csd_part_config = part_config;
+ }
+
+ mci->part_curr = part;
+
+ return 0;
+}
+
/* ------------------ attach to the blocklayer --------------------------- */
/**
@@ -1119,10 +1202,13 @@ static int sd_send_if_cond(struct mci *mci)
static int __maybe_unused mci_sd_write(struct block_device *blk,
const void *buffer, int block, int num_blocks)
{
- struct mci *mci = container_of(blk, struct mci, blk);
+ struct mci_part *part = container_of(blk, struct mci_part, blk);
+ struct mci *mci = part->mci;
struct mci_host *host = mci->host;
int rc;
+ mci_blk_part_switch(part);
+
if (host->card_write_protected && host->card_write_protected(host)) {
dev_err(mci->mci_dev, "card write protected\n");
return -EPERM;
@@ -1165,9 +1251,12 @@ static int __maybe_unused mci_sd_write(struct block_device *blk,
static int mci_sd_read(struct block_device *blk, void *buffer, int block,
int num_blocks)
{
- struct mci *mci = container_of(blk, struct mci, blk);
+ struct mci_part *part = container_of(blk, struct mci_part, blk);
+ struct mci *mci = part->mci;
int rc;
+ mci_blk_part_switch(part);
+
dev_dbg(mci->mci_dev, "%s: Read %d block(s), starting at %d\n",
__func__, num_blocks, block);
@@ -1338,18 +1427,6 @@ static int mci_check_if_already_initialized(struct mci *mci)
return 0;
}
-static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
-{
- unsigned ret = cap >> shift;
-
- if (ret > 0x7fffffff) {
- pr_warn("Limiting card size due to 31 bit contraints\n");
- return 0x7fffffff;
- }
-
- return (int)ret;
-}
-
static struct block_device_ops mci_ops = {
.read = mci_sd_read,
#ifdef CONFIG_BLOCK_WRITE
@@ -1357,6 +1434,28 @@ static struct block_device_ops mci_ops = {
#endif
};
+static int mci_set_boot(struct param_d *param, void *priv)
+{
+ struct mci *mci = priv;
+
+ mci->ext_csd_part_config &= ~(7 << 3);
+ mci->ext_csd_part_config |= mci->bootpart << 3;
+
+ return mci_switch(mci, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG, mci->ext_csd_part_config);
+}
+
+static const char *mci_boot_names[] = {
+ "disabled",
+ "boot0",
+ "boot1",
+ NULL, /* reserved */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ "user",
+};
+
/**
* Probe an MCI card at the given host interface
* @param mci MCI device instance
@@ -1365,7 +1464,7 @@ static struct block_device_ops mci_ops = {
static int mci_card_probe(struct mci *mci)
{
struct mci_host *host = mci->host;
- int rc, disknum;
+ int i, rc, disknum;
if (host->card_present && !host->card_present(host)) {
dev_err(mci->mci_dev, "no card inserted\n");
@@ -1402,6 +1501,13 @@ static int mci_card_probe(struct mci *mci)
if (rc)
goto on_error;
+ if (host->devname) {
+ mci->cdevname = strdup(host->devname);
+ } else {
+ disknum = cdev_find_free_index("disk");
+ mci->cdevname = asprintf("disk%d", disknum);
+ }
+
rc = mci_startup(mci);
if (rc) {
dev_dbg(mci->mci_dev, "Card's startup fails with %d\n", rc);
@@ -1411,36 +1517,39 @@ static int mci_card_probe(struct mci *mci)
dev_dbg(mci->mci_dev, "Card is up and running now, registering as a disk\n");
mci->ready_for_use = 1; /* TODO now or later? */
- /*
- * An MMC/SD card acts like an ordinary disk.
- * So, re-use the disk driver to gain access to this media
- */
- mci->blk.dev = mci->mci_dev;
- mci->blk.ops = &mci_ops;
-
- if (host->devname) {
- mci->blk.cdev.name = strdup(host->devname);
- } else {
- disknum = cdev_find_free_index("disk");
- mci->blk.cdev.name = asprintf("disk%d", disknum);
- }
+ for (i = 0; i < mci->nr_parts; i++) {
+ struct mci_part *part = &mci->part[i];
- mci->blk.blockbits = SECTOR_SHIFT;
- mci->blk.num_blocks = mci_calc_blk_cnt(mci->capacity, mci->blk.blockbits);
-
- rc = blockdevice_register(&mci->blk);
- if (rc != 0) {
- dev_err(mci->mci_dev, "Failed to register MCI/SD blockdevice\n");
- goto on_error;
- }
+ /*
+ * An MMC/SD card acts like an ordinary disk.
+ * So, re-use the disk driver to gain access to this media
+ */
+ part->blk.dev = mci->mci_dev;
+ part->blk.ops = &mci_ops;
- dev_info(mci->mci_dev, "registered %s\n", mci->blk.cdev.name);
+ rc = blockdevice_register(&part->blk);
+ if (rc != 0) {
+ dev_err(mci->mci_dev, "Failed to register MCI/SD blockdevice\n");
+ goto on_error;
+ }
+ dev_info(mci->mci_dev, "registered %s\n", part->blk.cdev.name);
+
+ /* create partitions on demand */
+ if (part->area_type == MMC_BLK_DATA_AREA_MAIN) {
+ rc = parse_partition_table(&part->blk);
+ if (rc != 0) {
+ dev_warn(mci->mci_dev, "No partition table found\n");
+ rc = 0; /* it's not a failure */
+ }
+ }
- /* create partitions on demand */
- rc = parse_partition_table(&mci->blk);
- if (rc != 0) {
- dev_warn(mci->mci_dev, "No partition table found\n");
- rc = 0; /* it's not a failure */
+ if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
+ part->area_type == MMC_BLK_DATA_AREA_BOOT &&
+ !mci->param_boot) {
+ mci->param_boot = dev_add_param_enum(mci->mci_dev, "boot",
+ mci_set_boot, NULL, &mci->bootpart,
+ mci_boot_names, ARRAY_SIZE(mci_boot_names), mci);
+ }
}
dev_dbg(mci->mci_dev, "SD Card successfully added\n");