Commit 64b499d8 authored by Marc Zyngier's avatar Marc Zyngier

irqchip/gic-v3: Configure SGIs as standard interrupts

Change the way we deal with GICv3 SGIs by turning them into proper
IRQs, and calling into the arch code to register the interrupt range
instead of a callback.
Reviewed-by: default avatarValentin Schneider <valentin.schneider@arm.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent 70a29c32
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
#define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1)
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
struct redist_region { struct redist_region {
void __iomem *redist_base; void __iomem *redist_base;
phys_addr_t phys_base; phys_addr_t phys_base;
...@@ -383,7 +385,7 @@ static int gic_irq_set_irqchip_state(struct irq_data *d, ...@@ -383,7 +385,7 @@ static int gic_irq_set_irqchip_state(struct irq_data *d,
{ {
u32 reg; u32 reg;
if (d->hwirq >= 8192) /* PPI/SPI only */ if (d->hwirq >= 8192) /* SGI/PPI/SPI only */
return -EINVAL; return -EINVAL;
switch (which) { switch (which) {
...@@ -550,12 +552,12 @@ static int gic_set_type(struct irq_data *d, unsigned int type) ...@@ -550,12 +552,12 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
u32 offset, index; u32 offset, index;
int ret; int ret;
/* Interrupt configuration for SGIs can't be changed */
if (irq < 16)
return -EINVAL;
range = get_intid_range(d); range = get_intid_range(d);
/* Interrupt configuration for SGIs can't be changed */
if (range == SGI_RANGE)
return type != IRQ_TYPE_EDGE_RISING ? -EINVAL : 0;
/* SPIs have restrictions on the supported types */ /* SPIs have restrictions on the supported types */
if ((range == SPI_RANGE || range == ESPI_RANGE) && if ((range == SPI_RANGE || range == ESPI_RANGE) &&
type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
...@@ -583,6 +585,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type) ...@@ -583,6 +585,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
{ {
if (get_intid_range(d) == SGI_RANGE)
return -EINVAL;
if (vcpu) if (vcpu)
irqd_set_forwarded_to_vcpu(d); irqd_set_forwarded_to_vcpu(d);
else else
...@@ -657,38 +662,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs ...@@ -657,38 +662,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
if ((irqnr >= 1020 && irqnr <= 1023)) if ((irqnr >= 1020 && irqnr <= 1023))
return; return;
/* Treat anything but SGIs in a uniform way */ if (static_branch_likely(&supports_deactivate_key))
if (likely(irqnr > 15)) {
int err;
if (static_branch_likely(&supports_deactivate_key))
gic_write_eoir(irqnr);
else
isb();
err = handle_domain_irq(gic_data.domain, irqnr, regs);
if (err) {
WARN_ONCE(true, "Unexpected interrupt received!\n");
gic_deactivate_unhandled(irqnr);
}
return;
}
if (irqnr < 16) {
gic_write_eoir(irqnr); gic_write_eoir(irqnr);
if (static_branch_likely(&supports_deactivate_key)) else
gic_write_dir(irqnr); isb();
#ifdef CONFIG_SMP
/* if (handle_domain_irq(gic_data.domain, irqnr, regs)) {
* Unlike GICv2, we don't need an smp_rmb() here. WARN_ONCE(true, "Unexpected interrupt received!\n");
* The control dependency from gic_read_iar to gic_deactivate_unhandled(irqnr);
* the ISB in gic_write_eoir is enough to ensure
* that any shared data read by handle_IPI will
* be read after the ACK.
*/
handle_IPI(irqnr, regs);
#else
WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
} }
} }
...@@ -1136,11 +1117,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) ...@@ -1136,11 +1117,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
gic_write_sgi1r(val); gic_write_sgi1r(val);
} }
static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
{ {
int cpu; int cpu;
if (WARN_ON(irq >= 16)) if (WARN_ON(d->hwirq >= 16))
return; return;
/* /*
...@@ -1154,7 +1135,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) ...@@ -1154,7 +1135,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
u16 tlist; u16 tlist;
tlist = gic_compute_target_list(&cpu, mask, cluster_id); tlist = gic_compute_target_list(&cpu, mask, cluster_id);
gic_send_sgi(cluster_id, tlist, irq); gic_send_sgi(cluster_id, tlist, d->hwirq);
} }
/* Force the above writes to ICC_SGI1R_EL1 to be executed */ /* Force the above writes to ICC_SGI1R_EL1 to be executed */
...@@ -1163,10 +1144,24 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) ...@@ -1163,10 +1144,24 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
static void __init gic_smp_init(void) static void __init gic_smp_init(void)
{ {
set_smp_cross_call(gic_raise_softirq); struct irq_fwspec sgi_fwspec = {
.fwnode = gic_data.fwnode,
.param_count = 1,
};
int base_sgi;
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
"irqchip/arm/gicv3:starting", "irqchip/arm/gicv3:starting",
gic_starting_cpu, NULL); gic_starting_cpu, NULL);
/* Register all 8 non-secure SGIs */
base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8,
NUMA_NO_NODE, &sgi_fwspec,
false, NULL);
if (WARN_ON(base_sgi <= 0))
return;
set_smp_ipi_range(base_sgi, 8);
} }
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
...@@ -1215,6 +1210,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, ...@@ -1215,6 +1210,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
} }
#else #else
#define gic_set_affinity NULL #define gic_set_affinity NULL
#define gic_ipi_send_mask NULL
#define gic_smp_init() do { } while(0) #define gic_smp_init() do { } while(0)
#endif #endif
...@@ -1257,6 +1253,7 @@ static struct irq_chip gic_chip = { ...@@ -1257,6 +1253,7 @@ static struct irq_chip gic_chip = {
.irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state,
.irq_nmi_setup = gic_irq_nmi_setup, .irq_nmi_setup = gic_irq_nmi_setup,
.irq_nmi_teardown = gic_irq_nmi_teardown, .irq_nmi_teardown = gic_irq_nmi_teardown,
.ipi_send_mask = gic_ipi_send_mask,
.flags = IRQCHIP_SET_TYPE_MASKED | .flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND, IRQCHIP_MASK_ON_SUSPEND,
...@@ -1274,6 +1271,7 @@ static struct irq_chip gic_eoimode1_chip = { ...@@ -1274,6 +1271,7 @@ static struct irq_chip gic_eoimode1_chip = {
.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,
.irq_nmi_setup = gic_irq_nmi_setup, .irq_nmi_setup = gic_irq_nmi_setup,
.irq_nmi_teardown = gic_irq_nmi_teardown, .irq_nmi_teardown = gic_irq_nmi_teardown,
.ipi_send_mask = gic_ipi_send_mask,
.flags = IRQCHIP_SET_TYPE_MASKED | .flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND, IRQCHIP_MASK_ON_SUSPEND,
...@@ -1289,6 +1287,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, ...@@ -1289,6 +1287,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
switch (__get_intid_range(hw)) { switch (__get_intid_range(hw)) {
case SGI_RANGE: case SGI_RANGE:
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_fasteoi_ipi,
NULL, NULL);
break;
case PPI_RANGE: case PPI_RANGE:
case EPPI_RANGE: case EPPI_RANGE:
irq_set_percpu_devid(irq); irq_set_percpu_devid(irq);
...@@ -1318,13 +1322,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, ...@@ -1318,13 +1322,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0; return 0;
} }
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
static int gic_irq_domain_translate(struct irq_domain *d, static int gic_irq_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec, struct irq_fwspec *fwspec,
unsigned long *hwirq, unsigned long *hwirq,
unsigned int *type) unsigned int *type)
{ {
if (fwspec->param_count == 1 && fwspec->param[0] < 16) {
*hwirq = fwspec->param[0];
*type = IRQ_TYPE_EDGE_RISING;
return 0;
}
if (is_of_node(fwspec->fwnode)) { if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count < 3) if (fwspec->param_count < 3)
return -EINVAL; return -EINVAL;
...@@ -1656,9 +1664,9 @@ static int __init gic_init_bases(void __iomem *dist_base, ...@@ -1656,9 +1664,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
gic_update_rdist_properties(); gic_update_rdist_properties();
gic_smp_init();
gic_dist_init(); gic_dist_init();
gic_cpu_init(); gic_cpu_init();
gic_smp_init();
gic_cpu_pm_init(); gic_cpu_pm_init();
if (gic_dist_supports_lpis()) { if (gic_dist_supports_lpis()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment