feat(plat/st): add STM32CubeProgrammer support on USB

Add a file to support over USB the STMicroelectronics tool
STM32CubeProgrammer in BL2 for STM32MP15x platform.

This tools is based on DFU stack.

Change-Id: I48a8f772cb0e9b8be24c06847f724f0470c0f917
Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
This commit is contained in:
Patrick Delaunay 2020-09-14 11:13:34 +02:00 committed by Manish Pandey
parent 9a138eb5f2
commit afad5214a7
2 changed files with 223 additions and 0 deletions

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef STM32CUBEPROGRAMMER_H
#define STM32CUBEPROGRAMMER_H
#include <stdint.h>
#include <usb_dfu.h>
/* Phase definition */
#define PHASE_FLASHLAYOUT 0U
#define PHASE_SSBL 3U
#define PHASE_CMD 0xF1U
#define PHASE_RESET 0xFFU
/* Functions provided by plat */
uint8_t usb_dfu_get_phase(uint8_t alt);
int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
uintptr_t ssbl_base,
size_t ssbl_len);
#endif /* STM32CUBEPROGRAMMER_H */

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2021, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <tools_share/firmware_image_package.h>
#include <stm32cubeprogrammer.h>
#include <usb_dfu.h>
/* Undefined download address */
#define UNDEFINED_DOWN_ADDR 0xFFFFFFFF
struct dfu_state {
uint8_t phase;
uintptr_t base;
size_t len;
uintptr_t address;
/* working buffer */
uint8_t buffer[UCHAR_MAX];
};
static struct dfu_state dfu_state;
/* minimal size of Get Pḧase = offset for additionnl information */
#define GET_PHASE_LEN 9
#define DFU_ERROR(...) \
{ \
ERROR(__VA_ARGS__); \
if (dfu->phase != PHASE_RESET) { \
snprintf((char *)&dfu->buffer[GET_PHASE_LEN], \
sizeof(dfu->buffer) - GET_PHASE_LEN, \
__VA_ARGS__); \
dfu->phase = PHASE_RESET; \
dfu->address = UNDEFINED_DOWN_ADDR; \
dfu->len = 0; \
} \
}
static bool is_valid_header(fip_toc_header_t *header)
{
if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) {
return true;
}
return false;
}
static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len,
void *user_data)
{
int result = 0;
uint32_t length = 0;
struct dfu_state *dfu = (struct dfu_state *)user_data;
switch (usb_dfu_get_phase(alt)) {
case PHASE_CMD:
/* Get Pḧase */
dfu->buffer[0] = dfu->phase;
dfu->buffer[1] = (uint8_t)(dfu->address);
dfu->buffer[2] = (uint8_t)(dfu->address >> 8);
dfu->buffer[3] = (uint8_t)(dfu->address >> 16);
dfu->buffer[4] = (uint8_t)(dfu->address >> 24);
dfu->buffer[5] = 0x00;
dfu->buffer[6] = 0x00;
dfu->buffer[7] = 0x00;
dfu->buffer[8] = 0x00;
length = GET_PHASE_LEN;
if (dfu->phase == PHASE_FLASHLAYOUT &&
dfu->address == UNDEFINED_DOWN_ADDR) {
INFO("Send detach request\n");
dfu->buffer[length++] = 0x01;
}
if (dfu->phase == PHASE_RESET) {
/* error information is added by DFU_ERROR macro */
length += strnlen((char *)&dfu->buffer[GET_PHASE_LEN],
sizeof(dfu->buffer) - GET_PHASE_LEN)
- 1;
}
break;
default:
DFU_ERROR("phase ID :%i, alternate %i for phase %i\n",
dfu->phase, alt, usb_dfu_get_phase(alt));
result = -EIO;
break;
}
if (result == 0) {
*len = length;
*buffer = (uintptr_t)dfu->buffer;
}
return result;
}
static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len,
void *user_data)
{
struct dfu_state *dfu = (struct dfu_state *)user_data;
if ((dfu->phase != usb_dfu_get_phase(alt)) ||
(dfu->address == UNDEFINED_DOWN_ADDR)) {
DFU_ERROR("phase ID :%i, alternate %i, address %x\n",
dfu->phase, alt, (uint32_t)dfu->address);
return -EIO;
}
VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len);
*buffer = dfu->address;
dfu->address += *len;
if (dfu->address - dfu->base > dfu->len) {
return -EIO;
}
return 0;
}
static int dfu_callback_manifestation(uint8_t alt, void *user_data)
{
struct dfu_state *dfu = (struct dfu_state *)user_data;
if (dfu->phase != usb_dfu_get_phase(alt)) {
ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n",
dfu->phase, alt, dfu->address);
return -EIO;
}
INFO("phase ID :%i, Manifestation %d at %lx\n",
dfu->phase, alt, dfu->address);
switch (dfu->phase) {
case PHASE_SSBL:
if (!is_valid_header((fip_toc_header_t *)dfu->base)) {
DFU_ERROR("FIP Header check failed for phase %d\n", alt);
return -EIO;
}
VERBOSE("FIP header looks OK.\n");
/* Configure End with request detach */
dfu->phase = PHASE_FLASHLAYOUT;
dfu->address = UNDEFINED_DOWN_ADDR;
dfu->len = 0;
break;
default:
DFU_ERROR("Unknown phase\n");
}
return 0;
}
/* Open a connection to the USB device */
static const struct usb_dfu_media usb_dfu_fops = {
.upload = dfu_callback_upload,
.download = dfu_callback_download,
.manifestation = dfu_callback_manifestation,
};
int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
uintptr_t base,
size_t len)
{
int ret;
usb_core_handle->user_data = (void *)&dfu_state;
INFO("DFU USB START...\n");
ret = usb_core_start(usb_core_handle);
if (ret != USBD_OK) {
return -EIO;
}
dfu_state.phase = PHASE_SSBL;
dfu_state.address = base;
dfu_state.base = base;
dfu_state.len = len;
ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops);
if (ret != USBD_OK) {
return -EIO;
}
INFO("DFU USB STOP...\n");
ret = usb_core_stop(usb_core_handle);
if (ret != USBD_OK) {
return -EIO;
}
return 0;
}