diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst new file mode 100644 index 0000000..24cf885 --- /dev/null +++ b/Documentation/boards/stm32mp.rst @@ -0,0 +1,71 @@ +STMicroelectronics STM32MP +========================== + +.. note:: + + Support for the STM32MP architecure in barebox is still in progress. + Bootstrapping an OS from mainline barebox is not yet supported. + +The STM32MP is a line of 32-bit ARM SoCs. They reuse peripherals of the +STM32 line of microcontrollers and can have a STM32 MCU embedded as co-processor +as well. + +The boot process of the STM32MP SoC is a two step process. +The first stage boot loader (FSBL) is loaded by the ROM code into the built-in +SYSRAM and executed. The FSBL sets up the SDRAM, install a secure monitor and +then the second stage boot loader (SSBL) is loaded into DRAM. + +When building barebox, the resulting ``barebox-${board}.img`` file has the STM32 +header preprended, so it can be loaded directly as SSBL by the ARM TF-A +(https://github.com/ARM-software/arm-trusted-firmware). Each entry point has a +header-less image ending in ``*.pblb`` as well. + +Use of barebox as FSBL is not supported. + +Building barebox +---------------- + +With multi-image and device trees, it's expected to have ``stm32mp_defconfig`` +as sole defconfig for all STM32MP boards:: + + make ARCH=arm stm32mp_defconfig + +The resulting images will be placed under ``images/``: + +:: + + barebox-stm32mp157c-dk2.img + + +Flashing barebox +---------------- + +An appropriate image for the boot media can be generated with following +``genimage(1)`` config:: + + image @STM32MP_BOARD@.img { + hdimage { + align = 1M + gpt = "true" + } + partition fsbl1 { + image = "tf-a-@STM32MP_BOARD@.stm32" + size = 256K + } + partition fsbl2 { + image = "tf-a-@STM32MP_BOARD@.stm32" + size = 256K + } + partition ssbl { + image = "barebox-@STM32MP_BOARD@.img" + size = 1M + } + } + +Image can then be flashed on e.g. a SD-Card. + +TODO +---- + +* Extend barebox MMCI support to support the SDMMC2 +* Extend barebox DesignWare MAC support to support the stmmac diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0f5190b..de45bcf 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -208,7 +208,7 @@ select GENERIC_GPIO config ARCH_STM32MP - bool "ST stm32mp1xx" + bool "STMicroelectronics STM32MP" select CPU_V7 select HAVE_PBL_MULTI_IMAGES select CLKDEV_LOOKUP diff --git a/arch/arm/dts/stm32mp157c.dtsi b/arch/arm/dts/stm32mp157c.dtsi index b97622c..8d9c84a 100644 --- a/arch/arm/dts/stm32mp157c.dtsi +++ b/arch/arm/dts/stm32mp157c.dtsi @@ -17,6 +17,6 @@ gpio8 = &gpioi; gpio9 = &gpioj; gpio10 = &gpiok; - gpio11 = &gpioz; + gpio25 = &gpioz; }; }; diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig index be16294..6bf950b 100644 --- a/arch/arm/mach-stm32mp/Kconfig +++ b/arch/arm/mach-stm32mp/Kconfig @@ -1,10 +1,14 @@ if ARCH_STM32MP -config ARCH_STM32MP1157 +config ARCH_NR_GPIO + int + default 416 + +config ARCH_STM32MP157 bool config MACH_STM32MP157C_DK2 - select ARCH_STM32MP1157 + select ARCH_STM32MP157 select ARM_USE_COMPRESSED_DTB bool "STM32MP157C-DK2 board" diff --git a/common/filetype.c b/common/filetype.c index 329f514..825bf25 100644 --- a/common/filetype.c +++ b/common/filetype.c @@ -79,6 +79,7 @@ [filetype_layerscape_qspi_image] = { "Layerscape QSPI image", "layerscape-qspi-PBL" }, [filetype_ubootvar] = { "U-Boot environmemnt variable data", "ubootvar" }, + [filetype_stm32_image_v1] = { "STM32 image (v1)", "stm32-image-v1" }, }; const char *file_type_to_string(enum filetype f) @@ -355,6 +356,14 @@ if (buf8[0] == 'M' && buf8[1] == 'Z') return filetype_exe; + if (bufsize < 256) + return filetype_unknown; + + if (strncmp(buf8, "STM\x32", 4) == 0) { + if (buf8[74] == 0x01) + return filetype_stm32_image_v1; + } + if (bufsize < 512) return filetype_unknown; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index caf1dc9..048f208 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -21,4 +21,9 @@ help This enables the reset controller driver for i.MX7 SoCs. +config RESET_STM32 + bool "STM32 Reset Driver" + help + This enables the reset controller driver for STM32MP and STM32 MCUs. + endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 0b55caa..8460c4b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_RESET_CONTROLLER) += core.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o +obj-$(CONFIG_RESET_STM32) += reset-stm32.o diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c new file mode 100644 index 0000000..5689dfd --- /dev/null +++ b/drivers/reset/reset-stm32.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Copyright (C) 2019, Ahmad Fatoum, Pengutronix + * Author(s): Patrice Chotard, for STMicroelectronics. + */ + +#include +#include +#include +#include +#include + +#define RCC_CL 0x4 + +struct stm32_reset { + void __iomem *base; + struct reset_controller_dev rcdev; + void (*reset)(void __iomem *reg, unsigned offset, bool assert); +}; + +static struct stm32_reset *to_stm32_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct stm32_reset, rcdev); +} + +static void stm32mp_reset(void __iomem *reg, unsigned offset, bool assert) +{ + if (!assert) + reg += RCC_CL; + + writel(BIT(offset), reg); +} + +static void stm32mcu_reset(void __iomem *reg, unsigned offset, bool assert) +{ + if (assert) + setbits_le32(reg, BIT(offset)); + else + clrbits_le32(reg, BIT(offset)); +} + +static void stm32_reset(struct stm32_reset *priv, unsigned long id, bool assert) +{ + int bank = (id / BITS_PER_LONG) * 4; + int offset = id % BITS_PER_LONG; + + priv->reset(priv->base + bank, offset, assert); +} + +static int stm32_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + stm32_reset(to_stm32_reset(rcdev), id, true); + return 0; +} + +static int stm32_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + stm32_reset(to_stm32_reset(rcdev), id, false); + return 0; +} + +static const struct reset_control_ops stm32_reset_ops = { + .assert = stm32_reset_assert, + .deassert = stm32_reset_deassert, +}; + +static int stm32_reset_probe(struct device_d *dev) +{ + struct stm32_reset *priv; + struct resource *iores; + int ret; + + priv = xzalloc(sizeof(*priv)); + ret = dev_get_drvdata(dev, (const void **)&priv->reset); + if (ret) + return ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + priv->base = IOMEM(iores->start); + priv->rcdev.nr_resets = (iores->end - iores->start) * BITS_PER_BYTE; + priv->rcdev.ops = &stm32_reset_ops; + priv->rcdev.of_node = dev->device_node; + + return reset_controller_register(&priv->rcdev); +} + +static const struct of_device_id stm32_rcc_reset_dt_ids[] = { + { .compatible = "st,stm32mp1-rcc", .data = stm32mp_reset }, + { .compatible = "st,stm32-rcc", .data = stm32mcu_reset }, + { /* sentinel */ }, +}; + +static struct driver_d stm32_rcc_reset_driver = { + .name = "stm32_rcc_reset", + .probe = stm32_reset_probe, + .of_compatible = DRV_OF_COMPAT(stm32_rcc_reset_dt_ids), +}; + +static int stm32_rcc_reset_init(void) +{ + return platform_driver_register(&stm32_rcc_reset_driver); +} +postcore_initcall(stm32_rcc_reset_init); diff --git a/images/Makefile.stm32mp b/images/Makefile.stm32mp index c49b1d7..910e029 100644 --- a/images/Makefile.stm32mp +++ b/images/Makefile.stm32mp @@ -1,8 +1,19 @@ # SPDX-License-Identifier: GPL-2.0-or-later # -# barebox image generation Makefile for STMicroelectronics MP1 +# barebox image generation Makefile for STMicroelectronics MP # +# %.stm32 - convert into STM32MP image +# -------------------------------------- + +$(obj)/%.stm32: $(obj)/% FORCE + $(call if_changed,stm32_image) + +STM32MP1_OPTS = -a 0xc0100000 -e 0xc0100000 -v1 + +# -------------------------------------- + pblb-$(CONFIG_MACH_STM32MP157C_DK2) += start_stm32mp157c_dk2 -FILE_barebox-stm32mp157c-dk2.img = start_stm32mp157c_dk2.pblb +FILE_barebox-stm32mp157c-dk2.img = start_stm32mp157c_dk2.pblb.stm32 +OPTS_start_stm32mp157c_dk2.pblb.stm32 = $(STM32MP1_OPTS) image-$(CONFIG_MACH_STM32MP157C_DK2) += barebox-stm32mp157c-dk2.img diff --git a/include/filetype.h b/include/filetype.h index f1be04e..90a03de 100644 --- a/include/filetype.h +++ b/include/filetype.h @@ -48,6 +48,7 @@ filetype_layerscape_image, filetype_layerscape_qspi_image, filetype_ubootvar, + filetype_stm32_image_v1, filetype_max, }; diff --git a/include/gpio.h b/include/gpio.h index e822fd5..1926ede 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -45,7 +45,11 @@ } #endif +#if defined(CONFIG_ARCH_NR_GPIO) && CONFIG_ARCH_NR_GPIO > 0 +#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO +#else #define ARCH_NR_GPIOS 256 +#endif static inline int gpio_is_valid(int gpio) { diff --git a/scripts/Makefile b/scripts/Makefile index 7d64da6..dffab53 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -22,6 +22,7 @@ hostprogs-$(CONFIG_ARCH_SOCFPGA) += socfpga_mkimage hostprogs-$(CONFIG_MXS_HOSTTOOLS)+= mxsimage mxsboot hostprogs-$(CONFIG_ARCH_LAYERSCAPE) += pblimage +hostprogs-$(CONFIG_ARCH_STM32MP) += stm32image HOSTCFLAGS += -I$(srctree)/scripts/include/ HOSTLDLIBS_mxsimage = `pkg-config --libs openssl` HOSTCFLAGS_omap3-usb-loader.o = `pkg-config --cflags libusb-1.0` diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 75e1734..fc5fe3d 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -512,6 +512,9 @@ $(obj)/%.bct: $(obj)/%.bct.cfg $(call cmd,cboot_bct) +quiet_cmd_stm32_image = STM32-IMG $@ + cmd_stm32_image = $(objtree)/scripts/stm32image $(OPTS_$(@F)) -i $< -o $@ + quiet_cmd_b64dec = B64DEC $@ cmd_b64dec = base64 -d $< > $@ diff --git a/scripts/stm32image.c b/scripts/stm32image.c new file mode 100644 index 0000000..c33bcca --- /dev/null +++ b/scripts/stm32image.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Copyright (C) 2019, Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compiler.h" + +#ifndef MAP_POPULATE +#define MAP_POPULATE 0 +#endif + +/* magic ='S' 'T' 'M' 0x32 */ +#define HEADER_MAGIC htobe32(0x53544D32) +#define VER_MAJOR_IDX 2 +#define VER_MINOR_IDX 1 +#define VER_VARIANT_IDX 0 +/* default option : bit0 => no signature */ +#define HEADER_DEFAULT_OPTION htole32(0x00000001) +/* default binary type for barebox */ +#define HEADER_TYPE_BAREBOX htole32(0x00000000) +#define HEADER_LENGTH 0x100 + +#define FSBL_LOADADDR 0x2ffc2400 +#define FSBL_ENTRYPOINT (FSBL_LOADADDR + HEADER_LENGTH) +#define MAX_FSBL_PAYLOAD_SIZE (247 * 1024) + +struct __attribute((packed)) stm32_header { + uint32_t magic_number; + uint32_t image_signature[64 / 4]; + uint32_t image_checksum; + uint8_t header_version[4]; + uint32_t image_length; + uint32_t image_entry_point; + uint32_t reserved1; + uint32_t load_address; + uint32_t reserved2; + uint32_t version_number; + uint32_t option_flags; + uint32_t ecdsa_algorithm; + uint32_t ecdsa_public_key[64 / 4]; + uint32_t padding[83 / 4]; + uint32_t binary_type; +}; + +static struct stm32_header stm32image_header; + +static const char *infile; +static const char *outfile; +static int in_fd; +static int out_fd; +static uint32_t loadaddr; +static uint32_t entrypoint; +static uint32_t pbl_size; +static uint32_t version = 0x01; + +static void stm32image_print_header(void) +{ + printf("Image Type : STMicroelectronics STM32 V%d.%d\n", + stm32image_header.header_version[VER_MAJOR_IDX], + stm32image_header.header_version[VER_MINOR_IDX]); + printf("Image Size : %u bytes\n", + le32toh(stm32image_header.image_length)); + printf("Image Load : 0x%08x\n", + le32toh(stm32image_header.load_address)); + printf("Entry Point : 0x%08x\n", + le32toh(stm32image_header.image_entry_point)); + printf("Checksum : 0x%08x\n", + le32toh(stm32image_header.image_checksum)); + printf("Option : 0x%08x\n", + le32toh(stm32image_header.option_flags)); + printf("BinaryType : 0x%08x\n", + le32toh(stm32image_header.binary_type)); +} + +static uint32_t stm32image_checksum(void) +{ + uint32_t csum = 0; + uint32_t len = pbl_size; + uint8_t *p; + + p = mmap(NULL, len, PROT_READ, MAP_PRIVATE | MAP_POPULATE, in_fd, 0); + if (p == MAP_FAILED) { + perror("mmap"); + exit(EXIT_FAILURE); + } + + for (; len > 0; len--) + csum += *p++; + + munmap(p, len); + + return csum; +} + +static void stm32image_set_header(void) +{ + + memset(&stm32image_header, 0, sizeof(struct stm32_header)); + + /* set default values */ + stm32image_header.magic_number = HEADER_MAGIC; + stm32image_header.header_version[VER_MAJOR_IDX] = version; + stm32image_header.option_flags = HEADER_DEFAULT_OPTION; + stm32image_header.ecdsa_algorithm = 1; + /* used to specify the 2nd-stage barebox address within dram */ + stm32image_header.load_address = loadaddr; + stm32image_header.binary_type = HEADER_TYPE_BAREBOX; + + stm32image_header.image_entry_point = htole32(entrypoint); + stm32image_header.image_length = htole32(pbl_size); + stm32image_header.image_checksum = stm32image_checksum(); +} + +static void stm32image_check_params(void) +{ + off_t ret; + + in_fd = open(infile, O_RDONLY); + if (in_fd < 0) { + fprintf(stderr, "Error: Cannot open %s for reading: %s\n", infile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!pbl_size) { + pbl_size = lseek(in_fd, 0, SEEK_END); + if (pbl_size == (uint32_t)-1) { + fprintf(stderr, "Cannot seek to end\n"); + exit(EXIT_FAILURE); + } + + ret = lseek(in_fd, 0, SEEK_SET); + if (ret == (off_t)-1) { + fprintf(stderr, "Cannot seek to start\n"); + exit(EXIT_FAILURE); + } + } + + out_fd = creat(outfile, 0644); + if (out_fd < 0) { + fprintf(stderr, "Cannot open %s for writing: %s\n", + outfile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (loadaddr < 0xc0000000) { + fprintf(stderr, "Error: loadaddr must be within the DDR memory space\n"); + exit(EXIT_FAILURE); + } +} + +static void copy_fd(int in, int out) +{ + int bs = 4096; + void *buf = malloc(bs); + + if (!buf) + exit(EXIT_FAILURE); + + while (1) { + int now, wr; + + now = read(in, buf, bs); + if (now < 0) { + fprintf(stderr, "read failed with %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!now) + break; + + wr = write(out, buf, now); + if (wr < 0) { + fprintf(stderr, "write failed with %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (wr != now) { + fprintf(stderr, "short write\n"); + exit(EXIT_FAILURE); + } + } + + free(buf); +} + +int main(int argc, char *argv[]) +{ + const char *verbose; + int opt, ret; + off_t pos; + entrypoint = FSBL_ENTRYPOINT; + + while ((opt = getopt(argc, argv, "i:o:a:e:s:v:h")) != -1) { + switch (opt) { + case 'i': + infile = optarg; + break; + case 'o': + outfile = optarg; + break; + case 'a': + loadaddr = strtol(optarg, NULL, 16); + break; + case 'e': + entrypoint = strtol(optarg, NULL, 16); + break; + case 's': + pbl_size = strtol(optarg, NULL, 16); + break; + case 'v': + version = strtol(optarg, NULL, 16); + break; + case 'h': + printf("%s [-i inputfile] [-o outputfile] [-a loadaddr] [-s pblimage size in byte]\n", argv[0]); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "Unknown option: -%c\n", opt); + exit(EXIT_FAILURE); + } + } + + if (!infile) { + fprintf(stderr, "No infile given\n"); + exit(EXIT_FAILURE); + } + + if (!outfile) { + fprintf(stderr, "No outfile given\n"); + exit(EXIT_FAILURE); + } + + stm32image_check_params(); + stm32image_set_header(); + + ret = write(out_fd, (const void *)&stm32image_header, sizeof(struct stm32_header)); + if (ret != 0x100) { + fprintf(stderr, "Error: write on %s: %s\n", outfile, + strerror(errno)); + exit(EXIT_FAILURE); + } + + verbose = getenv("V"); + if (verbose && !strcmp(verbose, "1")) + stm32image_print_header(); + + ret = ftruncate(out_fd, HEADER_LENGTH); + if (ret) { + fprintf(stderr, "Cannot truncate\n"); + exit(EXIT_FAILURE); + } + + pos = lseek(out_fd, HEADER_LENGTH, SEEK_SET); + if (pos == (off_t)-1) { + fprintf(stderr, "Cannot lseek 1\n"); + exit(EXIT_FAILURE); + } + + pos = lseek(in_fd, 0, SEEK_SET); + if (pos == (off_t)-1) { + fprintf(stderr, "Cannot lseek 2\n"); + exit(EXIT_FAILURE); + } + + copy_fd(in_fd, out_fd); + + close(in_fd); + close(out_fd); + + exit(EXIT_SUCCESS); +}