static void its_cpu_init_lpis()

in irq-gic-v3-its.c [3044:3169]


static void its_cpu_init_lpis(void)
{
	void __iomem *rbase = gic_data_rdist_rd_base();
	struct page *pend_page;
	phys_addr_t paddr;
	u64 val, tmp;

	if (gic_data_rdist()->flags & RD_LOCAL_LPI_ENABLED)
		return;

	val = readl_relaxed(rbase + GICR_CTLR);
	if ((gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) &&
	    (val & GICR_CTLR_ENABLE_LPIS)) {
		/*
		 * Check that we get the same property table on all
		 * RDs. If we don't, this is hopeless.
		 */
		paddr = gicr_read_propbaser(rbase + GICR_PROPBASER);
		paddr &= GENMASK_ULL(51, 12);
		if (WARN_ON(gic_rdists->prop_table_pa != paddr))
			add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);

		paddr = gicr_read_pendbaser(rbase + GICR_PENDBASER);
		paddr &= GENMASK_ULL(51, 16);

		WARN_ON(!gic_check_reserved_range(paddr, LPI_PENDBASE_SZ));
		gic_data_rdist()->flags |= RD_LOCAL_PENDTABLE_PREALLOCATED;

		goto out;
	}

	pend_page = gic_data_rdist()->pend_page;
	paddr = page_to_phys(pend_page);

	/* set PROPBASE */
	val = (gic_rdists->prop_table_pa |
	       GICR_PROPBASER_InnerShareable |
	       GICR_PROPBASER_RaWaWb |
	       ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK));

	gicr_write_propbaser(val, rbase + GICR_PROPBASER);
	tmp = gicr_read_propbaser(rbase + GICR_PROPBASER);

	if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) {
		if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) {
			/*
			 * The HW reports non-shareable, we must
			 * remove the cacheability attributes as
			 * well.
			 */
			val &= ~(GICR_PROPBASER_SHAREABILITY_MASK |
				 GICR_PROPBASER_CACHEABILITY_MASK);
			val |= GICR_PROPBASER_nC;
			gicr_write_propbaser(val, rbase + GICR_PROPBASER);
		}
		pr_info_once("GIC: using cache flushing for LPI property table\n");
		gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING;
	}

	/* set PENDBASE */
	val = (page_to_phys(pend_page) |
	       GICR_PENDBASER_InnerShareable |
	       GICR_PENDBASER_RaWaWb);

	gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
	tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER);

	if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) {
		/*
		 * The HW reports non-shareable, we must remove the
		 * cacheability attributes as well.
		 */
		val &= ~(GICR_PENDBASER_SHAREABILITY_MASK |
			 GICR_PENDBASER_CACHEABILITY_MASK);
		val |= GICR_PENDBASER_nC;
		gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
	}

	/* Enable LPIs */
	val = readl_relaxed(rbase + GICR_CTLR);
	val |= GICR_CTLR_ENABLE_LPIS;
	writel_relaxed(val, rbase + GICR_CTLR);

	if (gic_rdists->has_vlpis && !gic_rdists->has_rvpeid) {
		void __iomem *vlpi_base = gic_data_rdist_vlpi_base();

		/*
		 * It's possible for CPU to receive VLPIs before it is
		 * scheduled as a vPE, especially for the first CPU, and the
		 * VLPI with INTID larger than 2^(IDbits+1) will be considered
		 * as out of range and dropped by GIC.
		 * So we initialize IDbits to known value to avoid VLPI drop.
		 */
		val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK;
		pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n",
			smp_processor_id(), val);
		gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);

		/*
		 * Also clear Valid bit of GICR_VPENDBASER, in case some
		 * ancient programming gets left in and has possibility of
		 * corrupting memory.
		 */
		val = its_clear_vpend_valid(vlpi_base, 0, 0);
	}

	if (allocate_vpe_l1_table()) {
		/*
		 * If the allocation has failed, we're in massive trouble.
		 * Disable direct injection, and pray that no VM was
		 * already running...
		 */
		gic_rdists->has_rvpeid = false;
		gic_rdists->has_vlpis = false;
	}

	/* Make sure the GIC has seen the above */
	dsb(sy);
out:
	gic_data_rdist()->flags |= RD_LOCAL_LPI_ENABLED;
	pr_info("GICv3: CPU%d: using %s LPI pending table @%pa\n",
		smp_processor_id(),
		gic_data_rdist()->flags & RD_LOCAL_PENDTABLE_PREALLOCATED ?
		"reserved" : "allocated",
		&paddr);
}