diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 56a98f5273..f0ceede2d1 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -23,6 +23,12 @@ config DFU_NAND This option enables using DFU to read and write to NAND based storage. +config DFU_MTD + bool "MTD back end for DFU" + help + This option enables using DFU to read and write to MTD based + storage. + config DFU_RAM bool "RAM back end for DFU" help diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 61f2b71f91..c769d8c52a 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_FUNCTION_DFU) += dfu.o obj-$(CONFIG_DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_DFU_MTD) += dfu_mtd.o obj-$(CONFIG_DFU_NAND) += dfu_nand.o obj-$(CONFIG_DFU_RAM) += dfu_ram.o obj-$(CONFIG_DFU_SF) += dfu_sf.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 2c22b625b8..7bd5389b2b 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -400,6 +400,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, if (strcmp(interface, "mmc") == 0) { if (dfu_fill_entity_mmc(dfu, devstr, s)) return -1; + } else if (strcmp(interface, "mtd") == 0) { + if (dfu_fill_entity_mtd(dfu, devstr, s)) + return -1; } else if (strcmp(interface, "nand") == 0) { if (dfu_fill_entity_nand(dfu, devstr, s)) return -1; diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c new file mode 100644 index 0000000000..200d99d349 --- /dev/null +++ b/drivers/dfu/dfu_mtd.c @@ -0,0 +1,163 @@ +/* + * (C) Copyright 2021 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static int dfu_write_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + struct blk_desc *dev_desc; + u64 block_start, block_len; + int ret = -ENODEV; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + /* if buf == NULL return total size of the area */ + if (!buf) { + *len = dfu->data.nand.size; + return 0; + } + + dev_desc = rockchip_get_bootdev(); + if (!dev_desc) { + printf("%s: dev_desc is NULL!\n", __func__); + return -ENODEV; + } + + /* in case of ubi partition, erase rest of the partition */ + if (dfu->data.mtd.ubi && !offset) { + block_start = dfu->data.mtd.start >> 9; + block_len = dfu->data.mtd.size >> 9; + + ret = blk_derase(dev_desc, block_start, block_len); + if (ret != 0) + printf("Failure erase: %d\n", ret); + } + + block_start = (dfu->data.mtd.start + offset) >> 9; + block_len = (*len) >> 9; + + ret = blk_dwrite(dev_desc, block_start, block_len, buf); + if (ret == block_len) + ret = 0; + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size) +{ + *size = dfu->data.mtd.size; + + return 0; +} + +static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf, long *len) +{ + struct blk_desc *dev_desc; + u64 block_start, block_len; + int ret = -ENODEV; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + /* if buf == NULL return total size of the area */ + if (!buf) { + *len = dfu->data.nand.size; + return 0; + } + + dev_desc = rockchip_get_bootdev(); + if (!dev_desc) { + printf("%s: dev_desc is NULL!\n", __func__); + return -ENODEV; + } + + block_start = (dfu->data.mtd.start + offset) >> 9; + block_len = (*len) >> 9; + + ret = blk_dread(dev_desc, block_start, block_len, buf); + if (ret == block_len) + ret = 0; + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +static int dfu_flush_medium_mtd(struct dfu_entity *dfu) +{ + return 0; +} + +unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu) +{ + /* + * Currently, Poll Timeout != 0 is only needed on nand + * ubi partition, as the not used sectors need an erase + */ + if (dfu->data.mtd.ubi) + return DFU_MANIFEST_POLL_TIMEOUT; + + return DFU_DEFAULT_POLL_TIMEOUT; +} + +int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) +{ + struct blk_desc *dev_desc; + disk_partition_t dfu_part; + char *st; + + dfu->data.mtd.ubi = 0; + dfu->dev_type = DFU_DEV_MTD; + st = strsep(&s, " "); + + if (!strcmp(st, "raw")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.mtd.start = simple_strtoul(s, &s, 16); + s++; + dfu->data.mtd.size = simple_strtoul(s, &s, 16); + } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) { + dev_desc = rockchip_get_bootdev(); + if (!dev_desc) { + printf("%s: dev_desc is NULL!\n", __func__); + return -ENODEV; + } + dfu->layout = DFU_RAW_ADDR; + if (part_get_info_by_name(dev_desc, s, &dfu_part) < 0) + return -EIO; + + dfu->data.mtd.start = dfu_part.start << 9; + dfu->data.mtd.size = dfu_part.size << 9; + if (!strcmp(st, "partubi")) + dfu->data.mtd.ubi = 1; + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + return -1; + } + + dfu->get_medium_size = dfu_get_medium_size_mtd; + dfu->read_medium = dfu_read_medium_mtd; + dfu->write_medium = dfu_write_medium_mtd; + dfu->flush_medium = dfu_flush_medium_mtd; + dfu->poll_timeout = dfu_polltimeout_mtd; + + /* initial state */ + dfu->inited = 0; + + return 0; +} diff --git a/include/dfu.h b/include/dfu.h index 7e322d9d27..506291a49e 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -23,6 +23,7 @@ enum dfu_device_type { DFU_DEV_NAND, DFU_DEV_RAM, DFU_DEV_SF, + DFU_DEV_MTD, }; enum dfu_layout { @@ -67,6 +68,17 @@ struct nand_internal_data { unsigned int ubi; }; +struct mtd_internal_data { + /* RAW programming */ + u64 start; + u64 size; + + unsigned int dev; + unsigned int part; + /* for nand/ubi use */ + unsigned int ubi; +}; + struct ram_internal_data { void *start; unsigned int size; @@ -108,6 +120,7 @@ struct dfu_entity { struct nand_internal_data nand; struct ram_internal_data ram; struct sf_internal_data sf; + struct mtd_internal_data mtd; } data; int (*get_medium_size)(struct dfu_entity *dfu, u64 *size); @@ -214,6 +227,17 @@ static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, } #endif +#ifdef CONFIG_DFU_MTD +extern int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s); +#else +static inline int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, + char *s) +{ + puts("MTD support not available!\n"); + return -1; +} +#endif + #ifdef CONFIG_DFU_NAND extern int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s); #else