]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/kernel/pci-common.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/agk/linux-2.6-dm
[linux-2.6-omap-h63xx.git] / arch / powerpc / kernel / pci-common.c
index ea0c61e09b7640553cf70c92b184339ff3bfd0bc..1ec73938a00f122c7685c713aefb383934728b30 100644 (file)
@@ -56,6 +56,34 @@ resource_size_t isa_mem_base;
 /* Default PCI flags is 0 */
 unsigned int ppc_pci_flags;
 
+static struct dma_mapping_ops *pci_dma_ops;
+
+void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
+{
+       pci_dma_ops = dma_ops;
+}
+
+struct dma_mapping_ops *get_pci_dma_ops(void)
+{
+       return pci_dma_ops;
+}
+EXPORT_SYMBOL(get_pci_dma_ops);
+
+int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       return dma_set_mask(&dev->dev, mask);
+}
+
+int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       int rc;
+
+       rc = dma_set_mask(&dev->dev, mask);
+       dev->dev.coherent_dma_mask = dev->dma_mask;
+
+       return rc;
+}
+
 struct pci_controller *pcibios_alloc_controller(struct device_node *dev)
 {
        struct pci_controller *phb;
@@ -180,6 +208,26 @@ char __devinit *pcibios_setup(char *str)
        return str;
 }
 
+void __devinit pcibios_setup_new_device(struct pci_dev *dev)
+{
+       struct dev_archdata *sd = &dev->dev.archdata;
+
+       sd->of_node = pci_device_to_OF_node(dev);
+
+       DBG("PCI: device %s OF node: %s\n", pci_name(dev),
+           sd->of_node ? sd->of_node->full_name : "<none>");
+
+       sd->dma_ops = pci_dma_ops;
+#ifdef CONFIG_PPC32
+       sd->dma_data = (void *)PCI_DRAM_OFFSET;
+#endif
+       set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
+
+       if (ppc_md.pci_dma_dev_setup)
+               ppc_md.pci_dma_dev_setup(dev);
+}
+EXPORT_SYMBOL(pcibios_setup_new_device);
+
 /*
  * Reads the interrupt pin to determine if interrupt is use by card.
  * If the interrupt is used, then gets the interrupt line from the
@@ -371,7 +419,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
        struct pci_dev *pdev = NULL;
        struct resource *found = NULL;
        unsigned long prot = pgprot_val(protection);
-       unsigned long offset = pfn << PAGE_SHIFT;
+       resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT;
        int i;
 
        if (page_is_ram(pfn))
@@ -403,7 +451,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
                pci_dev_put(pdev);
        }
 
-       DBG("non-PCI map for %lx, prot: %lx\n", offset, prot);
+       DBG("non-PCI map for %llx, prot: %lx\n",
+           (unsigned long long)offset, prot);
 
        return __pgprot(prot);
 }
@@ -422,7 +471,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
 int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
                        enum pci_mmap_state mmap_state, int write_combine)
 {
-       resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT;
+       resource_size_t offset =
+               ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
        struct resource *rp;
        int ret;
 
@@ -441,6 +491,132 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
        return ret;
 }
 
+/* This provides legacy IO read access on a bus */
+int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
+{
+       unsigned long offset;
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct resource *rp = &hose->io_resource;
+       void __iomem *addr;
+
+       /* Check if port can be supported by that bus. We only check
+        * the ranges of the PHB though, not the bus itself as the rules
+        * for forwarding legacy cycles down bridges are not our problem
+        * here. So if the host bridge supports it, we do it.
+        */
+       offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+       offset += port;
+
+       if (!(rp->flags & IORESOURCE_IO))
+               return -ENXIO;
+       if (offset < rp->start || (offset + size) > rp->end)
+               return -ENXIO;
+       addr = hose->io_base_virt + port;
+
+       switch(size) {
+       case 1:
+               *((u8 *)val) = in_8(addr);
+               return 1;
+       case 2:
+               if (port & 1)
+                       return -EINVAL;
+               *((u16 *)val) = in_le16(addr);
+               return 2;
+       case 4:
+               if (port & 3)
+                       return -EINVAL;
+               *((u32 *)val) = in_le32(addr);
+               return 4;
+       }
+       return -EINVAL;
+}
+
+/* This provides legacy IO write access on a bus */
+int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
+{
+       unsigned long offset;
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct resource *rp = &hose->io_resource;
+       void __iomem *addr;
+
+       /* Check if port can be supported by that bus. We only check
+        * the ranges of the PHB though, not the bus itself as the rules
+        * for forwarding legacy cycles down bridges are not our problem
+        * here. So if the host bridge supports it, we do it.
+        */
+       offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+       offset += port;
+
+       if (!(rp->flags & IORESOURCE_IO))
+               return -ENXIO;
+       if (offset < rp->start || (offset + size) > rp->end)
+               return -ENXIO;
+       addr = hose->io_base_virt + port;
+
+       /* WARNING: The generic code is idiotic. It gets passed a pointer
+        * to what can be a 1, 2 or 4 byte quantity and always reads that
+        * as a u32, which means that we have to correct the location of
+        * the data read within those 32 bits for size 1 and 2
+        */
+       switch(size) {
+       case 1:
+               out_8(addr, val >> 24);
+               return 1;
+       case 2:
+               if (port & 1)
+                       return -EINVAL;
+               out_le16(addr, val >> 16);
+               return 2;
+       case 4:
+               if (port & 3)
+                       return -EINVAL;
+               out_le32(addr, val);
+               return 4;
+       }
+       return -EINVAL;
+}
+
+/* This provides legacy IO or memory mmap access on a bus */
+int pci_mmap_legacy_page_range(struct pci_bus *bus,
+                              struct vm_area_struct *vma,
+                              enum pci_mmap_state mmap_state)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       resource_size_t offset =
+               ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
+       resource_size_t size = vma->vm_end - vma->vm_start;
+       struct resource *rp;
+
+       pr_debug("pci_mmap_legacy_page_range(%04x:%02x, %s @%llx..%llx)\n",
+                pci_domain_nr(bus), bus->number,
+                mmap_state == pci_mmap_mem ? "MEM" : "IO",
+                (unsigned long long)offset,
+                (unsigned long long)(offset + size - 1));
+
+       if (mmap_state == pci_mmap_mem) {
+               if ((offset + size) > hose->isa_mem_size)
+                       return -ENXIO;
+               offset += hose->isa_mem_phys;
+       } else {
+               unsigned long io_offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               unsigned long roffset = offset + io_offset;
+               rp = &hose->io_resource;
+               if (!(rp->flags & IORESOURCE_IO))
+                       return -ENXIO;
+               if (roffset < rp->start || (roffset + size) > rp->end)
+                       return -ENXIO;
+               offset += hose->io_base_phys;
+       }
+       pr_debug(" -> mapping phys %llx\n", (unsigned long long)offset);
+
+       vma->vm_pgoff = offset >> PAGE_SHIFT;
+       vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
+                                    | _PAGE_NO_CACHE | _PAGE_GUARDED);
+       return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                              vma->vm_end - vma->vm_start,
+                              vma->vm_page_prot);
+}
+
 void pci_resource_to_user(const struct pci_dev *dev, int bar,
                          const struct resource *rsrc,
                          resource_size_t *start, resource_size_t *end)
@@ -543,6 +719,12 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
                cpu_addr = of_translate_address(dev, ranges + 3);
                size = of_read_number(ranges + pna + 3, 2);
                ranges += np;
+
+               /* If we failed translation or got a zero-sized region
+                * (some FW try to feed us with non sensical zero sized regions
+                * such as power3 which look like some kind of attempt at exposing
+                * the VGA memory hole)
+                */
                if (cpu_addr == OF_BAD_ADDR || size == 0)
                        continue;
 
@@ -616,6 +798,8 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
                                isa_hole = memno;
                                if (primary || isa_mem_base == 0)
                                        isa_mem_base = cpu_addr;
+                               hose->isa_mem_phys = cpu_addr;
+                               hose->isa_mem_size = size;
                        }
 
                        /* We get the PCI/Mem offset from the first range or
@@ -731,11 +915,6 @@ static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev)
 
        res->start = (res->start + offset) & mask;
        res->end = (res->end + offset) & mask;
-
-       pr_debug("PCI:%s            %016llx-%016llx\n",
-                pci_name(dev),
-                (unsigned long long)res->start,
-                (unsigned long long)res->end);
 }
 
 
@@ -781,6 +960,11 @@ static void __devinit pcibios_fixup_resources(struct pci_dev *dev)
                         (unsigned int)res->flags);
 
                fixup_resource(res, dev);
+
+               pr_debug("PCI:%s            %016llx-%016llx\n",
+                        pci_name(dev),
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end);
        }
 
        /* Call machine specific resource fixup */
@@ -789,58 +973,127 @@ static void __devinit pcibios_fixup_resources(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources);
 
-static void __devinit __pcibios_fixup_bus(struct pci_bus *bus)
+/* This function tries to figure out if a bridge resource has been initialized
+ * by the firmware or not. It doesn't have to be absolutely bullet proof, but
+ * things go more smoothly when it gets it right. It should covers cases such
+ * as Apple "closed" bridge resources and bare-metal pSeries unassigned bridges
+ */
+static int __devinit pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
+                                                          struct resource *res)
 {
        struct pci_controller *hose = pci_bus_to_host(bus);
        struct pci_dev *dev = bus->self;
+       resource_size_t offset;
+       u16 command;
+       int i;
 
-       pr_debug("PCI: Fixup bus %d (%s)\n", bus->number, dev ? pci_name(dev) : "PHB");
+       /* We don't do anything if PCI_PROBE_ONLY is set */
+       if (ppc_pci_flags & PPC_PCI_PROBE_ONLY)
+               return 0;
 
-       /* Fixup PCI<->PCI bridges. Host bridges are handled separately, for
-        * now differently between 32 and 64 bits.
-        */
-       if (dev != NULL) {
-               struct resource *res;
-               int i;
+       /* Job is a bit different between memory and IO */
+       if (res->flags & IORESOURCE_MEM) {
+               /* If the BAR is non-0 (res != pci_mem_offset) then it's probably been
+                * initialized by somebody
+                */
+               if (res->start != hose->pci_mem_offset)
+                       return 0;
 
-               for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
-                       if ((res = bus->resource[i]) == NULL)
-                               continue;
-                       if (!res->flags)
-                               continue;
-                       if (i >= 3 && bus->self->transparent)
-                               continue;
-                       /* On PowerMac, Apple leaves bridge windows open over
-                        * an inaccessible region of memory space (0...fffff)
-                        * which is somewhat bogus, but that's what they think
-                        * means disabled...
-                        *
-                        * We clear those to force them to be reallocated later
-                        *
-                        * We detect such regions by the fact that the base is
-                        * equal to the pci_mem_offset of the host bridge and
-                        * their size is smaller than 1M.
-                        */
-                       if (res->flags & IORESOURCE_MEM &&
-                           res->start == hose->pci_mem_offset &&
-                           res->end < 0x100000) {
-                               printk(KERN_INFO
-                                      "PCI: Closing bogus Apple Firmware"
-                                      " region %d on bus 0x%02x\n",
-                                      i, bus->number);
-                               res->flags = 0;
-                               continue;
-                       }
+               /* The BAR is 0, let's check if memory decoding is enabled on
+                * the bridge. If not, we consider it unassigned
+                */
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               if ((command & PCI_COMMAND_MEMORY) == 0)
+                       return 1;
 
-                       pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n",
-                                pci_name(dev), i,
-                                (unsigned long long)res->start,\
-                                (unsigned long long)res->end,
-                                (unsigned int)res->flags);
+               /* Memory decoding is enabled and the BAR is 0. If any of the bridge
+                * resources covers that starting address (0 then it's good enough for
+                * us for memory
+                */
+               for (i = 0; i < 3; i++) {
+                       if ((hose->mem_resources[i].flags & IORESOURCE_MEM) &&
+                           hose->mem_resources[i].start == hose->pci_mem_offset)
+                               return 0;
+               }
+
+               /* Well, it starts at 0 and we know it will collide so we may as
+                * well consider it as unassigned. That covers the Apple case.
+                */
+               return 1;
+       } else {
+               /* If the BAR is non-0, then we consider it assigned */
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               if (((res->start - offset) & 0xfffffffful) != 0)
+                       return 0;
+
+               /* Here, we are a bit different than memory as typically IO space
+                * starting at low addresses -is- valid. What we do instead if that
+                * we consider as unassigned anything that doesn't have IO enabled
+                * in the PCI command register, and that's it.
+                */
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               if (command & PCI_COMMAND_IO)
+                       return 0;
+
+               /* It's starting at 0 and IO is disabled in the bridge, consider
+                * it unassigned
+                */
+               return 1;
+       }
+}
 
-                       fixup_resource(res, dev);
+/* Fixup resources of a PCI<->PCI bridge */
+static void __devinit pcibios_fixup_bridge(struct pci_bus *bus)
+{
+       struct resource *res;
+       int i;
+
+       struct pci_dev *dev = bus->self;
+
+       for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
+               if ((res = bus->resource[i]) == NULL)
+                       continue;
+               if (!res->flags)
+                       continue;
+               if (i >= 3 && bus->self->transparent)
+                       continue;
+
+               pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n",
+                        pci_name(dev), i,
+                        (unsigned long long)res->start,\
+                        (unsigned long long)res->end,
+                        (unsigned int)res->flags);
+
+               /* Perform fixup */
+               fixup_resource(res, dev);
+
+               /* Try to detect uninitialized P2P bridge resources,
+                * and clear them out so they get re-assigned later
+                */
+               if (pcibios_uninitialized_bridge_resource(bus, res)) {
+                       res->flags = 0;
+                       pr_debug("PCI:%s            (unassigned)\n", pci_name(dev));
+               } else {
+
+                       pr_debug("PCI:%s            %016llx-%016llx\n",
+                                pci_name(dev),
+                                (unsigned long long)res->start,
+                                (unsigned long long)res->end);
                }
        }
+}
+
+static void __devinit __pcibios_fixup_bus(struct pci_bus *bus)
+{
+       struct pci_dev *dev = bus->self;
+
+       pr_debug("PCI: Fixup bus %d (%s)\n", bus->number, dev ? pci_name(dev) : "PHB");
+
+       /* Fixup PCI<->PCI bridges. Host bridges are handled separately, for
+        * now differently between 32 and 64 bits.
+        */
+       if (dev != NULL)
+               pcibios_fixup_bridge(bus);
 
        /* Additional setup that is different between 32 and 64 bits for now */
        pcibios_do_bus_setup(bus);