u-boot-brain/board/st/common/stm32mp_dfu.c
Patrick Delaunay 954bd1a923 stm32mp: add the command stm32prog
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>
2020-05-14 09:02:12 +02:00

246 lines
5.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
/*
* Copyright (C) 2020, STMicroelectronics - All Rights Reserved
*/
#include <common.h>
#include <blk.h>
#include <dfu.h>
#include <env.h>
#include <memalign.h>
#include <misc.h>
#include <mtd.h>
#include <mtd_node.h>
#include <asm/arch/stm32prog.h>
#define DFU_ALT_BUF_LEN SZ_1K
static void board_get_alt_info_mmc(struct udevice *dev, char *buf)
{
disk_partition_t info;
int p, len, devnum;
bool first = true;
const char *name;
struct mmc *mmc;
struct blk_desc *desc;
mmc = mmc_get_mmc_dev(dev);
if (!mmc)
return;
if (mmc_init(mmc))
return;
desc = mmc_get_blk_desc(mmc);
if (!desc)
return;
name = blk_get_if_type_name(desc->if_type);
devnum = desc->devnum;
len = strlen(buf);
if (buf[0] != '\0')
len += snprintf(buf + len,
DFU_ALT_BUF_LEN - len, "&");
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s %d=", name, devnum);
if (IS_MMC(mmc) && mmc->capacity_boot) {
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s%d_boot1 raw 0x0 0x%llx mmcpart 1;",
name, devnum, mmc->capacity_boot);
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s%d_boot2 raw 0x0 0x%llx mmcpart 2",
name, devnum, mmc->capacity_boot);
first = false;
}
for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) {
if (part_get_info(desc, p, &info))
continue;
if (!first)
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, ";");
first = false;
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s%d_%s part %d %d",
name, devnum, info.name, devnum, p);
}
}
static void board_get_alt_info_mtd(struct mtd_info *mtd, char *buf)
{
struct mtd_info *part;
bool first = true;
const char *name;
int len, partnum = 0;
name = mtd->name;
len = strlen(buf);
if (buf[0] != '\0')
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, "&");
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"mtd %s=", name);
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s raw 0x0 0x%llx ",
name, mtd->size);
list_for_each_entry(part, &mtd->partitions, node) {
partnum++;
if (!first)
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len, ";");
first = false;
len += snprintf(buf + len, DFU_ALT_BUF_LEN - len,
"%s_%s part %d",
name, part->name, partnum);
}
}
void set_dfu_alt_info(char *interface, char *devstr)
{
struct udevice *dev;
struct mtd_info *mtd;
ALLOC_CACHE_ALIGN_BUFFER(char, buf, DFU_ALT_BUF_LEN);
if (env_get("dfu_alt_info"))
return;
memset(buf, 0, sizeof(buf));
snprintf(buf, DFU_ALT_BUF_LEN,
"ram 0=%s", CONFIG_DFU_ALT_RAM0);
if (!uclass_get_device(UCLASS_MMC, 0, &dev))
board_get_alt_info_mmc(dev, buf);
if (!uclass_get_device(UCLASS_MMC, 1, &dev))
board_get_alt_info_mmc(dev, buf);
if (CONFIG_IS_ENABLED(MTD)) {
/* probe all MTD devices */
mtd_probe_devices();
/* probe SPI flash device on a bus */
if (!uclass_get_device(UCLASS_SPI_FLASH, 0, &dev)) {
mtd = get_mtd_device_nm("nor0");
if (!IS_ERR_OR_NULL(mtd))
board_get_alt_info_mtd(mtd, buf);
}
mtd = get_mtd_device_nm("nand0");
if (!IS_ERR_OR_NULL(mtd))
board_get_alt_info_mtd(mtd, buf);
mtd = get_mtd_device_nm("spi-nand0");
if (!IS_ERR_OR_NULL(mtd))
board_get_alt_info_mtd(mtd, buf);
}
#ifdef CONFIG_DFU_VIRT
strncat(buf, "&virt 0=OTP", DFU_ALT_BUF_LEN);
if (IS_ENABLED(CONFIG_PMIC_STPMIC1))
strncat(buf, "&virt 1=PMIC", DFU_ALT_BUF_LEN);
#endif
env_set("dfu_alt_info", buf);
puts("DFU alt info setting: done\n");
}
#if CONFIG_IS_ENABLED(DFU_VIRT)
#include <dfu.h>
#include <power/stpmic1.h>
static int dfu_otp_read(u64 offset, u8 *buffer, long *size)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_GET_DRIVER(stm32mp_bsec),
&dev);
if (ret)
return ret;
ret = misc_read(dev, offset + STM32_BSEC_OTP_OFFSET, buffer, *size);
if (ret >= 0) {
*size = ret;
ret = 0;
}
return 0;
}
static int dfu_pmic_read(u64 offset, u8 *buffer, long *size)
{
int ret;
#ifdef CONFIG_PMIC_STPMIC1
struct udevice *dev;
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_GET_DRIVER(stpmic1_nvm),
&dev);
if (ret)
return ret;
ret = misc_read(dev, 0xF8 + offset, buffer, *size);
if (ret >= 0) {
*size = ret;
ret = 0;
}
if (ret == -EACCES) {
*size = 0;
ret = 0;
}
#else
pr_err("PMIC update not supported");
ret = -EOPNOTSUPP;
#endif
return ret;
}
int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
switch (dfu->data.virt.dev_num) {
case 0x0:
return dfu_otp_read(offset, buf, len);
case 0x1:
return dfu_pmic_read(offset, buf, len);
}
if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
return stm32prog_read_medium_virt(dfu, offset, buf, len);
*len = 0;
return 0;
}
int dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset,
void *buf, long *len)
{
if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
return stm32prog_write_medium_virt(dfu, offset, buf, len);
return -EOPNOTSUPP;
}
int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
{
if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
return stm32prog_get_medium_size_virt(dfu, size);
*size = SZ_1K;
return 0;
}
#endif