[PATCH v4 0/5] Move qfw to DM, add Arm support

This series moves the QFW driver into a uclass, UCLASS_QFW, and splits the driver into qfw_pio and qfw_mmio. A sandbox driver is also added, and a DM unit test against that driver.
On x86 a U_BOOT_DRVINFO is used to configure the qfw_pio (with compiled- in constants used for PIO ports), otherwise we configure qfw_mmio from device tree if present.
Version 4 of this series adds a test for the qfw command, exercising it on QEMU in test/py/tests/test_qfw.py. The introductory "move QFW to DM" commit has been rolled into the uclass split one. Documentation is added.
To view the changes online, see: https://git.src.kameliya.ee/~kameliya/u-boot/log/qfw-priv
Asherah Connor (5): arm: x86: qemu: move qfw to DM uclass, add Arm support arm: x86: qemu: unify qfw driver functions as qfw_ qemu: add sandbox driver and tests test: qemu: add simple test for cmd_qfw qemu: add documentation to qfw.h
arch/arm/Kconfig | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------ arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 56 ++++---- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++ drivers/misc/Makefile | 7 +- drivers/misc/qfw.c | 242 ++++++++++++---------------------- drivers/misc/qfw_mmio.c | 119 +++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++ drivers/misc/qfw_sandbox.c | 129 ++++++++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 145 ++++++++++++++++---- test/dm/Makefile | 1 + test/dm/qfw.c | 42 ++++++ test/py/tests/test_qfw.py | 21 +++ 19 files changed, 752 insertions(+), 263 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c create mode 100644 drivers/misc/qfw_sandbox.c create mode 100644 test/dm/qfw.c create mode 100644 test/py/tests/test_qfw.py

Updates the QFW driver to use the driver model, splitting the driver into qfw_pio and qfw_mmio (for non-x86) in their own uclass.
Signed-off-by: Asherah Connor ashe@kivikakk.ee ---
Changes in v4: - PIO definitions are now #defines - qfw_*_plat structs are no longer in header files - Remove yield/pause in DMA wait loop - Change struct udevice *qfw_get_dev(void) to int qfw_get_dev(struct udevice **)
arch/arm/Kconfig | 1 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------- arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 52 ++++----- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++++ drivers/misc/Makefile | 6 +- drivers/misc/qfw.c | 238 ++++++++++++++------------------------- drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 61 ++++++---- 13 files changed, 466 insertions(+), 255 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d51abbeaf0..cd01dc458a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -937,6 +937,7 @@ config ARCH_QEMU imply DM_RNG imply DM_RTC imply RTC_PL031 + imply CMD_QFW
config ARCH_RMOBILE bool "Renesas ARM SoCs" diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index 9ce86b379c..c78e374644 100644 --- a/arch/x86/cpu/qemu/cpu.c +++ b/arch/x86/cpu/qemu/cpu.c @@ -22,7 +22,14 @@ int cpu_qemu_get_desc(const struct udevice *dev, char *buf, int size)
static int cpu_qemu_get_count(const struct udevice *dev) { - return qemu_fwcfg_online_cpus(); + int ret; + struct udevice *qfw_dev; + + ret = qfw_get_dev(&qfw_dev); + if (ret) + return ret; + + return qemu_fwcfg_online_cpus(qfw_dev); }
static const struct cpu_ops cpu_qemu_ops = { diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 044a429c13..605f51e1b8 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -8,6 +8,7 @@ #include <init.h> #include <pci.h> #include <qfw.h> +#include <dm/platdata.h> #include <asm/irq.h> #include <asm/post.h> #include <asm/processor.h> @@ -18,46 +19,10 @@ static bool i440fx;
#ifdef CONFIG_QFW
-/* on x86, the qfw registers are all IO ports */ -#define FW_CONTROL_PORT 0x510 -#define FW_DATA_PORT 0x511 -#define FW_DMA_PORT_LOW 0x514 -#define FW_DMA_PORT_HIGH 0x518 - -static void qemu_x86_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) -{ - uint32_t i = 0; - uint8_t *data = address; - - /* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 - * - * Note: on platform where the control register is IO port, the - * endianness is little endian. - */ - if (entry != FW_CFG_INVALID) - outw(cpu_to_le16(entry), FW_CONTROL_PORT); - - /* the endianness of data register is string-preserving */ - while (size--) - data[i++] = inb(FW_DATA_PORT); -} - -static void qemu_x86_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma) -{ - /* the DMA address register is big endian */ - outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH); - - while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR) - __asm__ __volatile__ ("pause"); -} - -static struct fw_cfg_arch_ops fwcfg_x86_ops = { - .arch_read_pio = qemu_x86_fwcfg_read_entry_pio, - .arch_read_dma = qemu_x86_fwcfg_read_entry_dma +U_BOOT_DRVINFO(x86_qfw_pio) = { + .name = "qfw_pio", }; + #endif
static void enable_pm_piix(void) @@ -132,10 +97,6 @@ static void qemu_chipset_init(void)
enable_pm_ich9(); } - -#ifdef CONFIG_QFW - qemu_fwcfg_init(&fwcfg_x86_ops); -#endif }
#if !CONFIG_IS_ENABLED(SPL_X86_32BIT_INIT) diff --git a/arch/x86/cpu/qfw_cpu.c b/arch/x86/cpu/qfw_cpu.c index b959eaddde..9700908064 100644 --- a/arch/x86/cpu/qfw_cpu.c +++ b/arch/x86/cpu/qfw_cpu.c @@ -18,7 +18,7 @@ int qemu_cpu_fixup(void) int cpu_num; int cpu_online; struct uclass *uc; - struct udevice *dev, *pdev; + struct udevice *dev, *pdev, *qfwdev; struct cpu_plat *plat; char *cpu;
@@ -39,6 +39,13 @@ int qemu_cpu_fixup(void) return -ENODEV; }
+ /* get qfw dev */ + ret = qfw_get_dev(&qfwdev); + if (ret) { + printf("unable to find qfw device\n"); + return ret; + } + /* calculate cpus that are already bound */ cpu_num = 0; for (uclass_find_first_device(UCLASS_CPU, &dev); @@ -48,7 +55,7 @@ int qemu_cpu_fixup(void) }
/* get actual cpu number */ - cpu_online = qemu_fwcfg_online_cpus(); + cpu_online = qemu_fwcfg_online_cpus(qfwdev); if (cpu_online < 0) { printf("unable to get online cpu number: %d\n", cpu_online); return cpu_online; diff --git a/cmd/qfw.c b/cmd/qfw.c index bb571487f0..87af408a8d 100644 --- a/cmd/qfw.c +++ b/cmd/qfw.c @@ -8,19 +8,22 @@ #include <env.h> #include <errno.h> #include <qfw.h> +#include <dm.h> + +static struct udevice *qfw_dev;
/* * This function prepares kernel for zboot. It loads kernel data * to 'load_addr', initrd to 'initrd_addr' and kernel command * line using qemu fw_cfg interface. */ -static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) +static int qemu_fwcfg_cmd_setup_kernel(void *load_addr, void *initrd_addr) { char *data_addr; uint32_t setup_size, kernel_size, cmdline_size, initrd_size;
- qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); - qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); + qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size); + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size);
if (setup_size == 0 || kernel_size == 0) { printf("warning: no kernel available\n"); @@ -28,28 +31,28 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) }
data_addr = load_addr; - qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, - le32_to_cpu(setup_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, + le32_to_cpu(setup_size), data_addr); data_addr += le32_to_cpu(setup_size);
- qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, - le32_to_cpu(kernel_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, + le32_to_cpu(kernel_size), data_addr); data_addr += le32_to_cpu(kernel_size);
data_addr = initrd_addr; - qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); + qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size); if (initrd_size == 0) { printf("warning: no initrd available\n"); } else { - qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, - le32_to_cpu(initrd_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, + le32_to_cpu(initrd_size), data_addr); data_addr += le32_to_cpu(initrd_size); }
- qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); if (cmdline_size) { - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, - le32_to_cpu(cmdline_size), data_addr); + qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, + le32_to_cpu(cmdline_size), data_addr); /* * if kernel cmdline only contains '\0', (e.g. no -append * when invoking qemu), do not update bootargs @@ -72,19 +75,18 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) return 0; }
-static int qemu_fwcfg_list_firmware(void) +static int qemu_fwcfg_cmd_list_firmware(void) { int ret; struct fw_cfg_file_iter iter; struct fw_file *file;
/* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(); + ret = qemu_fwcfg_read_firmware_list(qfw_dev); if (ret) return ret;
- - for (file = qemu_fwcfg_file_iter_init(&iter); + for (file = qemu_fwcfg_file_iter_init(qfw_dev, &iter); !qemu_fwcfg_file_iter_end(&iter); file = qemu_fwcfg_file_iter_next(&iter)) { printf("%-56s\n", file->cfg.name); @@ -96,7 +98,7 @@ static int qemu_fwcfg_list_firmware(void) static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - if (qemu_fwcfg_list_firmware() < 0) + if (qemu_fwcfg_cmd_list_firmware() < 0) return CMD_RET_FAILURE;
return 0; @@ -105,14 +107,7 @@ static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - int ret = qemu_fwcfg_online_cpus(); - if (ret < 0) { - printf("QEMU fw_cfg interface not found\n"); - return CMD_RET_FAILURE; - } - - printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus()); - + printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus(qfw_dev)); return 0; }
@@ -153,7 +148,7 @@ static int qemu_fwcfg_do_load(struct cmd_tbl *cmdtp, int flag, return CMD_RET_FAILURE; }
- return qemu_fwcfg_setup_kernel(load_addr, initrd_addr); + return qemu_fwcfg_cmd_setup_kernel(load_addr, initrd_addr); }
static struct cmd_tbl fwcfg_commands[] = { @@ -168,7 +163,8 @@ static int do_qemu_fw(struct cmd_tbl *cmdtp, int flag, int argc, int ret; struct cmd_tbl *fwcfg_cmd;
- if (!qemu_fwcfg_present()) { + ret = qfw_get_dev(&qfw_dev); + if (ret) { printf("QEMU fw_cfg interface not found\n"); return CMD_RET_USAGE; } diff --git a/common/Makefile b/common/Makefile index daeea67cf2..f174a06c33 100644 --- a/common/Makefile +++ b/common/Makefile @@ -137,3 +137,5 @@ obj-$(CONFIG_CMD_LOADB) += xyzModem.o obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o
obj-$(CONFIG_AVB_VERIFY) += avb_verify.o + +obj-$(CONFIG_QFW) += qfw.o diff --git a/common/qfw.c b/common/qfw.c new file mode 100644 index 0000000000..c0ffa20b74 --- /dev/null +++ b/common/qfw.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#include <dm.h> +#include <dm/uclass.h> +#include <qfw.h> +#include <stdlib.h> + +int qfw_get_dev(struct udevice **devp) +{ + return uclass_first_device(UCLASS_QFW, devp); +} + +int qemu_fwcfg_online_cpus(struct udevice *dev) +{ + u16 nb_cpus; + + qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus); + + return le16_to_cpu(nb_cpus); +} + +int qemu_fwcfg_read_firmware_list(struct udevice *dev) +{ + int i; + u32 count; + struct fw_file *file; + struct list_head *entry; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + /* don't read it twice */ + if (!list_empty(&qdev->fw_list)) + return 0; + + qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count); + if (!count) + return 0; + + count = be32_to_cpu(count); + for (i = 0; i < count; i++) { + file = malloc(sizeof(*file)); + if (!file) { + printf("error: allocating resource\n"); + goto err; + } + qfw_read_entry(dev, FW_CFG_INVALID, + sizeof(struct fw_cfg_file), &file->cfg); + file->addr = 0; + list_add_tail(&file->list, &qdev->fw_list); + } + + return 0; + +err: + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + free(file); + } + + return -ENOMEM; +} + +struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name) +{ + struct list_head *entry; + struct fw_file *file; + + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + list_for_each(entry, &qdev->fw_list) { + file = list_entry(entry, struct fw_file, list); + if (!strcmp(file->cfg.name, name)) + return file; + } + + return NULL; +} + +struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter) +{ + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + + iter->entry = qdev->fw_list.next; + iter->end = &qdev->fw_list; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) +{ + iter->entry = ((struct list_head *)iter->entry)->next; + return list_entry((struct list_head *)iter->entry, + struct fw_file, list); +} + +bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) +{ + return iter->entry == iter->end; +} + diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d737203704..2988289ea3 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,7 +55,11 @@ obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o -obj-$(CONFIG_QFW) += qfw.o +ifdef CONFIG_QFW +obj-y += qfw.o +obj-$(CONFIG_X86) += qfw_pio.o +obj-$(CONFIG_ARM) += qfw_mmio.o +endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index f6eb6583ed..7ad8f0979b 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -1,25 +1,22 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee */
+#define LOG_CATEGORY UCLASS_QFW + #include <common.h> #include <command.h> #include <errno.h> #include <log.h> #include <malloc.h> #include <qfw.h> -#include <asm/io.h> +#include <dm.h> +#include <misc.h> #ifdef CONFIG_GENERATE_ACPI_TABLE #include <asm/tables.h> #endif -#include <linux/list.h> - -static bool fwcfg_present; -static bool fwcfg_dma_present; -static struct fw_cfg_arch_ops *fwcfg_arch_ops; - -static LIST_HEAD(fw_list);
#ifdef CONFIG_GENERATE_ACPI_TABLE /* @@ -32,7 +29,8 @@ static LIST_HEAD(fw_list); * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) +static int bios_linker_allocate(struct udevice *dev, + struct bios_linker_entry *entry, ulong *addr) { uint32_t size, align; struct fw_file *file; @@ -45,7 +43,7 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) return -EINVAL; }
- file = qemu_fwcfg_find_file(entry->alloc.file); + file = qemu_fwcfg_find_file(dev, entry->alloc.file); if (!file) { printf("error: can't find file %s\n", entry->alloc.file); return -ENOENT; @@ -75,8 +73,8 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
- qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, (void *)aligned_addr); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, + (void *)aligned_addr); file->addr = aligned_addr;
/* adjust address for low memory allocation */ @@ -94,16 +92,17 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) * ACPI tables * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_pointer(struct bios_linker_entry *entry) +static int bios_linker_add_pointer(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *dest, *src; uint32_t offset = le32_to_cpu(entry->pointer.offset); uint64_t pointer = 0;
- dest = qemu_fwcfg_find_file(entry->pointer.dest_file); + dest = qemu_fwcfg_find_file(dev, entry->pointer.dest_file); if (!dest || !dest->addr) return -ENOENT; - src = qemu_fwcfg_find_file(entry->pointer.src_file); + src = qemu_fwcfg_find_file(dev, entry->pointer.src_file); if (!src || !src->addr) return -ENOENT;
@@ -127,13 +126,14 @@ static int bios_linker_add_pointer(struct bios_linker_entry *entry) * checksums * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_checksum(struct bios_linker_entry *entry) +static int bios_linker_add_checksum(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *file; uint8_t *data, cksum = 0; uint8_t *cksum_start;
- file = qemu_fwcfg_find_file(entry->cksum.file); + file = qemu_fwcfg_find_file(dev, entry->cksum.file); if (!file || !file->addr) return -ENOENT;
@@ -149,20 +149,27 @@ static int bios_linker_add_checksum(struct bios_linker_entry *entry) /* This function loads and patches ACPI tables provided by QEMU */ ulong write_acpi_tables(ulong addr) { - int i, ret = 0; + int i, ret; struct fw_file *file; struct bios_linker_entry *table_loader; struct bios_linker_entry *entry; uint32_t size; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return addr; + }
/* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(); + ret = qemu_fwcfg_read_firmware_list(dev); if (ret) { printf("error: can't read firmware file list\n"); return addr; }
- file = qemu_fwcfg_find_file("etc/table-loader"); + file = qemu_fwcfg_find_file(dev, "etc/table-loader"); if (!file) { printf("error: can't find etc/table-loader\n"); return addr; @@ -180,24 +187,23 @@ ulong write_acpi_tables(ulong addr) return addr; }
- qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, table_loader); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
for (i = 0; i < (size / sizeof(*entry)); i++) { entry = table_loader + i; switch (le32_to_cpu(entry->command)) { case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(entry, &addr); + ret = bios_linker_allocate(dev, entry, &addr); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: - ret = bios_linker_add_pointer(entry); + ret = bios_linker_add_pointer(dev, entry); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: - ret = bios_linker_add_checksum(entry); + ret = bios_linker_add_checksum(dev, entry); if (ret) goto out; break; @@ -209,7 +215,7 @@ ulong write_acpi_tables(ulong addr) out: if (ret) { struct fw_cfg_file_iter iter; - for (file = qemu_fwcfg_file_iter_init(&iter); + for (file = qemu_fwcfg_file_iter_init(dev, &iter); !qemu_fwcfg_file_iter_end(&iter); file = qemu_fwcfg_file_iter_next(&iter)) { if (file->addr) { @@ -225,170 +231,90 @@ out:
ulong acpi_get_rsdp_addr(void) { + int ret; struct fw_file *file; + struct udevice *dev;
- file = qemu_fwcfg_find_file("etc/acpi/rsdp"); + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return 0; + } + + file = qemu_fwcfg_find_file(dev, "etc/acpi/rsdp"); return file->addr; } #endif
-/* Read configuration item using fw_cfg PIO interface */ -static void qemu_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", - entry, size, address); + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev);
- return fwcfg_arch_ops->arch_read_pio(entry, size, address); + debug("%s: entry 0x%x, size %u address %p\n", __func__, entry, size, + address); + + ops->read_entry_io(qdev->dev, entry, size, address); }
-/* Read configuration item using fw_cfg DMA interface */ -static void qemu_fwcfg_read_entry_dma(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - struct fw_cfg_dma_access dma; + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev);
- dma.length = cpu_to_be32(size); - dma.address = cpu_to_be64((uintptr_t)address); - dma.control = cpu_to_be32(FW_CFG_DMA_READ); + struct qfw_dma dma = { + .length = cpu_to_be32(size), + .address = cpu_to_be64((uintptr_t)address), + .control = cpu_to_be32(FW_CFG_DMA_READ), + };
/* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 */ if (entry != FW_CFG_INVALID) dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
- barrier(); - - debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n", + debug("%s: entry 0x%x, size %u address %p, control 0x%x\n", __func__, entry, size, address, be32_to_cpu(dma.control));
- fwcfg_arch_ops->arch_read_dma(&dma); -} + barrier();
-bool qemu_fwcfg_present(void) -{ - return fwcfg_present; + ops->read_entry_dma(qdev->dev, &dma); }
-bool qemu_fwcfg_dma_present(void) +void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address) { - return fwcfg_dma_present; -} + struct qfw_dev *qdev = dev_get_uclass_priv(dev);
-void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address) -{ - if (fwcfg_dma_present) - qemu_fwcfg_read_entry_dma(entry, length, address); + if (qdev->dma_present) + qfw_read_entry_dma(qdev, entry, length, address); else - qemu_fwcfg_read_entry_pio(entry, length, address); + qfw_read_entry_io(qdev, entry, length, address); }
-int qemu_fwcfg_online_cpus(void) +int qfw_register(struct udevice *dev) { - uint16_t nb_cpus; + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + u32 qemu, dma_enabled;
- if (!fwcfg_present) + qdev->dev = dev; + INIT_LIST_HEAD(&qdev->fw_list); + + qfw_read_entry_io(qdev, FW_CFG_SIGNATURE, 4, &qemu); + if (be32_to_cpu(qemu) != QEMU_FW_CFG_SIGNATURE) return -ENODEV;
- qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); - - return le16_to_cpu(nb_cpus); -} - -int qemu_fwcfg_read_firmware_list(void) -{ - int i; - uint32_t count; - struct fw_file *file; - struct list_head *entry; - - /* don't read it twice */ - if (!list_empty(&fw_list)) - return 0; - - qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); - if (!count) - return 0; - - count = be32_to_cpu(count); - for (i = 0; i < count; i++) { - file = malloc(sizeof(*file)); - if (!file) { - printf("error: allocating resource\n"); - goto err; - } - qemu_fwcfg_read_entry(FW_CFG_INVALID, - sizeof(struct fw_cfg_file), &file->cfg); - file->addr = 0; - list_add_tail(&file->list, &fw_list); - } + qfw_read_entry_io(qdev, FW_CFG_ID, 1, &dma_enabled); + if (dma_enabled & FW_CFG_DMA_ENABLED) + qdev->dma_present = true;
return 0; - -err: - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - free(file); - } - - return -ENOMEM; -} - -struct fw_file *qemu_fwcfg_find_file(const char *name) -{ - struct list_head *entry; - struct fw_file *file; - - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - if (!strcmp(file->cfg.name, name)) - return file; - } - - return NULL; -} - -struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter) -{ - iter->entry = fw_list.next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); }
-struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) -{ - iter->entry = ((struct list_head *)iter->entry)->next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); -} - -bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) -{ - return iter->entry == &fw_list; -} - -void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops) -{ - uint32_t qemu; - uint32_t dma_enabled; +UCLASS_DRIVER(qfw) = { + .id = UCLASS_QFW, + .name = "qfw", + .per_device_auto = sizeof(struct qfw_dev), +};
- fwcfg_present = false; - fwcfg_dma_present = false; - fwcfg_arch_ops = NULL; - - if (!ops || !ops->arch_read_pio || !ops->arch_read_dma) - return; - fwcfg_arch_ops = ops; - - qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); - if (be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE) - fwcfg_present = true; - - if (fwcfg_present) { - qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); - if (dma_enabled & FW_CFG_DMA_ENABLED) - fwcfg_dma_present = true; - } -} diff --git a/drivers/misc/qfw_mmio.c b/drivers/misc/qfw_mmio.c new file mode 100644 index 0000000000..f397384054 --- /dev/null +++ b/drivers/misc/qfw_mmio.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MMIO interface for QFW + * + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/types.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device.h> +#include <qfw.h> + +struct qfw_mmio { + /* + * Each access to the 64-bit data register can be 8/16/32/64 bits wide. + */ + union { + u8 data8; + u16 data16; + u32 data32; + u64 data64; + }; + u16 selector; + u8 padding[6]; + u64 dma; +}; + +struct qfw_mmio_plat { + volatile struct qfw_mmio *mmio; +}; + +static void qfw_mmio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is MMIO, the register + * is big endian. + */ + if (entry != FW_CFG_INVALID) + plat->mmio->selector = cpu_to_be16(entry); + + /* the endianness of data register is string-preserving */ + while (size >= 8) { + *(u64 *)address = plat->mmio->data64; + address += 8; + size -= 8; + } + while (size >= 4) { + *(u32 *)address = plat->mmio->data32; + address += 4; + size -= 4; + } + while (size >= 2) { + *(u16 *)address = plat->mmio->data16; + address += 2; + size -= 2; + } + while (size >= 1) { + *(u8 *)address = plat->mmio->data8; + address += 1; + size -= 1; + } +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_mmio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* the DMA address register is big-endian */ + plat->mmio->dma = cpu_to_be64((uintptr_t)dma); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_mmio_of_to_plat(struct udevice *dev) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + plat->mmio = map_physmem(dev_read_addr(dev), + sizeof(struct qfw_mmio), + MAP_NOCACHE); + + return 0; +} + +static int qfw_mmio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_mmio_ops = { + .read_entry_io = qfw_mmio_read_entry_io, + .read_entry_dma = qfw_mmio_read_entry_dma, +}; + +static const struct udevice_id qfw_mmio_ids[] = { + { .compatible = "qemu,fw-cfg-mmio" }, + {} +}; + +U_BOOT_DRIVER(qfw_mmio) = { + .name = "qfw_mmio", + .id = UCLASS_QFW, + .of_match = qfw_mmio_ids, + .plat_auto = sizeof(struct qfw_mmio_plat), + .of_to_plat = qfw_mmio_of_to_plat, + .probe = qfw_mmio_probe, + .ops = &qfw_mmio_ops, +}; diff --git a/drivers/misc/qfw_pio.c b/drivers/misc/qfw_pio.c new file mode 100644 index 0000000000..e2f628d338 --- /dev/null +++ b/drivers/misc/qfw_pio.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PIO interface for QFW + * + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/io.h> +#include <dm/device.h> +#include <qfw.h> + +/* + * PIO ports are correct for x86, which appears to be the only arch that uses + * PIO. + */ +#define FW_CONTROL_PORT 0x510 +#define FW_DATA_PORT 0x511 +#define FW_DMA_PORT_LOW 0x514 +#define FW_DMA_PORT_HIGH 0x518 + +static void qfw_pio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is IO port, the + * endianness is little endian. + */ + if (entry != FW_CFG_INVALID) + outw(cpu_to_le16(entry), FW_CONTROL_PORT); + + /* the endianness of data register is string-preserving */ + u32 i = 0; + u8 *data = address; + + while (size--) + data[i++] = inb(FW_DATA_PORT); +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_pio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + /* the DMA address register is big-endian */ + outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_pio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_pio_ops = { + .read_entry_io = qfw_pio_read_entry_io, + .read_entry_dma = qfw_pio_read_entry_dma, +}; + +U_BOOT_DRIVER(qfw_pio) = { + .name = "qfw_pio", + .id = UCLASS_QFW, + .probe = qfw_pio_probe, + .ops = &qfw_pio_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d75de368c5..d800f679d5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -90,6 +90,7 @@ enum uclass_id { UCLASS_POWER_DOMAIN, /* (SoC) Power domains */ UCLASS_PWM, /* Pulse-width modulator */ UCLASS_PWRSEQ, /* Power sequence device */ + UCLASS_QFW, /* QEMU firmware config device */ UCLASS_RAM, /* RAM controller */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_REMOTEPROC, /* Remote Processor device */ diff --git a/include/qfw.h b/include/qfw.h index cea8e11d44..7b389dec25 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -82,19 +82,7 @@ struct fw_file { };
struct fw_cfg_file_iter { - struct list_head *entry; /* structure to iterate file list */ -}; - -struct fw_cfg_dma_access { - __be32 control; - __be32 length; - __be64 address; -}; - -struct fw_cfg_arch_ops { - void (*arch_read_pio)(uint16_t selector, uint32_t size, - void *address); - void (*arch_read_dma)(struct fw_cfg_dma_access *dma); + struct list_head *entry, *end; /* structures to iterate file list */ };
struct bios_linker_entry { @@ -146,32 +134,57 @@ struct bios_linker_entry { }; } __packed;
+struct qfw_dma { + __be32 control; + __be32 length; + __be64 address; +}; + +struct qfw_dev { + struct udevice *dev; + bool dma_present; + struct list_head fw_list; +}; + +struct dm_qfw_ops { + void (*read_entry_io)(struct udevice *dev, u16 entry, u32 size, + void *address); + void (*read_entry_dma)(struct udevice *dev, struct qfw_dma *dma); +}; + +#define dm_qfw_get_ops(dev) \ + ((struct dm_qfw_ops *)(dev)->driver->ops) + +int qfw_register(struct udevice *dev); + +struct udevice; + /** - * Initialize QEMU fw_cfg interface + * Get QEMU firmware config device. * - * @ops: arch specific read operations + * @devp Pointer to be filled with address of the qfw device. + * + * @return 0 on success, -ENODEV if the device is not available. */ -void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops); +int qfw_get_dev(struct udevice **devp);
-void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address); -int qemu_fwcfg_read_firmware_list(void); -struct fw_file *qemu_fwcfg_find_file(const char *name); +void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address); +int qemu_fwcfg_read_firmware_list(struct udevice *dev); +struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name);
/** * Get system cpu number * * @return: cpu number in system */ -int qemu_fwcfg_online_cpus(void); +int qemu_fwcfg_online_cpus(struct udevice *dev);
/* helper functions to iterate firmware file list */ -struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter); +struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter); struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter); bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter);
-bool qemu_fwcfg_present(void); -bool qemu_fwcfg_dma_present(void); - /** * qemu_cpu_fixup() - Fix up the CPUs for QEMU *

On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
Updates the QFW driver to use the driver model, splitting the driver into qfw_pio and qfw_mmio (for non-x86) in their own uclass.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
Changes in v4:
- PIO definitions are now #defines
- qfw_*_plat structs are no longer in header files
- Remove yield/pause in DMA wait loop
- Change struct udevice *qfw_get_dev(void) to int qfw_get_dev(struct udevice **)
arch/arm/Kconfig | 1 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------- arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 52 ++++----- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++++ drivers/misc/Makefile | 6 +- drivers/misc/qfw.c | 238 ++++++++++++++------------------------- drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 61 ++++++---- 13 files changed, 466 insertions(+), 255 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c
Reviewed-by: Simon Glass sjg@chromium.org

Hi Asherah,
On Wed, Feb 24, 2021 at 11:23 AM Asherah Connor ashe@kivikakk.ee wrote:
Updates the QFW driver to use the driver model, splitting the driver into qfw_pio and qfw_mmio (for non-x86) in their own uclass.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
Changes in v4:
- PIO definitions are now #defines
- qfw_*_plat structs are no longer in header files
- Remove yield/pause in DMA wait loop
- Change struct udevice *qfw_get_dev(void) to int qfw_get_dev(struct udevice **)
arch/arm/Kconfig | 1 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------- arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 52 ++++----- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++++ drivers/misc/Makefile | 6 +- drivers/misc/qfw.c | 238 ++++++++++++++------------------------- drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 61 ++++++---- 13 files changed, 466 insertions(+), 255 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d51abbeaf0..cd01dc458a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -937,6 +937,7 @@ config ARCH_QEMU imply DM_RNG imply DM_RTC imply RTC_PL031
imply CMD_QFW
This patch mixed two things together. The adding ARM support should not belong to this patch.
config ARCH_RMOBILE bool "Renesas ARM SoCs" diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index 9ce86b379c..c78e374644 100644
Regards, Bin

Hi Asherah,
On Fri, Feb 26, 2021 at 10:14 AM Bin Meng bmeng.cn@gmail.com wrote:
Hi Asherah,
On Wed, Feb 24, 2021 at 11:23 AM Asherah Connor ashe@kivikakk.ee wrote:
Updates the QFW driver to use the driver model, splitting the driver into qfw_pio and qfw_mmio (for non-x86) in their own uclass.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
Changes in v4:
- PIO definitions are now #defines
- qfw_*_plat structs are no longer in header files
- Remove yield/pause in DMA wait loop
- Change struct udevice *qfw_get_dev(void) to int qfw_get_dev(struct udevice **)
arch/arm/Kconfig | 1 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------- arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 52 ++++----- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++++ drivers/misc/Makefile | 6 +- drivers/misc/qfw.c | 238 ++++++++++++++------------------------- drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 61 ++++++---- 13 files changed, 466 insertions(+), 255 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d51abbeaf0..cd01dc458a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -937,6 +937,7 @@ config ARCH_QEMU imply DM_RNG imply DM_RTC imply RTC_PL031
imply CMD_QFW
This patch mixed two things together. The adding ARM support should not belong to this patch.
So we need to split the patch like this:
1. Convert the existing QFW driver of x86 to QFW uclass driver 2. Add a new QFW mmio driver 3. Enable the driver on QEMU ARM
Regards, Bin

Hi Bin,
On 21/02/26 10:02:p, Bin Meng wrote:
On Fri, Feb 26, 2021 at 10:14 AM Bin Meng bmeng.cn@gmail.com wrote:
This patch mixed two things together. The adding ARM support should not belong to this patch.
So we need to split the patch like this:
- Convert the existing QFW driver of x86 to QFW uclass driver
- Add a new QFW mmio driver
- Enable the driver on QEMU ARM
Thanks for taking a look! I'll redo the series accordingly.
Best,
Asherah

Hi Asherah,
On Wed, Feb 24, 2021 at 11:23 AM Asherah Connor ashe@kivikakk.ee wrote:
Updates the QFW driver to use the driver model, splitting the driver into qfw_pio and qfw_mmio (for non-x86) in their own uclass.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
Changes in v4:
- PIO definitions are now #defines
- qfw_*_plat structs are no longer in header files
- Remove yield/pause in DMA wait loop
- Change struct udevice *qfw_get_dev(void) to int qfw_get_dev(struct udevice **)
arch/arm/Kconfig | 1 + arch/x86/cpu/qemu/cpu.c | 9 +- arch/x86/cpu/qemu/qemu.c | 47 +------- arch/x86/cpu/qfw_cpu.c | 11 +- cmd/qfw.c | 52 ++++----- common/Makefile | 2 + common/qfw.c | 105 +++++++++++++++++ drivers/misc/Makefile | 6 +- drivers/misc/qfw.c | 238 ++++++++++++++------------------------- drivers/misc/qfw_mmio.c | 119 ++++++++++++++++++++ drivers/misc/qfw_pio.c | 69 ++++++++++++ include/dm/uclass-id.h | 1 + include/qfw.h | 61 ++++++---- 13 files changed, 466 insertions(+), 255 deletions(-) create mode 100644 common/qfw.c create mode 100644 drivers/misc/qfw_mmio.c create mode 100644 drivers/misc/qfw_pio.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d51abbeaf0..cd01dc458a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -937,6 +937,7 @@ config ARCH_QEMU imply DM_RNG imply DM_RTC imply RTC_PL031
imply CMD_QFW
config ARCH_RMOBILE bool "Renesas ARM SoCs" diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index 9ce86b379c..c78e374644 100644 --- a/arch/x86/cpu/qemu/cpu.c +++ b/arch/x86/cpu/qemu/cpu.c @@ -22,7 +22,14 @@ int cpu_qemu_get_desc(const struct udevice *dev, char *buf, int size)
static int cpu_qemu_get_count(const struct udevice *dev) {
return qemu_fwcfg_online_cpus();
int ret;
struct udevice *qfw_dev;
ret = qfw_get_dev(&qfw_dev);
if (ret)
return ret;
return qemu_fwcfg_online_cpus(qfw_dev);
}
static const struct cpu_ops cpu_qemu_ops = { diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 044a429c13..605f51e1b8 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -8,6 +8,7 @@ #include <init.h> #include <pci.h> #include <qfw.h> +#include <dm/platdata.h> #include <asm/irq.h> #include <asm/post.h> #include <asm/processor.h> @@ -18,46 +19,10 @@ static bool i440fx;
#ifdef CONFIG_QFW
-/* on x86, the qfw registers are all IO ports */ -#define FW_CONTROL_PORT 0x510 -#define FW_DATA_PORT 0x511 -#define FW_DMA_PORT_LOW 0x514 -#define FW_DMA_PORT_HIGH 0x518
-static void qemu_x86_fwcfg_read_entry_pio(uint16_t entry,
uint32_t size, void *address)
-{
uint32_t i = 0;
uint8_t *data = address;
/*
* writting FW_CFG_INVALID will cause read operation to resume at
* last offset, otherwise read will start at offset 0
*
* Note: on platform where the control register is IO port, the
* endianness is little endian.
*/
if (entry != FW_CFG_INVALID)
outw(cpu_to_le16(entry), FW_CONTROL_PORT);
/* the endianness of data register is string-preserving */
while (size--)
data[i++] = inb(FW_DATA_PORT);
-}
-static void qemu_x86_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma) -{
/* the DMA address register is big endian */
outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH);
while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR)
__asm__ __volatile__ ("pause");
-}
-static struct fw_cfg_arch_ops fwcfg_x86_ops = {
.arch_read_pio = qemu_x86_fwcfg_read_entry_pio,
.arch_read_dma = qemu_x86_fwcfg_read_entry_dma
+U_BOOT_DRVINFO(x86_qfw_pio) = {
.name = "qfw_pio",
};
#endif
static void enable_pm_piix(void) @@ -132,10 +97,6 @@ static void qemu_chipset_init(void)
enable_pm_ich9(); }
-#ifdef CONFIG_QFW
qemu_fwcfg_init(&fwcfg_x86_ops);
-#endif }
#if !CONFIG_IS_ENABLED(SPL_X86_32BIT_INIT) diff --git a/arch/x86/cpu/qfw_cpu.c b/arch/x86/cpu/qfw_cpu.c index b959eaddde..9700908064 100644 --- a/arch/x86/cpu/qfw_cpu.c +++ b/arch/x86/cpu/qfw_cpu.c @@ -18,7 +18,7 @@ int qemu_cpu_fixup(void) int cpu_num; int cpu_online; struct uclass *uc;
struct udevice *dev, *pdev;
struct udevice *dev, *pdev, *qfwdev; struct cpu_plat *plat; char *cpu;
@@ -39,6 +39,13 @@ int qemu_cpu_fixup(void) return -ENODEV; }
/* get qfw dev */
ret = qfw_get_dev(&qfwdev);
if (ret) {
printf("unable to find qfw device\n");
return ret;
}
/* calculate cpus that are already bound */ cpu_num = 0; for (uclass_find_first_device(UCLASS_CPU, &dev);
@@ -48,7 +55,7 @@ int qemu_cpu_fixup(void) }
/* get actual cpu number */
cpu_online = qemu_fwcfg_online_cpus();
cpu_online = qemu_fwcfg_online_cpus(qfwdev); if (cpu_online < 0) { printf("unable to get online cpu number: %d\n", cpu_online); return cpu_online;
diff --git a/cmd/qfw.c b/cmd/qfw.c index bb571487f0..87af408a8d 100644 --- a/cmd/qfw.c +++ b/cmd/qfw.c @@ -8,19 +8,22 @@ #include <env.h> #include <errno.h> #include <qfw.h> +#include <dm.h>
+static struct udevice *qfw_dev;
/*
- This function prepares kernel for zboot. It loads kernel data
- to 'load_addr', initrd to 'initrd_addr' and kernel command
- line using qemu fw_cfg interface.
*/ -static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) +static int qemu_fwcfg_cmd_setup_kernel(void *load_addr, void *initrd_addr) { char *data_addr; uint32_t setup_size, kernel_size, cmdline_size, initrd_size;
qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size);
qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size);
qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size);
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size); if (setup_size == 0 || kernel_size == 0) { printf("warning: no kernel available\n");
@@ -28,28 +31,28 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) }
data_addr = load_addr;
qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA,
le32_to_cpu(setup_size), data_addr);
qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA,
le32_to_cpu(setup_size), data_addr); data_addr += le32_to_cpu(setup_size);
qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA,
le32_to_cpu(kernel_size), data_addr);
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA,
le32_to_cpu(kernel_size), data_addr); data_addr += le32_to_cpu(kernel_size); data_addr = initrd_addr;
qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size);
qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size); if (initrd_size == 0) { printf("warning: no initrd available\n"); } else {
qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA,
le32_to_cpu(initrd_size), data_addr);
qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA,
le32_to_cpu(initrd_size), data_addr); data_addr += le32_to_cpu(initrd_size); }
qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); if (cmdline_size) {
qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA,
le32_to_cpu(cmdline_size), data_addr);
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA,
le32_to_cpu(cmdline_size), data_addr); /* * if kernel cmdline only contains '\0', (e.g. no -append * when invoking qemu), do not update bootargs
@@ -72,19 +75,18 @@ static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) return 0; }
-static int qemu_fwcfg_list_firmware(void) +static int qemu_fwcfg_cmd_list_firmware(void) { int ret; struct fw_cfg_file_iter iter; struct fw_file *file;
/* make sure fw_list is loaded */
ret = qemu_fwcfg_read_firmware_list();
ret = qemu_fwcfg_read_firmware_list(qfw_dev); if (ret) return ret;
for (file = qemu_fwcfg_file_iter_init(&iter);
for (file = qemu_fwcfg_file_iter_init(qfw_dev, &iter); !qemu_fwcfg_file_iter_end(&iter); file = qemu_fwcfg_file_iter_next(&iter)) { printf("%-56s\n", file->cfg.name);
@@ -96,7 +98,7 @@ static int qemu_fwcfg_list_firmware(void) static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
if (qemu_fwcfg_list_firmware() < 0)
if (qemu_fwcfg_cmd_list_firmware() < 0) return CMD_RET_FAILURE; return 0;
@@ -105,14 +107,7 @@ static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
int ret = qemu_fwcfg_online_cpus();
if (ret < 0) {
printf("QEMU fw_cfg interface not found\n");
return CMD_RET_FAILURE;
}
printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus());
printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus(qfw_dev)); return 0;
}
@@ -153,7 +148,7 @@ static int qemu_fwcfg_do_load(struct cmd_tbl *cmdtp, int flag, return CMD_RET_FAILURE; }
return qemu_fwcfg_setup_kernel(load_addr, initrd_addr);
return qemu_fwcfg_cmd_setup_kernel(load_addr, initrd_addr);
}
static struct cmd_tbl fwcfg_commands[] = { @@ -168,7 +163,8 @@ static int do_qemu_fw(struct cmd_tbl *cmdtp, int flag, int argc, int ret; struct cmd_tbl *fwcfg_cmd;
if (!qemu_fwcfg_present()) {
ret = qfw_get_dev(&qfw_dev);
if (ret) { printf("QEMU fw_cfg interface not found\n"); return CMD_RET_USAGE; }
diff --git a/common/Makefile b/common/Makefile index daeea67cf2..f174a06c33 100644 --- a/common/Makefile +++ b/common/Makefile @@ -137,3 +137,5 @@ obj-$(CONFIG_CMD_LOADB) += xyzModem.o obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o
obj-$(CONFIG_AVB_VERIFY) += avb_verify.o
+obj-$(CONFIG_QFW) += qfw.o diff --git a/common/qfw.c b/common/qfw.c new file mode 100644 index 0000000000..c0ffa20b74 --- /dev/null +++ b/common/qfw.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com
- (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee
- */
+#include <dm.h> +#include <dm/uclass.h> +#include <qfw.h> +#include <stdlib.h>
+int qfw_get_dev(struct udevice **devp) +{
return uclass_first_device(UCLASS_QFW, devp);
+}
+int qemu_fwcfg_online_cpus(struct udevice *dev) +{
u16 nb_cpus;
qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus);
return le16_to_cpu(nb_cpus);
+}
+int qemu_fwcfg_read_firmware_list(struct udevice *dev) +{
int i;
u32 count;
struct fw_file *file;
struct list_head *entry;
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
/* don't read it twice */
if (!list_empty(&qdev->fw_list))
return 0;
qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count);
if (!count)
return 0;
count = be32_to_cpu(count);
for (i = 0; i < count; i++) {
file = malloc(sizeof(*file));
if (!file) {
printf("error: allocating resource\n");
goto err;
}
qfw_read_entry(dev, FW_CFG_INVALID,
sizeof(struct fw_cfg_file), &file->cfg);
file->addr = 0;
list_add_tail(&file->list, &qdev->fw_list);
}
return 0;
+err:
list_for_each(entry, &qdev->fw_list) {
file = list_entry(entry, struct fw_file, list);
free(file);
}
return -ENOMEM;
+}
+struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name) +{
struct list_head *entry;
struct fw_file *file;
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
list_for_each(entry, &qdev->fw_list) {
file = list_entry(entry, struct fw_file, list);
if (!strcmp(file->cfg.name, name))
return file;
}
return NULL;
+}
+struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev,
struct fw_cfg_file_iter *iter)
+{
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
iter->entry = qdev->fw_list.next;
iter->end = &qdev->fw_list;
return list_entry((struct list_head *)iter->entry,
struct fw_file, list);
+}
+struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) +{
iter->entry = ((struct list_head *)iter->entry)->next;
return list_entry((struct list_head *)iter->entry,
struct fw_file, list);
+}
+bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) +{
return iter->entry == iter->end;
+}
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d737203704..2988289ea3 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,7 +55,11 @@ obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o -obj-$(CONFIG_QFW) += qfw.o +ifdef CONFIG_QFW +obj-y += qfw.o +obj-$(CONFIG_X86) += qfw_pio.o +obj-$(CONFIG_ARM) += qfw_mmio.o
This does not scale when we reuse this driver on RISC-V, as I saw your QEMU patch.
So we can do like this:
CONFIG_QFW_PIO and CONFIG_QFW_MMIO
Let QEMU x86 select CONFIG_QFW_PIO, and ARM (RISC-V in the future) select CONFIG_QFW_MMIO
+endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
[snip]
Regards, Bin

There's a mixture of "qemu_fwcfg_"-prefixed functions and "qfw_"-prefixed ones. Now that the qfw name is applied in multiple places (i.e. the uclass itself, and each of its drivers), let's consistently use the prefix "qfw_" for anything relating to the drivers.
Signed-off-by: Asherah Connor ashe@kivikakk.ee ---
(no changes since v1)
arch/x86/cpu/qemu/cpu.c | 2 +- arch/x86/cpu/qfw_cpu.c | 2 +- cmd/qfw.c | 10 +++++----- common/qfw.c | 14 +++++++------- drivers/misc/qfw.c | 20 ++++++++++---------- include/qfw.h | 16 ++++++++-------- 6 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/arch/x86/cpu/qemu/cpu.c b/arch/x86/cpu/qemu/cpu.c index c78e374644..735b656084 100644 --- a/arch/x86/cpu/qemu/cpu.c +++ b/arch/x86/cpu/qemu/cpu.c @@ -29,7 +29,7 @@ static int cpu_qemu_get_count(const struct udevice *dev) if (ret) return ret;
- return qemu_fwcfg_online_cpus(qfw_dev); + return qfw_online_cpus(qfw_dev); }
static const struct cpu_ops cpu_qemu_ops = { diff --git a/arch/x86/cpu/qfw_cpu.c b/arch/x86/cpu/qfw_cpu.c index 9700908064..ee00b8fe73 100644 --- a/arch/x86/cpu/qfw_cpu.c +++ b/arch/x86/cpu/qfw_cpu.c @@ -55,7 +55,7 @@ int qemu_cpu_fixup(void) }
/* get actual cpu number */ - cpu_online = qemu_fwcfg_online_cpus(qfwdev); + cpu_online = qfw_online_cpus(qfwdev); if (cpu_online < 0) { printf("unable to get online cpu number: %d\n", cpu_online); return cpu_online; diff --git a/cmd/qfw.c b/cmd/qfw.c index 87af408a8d..e6a9fdb2af 100644 --- a/cmd/qfw.c +++ b/cmd/qfw.c @@ -82,13 +82,13 @@ static int qemu_fwcfg_cmd_list_firmware(void) struct fw_file *file;
/* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(qfw_dev); + ret = qfw_read_firmware_list(qfw_dev); if (ret) return ret;
- for (file = qemu_fwcfg_file_iter_init(qfw_dev, &iter); - !qemu_fwcfg_file_iter_end(&iter); - file = qemu_fwcfg_file_iter_next(&iter)) { + for (file = qfw_file_iter_init(qfw_dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { printf("%-56s\n", file->cfg.name); }
@@ -107,7 +107,7 @@ static int qemu_fwcfg_do_list(struct cmd_tbl *cmdtp, int flag, static int qemu_fwcfg_do_cpus(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { - printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus(qfw_dev)); + printf("%d cpu(s) online\n", qfw_online_cpus(qfw_dev)); return 0; }
diff --git a/common/qfw.c b/common/qfw.c index c0ffa20b74..561b0dd9c0 100644 --- a/common/qfw.c +++ b/common/qfw.c @@ -14,7 +14,7 @@ int qfw_get_dev(struct udevice **devp) return uclass_first_device(UCLASS_QFW, devp); }
-int qemu_fwcfg_online_cpus(struct udevice *dev) +int qfw_online_cpus(struct udevice *dev) { u16 nb_cpus;
@@ -23,7 +23,7 @@ int qemu_fwcfg_online_cpus(struct udevice *dev) return le16_to_cpu(nb_cpus); }
-int qemu_fwcfg_read_firmware_list(struct udevice *dev) +int qfw_read_firmware_list(struct udevice *dev) { int i; u32 count; @@ -64,7 +64,7 @@ err: return -ENOMEM; }
-struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name) +struct fw_file *qfw_find_file(struct udevice *dev, const char *name) { struct list_head *entry; struct fw_file *file; @@ -80,8 +80,8 @@ struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name) return NULL; }
-struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev, - struct fw_cfg_file_iter *iter) +struct fw_file *qfw_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter) { struct qfw_dev *qdev = dev_get_uclass_priv(dev);
@@ -91,14 +91,14 @@ struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev, struct fw_file, list); }
-struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) +struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter) { iter->entry = ((struct list_head *)iter->entry)->next; return list_entry((struct list_head *)iter->entry, struct fw_file, list); }
-bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) +bool qfw_file_iter_end(struct fw_cfg_file_iter *iter) { return iter->entry == iter->end; } diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index 7ad8f0979b..b0c82e2098 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -43,7 +43,7 @@ static int bios_linker_allocate(struct udevice *dev, return -EINVAL; }
- file = qemu_fwcfg_find_file(dev, entry->alloc.file); + file = qfw_find_file(dev, entry->alloc.file); if (!file) { printf("error: can't find file %s\n", entry->alloc.file); return -ENOENT; @@ -99,10 +99,10 @@ static int bios_linker_add_pointer(struct udevice *dev, uint32_t offset = le32_to_cpu(entry->pointer.offset); uint64_t pointer = 0;
- dest = qemu_fwcfg_find_file(dev, entry->pointer.dest_file); + dest = qfw_find_file(dev, entry->pointer.dest_file); if (!dest || !dest->addr) return -ENOENT; - src = qemu_fwcfg_find_file(dev, entry->pointer.src_file); + src = qfw_find_file(dev, entry->pointer.src_file); if (!src || !src->addr) return -ENOENT;
@@ -133,7 +133,7 @@ static int bios_linker_add_checksum(struct udevice *dev, uint8_t *data, cksum = 0; uint8_t *cksum_start;
- file = qemu_fwcfg_find_file(dev, entry->cksum.file); + file = qfw_find_file(dev, entry->cksum.file); if (!file || !file->addr) return -ENOENT;
@@ -163,13 +163,13 @@ ulong write_acpi_tables(ulong addr) }
/* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(dev); + ret = qfw_read_firmware_list(dev); if (ret) { printf("error: can't read firmware file list\n"); return addr; }
- file = qemu_fwcfg_find_file(dev, "etc/table-loader"); + file = qfw_find_file(dev, "etc/table-loader"); if (!file) { printf("error: can't find etc/table-loader\n"); return addr; @@ -215,9 +215,9 @@ ulong write_acpi_tables(ulong addr) out: if (ret) { struct fw_cfg_file_iter iter; - for (file = qemu_fwcfg_file_iter_init(dev, &iter); - !qemu_fwcfg_file_iter_end(&iter); - file = qemu_fwcfg_file_iter_next(&iter)) { + for (file = qfw_file_iter_init(dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { if (file->addr) { free((void *)file->addr); file->addr = 0; @@ -241,7 +241,7 @@ ulong acpi_get_rsdp_addr(void) return 0; }
- file = qemu_fwcfg_find_file(dev, "etc/acpi/rsdp"); + file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } #endif diff --git a/include/qfw.h b/include/qfw.h index 7b389dec25..41d4db08d6 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -8,7 +8,7 @@
#include <linux/list.h>
-enum qemu_fwcfg_items { +enum { FW_CFG_SIGNATURE = 0x00, FW_CFG_ID = 0x01, FW_CFG_UUID = 0x02, @@ -169,21 +169,21 @@ struct udevice; int qfw_get_dev(struct udevice **devp);
void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address); -int qemu_fwcfg_read_firmware_list(struct udevice *dev); -struct fw_file *qemu_fwcfg_find_file(struct udevice *dev, const char *name); +int qfw_read_firmware_list(struct udevice *dev); +struct fw_file *qfw_find_file(struct udevice *dev, const char *name);
/** * Get system cpu number * * @return: cpu number in system */ -int qemu_fwcfg_online_cpus(struct udevice *dev); +int qfw_online_cpus(struct udevice *dev);
/* helper functions to iterate firmware file list */ -struct fw_file *qemu_fwcfg_file_iter_init(struct udevice *dev, - struct fw_cfg_file_iter *iter); -struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter); -bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter); +struct fw_file *qfw_file_iter_init(struct udevice *dev, + struct fw_cfg_file_iter *iter); +struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter); +bool qfw_file_iter_end(struct fw_cfg_file_iter *iter);
/** * qemu_cpu_fixup() - Fix up the CPUs for QEMU

On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
There's a mixture of "qemu_fwcfg_"-prefixed functions and "qfw_"-prefixed ones. Now that the qfw name is applied in multiple places (i.e. the uclass itself, and each of its drivers), let's consistently use the prefix "qfw_" for anything relating to the drivers.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
(no changes since v1)
arch/x86/cpu/qemu/cpu.c | 2 +- arch/x86/cpu/qfw_cpu.c | 2 +- cmd/qfw.c | 10 +++++----- common/qfw.c | 14 +++++++------- drivers/misc/qfw.c | 20 ++++++++++---------- include/qfw.h | 16 ++++++++-------- 6 files changed, 32 insertions(+), 32 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

We minimally exercise the sandbox driver.
Signed-off-by: Asherah Connor ashe@kivikakk.ee ---
(no changes since v1)
arch/sandbox/dts/sandbox.dtsi | 4 ++ arch/sandbox/dts/test.dts | 4 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_sandbox.c | 129 ++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/qfw.c | 42 +++++++++++ 6 files changed, 181 insertions(+) create mode 100644 drivers/misc/qfw_sandbox.c create mode 100644 test/dm/qfw.c
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index dc933f3bfc..7ce05b9662 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -390,6 +390,10 @@ sandbox_tee { compatible = "sandbox,tee"; }; + + qfw { + compatible = "sandbox,qemu-fw-cfg-mmio"; + }; };
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d4195b45bb..4b3f8831d5 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -112,6 +112,10 @@ compatible = "sandbox,dsi-host"; };
+ qfw { + compatible = "sandbox,qemu-fw-cfg-mmio"; + }; + a-test { reg = <0 1>; compatible = "denx,u-boot-fdt-test"; diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2988289ea3..c48f61b797 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,6 +59,7 @@ ifdef CONFIG_QFW obj-y += qfw.o obj-$(CONFIG_X86) += qfw_pio.o obj-$(CONFIG_ARM) += qfw_mmio.o +obj-$(CONFIG_SANDBOX) += qfw_sandbox.o endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o diff --git a/drivers/misc/qfw_sandbox.c b/drivers/misc/qfw_sandbox.c new file mode 100644 index 0000000000..fc7006ae19 --- /dev/null +++ b/drivers/misc/qfw_sandbox.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sandbox interface for QFW + * + * (C) Copyright 2015 Miao Yan yanmiaobest@gmail.com + * (C) Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/types.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device.h> +#include <qfw.h> + +struct qfw_sandbox_plat { + u8 file_dir_offset; +}; + +static void qfw_sandbox_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + debug("%s: entry 0x%x size %u address %p\n", __func__, entry, size, + address); + + switch (entry) { + case FW_CFG_SIGNATURE: + if (size == 4) + *((u32 *)address) = cpu_to_be32(QEMU_FW_CFG_SIGNATURE); + break; + case FW_CFG_ID: + /* Advertise DMA support */ + if (size == 1) + *((u8 *)address) = FW_CFG_DMA_ENABLED; + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, entry); + /* + * Sandbox driver doesn't support other entries here, assume we use DMA + * to read them -- the uclass driver will exclusively use it when + * advertised. + */ + } +} + +static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + u16 entry; + u32 control = be32_to_cpu(dma->control); + void *address = (void *)be64_to_cpu(dma->address); + u32 length = be32_to_cpu(dma->length); + struct qfw_sandbox_plat *plat = dev_get_plat(dev); + struct fw_cfg_file *file; + + debug("%s\n", __func__); + + if (!(control & FW_CFG_DMA_READ)) + return; + + if (control & FW_CFG_DMA_SELECT) { + /* Start new read. */ + entry = control >> 16; + + /* Arbitrary values to be used by tests. */ + switch (entry) { + case FW_CFG_NB_CPUS: + if (length == 2) + *((u16 *)address) = cpu_to_le16(5); + break; + case FW_CFG_FILE_DIR: + if (length == 4) { + *((u32 *)address) = cpu_to_be32(2); + plat->file_dir_offset = 1; + } + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, + entry); + } + } else if (plat->file_dir_offset && length == 64) { + file = address; + switch (plat->file_dir_offset) { + case 1: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST); + strcpy(file->name, "test-one"); + plat->file_dir_offset++; + break; + case 2: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST + 1); + strcpy(file->name, "test-two"); + plat->file_dir_offset++; + break; + } + } + + /* + * Signal that we are finished. No-one checks this in sandbox -- + * normally the platform-specific driver looks for it -- but let's + * replicate the behaviour in case someone relies on it later. + */ + dma->control = 0; +} + +static int qfw_sandbox_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_sandbox_ops = { + .read_entry_io = qfw_sandbox_read_entry_io, + .read_entry_dma = qfw_sandbox_read_entry_dma, +}; + +static const struct udevice_id qfw_sandbox_ids[] = { + { .compatible = "sandbox,qemu-fw-cfg-mmio" }, + {} +}; + +U_BOOT_DRIVER(qfw_sandbox) = { + .name = "qfw_sandbox", + .id = UCLASS_QFW, + .of_match = qfw_sandbox_ids, + .plat_auto = sizeof(struct qfw_sandbox_plat), + .probe = qfw_sandbox_probe, + .ops = &qfw_sandbox_ops, +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 6275ec56ea..bdca7ae613 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -95,5 +95,6 @@ obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o ifneq ($(CONFIG_PINMUX),) obj-$(CONFIG_PINCONF) += pinmux.o endif +obj-$(CONFIG_QFW) += qfw.o endif endif # !SPL diff --git a/test/dm/qfw.c b/test/dm/qfw.c new file mode 100644 index 0000000000..51b1c42dda --- /dev/null +++ b/test/dm/qfw.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Asherah Connor ashe@kivikakk.ee + */ + +#include <common.h> +#include <qfw.h> +#include <dm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +/* + * Exercise the device enough to be satisfied the initialisation and DMA + * interfaces work. + */ + +static int dm_test_qfw_cpus(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_asserteq(5, qfw_online_cpus(dev)); + + return 0; +} + +DM_TEST(dm_test_qfw_cpus, UT_TESTF_SCAN_FDT); + +static int dm_test_qfw_firmware_list(struct unit_test_state *uts) +{ + struct udevice *dev; + struct fw_file *file; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_assertok(qfw_read_firmware_list(dev)); + ut_assertok_ptr((file = qfw_find_file(dev, "test-one"))); + + return 0; +} + +DM_TEST(dm_test_qfw_firmware_list, UT_TESTF_SCAN_FDT);

On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
We minimally exercise the sandbox driver.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
(no changes since v1)
arch/sandbox/dts/sandbox.dtsi | 4 ++ arch/sandbox/dts/test.dts | 4 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_sandbox.c | 129 ++++++++++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/qfw.c | 42 +++++++++++ 6 files changed, 181 insertions(+) create mode 100644 drivers/misc/qfw_sandbox.c create mode 100644 test/dm/qfw.c
Reviewed-by: Simon Glass sjg@chromium.org

Signed-off-by: Asherah Connor ashe@kivikakk.ee ---
(no changes since v1)
test/py/tests/test_qfw.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/py/tests/test_qfw.py
diff --git a/test/py/tests/test_qfw.py b/test/py/tests/test_qfw.py new file mode 100644 index 0000000000..a2631a0fa6 --- /dev/null +++ b/test/py/tests/test_qfw.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2021, Asherah Connor ashe@kivikakk.ee + +# Test qfw command implementation + +import pytest + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_cpus(u_boot_console): + "Test QEMU firmware config reports the CPU count correctly." + + output = u_boot_console.run_command('qfw cpus') + assert '1 cpu(s) online' in output + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_list(u_boot_console): + "Test QEMU firmware config lists devices." + + output = u_boot_console.run_command('qfw list') + assert 'bootorder' in output + assert 'etc/table-loader' in output

On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
Signed-off-by: Asherah Connor ashe@kivikakk.ee
(no changes since v1)
test/py/tests/test_qfw.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/py/tests/test_qfw.py
Reviewed-by: Simon Glass sjg@chromium.org

Also rename a "length" to "size" for consistency with the rest of qfw.
Signed-off-by: Asherah Connor ashe@kivikakk.ee ---
(no changes since v1)
drivers/misc/qfw.c | 6 ++-- include/qfw.h | 86 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index b0c82e2098..3e8c1a9cda 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -283,14 +283,14 @@ static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size, ops->read_entry_dma(qdev->dev, &dma); }
-void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address) +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address) { struct qfw_dev *qdev = dev_get_uclass_priv(dev);
if (qdev->dma_present) - qfw_read_entry_dma(qdev, entry, length, address); + qfw_read_entry_dma(qdev, entry, size, address); else - qfw_read_entry_io(qdev, entry, length, address); + qfw_read_entry_io(qdev, entry, size, address); }
int qfw_register(struct udevice *dev) diff --git a/include/qfw.h b/include/qfw.h index 41d4db08d6..d59c71a5dd 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -8,6 +8,11 @@
#include <linux/list.h>
+/* + * List of firmware configuration item selectors. The official source of truth + * for these is the QEMU source itself; see + * https://github.com/qemu/qemu/blob/master/hw/nvram/fw_cfg.c + */ enum { FW_CFG_SIGNATURE = 0x00, FW_CFG_ID = 0x01, @@ -66,8 +71,10 @@ enum { #define FW_CFG_DMA_SKIP (1 << 2) #define FW_CFG_DMA_SELECT (1 << 3)
+/* Bit set in FW_CFG_ID response to indicate DMA interface availability. */ #define FW_CFG_DMA_ENABLED (1 << 1)
+/* Structs read from FW_CFG_FILE_DIR. */ struct fw_cfg_file { __be32 size; __be16 select; @@ -134,27 +141,56 @@ struct bios_linker_entry { }; } __packed;
+/* DMA transfer control data between UCLASS_QFW and QEMU. */ struct qfw_dma { __be32 control; __be32 length; __be64 address; };
+/* uclass per-device configuration information */ struct qfw_dev { - struct udevice *dev; - bool dma_present; - struct list_head fw_list; + struct udevice *dev; /* Transport device */ + bool dma_present; /* DMA interface usable? */ + struct list_head fw_list; /* Cached firmware file list */ };
+/* Ops used internally between UCLASS_QFW and its driver implementations. */ struct dm_qfw_ops { + /** + * read_entry_io() - Read a firmware config entry using the regular + * IO interface for the platform (either PIO or MMIO) + * + * Supply FW_CFG_INVALID as the entry to continue a previous read. In + * this case, no selector will be issued before reading. + * + * @dev: Device to use + * @entry: Firmware config entry number (e.g. FW_CFG_SIGNATURE) + * @size: Number of bytes to read + * @address: Target location for read + */ void (*read_entry_io)(struct udevice *dev, u16 entry, u32 size, void *address); + + /** + * read_entry_dma() - Read a firmware config entry using the DMA + * interface + * + * Supply FW_CFG_INVALID as the entry to continue a previous read. In + * this case, no selector will be issued before reading. + * + * This method assumes DMA availability has already been confirmed. + * + * @dev: Device to use + * @dma: DMA transfer control struct + */ void (*read_entry_dma)(struct udevice *dev, struct qfw_dma *dma); };
#define dm_qfw_get_ops(dev) \ ((struct dm_qfw_ops *)(dev)->driver->ops)
+/* Used internally by driver implementations on successful probe. */ int qfw_register(struct udevice *dev);
struct udevice; @@ -168,8 +204,37 @@ struct udevice; */ int qfw_get_dev(struct udevice **devp);
-void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address); +/** + * Read a QEMU firmware config entry + * + * The appropriate transport and interface will be selected automatically. + * + * @dev: Device to use + * @entry: Firmware config entry number (e.g. FW_CFG_SIGNATURE) + * @size: Number of bytes to read + * @address: Target location for read + */ +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address); + +/** + * Reads the QEMU firmware config file list, for later use with qfw_find_file. + * + * If the list has already been read, returns 0 (success). + * + * @dev: Device to use + * + * @return 0 on success, -ENOMEM if unable to allocate. + */ int qfw_read_firmware_list(struct udevice *dev); + +/** + * Finds a file by name in the QEMU firmware config file list. + * + * @dev: Device to use + * @name: Name of file to locate (e.g. "etc/table-loader") + * + * @return pointer to fw_file struct if found, NULL if not present. + */ struct fw_file *qfw_find_file(struct udevice *dev, const char *name);
/** @@ -179,7 +244,18 @@ struct fw_file *qfw_find_file(struct udevice *dev, const char *name); */ int qfw_online_cpus(struct udevice *dev);
-/* helper functions to iterate firmware file list */ +/** + * Helper functions to iterate firmware file list. Suggested use: + * + * struct fw_cfg_file_iter iter; + * struct fw_file *file; + * + * for (file = qfw_file_iter_init(dev, &iter); + * !qfw_file_iter_end(&iter); + * file = qfw_file_iter_next(&iter)) { + * // use `file' + * } + */ struct fw_file *qfw_file_iter_init(struct udevice *dev, struct fw_cfg_file_iter *iter); struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter);

Hi Asherah,
On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
Also rename a "length" to "size" for consistency with the rest of qfw.
Here also you add comments so should mention that.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
(no changes since v1)
drivers/misc/qfw.c | 6 ++-- include/qfw.h | 86 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

On 21/02/25 02:02:p, Simon Glass wrote:
On Tue, 23 Feb 2021 at 22:24, Asherah Connor ashe@kivikakk.ee wrote:
Also rename a "length" to "size" for consistency with the rest of qfw.
Here also you add comments so should mention that.
I thought that may be considered redundant with the subject; will add to both.
Best,
Asherah

On 2/24/21 4:23 AM, Asherah Connor wrote:
Also rename a "length" to "size" for consistency with the rest of qfw.
Signed-off-by: Asherah Connor ashe@kivikakk.ee
(no changes since v1)
drivers/misc/qfw.c | 6 ++-- include/qfw.h | 86 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index b0c82e2098..3e8c1a9cda 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -283,14 +283,14 @@ static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size, ops->read_entry_dma(qdev->dev, &dma); }
-void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address) +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address) { struct qfw_dev *qdev = dev_get_uclass_priv(dev);
if (qdev->dma_present)
qfw_read_entry_dma(qdev, entry, length, address);
elseqfw_read_entry_dma(qdev, entry, size, address);
qfw_read_entry_io(qdev, entry, length, address);
qfw_read_entry_io(qdev, entry, size, address);
}
int qfw_register(struct udevice *dev)
diff --git a/include/qfw.h b/include/qfw.h index 41d4db08d6..d59c71a5dd 100644 --- a/include/qfw.h +++ b/include/qfw.h @@ -8,6 +8,11 @@
#include <linux/list.h>
+/*
- List of firmware configuration item selectors. The official source of truth
- for these is the QEMU source itself; see
- */ enum { FW_CFG_SIGNATURE = 0x00, FW_CFG_ID = 0x01,
@@ -66,8 +71,10 @@ enum { #define FW_CFG_DMA_SKIP (1 << 2) #define FW_CFG_DMA_SELECT (1 << 3)
+/* Bit set in FW_CFG_ID response to indicate DMA interface availability. */ #define FW_CFG_DMA_ENABLED (1 << 1)
+/* Structs read from FW_CFG_FILE_DIR. */ struct fw_cfg_file { __be32 size; __be16 select; @@ -134,27 +141,56 @@ struct bios_linker_entry { }; } __packed;
+/* DMA transfer control data between UCLASS_QFW and QEMU. */ struct qfw_dma { __be32 control; __be32 length; __be64 address; };
+/* uclass per-device configuration information */ struct qfw_dev {
- struct udevice *dev;
- bool dma_present;
- struct list_head fw_list;
- struct udevice *dev; /* Transport device */
- bool dma_present; /* DMA interface usable? */
- struct list_head fw_list; /* Cached firmware file list */ };
+/* Ops used internally between UCLASS_QFW and its driver implementations. */ struct dm_qfw_ops {
/**
* read_entry_io() - Read a firmware config entry using the regular
* IO interface for the platform (either PIO or MMIO)
*
* Supply FW_CFG_INVALID as the entry to continue a previous read. In
* this case, no selector will be issued before reading.
*
* @dev: Device to use
* @entry: Firmware config entry number (e.g. FW_CFG_SIGNATURE)
* @size: Number of bytes to read
* @address: Target location for read
*/
void (*read_entry_io)(struct udevice *dev, u16 entry, u32 size, void *address);
/**
* read_entry_dma() - Read a firmware config entry using the DMA
* interface
*
* Supply FW_CFG_INVALID as the entry to continue a previous read. In
* this case, no selector will be issued before reading.
*
* This method assumes DMA availability has already been confirmed.
*
* @dev: Device to use
* @dma: DMA transfer control struct
*/
void (*read_entry_dma)(struct udevice *dev, struct qfw_dma *dma); };
#define dm_qfw_get_ops(dev) \ ((struct dm_qfw_ops *)(dev)->driver->ops)
+/* Used internally by driver implementations on successful probe. */ int qfw_register(struct udevice *dev);
struct udevice; @@ -168,8 +204,37 @@ struct udevice; */ int qfw_get_dev(struct udevice **devp);
-void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address); +/**
- Read a QEMU firmware config entry
This will not generate documentation for qfw_read_entry() with Sphinx.
See https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-do...
- The appropriate transport and interface will be selected automatically.
- @dev: Device to use
- @entry: Firmware config entry number (e.g. FW_CFG_SIGNATURE)
- @size: Number of bytes to read
- @address: Target location for read
- */
+void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address);
+/**
- Reads the QEMU firmware config file list, for later use with qfw_find_file.
- If the list has already been read, returns 0 (success).
- @dev: Device to use
- @return 0 on success, -ENOMEM if unable to allocate.
This is not valid Sphinx syntax.
- */ int qfw_read_firmware_list(struct udevice *dev);
+/**
- Finds a file by name in the QEMU firmware config file list.
Function name missing.
- @dev: Device to use
- @name: Name of file to locate (e.g. "etc/table-loader")
- @return pointer to fw_file struct if found, NULL if not present.
Invalid syntax.
*/ struct fw_file *qfw_find_file(struct udevice *dev, const char *name);
/**
@@ -179,7 +244,18 @@ struct fw_file *qfw_find_file(struct udevice *dev, const char *name); */ int qfw_online_cpus(struct udevice *dev);
-/* helper functions to iterate firmware file list */ +/**
- Helper functions to iterate firmware file list. Suggested use:
Please, describe each function individually in Sphinx style.
Best regards
Heinrich
- struct fw_cfg_file_iter iter;
- struct fw_file *file;
- for (file = qfw_file_iter_init(dev, &iter);
!qfw_file_iter_end(&iter);
file = qfw_file_iter_next(&iter)) {
// use `file'
- }
- */ struct fw_file *qfw_file_iter_init(struct udevice *dev, struct fw_cfg_file_iter *iter); struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter);

On 21/02/25 09:02:p, Heinrich Schuchardt wrote:
-void qfw_read_entry(struct udevice *dev, u16 entry, u32 length, void *address); +/**
- Read a QEMU firmware config entry
This will not generate documentation for qfw_read_entry() with Sphinx.
See https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-do...
Thank you! I am now testing documentation produced using
scripts/kernel-doc -man include/qfw.h | man --local-file -
If there's a better way, please let me know.
- @return 0 on success, -ENOMEM if unable to allocate.
This is not valid Sphinx syntax.
[...]
function name missing.
[...]
invalid syntax.
[...]
Please, describe each function individually in Sphinx style.
I'll correct these in the next version. Thank you!
Best,
Asherah

On 21/02/24 02:02:p, Asherah Connor wrote:
This series moves the QFW driver into a uclass, UCLASS_QFW, and splits the driver into qfw_pio and qfw_mmio. A sandbox driver is also added, and a DM unit test against that driver.
I thought it might be worth adding: I've continued and implemented in my own branch a qemu_ramfb VIDEO driver on top of this series (+158 -28), and it works great! I'm looking forward to sending it once this series is up to scratch.
Best,
Asherah
participants (4)
-
Asherah Connor
-
Bin Meng
-
Heinrich Schuchardt
-
Simon Glass