···11+Marvell Dove Power Management Unit interrupt controller22+33+Required properties:44+- compatible: shall be "marvell,dove-pmu-intc"55+- reg: base address of PMU interrupt registers starting with CAUSE register66+- interrupts: PMU interrupt of the main interrupt controller77+- interrupt-controller: identifies the node as an interrupt controller88+- #interrupt-cells: number of cells to encode an interrupt source, shall be 199+1010+Example:1111+ pmu_intc: pmu-interrupt-ctrl@d0050 {1212+ compatible = "marvell,dove-pmu-intc";1313+ interrupt-controller;1414+ #interrupt-cells = <1>;1515+ reg = <0xd0050 0x8>;1616+ interrupts = <33>;1717+ };
···11+/*22+ * Marvell Dove SoCs PMU IRQ chip driver.33+ *44+ * Andrew Lunn <andrew@lunn.ch>55+ *66+ * This file is licensed under the terms of the GNU General Public77+ * License version 2. This program is licensed "as is" without any88+ * warranty of any kind, whether express or implied.99+ */1010+1111+#include <linux/io.h>1212+#include <linux/irq.h>1313+#include <linux/of.h>1414+#include <linux/of_address.h>1515+#include <linux/of_irq.h>1616+#include <asm/exception.h>1717+#include <asm/mach/irq.h>1818+1919+#include "irqchip.h"2020+2121+#define DOVE_PMU_IRQ_CAUSE 0x002222+#define DOVE_PMU_IRQ_MASK 0x042323+2424+static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc)2525+{2626+ struct irq_domain *d = irq_get_handler_data(irq);2727+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);2828+ u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) &2929+ gc->mask_cache;3030+3131+ while (stat) {3232+ u32 hwirq = ffs(stat) - 1;3333+3434+ generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));3535+ stat &= ~(1 << hwirq);3636+ }3737+}3838+3939+static void pmu_irq_ack(struct irq_data *d)4040+{4141+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);4242+ struct irq_chip_type *ct = irq_data_get_chip_type(d);4343+ u32 mask = ~d->mask;4444+4545+ /*4646+ * The PMU mask register is not RW0C: it is RW. This means that4747+ * the bits take whatever value is written to them; if you write4848+ * a '1', you will set the interrupt.4949+ *5050+ * Unfortunately this means there is NO race free way to clear5151+ * these interrupts.5252+ *5353+ * So, let's structure the code so that the window is as small as5454+ * possible.5555+ */5656+ irq_gc_lock(gc);5757+ mask &= irq_reg_readl(gc->reg_base + ct->regs.ack);5858+ irq_reg_writel(mask, gc->reg_base + ct->regs.ack);5959+ irq_gc_unlock(gc);6060+}6161+6262+static int __init dove_pmu_irq_init(struct device_node *np,6363+ struct device_node *parent)6464+{6565+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;6666+ struct resource r;6767+ struct irq_domain *domain;6868+ struct irq_chip_generic *gc;6969+ int ret, irq, nrirqs = 7;7070+7171+ domain = irq_domain_add_linear(np, nrirqs,7272+ &irq_generic_chip_ops, NULL);7373+ if (!domain) {7474+ pr_err("%s: unable to add irq domain\n", np->name);7575+ return -ENOMEM;7676+ }7777+7878+ ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,7979+ handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);8080+ if (ret) {8181+ pr_err("%s: unable to alloc irq domain gc\n", np->name);8282+ return ret;8383+ }8484+8585+ ret = of_address_to_resource(np, 0, &r);8686+ if (ret) {8787+ pr_err("%s: unable to get resource\n", np->name);8888+ return ret;8989+ }9090+9191+ if (!request_mem_region(r.start, resource_size(&r), np->name)) {9292+ pr_err("%s: unable to request mem region\n", np->name);9393+ return -ENOMEM;9494+ }9595+9696+ /* Map the parent interrupt for the chained handler */9797+ irq = irq_of_parse_and_map(np, 0);9898+ if (irq <= 0) {9999+ pr_err("%s: unable to parse irq\n", np->name);100100+ return -EINVAL;101101+ }102102+103103+ gc = irq_get_domain_generic_chip(domain, 0);104104+ gc->reg_base = ioremap(r.start, resource_size(&r));105105+ if (!gc->reg_base) {106106+ pr_err("%s: unable to map resource\n", np->name);107107+ return -ENOMEM;108108+ }109109+110110+ gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE;111111+ gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK;112112+ gc->chip_types[0].chip.irq_ack = pmu_irq_ack;113113+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;114114+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;115115+116116+ /* mask and clear all interrupts */117117+ writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK);118118+ writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE);119119+120120+ irq_set_handler_data(irq, domain);121121+ irq_set_chained_handler(irq, dove_pmu_irq_handler);122122+123123+ return 0;124124+}125125+IRQCHIP_DECLARE(dove_pmu_intc,126126+ "marvell,dove-pmu-intc", dove_pmu_irq_init);