aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Morse <james.morse@arm.com>2016-12-14 12:14:46 +0000
committerThomas Abraham <thomas.abraham@arm.com>2019-09-24 10:26:23 +0530
commit20590eb40102eab5e20b12517e67a89188a6a02c (patch)
tree9609aae53ee0fb11d92096cfb774f30f1d6d4a04
parent7b97721b67101d647ea5580f2979d1aec9668c6a (diff)
UNTESTED firmware: arm_sdei: Add support for manipulating shared event routing
The SDEI API provides a way to set the affinity of shared events. Add API calls to allow the driver that registered the event to get and set the routing using Linux's CPU numbers. Any routing that is set is not saved or restored. In particular a driver may route a shared event to a CPU that is offline, or shortly go offline. Protecting against these are the callers responsibility. Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Sughosh Ganu <sughosh.ganu@arm.com>
-rw-r--r--arch/arm64/include/asm/sdei.h27
-rw-r--r--drivers/firmware/arm_sdei.c68
-rw-r--r--include/linux/arm_sdei.h11
3 files changed, 106 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h
index 63e0b92a5fbb..c46732896a5e 100644
--- a/arch/arm64/include/asm/sdei.h
+++ b/arch/arm64/include/asm/sdei.h
@@ -15,6 +15,7 @@
#include <linux/preempt.h>
#include <linux/types.h>
+#include <asm/smp_plat.h>
#include <asm/virt.h>
extern unsigned long sdei_exit_mode;
@@ -56,5 +57,31 @@ static inline bool on_sdei_stack(unsigned long sp,
return false;
}
+static inline int sdei_logical_cpu_to_fw_cpu(int cpu, u64 *fw_cpu)
+{
+ int err = -EINVAL;
+
+ if (cpu <= num_possible_cpus()) {
+ *fw_cpu = cpu_logical_map(cpu);
+ if (*fw_cpu != INVALID_HWID)
+ err = 0;
+ }
+
+ return err;
+}
+
+static inline int sdei_fw_cpu_to_logical_cpu(u64 fw_cpu, int *cpu)
+{
+ int err;
+
+ err = cpu_logical_map(fw_cpu);
+ if (err != -EINVAL) {
+ *cpu = err;
+ err = 0;
+ }
+
+ return err;
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ASM_SDEI_H */
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 9cd70d1a5622..26ef7d38a869 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -645,6 +645,74 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
}
EXPORT_SYMBOL(sdei_event_register);
+static int sdei_api_event_routing_set(u32 event_num, u64 routing_mode,
+ u64 affinity)
+{
+ return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_ROUTING_SET, event_num,
+ routing_mode, affinity, 0, 0, NULL);
+}
+int sdei_event_routing_set(u32 event_num, bool directed, int cpu)
+{
+ int err;
+ u64 affinity;
+ u32 routing_mode;
+ struct sdei_event *event;
+
+ event = sdei_event_find(event_num);
+ if (!event) {
+ pr_err("Event %u not registered\n", event_num);
+ return -EINVAL;
+ }
+
+ if (directed) {
+ routing_mode = SDEI_EVENT_REGISTER_RM_PE;
+ err = sdei_logical_cpu_to_fw_cpu(cpu, &affinity);
+ if (err) {
+ pr_err("Failed to determine firmware description for CPU %u.\n", cpu);
+ return err;
+ }
+ } else {
+ routing_mode = SDEI_EVENT_REGISTER_RM_ANY;
+ affinity = 0;
+ }
+ err = sdei_api_event_routing_set(event_num, routing_mode,
+ affinity);
+ if (err)
+ pr_err("Failed to set routing mode\n");
+
+ return err;
+}
+EXPORT_SYMBOL(sdei_event_routing_set);
+
+int sdei_event_routing_get(u32 event_num, bool *directed, int *cpu)
+{
+ int err;
+ u64 info;
+
+ err = sdei_api_event_get_info(event_num,
+ SDEI_EVENT_INFO_EV_ROUTING_MODE,
+ &info);
+ if (err) {
+ pr_err("Failed to query routing mode\n");
+ return err;
+ }
+ *directed = (info == SDEI_EVENT_REGISTER_RM_PE);
+
+ err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_ROUTING_AFF,
+ &info);
+ if (err) {
+ pr_err("Failed to query routing affinity\n");
+ return err;
+ }
+ err = sdei_fw_cpu_to_logical_cpu(info, cpu);
+ /* Don't print a warning if the affinity is bogus for RM_ANY */
+ if (err && *directed)
+ pr_err("Failed to convert firmware description for affinity 0x%llx\n", info);
+
+ return err;
+}
+EXPORT_SYMBOL(sdei_event_routing_get);
+
static int sdei_reregister_event(struct sdei_event *event)
{
int err;
diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
index 3305ea7f9dc7..5262c51a0b52 100644
--- a/include/linux/arm_sdei.h
+++ b/include/linux/arm_sdei.h
@@ -48,6 +48,17 @@ int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
sdei_event_callback *critical_cb);
int sdei_unregister_ghes(struct ghes *ghes);
+/*
+ * get/set the event routing.
+ * These calls take linux's cpu numbers and convert them to firmware affinity.
+ * !directed causes the event to be delivered to a CPU of the firmware's choice.
+ *
+ * This information is not saved/restored by the driver.
+ * The caller should handle CPU hotplug of its chosen CPU.
+ */
+int sdei_event_routing_set(u32 event_num, bool directed, int to_cpu);
+int sdei_event_routing_get(u32 event_num, bool *directed, int *to_cpu);
+
#ifdef CONFIG_ARM_SDE_INTERFACE
/* For use by arch code when CPU hotplug notifiers are not appropriate. */
int sdei_mask_local_cpu(void);