]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
stm32mp: stm32prog: add serial link support
authorPatrick Delaunay <patrick.delaunay@st.com>
Wed, 18 Mar 2020 08:25:00 +0000 (09:25 +0100)
committerPatrick Delaunay <patrick.delaunay@st.com>
Thu, 14 May 2020 07:02:12 +0000 (09:02 +0200)
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.delaunay@st.com>
Reviewed-by: Patrice Chotard <patrice.chotard@st.com>
arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c [new file with mode: 0644]
arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c

index 14f722759c94b926ef792e50f66a5427d29684c2..548a3789218ad8c4dda55b2899c4f118220f455b 100644 (file)
@@ -5,4 +5,5 @@
 
 obj-y += cmd_stm32prog.o
 obj-y += stm32prog.o
+obj-y += stm32prog_serial.o
 obj-y += stm32prog_usb.o
index 581f97e0b50b3992d97d5d550218ac667c280ed5..1769ba05f2899d4d83b74ff16c06809c368cfe6b 100644 (file)
@@ -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"
index d127afefaa993fbee345006164ddee44528670f3..0967bbc11ab0d9c2ad1ccf51e43ad4b9318aae84 100644 (file)
@@ -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);
        }
 }
index 83b27980f5d3773b0575f81f85fa24ae47989cb8..c4fdb5b8c3896e15850038c1ebe78f4e09eee0cc 100644 (file)
@@ -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 (file)
index 0000000..5a16979
--- /dev/null
@@ -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 */
+}
index 34f27c074f5209d02339fb0260cf3406d2f862d8..969245e199cb403971c1d6f279f6b4297d780be8 100644 (file)
@@ -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;
                }
        }