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