Hi Ariel, Thank you for the patch.
On Thu, Jul 17, 2025 at 10:58, Ariel D'Alessandro <ariel.dalessan...@collabora.com> wrote: > Fastboot currently supports MMC and NAND flash devices. Similarly, > extend the support to SPI flash memories. > > Note that in this initial implementation, partitions on the device are > not supported yet, but raw partitions can be set in U-Boot environment. > > To define a raw partition descriptor, add an environment variable > similar to the MMC case: > > ``` > fastboot_raw_partition_<raw partition name>=<offset> <size> > ``` > > for example: > > ``` > fastboot_raw_partition_boot=0x0 0x1000000 > ``` > > Signed-off-by: Ariel D'Alessandro <ariel.dalessan...@collabora.com> Thanks for the doc updates. This looks great! Reviewed-by: Mattijs Korpershoek <mkorpersh...@kernel.org> > --- > doc/android/fastboot.rst | 16 +- > drivers/fastboot/Kconfig | 6 +- > drivers/fastboot/Makefile | 1 + > drivers/fastboot/fb_command.c | 8 + > drivers/fastboot/fb_getvar.c | 6 + > drivers/fastboot/fb_spi_flash.c | 251 ++++++++++++++++++++++++++++++++ > include/fb_spi_flash.h | 42 ++++++ > 7 files changed, 326 insertions(+), 4 deletions(-) > create mode 100644 drivers/fastboot/fb_spi_flash.c > create mode 100644 include/fb_spi_flash.h > > diff --git a/doc/android/fastboot.rst b/doc/android/fastboot.rst > index 6f92cd28eb1..19e2ee9d407 100644 > --- a/doc/android/fastboot.rst > +++ b/doc/android/fastboot.rst > @@ -32,7 +32,7 @@ The following OEM commands are supported (if enabled): > - ``oem console`` - this dumps U-Boot console record buffer > - ``oem board`` - this executes a custom board function which is defined by > the vendor > > -Support for both eMMC and NAND devices is included. > +Support for eMMC, NAND and SPI flash memory devices is included. > > Client installation > ------------------- > @@ -97,8 +97,9 @@ Raw partition descriptors > ^^^^^^^^^^^^^^^^^^^^^^^^^ > > In cases where no partition table is present, a raw partition descriptor can > be > -defined, specifying the offset, size, and optionally the MMC hardware > partition > -number for a given partition name. > +defined, specifying the memory offset and size. > + > +Currently, this support is available only for eMMC and SPI flash memory > devices. > > This is useful when using fastboot to flash files (e.g. SPL or U-Boot) to a > specific offset in the eMMC boot partition, without having to update the > entire > @@ -106,6 +107,15 @@ boot partition. > > To define a raw partition descriptor, add an environment variable similar > to:: > > + fastboot_raw_partition_<raw partition name>=<offset> <size> > + > +for example:: > + > + fastboot_raw_partition_boot=0x100 0x1f00 > + > +Optionally, in the eMMC case, the hardware partition number can also be > +specified for a given partition name:: > + > fastboot_raw_partition_<raw partition name>=<offset> <size> [mmcpart > <num>] > > for example:: > diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig > index 70207573de2..843171902ae 100644 > --- a/drivers/fastboot/Kconfig > +++ b/drivers/fastboot/Kconfig > @@ -91,7 +91,7 @@ config FASTBOOT_USB_DEV > config FASTBOOT_FLASH > bool "Enable FASTBOOT FLASH command" > default y if ARCH_SUNXI || ARCH_ROCKCHIP > - depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS) > + depends on MMC || (MTD_RAW_NAND && CMD_MTDPARTS) || DM_SPI_FLASH > select IMAGE_SPARSE > help > The fastboot protocol includes a "flash" command for writing > @@ -119,6 +119,10 @@ config FASTBOOT_FLASH_NAND > bool "FASTBOOT on NAND" > depends on MTD_RAW_NAND && CMD_MTDPARTS > > +config FASTBOOT_FLASH_SPI > + bool "FASTBOOT on SPI flash" > + depends on DM_SPI_FLASH > + > endchoice > > config FASTBOOT_FLASH_MMC_DEV > diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile > index 048af5aa823..adedba0bf24 100644 > --- a/drivers/fastboot/Makefile > +++ b/drivers/fastboot/Makefile > @@ -5,3 +5,4 @@ obj-y += fb_getvar.o > obj-y += fb_command.o > obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o > obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o > +obj-$(CONFIG_FASTBOOT_FLASH_SPI) += fb_spi_flash.o > diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c > index 2cdbac50ac4..7697139b622 100644 > --- a/drivers/fastboot/fb_command.c > +++ b/drivers/fastboot/fb_command.c > @@ -10,6 +10,7 @@ > #include <fastboot-internal.h> > #include <fb_mmc.h> > #include <fb_nand.h> > +#include <fb_spi_flash.h> > #include <part.h> > #include <stdlib.h> > #include <vsprintf.h> > @@ -344,6 +345,10 @@ static void __maybe_unused flash(char *cmd_parameter, > char *response) > if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) > fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, > image_size, response); > + > + if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI)) > + fastboot_spi_flash_write(cmd_parameter, fastboot_buf_addr, > + image_size, response); > } > > /** > @@ -362,6 +367,9 @@ static void __maybe_unused erase(char *cmd_parameter, > char *response) > > if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) > fastboot_nand_erase(cmd_parameter, response); > + > + if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI)) > + fastboot_spi_flash_erase(cmd_parameter, response); > } > > /** > diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c > index 9c2ce65a4e5..6775ea397ab 100644 > --- a/drivers/fastboot/fb_getvar.c > +++ b/drivers/fastboot/fb_getvar.c > @@ -8,6 +8,7 @@ > #include <fastboot-internal.h> > #include <fb_mmc.h> > #include <fb_nand.h> > +#include <fb_spi_flash.h> > #include <fs.h> > #include <part.h> > #include <version.h> > @@ -123,6 +124,11 @@ static int getvar_get_part_info(const char *part_name, > char *response, > r = fastboot_nand_get_part_info(part_name, &part_info, > response); > if (r >= 0 && size) > *size = part_info->size; > + } else if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_SPI)) { > + r = fastboot_spi_flash_get_part_info(part_name, &disk_part, > + response); > + if (r >= 0 && size) > + *size = disk_part.size * disk_part.blksz; > } else { > fastboot_fail("this storage is not supported in bootloader", > response); > r = -ENODEV; > diff --git a/drivers/fastboot/fb_spi_flash.c b/drivers/fastboot/fb_spi_flash.c > new file mode 100644 > index 00000000000..691be7c7ef7 > --- /dev/null > +++ b/drivers/fastboot/fb_spi_flash.c > @@ -0,0 +1,251 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright 2025 Collabora Ltd. > + */ > + > +#include <blk.h> > +#include <config.h> > +#include <env.h> > +#include <fastboot.h> > +#include <image-sparse.h> > +#include <spi.h> > +#include <spi_flash.h> > +#include <dm.h> > +#include <dm/device-internal.h> > + > +static struct spi_flash *flash; > + > +__weak int board_fastboot_spi_flash_write_setup(void) > +{ > + return 0; > +} > + > +__weak int board_fastboot_spi_flash_erase_setup(void) > +{ > + return 0; > +} > + > +static int raw_part_get_info_by_name(const char *name, > + struct disk_partition *part_info) > +{ > + /* strlen("fastboot_raw_partition_") + PART_NAME_LEN + 1 */ > + char env_desc_name[23 + PART_NAME_LEN + 1]; > + char *raw_part_desc; > + const char *argv[2]; > + const char **parg = argv; > + > + /* check for raw partition descriptor */ > + strcpy(env_desc_name, "fastboot_raw_partition_"); > + strlcat(env_desc_name, name, sizeof(env_desc_name)); > + raw_part_desc = strdup(env_get(env_desc_name)); > + if (!raw_part_desc) > + return -ENODEV; > + > + /* parse partition descriptor: <start> <size> */ > + for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { > + *parg = strsep(&raw_part_desc, " "); > + if (!*parg) { > + pr_err("Invalid number of arguments.\n"); > + return -ENODEV; > + } > + } > + > + part_info->start = simple_strtoul(argv[0], NULL, 0); > + part_info->size = simple_strtoul(argv[1], NULL, 0); > + strlcpy((char *)part_info->name, name, PART_NAME_LEN); > + > + return 0; > +} > + > +static int fastboot_spi_flash_probe(void) > +{ > + unsigned int bus = CONFIG_SF_DEFAULT_BUS; > + unsigned int cs = CONFIG_SF_DEFAULT_CS; > + struct udevice *new, *bus_dev; > + int ret; > + > + /* Remove the old device, otherwise probe will just be a nop */ > + ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); > + if (!ret) > + device_remove(new, DM_REMOVE_NORMAL); > + > + spi_flash_probe_bus_cs(bus, cs, &new); > + flash = dev_get_uclass_priv(new); > + if (!flash) { > + printf("Failed to initialize SPI flash at %u:%u (error %d)\n", > + bus, cs, ret); > + return 1; > + } > + > + return 0; > +} > + > +static int fastboot_spi_flash_unlock(struct spi_flash *flash, > + struct disk_partition *part_info) > +{ > + int ret = spi_flash_protect(flash, part_info->start, part_info->size, > + false); > + > + if (ret && ret != -EOPNOTSUPP) { > + printf("Failed to unlock SPI flash (%d)\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static lbaint_t fb_spi_flash_sparse_write(struct sparse_storage *info, > + lbaint_t blk, lbaint_t blkcnt, > + const void *buffer) > +{ > + size_t len = blkcnt * info->blksz; > + u32 offset = blk * info->blksz; > + int ret; > + > + ret = spi_flash_erase(flash, offset, ROUND(len, flash->erase_size)); > + if (ret < 0) { > + printf("Failed to erase sparse chunk (%d)\n", ret); > + return ret; > + } > + > + ret = spi_flash_write(flash, offset, len, buffer); > + if (ret < 0) { > + printf("Failed to write sparse chunk (%d)\n", ret); > + return ret; > + } > + > + return blkcnt; > +} > + > +static lbaint_t fb_spi_flash_sparse_reserve(struct sparse_storage *info, > + lbaint_t blk, lbaint_t blkcnt) > +{ > + return blkcnt; > +} > + > +/** > + * fastboot_spi_flash_get_part_info() - Lookup SPI partition by name > + * > + * @part_name: Named device to lookup > + * @part_info: Pointer to returned struct disk_partition > + * @response: Pointer to fastboot response buffer > + * Return: 0 if OK, -ENOENT if no partition name was given, -ENODEV on > invalid > + * raw partition descriptor > + */ > +int fastboot_spi_flash_get_part_info(const char *part_name, > + struct disk_partition *part_info, > + char *response) > +{ > + int ret; > + > + if (!part_name || !strcmp(part_name, "")) { > + fastboot_fail("partition not given", response); > + return -ENOENT; > + } > + > + /* TODO: Support partitions on the device */ > + ret = raw_part_get_info_by_name(part_name, part_info); > + if (ret < 0) > + fastboot_fail("invalid partition or device", response); > + > + return ret; > +} > + > +/** > + * fastboot_spi_flash_write() - Write image to SPI for fastboot > + * > + * @cmd: Named device to write image to > + * @download_buffer: Pointer to image data > + * @download_bytes: Size of image data > + * @response: Pointer to fastboot response buffer > + */ > +void fastboot_spi_flash_write(const char *cmd, void *download_buffer, > + u32 download_bytes, char *response) > +{ > + struct disk_partition part_info; > + int ret; > + > + if (fastboot_spi_flash_get_part_info(cmd, &part_info, response)) > + return; > + > + if (fastboot_spi_flash_probe()) > + return; > + > + if (board_fastboot_spi_flash_write_setup()) > + return; > + > + if (fastboot_spi_flash_unlock(flash, &part_info)) > + return; > + > + if (is_sparse_image(download_buffer)) { > + struct sparse_storage sparse; > + > + sparse.blksz = flash->sector_size; > + sparse.start = part_info.start / sparse.blksz; > + sparse.size = part_info.size / sparse.blksz; > + sparse.write = fb_spi_flash_sparse_write; > + sparse.reserve = fb_spi_flash_sparse_reserve; > + sparse.mssg = fastboot_fail; > + > + printf("Flashing sparse image at offset " LBAFU "\n", > + sparse.start); > + > + ret = write_sparse_image(&sparse, cmd, download_buffer, > + response); > + } else { > + printf("Flashing raw image at offset " LBAFU "\n", > + part_info.start); > + > + ret = spi_flash_erase(flash, part_info.start, > + ROUND(download_bytes, flash->erase_size)); > + if (ret < 0) { > + printf("Failed to erase raw image (%d)\n", ret); > + return; > + } > + ret = spi_flash_write(flash, part_info.start, download_bytes, > + download_buffer); > + if (ret < 0) { > + printf("Failed to write raw image (%d)\n", ret); > + return; > + } > + printf("........ wrote %u bytes\n", download_bytes); > + } > + > + if (ret) > + fastboot_fail("error writing the image", response); > + else > + fastboot_okay(NULL, response); > +} > + > +/** > + * fastboot_spi_flash_erase() - Erase SPI for fastboot > + * > + * @cmd: Named device to erase > + * @response: Pointer to fastboot response buffer > + */ > +void fastboot_spi_flash_erase(const char *cmd, char *response) > +{ > + struct disk_partition part_info; > + int ret; > + > + if (fastboot_spi_flash_get_part_info(cmd, &part_info, response)) > + return; > + > + if (fastboot_spi_flash_probe()) > + return; > + > + if (board_fastboot_spi_flash_erase_setup()) > + return; > + > + if (fastboot_spi_flash_unlock(flash, &part_info)) > + return; > + > + ret = spi_flash_erase(flash, part_info.start, part_info.size); > + if (ret < 0) { > + pr_err("failed erasing from SPI flash"); > + fastboot_fail("failed erasing from SPI flash", response); > + return; > + } > + > + fastboot_okay(NULL, response); > +} > diff --git a/include/fb_spi_flash.h b/include/fb_spi_flash.h > new file mode 100644 > index 00000000000..904654748a4 > --- /dev/null > +++ b/include/fb_spi_flash.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright 2025 Collabora Ltd. > + */ > + > +#ifndef _FB_SPI_FLASH_H_ > +#define _FB_SPI_FLASH_H_ > + > +#include <part.h> > + > +/** > + * fastboot_spi_flash_get_part_info() - Lookup SPI flash partition by name > + * > + * @part_name: Named device to lookup > + * @part_info: Pointer to returned struct disk_partition > + * @response: Pointer to fastboot response buffer > + * Return: 0 if OK, -ENOENT if no partition name was given, -ENODEV on > invalid > + * raw partition descriptor > + */ > +int fastboot_spi_flash_get_part_info(const char *part_name, > + struct disk_partition *part_info, > + char *response); > + > +/** > + * fastboot_spi_flash_write() - Write image to SPI flash for fastboot > + * > + * @cmd: Named device to write image to > + * @download_buffer: Pointer to image data > + * @download_bytes: Size of image data > + * @response: Pointer to fastboot response buffer > + */ > +void fastboot_spi_flash_write(const char *cmd, void *download_buffer, > + u32 download_bytes, char *response); > + > +/** > + * fastboot_spi_flash_erase() - Erase SPI flash for fastboot > + * > + * @cmd: Named device to erase > + * @response: Pointer to fastboot response buffer > + */ > +void fastboot_spi_flash_erase(const char *cmd, char *response); > +#endif > -- > 2.50.0