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 + -
显示快捷键?