From 2c542426128a4e3d247939f868d19279ad48cb1f Mon Sep 17 00:00:00 2001 From: Daode Huang Date: Thu, 17 Oct 2019 16:25:29 +0800 Subject: irqchip: Remove redundant semicolon after while check drivers/irqchip with "make coccicheck M=drivers/irqchip/", it will report unneeded semicolon like below, just remove them. drivers/irqchip/irq-zevio.c:54:2-3: Unneeded semicolon drivers/irqchip/irq-gic-v3.c:177:2-3: Unneeded semicolon drivers/irqchip/irq-gic-v3.c:234:2-3: Unneeded semicolon Signed-off-by: Daode Huang Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1571300729-38822-1-git-send-email-huangdaode@hisilicon.com --- drivers/irqchip/irq-gic-v3.c | 4 ++-- drivers/irqchip/irq-zevio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 1edc99335a94..9e3515557b04 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -174,7 +174,7 @@ static void gic_do_wait_for_rwp(void __iomem *base) } cpu_relax(); udelay(1); - }; + } } /* Wait for completion of a distributor change */ @@ -231,7 +231,7 @@ static void gic_enable_redist(bool enable) break; cpu_relax(); udelay(1); - }; + } if (!count) pr_err_ratelimited("redistributor failed to %s...\n", enable ? "wakeup" : "sleep"); diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index 5a7efeb3892d..84163f1ebfcf 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c @@ -51,7 +51,7 @@ static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs) while (readl(zevio_irq_io + IO_STATUS)) { irqnr = readl(zevio_irq_io + IO_CURRENT); handle_domain_irq(zevio_irq_domain, irqnr, regs); - }; + } } static void __init zevio_init_irq_base(void __iomem *base) -- cgit v1.2.3 From 2bbdfcc54ba857ce5da9a5741379dd03ba94c947 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Thu, 17 Oct 2019 12:29:55 +0100 Subject: irqchip/gic-v3-its: Fix u64 to __le64 warnings The its_cmd_block struct can either have u64 or __le64 data in it, so make a anonymous union to remove the sparse warnings when converting to/from these. Signed-off-by: Ben Dooks Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191017112955.15853-1-ben.dooks@codethink.co.uk --- drivers/irqchip/irq-gic-v3-its.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 787e8eec9a7f..021e0c70e87c 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -305,7 +305,10 @@ struct its_cmd_desc { * The ITS command block, which is what the ITS actually parses. */ struct its_cmd_block { - u64 raw_cmd[4]; + union { + u64 raw_cmd[4]; + __le64 raw_cmd_le[4]; + }; }; #define ITS_CMD_QUEUE_SZ SZ_64K @@ -414,10 +417,10 @@ static void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size) static inline void its_fixup_cmd(struct its_cmd_block *cmd) { /* Let's fixup BE commands */ - cmd->raw_cmd[0] = cpu_to_le64(cmd->raw_cmd[0]); - cmd->raw_cmd[1] = cpu_to_le64(cmd->raw_cmd[1]); - cmd->raw_cmd[2] = cpu_to_le64(cmd->raw_cmd[2]); - cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]); + cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]); + cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]); + cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]); + cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]); } static struct its_collection *its_build_mapd_cmd(struct its_node *its, -- cgit v1.2.3 From 6468fc18b00685c82408f40e9569c0d3527862b8 Mon Sep 17 00:00:00 2001 From: Justin Chen Date: Thu, 24 Oct 2019 13:14:11 -0700 Subject: irqchip/irq-bcm7038-l1: Add PM support The current L1 controller does not mask any interrupts when dropping into suspend. This mean we can receive unexpected wake up sources. Modified the BCM7038 L1 controller to mask the all non-wake interrupts before dropping into suspend. Signed-off-by: Justin Chen Signed-off-by: Florian Fainelli Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191024201415.23454-2-f.fainelli@gmail.com --- drivers/irqchip/irq-bcm7038-l1.c | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index fc75c61233aa..689e487be80c 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -27,6 +27,7 @@ #include #include #include +#include #define IRQS_PER_WORD 32 #define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4) @@ -39,6 +40,10 @@ struct bcm7038_l1_chip { unsigned int n_words; struct irq_domain *domain; struct bcm7038_l1_cpu *cpus[NR_CPUS]; +#ifdef CONFIG_PM_SLEEP + struct list_head list; + u32 wake_mask[MAX_WORDS]; +#endif u8 affinity[MAX_WORDS * IRQS_PER_WORD]; }; @@ -287,6 +292,77 @@ static int __init bcm7038_l1_init_one(struct device_node *dn, return 0; } +#ifdef CONFIG_PM_SLEEP +/* + * We keep a list of bcm7038_l1_chip used for suspend/resume. This hack is + * used because the struct chip_type suspend/resume hooks are not called + * unless chip_type is hooked onto a generic_chip. Since this driver does + * not use generic_chip, we need to manually hook our resume/suspend to + * syscore_ops. + */ +static LIST_HEAD(bcm7038_l1_intcs_list); +static DEFINE_RAW_SPINLOCK(bcm7038_l1_intcs_lock); + +static int bcm7038_l1_suspend(void) +{ + struct bcm7038_l1_chip *intc; + int boot_cpu, word; + + /* Wakeup interrupt should only come from the boot cpu */ + boot_cpu = cpu_logical_map(0); + + list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) { + for (word = 0; word < intc->n_words; word++) { + l1_writel(~intc->wake_mask[word], + intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word)); + l1_writel(intc->wake_mask[word], + intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word)); + } + } + + return 0; +} + +static void bcm7038_l1_resume(void) +{ + struct bcm7038_l1_chip *intc; + int boot_cpu, word; + + boot_cpu = cpu_logical_map(0); + + list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) { + for (word = 0; word < intc->n_words; word++) { + l1_writel(intc->cpus[boot_cpu]->mask_cache[word], + intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word)); + l1_writel(~intc->cpus[boot_cpu]->mask_cache[word], + intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word)); + } + } +} + +static struct syscore_ops bcm7038_l1_syscore_ops = { + .suspend = bcm7038_l1_suspend, + .resume = bcm7038_l1_resume, +}; + +static int bcm7038_l1_set_wake(struct irq_data *d, unsigned int on) +{ + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); + unsigned long flags; + u32 word = d->hwirq / IRQS_PER_WORD; + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); + + raw_spin_lock_irqsave(&intc->lock, flags); + if (on) + intc->wake_mask[word] |= mask; + else + intc->wake_mask[word] &= ~mask; + raw_spin_unlock_irqrestore(&intc->lock, flags); + + return 0; +} +#endif + static struct irq_chip bcm7038_l1_irq_chip = { .name = "bcm7038-l1", .irq_mask = bcm7038_l1_mask, @@ -295,6 +371,9 @@ static struct irq_chip bcm7038_l1_irq_chip = { #ifdef CONFIG_SMP .irq_cpu_offline = bcm7038_l1_cpu_offline, #endif +#ifdef CONFIG_PM_SLEEP + .irq_set_wake = bcm7038_l1_set_wake, +#endif }; static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, @@ -340,6 +419,16 @@ int __init bcm7038_l1_of_init(struct device_node *dn, goto out_unmap; } +#ifdef CONFIG_PM_SLEEP + /* Add bcm7038_l1_chip into a list */ + raw_spin_lock(&bcm7038_l1_intcs_lock); + list_add_tail(&intc->list, &bcm7038_l1_intcs_list); + raw_spin_unlock(&bcm7038_l1_intcs_lock); + + if (list_is_singular(&bcm7038_l1_intcs_list)) + register_syscore_ops(&bcm7038_l1_syscore_ops); +#endif + pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n", dn, IRQS_PER_WORD * intc->n_words); -- cgit v1.2.3 From 27eebb60357ed5aa6659442f92907c0f7368d6ae Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 24 Oct 2019 13:14:13 -0700 Subject: irqchip/irq-bcm7038-l1: Enable parent IRQ if necessary If the 'brcm,irq-can-wake' property is specified, make sure we also enable the corresponding parent interrupt we are attached to. Signed-off-by: Florian Fainelli Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191024201415.23454-4-f.fainelli@gmail.com --- drivers/irqchip/irq-bcm7038-l1.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 689e487be80c..45879e59e58b 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -286,6 +286,10 @@ static int __init bcm7038_l1_init_one(struct device_node *dn, pr_err("failed to map parent interrupt %d\n", parent_irq); return -EINVAL; } + + if (of_property_read_bool(dn, "brcm,irq-can-wake")) + enable_irq_wake(parent_irq); + irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle, intc); -- cgit v1.2.3 From 96de80c14bc6b43b797299270e31b8924f89fa52 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 24 Oct 2019 13:14:15 -0700 Subject: irqchip/irq-bcm7038-l1: Support brcm,int-fwd-mask On some specific chips like 7211 we need to leave some interrupts untouched/forwarded to the VPU which is another agent in the system making use of that interrupt controller hardware (goes to both ARM GIC and VPU L1 interrupt controller). Make that possible by using the existing brcm,int-fwd-mask property and take necessary actions to avoid masking that interrupt as well as not allowing Linux to map them. Signed-off-by: Florian Fainelli Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191024201415.23454-6-f.fainelli@gmail.com --- drivers/irqchip/irq-bcm7038-l1.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 45879e59e58b..cbf01afcd2a6 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -44,6 +44,7 @@ struct bcm7038_l1_chip { struct list_head list; u32 wake_mask[MAX_WORDS]; #endif + u32 irq_fwd_mask[MAX_WORDS]; u8 affinity[MAX_WORDS * IRQS_PER_WORD]; }; @@ -254,6 +255,7 @@ static int __init bcm7038_l1_init_one(struct device_node *dn, resource_size_t sz; struct bcm7038_l1_cpu *cpu; unsigned int i, n_words, parent_irq; + int ret; if (of_address_to_resource(dn, idx, &res)) return -EINVAL; @@ -267,6 +269,14 @@ static int __init bcm7038_l1_init_one(struct device_node *dn, else if (intc->n_words != n_words) return -EINVAL; + ret = of_property_read_u32_array(dn , "brcm,int-fwd-mask", + intc->irq_fwd_mask, n_words); + if (ret != 0 && ret != -EINVAL) { + /* property exists but has the wrong number of words */ + pr_err("invalid brcm,int-fwd-mask property\n"); + return -EINVAL; + } + cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32), GFP_KERNEL); if (!cpu) @@ -277,8 +287,11 @@ static int __init bcm7038_l1_init_one(struct device_node *dn, return -ENOMEM; for (i = 0; i < n_words; i++) { - l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i)); - cpu->mask_cache[i] = 0xffffffff; + l1_writel(~intc->irq_fwd_mask[i], + cpu->map_base + reg_mask_set(intc, i)); + l1_writel(intc->irq_fwd_mask[i], + cpu->map_base + reg_mask_clr(intc, i)); + cpu->mask_cache[i] = ~intc->irq_fwd_mask[i]; } parent_irq = irq_of_parse_and_map(dn, idx); @@ -311,15 +324,17 @@ static int bcm7038_l1_suspend(void) { struct bcm7038_l1_chip *intc; int boot_cpu, word; + u32 val; /* Wakeup interrupt should only come from the boot cpu */ boot_cpu = cpu_logical_map(0); list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) { for (word = 0; word < intc->n_words; word++) { - l1_writel(~intc->wake_mask[word], + val = intc->wake_mask[word] | intc->irq_fwd_mask[word]; + l1_writel(~val, intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word)); - l1_writel(intc->wake_mask[word], + l1_writel(val, intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word)); } } @@ -383,6 +398,13 @@ static struct irq_chip bcm7038_l1_irq_chip = { static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw_irq) { + struct bcm7038_l1_chip *intc = d->host_data; + u32 mask = BIT(hw_irq % IRQS_PER_WORD); + u32 word = hw_irq / IRQS_PER_WORD; + + if (intc->irq_fwd_mask[word] & mask) + return -EPERM; + irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq); irq_set_chip_data(virq, d->host_data); irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq))); -- cgit v1.2.3 From 0dcd9f872769547f336741880bc7e721149c8d0a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 7 Nov 2019 13:21:15 +0100 Subject: irqchip: Add support for Layerscape external interrupt lines The LS1021A allows inverting the polarity of six interrupt lines IRQ[0:5] via the scfg_intpcr register, effectively allowing IRQ_TYPE_LEVEL_LOW and IRQ_TYPE_EDGE_FALLING for those. We just need to check the type, set the relevant bit in INTPCR accordingly, and fixup the type argument before calling the GIC's irq_set_type. In fact, the power-on-reset value of the INTPCR register on the LS1021A is so that all six lines have their polarity inverted. Hence any hardware connected to those lines is unusable without this: If the line is indeed active low, the generic GIC code will reject an irq spec with IRQ_TYPE_LEVEL_LOW, while if the line is active high, we must obviously disable the polarity inversion (writing 0 to the relevant bit) before unmasking the interrupt. Some other Layerscape SOCs (LS1043A, LS1046A) have a similar feature, just with a different number of external interrupt lines (and a different POR value for the INTPCR register). This driver should be prepared for supporting those by properly filling out the device tree node. I have the reference manuals for all three boards, but I've only tested the driver on an LS1021A. Unfortunately, the Kconfig symbol ARCH_LAYERSCAPE only exists on arm64, so do as is done for irq-ls-scfg-msi.c: introduce a new symbol which is set when either ARCH_LAYERSCAPE or SOC_LS1021A is set. Signed-off-by: Rasmus Villemoes Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191107122115.6244-3-linux@rasmusvillemoes.dk --- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ls-extirq.c | 197 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 drivers/irqchip/irq-ls-extirq.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index ccbb8973a324..bbb323462912 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -370,6 +370,10 @@ config MVEBU_PIC config MVEBU_SEI bool +config LS_EXTIRQ + def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE + select MFD_SYSCON + config LS_SCFG_MSI def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE depends on PCI && PCI_MSI diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index cc7c43932f16..e806dda690ea 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_MVEBU_ICU) += irq-mvebu-icu.o obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o obj-$(CONFIG_MVEBU_PIC) += irq-mvebu-pic.o obj-$(CONFIG_MVEBU_SEI) += irq-mvebu-sei.o +obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o diff --git a/drivers/irqchip/irq-ls-extirq.c b/drivers/irqchip/irq-ls-extirq.c new file mode 100644 index 000000000000..4d1179fed77c --- /dev/null +++ b/drivers/irqchip/irq-ls-extirq.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) "irq-ls-extirq: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAXIRQ 12 +#define LS1021A_SCFGREVCR 0x200 + +struct ls_extirq_data { + struct regmap *syscon; + u32 intpcr; + bool bit_reverse; + u32 nirq; + struct irq_fwspec map[MAXIRQ]; +}; + +static int +ls_extirq_set_type(struct irq_data *data, unsigned int type) +{ + struct ls_extirq_data *priv = data->chip_data; + irq_hw_number_t hwirq = data->hwirq; + u32 value, mask; + + if (priv->bit_reverse) + mask = 1U << (31 - hwirq); + else + mask = 1U << hwirq; + + switch (type) { + case IRQ_TYPE_LEVEL_LOW: + type = IRQ_TYPE_LEVEL_HIGH; + value = mask; + break; + case IRQ_TYPE_EDGE_FALLING: + type = IRQ_TYPE_EDGE_RISING; + value = mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_EDGE_RISING: + value = 0; + break; + default: + return -EINVAL; + } + regmap_update_bits(priv->syscon, priv->intpcr, mask, value); + + return irq_chip_set_type_parent(data, type); +} + +static struct irq_chip ls_extirq_chip = { + .name = "ls-extirq", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = ls_extirq_set_type, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_SET_TYPE_MASKED, +}; + +static int +ls_extirq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct ls_extirq_data *priv = domain->host_data; + struct irq_fwspec *fwspec = arg; + irq_hw_number_t hwirq; + + if (fwspec->param_count != 2) + return -EINVAL; + + hwirq = fwspec->param[0]; + if (hwirq >= priv->nirq) + return -EINVAL; + + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &ls_extirq_chip, + priv); + + return irq_domain_alloc_irqs_parent(domain, virq, 1, &priv->map[hwirq]); +} + +static const struct irq_domain_ops extirq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .alloc = ls_extirq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int +ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node) +{ + const __be32 *map; + u32 mapsize; + int ret; + + map = of_get_property(node, "interrupt-map", &mapsize); + if (!map) + return -ENOENT; + if (mapsize % sizeof(*map)) + return -EINVAL; + mapsize /= sizeof(*map); + + while (mapsize) { + struct device_node *ipar; + u32 hwirq, intsize, j; + + if (mapsize < 3) + return -EINVAL; + hwirq = be32_to_cpup(map); + if (hwirq >= MAXIRQ) + return -EINVAL; + priv->nirq = max(priv->nirq, hwirq + 1); + + ipar = of_find_node_by_phandle(be32_to_cpup(map + 2)); + map += 3; + mapsize -= 3; + if (!ipar) + return -EINVAL; + priv->map[hwirq].fwnode = &ipar->fwnode; + ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize); + if (ret) + return ret; + + if (intsize > mapsize) + return -EINVAL; + + priv->map[hwirq].param_count = intsize; + for (j = 0; j < intsize; ++j) + priv->map[hwirq].param[j] = be32_to_cpup(map++); + mapsize -= intsize; + } + return 0; +} + +static int __init +ls_extirq_of_init(struct device_node *node, struct device_node *parent) +{ + + struct irq_domain *domain, *parent_domain; + struct ls_extirq_data *priv; + int ret; + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("Cannot find parent domain\n"); + return -ENODEV; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->syscon = syscon_node_to_regmap(node->parent); + if (IS_ERR(priv->syscon)) { + ret = PTR_ERR(priv->syscon); + pr_err("Failed to lookup parent regmap\n"); + goto out; + } + ret = of_property_read_u32(node, "reg", &priv->intpcr); + if (ret) { + pr_err("Missing INTPCR offset value\n"); + goto out; + } + + ret = ls_extirq_parse_map(priv, node); + if (ret) + goto out; + + if (of_device_is_compatible(node, "fsl,ls1021a-extirq")) { + u32 revcr; + + ret = regmap_read(priv->syscon, LS1021A_SCFGREVCR, &revcr); + if (ret) + goto out; + priv->bit_reverse = (revcr != 0); + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node, + &extirq_domain_ops, priv); + if (!domain) + ret = -ENOMEM; + +out: + if (ret) + kfree(priv); + return ret; +} + +IRQCHIP_DECLARE(ls1021a_extirq, "fsl,ls1021a-extirq", ls_extirq_of_init); -- cgit v1.2.3 From 8e4d5a5bde8896c7fa36b173c613dbbbf9d5dc32 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 8 Nov 2019 14:58:17 +0530 Subject: drivers: irqchip: qcom-pdc: Move to an SoC independent compatible Remove the sdm845 SoC specific compatible to make the driver easily reusable across other SoC's with the same IP block. This will reduce further churn adding any SoC specific compatibles unless really needed. Signed-off-by: Rajendra Nayak Signed-off-by: Marc Zyngier Reviewed-by: Lina Iyer Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20191108092824.9773-7-rnayak@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index faa7d61b9d6c..c175333bb646 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -309,4 +309,4 @@ fail: return ret; } -IRQCHIP_DECLARE(pdc_sdm845, "qcom,sdm845-pdc", qcom_pdc_init); +IRQCHIP_DECLARE(qcom_pdc, "qcom,pdc", qcom_pdc_init); -- cgit v1.2.3 From 898aa5ce6158c5ccfc256bfc17963bc81981eef8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:57:55 +0000 Subject: irqchip/gic-v3-its: Free collection mapping on device teardown We allocate the collection mapping on device creation, but somehow free it on the irqdomain free path, which is pretty inconsistent and has led to bugs in the past. Move it to the point where we teardown the device, making the alloc/free symetric. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191108165805.3071-2-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 021e0c70e87c..d5d8f8fc0973 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2474,6 +2474,7 @@ static void its_free_device(struct its_device *its_dev) raw_spin_lock_irqsave(&its_dev->its->lock, flags); list_del(&its_dev->entry); raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); + kfree(its_dev->event_map.col_map); kfree(its_dev->itt); kfree(its_dev); } @@ -2682,7 +2683,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, its_lpi_free(its_dev->event_map.lpi_map, its_dev->event_map.lpi_base, its_dev->event_map.nr_lpis); - kfree(its_dev->event_map.col_map); /* Unmap device/itt */ its_send_mapd(its_dev, 0); -- cgit v1.2.3 From 2f4f064b31315c7c8986522cf38ef6d11fb77986 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:57:56 +0000 Subject: irqchip/gic-v3-its: Factor out wait_for_syncr primitive Waiting for a redistributor to have performed an operation is a common thing to do, and the idiom is already spread around. As we're going to make even more use of this, let's have a primitive that does just that. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-3-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-3-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index d5d8f8fc0973..78d3e73fc9c7 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1093,6 +1093,12 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) dsb(ishst); } +static void wait_for_syncr(void __iomem *rdbase) +{ + while (gic_read_lpir(rdbase + GICR_SYNCR) & 1) + cpu_relax(); +} + static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); @@ -2775,8 +2781,7 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to) rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base; gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR); - while (gic_read_lpir(rdbase + GICR_SYNCR) & 1) - cpu_relax(); + wait_for_syncr(rdbase); return; } @@ -2932,8 +2937,7 @@ static void its_vpe_send_inv(struct irq_data *d) rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base; gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR); - while (gic_read_lpir(rdbase + GICR_SYNCR) & 1) - cpu_relax(); + wait_for_syncr(rdbase); } else { its_vpe_send_cmd(vpe, its_send_inv); } @@ -2975,8 +2979,7 @@ static int its_vpe_set_irqchip_state(struct irq_data *d, gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR); } else { gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR); - while (gic_read_lpir(rdbase + GICR_SYNCR) & 1) - cpu_relax(); + wait_for_syncr(rdbase); } } else { if (state) -- cgit v1.2.3 From 425c09be0f091a3b5940261d9a5d8467d62987e7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:57:57 +0000 Subject: irqchip/gic-v3-its: Allow LPI invalidation via the DirectLPI interface We currently don't make much use of the DirectLPI feature, and it would be beneficial to do this more, if only because it becomes a mandatory feature for GICv4.1. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-4-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-4-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 78d3e73fc9c7..6065b09d6c43 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -191,6 +191,12 @@ static u16 get_its_list(struct its_vm *vm) return (u16)its_list; } +static inline u32 its_get_event_id(struct irq_data *d) +{ + struct its_device *its_dev = irq_data_get_irq_chip_data(d); + return d->hwirq - its_dev->event_map.lpi_base; +} + static struct its_collection *dev_event_to_col(struct its_device *its_dev, u32 event) { @@ -199,6 +205,13 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev, return its->collections + its_dev->event_map.col_map[event]; } +static struct its_collection *irq_to_col(struct irq_data *d) +{ + struct its_device *its_dev = irq_data_get_irq_chip_data(d); + + return dev_event_to_col(its_dev, its_get_event_id(d)); +} + static struct its_collection *valid_col(struct its_collection *col) { if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0))) @@ -1049,12 +1062,6 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) * irqchip functions - assumes MSI, mostly. */ -static inline u32 its_get_event_id(struct irq_data *d) -{ - struct its_device *its_dev = irq_data_get_irq_chip_data(d); - return d->hwirq - its_dev->event_map.lpi_base; -} - static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) { irq_hw_number_t hwirq; @@ -1099,12 +1106,28 @@ static void wait_for_syncr(void __iomem *rdbase) cpu_relax(); } +static void direct_lpi_inv(struct irq_data *d) +{ + struct its_collection *col; + void __iomem *rdbase; + + /* Target the redistributor this LPI is currently routed to */ + col = irq_to_col(d); + rdbase = per_cpu_ptr(gic_rdists->rdist, col->col_id)->rd_base; + gic_write_lpir(d->hwirq, rdbase + GICR_INVLPIR); + + wait_for_syncr(rdbase); +} + static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); lpi_write_config(d, clr, set); - its_send_inv(its_dev, its_get_event_id(d)); + if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d)) + direct_lpi_inv(d); + else + its_send_inv(its_dev, its_get_event_id(d)); } static void its_vlpi_set_doorbell(struct irq_data *d, bool enable) @@ -2935,8 +2958,9 @@ static void its_vpe_send_inv(struct irq_data *d) if (gic_rdists->has_direct_lpi) { void __iomem *rdbase; + /* Target the redistributor this VPE is currently known on */ rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base; - gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR); + gic_write_lpir(d->parent_data->hwirq, rdbase + GICR_INVLPIR); wait_for_syncr(rdbase); } else { its_vpe_send_cmd(vpe, its_send_inv); -- cgit v1.2.3 From 0dd57fed6b46659b2db1156cb9100fbcfef6fe5d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:57:58 +0000 Subject: irqchip/gic-v3-its: Make is_v4 use a TYPER copy Instead of caching the GICv4 compatibility in a discrete way, cache the TYPER register instead, which can then be used to implement the same functionnality. This will get used more extensively in subsequent patches. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-5-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-5-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6065b09d6c43..5bb3eacbbde4 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -102,6 +102,7 @@ struct its_node { struct its_collection *collections; struct fwnode_handle *fwnode_handle; u64 (*get_msi_base)(struct its_device *its_dev); + u64 typer; u64 cbaser_save; u32 ctlr_save; struct list_head its_device_list; @@ -112,10 +113,11 @@ struct its_node { int numa_node; unsigned int msi_domain_flags; u32 pre_its_base; /* for Socionext Synquacer */ - bool is_v4; int vlpi_redist_offset; }; +#define is_v4(its) (!!((its)->typer & GITS_TYPER_VLPIS)) + #define ITS_ITT_ALIGN SZ_256 /* The maximum number of VPEID bits supported by VLPI commands */ @@ -181,7 +183,7 @@ static u16 get_its_list(struct its_vm *vm) unsigned long its_list = 0; list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) + if (!is_v4(its)) continue; if (vm->vlpi_count[its->list_nr]) @@ -1037,7 +1039,7 @@ static void its_send_vmovp(struct its_vpe *vpe) /* Emit VMOVPs */ list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) + if (!is_v4(its)) continue; if (!vpe->its_vm->vlpi_count[its->list_nr]) @@ -1448,7 +1450,7 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) struct its_cmd_info *info = vcpu_info; /* Need a v4 ITS */ - if (!its_dev->its->is_v4) + if (!is_v4(its_dev->its)) return -EINVAL; /* Unmap request? */ @@ -2412,7 +2414,7 @@ static bool its_alloc_vpe_table(u32 vpe_id) list_for_each_entry(its, &its_nodes, entry) { struct its_baser *baser; - if (!its->is_v4) + if (!is_v4(its)) continue; baser = its_get_baser(its, GITS_BASER_TYPE_VCPU); @@ -2900,7 +2902,7 @@ static void its_vpe_invall(struct its_vpe *vpe) struct its_node *its; list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) + if (!is_v4(its)) continue; if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr]) @@ -3168,7 +3170,7 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, vpe->col_idx = cpumask_first(cpu_online_mask); list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) + if (!is_v4(its)) continue; its_send_vmapp(its, vpe, true); @@ -3194,7 +3196,7 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain, return; list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) + if (!is_v4(its)) continue; its_send_vmapp(its, vpe, false); @@ -3632,12 +3634,12 @@ static int __init its_probe_one(struct resource *res, INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->its_device_list); typer = gic_read_typer(its_base + GITS_TYPER); + its->typer = typer; its->base = its_base; its->phys_base = res->start; its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer); its->device_ids = GITS_TYPER_DEVBITS(typer); - its->is_v4 = !!(typer & GITS_TYPER_VLPIS); - if (its->is_v4) { + if (is_v4(its)) { if (!(typer & GITS_TYPER_VMOVP)) { err = its_compute_its_list_map(res, its_base); if (err < 0) @@ -3704,7 +3706,7 @@ static int __init its_probe_one(struct resource *res, gits_write_cwriter(0, its->base + GITS_CWRITER); ctlr = readl_relaxed(its->base + GITS_CTLR); ctlr |= GITS_CTLR_ENABLE; - if (its->is_v4) + if (is_v4(its)) ctlr |= GITS_CTLR_ImDe; writel_relaxed(ctlr, its->base + GITS_CTLR); @@ -4029,7 +4031,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists, return err; list_for_each_entry(its, &its_nodes, entry) - has_v4 |= its->is_v4; + has_v4 |= is_v4(its); if (has_v4 & rdists->has_vlpis) { if (its_init_vpe_domain() || -- cgit v1.2.3 From ffedbf0cba153c91a0da5d1280a5e639664c5ab3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:57:59 +0000 Subject: irqchip/gic-v3-its: Kill its->ite_size and use TYPER copy instead Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->ite_size, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-6-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-6-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 5bb3eacbbde4..c3a1e47e7836 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -108,7 +109,6 @@ struct its_node { struct list_head its_device_list; u64 flags; unsigned long list_nr; - u32 ite_size; u32 device_ids; int numa_node; unsigned int msi_domain_flags; @@ -2453,7 +2453,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, * sized as a power of two (and you need at least one bit...). */ nr_ites = max(2, nvecs); - sz = nr_ites * its->ite_size; + sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1); sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node); if (alloc_lpis) { @@ -3268,7 +3268,8 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) struct its_node *its = data; /* On QDF2400, the size of the ITE is 16Bytes */ - its->ite_size = 16; + its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE; + its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1); return true; } @@ -3637,7 +3638,6 @@ static int __init its_probe_one(struct resource *res, its->typer = typer; its->base = its_base; its->phys_base = res->start; - its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer); its->device_ids = GITS_TYPER_DEVBITS(typer); if (is_v4(its)) { if (!(typer & GITS_TYPER_VMOVP)) { -- cgit v1.2.3 From 576a83429757999f220f36f206044af2b9026672 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:00 +0000 Subject: irqchip/gic-v3-its: Kill its->device_ids and use TYPER copy instead Now that we have a copy of TYPER in the ITS structure, rely on this to provide the same service as its->device_ids, which gets axed. Errata workarounds are now updating the cached fields instead of requiring a separate field in the ITS structure. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-7-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-7-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c3a1e47e7836..e8aeb07e1cb0 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -109,7 +109,6 @@ struct its_node { struct list_head its_device_list; u64 flags; unsigned long list_nr; - u32 device_ids; int numa_node; unsigned int msi_domain_flags; u32 pre_its_base; /* for Socionext Synquacer */ @@ -117,6 +116,7 @@ struct its_node { }; #define is_v4(its) (!!((its)->typer & GITS_TYPER_VLPIS)) +#define device_ids(its) (FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1) #define ITS_ITT_ALIGN SZ_256 @@ -1956,9 +1956,9 @@ static bool its_parse_indirect_baser(struct its_node *its, if (new_order >= MAX_ORDER) { new_order = MAX_ORDER - 1; ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz); - pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n", + pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n", &its->phys_base, its_base_type_string[type], - its->device_ids, ids); + device_ids(its), ids); } *order = new_order; @@ -2004,7 +2004,7 @@ static int its_alloc_tables(struct its_node *its) case GITS_BASER_TYPE_DEVICE: indirect = its_parse_indirect_baser(its, baser, psz, &order, - its->device_ids); + device_ids(its)); break; case GITS_BASER_TYPE_VCPU: @@ -2395,7 +2395,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id) /* Don't allow device id that exceeds ITS hardware limit */ if (!baser) - return (ilog2(dev_id) < its->device_ids); + return (ilog2(dev_id) < device_ids(its)); return its_alloc_table_entry(its, baser, dev_id); } @@ -3247,8 +3247,9 @@ static bool __maybe_unused its_enable_quirk_cavium_22375(void *data) { struct its_node *its = data; - /* erratum 22375: only alloc 8MB table size */ - its->device_ids = 0x14; /* 20 bits, 8MB */ + /* erratum 22375: only alloc 8MB table size (20 bits) */ + its->typer &= ~GITS_TYPER_DEVBITS; + its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1); its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; return true; @@ -3303,8 +3304,10 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data) its->get_msi_base = its_irq_get_msi_base_pre_its; ids = ilog2(pre_its_window[1]) - 2; - if (its->device_ids > ids) - its->device_ids = ids; + if (device_ids(its) > ids) { + its->typer &= ~GITS_TYPER_DEVBITS; + its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1); + } /* the pre-ITS breaks isolation, so disable MSI remapping */ its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP; @@ -3537,7 +3540,7 @@ static int its_init_vpe_domain(void) } /* Use the last possible DevID */ - devid = GENMASK(its->device_ids - 1, 0); + devid = GENMASK(device_ids(its) - 1, 0); vpe_proxy.dev = its_create_device(its, devid, entries, false); if (!vpe_proxy.dev) { kfree(vpe_proxy.vpes); @@ -3638,7 +3641,6 @@ static int __init its_probe_one(struct resource *res, its->typer = typer; its->base = its_base; its->phys_base = res->start; - its->device_ids = GITS_TYPER_DEVBITS(typer); if (is_v4(its)) { if (!(typer & GITS_TYPER_VMOVP)) { err = its_compute_its_list_map(res, its_base); -- cgit v1.2.3 From c1d4d5cd203cc8ec83d67d4e2af51f1a9f01ba34 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:01 +0000 Subject: irqchip/gic-v3-its: Add its_vlpi_map helpers Obtaining the mapping information for a VLPI is something quite common, and the GICv4.1 code is going to make even more use of it. Expose it as a separate set of helpers. Signed-off-by: Marc Zyngier Reviewed-by: Zenghui Yu Link: https://lore.kernel.org/r/20191027144234.8395-8-maz@kernel.org Link: https://lore.kernel.org/r/20191108165805.3071-8-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 47 +++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e8aeb07e1cb0..e8d088c0a673 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -207,6 +207,15 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev, return its->collections + its_dev->event_map.col_map[event]; } +static struct its_vlpi_map *dev_event_to_vlpi_map(struct its_device *its_dev, + u32 event) +{ + if (WARN_ON_ONCE(event >= its_dev->event_map.nr_lpis)) + return NULL; + + return &its_dev->event_map.vlpi_maps[event]; +} + static struct its_collection *irq_to_col(struct irq_data *d) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); @@ -971,7 +980,7 @@ static void its_send_invall(struct its_node *its, struct its_collection *col) static void its_send_vmapti(struct its_device *dev, u32 id) { - struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id]; + struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id); struct its_cmd_desc desc; desc.its_vmapti_cmd.vpe = map->vpe; @@ -985,7 +994,7 @@ static void its_send_vmapti(struct its_device *dev, u32 id) static void its_send_vmovi(struct its_device *dev, u32 id) { - struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id]; + struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id); struct its_cmd_desc desc; desc.its_vmovi_cmd.vpe = map->vpe; @@ -1063,20 +1072,26 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) /* * irqchip functions - assumes MSI, mostly. */ +static struct its_vlpi_map *get_vlpi_map(struct irq_data *d) +{ + struct its_device *its_dev = irq_data_get_irq_chip_data(d); + u32 event = its_get_event_id(d); + + if (!irqd_is_forwarded_to_vcpu(d)) + return NULL; + + return dev_event_to_vlpi_map(its_dev, event); +} static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) { + struct its_vlpi_map *map = get_vlpi_map(d); irq_hw_number_t hwirq; void *va; u8 *cfg; - 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; - - va = page_address(its_dev->event_map.vm->vprop_page); - map = &its_dev->event_map.vlpi_maps[event]; + if (map) { + va = page_address(map->vm->vprop_page); hwirq = map->vintid; /* Remember the updated property */ @@ -1136,11 +1151,14 @@ static void its_vlpi_set_doorbell(struct irq_data *d, bool enable) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); + struct its_vlpi_map *map; - if (its_dev->event_map.vlpi_maps[event].db_enabled == enable) + map = dev_event_to_vlpi_map(its_dev, event); + + if (map->db_enabled == enable) return; - its_dev->event_map.vlpi_maps[event].db_enabled = enable; + map->db_enabled = enable; /* * More fun with the architecture: @@ -1369,19 +1387,18 @@ out: static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); - u32 event = its_get_event_id(d); + struct its_vlpi_map *map = get_vlpi_map(d); int ret = 0; mutex_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm || - !its_dev->event_map.vlpi_maps[event].vm) { + if (!its_dev->event_map.vm || !map->vm) { ret = -EINVAL; goto out; } /* Copy our mapping information to the incoming request */ - *info->map = its_dev->event_map.vlpi_maps[event]; + *info->map = *map; out: mutex_unlock(&its_dev->event_map.vlpi_lock); -- cgit v1.2.3 From 286146960a110cdae455a18cef47d5113d9a95c6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:02 +0000 Subject: irqchip/gic-v3-its: Synchronise INV command targetting a VLPI using VSYNC We have so far alwways invalidated VLPIs usinc an INV+SYNC sequence, but that's pretty wrong for two reasons: - SYNC only synchronises physical LPIs - The collection ID that for the associated LPI doesn't match the redistributor the vPE is associated with Instead, send an INV+VSYNC for forwarded LPIs, ensuring that the ITS can properly synchronise the invalidation of VLPIs. Fixes: 015ec0386ab6 ("irqchip/gic-v3-its: Add VLPI configuration handling") Reported-by: Zenghui Yu Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191108165805.3071-9-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e8d088c0a673..6a18b01edbc4 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -703,6 +703,24 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, return valid_vpe(its, desc->its_vmovp_cmd.vpe); } +static struct its_vpe *its_build_vinv_cmd(struct its_node *its, + struct its_cmd_block *cmd, + struct its_cmd_desc *desc) +{ + struct its_vlpi_map *map; + + map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev, + desc->its_inv_cmd.event_id); + + its_encode_cmd(cmd, GITS_CMD_INV); + its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); + its_encode_event_id(cmd, desc->its_inv_cmd.event_id); + + its_fixup_cmd(cmd); + + return valid_vpe(its, map->vpe); +} + static u64 its_cmd_ptr_to_offset(struct its_node *its, struct its_cmd_block *ptr) { @@ -1069,6 +1087,20 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) its_send_single_vcommand(its, its_build_vinvall_cmd, &desc); } +static void its_send_vinv(struct its_device *dev, u32 event_id) +{ + struct its_cmd_desc desc; + + /* + * There is no real VINV command. This is just a normal INV, + * with a VSYNC instead of a SYNC. + */ + desc.its_inv_cmd.dev = dev; + desc.its_inv_cmd.event_id = event_id; + + its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc); +} + /* * irqchip functions - assumes MSI, mostly. */ @@ -1143,8 +1175,10 @@ static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) lpi_write_config(d, clr, set); if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d)) direct_lpi_inv(d); - else + else if (!irqd_is_forwarded_to_vcpu(d)) its_send_inv(its_dev, its_get_event_id(d)); + else + its_send_vinv(its_dev, its_get_event_id(d)); } static void its_vlpi_set_doorbell(struct irq_data *d, bool enable) -- cgit v1.2.3 From ed0e4aa9cc74c08a429f4a389483c1076da2ca51 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:03 +0000 Subject: irqchip/gic-v3-its: Synchronise INT/CLEAR commands targetting a VLPI using VSYNC We have so far always injected/cleared VLPIs using either INT+SYNC or CLEAR+SYNC sequences, but that's pretty wrong for two reasons: - SYNC only synchronises physical LPIs - The collection ID that for the associated LPI doesn't match the redistributor the vPE is associated with Instead, send an {INT,CLEAR}+VSYNC for forwarded LPIs, ensuring that the ITS synchronises against the virtual pending table. Reported-by: Zenghui Yu Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191108165805.3071-10-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 79 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6a18b01edbc4..61b885133316 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -721,6 +721,42 @@ static struct its_vpe *its_build_vinv_cmd(struct its_node *its, return valid_vpe(its, map->vpe); } +static struct its_vpe *its_build_vint_cmd(struct its_node *its, + struct its_cmd_block *cmd, + struct its_cmd_desc *desc) +{ + struct its_vlpi_map *map; + + map = dev_event_to_vlpi_map(desc->its_int_cmd.dev, + desc->its_int_cmd.event_id); + + its_encode_cmd(cmd, GITS_CMD_INT); + its_encode_devid(cmd, desc->its_int_cmd.dev->device_id); + its_encode_event_id(cmd, desc->its_int_cmd.event_id); + + its_fixup_cmd(cmd); + + return valid_vpe(its, map->vpe); +} + +static struct its_vpe *its_build_vclear_cmd(struct its_node *its, + struct its_cmd_block *cmd, + struct its_cmd_desc *desc) +{ + struct its_vlpi_map *map; + + map = dev_event_to_vlpi_map(desc->its_clear_cmd.dev, + desc->its_clear_cmd.event_id); + + its_encode_cmd(cmd, GITS_CMD_CLEAR); + its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id); + its_encode_event_id(cmd, desc->its_clear_cmd.event_id); + + its_fixup_cmd(cmd); + + return valid_vpe(its, map->vpe); +} + static u64 its_cmd_ptr_to_offset(struct its_node *its, struct its_cmd_block *ptr) { @@ -1101,6 +1137,34 @@ static void its_send_vinv(struct its_device *dev, u32 event_id) its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc); } +static void its_send_vint(struct its_device *dev, u32 event_id) +{ + struct its_cmd_desc desc; + + /* + * There is no real VINT command. This is just a normal INT, + * with a VSYNC instead of a SYNC. + */ + desc.its_int_cmd.dev = dev; + desc.its_int_cmd.event_id = event_id; + + its_send_single_vcommand(dev->its, its_build_vint_cmd, &desc); +} + +static void its_send_vclear(struct its_device *dev, u32 event_id) +{ + struct its_cmd_desc desc; + + /* + * There is no real VCLEAR command. This is just a normal CLEAR, + * with a VSYNC instead of a SYNC. + */ + desc.its_clear_cmd.dev = dev; + desc.its_clear_cmd.event_id = event_id; + + its_send_single_vcommand(dev->its, its_build_vclear_cmd, &desc); +} + /* * irqchip functions - assumes MSI, mostly. */ @@ -1294,10 +1358,17 @@ static int its_irq_set_irqchip_state(struct irq_data *d, if (which != IRQCHIP_STATE_PENDING) return -EINVAL; - if (state) - its_send_int(its_dev, event); - else - its_send_clear(its_dev, event); + if (irqd_is_forwarded_to_vcpu(d)) { + if (state) + its_send_vint(its_dev, event); + else + its_send_vclear(its_dev, event); + } else { + if (state) + its_send_int(its_dev, event); + else + its_send_clear(its_dev, event); + } return 0; } -- cgit v1.2.3 From 046b5054f56691c7f5861197a812f3990f66b30e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:04 +0000 Subject: irqchip/gic-v3-its: Lock VLPI map array before translating it Obtaining the mapping ivformation for a VLPI should always be done with the vlpi_lock for this device held. Otherwise, we expose ourselves to races against a concurrent unmap. Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191108165805.3071-11-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 61b885133316..9c4f35ed134c 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1492,12 +1492,14 @@ out: static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); - struct its_vlpi_map *map = get_vlpi_map(d); + struct its_vlpi_map *map; int ret = 0; mutex_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm || !map->vm) { + map = get_vlpi_map(d); + + if (!its_dev->event_map.vm || !map) { ret = -EINVAL; goto out; } -- cgit v1.2.3 From 11635fa26dc7a715f3fc1c351846859e90985ae1 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 8 Nov 2019 16:58:05 +0000 Subject: irqchip/gic-v3-its: Make vlpi_lock a spinlock The VLPI map is currently a mutex, and that's a bad idea as this lock can be taken in non-preemptible contexts. Convert it to a raw spinlock, and turn the memory allocation of the VLPI map to be atomic. Reported-by: Heyi Guo Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191108165805.3071-12-maz@kernel.org --- drivers/irqchip/irq-gic-v3-its.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9c4f35ed134c..e05673bcd52b 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -132,7 +132,7 @@ struct event_lpi_map { u16 *col_map; irq_hw_number_t lpi_base; int nr_lpis; - struct mutex vlpi_lock; + raw_spinlock_t vlpi_lock; struct its_vm *vm; struct its_vlpi_map *vlpi_maps; int nr_vlpis; @@ -1436,13 +1436,13 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) if (!info->map) return -EINVAL; - mutex_lock(&its_dev->event_map.vlpi_lock); + raw_spin_lock(&its_dev->event_map.vlpi_lock); if (!its_dev->event_map.vm) { struct its_vlpi_map *maps; maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps), - GFP_KERNEL); + GFP_ATOMIC); if (!maps) { ret = -ENOMEM; goto out; @@ -1485,7 +1485,7 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) } out: - mutex_unlock(&its_dev->event_map.vlpi_lock); + raw_spin_unlock(&its_dev->event_map.vlpi_lock); return ret; } @@ -1495,7 +1495,7 @@ static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) struct its_vlpi_map *map; int ret = 0; - mutex_lock(&its_dev->event_map.vlpi_lock); + raw_spin_lock(&its_dev->event_map.vlpi_lock); map = get_vlpi_map(d); @@ -1508,7 +1508,7 @@ static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) *info->map = *map; out: - mutex_unlock(&its_dev->event_map.vlpi_lock); + raw_spin_unlock(&its_dev->event_map.vlpi_lock); return ret; } @@ -1518,7 +1518,7 @@ static int its_vlpi_unmap(struct irq_data *d) u32 event = its_get_event_id(d); int ret = 0; - mutex_lock(&its_dev->event_map.vlpi_lock); + raw_spin_lock(&its_dev->event_map.vlpi_lock); if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) { ret = -EINVAL; @@ -1548,7 +1548,7 @@ static int its_vlpi_unmap(struct irq_data *d) } out: - mutex_unlock(&its_dev->event_map.vlpi_lock); + raw_spin_unlock(&its_dev->event_map.vlpi_lock); return ret; } @@ -2608,7 +2608,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, dev->event_map.col_map = col_map; dev->event_map.lpi_base = lpi_base; dev->event_map.nr_lpis = nr_lpis; - mutex_init(&dev->event_map.vlpi_lock); + raw_spin_lock_init(&dev->event_map.vlpi_lock); dev->device_id = dev_id; INIT_LIST_HEAD(&dev->entry); -- cgit v1.2.3 From 0149385537e6d36f535fcd83cfcabf83a32f0836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Wed, 2 Oct 2019 16:44:52 +0200 Subject: irqchip: Place CONFIG_SIFIVE_PLIC into the menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Somehow CONFIG_SIFIVE_PLIC ended up outside of the "IRQ chip support" menu. Fixes: 8237f8bc4f6e ("irqchip: add a SiFive PLIC driver") Signed-off-by: Jonathan Neuschäfer Signed-off-by: Marc Zyngier Reviewed-by: Palmer Dabbelt Acked-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20191002144452.10178-1-j.neuschaefer@gmx.net --- drivers/irqchip/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bbb323462912..697e6a8ccaae 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -487,8 +487,6 @@ config TI_SCI_INTA_IRQCHIP If you wish to use interrupt aggregator irq resources managed by the TI System Controller, say Y here. Otherwise, say N. -endmenu - config SIFIVE_PLIC bool "SiFive Platform-Level Interrupt Controller" depends on RISCV @@ -500,3 +498,5 @@ config SIFIVE_PLIC interrupt sources are subordinate to the PLIC. If you don't know what to do here, say Y. + +endmenu -- cgit v1.2.3 From 20b44b4de61f2887694981e8cae74fe1bf58f950 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 2 Oct 2019 19:25:21 +0800 Subject: irqchip: ingenic: Drop redundant irq_suspend / irq_resume functions The same behaviour can be obtained by using the IRQCHIP_MASK_ON_SUSPEND flag on the IRQ chip. Signed-off-by: Paul Cercueil Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1570015525-27018-2-git-send-email-zhouyanjie@zoho.com --- drivers/irqchip/irq-ingenic.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index f126255b3260..06fa810e89bb 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -50,26 +49,6 @@ static irqreturn_t intc_cascade(int irq, void *data) return IRQ_HANDLED; } -static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask) -{ - struct irq_chip_regs *regs = &gc->chip_types->regs; - - writel(mask, gc->reg_base + regs->enable); - writel(~mask, gc->reg_base + regs->disable); -} - -void ingenic_intc_irq_suspend(struct irq_data *data) -{ - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - intc_irq_set_mask(gc, gc->wake_active); -} - -void ingenic_intc_irq_resume(struct irq_data *data) -{ - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - intc_irq_set_mask(gc, gc->mask_cache); -} - static struct irqaction intc_cascade_action = { .handler = intc_cascade, .name = "SoC intc cascade interrupt", @@ -127,8 +106,7 @@ static int __init ingenic_intc_of_init(struct device_node *node, ct->chip.irq_mask = irq_gc_mask_disable_reg; ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; ct->chip.irq_set_wake = irq_gc_set_wake; - ct->chip.irq_suspend = ingenic_intc_irq_suspend; - ct->chip.irq_resume = ingenic_intc_irq_resume; + ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND; irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, IRQ_NOPROBE | IRQ_LEVEL); -- cgit v1.2.3 From 52ecc87642f273a599c9913b29fd179c13de457b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 2 Oct 2019 19:25:22 +0800 Subject: irqchip: ingenic: Error out if IRQ domain creation failed If we cannot create the IRQ domain, the driver should fail to probe instead of succeeding with just a warning message. Signed-off-by: Paul Cercueil Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1570015525-27018-3-git-send-email-zhouyanjie@zoho.com --- drivers/irqchip/irq-ingenic.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index 06fa810e89bb..d97a3a500249 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -87,6 +87,14 @@ static int __init ingenic_intc_of_init(struct device_node *node, goto out_unmap_irq; } + domain = irq_domain_add_legacy(node, num_chips * 32, + JZ4740_IRQ_BASE, 0, + &irq_domain_simple_ops, NULL); + if (!domain) { + err = -ENOMEM; + goto out_unmap_base; + } + for (i = 0; i < num_chips; i++) { /* Mask all irqs */ writel(0xffffffff, intc->base + (i * CHIP_SIZE) + @@ -112,14 +120,11 @@ static int __init ingenic_intc_of_init(struct device_node *node, IRQ_NOPROBE | IRQ_LEVEL); } - domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0, - &irq_domain_simple_ops, NULL); - if (!domain) - pr_warn("unable to register IRQ domain\n"); - setup_irq(parent_irq, &intc_cascade_action); return 0; +out_unmap_base: + iounmap(intc->base); out_unmap_irq: irq_dispose_mapping(parent_irq); out_free: -- cgit v1.2.3 From 208caadce5d4d38f48af965206bbd4473d265080 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 2 Oct 2019 19:25:23 +0800 Subject: irqchip: ingenic: Get virq number from IRQ domain Get the virq number from the IRQ domain instead of calculating it from the hardcoded irq base. Signed-off-by: Paul Cercueil Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1570015525-27018-4-git-send-email-zhouyanjie@zoho.com --- drivers/irqchip/irq-ingenic.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index d97a3a500249..82a079fa3a3d 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -21,6 +21,7 @@ struct ingenic_intc_data { void __iomem *base; + struct irq_domain *domain; unsigned num_chips; }; @@ -34,6 +35,7 @@ struct ingenic_intc_data { static irqreturn_t intc_cascade(int irq, void *data) { struct ingenic_intc_data *intc = irq_get_handler_data(irq); + struct irq_domain *domain = intc->domain; uint32_t irq_reg; unsigned i; @@ -43,7 +45,8 @@ static irqreturn_t intc_cascade(int irq, void *data) if (!irq_reg) continue; - generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE); + irq = irq_find_mapping(domain, __fls(irq_reg) + (i * 32)); + generic_handle_irq(irq); } return IRQ_HANDLED; @@ -95,6 +98,8 @@ static int __init ingenic_intc_of_init(struct device_node *node, goto out_unmap_base; } + intc->domain = domain; + for (i = 0; i < num_chips; i++) { /* Mask all irqs */ writel(0xffffffff, intc->base + (i * CHIP_SIZE) + -- cgit v1.2.3 From 8bc7464b5140218cb714abae55ea4cfe26b30c96 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 2 Oct 2019 19:25:24 +0800 Subject: irqchip: ingenic: Alloc generic chips from IRQ domain By creating the generic chips from the IRQ domain, we don't rely on the JZ4740_IRQ_BASE macro. It also makes the code a bit cleaner. Signed-off-by: Paul Cercueil Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1570015525-27018-5-git-send-email-zhouyanjie@zoho.com --- drivers/irqchip/irq-ingenic.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index 82a079fa3a3d..06ab3ad22ad2 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -36,12 +36,14 @@ static irqreturn_t intc_cascade(int irq, void *data) { struct ingenic_intc_data *intc = irq_get_handler_data(irq); struct irq_domain *domain = intc->domain; + struct irq_chip_generic *gc; uint32_t irq_reg; unsigned i; for (i = 0; i < intc->num_chips; i++) { - irq_reg = readl(intc->base + (i * CHIP_SIZE) + - JZ_REG_INTC_PENDING); + gc = irq_get_domain_generic_chip(domain, i * 32); + + irq_reg = irq_reg_readl(gc, JZ_REG_INTC_PENDING); if (!irq_reg) continue; @@ -92,7 +94,7 @@ static int __init ingenic_intc_of_init(struct device_node *node, domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0, - &irq_domain_simple_ops, NULL); + &irq_generic_chip_ops, NULL); if (!domain) { err = -ENOMEM; goto out_unmap_base; @@ -100,17 +102,17 @@ static int __init ingenic_intc_of_init(struct device_node *node, intc->domain = domain; - for (i = 0; i < num_chips; i++) { - /* Mask all irqs */ - writel(0xffffffff, intc->base + (i * CHIP_SIZE) + - JZ_REG_INTC_SET_MASK); + err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC", + handle_level_irq, 0, + IRQ_NOPROBE | IRQ_LEVEL, 0); + if (err) + goto out_domain_remove; - gc = irq_alloc_generic_chip("INTC", 1, - JZ4740_IRQ_BASE + (i * 32), - intc->base + (i * CHIP_SIZE), - handle_level_irq); + for (i = 0; i < num_chips; i++) { + gc = irq_get_domain_generic_chip(domain, i * 32); gc->wake_enabled = IRQ_MSK(32); + gc->reg_base = intc->base + (i * CHIP_SIZE); ct = gc->chip_types; ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; @@ -121,13 +123,15 @@ static int __init ingenic_intc_of_init(struct device_node *node, ct->chip.irq_set_wake = irq_gc_set_wake; ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND; - irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, - IRQ_NOPROBE | IRQ_LEVEL); + /* Mask all irqs */ + irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK); } setup_irq(parent_irq, &intc_cascade_action); return 0; +out_domain_remove: + irq_domain_remove(domain); out_unmap_base: iounmap(intc->base); out_unmap_irq: -- cgit v1.2.3 From b8b0145f7d0e24d98a58b7e54051dca0c1d77526 Mon Sep 17 00:00:00 2001 From: Zhou Yanjie Date: Wed, 2 Oct 2019 19:25:25 +0800 Subject: irqchip: Ingenic: Add process for more than one irq at the same time. Add process for the situation that more than one irq is coming to a single chip at the same time. The original code will only respond to the lowest setted bit in JZ_REG_INTC_PENDING, and then exit the interrupt dispatch function. After exiting the interrupt dispatch function, since the second interrupt has not yet responded, the interrupt dispatch function is again entered to process the second interrupt. This creates additional unnecessary overhead, and the more interrupts that occur at the same time, the more overhead is added. The improved method in this patch is to check whether there are still unresponsive interrupts after processing the lowest setted bit interrupt. If there are any, the processing will be processed according to the bit in JZ_REG_INTC_PENDING, and the interrupt dispatch function will be exited until all processing is completed. Signed-off-by: Zhou Yanjie Signed-off-by: Marc Zyngier Reviewed-by: Paul Cercueil Link: https://lore.kernel.org/r/1570015525-27018-6-git-send-email-zhouyanjie@zoho.com --- drivers/irqchip/irq-ingenic.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index 06ab3ad22ad2..01d18b39069e 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2009-2010, Lars-Peter Clausen - * JZ4740 platform IRQ support + * Ingenic XBurst platform IRQ support */ #include @@ -37,18 +37,23 @@ static irqreturn_t intc_cascade(int irq, void *data) struct ingenic_intc_data *intc = irq_get_handler_data(irq); struct irq_domain *domain = intc->domain; struct irq_chip_generic *gc; - uint32_t irq_reg; + uint32_t pending; unsigned i; for (i = 0; i < intc->num_chips; i++) { gc = irq_get_domain_generic_chip(domain, i * 32); - irq_reg = irq_reg_readl(gc, JZ_REG_INTC_PENDING); - if (!irq_reg) + pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING); + if (!pending) continue; - irq = irq_find_mapping(domain, __fls(irq_reg) + (i * 32)); - generic_handle_irq(irq); + while (pending) { + int bit = __fls(pending); + + irq = irq_find_mapping(domain, bit + (i * 32)); + generic_handle_irq(irq); + pending &= ~BIT(bit); + } } return IRQ_HANDLED; -- cgit v1.2.3 From 761becb29183c4e2ad9ff5f63933170c8fffd544 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 5 Nov 2019 12:19:39 +0100 Subject: irqchip/ti-sci-inta: Use ERR_CAST inlined function instead of ERR_PTR(PTR_ERR(...)) A coccicheck run provided information like the following. drivers/irqchip/irq-ti-sci-inta.c:250:9-16: WARNING: ERR_CAST can be used with vint_desc. Generated by: scripts/coccinelle/api/err_cast.cocci Thus adjust the exception handling in one if branch. Signed-off-by: Markus Elfring Signed-off-by: Marc Zyngier Reviewed-by: Lokesh Vutla Link: https://lore.kernel.org/r/776b7135-26af-df7d-c3a9-4339f7bf1f15@web.de --- drivers/irqchip/irq-ti-sci-inta.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c index ef4d625d2d80..8f6e6b08eadf 100644 --- a/drivers/irqchip/irq-ti-sci-inta.c +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -246,8 +246,8 @@ static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *d /* No free bits available. Allocate a new vint */ vint_desc = ti_sci_inta_alloc_parent_irq(domain); if (IS_ERR(vint_desc)) { - mutex_unlock(&inta->vint_mutex); - return ERR_PTR(PTR_ERR(vint_desc)); + event_desc = ERR_CAST(vint_desc); + goto unlock; } free_bit = find_first_zero_bit(vint_desc->event_map, @@ -259,6 +259,7 @@ alloc_event: if (IS_ERR(event_desc)) clear_bit(free_bit, vint_desc->event_map); +unlock: mutex_unlock(&inta->vint_mutex); return event_desc; } -- cgit v1.2.3 From b2bb01ed0894c6d5d31cfa8aafb6ddbd7df2dd3f Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Fri, 15 Nov 2019 15:11:45 -0700 Subject: irqchip/qcom-pdc: Update max PDC interrupts Newer SoCs have increased the number of interrupts routed to the PDC interrupt controller. Update the definition of max PDC interrupts. Signed-off-by: Lina Iyer Signed-off-by: Marc Zyngier Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1573855915-9841-3-git-send-email-ilina@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index c175333bb646..690cf108ce06 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. */ #include @@ -18,7 +18,7 @@ #include #include -#define PDC_MAX_IRQS 126 +#define PDC_MAX_IRQS 168 #define CLEAR_INTR(reg, intr) (reg & ~(1 << intr)) #define ENABLE_INTR(reg, intr) (reg | (1 << intr)) -- cgit v1.2.3 From da3f875a4189e643f8eec7f0bffa39c90d3418c6 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Fri, 15 Nov 2019 15:11:46 -0700 Subject: irqchip/qcom-pdc: Do not toggle IRQ_ENABLE during mask/unmask When an interrupt is to be serviced, the convention is to mask the interrupt at the chip and unmask after servicing the interrupt. Enabling and disabling the interrupt at the PDC irqchip causes an interrupt storm due to the way dual edge interrupts are handled in hardware. Skip configuring the PDC when the IRQ is masked and unmasked, instead use the irq_enable/irq_disable callbacks to toggle the IRQ_ENABLE register at the PDC. The PDC's IRQ_ENABLE register is only used during the monitoring mode when the system is asleep and is not needed for active mode detection. Signed-off-by: Lina Iyer Signed-off-by: Marc Zyngier Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1573855915-9841-4-git-send-email-ilina@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 690cf108ce06..527c29e212bd 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -63,15 +63,25 @@ static void pdc_enable_intr(struct irq_data *d, bool on) raw_spin_unlock(&pdc_lock); } -static void qcom_pdc_gic_mask(struct irq_data *d) +static void qcom_pdc_gic_disable(struct irq_data *d) { pdc_enable_intr(d, false); + irq_chip_disable_parent(d); +} + +static void qcom_pdc_gic_enable(struct irq_data *d) +{ + pdc_enable_intr(d, true); + irq_chip_enable_parent(d); +} + +static void qcom_pdc_gic_mask(struct irq_data *d) +{ irq_chip_mask_parent(d); } static void qcom_pdc_gic_unmask(struct irq_data *d) { - pdc_enable_intr(d, true); irq_chip_unmask_parent(d); } @@ -148,6 +158,8 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_eoi = irq_chip_eoi_parent, .irq_mask = qcom_pdc_gic_mask, .irq_unmask = qcom_pdc_gic_unmask, + .irq_disable = qcom_pdc_gic_disable, + .irq_enable = qcom_pdc_gic_enable, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = qcom_pdc_gic_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | -- cgit v1.2.3 From 81ef8bf88065b07d597c723ca5b0f1f10a808de4 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Fri, 15 Nov 2019 15:11:47 -0700 Subject: irqchip/qcom-pdc: Add irqdomain for wakeup capable GPIOs Introduce a new domain for wakeup capable GPIOs. The domain can be requested using the bus token DOMAIN_BUS_WAKEUP. In the following patches, we will specify PDC as the wakeup-parent for the TLMM GPIO irqchip. Requesting a wakeup GPIO will setup the GPIO and the corresponding PDC interrupt as its parent. Co-developed-by: Stephen Boyd Signed-off-by: Stephen Boyd Signed-off-by: Lina Iyer Signed-off-by: Marc Zyngier Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1573855915-9841-5-git-send-email-ilina@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 104 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 527c29e212bd..4f2c76229fb7 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -13,12 +13,13 @@ #include #include #include +#include #include -#include #include #include #define PDC_MAX_IRQS 168 +#define PDC_MAX_GPIO_IRQS 256 #define CLEAR_INTR(reg, intr) (reg & ~(1 << intr)) #define ENABLE_INTR(reg, intr) (reg | (1 << intr)) @@ -26,6 +27,8 @@ #define IRQ_ENABLE_BANK 0x10 #define IRQ_i_CFG 0x110 +#define PDC_NO_PARENT_IRQ ~0UL + struct pdc_pin_region { u32 pin_base; u32 parent_base; @@ -65,23 +68,35 @@ static void pdc_enable_intr(struct irq_data *d, bool on) static void qcom_pdc_gic_disable(struct irq_data *d) { + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return; + pdc_enable_intr(d, false); irq_chip_disable_parent(d); } static void qcom_pdc_gic_enable(struct irq_data *d) { + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return; + pdc_enable_intr(d, true); irq_chip_enable_parent(d); } static void qcom_pdc_gic_mask(struct irq_data *d) { + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return; + irq_chip_mask_parent(d); } static void qcom_pdc_gic_unmask(struct irq_data *d) { + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return; + irq_chip_unmask_parent(d); } @@ -124,6 +139,9 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) int pin_out = d->hwirq; enum pdc_irq_config_bits pdc_type; + if (pin_out == GPIO_NO_WAKE_IRQ) + return 0; + switch (type) { case IRQ_TYPE_EDGE_RISING: pdc_type = PDC_EDGE_RISING; @@ -181,8 +199,7 @@ static irq_hw_number_t get_parent_hwirq(int pin) return (region->parent_base + pin - region->pin_base); } - WARN_ON(1); - return ~0UL; + return PDC_NO_PARENT_IRQ; } static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec, @@ -211,17 +228,17 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq, ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type); if (ret) - return -EINVAL; - - parent_hwirq = get_parent_hwirq(hwirq); - if (parent_hwirq == ~0UL) - return -EINVAL; + return ret; ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &qcom_pdc_gic_chip, NULL); if (ret) return ret; + parent_hwirq = get_parent_hwirq(hwirq); + if (parent_hwirq == PDC_NO_PARENT_IRQ) + return 0; + if (type & IRQ_TYPE_EDGE_BOTH) type = IRQ_TYPE_EDGE_RISING; @@ -244,6 +261,60 @@ static const struct irq_domain_ops qcom_pdc_ops = { .free = irq_domain_free_irqs_common, }; +static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq, parent_hwirq; + unsigned int type; + int ret; + + ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &qcom_pdc_gic_chip, NULL); + if (ret) + return ret; + + if (hwirq == GPIO_NO_WAKE_IRQ) + return 0; + + parent_hwirq = get_parent_hwirq(hwirq); + if (parent_hwirq == PDC_NO_PARENT_IRQ) + return 0; + + if (type & IRQ_TYPE_EDGE_BOTH) + type = IRQ_TYPE_EDGE_RISING; + + if (type & IRQ_TYPE_LEVEL_MASK) + type = IRQ_TYPE_LEVEL_HIGH; + + parent_fwspec.fwnode = domain->parent->fwnode; + parent_fwspec.param_count = 3; + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = parent_hwirq; + parent_fwspec.param[2] = type; + + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static int qcom_pdc_gpio_domain_select(struct irq_domain *d, + struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + return bus_token == DOMAIN_BUS_WAKEUP; +} + +static const struct irq_domain_ops qcom_pdc_gpio_ops = { + .select = qcom_pdc_gpio_domain_select, + .alloc = qcom_pdc_gpio_alloc, + .free = irq_domain_free_irqs_common, +}; + static int pdc_setup_pin_mapping(struct device_node *np) { int ret, n; @@ -282,7 +353,7 @@ static int pdc_setup_pin_mapping(struct device_node *np) static int qcom_pdc_init(struct device_node *node, struct device_node *parent) { - struct irq_domain *parent_domain, *pdc_domain; + struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain; int ret; pdc_base = of_iomap(node, 0); @@ -313,8 +384,23 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent) goto fail; } + pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain, + IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP, + PDC_MAX_GPIO_IRQS, + of_fwnode_handle(node), + &qcom_pdc_gpio_ops, NULL); + if (!pdc_gpio_domain) { + pr_err("%pOF: PDC domain add failed for GPIO domain\n", node); + ret = -ENOMEM; + goto remove; + } + + irq_domain_update_bus_token(pdc_gpio_domain, DOMAIN_BUS_WAKEUP); + return 0; +remove: + irq_domain_remove(pdc_domain); fail: kfree(pdc_region); iounmap(pdc_base); -- cgit v1.2.3 From e71374c07564536d38caed3e80a1ff1c4609161d Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 15 Nov 2019 15:11:50 -0700 Subject: irqchip/qcom-pdc: Add irqchip set/get state calls Add irqchip calls to set/get interrupt state from the parent interrupt controller. When GPIOs are renabled as interrupt lines, it is desirable to clear the interrupt state at the GIC. This avoids any unwanted interrupt as a result of stale pending state recorded when the line was used as a GPIO. Signed-off-by: Maulik Shah [Lina: updated commit text, rearranged code] Signed-off-by: Lina Iyer Signed-off-by: Marc Zyngier Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1573855915-9841-8-git-send-email-ilina@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 4f2c76229fb7..6ae9e1f0819d 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,26 @@ static u32 pdc_reg_read(int reg, u32 i) return readl_relaxed(pdc_base + reg + i * sizeof(u32)); } +static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, + bool *state) +{ + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return 0; + + return irq_chip_get_parent_state(d, which, state); +} + +static int qcom_pdc_gic_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, + bool value) +{ + if (d->hwirq == GPIO_NO_WAKE_IRQ) + return 0; + + return irq_chip_set_parent_state(d, which, value); +} + static void pdc_enable_intr(struct irq_data *d, bool on) { int pin_out = d->hwirq; @@ -178,6 +199,8 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_unmask = qcom_pdc_gic_unmask, .irq_disable = qcom_pdc_gic_disable, .irq_enable = qcom_pdc_gic_enable, + .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state, + .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = qcom_pdc_gic_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | -- cgit v1.2.3