]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/dfu/dfu_mmc.c
dfu: mmc: Provide support for eMMC boot partition access
[people/ms/u-boot.git] / drivers / dfu / dfu_mmc.c
index 5e10ea7e66478a7308039e066989c6547d19a565..63cc876612c9aa44df4539e5e6c1a3b1d0af0b14 100644 (file)
@@ -18,11 +18,29 @@ static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
                                dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
 static long dfu_file_buf_len;
 
+static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
+{
+       int ret;
+
+       if (part == mmc->part_num)
+               return 0;
+
+       ret = mmc_switch_part(dfu->dev_num, part);
+       if (ret) {
+               error("Cannot switch to partition %d\n", part);
+               return ret;
+       }
+       mmc->part_num = part;
+
+       return 0;
+}
+
 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
                        u64 offset, void *buf, long *len)
 {
        struct mmc *mmc = find_mmc_device(dfu->dev_num);
        u32 blk_start, blk_count, n = 0;
+       int ret, part_num_bkp = 0;
 
        /*
         * We must ensure that we work in lba_blk_size chunks, so ALIGN
@@ -39,6 +57,13 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
                return -EINVAL;
        }
 
+       if (dfu->data.mmc.hw_partition >= 0) {
+               part_num_bkp = mmc->part_num;
+               ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
+               if (ret)
+                       return ret;
+       }
+
        debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
              op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num,
              blk_start, blk_count, buf);
@@ -57,9 +82,17 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
 
        if (n != blk_count) {
                error("MMC operation failed");
+               if (dfu->data.mmc.hw_partition >= 0)
+                       mmc_access_part(dfu, mmc, part_num_bkp);
                return -EIO;
        }
 
+       if (dfu->data.mmc.hw_partition >= 0) {
+               ret = mmc_access_part(dfu, mmc, part_num_bkp);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -194,6 +227,8 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
  *     2nd and 3rd:
  *             lba_start and lba_size, for raw write
  *             mmc_dev and mmc_part, for filesystems and part
+ *     4th (optional):
+ *             mmcpart <num> (access to HW eMMC partitions)
  */
 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
 {
@@ -233,11 +268,22 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
                return -ENODEV;
        }
 
+       dfu->data.mmc.hw_partition = -EINVAL;
        if (!strcmp(entity_type, "raw")) {
                dfu->layout                     = DFU_RAW_ADDR;
                dfu->data.mmc.lba_start         = second_arg;
                dfu->data.mmc.lba_size          = third_arg;
                dfu->data.mmc.lba_blk_size      = mmc->read_bl_len;
+
+               /*
+                * Check for an extra entry at dfu_alt_info env variable
+                * specifying the mmc HW defined partition number
+                */
+               if (s)
+                       if (!strcmp(strsep(&s, " "), "mmcpart"))
+                               dfu->data.mmc.hw_partition =
+                                       simple_strtoul(s, NULL, 0);
+
        } else if (!strcmp(entity_type, "part")) {
                disk_partition_t partinfo;
                block_dev_desc_t *blk_dev = &mmc->block_dev;