mirror of
https://github.com/CTCaer/switch-l4t-atf.git
synced 2024-12-04 08:53:37 +00:00
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:
parent
9a138eb5f2
commit
afad5214a7
27
plat/st/common/include/stm32cubeprogrammer.h
Normal file
27
plat/st/common/include/stm32cubeprogrammer.h
Normal 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 */
|
196
plat/st/common/stm32cubeprogrammer_usb.c
Normal file
196
plat/st/common/stm32cubeprogrammer_usb.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user