| /* pci_common.c: PCI controller common support. |
| * |
| * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) |
| */ |
| |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <linux/init.h> |
| #include <linux/pci.h> |
| #include <linux/device.h> |
| |
| #include <asm/pbm.h> |
| #include <asm/prom.h> |
| #include <asm/of_device.h> |
| |
| #include "pci_impl.h" |
| |
| static void pci_register_legacy_regions(struct resource *io_res, |
| struct resource *mem_res) |
| { |
| struct resource *p; |
| |
| /* VGA Video RAM. */ |
| p = kzalloc(sizeof(*p), GFP_KERNEL); |
| if (!p) |
| return; |
| |
| p->name = "Video RAM area"; |
| p->start = mem_res->start + 0xa0000UL; |
| p->end = p->start + 0x1ffffUL; |
| p->flags = IORESOURCE_BUSY; |
| request_resource(mem_res, p); |
| |
| p = kzalloc(sizeof(*p), GFP_KERNEL); |
| if (!p) |
| return; |
| |
| p->name = "System ROM"; |
| p->start = mem_res->start + 0xf0000UL; |
| p->end = p->start + 0xffffUL; |
| p->flags = IORESOURCE_BUSY; |
| request_resource(mem_res, p); |
| |
| p = kzalloc(sizeof(*p), GFP_KERNEL); |
| if (!p) |
| return; |
| |
| p->name = "Video ROM"; |
| p->start = mem_res->start + 0xc0000UL; |
| p->end = p->start + 0x7fffUL; |
| p->flags = IORESOURCE_BUSY; |
| request_resource(mem_res, p); |
| } |
| |
| static void pci_register_iommu_region(struct pci_pbm_info *pbm) |
| { |
| u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL); |
| |
| if (vdma) { |
| struct resource *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) vdma[0]; |
| rp->end = rp->start + (unsigned long) vdma[1] - 1UL; |
| rp->flags = IORESOURCE_BUSY; |
| request_resource(&pbm->mem_space, rp); |
| } |
| } |
| |
| void pci_determine_mem_io_space(struct pci_pbm_info *pbm) |
| { |
| int i, saw_mem, saw_io; |
| |
| saw_mem = saw_io = 0; |
| for (i = 0; i < pbm->num_pbm_ranges; i++) { |
| struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; |
| unsigned long a; |
| int type; |
| |
| type = (pr->child_phys_hi >> 24) & 0x3; |
| a = (((unsigned long)pr->parent_phys_hi << 32UL) | |
| ((unsigned long)pr->parent_phys_lo << 0UL)); |
| |
| switch (type) { |
| case 0: |
| /* PCI config space, 16MB */ |
| pbm->config_space = a; |
| break; |
| |
| case 1: |
| /* 16-bit IO space, 16MB */ |
| pbm->io_space.start = a; |
| pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); |
| pbm->io_space.flags = IORESOURCE_IO; |
| saw_io = 1; |
| break; |
| |
| case 2: |
| /* 32-bit MEM space, 2GB */ |
| pbm->mem_space.start = a; |
| pbm->mem_space.end = a + (0x80000000UL - 1UL); |
| pbm->mem_space.flags = IORESOURCE_MEM; |
| saw_mem = 1; |
| break; |
| |
| case 3: |
| /* XXX 64-bit MEM handling XXX */ |
| |
| default: |
| break; |
| }; |
| } |
| |
| if (!saw_io || !saw_mem) { |
| prom_printf("%s: Fatal error, missing %s PBM range.\n", |
| pbm->name, |
| (!saw_io ? "IO" : "MEM")); |
| prom_halt(); |
| } |
| |
| printk("%s: PCI IO[%lx] MEM[%lx]\n", |
| pbm->name, |
| pbm->io_space.start, |
| pbm->mem_space.start); |
| |
| pbm->io_space.name = pbm->mem_space.name = pbm->name; |
| |
| request_resource(&ioport_resource, &pbm->io_space); |
| request_resource(&iomem_resource, &pbm->mem_space); |
| |
| pci_register_legacy_regions(&pbm->io_space, |
| &pbm->mem_space); |
| pci_register_iommu_region(pbm); |
| } |
| |
| /* Generic helper routines for PCI error reporting. */ |
| void pci_scan_for_target_abort(struct pci_controller_info *p, |
| struct pci_pbm_info *pbm, |
| struct pci_bus *pbus) |
| { |
| struct pci_dev *pdev; |
| struct pci_bus *bus; |
| |
| list_for_each_entry(pdev, &pbus->devices, bus_list) { |
| u16 status, error_bits; |
| |
| pci_read_config_word(pdev, PCI_STATUS, &status); |
| error_bits = |
| (status & (PCI_STATUS_SIG_TARGET_ABORT | |
| PCI_STATUS_REC_TARGET_ABORT)); |
| if (error_bits) { |
| pci_write_config_word(pdev, PCI_STATUS, error_bits); |
| printk("PCI%d(PBM%c): Device [%s] saw Target Abort [%016x]\n", |
| p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'), |
| pci_name(pdev), status); |
| } |
| } |
| |
| list_for_each_entry(bus, &pbus->children, node) |
| pci_scan_for_target_abort(p, pbm, bus); |
| } |
| |
| void pci_scan_for_master_abort(struct pci_controller_info *p, |
| struct pci_pbm_info *pbm, |
| struct pci_bus *pbus) |
| { |
| struct pci_dev *pdev; |
| struct pci_bus *bus; |
| |
| list_for_each_entry(pdev, &pbus->devices, bus_list) { |
| u16 status, error_bits; |
| |
| pci_read_config_word(pdev, PCI_STATUS, &status); |
| error_bits = |
| (status & (PCI_STATUS_REC_MASTER_ABORT)); |
| if (error_bits) { |
| pci_write_config_word(pdev, PCI_STATUS, error_bits); |
| printk("PCI%d(PBM%c): Device [%s] received Master Abort [%016x]\n", |
| p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'), |
| pci_name(pdev), status); |
| } |
| } |
| |
| list_for_each_entry(bus, &pbus->children, node) |
| pci_scan_for_master_abort(p, pbm, bus); |
| } |
| |
| void pci_scan_for_parity_error(struct pci_controller_info *p, |
| struct pci_pbm_info *pbm, |
| struct pci_bus *pbus) |
| { |
| struct pci_dev *pdev; |
| struct pci_bus *bus; |
| |
| list_for_each_entry(pdev, &pbus->devices, bus_list) { |
| u16 status, error_bits; |
| |
| pci_read_config_word(pdev, PCI_STATUS, &status); |
| error_bits = |
| (status & (PCI_STATUS_PARITY | |
| PCI_STATUS_DETECTED_PARITY)); |
| if (error_bits) { |
| pci_write_config_word(pdev, PCI_STATUS, error_bits); |
| printk("PCI%d(PBM%c): Device [%s] saw Parity Error [%016x]\n", |
| p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'), |
| pci_name(pdev), status); |
| } |
| } |
| |
| list_for_each_entry(bus, &pbus->children, node) |
| pci_scan_for_parity_error(p, pbm, bus); |
| } |