xref: /kvm-unit-tests/lib/arm/gic-v3.c (revision 2e2d471d2aeb249869b525a42dcf22659d6c4bc7)
191a6c3ceSAndrew Jones /*
291a6c3ceSAndrew Jones  * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com>
391a6c3ceSAndrew Jones  *
491a6c3ceSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
591a6c3ceSAndrew Jones  */
691a6c3ceSAndrew Jones #include <asm/gic.h>
791a6c3ceSAndrew Jones #include <asm/io.h>
891a6c3ceSAndrew Jones 
991a6c3ceSAndrew Jones void gicv3_set_redist_base(size_t stride)
1091a6c3ceSAndrew Jones {
1191a6c3ceSAndrew Jones 	u32 aff = mpidr_compress(get_mpidr());
1291a6c3ceSAndrew Jones 	void *ptr = gicv3_data.redist_base[0];
1391a6c3ceSAndrew Jones 	u64 typer;
1491a6c3ceSAndrew Jones 
1591a6c3ceSAndrew Jones 	do {
1691a6c3ceSAndrew Jones 		typer = gicv3_read_typer(ptr + GICR_TYPER);
1791a6c3ceSAndrew Jones 		if ((typer >> 32) == aff) {
1891a6c3ceSAndrew Jones 			gicv3_redist_base() = ptr;
1991a6c3ceSAndrew Jones 			return;
2091a6c3ceSAndrew Jones 		}
2191a6c3ceSAndrew Jones 		ptr += stride; /* skip RD_base, SGI_base, etc. */
2291a6c3ceSAndrew Jones 	} while (!(typer & GICR_TYPER_LAST));
2391a6c3ceSAndrew Jones 
2491a6c3ceSAndrew Jones 	/* should never reach here */
2591a6c3ceSAndrew Jones 	assert(0);
2691a6c3ceSAndrew Jones }
2791a6c3ceSAndrew Jones 
2891a6c3ceSAndrew Jones void gicv3_enable_defaults(void)
2991a6c3ceSAndrew Jones {
3091a6c3ceSAndrew Jones 	void *dist = gicv3_dist_base();
3191a6c3ceSAndrew Jones 	void *sgi_base;
3291a6c3ceSAndrew Jones 	unsigned int i;
3391a6c3ceSAndrew Jones 
3491a6c3ceSAndrew Jones 	gicv3_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER));
3591a6c3ceSAndrew Jones 	if (gicv3_data.irq_nr > 1020)
3691a6c3ceSAndrew Jones 		gicv3_data.irq_nr = 1020;
3791a6c3ceSAndrew Jones 
3891a6c3ceSAndrew Jones 	writel(0, dist + GICD_CTLR);
3991a6c3ceSAndrew Jones 	gicv3_dist_wait_for_rwp();
4091a6c3ceSAndrew Jones 
4191a6c3ceSAndrew Jones 	writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
4291a6c3ceSAndrew Jones 	       dist + GICD_CTLR);
4391a6c3ceSAndrew Jones 	gicv3_dist_wait_for_rwp();
4491a6c3ceSAndrew Jones 
4591a6c3ceSAndrew Jones 	for (i = 0; i < gicv3_data.irq_nr; i += 4)
4691a6c3ceSAndrew Jones 		writel(~0, dist + GICD_IGROUPR + i);
4791a6c3ceSAndrew Jones 
4891a6c3ceSAndrew Jones 	if (!gicv3_redist_base())
4991a6c3ceSAndrew Jones 		gicv3_set_redist_base(SZ_64K * 2);
5091a6c3ceSAndrew Jones 	sgi_base = gicv3_sgi_base();
5191a6c3ceSAndrew Jones 
5291a6c3ceSAndrew Jones 	writel(~0, sgi_base + GICR_IGROUPR0);
5391a6c3ceSAndrew Jones 
5491a6c3ceSAndrew Jones 	for (i = 0; i < 16; i += 4)
5591a6c3ceSAndrew Jones 		writel(GICD_INT_DEF_PRI_X4, sgi_base + GICR_IPRIORITYR0 + i);
5691a6c3ceSAndrew Jones 
5791a6c3ceSAndrew Jones 	writel(GICD_INT_EN_SET_SGI, sgi_base + GICR_ISENABLER0);
5891a6c3ceSAndrew Jones 
5991a6c3ceSAndrew Jones 	gicv3_write_pmr(GICC_INT_PRI_THRESHOLD);
6091a6c3ceSAndrew Jones 	gicv3_write_grpen1(1);
6191a6c3ceSAndrew Jones }
62*2e2d471dSAndrew Jones 
63*2e2d471dSAndrew Jones u32 gicv3_iar_irqnr(u32 iar)
64*2e2d471dSAndrew Jones {
65*2e2d471dSAndrew Jones 	return iar & ((1 << 24) - 1);
66*2e2d471dSAndrew Jones }
67*2e2d471dSAndrew Jones 
68*2e2d471dSAndrew Jones void gicv3_ipi_send_mask(int irq, const cpumask_t *dest)
69*2e2d471dSAndrew Jones {
70*2e2d471dSAndrew Jones 	u16 tlist;
71*2e2d471dSAndrew Jones 	int cpu;
72*2e2d471dSAndrew Jones 
73*2e2d471dSAndrew Jones 	assert(irq < 16);
74*2e2d471dSAndrew Jones 
75*2e2d471dSAndrew Jones 	/*
76*2e2d471dSAndrew Jones 	 * For each cpu in the mask collect its peers, which are also in
77*2e2d471dSAndrew Jones 	 * the mask, in order to form target lists.
78*2e2d471dSAndrew Jones 	 */
79*2e2d471dSAndrew Jones 	for_each_cpu(cpu, dest) {
80*2e2d471dSAndrew Jones 		u64 mpidr = cpus[cpu], sgi1r;
81*2e2d471dSAndrew Jones 		u64 cluster_id;
82*2e2d471dSAndrew Jones 
83*2e2d471dSAndrew Jones 		/*
84*2e2d471dSAndrew Jones 		 * GICv3 can send IPIs to up 16 peer cpus with a single
85*2e2d471dSAndrew Jones 		 * write to ICC_SGI1R_EL1 (using the target list). Peers
86*2e2d471dSAndrew Jones 		 * are cpus that have nearly identical MPIDRs, the only
87*2e2d471dSAndrew Jones 		 * difference being Aff0. The matching upper affinity
88*2e2d471dSAndrew Jones 		 * levels form the cluster ID.
89*2e2d471dSAndrew Jones 		 */
90*2e2d471dSAndrew Jones 		cluster_id = mpidr & ~0xffUL;
91*2e2d471dSAndrew Jones 		tlist = 0;
92*2e2d471dSAndrew Jones 
93*2e2d471dSAndrew Jones 		/*
94*2e2d471dSAndrew Jones 		 * Sort of open code for_each_cpu in order to have a
95*2e2d471dSAndrew Jones 		 * nested for_each_cpu loop.
96*2e2d471dSAndrew Jones 		 */
97*2e2d471dSAndrew Jones 		while (cpu < nr_cpus) {
98*2e2d471dSAndrew Jones 			if ((mpidr & 0xff) >= 16) {
99*2e2d471dSAndrew Jones 				printf("cpu%d MPIDR:aff0 is %d (>= 16)!\n",
100*2e2d471dSAndrew Jones 					cpu, (int)(mpidr & 0xff));
101*2e2d471dSAndrew Jones 				break;
102*2e2d471dSAndrew Jones 			}
103*2e2d471dSAndrew Jones 
104*2e2d471dSAndrew Jones 			tlist |= 1 << (mpidr & 0xf);
105*2e2d471dSAndrew Jones 
106*2e2d471dSAndrew Jones 			cpu = cpumask_next(cpu, dest);
107*2e2d471dSAndrew Jones 			if (cpu >= nr_cpus)
108*2e2d471dSAndrew Jones 				break;
109*2e2d471dSAndrew Jones 
110*2e2d471dSAndrew Jones 			mpidr = cpus[cpu];
111*2e2d471dSAndrew Jones 
112*2e2d471dSAndrew Jones 			if (cluster_id != (mpidr & ~0xffUL)) {
113*2e2d471dSAndrew Jones 				/*
114*2e2d471dSAndrew Jones 				 * The next cpu isn't in our cluster. Roll
115*2e2d471dSAndrew Jones 				 * back the cpu index allowing the outer
116*2e2d471dSAndrew Jones 				 * for_each_cpu to find it again with
117*2e2d471dSAndrew Jones 				 * cpumask_next
118*2e2d471dSAndrew Jones 				 */
119*2e2d471dSAndrew Jones 				--cpu;
120*2e2d471dSAndrew Jones 				break;
121*2e2d471dSAndrew Jones 			}
122*2e2d471dSAndrew Jones 		}
123*2e2d471dSAndrew Jones 
124*2e2d471dSAndrew Jones 		/* Send the IPIs for the target list of this cluster */
125*2e2d471dSAndrew Jones 		sgi1r = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3)	|
126*2e2d471dSAndrew Jones 			 MPIDR_TO_SGI_AFFINITY(cluster_id, 2)	|
127*2e2d471dSAndrew Jones 			 irq << 24				|
128*2e2d471dSAndrew Jones 			 MPIDR_TO_SGI_AFFINITY(cluster_id, 1)	|
129*2e2d471dSAndrew Jones 			 tlist);
130*2e2d471dSAndrew Jones 
131*2e2d471dSAndrew Jones 		gicv3_write_sgi1r(sgi1r);
132*2e2d471dSAndrew Jones 	}
133*2e2d471dSAndrew Jones 
134*2e2d471dSAndrew Jones 	/* Force the above writes to ICC_SGI1R_EL1 to be executed */
135*2e2d471dSAndrew Jones 	isb();
136*2e2d471dSAndrew Jones }
137*2e2d471dSAndrew Jones 
138*2e2d471dSAndrew Jones void gicv3_ipi_send_single(int irq, int cpu)
139*2e2d471dSAndrew Jones {
140*2e2d471dSAndrew Jones 	cpumask_t dest;
141*2e2d471dSAndrew Jones 
142*2e2d471dSAndrew Jones 	cpumask_clear(&dest);
143*2e2d471dSAndrew Jones 	cpumask_set_cpu(cpu, &dest);
144*2e2d471dSAndrew Jones 	gicv3_ipi_send_mask(irq, &dest);
145*2e2d471dSAndrew Jones }
146