From f9551a9c083b8acc1db38e234a630655bae8e771 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:49:40 +0100 Subject: irqchip/renesas-intc-irqpin: Remove obsolete platform data support Since commit 4baadb9e05c68962 ("ARM: shmobile: r8a7778: remove obsolete setup code"), all Renesas SoCs with a renesas-intc-irqpin module are only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to use platform data anymore, hence remove platform data configuration. Signed-off-by: Geert Uytterhoeven Acked-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lkml.kernel.org/r/1448376581-9202-2-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 38 ++++++++++--------------------- 1 file changed, 12 insertions(+), 26 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index c325806561be..7aefa500d210 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ @@ -75,7 +74,7 @@ struct intc_irqpin_irq { struct intc_irqpin_priv { struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; - struct renesas_intc_irqpin_config config; + unsigned int sense_bitfield_width; unsigned int number_of_irqs; struct platform_device *pdev; struct irq_chip irq_chip; @@ -171,7 +170,7 @@ static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) { /* The SENSE register is assumed to be 32-bit. */ - int bitfield_width = p->config.sense_bitfield_width; + int bitfield_width = p->sense_bitfield_width; int shift = 32 - (irq + 1) * bitfield_width; dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); @@ -378,7 +377,6 @@ MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); static int intc_irqpin_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct renesas_intc_irqpin_config *pdata = dev->platform_data; const struct of_device_id *of_id; struct intc_irqpin_priv *p; struct intc_irqpin_iomem *i; @@ -388,6 +386,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) void (*enable_fn)(struct irq_data *d); void (*disable_fn)(struct irq_data *d); const char *name = dev_name(dev); + bool control_parent; int ref_irq; int ret; int k; @@ -399,16 +398,11 @@ static int intc_irqpin_probe(struct platform_device *pdev) } /* deal with driver instance configuration */ - if (pdata) { - memcpy(&p->config, pdata, sizeof(*pdata)); - } else { - of_property_read_u32(dev->of_node, "sense-bitfield-width", - &p->config.sense_bitfield_width); - p->config.control_parent = of_property_read_bool(dev->of_node, - "control-parent"); - } - if (!p->config.sense_bitfield_width) - p->config.sense_bitfield_width = 4; /* default to 4 bits */ + of_property_read_u32(dev->of_node, "sense-bitfield-width", + &p->sense_bitfield_width); + control_parent = of_property_read_bool(dev->of_node, "control-parent"); + if (!p->sense_bitfield_width) + p->sense_bitfield_width = 4; /* default to 4 bits */ p->pdev = pdev; platform_set_drvdata(pdev, p); @@ -515,7 +509,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) } /* use more severe masking method if requested */ - if (p->config.control_parent) { + if (control_parent) { enable_fn = intc_irqpin_irq_enable_force; disable_fn = intc_irqpin_irq_disable_force; } else if (!p->shared_irqs) { @@ -534,10 +528,9 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(dev->of_node, - p->number_of_irqs, - p->config.irq_base, - &intc_irqpin_irq_domain_ops, p); + p->irq_domain = irq_domain_add_simple(dev->of_node, p->number_of_irqs, + 0, &intc_irqpin_irq_domain_ops, + p); if (!p->irq_domain) { ret = -ENXIO; dev_err(dev, "cannot initialize irq domain\n"); @@ -572,13 +565,6 @@ static int intc_irqpin_probe(struct platform_device *pdev) dev_info(dev, "driving %d irqs\n", p->number_of_irqs); - /* warn in case of mismatch if irq base is specified */ - if (p->config.irq_base) { - if (p->config.irq_base != p->irq[0].domain_irq) - dev_warn(dev, "irq base mismatch (%d/%d)\n", - p->config.irq_base, p->irq[0].domain_irq); - } - return 0; err1: -- cgit v1.2.3 From 1affe5946f7293b3747bfe7ef25dc5cfe9869012 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:49:41 +0100 Subject: irqchip/renesas-intc-irqpin: Remove intc_irqpin_priv.number_of_irqs intc_irqpin_priv.number_of_irqs is used inside intc_irqpin_probe() only, so it can just become a local variable. Signed-off-by: Geert Uytterhoeven Acked-by: Marc Zyngier Acked-by: Thomas Gleixner Link: https://lkml.kernel.org/r/1448376581-9202-3-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 7aefa500d210..7f6cf19aa6ac 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -75,7 +75,6 @@ struct intc_irqpin_priv { struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; unsigned int sense_bitfield_width; - unsigned int number_of_irqs; struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; @@ -387,6 +386,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) void (*disable_fn)(struct irq_data *d); const char *name = dev_name(dev); bool control_parent; + unsigned int nirqs; int ref_irq; int ret; int k; @@ -437,8 +437,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) p->irq[k].requested_irq = irq->start; } - p->number_of_irqs = k; - if (p->number_of_irqs < 1) { + nirqs = k; + if (nirqs < 1) { dev_err(dev, "not enough IRQ resources\n"); ret = -EINVAL; goto err0; @@ -492,7 +492,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) } /* mask all interrupts using priority */ - for (k = 0; k < p->number_of_irqs; k++) + for (k = 0; k < nirqs; k++) intc_irqpin_mask_unmask_prio(p, k, 1); /* clear all pending interrupts */ @@ -501,7 +501,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) /* scan for shared interrupt lines */ ref_irq = p->irq[0].requested_irq; p->shared_irqs = true; - for (k = 1; k < p->number_of_irqs; k++) { + for (k = 1; k < nirqs; k++) { if (ref_irq != p->irq[k].requested_irq) { p->shared_irqs = false; break; @@ -528,9 +528,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(dev->of_node, p->number_of_irqs, - 0, &intc_irqpin_irq_domain_ops, - p); + p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0, + &intc_irqpin_irq_domain_ops, p); if (!p->irq_domain) { ret = -ENXIO; dev_err(dev, "cannot initialize irq domain\n"); @@ -548,7 +547,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) } } else { /* request interrupts one by one */ - for (k = 0; k < p->number_of_irqs; k++) { + for (k = 0; k < nirqs; k++) { if (devm_request_irq(dev, p->irq[k].requested_irq, intc_irqpin_irq_handler, 0, name, &p->irq[k])) { @@ -560,10 +559,10 @@ static int intc_irqpin_probe(struct platform_device *pdev) } /* unmask all interrupts on prio level */ - for (k = 0; k < p->number_of_irqs; k++) + for (k = 0; k < nirqs; k++) intc_irqpin_mask_unmask_prio(p, k, 0); - dev_info(dev, "driving %d irqs\n", p->number_of_irqs); + dev_info(dev, "driving %d irqs\n", nirqs); return 0; -- cgit v1.2.3 From 86e57ca735a72b44aab90a649157a678b7069a6d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 16:08:13 +0100 Subject: irqchip/renesas-intc-irqpin: Improve clock error handling and reporting If the Renesas External IRQ Pin driver cannot find a functional clock, it prints a warning, .e.g. renesas_intc_irqpin fe78001c.interrupt-controller: unable to get clock and continues, as the clock is optional, depending on the SoC type. This warning may confuse users. To fix this, add a flag to indicate that the clock is mandatory or optional, and add a few more compatible entries: - If the clock is mandatory (on R-Mobile A1 or SH-Mobile AG5), a missing clock is now treated as a fatal error, - If the clock is optional (on R-Car Gen1, or using the generic "renesas,intc-irqpin" compatible value), the warning is no longer printed. This requires making struct intc_irqpin_irlm_config more generic by renaming it to intc_irqpin_config, and adding a flag to indicate if IRLM is needed. The new clock flag is merged with the existing shared_irqs boolean into a bitfield to save space. Suggested-by: Magnus Damm Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/1448377693-19597-1-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 45 ++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 12 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 7f6cf19aa6ac..713177d97c7a 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -79,12 +79,15 @@ struct intc_irqpin_priv { struct irq_chip irq_chip; struct irq_domain *irq_domain; struct clk *clk; - bool shared_irqs; + unsigned shared_irqs:1; + unsigned needs_clk:1; u8 shared_irq_mask; }; -struct intc_irqpin_irlm_config { +struct intc_irqpin_config { unsigned int irlm_bit; + unsigned needs_irlm:1; + unsigned needs_clk:1; }; static unsigned long intc_irqpin_read32(void __iomem *iomem) @@ -359,8 +362,15 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = { .xlate = irq_domain_xlate_twocell, }; -static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = { +static const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = { .irlm_bit = 23, /* ICR0.IRLM0 */ + .needs_irlm = 1, + .needs_clk = 0, +}; + +static const struct intc_irqpin_config intc_irqpin_rmobile = { + .needs_irlm = 0, + .needs_clk = 1, }; static const struct of_device_id intc_irqpin_dt_ids[] = { @@ -369,12 +379,17 @@ static const struct of_device_id intc_irqpin_dt_ids[] = { .data = &intc_irqpin_irlm_r8a777x }, { .compatible = "renesas,intc-irqpin-r8a7779", .data = &intc_irqpin_irlm_r8a777x }, + { .compatible = "renesas,intc-irqpin-r8a7740", + .data = &intc_irqpin_rmobile }, + { .compatible = "renesas,intc-irqpin-sh73a0", + .data = &intc_irqpin_rmobile }, {}, }; MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); static int intc_irqpin_probe(struct platform_device *pdev) { + const struct intc_irqpin_config *config = NULL; struct device *dev = &pdev->dev; const struct of_device_id *of_id; struct intc_irqpin_priv *p; @@ -407,9 +422,19 @@ static int intc_irqpin_probe(struct platform_device *pdev) p->pdev = pdev; platform_set_drvdata(pdev, p); + of_id = of_match_device(intc_irqpin_dt_ids, dev); + if (of_id && of_id->data) { + config = of_id->data; + p->needs_clk = config->needs_clk; + } + p->clk = devm_clk_get(dev, NULL); if (IS_ERR(p->clk)) { - dev_warn(dev, "unable to get clock\n"); + if (p->needs_clk) { + dev_err(dev, "unable to get clock\n"); + ret = PTR_ERR(p->clk); + goto err0; + } p->clk = NULL; } @@ -479,14 +504,10 @@ static int intc_irqpin_probe(struct platform_device *pdev) } /* configure "individual IRQ mode" where needed */ - of_id = of_match_device(intc_irqpin_dt_ids, dev); - if (of_id && of_id->data) { - const struct intc_irqpin_irlm_config *irlm_config = of_id->data; - + if (config && config->needs_irlm) { if (io[INTC_IRQPIN_REG_IRLM]) intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM, - irlm_config->irlm_bit, - 1, 1); + config->irlm_bit, 1, 1); else dev_warn(dev, "unable to select IRLM mode\n"); } @@ -500,10 +521,10 @@ static int intc_irqpin_probe(struct platform_device *pdev) /* scan for shared interrupt lines */ ref_irq = p->irq[0].requested_irq; - p->shared_irqs = true; + p->shared_irqs = 1; for (k = 1; k < nirqs; k++) { if (ref_irq != p->irq[k].requested_irq) { - p->shared_irqs = false; + p->shared_irqs = 0; break; } } -- cgit v1.2.3 From bbbb03c1a82eea24e9b15b2f96ecb0cc882ddd2c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 3 Dec 2015 16:20:12 +0800 Subject: irqchip/sunxi-nmi: Support sun9i A80 NMI controller The A80 moves the NMI controller into the PRCM address space, and also rearranges the registers. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Reviewed-by: Hans de Goede Link: https://lkml.kernel.org/r/1449130813-22400-4-git-send-email-wens@csie.org Signed-off-by: Jason Cooper --- drivers/irqchip/irq-sunxi-nmi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c index 4ef178078e5b..0820f67cc9a7 100644 --- a/drivers/irqchip/irq-sunxi-nmi.c +++ b/drivers/irqchip/irq-sunxi-nmi.c @@ -50,6 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { .enable = 0x34, }; +static struct sunxi_sc_nmi_reg_offs sun9i_reg_offs = { + .ctrl = 0x00, + .pend = 0x08, + .enable = 0x04, +}; + static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, u32 val) { @@ -207,3 +213,10 @@ static int __init sun7i_sc_nmi_irq_init(struct device_node *node, return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); } IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); + +static int __init sun9i_nmi_irq_init(struct device_node *node, + struct device_node *parent) +{ + return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs); +} +IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init); -- cgit v1.2.3 From 8673c1d7e8f0cc69b84c1c3356d869b74385fca7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 24 Oct 2015 00:15:52 +0200 Subject: irqchip/gic: Support RealView variant setup The ARM RealView PB11MPCore reference design has some special bits in a system controller register to set up the GIC in one of three modes: legacy, new with DCC, new without DCC. The register is also used to enable FIQ. Since the platform will not boot unless this register is set up to "new with DCC" mode, we need a special quirk to be compiled-in for the RealView platforms. If we find the right compatible string on the GIC TestChip, we enable this quirk by looking up the system controller and enabling the special bits. We depend on the CONFIG_REALVIEW_DT Kconfig symbol as the old boardfile code has the same fix hardcoded, and this is only needed for the attempts to modernize the RealView code using device tree. After fixing this, the PB11MPCore boots with device tree only. Cc: Thomas Gleixner Cc: Jason Cooper Signed-off-by: Linus Walleij Signed-off-by: Marc Zyngier --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-gic-realview.c | 43 ++++++++++++++++++++++++++++++++++++++ drivers/irqchip/irq-gic.c | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 drivers/irqchip/irq-gic-realview.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 177f78f6e6d6..c3f58db11083 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o +obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o diff --git a/drivers/irqchip/irq-gic-realview.c b/drivers/irqchip/irq-gic-realview.c new file mode 100644 index 000000000000..aa46eb280a7f --- /dev/null +++ b/drivers/irqchip/irq-gic-realview.c @@ -0,0 +1,43 @@ +/* + * Special GIC quirks for the ARM RealView + * Copyright (C) 2015 Linus Walleij + */ +#include +#include +#include +#include +#include +#include + +#define REALVIEW_SYS_LOCK_OFFSET 0x20 +#define REALVIEW_PB11MP_SYS_PLD_CTRL1 0x74 +#define VERSATILE_LOCK_VAL 0xA05F +#define PLD_INTMODE_MASK BIT(22)|BIT(23)|BIT(24) +#define PLD_INTMODE_LEGACY 0x0 +#define PLD_INTMODE_NEW_DCC BIT(22) +#define PLD_INTMODE_NEW_NO_DCC BIT(23) +#define PLD_INTMODE_FIQ_ENABLE BIT(24) + +static int __init +realview_gic_of_init(struct device_node *node, struct device_node *parent) +{ + static struct regmap *map; + + /* The PB11MPCore GIC needs to be configured in the syscon */ + map = syscon_regmap_lookup_by_compatible("arm,realview-pb11mp-syscon"); + if (!IS_ERR(map)) { + /* new irq mode with no DCC */ + regmap_write(map, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); + regmap_update_bits(map, REALVIEW_PB11MP_SYS_PLD_CTRL1, + PLD_INTMODE_NEW_NO_DCC, + PLD_INTMODE_MASK); + regmap_write(map, REALVIEW_SYS_LOCK_OFFSET, 0x0000); + pr_info("TC11MP GIC: set up interrupt controller to NEW mode, no DCC\n"); + } else { + pr_err("TC11MP GIC setup: could not find syscon\n"); + return -ENXIO; + } + return gic_of_init(node, parent); +} +IRQCHIP_DECLARE(armtc11mp_gic, "arm,tc11mp-gic", realview_gic_of_init); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index abf2ffaed392..9736a1b9d7fd 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1196,7 +1196,7 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) return true; } -static int __init +int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *cpu_base; -- cgit v1.2.3 From 58b8964990dc6b59198b25337624b8518cb1dd87 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 24 Oct 2015 00:15:53 +0200 Subject: irqchip/gic: Assign irqchip dynamically Instead of having the irqchip being a static struct, make it part of the per-instance data so we can assign it a dynamic name. This has the usable side effect of displaying the GIC with an instance number as GIC0, GIC1 ... GICn in /proc/interrupts, which is helpful when debugging cascaded GICs, such as on the ARM PB11MPCore. Cc: Thomas Gleixner Cc: Jason Cooper Signed-off-by: Linus Walleij Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 9736a1b9d7fd..174990c56d27 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -69,6 +69,7 @@ union gic_base { }; struct gic_chip_data { + struct irq_chip chip; union gic_base dist_base; union gic_base cpu_base; #ifdef CONFIG_CPU_PM @@ -383,7 +384,6 @@ static void gic_handle_cascade_irq(struct irq_desc *desc) } static struct irq_chip gic_chip = { - .name = "GIC", .irq_mask = gic_mask_irq, .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, @@ -925,20 +925,15 @@ void __init gic_init_physaddr(struct device_node *node) static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - struct irq_chip *chip = &gic_chip; - - if (static_key_true(&supports_deactivate)) { - if (d->host_data == (void *)&gic_data[0]) - chip = &gic_eoimode1_chip; - } + struct gic_chip_data *gic = d->host_data; if (hw < 32) { irq_set_percpu_devid(irq); - irq_domain_set_info(d, irq, hw, chip, d->host_data, + irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, handle_percpu_devid_irq, NULL, NULL); irq_set_status_flags(irq, IRQ_NOAUTOEN); } else { - irq_domain_set_info(d, irq, hw, chip, d->host_data, + irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, handle_fasteoi_irq, NULL, NULL); irq_set_probe(irq); } @@ -1045,6 +1040,15 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic_check_cpu_features(); gic = &gic_data[gic_nr]; + + /* Initialize irq_chip */ + if (static_key_true(&supports_deactivate) && gic_nr == 0) { + gic->chip = gic_eoimode1_chip; + } else { + gic->chip = gic_chip; + gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); + } + #ifdef CONFIG_GIC_NON_BANKED if (percpu_offset) { /* Frankein-GIC without banked registers... */ unsigned int cpu; -- cgit v1.2.3 From 86d14c72b7837589a4381b3bc2e117e7d842a92a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 16 Dec 2015 11:03:24 +0000 Subject: irqchip/gic-v2m: Fix of_node refcount on error On the error path, the v2m drivers drops the refcount on the parent node instead of doing it on the node that generated the error. Humph... Reported-by: Thomas Petazzoni Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 87f8d104acab..ee1e553ee7a6 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -389,7 +389,7 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) ret = gicv2m_init_one(child, parent); if (ret) { - of_node_put(node); + of_node_put(child); break; } } -- cgit v1.2.3 From 327ebe1f3a9b7e20e298b39d0cff627169a28012 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 16 Dec 2015 14:11:22 +0000 Subject: irqchip/gic: Make interrupt ID 1020 invalid The GIC has no such thing as interrupt 1020: the last valid ID is 1019, and the range 1020-1023 is reserved - 1023 indicating that no interrupt is pending. So let's make sure we don't try to handle this ID. This bug has been in since the initial GIC code was introduced in 8ad68bbf7a06 ("[ARM] Add support for ARM RealView board"). Reported-by: Eric Auger Cc: Catalin Marinas Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 174990c56d27..7f5f91984c1b 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -337,7 +337,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); irqnr = irqstat & GICC_IAR_INT_ID_MASK; - if (likely(irqnr > 15 && irqnr < 1021)) { + if (likely(irqnr > 15 && irqnr < 1020)) { if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); handle_domain_irq(gic->domain, irqnr, regs); -- cgit v1.2.3 From a27d21e03eb14a63dae12467a7655be3334ac80c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 18 Dec 2015 10:44:53 +0100 Subject: irqchip/gic: Kconfig the number of instances There is currently a hack in the GIC driver making it possible to pass the number of GIC instances from the platform-specific include files and thus override the variable MAX_GIC_NR. With multiplatform deployments, this will not work as we need to get rid of the platform-specific include files. It turns out that this feature is only used by the RealView platform which has a cascaded GIC. So move the configuration to Kconfig and bump to 2 instances if we're building for the RealView. The include file hacks can then be removed. Tested on the ARM PB11MPCore with its cascaded GIC. Suggested-by: Arnd Bergmann Signed-off-by: Linus Walleij Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 5 +++++ drivers/irqchip/irq-gic.c | 30 ++++++++++-------------------- 2 files changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 4d7294e5d982..bf29a8b2b7c5 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -8,6 +8,11 @@ config ARM_GIC select IRQ_DOMAIN_HIERARCHY select MULTI_IRQ_HANDLER +config ARM_GIC_MAX_NR + int + default 2 if ARCH_REALVIEW + default 1 + config ARM_GIC_V2M bool depends on ARM_GIC diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 7f5f91984c1b..fcbe0b90870d 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -100,11 +100,7 @@ static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; -#ifndef MAX_GIC_NR -#define MAX_GIC_NR 1 -#endif - -static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; +static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly; #ifdef CONFIG_GIC_NON_BANKED static void __iomem *gic_get_percpu_base(union gic_base *base) @@ -417,8 +413,7 @@ static struct irq_chip gic_eoimode1_chip = { void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) { - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, &gic_data[gic_nr]); } @@ -524,7 +519,7 @@ int gic_cpu_if_down(unsigned int gic_nr) void __iomem *cpu_base; u32 val = 0; - if (gic_nr >= MAX_GIC_NR) + if (gic_nr >= CONFIG_ARM_GIC_MAX_NR) return -EINVAL; cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); @@ -548,8 +543,7 @@ static void gic_dist_save(unsigned int gic_nr) void __iomem *dist_base; int i; - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); gic_irqs = gic_data[gic_nr].gic_irqs; dist_base = gic_data_dist_base(&gic_data[gic_nr]); @@ -587,8 +581,7 @@ static void gic_dist_restore(unsigned int gic_nr) unsigned int i; void __iomem *dist_base; - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); gic_irqs = gic_data[gic_nr].gic_irqs; dist_base = gic_data_dist_base(&gic_data[gic_nr]); @@ -634,8 +627,7 @@ static void gic_cpu_save(unsigned int gic_nr) void __iomem *dist_base; void __iomem *cpu_base; - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); dist_base = gic_data_dist_base(&gic_data[gic_nr]); cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); @@ -664,8 +656,7 @@ static void gic_cpu_restore(unsigned int gic_nr) void __iomem *dist_base; void __iomem *cpu_base; - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); dist_base = gic_data_dist_base(&gic_data[gic_nr]); cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); @@ -703,7 +694,7 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) { int i; - for (i = 0; i < MAX_GIC_NR; i++) { + for (i = 0; i < CONFIG_ARM_GIC_MAX_NR; i++) { #ifdef CONFIG_GIC_NON_BANKED /* Skip over unused GICs */ if (!gic_data[i].get_base) @@ -835,8 +826,7 @@ void gic_migrate_target(unsigned int new_cpu_id) int i, ror_val, cpu = smp_processor_id(); u32 val, cur_target_mask, active_mask; - if (gic_nr >= MAX_GIC_NR) - BUG(); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); dist_base = gic_data_dist_base(&gic_data[gic_nr]); if (!dist_base) @@ -1035,7 +1025,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, struct gic_chip_data *gic; int gic_irqs, irq_base, i; - BUG_ON(gic_nr >= MAX_GIC_NR); + BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); gic_check_cpu_features(); -- cgit v1.2.3 From 717c3dbc118ecbbd5dab06c7e02dac68d3f62e1d Mon Sep 17 00:00:00 2001 From: Ma Jun Date: Thu, 17 Dec 2015 19:56:35 +0800 Subject: irqchip/mgigen: Add platform device driver for mbigen device Mbigen means Message Based Interrupt Generator(MBIGEN). Its a kind of interrupt controller that collects the interrupts from external devices and generate msi interrupt. Mbigen is applied to reduce the number of wire connected interrupts. As the peripherals increasing, the interrupts lines needed is increasing much, especially on the Arm64 server SOC. Therefore, the interrupt pin in GIC is not enough to cover so many peripherals. Mbigen is designed to fix this problem. Mbigen chip locates in ITS or outside of ITS. Mbigen chip hardware structure shows as below: mbigen chip |---------------------|-------------------| mgn_node0 mgn_node1 mgn_node2 | |-------| |-------|------| dev1 dev1 dev2 dev1 dev3 dev4 Each mbigen chip contains several mbigen nodes. External devices can connect to mbigen node through wire connecting way. Because a mbigen node only can support 128 interrupt maximum, depends on the interrupt lines number of devices, a device can connects to one more mbigen nodes. Also, several different devices can connect to a same mbigen node. When devices triggered interrupt,mbigen chip detects and collects the interrupts and generates the MBI interrupts by writing the ITS Translator register. To simplify mbigen driver,I used a new conception--mbigen device. Each mbigen device is initialized as a platform device. Mbigen device presents the parts(register, pin definition etc.) in mbigen chip corresponding to a peripheral device. So from software view, the structure likes below mbigen chip |---------------------|-----------------| mbigen device1 mbigen device2 mbigen device3 | | | dev1 dev2 dev3 Reviewed-by: Marc Zyngier Signed-off-by: Ma Jun Signed-off-by: Marc Zyngier --- drivers/irqchip/Kconfig | 8 +++++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-mbigen.c | 78 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 drivers/irqchip/irq-mbigen.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 4d7294e5d982..b205e158e864 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -27,6 +27,14 @@ config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN +config HISILICON_IRQ_MBIGEN + bool "Support mbigen interrupt controller" + default n + depends on ARM_GIC_V3 && ARM_GIC_V3_ITS && GENERIC_MSI_IRQ_DOMAIN + help + Enable the mbigen interrupt controller used on + Hisilicon platform. + config ARM_NVIC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 177f78f6e6d6..cd76b11682eb 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o +obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c new file mode 100644 index 000000000000..9f036c22f9b2 --- /dev/null +++ b/drivers/irqchip/irq-mbigen.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Hisilicon Limited, All Rights Reserved. + * Author: Jun Ma + * Author: Yun Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +/** + * struct mbigen_device - holds the information of mbigen device. + * + * @pdev: pointer to the platform device structure of mbigen chip. + * @base: mapped address of this mbigen chip. + */ +struct mbigen_device { + struct platform_device *pdev; + void __iomem *base; +}; + +static int mbigen_device_probe(struct platform_device *pdev) +{ + struct mbigen_device *mgn_chip; + struct resource *res; + + mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL); + if (!mgn_chip) + return -ENOMEM; + + mgn_chip->pdev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mgn_chip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mgn_chip->base)) + return PTR_ERR(mgn_chip->base); + + platform_set_drvdata(pdev, mgn_chip); + + return 0; +} + +static const struct of_device_id mbigen_of_match[] = { + { .compatible = "hisilicon,mbigen-v2" }, + { /* END */ } +}; +MODULE_DEVICE_TABLE(of, mbigen_of_match); + +static struct platform_driver mbigen_platform_driver = { + .driver = { + .name = "Hisilicon MBIGEN-V2", + .owner = THIS_MODULE, + .of_match_table = mbigen_of_match, + }, + .probe = mbigen_device_probe, +}; + +module_platform_driver(mbigen_platform_driver); + +MODULE_AUTHOR("Jun Ma "); +MODULE_AUTHOR("Yun Wu "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hisilicon MBI Generator driver"); -- cgit v1.2.3 From 9650c60ebfec05fcf74d9b3eb97837501f2bb541 Mon Sep 17 00:00:00 2001 From: Ma Jun Date: Thu, 17 Dec 2015 19:56:36 +0800 Subject: irqchip/mbigen: Create irq domain for each mbigen device For peripheral devices which connect to mbigen,mbigen is a interrupt controller. So, we create irq domain for each mbigen device and add mbigen irq domain into irq hierarchy structure. Signed-off-by: Ma Jun Reviewed-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-mbigen.c | 138 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 9f036c22f9b2..2ab1c2d7232c 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -16,13 +16,39 @@ * along with this program. If not, see . */ +#include +#include #include +#include #include #include #include #include #include +/* Interrupt numbers per mbigen node supported */ +#define IRQS_PER_MBIGEN_NODE 128 + +/* 64 irqs (Pin0-pin63) are reserved for each mbigen chip */ +#define RESERVED_IRQ_PER_MBIGEN_CHIP 64 + +/* The maximum IRQ pin number of mbigen chip(start from 0) */ +#define MAXIMUM_IRQ_PIN_NUM 1407 + +/** + * In mbigen vector register + * bit[21:12]: event id value + * bit[11:0]: device id + */ +#define IRQ_EVENT_ID_SHIFT 12 +#define IRQ_EVENT_ID_MASK 0x3ff + +/* register range of each mbigen node */ +#define MBIGEN_NODE_OFFSET 0x1000 + +/* offset of vector register in mbigen node */ +#define REG_MBIGEN_VEC_OFFSET 0x200 + /** * struct mbigen_device - holds the information of mbigen device. * @@ -34,10 +60,107 @@ struct mbigen_device { void __iomem *base; }; +static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) +{ + unsigned int nid, pin; + + hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; + nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; + pin = hwirq % IRQS_PER_MBIGEN_NODE; + + return pin * 4 + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_VEC_OFFSET; +} + +static struct irq_chip mbigen_irq_chip = { + .name = "mbigen-v2", +}; + +static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg) +{ + struct irq_data *d = irq_get_irq_data(desc->irq); + void __iomem *base = d->chip_data; + u32 val; + + base += get_mbigen_vec_reg(d->hwirq); + val = readl_relaxed(base); + + val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT); + val |= (msg->data << IRQ_EVENT_ID_SHIFT); + + /* The address of doorbell is encoded in mbigen register by default + * So,we don't need to program the doorbell address at here + */ + writel_relaxed(val, base); +} + +static int mbigen_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 2) + return -EINVAL; + + if ((fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM) || + (fwspec->param[0] < RESERVED_IRQ_PER_MBIGEN_CHIP)) + return -EINVAL; + else + *hwirq = fwspec->param[0]; + + /* If there is no valid irq type, just use the default type */ + if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) || + (fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH)) + *type = fwspec->param[1]; + else + return -EINVAL; + + return 0; + } + return -EINVAL; +} + +static int mbigen_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, + void *args) +{ + struct irq_fwspec *fwspec = args; + irq_hw_number_t hwirq; + unsigned int type; + struct mbigen_device *mgn_chip; + int i, err; + + err = mbigen_domain_translate(domain, fwspec, &hwirq, &type); + if (err) + return err; + + err = platform_msi_domain_alloc(domain, virq, nr_irqs); + if (err) + return err; + + mgn_chip = platform_msi_get_host_data(domain); + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mbigen_irq_chip, mgn_chip->base); + + return 0; +} + +static struct irq_domain_ops mbigen_domain_ops = { + .translate = mbigen_domain_translate, + .alloc = mbigen_irq_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + static int mbigen_device_probe(struct platform_device *pdev) { struct mbigen_device *mgn_chip; struct resource *res; + struct irq_domain *domain; + u32 num_pins; mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL); if (!mgn_chip) @@ -50,8 +173,23 @@ static int mbigen_device_probe(struct platform_device *pdev) if (IS_ERR(mgn_chip->base)) return PTR_ERR(mgn_chip->base); + if (of_property_read_u32(pdev->dev.of_node, "num-pins", &num_pins) < 0) { + dev_err(&pdev->dev, "No num-pins property\n"); + return -EINVAL; + } + + domain = platform_msi_create_device_domain(&pdev->dev, num_pins, + mbigen_write_msg, + &mbigen_domain_ops, + mgn_chip); + + if (!domain) + return -ENOMEM; + platform_set_drvdata(pdev, mgn_chip); + dev_info(&pdev->dev, "Allocated %d MSIs\n", num_pins); + return 0; } -- cgit v1.2.3 From a6c2f87b8820e956ea0f731dcf0e45949bb37a8b Mon Sep 17 00:00:00 2001 From: Ma Jun Date: Thu, 17 Dec 2015 19:56:37 +0800 Subject: irqchip/mbigen: Implement the mbigen irq chip operation functions Add the interrupt controller chip operation functions of mbigen chip. Signed-off-by: Ma Jun Reviewed-by: Marc Zyngier Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-mbigen.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 2ab1c2d7232c..4dd3eb8a40b3 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -49,6 +49,20 @@ /* offset of vector register in mbigen node */ #define REG_MBIGEN_VEC_OFFSET 0x200 +/** + * offset of clear register in mbigen node + * This register is used to clear the status + * of interrupt + */ +#define REG_MBIGEN_CLEAR_OFFSET 0xa000 + +/** + * offset of interrupt type register + * This register is used to configure interrupt + * trigger type + */ +#define REG_MBIGEN_TYPE_OFFSET 0x0 + /** * struct mbigen_device - holds the information of mbigen device. * @@ -72,8 +86,75 @@ static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) + REG_MBIGEN_VEC_OFFSET; } +static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, + u32 *mask, u32 *addr) +{ + unsigned int nid, irq_ofst, ofst; + + hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; + nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; + irq_ofst = hwirq % IRQS_PER_MBIGEN_NODE; + + *mask = 1 << (irq_ofst % 32); + ofst = irq_ofst / 32 * 4; + + *addr = ofst + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_TYPE_OFFSET; +} + +static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq, + u32 *mask, u32 *addr) +{ + unsigned int ofst; + + hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; + ofst = hwirq / 32 * 4; + + *mask = 1 << (hwirq % 32); + *addr = ofst + REG_MBIGEN_CLEAR_OFFSET; +} + +static void mbigen_eoi_irq(struct irq_data *data) +{ + void __iomem *base = data->chip_data; + u32 mask, addr; + + get_mbigen_clear_reg(data->hwirq, &mask, &addr); + + writel_relaxed(mask, base + addr); + + irq_chip_eoi_parent(data); +} + +static int mbigen_set_type(struct irq_data *data, unsigned int type) +{ + void __iomem *base = data->chip_data; + u32 mask, addr, val; + + if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + get_mbigen_type_reg(data->hwirq, &mask, &addr); + + val = readl_relaxed(base + addr); + + if (type == IRQ_TYPE_LEVEL_HIGH) + val |= mask; + else + val &= ~mask; + + writel_relaxed(val, base + addr); + + return 0; +} + static struct irq_chip mbigen_irq_chip = { .name = "mbigen-v2", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = mbigen_eoi_irq, + .irq_set_type = mbigen_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, }; static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg) -- cgit v1.2.3 From 75aba7b0e9ac416ca53c0c97680b8e9aedf09284 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:28 -0800 Subject: irqdomain: Introduce is_fwnode_irqchip helper Since there will be several places checking if fwnode.type is equal FWNODE_IRQCHIP, this patch adds a convenient function for this purpose. Acked-by: Marc Zyngier Signed-off-by: Suravee Suthikulpanit Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index abf2ffaed392..fcd327f49e8e 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -972,7 +972,7 @@ static int gic_irq_domain_translate(struct irq_domain *d, return 0; } - if (fwspec->fwnode->type == FWNODE_IRQCHIP) { + if (is_fwnode_irqchip(fwspec->fwnode)) { if(fwspec->param_count != 2) return -EINVAL; -- cgit v1.2.3 From 4266ab1a8ff5715e48b2e89046305864650ce025 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:29 -0800 Subject: irqchip/gic-v2m: Refactor to prepare for ACPI support This patch replaces the struct device_node with struct fwnode_handle since this structure is common between DT and ACPI. It also refactors gicv2m_init_one() to prepare for ACPI support. The only functional change is removing the node name from pr_info. Reviewed-by: Marc Zyngier Signed-off-by: Suravee Suthikulpanit Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 53 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 21 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 87f8d104acab..779c3906a22e 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -55,7 +55,7 @@ static DEFINE_SPINLOCK(v2m_lock); struct v2m_data { struct list_head entry; - struct device_node *node; + struct fwnode_handle *fwnode; struct resource res; /* GICv2m resource */ void __iomem *base; /* GICv2m virt address */ u32 spi_start; /* The SPI number that MSIs start */ @@ -254,7 +254,7 @@ static void gicv2m_teardown(void) list_del(&v2m->entry); kfree(v2m->bm); iounmap(v2m->base); - of_node_put(v2m->node); + of_node_put(to_of_node(v2m->fwnode)); kfree(v2m); } } @@ -268,7 +268,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) if (!v2m) return 0; - inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node), + inner_domain = irq_domain_create_tree(v2m->fwnode, &gicv2m_domain_ops, v2m); if (!inner_domain) { pr_err("Failed to create GICv2m domain\n"); @@ -277,10 +277,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) inner_domain->bus_token = DOMAIN_BUS_NEXUS; inner_domain->parent = parent; - pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node), + pci_domain = pci_msi_create_irq_domain(v2m->fwnode, &gicv2m_msi_domain_info, inner_domain); - plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node), + plat_domain = platform_msi_create_irq_domain(v2m->fwnode, &gicv2m_pmsi_domain_info, inner_domain); if (!pci_domain || !plat_domain) { @@ -296,8 +296,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) return 0; } -static int __init gicv2m_init_one(struct device_node *node, - struct irq_domain *parent) +static int __init gicv2m_init_one(struct fwnode_handle *fwnode, + u32 spi_start, u32 nr_spis, + struct resource *res) { int ret; struct v2m_data *v2m; @@ -309,13 +310,9 @@ static int __init gicv2m_init_one(struct device_node *node, } INIT_LIST_HEAD(&v2m->entry); - v2m->node = node; + v2m->fwnode = fwnode; - ret = of_address_to_resource(node, 0, &v2m->res); - if (ret) { - pr_err("Failed to allocate v2m resource.\n"); - goto err_free_v2m; - } + memcpy(&v2m->res, res, sizeof(struct resource)); v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); if (!v2m->base) { @@ -324,10 +321,9 @@ static int __init gicv2m_init_one(struct device_node *node, goto err_free_v2m; } - if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && - !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { - pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", - v2m->spi_start, v2m->nr_spis); + if (spi_start && nr_spis) { + v2m->spi_start = spi_start; + v2m->nr_spis = nr_spis; } else { u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); @@ -359,10 +355,10 @@ static int __init gicv2m_init_one(struct device_node *node, } list_add_tail(&v2m->entry, &v2m_nodes); - pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, - (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, - v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); + pr_info("range[%#lx:%#lx], SPI[%d:%d]\n", + (unsigned long)res->start, (unsigned long)res->end, + v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); return 0; err_iounmap: @@ -384,10 +380,25 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) for (child = of_find_matching_node(node, gicv2m_device_id); child; child = of_find_matching_node(child, gicv2m_device_id)) { + u32 spi_start = 0, nr_spis = 0; + struct resource res; + if (!of_find_property(child, "msi-controller", NULL)) continue; - ret = gicv2m_init_one(child, parent); + ret = of_address_to_resource(child, 0, &res); + if (ret) { + pr_err("Failed to allocate v2m resource.\n"); + break; + } + + if (!of_property_read_u32(child, "arm,msi-base-spi", + &spi_start) && + !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis)) + pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", + spi_start, nr_spis); + + ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res); if (ret) { of_node_put(node); break; -- cgit v1.2.3 From 0644b3daca28dcb320373ae20069c269c9386304 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:30 -0800 Subject: irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support This patch introduces gicv2m_acpi_init(), which uses information in MADT GIC MSI frames structure to initialize GICv2m driver. It also exposes gicv2m_init() function, which simplifies callers to a single GICv2m init function. Reviewed-by: Marc Zyngier Tested-by: Duc Dang Acked-by: Rafael J. Wysocki Signed-off-by: Suravee Suthikulpanit Signed-off-by: Hanjun Guo Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 110 +++++++++++++++++++++++++++++++++++++++++- drivers/irqchip/irq-gic.c | 6 ++- 2 files changed, 114 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 779c3906a22e..7e2975df4473 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -15,9 +15,11 @@ #define pr_fmt(fmt) "GICv2m: " fmt +#include #include #include #include +#include #include #include #include @@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, fwspec.param[0] = 0; fwspec.param[1] = hwirq - 32; fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + } else if (is_fwnode_irqchip(domain->parent->fwnode)) { + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = hwirq; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; } else { return -EINVAL; } @@ -255,6 +262,8 @@ static void gicv2m_teardown(void) kfree(v2m->bm); iounmap(v2m->base); of_node_put(to_of_node(v2m->fwnode)); + if (is_fwnode_irqchip(v2m->fwnode)) + irq_domain_free_fwnode(v2m->fwnode); kfree(v2m); } } @@ -373,9 +382,11 @@ static struct of_device_id gicv2m_device_id[] = { {}, }; -int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) +static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, + struct irq_domain *parent) { int ret = 0; + struct device_node *node = to_of_node(parent_handle); struct device_node *child; for (child = of_find_matching_node(node, gicv2m_device_id); child; @@ -411,3 +422,100 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) gicv2m_teardown(); return ret; } + +#ifdef CONFIG_ACPI +static int acpi_num_msi; + +static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) +{ + struct v2m_data *data; + + if (WARN_ON(acpi_num_msi <= 0)) + return NULL; + + /* We only return the fwnode of the first MSI frame. */ + data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry); + if (!data) + return NULL; + + return data->fwnode; +} + +static int __init +acpi_parse_madt_msi(struct acpi_subtable_header *header, + const unsigned long end) +{ + int ret; + struct resource res; + u32 spi_start = 0, nr_spis = 0; + struct acpi_madt_generic_msi_frame *m; + struct fwnode_handle *fwnode; + + m = (struct acpi_madt_generic_msi_frame *)header; + if (BAD_MADT_ENTRY(m, end)) + return -EINVAL; + + res.start = m->base_address; + res.end = m->base_address + SZ_4K; + + if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { + spi_start = m->spi_base; + nr_spis = m->spi_count; + + pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n", + spi_start, nr_spis); + } + + fwnode = irq_domain_alloc_fwnode((void *)m->base_address); + if (!fwnode) { + pr_err("Unable to allocate GICv2m domain token\n"); + return -EINVAL; + } + + ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); + if (ret) + irq_domain_free_fwnode(fwnode); + + return ret; +} + +static int __init gicv2m_acpi_init(struct irq_domain *parent) +{ + int ret; + + if (acpi_num_msi > 0) + return 0; + + acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, + acpi_parse_madt_msi, 0); + + if (acpi_num_msi <= 0) + goto err_out; + + ret = gicv2m_allocate_domains(parent); + if (ret) + goto err_out; + + pci_msi_register_fwnode_provider(&gicv2m_get_fwnode); + + return 0; + +err_out: + gicv2m_teardown(); + return -EINVAL; +} +#else /* CONFIG_ACPI */ +static int __init gicv2m_acpi_init(struct irq_domain *parent) +{ + return -EINVAL; +} +#endif /* CONFIG_ACPI */ + +int __init gicv2m_init(struct fwnode_handle *parent_handle, + struct irq_domain *parent) +{ + if (is_of_node(parent_handle)) + return gicv2m_of_init(parent_handle, parent); + + return gicv2m_acpi_init(parent); +} diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index fcd327f49e8e..644e8bbe130c 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1234,7 +1234,7 @@ gic_of_init(struct device_node *node, struct device_node *parent) } if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) - gicv2m_of_init(node, gic_data[gic_cnt].domain); + gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain); gic_cnt++; return 0; @@ -1359,6 +1359,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); + + if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) + gicv2m_init(NULL, gic_data[0].domain); + return 0; } IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, -- cgit v1.2.3 From 401667bb8a2e0825090c5e6c15b899c1c36a4773 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 26 Dec 2015 13:47:21 -0800 Subject: irqchip/bcm2836: Fix initialization of the LOCAL_IRQ_CNT timers The irqchip's register area includes the the setup for the timer's scaling factors, and for the platform we want a fixed configuration of these registers. Signed-off-by: Eric Anholt Cc: linux-arm-kernel@lists.infradead.org Cc: Stephen Warren Cc: Lee Jones Cc: Florian Fainelli Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-rpi-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1451166444-11044-2-git-send-email-eric@anholt.net Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm2836.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index f68708281fcf..6ec125ef3607 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -21,6 +21,9 @@ #include #include +#define LOCAL_CONTROL 0x000 +#define LOCAL_PRESCALER 0x008 + /* * The low 2 bits identify the CPU that the GPU IRQ goes to, and the * next 2 bits identify the CPU that the GPU FIQ goes to. @@ -237,6 +240,27 @@ bcm2836_arm_irqchip_smp_init(void) #endif } +/* + * The LOCAL_IRQ_CNT* timer firings are based off of the external + * oscillator with some scaling. The firmware sets up CNTFRQ to + * report 19.2Mhz, but doesn't set up the scaling registers. + */ +static void bcm2835_init_local_timer_frequency(void) +{ + /* + * Set the timer to source from the 19.2Mhz crystal clock (bit + * 8 unset), and only increment by 1 instead of 2 (bit 9 + * unset). + */ + writel(0, intc.base + LOCAL_CONTROL); + + /* + * Set the timer prescaler to 1:1 (timer freq = input freq * + * 2**31 / prescaler) + */ + writel(0x80000000, intc.base + LOCAL_PRESCALER); +} + static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, struct device_node *parent) { @@ -246,6 +270,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, node->full_name); } + bcm2835_init_local_timer_frequency(); + intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, &bcm2836_arm_irqchip_intc_ops, NULL); -- cgit v1.2.3 From 41f4988cc287e5f836d3f6620c9f900bc9b560e9 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 26 Dec 2015 13:47:22 -0800 Subject: irqchip/bcm2836: Add SMP support for the 2836 The firmware sets the secondaries spinning waiting for a non-NULL value to show up in the last IPI mailbox. The original SMP port from the downstream tree was done by Andrea, and Eric cleaned it up/rewrote it a few times from there. Signed-off-by: Andrea Merello Signed-off-by: Eric Anholt Cc: linux-arm-kernel@lists.infradead.org Cc: Stephen Warren Cc: Lee Jones Cc: Florian Fainelli Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-rpi-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1451166444-11044-3-git-send-email-eric@anholt.net Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm2836.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index 6ec125ef3607..59ac40c39551 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -53,14 +53,16 @@ /* Same status bits as above, but for FIQ. */ #define LOCAL_FIQ_PENDING0 0x070 /* - * Mailbox0 write-to-set bits. There are 16 mailboxes, 4 per CPU, and + * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and * these bits are organized by mailbox number and then CPU number. We * use mailbox 0 for IPIs. The mailbox's interrupt is raised while * any bit is set. */ #define LOCAL_MAILBOX0_SET0 0x080 -/* Mailbox0 write-to-clear bits. */ +#define LOCAL_MAILBOX3_SET0 0x08c +/* Mailbox write-to-clear bits. */ #define LOCAL_MAILBOX0_CLR0 0x0c0 +#define LOCAL_MAILBOX3_CLR0 0x0cc #define LOCAL_IRQ_CNTPSIRQ 0 #define LOCAL_IRQ_CNTPNSIRQ 1 @@ -220,6 +222,24 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = { .notifier_call = bcm2836_arm_irqchip_cpu_notify, .priority = 100, }; + +int __init bcm2836_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + unsigned long secondary_startup_phys = + (unsigned long)virt_to_phys((void *)secondary_startup); + + dsb(); + writel(secondary_startup_phys, + intc.base + LOCAL_MAILBOX3_SET0 + 16 * cpu); + + return 0; +} + +static const struct smp_operations bcm2836_smp_ops __initconst = { + .smp_boot_secondary = bcm2836_smp_boot_secondary, +}; + #endif static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { @@ -237,6 +257,7 @@ bcm2836_arm_irqchip_smp_init(void) register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); + smp_set_ops(&bcm2836_smp_ops); #endif } -- cgit v1.2.3 From a51744ddcc62925ec4d3d3d3a8a13bdd2033af59 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 26 Dec 2015 13:47:23 -0800 Subject: irqchip/bcm2836: Tolerate IRQs while no flag is set in ISR On my RPi2 I got a lot of: unexpected IRQ trap at vector 00 This happens because bcm2836_arm_irqchip_handle_irq() is sometimes invoked even if the ISR is clear, and this case is not handled. This patch explicitly handle this case, fixing the kernel complaints about the bad IRQ lookup. Signed-off-by: Andrea Merello Reviewed-by: Eric Anholt Signed-off-by: Eric Anholt Cc: linux-arm-kernel@lists.infradead.org Cc: Stephen Warren Cc: Lee Jones Cc: Florian Fainelli Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-rpi-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1451166444-11044-4-git-send-email-eric@anholt.net Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm2836.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index 59ac40c39551..bb8f234b6b73 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -177,7 +177,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) writel(1 << ipi, mailbox0); handle_IPI(ipi, regs); #endif - } else { + } else if (stat) { u32 hwirq = ffs(stat) - 1; handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); -- cgit v1.2.3 From 64103f061573e3d7670ba295b07919fb8fc7594c Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 26 Dec 2015 13:47:24 -0800 Subject: irqchip/bcm2836: Make code more readable Avoid using hardcoded magics. We have a #define for this number. No functional changes. Signed-off-by: Andrea Merello Reviewed-by: Eric Anholt Signed-off-by: Eric Anholt Cc: linux-arm-kernel@lists.infradead.org Cc: Stephen Warren Cc: Lee Jones Cc: Florian Fainelli Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-rpi-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1451166444-11044-5-git-send-email-eric@anholt.net Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm2836.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index bb8f234b6b73..963065a0d774 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -167,7 +167,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) u32 stat; stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); - if (stat & 0x10) { + if (stat & BIT(LOCAL_IRQ_MAILBOX0)) { #ifdef CONFIG_SMP void __iomem *mailbox0 = (intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu); -- cgit v1.2.3 From 5a1ff480f4ec40ace313c16b0543c7c6af09e227 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Tue, 22 Dec 2015 16:24:23 -0800 Subject: irqchip/gicv2m: Miscellaneous fixes for v2m resources and SPI ranges This patch contain fixes for v2m resources and SPI ranges: * Fix off-by-one error when set up v2m resource end range in gicv2m_acpi_init(). * Fix the off-by-one print error for SPI range. * Use %pR to properly print resource range information. Both ACPI and DT should now print: GICv2m: range[mem 0xe1180000-0xe1180fff], SPI[64:319] Suggested-by: Bjorn Helgaas Signed-off-by: Suravee Suthikulpanit Cc: Lorenzo Pieralisi Cc: Will Deacon Cc: Catalin Marinas Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Link: http://lkml.kernel.org/r/1450830263-28914-1-git-send-email-Suravee.Suthikulpanit@amd.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-gic-v2m.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index e2d2d027637d..c779f83e511d 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -365,9 +365,8 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, list_add_tail(&v2m->entry, &v2m_nodes); - pr_info("range[%#lx:%#lx], SPI[%d:%d]\n", - (unsigned long)res->start, (unsigned long)res->end, - v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); + pr_info("range%pR, SPI[%d:%d]\n", res, + v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1)); return 0; err_iounmap: @@ -456,7 +455,8 @@ acpi_parse_madt_msi(struct acpi_subtable_header *header, return -EINVAL; res.start = m->base_address; - res.end = m->base_address + SZ_4K; + res.end = m->base_address + SZ_4K - 1; + res.flags = IORESOURCE_MEM; if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { spi_start = m->spi_base; -- cgit v1.2.3 From d01f8633d52e4dac5ee598b87d49fd23346ccfd6 Mon Sep 17 00:00:00 2001 From: Damien Riegel Date: Mon, 21 Dec 2015 15:11:23 -0500 Subject: irqchip/ts4800: Add TS-4800 interrupt controller This commit adds support for the TS-4800 interrupt controller. This controller is instantiated in a companion FPGA, and multiplex interrupts for other FPGA IPs. As this component is external to the SoC, the SoC might need to reserve pins, so this controller is implemented as a platform driver and doesn't use the IRQCHIP_DECLARE construct. Signed-off-by: Damien Riegel Cc: Jason Cooper Cc: Marc Zyngier Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: kernel@savoirfairelinux.com Link: http://lkml.kernel.org/r/1450728683-31416-2-git-send-email-damien.riegel@savoirfairelinux.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/Kconfig | 6 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ts4800.c | 163 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/irqchip/irq-ts4800.c (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index b5f51337ba74..11fc2a27fa2e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -151,6 +151,12 @@ config TB10X_IRQC select IRQ_DOMAIN select GENERIC_IRQ_CHIP +config TS4800_IRQ + tristate "TS-4800 IRQ controller" + select IRQ_DOMAIN + help + Support for the TS-4800 FPGA IRQ controller + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 83d1fce13e86..d4c2e4ebc308 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o obj-$(CONFIG_ST_IRQCHIP) += irq-st.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o +obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c new file mode 100644 index 000000000000..4192bdcd2734 --- /dev/null +++ b/drivers/irqchip/irq-ts4800.c @@ -0,0 +1,163 @@ +/* + * Multiplexed-IRQs driver for TS-4800's FPGA + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_MASK 0x4 +#define IRQ_STATUS 0x8 + +struct ts4800_irq_data { + void __iomem *base; + struct irq_domain *domain; + struct irq_chip irq_chip; +}; + +static void ts4800_irq_mask(struct irq_data *d) +{ + struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); + u16 reg = readw(data->base + IRQ_MASK); + u16 mask = 1 << d->hwirq; + + writew(reg | mask, data->base + IRQ_MASK); +} + +static void ts4800_irq_unmask(struct irq_data *d) +{ + struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); + u16 reg = readw(data->base + IRQ_MASK); + u16 mask = 1 << d->hwirq; + + writew(reg & ~mask, data->base + IRQ_MASK); +} + +static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct ts4800_irq_data *data = d->host_data; + + irq_set_chip_and_handler(irq, &data->irq_chip, handle_simple_irq); + irq_set_chip_data(irq, data); + irq_set_noprobe(irq); + + return 0; +} + +struct irq_domain_ops ts4800_ic_ops = { + .map = ts4800_irqdomain_map, + .xlate = irq_domain_xlate_onecell, +}; + +static void ts4800_ic_chained_handle_irq(struct irq_desc *desc) +{ + struct ts4800_irq_data *data = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u16 status = readw(data->base + IRQ_STATUS); + + chained_irq_enter(chip, desc); + + if (unlikely(status == 0)) { + handle_bad_irq(desc); + goto out; + } + + do { + unsigned int bit = __ffs(status); + int irq = irq_find_mapping(data->domain, bit); + + status &= ~(1 << bit); + generic_handle_irq(irq); + } while (status); + +out: + chained_irq_exit(chip, desc); +} + +static int ts4800_ic_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct ts4800_irq_data *data; + struct irq_chip *irq_chip; + struct resource *res; + int parent_irq; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + writew(0xFFFF, data->base + IRQ_MASK); + + parent_irq = irq_of_parse_and_map(node, 0); + if (!parent_irq) { + dev_err(&pdev->dev, "failed to get parent IRQ\n"); + return -EINVAL; + } + + irq_chip = &data->irq_chip; + irq_chip->name = dev_name(&pdev->dev); + irq_chip->irq_mask = ts4800_irq_mask; + irq_chip->irq_unmask = ts4800_irq_unmask; + + data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data); + if (!data->domain) { + dev_err(&pdev->dev, "cannot add IRQ domain\n"); + return -ENOMEM; + } + + irq_set_chained_handler_and_data(parent_irq, + ts4800_ic_chained_handle_irq, data); + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int ts4800_ic_remove(struct platform_device *pdev) +{ + struct ts4800_irq_data *data = platform_get_drvdata(pdev); + + irq_domain_remove(data->domain); + + return 0; +} + +static const struct of_device_id ts4800_ic_of_match[] = { + { .compatible = "technologic,ts4800-irqc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ts4800_ic_of_match); + +static struct platform_driver ts4800_ic_driver = { + .probe = ts4800_ic_probe, + .remove = ts4800_ic_remove, + .driver = { + .name = "ts4800-irqc", + .of_match_table = ts4800_ic_of_match, + }, +}; +module_platform_driver(ts4800_ic_driver); + +MODULE_AUTHOR("Damien Riegel "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_irqc"); -- cgit v1.2.3 From fee48dfcd76b21b9a7117c3014e1345697ff08ec Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Mon, 21 Dec 2015 17:53:14 +0900 Subject: irqchip/omap-intc: Remove duplicate setup for IRQ chip type handler Some OMAP interrupt controllers use generic level detection, so handle_level_irq() is used as the chip type handler. Allocated IRQ chip type handler doesn't need to set it again because irq_alloc_domain_generic_chips() has already registered it. Tested with BeagleBoneBlack Rev C. Signed-off-by: Milo Kim Cc: Tony Lindgren Cc: Jason Cooper Cc: Marc Zyngier Cc: linux-omap@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: http://lkml.kernel.org/r/1450687994-12580-1-git-send-email-milo.kim@ti.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-omap-intc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 8587d0f8d8c0..ed25175ae9fa 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -207,7 +207,6 @@ static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) ct = gc->chip_types; ct->type = IRQ_TYPE_LEVEL_MASK; - ct->handler = handle_level_irq; ct->chip.irq_ack = omap_mask_ack_irq; ct->chip.irq_mask = irq_gc_mask_disable_reg; -- cgit v1.2.3 From 1fd9a71076ccbcf731cf02408122600a6f2b5d17 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 30 Dec 2015 22:16:37 +0800 Subject: irqchip/zevio: Use irq_data_get_chip_type() helper Use irq_data_get_chip_type() instead of container_of(). Signed-off-by: Geliang Tang Cc: Jason Cooper Cc: Marc Zyngier Link: http://lkml.kernel.org/r/4cc3a3a7a74c7a1894892a85aa7eabbd1534fe96.1451484758.git.geliangtang@163.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-zevio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index 4c48fa88a03d..cb9d8ec37507 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c @@ -43,8 +43,7 @@ static void __iomem *zevio_irq_io; static void zevio_irq_ack(struct irq_data *irqd) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(irqd); - struct irq_chip_regs *regs = - &container_of(irqd->chip, struct irq_chip_type, chip)->regs; + struct irq_chip_regs *regs = &irq_data_get_chip_type(irqd)->regs; readl(gc->reg_base + regs->ack); } -- cgit v1.2.3 From d3b421cd07e4c0d4d6c0bbd55ca169c054fc081d Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 15 Dec 2015 19:56:12 +0530 Subject: irqchip/omap-intc: Add support for spurious irq handling Under some conditions, irq sorting procedure used by INTC can go wrong resulting in a spurious irq getting reported. If this condition is not handled, it results in endless stream of: unexpected IRQ trap at vector 00 messages from ack_bad_irq() Handle the spurious interrupt condition in omap-intc driver to prevent this. Measurements using kernel function profiler on AM335x EVM running at 720MHz show that after this patch omap_intc_handle_irq() takes about 37.4us against 34us before this patch. Signed-off-by: Sekhar Nori Acked-by: Tony Lindgren Cc: John Ogness Cc: Felipe Balbi Cc: Jason Cooper Cc: Marc Zyngier Link: http://lkml.kernel.org/r/9c78a6db02ac55f7af7371b417b6e414d2c3095b.1450188128.git.nsekhar@ti.com Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-omap-intc.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index ed25175ae9fa..9d1bcfc33e4c 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -47,6 +47,7 @@ #define INTC_ILR0 0x0100 #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ +#define SPURIOUSIRQ_MASK (0x1ffffff << 7) #define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 4 @@ -329,11 +330,35 @@ static int __init omap_init_irq(u32 base, struct device_node *node) static asmlinkage void __exception_irq_entry omap_intc_handle_irq(struct pt_regs *regs) { + extern unsigned long irq_err_count; u32 irqnr; irqnr = intc_readl(INTC_SIR); + + /* + * A spurious IRQ can result if interrupt that triggered the + * sorting is no longer active during the sorting (10 INTC + * functional clock cycles after interrupt assertion). Or a + * change in interrupt mask affected the result during sorting + * time. There is no special handling required except ignoring + * the SIR register value just read and retrying. + * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K + * + * Many a times, a spurious interrupt situation has been fixed + * by adding a flush for the posted write acking the IRQ in + * the device driver. Typically, this is going be the device + * driver whose interrupt was handled just before the spurious + * IRQ occurred. Pay attention to those device drivers if you + * run into hitting the spurious IRQ condition below. + */ + if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) { + pr_err_once("%s: spurious irq!\n", __func__); + irq_err_count++; + omap_ack_irq(NULL); + return; + } + irqnr &= ACTIVEIRQ_MASK; - WARN_ONCE(!irqnr, "Spurious IRQ ?\n"); handle_domain_irq(domain, irqnr, regs); } -- cgit v1.2.3