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*7d0b605aSŁukasz Majewski #include <mmc.h> 16cb383cd2SLukasz Majewski 17ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 18ea2453d5SPantelis Antoniou dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 19ea2453d5SPantelis Antoniou static long dfu_file_buf_len; 20ea2453d5SPantelis Antoniou 215a127c84SAfzal Mohammed static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, 22ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 23cb383cd2SLukasz Majewski { 24*7d0b605aSŁukasz Majewski struct mmc *mmc = find_mmc_device(dfu->dev_num); 25*7d0b605aSŁukasz Majewski u32 blk_start, blk_count, n = 0; 26cb383cd2SLukasz Majewski 27ea2453d5SPantelis Antoniou /* 28ea2453d5SPantelis Antoniou * We must ensure that we work in lba_blk_size chunks, so ALIGN 29ea2453d5SPantelis Antoniou * this value. 30ea2453d5SPantelis Antoniou */ 31ea2453d5SPantelis Antoniou *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); 32ea2453d5SPantelis Antoniou 33ea2453d5SPantelis Antoniou blk_start = dfu->data.mmc.lba_start + 34ea2453d5SPantelis Antoniou (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); 35ea2453d5SPantelis Antoniou blk_count = *len / dfu->data.mmc.lba_blk_size; 36ea2453d5SPantelis Antoniou if (blk_start + blk_count > 37ea2453d5SPantelis Antoniou dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { 38ea2453d5SPantelis Antoniou puts("Request would exceed designated area!\n"); 39ea2453d5SPantelis Antoniou return -EINVAL; 40ea2453d5SPantelis Antoniou } 41ea2453d5SPantelis Antoniou 42*7d0b605aSŁukasz Majewski debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, 43*7d0b605aSŁukasz Majewski op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, 44*7d0b605aSŁukasz Majewski blk_start, blk_count, buf); 45*7d0b605aSŁukasz Majewski switch (op) { 46*7d0b605aSŁukasz Majewski case DFU_OP_READ: 47*7d0b605aSŁukasz Majewski n = mmc->block_dev.block_read(dfu->dev_num, blk_start, 48*7d0b605aSŁukasz Majewski blk_count, buf); 49*7d0b605aSŁukasz Majewski break; 50*7d0b605aSŁukasz Majewski case DFU_OP_WRITE: 51*7d0b605aSŁukasz Majewski n = mmc->block_dev.block_write(dfu->dev_num, blk_start, 52*7d0b605aSŁukasz Majewski blk_count, buf); 53*7d0b605aSŁukasz Majewski break; 54*7d0b605aSŁukasz Majewski default: 55*7d0b605aSŁukasz Majewski error("Operation not supported\n"); 56*7d0b605aSŁukasz Majewski } 57cb383cd2SLukasz Majewski 58*7d0b605aSŁukasz Majewski if (n != blk_count) { 59*7d0b605aSŁukasz Majewski error("MMC operation failed"); 60*7d0b605aSŁukasz Majewski return -EIO; 61*7d0b605aSŁukasz Majewski } 62*7d0b605aSŁukasz Majewski 63*7d0b605aSŁukasz Majewski return 0; 64cb383cd2SLukasz Majewski } 65cb383cd2SLukasz Majewski 66ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) 67cb383cd2SLukasz Majewski { 68ea2453d5SPantelis Antoniou if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { 69ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 70ea2453d5SPantelis Antoniou return -EINVAL; 71cb383cd2SLukasz Majewski } 72cb383cd2SLukasz Majewski 73ea2453d5SPantelis Antoniou /* Add to the current buffer. */ 74ea2453d5SPantelis Antoniou memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); 75ea2453d5SPantelis Antoniou dfu_file_buf_len += *len; 76ea2453d5SPantelis Antoniou 77ea2453d5SPantelis Antoniou return 0; 78cb383cd2SLukasz Majewski } 79cb383cd2SLukasz Majewski 805a127c84SAfzal Mohammed static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, 81cb383cd2SLukasz Majewski void *buf, long *len) 82cb383cd2SLukasz Majewski { 83cb383cd2SLukasz Majewski char cmd_buf[DFU_CMD_BUF_SIZE]; 84cb383cd2SLukasz Majewski char *str_env; 85cb383cd2SLukasz Majewski int ret; 86cb383cd2SLukasz Majewski 8743e66272SŁukasz Majewski switch (dfu->layout) { 8843e66272SŁukasz Majewski case DFU_FS_FAT: 89ea2453d5SPantelis Antoniou sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", 90cb383cd2SLukasz Majewski op == DFU_OP_READ ? "load" : "write", 91cb383cd2SLukasz Majewski dfu->data.mmc.dev, dfu->data.mmc.part, 92ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 9343e66272SŁukasz Majewski break; 9443e66272SŁukasz Majewski case DFU_FS_EXT4: 95ea2453d5SPantelis Antoniou sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", 9643e66272SŁukasz Majewski op == DFU_OP_READ ? "load" : "write", 9743e66272SŁukasz Majewski dfu->data.mmc.dev, dfu->data.mmc.part, 98ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 9943e66272SŁukasz Majewski break; 10043e66272SŁukasz Majewski default: 10143e66272SŁukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 10243e66272SŁukasz Majewski dfu_get_layout(dfu->layout)); 103ea2453d5SPantelis Antoniou return -1; 10443e66272SŁukasz Majewski } 105cb383cd2SLukasz Majewski 10617eb1d8fSLukasz Majewski if (op == DFU_OP_WRITE) 10717eb1d8fSLukasz Majewski sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); 10817eb1d8fSLukasz Majewski 109cb383cd2SLukasz Majewski debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 110cb383cd2SLukasz Majewski 111cb383cd2SLukasz Majewski ret = run_command(cmd_buf, 0); 112cb383cd2SLukasz Majewski if (ret) { 113cb383cd2SLukasz Majewski puts("dfu: Read error!\n"); 114cb383cd2SLukasz Majewski return ret; 115cb383cd2SLukasz Majewski } 116cb383cd2SLukasz Majewski 11781c1d7b6SŁukasz Majewski if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { 118cb383cd2SLukasz Majewski str_env = getenv("filesize"); 119cb383cd2SLukasz Majewski if (str_env == NULL) { 120cb383cd2SLukasz Majewski puts("dfu: Wrong file size!\n"); 121cb383cd2SLukasz Majewski return -1; 122cb383cd2SLukasz Majewski } 123cb383cd2SLukasz Majewski *len = simple_strtoul(str_env, NULL, 16); 124cb383cd2SLukasz Majewski } 125cb383cd2SLukasz Majewski 126cb383cd2SLukasz Majewski return ret; 127cb383cd2SLukasz Majewski } 128cb383cd2SLukasz Majewski 129ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu, 130ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 131cb383cd2SLukasz Majewski { 132cb383cd2SLukasz Majewski int ret = -1; 133cb383cd2SLukasz Majewski 134cb383cd2SLukasz Majewski switch (dfu->layout) { 135cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 136ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 137cb383cd2SLukasz Majewski break; 138cb383cd2SLukasz Majewski case DFU_FS_FAT: 13943e66272SŁukasz Majewski case DFU_FS_EXT4: 140ea2453d5SPantelis Antoniou ret = mmc_file_buffer(dfu, buf, len); 141cb383cd2SLukasz Majewski break; 142cb383cd2SLukasz Majewski default: 143cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 144cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 145cb383cd2SLukasz Majewski } 146cb383cd2SLukasz Majewski 147cb383cd2SLukasz Majewski return ret; 148cb383cd2SLukasz Majewski } 149cb383cd2SLukasz Majewski 150ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu) 151ea2453d5SPantelis Antoniou { 152ea2453d5SPantelis Antoniou int ret = 0; 153ea2453d5SPantelis Antoniou 154ea2453d5SPantelis Antoniou if (dfu->layout != DFU_RAW_ADDR) { 155ea2453d5SPantelis Antoniou /* Do stuff here. */ 156ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, 157ea2453d5SPantelis Antoniou &dfu_file_buf_len); 158ea2453d5SPantelis Antoniou 159ea2453d5SPantelis Antoniou /* Now that we're done */ 160ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 161ea2453d5SPantelis Antoniou } 162ea2453d5SPantelis Antoniou 163ea2453d5SPantelis Antoniou return ret; 164ea2453d5SPantelis Antoniou } 165ea2453d5SPantelis Antoniou 166ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 167ea2453d5SPantelis Antoniou long *len) 168cb383cd2SLukasz Majewski { 169cb383cd2SLukasz Majewski int ret = -1; 170cb383cd2SLukasz Majewski 171cb383cd2SLukasz Majewski switch (dfu->layout) { 172cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 173ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 174cb383cd2SLukasz Majewski break; 175cb383cd2SLukasz Majewski case DFU_FS_FAT: 17643e66272SŁukasz Majewski case DFU_FS_EXT4: 177ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 178cb383cd2SLukasz Majewski break; 179cb383cd2SLukasz Majewski default: 180cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 181cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 182cb383cd2SLukasz Majewski } 183cb383cd2SLukasz Majewski 184cb383cd2SLukasz Majewski return ret; 185cb383cd2SLukasz Majewski } 186cb383cd2SLukasz Majewski 187cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) 188cb383cd2SLukasz Majewski { 1891b6ca18bSPantelis Antoniou int dev, part; 1901b6ca18bSPantelis Antoniou struct mmc *mmc; 1911b6ca18bSPantelis Antoniou block_dev_desc_t *blk_dev; 1921b6ca18bSPantelis Antoniou disk_partition_t partinfo; 193cb383cd2SLukasz Majewski char *st; 194cb383cd2SLukasz Majewski 195cb383cd2SLukasz Majewski dfu->dev_type = DFU_DEV_MMC; 196cb383cd2SLukasz Majewski st = strsep(&s, " "); 197cb383cd2SLukasz Majewski if (!strcmp(st, "mmc")) { 198cb383cd2SLukasz Majewski dfu->layout = DFU_RAW_ADDR; 199cb383cd2SLukasz Majewski dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); 200cb383cd2SLukasz Majewski dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); 201cb383cd2SLukasz Majewski dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); 202cb383cd2SLukasz Majewski } else if (!strcmp(st, "fat")) { 203cb383cd2SLukasz Majewski dfu->layout = DFU_FS_FAT; 20443e66272SŁukasz Majewski } else if (!strcmp(st, "ext4")) { 20543e66272SŁukasz Majewski dfu->layout = DFU_FS_EXT4; 2061b6ca18bSPantelis Antoniou } else if (!strcmp(st, "part")) { 2071b6ca18bSPantelis Antoniou 2081b6ca18bSPantelis Antoniou dfu->layout = DFU_RAW_ADDR; 2091b6ca18bSPantelis Antoniou 2101b6ca18bSPantelis Antoniou dev = simple_strtoul(s, &s, 10); 2111b6ca18bSPantelis Antoniou s++; 2121b6ca18bSPantelis Antoniou part = simple_strtoul(s, &s, 10); 2131b6ca18bSPantelis Antoniou 2141b6ca18bSPantelis Antoniou mmc = find_mmc_device(dev); 2151b6ca18bSPantelis Antoniou if (mmc == NULL || mmc_init(mmc)) { 216ea2453d5SPantelis Antoniou printf("%s: could not find mmc device #%d!\n", 217ea2453d5SPantelis Antoniou __func__, dev); 2181b6ca18bSPantelis Antoniou return -ENODEV; 2191b6ca18bSPantelis Antoniou } 2201b6ca18bSPantelis Antoniou 2211b6ca18bSPantelis Antoniou blk_dev = &mmc->block_dev; 2221b6ca18bSPantelis Antoniou if (get_partition_info(blk_dev, part, &partinfo) != 0) { 2231b6ca18bSPantelis Antoniou printf("%s: could not find partition #%d on mmc device #%d!\n", 2241b6ca18bSPantelis Antoniou __func__, part, dev); 2251b6ca18bSPantelis Antoniou return -ENODEV; 2261b6ca18bSPantelis Antoniou } 2271b6ca18bSPantelis Antoniou 2281b6ca18bSPantelis Antoniou dfu->data.mmc.lba_start = partinfo.start; 2291b6ca18bSPantelis Antoniou dfu->data.mmc.lba_size = partinfo.size; 2301b6ca18bSPantelis Antoniou dfu->data.mmc.lba_blk_size = partinfo.blksz; 2311b6ca18bSPantelis Antoniou 232cb383cd2SLukasz Majewski } else { 233cb383cd2SLukasz Majewski printf("%s: Memory layout (%s) not supported!\n", __func__, st); 2341b6ca18bSPantelis Antoniou return -ENODEV; 235cb383cd2SLukasz Majewski } 236cb383cd2SLukasz Majewski 23743e66272SŁukasz Majewski if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { 23843e66272SŁukasz Majewski dfu->data.mmc.dev = simple_strtoul(s, &s, 10); 23943e66272SŁukasz Majewski dfu->data.mmc.part = simple_strtoul(++s, &s, 10); 24043e66272SŁukasz Majewski } 24143e66272SŁukasz Majewski 242cb383cd2SLukasz Majewski dfu->read_medium = dfu_read_medium_mmc; 243cb383cd2SLukasz Majewski dfu->write_medium = dfu_write_medium_mmc; 244ea2453d5SPantelis Antoniou dfu->flush_medium = dfu_flush_medium_mmc; 245ea2453d5SPantelis Antoniou 246ea2453d5SPantelis Antoniou /* initial state */ 247ea2453d5SPantelis Antoniou dfu->inited = 0; 248cb383cd2SLukasz Majewski 249cb383cd2SLukasz Majewski return 0; 250cb383cd2SLukasz Majewski } 251