]> err.no Git - linux-2.6/blobdiff - arch/powerpc/platforms/cell/spu_base.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[linux-2.6] / arch / powerpc / platforms / cell / spu_base.c
index 95001cdfaa260dfa48377aa9ec3062811834b8fe..6bab44b7716b2d525bff0720c0c6a0f436a55acb 100644 (file)
@@ -81,9 +81,12 @@ struct spu_slb {
 void spu_invalidate_slbs(struct spu *spu)
 {
        struct spu_priv2 __iomem *priv2 = spu->priv2;
+       unsigned long flags;
 
+       spin_lock_irqsave(&spu->register_lock, flags);
        if (spu_mfc_sr1_get(spu) & MFC_STATE1_RELOCATE_MASK)
                out_be64(&priv2->slb_invalidate_all_W, 0UL);
+       spin_unlock_irqrestore(&spu->register_lock, flags);
 }
 EXPORT_SYMBOL_GPL(spu_invalidate_slbs);
 
@@ -132,27 +135,6 @@ int spu_64k_pages_available(void)
 }
 EXPORT_SYMBOL_GPL(spu_64k_pages_available);
 
-static int __spu_trap_invalid_dma(struct spu *spu)
-{
-       pr_debug("%s\n", __FUNCTION__);
-       spu->dma_callback(spu, SPE_EVENT_INVALID_DMA);
-       return 0;
-}
-
-static int __spu_trap_dma_align(struct spu *spu)
-{
-       pr_debug("%s\n", __FUNCTION__);
-       spu->dma_callback(spu, SPE_EVENT_DMA_ALIGNMENT);
-       return 0;
-}
-
-static int __spu_trap_error(struct spu *spu)
-{
-       pr_debug("%s\n", __FUNCTION__);
-       spu->dma_callback(spu, SPE_EVENT_SPE_ERROR);
-       return 0;
-}
-
 static void spu_restart_dma(struct spu *spu)
 {
        struct spu_priv2 __iomem *priv2 = spu->priv2;
@@ -169,7 +151,11 @@ static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb)
                        __func__, slbe, slb->vsid, slb->esid);
 
        out_be64(&priv2->slb_index_W, slbe);
+       /* set invalid before writing vsid */
+       out_be64(&priv2->slb_esid_RW, 0);
+       /* now it's safe to write the vsid */
        out_be64(&priv2->slb_vsid_RW, slb->vsid);
+       /* setting the new esid makes the entry valid again */
        out_be64(&priv2->slb_esid_RW, slb->esid);
 }
 
@@ -179,15 +165,8 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
        struct spu_slb slb;
        int psize;
 
-       pr_debug("%s\n", __FUNCTION__);
+       pr_debug("%s\n", __func__);
 
-       if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE, &spu->flags)) {
-               /* SLBs are pre-loaded for context switch, so
-                * we should never get here!
-                */
-               printk("%s: invalid access during switch!\n", __func__);
-               return 1;
-       }
        slb.esid = (ea & ESID_MASK) | SLB_ESID_V;
 
        switch(REGION_ID(ea)) {
@@ -236,7 +215,7 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
 extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX
 static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
 {
-       pr_debug("%s, %lx, %lx\n", __FUNCTION__, dsisr, ea);
+       pr_debug("%s, %lx, %lx\n", __func__, dsisr, ea);
 
        /* Handle kernel space hash faults immediately.
           User hash faults need to be deferred to process context. */
@@ -247,15 +226,12 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
                return 0;
        }
 
-       if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE, &spu->flags)) {
-               printk("%s: invalid access during switch!\n", __func__);
-               return 1;
-       }
-
+       spu->class_0_pending = 0;
        spu->dar = ea;
        spu->dsisr = dsisr;
-       mb();
+
        spu->stop_callback(spu);
+
        return 0;
 }
 
@@ -274,20 +250,58 @@ static void __spu_kernel_slb(void *addr, struct spu_slb *slb)
        slb->esid = (ea & ESID_MASK) | SLB_ESID_V;
 }
 
+/**
+ * Given an array of @nr_slbs SLB entries, @slbs, return non-zero if the
+ * address @new_addr is present.
+ */
+static inline int __slb_present(struct spu_slb *slbs, int nr_slbs,
+               void *new_addr)
+{
+       unsigned long ea = (unsigned long)new_addr;
+       int i;
+
+       for (i = 0; i < nr_slbs; i++)
+               if (!((slbs[i].esid ^ ea) & ESID_MASK))
+                       return 1;
+
+       return 0;
+}
+
 /**
  * Setup the SPU kernel SLBs, in preparation for a context save/restore. We
  * need to map both the context save area, and the save/restore code.
+ *
+ * Because the lscsa and code may cross segment boundaires, we check to see
+ * if mappings are required for the start and end of each range. We currently
+ * assume that the mappings are smaller that one segment - if not, something
+ * is seriously wrong.
  */
-void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, void *code)
+void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
+               void *code, int code_size)
 {
-       struct spu_slb code_slb, lscsa_slb;
+       struct spu_slb slbs[4];
+       int i, nr_slbs = 0;
+       /* start and end addresses of both mappings */
+       void *addrs[] = {
+               lscsa, (void *)lscsa + sizeof(*lscsa) - 1,
+               code, code + code_size - 1
+       };
+
+       /* check the set of addresses, and create a new entry in the slbs array
+        * if there isn't already a SLB for that address */
+       for (i = 0; i < ARRAY_SIZE(addrs); i++) {
+               if (__slb_present(slbs, nr_slbs, addrs[i]))
+                       continue;
 
-       __spu_kernel_slb(lscsa, &lscsa_slb);
-       __spu_kernel_slb(code, &code_slb);
+               __spu_kernel_slb(addrs[i], &slbs[nr_slbs]);
+               nr_slbs++;
+       }
 
-       spu_load_slb(spu, 0, &lscsa_slb);
-       if (lscsa_slb.esid != code_slb.esid)
-               spu_load_slb(spu, 1, &code_slb);
+       spin_lock_irq(&spu->register_lock);
+       /* Add the set of SLBs */
+       for (i = 0; i < nr_slbs; i++)
+               spu_load_slb(spu, i, &slbs[i]);
+       spin_unlock_irq(&spu->register_lock);
 }
 EXPORT_SYMBOL_GPL(spu_setup_kernel_slbs);
 
@@ -299,12 +313,13 @@ spu_irq_class_0(int irq, void *data)
 
        spu = data;
 
+       spin_lock(&spu->register_lock);
        mask = spu_int_mask_get(spu, 0);
-       stat = spu_int_stat_get(spu, 0);
-       stat &= mask;
+       stat = spu_int_stat_get(spu, 0) & mask;
 
-       spin_lock(&spu->register_lock);
        spu->class_0_pending |= stat;
+       spu->dsisr = spu_mfc_dsisr_get(spu);
+       spu->dar = spu_mfc_dar_get(spu);
        spin_unlock(&spu->register_lock);
 
        spu->stop_callback(spu);
@@ -314,31 +329,6 @@ spu_irq_class_0(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-int
-spu_irq_class_0_bottom(struct spu *spu)
-{
-       unsigned long flags;
-       unsigned long stat;
-
-       spin_lock_irqsave(&spu->register_lock, flags);
-       stat = spu->class_0_pending;
-       spu->class_0_pending = 0;
-
-       if (stat & 1) /* invalid DMA alignment */
-               __spu_trap_dma_align(spu);
-
-       if (stat & 2) /* invalid MFC DMA */
-               __spu_trap_invalid_dma(spu);
-
-       if (stat & 4) /* error on SPU */
-               __spu_trap_error(spu);
-
-       spin_unlock_irqrestore(&spu->register_lock, flags);
-
-       return (stat & 0x7) ? -EIO : 0;
-}
-EXPORT_SYMBOL_GPL(spu_irq_class_0_bottom);
-
 static irqreturn_t
 spu_irq_class_1(int irq, void *data)
 {
@@ -353,24 +343,24 @@ spu_irq_class_1(int irq, void *data)
        stat  = spu_int_stat_get(spu, 1) & mask;
        dar   = spu_mfc_dar_get(spu);
        dsisr = spu_mfc_dsisr_get(spu);
-       if (stat & 2) /* mapping fault */
+       if (stat & CLASS1_STORAGE_FAULT_INTR)
                spu_mfc_dsisr_set(spu, 0ul);
        spu_int_stat_clear(spu, 1, stat);
-       spin_unlock(&spu->register_lock);
-       pr_debug("%s: %lx %lx %lx %lx\n", __FUNCTION__, mask, stat,
-                       dar, dsisr);
 
-       if (stat & 1) /* segment fault */
+       if (stat & CLASS1_SEGMENT_FAULT_INTR)
                __spu_trap_data_seg(spu, dar);
 
-       if (stat & 2) { /* mapping fault */
+       spin_unlock(&spu->register_lock);
+       pr_debug("%s: %lx %lx %lx %lx\n", __func__, mask, stat,
+                       dar, dsisr);
+
+       if (stat & CLASS1_STORAGE_FAULT_INTR)
                __spu_trap_data_map(spu, dar, dsisr);
-       }
 
-       if (stat & 4) /* ls compare & suspend on get */
+       if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_GET_INTR)
                ;
 
-       if (stat & 8) /* ls compare & suspend on put */
+       if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_PUT_INTR)
                ;
 
        return stat ? IRQ_HANDLED : IRQ_NONE;
@@ -382,6 +372,8 @@ spu_irq_class_2(int irq, void *data)
        struct spu *spu;
        unsigned long stat;
        unsigned long mask;
+       const int mailbox_intrs =
+               CLASS2_MAILBOX_THRESHOLD_INTR | CLASS2_MAILBOX_INTR;
 
        spu = data;
        spin_lock(&spu->register_lock);
@@ -389,31 +381,30 @@ spu_irq_class_2(int irq, void *data)
        mask = spu_int_mask_get(spu, 2);
        /* ignore interrupts we're not waiting for */
        stat &= mask;
-       /*
-        * mailbox interrupts (0x1 and 0x10) are level triggered.
-        * mask them now before acknowledging.
-        */
-       if (stat & 0x11)
-               spu_int_mask_and(spu, 2, ~(stat & 0x11));
+
+       /* mailbox interrupts are level triggered. mask them now before
+        * acknowledging */
+       if (stat & mailbox_intrs)
+               spu_int_mask_and(spu, 2, ~(stat & mailbox_intrs));
        /* acknowledge all interrupts before the callbacks */
        spu_int_stat_clear(spu, 2, stat);
        spin_unlock(&spu->register_lock);
 
        pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, mask);
 
-       if (stat & 1)  /* PPC core mailbox */
+       if (stat & CLASS2_MAILBOX_INTR)
                spu->ibox_callback(spu);
 
-       if (stat & 2) /* SPU stop-and-signal */
+       if (stat & CLASS2_SPU_STOP_INTR)
                spu->stop_callback(spu);
 
-       if (stat & 4) /* SPU halted */
+       if (stat & CLASS2_SPU_HALT_INTR)
                spu->stop_callback(spu);
 
-       if (stat & 8) /* DMA tag group complete */
+       if (stat & CLASS2_SPU_DMA_TAG_GROUP_COMPLETE_INTR)
                spu->mfc_callback(spu);
 
-       if (stat & 0x10) /* SPU mailbox threshold */
+       if (stat & CLASS2_MAILBOX_THRESHOLD_INTR)
                spu->wbox_callback(spu);
 
        spu->stats.class2_intr++;
@@ -518,7 +509,7 @@ static int spu_shutdown(struct sys_device *sysdev)
 }
 
 static struct sysdev_class spu_sysdev_class = {
-       set_kset_name("spu"),
+       .name = "spu",
        .shutdown = spu_shutdown,
 };
 
@@ -538,13 +529,27 @@ EXPORT_SYMBOL_GPL(spu_add_sysdev_attr);
 int spu_add_sysdev_attr_group(struct attribute_group *attrs)
 {
        struct spu *spu;
+       int rc = 0;
 
        mutex_lock(&spu_full_list_mutex);
-       list_for_each_entry(spu, &spu_full_list, full_list)
-               sysfs_create_group(&spu->sysdev.kobj, attrs);
+       list_for_each_entry(spu, &spu_full_list, full_list) {
+               rc = sysfs_create_group(&spu->sysdev.kobj, attrs);
+
+               /* we're in trouble here, but try unwinding anyway */
+               if (rc) {
+                       printk(KERN_ERR "%s: can't create sysfs group '%s'\n",
+                                       __func__, attrs->name);
+
+                       list_for_each_entry_continue_reverse(spu,
+                                       &spu_full_list, full_list)
+                               sysfs_remove_group(&spu->sysdev.kobj, attrs);
+                       break;
+               }
+       }
+
        mutex_unlock(&spu_full_list_mutex);
 
-       return 0;
+       return rc;
 }
 EXPORT_SYMBOL_GPL(spu_add_sysdev_attr_group);
 
@@ -721,7 +726,7 @@ static int __init init_spu_base(void)
 
        if (ret < 0) {
                printk(KERN_WARNING "%s: Error initializing spus\n",
-                       __FUNCTION__);
+                       __func__);
                goto out_unregister_sysdev_class;
        }