[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>
This commit is contained in:
Родитель
b21010ed64
Коммит
b7f90a406f
|
@ -236,27 +236,34 @@ static irqreturn_t
|
||||||
spu_irq_class_0(int irq, void *data)
|
spu_irq_class_0(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct spu *spu;
|
struct spu *spu;
|
||||||
|
unsigned long stat, mask;
|
||||||
|
|
||||||
spu = data;
|
spu = data;
|
||||||
spu->class_0_pending = 1;
|
|
||||||
|
mask = spu_int_mask_get(spu, 0);
|
||||||
|
stat = spu_int_stat_get(spu, 0);
|
||||||
|
stat &= mask;
|
||||||
|
|
||||||
|
spin_lock(&spu->register_lock);
|
||||||
|
spu->class_0_pending |= stat;
|
||||||
|
spin_unlock(&spu->register_lock);
|
||||||
|
|
||||||
spu->stop_callback(spu);
|
spu->stop_callback(spu);
|
||||||
|
|
||||||
|
spu_int_stat_clear(spu, 0, stat);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
spu_irq_class_0_bottom(struct spu *spu)
|
spu_irq_class_0_bottom(struct spu *spu)
|
||||||
{
|
{
|
||||||
unsigned long stat, mask;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned long stat;
|
||||||
spu->class_0_pending = 0;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&spu->register_lock, flags);
|
spin_lock_irqsave(&spu->register_lock, flags);
|
||||||
mask = spu_int_mask_get(spu, 0);
|
stat = spu->class_0_pending;
|
||||||
stat = spu_int_stat_get(spu, 0);
|
spu->class_0_pending = 0;
|
||||||
|
|
||||||
stat &= mask;
|
|
||||||
|
|
||||||
if (stat & 1) /* invalid DMA alignment */
|
if (stat & 1) /* invalid DMA alignment */
|
||||||
__spu_trap_dma_align(spu);
|
__spu_trap_dma_align(spu);
|
||||||
|
@ -267,7 +274,6 @@ spu_irq_class_0_bottom(struct spu *spu)
|
||||||
if (stat & 4) /* error on SPU */
|
if (stat & 4) /* error on SPU */
|
||||||
__spu_trap_error(spu);
|
__spu_trap_error(spu);
|
||||||
|
|
||||||
spu_int_stat_clear(spu, 0, stat);
|
|
||||||
spin_unlock_irqrestore(&spu->register_lock, flags);
|
spin_unlock_irqrestore(&spu->register_lock, flags);
|
||||||
|
|
||||||
return (stat & 0x7) ? -EIO : 0;
|
return (stat & 0x7) ? -EIO : 0;
|
||||||
|
|
|
@ -130,6 +130,7 @@ struct spu {
|
||||||
u64 flags;
|
u64 flags;
|
||||||
u64 dar;
|
u64 dar;
|
||||||
u64 dsisr;
|
u64 dsisr;
|
||||||
|
u64 class_0_pending;
|
||||||
size_t ls_size;
|
size_t ls_size;
|
||||||
unsigned int slb_replace;
|
unsigned int slb_replace;
|
||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
|
@ -138,7 +139,6 @@ struct spu {
|
||||||
unsigned long long timestamp;
|
unsigned long long timestamp;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
pid_t tgid;
|
pid_t tgid;
|
||||||
int class_0_pending;
|
|
||||||
spinlock_t register_lock;
|
spinlock_t register_lock;
|
||||||
|
|
||||||
void (* wbox_callback)(struct spu *spu);
|
void (* wbox_callback)(struct spu *spu);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче