diff options
author | Felipe Contreras <felipe.contreras@gmail.com> | 2010-11-10 13:10:20 -0500 |
---|---|---|
committer | Omar Ramirez Luna <omar.ramirez@ti.com> | 2010-11-10 19:34:43 -0500 |
commit | f5bd96bbe320b9fe1c8132a1633e8582cd9d6245 (patch) | |
tree | 806f49035a0803211aa4c2daa910d2f412ff35c8 /drivers/staging/tidspbridge/core | |
parent | 9d4f81a722863c42472541cb71981d09613775b3 (diff) |
Revert "staging: tidspbridge - move all iommu related code to a new file"
This reverts commit f94378f9f9a897fc08e9d12733401ae52466e408.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Omar Ramirez Luna <omar.ramirez@ti.com>
Diffstat (limited to 'drivers/staging/tidspbridge/core')
-rw-r--r-- | drivers/staging/tidspbridge/core/_deh.h | 3 | ||||
-rw-r--r-- | drivers/staging/tidspbridge/core/_tiomap.h | 27 | ||||
-rw-r--r-- | drivers/staging/tidspbridge/core/dsp-mmu.c | 317 | ||||
-rw-r--r-- | drivers/staging/tidspbridge/core/tiomap3430.c | 178 | ||||
-rw-r--r-- | drivers/staging/tidspbridge/core/ue_deh.c | 86 |
5 files changed, 288 insertions, 323 deletions
diff --git a/drivers/staging/tidspbridge/core/_deh.h b/drivers/staging/tidspbridge/core/_deh.h index 8ae263387a8..f1254f0aa03 100644 --- a/drivers/staging/tidspbridge/core/_deh.h +++ b/drivers/staging/tidspbridge/core/_deh.h | |||
@@ -27,6 +27,9 @@ | |||
27 | struct deh_mgr { | 27 | struct deh_mgr { |
28 | struct bridge_dev_context *hbridge_context; /* Bridge context. */ | 28 | struct bridge_dev_context *hbridge_context; /* Bridge context. */ |
29 | struct ntfy_object *ntfy_obj; /* NTFY object */ | 29 | struct ntfy_object *ntfy_obj; /* NTFY object */ |
30 | |||
31 | /* MMU Fault DPC */ | ||
32 | struct tasklet_struct dpc_tasklet; | ||
30 | }; | 33 | }; |
31 | 34 | ||
32 | int mmu_fault_isr(struct iommu *mmu); | 35 | int mmu_fault_isr(struct iommu *mmu); |
diff --git a/drivers/staging/tidspbridge/core/_tiomap.h b/drivers/staging/tidspbridge/core/_tiomap.h index e0a801c1cb9..cd7ff8810a0 100644 --- a/drivers/staging/tidspbridge/core/_tiomap.h +++ b/drivers/staging/tidspbridge/core/_tiomap.h | |||
@@ -23,7 +23,8 @@ | |||
23 | #include <plat/clockdomain.h> | 23 | #include <plat/clockdomain.h> |
24 | #include <mach-omap2/prm-regbits-34xx.h> | 24 | #include <mach-omap2/prm-regbits-34xx.h> |
25 | #include <mach-omap2/cm-regbits-34xx.h> | 25 | #include <mach-omap2/cm-regbits-34xx.h> |
26 | #include <dspbridge/dsp-mmu.h> | 26 | #include <plat/iommu.h> |
27 | #include <plat/iovmm.h> | ||
27 | #include <dspbridge/devdefs.h> | 28 | #include <dspbridge/devdefs.h> |
28 | #include <dspbridge/dspioctl.h> /* for bridge_ioctl_extproc defn */ | 29 | #include <dspbridge/dspioctl.h> /* for bridge_ioctl_extproc defn */ |
29 | #include <dspbridge/sync.h> | 30 | #include <dspbridge/sync.h> |
@@ -379,4 +380,28 @@ extern s32 dsp_debug; | |||
379 | */ | 380 | */ |
380 | int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val); | 381 | int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val); |
381 | 382 | ||
383 | /** | ||
384 | * user_to_dsp_map() - maps user to dsp virtual address | ||
385 | * @mmu: Pointer to iommu handle. | ||
386 | * @uva: Virtual user space address. | ||
387 | * @da DSP address | ||
388 | * @size Buffer size to map. | ||
389 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
390 | * | ||
391 | * This function maps a user space buffer into DSP virtual address. | ||
392 | * | ||
393 | */ | ||
394 | u32 user_to_dsp_map(struct iommu *mmu, u32 uva, u32 da, u32 size, | ||
395 | struct page **usr_pgs); | ||
396 | |||
397 | /** | ||
398 | * user_to_dsp_unmap() - unmaps DSP virtual buffer. | ||
399 | * @mmu: Pointer to iommu handle. | ||
400 | * @da DSP address | ||
401 | * | ||
402 | * This function unmaps a user space buffer into DSP virtual address. | ||
403 | * | ||
404 | */ | ||
405 | int user_to_dsp_unmap(struct iommu *mmu, u32 da); | ||
406 | |||
382 | #endif /* _TIOMAP_ */ | 407 | #endif /* _TIOMAP_ */ |
diff --git a/drivers/staging/tidspbridge/core/dsp-mmu.c b/drivers/staging/tidspbridge/core/dsp-mmu.c deleted file mode 100644 index 983c95adc8f..00000000000 --- a/drivers/staging/tidspbridge/core/dsp-mmu.c +++ /dev/null | |||
@@ -1,317 +0,0 @@ | |||
1 | /* | ||
2 | * dsp-mmu.c | ||
3 | * | ||
4 | * DSP-BIOS Bridge driver support functions for TI OMAP processors. | ||
5 | * | ||
6 | * DSP iommu. | ||
7 | * | ||
8 | * Copyright (C) 2010 Texas Instruments, Inc. | ||
9 | * | ||
10 | * This package is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | ||
15 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | ||
16 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
17 | */ | ||
18 | |||
19 | #include <dspbridge/host_os.h> | ||
20 | #include <plat/dmtimer.h> | ||
21 | #include <dspbridge/dbdefs.h> | ||
22 | #include <dspbridge/dev.h> | ||
23 | #include <dspbridge/io_sm.h> | ||
24 | #include <dspbridge/dspdeh.h> | ||
25 | #include "_tiomap.h" | ||
26 | |||
27 | #include <dspbridge/dsp-mmu.h> | ||
28 | |||
29 | #define MMU_CNTL_TWL_EN (1 << 2) | ||
30 | |||
31 | static struct tasklet_struct mmu_tasklet; | ||
32 | |||
33 | #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE | ||
34 | static void mmu_fault_print_stack(struct bridge_dev_context *dev_context) | ||
35 | { | ||
36 | void *dummy_addr; | ||
37 | u32 fa, tmp; | ||
38 | struct iotlb_entry e; | ||
39 | struct iommu *mmu = dev_context->dsp_mmu; | ||
40 | dummy_addr = (void *)__get_free_page(GFP_ATOMIC); | ||
41 | |||
42 | /* | ||
43 | * Before acking the MMU fault, let's make sure MMU can only | ||
44 | * access entry #0. Then add a new entry so that the DSP OS | ||
45 | * can continue in order to dump the stack. | ||
46 | */ | ||
47 | tmp = iommu_read_reg(mmu, MMU_CNTL); | ||
48 | tmp &= ~MMU_CNTL_TWL_EN; | ||
49 | iommu_write_reg(mmu, tmp, MMU_CNTL); | ||
50 | fa = iommu_read_reg(mmu, MMU_FAULT_AD); | ||
51 | e.da = fa & PAGE_MASK; | ||
52 | e.pa = virt_to_phys(dummy_addr); | ||
53 | e.valid = 1; | ||
54 | e.prsvd = 1; | ||
55 | e.pgsz = IOVMF_PGSZ_4K & MMU_CAM_PGSZ_MASK; | ||
56 | e.endian = MMU_RAM_ENDIAN_LITTLE; | ||
57 | e.elsz = MMU_RAM_ELSZ_32; | ||
58 | e.mixed = 0; | ||
59 | |||
60 | load_iotlb_entry(mmu, &e); | ||
61 | |||
62 | dsp_clk_enable(DSP_CLK_GPT8); | ||
63 | |||
64 | dsp_gpt_wait_overflow(DSP_CLK_GPT8, 0xfffffffe); | ||
65 | |||
66 | /* Clear MMU interrupt */ | ||
67 | tmp = iommu_read_reg(mmu, MMU_IRQSTATUS); | ||
68 | iommu_write_reg(mmu, tmp, MMU_IRQSTATUS); | ||
69 | |||
70 | dump_dsp_stack(dev_context); | ||
71 | dsp_clk_disable(DSP_CLK_GPT8); | ||
72 | |||
73 | iopgtable_clear_entry(mmu, fa); | ||
74 | free_page((unsigned long)dummy_addr); | ||
75 | } | ||
76 | #endif | ||
77 | |||
78 | |||
79 | static void fault_tasklet(unsigned long data) | ||
80 | { | ||
81 | struct iommu *mmu = (struct iommu *)data; | ||
82 | struct bridge_dev_context *dev_ctx; | ||
83 | struct deh_mgr *dm; | ||
84 | u32 fa; | ||
85 | dev_get_deh_mgr(dev_get_first(), &dm); | ||
86 | dev_get_bridge_context(dev_get_first(), &dev_ctx); | ||
87 | |||
88 | if (!dm || !dev_ctx) | ||
89 | return; | ||
90 | |||
91 | fa = iommu_read_reg(mmu, MMU_FAULT_AD); | ||
92 | |||
93 | #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE | ||
94 | print_dsp_trace_buffer(dev_ctx); | ||
95 | dump_dl_modules(dev_ctx); | ||
96 | mmu_fault_print_stack(dev_ctx); | ||
97 | #endif | ||
98 | |||
99 | bridge_deh_notify(dm, DSP_MMUFAULT, fa); | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * ======== mmu_fault_isr ======== | ||
104 | * ISR to be triggered by a DSP MMU fault interrupt. | ||
105 | */ | ||
106 | static int mmu_fault_callback(struct iommu *mmu) | ||
107 | { | ||
108 | if (!mmu) | ||
109 | return -EPERM; | ||
110 | |||
111 | iommu_write_reg(mmu, 0, MMU_IRQENABLE); | ||
112 | tasklet_schedule(&mmu_tasklet); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * dsp_mmu_init() - initialize dsp_mmu module and returns a handle | ||
118 | * | ||
119 | * This function initialize dsp mmu module and returns a struct iommu | ||
120 | * handle to use it for dsp maps. | ||
121 | * | ||
122 | */ | ||
123 | struct iommu *dsp_mmu_init() | ||
124 | { | ||
125 | struct iommu *mmu; | ||
126 | |||
127 | mmu = iommu_get("iva2"); | ||
128 | |||
129 | if (!IS_ERR(mmu)) { | ||
130 | tasklet_init(&mmu_tasklet, fault_tasklet, (unsigned long)mmu); | ||
131 | mmu->isr = mmu_fault_callback; | ||
132 | } | ||
133 | |||
134 | return mmu; | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * dsp_mmu_exit() - destroy dsp mmu module | ||
139 | * @mmu: Pointer to iommu handle. | ||
140 | * | ||
141 | * This function destroys dsp mmu module. | ||
142 | * | ||
143 | */ | ||
144 | void dsp_mmu_exit(struct iommu *mmu) | ||
145 | { | ||
146 | if (mmu) | ||
147 | iommu_put(mmu); | ||
148 | tasklet_kill(&mmu_tasklet); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * user_va2_pa() - get physical address from userspace address. | ||
153 | * @mm: mm_struct Pointer of the process. | ||
154 | * @address: Virtual user space address. | ||
155 | * | ||
156 | */ | ||
157 | static u32 user_va2_pa(struct mm_struct *mm, u32 address) | ||
158 | { | ||
159 | pgd_t *pgd; | ||
160 | pmd_t *pmd; | ||
161 | pte_t *ptep, pte; | ||
162 | |||
163 | pgd = pgd_offset(mm, address); | ||
164 | if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { | ||
165 | pmd = pmd_offset(pgd, address); | ||
166 | if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { | ||
167 | ptep = pte_offset_map(pmd, address); | ||
168 | if (ptep) { | ||
169 | pte = *ptep; | ||
170 | if (pte_present(pte)) | ||
171 | return pte & PAGE_MASK; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * get_io_pages() - pin and get pages of io user's buffer. | ||
181 | * @mm: mm_struct Pointer of the process. | ||
182 | * @uva: Virtual user space address. | ||
183 | * @pages Pages to be pined. | ||
184 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
185 | * | ||
186 | */ | ||
187 | static int get_io_pages(struct mm_struct *mm, u32 uva, unsigned pages, | ||
188 | struct page **usr_pgs) | ||
189 | { | ||
190 | u32 pa; | ||
191 | int i; | ||
192 | struct page *pg; | ||
193 | |||
194 | for (i = 0; i < pages; i++) { | ||
195 | pa = user_va2_pa(mm, uva); | ||
196 | |||
197 | if (!pfn_valid(__phys_to_pfn(pa))) | ||
198 | break; | ||
199 | |||
200 | pg = phys_to_page(pa); | ||
201 | usr_pgs[i] = pg; | ||
202 | get_page(pg); | ||
203 | } | ||
204 | return i; | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * user_to_dsp_map() - maps user to dsp virtual address | ||
209 | * @mmu: Pointer to iommu handle. | ||
210 | * @uva: Virtual user space address. | ||
211 | * @da DSP address | ||
212 | * @size Buffer size to map. | ||
213 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
214 | * | ||
215 | * This function maps a user space buffer into DSP virtual address. | ||
216 | * | ||
217 | */ | ||
218 | u32 user_to_dsp_map(struct iommu *mmu, u32 uva, u32 da, u32 size, | ||
219 | struct page **usr_pgs) | ||
220 | { | ||
221 | int res, w; | ||
222 | unsigned pages; | ||
223 | int i; | ||
224 | struct vm_area_struct *vma; | ||
225 | struct mm_struct *mm = current->mm; | ||
226 | struct sg_table *sgt; | ||
227 | struct scatterlist *sg; | ||
228 | |||
229 | if (!size || !usr_pgs) | ||
230 | return -EINVAL; | ||
231 | |||
232 | pages = size / PG_SIZE4K; | ||
233 | |||
234 | down_read(&mm->mmap_sem); | ||
235 | vma = find_vma(mm, uva); | ||
236 | while (vma && (uva + size > vma->vm_end)) | ||
237 | vma = find_vma(mm, vma->vm_end + 1); | ||
238 | |||
239 | if (!vma) { | ||
240 | pr_err("%s: Failed to get VMA region for 0x%x (%d)\n", | ||
241 | __func__, uva, size); | ||
242 | up_read(&mm->mmap_sem); | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) | ||
246 | w = 1; | ||
247 | |||
248 | if (vma->vm_flags & VM_IO) | ||
249 | i = get_io_pages(mm, uva, pages, usr_pgs); | ||
250 | else | ||
251 | i = get_user_pages(current, mm, uva, pages, w, 1, | ||
252 | usr_pgs, NULL); | ||
253 | up_read(&mm->mmap_sem); | ||
254 | |||
255 | if (i < 0) | ||
256 | return i; | ||
257 | |||
258 | if (i < pages) { | ||
259 | res = -EFAULT; | ||
260 | goto err_pages; | ||
261 | } | ||
262 | |||
263 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | ||
264 | if (!sgt) { | ||
265 | res = -ENOMEM; | ||
266 | goto err_pages; | ||
267 | } | ||
268 | |||
269 | res = sg_alloc_table(sgt, pages, GFP_KERNEL); | ||
270 | |||
271 | if (res < 0) | ||
272 | goto err_sg; | ||
273 | |||
274 | for_each_sg(sgt->sgl, sg, sgt->nents, i) | ||
275 | sg_set_page(sg, usr_pgs[i], PAGE_SIZE, 0); | ||
276 | |||
277 | da = iommu_vmap(mmu, da, sgt, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); | ||
278 | |||
279 | if (!IS_ERR_VALUE(da)) | ||
280 | return da; | ||
281 | res = (int)da; | ||
282 | |||
283 | sg_free_table(sgt); | ||
284 | err_sg: | ||
285 | kfree(sgt); | ||
286 | i = pages; | ||
287 | err_pages: | ||
288 | while (i--) | ||
289 | put_page(usr_pgs[i]); | ||
290 | return res; | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * user_to_dsp_unmap() - unmaps DSP virtual buffer. | ||
295 | * @mmu: Pointer to iommu handle. | ||
296 | * @da DSP address | ||
297 | * | ||
298 | * This function unmaps a user space buffer into DSP virtual address. | ||
299 | * | ||
300 | */ | ||
301 | int user_to_dsp_unmap(struct iommu *mmu, u32 da) | ||
302 | { | ||
303 | unsigned i; | ||
304 | struct sg_table *sgt; | ||
305 | struct scatterlist *sg; | ||
306 | |||
307 | sgt = iommu_vunmap(mmu, da); | ||
308 | if (!sgt) | ||
309 | return -EFAULT; | ||
310 | |||
311 | for_each_sg(sgt->sgl, sg, sgt->nents, i) | ||
312 | put_page(sg_page(sg)); | ||
313 | sg_free_table(sgt); | ||
314 | kfree(sgt); | ||
315 | |||
316 | return 0; | ||
317 | } | ||
diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c index 53b38b2b9ce..984a35a068e 100644 --- a/drivers/staging/tidspbridge/core/tiomap3430.c +++ b/drivers/staging/tidspbridge/core/tiomap3430.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include "_tiomap.h" | 53 | #include "_tiomap.h" |
54 | #include "_tiomap_pwr.h" | 54 | #include "_tiomap_pwr.h" |
55 | #include "tiomap_io.h" | 55 | #include "tiomap_io.h" |
56 | #include "_deh.h" | ||
56 | 57 | ||
57 | /* Offset in shared mem to write to in order to synchronize start with DSP */ | 58 | /* Offset in shared mem to write to in order to synchronize start with DSP */ |
58 | #define SHMSYNCOFFSET 4 /* GPP byte offset */ | 59 | #define SHMSYNCOFFSET 4 /* GPP byte offset */ |
@@ -67,6 +68,7 @@ | |||
67 | #define MMU_SMALL_PAGE_MASK 0xFFFFF000 | 68 | #define MMU_SMALL_PAGE_MASK 0xFFFFF000 |
68 | #define OMAP3_IVA2_BOOTADDR_MASK 0xFFFFFC00 | 69 | #define OMAP3_IVA2_BOOTADDR_MASK 0xFFFFFC00 |
69 | #define PAGES_II_LVL_TABLE 512 | 70 | #define PAGES_II_LVL_TABLE 512 |
71 | #define PHYS_TO_PAGE(phys) pfn_to_page((phys) >> PAGE_SHIFT) | ||
70 | 72 | ||
71 | /* | 73 | /* |
72 | * This is a totally ugly layer violation, but needed until | 74 | * This is a totally ugly layer violation, but needed until |
@@ -364,16 +366,17 @@ static int bridge_brd_start(struct bridge_dev_context *dev_ctxt, | |||
364 | OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); | 366 | OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); |
365 | mmu = dev_context->dsp_mmu; | 367 | mmu = dev_context->dsp_mmu; |
366 | if (mmu) | 368 | if (mmu) |
367 | dsp_mmu_exit(mmu); | 369 | iommu_put(mmu); |
368 | mmu = dsp_mmu_init(); | 370 | mmu = iommu_get("iva2"); |
369 | if (IS_ERR(mmu)) { | 371 | if (IS_ERR(mmu)) { |
370 | dev_err(bridge, "dsp_mmu_init failed!\n"); | 372 | dev_err(bridge, "iommu_get failed!\n"); |
371 | dev_context->dsp_mmu = NULL; | 373 | dev_context->dsp_mmu = NULL; |
372 | status = (int)mmu; | 374 | status = (int)mmu; |
373 | } | 375 | } |
374 | } | 376 | } |
375 | if (!status) { | 377 | if (!status) { |
376 | dev_context->dsp_mmu = mmu; | 378 | dev_context->dsp_mmu = mmu; |
379 | mmu->isr = mmu_fault_isr; | ||
377 | sm_sg = &dev_context->sh_s; | 380 | sm_sg = &dev_context->sh_s; |
378 | sg0_da = iommu_kmap(mmu, sm_sg->seg0_da, sm_sg->seg0_pa, | 381 | sg0_da = iommu_kmap(mmu, sm_sg->seg0_da, sm_sg->seg0_pa, |
379 | sm_sg->seg0_size, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); | 382 | sm_sg->seg0_size, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); |
@@ -629,7 +632,7 @@ static int bridge_brd_stop(struct bridge_dev_context *dev_ctxt) | |||
629 | } | 632 | } |
630 | iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg0_da); | 633 | iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg0_da); |
631 | iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg1_da); | 634 | iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg1_da); |
632 | dsp_mmu_exit(dev_context->dsp_mmu); | 635 | iommu_put(dev_context->dsp_mmu); |
633 | dev_context->dsp_mmu = NULL; | 636 | dev_context->dsp_mmu = NULL; |
634 | } | 637 | } |
635 | /* Reset IVA IOMMU*/ | 638 | /* Reset IVA IOMMU*/ |
@@ -944,6 +947,173 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt, | |||
944 | } | 947 | } |
945 | 948 | ||
946 | /* | 949 | /* |
950 | * ======== user_va2_pa ======== | ||
951 | * Purpose: | ||
952 | * This function walks through the page tables to convert a userland | ||
953 | * virtual address to physical address | ||
954 | */ | ||
955 | static u32 user_va2_pa(struct mm_struct *mm, u32 address) | ||
956 | { | ||
957 | pgd_t *pgd; | ||
958 | pmd_t *pmd; | ||
959 | pte_t *ptep, pte; | ||
960 | |||
961 | pgd = pgd_offset(mm, address); | ||
962 | if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { | ||
963 | pmd = pmd_offset(pgd, address); | ||
964 | if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { | ||
965 | ptep = pte_offset_map(pmd, address); | ||
966 | if (ptep) { | ||
967 | pte = *ptep; | ||
968 | if (pte_present(pte)) | ||
969 | return pte & PAGE_MASK; | ||
970 | } | ||
971 | } | ||
972 | } | ||
973 | |||
974 | return 0; | ||
975 | } | ||
976 | |||
977 | /** | ||
978 | * get_io_pages() - pin and get pages of io user's buffer. | ||
979 | * @mm: mm_struct Pointer of the process. | ||
980 | * @uva: Virtual user space address. | ||
981 | * @pages Pages to be pined. | ||
982 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
983 | * | ||
984 | */ | ||
985 | static int get_io_pages(struct mm_struct *mm, u32 uva, unsigned pages, | ||
986 | struct page **usr_pgs) | ||
987 | { | ||
988 | u32 pa; | ||
989 | int i; | ||
990 | struct page *pg; | ||
991 | |||
992 | for (i = 0; i < pages; i++) { | ||
993 | pa = user_va2_pa(mm, uva); | ||
994 | |||
995 | if (!pfn_valid(__phys_to_pfn(pa))) | ||
996 | break; | ||
997 | |||
998 | pg = PHYS_TO_PAGE(pa); | ||
999 | usr_pgs[i] = pg; | ||
1000 | get_page(pg); | ||
1001 | } | ||
1002 | return i; | ||
1003 | } | ||
1004 | |||
1005 | /** | ||
1006 | * user_to_dsp_map() - maps user to dsp virtual address | ||
1007 | * @mmu: Pointer to iommu handle. | ||
1008 | * @uva: Virtual user space address. | ||
1009 | * @da DSP address | ||
1010 | * @size Buffer size to map. | ||
1011 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
1012 | * | ||
1013 | * This function maps a user space buffer into DSP virtual address. | ||
1014 | * | ||
1015 | */ | ||
1016 | u32 user_to_dsp_map(struct iommu *mmu, u32 uva, u32 da, u32 size, | ||
1017 | struct page **usr_pgs) | ||
1018 | { | ||
1019 | int res, w; | ||
1020 | unsigned pages, i; | ||
1021 | struct vm_area_struct *vma; | ||
1022 | struct mm_struct *mm = current->mm; | ||
1023 | struct sg_table *sgt; | ||
1024 | struct scatterlist *sg; | ||
1025 | |||
1026 | if (!size || !usr_pgs) | ||
1027 | return -EINVAL; | ||
1028 | |||
1029 | pages = size / PG_SIZE4K; | ||
1030 | |||
1031 | down_read(&mm->mmap_sem); | ||
1032 | vma = find_vma(mm, uva); | ||
1033 | while (vma && (uva + size > vma->vm_end)) | ||
1034 | vma = find_vma(mm, vma->vm_end + 1); | ||
1035 | |||
1036 | if (!vma) { | ||
1037 | pr_err("%s: Failed to get VMA region for 0x%x (%d)\n", | ||
1038 | __func__, uva, size); | ||
1039 | up_read(&mm->mmap_sem); | ||
1040 | return -EINVAL; | ||
1041 | } | ||
1042 | if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) | ||
1043 | w = 1; | ||
1044 | |||
1045 | if (vma->vm_flags & VM_IO) | ||
1046 | i = get_io_pages(mm, uva, pages, usr_pgs); | ||
1047 | else | ||
1048 | i = get_user_pages(current, mm, uva, pages, w, 1, | ||
1049 | usr_pgs, NULL); | ||
1050 | up_read(&mm->mmap_sem); | ||
1051 | |||
1052 | if (i < 0) | ||
1053 | return i; | ||
1054 | |||
1055 | if (i < pages) { | ||
1056 | res = -EFAULT; | ||
1057 | goto err_pages; | ||
1058 | } | ||
1059 | |||
1060 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | ||
1061 | if (!sgt) { | ||
1062 | res = -ENOMEM; | ||
1063 | goto err_pages; | ||
1064 | } | ||
1065 | |||
1066 | res = sg_alloc_table(sgt, pages, GFP_KERNEL); | ||
1067 | |||
1068 | if (res < 0) | ||
1069 | goto err_sg; | ||
1070 | |||
1071 | for_each_sg(sgt->sgl, sg, sgt->nents, i) | ||
1072 | sg_set_page(sg, usr_pgs[i], PAGE_SIZE, 0); | ||
1073 | |||
1074 | da = iommu_vmap(mmu, da, sgt, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); | ||
1075 | |||
1076 | if (!IS_ERR_VALUE(da)) | ||
1077 | return da; | ||
1078 | res = (int)da; | ||
1079 | |||
1080 | sg_free_table(sgt); | ||
1081 | err_sg: | ||
1082 | kfree(sgt); | ||
1083 | i = pages; | ||
1084 | err_pages: | ||
1085 | while (i--) | ||
1086 | put_page(usr_pgs[i]); | ||
1087 | return res; | ||
1088 | } | ||
1089 | |||
1090 | /** | ||
1091 | * user_to_dsp_unmap() - unmaps DSP virtual buffer. | ||
1092 | * @mmu: Pointer to iommu handle. | ||
1093 | * @da DSP address | ||
1094 | * | ||
1095 | * This function unmaps a user space buffer into DSP virtual address. | ||
1096 | * | ||
1097 | */ | ||
1098 | int user_to_dsp_unmap(struct iommu *mmu, u32 da) | ||
1099 | { | ||
1100 | unsigned i; | ||
1101 | struct sg_table *sgt; | ||
1102 | struct scatterlist *sg; | ||
1103 | |||
1104 | sgt = iommu_vunmap(mmu, da); | ||
1105 | if (!sgt) | ||
1106 | return -EFAULT; | ||
1107 | |||
1108 | for_each_sg(sgt->sgl, sg, sgt->nents, i) | ||
1109 | put_page(sg_page(sg)); | ||
1110 | sg_free_table(sgt); | ||
1111 | kfree(sgt); | ||
1112 | |||
1113 | return 0; | ||
1114 | } | ||
1115 | |||
1116 | /* | ||
947 | * ======== wait_for_start ======== | 1117 | * ======== wait_for_start ======== |
948 | * Wait for the singal from DSP that it has started, or time out. | 1118 | * Wait for the singal from DSP that it has started, or time out. |
949 | */ | 1119 | */ |
diff --git a/drivers/staging/tidspbridge/core/ue_deh.c b/drivers/staging/tidspbridge/core/ue_deh.c index e24ea0c7391..2e1ac89644d 100644 --- a/drivers/staging/tidspbridge/core/ue_deh.c +++ b/drivers/staging/tidspbridge/core/ue_deh.c | |||
@@ -31,6 +31,32 @@ | |||
31 | #include <dspbridge/drv.h> | 31 | #include <dspbridge/drv.h> |
32 | #include <dspbridge/wdt.h> | 32 | #include <dspbridge/wdt.h> |
33 | 33 | ||
34 | #define MMU_CNTL_TWL_EN (1 << 2) | ||
35 | |||
36 | static void mmu_fault_dpc(unsigned long data) | ||
37 | { | ||
38 | struct deh_mgr *deh = (void *)data; | ||
39 | |||
40 | if (!deh) | ||
41 | return; | ||
42 | |||
43 | bridge_deh_notify(deh, DSP_MMUFAULT, 0); | ||
44 | } | ||
45 | |||
46 | int mmu_fault_isr(struct iommu *mmu) | ||
47 | { | ||
48 | struct deh_mgr *dm; | ||
49 | |||
50 | dev_get_deh_mgr(dev_get_first(), &dm); | ||
51 | |||
52 | if (!dm) | ||
53 | return -EPERM; | ||
54 | |||
55 | iommu_write_reg(mmu, 0, MMU_IRQENABLE); | ||
56 | tasklet_schedule(&dm->dpc_tasklet); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
34 | int bridge_deh_create(struct deh_mgr **ret_deh, | 60 | int bridge_deh_create(struct deh_mgr **ret_deh, |
35 | struct dev_object *hdev_obj) | 61 | struct dev_object *hdev_obj) |
36 | { | 62 | { |
@@ -58,6 +84,9 @@ int bridge_deh_create(struct deh_mgr **ret_deh, | |||
58 | } | 84 | } |
59 | ntfy_init(deh->ntfy_obj); | 85 | ntfy_init(deh->ntfy_obj); |
60 | 86 | ||
87 | /* Create a MMUfault DPC */ | ||
88 | tasklet_init(&deh->dpc_tasklet, mmu_fault_dpc, (u32) deh); | ||
89 | |||
61 | /* Fill in context structure */ | 90 | /* Fill in context structure */ |
62 | deh->hbridge_context = hbridge_context; | 91 | deh->hbridge_context = hbridge_context; |
63 | 92 | ||
@@ -81,6 +110,9 @@ int bridge_deh_destroy(struct deh_mgr *deh) | |||
81 | kfree(deh->ntfy_obj); | 110 | kfree(deh->ntfy_obj); |
82 | } | 111 | } |
83 | 112 | ||
113 | /* Free DPC object */ | ||
114 | tasklet_kill(&deh->dpc_tasklet); | ||
115 | |||
84 | /* Deallocate the DEH manager object */ | 116 | /* Deallocate the DEH manager object */ |
85 | kfree(deh); | 117 | kfree(deh); |
86 | 118 | ||
@@ -101,6 +133,51 @@ int bridge_deh_register_notify(struct deh_mgr *deh, u32 event_mask, | |||
101 | return ntfy_unregister(deh->ntfy_obj, hnotification); | 133 | return ntfy_unregister(deh->ntfy_obj, hnotification); |
102 | } | 134 | } |
103 | 135 | ||
136 | #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE | ||
137 | static void mmu_fault_print_stack(struct bridge_dev_context *dev_context) | ||
138 | { | ||
139 | void *dummy_addr; | ||
140 | u32 fa, tmp; | ||
141 | struct iotlb_entry e; | ||
142 | struct iommu *mmu = dev_context->dsp_mmu; | ||
143 | dummy_addr = (void *)__get_free_page(GFP_ATOMIC); | ||
144 | |||
145 | /* | ||
146 | * Before acking the MMU fault, let's make sure MMU can only | ||
147 | * access entry #0. Then add a new entry so that the DSP OS | ||
148 | * can continue in order to dump the stack. | ||
149 | */ | ||
150 | tmp = iommu_read_reg(mmu, MMU_CNTL); | ||
151 | tmp &= ~MMU_CNTL_TWL_EN; | ||
152 | iommu_write_reg(mmu, tmp, MMU_CNTL); | ||
153 | fa = iommu_read_reg(mmu, MMU_FAULT_AD); | ||
154 | e.da = fa & PAGE_MASK; | ||
155 | e.pa = virt_to_phys(dummy_addr); | ||
156 | e.valid = 1; | ||
157 | e.prsvd = 1; | ||
158 | e.pgsz = IOVMF_PGSZ_4K & MMU_CAM_PGSZ_MASK; | ||
159 | e.endian = MMU_RAM_ENDIAN_LITTLE; | ||
160 | e.elsz = MMU_RAM_ELSZ_32; | ||
161 | e.mixed = 0; | ||
162 | |||
163 | load_iotlb_entry(dev_context->dsp_mmu, &e); | ||
164 | |||
165 | dsp_clk_enable(DSP_CLK_GPT8); | ||
166 | |||
167 | dsp_gpt_wait_overflow(DSP_CLK_GPT8, 0xfffffffe); | ||
168 | |||
169 | /* Clear MMU interrupt */ | ||
170 | tmp = iommu_read_reg(mmu, MMU_IRQSTATUS); | ||
171 | iommu_write_reg(mmu, tmp, MMU_IRQSTATUS); | ||
172 | |||
173 | dump_dsp_stack(dev_context); | ||
174 | dsp_clk_disable(DSP_CLK_GPT8); | ||
175 | |||
176 | iopgtable_clear_entry(mmu, fa); | ||
177 | free_page((unsigned long)dummy_addr); | ||
178 | } | ||
179 | #endif | ||
180 | |||
104 | static inline const char *event_to_string(int event) | 181 | static inline const char *event_to_string(int event) |
105 | { | 182 | { |
106 | switch (event) { | 183 | switch (event) { |
@@ -116,6 +193,7 @@ void bridge_deh_notify(struct deh_mgr *deh, int event, int info) | |||
116 | { | 193 | { |
117 | struct bridge_dev_context *dev_context; | 194 | struct bridge_dev_context *dev_context; |
118 | const char *str = event_to_string(event); | 195 | const char *str = event_to_string(event); |
196 | u32 fa; | ||
119 | 197 | ||
120 | if (!deh) | 198 | if (!deh) |
121 | return; | 199 | return; |
@@ -133,7 +211,13 @@ void bridge_deh_notify(struct deh_mgr *deh, int event, int info) | |||
133 | #endif | 211 | #endif |
134 | break; | 212 | break; |
135 | case DSP_MMUFAULT: | 213 | case DSP_MMUFAULT: |
136 | dev_err(bridge, "%s: %s, addr=0x%x", __func__, str, info); | 214 | fa = iommu_read_reg(dev_context->dsp_mmu, MMU_FAULT_AD); |
215 | dev_err(bridge, "%s: %s, addr=0x%x", __func__, str, fa); | ||
216 | #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE | ||
217 | print_dsp_trace_buffer(dev_context); | ||
218 | dump_dl_modules(dev_context); | ||
219 | mmu_fault_print_stack(dev_context); | ||
220 | #endif | ||
137 | break; | 221 | break; |
138 | default: | 222 | default: |
139 | dev_err(bridge, "%s: %s", __func__, str); | 223 | dev_err(bridge, "%s: %s", __func__, str); |