]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/sparc64/kernel/pci_psycho.c
of_platform_driver noise on sparce
[linux-2.6-omap-h63xx.git] / arch / sparc64 / kernel / pci_psycho.c
index f85b6bebb0be1c7548366e982270ed4e85c3a320..56605adbb5b3f9c43d57bc91e7f8b185f42ba704 100644 (file)
 #include <asm/irq.h>
 #include <asm/starfire.h>
 #include <asm/prom.h>
-#include <asm/oplib.h>
+#include <asm/upa.h>
 
 #include "pci_impl.h"
 #include "iommu_common.h"
+#include "psycho_common.h"
 
-/* All PSYCHO registers are 64-bits.  The following accessor
- * routines are how they are accessed.  The REG parameter
- * is a physical address.
- */
-#define psycho_read(__reg) \
-({     u64 __ret; \
-       __asm__ __volatile__("ldxa [%1] %2, %0" \
-                            : "=r" (__ret) \
-                            : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
-                            : "memory"); \
-       __ret; \
-})
-#define psycho_write(__reg, __val) \
-       __asm__ __volatile__("stxa %0, [%1] %2" \
-                            : /* no outputs */ \
-                            : "r" (__val), "r" (__reg), \
-                              "i" (ASI_PHYS_BYPASS_EC_E) \
-                            : "memory")
+#define DRIVER_NAME    "psycho"
+#define PFX            DRIVER_NAME ": "
 
 /* Misc. PSYCHO PCI controller register offsets and definitions. */
 #define PSYCHO_CONTROL         0x0010UL
 #define  PSYCHO_PCICTRL_RESV4   0x00000000000000c0UL /* Reserved                     */
 #define  PSYCHO_PCICTRL_AEN     0x000000000000003fUL /* PCI DVMA Arbitration Enable  */
 
-/* U2P Programmer's Manual, page 13-55, configuration space
- * address format:
- * 
- *  32             24 23 16 15    11 10       8 7   2  1 0
- * ---------------------------------------------------------
- * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 |
- * ---------------------------------------------------------
- */
-#define PSYCHO_CONFIG_BASE(PBM)        \
-       ((PBM)->config_space | (1UL << 24))
-#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG)  \
-       (((unsigned long)(BUS)   << 16) |       \
-        ((unsigned long)(DEVFN) << 8)  |       \
-        ((unsigned long)(REG)))
-
-static void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm,
-                                     unsigned char bus,
-                                     unsigned int devfn,
-                                     int where)
-{
-       if (!pbm)
-               return NULL;
-       return (void *)
-               (PSYCHO_CONFIG_BASE(pbm) |
-                PSYCHO_CONFIG_ENCODE(bus, devfn, where));
-}
-
 /* PSYCHO error handling support. */
-enum psycho_error_type {
-       UE_ERR, CE_ERR, PCI_ERR
-};
 
 /* Helper function of IOMMU error checking, which checks out
  * the state of the streaming buffers.  The IOMMU lock is
@@ -122,129 +77,10 @@ enum psycho_error_type {
 #define PSYCHO_STC_DATA_B      0xc000UL
 #define PSYCHO_STC_ERR_A       0xb400UL
 #define PSYCHO_STC_ERR_B       0xc400UL
-#define  PSYCHO_STCERR_WRITE    0x0000000000000002UL   /* Write Error */
-#define  PSYCHO_STCERR_READ     0x0000000000000001UL   /* Read Error */
 #define PSYCHO_STC_TAG_A       0xb800UL
 #define PSYCHO_STC_TAG_B       0xc800UL
-#define  PSYCHO_STCTAG_PPN      0x0fffffff00000000UL   /* Physical Page Number */
-#define  PSYCHO_STCTAG_VPN      0x00000000ffffe000UL   /* Virtual Page Number */
-#define  PSYCHO_STCTAG_VALID    0x0000000000000002UL   /* Valid */
-#define  PSYCHO_STCTAG_WRITE    0x0000000000000001UL   /* Writable */
 #define PSYCHO_STC_LINE_A      0xb900UL
 #define PSYCHO_STC_LINE_B      0xc900UL
-#define  PSYCHO_STCLINE_LINDX   0x0000000001e00000UL   /* LRU Index */
-#define  PSYCHO_STCLINE_SPTR    0x00000000001f8000UL   /* Dirty Data Start Pointer */
-#define  PSYCHO_STCLINE_LADDR   0x0000000000007f00UL   /* Line Address */
-#define  PSYCHO_STCLINE_EPTR    0x00000000000000fcUL   /* Dirty Data End Pointer */
-#define  PSYCHO_STCLINE_VALID   0x0000000000000002UL   /* Valid */
-#define  PSYCHO_STCLINE_FOFN    0x0000000000000001UL   /* Fetch Outstanding / Flush Necessary */
-
-static DEFINE_SPINLOCK(stc_buf_lock);
-static unsigned long stc_error_buf[128];
-static unsigned long stc_tag_buf[16];
-static unsigned long stc_line_buf[16];
-
-static void __psycho_check_one_stc(struct pci_pbm_info *pbm,
-                                  int is_pbm_a)
-{
-       struct strbuf *strbuf = &pbm->stc;
-       unsigned long regbase = pbm->controller_regs;
-       unsigned long err_base, tag_base, line_base;
-       u64 control;
-       int i;
-
-       if (is_pbm_a) {
-               err_base = regbase + PSYCHO_STC_ERR_A;
-               tag_base = regbase + PSYCHO_STC_TAG_A;
-               line_base = regbase + PSYCHO_STC_LINE_A;
-       } else {
-               err_base = regbase + PSYCHO_STC_ERR_B;
-               tag_base = regbase + PSYCHO_STC_TAG_B;
-               line_base = regbase + PSYCHO_STC_LINE_B;
-       }
-
-       spin_lock(&stc_buf_lock);
-
-       /* This is __REALLY__ dangerous.  When we put the
-        * streaming buffer into diagnostic mode to probe
-        * it's tags and error status, we _must_ clear all
-        * of the line tag valid bits before re-enabling
-        * the streaming buffer.  If any dirty data lives
-        * in the STC when we do this, we will end up
-        * invalidating it before it has a chance to reach
-        * main memory.
-        */
-       control = psycho_read(strbuf->strbuf_control);
-       psycho_write(strbuf->strbuf_control,
-                    (control | PSYCHO_STRBUF_CTRL_DENAB));
-       for (i = 0; i < 128; i++) {
-               unsigned long val;
-
-               val = psycho_read(err_base + (i * 8UL));
-               psycho_write(err_base + (i * 8UL), 0UL);
-               stc_error_buf[i] = val;
-       }
-       for (i = 0; i < 16; i++) {
-               stc_tag_buf[i] = psycho_read(tag_base + (i * 8UL));
-               stc_line_buf[i] = psycho_read(line_base + (i * 8UL));
-               psycho_write(tag_base + (i * 8UL), 0UL);
-               psycho_write(line_base + (i * 8UL), 0UL);
-       }
-
-       /* OK, state is logged, exit diagnostic mode. */
-       psycho_write(strbuf->strbuf_control, control);
-
-       for (i = 0; i < 16; i++) {
-               int j, saw_error, first, last;
-
-               saw_error = 0;
-               first = i * 8;
-               last = first + 8;
-               for (j = first; j < last; j++) {
-                       unsigned long errval = stc_error_buf[j];
-                       if (errval != 0) {
-                               saw_error++;
-                               printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n",
-                                      pbm->name,
-                                      j,
-                                      (errval & PSYCHO_STCERR_WRITE) ? 1 : 0,
-                                      (errval & PSYCHO_STCERR_READ) ? 1 : 0);
-                       }
-               }
-               if (saw_error != 0) {
-                       unsigned long tagval = stc_tag_buf[i];
-                       unsigned long lineval = stc_line_buf[i];
-                       printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)W(%d)]\n",
-                              pbm->name,
-                              i,
-                              ((tagval & PSYCHO_STCTAG_PPN) >> 19UL),
-                              (tagval & PSYCHO_STCTAG_VPN),
-                              ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0),
-                              ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0));
-                       printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)"
-                              "V(%d)FOFN(%d)]\n",
-                              pbm->name,
-                              i,
-                              ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL),
-                              ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL),
-                              ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL),
-                              ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL),
-                              ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0),
-                              ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0));
-               }
-       }
-
-       spin_unlock(&stc_buf_lock);
-}
-
-static void __psycho_check_stc_error(struct pci_pbm_info *pbm,
-                                    unsigned long afsr,
-                                    unsigned long afar,
-                                    enum psycho_error_type type)
-{
-       __psycho_check_one_stc(pbm,
-                              (pbm == &pbm->parent->pbm_A));
-}
 
 /* When an Uncorrectable Error or a PCI Error happens, we
  * interrogate the IOMMU state to see if it is the cause.
@@ -271,122 +107,7 @@ static void __psycho_check_stc_error(struct pci_pbm_info *pbm,
 #define PSYCHO_IOMMU_TSBBASE   0x0208UL
 #define PSYCHO_IOMMU_FLUSH     0x0210UL
 #define PSYCHO_IOMMU_TAG       0xa580UL
-#define  PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL)
-#define  PSYCHO_IOMMU_TAG_ERR   (0x1UL << 22UL)
-#define  PSYCHO_IOMMU_TAG_WRITE         (0x1UL << 21UL)
-#define  PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL)
-#define  PSYCHO_IOMMU_TAG_SIZE  (0x1UL << 19UL)
-#define  PSYCHO_IOMMU_TAG_VPAGE         0x7ffffUL
 #define PSYCHO_IOMMU_DATA      0xa600UL
-#define  PSYCHO_IOMMU_DATA_VALID (1UL << 30UL)
-#define  PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL)
-#define  PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL
-static void psycho_check_iommu_error(struct pci_pbm_info *pbm,
-                                    unsigned long afsr,
-                                    unsigned long afar,
-                                    enum psycho_error_type type)
-{
-       struct iommu *iommu = pbm->iommu;
-       unsigned long iommu_tag[16];
-       unsigned long iommu_data[16];
-       unsigned long flags;
-       u64 control;
-       int i;
-
-       spin_lock_irqsave(&iommu->lock, flags);
-       control = psycho_read(iommu->iommu_control);
-       if (control & PSYCHO_IOMMU_CTRL_XLTEERR) {
-               char *type_string;
-
-               /* Clear the error encountered bit. */
-               control &= ~PSYCHO_IOMMU_CTRL_XLTEERR;
-               psycho_write(iommu->iommu_control, control);
-
-               switch((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
-               case 0:
-                       type_string = "Protection Error";
-                       break;
-               case 1:
-                       type_string = "Invalid Error";
-                       break;
-               case 2:
-                       type_string = "TimeOut Error";
-                       break;
-               case 3:
-               default:
-                       type_string = "ECC Error";
-                       break;
-               };
-               printk("%s: IOMMU Error, type[%s]\n",
-                      pbm->name, type_string);
-
-               /* Put the IOMMU into diagnostic mode and probe
-                * it's TLB for entries with error status.
-                *
-                * It is very possible for another DVMA to occur
-                * while we do this probe, and corrupt the system
-                * further.  But we are so screwed at this point
-                * that we are likely to crash hard anyways, so
-                * get as much diagnostic information to the
-                * console as we can.
-                */
-               psycho_write(iommu->iommu_control,
-                            control | PSYCHO_IOMMU_CTRL_DENAB);
-               for (i = 0; i < 16; i++) {
-                       unsigned long base = pbm->controller_regs;
-
-                       iommu_tag[i] =
-                               psycho_read(base + PSYCHO_IOMMU_TAG + (i * 8UL));
-                       iommu_data[i] =
-                               psycho_read(base + PSYCHO_IOMMU_DATA + (i * 8UL));
-
-                       /* Now clear out the entry. */
-                       psycho_write(base + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
-                       psycho_write(base + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
-               }
-
-               /* Leave diagnostic mode. */
-               psycho_write(iommu->iommu_control, control);
-
-               for (i = 0; i < 16; i++) {
-                       unsigned long tag, data;
-
-                       tag = iommu_tag[i];
-                       if (!(tag & PSYCHO_IOMMU_TAG_ERR))
-                               continue;
-
-                       data = iommu_data[i];
-                       switch((tag & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) {
-                       case 0:
-                               type_string = "Protection Error";
-                               break;
-                       case 1:
-                               type_string = "Invalid Error";
-                               break;
-                       case 2:
-                               type_string = "TimeOut Error";
-                               break;
-                       case 3:
-                       default:
-                               type_string = "ECC Error";
-                               break;
-                       };
-                       printk("%s: IOMMU TAG(%d)[error(%s) wr(%d) str(%d) sz(%dK) vpg(%08lx)]\n",
-                              pbm->name, i, type_string,
-                              ((tag & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0),
-                              ((tag & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0),
-                              ((tag & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8),
-                              (tag & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
-                       printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n",
-                              pbm->name, i,
-                              ((data & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0),
-                              ((data & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0),
-                              (data & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT);
-               }
-       }
-       __psycho_check_stc_error(pbm, afsr, afar, type);
-       spin_unlock_irqrestore(&iommu->lock, flags);
-}
 
 /* Uncorrectable Errors.  Cause of the error and the address are
  * recorded in the UE_AFSR and UE_AFAR of PSYCHO.  They are errors
@@ -410,15 +131,14 @@ static void psycho_check_iommu_error(struct pci_pbm_info *pbm,
 static irqreturn_t psycho_ue_intr(int irq, void *dev_id)
 {
        struct pci_pbm_info *pbm = dev_id;
-       struct pci_controller_info *p = pbm->parent;
        unsigned long afsr_reg = pbm->controller_regs + PSYCHO_UE_AFSR;
        unsigned long afar_reg = pbm->controller_regs + PSYCHO_UE_AFAR;
        unsigned long afsr, afar, error_bits;
        int reported;
 
        /* Latch uncorrectable error status. */
-       afar = psycho_read(afar_reg);
-       afsr = psycho_read(afsr_reg);
+       afar = upa_readq(afar_reg);
+       afsr = upa_readq(afsr_reg);
 
        /* Clear the primary/secondary error status bits. */
        error_bits = afsr &
@@ -426,7 +146,7 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id)
                 PSYCHO_UEAFSR_SPIO | PSYCHO_UEAFSR_SDRD | PSYCHO_UEAFSR_SDWR);
        if (!error_bits)
                return IRQ_NONE;
-       psycho_write(afsr_reg, error_bits);
+       upa_writeq(error_bits, afsr_reg);
 
        /* Log the error. */
        printk("%s: Uncorrectable Error, primary error type[%s]\n",
@@ -463,8 +183,9 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id)
        printk("]\n");
 
        /* Interrogate both IOMMUs for error status. */
-       psycho_check_iommu_error(&p->pbm_A, afsr, afar, UE_ERR);
-       psycho_check_iommu_error(&p->pbm_B, afsr, afar, UE_ERR);
+       psycho_check_iommu_error(pbm, afsr, afar, UE_ERR);
+       if (pbm->sibling)
+               psycho_check_iommu_error(pbm->sibling, afsr, afar, UE_ERR);
 
        return IRQ_HANDLED;
 }
@@ -495,8 +216,8 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id)
        int reported;
 
        /* Latch error status. */
-       afar = psycho_read(afar_reg);
-       afsr = psycho_read(afsr_reg);
+       afar = upa_readq(afar_reg);
+       afsr = upa_readq(afsr_reg);
 
        /* Clear primary/secondary error status bits. */
        error_bits = afsr &
@@ -504,7 +225,7 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id)
                 PSYCHO_CEAFSR_SPIO | PSYCHO_CEAFSR_SDRD | PSYCHO_CEAFSR_SDWR);
        if (!error_bits)
                return IRQ_NONE;
-       psycho_write(afsr_reg, error_bits);
+       upa_writeq(error_bits, afsr_reg);
 
        /* Log the error. */
        printk("%s: Correctable Error, primary error type[%s]\n",
@@ -554,164 +275,9 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id)
  */
 #define PSYCHO_PCI_AFSR_A      0x2010UL
 #define PSYCHO_PCI_AFSR_B      0x4010UL
-#define  PSYCHO_PCIAFSR_PMA    0x8000000000000000UL /* Primary Master Abort Error   */
-#define  PSYCHO_PCIAFSR_PTA    0x4000000000000000UL /* Primary Target Abort Error   */
-#define  PSYCHO_PCIAFSR_PRTRY  0x2000000000000000UL /* Primary Excessive Retries    */
-#define  PSYCHO_PCIAFSR_PPERR  0x1000000000000000UL /* Primary Parity Error         */
-#define  PSYCHO_PCIAFSR_SMA    0x0800000000000000UL /* Secondary Master Abort Error */
-#define  PSYCHO_PCIAFSR_STA    0x0400000000000000UL /* Secondary Target Abort Error */
-#define  PSYCHO_PCIAFSR_SRTRY  0x0200000000000000UL /* Secondary Excessive Retries  */
-#define  PSYCHO_PCIAFSR_SPERR  0x0100000000000000UL /* Secondary Parity Error       */
-#define  PSYCHO_PCIAFSR_RESV1  0x00ff000000000000UL /* Reserved                     */
-#define  PSYCHO_PCIAFSR_BMSK   0x0000ffff00000000UL /* Bytemask of failed transfer  */
-#define  PSYCHO_PCIAFSR_BLK    0x0000000080000000UL /* Trans was block operation    */
-#define  PSYCHO_PCIAFSR_RESV2  0x0000000040000000UL /* Reserved                     */
-#define  PSYCHO_PCIAFSR_MID    0x000000003e000000UL /* MID causing the error        */
-#define  PSYCHO_PCIAFSR_RESV3  0x0000000001ffffffUL /* Reserved                     */
 #define PSYCHO_PCI_AFAR_A      0x2018UL
 #define PSYCHO_PCI_AFAR_B      0x4018UL
 
-static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm, int is_pbm_a)
-{
-       unsigned long csr_reg, csr, csr_error_bits;
-       irqreturn_t ret = IRQ_NONE;
-       u16 stat, *addr;
-
-       if (is_pbm_a) {
-               csr_reg = pbm->controller_regs + PSYCHO_PCIA_CTRL;
-       } else {
-               csr_reg = pbm->controller_regs + PSYCHO_PCIB_CTRL;
-       }
-       csr = psycho_read(csr_reg);
-       csr_error_bits =
-               csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR);
-       if (csr_error_bits) {
-               /* Clear the errors.  */
-               psycho_write(csr_reg, csr);
-
-               /* Log 'em.  */
-               if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR)
-                       printk("%s: PCI streaming byte hole error asserted.\n",
-                              pbm->name);
-               if (csr_error_bits & PSYCHO_PCICTRL_SERR)
-                       printk("%s: PCI SERR signal asserted.\n", pbm->name);
-               ret = IRQ_HANDLED;
-       }
-       addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
-                                       0, PCI_STATUS);
-       pci_config_read16(addr, &stat);
-       if (stat & (PCI_STATUS_PARITY |
-                   PCI_STATUS_SIG_TARGET_ABORT |
-                   PCI_STATUS_REC_TARGET_ABORT |
-                   PCI_STATUS_REC_MASTER_ABORT |
-                   PCI_STATUS_SIG_SYSTEM_ERROR)) {
-               printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
-                      pbm->name, stat);
-               pci_config_write16(addr, 0xffff);
-               ret = IRQ_HANDLED;
-       }
-       return ret;
-}
-
-static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id)
-{
-       struct pci_pbm_info *pbm = dev_id;
-       struct pci_controller_info *p = pbm->parent;
-       unsigned long afsr_reg, afar_reg;
-       unsigned long afsr, afar, error_bits;
-       int is_pbm_a, reported;
-
-       is_pbm_a = (pbm == &pbm->parent->pbm_A);
-       if (is_pbm_a) {
-               afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_A;
-               afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_A;
-       } else {
-               afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_B;
-               afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_B;
-       }
-
-       /* Latch error status. */
-       afar = psycho_read(afar_reg);
-       afsr = psycho_read(afsr_reg);
-
-       /* Clear primary/secondary error status bits. */
-       error_bits = afsr &
-               (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA |
-                PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR |
-                PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA |
-                PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR);
-       if (!error_bits)
-               return psycho_pcierr_intr_other(pbm, is_pbm_a);
-       psycho_write(afsr_reg, error_bits);
-
-       /* Log the error. */
-       printk("%s: PCI Error, primary error type[%s]\n",
-              pbm->name,
-              (((error_bits & PSYCHO_PCIAFSR_PMA) ?
-                "Master Abort" :
-                ((error_bits & PSYCHO_PCIAFSR_PTA) ?
-                 "Target Abort" :
-                 ((error_bits & PSYCHO_PCIAFSR_PRTRY) ?
-                  "Excessive Retries" :
-                  ((error_bits & PSYCHO_PCIAFSR_PPERR) ?
-                   "Parity Error" : "???"))))));
-       printk("%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n",
-              pbm->name,
-              (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL,
-              (afsr & PSYCHO_PCIAFSR_MID) >> 25UL,
-              (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0);
-       printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar);
-       printk("%s: PCI Secondary errors [", pbm->name);
-       reported = 0;
-       if (afsr & PSYCHO_PCIAFSR_SMA) {
-               reported++;
-               printk("(Master Abort)");
-       }
-       if (afsr & PSYCHO_PCIAFSR_STA) {
-               reported++;
-               printk("(Target Abort)");
-       }
-       if (afsr & PSYCHO_PCIAFSR_SRTRY) {
-               reported++;
-               printk("(Excessive Retries)");
-       }
-       if (afsr & PSYCHO_PCIAFSR_SPERR) {
-               reported++;
-               printk("(Parity Error)");
-       }
-       if (!reported)
-               printk("(none)");
-       printk("]\n");
-
-       /* For the error types shown, scan PBM's PCI bus for devices
-        * which have logged that error type.
-        */
-
-       /* If we see a Target Abort, this could be the result of an
-        * IOMMU translation error of some sort.  It is extremely
-        * useful to log this information as usually it indicates
-        * a bug in the IOMMU support code or a PCI device driver.
-        */
-       if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) {
-               psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR);
-               pci_scan_for_target_abort(pbm, pbm->pci_bus);
-       }
-       if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA))
-               pci_scan_for_master_abort(pbm, pbm->pci_bus);
-
-       /* For excessive retries, PSYCHO/PBM will abort the device
-        * and there is no way to specifically check for excessive
-        * retries in the config space status registers.  So what
-        * we hope is that we'll catch it via the master/target
-        * abort events.
-        */
-
-       if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR))
-               pci_scan_for_parity_error(pbm, pbm->pci_bus);
-
-       return IRQ_HANDLED;
-}
-
 /* XXX What about PowerFail/PowerManagement??? -DaveM */
 #define PSYCHO_ECC_CTRL                0x0020
 #define  PSYCHO_ECCCTRL_EE      0x8000000000000000UL /* Enable ECC Checking */
@@ -719,7 +285,7 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id)
 #define  PSYCHO_ECCCTRL_CE      0x2000000000000000UL /* Enable CE INterrupts */
 static void psycho_register_error_handlers(struct pci_pbm_info *pbm)
 {
-       struct of_device *op = of_find_device_by_node(pbm->prom_node);
+       struct of_device *op = of_find_device_by_node(pbm->op->node);
        unsigned long base = pbm->controller_regs;
        u64 tmp;
        int err;
@@ -762,27 +328,26 @@ static void psycho_register_error_handlers(struct pci_pbm_info *pbm)
                       "err=%d\n", pbm->name, err);
 
        /* Enable UE and CE interrupts for controller. */
-       psycho_write(base + PSYCHO_ECC_CTRL,
-                    (PSYCHO_ECCCTRL_EE |
-                     PSYCHO_ECCCTRL_UE |
-                     PSYCHO_ECCCTRL_CE));
+       upa_writeq((PSYCHO_ECCCTRL_EE |
+                   PSYCHO_ECCCTRL_UE |
+                   PSYCHO_ECCCTRL_CE), base + PSYCHO_ECC_CTRL);
 
        /* Enable PCI Error interrupts and clear error
         * bits for each PBM.
         */
-       tmp = psycho_read(base + PSYCHO_PCIA_CTRL);
+       tmp = upa_readq(base + PSYCHO_PCIA_CTRL);
        tmp |= (PSYCHO_PCICTRL_SERR |
                PSYCHO_PCICTRL_SBH_ERR |
                PSYCHO_PCICTRL_EEN);
        tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
-       psycho_write(base + PSYCHO_PCIA_CTRL, tmp);
+       upa_writeq(tmp, base + PSYCHO_PCIA_CTRL);
                     
-       tmp = psycho_read(base + PSYCHO_PCIB_CTRL);
+       tmp = upa_readq(base + PSYCHO_PCIB_CTRL);
        tmp |= (PSYCHO_PCICTRL_SERR |
                PSYCHO_PCICTRL_SBH_ERR |
                PSYCHO_PCICTRL_EEN);
        tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
-       psycho_write(base + PSYCHO_PCIB_CTRL, tmp);
+       upa_writeq(tmp, base + PSYCHO_PCIB_CTRL);
 }
 
 /* PSYCHO boot time probing and initialization. */
@@ -803,11 +368,12 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm)
        pci_config_write8(addr, 64);
 }
 
-static void __init psycho_scan_bus(struct pci_pbm_info *pbm)
+static void __init psycho_scan_bus(struct pci_pbm_info *pbm,
+                                  struct device *parent)
 {
        pbm_config_busmastering(pbm);
        pbm->is_66mhz_capable = 0;
-       pbm->pci_bus = pci_scan_one_pbm(pbm);
+       pbm->pci_bus = pci_scan_one_pbm(pbm, parent);
 
        /* After the PCI bus scan is complete, we can register
         * the error interrupt handlers.
@@ -815,61 +381,6 @@ static void __init psycho_scan_bus(struct pci_pbm_info *pbm)
        psycho_register_error_handlers(pbm);
 }
 
-static int psycho_iommu_init(struct pci_pbm_info *pbm)
-{
-       struct iommu *iommu = pbm->iommu;
-       unsigned long i;
-       u64 control;
-       int err;
-
-       /* Register addresses. */
-       iommu->iommu_control  = pbm->controller_regs + PSYCHO_IOMMU_CONTROL;
-       iommu->iommu_tsbbase  = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE;
-       iommu->iommu_flush    = pbm->controller_regs + PSYCHO_IOMMU_FLUSH;
-       iommu->iommu_tags     = iommu->iommu_flush + (0xa580UL - 0x0210UL);
-
-       /* PSYCHO's IOMMU lacks ctx flushing. */
-       iommu->iommu_ctxflush = 0;
-
-       /* We use the main control register of PSYCHO as the write
-        * completion register.
-        */
-       iommu->write_complete_reg = pbm->controller_regs + PSYCHO_CONTROL;
-
-       /*
-        * Invalidate TLB Entries.
-        */
-       control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL);
-       control |= PSYCHO_IOMMU_CTRL_DENAB;
-       psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control);
-       for(i = 0; i < 16; i++) {
-               psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
-               psycho_write(pbm->controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
-       }
-
-       /* Leave diag mode enabled for full-flushing done
-        * in pci_iommu.c
-        */
-       err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff,
-                              pbm->numa_node);
-       if (err)
-               return err;
-
-       psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TSBBASE,
-                    __pa(iommu->page_table));
-
-       control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL);
-       control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
-       control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB);
-       psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control);
-
-       /* If necessary, hook us up for starfire IRQ translations. */
-       if (this_is_starfire)
-               starfire_hookup(pbm->portid);
-
-       return 0;
-}
-
 #define PSYCHO_IRQ_RETRY       0x1a00UL
 #define PSYCHO_PCIA_DIAG       0x2020UL
 #define PSYCHO_PCIB_DIAG       0x4020UL
@@ -886,28 +397,28 @@ static void psycho_controller_hwinit(struct pci_pbm_info *pbm)
 {
        u64 tmp;
 
-       psycho_write(pbm->controller_regs + PSYCHO_IRQ_RETRY, 5);
+       upa_writeq(5, pbm->controller_regs + PSYCHO_IRQ_RETRY);
 
        /* Enable arbiter for all PCI slots. */
-       tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_CTRL);
+       tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_CTRL);
        tmp |= PSYCHO_PCICTRL_AEN;
-       psycho_write(pbm->controller_regs + PSYCHO_PCIA_CTRL, tmp);
+       upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_CTRL);
 
-       tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_CTRL);
+       tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_CTRL);
        tmp |= PSYCHO_PCICTRL_AEN;
-       psycho_write(pbm->controller_regs + PSYCHO_PCIB_CTRL, tmp);
+       upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_CTRL);
 
        /* Disable DMA write / PIO read synchronization on
         * both PCI bus segments.
         * [ U2P Erratum 1243770, STP2223BGA data sheet ]
         */
-       tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_DIAG);
+       tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_DIAG);
        tmp |= PSYCHO_PCIDIAG_DDWSYNC;
-       psycho_write(pbm->controller_regs + PSYCHO_PCIA_DIAG, tmp);
+       upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_DIAG);
 
-       tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_DIAG);
+       tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_DIAG);
        tmp |= PSYCHO_PCIDIAG_DDWSYNC;
-       psycho_write(pbm->controller_regs + PSYCHO_PCIB_DIAG, tmp);
+       upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_DIAG);
 }
 
 static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
@@ -920,10 +431,16 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
                pbm->stc.strbuf_control  = base + PSYCHO_STRBUF_CONTROL_A;
                pbm->stc.strbuf_pflush   = base + PSYCHO_STRBUF_FLUSH_A;
                pbm->stc.strbuf_fsync    = base + PSYCHO_STRBUF_FSYNC_A;
+               pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_A;
+               pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_A;
+               pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_A;
        } else {
                pbm->stc.strbuf_control  = base + PSYCHO_STRBUF_CONTROL_B;
                pbm->stc.strbuf_pflush   = base + PSYCHO_STRBUF_FLUSH_B;
                pbm->stc.strbuf_fsync    = base + PSYCHO_STRBUF_FSYNC_B;
+               pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_B;
+               pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_B;
+               pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_B;
        }
        /* PSYCHO's streaming buffer lacks ctx flushing. */
        pbm->stc.strbuf_ctxflush      = 0;
@@ -946,7 +463,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
         */
 #undef PSYCHO_STRBUF_RERUN_ENABLE
 #undef PSYCHO_STRBUF_RERUN_DISABLE
-       control = psycho_read(pbm->stc.strbuf_control);
+       control = upa_readq(pbm->stc.strbuf_control);
        control |= PSYCHO_STRBUF_CTRL_ENAB;
        control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR);
 #ifdef PSYCHO_STRBUF_RERUN_ENABLE
@@ -956,7 +473,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
        control |= PSYCHO_STRBUF_CTRL_RRDIS;
 #endif
 #endif
-       psycho_write(pbm->stc.strbuf_control, control);
+       upa_writeq(control, pbm->stc.strbuf_control);
 
        pbm->stc.strbuf_enabled = 1;
 }
@@ -968,111 +485,134 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
 #define PSYCHO_MEMSPACE_B      0x180000000UL
 #define PSYCHO_MEMSPACE_SIZE   0x07fffffffUL
 
-static void __init psycho_pbm_init(struct pci_controller_info *p,
-                           struct device_node *dp, int is_pbm_a)
+static void __init psycho_pbm_init(struct pci_pbm_info *pbm,
+                                  struct of_device *op, int is_pbm_a)
 {
-       struct property *prop;
-       struct pci_pbm_info *pbm;
-
-       if (is_pbm_a)
-               pbm = &p->pbm_A;
-       else
-               pbm = &p->pbm_B;
-
-       pbm->next = pci_pbm_root;
-       pci_pbm_root = pbm;
-
-       pbm->numa_node = -1;
-
-       pbm->scan_bus = psycho_scan_bus;
-       pbm->pci_ops = &sun4u_pci_ops;
-       pbm->config_space_reg_bits = 8;
-
-       pbm->index = pci_num_pbms++;
-
-       pbm->chip_type = PBM_CHIP_TYPE_PSYCHO;
-       pbm->chip_version = 0;
-       prop = of_find_property(dp, "version#", NULL);
-       if (prop)
-               pbm->chip_version = *(int *) prop->value;
-       pbm->chip_revision = 0;
-       prop = of_find_property(dp, "module-revision#", NULL);
-       if (prop)
-               pbm->chip_revision = *(int *) prop->value;
-
-       pbm->parent = p;
-       pbm->prom_node = dp;
-       pbm->name = dp->full_name;
-
-       printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n",
-              pbm->name,
-              pbm->chip_version, pbm->chip_revision);
-
-       pci_determine_mem_io_space(pbm);
+       psycho_pbm_init_common(pbm, op, "PSYCHO", PBM_CHIP_TYPE_PSYCHO);
+       psycho_pbm_strbuf_init(pbm, is_pbm_a);
+       psycho_scan_bus(pbm, &op->dev);
+}
 
-       pci_get_pbm_props(pbm);
+static struct pci_pbm_info * __init psycho_find_sibling(u32 upa_portid)
+{
+       struct pci_pbm_info *pbm;
 
-       psycho_pbm_strbuf_init(pbm, is_pbm_a);
+       for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
+               if (pbm->portid == upa_portid)
+                       return pbm;
+       }
+       return NULL;
 }
 
 #define PSYCHO_CONFIGSPACE     0x001000000UL
 
-void __init psycho_init(struct device_node *dp, char *model_name)
+static int __init psycho_probe(struct of_device *op,
+                                 const struct of_device_id *match)
 {
-       struct linux_prom64_registers *pr_regs;
-       struct pci_controller_info *p;
+       const struct linux_prom64_registers *pr_regs;
+       struct device_node *dp = op->node;
        struct pci_pbm_info *pbm;
        struct iommu *iommu;
-       struct property *prop;
+       int is_pbm_a, err;
        u32 upa_portid;
-       int is_pbm_a;
 
-       upa_portid = 0xff;
-       prop = of_find_property(dp, "upa-portid", NULL);
-       if (prop)
-               upa_portid = *(u32 *) prop->value;
+       upa_portid = of_getintprop_default(dp, "upa-portid", 0xff);
 
-       for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
-               struct pci_controller_info *p = pbm->parent;
+       err = -ENOMEM;
+       pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+       if (!pbm) {
+               printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n");
+               goto out_err;
+       }
 
-               if (p->pbm_A.portid == upa_portid) {
-                       is_pbm_a = (p->pbm_A.prom_node == NULL);
-                       psycho_pbm_init(p, dp, is_pbm_a);
-                       return;
+       pbm->sibling = psycho_find_sibling(upa_portid);
+       if (pbm->sibling) {
+               iommu = pbm->sibling->iommu;
+       } else {
+               iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL);
+               if (!iommu) {
+                       printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n");
+                       goto out_free_controller;
                }
        }
 
-       p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
-       if (!p)
-               goto fatal_memory_error;
-       iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
-       if (!iommu)
-               goto fatal_memory_error;
+       pbm->iommu = iommu;
+       pbm->portid = upa_portid;
 
-       p->pbm_A.iommu = p->pbm_B.iommu = iommu;
+       pr_regs = of_get_property(dp, "reg", NULL);
+       err = -ENODEV;
+       if (!pr_regs) {
+               printk(KERN_ERR PFX "No reg property.\n");
+               goto out_free_iommu;
+       }
 
-       p->pbm_A.portid = upa_portid;
-       p->pbm_B.portid = upa_portid;
+       is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
 
-       prop = of_find_property(dp, "reg", NULL);
-       pr_regs = prop->value;
+       pbm->controller_regs = pr_regs[2].phys_addr;
+       pbm->config_space = (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
 
-       p->pbm_A.controller_regs = pr_regs[2].phys_addr;
-       p->pbm_B.controller_regs = pr_regs[2].phys_addr;
+       if (is_pbm_a) {
+               pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_A;
+               pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_A;
+               pbm->pci_csr  = pbm->controller_regs + PSYCHO_PCIA_CTRL;
+       } else {
+               pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_B;
+               pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_B;
+               pbm->pci_csr  = pbm->controller_regs + PSYCHO_PCIB_CTRL;
+       }
 
-       p->pbm_A.config_space = p->pbm_B.config_space =
-               (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
+       psycho_controller_hwinit(pbm);
+       if (!pbm->sibling) {
+               err = psycho_iommu_init(pbm, 128, 0xc0000000,
+                                       0xffffffff, PSYCHO_CONTROL);
+               if (err)
+                       goto out_free_iommu;
 
-       psycho_controller_hwinit(&p->pbm_A);
+               /* If necessary, hook us up for starfire IRQ translations. */
+               if (this_is_starfire)
+                       starfire_hookup(pbm->portid);
+       }
 
-       if (psycho_iommu_init(&p->pbm_A))
-               goto fatal_memory_error;
+       psycho_pbm_init(pbm, op, is_pbm_a);
 
-       is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
-       psycho_pbm_init(p, dp, is_pbm_a);
-       return;
+       pbm->next = pci_pbm_root;
+       pci_pbm_root = pbm;
+
+       if (pbm->sibling)
+               pbm->sibling->sibling = pbm;
+
+       dev_set_drvdata(&op->dev, pbm);
+
+       return 0;
+
+out_free_iommu:
+       if (!pbm->sibling)
+               kfree(pbm->iommu);
 
-fatal_memory_error:
-       prom_printf("PSYCHO: Fatal memory allocation error.\n");
-       prom_halt();
+out_free_controller:
+       kfree(pbm);
+
+out_err:
+       return err;
 }
+
+static struct of_device_id __initdata psycho_match[] = {
+       {
+               .name = "pci",
+               .compatible = "pci108e,8000",
+       },
+       {},
+};
+
+static struct of_platform_driver psycho_driver = {
+       .name           = DRIVER_NAME,
+       .match_table    = psycho_match,
+       .probe          = psycho_probe,
+};
+
+static int __init psycho_init(void)
+{
+       return of_register_driver(&psycho_driver, &of_bus_type);
+}
+
+subsys_initcall(psycho_init);