diff options
Diffstat (limited to 'drivers/firmware')
26 files changed, 497 insertions, 55 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index d78dd3c82e9d..3315e3c21586 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -7,7 +7,7 @@ menu "Firmware Drivers" config ARM_SCMI_PROTOCOL - bool "ARM System Control and Management Interface (SCMI) Message Protocol" + tristate "ARM System Control and Management Interface (SCMI) Message Protocol" depends on ARM || ARM64 || COMPILE_TEST depends on MAILBOX help diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 99510be9f5ed..5e013b6a3692 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -22,7 +22,7 @@ obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o -obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ +obj-y += arm_scmi/ 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 6f9cbc4aef22..bc0d54f8e861 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -1,9 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o scmi-bus-y = bus.o scmi-driver-y = driver.o notify.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o +scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ + $(scmi-transport-y) +obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index db55c43a2cbd..1377ec76a45d 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -230,7 +230,7 @@ static void scmi_devices_unregister(void) bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister); } -static int __init scmi_bus_init(void) +int __init scmi_bus_init(void) { int retval; @@ -240,12 +240,10 @@ static int __init scmi_bus_init(void) return retval; } -subsys_initcall(scmi_bus_init); -static void __exit scmi_bus_exit(void) +void __exit scmi_bus_exit(void) { scmi_devices_unregister(); bus_unregister(&scmi_bus_type); ida_destroy(&scmi_bus_id); } -module_exit(scmi_bus_exit); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 75e39882746e..c1cfe3ee3d55 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -318,7 +318,7 @@ scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id) return clk; } -static struct scmi_clk_ops clk_ops = { +static const struct scmi_clk_ops clk_ops = { .count_get = scmi_clock_count_get, .info_get = scmi_clock_info_get, .rate_get = scmi_clock_rate_get, @@ -364,9 +364,4 @@ static int scmi_clock_protocol_init(struct scmi_handle *handle) return 0; } -static int __init scmi_clock_init(void) -{ - return scmi_protocol_register(SCMI_PROTOCOL_CLOCK, - &scmi_clock_protocol_init); -} -subsys_initcall(scmi_clock_init); +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_CLOCK, clock) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index c113e578cc6c..37fb583f1bf5 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -156,6 +156,30 @@ void scmi_setup_protocol_implemented(const struct scmi_handle *handle, int scmi_base_protocol_init(struct scmi_handle *h); +int __init scmi_bus_init(void); +void __exit scmi_bus_exit(void); + +#define DECLARE_SCMI_REGISTER_UNREGISTER(func) \ + int __init scmi_##func##_register(void); \ + void __exit scmi_##func##_unregister(void) +DECLARE_SCMI_REGISTER_UNREGISTER(clock); +DECLARE_SCMI_REGISTER_UNREGISTER(perf); +DECLARE_SCMI_REGISTER_UNREGISTER(power); +DECLARE_SCMI_REGISTER_UNREGISTER(reset); +DECLARE_SCMI_REGISTER_UNREGISTER(sensors); +DECLARE_SCMI_REGISTER_UNREGISTER(system); + +#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \ +int __init scmi_##name##_register(void) \ +{ \ + return scmi_protocol_register((id), &scmi_##name##_protocol_init); \ +} \ +\ +void __exit scmi_##name##_unregister(void) \ +{ \ + scmi_protocol_unregister((id)); \ +} + /* SCMI Transport */ /** * struct scmi_chan_info - Structure representing a SCMI channel information @@ -210,7 +234,7 @@ struct scmi_transport_ops { * @max_msg_size: Maximum size of data per message that can be handled. */ struct scmi_desc { - struct scmi_transport_ops *ops; + const struct scmi_transport_ops *ops; int max_rx_timeout_ms; int max_msg; int max_msg_size; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 03ec74242c14..c5dea87edf8f 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -730,6 +730,7 @@ struct scmi_prot_devnames { static struct scmi_prot_devnames devnames[] = { { SCMI_PROTOCOL_POWER, { "genpd" },}, + { SCMI_PROTOCOL_SYSTEM, { "syspower" },}, { SCMI_PROTOCOL_PERF, { "cpufreq" },}, { SCMI_PROTOCOL_CLOCK, { "clocks" },}, { SCMI_PROTOCOL_SENSOR, { "hwmon" },}, @@ -928,7 +929,35 @@ static struct platform_driver scmi_driver = { .remove = scmi_remove, }; -module_platform_driver(scmi_driver); +static int __init scmi_driver_init(void) +{ + scmi_bus_init(); + + scmi_clock_register(); + scmi_perf_register(); + scmi_power_register(); + scmi_reset_register(); + scmi_sensors_register(); + scmi_system_register(); + + return platform_driver_register(&scmi_driver); +} +subsys_initcall(scmi_driver_init); + +static void __exit scmi_driver_exit(void) +{ + scmi_bus_exit(); + + scmi_clock_unregister(); + scmi_perf_unregister(); + scmi_power_unregister(); + scmi_reset_unregister(); + scmi_sensors_unregister(); + scmi_system_unregister(); + + platform_driver_unregister(&scmi_driver); +} +module_exit(scmi_driver_exit); MODULE_ALIAS("platform: arm-scmi"); MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 6998dc86b5ce..4626404be541 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -110,7 +110,7 @@ static int mailbox_chan_free(int id, void *p, void *data) struct scmi_chan_info *cinfo = p; struct scmi_mailbox *smbox = cinfo->transport_info; - if (!IS_ERR(smbox->chan)) { + if (smbox && !IS_ERR(smbox->chan)) { mbox_free_channel(smbox->chan); cinfo->transport_info = NULL; smbox->chan = NULL; @@ -181,7 +181,7 @@ mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) return shmem_poll_done(smbox->shmem, xfer); } -static struct scmi_transport_ops scmi_mailbox_ops = { +static const struct scmi_transport_ops scmi_mailbox_ops = { .chan_available = mailbox_chan_available, .chan_setup = mailbox_chan_setup, .chan_free = mailbox_chan_free, diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 4731daaacd19..2754f9d01636 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1421,7 +1421,7 @@ static void scmi_protocols_late_init(struct work_struct *work) * notify_ops are attached to the handle so that can be accessed * directly from an scmi_driver to register its own notifiers. */ -static struct scmi_notify_ops notify_ops = { +static const struct scmi_notify_ops notify_ops = { .register_event_notifier = scmi_register_notifier, .unregister_event_notifier = scmi_unregister_notifier, }; diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 3e1e87012c95..ed475b40bd08 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -748,7 +748,7 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle, return dom->fc_info && dom->fc_info->level_set_addr; } -static struct scmi_perf_ops perf_ops = { +static const struct scmi_perf_ops perf_ops = { .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, .level_set = scmi_perf_level_set, @@ -890,9 +890,4 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle) return 0; } -static int __init scmi_perf_init(void) -{ - return scmi_protocol_register(SCMI_PROTOCOL_PERF, - &scmi_perf_protocol_init); -} -subsys_initcall(scmi_perf_init); +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_PERF, perf) diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 46f213644c49..1f37258e9bee 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -184,7 +184,7 @@ static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain) return dom->name; } -static struct scmi_power_ops power_ops = { +static const struct scmi_power_ops power_ops = { .num_domains_get = scmi_power_num_domains_get, .name_get = scmi_power_name_get, .state_set = scmi_power_state_set, @@ -301,9 +301,4 @@ static int scmi_power_protocol_init(struct scmi_handle *handle) return 0; } -static int __init scmi_power_init(void) -{ - return scmi_protocol_register(SCMI_PROTOCOL_POWER, - &scmi_power_protocol_init); -} -subsys_initcall(scmi_power_init); +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_POWER, power) diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 3691bafca057..f063cfe17e02 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -194,7 +194,7 @@ scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain) return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET); } -static struct scmi_reset_ops reset_ops = { +static const struct scmi_reset_ops reset_ops = { .num_domains_get = scmi_reset_num_domains_get, .name_get = scmi_reset_name_get, .latency_get = scmi_reset_latency_get, @@ -313,9 +313,4 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle) return 0; } -static int __init scmi_reset_init(void) -{ - return scmi_protocol_register(SCMI_PROTOCOL_RESET, - &scmi_reset_protocol_init); -} -subsys_initcall(scmi_reset_init); +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_RESET, reset) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 1af0ad362e82..9703cf6356a0 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -275,7 +275,7 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle) return si->num_sensors; } -static struct scmi_sensor_ops sensor_ops = { +static const struct scmi_sensor_ops sensor_ops = { .count_get = scmi_sensor_count_get, .info_get = scmi_sensor_info_get, .trip_point_config = scmi_sensor_trip_point_config, @@ -365,9 +365,4 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle) return 0; } -static int __init scmi_sensors_init(void) -{ - return scmi_protocol_register(SCMI_PROTOCOL_SENSOR, - &scmi_sensors_protocol_init); -} -subsys_initcall(scmi_sensors_init); +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_SENSOR, sensors) diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index a1537d123e38..1a03c3ec0230 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -137,7 +137,7 @@ smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) return shmem_poll_done(scmi_info->shmem, xfer); } -static struct scmi_transport_ops scmi_smc_ops = { +static const struct scmi_transport_ops scmi_smc_ops = { .chan_available = smc_chan_available, .chan_setup = smc_chan_setup, .chan_free = smc_chan_free, diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c new file mode 100644 index 000000000000..283e12d5f24b --- /dev/null +++ b/drivers/firmware/arm_scmi/system.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) System Power Protocol + * + * Copyright (C) 2020 ARM Ltd. + */ + +#define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt + +#include <linux/scmi_protocol.h> + +#include "common.h" +#include "notify.h" + +#define SCMI_SYSTEM_NUM_SOURCES 1 + +enum scmi_system_protocol_cmd { + SYSTEM_POWER_STATE_NOTIFY = 0x5, +}; + +struct scmi_system_power_state_notify { + __le32 notify_enable; +}; + +struct scmi_system_power_state_notifier_payld { + __le32 agent_id; + __le32 flags; + __le32 system_state; +}; + +struct scmi_system_info { + u32 version; +}; + +static int scmi_system_request_notify(const struct scmi_handle *handle, + bool enable) +{ + int ret; + struct scmi_xfer *t; + struct scmi_system_power_state_notify *notify; + + ret = scmi_xfer_get_init(handle, SYSTEM_POWER_STATE_NOTIFY, + SCMI_PROTOCOL_SYSTEM, sizeof(*notify), 0, &t); + if (ret) + return ret; + + notify = t->tx.buf; + notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_system_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_system_request_notify(handle, enable); + if (ret) + pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret); + + return ret; +} + +static void *scmi_system_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_system_power_state_notifier_payld *p = payld; + struct scmi_system_power_state_notifier_report *r = report; + + if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER || + sizeof(*p) != payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->flags = le32_to_cpu(p->flags); + r->system_state = le32_to_cpu(p->system_state); + *src_id = 0; + + return r; +} + +static const struct scmi_event system_events[] = { + { + .id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER, + .max_payld_sz = + sizeof(struct scmi_system_power_state_notifier_payld), + .max_report_sz = + sizeof(struct scmi_system_power_state_notifier_report), + }, +}; + +static const struct scmi_event_ops system_event_ops = { + .set_notify_enabled = scmi_system_set_notify_enabled, + .fill_custom_report = scmi_system_fill_custom_report, +}; + +static int scmi_system_protocol_init(struct scmi_handle *handle) +{ + u32 version; + struct scmi_system_info *pinfo; + + scmi_version_get(handle, SCMI_PROTOCOL_SYSTEM, &version); + + dev_dbg(handle->dev, "System Power Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + scmi_register_protocol_events(handle, + SCMI_PROTOCOL_SYSTEM, SCMI_PROTO_QUEUE_SZ, + &system_event_ops, + system_events, + ARRAY_SIZE(system_events), + SCMI_SYSTEM_NUM_SOURCES); + + pinfo->version = version; + handle->system_priv = pinfo; + + return 0; +} + +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_SYSTEM, system) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 5066d1f1d687..d51ca0428bb8 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -21,7 +21,7 @@ EXPORT_SYMBOL_GPL(dmi_kobj); /* * DMI stands for "Desktop Management Interface". It is part * of and an antecedent to, SMBIOS, which stands for System - * Management BIOS. See further: http://www.dmtf.org/standards + * Management BIOS. See further: https://www.dmtf.org/standards */ static const char dmi_empty_string[] = ""; diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index da1887f72a51..36ec1f718893 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -106,7 +106,7 @@ config EFI_GENERIC_STUB config EFI_ARMSTUB_DTB_LOADER bool "Enable the DTB loader" - depends on EFI_GENERIC_STUB + depends on EFI_GENERIC_STUB && !RISCV default y help Select this config option to add support for the dtb= command @@ -123,6 +123,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER bool "Enable the command line initrd loader" if !X86 depends on EFI_STUB && (EFI_GENERIC_STUB || X86) default y + depends on !RISCV help Select this config option to add support for the initrd= command line parameter, allowing an initrd that resides on the same volume diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index e8da782280b6..d6ca2da19339 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -36,6 +36,8 @@ fake_map-$(CONFIG_X86) += x86_fake_mem.o arm-obj-$(CONFIG_EFI) := efi-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) +riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o +obj-$(CONFIG_RISCV) += $(riscv-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o obj-$(CONFIG_EFI_EARLYCON) += earlycon.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 039a9acab817..8a94388e38b3 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -23,6 +23,8 @@ cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) +cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ + -fpic cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt @@ -64,6 +66,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o fdt.o string.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o lib-$(CONFIG_X86) += x86-stub.o +lib-$(CONFIG_RISCV) += riscv-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) # Even when -mbranch-protection=none is set, Clang will generate a @@ -112,6 +115,13 @@ STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS +# For RISC-V, we don't need anything special other than arm64. Keep all the +# symbols in .init section and make sure that no absolute symbols references +# doesn't exist. +STUBCOPY_FLAGS-$(CONFIG_RISCV) += --prefix-alloc-sections=.init \ + --prefix-symbols=__efistub_ +STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20 + $(obj)/%.stub.o: $(obj)/%.o FORCE $(call if_changed,stubcopy) diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 311a16802dd6..914a343c7785 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -17,7 +17,10 @@ /* * This is the base address at which to start allocating virtual memory ranges - * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use + * for UEFI Runtime Services. + * + * For ARM/ARM64: + * This is in the low TTBR0 range so that we can use * any allocation we choose, and eliminate the risk of a conflict after kexec. * The value chosen is the largest non-zero power of 2 suitable for this purpose * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can @@ -25,6 +28,12 @@ * Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split, * map everything below 1 GB. (512 MB is a reasonable upper bound for the * entire footprint of the UEFI runtime services memory regions) + * + * For RISC-V: + * There is no specific reason for which, this address (512MB) can't be used + * EFI runtime virtual address for RISC-V. It also helps to use EFI runtime + * services on both RV32/RV64. Keep the same runtime virtual address for RISC-V + * as well to minimize the code churn. */ #define EFI_RT_VIRTUAL_BASE SZ_512M #define EFI_RT_VIRTUAL_SIZE SZ_512M diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c new file mode 100644 index 000000000000..380e4e251399 --- /dev/null +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Western Digital Corporation or its affiliates. + */ + +#include <linux/efi.h> +#include <linux/libfdt.h> + +#include <asm/efi.h> +#include <asm/sections.h> + +#include "efistub.h" + +/* + * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and + * 4MB for 32 bit. + */ +#ifdef CONFIG_64BIT +#define MIN_KIMG_ALIGN SZ_2M +#else +#define MIN_KIMG_ALIGN SZ_4M +#endif + +typedef void __noreturn (*jump_kernel_func)(unsigned int, unsigned long); + +static u32 hartid; + +static u32 get_boot_hartid_from_fdt(void) +{ + const void *fdt; + int chosen_node, len; + const fdt32_t *prop; + + fdt = get_efi_config_table(DEVICE_TREE_GUID); + if (!fdt) + return U32_MAX; + + chosen_node = fdt_path_offset(fdt, "/chosen"); + if (chosen_node < 0) + return U32_MAX; + + prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); + if (!prop || len != sizeof(u32)) + return U32_MAX; + + return fdt32_to_cpu(*prop); +} + +efi_status_t check_platform_features(void) +{ + hartid = get_boot_hartid_from_fdt(); + if (hartid == U32_MAX) { + efi_err("/chosen/boot-hartid missing or invalid!\n"); + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} + +void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, + unsigned long fdt_size) +{ + unsigned long stext_offset = _start_kernel - _start; + unsigned long kernel_entry = entrypoint + stext_offset; + jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; + + /* + * Jump to real kernel here with following constraints. + * 1. MMU should be disabled. + * 2. a0 should contain hartid + * 3. a1 should DT address + */ + csr_write(CSR_SATP, 0); + jump_kernel(hartid, fdt); +} + +efi_status_t handle_kernel_image(unsigned long *image_addr, + unsigned long *image_size, + unsigned long *reserve_addr, + unsigned long *reserve_size, + efi_loaded_image_t *image) +{ + unsigned long kernel_size = 0; + unsigned long preferred_addr; + efi_status_t status; + + kernel_size = _edata - _start; + *image_addr = (unsigned long)_start; + *image_size = kernel_size + (_end - _edata); + + /* + * RISC-V kernel maps PAGE_OFFSET virtual address to the same physical + * address where kernel is booted. That's why kernel should boot from + * as low as possible to avoid wastage of memory. Currently, dram_base + * is occupied by the firmware. So the preferred address for kernel to + * boot is next aligned address. If preferred address is not available, + * relocate_kernel will fall back to efi_low_alloc_above to allocate + * lowest possible memory region as long as the address and size meets + * the alignment constraints. + */ + preferred_addr = MIN_KIMG_ALIGN; + status = efi_relocate_kernel(image_addr, kernel_size, *image_size, + preferred_addr, MIN_KIMG_ALIGN, 0x0); + + if (status != EFI_SUCCESS) { + efi_err("Failed to relocate kernel\n"); + *image_size = 0; + } + return status; +} diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c new file mode 100644 index 000000000000..d28e715d2bcc --- /dev/null +++ b/drivers/firmware/efi/riscv-runtime.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Extensible Firmware Interface + * + * Copyright (C) 2020 Western Digital Corporation or its affiliates. + * + * Based on Extensible Firmware Interface Specification version 2.4 + * Adapted from drivers/firmware/efi/arm-runtime.c + * + */ + +#include <linux/dmi.h> +#include <linux/efi.h> +#include <linux/io.h> +#include <linux/memblock.h> +#include <linux/mm_types.h> +#include <linux/preempt.h> +#include <linux/rbtree.h> +#include <linux/rwsem.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/pgtable.h> + +#include <asm/cacheflush.h> +#include <asm/efi.h> +#include <asm/mmu.h> +#include <asm/pgalloc.h> + +static bool __init efi_virtmap_init(void) +{ + efi_memory_desc_t *md; + + efi_mm.pgd = pgd_alloc(&efi_mm); + mm_init_cpumask(&efi_mm); + init_new_context(NULL, &efi_mm); + + for_each_efi_memory_desc(md) { + phys_addr_t phys = md->phys_addr; + int ret; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + return false; + + ret = efi_create_mapping(&efi_mm, md); + if (ret) { + pr_warn(" EFI remap %pa: failed to create mapping (%d)\n", + &phys, ret); + return false; + } + } + + if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions)) + return false; + + return true; +} + +/* + * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., + * non-early mapping of the UEFI system table and virtual mappings for all + * EFI_MEMORY_RUNTIME regions. + */ +static int __init riscv_enable_runtime_services(void) +{ + u64 mapsize; + + if (!efi_enabled(EFI_BOOT)) { + pr_info("EFI services will not be available.\n"); + return 0; + } + + efi_memmap_unmap(); + + mapsize = efi.memmap.desc_size * efi.memmap.nr_map; + + if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) { + pr_err("Failed to remap EFI memory map\n"); + return 0; + } + + if (efi_soft_reserve_enabled()) { + efi_memory_desc_t *md; + + for_each_efi_memory_desc(md) { + int md_size = md->num_pages << EFI_PAGE_SHIFT; + struct resource *res; + + if (!(md->attribute & EFI_MEMORY_SP)) + continue; + + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (WARN_ON(!res)) + break; + + res->start = md->phys_addr; + res->end = md->phys_addr + md_size - 1; + res->name = "Soft Reserved"; + res->flags = IORESOURCE_MEM; + res->desc = IORES_DESC_SOFT_RESERVED; + + insert_resource(&iomem_resource, res); + } + } + + if (efi_runtime_disabled()) { + pr_info("EFI runtime services will be disabled.\n"); + return 0; + } + + if (efi_enabled(EFI_RUNTIME_SERVICES)) { + pr_info("EFI runtime services access via paravirt.\n"); + return 0; + } + + pr_info("Remapping and enabling EFI services.\n"); + + if (!efi_virtmap_init()) { + pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n"); + return -ENOMEM; + } + + /* Set up runtime services function pointers */ + efi_native_runtime_setup(); + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + + return 0; +} +early_initcall(riscv_enable_runtime_services); + +void efi_virtmap_load(void) +{ + preempt_disable(); + switch_mm(current->active_mm, &efi_mm, NULL); +} + +void efi_virtmap_unload(void) +{ + switch_mm(&efi_mm, current->active_mm, NULL); + preempt_enable(); +} diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index af3d6d9ead28..946eea292b52 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -46,6 +46,7 @@ #include <dt-bindings/firmware/imx/rsrc.h> #include <linux/firmware/imx/sci.h> +#include <linux/firmware/imx/svc/rm.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> @@ -256,6 +257,9 @@ imx_scu_add_pm_domain(struct device *dev, int idx, struct imx_sc_pm_domain *sc_pd; int ret; + if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) + return NULL; + sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); if (!sc_pd) return ERR_PTR(-ENOMEM); diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c index 4e80921ee212..00c88b809c0c 100644 --- a/drivers/firmware/smccc/smccc.c +++ b/drivers/firmware/smccc/smccc.c @@ -24,8 +24,10 @@ enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void) return smccc_conduit; } +EXPORT_SYMBOL_GPL(arm_smccc_1_1_get_conduit); u32 arm_smccc_get_version(void) { return smccc_version; } +EXPORT_SYMBOL_GPL(arm_smccc_get_version); diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 4d93d8925e14..0742a90cb844 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -856,7 +856,8 @@ static const struct tegra_bpmp_soc tegra210_soc = { static const struct of_device_id tegra_bpmp_match[] = { #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, #endif #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 722af9ee53d6..896f53ec7857 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1106,7 +1106,8 @@ static int ti_sci_cmd_get_clock(const struct ti_sci_handle *handle, u32 dev_id, static int ti_sci_cmd_idle_clock(const struct ti_sci_handle *handle, u32 dev_id, u32 clk_id) { - return ti_sci_set_clock_state(handle, dev_id, clk_id, 0, + return ti_sci_set_clock_state(handle, dev_id, clk_id, + MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE, MSG_CLOCK_SW_STATE_UNREQ); } @@ -1125,7 +1126,8 @@ static int ti_sci_cmd_idle_clock(const struct ti_sci_handle *handle, static int ti_sci_cmd_put_clock(const struct ti_sci_handle *handle, u32 dev_id, u32 clk_id) { - return ti_sci_set_clock_state(handle, dev_id, clk_id, 0, + return ti_sci_set_clock_state(handle, dev_id, clk_id, + MSG_FLAG_CLOCK_ALLOW_FREQ_CHANGE, MSG_CLOCK_SW_STATE_AUTO); } |