diff options
author | Masato Noguchi <Masato.Noguchi@jp.sony.com> | 2007-09-07 04:28:27 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-09-10 14:30:36 -0400 |
commit | b7f90a406ff72d6698b619210c205e3375dd099a (patch) | |
tree | 04897cefacd9589699f99dcd50f6469a9cab687f /arch/powerpc | |
parent | b21010ed6498391c0f359f2a89c907533fe07fec (diff) |
[POWERPC] cell/PS3: Fix a bug that causes the PS3 to hang on the SPU Class 0 interrupt.
The Cell BE Architecture spec states that the SPU MFC Class 0 interrupt
is edge-triggered. The current spu interrupt handler assumes this
behavior and does not clear the interrupt status.
The PS3 hypervisor visualizes all SPU interrupts as level, and on return
from the interrupt handler the hypervisor will deliver a new virtual
interrupt for any unmasked interrupts which for which the status has not
been cleared. This fix clears the interrupt status in the interrupt
handler.
Signed-off-by: Masato Noguchi <Masato.Noguchi@jp.sony.com>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Acked-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/platforms/cell/spu_base.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 095a30304c56..106d2921e2d9 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c | |||
@@ -236,27 +236,34 @@ static irqreturn_t | |||
236 | spu_irq_class_0(int irq, void *data) | 236 | spu_irq_class_0(int irq, void *data) |
237 | { | 237 | { |
238 | struct spu *spu; | 238 | struct spu *spu; |
239 | unsigned long stat, mask; | ||
239 | 240 | ||
240 | spu = data; | 241 | spu = data; |
241 | spu->class_0_pending = 1; | 242 | |
243 | mask = spu_int_mask_get(spu, 0); | ||
244 | stat = spu_int_stat_get(spu, 0); | ||
245 | stat &= mask; | ||
246 | |||
247 | spin_lock(&spu->register_lock); | ||
248 | spu->class_0_pending |= stat; | ||
249 | spin_unlock(&spu->register_lock); | ||
250 | |||
242 | spu->stop_callback(spu); | 251 | spu->stop_callback(spu); |
243 | 252 | ||
253 | spu_int_stat_clear(spu, 0, stat); | ||
254 | |||
244 | return IRQ_HANDLED; | 255 | return IRQ_HANDLED; |
245 | } | 256 | } |
246 | 257 | ||
247 | int | 258 | int |
248 | spu_irq_class_0_bottom(struct spu *spu) | 259 | spu_irq_class_0_bottom(struct spu *spu) |
249 | { | 260 | { |
250 | unsigned long stat, mask; | ||
251 | unsigned long flags; | 261 | unsigned long flags; |
252 | 262 | unsigned long stat; | |
253 | spu->class_0_pending = 0; | ||
254 | 263 | ||
255 | spin_lock_irqsave(&spu->register_lock, flags); | 264 | spin_lock_irqsave(&spu->register_lock, flags); |
256 | mask = spu_int_mask_get(spu, 0); | 265 | stat = spu->class_0_pending; |
257 | stat = spu_int_stat_get(spu, 0); | 266 | spu->class_0_pending = 0; |
258 | |||
259 | stat &= mask; | ||
260 | 267 | ||
261 | if (stat & 1) /* invalid DMA alignment */ | 268 | if (stat & 1) /* invalid DMA alignment */ |
262 | __spu_trap_dma_align(spu); | 269 | __spu_trap_dma_align(spu); |
@@ -267,7 +274,6 @@ spu_irq_class_0_bottom(struct spu *spu) | |||
267 | if (stat & 4) /* error on SPU */ | 274 | if (stat & 4) /* error on SPU */ |
268 | __spu_trap_error(spu); | 275 | __spu_trap_error(spu); |
269 | 276 | ||
270 | spu_int_stat_clear(spu, 0, stat); | ||
271 | spin_unlock_irqrestore(&spu->register_lock, flags); | 277 | spin_unlock_irqrestore(&spu->register_lock, flags); |
272 | 278 | ||
273 | return (stat & 0x7) ? -EIO : 0; | 279 | return (stat & 0x7) ? -EIO : 0; |