X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fsparc64%2Fkernel%2Fpci_sabre.c;h=f67bb7f078cfa936f2980f810287a4a70ea8ac7d;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=f04c6d288bd0592c7f7c05216acfd7f4d31061c5;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index f04c6d288..f67bb7f07 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "pci_impl.h" #include "iommu_common.h" @@ -485,6 +484,191 @@ static struct pci_ops sabre_ops = { .write = sabre_write_pci_cfg, }; +static unsigned long sabre_pcislot_imap_offset(unsigned long ino) +{ + unsigned int bus = (ino & 0x10) >> 4; + unsigned int slot = (ino & 0x0c) >> 2; + + if (bus == 0) + return SABRE_IMAP_A_SLOT0 + (slot * 8); + else + return SABRE_IMAP_B_SLOT0 + (slot * 8); +} + +static unsigned long __onboard_imap_off[] = { +/*0x20*/ SABRE_IMAP_SCSI, +/*0x21*/ SABRE_IMAP_ETH, +/*0x22*/ SABRE_IMAP_BPP, +/*0x23*/ SABRE_IMAP_AU_REC, +/*0x24*/ SABRE_IMAP_AU_PLAY, +/*0x25*/ SABRE_IMAP_PFAIL, +/*0x26*/ SABRE_IMAP_KMS, +/*0x27*/ SABRE_IMAP_FLPY, +/*0x28*/ SABRE_IMAP_SHW, +/*0x29*/ SABRE_IMAP_KBD, +/*0x2a*/ SABRE_IMAP_MS, +/*0x2b*/ SABRE_IMAP_SER, +/*0x2c*/ 0 /* reserved */, +/*0x2d*/ 0 /* reserved */, +/*0x2e*/ SABRE_IMAP_UE, +/*0x2f*/ SABRE_IMAP_CE, +/*0x30*/ SABRE_IMAP_PCIERR, +}; +#define SABRE_ONBOARD_IRQ_BASE 0x20 +#define SABRE_ONBOARD_IRQ_LAST 0x30 +#define sabre_onboard_imap_offset(__ino) \ + __onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE] + +#define sabre_iclr_offset(ino) \ + ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \ + (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3))) + +/* PCI SABRE INO number to Sparc PIL level. */ +static unsigned char sabre_pil_table[] = { +/*0x00*/0, 0, 0, 0, /* PCI A slot 0 Int A, B, C, D */ +/*0x04*/0, 0, 0, 0, /* PCI A slot 1 Int A, B, C, D */ +/*0x08*/0, 0, 0, 0, /* PCI A slot 2 Int A, B, C, D */ +/*0x0c*/0, 0, 0, 0, /* PCI A slot 3 Int A, B, C, D */ +/*0x10*/0, 0, 0, 0, /* PCI B slot 0 Int A, B, C, D */ +/*0x14*/0, 0, 0, 0, /* PCI B slot 1 Int A, B, C, D */ +/*0x18*/0, 0, 0, 0, /* PCI B slot 2 Int A, B, C, D */ +/*0x1c*/0, 0, 0, 0, /* PCI B slot 3 Int A, B, C, D */ +/*0x20*/5, /* SCSI */ +/*0x21*/5, /* Ethernet */ +/*0x22*/8, /* Parallel Port */ +/*0x23*/13, /* Audio Record */ +/*0x24*/14, /* Audio Playback */ +/*0x25*/15, /* PowerFail */ +/*0x26*/5, /* second SCSI */ +/*0x27*/11, /* Floppy */ +/*0x28*/5, /* Spare Hardware */ +/*0x29*/9, /* Keyboard */ +/*0x2a*/5, /* Mouse */ +/*0x2b*/12, /* Serial */ +/*0x2c*/10, /* Timer 0 */ +/*0x2d*/11, /* Timer 1 */ +/*0x2e*/15, /* Uncorrectable ECC */ +/*0x2f*/15, /* Correctable ECC */ +/*0x30*/15, /* PCI Bus A Error */ +/*0x31*/15, /* PCI Bus B Error */ +/*0x32*/15, /* Power Management */ +}; + +static int sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino) +{ + int ret; + + if (pdev && + pdev->vendor == PCI_VENDOR_ID_SUN && + pdev->device == PCI_DEVICE_ID_SUN_RIO_USB) + return 9; + + ret = sabre_pil_table[ino]; + if (ret == 0 && pdev == NULL) { + ret = 5; + } else if (ret == 0) { + switch ((pdev->class >> 16) & 0xff) { + case PCI_BASE_CLASS_STORAGE: + ret = 5; + break; + + case PCI_BASE_CLASS_NETWORK: + ret = 6; + break; + + case PCI_BASE_CLASS_DISPLAY: + ret = 9; + break; + + case PCI_BASE_CLASS_MULTIMEDIA: + case PCI_BASE_CLASS_MEMORY: + case PCI_BASE_CLASS_BRIDGE: + case PCI_BASE_CLASS_SERIAL: + ret = 10; + break; + + default: + ret = 5; + break; + }; + } + return ret; +} + +/* When a device lives behind a bridge deeper in the PCI bus topology + * than APB, a special sequence must run to make sure all pending DMA + * transfers at the time of IRQ delivery are visible in the coherency + * domain by the cpu. This sequence is to perform a read on the far + * side of the non-APB bridge, then perform a read of Sabre's DMA + * write-sync register. + */ +static void sabre_wsync_handler(struct ino_bucket *bucket, void *_arg1, void *_arg2) +{ + struct pci_dev *pdev = _arg1; + unsigned long sync_reg = (unsigned long) _arg2; + u16 _unused; + + pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused); + sabre_read(sync_reg); +} + +static unsigned int sabre_irq_build(struct pci_pbm_info *pbm, + struct pci_dev *pdev, + unsigned int ino) +{ + struct ino_bucket *bucket; + unsigned long imap, iclr; + unsigned long imap_off, iclr_off; + int pil, inofixup = 0; + + ino &= PCI_IRQ_INO; + if (ino < SABRE_ONBOARD_IRQ_BASE) { + /* PCI slot */ + imap_off = sabre_pcislot_imap_offset(ino); + } else { + /* onboard device */ + if (ino > SABRE_ONBOARD_IRQ_LAST) { + prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino); + prom_halt(); + } + imap_off = sabre_onboard_imap_offset(ino); + } + + /* Now build the IRQ bucket. */ + pil = sabre_ino_to_pil(pdev, ino); + + if (PIL_RESERVED(pil)) + BUG(); + + imap = pbm->controller_regs + imap_off; + imap += 4; + + iclr_off = sabre_iclr_offset(ino); + iclr = pbm->controller_regs + iclr_off; + iclr += 4; + + if ((ino & 0x20) == 0) + inofixup = ino & 0x03; + + bucket = __bucket(build_irq(pil, inofixup, iclr, imap)); + bucket->flags |= IBF_PCI; + + if (pdev) { + struct pcidev_cookie *pcp = pdev->sysdata; + + if (pdev->bus->number != pcp->pbm->pci_first_busno) { + struct pci_controller_info *p = pcp->pbm->parent; + struct irq_desc *d = bucket->irq_info; + + d->pre_handler = sabre_wsync_handler; + d->pre_handler_arg1 = pdev; + d->pre_handler_arg2 = (void *) + p->pbm_A.controller_regs + SABRE_WRSYNC; + } + } + return __irq(bucket); +} + /* SABRE error handling support. */ static void sabre_check_iommu_error(struct pci_controller_info *p, unsigned long afsr, @@ -821,30 +1005,17 @@ static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs return IRQ_HANDLED; } +/* XXX What about PowerFail/PowerManagement??? -DaveM */ +#define SABRE_UE_INO 0x2e +#define SABRE_CE_INO 0x2f +#define SABRE_PCIERR_INO 0x30 static void sabre_register_error_handlers(struct pci_controller_info *p) { struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */ - struct device_node *dp = pbm->prom_node; - struct of_device *op; unsigned long base = pbm->controller_regs; + unsigned long irq, portid = pbm->portid; u64 tmp; - if (pbm->chip_type == PBM_CHIP_TYPE_SABRE) - dp = dp->parent; - - op = of_find_device_by_node(dp); - if (!op) - return; - - /* Sabre/Hummingbird IRQ property layout is: - * 0: PCI ERR - * 1: UE ERR - * 2: CE ERR - * 3: POWER FAIL - */ - if (op->num_irqs < 4) - return; - /* We clear the error bits in the appropriate AFSR before * registering the handler so that we don't get spurious * interrupts. @@ -853,16 +1024,32 @@ static void sabre_register_error_handlers(struct pci_controller_info *p) (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE)); - - request_irq(op->irqs[1], sabre_ue_intr, IRQF_SHARED, "SABRE UE", p); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO); + if (request_irq(irq, sabre_ue_intr, + SA_SHIRQ, "SABRE UE", p) < 0) { + prom_printf("SABRE%d: Cannot register UE interrupt.\n", + p->index); + prom_halt(); + } sabre_write(base + SABRE_CE_AFSR, (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR)); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO); + if (request_irq(irq, sabre_ce_intr, + SA_SHIRQ, "SABRE CE", p) < 0) { + prom_printf("SABRE%d: Cannot register CE interrupt.\n", + p->index); + prom_halt(); + } - request_irq(op->irqs[2], sabre_ce_intr, IRQF_SHARED, "SABRE CE", p); - request_irq(op->irqs[0], sabre_pcierr_intr, IRQF_SHARED, - "SABRE PCIERR", p); + irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO); + if (request_irq(irq, sabre_pcierr_intr, + SA_SHIRQ, "SABRE PCIERR", p) < 0) { + prom_printf("SABRE%d: Cannot register PciERR interrupt.\n", + p->index); + prom_halt(); + } tmp = sabre_read(base + SABRE_PCICTRL); tmp |= SABRE_PCICTRL_ERREN; @@ -1196,36 +1383,34 @@ static void pbm_register_toplevel_resources(struct pci_controller_info *p, &pbm->mem_space); } -static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 dma_start, u32 dma_end) +static void sabre_pbm_init(struct pci_controller_info *p, int sabre_node, u32 dma_begin) { struct pci_pbm_info *pbm; - struct device_node *node; - struct property *prop; - u32 *busrange; - int len, simbas_found; + char namebuf[128]; + u32 busrange[2]; + int node, simbas_found; simbas_found = 0; - node = dp->child; - while (node != NULL) { - if (strcmp(node->name, "pci")) - goto next_pci; + node = prom_getchild(sabre_node); + while ((node = prom_searchsiblings(node, "pci")) != 0) { + int err; - prop = of_find_property(node, "model", NULL); - if (!prop || strncmp(prop->value, "SUNW,simba", prop->length)) + err = prom_getproperty(node, "model", namebuf, sizeof(namebuf)); + if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err)) goto next_pci; - simbas_found++; + err = prom_getproperty(node, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if (err == 0 || err == -1) { + prom_printf("APB: Error, cannot get PCI bus-range.\n"); + prom_halt(); + } - prop = of_find_property(node, "bus-range", NULL); - busrange = prop->value; + simbas_found++; if (busrange[0] == 1) pbm = &p->pbm_B; else pbm = &p->pbm_A; - - pbm->name = node->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); - pbm->chip_type = PBM_CHIP_TYPE_SABRE; pbm->parent = p; pbm->prom_node = node; @@ -1233,70 +1418,83 @@ static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; - prop = of_find_property(node, "ranges", &len); - if (prop) { - pbm->pbm_ranges = prop->value; + prom_getstring(node, "name", pbm->prom_name, sizeof(pbm->prom_name)); + err = prom_getproperty(node, "ranges", + (char *)pbm->pbm_ranges, + sizeof(pbm->pbm_ranges)); + if (err != -1) pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - } else { + (err / sizeof(struct linux_prom_pci_ranges)); + else pbm->num_pbm_ranges = 0; - } - - prop = of_find_property(node, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - prop = of_find_property(node, "interrupt-map-mask", - NULL); - pbm->pbm_intmask = prop->value; + err = prom_getproperty(node, "interrupt-map", + (char *)pbm->pbm_intmap, + sizeof(pbm->pbm_intmap)); + if (err != -1) { + pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); + err = prom_getproperty(node, "interrupt-map-mask", + (char *)&pbm->pbm_intmask, + sizeof(pbm->pbm_intmask)); + if (err == -1) { + prom_printf("APB: Fatal error, no interrupt-map-mask.\n"); + prom_halt(); + } } else { pbm->num_pbm_intmap = 0; + memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } pbm_register_toplevel_resources(p, pbm); next_pci: - node = node->sibling; + node = prom_getsibling(node); + if (!node) + break; } if (simbas_found == 0) { - struct resource *rp; + int err; /* No APBs underneath, probably this is a hummingbird * system. */ pbm = &p->pbm_A; pbm->parent = p; - pbm->prom_node = dp; + pbm->prom_node = sabre_node; pbm->pci_first_busno = p->pci_first_busno; pbm->pci_last_busno = p->pci_last_busno; - prop = of_find_property(dp, "ranges", &len); - if (prop) { - pbm->pbm_ranges = prop->value; + prom_getstring(sabre_node, "name", pbm->prom_name, sizeof(pbm->prom_name)); + err = prom_getproperty(sabre_node, "ranges", + (char *) pbm->pbm_ranges, + sizeof(pbm->pbm_ranges)); + if (err != -1) pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - } else { + (err / sizeof(struct linux_prom_pci_ranges)); + else pbm->num_pbm_ranges = 0; - } - - prop = of_find_property(dp, "interrupt-map", &len); - if (prop) { - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - prop = of_find_property(dp, "interrupt-map-mask", - NULL); - pbm->pbm_intmask = prop->value; + err = prom_getproperty(sabre_node, "interrupt-map", + (char *) pbm->pbm_intmap, + sizeof(pbm->pbm_intmap)); + + if (err != -1) { + pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); + err = prom_getproperty(sabre_node, "interrupt-map-mask", + (char *)&pbm->pbm_intmask, + sizeof(pbm->pbm_intmask)); + if (err == -1) { + prom_printf("Hummingbird: Fatal error, no interrupt-map-mask.\n"); + prom_halt(); + } } else { pbm->num_pbm_intmap = 0; + memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } - pbm->name = dp->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); + sprintf(pbm->name, "SABRE%d PBM%c", p->index, + (pbm == &p->pbm_A ? 'A' : 'B')); pbm->io_space.name = pbm->mem_space.name = pbm->name; /* Hack up top-level resources. */ @@ -1304,10 +1502,8 @@ static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp pbm->io_space.end = pbm->io_space.start + (1UL << 24) - 1UL; pbm->io_space.flags = IORESOURCE_IO; - pbm->mem_space.start = - (p->pbm_A.controller_regs + SABRE_MEMSPACE); - pbm->mem_space.end = - (pbm->mem_space.start + ((1UL << 32UL) - 1UL)); + pbm->mem_space.start = p->pbm_A.controller_regs + SABRE_MEMSPACE; + pbm->mem_space.end = pbm->mem_space.start + (unsigned long)dma_begin - 1UL; pbm->mem_space.flags = IORESOURCE_MEM; if (request_resource(&ioport_resource, &pbm->io_space) < 0) { @@ -1319,31 +1515,19 @@ static void sabre_pbm_init(struct pci_controller_info *p, struct device_node *dp prom_halt(); } - rp = kmalloc(sizeof(*rp), GFP_KERNEL); - if (!rp) { - prom_printf("Cannot allocate IOMMU resource.\n"); - prom_halt(); - } - rp->name = "IOMMU"; - rp->start = pbm->mem_space.start + (unsigned long) dma_start; - rp->end = pbm->mem_space.start + (unsigned long) dma_end - 1UL; - rp->flags = IORESOURCE_BUSY; - request_resource(&pbm->mem_space, rp); - pci_register_legacy_regions(&pbm->io_space, &pbm->mem_space); } } -void sabre_init(struct device_node *dp, char *model_name) +void sabre_init(int pnode, char *model_name) { - struct linux_prom64_registers *pr_regs; + struct linux_prom64_registers pr_regs[2]; struct pci_controller_info *p; struct pci_iommu *iommu; - struct property *prop; - int tsbsize; - u32 *busrange; - u32 *vdma; + int tsbsize, err; + u32 busrange[2]; + u32 vdma[2]; u32 upa_portid, dma_mask; u64 clear_irq; @@ -1351,21 +1535,22 @@ void sabre_init(struct device_node *dp, char *model_name) if (!strcmp(model_name, "pci108e,a001")) hummingbird_p = 1; else if (!strcmp(model_name, "SUNW,sabre")) { - prop = of_find_property(dp, "compatible", NULL); - if (prop) { - const char *compat = prop->value; + char compat[64]; - if (!strcmp(compat, "pci108e,a001")) - hummingbird_p = 1; - } - if (!hummingbird_p) { - struct device_node *dp; + if (prom_getproperty(pnode, "compatible", + compat, sizeof(compat)) > 0 && + !strcmp(compat, "pci108e,a001")) { + hummingbird_p = 1; + } else { + int cpu_node; /* Of course, Sun has to encode things a thousand * different ways, inconsistently. */ - cpu_find_by_instance(0, &dp, NULL); - if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe")) + cpu_find_by_instance(0, &cpu_node, NULL); + if (prom_getproperty(cpu_node, "name", + compat, sizeof(compat)) > 0 && + !strcmp(compat, "SUNW,UltraSPARC-IIe")) hummingbird_p = 1; } } @@ -1383,10 +1568,7 @@ void sabre_init(struct device_node *dp, char *model_name) } p->pbm_A.iommu = p->pbm_B.iommu = iommu; - upa_portid = 0xff; - prop = of_find_property(dp, "upa-portid", NULL); - if (prop) - upa_portid = *(u32 *) prop->value; + upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff); p->next = pci_controller_root; pci_controller_root = p; @@ -1396,6 +1578,7 @@ void sabre_init(struct device_node *dp, char *model_name) p->index = pci_num_controllers++; p->pbms_same_domain = 1; p->scan_bus = sabre_scan_bus; + p->irq_build = sabre_irq_build; p->base_address_update = sabre_base_address_update; p->resource_adjust = sabre_resource_adjust; p->pci_ops = &sabre_ops; @@ -1403,9 +1586,13 @@ void sabre_init(struct device_node *dp, char *model_name) /* * Map in SABRE register set and report the presence of this SABRE. */ - - prop = of_find_property(dp, "reg", NULL); - pr_regs = prop->value; + err = prom_getproperty(pnode, "reg", + (char *)&pr_regs[0], sizeof(pr_regs)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get U2P registers " + "from PROM.\n"); + prom_halt(); + } /* * First REG in property is base of entire SABRE register space. @@ -1413,6 +1600,9 @@ void sabre_init(struct device_node *dp, char *model_name) p->pbm_A.controller_regs = pr_regs[0].phys_addr; p->pbm_B.controller_regs = pr_regs[0].phys_addr; + printk("PCI: Found SABRE, main regs at %016lx\n", + p->pbm_A.controller_regs); + /* Clear interrupts */ /* PCI first */ @@ -1431,9 +1621,16 @@ void sabre_init(struct device_node *dp, char *model_name) /* Now map in PCI config space for entire SABRE. */ p->pbm_A.config_space = p->pbm_B.config_space = (p->pbm_A.controller_regs + SABRE_CONFIGSPACE); - - prop = of_find_property(dp, "virtual-dma", NULL); - vdma = prop->value; + printk("SABRE: Shared PCI config space at %016lx\n", + p->pbm_A.config_space); + + err = prom_getproperty(pnode, "virtual-dma", + (char *)&vdma[0], sizeof(vdma)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get virtual-dma property " + "from PROM.\n"); + prom_halt(); + } dma_mask = vdma[0]; switch(vdma[1]) { @@ -1457,13 +1654,21 @@ void sabre_init(struct device_node *dp, char *model_name) sabre_iommu_init(p, tsbsize, vdma[0], dma_mask); - prop = of_find_property(dp, "bus-range", NULL); - busrange = prop->value; + printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]); + + err = prom_getproperty(pnode, "bus-range", + (char *)&busrange[0], sizeof(busrange)); + if(err == 0 || err == -1) { + prom_printf("SABRE: Error, cannot get PCI bus-range " + " from PROM.\n"); + prom_halt(); + } + p->pci_first_busno = busrange[0]; p->pci_last_busno = busrange[1]; /* * Look for APB underneath. */ - sabre_pbm_init(p, dp, vdma[0], vdma[0] + vdma[1]); + sabre_pbm_init(p, pnode, vdma[0]); }