Commit 73a4c521 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq fixes from Ingo Molnar:
 "irqchip driver fixes: most of them are race fixes for ARM GIC (General
  Interrupt Controller) variants, but also a fix for the ARM MMP
  (Marvell PXA168 et al) irqchip affecting OLPC keyboards"

* 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  irqchip/gic-v3-its: Fix ITT_entry_size accessor
  irqchip/mmp: Only touch the PJ4 IRQ & FIQ bits on enable/disable
  irqchip/gic-v3-its: Gracefully fail on LPI exhaustion
  irqchip/gic-v3-its: Plug allocation race for devices sharing a DevID
  irqchip/gic-v4: Fix occasional VLPI drop
parents 212146f0 8087f407
...@@ -97,9 +97,14 @@ struct its_device; ...@@ -97,9 +97,14 @@ struct its_device;
* The ITS structure - contains most of the infrastructure, with the * The ITS structure - contains most of the infrastructure, with the
* top-level MSI domain, the command queue, the collections, and the * top-level MSI domain, the command queue, the collections, and the
* list of devices writing to it. * list of devices writing to it.
*
* dev_alloc_lock has to be taken for device allocations, while the
* spinlock must be taken to parse data structures such as the device
* list.
*/ */
struct its_node { struct its_node {
raw_spinlock_t lock; raw_spinlock_t lock;
struct mutex dev_alloc_lock;
struct list_head entry; struct list_head entry;
void __iomem *base; void __iomem *base;
phys_addr_t phys_base; phys_addr_t phys_base;
...@@ -156,6 +161,7 @@ struct its_device { ...@@ -156,6 +161,7 @@ struct its_device {
void *itt; void *itt;
u32 nr_ites; u32 nr_ites;
u32 device_id; u32 device_id;
bool shared;
}; };
static struct { static struct {
...@@ -1580,6 +1586,9 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids) ...@@ -1580,6 +1586,9 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids)
nr_irqs /= 2; nr_irqs /= 2;
} while (nr_irqs > 0); } while (nr_irqs > 0);
if (!nr_irqs)
err = -ENOSPC;
if (err) if (err)
goto out; goto out;
...@@ -2059,6 +2068,29 @@ static int __init allocate_lpi_tables(void) ...@@ -2059,6 +2068,29 @@ static int __init allocate_lpi_tables(void)
return 0; return 0;
} }
static u64 its_clear_vpend_valid(void __iomem *vlpi_base)
{
u32 count = 1000000; /* 1s! */
bool clean;
u64 val;
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
val &= ~GICR_VPENDBASER_Valid;
gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
do {
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
clean = !(val & GICR_VPENDBASER_Dirty);
if (!clean) {
count--;
cpu_relax();
udelay(1);
}
} while (!clean && count);
return val;
}
static void its_cpu_init_lpis(void) static void its_cpu_init_lpis(void)
{ {
void __iomem *rbase = gic_data_rdist_rd_base(); void __iomem *rbase = gic_data_rdist_rd_base();
...@@ -2144,6 +2176,30 @@ static void its_cpu_init_lpis(void) ...@@ -2144,6 +2176,30 @@ static void its_cpu_init_lpis(void)
val |= GICR_CTLR_ENABLE_LPIS; val |= GICR_CTLR_ENABLE_LPIS;
writel_relaxed(val, rbase + GICR_CTLR); writel_relaxed(val, rbase + GICR_CTLR);
if (gic_rdists->has_vlpis) {
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
/*
* It's possible for CPU to receive VLPIs before it is
* sheduled 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);
gits_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);
WARN_ON(val & GICR_VPENDBASER_Dirty);
}
/* Make sure the GIC has seen the above */ /* Make sure the GIC has seen the above */
dsb(sy); dsb(sy);
out: out:
...@@ -2422,6 +2478,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, ...@@ -2422,6 +2478,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
struct its_device *its_dev; struct its_device *its_dev;
struct msi_domain_info *msi_info; struct msi_domain_info *msi_info;
u32 dev_id; u32 dev_id;
int err = 0;
/* /*
* We ignore "dev" entierely, and rely on the dev_id that has * We ignore "dev" entierely, and rely on the dev_id that has
...@@ -2444,6 +2501,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, ...@@ -2444,6 +2501,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
return -EINVAL; return -EINVAL;
} }
mutex_lock(&its->dev_alloc_lock);
its_dev = its_find_device(its, dev_id); its_dev = its_find_device(its, dev_id);
if (its_dev) { if (its_dev) {
/* /*
...@@ -2451,18 +2509,22 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, ...@@ -2451,18 +2509,22 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
* another alias (PCI bridge of some sort). No need to * another alias (PCI bridge of some sort). No need to
* create the device. * create the device.
*/ */
its_dev->shared = true;
pr_debug("Reusing ITT for devID %x\n", dev_id); pr_debug("Reusing ITT for devID %x\n", dev_id);
goto out; goto out;
} }
its_dev = its_create_device(its, dev_id, nvec, true); its_dev = its_create_device(its, dev_id, nvec, true);
if (!its_dev) if (!its_dev) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
out: out:
mutex_unlock(&its->dev_alloc_lock);
info->scratchpad[0].ptr = its_dev; info->scratchpad[0].ptr = its_dev;
return 0; return err;
} }
static struct msi_domain_ops its_msi_domain_ops = { static struct msi_domain_ops its_msi_domain_ops = {
...@@ -2566,6 +2628,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -2566,6 +2628,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
{ {
struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct irq_data *d = irq_domain_get_irq_data(domain, virq);
struct its_device *its_dev = irq_data_get_irq_chip_data(d); struct its_device *its_dev = irq_data_get_irq_chip_data(d);
struct its_node *its = its_dev->its;
int i; int i;
for (i = 0; i < nr_irqs; i++) { for (i = 0; i < nr_irqs; i++) {
...@@ -2580,8 +2643,14 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -2580,8 +2643,14 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
irq_domain_reset_irq_data(data); irq_domain_reset_irq_data(data);
} }
/* If all interrupts have been freed, start mopping the floor */ mutex_lock(&its->dev_alloc_lock);
if (bitmap_empty(its_dev->event_map.lpi_map,
/*
* If all interrupts have been freed, start mopping the
* floor. This is conditionned on the device not being shared.
*/
if (!its_dev->shared &&
bitmap_empty(its_dev->event_map.lpi_map,
its_dev->event_map.nr_lpis)) { its_dev->event_map.nr_lpis)) {
its_lpi_free(its_dev->event_map.lpi_map, its_lpi_free(its_dev->event_map.lpi_map,
its_dev->event_map.lpi_base, its_dev->event_map.lpi_base,
...@@ -2593,6 +2662,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -2593,6 +2662,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
its_free_device(its_dev); its_free_device(its_dev);
} }
mutex_unlock(&its->dev_alloc_lock);
irq_domain_free_irqs_parent(domain, virq, nr_irqs); irq_domain_free_irqs_parent(domain, virq, nr_irqs);
} }
...@@ -2755,26 +2826,11 @@ static void its_vpe_schedule(struct its_vpe *vpe) ...@@ -2755,26 +2826,11 @@ static void its_vpe_schedule(struct its_vpe *vpe)
static void its_vpe_deschedule(struct its_vpe *vpe) static void its_vpe_deschedule(struct its_vpe *vpe)
{ {
void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
u32 count = 1000000; /* 1s! */
bool clean;
u64 val; u64 val;
/* We're being scheduled out */ val = its_clear_vpend_valid(vlpi_base);
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
val &= ~GICR_VPENDBASER_Valid;
gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
do {
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
clean = !(val & GICR_VPENDBASER_Dirty);
if (!clean) {
count--;
cpu_relax();
udelay(1);
}
} while (!clean && count);
if (unlikely(!clean && !count)) { if (unlikely(val & GICR_VPENDBASER_Dirty)) {
pr_err_ratelimited("ITS virtual pending table not cleaning\n"); pr_err_ratelimited("ITS virtual pending table not cleaning\n");
vpe->idai = false; vpe->idai = false;
vpe->pending_last = true; vpe->pending_last = true;
...@@ -3517,6 +3573,7 @@ static int __init its_probe_one(struct resource *res, ...@@ -3517,6 +3573,7 @@ static int __init its_probe_one(struct resource *res,
} }
raw_spin_lock_init(&its->lock); raw_spin_lock_init(&its->lock);
mutex_init(&its->dev_alloc_lock);
INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->entry);
INIT_LIST_HEAD(&its->its_device_list); INIT_LIST_HEAD(&its->its_device_list);
typer = gic_read_typer(its_base + GITS_TYPER); typer = gic_read_typer(its_base + GITS_TYPER);
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
#define SEL_INT_PENDING (1 << 6) #define SEL_INT_PENDING (1 << 6)
#define SEL_INT_NUM_MASK 0x3f #define SEL_INT_NUM_MASK 0x3f
#define MMP2_ICU_INT_ROUTE_PJ4_IRQ (1 << 5)
#define MMP2_ICU_INT_ROUTE_PJ4_FIQ (1 << 6)
struct icu_chip_data { struct icu_chip_data {
int nr_irqs; int nr_irqs;
unsigned int virq_base; unsigned int virq_base;
...@@ -190,7 +193,8 @@ static const struct mmp_intc_conf mmp_conf = { ...@@ -190,7 +193,8 @@ static const struct mmp_intc_conf mmp_conf = {
static const struct mmp_intc_conf mmp2_conf = { static const struct mmp_intc_conf mmp2_conf = {
.conf_enable = 0x20, .conf_enable = 0x20,
.conf_disable = 0x0, .conf_disable = 0x0,
.conf_mask = 0x7f, .conf_mask = MMP2_ICU_INT_ROUTE_PJ4_IRQ |
MMP2_ICU_INT_ROUTE_PJ4_FIQ,
}; };
static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs)
......
...@@ -319,7 +319,7 @@ ...@@ -319,7 +319,7 @@
#define GITS_TYPER_PLPIS (1UL << 0) #define GITS_TYPER_PLPIS (1UL << 0)
#define GITS_TYPER_VLPIS (1UL << 1) #define GITS_TYPER_VLPIS (1UL << 1)
#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4
#define GITS_TYPER_ITT_ENTRY_SIZE(r) ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0x1f) + 1) #define GITS_TYPER_ITT_ENTRY_SIZE(r) ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1)
#define GITS_TYPER_IDBITS_SHIFT 8 #define GITS_TYPER_IDBITS_SHIFT 8
#define GITS_TYPER_DEVBITS_SHIFT 13 #define GITS_TYPER_DEVBITS_SHIFT 13
#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) #define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
......
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