mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 07:01:32 +00:00
irqchip update for 5.0-rc6
- 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 -----BEGIN PGP SIGNATURE----- iQJJBAABCgAzFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAlxaspYVHG1hcmMuenlu Z2llckBhcm0uY29tAAoJECPQ0LrRPXpDMS8QAIUNTdbpgtCc/3Lr4n1qHlzGCR1N n4FHYgpR1txcPcVyu92jLxEwnlzSw2zjexuqUNaUYVUKe6wzEAXpDba+dDhvLrW5 JfT1RE7cDMWu34iKAorGg1sqAb3yl67d6Oi88abbNdv3zSa9EVyVBRp3eTWotNzg EAtVorirI41CZNirt3ZU0TRMX0wAc5g7HTq0AwBcQSsg0P6ZCyVqZU5w/JNsPzWH VIpYue8XlDNQ8SDJHRppHhyrghVHG0tpSHKo9I8cA9+4CK6CMd8OwZhdNsf2ISiL vcEOyuOQfIIyTry59F11eiwlMNa4nmsLlmzezOZUeb3NafxsqMmDsbjbOiDMIkzh WUt8BlWOSyy2sWlokX3Qu9CLlWxa+QydGokNdUufLYOAVQG+VDUD5lpaposUSNAe 0thGXenlBO43NqTdldZu2L6E0Wb0M0CTuILOSg6M+6olTR5hsGJiUaV+3WLJHQi3 gLzbAFu5JRzpJWqOPh/9pF5dQQIZ8Ls/C2oJlYt+CTiYA3je4pzWzEsmP2CBTYUy WZEq/mhcze0kFu2b7FnYNhvYkSPool4t5GajwoBYb34N2rnJa7KVYsBDAV0gUJ1t MZbTTzWUeWpsm31uZlvKiPSDHqpUPucZ9HtqBH8OKef6IZz4tMK7v9qx1yZ/C2ZL dSNbXHBEI7X44Ea2 =Crvb -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
8087f40736
@ -97,9 +97,14 @@ struct its_device;
|
||||
* The ITS structure - contains most of the infrastructure, with the
|
||||
* top-level MSI domain, the command queue, the collections, and the
|
||||
* 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 {
|
||||
raw_spinlock_t lock;
|
||||
struct mutex dev_alloc_lock;
|
||||
struct list_head entry;
|
||||
void __iomem *base;
|
||||
phys_addr_t phys_base;
|
||||
@ -156,6 +161,7 @@ struct its_device {
|
||||
void *itt;
|
||||
u32 nr_ites;
|
||||
u32 device_id;
|
||||
bool shared;
|
||||
};
|
||||
|
||||
static struct {
|
||||
@ -1580,6 +1586,9 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids)
|
||||
nr_irqs /= 2;
|
||||
} while (nr_irqs > 0);
|
||||
|
||||
if (!nr_irqs)
|
||||
err = -ENOSPC;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -2059,6 +2068,29 @@ static int __init allocate_lpi_tables(void)
|
||||
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)
|
||||
{
|
||||
void __iomem *rbase = gic_data_rdist_rd_base();
|
||||
@ -2144,6 +2176,30 @@ static void its_cpu_init_lpis(void)
|
||||
val |= GICR_CTLR_ENABLE_LPIS;
|
||||
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 */
|
||||
dsb(sy);
|
||||
out:
|
||||
@ -2422,6 +2478,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
struct its_device *its_dev;
|
||||
struct msi_domain_info *msi_info;
|
||||
u32 dev_id;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&its->dev_alloc_lock);
|
||||
its_dev = its_find_device(its, dev_id);
|
||||
if (its_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
|
||||
* create the device.
|
||||
*/
|
||||
its_dev->shared = true;
|
||||
pr_debug("Reusing ITT for devID %x\n", dev_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
its_dev = its_create_device(its, dev_id, nvec, true);
|
||||
if (!its_dev)
|
||||
return -ENOMEM;
|
||||
if (!its_dev) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
|
||||
out:
|
||||
mutex_unlock(&its->dev_alloc_lock);
|
||||
info->scratchpad[0].ptr = its_dev;
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
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_node *its = its_dev->its;
|
||||
int 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,
|
||||
irq_domain_reset_irq_data(data);
|
||||
}
|
||||
|
||||
/* If all interrupts have been freed, start mopping the floor */
|
||||
if (bitmap_empty(its_dev->event_map.lpi_map,
|
||||
mutex_lock(&its->dev_alloc_lock);
|
||||
|
||||
/*
|
||||
* 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_lpi_free(its_dev->event_map.lpi_map,
|
||||
its_dev->event_map.lpi_base,
|
||||
@ -2593,6 +2662,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
its_free_device(its_dev);
|
||||
}
|
||||
|
||||
mutex_unlock(&its->dev_alloc_lock);
|
||||
|
||||
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
|
||||
}
|
||||
|
||||
@ -2755,26 +2826,11 @@ static void its_vpe_schedule(struct its_vpe *vpe)
|
||||
static void its_vpe_deschedule(struct its_vpe *vpe)
|
||||
{
|
||||
void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
|
||||
u32 count = 1000000; /* 1s! */
|
||||
bool clean;
|
||||
u64 val;
|
||||
|
||||
/* We're being scheduled out */
|
||||
val = gits_read_vpendbaser(vlpi_base + GICR_VPENDBASER);
|
||||
val &= ~GICR_VPENDBASER_Valid;
|
||||
gits_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
|
||||
val = its_clear_vpend_valid(vlpi_base);
|
||||
|
||||
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");
|
||||
vpe->idai = false;
|
||||
vpe->pending_last = true;
|
||||
@ -3517,6 +3573,7 @@ static int __init its_probe_one(struct resource *res,
|
||||
}
|
||||
|
||||
raw_spin_lock_init(&its->lock);
|
||||
mutex_init(&its->dev_alloc_lock);
|
||||
INIT_LIST_HEAD(&its->entry);
|
||||
INIT_LIST_HEAD(&its->its_device_list);
|
||||
typer = gic_read_typer(its_base + GITS_TYPER);
|
||||
|
@ -34,6 +34,9 @@
|
||||
#define SEL_INT_PENDING (1 << 6)
|
||||
#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 {
|
||||
int nr_irqs;
|
||||
unsigned int virq_base;
|
||||
@ -190,7 +193,8 @@ static const struct mmp_intc_conf mmp_conf = {
|
||||
static const struct mmp_intc_conf mmp2_conf = {
|
||||
.conf_enable = 0x20,
|
||||
.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)
|
||||
|
@ -319,7 +319,7 @@
|
||||
#define GITS_TYPER_PLPIS (1UL << 0)
|
||||
#define GITS_TYPER_VLPIS (1UL << 1)
|
||||
#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_DEVBITS_SHIFT 13
|
||||
#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
|
||||
|
Loading…
Reference in New Issue
Block a user