irqchip updates for 4.15, take #2

- A number of MIPS GIC updates and cleanups
 - One GICv4 update
 - Another firmware workaround for GICv2
 - Support for Mason8 GPIOs
 - Tiny documentation fix
 -----BEGIN PGP SIGNATURE-----
 
 iQJJBAABCAAzFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAln7UeAVHG1hcmMuenlu
 Z2llckBhcm0uY29tAAoJECPQ0LrRPXpDtLkQAII8Ij6h2nWN8MaSOcrOsar58PU9
 gfa8HidjH7QRht5xcpaAYXO0+VO7etP37JpTlzcJfpaAU8tFTKAMJKbWaHRe2KKV
 YbfRYWhnyizmb2Sn5+JDKlVVPAgR9tZN9UQyDceERb4yZmGOc9nHBObJIPAYtvnr
 zKLPo5ldVs7bCQan+uGXMyncVGgJ70rYCNPhHqzyNbAUgkI3XA85Wk9NgBXQCr/2
 8SiE6hs+QdkELy545Pi0oPxvqY5q4f2QmgOjWhQtJgbAQvWpizMi2Qz4zH8Gs89t
 Iln6zaw8315vfPSiyXckbIlMoUUzejLuxtvwvdgdrjE5vogNkj+Z874R3yNbLZ2H
 CW+DZz9gv+e2uzIiqBBpw1DatWBQQxoXvUVW1fyEOqwZPpr7JvAKwJ9Nwhc0kUdG
 H2AjO1KL/WqiqCpEDAYF+zY3zLUSAaoNjMWX8+nlaUstgQO4hSyPyU3RUJXmpHf8
 DHIVelZlhurKjQP6SfH1QKs+Wqv2QAZb8Otb8Pt/IMw73mXmRMbMPB7uOc2A7TqG
 uceAQuOyVBG+QF2dGiXZ5RqTNtL1gWns+S+abJCtRkD+JV83teZw3I4sMnVpeqtO
 rdavJVejrePck8Tgd1qOCGfXUQrBhCG1xJFkt/Y9tWbduU0R4V1y5J7LcGBLMYSy
 cV5WD3fejfjayTlT
 =Aozt
 -----END PGP SIGNATURE-----

Merge tag 'irqchip-4.15-2' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull the second batch of irqchip updates for 4.15 from marc Zyngier:

 - A number of MIPS GIC updates and cleanups
 - One GICv4 update
 - Another firmware workaround for GICv2
 - Support for Mason8 GPIOs
 - Tiny documentation fix
This commit is contained in:
Thomas Gleixner 2017-11-02 19:18:08 +01:00
commit 722c908f84
241 changed files with 3090 additions and 1903 deletions

View file

@ -1008,9 +1008,15 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
if (irqd_is_forwarded_to_vcpu(d)) {
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
u32 event = its_get_event_id(d);
struct its_vlpi_map *map;
prop_page = its_dev->event_map.vm->vprop_page;
hwirq = its_dev->event_map.vlpi_maps[event].vintid;
map = &its_dev->event_map.vlpi_maps[event];
hwirq = map->vintid;
/* Remember the updated property */
map->properties &= ~clr;
map->properties |= set | LPI_PROP_GROUP1;
} else {
prop_page = gic_rdists->prop_page;
hwirq = d->hwirq;
@ -1249,12 +1255,20 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
/* Ensure all the VPEs are mapped on this ITS */
its_map_vm(its_dev->its, info->map->vm);
/*
* Flag the interrupt as forwarded so that we can
* start poking the virtual property table.
*/
irqd_set_forwarded_to_vcpu(d);
/* Write out the property to the prop table */
lpi_write_config(d, 0xff, info->map->properties);
/* Drop the physical mapping */
its_send_discard(its_dev, event);
/* and install the virtual one */
its_send_vmapti(its_dev, event);
irqd_set_forwarded_to_vcpu(d);
/* Increment the number of VLPIs */
its_dev->event_map.nr_vlpis++;

View file

@ -1256,6 +1256,19 @@ static void gic_teardown(struct gic_chip_data *gic)
#ifdef CONFIG_OF
static int gic_cnt __initdata;
static bool gicv2_force_probe;
static int __init gicv2_force_probe_cfg(char *buf)
{
return strtobool(buf, &gicv2_force_probe);
}
early_param("irqchip.gicv2_force_probe", gicv2_force_probe_cfg);
static bool gic_check_gicv2(void __iomem *base)
{
u32 val = readl_relaxed(base + GIC_CPU_IDENT);
return (val & 0xff0fff) == 0x02043B;
}
static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
{
@ -1265,20 +1278,60 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
if (!is_hyp_mode_available())
return false;
if (resource_size(&cpuif_res) < SZ_8K)
return false;
if (resource_size(&cpuif_res) == SZ_128K) {
u32 val_low, val_high;
if (resource_size(&cpuif_res) < SZ_8K) {
void __iomem *alt;
/*
* Check for a stupid firmware that only exposes the
* first page of a GICv2.
*/
if (!gic_check_gicv2(*base))
return false;
if (!gicv2_force_probe) {
pr_warn("GIC: GICv2 detected, but range too small and irqchip.gicv2_force_probe not set\n");
return false;
}
alt = ioremap(cpuif_res.start, SZ_8K);
if (!alt)
return false;
if (!gic_check_gicv2(alt + SZ_4K)) {
/*
* The first page was that of a GICv2, and
* the second was *something*. Let's trust it
* to be a GICv2, and update the mapping.
*/
pr_warn("GIC: GICv2 at %pa, but range is too small (broken DT?), assuming 8kB\n",
&cpuif_res.start);
iounmap(*base);
*base = alt;
return true;
}
/*
* Verify that we have the first 4kB of a GIC400
* We detected *two* initial GICv2 pages in a
* row. Could be a GICv2 aliased over two 64kB
* pages. Update the resource, map the iospace, and
* pray.
*/
iounmap(alt);
alt = ioremap(cpuif_res.start, SZ_128K);
if (!alt)
return false;
pr_warn("GIC: Aliased GICv2 at %pa, trying to find the canonical range over 128kB\n",
&cpuif_res.start);
cpuif_res.end = cpuif_res.start + SZ_128K -1;
iounmap(*base);
*base = alt;
}
if (resource_size(&cpuif_res) == SZ_128K) {
/*
* Verify that we have the first 4kB of a GICv2
* aliased over the first 64kB by checking the
* GICC_IIDR register on both ends.
*/
val_low = readl_relaxed(*base + GIC_CPU_IDENT);
val_high = readl_relaxed(*base + GIC_CPU_IDENT + 0xf000);
if ((val_low & 0xffff0fff) != 0x0202043B ||
val_low != val_high)
if (!gic_check_gicv2(*base) ||
!gic_check_gicv2(*base + 0xf000))
return false;
/*

View file

@ -47,6 +47,10 @@ struct meson_gpio_irq_params {
unsigned int nr_hwirq;
};
static const struct meson_gpio_irq_params meson8_params = {
.nr_hwirq = 134,
};
static const struct meson_gpio_irq_params meson8b_params = {
.nr_hwirq = 119,
};
@ -60,6 +64,7 @@ static const struct meson_gpio_irq_params gxl_params = {
};
static const struct of_device_id meson_irq_gpio_matches[] = {
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
{ .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params },
{ .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params },

View file

@ -8,6 +8,7 @@
*/
#include <linux/bitmap.h>
#include <linux/clocksource.h>
#include <linux/cpuhotplug.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@ -48,12 +49,16 @@ static DEFINE_SPINLOCK(gic_lock);
static struct irq_domain *gic_irq_domain;
static struct irq_domain *gic_ipi_domain;
static int gic_shared_intrs;
static int gic_vpes;
static unsigned int gic_cpu_pin;
static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
static DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
static DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS);
static struct gic_all_vpes_chip_data {
u32 map;
bool mask;
} gic_all_vpes_chip_data[GIC_NUM_LOCAL_INTRS];
static void gic_clear_pcpu_masks(unsigned int intr)
{
@ -175,14 +180,13 @@ static void gic_mask_irq(struct irq_data *d)
static void gic_unmask_irq(struct irq_data *d)
{
struct cpumask *affinity = irq_data_get_affinity_mask(d);
unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq);
unsigned int cpu;
write_gic_smask(intr);
gic_clear_pcpu_masks(intr);
cpu = cpumask_first_and(affinity, cpu_online_mask);
cpu = cpumask_first(irq_data_get_effective_affinity_mask(d));
set_bit(intr, per_cpu_ptr(pcpu_masks, cpu));
}
@ -195,46 +199,46 @@ static void gic_ack_irq(struct irq_data *d)
static int gic_set_type(struct irq_data *d, unsigned int type)
{
unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
unsigned int irq, pol, trig, dual;
unsigned long flags;
bool is_edge;
irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
spin_lock_irqsave(&gic_lock, flags);
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_FALLING:
change_gic_pol(irq, GIC_POL_FALLING_EDGE);
change_gic_trig(irq, GIC_TRIG_EDGE);
change_gic_dual(irq, GIC_DUAL_SINGLE);
is_edge = true;
pol = GIC_POL_FALLING_EDGE;
trig = GIC_TRIG_EDGE;
dual = GIC_DUAL_SINGLE;
break;
case IRQ_TYPE_EDGE_RISING:
change_gic_pol(irq, GIC_POL_RISING_EDGE);
change_gic_trig(irq, GIC_TRIG_EDGE);
change_gic_dual(irq, GIC_DUAL_SINGLE);
is_edge = true;
pol = GIC_POL_RISING_EDGE;
trig = GIC_TRIG_EDGE;
dual = GIC_DUAL_SINGLE;
break;
case IRQ_TYPE_EDGE_BOTH:
/* polarity is irrelevant in this case */
change_gic_trig(irq, GIC_TRIG_EDGE);
change_gic_dual(irq, GIC_DUAL_DUAL);
is_edge = true;
pol = 0; /* Doesn't matter */
trig = GIC_TRIG_EDGE;
dual = GIC_DUAL_DUAL;
break;
case IRQ_TYPE_LEVEL_LOW:
change_gic_pol(irq, GIC_POL_ACTIVE_LOW);
change_gic_trig(irq, GIC_TRIG_LEVEL);
change_gic_dual(irq, GIC_DUAL_SINGLE);
is_edge = false;
pol = GIC_POL_ACTIVE_LOW;
trig = GIC_TRIG_LEVEL;
dual = GIC_DUAL_SINGLE;
break;
case IRQ_TYPE_LEVEL_HIGH:
default:
change_gic_pol(irq, GIC_POL_ACTIVE_HIGH);
change_gic_trig(irq, GIC_TRIG_LEVEL);
change_gic_dual(irq, GIC_DUAL_SINGLE);
is_edge = false;
pol = GIC_POL_ACTIVE_HIGH;
trig = GIC_TRIG_LEVEL;
dual = GIC_DUAL_SINGLE;
break;
}
if (is_edge)
change_gic_pol(irq, pol);
change_gic_trig(irq, trig);
change_gic_dual(irq, dual);
if (trig == GIC_TRIG_EDGE)
irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller,
handle_edge_irq, NULL);
else
@ -339,13 +343,17 @@ static struct irq_chip gic_local_irq_controller = {
static void gic_mask_local_irq_all_vpes(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
int i;
struct gic_all_vpes_chip_data *cd;
unsigned long flags;
int intr, cpu;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
cd->mask = false;
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
write_gic_vl_other(mips_cm_vp_id(i));
for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_rmask(BIT(intr));
}
spin_unlock_irqrestore(&gic_lock, flags);
@ -353,22 +361,40 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d)
static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
int i;
struct gic_all_vpes_chip_data *cd;
unsigned long flags;
int intr, cpu;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
cd->mask = true;
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
write_gic_vl_other(mips_cm_vp_id(i));
for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_smask(BIT(intr));
}
spin_unlock_irqrestore(&gic_lock, flags);
}
static void gic_all_vpes_irq_cpu_online(struct irq_data *d)
{
struct gic_all_vpes_chip_data *cd;
unsigned int intr;
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
cd = irq_data_get_irq_chip_data(d);
write_gic_vl_map(intr, cd->map);
if (cd->mask)
write_gic_vl_smask(BIT(intr));
}
static struct irq_chip gic_all_vpes_local_irq_controller = {
.name = "MIPS GIC Local",
.irq_mask = gic_mask_local_irq_all_vpes,
.irq_unmask = gic_unmask_local_irq_all_vpes,
.name = "MIPS GIC Local",
.irq_mask = gic_mask_local_irq_all_vpes,
.irq_unmask = gic_unmask_local_irq_all_vpes,
.irq_cpu_online = gic_all_vpes_irq_cpu_online,
};
static void __gic_irq_dispatch(void)
@ -383,50 +409,21 @@ static void gic_irq_dispatch(struct irq_desc *desc)
gic_handle_shared_int(true);
}
static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
int intr = GIC_HWIRQ_TO_LOCAL(hw);
int i;
unsigned long flags;
u32 val;
if (!gic_local_irq_is_routable(intr))
return -EPERM;
if (intr > GIC_LOCAL_INT_FDC) {
pr_err("Invalid local IRQ %d\n", intr);
return -EINVAL;
}
if (intr == GIC_LOCAL_INT_TIMER) {
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_PIN_MAP_TO_PIN | timer_cpu_pin;
} else {
val = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
}
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
write_gic_vl_other(mips_cm_vp_id(i));
write_gic_vo_map(intr, val);
}
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
}
static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw, unsigned int cpu)
{
int intr = GIC_HWIRQ_TO_SHARED(hw);
struct irq_data *data;
unsigned long flags;
data = irq_get_irq_data(virq);
spin_lock_irqsave(&gic_lock, flags);
write_gic_map_pin(intr, GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin);
write_gic_map_vp(intr, BIT(mips_cm_vp_id(cpu)));
gic_clear_pcpu_masks(intr);
set_bit(intr, per_cpu_ptr(pcpu_masks, cpu));
irq_data_update_effective_affinity(data, cpumask_of(cpu));
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
@ -454,7 +451,11 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
int err;
struct gic_all_vpes_chip_data *cd;
unsigned long flags;
unsigned int intr;
int err, cpu;
u32 map;
if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
/* verify that shared irqs don't conflict with an IPI irq */
@ -471,8 +472,14 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
return gic_shared_irq_domain_map(d, virq, hwirq, 0);
}
switch (GIC_HWIRQ_TO_LOCAL(hwirq)) {
intr = GIC_HWIRQ_TO_LOCAL(hwirq);
map = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
switch (intr) {
case GIC_LOCAL_INT_TIMER:
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
map = GIC_MAP_PIN_MAP_TO_PIN | timer_cpu_pin;
/* fall-through */
case GIC_LOCAL_INT_PERFCTR:
case GIC_LOCAL_INT_FDC:
/*
@ -480,9 +487,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
* the rest of the MIPS kernel code does not use the
* percpu IRQ API for them.
*/
cd = &gic_all_vpes_chip_data[intr];
cd->map = map;
err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
&gic_all_vpes_local_irq_controller,
NULL);
cd);
if (err)
return err;
@ -501,7 +510,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
break;
}
return gic_local_irq_domain_map(d, virq, hwirq);
if (!gic_local_irq_is_routable(intr))
return -EPERM;
spin_lock_irqsave(&gic_lock, flags);
for_each_online_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_map(intr, map);
}
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
}
static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
@ -633,11 +652,25 @@ static const struct irq_domain_ops gic_ipi_domain_ops = {
.match = gic_ipi_domain_match,
};
static int gic_cpu_startup(unsigned int cpu)
{
/* Enable or disable EIC */
change_gic_vl_ctl(GIC_VX_CTL_EIC,
cpu_has_veic ? GIC_VX_CTL_EIC : 0);
/* Clear all local IRQ masks (ie. disable all local interrupts) */
write_gic_vl_rmask(~0);
/* Invoke irq_cpu_online callbacks to enable desired interrupts */
irq_cpu_online();
return 0;
}
static int __init gic_of_init(struct device_node *node,
struct device_node *parent)
{
unsigned int cpu_vec, i, j, gicconfig, cpu, v[2];
unsigned int cpu_vec, i, gicconfig, v[2], num_ipis;
unsigned long reserved;
phys_addr_t gic_base;
struct resource res;
@ -645,7 +678,7 @@ static int __init gic_of_init(struct device_node *node,
/* Find the first available CPU vector. */
i = 0;
reserved = (C_SW0 | C_SW1) >> __fls(C_SW0);
reserved = (C_SW0 | C_SW1) >> __ffs(C_SW0);
while (!of_property_read_u32_index(node, "mti,reserved-cpu-vectors",
i++, &cpu_vec))
reserved |= BIT(cpu_vec);
@ -684,20 +717,10 @@ static int __init gic_of_init(struct device_node *node,
gicconfig = read_gic_config();
gic_shared_intrs = gicconfig & GIC_CONFIG_NUMINTERRUPTS;
gic_shared_intrs >>= __fls(GIC_CONFIG_NUMINTERRUPTS);
gic_shared_intrs >>= __ffs(GIC_CONFIG_NUMINTERRUPTS);
gic_shared_intrs = (gic_shared_intrs + 1) * 8;
gic_vpes = gicconfig & GIC_CONFIG_PVPS;
gic_vpes >>= __fls(GIC_CONFIG_PVPS);
gic_vpes = gic_vpes + 1;
if (cpu_has_veic) {
/* Set EIC mode for all VPEs */
for_each_present_cpu(cpu) {
write_gic_vl_other(mips_cm_vp_id(cpu));
write_gic_vo_ctl(GIC_VX_CTL_EIC);
}
/* Always use vector 1 in EIC mode */
gic_cpu_pin = 0;
timer_cpu_pin = gic_cpu_pin;
@ -753,10 +776,12 @@ static int __init gic_of_init(struct device_node *node,
!of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
bitmap_set(ipi_resrv, v[0], v[1]);
} else {
/* Make the last 2 * gic_vpes available for IPIs */
bitmap_set(ipi_resrv,
gic_shared_intrs - 2 * gic_vpes,
2 * gic_vpes);
/*
* Reserve 2 interrupts per possible CPU/VP for use as IPIs,
* meeting the requirements of arch/mips SMP.
*/
num_ipis = 2 * num_possible_cpus();
bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis);
}
bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS);
@ -770,15 +795,8 @@ static int __init gic_of_init(struct device_node *node,
write_gic_rmask(i);
}
for (i = 0; i < gic_vpes; i++) {
write_gic_vl_other(mips_cm_vp_id(i));
for (j = 0; j < GIC_NUM_LOCAL_INTRS; j++) {
if (!gic_local_irq_is_routable(j))
continue;
write_gic_vo_rmask(BIT(j));
}
}
return 0;
return cpuhp_setup_state(CPUHP_AP_IRQ_MIPS_GIC_STARTING,
"irqchip/mips/gic:starting",
gic_cpu_startup, NULL);
}
IRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init);