6
6
*/
7
7
8
8
#include <linux/arm-smccc.h>
9
+ #include <linux/cpuhotplug.h>
9
10
#include <linux/delay.h>
10
11
#include <linux/device.h>
11
12
#include <linux/interrupt.h>
13
+ #include <linux/irqdomain.h>
12
14
#include <linux/io.h>
13
15
#include <linux/kernel.h>
14
16
#include <linux/mailbox_controller.h>
15
17
#include <linux/mailbox/zynqmp-ipi-message.h>
16
18
#include <linux/module.h>
17
19
#include <linux/of.h>
18
20
#include <linux/of_address.h>
21
+ #include <linux/of_irq.h>
19
22
#include <linux/platform_device.h>
20
23
21
24
/* IPI agent ID any */
59
62
#define DST_BIT_POS 9U
60
63
#define SRC_BITMASK GENMASK(11, 8)
61
64
65
+ #define MAX_SGI 16
66
+
62
67
/**
63
68
* struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel
64
69
* @is_opened: indicate if the IPI channel is opened
@@ -111,6 +116,7 @@ struct zynqmp_ipi_mbox {
111
116
* @irq: IPI agent interrupt ID
112
117
* @method: IPI SMC or HVC is going to be used
113
118
* @local_id: local IPI agent ID
119
+ * @virq_sgi: IRQ number mapped to SGI
114
120
* @num_mboxes: number of mailboxes of this IPI agent
115
121
* @ipi_mboxes: IPI mailboxes of this IPI agent
116
122
*/
@@ -119,10 +125,13 @@ struct zynqmp_ipi_pdata {
119
125
int irq ;
120
126
unsigned int method ;
121
127
u32 local_id ;
128
+ int virq_sgi ;
122
129
int num_mboxes ;
123
130
struct zynqmp_ipi_mbox ipi_mboxes [] __counted_by (num_mboxes );
124
131
};
125
132
133
+ static DEFINE_PER_CPU (struct zynqmp_ipi_pdata * , per_cpu_pdata ) ;
134
+
126
135
static struct device_driver zynqmp_ipi_mbox_driver = {
127
136
.owner = THIS_MODULE ,
128
137
.name = "zynqmp-ipi-mbox" ,
@@ -189,6 +198,14 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data)
189
198
return status ;
190
199
}
191
200
201
+ static irqreturn_t zynqmp_sgi_interrupt (int irq , void * data )
202
+ {
203
+ struct zynqmp_ipi_pdata * * pdata_ptr = data ;
204
+ struct zynqmp_ipi_pdata * pdata = * pdata_ptr ;
205
+
206
+ return zynqmp_ipi_interrupt (irq , pdata );
207
+ }
208
+
192
209
/**
193
210
* zynqmp_ipi_peek_data - Peek to see if there are any rx messages.
194
211
*
@@ -748,6 +765,112 @@ static int versal_ipi_setup(struct zynqmp_ipi_mbox *ipi_mbox,
748
765
return 0 ;
749
766
}
750
767
768
+ static int xlnx_mbox_cpuhp_start (unsigned int cpu )
769
+ {
770
+ struct zynqmp_ipi_pdata * pdata ;
771
+
772
+ pdata = get_cpu_var (per_cpu_pdata );
773
+ put_cpu_var (per_cpu_pdata );
774
+ enable_percpu_irq (pdata -> virq_sgi , IRQ_TYPE_NONE );
775
+
776
+ return 0 ;
777
+ }
778
+
779
+ static int xlnx_mbox_cpuhp_down (unsigned int cpu )
780
+ {
781
+ struct zynqmp_ipi_pdata * pdata ;
782
+
783
+ pdata = get_cpu_var (per_cpu_pdata );
784
+ put_cpu_var (per_cpu_pdata );
785
+ disable_percpu_irq (pdata -> virq_sgi );
786
+
787
+ return 0 ;
788
+ }
789
+
790
+ static void xlnx_disable_percpu_irq (void * data )
791
+ {
792
+ struct zynqmp_ipi_pdata * pdata ;
793
+
794
+ pdata = * this_cpu_ptr (& per_cpu_pdata );
795
+
796
+ disable_percpu_irq (pdata -> virq_sgi );
797
+ }
798
+
799
+ static int xlnx_mbox_init_sgi (struct platform_device * pdev ,
800
+ int sgi_num ,
801
+ struct zynqmp_ipi_pdata * pdata )
802
+ {
803
+ int ret = 0 ;
804
+ int cpu ;
805
+
806
+ /*
807
+ * IRQ related structures are used for the following:
808
+ * for each SGI interrupt ensure its mapped by GIC IRQ domain
809
+ * and that each corresponding linux IRQ for the HW IRQ has
810
+ * a handler for when receiving an interrupt from the remote
811
+ * processor.
812
+ */
813
+ struct irq_domain * domain ;
814
+ struct irq_fwspec sgi_fwspec ;
815
+ struct device_node * interrupt_parent = NULL ;
816
+ struct device * dev = & pdev -> dev ;
817
+
818
+ /* Find GIC controller to map SGIs. */
819
+ interrupt_parent = of_irq_find_parent (dev -> of_node );
820
+ if (!interrupt_parent ) {
821
+ dev_err (& pdev -> dev , "Failed to find property for Interrupt parent\n" );
822
+ return - EINVAL ;
823
+ }
824
+
825
+ /* Each SGI needs to be associated with GIC's IRQ domain. */
826
+ domain = irq_find_host (interrupt_parent );
827
+ of_node_put (interrupt_parent );
828
+
829
+ /* Each mapping needs GIC domain when finding IRQ mapping. */
830
+ sgi_fwspec .fwnode = domain -> fwnode ;
831
+
832
+ /*
833
+ * When irq domain looks at mapping each arg is as follows:
834
+ * 3 args for: interrupt type (SGI), interrupt # (set later), type
835
+ */
836
+ sgi_fwspec .param_count = 1 ;
837
+
838
+ /* Set SGI's hwirq */
839
+ sgi_fwspec .param [0 ] = sgi_num ;
840
+ pdata -> virq_sgi = irq_create_fwspec_mapping (& sgi_fwspec );
841
+
842
+ for_each_possible_cpu (cpu )
843
+ per_cpu (per_cpu_pdata , cpu ) = pdata ;
844
+
845
+ ret = request_percpu_irq (pdata -> virq_sgi , zynqmp_sgi_interrupt , pdev -> name ,
846
+ & per_cpu_pdata );
847
+ WARN_ON (ret );
848
+ if (ret ) {
849
+ irq_dispose_mapping (pdata -> virq_sgi );
850
+ return ret ;
851
+ }
852
+
853
+ irq_to_desc (pdata -> virq_sgi );
854
+ irq_set_status_flags (pdata -> virq_sgi , IRQ_PER_CPU );
855
+
856
+ /* Setup function for the CPU hot-plug cases */
857
+ cpuhp_setup_state (CPUHP_AP_ONLINE_DYN , "mailbox/sgi:starting" ,
858
+ xlnx_mbox_cpuhp_start , xlnx_mbox_cpuhp_down );
859
+
860
+ return ret ;
861
+ }
862
+
863
+ static void xlnx_mbox_cleanup_sgi (struct zynqmp_ipi_pdata * pdata )
864
+ {
865
+ cpuhp_remove_state (CPUHP_AP_ONLINE_DYN );
866
+
867
+ on_each_cpu (xlnx_disable_percpu_irq , NULL , 1 );
868
+
869
+ irq_clear_status_flags (pdata -> virq_sgi , IRQ_PER_CPU );
870
+ free_percpu_irq (pdata -> virq_sgi , & per_cpu_pdata );
871
+ irq_dispose_mapping (pdata -> virq_sgi );
872
+ }
873
+
751
874
/**
752
875
* zynqmp_ipi_free_mboxes - Free IPI mailboxes devices
753
876
*
@@ -758,6 +881,9 @@ static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata)
758
881
struct zynqmp_ipi_mbox * ipi_mbox ;
759
882
int i ;
760
883
884
+ if (pdata -> irq < MAX_SGI )
885
+ xlnx_mbox_cleanup_sgi (pdata );
886
+
761
887
i = pdata -> num_mboxes ;
762
888
for (; i >= 0 ; i -- ) {
763
889
ipi_mbox = & pdata -> ipi_mboxes [i ];
@@ -773,7 +899,8 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
773
899
{
774
900
struct device * dev = & pdev -> dev ;
775
901
struct device_node * nc , * np = pdev -> dev .of_node ;
776
- struct zynqmp_ipi_pdata * pdata ;
902
+ struct zynqmp_ipi_pdata __percpu * pdata ;
903
+ struct of_phandle_args out_irq ;
777
904
struct zynqmp_ipi_mbox * mbox ;
778
905
int num_mboxes , ret = - EINVAL ;
779
906
setup_ipi_fn ipi_fn ;
@@ -821,14 +948,32 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
821
948
mbox ++ ;
822
949
}
823
950
824
- /* IPI IRQ */
825
- ret = platform_get_irq ( pdev , 0 );
826
- if ( ret < 0 )
951
+ ret = of_irq_parse_one ( dev_of_node ( dev ), 0 , & out_irq );
952
+ if ( ret < 0 ) {
953
+ dev_err ( dev , "failed to parse interrupts\n" );
827
954
goto free_mbox_dev ;
955
+ }
956
+ ret = out_irq .args [1 ];
957
+
958
+ /*
959
+ * If Interrupt number is in SGI range, then request SGI else request
960
+ * IPI system IRQ.
961
+ */
962
+ if (ret < MAX_SGI ) {
963
+ pdata -> irq = ret ;
964
+ ret = xlnx_mbox_init_sgi (pdev , pdata -> irq , pdata );
965
+ if (ret )
966
+ goto free_mbox_dev ;
967
+ } else {
968
+ ret = platform_get_irq (pdev , 0 );
969
+ if (ret < 0 )
970
+ goto free_mbox_dev ;
971
+
972
+ pdata -> irq = ret ;
973
+ ret = devm_request_irq (dev , pdata -> irq , zynqmp_ipi_interrupt ,
974
+ IRQF_SHARED , dev_name (dev ), pdata );
975
+ }
828
976
829
- pdata -> irq = ret ;
830
- ret = devm_request_irq (dev , pdata -> irq , zynqmp_ipi_interrupt ,
831
- IRQF_SHARED , dev_name (dev ), pdata );
832
977
if (ret ) {
833
978
dev_err (dev , "IRQ %d is not requested successfully.\n" ,
834
979
pdata -> irq );
0 commit comments