22
22
#define DRIVER_NAME "ARM FF-A"
23
23
#define pr_fmt (fmt ) DRIVER_NAME ": " fmt
24
24
25
+ #include <linux/acpi.h>
25
26
#include <linux/arm_ffa.h>
26
27
#include <linux/bitfield.h>
28
+ #include <linux/cpuhotplug.h>
27
29
#include <linux/device.h>
30
+ #include <linux/interrupt.h>
28
31
#include <linux/io.h>
29
32
#include <linux/kernel.h>
30
33
#include <linux/module.h>
31
34
#include <linux/mm.h>
35
+ #include <linux/of_irq.h>
32
36
#include <linux/scatterlist.h>
33
37
#include <linux/slab.h>
38
+ #include <linux/smp.h>
34
39
#include <linux/uuid.h>
35
40
36
41
#include "common.h"
@@ -76,6 +81,10 @@ static inline int ffa_to_linux_errno(int errno)
76
81
return - EINVAL ;
77
82
}
78
83
84
+ struct ffa_pcpu_irq {
85
+ struct ffa_drv_info * info ;
86
+ };
87
+
79
88
struct ffa_drv_info {
80
89
u32 version ;
81
90
u16 vm_id ;
@@ -85,6 +94,11 @@ struct ffa_drv_info {
85
94
void * tx_buffer ;
86
95
bool mem_ops_native ;
87
96
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 ;
88
102
};
89
103
90
104
static struct ffa_drv_info * drv_info ;
@@ -917,34 +931,182 @@ static void ffa_setup_partitions(void)
917
931
kfree (pbuf );
918
932
}
919
933
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 )
921
940
{
922
- int ret ;
941
+ struct ffa_pcpu_irq * pcpu = irq_data ;
942
+ struct ffa_drv_info * info = pcpu -> info ;
923
943
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 ;
928
964
}
929
965
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 );
931
1043
if (ret ) {
932
- pr_err ("notification_bitmap_create error %d \n" , ret );
1044
+ pr_err ("Error registering notification IRQ %d: %d \n" , irq , ret );
933
1045
return ret ;
934
1046
}
935
- drv_info -> bitmap_created = true;
936
1047
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 ;
937
1061
return 0 ;
938
1062
}
939
1063
940
1064
static void ffa_notifications_cleanup (void )
941
1065
{
1066
+ ffa_uninit_pcpu_irq ();
1067
+ ffa_sched_recv_irq_unmap ();
1068
+
942
1069
if (drv_info -> bitmap_created ) {
943
1070
ffa_notification_bitmap_destroy ();
944
1071
drv_info -> bitmap_created = false;
945
1072
}
946
1073
}
947
1074
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
+
948
1110
static int __init ffa_init (void )
949
1111
{
950
1112
int ret ;
@@ -1000,7 +1162,11 @@ static int __init ffa_init(void)
1000
1162
1001
1163
ffa_set_up_mem_ops_native_flag ();
1002
1164
1003
- return ffa_notifications_setup ();
1165
+ ret = ffa_notifications_setup ();
1166
+ if (ret )
1167
+ goto free_pages ;
1168
+
1169
+ return 0 ;
1004
1170
free_pages :
1005
1171
if (drv_info -> tx_buffer )
1006
1172
free_pages_exact (drv_info -> tx_buffer , RXTX_BUFFER_SIZE );
0 commit comments