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 * 7cb383cd2SLukasz Majewski * This program is free software; you can redistribute it and/or modify 8cb383cd2SLukasz Majewski * it under the terms of the GNU General Public License as published by 9cb383cd2SLukasz Majewski * the Free Software Foundation; either version 2 of the License, or 10cb383cd2SLukasz Majewski * (at your option) any later version. 11cb383cd2SLukasz Majewski * 12cb383cd2SLukasz Majewski * This program is distributed in the hope that it will be useful, 13cb383cd2SLukasz Majewski * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cb383cd2SLukasz Majewski * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15cb383cd2SLukasz Majewski * GNU General Public License for more details. 16cb383cd2SLukasz Majewski * 17cb383cd2SLukasz Majewski * You should have received a copy of the GNU General Public License 18cb383cd2SLukasz Majewski * along with this program; if not, write to the Free Software 19cb383cd2SLukasz Majewski * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20cb383cd2SLukasz Majewski */ 21cb383cd2SLukasz Majewski 22cb383cd2SLukasz Majewski #include <common.h> 23cb383cd2SLukasz Majewski #include <malloc.h> 241b6ca18bSPantelis Antoniou #include <errno.h> 25*ea2453d5SPantelis Antoniou #include <div64.h> 26cb383cd2SLukasz Majewski #include <dfu.h> 27cb383cd2SLukasz Majewski 28cb383cd2SLukasz Majewski enum dfu_mmc_op { 29cb383cd2SLukasz Majewski DFU_OP_READ = 1, 30cb383cd2SLukasz Majewski DFU_OP_WRITE, 31cb383cd2SLukasz Majewski }; 32cb383cd2SLukasz Majewski 33*ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) 34*ea2453d5SPantelis Antoniou dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 35*ea2453d5SPantelis Antoniou static long dfu_file_buf_len; 36*ea2453d5SPantelis Antoniou 37cb383cd2SLukasz Majewski static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, 38*ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 39cb383cd2SLukasz Majewski { 40cb383cd2SLukasz Majewski char cmd_buf[DFU_CMD_BUF_SIZE]; 41*ea2453d5SPantelis Antoniou u32 blk_start, blk_count; 42cb383cd2SLukasz Majewski 43*ea2453d5SPantelis Antoniou /* 44*ea2453d5SPantelis Antoniou * We must ensure that we work in lba_blk_size chunks, so ALIGN 45*ea2453d5SPantelis Antoniou * this value. 46*ea2453d5SPantelis Antoniou */ 47*ea2453d5SPantelis Antoniou *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); 48*ea2453d5SPantelis Antoniou 49*ea2453d5SPantelis Antoniou blk_start = dfu->data.mmc.lba_start + 50*ea2453d5SPantelis Antoniou (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); 51*ea2453d5SPantelis Antoniou blk_count = *len / dfu->data.mmc.lba_blk_size; 52*ea2453d5SPantelis Antoniou if (blk_start + blk_count > 53*ea2453d5SPantelis Antoniou dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { 54*ea2453d5SPantelis Antoniou puts("Request would exceed designated area!\n"); 55*ea2453d5SPantelis Antoniou return -EINVAL; 56*ea2453d5SPantelis Antoniou } 57*ea2453d5SPantelis Antoniou 58*ea2453d5SPantelis Antoniou sprintf(cmd_buf, "mmc %s %p %x %x", 59cb383cd2SLukasz Majewski op == DFU_OP_READ ? "read" : "write", 60*ea2453d5SPantelis Antoniou buf, blk_start, blk_count); 61cb383cd2SLukasz Majewski 62cb383cd2SLukasz Majewski debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 63cb383cd2SLukasz Majewski return run_command(cmd_buf, 0); 64cb383cd2SLukasz Majewski } 65cb383cd2SLukasz Majewski 66*ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) 67cb383cd2SLukasz Majewski { 68*ea2453d5SPantelis Antoniou if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { 69*ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 70*ea2453d5SPantelis Antoniou return -EINVAL; 71cb383cd2SLukasz Majewski } 72cb383cd2SLukasz Majewski 73*ea2453d5SPantelis Antoniou /* Add to the current buffer. */ 74*ea2453d5SPantelis Antoniou memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); 75*ea2453d5SPantelis Antoniou dfu_file_buf_len += *len; 76*ea2453d5SPantelis Antoniou 77*ea2453d5SPantelis Antoniou return 0; 78cb383cd2SLukasz Majewski } 79cb383cd2SLukasz Majewski 80cb383cd2SLukasz Majewski static int mmc_file_op(enum dfu_mmc_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: 89*ea2453d5SPantelis 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, 92*ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 93*ea2453d5SPantelis Antoniou if (op == DFU_OP_WRITE) 94*ea2453d5SPantelis Antoniou sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); 9543e66272SŁukasz Majewski break; 9643e66272SŁukasz Majewski case DFU_FS_EXT4: 97*ea2453d5SPantelis Antoniou sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", 9843e66272SŁukasz Majewski op == DFU_OP_READ ? "load" : "write", 9943e66272SŁukasz Majewski dfu->data.mmc.dev, dfu->data.mmc.part, 100*ea2453d5SPantelis Antoniou (unsigned int) buf, dfu->name); 10143e66272SŁukasz Majewski break; 10243e66272SŁukasz Majewski default: 10343e66272SŁukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 10443e66272SŁukasz Majewski dfu_get_layout(dfu->layout)); 105*ea2453d5SPantelis Antoniou return -1; 10643e66272SŁukasz Majewski } 107cb383cd2SLukasz Majewski 108cb383cd2SLukasz Majewski debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); 109cb383cd2SLukasz Majewski 110cb383cd2SLukasz Majewski ret = run_command(cmd_buf, 0); 111cb383cd2SLukasz Majewski if (ret) { 112cb383cd2SLukasz Majewski puts("dfu: Read error!\n"); 113cb383cd2SLukasz Majewski return ret; 114cb383cd2SLukasz Majewski } 115cb383cd2SLukasz Majewski 11681c1d7b6SŁukasz Majewski if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { 117cb383cd2SLukasz Majewski str_env = getenv("filesize"); 118cb383cd2SLukasz Majewski if (str_env == NULL) { 119cb383cd2SLukasz Majewski puts("dfu: Wrong file size!\n"); 120cb383cd2SLukasz Majewski return -1; 121cb383cd2SLukasz Majewski } 122cb383cd2SLukasz Majewski *len = simple_strtoul(str_env, NULL, 16); 123cb383cd2SLukasz Majewski } 124cb383cd2SLukasz Majewski 125cb383cd2SLukasz Majewski return ret; 126cb383cd2SLukasz Majewski } 127cb383cd2SLukasz Majewski 128*ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu, 129*ea2453d5SPantelis Antoniou u64 offset, void *buf, long *len) 130cb383cd2SLukasz Majewski { 131cb383cd2SLukasz Majewski int ret = -1; 132cb383cd2SLukasz Majewski 133cb383cd2SLukasz Majewski switch (dfu->layout) { 134cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 135*ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 136cb383cd2SLukasz Majewski break; 137cb383cd2SLukasz Majewski case DFU_FS_FAT: 13843e66272SŁukasz Majewski case DFU_FS_EXT4: 139*ea2453d5SPantelis Antoniou ret = mmc_file_buffer(dfu, buf, len); 140cb383cd2SLukasz Majewski break; 141cb383cd2SLukasz Majewski default: 142cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 143cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 144cb383cd2SLukasz Majewski } 145cb383cd2SLukasz Majewski 146cb383cd2SLukasz Majewski return ret; 147cb383cd2SLukasz Majewski } 148cb383cd2SLukasz Majewski 149*ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu) 150*ea2453d5SPantelis Antoniou { 151*ea2453d5SPantelis Antoniou int ret = 0; 152*ea2453d5SPantelis Antoniou 153*ea2453d5SPantelis Antoniou if (dfu->layout != DFU_RAW_ADDR) { 154*ea2453d5SPantelis Antoniou /* Do stuff here. */ 155*ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, 156*ea2453d5SPantelis Antoniou &dfu_file_buf_len); 157*ea2453d5SPantelis Antoniou 158*ea2453d5SPantelis Antoniou /* Now that we're done */ 159*ea2453d5SPantelis Antoniou dfu_file_buf_len = 0; 160*ea2453d5SPantelis Antoniou } 161*ea2453d5SPantelis Antoniou 162*ea2453d5SPantelis Antoniou return ret; 163*ea2453d5SPantelis Antoniou } 164*ea2453d5SPantelis Antoniou 165*ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 166*ea2453d5SPantelis Antoniou long *len) 167cb383cd2SLukasz Majewski { 168cb383cd2SLukasz Majewski int ret = -1; 169cb383cd2SLukasz Majewski 170cb383cd2SLukasz Majewski switch (dfu->layout) { 171cb383cd2SLukasz Majewski case DFU_RAW_ADDR: 172*ea2453d5SPantelis Antoniou ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 173cb383cd2SLukasz Majewski break; 174cb383cd2SLukasz Majewski case DFU_FS_FAT: 17543e66272SŁukasz Majewski case DFU_FS_EXT4: 176*ea2453d5SPantelis Antoniou ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 177cb383cd2SLukasz Majewski break; 178cb383cd2SLukasz Majewski default: 179cb383cd2SLukasz Majewski printf("%s: Layout (%s) not (yet) supported!\n", __func__, 180cb383cd2SLukasz Majewski dfu_get_layout(dfu->layout)); 181cb383cd2SLukasz Majewski } 182cb383cd2SLukasz Majewski 183cb383cd2SLukasz Majewski return ret; 184cb383cd2SLukasz Majewski } 185cb383cd2SLukasz Majewski 186cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) 187cb383cd2SLukasz Majewski { 1881b6ca18bSPantelis Antoniou int dev, part; 1891b6ca18bSPantelis Antoniou struct mmc *mmc; 1901b6ca18bSPantelis Antoniou block_dev_desc_t *blk_dev; 1911b6ca18bSPantelis Antoniou disk_partition_t partinfo; 192cb383cd2SLukasz Majewski char *st; 193cb383cd2SLukasz Majewski 194cb383cd2SLukasz Majewski dfu->dev_type = DFU_DEV_MMC; 195cb383cd2SLukasz Majewski st = strsep(&s, " "); 196cb383cd2SLukasz Majewski if (!strcmp(st, "mmc")) { 197cb383cd2SLukasz Majewski dfu->layout = DFU_RAW_ADDR; 198cb383cd2SLukasz Majewski dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); 199cb383cd2SLukasz Majewski dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); 200cb383cd2SLukasz Majewski dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); 201cb383cd2SLukasz Majewski } else if (!strcmp(st, "fat")) { 202cb383cd2SLukasz Majewski dfu->layout = DFU_FS_FAT; 20343e66272SŁukasz Majewski } else if (!strcmp(st, "ext4")) { 20443e66272SŁukasz Majewski dfu->layout = DFU_FS_EXT4; 2051b6ca18bSPantelis Antoniou } else if (!strcmp(st, "part")) { 2061b6ca18bSPantelis Antoniou 2071b6ca18bSPantelis Antoniou dfu->layout = DFU_RAW_ADDR; 2081b6ca18bSPantelis Antoniou 2091b6ca18bSPantelis Antoniou dev = simple_strtoul(s, &s, 10); 2101b6ca18bSPantelis Antoniou s++; 2111b6ca18bSPantelis Antoniou part = simple_strtoul(s, &s, 10); 2121b6ca18bSPantelis Antoniou 2131b6ca18bSPantelis Antoniou mmc = find_mmc_device(dev); 2141b6ca18bSPantelis Antoniou if (mmc == NULL || mmc_init(mmc)) { 215*ea2453d5SPantelis Antoniou printf("%s: could not find mmc device #%d!\n", 216*ea2453d5SPantelis Antoniou __func__, dev); 2171b6ca18bSPantelis Antoniou return -ENODEV; 2181b6ca18bSPantelis Antoniou } 2191b6ca18bSPantelis Antoniou 2201b6ca18bSPantelis Antoniou blk_dev = &mmc->block_dev; 2211b6ca18bSPantelis Antoniou if (get_partition_info(blk_dev, part, &partinfo) != 0) { 2221b6ca18bSPantelis Antoniou printf("%s: could not find partition #%d on mmc device #%d!\n", 2231b6ca18bSPantelis Antoniou __func__, part, dev); 2241b6ca18bSPantelis Antoniou return -ENODEV; 2251b6ca18bSPantelis Antoniou } 2261b6ca18bSPantelis Antoniou 2271b6ca18bSPantelis Antoniou dfu->data.mmc.lba_start = partinfo.start; 2281b6ca18bSPantelis Antoniou dfu->data.mmc.lba_size = partinfo.size; 2291b6ca18bSPantelis Antoniou dfu->data.mmc.lba_blk_size = partinfo.blksz; 2301b6ca18bSPantelis Antoniou 231cb383cd2SLukasz Majewski } else { 232cb383cd2SLukasz Majewski printf("%s: Memory layout (%s) not supported!\n", __func__, st); 2331b6ca18bSPantelis Antoniou return -ENODEV; 234cb383cd2SLukasz Majewski } 235cb383cd2SLukasz Majewski 23643e66272SŁukasz Majewski if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { 23743e66272SŁukasz Majewski dfu->data.mmc.dev = simple_strtoul(s, &s, 10); 23843e66272SŁukasz Majewski dfu->data.mmc.part = simple_strtoul(++s, &s, 10); 23943e66272SŁukasz Majewski } 24043e66272SŁukasz Majewski 241cb383cd2SLukasz Majewski dfu->read_medium = dfu_read_medium_mmc; 242cb383cd2SLukasz Majewski dfu->write_medium = dfu_write_medium_mmc; 243*ea2453d5SPantelis Antoniou dfu->flush_medium = dfu_flush_medium_mmc; 244*ea2453d5SPantelis Antoniou 245*ea2453d5SPantelis Antoniou /* initial state */ 246*ea2453d5SPantelis Antoniou dfu->inited = 0; 247cb383cd2SLukasz Majewski 248cb383cd2SLukasz Majewski return 0; 249cb383cd2SLukasz Majewski } 250