diff options
| -rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 80 | 
1 files changed, 80 insertions, 0 deletions
| diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 0f1fe56ce0af..ae4e7b355b46 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2443,6 +2443,72 @@ static u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask)  	return 0;  } +static bool allocate_vpe_l2_table(int cpu, u32 id) +{ +	void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base; +	u64 val, gpsz, npg; +	unsigned int psz, esz, idx; +	struct page *page; +	__le64 *table; + +	if (!gic_rdists->has_rvpeid) +		return true; + +	val  = gits_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER); + +	esz  = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val) + 1; +	gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val); +	npg  = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1; + +	switch (gpsz) { +	default: +		WARN_ON(1); +		/* fall through */ +	case GIC_PAGE_SIZE_4K: +		psz = SZ_4K; +		break; +	case GIC_PAGE_SIZE_16K: +		psz = SZ_16K; +		break; +	case GIC_PAGE_SIZE_64K: +		psz = SZ_64K; +		break; +	} + +	/* Don't allow vpe_id that exceeds single, flat table limit */ +	if (!(val & GICR_VPROPBASER_4_1_INDIRECT)) +		return (id < (npg * psz / (esz * SZ_8))); + +	/* Compute 1st level table index & check if that exceeds table limit */ +	idx = id >> ilog2(psz / (esz * SZ_8)); +	if (idx >= (npg * psz / GITS_LVL1_ENTRY_SIZE)) +		return false; + +	table = gic_data_rdist_cpu(cpu)->vpe_l1_base; + +	/* Allocate memory for 2nd level table */ +	if (!table[idx]) { +		page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(psz)); +		if (!page) +			return false; + +		/* Flush Lvl2 table to PoC if hw doesn't support coherency */ +		if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK)) +			gic_flush_dcache_to_poc(page_address(page), psz); + +		table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID); + +		/* Flush Lvl1 entry to PoC if hw doesn't support coherency */ +		if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK)) +			gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE); + +		/* Ensure updated table contents are visible to RD hardware */ +		dsb(sy); +	} + +	return true; +} +  static int allocate_vpe_l1_table(void)  {  	void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); @@ -2957,6 +3023,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)  static bool its_alloc_vpe_table(u32 vpe_id)  {  	struct its_node *its; +	int cpu;  	/*  	 * Make sure the L2 tables are allocated on *all* v4 ITSs. We @@ -2979,6 +3046,19 @@ static bool its_alloc_vpe_table(u32 vpe_id)  			return false;  	} +	/* Non v4.1? No need to iterate RDs and go back early. */ +	if (!gic_rdists->has_rvpeid) +		return true; + +	/* +	 * Make sure the L2 tables are allocated for all copies of +	 * the L1 table on *all* v4.1 RDs. +	 */ +	for_each_possible_cpu(cpu) { +		if (!allocate_vpe_l2_table(cpu, vpe_id)) +			return false; +	} +  	return true;  } | 
