From fcf5c04193b48c3f5e2d337a85d28c4026f90ac3 Mon Sep 17 00:00:00 2001 From: Miao Yan Date: Sun, 22 May 2016 19:37:14 -0700 Subject: x86: qemu: split qfw command interface and qfw core This patch splits qfw command interface and qfw core function into two files, and introduces a new Kconfig option (CONFIG_QFW) for qfw core. Now when qfw command interface is enabled, it will automatically select qfw core. This patch also makes the ACPI table generation select CONFIG_QFW. Signed-off-by: Miao Yan Reviewed-by: Bin Meng --- drivers/misc/Kconfig | 6 ++ drivers/misc/Makefile | 1 + drivers/misc/qemu_fw_cfg.c | 184 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 drivers/misc/qemu_fw_cfg.c (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index af8667f030c..fa537002d4b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -138,4 +138,10 @@ config WINBOND_W83627 legacy UART or other devices in the Winbond Super IO chips on X86 platforms. +config QFW + bool + help + Hidden option to enable QEMU fw_cfg interface. This will be selected by + either CONFIG_CMD_QEMU_FW_CFG or CONFIG_GENERATE_ACPI_TABLE. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5969d344441..4893086b72c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_RESET) += reset-uclass.o obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o +obj-$(CONFIG_QFW) += qemu_fw_cfg.o diff --git a/drivers/misc/qemu_fw_cfg.c b/drivers/misc/qemu_fw_cfg.c new file mode 100644 index 00000000000..a574bd1ec18 --- /dev/null +++ b/drivers/misc/qemu_fw_cfg.c @@ -0,0 +1,184 @@ +/* + * (C) Copyright 2015 Miao Yan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool fwcfg_present; +static bool fwcfg_dma_present; + +static LIST_HEAD(fw_list); + +/* Read configuration item using fw_cfg PIO interface */ +static void qemu_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 + */ + if (entry != FW_CFG_INVALID) + outw(entry, FW_CONTROL_PORT); + while (size--) + data[i++] = inb(FW_DATA_PORT); +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qemu_fwcfg_read_entry_dma(uint16_t entry, + uint32_t size, void *address) +{ + struct fw_cfg_dma_access dma; + + dma.length = cpu_to_be32(size); + dma.address = cpu_to_be64((uintptr_t)address); + dma.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 + */ + if (entry != FW_CFG_INVALID) + dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); + + barrier(); + + debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n", + address, size, be32_to_cpu(dma.control)); + + outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH); + + while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR) + __asm__ __volatile__ ("pause"); +} + +bool qemu_fwcfg_present(void) +{ + return fwcfg_present; +} + +bool qemu_fwcfg_dma_present(void) +{ + return fwcfg_dma_present; +} + +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); + else + qemu_fwcfg_read_entry_pio(entry, length, address); +} + +int qemu_fwcfg_online_cpus(void) +{ + uint16_t nb_cpus; + + if (!fwcfg_present) + 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); + } + + 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(void) +{ + uint32_t qemu; + uint32_t dma_enabled; + + fwcfg_present = false; + fwcfg_dma_present = false; + + 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; + } +} -- cgit v1.2.3 From 2e82e745a4cf6bad10d9fe5a53db6592f40ff903 Mon Sep 17 00:00:00 2001 From: Miao Yan Date: Sun, 22 May 2016 19:37:15 -0700 Subject: x86: qemu: move x86 specific operations out of qfw core The original implementation of qfw includes several x86 specific operations, like directly calling outb/inb and using some inline assembly code which prevents it being ported to other architectures. This patch adds callback functions and moves those to arch/x86/ Signed-off-by: Miao Yan Reviewed-by: Bin Meng --- drivers/misc/qemu_fw_cfg.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/qemu_fw_cfg.c b/drivers/misc/qemu_fw_cfg.c index a574bd1ec18..0f72549321c 100644 --- a/drivers/misc/qemu_fw_cfg.c +++ b/drivers/misc/qemu_fw_cfg.c @@ -14,6 +14,7 @@ static bool fwcfg_present; static bool fwcfg_dma_present; +static struct fw_cfg_arch_ops *fwcfg_arch_ops; static LIST_HEAD(fw_list); @@ -21,17 +22,10 @@ static LIST_HEAD(fw_list); static void qemu_fwcfg_read_entry_pio(uint16_t entry, uint32_t size, void *address) { - uint32_t i = 0; - uint8_t *data = address; + debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", + entry, size, address); - /* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 - */ - if (entry != FW_CFG_INVALID) - outw(entry, FW_CONTROL_PORT); - while (size--) - data[i++] = inb(FW_DATA_PORT); + return fwcfg_arch_ops->arch_read_pio(entry, size, address); } /* Read configuration item using fw_cfg DMA interface */ @@ -53,13 +47,10 @@ static void qemu_fwcfg_read_entry_dma(uint16_t entry, barrier(); - debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n", - address, size, be32_to_cpu(dma.control)); - - outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH); + debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n", + entry, size, address, be32_to_cpu(dma.control)); - while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR) - __asm__ __volatile__ ("pause"); + fwcfg_arch_ops->arch_read_dma(&dma); } bool qemu_fwcfg_present(void) @@ -164,13 +155,18 @@ bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) return iter->entry == &fw_list; } -void qemu_fwcfg_init(void) +void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops) { uint32_t qemu; uint32_t dma_enabled; 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) -- cgit v1.2.3 From 1868659002a6b7ab3b1da7be74f53d3e10e915be Mon Sep 17 00:00:00 2001 From: Miao Yan Date: Sun, 22 May 2016 19:37:17 -0700 Subject: cmd: qfw: rename qemu_fw_cfg.[c|h] to qfw.[c|h] Make file names consistent with CONFIG_QFW and CONFIG_CMD_QFW Signed-off-by: Miao Yan Reviewed-by: Bin Meng --- drivers/misc/Kconfig | 2 +- drivers/misc/Makefile | 2 +- drivers/misc/qemu_fw_cfg.c | 180 --------------------------------------------- drivers/misc/qfw.c | 180 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 182 deletions(-) delete mode 100644 drivers/misc/qemu_fw_cfg.c create mode 100644 drivers/misc/qfw.c (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fa537002d4b..c40f6b577f3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -142,6 +142,6 @@ config QFW bool help Hidden option to enable QEMU fw_cfg interface. This will be selected by - either CONFIG_CMD_QEMU_FW_CFG or CONFIG_GENERATE_ACPI_TABLE. + either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4893086b72c..98704f2085d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -43,4 +43,4 @@ obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_RESET) += reset-uclass.o obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o -obj-$(CONFIG_QFW) += qemu_fw_cfg.o +obj-$(CONFIG_QFW) += qfw.o diff --git a/drivers/misc/qemu_fw_cfg.c b/drivers/misc/qemu_fw_cfg.c deleted file mode 100644 index 0f72549321c..00000000000 --- a/drivers/misc/qemu_fw_cfg.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * (C) Copyright 2015 Miao Yan - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include - -static bool fwcfg_present; -static bool fwcfg_dma_present; -static struct fw_cfg_arch_ops *fwcfg_arch_ops; - -static LIST_HEAD(fw_list); - -/* Read configuration item using fw_cfg PIO interface */ -static void qemu_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) -{ - debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", - entry, size, address); - - return fwcfg_arch_ops->arch_read_pio(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) -{ - struct fw_cfg_dma_access dma; - - dma.length = cpu_to_be32(size); - dma.address = cpu_to_be64((uintptr_t)address); - dma.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 - */ - 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", - entry, size, address, be32_to_cpu(dma.control)); - - fwcfg_arch_ops->arch_read_dma(&dma); -} - -bool qemu_fwcfg_present(void) -{ - return fwcfg_present; -} - -bool qemu_fwcfg_dma_present(void) -{ - return fwcfg_dma_present; -} - -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); - else - qemu_fwcfg_read_entry_pio(entry, length, address); -} - -int qemu_fwcfg_online_cpus(void) -{ - uint16_t nb_cpus; - - if (!fwcfg_present) - 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); - } - - 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; - - 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.c b/drivers/misc/qfw.c new file mode 100644 index 00000000000..59d9376d768 --- /dev/null +++ b/drivers/misc/qfw.c @@ -0,0 +1,180 @@ +/* + * (C) Copyright 2015 Miao Yan + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool fwcfg_present; +static bool fwcfg_dma_present; +static struct fw_cfg_arch_ops *fwcfg_arch_ops; + +static LIST_HEAD(fw_list); + +/* Read configuration item using fw_cfg PIO interface */ +static void qemu_fwcfg_read_entry_pio(uint16_t entry, + uint32_t size, void *address) +{ + debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", + entry, size, address); + + return fwcfg_arch_ops->arch_read_pio(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) +{ + struct fw_cfg_dma_access dma; + + dma.length = cpu_to_be32(size); + dma.address = cpu_to_be64((uintptr_t)address); + dma.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 + */ + 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", + entry, size, address, be32_to_cpu(dma.control)); + + fwcfg_arch_ops->arch_read_dma(&dma); +} + +bool qemu_fwcfg_present(void) +{ + return fwcfg_present; +} + +bool qemu_fwcfg_dma_present(void) +{ + return fwcfg_dma_present; +} + +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); + else + qemu_fwcfg_read_entry_pio(entry, length, address); +} + +int qemu_fwcfg_online_cpus(void) +{ + uint16_t nb_cpus; + + if (!fwcfg_present) + 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); + } + + 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; + + 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; + } +} -- cgit v1.2.3 From eece493a7ac14f5049a8bd7031ba37e2c6e2460a Mon Sep 17 00:00:00 2001 From: Miao Yan Date: Sun, 22 May 2016 19:37:20 -0700 Subject: cmd: qfw: bring ACPI generation code into qfw core Loading ACPI table from QEMU's fw_cfg interface is not x86 specific (ARM64 may also make use of it). So move the code to common place. Signed-off-by: Miao Yan Reviewed-by: Bin Meng --- drivers/misc/qfw.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index 59d9376d768..d43d1d300ac 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -10,6 +10,9 @@ #include #include #include +#ifdef CONFIG_GENERATE_ACPI_TABLE +#include +#endif #include static bool fwcfg_present; @@ -18,6 +21,209 @@ static struct fw_cfg_arch_ops *fwcfg_arch_ops; static LIST_HEAD(fw_list); +#ifdef CONFIG_GENERATE_ACPI_TABLE +/* + * This function allocates memory for ACPI tables + * + * @entry : BIOS linker command entry which tells where to allocate memory + * (either high memory or low memory) + * @addr : The address that should be used for low memory allcation. If the + * memory allocation request is 'ZONE_HIGH' then this parameter will + * be ignored. + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_allocate(struct bios_linker_entry *entry, u32 *addr) +{ + uint32_t size, align; + struct fw_file *file; + unsigned long aligned_addr; + + align = le32_to_cpu(entry->alloc.align); + /* align must be power of 2 */ + if (align & (align - 1)) { + printf("error: wrong alignment %u\n", align); + return -EINVAL; + } + + file = qemu_fwcfg_find_file(entry->alloc.file); + if (!file) { + printf("error: can't find file %s\n", entry->alloc.file); + return -ENOENT; + } + + size = be32_to_cpu(file->cfg.size); + + /* + * ZONE_HIGH means we need to allocate from high memory, since + * malloc space is already at the end of RAM, so we directly use it. + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed + * in which is low memory + */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + aligned_addr = (unsigned long)memalign(align, size); + if (!aligned_addr) { + printf("error: allocating resource\n"); + return -ENOMEM; + } + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { + aligned_addr = ALIGN(*addr, align); + } else { + printf("error: invalid allocation zone\n"); + return -EINVAL; + } + + 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); + file->addr = aligned_addr; + + /* adjust address for low memory allocation */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + *addr = (aligned_addr + size); + + return 0; +} + +/* + * This function patches ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells how to patch + * ACPI tables + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_pointer(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); + if (!dest || !dest->addr) + return -ENOENT; + src = qemu_fwcfg_find_file(entry->pointer.src_file); + if (!src || !src->addr) + return -ENOENT; + + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", + dest->addr, src->addr, offset, entry->pointer.size, pointer); + + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src->addr; + pointer = cpu_to_le64(pointer); + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); + + return 0; +} + +/* + * This function updates checksum fields of ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells where to update ACPI table + * checksums + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_checksum(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); + if (!file || !file->addr) + return -ENOENT; + + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); + cksum = table_compute_checksum(cksum_start, + le32_to_cpu(entry->cksum.length)); + *data = cksum; + + return 0; +} + +/* This function loads and patches ACPI tables provided by QEMU */ +u32 write_acpi_tables(u32 addr) +{ + int i, ret = 0; + struct fw_file *file; + struct bios_linker_entry *table_loader; + struct bios_linker_entry *entry; + uint32_t size; + + /* make sure fw_list is loaded */ + ret = qemu_fwcfg_read_firmware_list(); + if (ret) { + printf("error: can't read firmware file list\n"); + return addr; + } + + file = qemu_fwcfg_find_file("etc/table-loader"); + if (!file) { + printf("error: can't find etc/table-loader\n"); + return addr; + } + + size = be32_to_cpu(file->cfg.size); + if ((size % sizeof(*entry)) != 0) { + printf("error: table-loader maybe corrupted\n"); + return addr; + } + + table_loader = malloc(size); + if (!table_loader) { + printf("error: no memory for table-loader\n"); + return addr; + } + + qemu_fwcfg_read_entry(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); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + ret = bios_linker_add_pointer(entry); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + ret = bios_linker_add_checksum(entry); + if (ret) + goto out; + break; + default: + break; + } + } + +out: + if (ret) { + struct fw_cfg_file_iter iter; + for (file = qemu_fwcfg_file_iter_init(&iter); + !qemu_fwcfg_file_iter_end(&iter); + file = qemu_fwcfg_file_iter_next(&iter)) { + if (file->addr) { + free((void *)file->addr); + file->addr = 0; + } + } + } + + free(table_loader); + return 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) -- cgit v1.2.3 From 11636258981a083957c19f3979796fde5e7e8080 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 12 May 2016 12:03:35 -0600 Subject: Rename reset to sysreset The current reset API implements a method to reset the entire system. In the near future, I'd like to introduce code that implements the device tree reset bindings; i.e. the equivalent of the Linux kernel's reset API. This controls resets to individual HW blocks or external chips with reset signals. It doesn't make sense to merge the two APIs into one since they have different semantic purposes. Resolve the naming conflict by renaming the existing reset API to sysreset instead, so the new reset API can be called just reset. Signed-off-by: Stephen Warren Acked-by: Simon Glass --- drivers/misc/Kconfig | 10 ++-- drivers/misc/Makefile | 4 +- drivers/misc/reset-uclass.c | 81 -------------------------------- drivers/misc/reset_sandbox.c | 100 --------------------------------------- drivers/misc/sysreset-uclass.c | 81 ++++++++++++++++++++++++++++++++ drivers/misc/sysreset_sandbox.c | 101 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 189 insertions(+), 188 deletions(-) delete mode 100644 drivers/misc/reset-uclass.c delete mode 100644 drivers/misc/reset_sandbox.c create mode 100644 drivers/misc/sysreset-uclass.c create mode 100644 drivers/misc/sysreset_sandbox.c (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c40f6b577f3..2373037685c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -121,13 +121,13 @@ config PCA9551_I2C_ADDR help The I2C address of the PCA9551 LED controller. -config RESET - bool "Enable support for reset drivers" +config SYSRESET + bool "Enable support for system reset drivers" depends on DM help - Enable reset drivers which can be used to reset the CPU or board. - Each driver can provide a reset method which will be called to - effect a reset. The uclass will try all available drivers when + Enable system reset drivers which can be used to reset the CPU or + board. Each driver can provide a reset method which will be called + to effect a reset. The uclass will try all available drivers when reset_walk() is called. config WINBOND_W83627 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 98704f2085d..066639ba1f1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NS87308) += ns87308.o obj-$(CONFIG_PDSP188x) += pdsp188x.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o -obj-$(CONFIG_SANDBOX) += reset_sandbox.o +obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o ifdef CONFIG_DM_I2C obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o endif @@ -40,7 +40,7 @@ obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o -obj-$(CONFIG_RESET) += reset-uclass.o +obj-$(CONFIG_SYSRESET) += sysreset-uclass.o obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o obj-$(CONFIG_QFW) += qfw.o diff --git a/drivers/misc/reset-uclass.c b/drivers/misc/reset-uclass.c deleted file mode 100644 index fdb5c6fcff3..00000000000 --- a/drivers/misc/reset-uclass.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2015 Google, Inc - * Written by Simon Glass - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int reset_request(struct udevice *dev, enum reset_t type) -{ - struct reset_ops *ops = reset_get_ops(dev); - - if (!ops->request) - return -ENOSYS; - - return ops->request(dev, type); -} - -int reset_walk(enum reset_t type) -{ - struct udevice *dev; - int ret = -ENOSYS; - - while (ret != -EINPROGRESS && type < RESET_COUNT) { - for (uclass_first_device(UCLASS_RESET, &dev); - dev; - uclass_next_device(&dev)) { - ret = reset_request(dev, type); - if (ret == -EINPROGRESS) - break; - } - type++; - } - - return ret; -} - -void reset_walk_halt(enum reset_t type) -{ - int ret; - - ret = reset_walk(type); - - /* Wait for the reset to take effect */ - if (ret == -EINPROGRESS) - mdelay(100); - - /* Still no reset? Give up */ - printf("Reset not supported on this platform\n"); - hang(); -} - -/** - * reset_cpu() - calls reset_walk(RESET_WARM) - */ -void reset_cpu(ulong addr) -{ - reset_walk_halt(RESET_WARM); -} - - -int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - reset_walk_halt(RESET_WARM); - - return 0; -} - -UCLASS_DRIVER(reset) = { - .id = UCLASS_RESET, - .name = "reset", -}; diff --git a/drivers/misc/reset_sandbox.c b/drivers/misc/reset_sandbox.c deleted file mode 100644 index 2691bb031a5..00000000000 --- a/drivers/misc/reset_sandbox.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2015 Google, Inc - * Written by Simon Glass - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -static int sandbox_warm_reset_request(struct udevice *dev, enum reset_t type) -{ - struct sandbox_state *state = state_get_current(); - - switch (type) { - case RESET_WARM: - state->last_reset = type; - break; - default: - return -ENOSYS; - } - if (!state->reset_allowed[type]) - return -EACCES; - - return -EINPROGRESS; -} - -static int sandbox_reset_request(struct udevice *dev, enum reset_t type) -{ - struct sandbox_state *state = state_get_current(); - - /* - * If we have a device tree, the device we created from platform data - * (see the U_BOOT_DEVICE() declaration below) should not do anything. - * If we are that device, return an error. - */ - if (state->fdt_fname && dev->of_offset == -1) - return -ENODEV; - - switch (type) { - case RESET_COLD: - state->last_reset = type; - break; - case RESET_POWER: - state->last_reset = type; - if (!state->reset_allowed[type]) - return -EACCES; - sandbox_exit(); - break; - default: - return -ENOSYS; - } - if (!state->reset_allowed[type]) - return -EACCES; - - return -EINPROGRESS; -} - -static struct reset_ops sandbox_reset_ops = { - .request = sandbox_reset_request, -}; - -static const struct udevice_id sandbox_reset_ids[] = { - { .compatible = "sandbox,reset" }, - { } -}; - -U_BOOT_DRIVER(reset_sandbox) = { - .name = "reset_sandbox", - .id = UCLASS_RESET, - .of_match = sandbox_reset_ids, - .ops = &sandbox_reset_ops, -}; - -static struct reset_ops sandbox_warm_reset_ops = { - .request = sandbox_warm_reset_request, -}; - -static const struct udevice_id sandbox_warm_reset_ids[] = { - { .compatible = "sandbox,warm-reset" }, - { } -}; - -U_BOOT_DRIVER(warm_reset_sandbox) = { - .name = "warm_reset_sandbox", - .id = UCLASS_RESET, - .of_match = sandbox_warm_reset_ids, - .ops = &sandbox_warm_reset_ops, -}; - -/* This is here in case we don't have a device tree */ -U_BOOT_DEVICE(reset_sandbox_non_fdt) = { - .name = "reset_sandbox", -}; diff --git a/drivers/misc/sysreset-uclass.c b/drivers/misc/sysreset-uclass.c new file mode 100644 index 00000000000..e41efcaca60 --- /dev/null +++ b/drivers/misc/sysreset-uclass.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + struct sysreset_ops *ops = sysreset_get_ops(dev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(dev, type); +} + +int sysreset_walk(enum sysreset_t type) +{ + struct udevice *dev; + int ret = -ENOSYS; + + while (ret != -EINPROGRESS && type < SYSRESET_COUNT) { + for (uclass_first_device(UCLASS_SYSRESET, &dev); + dev; + uclass_next_device(&dev)) { + ret = sysreset_request(dev, type); + if (ret == -EINPROGRESS) + break; + } + type++; + } + + return ret; +} + +void sysreset_walk_halt(enum sysreset_t type) +{ + int ret; + + ret = sysreset_walk(type); + + /* Wait for the reset to take effect */ + if (ret == -EINPROGRESS) + mdelay(100); + + /* Still no reset? Give up */ + printf("System reset not supported on this platform\n"); + hang(); +} + +/** + * reset_cpu() - calls sysreset_walk(SYSRESET_WARM) + */ +void reset_cpu(ulong addr) +{ + sysreset_walk_halt(SYSRESET_WARM); +} + + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + sysreset_walk_halt(SYSRESET_WARM); + + return 0; +} + +UCLASS_DRIVER(sysreset) = { + .id = UCLASS_SYSRESET, + .name = "sysreset", +}; diff --git a/drivers/misc/sysreset_sandbox.c b/drivers/misc/sysreset_sandbox.c new file mode 100644 index 00000000000..7ae7f386ee0 --- /dev/null +++ b/drivers/misc/sysreset_sandbox.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static int sandbox_warm_sysreset_request(struct udevice *dev, + enum sysreset_t type) +{ + struct sandbox_state *state = state_get_current(); + + switch (type) { + case SYSRESET_WARM: + state->last_sysreset = type; + break; + default: + return -ENOSYS; + } + if (!state->sysreset_allowed[type]) + return -EACCES; + + return -EINPROGRESS; +} + +static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + struct sandbox_state *state = state_get_current(); + + /* + * If we have a device tree, the device we created from platform data + * (see the U_BOOT_DEVICE() declaration below) should not do anything. + * If we are that device, return an error. + */ + if (state->fdt_fname && dev->of_offset == -1) + return -ENODEV; + + switch (type) { + case SYSRESET_COLD: + state->last_sysreset = type; + break; + case SYSRESET_POWER: + state->last_sysreset = type; + if (!state->sysreset_allowed[type]) + return -EACCES; + sandbox_exit(); + break; + default: + return -ENOSYS; + } + if (!state->sysreset_allowed[type]) + return -EACCES; + + return -EINPROGRESS; +} + +static struct sysreset_ops sandbox_sysreset_ops = { + .request = sandbox_sysreset_request, +}; + +static const struct udevice_id sandbox_sysreset_ids[] = { + { .compatible = "sandbox,reset" }, + { } +}; + +U_BOOT_DRIVER(sysreset_sandbox) = { + .name = "sysreset_sandbox", + .id = UCLASS_SYSRESET, + .of_match = sandbox_sysreset_ids, + .ops = &sandbox_sysreset_ops, +}; + +static struct sysreset_ops sandbox_warm_sysreset_ops = { + .request = sandbox_warm_sysreset_request, +}; + +static const struct udevice_id sandbox_warm_sysreset_ids[] = { + { .compatible = "sandbox,warm-reset" }, + { } +}; + +U_BOOT_DRIVER(warm_sysreset_sandbox) = { + .name = "warm_sysreset_sandbox", + .id = UCLASS_SYSRESET, + .of_match = sandbox_warm_sysreset_ids, + .ops = &sandbox_warm_sysreset_ops, +}; + +/* This is here in case we don't have a device tree */ +U_BOOT_DEVICE(sysreset_sandbox_non_fdt) = { + .name = "sysreset_sandbox", +}; -- cgit v1.2.3 From 5c0862155c883b9e134fae72d25f1b5a93260510 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 14 May 2016 14:02:54 -0600 Subject: reset: Drop the reset failure message This adds to code size and is not needed, since hang() will print a message. Signed-off-by: Simon Glass --- drivers/misc/sysreset-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/sysreset-uclass.c b/drivers/misc/sysreset-uclass.c index e41efcaca60..3566d17fb1b 100644 --- a/drivers/misc/sysreset-uclass.c +++ b/drivers/misc/sysreset-uclass.c @@ -55,7 +55,7 @@ void sysreset_walk_halt(enum sysreset_t type) mdelay(100); /* Still no reset? Give up */ - printf("System reset not supported on this platform\n"); + debug("System reset not supported on this platform\n"); hang(); } -- cgit v1.2.3