xref: /kvm-unit-tests/lib/arm/gic-v3.c (revision a5a2d35cba2e4b99ef5dfa77d220a28a7d0159cd)
1 /*
2  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
3  *
4  * This work is licensed under the terms of the GNU LGPL, version 2.
5  */
6 #include <asm/gic.h>
7 #include <asm/io.h>
8 
9 void gicv3_set_redist_base(size_t stride)
10 {
11 	u32 aff = mpidr_compress(get_mpidr());
12 	u64 typer;
13 	int i = 0;
14 
15 	while (gicv3_data.redist_bases[i]) {
16 		void *ptr = gicv3_data.redist_bases[i];
17 		do {
18 			typer = gicv3_read_typer(ptr + GICR_TYPER);
19 			if ((typer >> 32) == aff) {
20 				gicv3_redist_base() = ptr;
21 				return;
22 			}
23 			ptr += stride; /* skip RD_base, SGI_base, etc. */
24 		} while (!(typer & GICR_TYPER_LAST));
25 		++i;
26 	}
27 
28 	/* should never reach here */
29 	assert(0);
30 }
31 
32 void gicv3_enable_defaults(void)
33 {
34 	void *dist = gicv3_dist_base();
35 	void *sgi_base;
36 	unsigned int i;
37 
38 	gicv3_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER));
39 	if (gicv3_data.irq_nr > 1020)
40 		gicv3_data.irq_nr = 1020;
41 
42 	writel(0, dist + GICD_CTLR);
43 	gicv3_dist_wait_for_rwp();
44 
45 	writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
46 	       dist + GICD_CTLR);
47 	gicv3_dist_wait_for_rwp();
48 
49 	for (i = 0; i < gicv3_data.irq_nr; i += 4)
50 		writel(~0, dist + GICD_IGROUPR + i);
51 
52 	if (!gicv3_redist_base())
53 		gicv3_set_redist_base(SZ_64K * 2);
54 	sgi_base = gicv3_sgi_base();
55 
56 	writel(~0, sgi_base + GICR_IGROUPR0);
57 
58 	for (i = 0; i < 16; i += 4)
59 		writel(GICD_INT_DEF_PRI_X4, sgi_base + GICR_IPRIORITYR0 + i);
60 
61 	writel(GICD_INT_EN_SET_SGI, sgi_base + GICR_ISENABLER0);
62 
63 	gicv3_write_pmr(GICC_INT_PRI_THRESHOLD);
64 	gicv3_write_grpen1(1);
65 }
66 
67 u32 gicv3_iar_irqnr(u32 iar)
68 {
69 	return iar & ((1 << 24) - 1);
70 }
71 
72 void gicv3_ipi_send_mask(int irq, const cpumask_t *dest)
73 {
74 	u16 tlist;
75 	int cpu;
76 
77 	assert(irq < 16);
78 
79 	/*
80 	 * For each cpu in the mask collect its peers, which are also in
81 	 * the mask, in order to form target lists.
82 	 */
83 	for_each_cpu(cpu, dest) {
84 		u64 mpidr = cpus[cpu], sgi1r;
85 		u64 cluster_id;
86 
87 		/*
88 		 * GICv3 can send IPIs to up 16 peer cpus with a single
89 		 * write to ICC_SGI1R_EL1 (using the target list). Peers
90 		 * are cpus that have nearly identical MPIDRs, the only
91 		 * difference being Aff0. The matching upper affinity
92 		 * levels form the cluster ID.
93 		 */
94 		cluster_id = mpidr & ~0xffUL;
95 		tlist = 0;
96 
97 		/*
98 		 * Sort of open code for_each_cpu in order to have a
99 		 * nested for_each_cpu loop.
100 		 */
101 		while (cpu < nr_cpus) {
102 			if ((mpidr & 0xff) >= 16) {
103 				printf("cpu%d MPIDR:aff0 is %d (>= 16)!\n",
104 					cpu, (int)(mpidr & 0xff));
105 				break;
106 			}
107 
108 			tlist |= 1 << (mpidr & 0xf);
109 
110 			cpu = cpumask_next(cpu, dest);
111 			if (cpu >= nr_cpus)
112 				break;
113 
114 			mpidr = cpus[cpu];
115 
116 			if (cluster_id != (mpidr & ~0xffUL)) {
117 				/*
118 				 * The next cpu isn't in our cluster. Roll
119 				 * back the cpu index allowing the outer
120 				 * for_each_cpu to find it again with
121 				 * cpumask_next
122 				 */
123 				--cpu;
124 				break;
125 			}
126 		}
127 
128 		/* Send the IPIs for the target list of this cluster */
129 		sgi1r = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3)	|
130 			 MPIDR_TO_SGI_AFFINITY(cluster_id, 2)	|
131 			 irq << 24				|
132 			 MPIDR_TO_SGI_AFFINITY(cluster_id, 1)	|
133 			 tlist);
134 
135 		gicv3_write_sgi1r(sgi1r);
136 	}
137 
138 	/* Force the above writes to ICC_SGI1R_EL1 to be executed */
139 	isb();
140 }
141 
142 void gicv3_ipi_send_single(int irq, int cpu)
143 {
144 	cpumask_t dest;
145 
146 	cpumask_clear(&dest);
147 	cpumask_set_cpu(cpu, &dest);
148 	gicv3_ipi_send_mask(irq, &dest);
149 }
150