diff options
Diffstat (limited to 'drivers/irqchip/irq-gic-v3-its.c')
| -rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 84 | 
1 files changed, 76 insertions, 8 deletions
| diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index eb0882d15366..d25b7a864bbb 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -46,6 +46,10 @@  #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING	(1 << 0)  #define RDIST_FLAGS_RD_TABLES_PREALLOCATED	(1 << 1) +#define RD_LOCAL_LPI_ENABLED                    BIT(0) +#define RD_LOCAL_PENDTABLE_PREALLOCATED         BIT(1) +#define RD_LOCAL_MEMRESERVE_DONE                BIT(2) +  static u32 lpi_id_bits;  /* @@ -742,7 +746,7 @@ static struct its_collection *its_build_invall_cmd(struct its_node *its,  	its_fixup_cmd(cmd); -	return NULL; +	return desc->its_invall_cmd.col;  }  static struct its_vpe *its_build_vinvall_cmd(struct its_node *its, @@ -3044,7 +3048,7 @@ static void its_cpu_init_lpis(void)  	phys_addr_t paddr;  	u64 val, tmp; -	if (gic_data_rdist()->lpi_enabled) +	if (gic_data_rdist()->flags & RD_LOCAL_LPI_ENABLED)  		return;  	val = readl_relaxed(rbase + GICR_CTLR); @@ -3063,15 +3067,13 @@ static void its_cpu_init_lpis(void)  		paddr &= GENMASK_ULL(51, 16);  		WARN_ON(!gic_check_reserved_range(paddr, LPI_PENDBASE_SZ)); -		its_free_pending_table(gic_data_rdist()->pend_page); -		gic_data_rdist()->pend_page = NULL; +		gic_data_rdist()->flags |= RD_LOCAL_PENDTABLE_PREALLOCATED;  		goto out;  	}  	pend_page = gic_data_rdist()->pend_page;  	paddr = page_to_phys(pend_page); -	WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ));  	/* set PROPBASE */  	val = (gic_rdists->prop_table_pa | @@ -3158,10 +3160,11 @@ static void its_cpu_init_lpis(void)  	/* Make sure the GIC has seen the above */  	dsb(sy);  out: -	gic_data_rdist()->lpi_enabled = true; +	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()->pend_page ? "allocated" : "reserved", +		gic_data_rdist()->flags & RD_LOCAL_PENDTABLE_PREALLOCATED ? +		"reserved" : "allocated",  		&paddr);  } @@ -5138,7 +5141,7 @@ static int redist_disable_lpis(void)  	 *  	 * If running with preallocated tables, there is nothing to do.  	 */ -	if (gic_data_rdist()->lpi_enabled || +	if ((gic_data_rdist()->flags & RD_LOCAL_LPI_ENABLED) ||  	    (gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED))  		return 0; @@ -5200,6 +5203,51 @@ int its_cpu_init(void)  	return 0;  } +static void rdist_memreserve_cpuhp_cleanup_workfn(struct work_struct *work) +{ +	cpuhp_remove_state_nocalls(gic_rdists->cpuhp_memreserve_state); +	gic_rdists->cpuhp_memreserve_state = CPUHP_INVALID; +} + +static DECLARE_WORK(rdist_memreserve_cpuhp_cleanup_work, +		    rdist_memreserve_cpuhp_cleanup_workfn); + +static int its_cpu_memreserve_lpi(unsigned int cpu) +{ +	struct page *pend_page; +	int ret = 0; + +	/* This gets to run exactly once per CPU */ +	if (gic_data_rdist()->flags & RD_LOCAL_MEMRESERVE_DONE) +		return 0; + +	pend_page = gic_data_rdist()->pend_page; +	if (WARN_ON(!pend_page)) { +		ret = -ENOMEM; +		goto out; +	} +	/* +	 * If the pending table was pre-programmed, free the memory we +	 * preemptively allocated. Otherwise, reserve that memory for +	 * later kexecs. +	 */ +	if (gic_data_rdist()->flags & RD_LOCAL_PENDTABLE_PREALLOCATED) { +		its_free_pending_table(pend_page); +		gic_data_rdist()->pend_page = NULL; +	} else { +		phys_addr_t paddr = page_to_phys(pend_page); +		WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ)); +	} + +out: +	/* Last CPU being brought up gets to issue the cleanup */ +	if (cpumask_equal(&cpus_booted_once_mask, cpu_possible_mask)) +		schedule_work(&rdist_memreserve_cpuhp_cleanup_work); + +	gic_data_rdist()->flags |= RD_LOCAL_MEMRESERVE_DONE; +	return ret; +} +  static const struct of_device_id its_device_id[] = {  	{	.compatible	= "arm,gic-v3-its",	},  	{}, @@ -5383,6 +5431,26 @@ static void __init its_acpi_probe(void)  static void __init its_acpi_probe(void) { }  #endif +int __init its_lpi_memreserve_init(void) +{ +	int state; + +	if (!efi_enabled(EFI_CONFIG_TABLES)) +		return 0; + +	gic_rdists->cpuhp_memreserve_state = CPUHP_INVALID; +	state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, +				  "irqchip/arm/gicv3/memreserve:online", +				  its_cpu_memreserve_lpi, +				  NULL); +	if (state < 0) +		return state; + +	gic_rdists->cpuhp_memreserve_state = state; + +	return 0; +} +  int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,  		    struct irq_domain *parent_domain)  { |