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

irqchip: Add TB10x interrupt controller driver

The SOC interrupt controller driver for the Abilis Systems TB10x series of
SOCs based on ARC700 CPUs.

Signed-off-by: Christian Ruppert <christian.ruppert@abilis.com>
Signed-off-by: Pierrick Hascoet <pierrick.hascoet@abilis.com>
Cc: Vineet Gupta <Vineet.Gupta1@synopsys.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: devicetree-discuss@lists.ozlabs.org
Link: http://lkml.kernel.org/r/1372177797-9458-1-git-send-email-christian.ruppert@abilis.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Christian Ruppert and committed by
Thomas Gleixner
b06eb017 fbab62c5

+239
+38
Documentation/devicetree/bindings/interrupt-controller/abilis,tb10x-ictl.txt
··· 1 + TB10x Top Level Interrupt Controller 2 + ==================================== 3 + 4 + The Abilis TB10x SOC contains a custom interrupt controller. It performs 5 + one-to-one mapping of external interrupt sources to CPU interrupts and 6 + provides support for reconfigurable trigger modes. 7 + 8 + Required properties 9 + ------------------- 10 + 11 + - compatible: Should be "abilis,tb10x-ictl" 12 + - reg: specifies physical base address and size of register range. 13 + - interrupt-congroller: Identifies the node as an interrupt controller. 14 + - #interrupt cells: Specifies the number of cells used to encode an interrupt 15 + source connected to this controller. The value shall be 2. 16 + - interrupt-parent: Specifies the parent interrupt controller. 17 + - interrupts: Specifies the list of interrupt lines which are handled by 18 + the interrupt controller in the parent controller's notation. Interrupts 19 + are mapped one-to-one to parent interrupts. 20 + 21 + Example 22 + ------- 23 + 24 + intc: interrupt-controller { /* Parent interrupt controller */ 25 + interrupt-controller; 26 + #interrupt-cells = <1>; /* For example below */ 27 + /* ... */ 28 + }; 29 + 30 + tb10x_ictl: pic@2000 { /* TB10x interrupt controller */ 31 + compatible = "abilis,tb10x-ictl"; 32 + reg = <0x2000 0x20>; 33 + interrupt-controller; 34 + #interrupt-cells = <2>; 35 + interrupt-parent = <&intc>; 36 + interrupts = <5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 37 + 20 21 22 23 24 25 26 27 28 29 30 31>; 38 + };
+5
drivers/irqchip/Kconfig
··· 38 38 bool 39 39 select IRQ_DOMAIN 40 40 41 + config TB10X_IRQC 42 + bool 43 + select IRQ_DOMAIN 44 + select GENERIC_IRQ_CHIP 45 + 41 46 config VERSATILE_FPGA_IRQ 42 47 bool 43 48 select IRQ_DOMAIN
+1
drivers/irqchip/Makefile
··· 17 17 obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o 18 18 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 19 19 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 20 + obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
+195
drivers/irqchip/irq-tb10x.c
··· 1 + /* 2 + * Abilis Systems interrupt controller driver 3 + * 4 + * Copyright (C) Abilis Systems 2012 5 + * 6 + * Author: Christian Ruppert <christian.ruppert@abilis.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 + */ 21 + 22 + #include <linux/interrupt.h> 23 + #include <linux/irqdomain.h> 24 + #include <linux/irq.h> 25 + #include <linux/of_irq.h> 26 + #include <linux/of_address.h> 27 + #include <linux/of_platform.h> 28 + #include <linux/io.h> 29 + #include <linux/slab.h> 30 + #include <linux/bitops.h> 31 + #include "irqchip.h" 32 + 33 + #define AB_IRQCTL_INT_ENABLE 0x00 34 + #define AB_IRQCTL_INT_STATUS 0x04 35 + #define AB_IRQCTL_SRC_MODE 0x08 36 + #define AB_IRQCTL_SRC_POLARITY 0x0C 37 + #define AB_IRQCTL_INT_MODE 0x10 38 + #define AB_IRQCTL_INT_POLARITY 0x14 39 + #define AB_IRQCTL_INT_FORCE 0x18 40 + 41 + #define AB_IRQCTL_MAXIRQ 32 42 + 43 + static inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg, 44 + u32 val) 45 + { 46 + irq_reg_writel(val, gc->reg_base + reg); 47 + } 48 + 49 + static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) 50 + { 51 + return irq_reg_readl(gc->reg_base + reg); 52 + } 53 + 54 + static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) 55 + { 56 + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 57 + uint32_t im, mod, pol; 58 + 59 + im = data->mask; 60 + 61 + irq_gc_lock(gc); 62 + 63 + mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im; 64 + pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im; 65 + 66 + switch (flow_type & IRQF_TRIGGER_MASK) { 67 + case IRQ_TYPE_EDGE_FALLING: 68 + pol ^= im; 69 + break; 70 + case IRQ_TYPE_LEVEL_HIGH: 71 + mod ^= im; 72 + break; 73 + case IRQ_TYPE_NONE: 74 + flow_type = IRQ_TYPE_LEVEL_LOW; 75 + case IRQ_TYPE_LEVEL_LOW: 76 + mod ^= im; 77 + pol ^= im; 78 + break; 79 + case IRQ_TYPE_EDGE_RISING: 80 + break; 81 + default: 82 + irq_gc_unlock(gc); 83 + pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", 84 + __func__, data->irq); 85 + return -EBADR; 86 + } 87 + 88 + irqd_set_trigger_type(data, flow_type); 89 + irq_setup_alt_chip(data, flow_type); 90 + 91 + ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, mod); 92 + ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, pol); 93 + ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, im); 94 + 95 + irq_gc_unlock(gc); 96 + 97 + return IRQ_SET_MASK_OK; 98 + } 99 + 100 + static void tb10x_irq_cascade(unsigned int irq, struct irq_desc *desc) 101 + { 102 + struct irq_domain *domain = irq_desc_get_handler_data(desc); 103 + 104 + generic_handle_irq(irq_find_mapping(domain, irq)); 105 + } 106 + 107 + static int __init of_tb10x_init_irq(struct device_node *ictl, 108 + struct device_node *parent) 109 + { 110 + int i, ret, nrirqs = of_irq_count(ictl); 111 + struct resource mem; 112 + struct irq_chip_generic *gc; 113 + struct irq_domain *domain; 114 + void __iomem *reg_base; 115 + 116 + if (of_address_to_resource(ictl, 0, &mem)) { 117 + pr_err("%s: No registers declared in DeviceTree.\n", 118 + ictl->name); 119 + return -EINVAL; 120 + } 121 + 122 + if (!request_mem_region(mem.start, resource_size(&mem), 123 + ictl->name)) { 124 + pr_err("%s: Request mem region failed.\n", ictl->name); 125 + return -EBUSY; 126 + } 127 + 128 + reg_base = ioremap(mem.start, resource_size(&mem)); 129 + if (!reg_base) { 130 + ret = -EBUSY; 131 + pr_err("%s: ioremap failed.\n", ictl->name); 132 + goto ioremap_fail; 133 + } 134 + 135 + domain = irq_domain_add_linear(ictl, AB_IRQCTL_MAXIRQ, 136 + &irq_generic_chip_ops, NULL); 137 + if (!domain) { 138 + ret = -ENOMEM; 139 + pr_err("%s: Could not register interrupt domain.\n", 140 + ictl->name); 141 + goto irq_domain_add_fail; 142 + } 143 + 144 + ret = irq_alloc_domain_generic_chips(domain, AB_IRQCTL_MAXIRQ, 145 + 2, ictl->name, handle_level_irq, 146 + IRQ_NOREQUEST, IRQ_NOPROBE, 147 + IRQ_GC_INIT_MASK_CACHE); 148 + if (ret) { 149 + pr_err("%s: Could not allocate generic interrupt chip.\n", 150 + ictl->name); 151 + goto gc_alloc_fail; 152 + } 153 + 154 + gc = domain->gc->gc[0]; 155 + gc->reg_base = reg_base; 156 + 157 + gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; 158 + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 159 + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 160 + gc->chip_types[0].chip.irq_set_type = tb10x_irq_set_type; 161 + gc->chip_types[0].regs.mask = AB_IRQCTL_INT_ENABLE; 162 + 163 + gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; 164 + gc->chip_types[1].chip.name = gc->chip_types[0].chip.name; 165 + gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; 166 + gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; 167 + gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; 168 + gc->chip_types[1].chip.irq_set_type = tb10x_irq_set_type; 169 + gc->chip_types[1].regs.ack = AB_IRQCTL_INT_STATUS; 170 + gc->chip_types[1].regs.mask = AB_IRQCTL_INT_ENABLE; 171 + gc->chip_types[1].handler = handle_edge_irq; 172 + 173 + for (i = 0; i < nrirqs; i++) { 174 + unsigned int irq = irq_of_parse_and_map(ictl, i); 175 + 176 + irq_set_handler_data(irq, domain); 177 + irq_set_chained_handler(irq, tb10x_irq_cascade); 178 + } 179 + 180 + ab_irqctl_writereg(gc, AB_IRQCTL_INT_ENABLE, 0); 181 + ab_irqctl_writereg(gc, AB_IRQCTL_INT_MODE, 0); 182 + ab_irqctl_writereg(gc, AB_IRQCTL_INT_POLARITY, 0); 183 + ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, ~0UL); 184 + 185 + return 0; 186 + 187 + gc_alloc_fail: 188 + irq_domain_remove(domain); 189 + irq_domain_add_fail: 190 + iounmap(reg_base); 191 + ioremap_fail: 192 + release_mem_region(mem.start, resource_size(&mem)); 193 + return ret; 194 + } 195 + IRQCHIP_DECLARE(tb10x_intc, "abilis,tb10x-ictl", of_tb10x_init_irq);