aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@amacapital.net>2013-05-13 19:58:40 -0400
committerDave Airlie <airlied@redhat.com>2013-05-30 23:02:52 -0400
commitd0d98eedee2178c803dd824bb09f52b0e2ac1811 (patch)
tree302ece15c574dc061b1dea4e67125c7b01342154
parente81f3d81e282a156b47c1c2c09a1976e34073060 (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>
-rw-r--r--arch/x86/include/asm/io.h7
-rw-r--r--arch/x86/include/asm/mtrr.h10
-rw-r--r--arch/x86/kernel/cpu/mtrr/main.c71
-rw-r--r--include/linux/io.h25
4 files changed, 112 insertions, 1 deletions
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index d8e8eefbe24c..34f69cb9350a 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -345,4 +345,11 @@ extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
345 345
346#define IO_SPACE_LIMIT 0xffff 346#define IO_SPACE_LIMIT 0xffff
347 347
348#ifdef CONFIG_MTRR
349extern int __must_check arch_phys_wc_add(unsigned long base,
350 unsigned long size);
351extern void arch_phys_wc_del(int handle);
352#define arch_phys_wc_add arch_phys_wc_add
353#endif
354
348#endif /* _ASM_X86_IO_H */ 355#endif /* _ASM_X86_IO_H */
diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h
index e235582f9930..f768f6298419 100644
--- a/arch/x86/include/asm/mtrr.h
+++ b/arch/x86/include/asm/mtrr.h
@@ -26,7 +26,10 @@
26#include <uapi/asm/mtrr.h> 26#include <uapi/asm/mtrr.h>
27 27
28 28
29/* The following functions are for use by other drivers */ 29/*
30 * The following functions are for use by other drivers that cannot use
31 * arch_phys_wc_add and arch_phys_wc_del.
32 */
30# ifdef CONFIG_MTRR 33# ifdef CONFIG_MTRR
31extern u8 mtrr_type_lookup(u64 addr, u64 end); 34extern u8 mtrr_type_lookup(u64 addr, u64 end);
32extern void mtrr_save_fixed_ranges(void *); 35extern void mtrr_save_fixed_ranges(void *);
@@ -45,6 +48,7 @@ extern void mtrr_aps_init(void);
45extern void mtrr_bp_restore(void); 48extern void mtrr_bp_restore(void);
46extern int mtrr_trim_uncached_memory(unsigned long end_pfn); 49extern int mtrr_trim_uncached_memory(unsigned long end_pfn);
47extern int amd_special_default_mtrr(void); 50extern int amd_special_default_mtrr(void);
51extern int phys_wc_to_mtrr_index(int handle);
48# else 52# else
49static inline u8 mtrr_type_lookup(u64 addr, u64 end) 53static inline u8 mtrr_type_lookup(u64 addr, u64 end)
50{ 54{
@@ -80,6 +84,10 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
80static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) 84static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
81{ 85{
82} 86}
87static inline int phys_wc_to_mtrr_index(int handle)
88{
89 return -1;
90}
83 91
84#define mtrr_ap_init() do {} while (0) 92#define mtrr_ap_init() do {} while (0)
85#define mtrr_bp_init() do {} while (0) 93#define mtrr_bp_init() do {} while (0)
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
57u32 num_var_ranges; 61u32 num_var_ranges;
58 62
59unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; 63unsigned 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}
525EXPORT_SYMBOL(mtrr_del); 529EXPORT_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 */
543int 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}
558EXPORT_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 */
569void 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}
576EXPORT_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 */
589int 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}
596EXPORT_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
diff --git a/include/linux/io.h b/include/linux/io.h
index 069e4075f872..f4f42faec686 100644
--- a/include/linux/io.h
+++ b/include/linux/io.h
@@ -76,4 +76,29 @@ void devm_ioremap_release(struct device *dev, void *res);
76#define arch_has_dev_port() (1) 76#define arch_has_dev_port() (1)
77#endif 77#endif
78 78
79/*
80 * Some systems (x86 without PAT) have a somewhat reliable way to mark a
81 * physical address range such that uncached mappings will actually
82 * end up write-combining. This facility should be used in conjunction
83 * with pgprot_writecombine, ioremap-wc, or set_memory_wc, since it has
84 * no effect if the per-page mechanisms are functional.
85 * (On x86 without PAT, these functions manipulate MTRRs.)
86 *
87 * arch_phys_del_wc(0) or arch_phys_del_wc(any error code) is guaranteed
88 * to have no effect.
89 */
90#ifndef arch_phys_wc_add
91static inline int __must_check arch_phys_wc_add(unsigned long base,
92 unsigned long size)
93{
94 return 0; /* It worked (i.e. did nothing). */
95}
96
97static inline void arch_phys_wc_del(int handle)
98{
99}
100
101#define arch_phys_wc_add arch_phys_wc_add
102#endif
103
79#endif /* _LINUX_IO_H */ 104#endif /* _LINUX_IO_H */