diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2019-02-07 15:12:40 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-02-07 15:12:40 -0500 |
commit | 8087f407364d50d5c3ac8e39365548351e2859f2 (patch) | |
tree | 49826fed88a84e0b36cd517d1953b7292e4b896a | |
parent | 37b144df8099465bf8d08296d8b424c77fd9b0ac (diff) | |
parent | 56841070ccc87b463ac037d2d1f2beb8e5e35f0c (diff) |
Merge tag 'irqchip-5.0-3' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/urgent
Pull irqchip updates from Marc Zyngier:
- Another GICv3 ITS fix for devices sharing the same DevID
- Don't return invalid data on exhaustion of the GICv3 LPI pool
- Fix a GICv3 field decoding bug leading to memory over-allocation
- Init GICv4 at boot time instead of lazy init
- Fix interrupt masking on PJ4
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 101 | ||||
-rw-r--r-- | drivers/irqchip/irq-mmp.c | 6 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic-v3.h | 2 |
3 files changed, 85 insertions, 24 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 7f2a45445b00..c3aba3fc818d 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -97,9 +97,14 @@ struct its_device; | |||
97 | * The ITS structure - contains most of the infrastructure, with the | 97 | * The ITS structure - contains most of the infrastructure, with the |
98 | * top-level MSI domain, the command queue, the collections, and the | 98 | * top-level MSI domain, the command queue, the collections, and the |
99 | * list of devices writing to it. | 99 | * list of devices writing to it. |
100 | * | ||
101 | * dev_alloc_lock has to be taken for device allocations, while the | ||
102 | * spinlock must be taken to parse data structures such as the device | ||
103 | * list. | ||
100 | */ | 104 | */ |
101 | struct its_node { | 105 | struct its_node { |
102 | raw_spinlock_t lock; | 106 | raw_spinlock_t lock; |
107 | struct mutex dev_alloc_lock; | ||
103 | struct list_head entry; | 108 | struct list_head entry; |
104 | void __iomem *base; | 109 | void __iomem *base; |
105 | phys_addr_t phys_base; | 110 | phys_addr_t phys_base; |
@@ -156,6 +161,7 @@ struct its_device { | |||
156 | void *itt; | 161 | void *itt; |
157 | u32 nr_ites; | 162 | u32 nr_ites; |
158 | u32 device_id; | 163 | u32 device_id; |
164 | bool shared; | ||
159 | }; | 165 | }; |
160 | 166 | ||
161 | static struct { | 167 | static struct { |
@@ -1580,6 +1586,9 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids) | |||
1580 | nr_irqs /= 2; | 1586 | nr_irqs /= 2; |
1581 | } while (nr_irqs > 0); | 1587 | } while (nr_irqs > 0); |
1582 | 1588 | ||
1589 | if (!nr_irqs) | ||
1590 | err = -ENOSPC; | ||
1591 | |||
1583 | if (err) | 1592 | if (err) |
1584 | goto out; | 1593 | goto out; |
1585 | 1594 | ||
@@ -2059,6 +2068,29 @@ static int __init allocate_lpi_tables(void) | |||
2059 | return 0; | 2068 | return 0; |
2060 | } | 2069 | } |
2061 | 2070 | ||
2071 | static u64 its_clear_vpend_valid(void __iomem *vlpi_base) | ||
2072 | { | ||
2073 | u32 count = 1000000; /* 1s! */ | ||
2074 | bool clean; | ||
2075 | u64 val; | ||
2076 | |||
2077 | val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); | ||
2078 | val &= ~GICR_VPENDBASER_Valid; | ||
2079 | gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); | ||
2080 | |||
2081 | do { | ||
2082 | val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); | ||
2083 | clean = !(val & GICR_VPENDBASER_Dirty); | ||
2084 | if (!clean) { | ||
2085 | count--; | ||
2086 | cpu_relax(); | ||
2087 | udelay(1); | ||
2088 | } | ||
2089 | } while (!clean && count); | ||
2090 | |||
2091 | return val; | ||
2092 | } | ||
2093 | |||
2062 | static void its_cpu_init_lpis(void) | 2094 | static void its_cpu_init_lpis(void) |
2063 | { | 2095 | { |
2064 | void __iomem *rbase = gic_data_rdist_rd_base(); | 2096 | void __iomem *rbase = gic_data_rdist_rd_base(); |
@@ -2144,6 +2176,30 @@ static void its_cpu_init_lpis(void) | |||
2144 | val |= GICR_CTLR_ENABLE_LPIS; | 2176 | val |= GICR_CTLR_ENABLE_LPIS; |
2145 | writel_relaxed(val, rbase + GICR_CTLR); | 2177 | writel_relaxed(val, rbase + GICR_CTLR); |
2146 | 2178 | ||
2179 | if (gic_rdists->has_vlpis) { | ||
2180 | void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); | ||
2181 | |||
2182 | /* | ||
2183 | * It's possible for CPU to receive VLPIs before it is | ||
2184 | * sheduled as a vPE, especially for the first CPU, and the | ||
2185 | * VLPI with INTID larger than 2^(IDbits+1) will be considered | ||
2186 | * as out of range and dropped by GIC. | ||
2187 | * So we initialize IDbits to known value to avoid VLPI drop. | ||
2188 | */ | ||
2189 | val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK; | ||
2190 | pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n", | ||
2191 | smp_processor_id(), val); | ||
2192 | gits_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); | ||
2193 | |||
2194 | /* | ||
2195 | * Also clear Valid bit of GICR_VPENDBASER, in case some | ||
2196 | * ancient programming gets left in and has possibility of | ||
2197 | * corrupting memory. | ||
2198 | */ | ||
2199 | val = its_clear_vpend_valid(vlpi_base); | ||
2200 | WARN_ON(val & GICR_VPENDBASER_Dirty); | ||
2201 | } | ||
2202 | |||
2147 | /* Make sure the GIC has seen the above */ | 2203 | /* Make sure the GIC has seen the above */ |
2148 | dsb(sy); | 2204 | dsb(sy); |
2149 | out: | 2205 | out: |
@@ -2422,6 +2478,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, | |||
2422 | struct its_device *its_dev; | 2478 | struct its_device *its_dev; |
2423 | struct msi_domain_info *msi_info; | 2479 | struct msi_domain_info *msi_info; |
2424 | u32 dev_id; | 2480 | u32 dev_id; |
2481 | int err = 0; | ||
2425 | 2482 | ||
2426 | /* | 2483 | /* |
2427 | * We ignore "dev" entierely, and rely on the dev_id that has | 2484 | * 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 | return -EINVAL; | 2501 | return -EINVAL; |
2445 | } | 2502 | } |
2446 | 2503 | ||
2504 | mutex_lock(&its->dev_alloc_lock); | ||
2447 | its_dev = its_find_device(its, dev_id); | 2505 | its_dev = its_find_device(its, dev_id); |
2448 | if (its_dev) { | 2506 | if (its_dev) { |
2449 | /* | 2507 | /* |
@@ -2451,18 +2509,22 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, | |||
2451 | * another alias (PCI bridge of some sort). No need to | 2509 | * another alias (PCI bridge of some sort). No need to |
2452 | * create the device. | 2510 | * create the device. |
2453 | */ | 2511 | */ |
2512 | its_dev->shared = true; | ||
2454 | pr_debug("Reusing ITT for devID %x\n", dev_id); | 2513 | pr_debug("Reusing ITT for devID %x\n", dev_id); |
2455 | goto out; | 2514 | goto out; |
2456 | } | 2515 | } |
2457 | 2516 | ||
2458 | its_dev = its_create_device(its, dev_id, nvec, true); | 2517 | its_dev = its_create_device(its, dev_id, nvec, true); |
2459 | if (!its_dev) | 2518 | if (!its_dev) { |
2460 | return -ENOMEM; | 2519 | err = -ENOMEM; |
2520 | goto out; | ||
2521 | } | ||
2461 | 2522 | ||
2462 | pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); | 2523 | pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); |
2463 | out: | 2524 | out: |
2525 | mutex_unlock(&its->dev_alloc_lock); | ||
2464 | info->scratchpad[0].ptr = its_dev; | 2526 | info->scratchpad[0].ptr = its_dev; |
2465 | return 0; | 2527 | return err; |
2466 | } | 2528 | } |
2467 | 2529 | ||
2468 | static struct msi_domain_ops its_msi_domain_ops = { | 2530 | 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 | { | 2628 | { |
2567 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); | 2629 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); |
2568 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | 2630 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); |
2631 | struct its_node *its = its_dev->its; | ||
2569 | int i; | 2632 | int i; |
2570 | 2633 | ||
2571 | for (i = 0; i < nr_irqs; i++) { | 2634 | 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 | irq_domain_reset_irq_data(data); | 2643 | irq_domain_reset_irq_data(data); |
2581 | } | 2644 | } |
2582 | 2645 | ||
2583 | /* If all interrupts have been freed, start mopping the floor */ | 2646 | mutex_lock(&its->dev_alloc_lock); |
2584 | if (bitmap_empty(its_dev->event_map.lpi_map, | 2647 | |
2648 | /* | ||
2649 | * If all interrupts have been freed, start mopping the | ||
2650 | * floor. This is conditionned on the device not being shared. | ||
2651 | */ | ||
2652 | if (!its_dev->shared && | ||
2653 | bitmap_empty(its_dev->event_map.lpi_map, | ||
2585 | its_dev->event_map.nr_lpis)) { | 2654 | its_dev->event_map.nr_lpis)) { |
2586 | its_lpi_free(its_dev->event_map.lpi_map, | 2655 | its_lpi_free(its_dev->event_map.lpi_map, |
2587 | its_dev->event_map.lpi_base, | 2656 | its_dev->event_map.lpi_base, |
@@ -2593,6 +2662,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, | |||
2593 | its_free_device(its_dev); | 2662 | its_free_device(its_dev); |
2594 | } | 2663 | } |
2595 | 2664 | ||
2665 | mutex_unlock(&its->dev_alloc_lock); | ||
2666 | |||
2596 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | 2667 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); |
2597 | } | 2668 | } |
2598 | 2669 | ||
@@ -2755,26 +2826,11 @@ static void its_vpe_schedule(struct its_vpe *vpe) | |||
2755 | static void its_vpe_deschedule(struct its_vpe *vpe) | 2826 | static void its_vpe_deschedule(struct its_vpe *vpe) |
2756 | { | 2827 | { |
2757 | void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); | 2828 | void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); |
2758 | u32 count = 1000000; /* 1s! */ | ||
2759 | bool clean; | ||
2760 | u64 val; | 2829 | u64 val; |
2761 | 2830 | ||
2762 | /* We're being scheduled out */ | 2831 | val = its_clear_vpend_valid(vlpi_base); |
2763 | val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); | ||
2764 | val &= ~GICR_VPENDBASER_Valid; | ||
2765 | gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); | ||
2766 | |||
2767 | do { | ||
2768 | val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER); | ||
2769 | clean = !(val & GICR_VPENDBASER_Dirty); | ||
2770 | if (!clean) { | ||
2771 | count--; | ||
2772 | cpu_relax(); | ||
2773 | udelay(1); | ||
2774 | } | ||
2775 | } while (!clean && count); | ||
2776 | 2832 | ||
2777 | if (unlikely(!clean && !count)) { | 2833 | if (unlikely(val & GICR_VPENDBASER_Dirty)) { |
2778 | pr_err_ratelimited("ITS virtual pending table not cleaning\n"); | 2834 | pr_err_ratelimited("ITS virtual pending table not cleaning\n"); |
2779 | vpe->idai = false; | 2835 | vpe->idai = false; |
2780 | vpe->pending_last = true; | 2836 | vpe->pending_last = true; |
@@ -3517,6 +3573,7 @@ static int __init its_probe_one(struct resource *res, | |||
3517 | } | 3573 | } |
3518 | 3574 | ||
3519 | raw_spin_lock_init(&its->lock); | 3575 | raw_spin_lock_init(&its->lock); |
3576 | mutex_init(&its->dev_alloc_lock); | ||
3520 | INIT_LIST_HEAD(&its->entry); | 3577 | INIT_LIST_HEAD(&its->entry); |
3521 | INIT_LIST_HEAD(&its->its_device_list); | 3578 | INIT_LIST_HEAD(&its->its_device_list); |
3522 | typer = gic_read_typer(its_base + GITS_TYPER); | 3579 | typer = gic_read_typer(its_base + GITS_TYPER); |
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 25f32e1d7764..3496b61a312a 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c | |||
@@ -34,6 +34,9 @@ | |||
34 | #define SEL_INT_PENDING (1 << 6) | 34 | #define SEL_INT_PENDING (1 << 6) |
35 | #define SEL_INT_NUM_MASK 0x3f | 35 | #define SEL_INT_NUM_MASK 0x3f |
36 | 36 | ||
37 | #define MMP2_ICU_INT_ROUTE_PJ4_IRQ (1 << 5) | ||
38 | #define MMP2_ICU_INT_ROUTE_PJ4_FIQ (1 << 6) | ||
39 | |||
37 | struct icu_chip_data { | 40 | struct icu_chip_data { |
38 | int nr_irqs; | 41 | int nr_irqs; |
39 | unsigned int virq_base; | 42 | unsigned int virq_base; |
@@ -190,7 +193,8 @@ static const struct mmp_intc_conf mmp_conf = { | |||
190 | static const struct mmp_intc_conf mmp2_conf = { | 193 | static const struct mmp_intc_conf mmp2_conf = { |
191 | .conf_enable = 0x20, | 194 | .conf_enable = 0x20, |
192 | .conf_disable = 0x0, | 195 | .conf_disable = 0x0, |
193 | .conf_mask = 0x7f, | 196 | .conf_mask = MMP2_ICU_INT_ROUTE_PJ4_IRQ | |
197 | MMP2_ICU_INT_ROUTE_PJ4_FIQ, | ||
194 | }; | 198 | }; |
195 | 199 | ||
196 | static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) | 200 | static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) |
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 071b4cbdf010..c848a7cc502e 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h | |||
@@ -319,7 +319,7 @@ | |||
319 | #define GITS_TYPER_PLPIS (1UL << 0) | 319 | #define GITS_TYPER_PLPIS (1UL << 0) |
320 | #define GITS_TYPER_VLPIS (1UL << 1) | 320 | #define GITS_TYPER_VLPIS (1UL << 1) |
321 | #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 | 321 | #define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 |
322 | #define GITS_TYPER_ITT_ENTRY_SIZE(r) ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0x1f) + 1) | 322 | #define GITS_TYPER_ITT_ENTRY_SIZE(r) ((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1) |
323 | #define GITS_TYPER_IDBITS_SHIFT 8 | 323 | #define GITS_TYPER_IDBITS_SHIFT 8 |
324 | #define GITS_TYPER_DEVBITS_SHIFT 13 | 324 | #define GITS_TYPER_DEVBITS_SHIFT 13 |
325 | #define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) | 325 | #define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) |