| 1 |
--- a/arch/alpha/kernel/pci.c.ORIG 2008-10-17 13:10:37.000000000 -0400
|
| 2 |
+++ a/arch/alpha/kernel/pci.c 2008-10-22 08:51:27.000000000 -0400
|
| 3 |
@@ -560,6 +560,176 @@ void pci_iounmap(struct pci_dev *dev, vo
|
| 4 |
EXPORT_SYMBOL(pci_iomap);
|
| 5 |
EXPORT_SYMBOL(pci_iounmap);
|
| 6 |
|
| 7 |
+/* Platform support for /proc/bus/pci/X/Y mmap()s. */
|
| 8 |
+
|
| 9 |
+/* If the user uses a host-bridge as the PCI device, he may use
|
| 10 |
+ * this to perform a raw mmap() of the I/O or MEM space behind
|
| 11 |
+ * that controller.
|
| 12 |
+ *
|
| 13 |
+ * This can be useful for execution of x86 PCI bios initialization code
|
| 14 |
+ * on a PCI card, like the xfree86 int10 stuff does.
|
| 15 |
+ */
|
| 16 |
+static int __pci_mmap_make_offset_bus(struct pci_dev *pdev,
|
| 17 |
+ struct vm_area_struct *vma,
|
| 18 |
+ enum pci_mmap_state mmap_state)
|
| 19 |
+{
|
| 20 |
+ struct pci_controller *hose = pdev->sysdata;
|
| 21 |
+ unsigned long space_size, user_offset, user_size;
|
| 22 |
+
|
| 23 |
+ if (mmap_state == pci_mmap_io) {
|
| 24 |
+ space_size = (hose->io_space->end -
|
| 25 |
+ hose->io_space->start) + 1;
|
| 26 |
+ } else {
|
| 27 |
+ space_size = (hose->mem_space->end -
|
| 28 |
+ hose->mem_space->start) + 1;
|
| 29 |
+ }
|
| 30 |
+
|
| 31 |
+ /* Make sure the request is in range. */
|
| 32 |
+ user_offset = vma->vm_pgoff << PAGE_SHIFT;
|
| 33 |
+ user_size = vma->vm_end - vma->vm_start;
|
| 34 |
+
|
| 35 |
+ if (user_offset >= space_size ||
|
| 36 |
+ (user_offset + user_size) > space_size)
|
| 37 |
+ return -EINVAL;
|
| 38 |
+
|
| 39 |
+ /* FIXME FIXME: for now, only worry about DENSE IO and MEM */
|
| 40 |
+ if (mmap_state == pci_mmap_io) {
|
| 41 |
+ vma->vm_pgoff = (hose->dense_io_base +
|
| 42 |
+ user_offset) >> PAGE_SHIFT;
|
| 43 |
+ } else {
|
| 44 |
+ vma->vm_pgoff = (hose->dense_mem_base +
|
| 45 |
+ user_offset) >> PAGE_SHIFT;
|
| 46 |
+ }
|
| 47 |
+
|
| 48 |
+ return 0;
|
| 49 |
+}
|
| 50 |
+
|
| 51 |
+/* Adjust vm_pgoff of VMA such that it is the physical page offset
|
| 52 |
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
|
| 53 |
+ *
|
| 54 |
+ * Basically, the user finds the base address for his device which he wishes
|
| 55 |
+ * to mmap. They read the 32-bit value from the config space base register,
|
| 56 |
+ * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
|
| 57 |
+ * offset parameter of mmap on /proc/bus/pci/XXX for that device.
|
| 58 |
+ *
|
| 59 |
+ * Returns negative error code on failure, zero on success.
|
| 60 |
+ */
|
| 61 |
+static int __pci_mmap_make_offset(struct pci_dev *pdev,
|
| 62 |
+ struct vm_area_struct *vma,
|
| 63 |
+ enum pci_mmap_state mmap_state)
|
| 64 |
+{
|
| 65 |
+ unsigned long user_paddr, user_size;
|
| 66 |
+ int i, err;
|
| 67 |
+ unsigned long orig_user_paddr = vma->vm_pgoff << PAGE_SHIFT;
|
| 68 |
+
|
| 69 |
+ /* First compute the physical address in vma->vm_pgoff,
|
| 70 |
+ * making sure the user offset is within range in the
|
| 71 |
+ * appropriate PCI space.
|
| 72 |
+ */
|
| 73 |
+ err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
|
| 74 |
+ if (err)
|
| 75 |
+ return err;
|
| 76 |
+
|
| 77 |
+ /* If this is a mapping on a host bridge, any address
|
| 78 |
+ * is OK.
|
| 79 |
+ */
|
| 80 |
+ if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
|
| 81 |
+ return err;
|
| 82 |
+
|
| 83 |
+ /* Otherwise make sure it's in the range for one of the
|
| 84 |
+ * device's resources.
|
| 85 |
+ */
|
| 86 |
+ user_paddr = vma->vm_pgoff << PAGE_SHIFT;
|
| 87 |
+ user_size = vma->vm_end - vma->vm_start;
|
| 88 |
+
|
| 89 |
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
|
| 90 |
+ struct resource *rp = &pdev->resource[i];
|
| 91 |
+
|
| 92 |
+ /* Active? */
|
| 93 |
+ if (!rp->flags)
|
| 94 |
+ continue;
|
| 95 |
+
|
| 96 |
+ /* Same type? */
|
| 97 |
+ if (i == PCI_ROM_RESOURCE) {
|
| 98 |
+ if (mmap_state != pci_mmap_mem)
|
| 99 |
+ continue;
|
| 100 |
+ } else {
|
| 101 |
+ if ((mmap_state == pci_mmap_io &&
|
| 102 |
+ (rp->flags & IORESOURCE_IO) == 0) ||
|
| 103 |
+ (mmap_state == pci_mmap_mem &&
|
| 104 |
+ (rp->flags & IORESOURCE_MEM) == 0))
|
| 105 |
+ continue;
|
| 106 |
+ }
|
| 107 |
+
|
| 108 |
+ if ((rp->start <= user_paddr) &&
|
| 109 |
+ (user_paddr + user_size) <= (rp->end + 1UL))
|
| 110 |
+ break;
|
| 111 |
+ if ((rp->start <= orig_user_paddr) &&
|
| 112 |
+ (orig_user_paddr + user_size) <= (rp->end + 1UL)) {
|
| 113 |
+ printk(KERN_WARNING "pci: res-range w/o bus base\n");
|
| 114 |
+ break;
|
| 115 |
+ }
|
| 116 |
+ }
|
| 117 |
+
|
| 118 |
+ if (i > PCI_ROM_RESOURCE)
|
| 119 |
+ return -EINVAL;
|
| 120 |
+
|
| 121 |
+ return 0;
|
| 122 |
+}
|
| 123 |
+
|
| 124 |
+/* Set vm_flags of VMA, as appropriate for this architecture, for a pci device
|
| 125 |
+ * mapping.
|
| 126 |
+ */
|
| 127 |
+static void __pci_mmap_set_flags(struct pci_dev *dev,
|
| 128 |
+ struct vm_area_struct *vma,
|
| 129 |
+ enum pci_mmap_state mmap_state)
|
| 130 |
+{
|
| 131 |
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
|
| 132 |
+}
|
| 133 |
+
|
| 134 |
+/* Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
|
| 135 |
+ * device mapping.
|
| 136 |
+ */
|
| 137 |
+static void __pci_mmap_set_pgprot(struct pci_dev *dev,
|
| 138 |
+ struct vm_area_struct *vma,
|
| 139 |
+ enum pci_mmap_state mmap_state)
|
| 140 |
+{
|
| 141 |
+ /* FIXME FIXME *//* Our io_remap_pfn_range takes care of this, do nothing. */
|
| 142 |
+}
|
| 143 |
+
|
| 144 |
+/* Perform the actual remap of the pages for a PCI device mapping, as appropriate
|
| 145 |
+ * for this architecture. The region in the process to map is described by vm_start
|
| 146 |
+ * and vm_end members of VMA, the base physical address is found in vm_pgoff.
|
| 147 |
+ * The pci device structure is provided so that architectures may make mapping
|
| 148 |
+ * decisions on a per-device or per-bus basis.
|
| 149 |
+ *
|
| 150 |
+ * Returns a negative error code on failure, zero on success.
|
| 151 |
+ */
|
| 152 |
+int pci_mmap_page_range(struct pci_dev *dev,
|
| 153 |
+ struct vm_area_struct *vma,
|
| 154 |
+ enum pci_mmap_state mmap_state,
|
| 155 |
+ int write_combine)
|
| 156 |
+{
|
| 157 |
+ int ret;
|
| 158 |
+
|
| 159 |
+ ret = __pci_mmap_make_offset(dev, vma, mmap_state);
|
| 160 |
+ if (ret < 0)
|
| 161 |
+ return ret;
|
| 162 |
+
|
| 163 |
+ __pci_mmap_set_flags(dev, vma, mmap_state);
|
| 164 |
+ __pci_mmap_set_pgprot(dev, vma, mmap_state);
|
| 165 |
+
|
| 166 |
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
| 167 |
+ ret = io_remap_pfn_range(vma, vma->vm_start,
|
| 168 |
+ vma->vm_pgoff,
|
| 169 |
+ vma->vm_end - vma->vm_start,
|
| 170 |
+ vma->vm_page_prot);
|
| 171 |
+ if (ret)
|
| 172 |
+ return ret;
|
| 173 |
+
|
| 174 |
+ return 0;
|
| 175 |
+}
|
| 176 |
+
|
| 177 |
/* FIXME: Some boxes have multiple ISA bridges! */
|
| 178 |
struct pci_dev *isa_bridge;
|
| 179 |
EXPORT_SYMBOL(isa_bridge);
|