Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'mvebu-irqchip-3.14' of git://git.infradead.org/linux-mvebu into irq/core

mvebu irqchip changes for v3.14

- add Dove PMU interrupt controller

Duh. I completely forgot about that one...

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

+144
+17
Documentation/devicetree/bindings/interrupt-controller/marvell,dove-pmu-intc.txt
··· 1 + Marvell Dove Power Management Unit interrupt controller 2 + 3 + Required properties: 4 + - compatible: shall be "marvell,dove-pmu-intc" 5 + - reg: base address of PMU interrupt registers starting with CAUSE register 6 + - interrupts: PMU interrupt of the main interrupt controller 7 + - interrupt-controller: identifies the node as an interrupt controller 8 + - #interrupt-cells: number of cells to encode an interrupt source, shall be 1 9 + 10 + Example: 11 + pmu_intc: pmu-interrupt-ctrl@d0050 { 12 + compatible = "marvell,dove-pmu-intc"; 13 + interrupt-controller; 14 + #interrupt-cells = <1>; 15 + reg = <0xd0050 0x8>; 16 + interrupts = <33>; 17 + };
+1
drivers/irqchip/Makefile
··· 1 1 obj-$(CONFIG_IRQCHIP) += irqchip.o 2 2 3 3 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 4 + obj-$(CONFIG_ARCH_DOVE) += irq-dove.o 4 5 obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o 5 6 obj-$(CONFIG_ARCH_MMP) += irq-mmp.o 6 7 obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
+126
drivers/irqchip/irq-dove.c
··· 1 + /* 2 + * Marvell Dove SoCs PMU IRQ chip driver. 3 + * 4 + * Andrew Lunn <andrew@lunn.ch> 5 + * 6 + * This file is licensed under the terms of the GNU General Public 7 + * License version 2. This program is licensed "as is" without any 8 + * warranty of any kind, whether express or implied. 9 + */ 10 + 11 + #include <linux/io.h> 12 + #include <linux/irq.h> 13 + #include <linux/of.h> 14 + #include <linux/of_address.h> 15 + #include <linux/of_irq.h> 16 + #include <asm/exception.h> 17 + #include <asm/mach/irq.h> 18 + 19 + #include "irqchip.h" 20 + 21 + #define DOVE_PMU_IRQ_CAUSE 0x00 22 + #define DOVE_PMU_IRQ_MASK 0x04 23 + 24 + static void dove_pmu_irq_handler(unsigned int irq, struct irq_desc *desc) 25 + { 26 + struct irq_domain *d = irq_get_handler_data(irq); 27 + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); 28 + u32 stat = readl_relaxed(gc->reg_base + DOVE_PMU_IRQ_CAUSE) & 29 + gc->mask_cache; 30 + 31 + while (stat) { 32 + u32 hwirq = ffs(stat) - 1; 33 + 34 + generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); 35 + stat &= ~(1 << hwirq); 36 + } 37 + } 38 + 39 + static void pmu_irq_ack(struct irq_data *d) 40 + { 41 + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 42 + struct irq_chip_type *ct = irq_data_get_chip_type(d); 43 + u32 mask = ~d->mask; 44 + 45 + /* 46 + * The PMU mask register is not RW0C: it is RW. This means that 47 + * the bits take whatever value is written to them; if you write 48 + * a '1', you will set the interrupt. 49 + * 50 + * Unfortunately this means there is NO race free way to clear 51 + * these interrupts. 52 + * 53 + * So, let's structure the code so that the window is as small as 54 + * possible. 55 + */ 56 + irq_gc_lock(gc); 57 + mask &= irq_reg_readl(gc->reg_base + ct->regs.ack); 58 + irq_reg_writel(mask, gc->reg_base + ct->regs.ack); 59 + irq_gc_unlock(gc); 60 + } 61 + 62 + static int __init dove_pmu_irq_init(struct device_node *np, 63 + struct device_node *parent) 64 + { 65 + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 66 + struct resource r; 67 + struct irq_domain *domain; 68 + struct irq_chip_generic *gc; 69 + int ret, irq, nrirqs = 7; 70 + 71 + domain = irq_domain_add_linear(np, nrirqs, 72 + &irq_generic_chip_ops, NULL); 73 + if (!domain) { 74 + pr_err("%s: unable to add irq domain\n", np->name); 75 + return -ENOMEM; 76 + } 77 + 78 + ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, 79 + handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); 80 + if (ret) { 81 + pr_err("%s: unable to alloc irq domain gc\n", np->name); 82 + return ret; 83 + } 84 + 85 + ret = of_address_to_resource(np, 0, &r); 86 + if (ret) { 87 + pr_err("%s: unable to get resource\n", np->name); 88 + return ret; 89 + } 90 + 91 + if (!request_mem_region(r.start, resource_size(&r), np->name)) { 92 + pr_err("%s: unable to request mem region\n", np->name); 93 + return -ENOMEM; 94 + } 95 + 96 + /* Map the parent interrupt for the chained handler */ 97 + irq = irq_of_parse_and_map(np, 0); 98 + if (irq <= 0) { 99 + pr_err("%s: unable to parse irq\n", np->name); 100 + return -EINVAL; 101 + } 102 + 103 + gc = irq_get_domain_generic_chip(domain, 0); 104 + gc->reg_base = ioremap(r.start, resource_size(&r)); 105 + if (!gc->reg_base) { 106 + pr_err("%s: unable to map resource\n", np->name); 107 + return -ENOMEM; 108 + } 109 + 110 + gc->chip_types[0].regs.ack = DOVE_PMU_IRQ_CAUSE; 111 + gc->chip_types[0].regs.mask = DOVE_PMU_IRQ_MASK; 112 + gc->chip_types[0].chip.irq_ack = pmu_irq_ack; 113 + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 114 + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 115 + 116 + /* mask and clear all interrupts */ 117 + writel(0, gc->reg_base + DOVE_PMU_IRQ_MASK); 118 + writel(0, gc->reg_base + DOVE_PMU_IRQ_CAUSE); 119 + 120 + irq_set_handler_data(irq, domain); 121 + irq_set_chained_handler(irq, dove_pmu_irq_handler); 122 + 123 + return 0; 124 + } 125 + IRQCHIP_DECLARE(dove_pmu_intc, 126 + "marvell,dove-pmu-intc", dove_pmu_irq_init);