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