* [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller.
@ 2013-09-30 21:38 Andrew Lunn
2013-09-30 21:38 ` [PATCH 2/3] ARM: Dove: Add DT node for " Andrew Lunn
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Andrew Lunn @ 2013-09-30 21:38 UTC (permalink / raw)
To: linux-arm-kernel
Dove has a Power Management Unit with its own interrupt
controller. This is chained on the main interrupt controller. Add a
driver, making use of generic chip where possible.
Signed-off-by: Andrew Lunn <andrew@lunn•ch>
cc: devicetree at vger.kernel.org
cc: pawel.moll at arm.com
cc: mark.rutland at arm.com
cc: swarren at wwwdotorg.org
cc: ian.campbell at citrix.com
---
.../interrupt-controller/marvell,dove-pmu-intc.txt | 17 +++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-dove.c | 127 +++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt
create mode 100644 drivers/irqchip/irq-dove.c
diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt
new file mode 100644
index 0000000..1feb582
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt
@@ -0,0 +1,17 @@
+Marvell Dove Power Management Unit interrupt controller
+
+Required properties:
+- compatible: shall be "marvell,dove-pmu-intc"
+- reg: base address of PMU interrupt registers starting with CAUSE register
+- interrupts: PMU interrupt of the main interrupt controller
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: number of cells to encode an interrupt source, shall be 1
+
+Example:
+ pmu_intc: pmu-interrupt-ctrl at d0050 {
+ compatible = "marvell,dove-pmu-intc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xd0050 0x8>;
+ interrupts = <33>;
+ };
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c60b901..f743006 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
+obj-$(CONFIG_ARCH_DOVE) += irq-dove.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
diff --git a/drivers/irqchip/irq-dove.c b/drivers/irqchip/irq-dove.c
new file mode 100644
index 0000000..de66f02
--- /dev/null
+++ b/drivers/irqchip/irq-dove.c
@@ -0,0 +1,127 @@
+/*
+ * Marvell Dove SoCs PMU IRQ chip driver.
+ *
+ * Andrew Lunn <andrew@lunn•ch>
+ *
+ * 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 <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+#define DOVE_PMU_IRQ_CAUSE 0x00
+#define DOVE_PMU_IRQ_MASK 0x04
+
+static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_domain *d = irq_get_handler_data(irq);
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
+ u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) &
+ gc->mask_cache;
+
+ while (stat) {
+ u32 hwirq = ffs(stat) - 1;
+
+ generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
+ stat &= ~(1 << hwirq);
+ }
+}
+
+static void pmu_irq_ack(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ u32 mask = ~d->mask;
+
+ /*
+ * The PMU mask register is not RW0C: it is RW. This means that
+ * the bits take whatever value is written to them; if you write
+ * a '1', you will set the interrupt.
+ *
+ * Unfortunately this means there is NO race free way to clear
+ * these interrupts.
+ *
+ * So, let's structure the code so that the window is as small as
+ * possible.
+ */
+ irq_gc_lock(gc);
+
+ mask &= irq_reg_readl(gc->reg_base + ct->regs.ack);
+ irq_reg_writel(mask, gc->reg_base + ct->regs.ack);
+ irq_gc_unlock(gc);
+}
+
+static int __init dove_pmu_irq_init(struct device_node *np,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct resource r;
+ struct irq_domain *domain;
+ struct irq_chip_generic *gc;
+ int ret, irq, nrirqs = 7;
+
+ domain = irq_domain_add_linear(np, nrirqs,
+ &irq_generic_chip_ops, NULL);
+ if (!domain) {
+ pr_err("%s: unable to add irq domain\n", np->name);
+ return -ENOMEM;
+ }
+
+ ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
+ handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: unable to alloc irq domain gc\n", np->name);
+ return ret;
+ }
+
+ ret = of_address_to_resource(np, 0, &r);
+ if (ret) {
+ pr_err("%s: unable to get resource\n", np->name);
+ return ret;
+ }
+
+ if (!request_mem_region(r.start, resource_size(&r), np->name)) {
+ pr_err("%s: unable to request mem region\n", np->name);
+ return -ENOMEM;
+ }
+
+ /* Map the parent interrupt for the chained handler */
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ pr_err("%s: unable to parse irq\n", np->name);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(domain, 0);
+ gc->reg_base = ioremap(r.start, resource_size(&r));
+ if (!gc->reg_base) {
+ pr_err("%s: unable to map resource\n", np->name);
+ return -ENOMEM;
+ }
+
+ gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE;
+ gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK;
+ gc->chip_types[0].chip.irq_ack = pmu_irq_ack;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ /* mask and clear all interrupts */
+ writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK);
+ writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE);
+
+ irq_set_handler_data(irq, domain);
+ irq_set_chained_handler(irq, dove_pmu_irq_handler);
+
+ return 0;
+}
+IRQCHIP_DECLARE(dove_pmu_intc,
+ "marvell,dove-pmu-intc", dove_pmu_irq_init);
--
1.8.4.rc3
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/3] ARM: Dove: Add DT node for PMU interrupt controller. 2013-09-30 21:38 [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller Andrew Lunn @ 2013-09-30 21:38 ` Andrew Lunn 2013-09-30 21:38 ` [PATCH 3/3] ARM: Dove: Add RTC interrupt via " Andrew Lunn ` (2 subsequent siblings) 3 siblings, 0 replies; 6+ messages in thread From: Andrew Lunn @ 2013-09-30 21:38 UTC (permalink / raw) To: linux-arm-kernel Instantiate the PMU interrupt controller which Dove has. Signed-off-by: Andrew Lunn <andrew@lunn•ch> --- arch/arm/boot/dts/dove.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi index cc27916..52b992a 100644 --- a/arch/arm/boot/dts/dove.dtsi +++ b/arch/arm/boot/dts/dove.dtsi @@ -66,6 +66,15 @@ marvell,#interrupts = <5>; }; + pmu_intc: pmu-interrupt-ctrl at d0050 { + compatible = "marvell,dove-pmu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0xd0050 0x8>; + interrupts = <33>; + marvell,#interrupts = <7>; + }; + core_clk: core-clocks at d0214 { compatible = "marvell,dove-core-clock"; reg = <0xd0214 0x4>; -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] ARM: Dove: Add RTC interrupt via PMU interrupt controller. 2013-09-30 21:38 [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller Andrew Lunn 2013-09-30 21:38 ` [PATCH 2/3] ARM: Dove: Add DT node for " Andrew Lunn @ 2013-09-30 21:38 ` Andrew Lunn 2013-10-01 0:54 ` [PATCH 1/3] irqchip: irq-dove: Add " Jason Cooper 2013-10-01 11:22 ` Sebastian Hesselbarth 3 siblings, 0 replies; 6+ messages in thread From: Andrew Lunn @ 2013-09-30 21:38 UTC (permalink / raw) To: linux-arm-kernel The RTC on Dove has an interrupt on the PMU interrupt controller. Add this interrupt to the RTC node. Tested using the "RTC Driver Test Example" Signed-off-by: Andrew Lunn <andrew@lunn•ch> --- arch/arm/boot/dts/dove.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi index 52b992a..1fd615d 100644 --- a/arch/arm/boot/dts/dove.dtsi +++ b/arch/arm/boot/dts/dove.dtsi @@ -426,6 +426,8 @@ rtc: real-time-clock at d8500 { compatible = "marvell,orion-rtc"; reg = <0xd8500 0x20>; + interrupt-parent = <&pmu_intc>; + interrupts = <5>; }; crypto: crypto-engine at 30000 { -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller. 2013-09-30 21:38 [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller Andrew Lunn 2013-09-30 21:38 ` [PATCH 2/3] ARM: Dove: Add DT node for " Andrew Lunn 2013-09-30 21:38 ` [PATCH 3/3] ARM: Dove: Add RTC interrupt via " Andrew Lunn @ 2013-10-01 0:54 ` Jason Cooper 2013-10-08 16:34 ` Jason Cooper 2013-10-01 11:22 ` Sebastian Hesselbarth 3 siblings, 1 reply; 6+ messages in thread From: Jason Cooper @ 2013-10-01 0:54 UTC (permalink / raw) To: linux-arm-kernel Thomas et al, Once you folks are happy with this driver, I can take patches 2 and 3 (the dove dts changes). We have quite a bit of flux in those files this window. thx, Jason. On Mon, Sep 30, 2013 at 11:38:40PM +0200, Andrew Lunn wrote: > Dove has a Power Management Unit with its own interrupt > controller. This is chained on the main interrupt controller. Add a > driver, making use of generic chip where possible. > > Signed-off-by: Andrew Lunn <andrew@lunn•ch> > > cc: devicetree at vger.kernel.org > cc: pawel.moll at arm.com > cc: mark.rutland at arm.com > cc: swarren at wwwdotorg.org > cc: ian.campbell at citrix.com > --- > .../interrupt-controller/marvell,dove-pmu-intc.txt | 17 +++ > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-dove.c | 127 +++++++++++++++++++++ > 3 files changed, 145 insertions(+) > create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > create mode 100644 drivers/irqchip/irq-dove.c > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > new file mode 100644 > index 0000000..1feb582 > --- /dev/null > +++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > @@ -0,0 +1,17 @@ > +Marvell Dove Power Management Unit interrupt controller > + > +Required properties: > +- compatible: shall be "marvell,dove-pmu-intc" > +- reg: base address of PMU interrupt registers starting with CAUSE register > +- interrupts: PMU interrupt of the main interrupt controller > +- interrupt-controller: identifies the node as an interrupt controller > +- #interrupt-cells: number of cells to encode an interrupt source, shall be 1 > + > +Example: > + pmu_intc: pmu-interrupt-ctrl at d0050 { > + compatible = "marvell,dove-pmu-intc"; > + interrupt-controller; > + #interrupt-cells = <1>; > + reg = <0xd0050 0x8>; > + interrupts = <33>; > + }; > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > index c60b901..f743006 100644 > --- a/drivers/irqchip/Makefile > +++ b/drivers/irqchip/Makefile > @@ -1,6 +1,7 @@ > obj-$(CONFIG_IRQCHIP) += irqchip.o > > obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o > +obj-$(CONFIG_ARCH_DOVE) += irq-dove.o > obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o > obj-$(CONFIG_ARCH_MMP) += irq-mmp.o > obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o > diff --git a/drivers/irqchip/irq-dove.c b/drivers/irqchip/irq-dove.c > new file mode 100644 > index 0000000..de66f02 > --- /dev/null > +++ b/drivers/irqchip/irq-dove.c > @@ -0,0 +1,127 @@ > +/* > + * Marvell Dove SoCs PMU IRQ chip driver. > + * > + * Andrew Lunn <andrew@lunn•ch> > + * > + * 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 <linux/io.h> > +#include <linux/irq.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <asm/exception.h> > +#include <asm/mach/irq.h> > + > +#include "irqchip.h" > + > +#define DOVE_PMU_IRQ_CAUSE 0x00 > +#define DOVE_PMU_IRQ_MASK 0x04 > + > +static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc) > +{ > + struct irq_domain *d = irq_get_handler_data(irq); > + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); > + u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) & > + gc->mask_cache; > + > + while (stat) { > + u32 hwirq = ffs(stat) - 1; > + > + generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); > + stat &= ~(1 << hwirq); > + } > +} > + > +static void pmu_irq_ack(struct irq_data *d) > +{ > + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); > + struct irq_chip_type *ct = irq_data_get_chip_type(d); > + u32 mask = ~d->mask; > + > + /* > + * The PMU mask register is not RW0C: it is RW. This means that > + * the bits take whatever value is written to them; if you write > + * a '1', you will set the interrupt. > + * > + * Unfortunately this means there is NO race free way to clear > + * these interrupts. > + * > + * So, let's structure the code so that the window is as small as > + * possible. > + */ > + irq_gc_lock(gc); > + > + mask &= irq_reg_readl(gc->reg_base + ct->regs.ack); > + irq_reg_writel(mask, gc->reg_base + ct->regs.ack); > + irq_gc_unlock(gc); > +} > + > +static int __init dove_pmu_irq_init(struct device_node *np, > + struct device_node *parent) > +{ > + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; > + struct resource r; > + struct irq_domain *domain; > + struct irq_chip_generic *gc; > + int ret, irq, nrirqs = 7; > + > + domain = irq_domain_add_linear(np, nrirqs, > + &irq_generic_chip_ops, NULL); > + if (!domain) { > + pr_err("%s: unable to add irq domain\n", np->name); > + return -ENOMEM; > + } > + > + ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, > + handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); > + if (ret) { > + pr_err("%s: unable to alloc irq domain gc\n", np->name); > + return ret; > + } > + > + ret = of_address_to_resource(np, 0, &r); > + if (ret) { > + pr_err("%s: unable to get resource\n", np->name); > + return ret; > + } > + > + if (!request_mem_region(r.start, resource_size(&r), np->name)) { > + pr_err("%s: unable to request mem region\n", np->name); > + return -ENOMEM; > + } > + > + /* Map the parent interrupt for the chained handler */ > + irq = irq_of_parse_and_map(np, 0); > + if (irq <= 0) { > + pr_err("%s: unable to parse irq\n", np->name); > + return -EINVAL; > + } > + > + gc = irq_get_domain_generic_chip(domain, 0); > + gc->reg_base = ioremap(r.start, resource_size(&r)); > + if (!gc->reg_base) { > + pr_err("%s: unable to map resource\n", np->name); > + return -ENOMEM; > + } > + > + gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE; > + gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK; > + gc->chip_types[0].chip.irq_ack = pmu_irq_ack; > + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; > + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; > + > + /* mask and clear all interrupts */ > + writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK); > + writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE); > + > + irq_set_handler_data(irq, domain); > + irq_set_chained_handler(irq, dove_pmu_irq_handler); > + > + return 0; > +} > +IRQCHIP_DECLARE(dove_pmu_intc, > + "marvell,dove-pmu-intc", dove_pmu_irq_init); > -- > 1.8.4.rc3 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller. 2013-10-01 0:54 ` [PATCH 1/3] irqchip: irq-dove: Add " Jason Cooper @ 2013-10-08 16:34 ` Jason Cooper 0 siblings, 0 replies; 6+ messages in thread From: Jason Cooper @ 2013-10-08 16:34 UTC (permalink / raw) To: linux-arm-kernel Thomas (tglx), On Mon, Sep 30, 2013 at 08:54:41PM -0400, Jason Cooper wrote: > Thomas et al, > > Once you folks are happy with this driver, I can take patches 2 and 3 > (the dove dts changes). We have quite a bit of flux in those files this > window. This has a Tested-by now, and we'd like to see this merged for v3.13. Do you intend to merge it, or would you like me to take it with your Ack? thx, Jason. > On Mon, Sep 30, 2013 at 11:38:40PM +0200, Andrew Lunn wrote: > > Dove has a Power Management Unit with its own interrupt > > controller. This is chained on the main interrupt controller. Add a > > driver, making use of generic chip where possible. > > > > Signed-off-by: Andrew Lunn <andrew@lunn•ch> > > > > cc: devicetree at vger.kernel.org > > cc: pawel.moll at arm.com > > cc: mark.rutland at arm.com > > cc: swarren at wwwdotorg.org > > cc: ian.campbell at citrix.com > > --- > > .../interrupt-controller/marvell,dove-pmu-intc.txt | 17 +++ > > drivers/irqchip/Makefile | 1 + > > drivers/irqchip/irq-dove.c | 127 +++++++++++++++++++++ > > 3 files changed, 145 insertions(+) > > create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > > create mode 100644 drivers/irqchip/irq-dove.c > > > > diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > > new file mode 100644 > > index 0000000..1feb582 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt > > @@ -0,0 +1,17 @@ > > +Marvell Dove Power Management Unit interrupt controller > > + > > +Required properties: > > +- compatible: shall be "marvell,dove-pmu-intc" > > +- reg: base address of PMU interrupt registers starting with CAUSE register > > +- interrupts: PMU interrupt of the main interrupt controller > > +- interrupt-controller: identifies the node as an interrupt controller > > +- #interrupt-cells: number of cells to encode an interrupt source, shall be 1 > > + > > +Example: > > + pmu_intc: pmu-interrupt-ctrl at d0050 { > > + compatible = "marvell,dove-pmu-intc"; > > + interrupt-controller; > > + #interrupt-cells = <1>; > > + reg = <0xd0050 0x8>; > > + interrupts = <33>; > > + }; > > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > > index c60b901..f743006 100644 > > --- a/drivers/irqchip/Makefile > > +++ b/drivers/irqchip/Makefile > > @@ -1,6 +1,7 @@ > > obj-$(CONFIG_IRQCHIP) += irqchip.o > > > > obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o > > +obj-$(CONFIG_ARCH_DOVE) += irq-dove.o > > obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o > > obj-$(CONFIG_ARCH_MMP) += irq-mmp.o > > obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o > > diff --git a/drivers/irqchip/irq-dove.c b/drivers/irqchip/irq-dove.c > > new file mode 100644 > > index 0000000..de66f02 > > --- /dev/null > > +++ b/drivers/irqchip/irq-dove.c > > @@ -0,0 +1,127 @@ > > +/* > > + * Marvell Dove SoCs PMU IRQ chip driver. > > + * > > + * Andrew Lunn <andrew@lunn•ch> > > + * > > + * 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 <linux/io.h> > > +#include <linux/irq.h> > > +#include <linux/of.h> > > +#include <linux/of_address.h> > > +#include <linux/of_irq.h> > > +#include <asm/exception.h> > > +#include <asm/mach/irq.h> > > + > > +#include "irqchip.h" > > + > > +#define DOVE_PMU_IRQ_CAUSE 0x00 > > +#define DOVE_PMU_IRQ_MASK 0x04 > > + > > +static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc) > > +{ > > + struct irq_domain *d = irq_get_handler_data(irq); > > + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); > > + u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) & > > + gc->mask_cache; > > + > > + while (stat) { > > + u32 hwirq = ffs(stat) - 1; > > + > > + generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); > > + stat &= ~(1 << hwirq); > > + } > > +} > > + > > +static void pmu_irq_ack(struct irq_data *d) > > +{ > > + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); > > + struct irq_chip_type *ct = irq_data_get_chip_type(d); > > + u32 mask = ~d->mask; > > + > > + /* > > + * The PMU mask register is not RW0C: it is RW. This means that > > + * the bits take whatever value is written to them; if you write > > + * a '1', you will set the interrupt. > > + * > > + * Unfortunately this means there is NO race free way to clear > > + * these interrupts. > > + * > > + * So, let's structure the code so that the window is as small as > > + * possible. > > + */ > > + irq_gc_lock(gc); > > + > > + mask &= irq_reg_readl(gc->reg_base + ct->regs.ack); > > + irq_reg_writel(mask, gc->reg_base + ct->regs.ack); > > + irq_gc_unlock(gc); > > +} > > + > > +static int __init dove_pmu_irq_init(struct device_node *np, > > + struct device_node *parent) > > +{ > > + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; > > + struct resource r; > > + struct irq_domain *domain; > > + struct irq_chip_generic *gc; > > + int ret, irq, nrirqs = 7; > > + > > + domain = irq_domain_add_linear(np, nrirqs, > > + &irq_generic_chip_ops, NULL); > > + if (!domain) { > > + pr_err("%s: unable to add irq domain\n", np->name); > > + return -ENOMEM; > > + } > > + > > + ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, > > + handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); > > + if (ret) { > > + pr_err("%s: unable to alloc irq domain gc\n", np->name); > > + return ret; > > + } > > + > > + ret = of_address_to_resource(np, 0, &r); > > + if (ret) { > > + pr_err("%s: unable to get resource\n", np->name); > > + return ret; > > + } > > + > > + if (!request_mem_region(r.start, resource_size(&r), np->name)) { > > + pr_err("%s: unable to request mem region\n", np->name); > > + return -ENOMEM; > > + } > > + > > + /* Map the parent interrupt for the chained handler */ > > + irq = irq_of_parse_and_map(np, 0); > > + if (irq <= 0) { > > + pr_err("%s: unable to parse irq\n", np->name); > > + return -EINVAL; > > + } > > + > > + gc = irq_get_domain_generic_chip(domain, 0); > > + gc->reg_base = ioremap(r.start, resource_size(&r)); > > + if (!gc->reg_base) { > > + pr_err("%s: unable to map resource\n", np->name); > > + return -ENOMEM; > > + } > > + > > + gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE; > > + gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK; > > + gc->chip_types[0].chip.irq_ack = pmu_irq_ack; > > + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; > > + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; > > + > > + /* mask and clear all interrupts */ > > + writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK); > > + writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE); > > + > > + irq_set_handler_data(irq, domain); > > + irq_set_chained_handler(irq, dove_pmu_irq_handler); > > + > > + return 0; > > +} > > +IRQCHIP_DECLARE(dove_pmu_intc, > > + "marvell,dove-pmu-intc", dove_pmu_irq_init); > > -- > > 1.8.4.rc3 > > > > > > _______________________________________________ > > linux-arm-kernel mailing list > > linux-arm-kernel at lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller. 2013-09-30 21:38 [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller Andrew Lunn ` (2 preceding siblings ...) 2013-10-01 0:54 ` [PATCH 1/3] irqchip: irq-dove: Add " Jason Cooper @ 2013-10-01 11:22 ` Sebastian Hesselbarth 3 siblings, 0 replies; 6+ messages in thread From: Sebastian Hesselbarth @ 2013-10-01 11:22 UTC (permalink / raw) To: linux-arm-kernel On 09/30/2013 11:38 PM, Andrew Lunn wrote: > Dove has a Power Management Unit with its own interrupt > controller. This is chained on the main interrupt controller. Add a > driver, making use of generic chip where possible. > > Signed-off-by: Andrew Lunn <andrew@lunn•ch> Andrew, thanks for providing pmu irq driver. I also tested the whole patch set with the RTC driver example on CuBox: Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail•com> Anyway, I have one little nit below. [...] > diff --git a/drivers/irqchip/irq-dove.c b/drivers/irqchip/irq-dove.c > new file mode 100644 > index 0000000..de66f02 > --- /dev/null > +++ b/drivers/irqchip/irq-dove.c > @@ -0,0 +1,127 @@ > +/* > + * Marvell Dove SoCs PMU IRQ chip driver. > + * > + * Andrew Lunn <andrew@lunn•ch> > + * > + * 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 <linux/io.h> > +#include <linux/irq.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <asm/exception.h> > +#include <asm/mach/irq.h> > + > +#include "irqchip.h" > + > +#define DOVE_PMU_IRQ_CAUSE 0x00 > +#define DOVE_PMU_IRQ_MASK 0x04 > + > +static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc) > +{ > + struct irq_domain *d = irq_get_handler_data(irq); > + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); > + u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) & > + gc->mask_cache; > + > + while (stat) { > + u32 hwirq = ffs(stat) - 1; > + > + generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); > + stat &= ~(1 << hwirq); > + } > +} > + > +static void pmu_irq_ack(struct irq_data *d) > +{ > + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); > + struct irq_chip_type *ct = irq_data_get_chip_type(d); > + u32 mask = ~d->mask; > + > + /* > + * The PMU mask register is not RW0C: it is RW. This means that > + * the bits take whatever value is written to them; if you write > + * a '1', you will set the interrupt. > + * > + * Unfortunately this means there is NO race free way to clear > + * these interrupts. > + * > + * So, let's structure the code so that the window is as small as > + * possible. > + */ > + irq_gc_lock(gc); > + nit: superfluous empty line > + mask &= irq_reg_readl(gc->reg_base + ct->regs.ack); > + irq_reg_writel(mask, gc->reg_base + ct->regs.ack); > + irq_gc_unlock(gc); > +} > + > +static int __init dove_pmu_irq_init(struct device_node *np, > + struct device_node *parent) > +{ > + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; > + struct resource r; > + struct irq_domain *domain; > + struct irq_chip_generic *gc; > + int ret, irq, nrirqs = 7; > + > + domain = irq_domain_add_linear(np, nrirqs, > + &irq_generic_chip_ops, NULL); > + if (!domain) { > + pr_err("%s: unable to add irq domain\n", np->name); > + return -ENOMEM; > + } > + > + ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, > + handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); > + if (ret) { > + pr_err("%s: unable to alloc irq domain gc\n", np->name); > + return ret; > + } > + > + ret = of_address_to_resource(np, 0, &r); > + if (ret) { > + pr_err("%s: unable to get resource\n", np->name); > + return ret; > + } > + > + if (!request_mem_region(r.start, resource_size(&r), np->name)) { > + pr_err("%s: unable to request mem region\n", np->name); > + return -ENOMEM; > + } > + > + /* Map the parent interrupt for the chained handler */ > + irq = irq_of_parse_and_map(np, 0); > + if (irq <= 0) { > + pr_err("%s: unable to parse irq\n", np->name); > + return -EINVAL; > + } > + > + gc = irq_get_domain_generic_chip(domain, 0); > + gc->reg_base = ioremap(r.start, resource_size(&r)); > + if (!gc->reg_base) { > + pr_err("%s: unable to map resource\n", np->name); > + return -ENOMEM; > + } > + > + gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE; > + gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK; > + gc->chip_types[0].chip.irq_ack = pmu_irq_ack; > + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; > + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; > + > + /* mask and clear all interrupts */ > + writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK); > + writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE); > + > + irq_set_handler_data(irq, domain); > + irq_set_chained_handler(irq, dove_pmu_irq_handler); > + > + return 0; > +} > +IRQCHIP_DECLARE(dove_pmu_intc, > + "marvell,dove-pmu-intc", dove_pmu_irq_init); > ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2013-10-08 16:34 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-09-30 21:38 [PATCH 1/3] irqchip: irq-dove: Add PMU interrupt controller Andrew Lunn 2013-09-30 21:38 ` [PATCH 2/3] ARM: Dove: Add DT node for " Andrew Lunn 2013-09-30 21:38 ` [PATCH 3/3] ARM: Dove: Add RTC interrupt via " Andrew Lunn 2013-10-01 0:54 ` [PATCH 1/3] irqchip: irq-dove: Add " Jason Cooper 2013-10-08 16:34 ` Jason Cooper 2013-10-01 11:22 ` Sebastian Hesselbarth
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox