blob: 2642390043a5f22afebf4325024ae9c43f8b0090 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13/* Architecture-specific VCM functions */
14
15#include <linux/kernel.h>
16#include <linux/vcm_mm.h>
17
18#include <asm/pgtable-hwdef.h>
19#include <asm/tlbflush.h>
20
21#define MRC(reg, processor, op1, crn, crm, op2) \
22__asm__ __volatile__ ( \
23" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 " \n" \
24: "=r" (reg))
25
26#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
27#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
28
29
30/* Local type attributes (not the same as VCM) */
31#define ARM_MT_NORMAL 2
32#define ARM_MT_STRONGLYORDERED 0
33#define ARM_MT_DEVICE 1
34
35#define ARM_CP_NONCACHED 0
36#define ARM_CP_WB_WA 1
37#define ARM_CP_WB_NWA 3
38#define ARM_CP_WT_NWA 2
39
40#define smmu_err(a, ...) \
41 pr_err("ERROR %s %i " a, __func__, __LINE__, ##__VA_ARGS__)
42
43#define FL_OFFSET(va) (((va) & 0xFFF00000) >> 20)
44#define SL_OFFSET(va) (((va) & 0xFF000) >> 12)
45
46int vcm_driver_tex_class[4];
47
48static int find_tex_class(int icp, int ocp, int mt, int nos)
49{
50 int i = 0;
51 unsigned int prrr = 0;
52 unsigned int nmrr = 0;
53 int c_icp, c_ocp, c_mt, c_nos;
54
55 RCP15_PRRR(prrr);
56 RCP15_NMRR(nmrr);
57
58 /* There are only 8 classes on this architecture */
59 /* If they add more classes, registers will VASTLY change */
60 for (i = 0; i < 8; i++) {
61 c_nos = prrr & (1 << (i + 24)) ? 1 : 0;
62 c_mt = (prrr & (3 << (i * 2))) >> (i * 2);
63 c_icp = (nmrr & (3 << (i * 2))) >> (i * 2);
64 c_ocp = (nmrr & (3 << (i * 2 + 16))) >> (i * 2 + 16);
65
66 if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
67 return i;
68 }
69 smmu_err("Could not find TEX class for ICP=%d, OCP=%d, MT=%d, NOS=%d\n",
70 icp, ocp, mt, nos);
71
72 /* In reality, we may want to remove this panic. Some classes just */
73 /* will not be available, and will fail in smmu_set_attr */
74 panic("SMMU: Could not determine TEX attribute mapping.\n");
75 return -1;
76}
77
78
79int vcm_setup_tex_classes(void)
80{
81 unsigned int cpu_prrr;
82 unsigned int cpu_nmrr;
83
84 if (!(get_cr() & CR_TRE)) /* No TRE? */
85 panic("TEX remap not enabled, but the SMMU driver needs it!\n");
86
87 RCP15_PRRR(cpu_prrr);
88 RCP15_NMRR(cpu_nmrr);
89
90 vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED] =
91 find_tex_class(ARM_CP_NONCACHED, ARM_CP_NONCACHED,
92 ARM_MT_NORMAL, 1);
93
94 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA] =
95 find_tex_class(ARM_CP_WB_WA, ARM_CP_WB_WA,
96 ARM_MT_NORMAL, 1);
97
98 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA] =
99 find_tex_class(ARM_CP_WB_NWA, ARM_CP_WB_NWA,
100 ARM_MT_NORMAL, 1);
101
102 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT] =
103 find_tex_class(ARM_CP_WT_NWA, ARM_CP_WT_NWA,
104 ARM_MT_NORMAL, 1);
105#ifdef DEBUG_TEX
106 printk(KERN_INFO "VCM driver debug: Using TEX classes: %d %d %d %d\n",
107 vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED],
108 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA],
109 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA],
110 vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT]);
111#endif
112 return 0;
113}
114
115
116int set_arm7_pte_attr(unsigned long pt_base, unsigned long va,
117 unsigned long len, unsigned int attr)
118{
119 unsigned long *fl_table = NULL;
120 unsigned long *fl_pte = NULL;
121 unsigned long fl_offset = 0;
122 unsigned long *sl_table = NULL;
123 unsigned long *sl_pte = NULL;
124 unsigned long sl_offset = 0;
125 int i;
126 int sh = 0;
127 int class = 0;
128
129 /* Alignment */
130 if (va & (len-1)) {
131 smmu_err("misaligned va: %p\n", (void *) va);
132 goto fail;
133 }
134 if (attr > 7) {
135 smmu_err("bad attribute: %d\n", attr);
136 goto fail;
137 }
138
139 sh = (attr & VCM_DEV_ATTR_SH) ? 1 : 0;
140 class = vcm_driver_tex_class[attr & 0x03];
141
142 if (class > 7 || class < 0) { /* Bad class */
143 smmu_err("bad tex class: %d\n", class);
144 goto fail;
145 }
146
147 if (len != SZ_16M && len != SZ_1M &&
148 len != SZ_64K && len != SZ_4K) {
149 smmu_err("bad size: %lu\n", len);
150 goto fail;
151 }
152
153 fl_table = (unsigned long *) pt_base;
154
155 if (!fl_table) {
156 smmu_err("null page table\n");
157 goto fail;
158 }
159
160 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
161 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
162
163 if (*fl_pte == 0) { /* Nothing there! */
164 smmu_err("first level pte is 0\n");
165 goto fail;
166 }
167
168 /* Supersection attributes */
169 if (len == SZ_16M) {
170 for (i = 0; i < 16; i++) {
171 /* Clear the old bits */
172 *(fl_pte+i) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE |
173 PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1));
174
175 /* Assign new class and S bit */
176 *(fl_pte+i) |= sh ? PMD_SECT_S : 0;
177 *(fl_pte+i) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0;
178 *(fl_pte+i) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0;
179 *(fl_pte+i) |= class & 0x04 ? PMD_SECT_TEX(1) : 0;
180 }
181 } else if (len == SZ_1M) {
182
183 /* Clear the old bits */
184 *(fl_pte) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE |
185 PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1));
186
187 /* Assign new class and S bit */
188 *(fl_pte) |= sh ? PMD_SECT_S : 0;
189 *(fl_pte) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0;
190 *(fl_pte) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0;
191 *(fl_pte) |= class & 0x04 ? PMD_SECT_TEX(1) : 0;
192 }
193
194 sl_table = (unsigned long *) __va(((*fl_pte) & 0xFFFFFC00));
195 sl_offset = SL_OFFSET(va);
196 sl_pte = sl_table + sl_offset;
197
198 if (len == SZ_64K) {
199 for (i = 0; i < 16; i++) {
200 /* Clear the old bits */
201 *(sl_pte+i) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE |
202 PTE_BUFFERABLE | PTE_EXT_TEX(1));
203
204 /* Assign new class and S bit */
205 *(sl_pte+i) |= sh ? PTE_EXT_SHARED : 0;
206 *(sl_pte+i) |= class & 0x01 ? PTE_BUFFERABLE : 0;
207 *(sl_pte+i) |= class & 0x02 ? PTE_CACHEABLE : 0;
208 *(sl_pte+i) |= class & 0x04 ? PTE_EXT_TEX(1) : 0;
209 }
210 } else if (len == SZ_4K) {
211 /* Clear the old bits */
212 *(sl_pte) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE |
213 PTE_BUFFERABLE | PTE_EXT_TEX(1));
214
215 /* Assign new class and S bit */
216 *(sl_pte) |= sh ? PTE_EXT_SHARED : 0;
217 *(sl_pte) |= class & 0x01 ? PTE_BUFFERABLE : 0;
218 *(sl_pte) |= class & 0x02 ? PTE_CACHEABLE : 0;
219 *(sl_pte) |= class & 0x04 ? PTE_EXT_TEX(1) : 0;
220 }
221
222
223 mb();
224 return 0;
225fail:
226 return 1;
227}
228
229
230int cpu_set_attr(unsigned long va, unsigned long len, unsigned int attr)
231{
232 int ret;
233 pgd_t *pgd = init_mm.pgd;
234
235 if (!pgd) {
236 smmu_err("null pgd\n");
237 goto fail;
238 }
239
240 ret = set_arm7_pte_attr((unsigned long)pgd, va, len, attr);
241
242 if (ret != 0) {
243 smmu_err("could not set attribute: \
244 pgd=%p, va=%p, len=%lu, attr=%d\n",
245 (void *) pgd, (void *) va, len, attr);
246 goto fail;
247 }
248 dmb();
249 flush_tlb_all();
250 return 0;
251fail:
252 return -1;
253}