diff options
author | Tony Prisk <linux@prisktech.co.nz> | 2012-10-10 03:59:32 -0400 |
---|---|---|
committer | Tony Prisk <linux@prisktech.co.nz> | 2012-10-25 23:45:11 -0400 |
commit | 0c464d588b6911ca84851ce4762695201d866c7c (patch) | |
tree | 794563933d34573f72c43609b8cb357c9f11e6c7 /arch/arm/mach-vt8500/irq.c | |
parent | ddffeb8c4d0331609ef2581d84de4d763607bd37 (diff) |
arm: vt8500: Convert irq.c for multiplatform integration
This patch converts arch-vt8500/irq.c to MULTI_IRQ_HANDLER and
SPARSE_IRQ. IRQ domain is changed from legacy to linear.
Also, remove legacy code in include/mach/entry-macro.S and
include/mach/irq.h to prepare for multiplatform.
Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm/mach-vt8500/irq.c')
-rw-r--r-- | arch/arm/mach-vt8500/irq.c | 108 |
1 files changed, 70 insertions, 38 deletions
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c index f8f9ab9bc56e..b9cf5ce9efbb 100644 --- a/arch/arm/mach-vt8500/irq.c +++ b/arch/arm/mach-vt8500/irq.c | |||
@@ -36,7 +36,7 @@ | |||
36 | #include <linux/of_address.h> | 36 | #include <linux/of_address.h> |
37 | 37 | ||
38 | #include <asm/irq.h> | 38 | #include <asm/irq.h> |
39 | 39 | #include <asm/exception.h> | |
40 | 40 | ||
41 | #define VT8500_ICPC_IRQ 0x20 | 41 | #define VT8500_ICPC_IRQ 0x20 |
42 | #define VT8500_ICPC_FIQ 0x24 | 42 | #define VT8500_ICPC_FIQ 0x24 |
@@ -66,30 +66,34 @@ | |||
66 | #define VT8500_EDGE ( VT8500_TRIGGER_RISING \ | 66 | #define VT8500_EDGE ( VT8500_TRIGGER_RISING \ |
67 | | VT8500_TRIGGER_FALLING) | 67 | | VT8500_TRIGGER_FALLING) |
68 | 68 | ||
69 | static int irq_cnt; | 69 | /* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ |
70 | #define VT8500_INTC_MAX 2 | ||
70 | 71 | ||
71 | struct vt8500_irq_priv { | 72 | struct vt8500_irq_data { |
72 | void __iomem *base; | 73 | void __iomem *base; /* IO Memory base address */ |
74 | struct irq_domain *domain; /* Domain for this controller */ | ||
73 | }; | 75 | }; |
74 | 76 | ||
77 | /* Global variable for accessing io-mem addresses */ | ||
78 | static struct vt8500_irq_data intc[VT8500_INTC_MAX]; | ||
79 | static u32 active_cnt = 0; | ||
80 | |||
75 | static void vt8500_irq_mask(struct irq_data *d) | 81 | static void vt8500_irq_mask(struct irq_data *d) |
76 | { | 82 | { |
77 | struct vt8500_irq_priv *priv = | 83 | struct vt8500_irq_data *priv = d->domain->host_data; |
78 | (struct vt8500_irq_priv *)(d->domain->host_data); | ||
79 | void __iomem *base = priv->base; | 84 | void __iomem *base = priv->base; |
80 | u8 edge; | 85 | void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); |
86 | u8 edge, dctr; | ||
87 | u32 status; | ||
81 | 88 | ||
82 | edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; | 89 | edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; |
83 | if (edge) { | 90 | if (edge) { |
84 | void __iomem *stat_reg = base + VT8500_ICIS | 91 | status = readl(stat_reg); |
85 | + (d->hwirq < 32 ? 0 : 4); | ||
86 | unsigned status = readl(stat_reg); | ||
87 | 92 | ||
88 | status |= (1 << (d->hwirq & 0x1f)); | 93 | status |= (1 << (d->hwirq & 0x1f)); |
89 | writel(status, stat_reg); | 94 | writel(status, stat_reg); |
90 | } else { | 95 | } else { |
91 | u8 dctr = readb(base + VT8500_ICDC + d->hwirq); | 96 | dctr = readb(base + VT8500_ICDC + d->hwirq); |
92 | |||
93 | dctr &= ~VT8500_INT_ENABLE; | 97 | dctr &= ~VT8500_INT_ENABLE; |
94 | writeb(dctr, base + VT8500_ICDC + d->hwirq); | 98 | writeb(dctr, base + VT8500_ICDC + d->hwirq); |
95 | } | 99 | } |
@@ -97,8 +101,7 @@ static void vt8500_irq_mask(struct irq_data *d) | |||
97 | 101 | ||
98 | static void vt8500_irq_unmask(struct irq_data *d) | 102 | static void vt8500_irq_unmask(struct irq_data *d) |
99 | { | 103 | { |
100 | struct vt8500_irq_priv *priv = | 104 | struct vt8500_irq_data *priv = d->domain->host_data; |
101 | (struct vt8500_irq_priv *)(d->domain->host_data); | ||
102 | void __iomem *base = priv->base; | 105 | void __iomem *base = priv->base; |
103 | u8 dctr; | 106 | u8 dctr; |
104 | 107 | ||
@@ -109,8 +112,7 @@ static void vt8500_irq_unmask(struct irq_data *d) | |||
109 | 112 | ||
110 | static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) | 113 | static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) |
111 | { | 114 | { |
112 | struct vt8500_irq_priv *priv = | 115 | struct vt8500_irq_data *priv = d->domain->host_data; |
113 | (struct vt8500_irq_priv *)(d->domain->host_data); | ||
114 | void __iomem *base = priv->base; | 116 | void __iomem *base = priv->base; |
115 | u8 dctr; | 117 | u8 dctr; |
116 | 118 | ||
@@ -148,17 +150,15 @@ static struct irq_chip vt8500_irq_chip = { | |||
148 | 150 | ||
149 | static void __init vt8500_init_irq_hw(void __iomem *base) | 151 | static void __init vt8500_init_irq_hw(void __iomem *base) |
150 | { | 152 | { |
151 | unsigned int i; | 153 | u32 i; |
152 | 154 | ||
153 | /* Enable rotating priority for IRQ */ | 155 | /* Enable rotating priority for IRQ */ |
154 | writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); | 156 | writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); |
155 | writel(0x00, base + VT8500_ICPC_FIQ); | 157 | writel(0x00, base + VT8500_ICPC_FIQ); |
156 | 158 | ||
157 | for (i = 0; i < 64; i++) { | 159 | /* Disable all interrupts and route them to IRQ */ |
158 | /* Disable all interrupts and route them to IRQ */ | 160 | for (i = 0; i < 64; i++) |
159 | writeb(VT8500_INT_DISABLE | ICDC_IRQ, | 161 | writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); |
160 | base + VT8500_ICDC + i); | ||
161 | } | ||
162 | } | 162 | } |
163 | 163 | ||
164 | static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, | 164 | static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, |
@@ -175,33 +175,67 @@ static struct irq_domain_ops vt8500_irq_domain_ops = { | |||
175 | .xlate = irq_domain_xlate_onecell, | 175 | .xlate = irq_domain_xlate_onecell, |
176 | }; | 176 | }; |
177 | 177 | ||
178 | asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) | ||
179 | { | ||
180 | u32 stat, i; | ||
181 | int irqnr, virq; | ||
182 | void __iomem *base; | ||
183 | |||
184 | /* Loop through each active controller */ | ||
185 | for (i=0; i<active_cnt; i++) { | ||
186 | base = intc[i].base; | ||
187 | irqnr = readl_relaxed(base) & 0x3F; | ||
188 | /* | ||
189 | Highest Priority register default = 63, so check that this | ||
190 | is a real interrupt by checking the status register | ||
191 | */ | ||
192 | if (irqnr == 63) { | ||
193 | stat = readl_relaxed(base + VT8500_ICIS + 4); | ||
194 | if (!(stat & BIT(31))) | ||
195 | continue; | ||
196 | } | ||
197 | |||
198 | virq = irq_find_mapping(intc[i].domain, irqnr); | ||
199 | handle_IRQ(virq, regs); | ||
200 | } | ||
201 | } | ||
202 | |||
178 | int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) | 203 | int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) |
179 | { | 204 | { |
180 | struct irq_domain *vt8500_irq_domain; | ||
181 | struct vt8500_irq_priv *priv; | ||
182 | int irq, i; | 205 | int irq, i; |
183 | struct device_node *np = node; | 206 | struct device_node *np = node; |
184 | 207 | ||
185 | priv = kzalloc(sizeof(struct vt8500_irq_priv), GFP_KERNEL); | 208 | if (active_cnt == VT8500_INTC_MAX) { |
186 | priv->base = of_iomap(np, 0); | 209 | pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", |
210 | __func__); | ||
211 | goto out; | ||
212 | } | ||
213 | |||
214 | intc[active_cnt].base = of_iomap(np, 0); | ||
215 | intc[active_cnt].domain = irq_domain_add_linear(node, 64, | ||
216 | &vt8500_irq_domain_ops, &intc[active_cnt]); | ||
187 | 217 | ||
188 | vt8500_irq_domain = irq_domain_add_legacy(node, 64, irq_cnt, 0, | 218 | if (!intc[active_cnt].base) { |
189 | &vt8500_irq_domain_ops, priv); | 219 | pr_err("%s: Unable to map IO memory\n", __func__); |
190 | if (!vt8500_irq_domain) | 220 | goto out; |
191 | pr_err("%s: Unable to add wmt irq domain!\n", __func__); | 221 | } |
222 | |||
223 | if (!intc[active_cnt].domain) { | ||
224 | pr_err("%s: Unable to add irq domain!\n", __func__); | ||
225 | goto out; | ||
226 | } | ||
192 | 227 | ||
193 | irq_set_default_host(vt8500_irq_domain); | 228 | vt8500_init_irq_hw(intc[active_cnt].base); |
194 | 229 | ||
195 | vt8500_init_irq_hw(priv->base); | 230 | pr_info("vt8500-irq: Added interrupt controller\n"); |
196 | 231 | ||
197 | pr_info("Added IRQ Controller @ %x [virq_base = %d]\n", | 232 | active_cnt++; |
198 | (u32)(priv->base), irq_cnt); | ||
199 | 233 | ||
200 | /* check if this is a slaved controller */ | 234 | /* check if this is a slaved controller */ |
201 | if (of_irq_count(np) != 0) { | 235 | if (of_irq_count(np) != 0) { |
202 | /* check that we have the correct number of interrupts */ | 236 | /* check that we have the correct number of interrupts */ |
203 | if (of_irq_count(np) != 8) { | 237 | if (of_irq_count(np) != 8) { |
204 | pr_err("%s: Incorrect IRQ map for slave controller\n", | 238 | pr_err("%s: Incorrect IRQ map for slaved controller\n", |
205 | __func__); | 239 | __func__); |
206 | return -EINVAL; | 240 | return -EINVAL; |
207 | } | 241 | } |
@@ -213,9 +247,7 @@ int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) | |||
213 | 247 | ||
214 | pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); | 248 | pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); |
215 | } | 249 | } |
216 | 250 | out: | |
217 | irq_cnt += 64; | ||
218 | |||
219 | return 0; | 251 | return 0; |
220 | } | 252 | } |
221 | 253 | ||