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