diff options
Diffstat (limited to 'arch/powerpc/sysdev/uic.c')
-rw-r--r-- | arch/powerpc/sysdev/uic.c | 75 |
1 files changed, 63 insertions, 12 deletions
diff --git a/arch/powerpc/sysdev/uic.c b/arch/powerpc/sysdev/uic.c index 89059895a20d..5149716c734d 100644 --- a/arch/powerpc/sysdev/uic.c +++ b/arch/powerpc/sysdev/uic.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <linux/irq.h> | 25 | #include <linux/irq.h> |
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/kernel_stat.h> | ||
27 | #include <asm/irq.h> | 28 | #include <asm/irq.h> |
28 | #include <asm/io.h> | 29 | #include <asm/io.h> |
29 | #include <asm/prom.h> | 30 | #include <asm/prom.h> |
@@ -55,9 +56,6 @@ struct uic { | |||
55 | 56 | ||
56 | /* For secondary UICs, the cascade interrupt's irqaction */ | 57 | /* For secondary UICs, the cascade interrupt's irqaction */ |
57 | struct irqaction cascade; | 58 | struct irqaction cascade; |
58 | |||
59 | /* The device node of the interrupt controller */ | ||
60 | struct device_node *of_node; | ||
61 | }; | 59 | }; |
62 | 60 | ||
63 | static void uic_unmask_irq(unsigned int virq) | 61 | static void uic_unmask_irq(unsigned int virq) |
@@ -142,7 +140,7 @@ static int uic_set_irq_type(unsigned int virq, unsigned int flow_type) | |||
142 | 140 | ||
143 | desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); | 141 | desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); |
144 | desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; | 142 | desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; |
145 | if (trigger) | 143 | if (!trigger) |
146 | desc->status |= IRQ_LEVEL; | 144 | desc->status |= IRQ_LEVEL; |
147 | 145 | ||
148 | spin_unlock_irqrestore(&uic->lock, flags); | 146 | spin_unlock_irqrestore(&uic->lock, flags); |
@@ -159,10 +157,62 @@ static struct irq_chip uic_irq_chip = { | |||
159 | .set_type = uic_set_irq_type, | 157 | .set_type = uic_set_irq_type, |
160 | }; | 158 | }; |
161 | 159 | ||
162 | static int uic_host_match(struct irq_host *h, struct device_node *node) | 160 | /** |
161 | * handle_uic_irq - irq flow handler for UIC | ||
162 | * @irq: the interrupt number | ||
163 | * @desc: the interrupt description structure for this irq | ||
164 | * | ||
165 | * This is modified version of the generic handle_level_irq() suitable | ||
166 | * for the UIC. On the UIC, acking (i.e. clearing the SR bit) a level | ||
167 | * irq will have no effect if the interrupt is still asserted by the | ||
168 | * device, even if the interrupt is already masked. Therefore, unlike | ||
169 | * the standard handle_level_irq(), we must ack the interrupt *after* | ||
170 | * invoking the ISR (which should have de-asserted the interrupt in | ||
171 | * the external source). For edge interrupts we ack at the beginning | ||
172 | * instead of the end, to keep the window in which we can miss an | ||
173 | * interrupt as small as possible. | ||
174 | */ | ||
175 | void fastcall handle_uic_irq(unsigned int irq, struct irq_desc *desc) | ||
163 | { | 176 | { |
164 | struct uic *uic = h->host_data; | 177 | unsigned int cpu = smp_processor_id(); |
165 | return uic->of_node == node; | 178 | struct irqaction *action; |
179 | irqreturn_t action_ret; | ||
180 | |||
181 | spin_lock(&desc->lock); | ||
182 | if (desc->status & IRQ_LEVEL) | ||
183 | desc->chip->mask(irq); | ||
184 | else | ||
185 | desc->chip->mask_ack(irq); | ||
186 | |||
187 | if (unlikely(desc->status & IRQ_INPROGRESS)) | ||
188 | goto out_unlock; | ||
189 | desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); | ||
190 | kstat_cpu(cpu).irqs[irq]++; | ||
191 | |||
192 | /* | ||
193 | * If its disabled or no action available | ||
194 | * keep it masked and get out of here | ||
195 | */ | ||
196 | action = desc->action; | ||
197 | if (unlikely(!action || (desc->status & IRQ_DISABLED))) { | ||
198 | desc->status |= IRQ_PENDING; | ||
199 | goto out_unlock; | ||
200 | } | ||
201 | |||
202 | desc->status |= IRQ_INPROGRESS; | ||
203 | desc->status &= ~IRQ_PENDING; | ||
204 | spin_unlock(&desc->lock); | ||
205 | |||
206 | action_ret = handle_IRQ_event(irq, action); | ||
207 | |||
208 | spin_lock(&desc->lock); | ||
209 | desc->status &= ~IRQ_INPROGRESS; | ||
210 | if (desc->status & IRQ_LEVEL) | ||
211 | desc->chip->ack(irq); | ||
212 | if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | ||
213 | desc->chip->unmask(irq); | ||
214 | out_unlock: | ||
215 | spin_unlock(&desc->lock); | ||
166 | } | 216 | } |
167 | 217 | ||
168 | static int uic_host_map(struct irq_host *h, unsigned int virq, | 218 | static int uic_host_map(struct irq_host *h, unsigned int virq, |
@@ -173,7 +223,7 @@ static int uic_host_map(struct irq_host *h, unsigned int virq, | |||
173 | set_irq_chip_data(virq, uic); | 223 | set_irq_chip_data(virq, uic); |
174 | /* Despite the name, handle_level_irq() works for both level | 224 | /* Despite the name, handle_level_irq() works for both level |
175 | * and edge irqs on UIC. FIXME: check this is correct */ | 225 | * and edge irqs on UIC. FIXME: check this is correct */ |
176 | set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq); | 226 | set_irq_chip_and_handler(virq, &uic_irq_chip, handle_uic_irq); |
177 | 227 | ||
178 | /* Set default irq type */ | 228 | /* Set default irq type */ |
179 | set_irq_type(virq, IRQ_TYPE_NONE); | 229 | set_irq_type(virq, IRQ_TYPE_NONE); |
@@ -194,7 +244,6 @@ static int uic_host_xlate(struct irq_host *h, struct device_node *ct, | |||
194 | } | 244 | } |
195 | 245 | ||
196 | static struct irq_host_ops uic_host_ops = { | 246 | static struct irq_host_ops uic_host_ops = { |
197 | .match = uic_host_match, | ||
198 | .map = uic_host_map, | 247 | .map = uic_host_map, |
199 | .xlate = uic_host_xlate, | 248 | .xlate = uic_host_xlate, |
200 | }; | 249 | }; |
@@ -207,6 +256,9 @@ irqreturn_t uic_cascade(int virq, void *data) | |||
207 | int subvirq; | 256 | int subvirq; |
208 | 257 | ||
209 | msr = mfdcr(uic->dcrbase + UIC_MSR); | 258 | msr = mfdcr(uic->dcrbase + UIC_MSR); |
259 | if (!msr) /* spurious interrupt */ | ||
260 | return IRQ_HANDLED; | ||
261 | |||
210 | src = 32 - ffs(msr); | 262 | src = 32 - ffs(msr); |
211 | 263 | ||
212 | subvirq = irq_linear_revmap(uic->irqhost, src); | 264 | subvirq = irq_linear_revmap(uic->irqhost, src); |
@@ -229,7 +281,6 @@ static struct uic * __init uic_init_one(struct device_node *node) | |||
229 | 281 | ||
230 | memset(uic, 0, sizeof(*uic)); | 282 | memset(uic, 0, sizeof(*uic)); |
231 | spin_lock_init(&uic->lock); | 283 | spin_lock_init(&uic->lock); |
232 | uic->of_node = of_node_get(node); | ||
233 | indexp = of_get_property(node, "cell-index", &len); | 284 | indexp = of_get_property(node, "cell-index", &len); |
234 | if (!indexp || (len != sizeof(u32))) { | 285 | if (!indexp || (len != sizeof(u32))) { |
235 | printk(KERN_ERR "uic: Device node %s has missing or invalid " | 286 | printk(KERN_ERR "uic: Device node %s has missing or invalid " |
@@ -246,8 +297,8 @@ static struct uic * __init uic_init_one(struct device_node *node) | |||
246 | } | 297 | } |
247 | uic->dcrbase = *dcrreg; | 298 | uic->dcrbase = *dcrreg; |
248 | 299 | ||
249 | uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS, | 300 | uic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR, |
250 | &uic_host_ops, -1); | 301 | NR_UIC_INTS, &uic_host_ops, -1); |
251 | if (! uic->irqhost) { | 302 | if (! uic->irqhost) { |
252 | of_node_put(node); | 303 | of_node_put(node); |
253 | return NULL; /* FIXME: panic? */ | 304 | return NULL; /* FIXME: panic? */ |