1cb383cd2SLukasz Majewski /* 2cb383cd2SLukasz Majewski * dfu.c -- DFU back-end routines 3cb383cd2SLukasz Majewski * 4cb383cd2SLukasz Majewski * Copyright (C) 2012 Samsung Electronics 5cb383cd2SLukasz Majewski * author: Lukasz Majewski <l.majewski@samsung.com> 6cb383cd2SLukasz Majewski * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 8cb383cd2SLukasz Majewski */ 9cb383cd2SLukasz Majewski 10cb383cd2SLukasz Majewski #include <common.h> 11cb383cd2SLukasz Majewski #include <malloc.h> 121b6ca18bSPantelis Antoniou #include <errno.h> 13ea2453d5SPantelis Antoniou #include <div64.h> 14cb383cd2SLukasz Majewski #include <dfu.h> 15*0e285b50SStephen Warren #include <ext4fs.h> 16*0e285b50SStephen Warren #include <fat.h> 177d0b605aSŁukasz Majewski #include <mmc.h> 18cb383cd2SLukasz Majewski 19ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 20ea2453d5SPantelis Antoniou dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 21ea2453d5SPantelis Antoniou static long dfu_file_buf_len; 22ea2453d5SPantelis Antoniou 23c8151b4aSLukasz Majewski static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) 24c8151b4aSLukasz Majewski { 25c8151b4aSLukasz Majewski int ret; 26c8151b4aSLukasz Majewski 27c8151b4aSLukasz Majewski if (part == mmc->part_num) 28c8151b4aSLukasz Majewski return 0; 29c8151b4aSLukasz Majewski 30c8151b4aSLukasz Majewski ret = mmc_switch_part(dfu->dev_num, part); 31c8151b4aSLukasz Majewski if (ret) { 32c8151b4aSLukasz Majewski error("Cannot switch to partition %d\n", part); 33c8151b4aSLukasz Majewski return ret; 34c8151b4aSLukasz Majewski } 35c8151b4aSLukasz Majewski mmc->part_num = part; 36c8151b4aSLukasz Majewski 37c8151b4aSLukasz Majewski return 0; 38c8151b4aSLukasz Majewski } 39c8151b4aSLukasz Majewski 405a127c84SAfzal Mohammed static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, 41ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 42cb383cd2SLukasz Majewski { 437d0b605aSŁukasz Majewski struct mmc *mmc = find_mmc_device(dfu->dev_num); 447d0b605aSŁukasz Majewski u32 blk_start, blk_count, n = 0; 45c8151b4aSLukasz Majewski int ret, part_num_bkp = 0; 46cb383cd2SLukasz Majewski 47ea2453d5SPantelis Antoniou /* 48ea2453d5SPantelis Antoniou * We must ensure that we work in lba_blk_size chunks, so ALIGN 49ea2453d5SPantelis Antoniou * this value. 50ea2453d5SPantelis Antoniou */ 51ea2453d5SPantelis Antoniou *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); 52ea2453d5SPantelis Antoniou 53ea2453d5SPantelis Antoniou blk_start = dfu->data.mmc.lba_start + 54ea2453d5SPantelis Antoniou (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); 55ea2453d5SPantelis Antoniou blk_count = *len / dfu->data.mmc.lba_blk_size; 56ea2453d5SPantelis Antoniou if (blk_start + blk_count > 57ea2453d5SPantelis Antoniou dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { 58ea2453d5SPantelis Antoniou puts("Request would exceed designated area!\n"); 59ea2453d5SPantelis Antoniou return -EINVAL; 60ea2453d5SPantelis Antoniou } 61ea2453d5SPantelis Antoniou 62c8151b4aSLukasz Majewski if (dfu->data.mmc.hw_partition >= 0) { 63c8151b4aSLukasz Majewski part_num_bkp = mmc->part_num; 64c8151b4aSLukasz Majewski ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition); 65c8151b4aSLukasz Majewski if (ret) 66c8151b4aSLukasz Majewski return ret; 67c8151b4aSLukasz Majewski } 68c8151b4aSLukasz Majewski 697d0b605aSŁukasz Majewski debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, 707d0b605aSŁukasz Majewski op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, 717d0b605aSŁukasz Majewski blk_start, blk_count, buf); 727d0b605aSŁukasz Majewski switch (op) { 737d0b605aSŁukasz Majewski case DFU_OP_READ: 747d0b605aSŁukasz Majewski n = mmc->block_dev.block_read(dfu->dev_num, blk_start, 757d0b605aSŁukasz Majewski blk_count, buf); 767d0b605aSŁukasz Majewski break; 777d0b605aSŁukasz Majewski case DFU_OP_WRITE: 787d0b605aSŁukasz Majewski n = mmc->block_dev.block_write(dfu->dev_num, blk_start, 797d0b605aSŁukasz Majewski blk_count, buf); 807d0b605aSŁukasz Majewski break; 817d0b605aSŁukasz Majewski default: 827d0b605aSŁukasz Majewski error("Operation not supported\n"); 837d0b605aSŁukasz Majewski } 84cb383cd2SLukasz Majewski 857d0b605aSŁukasz Majewski if (n != blk_count) { 867d0b605aSŁukasz Majewski error("MMC operation failed"); 87c8151b4aSLukasz Majewski if (dfu->data.mmc.hw_partition >= 0) 88c8151b4aSLukasz Majewski mmc_access_part(dfu, mmc, part_num_bkp); 897d0b605aSŁukasz Majewski return -EIO; 907d0b605aSŁukasz Majewski } 917d0b605aSŁukasz Majewski 92c8151b4aSLukasz Majewski if (dfu->data.mmc.hw_partition >= 0) { 93c8151b4aSLukasz Majewski ret = mmc_access_part(dfu, mmc, part_num_bkp); 94c8151b4aSLukasz Majewski if (ret) 95c8151b4aSLukasz Majewski return ret; 96c8151b4aSLukasz Majewski } 97c8151b4aSLukasz Majewski 987d0b605aSŁukasz Majewski return 0; 99cb383cd2SLukasz Majewski } 100cb383cd2SLukasz Majewski 101ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) 102cb383cd2SLukasz Majewski { 103ea2453d5SPantelis Antoniou if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { 104ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 105ea2453d5SPantelis Antoniou return -EINVAL; 106cb383cd2SLukasz Majewski } 107cb383cd2SLukasz Majewski 108ea2453d5SPantelis Antoniou /* Add to the current buffer. */ 109ea2453d5SPantelis Antoniou memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); 110ea2453d5SPantelis Antoniou dfu_file_buf_len += *len; 111ea2453d5SPantelis Antoniou 112ea2453d5SPantelis Antoniou return 0; 113cb383cd2SLukasz Majewski } 114cb383cd2SLukasz Majewski 1155a127c84SAfzal Mohammed static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, 116cb383cd2SLukasz Majewski void *buf, long *len) 117cb383cd2SLukasz Majewski { 118*0e285b50SStephen Warren const char *fsname, *opname; 119cb383cd2SLukasz Majewski char cmd_buf[DFU_CMD_BUF_SIZE]; 120cb383cd2SLukasz Majewski char *str_env; 121cb383cd2SLukasz Majewski int ret; 122cb383cd2SLukasz Majewski 12343e66272SŁukasz Majewski switch (dfu->layout) { 12443e66272SŁukasz Majewski case DFU_FS_FAT: 125*0e285b50SStephen Warren fsname = "fat"; 12643e66272SŁukasz Majewski break; 12743e66272SŁukasz Majewski case DFU_FS_EXT4: 128*0e285b50SStephen Warren fsname = "ext4"; 12943e66272SŁukasz Majewski break; 13043e66272SŁukasz Majewski default: 13143e66272SŁukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 13243e66272SŁukasz Majewski dfu_get_layout(dfu->layout)); 133ea2453d5SPantelis Antoniou return -1; 13443e66272SŁukasz Majewski } 135cb383cd2SLukasz Majewski 136*0e285b50SStephen Warren switch (op) { 137*0e285b50SStephen Warren case DFU_OP_READ: 138*0e285b50SStephen Warren opname = "load"; 139*0e285b50SStephen Warren break; 140*0e285b50SStephen Warren case DFU_OP_WRITE: 141*0e285b50SStephen Warren opname = "write"; 142*0e285b50SStephen Warren break; 143*0e285b50SStephen Warren case DFU_OP_SIZE: 144*0e285b50SStephen Warren opname = "size"; 145*0e285b50SStephen Warren break; 146*0e285b50SStephen Warren default: 147*0e285b50SStephen Warren return -1; 148*0e285b50SStephen Warren } 149*0e285b50SStephen Warren 150*0e285b50SStephen Warren sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname, 151*0e285b50SStephen Warren dfu->data.mmc.dev, dfu->data.mmc.part); 152*0e285b50SStephen Warren 153*0e285b50SStephen Warren if (op != DFU_OP_SIZE) 154*0e285b50SStephen Warren sprintf(cmd_buf + strlen(cmd_buf), " 0x%x", (unsigned int)buf); 155*0e285b50SStephen Warren 156*0e285b50SStephen Warren sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name); 157*0e285b50SStephen Warren 15817eb1d8fSLukasz Majewski if (op == DFU_OP_WRITE) 15917eb1d8fSLukasz Majewski sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); 16017eb1d8fSLukasz Majewski 161cb383cd2SLukasz Majewski debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 162cb383cd2SLukasz Majewski 163cb383cd2SLukasz Majewski ret = run_command(cmd_buf, 0); 164cb383cd2SLukasz Majewski if (ret) { 165cb383cd2SLukasz Majewski puts("dfu: Read error!\n"); 166cb383cd2SLukasz Majewski return ret; 167cb383cd2SLukasz Majewski } 168cb383cd2SLukasz Majewski 169*0e285b50SStephen Warren if (op != DFU_OP_WRITE) { 170cb383cd2SLukasz Majewski str_env = getenv("filesize"); 171cb383cd2SLukasz Majewski if (str_env == NULL) { 172cb383cd2SLukasz Majewski puts("dfu: Wrong file size!\n"); 173cb383cd2SLukasz Majewski return -1; 174cb383cd2SLukasz Majewski } 175cb383cd2SLukasz Majewski *len = simple_strtoul(str_env, NULL, 16); 176cb383cd2SLukasz Majewski } 177cb383cd2SLukasz Majewski 178cb383cd2SLukasz Majewski return ret; 179cb383cd2SLukasz Majewski } 180cb383cd2SLukasz Majewski 181ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu, 182ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 183cb383cd2SLukasz Majewski { 184cb383cd2SLukasz Majewski int ret = -1; 185cb383cd2SLukasz Majewski 186cb383cd2SLukasz Majewski switch (dfu->layout) { 187cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 188ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 189cb383cd2SLukasz Majewski break; 190cb383cd2SLukasz Majewski case DFU_FS_FAT: 19143e66272SŁukasz Majewski case DFU_FS_EXT4: 192ea2453d5SPantelis Antoniou ret = mmc_file_buffer(dfu, buf, len); 193cb383cd2SLukasz Majewski break; 194cb383cd2SLukasz Majewski default: 195cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 196cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 197cb383cd2SLukasz Majewski } 198cb383cd2SLukasz Majewski 199cb383cd2SLukasz Majewski return ret; 200cb383cd2SLukasz Majewski } 201cb383cd2SLukasz Majewski 202ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu) 203ea2453d5SPantelis Antoniou { 204ea2453d5SPantelis Antoniou int ret = 0; 205ea2453d5SPantelis Antoniou 206ea2453d5SPantelis Antoniou if (dfu->layout != DFU_RAW_ADDR) { 207ea2453d5SPantelis Antoniou /* Do stuff here. */ 208ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, 209ea2453d5SPantelis Antoniou &dfu_file_buf_len); 210ea2453d5SPantelis Antoniou 211ea2453d5SPantelis Antoniou /* Now that we're done */ 212ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 213ea2453d5SPantelis Antoniou } 214ea2453d5SPantelis Antoniou 215ea2453d5SPantelis Antoniou return ret; 216ea2453d5SPantelis Antoniou } 217ea2453d5SPantelis Antoniou 218*0e285b50SStephen Warren long dfu_get_medium_size_mmc(struct dfu_entity *dfu) 219*0e285b50SStephen Warren { 220*0e285b50SStephen Warren int ret; 221*0e285b50SStephen Warren long len; 222*0e285b50SStephen Warren 223*0e285b50SStephen Warren switch (dfu->layout) { 224*0e285b50SStephen Warren case DFU_RAW_ADDR: 225*0e285b50SStephen Warren return dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size; 226*0e285b50SStephen Warren case DFU_FS_FAT: 227*0e285b50SStephen Warren case DFU_FS_EXT4: 228*0e285b50SStephen Warren ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len); 229*0e285b50SStephen Warren if (ret < 0) 230*0e285b50SStephen Warren return ret; 231*0e285b50SStephen Warren return len; 232*0e285b50SStephen Warren default: 233*0e285b50SStephen Warren printf("%s: Layout (%s) not (yet) supported!\n", __func__, 234*0e285b50SStephen Warren dfu_get_layout(dfu->layout)); 235*0e285b50SStephen Warren return -1; 236*0e285b50SStephen Warren } 237*0e285b50SStephen Warren } 238*0e285b50SStephen Warren 239ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 240ea2453d5SPantelis Antoniou long *len) 241cb383cd2SLukasz Majewski { 242cb383cd2SLukasz Majewski int ret = -1; 243cb383cd2SLukasz Majewski 244cb383cd2SLukasz Majewski switch (dfu->layout) { 245cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 246ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 247cb383cd2SLukasz Majewski break; 248cb383cd2SLukasz Majewski case DFU_FS_FAT: 24943e66272SŁukasz Majewski case DFU_FS_EXT4: 250ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 251cb383cd2SLukasz Majewski break; 252cb383cd2SLukasz Majewski default: 253cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 254cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 255cb383cd2SLukasz Majewski } 256cb383cd2SLukasz Majewski 257cb383cd2SLukasz Majewski return ret; 258cb383cd2SLukasz Majewski } 259cb383cd2SLukasz Majewski 260711b931fSMateusz Zalega /* 261711b931fSMateusz Zalega * @param s Parameter string containing space-separated arguments: 262711b931fSMateusz Zalega * 1st: 263711b931fSMateusz Zalega * raw (raw read/write) 264711b931fSMateusz Zalega * fat (files) 265711b931fSMateusz Zalega * ext4 (^) 266711b931fSMateusz Zalega * part (partition image) 267711b931fSMateusz Zalega * 2nd and 3rd: 268711b931fSMateusz Zalega * lba_start and lba_size, for raw write 269711b931fSMateusz Zalega * mmc_dev and mmc_part, for filesystems and part 270c8151b4aSLukasz Majewski * 4th (optional): 271c8151b4aSLukasz Majewski * mmcpart <num> (access to HW eMMC partitions) 272711b931fSMateusz Zalega */ 273cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) 274cb383cd2SLukasz Majewski { 275711b931fSMateusz Zalega const char *entity_type; 276711b931fSMateusz Zalega size_t second_arg; 277711b931fSMateusz Zalega size_t third_arg; 278711b931fSMateusz Zalega 2791b6ca18bSPantelis Antoniou struct mmc *mmc; 280711b931fSMateusz Zalega 281711b931fSMateusz Zalega const char *argv[3]; 282711b931fSMateusz Zalega const char **parg = argv; 283711b931fSMateusz Zalega 284711b931fSMateusz Zalega for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { 285711b931fSMateusz Zalega *parg = strsep(&s, " "); 286711b931fSMateusz Zalega if (*parg == NULL) { 287711b931fSMateusz Zalega error("Invalid number of arguments.\n"); 288711b931fSMateusz Zalega return -ENODEV; 289711b931fSMateusz Zalega } 290711b931fSMateusz Zalega } 291711b931fSMateusz Zalega 292711b931fSMateusz Zalega entity_type = argv[0]; 293b7d4259aSMateusz Zalega /* 294b7d4259aSMateusz Zalega * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8, 295b7d4259aSMateusz Zalega * with default 10. 296b7d4259aSMateusz Zalega */ 297b7d4259aSMateusz Zalega second_arg = simple_strtoul(argv[1], NULL, 0); 298b7d4259aSMateusz Zalega third_arg = simple_strtoul(argv[2], NULL, 0); 299711b931fSMateusz Zalega 300711b931fSMateusz Zalega mmc = find_mmc_device(dfu->dev_num); 301711b931fSMateusz Zalega if (mmc == NULL) { 302711b931fSMateusz Zalega error("Couldn't find MMC device no. %d.\n", dfu->dev_num); 303711b931fSMateusz Zalega return -ENODEV; 304711b931fSMateusz Zalega } 305711b931fSMateusz Zalega 306711b931fSMateusz Zalega if (mmc_init(mmc)) { 307711b931fSMateusz Zalega error("Couldn't init MMC device.\n"); 308711b931fSMateusz Zalega return -ENODEV; 309711b931fSMateusz Zalega } 310711b931fSMateusz Zalega 311c8151b4aSLukasz Majewski dfu->data.mmc.hw_partition = -EINVAL; 312711b931fSMateusz Zalega if (!strcmp(entity_type, "raw")) { 313711b931fSMateusz Zalega dfu->layout = DFU_RAW_ADDR; 314711b931fSMateusz Zalega dfu->data.mmc.lba_start = second_arg; 315711b931fSMateusz Zalega dfu->data.mmc.lba_size = third_arg; 316711b931fSMateusz Zalega dfu->data.mmc.lba_blk_size = mmc->read_bl_len; 317c8151b4aSLukasz Majewski 318c8151b4aSLukasz Majewski /* 319c8151b4aSLukasz Majewski * Check for an extra entry at dfu_alt_info env variable 320c8151b4aSLukasz Majewski * specifying the mmc HW defined partition number 321c8151b4aSLukasz Majewski */ 322c8151b4aSLukasz Majewski if (s) 323c8151b4aSLukasz Majewski if (!strcmp(strsep(&s, " "), "mmcpart")) 324c8151b4aSLukasz Majewski dfu->data.mmc.hw_partition = 325c8151b4aSLukasz Majewski simple_strtoul(s, NULL, 0); 326c8151b4aSLukasz Majewski 327711b931fSMateusz Zalega } else if (!strcmp(entity_type, "part")) { 3281b6ca18bSPantelis Antoniou disk_partition_t partinfo; 329711b931fSMateusz Zalega block_dev_desc_t *blk_dev = &mmc->block_dev; 330711b931fSMateusz Zalega int mmcdev = second_arg; 331711b931fSMateusz Zalega int mmcpart = third_arg; 332cb383cd2SLukasz Majewski 333711b931fSMateusz Zalega if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) { 334711b931fSMateusz Zalega error("Couldn't find part #%d on mmc device #%d\n", 335711b931fSMateusz Zalega mmcpart, mmcdev); 3361b6ca18bSPantelis Antoniou return -ENODEV; 3371b6ca18bSPantelis Antoniou } 3381b6ca18bSPantelis Antoniou 339711b931fSMateusz Zalega dfu->layout = DFU_RAW_ADDR; 3401b6ca18bSPantelis Antoniou dfu->data.mmc.lba_start = partinfo.start; 3411b6ca18bSPantelis Antoniou dfu->data.mmc.lba_size = partinfo.size; 3421b6ca18bSPantelis Antoniou dfu->data.mmc.lba_blk_size = partinfo.blksz; 343711b931fSMateusz Zalega } else if (!strcmp(entity_type, "fat")) { 344711b931fSMateusz Zalega dfu->layout = DFU_FS_FAT; 345711b931fSMateusz Zalega } else if (!strcmp(entity_type, "ext4")) { 346711b931fSMateusz Zalega dfu->layout = DFU_FS_EXT4; 347cb383cd2SLukasz Majewski } else { 348711b931fSMateusz Zalega error("Memory layout (%s) not supported!\n", entity_type); 3491b6ca18bSPantelis Antoniou return -ENODEV; 350cb383cd2SLukasz Majewski } 351cb383cd2SLukasz Majewski 352711b931fSMateusz Zalega /* if it's NOT a raw write */ 353711b931fSMateusz Zalega if (strcmp(entity_type, "raw")) { 354711b931fSMateusz Zalega dfu->data.mmc.dev = second_arg; 355711b931fSMateusz Zalega dfu->data.mmc.part = third_arg; 35643e66272SŁukasz Majewski } 35743e66272SŁukasz Majewski 358711b931fSMateusz Zalega dfu->dev_type = DFU_DEV_MMC; 359*0e285b50SStephen Warren dfu->get_medium_size = dfu_get_medium_size_mmc; 360cb383cd2SLukasz Majewski dfu->read_medium = dfu_read_medium_mmc; 361cb383cd2SLukasz Majewski dfu->write_medium = dfu_write_medium_mmc; 362ea2453d5SPantelis Antoniou dfu->flush_medium = dfu_flush_medium_mmc; 363ea2453d5SPantelis Antoniou dfu->inited = 0; 364cb383cd2SLukasz Majewski 365cb383cd2SLukasz Majewski return 0; 366cb383cd2SLukasz Majewski } 367