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 | } |