#include "pci_impl.h"
-unsigned long pci_memspace_mask = 0xffffffffUL;
-
#ifndef CONFIG_PCI
/* A "nop" PCI implementation. */
asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
{ "SUNW,sun4v-pci", sun4v_pci_init },
{ "pciex108e,80f0", fire_pci_init },
};
-#define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \
- sizeof(pci_controller_table[0]))
+#define PCI_NUM_CONTROLLER_TYPES ARRAY_SIZE(pci_controller_table)
static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp)
{
{
struct device_node *child;
const u32 *reg;
- int reglen, devfn;
+ int reglen, devfn, prev_devfn;
struct pci_dev *dev;
if (ofpci_verbose)
node->full_name, bus->number);
child = NULL;
+ prev_devfn = -1;
while ((child = of_get_next_child(node, child)) != NULL) {
if (ofpci_verbose)
printk(" * %s\n", child->full_name);
reg = of_get_property(child, "reg", ®len);
if (reg == NULL || reglen < 20)
continue;
+
devfn = (reg[0] >> 8) & 0xff;
+ /* This is a workaround for some device trees
+ * which list PCI devices twice. On the V100
+ * for example, device number 3 is listed twice.
+ * Once as "pm" and once again as "lomp".
+ */
+ if (devfn == prev_devfn)
+ continue;
+ prev_devfn = devfn;
+
/* create a new pci_dev for this device */
dev = of_create_pci_dev(pbm, child, bus, devfn, 0);
if (!dev)
return 0;
}
-/* Adjust vm_pgoff of VMA such that it is the physical page offset corresponding
- * to the 32-bit pci bus offset for DEV requested by the user.
+/* Adjust vm_pgoff of VMA such that it is the physical page offset
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
*
* Basically, the user finds the base address for his device which he wishes
* to mmap. They read the 32-bit value from the config space base register,
*
* Returns negative error code on failure, zero on success.
*/
-static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
+static int __pci_mmap_make_offset(struct pci_dev *pdev,
+ struct vm_area_struct *vma,
enum pci_mmap_state mmap_state)
{
- unsigned long user_offset = vma->vm_pgoff << PAGE_SHIFT;
- unsigned long user32 = user_offset & pci_memspace_mask;
- unsigned long largest_base, this_base, addr32;
- int i;
+ unsigned long user_paddr, user_size;
+ int i, err;
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
- return __pci_mmap_make_offset_bus(dev, vma, mmap_state);
+ /* First compute the physical address in vma->vm_pgoff,
+ * making sure the user offset is within range in the
+ * appropriate PCI space.
+ */
+ err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
+ if (err)
+ return err;
+
+ /* If this is a mapping on a host bridge, any address
+ * is OK.
+ */
+ if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+ return err;
+
+ /* Otherwise make sure it's in the range for one of the
+ * device's resources.
+ */
+ user_paddr = vma->vm_pgoff << PAGE_SHIFT;
+ user_size = vma->vm_end - vma->vm_start;
- /* Figure out which base address this is for. */
- largest_base = 0UL;
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
- struct resource *rp = &dev->resource[i];
+ struct resource *rp = &pdev->resource[i];
/* Active? */
if (!rp->flags)
continue;
}
- this_base = rp->start;
-
- addr32 = (this_base & PAGE_MASK) & pci_memspace_mask;
-
- if (mmap_state == pci_mmap_io)
- addr32 &= 0xffffff;
-
- if (addr32 <= user32 && this_base > largest_base)
- largest_base = this_base;
+ if ((rp->start <= user_paddr) &&
+ (user_paddr + user_size) <= (rp->end + 1UL))
+ break;
}
- if (largest_base == 0UL)
+ if (i > PCI_ROM_RESOURCE)
return -EINVAL;
- /* Now construct the final physical address. */
- if (mmap_state == pci_mmap_io)
- vma->vm_pgoff = (((largest_base & ~0xffffffUL) | user32) >> PAGE_SHIFT);
- else
- vma->vm_pgoff = (((largest_base & ~(pci_memspace_mask)) | user32) >> PAGE_SHIFT);
-
return 0;
}
return (device_mask & dma_addr_mask) == dma_addr_mask;
}
+void pci_resource_to_user(const struct pci_dev *pdev, int bar,
+ const struct resource *rp, resource_size_t *start,
+ resource_size_t *end)
+{
+ struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
+ unsigned long offset;
+
+ if (rp->flags & IORESOURCE_IO)
+ offset = pbm->io_space.start;
+ else
+ offset = pbm->mem_space.start;
+
+ *start = rp->start - offset;
+ *end = rp->end - offset;
+}
+
#endif /* !(CONFIG_PCI) */