dsp_mem.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,475 行 · 第 1/3 页
C
1,475 行
/* * linux/arch/arm/mach-omap/dsp/dsp_mem.c * * OMAP DSP memory driver * * Copyright (C) 2002-2004 Nokia Corporation * * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: dsp_mem.c * $Revision: 3.0.1 * $Date: 2004/06/29 * */#include <linux/module.h>#include <linux/init.h>#include <linux/major.h>#include <linux/fs.h>#include <linux/proc_fs.h>#include <linux/bootmem.h>#include <linux/fb.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/ioctls.h>#include <asm/delay.h>#include <asm/irq.h>#include <asm/pgalloc.h>#include <asm/pgtable.h>#include <asm/hardware/clock.h>#include <asm/arch/dsp.h>#include "uaccess_dsp.h"#include "dsp.h"#define SZ_1MB 0x100000#define SZ_64KB 0x10000#define SZ_4KB 0x1000#define SZ_1KB 0x400#define ceil(adr,align) (((adr)+align-1)&~(align-1))#define is_aligned(adr,align) (!((adr)&((align)-1)))#define PMD2_MASK (~(PMD_SIZE*2-1))#define PMD2_ALIGN(addr) (((addr)+PMD_SIZE*2-1)&(PMD2_MASK))#define dspmmu_enable() \ do { \ omap_writew(DSPMMU_CNTL_MMU_EN | DSPMMU_CNTL_RESET_SW, \ DSPMMU_CNTL); \ } while(0)#define dspmmu_disable() \ do { omap_writew(0, DSPMMU_CNTL); } while(0)#define dspmmu_flush() \ do { \ omap_writew(DSPMMU_FLUSH_ENTRY_FLUSH_ENTRY, \ DSPMMU_FLUSH_ENTRY); \ } while(0)#define __dspmmu_gflush() \ do { omap_writew(DSPMMU_GFLUSH_GFLUSH, DSPMMU_GFLUSH); } while(0)#define __dspmmu_itack() \ do { omap_writew(DSPMMU_IT_ACK_IT_ACK, DSPMMU_IT_ACK); } while(0)#define EMIF_PRIO_LB_MASK 0x0000f000#define EMIF_PRIO_LB_SHIFT 12#define EMIF_PRIO_DMA_MASK 0x00000f00#define EMIF_PRIO_DMA_SHIFT 8#define EMIF_PRIO_DSP_MASK 0x00000070#define EMIF_PRIO_DSP_SHIFT 4#define EMIF_PRIO_MPU_MASK 0x00000007#define EMIF_PRIO_MPU_SHIFT 0#define set_emiff_dma_prio(prio) \ do { \ omap_writel((omap_readl(EMIFF_PRIO) & ~EMIF_PRIO_DMA_MASK) | \ ((prio) << EMIF_PRIO_DMA_SHIFT), \ EMIFF_PRIO); \ } while(0)struct exmap { unsigned int valid:1; unsigned int cntnu:1; /* grouping */ void *buf; void *vadr; unsigned int order;};#define DSPMMU_TLB_LINES 32static struct exmap exmap[DSPMMU_TLB_LINES];static int omap_dsp_exunmap(unsigned long dspadr);static void *dspvect_page = NULL;static unsigned long dsp_fault_adr;static __inline__ unsigned long lineup_offset(unsigned long adr, unsigned long ref, unsigned long mask){ unsigned long newadr; newadr = (adr & ~mask) | (ref & mask); if (newadr < adr) newadr += mask + 1; return newadr;}/* * ARM MMU operations */static int omap_dsp_exmap_set_armmmu(unsigned long virt, unsigned long phys, unsigned long size){ printk(KERN_DEBUG "omapdsp: mapping in ARM MMU, " "v=0x%08lx, p=0x%08lx, sz=0x%lx\n", virt, phys, size);#if PMD_SIZE != SZ_1MB#error PMD_SIZE is not as expected!!!#endif if (size == SZ_1MB) { /* == PMD_SIZE */ int prot_sect; pmd_t pmd, *pmdp; printk(KERN_DEBUG " (using 1MB section)\n"); prot_sect = PMD_TYPE_SECT | PMD_DOMAIN(DOMAIN_IO) | PMD_SECT_AP_WRITE; pmd_val(pmd) = phys | prot_sect; pmdp = pmd_offset(pgd_offset_k(virt), virt); if (virt & (1 << 20)) pmdp++; set_pmd(pmdp, pmd); } else { /* 64KB, 4KB */ int prot_pmd, prot_pte; long off; unsigned long sz_left; pmd_t *pmdp; pte_t *ptep; printk(KERN_DEBUG " (using 4KB page(s))\n"); prot_pmd = PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO); prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE; pmdp = pmd_offset(pgd_offset_k(virt), virt); if (pmd_none(*pmdp)) { pte_t *ptep = pte_alloc_one_kernel(&init_mm, 0); if (ptep == NULL) return -ENOMEM; /* note: two PMDs will be set */ pmd_populate_kernel(&init_mm, pmdp, ptep); } off = phys - virt; for (sz_left = size; sz_left >= PAGE_SIZE; sz_left -= PAGE_SIZE, virt += PAGE_SIZE) { ptep = pte_offset_kernel(pmdp, virt); set_pte(ptep, __pte((virt + off) | prot_pte)); } if (sz_left) BUG(); } return 0;}static void omap_dsp_exmap_clear_armmmu(unsigned long virt, unsigned long size){ printk(KERN_DEBUG "omapdsp: unmapping in ARM MMU, " "v=0x%08lx, sz=0x%lx\n", virt, size); if (size == SZ_1MB) { /* == PMD_SIZE */ pmd_t *pmdp; pmdp = pmd_offset(pgd_offset_k(virt), virt); printk(KERN_DEBUG " (clearing 1MB section)\n"); pmd_clear(pmdp); } else { /* 64KB, 4KB */ unsigned long sz_left; pmd_t *pmdp; pte_t *ptep; for (sz_left = size; sz_left >= PAGE_SIZE; sz_left -= PAGE_SIZE, virt += PAGE_SIZE) { pmdp = pmd_offset(pgd_offset_k(virt), virt); ptep = pte_offset_kernel(pmdp, virt); pte_clear(ptep); } if (sz_left) BUG(); }}void omap_dsp_map_update(struct task_struct *tsk){ unsigned long virt; pmd_t *pmdp, *s_pmd, *e_pmd; printk(KERN_DEBUG "omapdsp: updating memory mapping for DSP space. " "(pid=%d)\n", tsk->pid); virt = PMD2_ALIGN(dspmem_base); s_pmd = pmd_offset(pgd_offset(tsk->mm, virt), virt); virt = PMD2_ALIGN(dspmem_base + DSPSPACE_SIZE); e_pmd = pmd_offset(pgd_offset(tsk->mm, virt), virt); for (pmdp = s_pmd; pmdp < e_pmd; pmdp += 2) { pmd_clear(pmdp); }}static int omap_dsp_exmap_valid(void *vadr, size_t len){ int i;start: for (i = 0; i < DSPMMU_TLB_LINES; i++) { void *mapadr; unsigned long mapsize; if (!exmap[i].valid) continue; mapadr = (void *)exmap[i].vadr; mapsize = 1 << (exmap[i].order + PAGE_SHIFT); if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) { if (vadr + len <= mapadr + mapsize) { /* this map covers whole address. */ return 1; } else { /* * this map covers partially. * check rest portion. */ len -= mapadr + mapsize - vadr; vadr = mapadr + mapsize; goto start; } } } return 0;}/* * omap_dsp_virt_to_phys() * returns physical address, and sets len to valid length */unsigned long omap_dsp_virt_to_phys(void *vadr, size_t *len){ int i; if (is_dsp_internal_mem(vadr)) { /* DSRAM or SARAM */ *len = dspmem_base + dspmem_size - (unsigned long)vadr; return (unsigned long)vadr; } /* EXRAM */ for (i = 0; i < DSPMMU_TLB_LINES; i++) { void *mapadr; unsigned long mapsize; if (!exmap[i].valid) continue; mapadr = (void *)exmap[i].vadr; mapsize = 1 << (exmap[i].order + PAGE_SHIFT); if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) { *len = mapadr + mapsize - vadr; return __pa(exmap[i].buf); } } /* valid mapping not found */ return 0;}/* * DSP MMU operations */static __inline__ unsigned short get_cam_l_va_mask(unsigned short slst){ switch (slst) { case DSPMMU_CAM_L_SLST_1MB: return DSPMMU_CAM_L_VA_TAG_L1_MASK | DSPMMU_CAM_L_VA_TAG_L2_MASK_1MB; case DSPMMU_CAM_L_SLST_64KB: return DSPMMU_CAM_L_VA_TAG_L1_MASK | DSPMMU_CAM_L_VA_TAG_L2_MASK_64KB; case DSPMMU_CAM_L_SLST_4KB: return DSPMMU_CAM_L_VA_TAG_L1_MASK | DSPMMU_CAM_L_VA_TAG_L2_MASK_4KB; case DSPMMU_CAM_L_SLST_1KB: return DSPMMU_CAM_L_VA_TAG_L1_MASK | DSPMMU_CAM_L_VA_TAG_L2_MASK_1KB; } return 0;}static __inline__ void get_tlb_lock(int *base, int *victim){ unsigned short lock = omap_readw(DSPMMU_LOCK); if (base != NULL) *base = (lock & DSPMMU_LOCK_BASE_MASK) >> DSPMMU_LOCK_BASE_SHIFT; if (victim != NULL) *victim = (lock & DSPMMU_LOCK_VICTIM_MASK) >> DSPMMU_LOCK_VICTIM_SHIFT;}static __inline__ void set_tlb_lock(int base, int victim){ omap_writew((base << DSPMMU_LOCK_BASE_SHIFT) | (victim << DSPMMU_LOCK_VICTIM_SHIFT), DSPMMU_LOCK);}static __inline__ void read_tlb(unsigned short lbase, unsigned short victim, unsigned short *cam_h, unsigned short *cam_l, unsigned short *ram_h, unsigned short *ram_l){ /* set victim */ set_tlb_lock(lbase, victim); /* read a TLB entry */ omap_writew(DSPMMU_LD_TLB_RD, DSPMMU_LD_TLB); if (cam_h != NULL) *cam_h = omap_readw(DSPMMU_READ_CAM_H); if (cam_l != NULL) *cam_l = omap_readw(DSPMMU_READ_CAM_L); if (ram_h != NULL) *ram_h = omap_readw(DSPMMU_READ_RAM_H); if (ram_l != NULL) *ram_l = omap_readw(DSPMMU_READ_RAM_L);}static __inline__ void load_tlb(unsigned short cam_h, unsigned short cam_l, unsigned short ram_h, unsigned short ram_l){ omap_writew(cam_h, DSPMMU_CAM_H); omap_writew(cam_l, DSPMMU_CAM_L); omap_writew(ram_h, DSPMMU_RAM_H); omap_writew(ram_l, DSPMMU_RAM_L); /* flush the entry */ dspmmu_flush(); /* load a TLB entry */ omap_writew(DSPMMU_LD_TLB_LD, DSPMMU_LD_TLB);}static size_t omap_dsp_read_static_tlb(unsigned long vadr){ /* * this function returns the size to the end of mapped page */ int lbase, victim; size_t retsz = 0; int i; clk_use(dsp_ck_handle); get_tlb_lock(&lbase, &victim); for (i = 0; i < lbase; i++) { unsigned short cam_h, cam_l; unsigned short cam_l_va_mask, cam_vld, slst; unsigned long cam_va; unsigned long pgsz; /* read a TLB entry */ read_tlb(lbase, i, &cam_h, &cam_l, NULL, NULL); cam_vld = cam_l & DSPMMU_CAM_L_V; if (!cam_vld) continue; slst = cam_l & DSPMMU_CAM_L_SLST_MASK; cam_l_va_mask = get_cam_l_va_mask(slst); pgsz = (slst == DSPMMU_CAM_L_SLST_1MB) ? SZ_1MB: (slst == DSPMMU_CAM_L_SLST_64KB)? SZ_64KB: (slst == DSPMMU_CAM_L_SLST_4KB) ? SZ_4KB: SZ_1KB; cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 | (unsigned long)(cam_l & cam_l_va_mask) << 6; if ((cam_va <= vadr) && (vadr < cam_va + pgsz)) { unsigned long offset = vadr - cam_va; retsz = pgsz - offset; break; } } /* restore victim entry */ set_tlb_lock(lbase, victim); clk_unuse(dsp_ck_handle); return retsz;}static int omap_dsp_load_static_tlb(unsigned long vadr, unsigned long padr, unsigned short slst, unsigned short prsvd, unsigned short ap){ int lbase, victim; unsigned short cam_l_va_mask; clk_use(dsp_ck_handle); get_tlb_lock(&lbase, NULL); for (victim = 0; victim < lbase; victim++) { unsigned short cam_l; /* read a TLB entry */ read_tlb(lbase, victim, NULL, &cam_l, NULL, NULL); if (!(cam_l & DSPMMU_CAM_L_V)) goto found_victim; } set_tlb_lock(lbase, victim);found_victim: /* The last (31st) entry cannot be locked? */ if (victim == 31) { printk(KERN_ERR "omapdsp: TLB is full.\n"); return -EBUSY; } /* overlap check */ if (omap_dsp_read_static_tlb(vadr)) { printk(KERN_ERR "omapdsp: DSP TLB page overlap!\n"); return -EINVAL; } cam_l_va_mask = get_cam_l_va_mask(slst); if (vadr & ~(DSPMMU_CAM_H_VA_TAG_H_MASK << 22 | (unsigned long)cam_l_va_mask << 6)) { printk(KERN_ERR "omapdsp: mapping vadr (0x%06lx) is not " "aligned boundary\n", vadr); return -EINVAL; } load_tlb(vadr >> 22, (vadr >> 6 & cam_l_va_mask) | prsvd | slst, padr >> 16, (padr & DSPMMU_RAM_L_RAM_LSB_MASK) | ap); /* update lock base */ if (victim == lbase) lbase++; set_tlb_lock(lbase, lbase); clk_unuse(dsp_ck_handle); return 0;}static int omap_dsp_clear_static_tlb(unsigned long vadr){ int lbase; int i; int max_valid = 0; clk_use(dsp_ck_handle); get_tlb_lock(&lbase, NULL); for (i = 0; i < lbase; i++) { unsigned short cam_h, cam_l; unsigned short cam_l_va_mask, cam_vld, slst; unsigned long cam_va; /* read a TLB entry */ read_tlb(lbase, i, &cam_h, &cam_l, NULL, NULL); cam_vld = cam_l & DSPMMU_CAM_L_V; if (!cam_vld) continue; slst = cam_l & DSPMMU_CAM_L_SLST_MASK;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?