/* * Copyright (C) 2016, Red Hat Inc, Andrew Jones * * This work is licensed under the terms of the GNU LGPL, version 2. */ #include #include void gicv3_set_redist_base(size_t stride) { u32 aff = mpidr_compress(get_mpidr()); void *ptr = gicv3_data.redist_base[0]; u64 typer; do { typer = gicv3_read_typer(ptr + GICR_TYPER); if ((typer >> 32) == aff) { gicv3_redist_base() = ptr; return; } ptr += stride; /* skip RD_base, SGI_base, etc. */ } while (!(typer & GICR_TYPER_LAST)); /* should never reach here */ assert(0); } void gicv3_enable_defaults(void) { void *dist = gicv3_dist_base(); void *sgi_base; unsigned int i; gicv3_data.irq_nr = GICD_TYPER_IRQS(readl(dist + GICD_TYPER)); if (gicv3_data.irq_nr > 1020) gicv3_data.irq_nr = 1020; writel(0, dist + GICD_CTLR); gicv3_dist_wait_for_rwp(); writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, dist + GICD_CTLR); gicv3_dist_wait_for_rwp(); for (i = 0; i < gicv3_data.irq_nr; i += 4) writel(~0, dist + GICD_IGROUPR + i); if (!gicv3_redist_base()) gicv3_set_redist_base(SZ_64K * 2); sgi_base = gicv3_sgi_base(); writel(~0, sgi_base + GICR_IGROUPR0); for (i = 0; i < 16; i += 4) writel(GICD_INT_DEF_PRI_X4, sgi_base + GICR_IPRIORITYR0 + i); writel(GICD_INT_EN_SET_SGI, sgi_base + GICR_ISENABLER0); gicv3_write_pmr(GICC_INT_PRI_THRESHOLD); gicv3_write_grpen1(1); } u32 gicv3_iar_irqnr(u32 iar) { return iar & ((1 << 24) - 1); } void gicv3_ipi_send_mask(int irq, const cpumask_t *dest) { u16 tlist; int cpu; assert(irq < 16); /* * For each cpu in the mask collect its peers, which are also in * the mask, in order to form target lists. */ for_each_cpu(cpu, dest) { u64 mpidr = cpus[cpu], sgi1r; u64 cluster_id; /* * GICv3 can send IPIs to up 16 peer cpus with a single * write to ICC_SGI1R_EL1 (using the target list). Peers * are cpus that have nearly identical MPIDRs, the only * difference being Aff0. The matching upper affinity * levels form the cluster ID. */ cluster_id = mpidr & ~0xffUL; tlist = 0; /* * Sort of open code for_each_cpu in order to have a * nested for_each_cpu loop. */ while (cpu < nr_cpus) { if ((mpidr & 0xff) >= 16) { printf("cpu%d MPIDR:aff0 is %d (>= 16)!\n", cpu, (int)(mpidr & 0xff)); break; } tlist |= 1 << (mpidr & 0xf); cpu = cpumask_next(cpu, dest); if (cpu >= nr_cpus) break; mpidr = cpus[cpu]; if (cluster_id != (mpidr & ~0xffUL)) { /* * The next cpu isn't in our cluster. Roll * back the cpu index allowing the outer * for_each_cpu to find it again with * cpumask_next */ --cpu; break; } } /* Send the IPIs for the target list of this cluster */ sgi1r = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3) | MPIDR_TO_SGI_AFFINITY(cluster_id, 2) | irq << 24 | MPIDR_TO_SGI_AFFINITY(cluster_id, 1) | tlist); gicv3_write_sgi1r(sgi1r); } /* Force the above writes to ICC_SGI1R_EL1 to be executed */ isb(); } void gicv3_ipi_send_single(int irq, int cpu) { cpumask_t dest; cpumask_clear(&dest); cpumask_set_cpu(cpu, &dest); gicv3_ipi_send_mask(irq, &dest); }