diff options
author | Andy Lutomirski <luto@amacapital.net> | 2013-05-13 19:58:40 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-05-30 23:02:52 -0400 |
commit | d0d98eedee2178c803dd824bb09f52b0e2ac1811 (patch) | |
tree | 302ece15c574dc061b1dea4e67125c7b01342154 /arch/x86/kernel/cpu/mtrr | |
parent | e81f3d81e282a156b47c1c2c09a1976e34073060 (diff) |
Add arch_phys_wc_{add, del} to manipulate WC MTRRs if needed
Several drivers currently use mtrr_add through various #ifdef guards
and/or drm wrappers. The vast majority of them want to add WC MTRRs
on x86 systems and don't actually need the MTRR if PAT (i.e.
ioremap_wc, etc) are working.
arch_phys_wc_add and arch_phys_wc_del are new functions, available
on all architectures and configurations, that add WC MTRRs on x86 if
needed (and handle errors) and do nothing at all otherwise. They're
also easier to use than mtrr_add and mtrr_del, so the call sites can
be simplified.
As an added benefit, this will avoid wasting MTRRs and possibly
warning pointlessly on PAT-supporting systems.
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'arch/x86/kernel/cpu/mtrr')
-rw-r--r-- | arch/x86/kernel/cpu/mtrr/main.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index 726bf963c227..3533d4d16f8c 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c | |||
@@ -51,9 +51,13 @@ | |||
51 | #include <asm/e820.h> | 51 | #include <asm/e820.h> |
52 | #include <asm/mtrr.h> | 52 | #include <asm/mtrr.h> |
53 | #include <asm/msr.h> | 53 | #include <asm/msr.h> |
54 | #include <asm/pat.h> | ||
54 | 55 | ||
55 | #include "mtrr.h" | 56 | #include "mtrr.h" |
56 | 57 | ||
58 | /* arch_phys_wc_add returns an MTRR register index plus this offset. */ | ||
59 | #define MTRR_TO_PHYS_WC_OFFSET 1000 | ||
60 | |||
57 | u32 num_var_ranges; | 61 | u32 num_var_ranges; |
58 | 62 | ||
59 | unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; | 63 | unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; |
@@ -524,6 +528,73 @@ int mtrr_del(int reg, unsigned long base, unsigned long size) | |||
524 | } | 528 | } |
525 | EXPORT_SYMBOL(mtrr_del); | 529 | EXPORT_SYMBOL(mtrr_del); |
526 | 530 | ||
531 | /** | ||
532 | * arch_phys_wc_add - add a WC MTRR and handle errors if PAT is unavailable | ||
533 | * @base: Physical base address | ||
534 | * @size: Size of region | ||
535 | * | ||
536 | * If PAT is available, this does nothing. If PAT is unavailable, it | ||
537 | * attempts to add a WC MTRR covering size bytes starting at base and | ||
538 | * logs an error if this fails. | ||
539 | * | ||
540 | * Drivers must store the return value to pass to mtrr_del_wc_if_needed, | ||
541 | * but drivers should not try to interpret that return value. | ||
542 | */ | ||
543 | int arch_phys_wc_add(unsigned long base, unsigned long size) | ||
544 | { | ||
545 | int ret; | ||
546 | |||
547 | if (pat_enabled) | ||
548 | return 0; /* Success! (We don't need to do anything.) */ | ||
549 | |||
550 | ret = mtrr_add(base, size, MTRR_TYPE_WRCOMB, true); | ||
551 | if (ret < 0) { | ||
552 | pr_warn("Failed to add WC MTRR for [%p-%p]; performance may suffer.", | ||
553 | (void *)base, (void *)(base + size - 1)); | ||
554 | return ret; | ||
555 | } | ||
556 | return ret + MTRR_TO_PHYS_WC_OFFSET; | ||
557 | } | ||
558 | EXPORT_SYMBOL(arch_phys_wc_add); | ||
559 | |||
560 | /* | ||
561 | * arch_phys_wc_del - undoes arch_phys_wc_add | ||
562 | * @handle: Return value from arch_phys_wc_add | ||
563 | * | ||
564 | * This cleans up after mtrr_add_wc_if_needed. | ||
565 | * | ||
566 | * The API guarantees that mtrr_del_wc_if_needed(error code) and | ||
567 | * mtrr_del_wc_if_needed(0) do nothing. | ||
568 | */ | ||
569 | void arch_phys_wc_del(int handle) | ||
570 | { | ||
571 | if (handle >= 1) { | ||
572 | WARN_ON(handle < MTRR_TO_PHYS_WC_OFFSET); | ||
573 | mtrr_del(handle - MTRR_TO_PHYS_WC_OFFSET, 0, 0); | ||
574 | } | ||
575 | } | ||
576 | EXPORT_SYMBOL(arch_phys_wc_del); | ||
577 | |||
578 | /* | ||
579 | * phys_wc_to_mtrr_index - translates arch_phys_wc_add's return value | ||
580 | * @handle: Return value from arch_phys_wc_add | ||
581 | * | ||
582 | * This will turn the return value from arch_phys_wc_add into an mtrr | ||
583 | * index suitable for debugging. | ||
584 | * | ||
585 | * Note: There is no legitimate use for this function, except possibly | ||
586 | * in printk line. Alas there is an illegitimate use in some ancient | ||
587 | * drm ioctls. | ||
588 | */ | ||
589 | int phys_wc_to_mtrr_index(int handle) | ||
590 | { | ||
591 | if (handle < MTRR_TO_PHYS_WC_OFFSET) | ||
592 | return -1; | ||
593 | else | ||
594 | return handle - MTRR_TO_PHYS_WC_OFFSET; | ||
595 | } | ||
596 | EXPORT_SYMBOL_GPL(phys_wc_to_mtrr_index); | ||
597 | |||
527 | /* | 598 | /* |
528 | * HACK ALERT! | 599 | * HACK ALERT! |
529 | * These should be called implicitly, but we can't yet until all the initcall | 600 | * These should be called implicitly, but we can't yet until all the initcall |