]> Pileus Git - ~andy/linux/blob - drivers/irqchip/irq-dw-apb-ictl.c
Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[~andy/linux] / drivers / irqchip / irq-dw-apb-ictl.c
1 /*
2  * Synopsys DW APB ICTL irqchip driver.
3  *
4  * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
5  *
6  * based on GPL'ed 2.6 kernel sources
7  *  (c) Marvell International Ltd.
8  *
9  * This file is licensed under the terms of the GNU General Public
10  * License version 2.  This program is licensed "as is" without any
11  * warranty of any kind, whether express or implied.
12  */
13
14 #include <linux/io.h>
15 #include <linux/irq.h>
16 #include <linux/irqchip/chained_irq.h>
17 #include <linux/of_address.h>
18 #include <linux/of_irq.h>
19
20 #include "irqchip.h"
21
22 #define APB_INT_ENABLE_L        0x00
23 #define APB_INT_ENABLE_H        0x04
24 #define APB_INT_MASK_L          0x08
25 #define APB_INT_MASK_H          0x0c
26 #define APB_INT_FINALSTATUS_L   0x30
27 #define APB_INT_FINALSTATUS_H   0x34
28
29 static void dw_apb_ictl_handler(unsigned int irq, struct irq_desc *desc)
30 {
31         struct irq_chip *chip = irq_get_chip(irq);
32         struct irq_chip_generic *gc = irq_get_handler_data(irq);
33         struct irq_domain *d = gc->private;
34         u32 stat;
35         int n;
36
37         chained_irq_enter(chip, desc);
38
39         for (n = 0; n < gc->num_ct; n++) {
40                 stat = readl_relaxed(gc->reg_base +
41                                      APB_INT_FINALSTATUS_L + 4 * n);
42                 while (stat) {
43                         u32 hwirq = ffs(stat) - 1;
44                         generic_handle_irq(irq_find_mapping(d,
45                                             gc->irq_base + hwirq + 32 * n));
46                         stat &= ~(1 << hwirq);
47                 }
48         }
49
50         chained_irq_exit(chip, desc);
51 }
52
53 static int __init dw_apb_ictl_init(struct device_node *np,
54                                    struct device_node *parent)
55 {
56         unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
57         struct resource r;
58         struct irq_domain *domain;
59         struct irq_chip_generic *gc;
60         void __iomem *iobase;
61         int ret, nrirqs, irq;
62         u32 reg;
63
64         /* Map the parent interrupt for the chained handler */
65         irq = irq_of_parse_and_map(np, 0);
66         if (irq <= 0) {
67                 pr_err("%s: unable to parse irq\n", np->full_name);
68                 return -EINVAL;
69         }
70
71         ret = of_address_to_resource(np, 0, &r);
72         if (ret) {
73                 pr_err("%s: unable to get resource\n", np->full_name);
74                 return ret;
75         }
76
77         if (!request_mem_region(r.start, resource_size(&r), np->full_name)) {
78                 pr_err("%s: unable to request mem region\n", np->full_name);
79                 return -ENOMEM;
80         }
81
82         iobase = ioremap(r.start, resource_size(&r));
83         if (!iobase) {
84                 pr_err("%s: unable to map resource\n", np->full_name);
85                 ret = -ENOMEM;
86                 goto err_release;
87         }
88
89         /*
90          * DW IP can be configured to allow 2-64 irqs. We can determine
91          * the number of irqs supported by writing into enable register
92          * and look for bits not set, as corresponding flip-flops will
93          * have been removed by sythesis tool.
94          */
95
96         /* mask and enable all interrupts */
97         writel(~0, iobase + APB_INT_MASK_L);
98         writel(~0, iobase + APB_INT_MASK_H);
99         writel(~0, iobase + APB_INT_ENABLE_L);
100         writel(~0, iobase + APB_INT_ENABLE_H);
101
102         reg = readl(iobase + APB_INT_ENABLE_H);
103         if (reg)
104                 nrirqs = 32 + fls(reg);
105         else
106                 nrirqs = fls(readl(iobase + APB_INT_ENABLE_L));
107
108         domain = irq_domain_add_linear(np, nrirqs,
109                                        &irq_generic_chip_ops, NULL);
110         if (!domain) {
111                 pr_err("%s: unable to add irq domain\n", np->full_name);
112                 ret = -ENOMEM;
113                 goto err_unmap;
114         }
115
116         ret = irq_alloc_domain_generic_chips(domain, 32, (nrirqs > 32) ? 2 : 1,
117                                              np->name, handle_level_irq, clr, 0,
118                                              IRQ_GC_INIT_MASK_CACHE);
119         if (ret) {
120                 pr_err("%s: unable to alloc irq domain gc\n", np->full_name);
121                 goto err_unmap;
122         }
123
124         gc = irq_get_domain_generic_chip(domain, 0);
125         gc->private = domain;
126         gc->reg_base = iobase;
127
128         gc->chip_types[0].regs.mask = APB_INT_MASK_L;
129         gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
130         gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
131
132         if (nrirqs > 32) {
133                 gc->chip_types[1].regs.mask = APB_INT_MASK_H;
134                 gc->chip_types[1].chip.irq_mask = irq_gc_mask_set_bit;
135                 gc->chip_types[1].chip.irq_unmask = irq_gc_mask_clr_bit;
136         }
137
138         irq_set_handler_data(irq, gc);
139         irq_set_chained_handler(irq, dw_apb_ictl_handler);
140
141         return 0;
142
143 err_unmap:
144         iounmap(iobase);
145 err_release:
146         release_mem_region(r.start, resource_size(&r));
147         return ret;
148 }
149 IRQCHIP_DECLARE(dw_apb_ictl,
150                 "snps,dw-apb-ictl", dw_apb_ictl_init);