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