[U-Boot] [RFC PATCH 00/14] dfu: update dfu stack and use them for stm32mp1

This serie based on v2019.07 propose some update on the DFU stack: - add capability to have several DFU backend running in parallel - add MTD backend for NAND, NOR or SPI-NAND - add VIRTUAL backend for board/command specific behavior - add some weak callback
To test the feature and as example, I update the stm32mp1 to use the new features (tested with command "dfu 0")
This serie prepares the DFU backend for communication with STM32CubeProgrammer on stm32mp1 platform (stm32prog command). This STMicroelectronics tool is based on DFU protocol and update the boot devices and the OTPs on the ST boards.
Patrick Delaunay (14): dfu: cosmetic: cleanup sf to avoid checkpatch error dfu: sf: add partition support for nor backend dfu: prepare the support of multiple interface dfu: allow to manage DFU on several devices dfu: allow read with 0 data for EOF indication dfu: add backend for MTD device dfu: add partition support for MTD backend dfu: add DFU virtual backend dfu: add callback for flush and initiated operation stm32mp1: activate DFU support and command MTD stm32mp1: activate SET_DFU_ALT_INFO stp32mp1: configs: activate CONFIG_MTD_SPI_NAND stm32mp1: board: add spi nand support stm32mp1: add support for virtual partition read
board/st/stm32mp1/README | 111 +++++++++++++ board/st/stm32mp1/stm32mp1.c | 165 ++++++++++++++++++- cmd/dfu.c | 21 ++- configs/stm32mp15_basic_defconfig | 6 + configs/stm32mp15_trusted_defconfig | 6 + drivers/dfu/Kconfig | 13 ++ drivers/dfu/Makefile | 2 + drivers/dfu/dfu.c | 145 +++++++++++++++-- drivers/dfu/dfu_mtd.c | 306 ++++++++++++++++++++++++++++++++++++ drivers/dfu/dfu_sf.c | 55 ++++++- drivers/dfu/dfu_virt.c | 49 ++++++ include/configs/stm32mp1.h | 38 ++++- include/dfu.h | 51 ++++++ 13 files changed, 939 insertions(+), 29 deletions(-) create mode 100644 drivers/dfu/dfu_mtd.c create mode 100644 drivers/dfu/dfu_virt.c

Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu.c | 7 ++++--- drivers/dfu/dfu_sf.c | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 3189495..eb3a3c6 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -477,14 +477,15 @@ int dfu_config_entities(char *env, char *interface, char *devstr)
const char *dfu_get_dev_type(enum dfu_device_type t) { - const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", "SF" }; + const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", + "SF"}; return dev_t[t]; }
const char *dfu_get_layout(enum dfu_layout l) { - const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", - "EXT3", "EXT4", "RAM_ADDR" }; + const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", + "EXT3", "EXT4", "RAM_ADDR" }; return dfu_layout[l]; }
diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index 066e767..b78fcfd 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -19,7 +19,7 @@ static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) }
static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf, - long *len) + long *len) { return spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset, *len, buf); @@ -32,7 +32,7 @@ static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset) }
static int dfu_write_medium_sf(struct dfu_entity *dfu, - u64 offset, void *buf, long *len) + u64 offset, void *buf, long *len) { int ret;

Hi Patrick,
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu.c | 7 ++++--- drivers/dfu/dfu_sf.c | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 3189495..eb3a3c6 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -477,14 +477,15 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) {
- const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND",
"RAM", "SF" };
- const char *const dev_t[] = {NULL, "eMMC", "OneNAND",
"NAND", "RAM",
return dev_t[t];"SF"};
}
const char *dfu_get_layout(enum dfu_layout l) {
- const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
"EXT3", "EXT4",
"RAM_ADDR" };
- const char *const dfu_layout[] = {NULL, "RAW_ADDR", "FAT",
"EXT2",
"EXT3", "EXT4",
"RAM_ADDR" }; return dfu_layout[l]; }
diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index 066e767..b78fcfd 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -19,7 +19,7 @@ static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) }
static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
long *len)
{ return spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset, *len, buf); @@ -32,7 +32,7 @@ static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset) }
static int dfu_write_medium_sf(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
u64 offset, void *buf, long *len)
{ int ret;
Thanks for fixing this.
Acked-by: Lukasz Majewski lukma@denx.de
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Copy the partition support from NAND backend to SF, support part and partubi option. In case of ubi partition, erase the rest of the partition as it is mandatory for UBI.
for example:
U-Boot> env set dfu_alt_info "spl part 0 1;\ u-boot part 0 2;u-boot-env part 0 3;UBI partubi 0 4" U-Boot> dfu 0 sf 0:0:10000000:0
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu_sf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 2 ++ 2 files changed, 53 insertions(+)
diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index b78fcfd..d401b76 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -10,6 +10,8 @@ #include <dfu.h> #include <spi.h> #include <spi_flash.h> +#include <jffs2/load_kernel.h> +#include <linux/mtd/mtd.h>
static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) { @@ -52,11 +54,33 @@ static int dfu_write_medium_sf(struct dfu_entity *dfu,
static int dfu_flush_medium_sf(struct dfu_entity *dfu) { + u64 off, length; + + if (!dfu->data.sf.ubi) + return 0; + + /* in case of ubi partition, erase rest of the partition */ + off = find_sector(dfu, dfu->data.sf.start, dfu->offset); + /* last write ended with unaligned length jump to next */ + if (off != dfu->data.sf.start + dfu->offset) + off += dfu->data.sf.dev->sector_size; + length = dfu->data.sf.start + dfu->data.sf.size - off; + if (length) + return spi_flash_erase(dfu->data.sf.dev, off, length); + return 0; }
static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu) { + /* + * Currently, Poll Timeout != 0 is only needed on nand + * ubi partition, as sectors which are not used need + * to be erased + */ + if (dfu->data.sf.ubi) + return DFU_MANIFEST_POLL_TIMEOUT; + return DFU_DEFAULT_POLL_TIMEOUT; }
@@ -133,6 +157,33 @@ int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s) dfu->data.sf.start = simple_strtoul(s, &s, 16); s++; dfu->data.sf.size = simple_strtoul(s, &s, 16); + } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) { + char mtd_id[32]; + struct mtd_device *mtd_dev; + u8 part_num; + struct part_info *pi; + int ret, dev, part; + + dfu->layout = DFU_RAW_ADDR; + + dev = simple_strtoul(s, &s, 10); + s++; + part = simple_strtoul(s, &s, 10); + + sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1); + printf("using id '%s'\n", mtd_id); + + mtdparts_init(); + + ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); + if (ret != 0) { + printf("Could not locate '%s'\n", mtd_id); + return -1; + } + dfu->data.sf.start = pi->offset; + dfu->data.sf.size = pi->size; + if (!strcmp(st, "partubi")) + dfu->data.sf.ubi = 1; } else { printf("%s: Memory layout (%s) not supported!\n", __func__, st); spi_flash_free(dfu->data.sf.dev); diff --git a/include/dfu.h b/include/dfu.h index 145a157..bf51ab7 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -77,6 +77,8 @@ struct sf_internal_data { /* RAW programming */ u64 start; u64 size; + /* for sf/ubi use */ + unsigned int ubi; };
#define DFU_NAME_SIZE 32

Hi Patrick,
Copy the partition support from NAND backend to SF, support part and partubi option. In case of ubi partition, erase the rest of the partition as it is mandatory for UBI.
for example:
U-Boot> env set dfu_alt_info "spl part 0 1;\ u-boot part 0 2;u-boot-env part 0 3;UBI partubi 0 4" U-Boot> dfu 0 sf 0:0:10000000:0
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu_sf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 2 ++ 2 files changed, 53 insertions(+)
diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c index b78fcfd..d401b76 100644 --- a/drivers/dfu/dfu_sf.c +++ b/drivers/dfu/dfu_sf.c @@ -10,6 +10,8 @@ #include <dfu.h> #include <spi.h> #include <spi_flash.h> +#include <jffs2/load_kernel.h> +#include <linux/mtd/mtd.h>
static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) { @@ -52,11 +54,33 @@ static int dfu_write_medium_sf(struct dfu_entity *dfu, static int dfu_flush_medium_sf(struct dfu_entity *dfu) {
- u64 off, length;
- if (!dfu->data.sf.ubi)
return 0;
- /* in case of ubi partition, erase rest of the partition */
- off = find_sector(dfu, dfu->data.sf.start, dfu->offset);
- /* last write ended with unaligned length jump to next */
- if (off != dfu->data.sf.start + dfu->offset)
off += dfu->data.sf.dev->sector_size;
- length = dfu->data.sf.start + dfu->data.sf.size - off;
- if (length)
return spi_flash_erase(dfu->data.sf.dev, off,
length); + return 0; }
static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu) {
- /*
* Currently, Poll Timeout != 0 is only needed on nand
^^^^^ - please update the comment
* ubi partition, as sectors which are not used need
* to be erased
*/
- if (dfu->data.sf.ubi)
return DFU_MANIFEST_POLL_TIMEOUT;
- return DFU_DEFAULT_POLL_TIMEOUT;
}
@@ -133,6 +157,33 @@ int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s) dfu->data.sf.start = simple_strtoul(s, &s, 16); s++; dfu->data.sf.size = simple_strtoul(s, &s, 16);
- } else if ((!strcmp(st, "part")) || (!strcmp(st,
"partubi"))) {
char mtd_id[32];
struct mtd_device *mtd_dev;
u8 part_num;
struct part_info *pi;
int ret, dev, part;
dfu->layout = DFU_RAW_ADDR;
dev = simple_strtoul(s, &s, 10);
s++;
part = simple_strtoul(s, &s, 10);
sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1);
printf("using id '%s'\n", mtd_id);
mtdparts_init();
ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num,
&pi);
if (ret != 0) {
printf("Could not locate '%s'\n", mtd_id);
return -1;
}
dfu->data.sf.start = pi->offset;
dfu->data.sf.size = pi->size;
if (!strcmp(st, "partubi"))
} else { printf("%s: Memory layout (%s) not supported!\n",dfu->data.sf.ubi = 1;
__func__, st); spi_flash_free(dfu->data.sf.dev); diff --git a/include/dfu.h b/include/dfu.h index 145a157..bf51ab7 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -77,6 +77,8 @@ struct sf_internal_data { /* RAW programming */ u64 start; u64 size;
- /* for sf/ubi use */
- unsigned int ubi;
};
#define DFU_NAME_SIZE 32
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Split the function dfu_config_entities with 2 new functions - dfu_alt_init - dfu_alt_add
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu.c | 51 +++++++++++++++++++++++++++++++++++++++------------ include/dfu.h | 2 ++ 2 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index eb3a3c6..79a652e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -438,13 +438,12 @@ void dfu_free_entities(void) alt_num_cnt = 0; }
-int dfu_config_entities(char *env, char *interface, char *devstr) +int dfu_alt_init(int num, struct dfu_entity **dfu) { - struct dfu_entity *dfu; - int i, ret; char *s; + int ret;
- dfu_alt_num = dfu_find_alt_num(env); + dfu_alt_num = num; debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
dfu_hash_algo = NULL; @@ -455,21 +454,49 @@ int dfu_config_entities(char *env, char *interface, char *devstr) pr_err("Hash algorithm %s not supported\n", s); }
- dfu = calloc(sizeof(*dfu), dfu_alt_num); - if (!dfu) + *dfu = calloc(sizeof(struct dfu_entity), dfu_alt_num); + if (!*dfu) + return -1; + + return 0; +} + +int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s) +{ + struct dfu_entity *p_dfu; + int ret; + + if (alt_num_cnt >= dfu_alt_num) + return -1; + + p_dfu = &dfu[alt_num_cnt]; + ret = dfu_fill_entity(p_dfu, s, alt_num_cnt, interface, devstr); + if (ret) return -1; - for (i = 0; i < dfu_alt_num; i++) {
+ list_add_tail(&p_dfu->list, &dfu_list); + alt_num_cnt++; + + return 0; +} + +int dfu_config_entities(char *env, char *interface, char *devstr) +{ + struct dfu_entity *dfu; + int i, ret; + char *s; + + ret = dfu_alt_init(dfu_find_alt_num(env), &dfu); + if (ret) + return -1; + + for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";"); - ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, - devstr); + ret = dfu_alt_add(dfu, interface, devstr, s); if (ret) { /* We will free "dfu" in dfu_free_entities() */ return -1; } - - list_add_tail(&dfu[i].list, &dfu_list); - alt_num_cnt++; }
return 0; diff --git a/include/dfu.h b/include/dfu.h index bf51ab7..7d60ffc 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -143,6 +143,8 @@ struct dfu_entity { #ifdef CONFIG_SET_DFU_ALT_INFO void set_dfu_alt_info(char *interface, char *devstr); #endif +int dfu_alt_init(int num, struct dfu_entity **dfu); +int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s); int dfu_config_entities(char *s, char *interface, char *devstr); void dfu_free_entities(void); void dfu_show_entities(void);

Hi Patrick,
Split the function dfu_config_entities with 2 new functions
- dfu_alt_init
- dfu_alt_add
Ok.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu.c | 51 +++++++++++++++++++++++++++++++++++++++------------ include/dfu.h | 2 ++ 2 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index eb3a3c6..79a652e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -438,13 +438,12 @@ void dfu_free_entities(void) alt_num_cnt = 0; }
-int dfu_config_entities(char *env, char *interface, char *devstr) +int dfu_alt_init(int num, struct dfu_entity **dfu) {
- struct dfu_entity *dfu;
- int i, ret; char *s;
- int ret;
- dfu_alt_num = dfu_find_alt_num(env);
dfu_alt_num = num; debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
dfu_hash_algo = NULL;
@@ -455,21 +454,49 @@ int dfu_config_entities(char *env, char *interface, char *devstr) pr_err("Hash algorithm %s not supported\n", s); }
- dfu = calloc(sizeof(*dfu), dfu_alt_num);
- if (!dfu)
- *dfu = calloc(sizeof(struct dfu_entity), dfu_alt_num);
- if (!*dfu)
return -1;
I'm aware that the dfu.c file in some places uses -1 or -<errno> as return value.
I would prefer that with new code we shall add -<errno>. In this particular case it would be -ENOMEM
- return 0;
+}
+int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s) +{
- struct dfu_entity *p_dfu;
- int ret;
- if (alt_num_cnt >= dfu_alt_num)
return -1;
- p_dfu = &dfu[alt_num_cnt];
- ret = dfu_fill_entity(p_dfu, s, alt_num_cnt, interface,
devstr);
- if (ret) return -1;
- for (i = 0; i < dfu_alt_num; i++) {
- list_add_tail(&p_dfu->list, &dfu_list);
- alt_num_cnt++;
- return 0;
+}
+int dfu_config_entities(char *env, char *interface, char *devstr) +{
- struct dfu_entity *dfu;
- int i, ret;
- char *s;
- ret = dfu_alt_init(dfu_find_alt_num(env), &dfu);
- if (ret)
return -1;
- for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";");
ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt,
interface,
devstr);
if (ret) { /* We will free "dfu" in dfu_free_entities()ret = dfu_alt_add(dfu, interface, devstr, s);
*/ return -1; }
list_add_tail(&dfu[i].list, &dfu_list);
alt_num_cnt++;
}
return 0;
diff --git a/include/dfu.h b/include/dfu.h index bf51ab7..7d60ffc 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -143,6 +143,8 @@ struct dfu_entity { #ifdef CONFIG_SET_DFU_ALT_INFO void set_dfu_alt_info(char *interface, char *devstr); #endif +int dfu_alt_init(int num, struct dfu_entity **dfu); +int dfu_alt_add(struct dfu_entity *dfu, char *interface, char *devstr, char *s); int dfu_config_entities(char *s, char *interface, char *devstr); void dfu_free_entities(void); void dfu_show_entities(void);
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add support of DFU for several interface/device with one command.
The format for "dfu_alt_info" in this case is : interface with devstring'='alternate list (';' separated) and each interface is separated by '&'
The previous behavior is always supported.
One example for NOR (bootloaders) + NAND (rootfs in UBI):
U-Boot> env set dfu_alt_info \ "sf 0:0:10000000:0=spl part 0 1;u-boot part 0 2; \ u-boot-env part 0 3&nand 0=UBI partubi 0,3"
U-Boot> dfu 0 list
DFU alt settings list: dev: SF alt: 0 name: spl layout: RAW_ADDR dev: SF alt: 1 name: ssbl layout: RAW_ADDR dev: SF alt: 2 name: u-boot-env layout: RAW_ADDR dev: NAND alt: 3 name: UBI layout: RAW_ADDR
U-Boot> dfu 0
$> dfu-util -l
Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=3, name="UBI", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=2, name="u-boot-env", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=1, name="u-boot", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=0, name="spl", serial="002700333338511934383330"
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
cmd/dfu.c | 21 +++++++++++-------- drivers/dfu/dfu.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-)
diff --git a/cmd/dfu.c b/cmd/dfu.c index 91a750a..33491d0 100644 --- a/cmd/dfu.c +++ b/cmd/dfu.c @@ -21,23 +21,28 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
- if (argc < 4) + if (argc < 2) return CMD_RET_USAGE;
#ifdef CONFIG_DFU_OVER_USB char *usb_controller = argv[1]; #endif #if defined(CONFIG_DFU_OVER_USB) || defined(CONFIG_DFU_OVER_TFTP) - char *interface = argv[2]; - char *devstring = argv[3]; + char *interface = NULL; + char *devstring = NULL; + + if (argc >= 4) { + interface = argv[2]; + devstring = argv[3]; + } #endif
int ret = 0; #ifdef CONFIG_DFU_OVER_TFTP unsigned long addr = 0; if (!strcmp(argv[1], "tftp")) { - if (argc == 5) - addr = simple_strtoul(argv[4], NULL, 0); + if (argc == 5 || argc == 3) + addr = simple_strtoul(argv[argc - 1], NULL, 0);
return update_tftp(addr, interface, devstring); } @@ -48,7 +53,7 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) goto done;
ret = CMD_RET_SUCCESS; - if (argc > 4 && strcmp(argv[4], "list") == 0) { + if (strcmp(argv[argc - 1], "list") == 0) { dfu_show_entities(); goto done; } @@ -67,7 +72,7 @@ U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, "Device Firmware Upgrade", "" #ifdef CONFIG_DFU_OVER_USB - "<USB_controller> <interface> <dev> [list]\n" + "<USB_controller> [<interface> <dev>] [list]\n" " - device firmware upgrade via <USB_controller>\n" " on device <dev>, attached to interface\n" " <interface>\n" @@ -77,7 +82,7 @@ U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, #ifdef CONFIG_DFU_OVER_USB "dfu " #endif - "tftp <interface> <dev> [<addr>]\n" + "tftp [<interface> <dev>] [<addr>]\n" " - device firmware upgrade via TFTP\n" " on device <dev>, attached to interface\n" " <interface>\n" diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 79a652e..01ec690 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -52,6 +52,54 @@ static int dfu_find_alt_num(const char *s) return ++i; }
+/* + * treat dfu_alt_info with several interface information + * to allow DFU on several device with one command, + * the string format is + * interface devstring'='alternate list (';' separated) + * and each interface separated by '&' + */ +int dfu_config_interfaces(char *env) +{ + struct dfu_entity *dfu; + char *s, *i, *d, *a, *part; + int ret = -EINVAL; + int n = 1; + + s = env; + for (; *s; s++) { + if (*s == ';') + n++; + if (*s == '&') + n++; + } + ret = dfu_alt_init(n, &dfu); + if (ret) + return ret; + + s = env; + while (s) { + ret = -EINVAL; + i = strsep(&s, " "); + if (!i) + break; + d = strsep(&s, "="); + if (!d) + break; + a = strsep(&s, "&"); + if (!a) + a = s; + do { + part = strsep(&a, ";"); + ret = dfu_alt_add(dfu, i, d, part); + if (ret) + return ret; + } while (a); + } + + return ret; +} + int dfu_init_env_entities(char *interface, char *devstr) { const char *str_env; @@ -68,7 +116,11 @@ int dfu_init_env_entities(char *interface, char *devstr) }
env_bkp = strdup(str_env); - ret = dfu_config_entities(env_bkp, interface, devstr); + if (!interface && !devstr) + ret = dfu_config_interfaces(env_bkp); + else + ret = dfu_config_entities(env_bkp, interface, devstr); + if (ret) { pr_err("DFU entities configuration failed!\n"); pr_err("(partition table does not match dfu_alt_info?)\n"); @@ -82,6 +134,7 @@ done:
static unsigned char *dfu_buf; static unsigned long dfu_buf_size; +static enum dfu_device_type dfu_buf_device_type;
unsigned char *dfu_free_buf(void) { @@ -99,6 +152,10 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) { char *s;
+ /* manage several entity with several contraint */ + if (dfu_buf && dfu->dev_type != dfu_buf_device_type) + dfu_free_buf(); + if (dfu_buf != NULL) return dfu_buf;
@@ -117,6 +174,7 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) printf("%s: Could not memalign 0x%lx bytes\n", __func__, dfu_buf_size);
+ dfu_buf_device_type = dfu->dev_type; return dfu_buf; }

Hi Patrick,
Add support of DFU for several interface/device with one command.
The format for "dfu_alt_info" in this case is : interface with devstring'='alternate list (';' separated) and each interface is separated by '&'
The previous behavior is always supported.
Good.
One example for NOR (bootloaders) + NAND (rootfs in UBI):
U-Boot> env set dfu_alt_info \ "sf 0:0:10000000:0=spl part 0 1;u-boot part 0 2; \ u-boot-env part 0 3&nand 0=UBI partubi 0,3"
U-Boot> dfu 0 list
DFU alt settings list: dev: SF alt: 0 name: spl layout: RAW_ADDR dev: SF alt: 1 name: ssbl layout: RAW_ADDR dev: SF alt: 2 name: u-boot-env layout: RAW_ADDR dev: NAND alt: 3 name: UBI layout: RAW_ADDR
U-Boot> dfu 0
$> dfu-util -l
Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=3, name="UBI", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=2, name="u-boot-env", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=1, name="u-boot", serial="002700333338511934383330" Found DFU: [0483:5720] ver=9999, devnum=96, cfg=1, intf=0, alt=0, name="spl", serial="002700333338511934383330"
Please add this info (with above example usage) to ./doc/README.dfu (in a similar way as I did it some time ago for ./doc/README.dfutftp).
I also think that it would be beneficial for the community to add a separate entry (in this file) for the description of ST's way to program their platform with this code.
(I mean simple howto for people who would like to start playing around with ST & U-Boot & DFU).
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
cmd/dfu.c | 21 +++++++++++-------- drivers/dfu/dfu.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-)
diff --git a/cmd/dfu.c b/cmd/dfu.c index 91a750a..33491d0 100644 --- a/cmd/dfu.c +++ b/cmd/dfu.c @@ -21,23 +21,28 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
- if (argc < 4)
- if (argc < 2) return CMD_RET_USAGE;
#ifdef CONFIG_DFU_OVER_USB char *usb_controller = argv[1]; #endif #if defined(CONFIG_DFU_OVER_USB) || defined(CONFIG_DFU_OVER_TFTP)
- char *interface = argv[2];
- char *devstring = argv[3];
- char *interface = NULL;
- char *devstring = NULL;
- if (argc >= 4) {
interface = argv[2];
devstring = argv[3];
- }
#endif
int ret = 0; #ifdef CONFIG_DFU_OVER_TFTP unsigned long addr = 0; if (!strcmp(argv[1], "tftp")) {
if (argc == 5)
addr = simple_strtoul(argv[4], NULL, 0);
if (argc == 5 || argc == 3)
addr = simple_strtoul(argv[argc - 1], NULL,
0); return update_tftp(addr, interface, devstring); } @@ -48,7 +53,7 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) goto done;
ret = CMD_RET_SUCCESS;
- if (argc > 4 && strcmp(argv[4], "list") == 0) {
- if (strcmp(argv[argc - 1], "list") == 0) { dfu_show_entities(); goto done; }
@@ -67,7 +72,7 @@ U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, "Device Firmware Upgrade", "" #ifdef CONFIG_DFU_OVER_USB
- "<USB_controller> <interface> <dev> [list]\n"
- "<USB_controller> [<interface> <dev>] [list]\n" " - device firmware upgrade via <USB_controller>\n" " on device <dev>, attached to interface\n" " <interface>\n"
@@ -77,7 +82,7 @@ U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu, #ifdef CONFIG_DFU_OVER_USB "dfu " #endif
- "tftp <interface> <dev> [<addr>]\n"
- "tftp [<interface> <dev>] [<addr>]\n" " - device firmware upgrade via TFTP\n" " on device <dev>, attached to interface\n" " <interface>\n"
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 79a652e..01ec690 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -52,6 +52,54 @@ static int dfu_find_alt_num(const char *s) return ++i; }
+/*
- treat dfu_alt_info with several interface information
- to allow DFU on several device with one command,
- the string format is
- interface devstring'='alternate list (';' separated)
- and each interface separated by '&'
- */
+int dfu_config_interfaces(char *env) +{
- struct dfu_entity *dfu;
- char *s, *i, *d, *a, *part;
- int ret = -EINVAL;
- int n = 1;
- s = env;
- for (; *s; s++) {
if (*s == ';')
n++;
if (*s == '&')
n++;
- }
- ret = dfu_alt_init(n, &dfu);
- if (ret)
return ret;
- s = env;
- while (s) {
ret = -EINVAL;
i = strsep(&s, " ");
if (!i)
break;
d = strsep(&s, "=");
if (!d)
break;
a = strsep(&s, "&");
if (!a)
a = s;
do {
part = strsep(&a, ";");
ret = dfu_alt_add(dfu, i, d, part);
if (ret)
return ret;
} while (a);
- }
- return ret;
+}
int dfu_init_env_entities(char *interface, char *devstr) { const char *str_env; @@ -68,7 +116,11 @@ int dfu_init_env_entities(char *interface, char *devstr) }
env_bkp = strdup(str_env);
- ret = dfu_config_entities(env_bkp, interface, devstr);
- if (!interface && !devstr)
ret = dfu_config_interfaces(env_bkp);
- else
ret = dfu_config_entities(env_bkp, interface,
devstr); + if (ret) { pr_err("DFU entities configuration failed!\n"); pr_err("(partition table does not match dfu_alt_info?)\n"); @@ -82,6 +134,7 @@ done:
static unsigned char *dfu_buf; static unsigned long dfu_buf_size; +static enum dfu_device_type dfu_buf_device_type;
unsigned char *dfu_free_buf(void) { @@ -99,6 +152,10 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) { char *s;
- /* manage several entity with several contraint */
- if (dfu_buf && dfu->dev_type != dfu_buf_device_type)
dfu_free_buf();
- if (dfu_buf != NULL) return dfu_buf;
@@ -117,6 +174,7 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) printf("%s: Could not memalign 0x%lx bytes\n", __func__, dfu_buf_size);
- dfu_buf_device_type = dfu->dev_type; return dfu_buf;
}
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

This patch allows into the DFU backend to indicate that there is no remaining data (for EOF for example). That allows users to read a buffer greater than the device size; the dfu stack stops the read request when the backend return a length=0 without error.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 01ec690..bcfa170 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -395,6 +395,8 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) debug("%s: Read error!\n", __func__); return ret; } + if (dfu->b_left == 0) + break; dfu->offset += dfu->b_left; dfu->r_left -= dfu->b_left;

Hi Patrick,
This patch allows into the DFU backend to indicate that there is no remaining data (for EOF for example). That allows users to read a buffer greater than the device size; the dfu stack stops the read request when the backend return a length=0 without error.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 01ec690..bcfa170 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -395,6 +395,8 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) debug("%s: Read error!\n", __func__); return ret; }
if (dfu->b_left == 0)
break; dfu->offset += dfu->b_left; dfu->r_left -= dfu->b_left;
Reviewed-by: Lukasz Majewski lukma@denx.de
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add DFU backend for MTD device: allow to read and write on any MTD device (RAW or SPI)
For example :
set dfu_alt_info "nand_raw raw 0x0 0x100000" dfu 0 mtd nand0
This MTD backend provides the same level than dfu nand backend for NAND in RAW mode and sf backend for NOR; So it can replace booth of them but it can also add support of spi-nand.
set dfu_alt_info "nand_raw raw 0x0 0x100000" dfu 0 mtd spi-nand0
The backend code is based on the MTD command introduced by commit 5db66b3aee6f ("cmd: mtd: add 'mtd' command")
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/Kconfig | 6 ++ drivers/dfu/Makefile | 1 + drivers/dfu/dfu.c | 5 +- drivers/dfu/dfu_mtd.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 21 +++++ 5 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 drivers/dfu/dfu_mtd.c
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 4692736..ee664a3 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -46,5 +46,11 @@ config DFU_SF This option enables using DFU to read and write to SPI flash based storage.
+config DFU_MTD + bool "MTD back end for DFU" + depends on MTD + help + This option enables using DFU to read and write to on any MTD device. + endif endmenu diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 4164f34..ebb119f 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -5,6 +5,7 @@
obj-$(CONFIG_$(SPL_)DFU) += dfu.o obj-$(CONFIG_$(SPL_)DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index bcfa170..ab7fdc0 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -461,6 +461,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; @@ -565,7 +568,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", - "SF"}; + "SF", "MTD"}; return dev_t[t]; }
diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c new file mode 100644 index 0000000..1168a6e --- /dev/null +++ b/drivers/dfu/dfu_mtd.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * dfu_mtd.c -- DFU for MTD device. + * + * Copyright (C) 2019,STMicroelectronics - All Rights Reserved + * + * Based on dfu_nand.c + */ + +#include <common.h> +#include <dfu.h> +#include <mtd.h> + +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->erasesize); +} + +static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + u64 off, lim, remaining; + struct mtd_info *mtd = dfu->data.mtd.info; + struct mtd_oob_ops io_op = {}; + int ret = 0; + bool has_pages = mtd->type == MTD_NANDFLASH || + mtd->type == MTD_MLCNANDFLASH; + + /* if buf == NULL return total size of the area */ + if (!buf) { + *len = dfu->data.mtd.size; + return 0; + } + + off = dfu->data.mtd.start + offset + dfu->bad_skip; + lim = dfu->data.mtd.start + dfu->data.mtd.size; + + if (off >= lim) { + printf("Limit reached 0x%llx\n", lim); + *len = 0; + return op == DFU_OP_READ ? 0 : -EIO; + } + /* limit request with the available size */ + if (off + *len >= lim) + *len = lim - off; + + if (!mtd_is_aligned_with_block_size(mtd, off)) { + printf("Offset not aligned with a block (0x%x)\n", + mtd->erasesize); + return 0; + } + + /* first erase */ + if (op == DFU_OP_WRITE) { + struct erase_info erase_op = {}; + + erase_op.mtd = mtd; + erase_op.addr = off; + erase_op.len = round_up(*len, mtd->erasesize); + erase_op.scrub = 0; + + while (erase_op.len) { + if (erase_op.addr + erase_op.len > lim) { + printf("Limit reached 0x%llx while erasing at offset 0x%llx\n", + lim, off); + return -EIO; + } + + ret = mtd_erase(mtd, &erase_op); + /* Abort if its not a bad block error */ + if (ret != -EIO) + break; + + printf("Skipping bad block at 0x%08llx\n", + erase_op.fail_addr); + + /* Continue erase behind bad block */ + erase_op.len -= erase_op.fail_addr - erase_op.addr; + erase_op.addr = erase_op.fail_addr + mtd->erasesize; + } + if (ret && ret != -EIO) { + printf("Failure while erasing at offset 0x%llx\n", + erase_op.fail_addr); + return 0; + } + } + + io_op.mode = MTD_OPS_AUTO_OOB; + io_op.len = *len; + if (has_pages && io_op.len > mtd->writesize) + io_op.len = mtd->writesize; + io_op.ooblen = 0; + io_op.datbuf = buf; + io_op.oobbuf = NULL; + + /* Loop over to do the actual read/write */ + remaining = *len; + while (remaining) { + if (off + remaining > lim) { + printf("Limit reached 0x%llx while %s at offset 0x%llx\n", + lim, op == DFU_OP_READ ? "reading" : "writing", + off); + if (op == DFU_OP_READ) { + *len -= remaining; + return 0; + } else { + return -EIO; + } + } + + /* Skip the block if it is bad */ + if (mtd_is_aligned_with_block_size(mtd, off) && + mtd_block_isbad(mtd, off)) { + off += mtd->erasesize; + dfu->bad_skip += mtd->erasesize; + continue; + } + + if (op == DFU_OP_READ) + ret = mtd_read_oob(mtd, off, &io_op); + else + ret = mtd_write_oob(mtd, off, &io_op); + + if (ret) { + printf("Failure while %s at offset 0x%llx\n", + op == DFU_OP_READ ? "reading" : "writing", off); + return -EIO; + } + + off += io_op.retlen; + remaining -= io_op.retlen; + io_op.datbuf += io_op.retlen; + io_op.len = remaining; + if (has_pages && io_op.len > mtd->writesize) + io_op.len = mtd->writesize; + } + + return ret; +} + +static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size) +{ + *size = dfu->data.mtd.info->size; + + return 0; +} + +static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf, + long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mtd_block_op(DFU_OP_READ, dfu, offset, buf, len); + break; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + } + + return ret; +} + +static int dfu_write_medium_mtd(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + int ret = -1; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + ret = mtd_block_op(DFU_OP_WRITE, dfu, offset, buf, len); + 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; +} + +static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu) +{ + return DFU_DEFAULT_POLL_TIMEOUT; +} + +int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) +{ + char *st; + struct mtd_info *mtd; + bool has_pages; + + mtd = get_mtd_device_nm(devstr); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + put_mtd_device(mtd); + + dfu->dev_type = DFU_DEV_MTD; + dfu->data.mtd.info = mtd; + + has_pages = mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH; + dfu->max_buf_size = has_pages ? mtd->erasesize : 0; + + 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 { + printf("%s: (%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 7d60ffc..924952f 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -22,6 +22,7 @@ enum dfu_device_type { DFU_DEV_NAND, DFU_DEV_RAM, DFU_DEV_SF, + DFU_DEV_MTD, };
enum dfu_layout { @@ -55,6 +56,14 @@ struct mmc_internal_data { unsigned int part; };
+struct mtd_internal_data { + struct mtd_info *info; + + /* RAW programming */ + u64 start; + u64 size; +}; + struct nand_internal_data { /* RAW programming */ u64 start; @@ -105,6 +114,7 @@ struct dfu_entity {
union { struct mmc_internal_data mmc; + struct mtd_internal_data mtd; struct nand_internal_data nand; struct ram_internal_data ram; struct sf_internal_data sf; @@ -249,6 +259,17 @@ static inline int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, } #endif
+#if CONFIG_IS_ENABLED(DFU_MTD) +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 + /** * dfu_tftp_write - Write TFTP data to DFU medium *

Hi Patrick,
Add DFU backend for MTD device: allow to read and write on any MTD device (RAW or SPI)
For example :
set dfu_alt_info "nand_raw raw 0x0 0x100000" dfu 0 mtd nand0
This MTD backend provides the same level than dfu nand backend for NAND in RAW mode and sf backend for NOR; So it can replace booth of them but it can also add support of spi-nand.
set dfu_alt_info "nand_raw raw 0x0 0x100000" dfu 0 mtd spi-nand0
The backend code is based on the MTD command introduced by commit 5db66b3aee6f ("cmd: mtd: add 'mtd' command")
Please also add documentation entry for this feature/extension.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/Kconfig | 6 ++ drivers/dfu/Makefile | 1 + drivers/dfu/dfu.c | 5 +- drivers/dfu/dfu_mtd.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 21 +++++ 5 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 drivers/dfu/dfu_mtd.c
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 4692736..ee664a3 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -46,5 +46,11 @@ config DFU_SF This option enables using DFU to read and write to SPI flash based storage.
+config DFU_MTD
- bool "MTD back end for DFU"
- depends on MTD
- help
This option enables using DFU to read and write to on any
MTD device. + endif endmenu diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 4164f34..ebb119f 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -5,6 +5,7 @@
obj-$(CONFIG_$(SPL_)DFU) += dfu.o obj-$(CONFIG_$(SPL_)DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index bcfa170..ab7fdc0 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -461,6 +461,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))
} else if (strcmp(interface, "nand") == 0) { if (dfu_fill_entity_nand(dfu, devstr, s)) return -1;return -1;
@@ -565,7 +568,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
"SF"};
return dev_t[t];"SF", "MTD"};
}
diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c new file mode 100644 index 0000000..1168a6e --- /dev/null +++ b/drivers/dfu/dfu_mtd.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- dfu_mtd.c -- DFU for MTD device.
- Copyright (C) 2019,STMicroelectronics - All Rights Reserved
- Based on dfu_nand.c
- */
+#include <common.h> +#include <dfu.h> +#include <mtd.h>
+static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{
- return !do_div(size, mtd->erasesize);
+}
+static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
+{
- u64 off, lim, remaining;
- struct mtd_info *mtd = dfu->data.mtd.info;
- struct mtd_oob_ops io_op = {};
- int ret = 0;
- bool has_pages = mtd->type == MTD_NANDFLASH ||
mtd->type == MTD_MLCNANDFLASH;
- /* if buf == NULL return total size of the area */
- if (!buf) {
*len = dfu->data.mtd.size;
return 0;
- }
- off = dfu->data.mtd.start + offset + dfu->bad_skip;
- lim = dfu->data.mtd.start + dfu->data.mtd.size;
- if (off >= lim) {
printf("Limit reached 0x%llx\n", lim);
*len = 0;
return op == DFU_OP_READ ? 0 : -EIO;
- }
- /* limit request with the available size */
- if (off + *len >= lim)
*len = lim - off;
- if (!mtd_is_aligned_with_block_size(mtd, off)) {
printf("Offset not aligned with a block (0x%x)\n",
mtd->erasesize);
return 0;
- }
- /* first erase */
- if (op == DFU_OP_WRITE) {
struct erase_info erase_op = {};
erase_op.mtd = mtd;
erase_op.addr = off;
erase_op.len = round_up(*len, mtd->erasesize);
erase_op.scrub = 0;
while (erase_op.len) {
if (erase_op.addr + erase_op.len > lim) {
printf("Limit reached 0x%llx while
erasing at offset 0x%llx\n",
lim, off);
return -EIO;
}
ret = mtd_erase(mtd, &erase_op);
/* Abort if its not a bad block error */
if (ret != -EIO)
break;
printf("Skipping bad block at 0x%08llx\n",
erase_op.fail_addr);
/* Continue erase behind bad block */
erase_op.len -= erase_op.fail_addr -
erase_op.addr;
erase_op.addr = erase_op.fail_addr +
mtd->erasesize;
}
if (ret && ret != -EIO) {
printf("Failure while erasing at offset
0x%llx\n",
erase_op.fail_addr);
return 0;
}
- }
- io_op.mode = MTD_OPS_AUTO_OOB;
- io_op.len = *len;
- if (has_pages && io_op.len > mtd->writesize)
io_op.len = mtd->writesize;
- io_op.ooblen = 0;
- io_op.datbuf = buf;
- io_op.oobbuf = NULL;
- /* Loop over to do the actual read/write */
- remaining = *len;
- while (remaining) {
if (off + remaining > lim) {
printf("Limit reached 0x%llx while %s at
offset 0x%llx\n",
lim, op == DFU_OP_READ ? "reading" :
"writing",
off);
if (op == DFU_OP_READ) {
*len -= remaining;
return 0;
} else {
return -EIO;
}
}
/* Skip the block if it is bad */
if (mtd_is_aligned_with_block_size(mtd, off) &&
mtd_block_isbad(mtd, off)) {
off += mtd->erasesize;
dfu->bad_skip += mtd->erasesize;
continue;
}
if (op == DFU_OP_READ)
ret = mtd_read_oob(mtd, off, &io_op);
else
ret = mtd_write_oob(mtd, off, &io_op);
if (ret) {
printf("Failure while %s at offset 0x%llx\n",
op == DFU_OP_READ ? "reading" :
"writing", off);
return -EIO;
}
off += io_op.retlen;
remaining -= io_op.retlen;
io_op.datbuf += io_op.retlen;
io_op.len = remaining;
if (has_pages && io_op.len > mtd->writesize)
io_op.len = mtd->writesize;
- }
- return ret;
+}
+static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size) +{
- *size = dfu->data.mtd.info->size;
- return 0;
+}
+static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf,
long *len)
+{
- int ret = -1;
- switch (dfu->layout) {
- case DFU_RAW_ADDR:
ret = mtd_block_op(DFU_OP_READ, dfu, offset, buf,
len);
break;
- default:
printf("%s: Layout (%s) not (yet) supported!\n",
__func__,
dfu_get_layout(dfu->layout));
- }
- return ret;
+}
+static int dfu_write_medium_mtd(struct dfu_entity *dfu,
u64 offset, void *buf, long *len)
+{
- int ret = -1;
- switch (dfu->layout) {
- case DFU_RAW_ADDR:
ret = mtd_block_op(DFU_OP_WRITE, dfu, offset, buf,
len);
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;
+}
+static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu) +{
- return DFU_DEFAULT_POLL_TIMEOUT;
+}
+int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) +{
- char *st;
- struct mtd_info *mtd;
- bool has_pages;
- mtd = get_mtd_device_nm(devstr);
- if (IS_ERR_OR_NULL(mtd))
return -ENODEV;
- put_mtd_device(mtd);
- dfu->dev_type = DFU_DEV_MTD;
- dfu->data.mtd.info = mtd;
- has_pages = mtd->type == MTD_NANDFLASH || mtd->type ==
MTD_MLCNANDFLASH;
- dfu->max_buf_size = has_pages ? mtd->erasesize : 0;
- 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 {
printf("%s: (%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 7d60ffc..924952f 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -22,6 +22,7 @@ enum dfu_device_type { DFU_DEV_NAND, DFU_DEV_RAM, DFU_DEV_SF,
- DFU_DEV_MTD,
};
enum dfu_layout { @@ -55,6 +56,14 @@ struct mmc_internal_data { unsigned int part; };
+struct mtd_internal_data {
- struct mtd_info *info;
- /* RAW programming */
- u64 start;
- u64 size;
+};
struct nand_internal_data { /* RAW programming */ u64 start; @@ -105,6 +114,7 @@ struct dfu_entity {
union { struct mmc_internal_data mmc;
struct nand_internal_data nand; struct ram_internal_data ram; struct sf_internal_data sf;struct mtd_internal_data mtd;
@@ -249,6 +259,17 @@ static inline int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, } #endif
+#if CONFIG_IS_ENABLED(DFU_MTD) +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
/**
- dfu_tftp_write - Write TFTP data to DFU medium
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add the support of MTD partition for the MTD backend.
The expected dfu_alt_info for one alternate on the mtd device : <name> part <part_id> <name> partubi <part_id>
"partubi" also erase up to the end of the partition after write operation.
For example: dfu_alt_info = "spl part 1;u-boot part 2; UBI partubi 3"
U-Boot> dfu 0 mtd nand0
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu_mtd.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/dfu.h | 2 ++ 2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c index 1168a6e..223b0fe 100644 --- a/drivers/dfu/dfu_mtd.c +++ b/drivers/dfu/dfu_mtd.c @@ -10,6 +10,7 @@ #include <common.h> #include <dfu.h> #include <mtd.h> +#include <jffs2/load_kernel.h>
static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) { @@ -181,11 +182,49 @@ static int dfu_write_medium_mtd(struct dfu_entity *dfu,
static int dfu_flush_medium_mtd(struct dfu_entity *dfu) { + struct mtd_info *mtd = dfu->data.mtd.info; + int ret; + + /* in case of ubi partition, erase rest of the partition */ + if (dfu->data.nand.ubi) { + struct erase_info erase_op = {}; + + erase_op.mtd = dfu->data.mtd.info; + erase_op.addr = round_up(dfu->data.mtd.start + dfu->offset + + dfu->bad_skip, mtd->erasesize); + erase_op.len = dfu->data.mtd.start + dfu->data.mtd.size - + erase_op.addr; + erase_op.scrub = 0; + + while (erase_op.len) { + ret = mtd_erase(mtd, &erase_op); + /* Abort if its not a bad block error */ + if (ret != -EIO) + break; + + printf("Skipping bad block at 0x%08llx\n", + erase_op.fail_addr); + + /* Skip bad block and continue behind it */ + erase_op.addr = erase_op.fail_addr + mtd->erasesize; + erase_op.len = dfu->data.mtd.start + + dfu->data.mtd.size - + erase_op.addr; + } + } return 0; }
static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu) { + /* + * Currently, Poll Timeout != 0 is only needed on nand + * ubi partition, as sectors which are not used need + * to be erased + */ + if (dfu->data.nand.ubi) + return DFU_MANIFEST_POLL_TIMEOUT; + return DFU_DEFAULT_POLL_TIMEOUT; }
@@ -194,6 +233,7 @@ int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) char *st; struct mtd_info *mtd; bool has_pages; + int ret, part;
mtd = get_mtd_device_nm(devstr); if (IS_ERR_OR_NULL(mtd)) @@ -212,11 +252,47 @@ int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) 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"))) { + char mtd_id[32]; + struct mtd_device *mtd_dev; + u8 part_num; + struct part_info *pi; + + dfu->layout = DFU_RAW_ADDR; + + part = simple_strtoul(s, &s, 10); + + sprintf(mtd_id, "%s,%d", devstr, part - 1); + printf("using id '%s'\n", mtd_id); + + mtdparts_init(); + + ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); + if (ret != 0) { + printf("Could not locate '%s'\n", mtd_id); + return -1; + } + + dfu->data.mtd.start = pi->offset; + dfu->data.mtd.size = pi->size; + if (!strcmp(st, "partubi")) + dfu->data.mtd.ubi = 1; } else { - printf("%s: (%s) not supported!\n", __func__, st); + printf("%s: Memory layout (%s) not supported!\n", __func__, st); return -1; }
+ if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.start)) { + printf("Offset not aligned with a block (0x%x)\n", + mtd->erasesize); + return -EINVAL; + } + if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.size)) { + printf("Size not aligned with a block (0x%x)\n", + mtd->erasesize); + return -EINVAL; + } + dfu->get_medium_size = dfu_get_medium_size_mtd; dfu->read_medium = dfu_read_medium_mtd; dfu->write_medium = dfu_write_medium_mtd; diff --git a/include/dfu.h b/include/dfu.h index 924952f..a90732c 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -62,6 +62,8 @@ struct mtd_internal_data { /* RAW programming */ u64 start; u64 size; + /* for ubi partition */ + unsigned int ubi; };
struct nand_internal_data {

Hi Patrick,
Add the support of MTD partition for the MTD backend.
The expected dfu_alt_info for one alternate on the mtd device : <name> part <part_id> <name> partubi <part_id>
"partubi" also erase up to the end of the partition after write operation.
For example: dfu_alt_info = "spl part 1;u-boot part 2; UBI partubi 3"
As in the previous comments - please add this info to dedicated doc entry.
U-Boot> dfu 0 mtd nand0
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu_mtd.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/dfu.h | 2 ++ 2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c index 1168a6e..223b0fe 100644 --- a/drivers/dfu/dfu_mtd.c +++ b/drivers/dfu/dfu_mtd.c @@ -10,6 +10,7 @@ #include <common.h> #include <dfu.h> #include <mtd.h> +#include <jffs2/load_kernel.h>
static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) { @@ -181,11 +182,49 @@ static int dfu_write_medium_mtd(struct dfu_entity *dfu, static int dfu_flush_medium_mtd(struct dfu_entity *dfu) {
- struct mtd_info *mtd = dfu->data.mtd.info;
- int ret;
- /* in case of ubi partition, erase rest of the partition */
- if (dfu->data.nand.ubi) {
struct erase_info erase_op = {};
erase_op.mtd = dfu->data.mtd.info;
erase_op.addr = round_up(dfu->data.mtd.start +
dfu->offset +
dfu->bad_skip,
mtd->erasesize);
erase_op.len = dfu->data.mtd.start +
dfu->data.mtd.size -
erase_op.addr;
erase_op.scrub = 0;
while (erase_op.len) {
ret = mtd_erase(mtd, &erase_op);
/* Abort if its not a bad block error */
if (ret != -EIO)
break;
printf("Skipping bad block at 0x%08llx\n",
erase_op.fail_addr);
/* Skip bad block and continue behind it */
erase_op.addr = erase_op.fail_addr +
mtd->erasesize;
erase_op.len = dfu->data.mtd.start +
dfu->data.mtd.size -
erase_op.addr;
}
- } return 0;
}
static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu) {
- /*
* Currently, Poll Timeout != 0 is only needed on nand
* ubi partition, as sectors which are not used need
* to be erased
*/
- if (dfu->data.nand.ubi)
return DFU_MANIFEST_POLL_TIMEOUT;
- return DFU_DEFAULT_POLL_TIMEOUT;
}
@@ -194,6 +233,7 @@ int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) char *st; struct mtd_info *mtd; bool has_pages;
int ret, part;
mtd = get_mtd_device_nm(devstr); if (IS_ERR_OR_NULL(mtd))
@@ -212,11 +252,47 @@ int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s) 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"))) {
char mtd_id[32];
struct mtd_device *mtd_dev;
u8 part_num;
struct part_info *pi;
dfu->layout = DFU_RAW_ADDR;
part = simple_strtoul(s, &s, 10);
sprintf(mtd_id, "%s,%d", devstr, part - 1);
printf("using id '%s'\n", mtd_id);
mtdparts_init();
ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num,
&pi);
if (ret != 0) {
printf("Could not locate '%s'\n", mtd_id);
return -1;
-ENODEV maybe would be more appropriate ?
}
dfu->data.mtd.start = pi->offset;
dfu->data.mtd.size = pi->size;
if (!strcmp(st, "partubi"))
} else {dfu->data.mtd.ubi = 1;
printf("%s: (%s) not supported!\n", __func__, st);
printf("%s: Memory layout (%s) not supported!\n",
__func__, st); return -1; }
- if (!mtd_is_aligned_with_block_size(mtd,
dfu->data.mtd.start)) {
printf("Offset not aligned with a block (0x%x)\n",
mtd->erasesize);
return -EINVAL;
- }
- if (!mtd_is_aligned_with_block_size(mtd,
dfu->data.mtd.size)) {
printf("Size not aligned with a block (0x%x)\n",
mtd->erasesize);
return -EINVAL;
- }
- dfu->get_medium_size = dfu_get_medium_size_mtd; dfu->read_medium = dfu_read_medium_mtd; dfu->write_medium = dfu_write_medium_mtd;
diff --git a/include/dfu.h b/include/dfu.h index 924952f..a90732c 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -62,6 +62,8 @@ struct mtd_internal_data { /* RAW programming */ u64 start; u64 size;
- /* for ubi partition */
- unsigned int ubi;
};
struct nand_internal_data {
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add a virtual DFU backend to allow board specific read and write (for OTP update for example).
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/Kconfig | 7 +++++++ drivers/dfu/Makefile | 1 + drivers/dfu/dfu.c | 5 ++++- drivers/dfu/dfu_virt.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 22 ++++++++++++++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 drivers/dfu/dfu_virt.c
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index ee664a3..c0e6e5d 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -52,5 +52,12 @@ config DFU_MTD help This option enables using DFU to read and write to on any MTD device.
+config DFU_VIRT + bool "VIRTUAL flash back end for DFU" + help + This option enables using DFU to read and write to VIRTUAL device + used at board level to manage specific behavior + (OTP update for example). + endif endmenu diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index ebb119f..0d7925c 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o +obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index ab7fdc0..a960b6e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -473,6 +473,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, } else if (strcmp(interface, "sf") == 0) { if (dfu_fill_entity_sf(dfu, devstr, s)) return -1; + } else if (strcmp(interface, "virt") == 0) { + if (dfu_fill_entity_virt(dfu, devstr, s)) + return -1; } else { printf("%s: Device %s not (yet) supported!\n", __func__, interface); @@ -568,7 +571,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM", - "SF", "MTD"}; + "SF", "MTD", "VIRT"}; return dev_t[t]; }
diff --git a/drivers/dfu/dfu_virt.c b/drivers/dfu/dfu_virt.c new file mode 100644 index 0000000..ea8c71f --- /dev/null +++ b/drivers/dfu/dfu_virt.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ +#include <common.h> +#include <dfu.h> +#include <errno.h> +#include <malloc.h> + +int __weak dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + debug("%s: off=0x%llx, len=0x%x\n", __func__, offset, (u32)*len); + + return 0; +} + +int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size) +{ + *size = 0; + + return 0; +} + +int __weak dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + debug("%s: off=0x%llx, len=0x%x\n", __func__, offset, (u32)*len); + *len = 0; + + return 0; +} + +int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char *s) +{ + debug("%s: devstr = %s\n", __func__, devstr); + + dfu->dev_type = DFU_DEV_VIRT; + dfu->layout = DFU_RAW_ADDR; + dfu->data.virt.dev_num = simple_strtoul(devstr, NULL, 10); + + dfu->write_medium = dfu_write_medium_virt; + dfu->get_medium_size = dfu_get_medium_size_virt; + dfu->read_medium = dfu_read_medium_virt; + + dfu->inited = 0; + + return 0; +} diff --git a/include/dfu.h b/include/dfu.h index a90732c..4de7d35 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -23,6 +23,7 @@ enum dfu_device_type { DFU_DEV_RAM, DFU_DEV_SF, DFU_DEV_MTD, + DFU_DEV_VIRT, };
enum dfu_layout { @@ -92,6 +93,10 @@ struct sf_internal_data { unsigned int ubi; };
+struct virt_internal_data { + int dev_num; +}; + #define DFU_NAME_SIZE 32 #ifndef CONFIG_SYS_DFU_DATA_BUF_SIZE #define CONFIG_SYS_DFU_DATA_BUF_SIZE (1024*1024*8) /* 8 MiB */ @@ -120,6 +125,7 @@ struct dfu_entity { struct nand_internal_data nand; struct ram_internal_data ram; struct sf_internal_data sf; + struct virt_internal_data virt; } data;
int (*get_medium_size)(struct dfu_entity *dfu, u64 *size); @@ -272,6 +278,22 @@ static inline int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, } #endif
+#ifdef CONFIG_DFU_VIRT +int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char *s); +int dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len); +int dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size); +int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len); +#else +static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, + char *s) +{ + puts("VIRT support not available!\n"); + return -1; +} +#endif + /** * dfu_tftp_write - Write TFTP data to DFU medium *

Hi Patrick,
Add a virtual DFU backend to allow board specific read and write (for OTP update for example).
This looks like a great enhancement. Please add detailed documentation entry of its intended usage.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/Kconfig | 7 +++++++ drivers/dfu/Makefile | 1 + drivers/dfu/dfu.c | 5 ++++- drivers/dfu/dfu_virt.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 22 ++++++++++++++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 drivers/dfu/dfu_virt.c
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index ee664a3..c0e6e5d 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -52,5 +52,12 @@ config DFU_MTD help This option enables using DFU to read and write to on any MTD device. +config DFU_VIRT
- bool "VIRTUAL flash back end for DFU"
- help
This option enables using DFU to read and write to VIRTUAL
device
used at board level to manage specific behavior
(OTP update for example).
endif endmenu diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index ebb119f..0d7925c 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o +obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index ab7fdc0..a960b6e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -473,6 +473,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, } else if (strcmp(interface, "sf") == 0) { if (dfu_fill_entity_sf(dfu, devstr, s)) return -1;
- } else if (strcmp(interface, "virt") == 0) {
if (dfu_fill_entity_virt(dfu, devstr, s))
} else { printf("%s: Device %s not (yet) supported!\n", __func__, interface);return -1;
@@ -568,7 +571,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr) const char *dfu_get_dev_type(enum dfu_device_type t) { const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
"SF", "MTD"};
return dev_t[t];"SF", "MTD", "VIRT"};
}
diff --git a/drivers/dfu/dfu_virt.c b/drivers/dfu/dfu_virt.c new file mode 100644 index 0000000..ea8c71f --- /dev/null +++ b/drivers/dfu/dfu_virt.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/*
- Copyright (C) 2019, STMicroelectronics - All Rights Reserved
- */
+#include <common.h> +#include <dfu.h> +#include <errno.h> +#include <malloc.h>
+int __weak dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
+{
- debug("%s: off=0x%llx, len=0x%x\n", __func__, offset,
(u32)*len); +
- return 0;
+}
+int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size) +{
- *size = 0;
- return 0;
+}
+int __weak dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
+{
- debug("%s: off=0x%llx, len=0x%x\n", __func__, offset,
(u32)*len);
- *len = 0;
- return 0;
+}
+int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char *s) +{
- debug("%s: devstr = %s\n", __func__, devstr);
- dfu->dev_type = DFU_DEV_VIRT;
- dfu->layout = DFU_RAW_ADDR;
- dfu->data.virt.dev_num = simple_strtoul(devstr, NULL, 10);
- dfu->write_medium = dfu_write_medium_virt;
- dfu->get_medium_size = dfu_get_medium_size_virt;
- dfu->read_medium = dfu_read_medium_virt;
- dfu->inited = 0;
- return 0;
+} diff --git a/include/dfu.h b/include/dfu.h index a90732c..4de7d35 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -23,6 +23,7 @@ enum dfu_device_type { DFU_DEV_RAM, DFU_DEV_SF, DFU_DEV_MTD,
- DFU_DEV_VIRT,
};
enum dfu_layout { @@ -92,6 +93,10 @@ struct sf_internal_data { unsigned int ubi; };
+struct virt_internal_data {
- int dev_num;
+};
#define DFU_NAME_SIZE 32 #ifndef CONFIG_SYS_DFU_DATA_BUF_SIZE #define CONFIG_SYS_DFU_DATA_BUF_SIZE (1024*1024*8) /* 8 MiB */ @@ -120,6 +125,7 @@ struct dfu_entity { struct nand_internal_data nand; struct ram_internal_data ram; struct sf_internal_data sf;
struct virt_internal_data virt;
} data;
int (*get_medium_size)(struct dfu_entity *dfu, u64 *size);
@@ -272,6 +278,22 @@ static inline int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, } #endif
+#ifdef CONFIG_DFU_VIRT +int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, char *s); +int dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len);
+int dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size); +int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len);
+#else +static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr,
char *s)
+{
- puts("VIRT support not available!\n");
- return -1;
+} +#endif
/**
- dfu_tftp_write - Write TFTP data to DFU medium
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add weak callback to allow board specific behavior - flush - initiated
This patch prepare usage of DFU back end for communication with STM32CubeProgrammer on stm32mp1 platform with stm32prog command.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
drivers/dfu/dfu.c | 19 +++++++++++++++++++ include/dfu.h | 2 ++ 2 files changed, 21 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index a960b6e..e642b09 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -22,6 +22,22 @@ static int alt_num_cnt; static struct hash_algo *dfu_hash_algo;
/* + * The purpose of the dfu_flush_callback() function is to + * provide callback for dfu user + */ +__weak void dfu_flush_callback(struct dfu_entity *dfu) +{ +} + +/* + * The purpose of the dfu_flush_callback() function is to + * provide callback for dfu user + */ +__weak void dfu_initiated_callback(struct dfu_entity *dfu) +{ +} + +/* * The purpose of the dfu_usb_get_reset() function is to * provide information if after USB_DETACH request * being sent the dfu-util performed reset of USB @@ -262,6 +278,7 @@ int dfu_transaction_initiate(struct dfu_entity *dfu, bool read) }
dfu->inited = 1; + dfu_initiated_callback(dfu);
return 0; } @@ -281,6 +298,8 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name, dfu->crc);
+ dfu_flush_callback(dfu); + dfu_transaction_cleanup(dfu);
return ret; diff --git a/include/dfu.h b/include/dfu.h index 4de7d35..5d85cc3 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -182,6 +182,8 @@ bool dfu_usb_get_reset(void); int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_flush(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +void dfu_flush_callback(struct dfu_entity *dfu); +void dfu_initiated_callback(struct dfu_entity *dfu);
/* * dfu_defer_flush - pointer to store dfu_entity for deferred flashing.

Hi Patrick,
Add weak callback to allow board specific behavior
- flush
- initiated
This patch prepare usage of DFU back end for communication with STM32CubeProgrammer on stm32mp1 platform with stm32prog command.
Would the stm32prog need to modify the environment variables ? For example to update the "VERSION" or any other one?
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com
drivers/dfu/dfu.c | 19 +++++++++++++++++++ include/dfu.h | 2 ++ 2 files changed, 21 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index a960b6e..e642b09 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -22,6 +22,22 @@ static int alt_num_cnt; static struct hash_algo *dfu_hash_algo;
/*
- The purpose of the dfu_flush_callback() function is to
- provide callback for dfu user
- */
+__weak void dfu_flush_callback(struct dfu_entity *dfu) +{ +}
+/*
- The purpose of the dfu_flush_callback() function is to
- provide callback for dfu user
- */
+__weak void dfu_initiated_callback(struct dfu_entity *dfu) +{ +}
+/*
- The purpose of the dfu_usb_get_reset() function is to
- provide information if after USB_DETACH request
- being sent the dfu-util performed reset of USB
@@ -262,6 +278,7 @@ int dfu_transaction_initiate(struct dfu_entity *dfu, bool read) }
dfu->inited = 1;
dfu_initiated_callback(dfu);
return 0;
} @@ -281,6 +298,8 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name, dfu->crc);
dfu_flush_callback(dfu);
dfu_transaction_cleanup(dfu);
return ret;
diff --git a/include/dfu.h b/include/dfu.h index 4de7d35..5d85cc3 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -182,6 +182,8 @@ bool dfu_usb_get_reset(void); int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_flush(struct dfu_entity *de, void *buf, int size, int blk_seq_num); +void dfu_flush_callback(struct dfu_entity *dfu); +void dfu_initiated_callback(struct dfu_entity *dfu); /*
- dfu_defer_flush - pointer to store dfu_entity for deferred
flashing.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de

Add support of DFU for MMC, MTD, RAM and MTD command.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
configs/stm32mp15_basic_defconfig | 4 ++++ configs/stm32mp15_trusted_defconfig | 4 ++++ 2 files changed, 8 insertions(+)
diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 4aa184f..3f1cc49 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -29,6 +29,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y +CONFIG_CMD_MTD=y CONFIG_CMD_SF=y CONFIG_CMD_SPI=y CONFIG_CMD_USB=y @@ -53,6 +54,9 @@ CONFIG_ENV_EXT4_FILE="/uboot.env" CONFIG_ENV_UBI_PART="UBI" CONFIG_ENV_UBI_VOLUME="uboot_config" CONFIG_STM32_ADC=y +CONFIG_DFU_MMC=y +CONFIG_DFU_RAM=y +CONFIG_DFU_MTD=y CONFIG_USB_FUNCTION_FASTBOOT=y CONFIG_FASTBOOT_BUF_ADDR=0xC0000000 CONFIG_FASTBOOT_BUF_SIZE=0x02000000 diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig index 5fe9477..5eb9a8c 100644 --- a/configs/stm32mp15_trusted_defconfig +++ b/configs/stm32mp15_trusted_defconfig @@ -22,6 +22,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y +CONFIG_CMD_MTD=y CONFIG_CMD_SF=y CONFIG_CMD_SPI=y CONFIG_CMD_USB=y @@ -45,6 +46,9 @@ CONFIG_ENV_EXT4_FILE="/uboot.env" CONFIG_ENV_UBI_PART="UBI" CONFIG_ENV_UBI_VOLUME="uboot_config" CONFIG_STM32_ADC=y +CONFIG_DFU_MMC=y +CONFIG_DFU_RAM=y +CONFIG_DFU_MTD=y CONFIG_USB_FUNCTION_FASTBOOT=y CONFIG_FASTBOOT_BUF_ADDR=0xC0000000 CONFIG_FASTBOOT_BUF_SIZE=0x02000000

Generate automatically dfu_alt_info for the supported device. The simple command "dfu 0" allows to start the dfu stack on usb 0 for the supported devices: - dfu mtd for nand0 - dfu mtd for nor0 - dfu mmc for SDCard - dfu mmc for eMMC - dfu ram for images in DDR
The DUF alternate use the "part", "partubi" and "mmcpart" options to select the correct MTD or GPT partition or the eMMC hw boot partition.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
board/st/stm32mp1/README | 111 +++++++++++++++++++++++++++++++++++++++++++ board/st/stm32mp1/stm32mp1.c | 51 ++++++++++++++++++++ include/configs/stm32mp1.h | 32 +++++++++++++ 3 files changed, 194 insertions(+)
diff --git a/board/st/stm32mp1/README b/board/st/stm32mp1/README index b0c8325..eed3f95 100644 --- a/board/st/stm32mp1/README +++ b/board/st/stm32mp1/README @@ -334,3 +334,114 @@ on bank 0 to access to internal OTP: 4 check env update STM32MP> print ethaddr ethaddr=12:34:56:78:9a:bc + +10. DFU support +=============== + +The DFU is supported on ST board. +The env variable dfu_alt_info is automatically build, and all +the memory present on the ST boards are exported. + +The mode is started by + +STM32MP> dfu 0 + +On EV1 board: + +STM32MP> dfu 0 list + +DFU alt settings list: +dev: RAM alt: 0 name: uImage layout: RAM_ADDR +dev: RAM alt: 1 name: devicetree.dtb layout: RAM_ADDR +dev: RAM alt: 2 name: uramdisk.image.gz layout: RAM_ADDR +dev: eMMC alt: 3 name: sdcard_fsbl1 layout: RAW_ADDR +dev: eMMC alt: 4 name: sdcard_fsbl2 layout: RAW_ADDR +dev: eMMC alt: 5 name: sdcard_ssbl layout: RAW_ADDR +dev: eMMC alt: 6 name: sdcard_bootfs layout: RAW_ADDR +dev: eMMC alt: 7 name: sdcard_vendorfs layout: RAW_ADDR +dev: eMMC alt: 8 name: sdcard_rootfs layout: RAW_ADDR +dev: eMMC alt: 9 name: sdcard_userfs layout: RAW_ADDR +dev: eMMC alt: 10 name: emmc_fsbl1 layout: RAW_ADDR +dev: eMMC alt: 11 name: emmc_fsbl2 layout: RAW_ADDR +dev: eMMC alt: 12 name: emmc_ssbl layout: RAW_ADDR +dev: eMMC alt: 13 name: emmc_bootfs layout: RAW_ADDR +dev: eMMC alt: 14 name: emmc_vendorfs layout: RAW_ADDR +dev: eMMC alt: 15 name: emmc_rootfs layout: RAW_ADDR +dev: eMMC alt: 16 name: emmc_userfs layout: RAW_ADDR +dev: MTD alt: 17 name: nor_fsbl1 layout: RAW_ADDR +dev: MTD alt: 18 name: nor_fsbl2 layout: RAW_ADDR +dev: MTD alt: 19 name: nor_ssbl layout: RAW_ADDR +dev: MTD alt: 20 name: nor_env layout: RAW_ADDR +dev: MTD alt: 21 name: nand_fsbl layout: RAW_ADDR +dev: MTD alt: 22 name: nand_ssbl1 layout: RAW_ADDR +dev: MTD alt: 23 name: nand_ssbl2 layout: RAW_ADDR +dev: MTD alt: 24 name: nand_UBI layout: RAW_ADDR +dev: VIRT alt: 25 name: OTP layout: RAW_ADDR +dev: VIRT alt: 26 name: PMIC layout: RAW_ADDR + +All the supported device are exported for dfu-util tool: + +$> dfu-util -l +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=26, name="PMIC", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=25, name="OTP", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=24, name="nand_UBI", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=23, name="nand_ssbl2", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=22, name="nand_ssbl1", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=21, name="nand_fsbl", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=20, name="nor_env", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=19, name="nor_ssbl", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=18, name="nor_fsbl2", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=17, name="nor_fsbl1", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=16, name="emmc_userfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=15, name="emmc_rootfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=14, name="emmc_vendorfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=13, name="emmc_bootfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=12, name="emmc_ssbl", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=11, name="emmc_fsbl2", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=10, name="emmc_fsbl1", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=9, name="sdcard_userfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=8, name="sdcard_rootfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=7, name="sdcard_vendorfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=6, name="sdcard_bootfs", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=5, name="sdcard_ssbl", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=4, name="sdcard_fsbl2", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=3, name="sdcard_fsbl1", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=2, name="uramdisk.image.gz", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=1, name="devicetree.dtb", serial="002700333338511934383330" +Found DFU: [0483:5720] ver=9999, devnum=99, cfg=1, intf=0, alt=0, name="uImage", serial="002700333338511934383330" + +You can update the boot device: + +#SDCARD +$> dfu-util -d 0483:5720 -a 3 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 4 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 5 -D u-boot-stm32mp157c-ev1-trusted.img +$> dfu-util -d 0483:5720 -a 6 -D st-image-bootfs-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 7 -D st-image-vendorfs-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 8 -D st-image-weston-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 9 -D st-image-userfs-openstlinux-weston-stm32mp1.ext4 + +#EMMC +$> dfu-util -d 0483:5720 -a 10 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 11 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 12 -D u-boot-stm32mp157c-ev1-trusted.img +$> dfu-util -d 0483:5720 -a 13 -D st-image-bootfs-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 14 -D st-image-vendorfs-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 15 -D st-image-weston-openstlinux-weston-stm32mp1.ext4 +$> dfu-util -d 0483:5720 -a 16 -D st-image-userfs-openstlinux-weston-stm32mp1.ext4 + +#NOR +$> dfu-util -d 0483:5720 -a 17 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 18 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 19 -D u-boot-stm32mp157c-ev1-trusted.img + +#NAND (UBI partition used for NAND only boot or NOR + NAND boot) +$> dfu-util -d 0483:5720 -a 21 -D tf-a-stm32mp157c-ev1-trusted.stm32 +$> dfu-util -d 0483:5720 -a 22 -D u-boot-stm32mp157c-ev1-trusted.img +$> dfu-util -d 0483:5720 -a 23 -D u-boot-stm32mp157c-ev1-trusted.img +$> dfu-util -d 0483:5720 -a 24 -D st-image-weston-openstlinux-weston-stm32mp1_nand_4_256_multivolume.ubi + +And you can also dump the OTP and the PMIC NVM with: + +$> dfu-util -d 0483:5720 -a 25 -U otp.bin +$> dfu-util -d 0483:5720 -a 26 -U pmic.bin diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index b1e592c..7656c66 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -12,6 +12,7 @@ #include <generic-phy.h> #include <i2c.h> #include <led.h> +#include <memalign.h> #include <misc.h> #include <phy.h> #include <reset.h> @@ -745,3 +746,53 @@ void board_mtdparts_default(const char **mtdids, const char **mtdparts) debug("%s:mtdids=%s & mtdparts=%s\n", __func__, ids, parts); } #endif + +#ifdef CONFIG_SET_DFU_ALT_INFO +#define DFU_ALT_BUF_LEN SZ_1K + +static void board_get_alt_info(const char *dev, char *buff) +{ + char var_name[32] = "dfu_alt_info_"; + int ret; + + ALLOC_CACHE_ALIGN_BUFFER(char, tmp_alt, DFU_ALT_BUF_LEN); + + /* name of env variable to read = dfu_alt_info_<dev> */ + strcat(var_name, dev); + ret = env_get_f(var_name, tmp_alt, DFU_ALT_BUF_LEN); + if (ret) { + if (buff[0] != '\0') + strcat(buff, "&"); + strncat(buff, tmp_alt, DFU_ALT_BUF_LEN); + } +} + +void set_dfu_alt_info(char *interface, char *devstr) +{ + struct udevice *dev; + + ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN); + + if (env_get("dfu_alt_info")) + return; + + memset(buf, 0, sizeof(buf)); + + board_get_alt_info("ram", buf); + + if (!uclass_get_device(UCLASS_MMC, 0, &dev)) + board_get_alt_info("mmc0", buf); + + if (!uclass_get_device(UCLASS_MMC, 1, &dev)) + board_get_alt_info("mmc1", buf); + + if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev)) + board_get_alt_info("nor0", buf); + + if (!uclass_get_device(UCLASS_MTD, 0, &dev)) + board_get_alt_info("nand0", buf); + + env_set("dfu_alt_info", buf); + puts("DFU alt info setting: done\n"); +} +#endif diff --git a/include/configs/stm32mp1.h b/include/configs/stm32mp1.h index 1d385e0..27b2bdb 100644 --- a/include/configs/stm32mp1.h +++ b/include/configs/stm32mp1.h @@ -85,6 +85,8 @@ #define CONFIG_SYS_AUTOLOAD "no" #endif
+#define CONFIG_SET_DFU_ALT_INFO + /*****************************************************************************/ #ifdef CONFIG_DISTRO_DEFAULTS /*****************************************************************************/ @@ -129,6 +131,34 @@ "mtdparts_nor0=256k(fsbl1),256k(fsbl2),2m(ssbl),256k(u-boot-env),-(nor_user)\0" \ "mtdparts_nand0=2m(fsbl),2m(ssbl1),2m(ssbl2),-(UBI)\0"
+#define STM32MP_DFU_ALT_RAM \ + "dfu_alt_info_ram=ram 0=" \ + "uImage ram ${kernel_addr_r} 0x2000000;" \ + "devicetree.dtb ram ${fdt_addr_r} 0x100000;" \ + "uramdisk.image.gz ram ${ramdisk_addr_r} 0x10000000\0" + +#ifdef CONFIG_SET_DFU_ALT_INFO +#define STM32MP_DFU_ALT_INFO \ + "dfu_alt_info_nor0=mtd nor0=" \ + "nor_fsbl1 part 1;nor_fsbl2 part 2;" \ + "nor_ssbl part 3;nor_env part 4\0" \ + "dfu_alt_info_nand0=mtd nand0="\ + "nand_fsbl part 1;nand_ssbl1 part 2;" \ + "nand_ssbl2 part 3;nand_UBI partubi 4\0" \ + "dfu_alt_info_mmc0=mmc 0=" \ + "sdcard_fsbl1 part 0 1;sdcard_fsbl2 part 0 2;" \ + "sdcard_ssbl part 0 3;sdcard_bootfs part 0 4;" \ + "sdcard_vendorfs part 0 5;sdcard_rootfs part 0 6;" \ + "sdcard_userfs part 0 7\0" \ + "dfu_alt_info_mmc1=mmc 1=" \ + "emmc_fsbl1 raw 0x0 0x200 mmcpart 1;" \ + "emmc_fsbl2 raw 0x0 0x200 mmcpart 2;emmc_ssbl part 1 1;" \ + "emmc_bootfs part 1 2;emmc_vendorfs part 1 3;" \ + "emmc_rootfs part 1 4;emmc_userfs part 1 5\0" +#else +#define STM32MP_DFU_ALT_INFO +#endif + /* * memory layout for 32M uncompressed/compressed kernel, * 1M fdt, 1M script, 1M pxe and 1M for splashimage @@ -145,6 +175,8 @@ "initrd_high=0xffffffff\0" \ STM32MP_BOOTCMD \ STM32MP_MTDPARTS \ + STM32MP_DFU_ALT_RAM \ + STM32MP_DFU_ALT_INFO \ BOOTENV
#endif /* ifndef CONFIG_SPL_BUILD */

Activate the support of SPI NAND in stm32mp1 U-Boot.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
configs/stm32mp15_basic_defconfig | 1 + configs/stm32mp15_trusted_defconfig | 1 + 2 files changed, 2 insertions(+)
diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 3f1cc49..4728376 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -75,6 +75,7 @@ CONFIG_STM32_SDMMC2=y CONFIG_MTD=y CONFIG_NAND=y CONFIG_NAND_STM32_FMC2=y +CONFIG_MTD_SPI_NAND=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig index 5eb9a8c..0d9e13e 100644 --- a/configs/stm32mp15_trusted_defconfig +++ b/configs/stm32mp15_trusted_defconfig @@ -67,6 +67,7 @@ CONFIG_STM32_SDMMC2=y CONFIG_MTD=y CONFIG_NAND=y CONFIG_NAND_STM32_FMC2=y +CONFIG_MTD_SPI_NAND=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y

This patch adds the support of the spi nand device in mtdparts command and in dfu_alt_info.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com ---
board/st/stm32mp1/stm32mp1.c | 33 ++++++++++++++++++++++++++++++--- include/configs/stm32mp1.h | 6 +++++- 2 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 7656c66..68bd06c 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -14,6 +14,7 @@ #include <led.h> #include <memalign.h> #include <misc.h> +#include <mtd.h> #include <phy.h> #include <reset.h> #include <syscon.h> @@ -720,8 +721,9 @@ static void board_get_mtdparts(const char *dev,
void board_mtdparts_default(const char **mtdids, const char **mtdparts) { + struct mtd_info *mtd; struct udevice *dev; - static char parts[2 * MTDPARTS_LEN + 1]; + static char parts[3 * MTDPARTS_LEN + 1]; static char ids[MTDIDS_LEN + 1]; static bool mtd_initialized;
@@ -734,8 +736,24 @@ void board_mtdparts_default(const char **mtdids, const char **mtdparts) memset(parts, 0, sizeof(parts)); memset(ids, 0, sizeof(ids));
- if (!uclass_get_device(UCLASS_MTD, 0, &dev)) + /* probe all MTD devices */ + for (uclass_first_device(UCLASS_MTD, &dev); + dev; + uclass_next_device(&dev)) { + pr_debug("mtd device = %s\n", dev->name); + } + + mtd = get_mtd_device_nm("nand0"); + if (!IS_ERR_OR_NULL(mtd)) { board_get_mtdparts("nand0", ids, parts); + put_mtd_device(mtd); + } + + mtd = get_mtd_device_nm("spi-nand0"); + if (!IS_ERR_OR_NULL(mtd)) { + board_get_mtdparts("spi-nand0", ids, parts); + put_mtd_device(mtd); + }
if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev)) board_get_mtdparts("nor0", ids, parts); @@ -770,6 +788,7 @@ static void board_get_alt_info(const char *dev, char *buff) void set_dfu_alt_info(char *interface, char *devstr) { struct udevice *dev; + struct mtd_info *mtd;
ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
@@ -778,6 +797,9 @@ void set_dfu_alt_info(char *interface, char *devstr)
memset(buf, 0, sizeof(buf));
+ /* probe all MTD devices */ + mtd_probe_devices(); + board_get_alt_info("ram", buf);
if (!uclass_get_device(UCLASS_MMC, 0, &dev)) @@ -789,9 +811,14 @@ void set_dfu_alt_info(char *interface, char *devstr) if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev)) board_get_alt_info("nor0", buf);
- if (!uclass_get_device(UCLASS_MTD, 0, &dev)) + mtd = get_mtd_device_nm("nand0"); + if (!IS_ERR_OR_NULL(mtd)) board_get_alt_info("nand0", buf);
+ mtd = get_mtd_device_nm("spi-nand0"); + if (!IS_ERR_OR_NULL(mtd)) + board_get_alt_info("spi-nand0", buf); + env_set("dfu_alt_info", buf); puts("DFU alt info setting: done\n"); } diff --git a/include/configs/stm32mp1.h b/include/configs/stm32mp1.h index 27b2bdb..6dcf0e2 100644 --- a/include/configs/stm32mp1.h +++ b/include/configs/stm32mp1.h @@ -129,7 +129,8 @@
#define STM32MP_MTDPARTS \ "mtdparts_nor0=256k(fsbl1),256k(fsbl2),2m(ssbl),256k(u-boot-env),-(nor_user)\0" \ - "mtdparts_nand0=2m(fsbl),2m(ssbl1),2m(ssbl2),-(UBI)\0" + "mtdparts_nand0=2m(fsbl),2m(ssbl1),2m(ssbl2),-(UBI)\0" \ + "mtdparts_spi-nand0=2m(fsbl),2m(ssbl1),2m(ssbl2),-(UBI)\0"
#define STM32MP_DFU_ALT_RAM \ "dfu_alt_info_ram=ram 0=" \ @@ -145,6 +146,9 @@ "dfu_alt_info_nand0=mtd nand0="\ "nand_fsbl part 1;nand_ssbl1 part 2;" \ "nand_ssbl2 part 3;nand_UBI partubi 4\0" \ + "dfu_alt_info_spi-nand0=mtd spi-nand0="\ + "spi-nand_fsbl part 1;spi-nand_ssbl1 part 2;" \ + "spi-nand_ssbl2 part 3;spi-nand_UBI partubi 4\0" \ "dfu_alt_info_mmc0=mmc 0=" \ "sdcard_fsbl1 part 0 1;sdcard_fsbl2 part 0 2;" \ "sdcard_ssbl part 0 3;sdcard_bootfs part 0 4;" \

Add read for OTP and PMIC NVM with alternates on virtual DFU device.
Signed-off-by: Patrick Delaunay patrick.delaunay@st.com # Conflicts: # board/st/stm32mp1/stm32mp1.c
---
board/st/stm32mp1/stm32mp1.c | 83 +++++++++++++++++++++++++++++++++++++ configs/stm32mp15_basic_defconfig | 1 + configs/stm32mp15_trusted_defconfig | 1 + 3 files changed, 85 insertions(+)
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c index 68bd06c..4d5cd70 100644 --- a/board/st/stm32mp1/stm32mp1.c +++ b/board/st/stm32mp1/stm32mp1.c @@ -819,7 +819,90 @@ void set_dfu_alt_info(char *interface, char *devstr) if (!IS_ERR_OR_NULL(mtd)) board_get_alt_info("spi-nand0", buf);
+#ifdef CONFIG_DFU_VIRT + strncat(buf, "&virt 0=OTP", DFU_ALT_BUF_LEN); + + if (IS_ENABLED(CONFIG_PMIC_STPMIC1)) + strncat(buf, "&virt 1=PMIC", DFU_ALT_BUF_LEN); +#endif + env_set("dfu_alt_info", buf); puts("DFU alt info setting: done\n"); } + +#if CONFIG_IS_ENABLED(DFU_VIRT) +#include <dfu.h> +#include <power/stpmic1.h> + +int dfu_otp_read(u64 offset, u8 *buffer, long *size) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stm32mp_bsec), + &dev); + if (ret) + return ret; + + ret = misc_read(dev, offset + STM32_BSEC_OTP_OFFSET, buffer, *size); + if (ret >= 0) { + *size = ret; + ret = 0; + } + + return 0; +} + +int dfu_pmic_read(u64 offset, u8 *buffer, long *size) +{ + int ret; +#ifdef CONFIG_PMIC_STPMIC1 + struct udevice *dev; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stpmic1_nvm), + &dev); + if (ret) + return ret; + + ret = misc_read(dev, 0xF8 + offset, buffer, *size); + if (ret >= 0) { + *size = ret; + ret = 0; + } + if (ret == -EACCES) { + *size = 0; + ret = 0; + } +#else + pr_err("PMIC update not supported"); + ret = -EOPNOTSUPP; +#endif + + return ret; +} + +int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + switch (dfu->data.virt.dev_num) { + case 0x0: + return dfu_otp_read(offset, buf, len); + case 0x1: + return dfu_pmic_read(offset, buf, len); + } + *len = 0; + return 0; +} + +int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size) +{ + *size = SZ_1K; + + return 0; +} + +#endif + #endif diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 4728376..ba7b2f6 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -57,6 +57,7 @@ CONFIG_STM32_ADC=y CONFIG_DFU_MMC=y CONFIG_DFU_RAM=y CONFIG_DFU_MTD=y +CONFIG_DFU_VIRT=y CONFIG_USB_FUNCTION_FASTBOOT=y CONFIG_FASTBOOT_BUF_ADDR=0xC0000000 CONFIG_FASTBOOT_BUF_SIZE=0x02000000 diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig index 0d9e13e..25d9e4f 100644 --- a/configs/stm32mp15_trusted_defconfig +++ b/configs/stm32mp15_trusted_defconfig @@ -49,6 +49,7 @@ CONFIG_STM32_ADC=y CONFIG_DFU_MMC=y CONFIG_DFU_RAM=y CONFIG_DFU_MTD=y +CONFIG_DFU_VIRT=y CONFIG_USB_FUNCTION_FASTBOOT=y CONFIG_FASTBOOT_BUF_ADDR=0xC0000000 CONFIG_FASTBOOT_BUF_SIZE=0x02000000

Hi Patrick,
This serie based on v2019.07 propose some update on the DFU stack:
- add capability to have several DFU backend running in parallel
- add MTD backend for NAND, NOR or SPI-NAND
- add VIRTUAL backend for board/command specific behavior
- add some weak callback
To test the feature and as example, I update the stm32mp1 to use the new features (tested with command "dfu 0")
This serie prepares the DFU backend for communication with STM32CubeProgrammer on stm32mp1 platform (stm32prog command). This STMicroelectronics tool is based on DFU protocol and update the boot devices and the OTPs on the ST boards.
Thanks for such great enhancement on DFU. I've added Heiko to CC, so he also would be aware of your work (as some of his boards use DFU for update).
Please add detailed documentation entry (as I've indicated in other patches reviewed) for above features.
The roadmap:
- Please send v1 (without RFC). (Please run buildman on several siemens and trats/trats2/odroid-x* boards)
- I will test it on odroid-XU3 (and probably Heiko would test it for regression).
A side question - would you need in some point updating envs via DFU (no matter if those would be a whole image created with mkimage or an individual one) ?
Patrick Delaunay (14): dfu: cosmetic: cleanup sf to avoid checkpatch error dfu: sf: add partition support for nor backend dfu: prepare the support of multiple interface dfu: allow to manage DFU on several devices dfu: allow read with 0 data for EOF indication dfu: add backend for MTD device dfu: add partition support for MTD backend dfu: add DFU virtual backend dfu: add callback for flush and initiated operation stm32mp1: activate DFU support and command MTD stm32mp1: activate SET_DFU_ALT_INFO stp32mp1: configs: activate CONFIG_MTD_SPI_NAND stm32mp1: board: add spi nand support stm32mp1: add support for virtual partition read
board/st/stm32mp1/README | 111 +++++++++++++ board/st/stm32mp1/stm32mp1.c | 165 ++++++++++++++++++- cmd/dfu.c | 21 ++- configs/stm32mp15_basic_defconfig | 6 + configs/stm32mp15_trusted_defconfig | 6 + drivers/dfu/Kconfig | 13 ++ drivers/dfu/Makefile | 2 + drivers/dfu/dfu.c | 145 +++++++++++++++-- drivers/dfu/dfu_mtd.c | 306 ++++++++++++++++++++++++++++++++++++ drivers/dfu/dfu_sf.c | 55 ++++++- drivers/dfu/dfu_virt.c | 49 ++++++ include/configs/stm32mp1.h | 38 ++++- include/dfu.h | 51 ++++++ 13 files changed, 939 insertions(+), 29 deletions(-) create mode 100644 drivers/dfu/dfu_mtd.c create mode 100644 drivers/dfu/dfu_virt.c
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
participants (2)
-
Lukasz Majewski
-
Patrick Delaunay