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 | |
| 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>
| -rw-r--r-- | drivers/staging/tidspbridge/Makefile | 2 | ||||
| -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 | ||||
| -rw-r--r-- | drivers/staging/tidspbridge/include/dspbridge/dsp-mmu.h | 67 |
7 files changed, 289 insertions, 391 deletions
diff --git a/drivers/staging/tidspbridge/Makefile b/drivers/staging/tidspbridge/Makefile index 37bd3bcc03af..1a091faac046 100644 --- a/drivers/staging/tidspbridge/Makefile +++ b/drivers/staging/tidspbridge/Makefile | |||
| @@ -2,7 +2,7 @@ obj-$(CONFIG_TIDSPBRIDGE) += bridgedriver.o | |||
| 2 | 2 | ||
| 3 | libgen = gen/gb.o gen/gs.o gen/gh.o gen/uuidutil.o | 3 | libgen = gen/gb.o gen/gs.o gen/gh.o gen/uuidutil.o |
| 4 | libcore = core/chnl_sm.o core/msg_sm.o core/io_sm.o core/tiomap3430.o \ | 4 | libcore = core/chnl_sm.o core/msg_sm.o core/io_sm.o core/tiomap3430.o \ |
| 5 | core/tiomap3430_pwr.o core/tiomap_io.o core/dsp-mmu.o \ | 5 | core/tiomap3430_pwr.o core/tiomap_io.o \ |
| 6 | core/ue_deh.o core/wdt.o core/dsp-clock.o core/sync.o | 6 | core/ue_deh.o core/wdt.o core/dsp-clock.o core/sync.o |
| 7 | libpmgr = pmgr/chnl.o pmgr/io.o pmgr/msg.o pmgr/cod.o pmgr/dev.o pmgr/dspapi.o \ | 7 | libpmgr = pmgr/chnl.o pmgr/io.o pmgr/msg.o pmgr/cod.o pmgr/dev.o pmgr/dspapi.o \ |
| 8 | pmgr/dmm.o pmgr/cmm.o pmgr/dbll.o | 8 | pmgr/dmm.o pmgr/cmm.o pmgr/dbll.o |
diff --git a/drivers/staging/tidspbridge/core/_deh.h b/drivers/staging/tidspbridge/core/_deh.h index 8ae263387a87..f1254f0aa037 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 e0a801c1cb98..cd7ff8810a0b 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 983c95adc8ff..000000000000 --- 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 53b38b2b9ce1..984a35a068ef 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 e24ea0c73914..2e1ac89644d3 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); |
diff --git a/drivers/staging/tidspbridge/include/dspbridge/dsp-mmu.h b/drivers/staging/tidspbridge/include/dspbridge/dsp-mmu.h deleted file mode 100644 index cb38d4cc0734..000000000000 --- a/drivers/staging/tidspbridge/include/dspbridge/dsp-mmu.h +++ /dev/null | |||
| @@ -1,67 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * dsp-mmu.h | ||
| 3 | * | ||
| 4 | * DSP-BIOS Bridge driver support functions for TI OMAP processors. | ||
| 5 | * | ||
| 6 | * DSP iommu. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2005-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 | #ifndef _DSP_MMU_ | ||
| 20 | #define _DSP_MMU_ | ||
| 21 | |||
| 22 | #include <plat/iommu.h> | ||
| 23 | #include <plat/iovmm.h> | ||
| 24 | |||
| 25 | /** | ||
| 26 | * dsp_mmu_init() - initialize dsp_mmu module and returns a handle | ||
| 27 | * | ||
| 28 | * This function initialize dsp mmu module and returns a struct iommu | ||
| 29 | * handle to use it for dsp maps. | ||
| 30 | * | ||
| 31 | */ | ||
| 32 | struct iommu *dsp_mmu_init(void); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * dsp_mmu_exit() - destroy dsp mmu module | ||
| 36 | * @mmu: Pointer to iommu handle. | ||
| 37 | * | ||
| 38 | * This function destroys dsp mmu module. | ||
| 39 | * | ||
| 40 | */ | ||
| 41 | void dsp_mmu_exit(struct iommu *mmu); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * user_to_dsp_map() - maps user to dsp virtual address | ||
| 45 | * @mmu: Pointer to iommu handle. | ||
| 46 | * @uva: Virtual user space address. | ||
| 47 | * @da DSP address | ||
| 48 | * @size Buffer size to map. | ||
| 49 | * @usr_pgs struct page array pointer where the user pages will be stored | ||
| 50 | * | ||
| 51 | * This function maps a user space buffer into DSP virtual address. | ||
| 52 | * | ||
| 53 | */ | ||
| 54 | u32 user_to_dsp_map(struct iommu *mmu, u32 uva, u32 da, u32 size, | ||
| 55 | struct page **usr_pgs); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * user_to_dsp_unmap() - unmaps DSP virtual buffer. | ||
| 59 | * @mmu: Pointer to iommu handle. | ||
| 60 | * @da DSP address | ||
| 61 | * | ||
| 62 | * This function unmaps a user space buffer into DSP virtual address. | ||
| 63 | * | ||
| 64 | */ | ||
| 65 | int user_to_dsp_unmap(struct iommu *mmu, u32 da); | ||
| 66 | |||
| 67 | #endif | ||
