mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-12 07:57:21 +00:00
954bd1a923
Add a specific command stm32prog for STM32MP soc family witch allows to program the boot devices with the tool STM32CubeProgrammer (http://www.st.com/STM32CubeProg). This command uses the same UART STM32 protocol than MCU STM32 with or USB with DFU protocol v1.1 (ithe MCU ST extension are no supported). The executed actions are based on a tab separated value file with a stm32 header, the FlashLayout file (https://wiki.st.com/stm32mpu/wiki/STM32CubeProgrammer_flashlayout). This file is parsed by the U-Boot command to: - initialize the devices - create the partition table on each device - initialize the DFU backend to access to not volatile memory (NOR/NAND/SD/eMMC) or to virtual device (OTP/PMIC) Up to STM32PROG_MAX_DEV (5) devices can be updated with a FlashLayout. The communication between U-Boot and STM32CubeProgrammer is done with the specific alternate configuration (see "AN5275: USB DFU/USART protocols used in STM32MP1 Series bootloaders" for details). The command stm32prog is executed when a boot from USB is detected (selected with bootpins) and we can program the boot devices with a simple command (on Windows or Linux): PC $> STM32_Programmer_CLI -c port=usb1 -w flaslayout.tsv 1/ the ROM code loads TF-A in embedded RAM (DFU or uart) 2/ TF-A loads flashlayout file and U-Boot in DDR (DFU or uart) 3/ U-Boot executes the stm32prog command (DFU or uart) Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com> Reviewed-by: Patrice Chotard <patrice.chotard@st.com> Reviewed-by: Patrice Chotard <patrice.chotard@st.com>
206 lines
4.5 KiB
C
206 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
|
/*
|
|
* Copyright (C) 2020, STMicroelectronics - All Rights Reserved
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dfu.h>
|
|
#include <g_dnl.h>
|
|
#include <usb.h>
|
|
#include <asm/arch/stm32prog.h>
|
|
#include <asm/arch/sys_proto.h>
|
|
#include "stm32prog.h"
|
|
|
|
static int stm32prog_set_phase(struct stm32prog_data *data, u8 phase,
|
|
u32 offset)
|
|
{
|
|
struct stm32prog_part_t *part;
|
|
int i;
|
|
|
|
if (phase == data->phase) {
|
|
data->offset = offset;
|
|
return 0;
|
|
}
|
|
|
|
/* found partition for phase */
|
|
for (i = 0; i < data->part_nb; i++) {
|
|
part = &data->part_array[i];
|
|
if (part->id == phase) {
|
|
data->cur_part = part;
|
|
data->phase = phase;
|
|
data->offset = offset;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int stm32prog_cmd_write(u64 offset, void *buf, long *len)
|
|
{
|
|
u8 phase;
|
|
u32 address;
|
|
u8 *pt = buf;
|
|
void (*entry)(void);
|
|
int ret;
|
|
|
|
if (*len < 5) {
|
|
pr_err("size not allowed\n");
|
|
return -EINVAL;
|
|
}
|
|
if (offset) {
|
|
pr_err("invalid offset\n");
|
|
return -EINVAL;
|
|
}
|
|
phase = pt[0];
|
|
address = (pt[1] << 24) | (pt[2] << 16) | (pt[3] << 8) | pt[4];
|
|
if (phase == PHASE_RESET) {
|
|
entry = (void *)address;
|
|
printf("## Starting application at 0x%x ...\n", address);
|
|
(*entry)();
|
|
printf("## Application terminated\n");
|
|
return 0;
|
|
}
|
|
/* set phase and offset */
|
|
ret = stm32prog_set_phase(stm32prog_data, phase, address);
|
|
if (ret)
|
|
pr_err("failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
#define PHASE_MIN_SIZE 9
|
|
static int stm32prog_cmd_read(u64 offset, void *buf, long *len)
|
|
{
|
|
u32 destination = DEFAULT_ADDRESS; /* destination address */
|
|
u32 dfu_offset;
|
|
u8 *pt_buf = buf;
|
|
int phase;
|
|
char *err_msg;
|
|
int length;
|
|
|
|
if (*len < PHASE_MIN_SIZE) {
|
|
pr_err("request exceeds allowed area\n");
|
|
return -EINVAL;
|
|
}
|
|
if (offset) {
|
|
*len = 0; /* EOF for second request */
|
|
return 0;
|
|
}
|
|
phase = stm32prog_data->phase;
|
|
if (phase == PHASE_FLASHLAYOUT)
|
|
destination = STM32_DDR_BASE;
|
|
dfu_offset = stm32prog_data->offset;
|
|
|
|
/* mandatory header, size = PHASE_MIN_SIZE */
|
|
*pt_buf++ = (u8)(phase & 0xFF);
|
|
*pt_buf++ = (u8)(destination);
|
|
*pt_buf++ = (u8)(destination >> 8);
|
|
*pt_buf++ = (u8)(destination >> 16);
|
|
*pt_buf++ = (u8)(destination >> 24);
|
|
*pt_buf++ = (u8)(dfu_offset);
|
|
*pt_buf++ = (u8)(dfu_offset >> 8);
|
|
*pt_buf++ = (u8)(dfu_offset >> 16);
|
|
*pt_buf++ = (u8)(dfu_offset >> 24);
|
|
|
|
if (phase == PHASE_RESET || phase == PHASE_DO_RESET) {
|
|
err_msg = stm32prog_get_error(stm32prog_data);
|
|
length = strlen(err_msg);
|
|
if (length + PHASE_MIN_SIZE > *len)
|
|
length = *len - PHASE_MIN_SIZE;
|
|
|
|
memcpy(pt_buf, err_msg, length);
|
|
*len = PHASE_MIN_SIZE + length;
|
|
stm32prog_do_reset(stm32prog_data);
|
|
} else if (phase == PHASE_FLASHLAYOUT) {
|
|
*pt_buf++ = stm32prog_data->part_nb ? 1 : 0;
|
|
*len = PHASE_MIN_SIZE + 1;
|
|
} else {
|
|
*len = PHASE_MIN_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stm32prog_write_medium_virt(struct dfu_entity *dfu, u64 offset,
|
|
void *buf, long *len)
|
|
{
|
|
if (dfu->dev_type != DFU_DEV_VIRT)
|
|
return -EINVAL;
|
|
|
|
switch (dfu->data.virt.dev_num) {
|
|
case PHASE_CMD:
|
|
return stm32prog_cmd_write(offset, buf, len);
|
|
}
|
|
*len = 0;
|
|
return 0;
|
|
}
|
|
|
|
int stm32prog_read_medium_virt(struct dfu_entity *dfu, u64 offset,
|
|
void *buf, long *len)
|
|
{
|
|
if (dfu->dev_type != DFU_DEV_VIRT)
|
|
return -EINVAL;
|
|
|
|
switch (dfu->data.virt.dev_num) {
|
|
case PHASE_CMD:
|
|
return stm32prog_cmd_read(offset, buf, len);
|
|
}
|
|
*len = 0;
|
|
return 0;
|
|
}
|
|
|
|
int stm32prog_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
|
|
{
|
|
if (dfu->dev_type != DFU_DEV_VIRT) {
|
|
*size = 0;
|
|
pr_debug("%s, invalid dev_type = %d\n",
|
|
__func__, dfu->dev_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (dfu->data.virt.dev_num) {
|
|
case PHASE_CMD:
|
|
*size = 512;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool stm32prog_usb_loop(struct stm32prog_data *data, int dev)
|
|
{
|
|
int ret;
|
|
bool result;
|
|
/* USB download gadget for STM32 Programmer */
|
|
char product[128];
|
|
|
|
snprintf(product, sizeof(product),
|
|
"USB download gadget@Device ID /0x%03X, @Revision ID /0x%04X",
|
|
get_cpu_dev(), get_cpu_rev());
|
|
g_dnl_set_product(product);
|
|
|
|
if (stm32prog_data->phase == PHASE_FLASHLAYOUT) {
|
|
ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu");
|
|
if (ret || stm32prog_data->phase == PHASE_DO_RESET)
|
|
return ret;
|
|
/* prepare the second enumeration with the FlashLayout */
|
|
if (stm32prog_data->phase == PHASE_FLASHLAYOUT)
|
|
stm32prog_dfu_init(data);
|
|
/* found next selected partition */
|
|
stm32prog_next_phase(data);
|
|
}
|
|
|
|
ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu");
|
|
|
|
result = !!(ret) || (stm32prog_data->phase == PHASE_DO_RESET);
|
|
|
|
g_dnl_set_product(NULL);
|
|
|
|
return result;
|
|
}
|
|
|
|
int g_dnl_get_board_bcd_device_number(int gcnum)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
return 0x200;
|
|
}
|