diff options
Diffstat (limited to 'drivers/firmware')
35 files changed, 1392 insertions, 157 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index cac16c4b0df3..9026df923542 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # For a description of the syntax of this configuration file, # see Documentation/kbuild/kconfig-language.txt. @@ -5,20 +6,6 @@ menu "Firmware Drivers" -config ARM_PSCI_FW - bool - -config ARM_PSCI_CHECKER - bool "ARM PSCI checker" - depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST - help - Run the PSCI checker during startup. This checks that hotplug and - suspend operations work correctly when using PSCI. - - The torture tests may interfere with the PSCI checker by turning CPUs - on and off through hotplug, so for now torture tests and PSCI checker - are mutually exclusive. - config ARM_SCMI_PROTOCOL bool "ARM System Control and Management Interface (SCMI) Message Protocol" depends on ARM || ARM64 || COMPILE_TEST @@ -267,9 +254,26 @@ config TI_SCI_PROTOCOL This protocol library is used by client drivers to use the features provided by the system controller. +config TRUSTED_FOUNDATIONS + bool "Trusted Foundations secure monitor support" + depends on ARM + help + Some devices (including most early Tegra-based consumer devices on + the market) are booted with the Trusted Foundations secure monitor + active, requiring some core operations to be performed by the secure + monitor instead of the kernel. + + This option allows the kernel to invoke the secure monitor whenever + required on devices using Trusted Foundations. See the functions and + comments in linux/firmware/trusted_foundations.h or the device tree + bindings for "tlm,trusted-foundations" for details on how to use it. + + Choose N if you don't know what this is about. + config HAVE_ARM_SMCCC bool +source "drivers/firmware/psci/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 80feb635120f..3fa0b34eb72f 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -2,8 +2,6 @@ # # Makefile for the linux kernel. # -obj-$(CONFIG_ARM_PSCI_FW) += psci.o -obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o @@ -23,8 +21,10 @@ obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o +obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ +obj-y += psci/ obj-y += broadcom/ obj-y += meson/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 99e36c580fbc..c47d28d556b6 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-bus-y = bus.o scmi-driver-y = driver.o diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 8f952f2f1a29..b5bc4c7a8fab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -654,9 +654,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) static int scmi_mailbox_check(struct device_node *np) { - struct of_phandle_args arg; - - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } static int scmi_mbox_free_channel(int id, void *p, void *data) @@ -798,7 +796,9 @@ static int scmi_probe(struct platform_device *pdev) return -EINVAL; } - desc = of_match_device(scmi_of_match, dev)->data; + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index e6376f985ef7..9cd70d1a5622 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -165,6 +165,7 @@ static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0, return err; } +NOKPROBE_SYMBOL(invoke_sdei_fn); static struct sdei_event *sdei_event_find(u32 event_num) { @@ -879,6 +880,7 @@ static void sdei_smccc_smc(unsigned long function_id, { arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); } +NOKPROBE_SYMBOL(sdei_smccc_smc); static void sdei_smccc_hvc(unsigned long function_id, unsigned long arg0, unsigned long arg1, @@ -887,6 +889,7 @@ static void sdei_smccc_hvc(unsigned long function_id, { arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); } +NOKPROBE_SYMBOL(sdei_smccc_hvc); int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, sdei_event_callback *critical_cb) diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig index f77cdb3a041f..64680824f984 100644 --- a/drivers/firmware/broadcom/Kconfig +++ b/drivers/firmware/broadcom/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config BCM47XX_NVRAM bool "Broadcom NVRAM driver" depends on BCM47XX || ARCH_BCM_5301X diff --git a/drivers/firmware/broadcom/Makefile b/drivers/firmware/broadcom/Makefile index f93efc479b8b..72c7fdc20c77 100644 --- a/drivers/firmware/broadcom/Makefile +++ b/drivers/firmware/broadcom/Makefile @@ -1,2 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index ecf2eeb5f6f9..b6180023eba7 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * dmi-sysfs.c * diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 099d83e4e910..35ed56b9c34f 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/types.h> #include <linux/string.h> #include <linux/init.h> @@ -416,11 +417,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) nr++; } -void __init dmi_memdev_walk(void) +static void __init dmi_memdev_walk(void) { - if (!dmi_available) - return; - if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) { dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr); if (dmi_memdev) @@ -614,7 +612,7 @@ static int __init dmi_smbios3_present(const u8 *buf) return 1; } -void __init dmi_scan_machine(void) +static void __init dmi_scan_machine(void) { char __iomem *p, *q; char buf[32]; @@ -769,15 +767,20 @@ static int __init dmi_init(void) subsys_initcall(dmi_init); /** - * dmi_set_dump_stack_arch_desc - set arch description for dump_stack() + * dmi_setup - scan and setup DMI system information * - * Invoke dump_stack_set_arch_desc() with DMI system information so that - * DMI identifiers are printed out on task dumps. Arch boot code should - * call this function after dmi_scan_machine() if it wants to print out DMI - * identifiers on task dumps. + * Scan the DMI system information. This setups DMI identifiers + * (dmi_system_id) for printing it out on task dumps and prepares + * DIMM entry information (dmi_memdev_info) from the SMBIOS table + * for using this when reporting memory errors. */ -void __init dmi_set_dump_stack_arch_desc(void) +void __init dmi_setup(void) { + dmi_scan_machine(); + if (!dmi_available) + return; + + dmi_memdev_walk(); dump_stack_set_arch_desc("%s", dmi_ids_string); } @@ -841,7 +844,7 @@ static bool dmi_is_end_of_table(const struct dmi_system_id *dmi) * returns non zero or we hit the end. Callback function is called for * each successful match. Returns the number of matches. * - * dmi_scan_machine must be called before this function is called. + * dmi_setup must be called before this function is called. */ int dmi_check_system(const struct dmi_system_id *list) { @@ -871,7 +874,7 @@ EXPORT_SYMBOL(dmi_check_system); * Walk the blacklist table until the first match is found. Return the * pointer to the matching entry or NULL if there's no match. * - * dmi_scan_machine must be called before this function is called. + * dmi_setup must be called before this function is called. */ const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) { diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 190be0b1d109..d4ea929e8b34 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "EFI (Extensible Firmware Interface) Support" depends on EFI diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 0c1af675c338..e2ac5fa5531b 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -162,13 +162,11 @@ void efi_virtmap_unload(void) static int __init arm_dmi_init(void) { /* - * On arm64/ARM, DMI depends on UEFI, and dmi_scan_machine() needs to + * On arm64/ARM, DMI depends on UEFI, and dmi_setup() needs to * be called early because dmi_id_init(), which is an arch_initcall * itself, depends on dmi_scan_machine() having been called already. */ - dmi_scan_machine(); - if (dmi_available) - dmi_set_dump_stack_arch_desc(); + dmi_setup(); return 0; } core_initcall(arm_dmi_init); diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index b0103e16fc1b..0460c7581220 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -16,9 +16,9 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \ - $(DISABLE_STACKLEAK_PLUGIN) -cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \ +cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ + -fpie $(DISABLE_STACKLEAK_PLUGIN) +cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) @@ -71,7 +71,6 @@ CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y) lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y)) -STUBCOPY_RM-y := -R *ksymtab* -R *kcrctab* STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS @@ -86,12 +85,13 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE # this time, use objcopy and leave all sections in place. # quiet_cmd_stubcopy = STUBCPY $@ - cmd_stubcopy = if $(STRIP) --strip-debug $(STUBCOPY_RM-y) -o $@ $<; \ - then if $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y); \ - then (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \ - rm -f $@; /bin/false); \ - else $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; fi \ - else /bin/false; fi + cmd_stubcopy = \ + $(STRIP) --strip-debug -o $@ $<; \ + if $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y); then \ + echo "$@: absolute symbol references not allowed in the EFI stub" >&2; \ + /bin/false; \ + fi; \ + $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@ # # ARM discards the .data section because it disallows r/w data in the diff --git a/drivers/firmware/efi/test/Makefile b/drivers/firmware/efi/test/Makefile index bcd4577d40e6..4197088550e6 100644 --- a/drivers/firmware/efi/test/Makefile +++ b/drivers/firmware/efi/test/Makefile @@ -1 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_EFI_TEST) += efi_test.o diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 91a0404affe2..a3a6ca659ffa 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig GOOGLE_FIRMWARE bool "Google Firmware Drivers" default n diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 82ce1e6d261e..edaa4e5d84ad 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2010 Google Inc. All Rights Reserved. * Author: dlaurie@google.com (Duncan Laurie) diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index c0c0b4e4e281..f240946ed701 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -254,7 +254,7 @@ static int vpd_section_destroy(struct vpd_section *sec) static int vpd_sections_init(phys_addr_t physaddr) { - struct vpd_cbmem __iomem *temp; + struct vpd_cbmem *temp; struct vpd_cbmem header; int ret = 0; @@ -262,7 +262,7 @@ static int vpd_sections_init(phys_addr_t physaddr) if (!temp) return -ENOMEM; - memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem)); + memcpy(&header, temp, sizeof(struct vpd_cbmem)); memunmap(temp); if (header.magic != VPD_CBMEM_MAGIC) diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index 6a7a7c2c5b5f..42b566f8903f 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config IMX_SCU bool "IMX SCU Protocol driver" depends on IMX_MBOX diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 1b2e15b3c9ca..802c4ad8e8f9 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c new file mode 100644 index 000000000000..043833ad3c1a --- /dev/null +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + * Implementation of the SCU IRQ functions using MU. + * + */ + +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/mailbox_client.h> + +#define IMX_SC_IRQ_FUNC_ENABLE 1 +#define IMX_SC_IRQ_FUNC_STATUS 2 +#define IMX_SC_IRQ_NUM_GROUP 4 + +static u32 mu_resource_id; + +struct imx_sc_msg_irq_get_status { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + u8 group; + u8 reserved; + } __packed req; + struct { + u32 status; + } resp; + } data; +}; + +struct imx_sc_msg_irq_enable { + struct imx_sc_rpc_msg hdr; + u32 mask; + u16 resource; + u8 group; + u8 enable; +} __packed; + +static struct imx_sc_ipc *imx_sc_irq_ipc_handle; +static struct work_struct imx_sc_irq_work; +static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); + +int imx_scu_irq_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_register_notifier); + +int imx_scu_irq_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); + +static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) +{ + return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + status, (void *)group); +} + +static void imx_scu_irq_work_handler(struct work_struct *work) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u32 irq_status; + int ret; + u8 i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = i; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) { + pr_err("get irq group %d status failed, ret %d\n", + i, ret); + return; + } + + irq_status = msg.data.resp.status; + if (!irq_status) + continue; + + imx_scu_irq_notifier_call_chain(irq_status, &i); + } +} + +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) +{ + struct imx_sc_msg_irq_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_ENABLE; + hdr->size = 3; + + msg.resource = mu_resource_id; + msg.group = group; + msg.mask = mask; + msg.enable = enable; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + pr_err("enable irq failed, group %d, mask %d, ret %d\n", + group, mask, ret); + + return ret; +} +EXPORT_SYMBOL(imx_scu_irq_group_enable); + +static void imx_scu_irq_callback(struct mbox_client *c, void *msg) +{ + schedule_work(&imx_sc_irq_work); +} + +int imx_scu_enable_general_irq_channel(struct device *dev) +{ + struct of_phandle_args spec; + struct mbox_client *cl; + struct mbox_chan *ch; + int ret = 0, i = 0; + + ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); + if (ret) + return ret; + + cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->dev = dev; + cl->rx_callback = imx_scu_irq_callback; + + /* SCU general IRQ uses general interrupt channel 3 */ + ch = mbox_request_channel_byname(cl, "gip3"); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); + devm_kfree(dev, cl); + return ret; + } + + INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); + + if (!of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", 0, &spec)) + i = of_alias_get_id(spec.np, "mu"); + + /* use mu1 as general mu irq channel if failed */ + if (i < 0) + i = 1; + + mu_resource_id = IMX_SC_R_MU_0A + i; + + return ret; +} +EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2bb1a19c413f..04a24a863d6e 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -10,6 +10,7 @@ #include <linux/err.h> #include <linux/firmware/imx/types.h> #include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/sci.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> @@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_enable_general_irq_channel(dev); + if (ret) + dev_warn(dev, + "failed to enable general irq channel: %d\n", ret); + dev_info(dev, "NXP i.MX SCU Initialized\n"); return devm_of_platform_populate(dev); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 39a94c7177fc..480cec69e2c9 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -74,7 +74,10 @@ struct imx_sc_pd_range { char *name; u32 rsrc; u8 num; + + /* add domain index */ bool postfix; + u8 start_from; }; struct imx_sc_pd_soc { @@ -84,71 +87,75 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, 1 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, - { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, - { "con-nand", IMX_SC_R_NAND, 1, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, - - /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, - { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, - - /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + + /* VPU SS */ + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, + + /* MIPI SS */ + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, - /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, - { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + /* LVDS SS */ + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -236,7 +243,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, if (pd_ranges->postfix) snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, idx); + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); else snprintf(sc_pd->name, sizeof(sc_pd->name), "%s", pd_ranges->name); diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index c51462f5aa1e..a5dc0629f225 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -425,7 +425,7 @@ static ssize_t ibft_attr_show_acpitbl(void *data, int type, char *buf) switch (type) { case ISCSI_BOOT_ACPITBL_SIGNATURE: - str += sprintf_string(str, ACPI_NAME_SIZE, + str += sprintf_string(str, ACPI_NAMESEG_SIZE, entry->header->header.signature); break; case ISCSI_BOOT_ACPITBL_OEM_ID: diff --git a/drivers/firmware/meson/Kconfig b/drivers/firmware/meson/Kconfig index 170d7e8bcdfb..2671dcd0ad92 100644 --- a/drivers/firmware/meson/Kconfig +++ b/drivers/firmware/meson/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Amlogic Secure Monitor driver # diff --git a/drivers/firmware/meson/Makefile b/drivers/firmware/meson/Makefile index 9ab3884f96bc..c6c09483b622 100644 --- a/drivers/firmware/meson/Makefile +++ b/drivers/firmware/meson/Makefile @@ -1 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MESON_SM) += meson_sm.o diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig new file mode 100644 index 000000000000..97944168b5e6 --- /dev/null +++ b/drivers/firmware/psci/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +config ARM_PSCI_FW + bool + +config ARM_PSCI_CHECKER + bool "ARM PSCI checker" + depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST + help + Run the PSCI checker during startup. This checks that hotplug and + suspend operations work correctly when using PSCI. + + The torture tests may interfere with the PSCI checker by turning CPUs + on and off through hotplug, so for now torture tests and PSCI checker + are mutually exclusive. diff --git a/drivers/firmware/psci/Makefile b/drivers/firmware/psci/Makefile new file mode 100644 index 000000000000..1956b882470f --- /dev/null +++ b/drivers/firmware/psci/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +# +obj-$(CONFIG_ARM_PSCI_FW) += psci.o +obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci/psci.c index c80ec1d03274..fe090ef43d28 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci/psci.c @@ -88,6 +88,7 @@ static u32 psci_function_id[PSCI_FN_MAX]; PSCI_1_0_EXT_POWER_STATE_TYPE_MASK) static u32 psci_cpu_suspend_feature; +static bool psci_system_reset2_supported; static inline bool psci_has_ext_power_state(void) { @@ -95,6 +96,11 @@ static inline bool psci_has_ext_power_state(void) PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; } +static inline bool psci_has_osi_support(void) +{ + return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED; +} + static inline bool psci_power_state_loses_context(u32 state) { const u32 mask = psci_has_ext_power_state() ? @@ -253,7 +259,17 @@ static int get_set_conduit_method(struct device_node *np) static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) { - invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && + psci_system_reset2_supported) { + /* + * reset_type[31] = 0 (architectural) + * reset_type[30:0] = 0 (SYSTEM_WARM_RESET) + * cookie = 0 (ignored by the implementation) + */ + invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0); + } else { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + } } static void psci_sys_poweroff(void) @@ -270,9 +286,26 @@ static int __init psci_features(u32 psci_func_id) #ifdef CONFIG_CPU_IDLE static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); +static int psci_dt_parse_state_node(struct device_node *np, u32 *state) +{ + int err = of_property_read_u32(np, "arm,psci-suspend-param", state); + + if (err) { + pr_warn("%pOF missing arm,psci-suspend-param property\n", np); + return err; + } + + if (!psci_power_state_is_valid(*state)) { + pr_warn("Invalid PSCI power state %#x\n", *state); + return -EINVAL; + } + + return 0; +} + static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) { - int i, ret, count = 0; + int i, ret = 0, count = 0; u32 *psci_states; struct device_node *state_node; @@ -291,29 +324,16 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) return -ENOMEM; for (i = 0; i < count; i++) { - u32 state; - state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + ret = psci_dt_parse_state_node(state_node, &psci_states[i]); + of_node_put(state_node); - ret = of_property_read_u32(state_node, - "arm,psci-suspend-param", - &state); - if (ret) { - pr_warn(" * %pOF missing arm,psci-suspend-param property\n", - state_node); - of_node_put(state_node); + if (ret) goto free_mem; - } - of_node_put(state_node); - pr_debug("psci-power-state %#x index %d\n", state, i); - if (!psci_power_state_is_valid(state)) { - pr_warn("Invalid PSCI power state %#x\n", state); - ret = -EINVAL; - goto free_mem; - } - psci_states[i] = state; + pr_debug("psci-power-state %#x index %d\n", psci_states[i], i); } + /* Idle states parsed correctly, initialize per-cpu pointer */ per_cpu(psci_power_state, cpu) = psci_states; return 0; @@ -451,6 +471,16 @@ static const struct platform_suspend_ops psci_suspend_ops = { .enter = psci_system_suspend_enter, }; +static void __init psci_init_system_reset2(void) +{ + int ret; + + ret = psci_features(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2)); + + if (ret != PSCI_RET_NOT_SUPPORTED) + psci_system_reset2_supported = true; +} + static void __init psci_init_system_suspend(void) { int ret; @@ -588,6 +618,7 @@ static int __init psci_probe(void) psci_init_smccc(); psci_init_cpu_suspend(); psci_init_system_suspend(); + psci_init_system_reset2(); } return 0; @@ -605,9 +636,9 @@ static int __init psci_0_2_init(struct device_node *np) int err; err = get_set_conduit_method(np); - if (err) - goto out_put_node; + return err; + /* * Starting with v0.2, the PSCI specification introduced a call * (PSCI_VERSION) that allows probing the firmware version, so @@ -615,11 +646,7 @@ static int __init psci_0_2_init(struct device_node *np) * can be carried out according to the specific version reported * by firmware */ - err = psci_probe(); - -out_put_node: - of_node_put(np); - return err; + return psci_probe(); } /* @@ -631,9 +658,8 @@ static int __init psci_0_1_init(struct device_node *np) int err; err = get_set_conduit_method(np); - if (err) - goto out_put_node; + return err; pr_info("Using PSCI v0.1 Function IDs from DT\n"); @@ -657,15 +683,27 @@ static int __init psci_0_1_init(struct device_node *np) psci_ops.migrate = psci_migrate; } -out_put_node: - of_node_put(np); - return err; + return 0; +} + +static int __init psci_1_0_init(struct device_node *np) +{ + int err; + + err = psci_0_2_init(np); + if (err) + return err; + + if (psci_has_osi_support()) + pr_info("OSI mode supported.\n"); + + return 0; } static const struct of_device_id psci_of_match[] __initconst = { { .compatible = "arm,psci", .data = psci_0_1_init}, { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, - { .compatible = "arm,psci-1.0", .data = psci_0_2_init}, + { .compatible = "arm,psci-1.0", .data = psci_1_0_init}, {}, }; @@ -674,6 +712,7 @@ int __init psci_dt_init(void) struct device_node *np; const struct of_device_id *matched_np; psci_initcall_t init_fn; + int ret; np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); @@ -681,7 +720,10 @@ int __init psci_dt_init(void) return -ENODEV; init_fn = (psci_initcall_t)matched_np->data; - return init_fn(np); + ret = init_fn(np); + + of_node_put(np); + return ret; } #ifdef CONFIG_ACPI diff --git a/drivers/firmware/psci_checker.c b/drivers/firmware/psci/psci_checker.c index 346943657962..346943657962 100644 --- a/drivers/firmware/psci_checker.c +++ b/drivers/firmware/psci/psci_checker.c diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index ff2730d5c468..a887731f50d6 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "Tegra firmware driver" config TEGRA_IVC diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 676b01caff05..49c87e00fafb 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only tegra-bpmp-y = bpmp.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 3fbbb61012c4..ef93406ace1b 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -65,18 +65,36 @@ struct ti_sci_xfers_info { }; /** + * struct ti_sci_rm_type_map - Structure representing TISCI Resource + * management representation of dev_ids. + * @dev_id: TISCI device ID + * @type: Corresponding id as identified by TISCI RM. + * + * Note: This is used only as a work around for using RM range apis + * for AM654 SoC. For future SoCs dev_id will be used as type + * for RM range APIs. In order to maintain ABI backward compatibility + * type is not being changed for AM654 SoC. + */ +struct ti_sci_rm_type_map { + u32 dev_id; + u16 type; +}; + +/** * struct ti_sci_desc - Description of SoC integration * @default_host_id: Host identifier representing the compute entity * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) * @max_msgs: Maximum number of messages that can be pending * simultaneously in the system * @max_msg_size: Maximum size of data per message that can be handled. + * @rm_type_map: RM resource type mapping structure. */ struct ti_sci_desc { u8 default_host_id; int max_rx_timeout_ms; int max_msgs; int max_msg_size; + struct ti_sci_rm_type_map *rm_type_map; }; /** @@ -1600,6 +1618,392 @@ fail: return ret; } +static int ti_sci_get_resource_type(struct ti_sci_info *info, u16 dev_id, + u16 *type) +{ + struct ti_sci_rm_type_map *rm_type_map = info->desc->rm_type_map; + bool found = false; + int i; + + /* If map is not provided then assume dev_id is used as type */ + if (!rm_type_map) { + *type = dev_id; + return 0; + } + + for (i = 0; rm_type_map[i].dev_id; i++) { + if (rm_type_map[i].dev_id == dev_id) { + *type = rm_type_map[i].type; + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + return 0; +} + +/** + * ti_sci_get_resource_range - Helper to get a range of resources assigned + * to a host. Resource is uniquely identified by + * type and subtype. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + struct ti_sci_msg_resp_get_resource_range *resp; + struct ti_sci_msg_req_get_resource_range *req; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + u16 type; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + + ret = ti_sci_get_resource_type(info, dev_id, &type); + if (ret) { + dev_err(dev, "rm type lookup failed for %u\n", dev_id); + goto fail; + } + + req = (struct ti_sci_msg_req_get_resource_range *)xfer->xfer_buf; + req->secondary_host = s_host; + req->type = type & MSG_RM_RESOURCE_TYPE_MASK; + req->subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->xfer_buf; + + if (!ti_sci_is_response_ack(resp)) { + ret = -ENODEV; + } else if (!resp->range_start && !resp->range_num) { + ret = -ENODEV; + } else { + *range_start = resp->range_start; + *range_num = resp->range_num; + }; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_get_resource_range - Get a range of resources assigned to host + * that is same as ti sci interface host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, + TI_SCI_IRQ_SECONDARY_HOST_INVALID, + range_start, range_num); +} + +/** + * ti_sci_cmd_get_resource_range_from_shost - Get a range of resources + * assigned to a specified host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static +int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, + range_start, range_num); +} + +/** + * ti_sci_manage_irq() - Helper api to configure/release the irq route between + * the requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * @type: Request type irq set or release. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_manage_irq(const struct ti_sci_handle *handle, + u32 valid_params, u16 src_id, u16 src_index, + u16 dst_id, u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host, + u16 type) +{ + struct ti_sci_msg_req_manage_irq *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, type, TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_manage_irq *)xfer->xfer_buf; + req->valid_params = valid_params; + req->src_id = src_id; + req->src_index = src_index; + req->dst_id = dst_id; + req->dst_host_irq = dst_host_irq; + req->ia_id = ia_id; + req->vint = vint; + req->global_event = global_event; + req->vint_status_bit = vint_status_bit; + req->secondary_host = s_host; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_set_irq() - Helper api to configure the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ set with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_SET_IRQ); +} + +/** + * ti_sci_free_irq() - Helper api to free the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ release with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_FREE_IRQ); +} + +/** + * ti_sci_cmd_set_irq() - Configure a host irq route between the requested + * source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_set_event_map() - Configure an event based irq route between the + * requested source and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | MSG_FLAG_VINT_VALID | + MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + +/** + * ti_sci_cmd_free_irq() - Free a host irq route between the between the + * requested source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_free_event_map() - Free an event map between the requested source + * and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | + MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + /* * ti_sci_setup_ops() - Setup the operations structures * @info: pointer to TISCI pointer @@ -1610,6 +2014,8 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) struct ti_sci_core_ops *core_ops = &ops->core_ops; struct ti_sci_dev_ops *dops = &ops->dev_ops; struct ti_sci_clk_ops *cops = &ops->clk_ops; + struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops; + struct ti_sci_rm_irq_ops *iops = &ops->rm_irq_ops; core_ops->reboot_device = ti_sci_cmd_core_reboot; @@ -1640,6 +2046,15 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq; cops->set_freq = ti_sci_cmd_clk_set_freq; cops->get_freq = ti_sci_cmd_clk_get_freq; + + rm_core_ops->get_range = ti_sci_cmd_get_resource_range; + rm_core_ops->get_range_from_shost = + ti_sci_cmd_get_resource_range_from_shost; + + iops->set_irq = ti_sci_cmd_set_irq; + iops->set_event_map = ti_sci_cmd_set_event_map; + iops->free_irq = ti_sci_cmd_free_irq; + iops->free_event_map = ti_sci_cmd_free_event_map; } /** @@ -1764,6 +2179,219 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) } EXPORT_SYMBOL_GPL(devm_ti_sci_get_handle); +/** + * ti_sci_get_by_phandle() - Get the TI SCI handle using DT phandle + * @np: device node + * @property: property name containing phandle on TISCI node + * + * NOTE: The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * ti_sci_put_handle must be balanced with successful ti_sci_get_by_phandle + * Return: pointer to handle if successful, else: + * -EPROBE_DEFER if the instance is not ready + * -ENODEV if the required node handler is missing + * -EINVAL if invalid conditions are encountered. + */ +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, + const char *property) +{ + struct ti_sci_handle *handle = NULL; + struct device_node *ti_sci_np; + struct ti_sci_info *info; + struct list_head *p; + + if (!np) { + pr_err("I need a device pointer\n"); + return ERR_PTR(-EINVAL); + } + + ti_sci_np = of_parse_phandle(np, property, 0); + if (!ti_sci_np) + return ERR_PTR(-ENODEV); + + mutex_lock(&ti_sci_list_mutex); + list_for_each(p, &ti_sci_list) { + info = list_entry(p, struct ti_sci_info, node); + if (ti_sci_np == info->dev->of_node) { + handle = &info->handle; + info->users++; + break; + } + } + mutex_unlock(&ti_sci_list_mutex); + of_node_put(ti_sci_np); + + if (!handle) + return ERR_PTR(-EPROBE_DEFER); + + return handle; +} +EXPORT_SYMBOL_GPL(ti_sci_get_by_phandle); + +/** + * devm_ti_sci_get_by_phandle() - Managed get handle using phandle + * @dev: Device pointer requesting TISCI handle + * @property: property name containing phandle on TISCI node + * + * NOTE: This releases the handle once the device resources are + * no longer needed. MUST NOT BE released with ti_sci_put_handle. + * The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * + * Return: 0 if all went fine, else corresponding error. + */ +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, + const char *property) +{ + const struct ti_sci_handle *handle; + const struct ti_sci_handle **ptr; + + ptr = devres_alloc(devm_ti_sci_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + handle = ti_sci_get_by_phandle(dev_of_node(dev), property); + + if (!IS_ERR(handle)) { + *ptr = handle; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return handle; +} +EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle); + +/** + * ti_sci_get_free_resource() - Get a free resource from TISCI resource. + * @res: Pointer to the TISCI resource + * + * Return: resource num if all went ok else TI_SCI_RESOURCE_NULL. + */ +u16 ti_sci_get_free_resource(struct ti_sci_resource *res) +{ + unsigned long flags; + u16 set, free_bit; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + free_bit = find_first_zero_bit(res->desc[set].res_map, + res->desc[set].num); + if (free_bit != res->desc[set].num) { + set_bit(free_bit, res->desc[set].res_map); + raw_spin_unlock_irqrestore(&res->lock, flags); + return res->desc[set].start + free_bit; + } + } + raw_spin_unlock_irqrestore(&res->lock, flags); + + return TI_SCI_RESOURCE_NULL; +} +EXPORT_SYMBOL_GPL(ti_sci_get_free_resource); + +/** + * ti_sci_release_resource() - Release a resource from TISCI resource. + * @res: Pointer to the TISCI resource + * @id: Resource id to be released. + */ +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) +{ + unsigned long flags; + u16 set; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + if (res->desc[set].start <= id && + (res->desc[set].num + res->desc[set].start) > id) + clear_bit(id - res->desc[set].start, + res->desc[set].res_map); + } + raw_spin_unlock_irqrestore(&res->lock, flags); +} +EXPORT_SYMBOL_GPL(ti_sci_release_resource); + +/** + * ti_sci_get_num_resources() - Get the number of resources in TISCI resource + * @res: Pointer to the TISCI resource + * + * Return: Total number of available resources. + */ +u32 ti_sci_get_num_resources(struct ti_sci_resource *res) +{ + u32 set, count = 0; + + for (set = 0; set < res->sets; set++) + count += res->desc[set].num; + + return count; +} +EXPORT_SYMBOL_GPL(ti_sci_get_num_resources); + +/** + * devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to a device + * @handle: TISCI handle + * @dev: Device pointer to which the resource is assigned + * @dev_id: TISCI device id to which the resource is assigned + * @of_prop: property name by which the resource are represented + * + * Return: Pointer to ti_sci_resource if all went well else appropriate + * error pointer. + */ +struct ti_sci_resource * +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, + struct device *dev, u32 dev_id, char *of_prop) +{ + struct ti_sci_resource *res; + u32 resource_subtype; + int i, ret; + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + res->sets = of_property_count_elems_of_size(dev_of_node(dev), of_prop, + sizeof(u32)); + if (res->sets < 0) { + dev_err(dev, "%s resource type ids not available\n", of_prop); + return ERR_PTR(res->sets); + } + + res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc), + GFP_KERNEL); + if (!res->desc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < res->sets; i++) { + ret = of_property_read_u32_index(dev_of_node(dev), of_prop, i, + &resource_subtype); + if (ret) + return ERR_PTR(-EINVAL); + + ret = handle->ops.rm_core_ops.get_range(handle, dev_id, + resource_subtype, + &res->desc[i].start, + &res->desc[i].num); + if (ret) { + dev_err(dev, "dev = %d subtype %d not allocated for this host\n", + dev_id, resource_subtype); + return ERR_PTR(ret); + } + + dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n", + dev_id, resource_subtype, res->desc[i].start, + res->desc[i].num); + + res->desc[i].res_map = + devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) * + sizeof(*res->desc[i].res_map), GFP_KERNEL); + if (!res->desc[i].res_map) + return ERR_PTR(-ENOMEM); + } + raw_spin_lock_init(&res->lock); + + return res; +} + static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd) { @@ -1784,10 +2412,33 @@ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ .max_msgs = 20, .max_msg_size = 64, + .rm_type_map = NULL, +}; + +static struct ti_sci_rm_type_map ti_sci_am654_rm_type_map[] = { + {.dev_id = 56, .type = 0x00b}, /* GIC_IRQ */ + {.dev_id = 179, .type = 0x000}, /* MAIN_NAV_UDMASS_IA0 */ + {.dev_id = 187, .type = 0x009}, /* MAIN_NAV_RA */ + {.dev_id = 188, .type = 0x006}, /* MAIN_NAV_UDMAP */ + {.dev_id = 194, .type = 0x007}, /* MCU_NAV_UDMAP */ + {.dev_id = 195, .type = 0x00a}, /* MCU_NAV_RA */ + {.dev_id = 0, .type = 0x000}, /* end of table */ +}; + +/* Description for AM654 */ +static const struct ti_sci_desc ti_sci_pmmc_am654_desc = { + .default_host_id = 12, + /* Conservative duration */ + .max_rx_timeout_ms = 10000, + /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ + .max_msgs = 20, + .max_msg_size = 60, + .rm_type_map = ti_sci_am654_rm_type_map, }; static const struct of_device_id ti_sci_of_match[] = { {.compatible = "ti,k2g-sci", .data = &ti_sci_pmmc_k2g_desc}, + {.compatible = "ti,am654-sci", .data = &ti_sci_pmmc_am654_desc}, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, ti_sci_of_match); diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 12bf316b68df..4983827151bf 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -35,6 +35,13 @@ #define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d #define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e +/* Resource Management Requests */ +#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 + +/* IRQ requests */ +#define TI_SCI_MSG_SET_IRQ 0x1000 +#define TI_SCI_MSG_FREE_IRQ 0x1001 + /** * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses * @type: Type of messages: One of TI_SCI_MSG* values @@ -461,4 +468,99 @@ struct ti_sci_msg_resp_get_clock_freq { u64 freq_hz; } __packed; +#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff + +/** + * struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned + * range of resources. + * @hdr: Generic Header + * @type: Unique resource assignment type + * @subtype: Resource assignment subtype within the resource type. + * @secondary_host: Host processing entity to which the resources are + * allocated. This is required only when the destination + * host id id different from ti sci interface host id, + * else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed. + * + * Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested + * resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_req_get_resource_range { + struct ti_sci_msg_hdr hdr; +#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0) +#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0) + u16 type; + u8 subtype; + u8 secondary_host; +} __packed; + +/** + * struct ti_sci_msg_resp_get_resource_range - Response to resource get range. + * @hdr: Generic Header + * @range_start: Start index of the resource range. + * @range_num: Number of resources in the range. + * + * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_resp_get_resource_range { + struct ti_sci_msg_hdr hdr; + u16 range_start; + u16 range_num; +} __packed; + +/** + * struct ti_sci_msg_req_manage_irq - Request to configure/release the route + * between the dev and the host. + * @hdr: Generic Header + * @valid_params: Bit fields defining the validity of interrupt source + * parameters. If a bit is not set, then corresponding + * field is not valid and will not be used for route set. + * Bit field definitions: + * 0 - Valid bit for @dst_id + * 1 - Valid bit for @dst_host_irq + * 2 - Valid bit for @ia_id + * 3 - Valid bit for @vint + * 4 - Valid bit for @global_event + * 5 - Valid bit for @vint_status_bit_index + * 31 - Valid bit for @secondary_host + * @src_id: IRQ source peripheral ID. + * @src_index: IRQ source index within the peripheral + * @dst_id: IRQ Destination ID. Based on the architecture it can be + * IRQ controller or host processor ID. + * @dst_host_irq: IRQ number of the destination host IRQ controller + * @ia_id: Device ID of the interrupt aggregator in which the + * vint resides. + * @vint: Virtual interrupt number if the interrupt route + * is through an interrupt aggregator. + * @global_event: Global event that is to be mapped to interrupt + * aggregator virtual interrupt status bit. + * @vint_status_bit: Virtual interrupt status bit if the interrupt route + * utilizes an interrupt aggregator status bit. + * @secondary_host: Host ID of the IRQ destination computing entity. This is + * required only when destination host id is different + * from ti sci interface host id. + * + * Request type is TI_SCI_MSG_SET/RELEASE_IRQ. + * Response is generic ACK / NACK message. + */ +struct ti_sci_msg_req_manage_irq { + struct ti_sci_msg_hdr hdr; +#define MSG_FLAG_DST_ID_VALID TI_SCI_MSG_FLAG(0) +#define MSG_FLAG_DST_HOST_IRQ_VALID TI_SCI_MSG_FLAG(1) +#define MSG_FLAG_IA_ID_VALID TI_SCI_MSG_FLAG(2) +#define MSG_FLAG_VINT_VALID TI_SCI_MSG_FLAG(3) +#define MSG_FLAG_GLB_EVNT_VALID TI_SCI_MSG_FLAG(4) +#define MSG_FLAG_VINT_STS_BIT_VALID TI_SCI_MSG_FLAG(5) +#define MSG_FLAG_SHOST_VALID TI_SCI_MSG_FLAG(31) + u32 valid_params; + u16 src_id; + u16 src_index; + u16 dst_id; + u16 dst_host_irq; + u16 ia_id; + u16 vint; + u16 global_event; + u8 vint_status_bit; + u8 secondary_host; +} __packed; + #endif /* __TI_SCI_H */ diff --git a/drivers/firmware/trusted_foundations.c b/drivers/firmware/trusted_foundations.c new file mode 100644 index 000000000000..fd4999388ff1 --- /dev/null +++ b/drivers/firmware/trusted_foundations.c @@ -0,0 +1,176 @@ +/* + * Trusted Foundations support for ARM CPUs + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of.h> + +#include <linux/firmware/trusted_foundations.h> + +#include <asm/firmware.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/outercache.h> + +#define TF_CACHE_MAINT 0xfffff100 + +#define TF_CACHE_ENABLE 1 +#define TF_CACHE_DISABLE 2 + +#define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200 + +#define TF_CPU_PM 0xfffffffc +#define TF_CPU_PM_S3 0xffffffe3 +#define TF_CPU_PM_S2 0xffffffe6 +#define TF_CPU_PM_S2_NO_MC_CLK 0xffffffe5 +#define TF_CPU_PM_S1 0xffffffe4 +#define TF_CPU_PM_S1_NOFLUSH_L2 0xffffffe7 + +static unsigned long cpu_boot_addr; + +static void tf_generic_smc(u32 type, u32 arg1, u32 arg2) +{ + register u32 r0 asm("r0") = type; + register u32 r1 asm("r1") = arg1; + register u32 r2 asm("r2") = arg2; + + asm volatile( + ".arch_extension sec\n\t" + "stmfd sp!, {r4 - r11}\n\t" + __asmeq("%0", "r0") + __asmeq("%1", "r1") + __asmeq("%2", "r2") + "mov r3, #0\n\t" + "mov r4, #0\n\t" + "smc #0\n\t" + "ldmfd sp!, {r4 - r11}\n\t" + : + : "r" (r0), "r" (r1), "r" (r2) + : "memory", "r3", "r12", "lr"); +} + +static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr) +{ + cpu_boot_addr = boot_addr; + tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, cpu_boot_addr, 0); + + return 0; +} + +static int tf_prepare_idle(unsigned long mode) +{ + switch (mode) { + case TF_PM_MODE_LP0: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S3, cpu_boot_addr); + break; + + case TF_PM_MODE_LP1: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2, cpu_boot_addr); + break; + + case TF_PM_MODE_LP1_NO_MC_CLK: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2_NO_MC_CLK, + cpu_boot_addr); + break; + + case TF_PM_MODE_LP2: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1, cpu_boot_addr); + break; + + case TF_PM_MODE_LP2_NOFLUSH_L2: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1_NOFLUSH_L2, + cpu_boot_addr); + break; + + default: + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_CACHE_L2X0 +static void tf_cache_write_sec(unsigned long val, unsigned int reg) +{ + u32 l2x0_way_mask = 0xff; + + switch (reg) { + case L2X0_CTRL: + if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_ASSOCIATIVITY_16) + l2x0_way_mask = 0xffff; + + if (val == L2X0_CTRL_EN) + tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_ENABLE, + l2x0_saved_regs.aux_ctrl); + else + tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_DISABLE, + l2x0_way_mask); + break; + + default: + break; + } +} + +static int tf_init_cache(void) +{ + outer_cache.write_sec = tf_cache_write_sec; + + return 0; +} +#endif /* CONFIG_CACHE_L2X0 */ + +static const struct firmware_ops trusted_foundations_ops = { + .set_cpu_boot_addr = tf_set_cpu_boot_addr, + .prepare_idle = tf_prepare_idle, +#ifdef CONFIG_CACHE_L2X0 + .l2x0_init = tf_init_cache, +#endif +}; + +void register_trusted_foundations(struct trusted_foundations_platform_data *pd) +{ + /* + * we are not using version information for now since currently + * supported SMCs are compatible with all TF releases + */ + register_firmware_ops(&trusted_foundations_ops); +} + +void of_register_trusted_foundations(void) +{ + struct device_node *node; + struct trusted_foundations_platform_data pdata; + int err; + + node = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"); + if (!node) + return; + + err = of_property_read_u32(node, "tlm,version-major", + &pdata.version_major); + if (err != 0) + panic("Trusted Foundation: missing version-major property\n"); + err = of_property_read_u32(node, "tlm,version-minor", + &pdata.version_minor); + if (err != 0) + panic("Trusted Foundation: missing version-minor property\n"); + register_trusted_foundations(&pdata); +} + +bool trusted_foundations_registered(void) +{ + return firmware_ops == &trusted_foundations_ops; +} diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 2771df6df379..c6d0724da4db 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -90,9 +90,6 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) int ret; struct zynqmp_pm_query_data qdata = {0}; - if (!eemi_ops) - return -ENXIO; - switch (pm_id) { case PM_GET_API_VERSION: ret = eemi_ops->get_api_version(&pm_api_version); @@ -163,21 +160,14 @@ static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, strcpy(debugfs_buf, ""); - if (*off != 0 || len == 0) + if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1) return -EINVAL; - kern_buff = kzalloc(len, GFP_KERNEL); - if (!kern_buff) - return -ENOMEM; - + kern_buff = memdup_user_nul(ptr, len); + if (IS_ERR(kern_buff)) + return PTR_ERR(kern_buff); tmp_buff = kern_buff; - ret = strncpy_from_user(kern_buff, ptr, len); - if (ret < 0) { - ret = -EFAULT; - goto err; - } - /* Read the API name from a user request */ pm_api_req = strsep(&kern_buff, " "); diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 98f936125643..fd3d83745208 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -24,6 +24,8 @@ #include <linux/firmware/xlnx-zynqmp.h> #include "zynqmp-debug.h" +static const struct zynqmp_eemi_ops *eemi_ops_tbl; + static const struct mfd_cell firmware_devs[] = { { .name = "zynqmp_power_controller", @@ -538,6 +540,49 @@ static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, } /** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Bitstream type + * -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration + * -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration + * + * This function provides access to pmufw. To transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(const u64 address, const u32 size, + const u32 flags) +{ + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, lower_32_bits(address), + upper_32_bits(address), size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the pmufw to get the PCAP + * status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + +/** * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller * master has initialized its own power management * @@ -640,6 +685,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { .request_node = zynqmp_pm_request_node, .release_node = zynqmp_pm_release_node, .set_requirement = zynqmp_pm_set_requirement, + .fpga_load = zynqmp_pm_fpga_load, + .fpga_get_status = zynqmp_pm_fpga_get_status, }; /** @@ -649,7 +696,11 @@ static const struct zynqmp_eemi_ops eemi_ops = { */ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { - return &eemi_ops; + if (eemi_ops_tbl) + return eemi_ops_tbl; + else + return ERR_PTR(-EPROBE_DEFER); + } EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops); @@ -694,6 +745,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) pr_info("%s Trustzone version v%d.%d\n", __func__, pm_tz_version >> 16, pm_tz_version & 0xFFFF); + /* Assign eemi_ops_table */ + eemi_ops_tbl = &eemi_ops; + zynqmp_pm_api_debugfs_init(); ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, |