diff options
author | Tony Prisk <linux@prisktech.co.nz> | 2013-03-23 21:12:25 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2013-04-13 01:43:24 -0400 |
commit | 06ff14c05426ec7b7600521c4e1ae19732797e15 (patch) | |
tree | b2c209e9024f49e62ee6d2b9d43ccacaa52a04b5 /drivers/irqchip | |
parent | 26b2da5ae5041e4a6d11a3d9cb1d1097c447d90d (diff) |
irqchip: vt8500: Convert arch-vt8500 to new irqchip infrastructure
This patch moves the arch-vt8500 irq code to drivers/irqchip and converts
it to use the new IRQCHIP_DECLARE and irqchip_init. This allows the removal
of some more functions from common.h
Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-vt8500.c | 259 |
2 files changed, 260 insertions, 0 deletions
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index acf98953272a..48fbdf978494 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -12,3 +12,4 @@ obj-$(CONFIG_ARM_VIC) += irq-vic.o | |||
12 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o | 12 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o |
13 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o | 13 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o |
14 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 14 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o |
15 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | ||
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c new file mode 100644 index 000000000000..d97059550a2c --- /dev/null +++ b/drivers/irqchip/irq-vt8500.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-vt8500/irq.c | ||
3 | * | ||
4 | * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> | ||
5 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
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 | /* | ||
23 | * This file is copied and modified from the original irq.c provided by | ||
24 | * Alexey Charkov. Minor changes have been made for Device Tree Support. | ||
25 | */ | ||
26 | |||
27 | #include <linux/slab.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/irq.h> | ||
30 | #include <linux/irqdomain.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/bitops.h> | ||
33 | |||
34 | #include <linux/of.h> | ||
35 | #include <linux/of_irq.h> | ||
36 | #include <linux/of_address.h> | ||
37 | |||
38 | #include <asm/irq.h> | ||
39 | #include <asm/exception.h> | ||
40 | #include <asm/mach/irq.h> | ||
41 | |||
42 | #include "irqchip.h" | ||
43 | |||
44 | #define VT8500_ICPC_IRQ 0x20 | ||
45 | #define VT8500_ICPC_FIQ 0x24 | ||
46 | #define VT8500_ICDC 0x40 /* Destination Control 64*u32 */ | ||
47 | #define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */ | ||
48 | |||
49 | /* ICPC */ | ||
50 | #define ICPC_MASK 0x3F | ||
51 | #define ICPC_ROTATE BIT(6) | ||
52 | |||
53 | /* IC_DCTR */ | ||
54 | #define ICDC_IRQ 0x00 | ||
55 | #define ICDC_FIQ 0x01 | ||
56 | #define ICDC_DSS0 0x02 | ||
57 | #define ICDC_DSS1 0x03 | ||
58 | #define ICDC_DSS2 0x04 | ||
59 | #define ICDC_DSS3 0x05 | ||
60 | #define ICDC_DSS4 0x06 | ||
61 | #define ICDC_DSS5 0x07 | ||
62 | |||
63 | #define VT8500_INT_DISABLE 0 | ||
64 | #define VT8500_INT_ENABLE BIT(3) | ||
65 | |||
66 | #define VT8500_TRIGGER_HIGH 0 | ||
67 | #define VT8500_TRIGGER_RISING BIT(5) | ||
68 | #define VT8500_TRIGGER_FALLING BIT(6) | ||
69 | #define VT8500_EDGE ( VT8500_TRIGGER_RISING \ | ||
70 | | VT8500_TRIGGER_FALLING) | ||
71 | |||
72 | /* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ | ||
73 | #define VT8500_INTC_MAX 2 | ||
74 | |||
75 | struct vt8500_irq_data { | ||
76 | void __iomem *base; /* IO Memory base address */ | ||
77 | struct irq_domain *domain; /* Domain for this controller */ | ||
78 | }; | ||
79 | |||
80 | /* Global variable for accessing io-mem addresses */ | ||
81 | static struct vt8500_irq_data intc[VT8500_INTC_MAX]; | ||
82 | static u32 active_cnt = 0; | ||
83 | |||
84 | static void vt8500_irq_mask(struct irq_data *d) | ||
85 | { | ||
86 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
87 | void __iomem *base = priv->base; | ||
88 | void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); | ||
89 | u8 edge, dctr; | ||
90 | u32 status; | ||
91 | |||
92 | edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; | ||
93 | if (edge) { | ||
94 | status = readl(stat_reg); | ||
95 | |||
96 | status |= (1 << (d->hwirq & 0x1f)); | ||
97 | writel(status, stat_reg); | ||
98 | } else { | ||
99 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
100 | dctr &= ~VT8500_INT_ENABLE; | ||
101 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static void vt8500_irq_unmask(struct irq_data *d) | ||
106 | { | ||
107 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
108 | void __iomem *base = priv->base; | ||
109 | u8 dctr; | ||
110 | |||
111 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
112 | dctr |= VT8500_INT_ENABLE; | ||
113 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
114 | } | ||
115 | |||
116 | static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) | ||
117 | { | ||
118 | struct vt8500_irq_data *priv = d->domain->host_data; | ||
119 | void __iomem *base = priv->base; | ||
120 | u8 dctr; | ||
121 | |||
122 | dctr = readb(base + VT8500_ICDC + d->hwirq); | ||
123 | dctr &= ~VT8500_EDGE; | ||
124 | |||
125 | switch (flow_type) { | ||
126 | case IRQF_TRIGGER_LOW: | ||
127 | return -EINVAL; | ||
128 | case IRQF_TRIGGER_HIGH: | ||
129 | dctr |= VT8500_TRIGGER_HIGH; | ||
130 | __irq_set_handler_locked(d->irq, handle_level_irq); | ||
131 | break; | ||
132 | case IRQF_TRIGGER_FALLING: | ||
133 | dctr |= VT8500_TRIGGER_FALLING; | ||
134 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
135 | break; | ||
136 | case IRQF_TRIGGER_RISING: | ||
137 | dctr |= VT8500_TRIGGER_RISING; | ||
138 | __irq_set_handler_locked(d->irq, handle_edge_irq); | ||
139 | break; | ||
140 | } | ||
141 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct irq_chip vt8500_irq_chip = { | ||
147 | .name = "vt8500", | ||
148 | .irq_ack = vt8500_irq_mask, | ||
149 | .irq_mask = vt8500_irq_mask, | ||
150 | .irq_unmask = vt8500_irq_unmask, | ||
151 | .irq_set_type = vt8500_irq_set_type, | ||
152 | }; | ||
153 | |||
154 | static void __init vt8500_init_irq_hw(void __iomem *base) | ||
155 | { | ||
156 | u32 i; | ||
157 | |||
158 | /* Enable rotating priority for IRQ */ | ||
159 | writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); | ||
160 | writel(0x00, base + VT8500_ICPC_FIQ); | ||
161 | |||
162 | /* Disable all interrupts and route them to IRQ */ | ||
163 | for (i = 0; i < 64; i++) | ||
164 | writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); | ||
165 | } | ||
166 | |||
167 | static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, | ||
168 | irq_hw_number_t hw) | ||
169 | { | ||
170 | irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); | ||
171 | set_irq_flags(virq, IRQF_VALID); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static struct irq_domain_ops vt8500_irq_domain_ops = { | ||
177 | .map = vt8500_irq_map, | ||
178 | .xlate = irq_domain_xlate_onecell, | ||
179 | }; | ||
180 | |||
181 | asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) | ||
182 | { | ||
183 | u32 stat, i; | ||
184 | int irqnr, virq; | ||
185 | void __iomem *base; | ||
186 | |||
187 | /* Loop through each active controller */ | ||
188 | for (i=0; i<active_cnt; i++) { | ||
189 | base = intc[i].base; | ||
190 | irqnr = readl_relaxed(base) & 0x3F; | ||
191 | /* | ||
192 | Highest Priority register default = 63, so check that this | ||
193 | is a real interrupt by checking the status register | ||
194 | */ | ||
195 | if (irqnr == 63) { | ||
196 | stat = readl_relaxed(base + VT8500_ICIS + 4); | ||
197 | if (!(stat & BIT(31))) | ||
198 | continue; | ||
199 | } | ||
200 | |||
201 | virq = irq_find_mapping(intc[i].domain, irqnr); | ||
202 | handle_IRQ(virq, regs); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) | ||
207 | { | ||
208 | int irq, i; | ||
209 | struct device_node *np = node; | ||
210 | |||
211 | if (active_cnt == VT8500_INTC_MAX) { | ||
212 | pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", | ||
213 | __func__); | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | intc[active_cnt].base = of_iomap(np, 0); | ||
218 | intc[active_cnt].domain = irq_domain_add_linear(node, 64, | ||
219 | &vt8500_irq_domain_ops, &intc[active_cnt]); | ||
220 | |||
221 | if (!intc[active_cnt].base) { | ||
222 | pr_err("%s: Unable to map IO memory\n", __func__); | ||
223 | goto out; | ||
224 | } | ||
225 | |||
226 | if (!intc[active_cnt].domain) { | ||
227 | pr_err("%s: Unable to add irq domain!\n", __func__); | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | set_handle_irq(vt8500_handle_irq); | ||
232 | |||
233 | vt8500_init_irq_hw(intc[active_cnt].base); | ||
234 | |||
235 | pr_info("vt8500-irq: Added interrupt controller\n"); | ||
236 | |||
237 | active_cnt++; | ||
238 | |||
239 | /* check if this is a slaved controller */ | ||
240 | if (of_irq_count(np) != 0) { | ||
241 | /* check that we have the correct number of interrupts */ | ||
242 | if (of_irq_count(np) != 8) { | ||
243 | pr_err("%s: Incorrect IRQ map for slaved controller\n", | ||
244 | __func__); | ||
245 | return -EINVAL; | ||
246 | } | ||
247 | |||
248 | for (i = 0; i < 8; i++) { | ||
249 | irq = irq_of_parse_and_map(np, i); | ||
250 | enable_irq(irq); | ||
251 | } | ||
252 | |||
253 | pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); | ||
254 | } | ||
255 | out: | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init); | ||