Skip to content

Commit 897e9e6

Browse files
committed
firmware: arm_ffa: Initial support for scheduler receiver interrupt
The Framework uses the schedule receiver interrupt to inform the receiver’s scheduler that the receiver must be run to handle a pending notification. A receiver’s scheduler can obtain the description of the schedule receiver interrupt by invoking the FFA_FEATURES interface. The delivery of the physical schedule receiver interrupt from the secure state to the non-secure state depends upon the state of the interrupt controller as configured by the hypervisor. The schedule seceiver interrupt is assumed to be a SGI. The Arm GIC specification defines 16 SGIs. It recommends that they are equally divided between the non-secure and secure states. OS like Linux kernel in the non-secure state typically do not have SGIs to spare. The usage of SGIs in the secure state is however limited. It is more likely that software in the Secure world does not use all the SGIs allocated to it. It is recommended that the secure world software donates an unused SGI to the normal world for use as the schedule receiver interrupt. This implies that secure world software must configure the SGI in the GIC as a non-secure interrupt before presenting it to the normal world. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sudeep Holla <[email protected]>
1 parent 3522be4 commit 897e9e6

File tree

1 file changed

+176
-10
lines changed

1 file changed

+176
-10
lines changed

drivers/firmware/arm_ffa/driver.c

Lines changed: 176 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,20 @@
2222
#define DRIVER_NAME "ARM FF-A"
2323
#define pr_fmt(fmt) DRIVER_NAME ": " fmt
2424

25+
#include <linux/acpi.h>
2526
#include <linux/arm_ffa.h>
2627
#include <linux/bitfield.h>
28+
#include <linux/cpuhotplug.h>
2729
#include <linux/device.h>
30+
#include <linux/interrupt.h>
2831
#include <linux/io.h>
2932
#include <linux/kernel.h>
3033
#include <linux/module.h>
3134
#include <linux/mm.h>
35+
#include <linux/of_irq.h>
3236
#include <linux/scatterlist.h>
3337
#include <linux/slab.h>
38+
#include <linux/smp.h>
3439
#include <linux/uuid.h>
3540

3641
#include "common.h"
@@ -76,6 +81,10 @@ static inline int ffa_to_linux_errno(int errno)
7681
return -EINVAL;
7782
}
7883

84+
struct ffa_pcpu_irq {
85+
struct ffa_drv_info *info;
86+
};
87+
7988
struct ffa_drv_info {
8089
u32 version;
8190
u16 vm_id;
@@ -85,6 +94,11 @@ struct ffa_drv_info {
8594
void *tx_buffer;
8695
bool mem_ops_native;
8796
bool bitmap_created;
97+
unsigned int sched_recv_irq;
98+
unsigned int cpuhp_state;
99+
struct ffa_pcpu_irq __percpu *irq_pcpu;
100+
struct workqueue_struct *notif_pcpu_wq;
101+
struct work_struct irq_work;
88102
};
89103

90104
static struct ffa_drv_info *drv_info;
@@ -917,34 +931,182 @@ static void ffa_setup_partitions(void)
917931
kfree(pbuf);
918932
}
919933

920-
static int ffa_notifications_setup(void)
934+
/* FFA FEATURE IDs */
935+
#define FFA_FEAT_NOTIFICATION_PENDING_INT (1)
936+
#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2)
937+
#define FFA_FEAT_MANAGED_EXIT_INT (3)
938+
939+
static irqreturn_t irq_handler(int irq, void *irq_data)
921940
{
922-
int ret;
941+
struct ffa_pcpu_irq *pcpu = irq_data;
942+
struct ffa_drv_info *info = pcpu->info;
923943

924-
ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
925-
if (ret) {
926-
pr_err("Notifications not supported, continuing with it ..\n");
927-
return 0;
944+
queue_work(info->notif_pcpu_wq, &info->irq_work);
945+
946+
return IRQ_HANDLED;
947+
}
948+
949+
static void ffa_sched_recv_irq_work_fn(struct work_struct *work)
950+
{
951+
ffa_notification_info_get();
952+
}
953+
954+
static int ffa_sched_recv_irq_map(void)
955+
{
956+
int ret, irq, sr_intid;
957+
958+
/* The returned sr_intid is assumed to be SGI donated to NS world */
959+
ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL);
960+
if (ret < 0) {
961+
if (ret != -EOPNOTSUPP)
962+
pr_err("Failed to retrieve scheduler Rx interrupt\n");
963+
return ret;
928964
}
929965

930-
ret = ffa_notification_bitmap_create();
966+
if (acpi_disabled) {
967+
struct of_phandle_args oirq = {};
968+
struct device_node *gic;
969+
970+
/* Only GICv3 supported currently with the device tree */
971+
gic = of_find_compatible_node(NULL, NULL, "arm,gic-v3");
972+
if (!gic)
973+
return -ENXIO;
974+
975+
oirq.np = gic;
976+
oirq.args_count = 1;
977+
oirq.args[0] = sr_intid;
978+
irq = irq_create_of_mapping(&oirq);
979+
of_node_put(gic);
980+
#ifdef CONFIG_ACPI
981+
} else {
982+
irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE,
983+
ACPI_ACTIVE_HIGH);
984+
#endif
985+
}
986+
987+
if (irq <= 0) {
988+
pr_err("Failed to create IRQ mapping!\n");
989+
return -ENODATA;
990+
}
991+
992+
return irq;
993+
}
994+
995+
static void ffa_sched_recv_irq_unmap(void)
996+
{
997+
if (drv_info->sched_recv_irq)
998+
irq_dispose_mapping(drv_info->sched_recv_irq);
999+
}
1000+
1001+
static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu)
1002+
{
1003+
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
1004+
return 0;
1005+
}
1006+
1007+
static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu)
1008+
{
1009+
disable_percpu_irq(drv_info->sched_recv_irq);
1010+
return 0;
1011+
}
1012+
1013+
static void ffa_uninit_pcpu_irq(void)
1014+
{
1015+
if (drv_info->cpuhp_state)
1016+
cpuhp_remove_state(drv_info->cpuhp_state);
1017+
1018+
if (drv_info->notif_pcpu_wq)
1019+
destroy_workqueue(drv_info->notif_pcpu_wq);
1020+
1021+
if (drv_info->sched_recv_irq)
1022+
free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu);
1023+
1024+
if (drv_info->irq_pcpu)
1025+
free_percpu(drv_info->irq_pcpu);
1026+
}
1027+
1028+
static int ffa_init_pcpu_irq(unsigned int irq)
1029+
{
1030+
struct ffa_pcpu_irq __percpu *irq_pcpu;
1031+
int ret, cpu;
1032+
1033+
irq_pcpu = alloc_percpu(struct ffa_pcpu_irq);
1034+
if (!irq_pcpu)
1035+
return -ENOMEM;
1036+
1037+
for_each_present_cpu(cpu)
1038+
per_cpu_ptr(irq_pcpu, cpu)->info = drv_info;
1039+
1040+
drv_info->irq_pcpu = irq_pcpu;
1041+
1042+
ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu);
9311043
if (ret) {
932-
pr_err("notification_bitmap_create error %d\n", ret);
1044+
pr_err("Error registering notification IRQ %d: %d\n", irq, ret);
9331045
return ret;
9341046
}
935-
drv_info->bitmap_created = true;
9361047

1048+
INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn);
1049+
drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification");
1050+
if (!drv_info->notif_pcpu_wq)
1051+
return -EINVAL;
1052+
1053+
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ffa/pcpu-irq:starting",
1054+
ffa_cpuhp_pcpu_irq_enable,
1055+
ffa_cpuhp_pcpu_irq_disable);
1056+
1057+
if (ret < 0)
1058+
return ret;
1059+
1060+
drv_info->cpuhp_state = ret;
9371061
return 0;
9381062
}
9391063

9401064
static void ffa_notifications_cleanup(void)
9411065
{
1066+
ffa_uninit_pcpu_irq();
1067+
ffa_sched_recv_irq_unmap();
1068+
9421069
if (drv_info->bitmap_created) {
9431070
ffa_notification_bitmap_destroy();
9441071
drv_info->bitmap_created = false;
9451072
}
9461073
}
9471074

1075+
static int ffa_notifications_setup(void)
1076+
{
1077+
int ret, irq;
1078+
1079+
ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
1080+
if (ret) {
1081+
pr_err("Notifications not supported, continuing with it ..\n");
1082+
return 0;
1083+
}
1084+
1085+
ret = ffa_notification_bitmap_create();
1086+
if (ret) {
1087+
pr_err("notification_bitmap_create error %d\n", ret);
1088+
return ret;
1089+
}
1090+
drv_info->bitmap_created = true;
1091+
1092+
irq = ffa_sched_recv_irq_map();
1093+
if (irq <= 0) {
1094+
ret = irq;
1095+
goto cleanup;
1096+
}
1097+
1098+
drv_info->sched_recv_irq = irq;
1099+
1100+
ret = ffa_init_pcpu_irq(irq);
1101+
if (ret)
1102+
goto cleanup;
1103+
1104+
return 0;
1105+
cleanup:
1106+
ffa_notifications_cleanup();
1107+
return ret;
1108+
}
1109+
9481110
static int __init ffa_init(void)
9491111
{
9501112
int ret;
@@ -1000,7 +1162,11 @@ static int __init ffa_init(void)
10001162

10011163
ffa_set_up_mem_ops_native_flag();
10021164

1003-
return ffa_notifications_setup();
1165+
ret = ffa_notifications_setup();
1166+
if (ret)
1167+
goto free_pages;
1168+
1169+
return 0;
10041170
free_pages:
10051171
if (drv_info->tx_buffer)
10061172
free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);

0 commit comments

Comments
 (0)