1350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth/* 2350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * Synopsys DW APB ICTL irqchip driver. 3350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * 4350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 5350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * 6350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * based on GPL'ed 2.6 kernel sources 7350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * (c) Marvell International Ltd. 8350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * 9350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * This file is licensed under the terms of the GNU General Public 10350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * License version 2. This program is licensed "as is" without any 11350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * warranty of any kind, whether express or implied. 12350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth */ 13350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 14350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include <linux/io.h> 15350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include <linux/irq.h> 16350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include <linux/irqchip/chained_irq.h> 17350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include <linux/of_address.h> 18350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include <linux/of_irq.h> 19350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 20350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#include "irqchip.h" 21350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 22350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_ENABLE_L 0x00 23350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_ENABLE_H 0x04 24350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_MASK_L 0x08 25350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_MASK_H 0x0c 26350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_FINALSTATUS_L 0x30 27350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth#define APB_INT_FINALSTATUS_H 0x34 28350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 29350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarthstatic void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc) 30350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth{ 31350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct irq_chip *chip = irq_get_chip(irq); 32350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct irq_chip_generic *gc = irq_get_handler_data(irq); 33350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct irq_domain *d = gc->private; 34350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth u32 stat; 35350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth int n; 36350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 37350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth chained_irq_enter(chip, desc); 38350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 39350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth for (n = 0; n < gc->num_ct; n++) { 40350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth stat = readl_relaxed(gc->reg_base + 41350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth APB_INT_FINALSTATUS_L + 4 * n); 42350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth while (stat) { 43350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth u32 hwirq = ffs(stat) - 1; 44350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth generic_handle_irq(irq_find_mapping(d, 45350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->irq_base + hwirq + 32 * n)); 46350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth stat &= ~(1 << hwirq); 47350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 48350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 49350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 50350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth chained_irq_exit(chip, desc); 51350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth} 52350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 53350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarthstatic int __init dw_apb_ictl_init(struct device_node *np, 54350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct device_node *parent) 55350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth{ 56350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 57350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct resource r; 58350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct irq_domain *domain; 59350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth struct irq_chip_generic *gc; 60350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth void __iomem *iobase; 61350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth int ret, nrirqs, irq; 62350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth u32 reg; 63350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 64350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth /* Map the parent interrupt for the chained handler */ 65350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth irq = irq_of_parse_and_map(np, 0); 66350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (irq <= 0) { 67350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to parse irq\n", np->full_name); 68350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth return -EINVAL; 69350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 70350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 71350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth ret = of_address_to_resource(np, 0, &r); 72350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (ret) { 73350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to get resource\n", np->full_name); 74350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth return ret; 75350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 76350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 77350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (!request_mem_region(r.start, resource_size(&r), np->full_name)) { 78350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to request mem region\n", np->full_name); 79350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth return -ENOMEM; 80350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 81350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 82350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth iobase = ioremap(r.start, resource_size(&r)); 83350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (!iobase) { 84350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to map resource\n", np->full_name); 85350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth ret = -ENOMEM; 86350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth goto err_release; 87350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 88350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 89350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth /* 90350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * DW IP can be configured to allow 2-64 irqs. We can determine 91350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * the number of irqs supported by writing into enable register 92350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * and look for bits not set, as corresponding flip-flops will 93350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth * have been removed by sythesis tool. 94350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth */ 95350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 96350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth /* mask and enable all interrupts */ 97350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth writel(~0, iobase + APB_INT_MASK_L); 98350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth writel(~0, iobase + APB_INT_MASK_H); 99350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth writel(~0, iobase + APB_INT_ENABLE_L); 100350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth writel(~0, iobase + APB_INT_ENABLE_H); 101350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 102350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth reg = readl(iobase + APB_INT_ENABLE_H); 103350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (reg) 104350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth nrirqs = 32 + fls(reg); 105350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth else 106350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth nrirqs = fls(readl(iobase + APB_INT_ENABLE_L)); 107350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 108350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth domain = irq_domain_add_linear(np, nrirqs, 109350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth &irq_generic_chip_ops, NULL); 110350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (!domain) { 111350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to add irq domain\n", np->full_name); 112350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth ret = -ENOMEM; 113350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth goto err_unmap; 114350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 115350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 116350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1, 117350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth np->name, handle_level_irq, clr, 0, 118350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth IRQ_GC_INIT_MASK_CACHE); 119350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (ret) { 120350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth pr_err("%s: unable to alloc irq domain gc\n", np->full_name); 121350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth goto err_unmap; 122350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 123350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 124350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc = irq_get_domain_generic_chip(domain, 0); 125350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->private = domain; 126350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->reg_base = iobase; 127350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 128350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[0].regs.mask = APB_INT_MASK_L; 129350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; 130350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; 131350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 132350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth if (nrirqs > 32) { 133350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[1].regs.mask = APB_INT_MASK_H; 134350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit; 135350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit; 136350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth } 137350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 138350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth irq_set_handler_data(irq, gc); 139350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth irq_set_chained_handler(irq, dw_apb_ictl_handler); 140350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 141350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth return 0; 142350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth 143350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbartherr_unmap: 144350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth iounmap(iobase); 145350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbartherr_release: 146350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth release_mem_region(r.start, resource_size(&r)); 147350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth return ret; 148350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth} 149350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian HesselbarthIRQCHIP_DECLARE(dw_apb_ictl, 150350d71b94fc9ed4ba9a349786f928aa5e594adc1Sebastian Hesselbarth "snps,dw-apb-ictl", dw_apb_ictl_init); 151