Hi

On 3/18/20 9:25 AM, Patrick Delaunay wrote:
> Add a support of UART, using the same protocol than MCU STM32.
>
> See "AN5275: USB DFU/USART protocols used in STM32MP1 Series
> bootloaders" for details.
>
> Signed-off-by: Patrick Delaunay <patrick.delau...@st.com>
> ---
>
>  arch/arm/mach-stm32mp/cmd_stm32prog/Makefile  |   1 +
>  .../cmd_stm32prog/cmd_stm32prog.c             |  11 +-
>  .../mach-stm32mp/cmd_stm32prog/stm32prog.c    |   4 +
>  .../mach-stm32mp/cmd_stm32prog/stm32prog.h    |  11 +
>  .../cmd_stm32prog/stm32prog_serial.c          | 993 ++++++++++++++++++
>  .../cmd_stm32prog/stm32prog_usb.c             |   2 +
>  6 files changed, 1021 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
>
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
> index 14f722759c..548a378921 100644
> --- a/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
> @@ -5,4 +5,5 @@
>  
>  obj-y += cmd_stm32prog.o
>  obj-y += stm32prog.o
> +obj-y += stm32prog_serial.o
>  obj-y += stm32prog_usb.o
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
> index 581f97e0b5..1769ba05f2 100644
> --- a/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
> @@ -25,11 +25,14 @@ static int do_stm32prog(cmd_tbl_t *cmdtp, int flag, int 
> argc,
>  
>       if (!strcmp(argv[1], "usb"))
>               link = LINK_USB;
> +     else if (!strcmp(argv[1], "serial"))
> +             link = LINK_SERIAL;
>  
>       if (link == LINK_UNDEFINED) {
>               pr_err("not supported link=%s\n", argv[1]);
>               return CMD_RET_USAGE;
>       }
> +
>       dev = (int)simple_strtoul(argv[2], NULL, 10);
>  
>       addr = STM32_DDR_BASE;
> @@ -60,6 +63,12 @@ static int do_stm32prog(cmd_tbl_t *cmdtp, int flag, int 
> argc,
>               goto cleanup;
>  
>       switch (link) {
> +     case LINK_SERIAL:
> +             ret = stm32prog_serial_init(data, dev);
> +             if (ret)
> +                     goto cleanup;
> +             reset = stm32prog_serial_loop(data);
> +             break;
>       case LINK_USB:
>               reset = stm32prog_usb_loop(data, dev);
>               break;
> @@ -90,7 +99,7 @@ cleanup:
>  U_BOOT_CMD(stm32prog, 5, 0, do_stm32prog,
>          "<link> <dev> [<addr>] [<size>]\n"
>          "start communication with tools STM32Cubeprogrammer on <link> with 
> Flashlayout at <addr>",
> -        "<link> = usb\n"
> +        "<link> = serial|usb\n"
>          "<dev>  = device instance\n"
>          "<addr> = address of flashlayout\n"
>          "<size> = size of flashlayout\n"
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
> index d127afefaa..0967bbc11a 100644
> --- a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
> @@ -1486,6 +1486,7 @@ void stm32prog_next_phase(struct stm32prog_data *data)
>       }
>  
>       /* found next selected partition */
> +     data->dfu_seq = 0;
>       data->cur_part = NULL;
>       data->phase = PHASE_END;
>       found = false;
> @@ -1653,6 +1654,7 @@ int stm32prog_dfu_init(struct stm32prog_data *data)
>  int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size)
>  {
>       memset(data, 0x0, sizeof(*data));
> +     data->read_phase = PHASE_RESET;
>       data->phase = PHASE_FLASHLAYOUT;
>  
>       return parse_flash_layout(data, addr, size);
> @@ -1664,6 +1666,7 @@ void stm32prog_clean(struct stm32prog_data *data)
>       dfu_free_entities();
>       free(data->part_array);
>       free(data->otp_part);
> +     free(data->buffer);
>       free(data->header_data);
>  }
>  
> @@ -1709,6 +1712,7 @@ void dfu_initiated_callback(struct dfu_entity *dfu)
>       /* force the saved offset for the current partition */
>       if (dfu->alt == stm32prog_data->cur_part->alt_id) {
>               dfu->offset = stm32prog_data->offset;
> +             stm32prog_data->dfu_seq = 0;
>               pr_debug("dfu offset = 0x%llx\n", dfu->offset);
>       }
>  }
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
> index 83b27980f5..c4fdb5b8c3 100644
> --- a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
> @@ -31,6 +31,7 @@ enum stm32prog_target {
>  };
>  
>  enum stm32prog_link_t {
> +     LINK_SERIAL,
>       LINK_USB,
>       LINK_UNDEFINED,
>  };
> @@ -127,6 +128,14 @@ struct stm32prog_data {
>       /* STM32 header information */
>       struct raw_header_s     *header_data;
>       struct image_header_s   header;
> +
> +     /* SERIAL information */
> +     u32     cursor;
> +     u32     packet_number;
> +     u32     checksum;
> +     u8      *buffer; /* size = USART_RAM_BUFFER_SIZE*/
> +     int     dfu_seq;
> +     u8      read_phase;
>  };
>  
>  extern struct stm32prog_data *stm32prog_data;
> @@ -163,6 +172,8 @@ char *stm32prog_get_error(struct stm32prog_data *data);
>  
>  /* Main function */
>  int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size);
> +int stm32prog_serial_init(struct stm32prog_data *data, int link_dev);
> +bool stm32prog_serial_loop(struct stm32prog_data *data);
>  bool stm32prog_usb_loop(struct stm32prog_data *data, int dev);
>  void stm32prog_clean(struct stm32prog_data *data);
>  
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
> new file mode 100644
> index 0000000000..5a16979adc
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c
> @@ -0,0 +1,993 @@
> +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
> +/*
> + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
> + */
> +
> +#include <common.h>
> +#include <console.h>
> +#include <dfu.h>
> +#include <malloc.h>
> +#include <serial.h>
> +#include <watchdog.h>
> +#include <dm/lists.h>
> +#include <dm/device-internal.h>
> +#include "stm32prog.h"
> +
> +/* - configuration part -----------------------------*/
> +#define USART_BL_VERSION     0x40    /* USART bootloader version V4.0*/
> +#define UBOOT_BL_VERSION     0x03    /* bootloader version V0.3*/
> +#define DEVICE_ID_BYTE1              0x05    /* MSB byte of device ID*/
> +#define DEVICE_ID_BYTE2              0x00    /* LSB byte of device ID*/
> +#define USART_RAM_BUFFER_SIZE        256     /* Size of USART_RAM_Buf 
> buffer*/
> +
> +/* - Commands -----------------------------*/
> +#define GET_CMD_COMMAND              0x00    /* Get CMD command*/
> +#define GET_VER_COMMAND              0x01    /* Get Version command*/
> +#define GET_ID_COMMAND               0x02    /* Get ID command*/
> +#define GET_PHASE_COMMAND    0x03    /* Get Phase command*/
> +#define RM_COMMAND           0x11    /* Read Memory command*/
> +#define READ_PART_COMMAND    0x12    /* Read Partition command*/
> +#define START_COMMAND                0x21    /* START command (Go)*/
> +#define DOWNLOAD_COMMAND     0x31    /* Download command*/
> +/* existing command for other STM32 but not used */
> +/* ERASE                     0x43 */
> +/* EXTENDED_ERASE            0x44 */
> +/* WRITE_UNPROTECTED         0x73 */
> +/* READOUT_PROTECT           0x82 */
> +/* READOUT_UNPROTECT         0x92 */
> +
> +/* - miscellaneous defines ----------------------------------------*/
> +#define INIT_BYTE            0x7F    /*Init Byte ID*/
> +#define ACK_BYTE             0x79    /*Acknowlede Byte ID*/
> +#define NACK_BYTE            0x1F    /*No Acknowlede Byte ID*/
> +#define ABORT_BYTE           0x5F    /*ABORT*/
> +
> +struct udevice *down_serial_dev;
> +
> +const u8 cmd_id[] = {
> +     GET_CMD_COMMAND,
> +     GET_VER_COMMAND,
> +     GET_ID_COMMAND,
> +     GET_PHASE_COMMAND,
> +     RM_COMMAND,
> +     READ_PART_COMMAND,
> +     START_COMMAND,
> +     DOWNLOAD_COMMAND
> +};
> +
> +#define NB_CMD sizeof(cmd_id)
> +
> +/* DFU support for serial *********************************************/
> +static struct dfu_entity *stm32prog_get_entity(struct stm32prog_data *data)
> +{
> +     int alt_id;
> +
> +     if (!data->cur_part)
> +             if (data->phase == PHASE_FLASHLAYOUT)
> +                     alt_id = 0;
> +             else
> +                     return NULL;
> +     else
> +             alt_id = data->cur_part->alt_id;
> +
> +     return dfu_get_entity(alt_id);
> +}
> +
> +static int stm32prog_write(struct stm32prog_data *data, u8 *buffer,
> +                        u32 buffer_size)
> +{
> +     struct dfu_entity *dfu_entity;
> +     u8 ret = 0;
> +
> +     dfu_entity = stm32prog_get_entity(data);
> +     if (!dfu_entity)
> +             return -ENODEV;
> +
> +     ret = dfu_write(dfu_entity,
> +                     buffer,
> +                     buffer_size,
> +                     data->dfu_seq);
> +
> +     if (ret) {
> +             stm32prog_err("DFU write failed [%d] cnt: %d",
> +                           ret, data->dfu_seq);
> +     }
> +     data->dfu_seq++;
> +     /* handle rollover as in driver/dfu/dfu.c */
> +     data->dfu_seq &= 0xffff;
> +     if (buffer_size == 0)
> +             data->dfu_seq = 0; /* flush done */
> +
> +     return ret;
> +}
> +
> +static int stm32prog_read(struct stm32prog_data *data, u8 phase, u32 offset,
> +                       u8 *buffer, u32 buffer_size)
> +{
> +     struct dfu_entity *dfu_entity;
> +     struct stm32prog_part_t *part;
> +     u32 size;
> +     int ret, i;
> +
> +     if (data->dfu_seq) {
> +             stm32prog_err("DFU write pending for phase %d, seq %d",
> +                           data->phase, data->dfu_seq);
> +             return -EINVAL;
> +     }
> +     if (phase == PHASE_FLASHLAYOUT || phase > PHASE_LAST_USER) {
> +             stm32prog_err("read failed : phase %d is invalid", phase);
> +             return -EINVAL;
> +     }
> +     if (data->read_phase <= PHASE_LAST_USER &&
> +         phase != data->read_phase) {
> +             /* clear previous read session */
> +             dfu_entity = dfu_get_entity(data->read_phase - 1);
> +             if (dfu_entity)
> +                     dfu_transaction_cleanup(dfu_entity);
> +     }
> +
> +     dfu_entity = NULL;
> +     /* found partition for the expected phase */
> +     for (i = 0; i < data->part_nb; i++) {
> +             part = &data->part_array[i];
> +             if (part->id == phase)
> +                     dfu_entity = dfu_get_entity(part->alt_id);
> +     }
> +     if (!dfu_entity) {
> +             stm32prog_err("read failed : phase %d is unknown", phase);
> +             return -ENODEV;
> +     }
> +
> +     /* clear pending read before to force offset */
> +     if (dfu_entity->inited &&
> +         (data->read_phase != phase || data->offset != offset))
> +             dfu_transaction_cleanup(dfu_entity);
> +
> +     /* initiate before to force offset */
> +     if (!dfu_entity->inited) {
> +             ret = dfu_transaction_initiate(dfu_entity, true);
> +                     if (ret < 0) {
> +                             stm32prog_err("DFU read init failed [%d] phase 
> = %d offset = 0x%08x",
> +                                           ret, phase, offset);
> +                     return ret;
> +             }
> +     }
> +     /* force new offset */
> +     if (dfu_entity->offset != offset)
> +             dfu_entity->offset = offset;
> +     data->offset = offset;
> +     data->read_phase = phase;
> +     pr_debug("\nSTM32 download read %s offset=0x%x\n",
> +              dfu_entity->name, offset);
> +     ret = dfu_read(dfu_entity, buffer, buffer_size,
> +                    dfu_entity->i_blk_seq_num);
> +     if (ret < 0) {
> +             stm32prog_err("DFU read failed [%d] phase = %d offset = 0x%08x",
> +                           ret, phase, offset);
> +             return ret;
> +     }
> +
> +     size = ret;
> +
> +     if (size < buffer_size) {
> +             data->offset = 0;
> +             data->read_phase = PHASE_END;
> +             memset(buffer + size, 0, buffer_size - size);
> +     } else {
> +             data->offset += size;
> +     }
> +
> +     return ret;
> +}
> +
> +/* UART access ***************************************************/
> +int stm32prog_serial_init(struct stm32prog_data *data, int link_dev)
> +{
> +     struct udevice *dev = NULL;
> +     int node;
> +     char alias[10];
> +     const char *path;
> +     struct dm_serial_ops *ops;
> +     /* no parity, 8 bits, 1 stop */
> +     u32 serial_config = SERIAL_DEFAULT_CONFIG;
> +
> +     down_serial_dev = NULL;
> +
> +     sprintf(alias, "serial%d", link_dev);
> +     path = fdt_get_alias(gd->fdt_blob, alias);
> +     if (!path) {
> +             pr_err("%s alias not found", alias);
> +             return -ENODEV;
> +     }
> +     node = fdt_path_offset(gd->fdt_blob, path);
> +     if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node,
> +                                         &dev)) {
> +             down_serial_dev = dev;
> +     } else if (node > 0 &&
> +                !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node),
> +                                &dev, false)) {
> +             if (!device_probe(dev))
> +                     down_serial_dev = dev;
> +     }
> +     if (!down_serial_dev) {
> +             pr_err("%s = %s device not found", alias, path);
> +             return -ENODEV;
> +     }
> +
> +     /* force silent console on uart only when used */
> +     if (gd->cur_serial_dev == down_serial_dev)
> +             gd->flags |= GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT;
> +     else
> +             gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT);
> +
> +     ops = serial_get_ops(down_serial_dev);
> +
> +     if (!ops) {
> +             pr_err("%s = %s missing ops", alias, path);
> +             return -ENODEV;
> +     }
> +     if (!ops->setconfig) {
> +             pr_err("%s = %s missing setconfig", alias, path);
> +             return -ENODEV;
> +     }
> +
> +     clrsetbits_le32(&serial_config, SERIAL_PAR_MASK, SERIAL_PAR_EVEN);
> +
> +     data->buffer = memalign(CONFIG_SYS_CACHELINE_SIZE,
> +                             USART_RAM_BUFFER_SIZE);
> +
> +     return ops->setconfig(down_serial_dev, serial_config);
> +}
> +
> +static void stm32prog_serial_flush(void)
> +{
> +     struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
> +     int err;
> +
> +     do {
> +             err = ops->getc(down_serial_dev);
> +     } while (err != -EAGAIN);
> +}
> +
> +static int stm32prog_serial_getc_err(void)
> +{
> +     struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
> +     int err;
> +
> +     do {
> +             err = ops->getc(down_serial_dev);
> +             if (err == -EAGAIN) {
> +                     ctrlc();
> +                     WATCHDOG_RESET();
> +             }
> +     } while ((err == -EAGAIN) && (!had_ctrlc()));
> +
> +     return err;
> +}
> +
> +static u8 stm32prog_serial_getc(void)
> +{
> +     int err;
> +
> +     err = stm32prog_serial_getc_err();
> +
> +     return err >= 0 ? err : 0;
> +}
> +
> +static bool stm32prog_serial_get_buffer(u8 *buffer, u32 *count)
> +{
> +     struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
> +     int err;
> +
> +     do {
> +             err = ops->getc(down_serial_dev);
> +             if (err >= 0) {
> +                     *buffer++ = err;
> +                     *count -= 1;
> +             } else if (err == -EAGAIN) {
> +                     ctrlc();
> +                     WATCHDOG_RESET();
> +             } else {
> +                     break;
> +             }
> +     } while (*count && !had_ctrlc());
> +
> +     return !!(err < 0);
> +}
> +
> +static void stm32prog_serial_putc(u8 w_byte)
> +{
> +     struct dm_serial_ops *ops = serial_get_ops(down_serial_dev);
> +     int err;
> +
> +     do {
> +             err = ops->putc(down_serial_dev, w_byte);
> +     } while (err == -EAGAIN);
> +}
> +
> +/* Helper function ************************************************/
> +
> +static u8 stm32prog_header(struct stm32prog_data *data)
> +{
> +     u8 ret;
> +     u8 boot = 0;
> +     struct dfu_entity *dfu_entity;
> +     u64 size = 0;
> +
> +     dfu_entity = stm32prog_get_entity(data);
> +     if (!dfu_entity)
> +             return -ENODEV;
> +
> +     printf("\nSTM32 download write %s\n", dfu_entity->name);
> +
> +     /* force cleanup to avoid issue with previous read */
> +     dfu_transaction_cleanup(dfu_entity);
> +
> +     ret = stm32prog_header_check(data->header_data,
> +                                  &data->header);
> +
> +     /* no header : max size is partition size */
> +     if (ret) {
> +             dfu_entity->get_medium_size(dfu_entity, &size);
> +             data->header.image_length = size;
> +     }
> +
> +     /**** Flash the header if necessary for boot partition */
> +     if (data->phase < PHASE_FIRST_USER)
> +             boot = 1;
> +
> +     /* write header if boot partition */
> +     if (boot) {
> +             if (ret) {
> +                     stm32prog_err("invalid header (error %d)", ret);
> +             } else {
> +                     ret = stm32prog_write(data,
> +                                           (u8 *)data->header_data,
> +                                           BL_HEADER_SIZE);
> +             }
> +     } else {
> +             if (ret)
> +                     printf("  partition without checksum\n");
> +             ret = 0;
> +     }
> +
> +     free(data->header_data);
> +     data->header_data = NULL;
> +
> +     return ret;
> +}
> +
> +static u8 stm32prog_start(struct stm32prog_data *data, u32 address)
> +{
> +     u8 ret = 0;
> +     struct dfu_entity *dfu_entity;
> +
> +     if (address < 0x100) {
> +             if (address == PHASE_OTP)
> +                     return stm32prog_otp_start(data);
> +
> +             if (address == PHASE_PMIC)
> +                     return stm32prog_pmic_start(data);
> +
> +             if (address == PHASE_RESET || address == PHASE_END) {
> +                     data->cur_part = NULL;
> +                     data->dfu_seq = 0;
> +                     data->phase = address;
> +                     return 0;
> +             }
> +             if (address != data->phase) {
> +                     stm32prog_err("invalid received phase id %d, current 
> phase is %d",
> +                                   (u8)address, (u8)data->phase);
> +                     return -EINVAL;
> +             }
> +     }
> +     /* check the last loaded partition */
> +     if (address == DEFAULT_ADDRESS || address == data->phase) {
> +             switch (data->phase) {
> +             case PHASE_END:
> +             case PHASE_RESET:
> +             case PHASE_DO_RESET:
> +                     data->cur_part = NULL;
> +                     data->phase = PHASE_DO_RESET;
> +                     return 0;
> +             }
> +             dfu_entity = stm32prog_get_entity(data);
> +             if (!dfu_entity)
> +                     return -ENODEV;
> +
> +             if (data->dfu_seq) {
> +                     ret = dfu_flush(dfu_entity, NULL, 0, data->dfu_seq);
> +                     data->dfu_seq = 0;
> +                     if (ret) {
> +                             stm32prog_err("DFU flush failed [%d]", ret);
> +                             return ret;
> +                     }
> +             }
> +             printf("\n  received length = 0x%x\n", data->cursor);
> +             if (data->header.present) {
> +                     if (data->cursor !=
> +                         (data->header.image_length + BL_HEADER_SIZE)) {
> +                             stm32prog_err("transmission interrupted 
> (length=0x%x expected=0x%x)",
> +                                           data->cursor,
> +                                           data->header.image_length +
> +                                           BL_HEADER_SIZE);
> +                             return -EIO;
> +                     }
> +                     if (data->header.image_checksum != data->checksum) {
> +                             stm32prog_err("invalid checksum received (0x%x 
> expected 0x%x)",
> +                                           data->checksum,
> +                                           data->header.image_checksum);
> +                             return -EIO;
> +                     }
> +                     printf("\n  checksum OK (0x%x)\n", data->checksum);
> +             }
> +
> +             /* update DFU with received flashlayout */
> +             if (data->phase == PHASE_FLASHLAYOUT)
> +                     stm32prog_dfu_init(data);
> +     } else {
> +             void (*entry)(void) = (void *)address;
> +
> +             printf("## Starting application at 0x%x ...\n", address);
> +             (*entry)();
> +             printf("## Application terminated\n");
> +             ret = -ENOEXEC;
> +     }
> +
> +     return ret;
> +}
> +
> +/**
> + * get_address() - Get address if it is valid
> + *
> + * @tmp_xor:         Current xor value to update
> + * @return The address area
> + */
> +static u32 get_address(u8 *tmp_xor)
> +{
> +     u32 address = 0x0;
> +     u8 data;
> +
> +     data = stm32prog_serial_getc();
> +     *tmp_xor ^= data;
> +     address |= ((u32)data) << 24;
> +
> +     data = stm32prog_serial_getc();
> +     address |= ((u32)data) << 16;
> +     *tmp_xor ^= data;
> +
> +     data = stm32prog_serial_getc();
> +     address |= ((u32)data) << 8;
> +     *tmp_xor ^= data;
> +
> +     data = stm32prog_serial_getc();
> +     address |= ((u32)data);
> +     *tmp_xor ^= data;
> +
> +     return address;
> +}
> +
> +static void stm32prog_serial_result(u8 result)
> +{
> +     /* always flush fifo before to send result */
> +     stm32prog_serial_flush();
> +     stm32prog_serial_putc(result);
> +}
> +
> +/* Command -----------------------------------------------*/
> +/**
> + * get_cmd_command() - Respond to Get command
> + *
> + * @data:            Current command context
> + */
> +static void get_cmd_command(struct stm32prog_data *data)
> +{
> +     u32 counter = 0x0;
> +
> +     stm32prog_serial_putc(NB_CMD);
> +     stm32prog_serial_putc(USART_BL_VERSION);
> +
> +     for (counter = 0; counter < NB_CMD; counter++)
> +             stm32prog_serial_putc(cmd_id[counter]);
> +
> +     stm32prog_serial_result(ACK_BYTE);
> +}
> +
> +/**
> + * get_version_command() - Respond to Get Version command
> + *
> + * @data:            Current command context
> + */
> +static void get_version_command(struct stm32prog_data *data)
> +{
> +     stm32prog_serial_putc(UBOOT_BL_VERSION);
> +     stm32prog_serial_result(ACK_BYTE);
> +}
> +
> +/**
> + * get_id_command() - Respond to Get ID command
> + *
> + * @data:            Current command context
> + */
> +static void get_id_command(struct stm32prog_data *data)
> +{
> +     /* Send Device IDCode */
> +     stm32prog_serial_putc(0x1);
> +     stm32prog_serial_putc(DEVICE_ID_BYTE1);
> +     stm32prog_serial_putc(DEVICE_ID_BYTE2);
> +     stm32prog_serial_result(ACK_BYTE);
> +}
> +
> +/**
> + * get_phase_command() - Respond to Get phase
> + *
> + * @data:            Current command context
> + */
> +static void get_phase_command(struct stm32prog_data *data)
> +{
> +     char *err_msg = NULL;
> +     u8 i, length = 0;
> +     u32 destination = DEFAULT_ADDRESS; /* destination address */
> +     int phase = data->phase;
> +
> +     if (phase == PHASE_RESET || phase == PHASE_DO_RESET) {
> +             err_msg = stm32prog_get_error(data);
> +             length = strlen(err_msg);
> +     }
> +     if (phase == PHASE_FLASHLAYOUT)
> +             destination = STM32_DDR_BASE;
> +
> +     stm32prog_serial_putc(length + 5);           /* Total length */
> +     stm32prog_serial_putc(phase & 0xFF);         /* partition ID */
> +     stm32prog_serial_putc(destination);          /* byte 1 of address */
> +     stm32prog_serial_putc(destination >> 8);     /* byte 2 of address */
> +     stm32prog_serial_putc(destination >> 16);    /* byte 3 of address */
> +     stm32prog_serial_putc(destination >> 24);    /* byte 4 of address */
> +
> +     stm32prog_serial_putc(length);               /* Information length */
> +     for (i = 0; i < length; i++)
> +             stm32prog_serial_putc(err_msg[i]);
> +     stm32prog_serial_result(ACK_BYTE);
> +
> +     if (phase == PHASE_RESET)
> +             stm32prog_do_reset(data);
> +}
> +
> +/**
> + * read_memory_command() - Read data from memory
> + *
> + * @data:            Current command context
> + */
> +static void read_memory_command(struct stm32prog_data *data)
> +{
> +     u32 address = 0x0;
> +     u8 rcv_data = 0x0, tmp_xor = 0x0;
> +     u32 counter = 0x0;
> +
> +     /* Read memory address */
> +     address = get_address(&tmp_xor);
> +
> +     /* If address memory is not received correctly */
> +     rcv_data = stm32prog_serial_getc();
> +     if (rcv_data != tmp_xor) {
> +             stm32prog_serial_result(NACK_BYTE);
> +             return;
> +     }
> +
> +     stm32prog_serial_result(ACK_BYTE);
> +
> +     /* Read the number of bytes to be received:
> +      * Max NbrOfData = Data + 1 = 256
> +      */
> +     rcv_data = stm32prog_serial_getc();
> +     tmp_xor = ~rcv_data;
> +     if (stm32prog_serial_getc() != tmp_xor) {
> +             stm32prog_serial_result(NACK_BYTE);
> +             return;
> +     }
> +
> +     /* If checksum is correct send ACK */
> +     stm32prog_serial_result(ACK_BYTE);
> +
> +     /* Send data to the host:
> +      * Number of data to read = data + 1
> +      */
> +     for (counter = (rcv_data + 1); counter != 0; counter--)
> +             stm32prog_serial_putc(*(u8 *)(address++));
> +}
> +
> +/**
> + * start_command() - Respond to start command
> + *
> + * Jump to user application in RAM or partition check
> + *
> + * @data:            Current command context
> + */
> +static void start_command(struct stm32prog_data *data)
> +{
> +     u32 address = 0;
> +     u8 tmp_xor = 0x0;
> +     u8 ret, rcv_data;
> +
> +     /* Read memory address */
> +     address = get_address(&tmp_xor);
> +
> +     /* If address memory is not received correctly */
> +     rcv_data = stm32prog_serial_getc();
> +     if (rcv_data != tmp_xor) {
> +             stm32prog_serial_result(NACK_BYTE);
> +             return;
> +     }
> +     /* validate partition */
> +     ret = stm32prog_start(data,
> +                           address);
> +
> +     if (ret)
> +             stm32prog_serial_result(ABORT_BYTE);
> +     else
> +             stm32prog_serial_result(ACK_BYTE);
> +}
> +
> +/**
> + * download_command() - Respond to download command
> + *
> + * Write data to not volatile memory, Flash
> + *
> + * @data:            Current command context
> + */
> +static void download_command(struct stm32prog_data *data)
> +{
> +     u32 address = 0x0;
> +     u8 my_xor = 0x0;
> +     u8 rcv_xor;
> +     u32 counter = 0x0, codesize = 0x0;
> +     u8 *ramaddress = 0;
> +     u8 rcv_data = 0x0;
> +     struct image_header_s *image_header = &data->header;
> +     u32 cursor = data->cursor;
> +     long size = 0;
> +     u8 operation;
> +     u32 packet_number;
> +     u32 result = ACK_BYTE;
> +     u8 ret;
> +     unsigned int i;
> +     bool error;
> +     int rcv;
> +
> +     address = get_address(&my_xor);
> +
> +     /* If address memory is not received correctly */
> +     rcv_xor = stm32prog_serial_getc();
> +     if (rcv_xor != my_xor) {
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +
> +     /* If address valid send ACK */
> +     stm32prog_serial_result(ACK_BYTE);
> +
> +     /* get packet number and operation type */
> +     operation = (u8)((u32)address >> 24);
> +     packet_number = ((u32)(((u32)address << 8))) >> 8;
> +
> +     switch (operation) {
> +     /* supported operation */
> +     case PHASE_FLASHLAYOUT:
> +     case PHASE_OTP:
> +     case PHASE_PMIC:
> +             break;
> +     default:
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +     /* check the packet number */
> +     if (packet_number == 0) {
> +             /* erase: re-initialize the image_header struct */
> +             data->packet_number = 0;
> +             if (data->header_data)
> +                     memset(data->header_data, 0, BL_HEADER_SIZE);
> +             else
> +                     data->header_data = calloc(1, BL_HEADER_SIZE);
> +             cursor = 0;
> +             data->cursor = 0;
> +             data->checksum = 0;
> +             /*idx = cursor;*/
> +     } else {
> +             data->packet_number++;
> +     }
> +
> +     /* Check with the number of current packet if the device receive
> +      * the true packet
> +      */
> +     if (packet_number != data->packet_number) {
> +             data->packet_number--;
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +
> +     /*-- Read number of bytes to be written and data -----------*/
> +
> +     /* Read the number of bytes to be written:
> +      * Max NbrOfData = data + 1 <= 256
> +      */
> +     rcv_data = stm32prog_serial_getc();
> +
> +     /* NbrOfData to write = data + 1 */
> +     codesize = rcv_data + 0x01;
> +
> +     if (codesize > USART_RAM_BUFFER_SIZE) {
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +
> +     /* Checksum Initialization */
> +     my_xor = rcv_data;
> +
> +     /* UART receive data and send to Buffer */
> +     counter = codesize;
> +     error = stm32prog_serial_get_buffer(data->buffer, &counter);
> +
> +     /* read checksum */
> +     if (!error) {
> +             rcv = stm32prog_serial_getc_err();
> +             error = !!(rcv < 0);
> +             rcv_xor = rcv;
> +     }
> +
> +     if (error) {
> +             printf("transmission error on packet %d, byte %d\n",
> +                    packet_number, codesize - counter);
> +             /* waiting end of packet before flush & NACK */
> +             mdelay(30);
> +             data->packet_number--;
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +
> +     /* Compute Checksum */
> +     ramaddress = data->buffer;
> +     for (counter = codesize; counter != 0; counter--)
> +             my_xor ^= *(ramaddress++);
> +
> +     /* If Checksum is incorrect */
> +     if (rcv_xor != my_xor) {
> +             printf("checksum error on packet %d\n",
> +                    packet_number);
> +             /* wait to be sure that all data are received
> +              * in the FIFO before flush
> +              */
> +             mdelay(30);
> +             data->packet_number--;
> +             result = NACK_BYTE;
> +             goto end;
> +     }
> +
> +     /* Update current position in buffer */
> +     data->cursor += codesize;
> +
> +     if (operation == PHASE_OTP) {
> +             size = data->cursor - cursor;
> +             /* no header for OTP */
> +             if (stm32prog_otp_write(data, cursor,
> +                                     data->buffer, &size))
> +                     result = ABORT_BYTE;
> +             goto end;
> +     }
> +
> +     if (operation == PHASE_PMIC) {
> +             size = data->cursor - cursor;
> +             /* no header for PMIC */
> +             if (stm32prog_pmic_write(data, cursor,
> +                                      data->buffer, &size))
> +                     result = ABORT_BYTE;
> +             goto end;
> +     }
> +
> +     if (cursor < BL_HEADER_SIZE) {
> +             /* size = portion of header in this chunck */
> +             if (data->cursor >= BL_HEADER_SIZE)
> +                     size = BL_HEADER_SIZE - cursor;
> +             else
> +                     size = data->cursor - cursor;
> +             memcpy((void *)((u32)(data->header_data) + cursor),
> +                    data->buffer, size);
> +             cursor += size;
> +
> +             if (cursor == BL_HEADER_SIZE) {
> +                     /* Check and Write the header */
> +                     if (stm32prog_header(data)) {
> +                             result = ABORT_BYTE;
> +                             goto end;
> +                     }
> +             } else {
> +                     goto end;
> +             }
> +     }
> +
> +     if (image_header->present) {
> +             if (data->cursor <= BL_HEADER_SIZE)
> +                     goto end;
> +             /* compute checksum on payload */
> +             for (i = (unsigned long)size; i < codesize; i++)
> +                     data->checksum += data->buffer[i];
> +
> +             if (data->cursor >
> +                 image_header->image_length + BL_HEADER_SIZE) {
> +                     pr_err("expected size exceeded\n");
> +                     result = ABORT_BYTE;
> +                     goto end;
> +             }
> +
> +             /* write data (payload) */
> +             ret = stm32prog_write(data,
> +                                   &data->buffer[size],
> +                                   codesize - size);
> +     } else {
> +             /* write all */
> +             ret = stm32prog_write(data,
> +                                   data->buffer,
> +                                   codesize);
> +     }
> +     if (ret)
> +             result = ABORT_BYTE;
> +
> +end:
> +     stm32prog_serial_result(result);
> +}
> +
> +/**
> + * read_partition() - Respond to read command
> + *
> + * Read data from not volatile memory, Flash
> + *
> + * @data:            Current command context
> + */
> +static void read_partition_command(struct stm32prog_data *data)
> +{
> +     u32 i, part_id, codesize, offset = 0, rcv_data;
> +     long size;
> +     u8 tmp_xor;
> +     int res;
> +     u8 buffer[256];
> +
> +     part_id = stm32prog_serial_getc();
> +     tmp_xor = part_id;
> +
> +     offset = get_address(&tmp_xor);
> +
> +     rcv_data = stm32prog_serial_getc();
> +     if (rcv_data != tmp_xor) {
> +             pr_debug("1st checksum received = %x, computed %x\n",
> +                      rcv_data, tmp_xor);
> +             goto error;
> +     }
> +     stm32prog_serial_putc(ACK_BYTE);
> +
> +     /* NbrOfData to read = data + 1 */
> +     rcv_data = stm32prog_serial_getc();
> +     codesize = rcv_data + 0x01;
> +     tmp_xor = rcv_data;
> +
> +     rcv_data = stm32prog_serial_getc();
> +     if ((rcv_data ^ tmp_xor) != 0xFF) {
> +             pr_debug("2nd checksum received = %x, computed %x\n",
> +                      rcv_data, tmp_xor);
> +             goto error;
> +     }
> +
> +     pr_debug("%s : %x\n", __func__, part_id);
> +     rcv_data = 0;
> +     switch (part_id) {
> +     case PHASE_OTP:
> +             size = codesize;
> +             if (!stm32prog_otp_read(data, offset, buffer, &size))
> +                     rcv_data = size;
> +             break;
> +     case PHASE_PMIC:
> +             size = codesize;
> +             if (!stm32prog_pmic_read(data, offset, buffer, &size))
> +                     rcv_data = size;
> +             break;
> +     default:
> +             res = stm32prog_read(data, part_id, offset,
> +                                  buffer, codesize);
> +             if (res > 0)
> +                     rcv_data = res;
> +             break;
> +     }
> +     if (rcv_data > 0) {
> +             stm32prog_serial_putc(ACK_BYTE);
> +             /*----------- Send data to the host -----------*/
> +             for (i = 0; i < rcv_data; i++)
> +                     stm32prog_serial_putc(buffer[i]);
> +             /*----------- Send filler to the host -----------*/
> +             for (; i < codesize; i++)
> +                     stm32prog_serial_putc(0x0);
> +             return;
> +     }
> +     stm32prog_serial_result(ABORT_BYTE);
> +     return;
> +
> +error:
> +     stm32prog_serial_result(NACK_BYTE);
> +}
> +
> +/* MAIN function = SERIAL LOOP 
> ***********************************************/
> +
> +/**
> + * stm32prog_serial_loop() - USART bootloader Loop routine
> + *
> + * @data:            Current command context
> + * @return true if reset is needed after loop
> + */
> +bool stm32prog_serial_loop(struct stm32prog_data *data)
> +{
> +     u32 counter = 0x0;
> +     u8 command = 0x0;
> +     u8 found;
> +     int phase = data->phase;
> +
> +     /* element of cmd_func need to aligned with cmd_id[]*/
> +     void (*cmd_func[NB_CMD])(struct stm32prog_data *) = {
> +             /* GET_CMD_COMMAND */   get_cmd_command,
> +             /* GET_VER_COMMAND */   get_version_command,
> +             /* GET_ID_COMMAND */    get_id_command,
> +             /* GET_PHASE_COMMAND */ get_phase_command,
> +             /* RM_COMMAND */        read_memory_command,
> +             /* READ_PART_COMMAND */ read_partition_command,
> +             /* START_COMMAND */     start_command,
> +             /* DOWNLOAD_COMMAND */  download_command
> +     };
> +
> +     /* flush and NACK pending command received during u-boot init
> +      * request command reemit
> +      */
> +     stm32prog_serial_result(NACK_BYTE);
> +
> +     clear_ctrlc(); /* forget any previous Control C */
> +     while (!had_ctrlc()) {
> +             phase = data->phase;
> +
> +             if (phase == PHASE_DO_RESET)
> +                     return true;
> +
> +             /* Get the user command: read first byte */
> +             command = stm32prog_serial_getc();
> +
> +             if (command == INIT_BYTE) {
> +                     puts("\nConnected\n");
> +                     stm32prog_serial_result(ACK_BYTE);
> +                     continue;
> +             }
> +
> +             found = 0;
> +             for (counter = 0; counter < NB_CMD; counter++)
> +                     if (cmd_id[counter] == command) {
> +                             found = 1;
> +                             break;
> +                     }
> +             if (found)
> +                     if ((command ^ stm32prog_serial_getc()) != 0xFF)
> +                             found = 0;
> +             if (!found) {
> +                     /* wait to be sure that all data are received
> +                      * in the FIFO before flush (CMD and XOR)
> +                      */
> +                     mdelay(3);
> +                     stm32prog_serial_result(NACK_BYTE);
> +             } else {
> +                     stm32prog_serial_result(ACK_BYTE);
> +                     cmd_func[counter](data);
> +             }
> +             WATCHDOG_RESET();
> +     }
> +
> +     /* clean device */
> +     if (gd->cur_serial_dev == down_serial_dev) {
> +             /* restore console on uart */
> +             gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT);
> +     }
> +     down_serial_dev = NULL;
> +
> +     return false; /* no reset after ctrlc */
> +}
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c 
> b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
> index 34f27c074f..969245e199 100644
> --- a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
> @@ -19,6 +19,7 @@ static int stm32prog_set_phase(struct stm32prog_data *data, 
> u8 phase,
>  
>       if (phase == data->phase) {
>               data->offset = offset;
> +             data->dfu_seq = 0;
>               return 0;
>       }
>  
> @@ -29,6 +30,7 @@ static int stm32prog_set_phase(struct stm32prog_data *data, 
> u8 phase,
>                       data->cur_part = part;
>                       data->phase = phase;
>                       data->offset = offset;
> +                     data->dfu_seq = 0;
>                       return 0;
>               }
>       }

Reviewed-by: Patrice Chotard <patrice.chot...@st.com>

Thanks

Patrice

Reply via email to