diff options
Diffstat (limited to 'arch/x86/pci/i386.c')
| -rw-r--r-- | arch/x86/pci/i386.c | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 103b9dff1213..2ead72363077 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c | |||
| @@ -30,6 +30,9 @@ | |||
| 30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
| 31 | #include <linux/ioport.h> | 31 | #include <linux/ioport.h> |
| 32 | #include <linux/errno.h> | 32 | #include <linux/errno.h> |
| 33 | #include <linux/bootmem.h> | ||
| 34 | |||
| 35 | #include <asm/pat.h> | ||
| 33 | 36 | ||
| 34 | #include "pci.h" | 37 | #include "pci.h" |
| 35 | 38 | ||
| @@ -297,10 +300,35 @@ void pcibios_set_master(struct pci_dev *dev) | |||
| 297 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); | 300 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); |
| 298 | } | 301 | } |
| 299 | 302 | ||
| 303 | static void pci_unmap_page_range(struct vm_area_struct *vma) | ||
| 304 | { | ||
| 305 | u64 addr = (u64)vma->vm_pgoff << PAGE_SHIFT; | ||
| 306 | free_memtype(addr, addr + vma->vm_end - vma->vm_start); | ||
| 307 | } | ||
| 308 | |||
| 309 | static void pci_track_mmap_page_range(struct vm_area_struct *vma) | ||
| 310 | { | ||
| 311 | u64 addr = (u64)vma->vm_pgoff << PAGE_SHIFT; | ||
| 312 | unsigned long flags = pgprot_val(vma->vm_page_prot) | ||
| 313 | & _PAGE_CACHE_MASK; | ||
| 314 | |||
| 315 | reserve_memtype(addr, addr + vma->vm_end - vma->vm_start, flags, NULL); | ||
| 316 | } | ||
| 317 | |||
| 318 | static struct vm_operations_struct pci_mmap_ops = { | ||
| 319 | .open = pci_track_mmap_page_range, | ||
| 320 | .close = pci_unmap_page_range, | ||
| 321 | }; | ||
| 322 | |||
| 300 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 323 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, |
| 301 | enum pci_mmap_state mmap_state, int write_combine) | 324 | enum pci_mmap_state mmap_state, int write_combine) |
| 302 | { | 325 | { |
| 303 | unsigned long prot; | 326 | unsigned long prot; |
| 327 | u64 addr = vma->vm_pgoff << PAGE_SHIFT; | ||
| 328 | unsigned long len = vma->vm_end - vma->vm_start; | ||
| 329 | unsigned long flags; | ||
| 330 | unsigned long new_flags; | ||
| 331 | int retval; | ||
| 304 | 332 | ||
| 305 | /* I/O space cannot be accessed via normal processor loads and | 333 | /* I/O space cannot be accessed via normal processor loads and |
| 306 | * stores on this platform. | 334 | * stores on this platform. |
| @@ -308,21 +336,50 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | |||
| 308 | if (mmap_state == pci_mmap_io) | 336 | if (mmap_state == pci_mmap_io) |
| 309 | return -EINVAL; | 337 | return -EINVAL; |
| 310 | 338 | ||
| 311 | /* Leave vm_pgoff as-is, the PCI space address is the physical | ||
| 312 | * address on this platform. | ||
| 313 | */ | ||
| 314 | prot = pgprot_val(vma->vm_page_prot); | 339 | prot = pgprot_val(vma->vm_page_prot); |
| 315 | if (boot_cpu_data.x86 > 3) | 340 | if (pat_wc_enabled && write_combine) |
| 316 | prot |= _PAGE_PCD | _PAGE_PWT; | 341 | prot |= _PAGE_CACHE_WC; |
| 342 | else if (boot_cpu_data.x86 > 3) | ||
| 343 | prot |= _PAGE_CACHE_UC; | ||
| 344 | |||
| 317 | vma->vm_page_prot = __pgprot(prot); | 345 | vma->vm_page_prot = __pgprot(prot); |
| 318 | 346 | ||
| 319 | /* Write-combine setting is ignored, it is changed via the mtrr | 347 | flags = pgprot_val(vma->vm_page_prot) & _PAGE_CACHE_MASK; |
| 320 | * interfaces on this platform. | 348 | retval = reserve_memtype(addr, addr + len, flags, &new_flags); |
| 321 | */ | 349 | if (retval) |
| 350 | return retval; | ||
| 351 | |||
| 352 | if (flags != new_flags) { | ||
| 353 | /* | ||
| 354 | * Do not fallback to certain memory types with certain | ||
| 355 | * requested type: | ||
| 356 | * - request is uncached, return cannot be write-back | ||
| 357 | * - request is uncached, return cannot be write-combine | ||
| 358 | * - request is write-combine, return cannot be write-back | ||
| 359 | */ | ||
| 360 | if ((flags == _PAGE_CACHE_UC && | ||
| 361 | (new_flags == _PAGE_CACHE_WB || | ||
| 362 | new_flags == _PAGE_CACHE_WC)) || | ||
| 363 | (flags == _PAGE_CACHE_WC && | ||
| 364 | new_flags == _PAGE_CACHE_WB)) { | ||
| 365 | free_memtype(addr, addr+len); | ||
| 366 | return -EINVAL; | ||
| 367 | } | ||
| 368 | flags = new_flags; | ||
| 369 | } | ||
| 370 | |||
| 371 | if (vma->vm_pgoff <= max_pfn_mapped && | ||
| 372 | ioremap_change_attr((unsigned long)__va(addr), len, flags)) { | ||
| 373 | free_memtype(addr, addr + len); | ||
| 374 | return -EINVAL; | ||
| 375 | } | ||
| 376 | |||
| 322 | if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | 377 | if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, |
| 323 | vma->vm_end - vma->vm_start, | 378 | vma->vm_end - vma->vm_start, |
| 324 | vma->vm_page_prot)) | 379 | vma->vm_page_prot)) |
| 325 | return -EAGAIN; | 380 | return -EAGAIN; |
| 326 | 381 | ||
| 382 | vma->vm_ops = &pci_mmap_ops; | ||
| 383 | |||
| 327 | return 0; | 384 | return 0; |
| 328 | } | 385 | } |
