Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | #include <linux/init.h> |
2 | #include <linux/mm.h> | |
3 | #include <asm/mtrr.h> | |
4 | #include <asm/msr.h> | |
5 | ||
6 | #include "mtrr.h" | |
7 | ||
8 | static void | |
9 | amd_get_mtrr(unsigned int reg, unsigned long *base, | |
42204455 | 10 | unsigned long *size, mtrr_type *type) |
1da177e4 LT |
11 | { |
12 | unsigned long low, high; | |
13 | ||
14 | rdmsr(MSR_K6_UWCCR, low, high); | |
42204455 | 15 | /* Upper dword is region 1, lower is region 0 */ |
1da177e4 LT |
16 | if (reg == 1) |
17 | low = high; | |
42204455 | 18 | /* The base masks off on the right alignment */ |
1da177e4 LT |
19 | *base = (low & 0xFFFE0000) >> PAGE_SHIFT; |
20 | *type = 0; | |
21 | if (low & 1) | |
22 | *type = MTRR_TYPE_UNCACHABLE; | |
23 | if (low & 2) | |
24 | *type = MTRR_TYPE_WRCOMB; | |
25 | if (!(low & 3)) { | |
26 | *size = 0; | |
27 | return; | |
28 | } | |
29 | /* | |
42204455 JSR |
30 | * This needs a little explaining. The size is stored as an |
31 | * inverted mask of bits of 128K granularity 15 bits long offset | |
32 | * 2 bits. | |
1da177e4 | 33 | * |
42204455 JSR |
34 | * So to get a size we do invert the mask and add 1 to the lowest |
35 | * mask bit (4 as its 2 bits in). This gives us a size we then shift | |
36 | * to turn into 128K blocks. | |
1da177e4 | 37 | * |
42204455 | 38 | * eg 111 1111 1111 1100 is 512K |
1da177e4 | 39 | * |
42204455 JSR |
40 | * invert 000 0000 0000 0011 |
41 | * +1 000 0000 0000 0100 | |
42 | * *128K ... | |
1da177e4 LT |
43 | */ |
44 | low = (~low) & 0x1FFFC; | |
45 | *size = (low + 4) << (15 - PAGE_SHIFT); | |
1da177e4 LT |
46 | } |
47 | ||
42204455 JSR |
48 | /** |
49 | * amd_set_mtrr - Set variable MTRR register on the local CPU. | |
50 | * | |
51 | * @reg The register to set. | |
52 | * @base The base address of the region. | |
53 | * @size The size of the region. If this is 0 the region is disabled. | |
54 | * @type The type of the region. | |
55 | * | |
56 | * Returns nothing. | |
57 | */ | |
58 | static void | |
59 | amd_set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type) | |
1da177e4 LT |
60 | { |
61 | u32 regs[2]; | |
62 | ||
63 | /* | |
42204455 | 64 | * Low is MTRR0, High MTRR 1 |
1da177e4 LT |
65 | */ |
66 | rdmsr(MSR_K6_UWCCR, regs[0], regs[1]); | |
67 | /* | |
42204455 | 68 | * Blank to disable |
1da177e4 | 69 | */ |
42204455 | 70 | if (size == 0) { |
1da177e4 | 71 | regs[reg] = 0; |
42204455 JSR |
72 | } else { |
73 | /* | |
74 | * Set the register to the base, the type (off by one) and an | |
75 | * inverted bitmask of the size The size is the only odd | |
76 | * bit. We are fed say 512K We invert this and we get 111 1111 | |
77 | * 1111 1011 but if you subtract one and invert you get the | |
78 | * desired 111 1111 1111 1100 mask | |
79 | * | |
80 | * But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! | |
81 | */ | |
1da177e4 LT |
82 | regs[reg] = (-size >> (15 - PAGE_SHIFT) & 0x0001FFFC) |
83 | | (base << PAGE_SHIFT) | (type + 1); | |
42204455 | 84 | } |
1da177e4 LT |
85 | |
86 | /* | |
42204455 JSR |
87 | * The writeback rule is quite specific. See the manual. Its |
88 | * disable local interrupts, write back the cache, set the mtrr | |
1da177e4 LT |
89 | */ |
90 | wbinvd(); | |
91 | wrmsr(MSR_K6_UWCCR, regs[0], regs[1]); | |
92 | } | |
93 | ||
42204455 JSR |
94 | static int |
95 | amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type) | |
1da177e4 | 96 | { |
42204455 JSR |
97 | /* |
98 | * Apply the K6 block alignment and size rules | |
99 | * In order | |
100 | * o Uncached or gathering only | |
101 | * o 128K or bigger block | |
102 | * o Power of 2 block | |
103 | * o base suitably aligned to the power | |
104 | */ | |
1da177e4 LT |
105 | if (type > MTRR_TYPE_WRCOMB || size < (1 << (17 - PAGE_SHIFT)) |
106 | || (size & ~(size - 1)) - size || (base & (size - 1))) | |
107 | return -EINVAL; | |
108 | return 0; | |
109 | } | |
110 | ||
111 | static struct mtrr_ops amd_mtrr_ops = { | |
112 | .vendor = X86_VENDOR_AMD, | |
113 | .set = amd_set_mtrr, | |
114 | .get = amd_get_mtrr, | |
115 | .get_free_region = generic_get_free_region, | |
116 | .validate_add_page = amd_validate_add_page, | |
117 | .have_wrcomb = positive_have_wrcomb, | |
118 | }; | |
119 | ||
120 | int __init amd_init_mtrr(void) | |
121 | { | |
122 | set_mtrr_ops(&amd_mtrr_ops); | |
123 | return 0; | |
124 | } |