diff options
Diffstat (limited to 'drivers/dsp/syslink/procmgr/proc4430')
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/Kbuild | 10 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/dmm4430.c | 355 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/dmm4430.h | 50 | ||||
-rw-r--r-- | drivers/dsp/syslink/procmgr/proc4430/ducatienabler.c | 1378 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/hw_mmu.c | 661 | ||||
-rw-r--r-- | drivers/dsp/syslink/procmgr/proc4430/proc4430.c | 1053 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/proc4430.h | 147 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/proc4430_drv.c | 400 | ||||
-rwxr-xr-x | drivers/dsp/syslink/procmgr/proc4430/proc4430_drvdefs.h | 169 |
9 files changed, 4223 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/procmgr/proc4430/Kbuild b/drivers/dsp/syslink/procmgr/proc4430/Kbuild new file mode 100755 index 000000000000..f82f2e23f79a --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/Kbuild @@ -0,0 +1,10 @@ +libomap_proc4430 = proc4430.o proc4430_drv.o dmm4430.o \ + ducatienabler.o hw_mmu.o + +obj-$(CONFIG_SYSLINK_PROC) += syslink_proc4430.o +syslink_proc4430-objs = $(libomap_proc4430) + +ccflags-y += -Wno-strict-prototypes -DUSE_LEVEL_1_MACROS + +#Header files +ccflags-y += -Iarch/arm/plat-omap/include/syslink diff --git a/drivers/dsp/syslink/procmgr/proc4430/dmm4430.c b/drivers/dsp/syslink/procmgr/proc4430/dmm4430.c new file mode 100755 index 000000000000..d2ed9ece2245 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/dmm4430.c @@ -0,0 +1,355 @@ +/* + * dmm4430.c + * + * Syslink support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * ======== dmm.c ======== + * Purpose: + *The Dynamic Memory Manager (DMM) module manages the DSP Virtual address + *space that can be directly mapped to any MPU buffer or memory region + * + * Public Functions: + *dmm_create_tables + *dmm_create + *dmm_destroy + *dmm_exit + *dmm_init + *dmm_map_memory + *DMM_Reset + *dmm_reserve_memory + *dmm_unmap_memory + *dmm_unreserve_memory + * + * Private Functions: + *add_region + *create_region + *get_region + * get_free_region + * get_mapped_region + * + * Notes: + *Region: Generic memory entitiy having a start address and a size + *Chunk: Reserved region + * + *! + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <linux/vmalloc.h> +#include <asm/page.h> +#include "dmm4430.h" + + +#define DMM_ADDR_VIRTUAL(x, a) \ + do { \ + x = (((struct map_page *)(a) - p_virt_mapping_tbl) * PAGE_SIZE \ + + dyn_mem_map_begin);\ + } while (0) + +#define DMM_ADDR_TO_INDEX(i, a) \ + do { \ + i = (((a) - dyn_mem_map_begin) / PAGE_SIZE); \ + } while (0) + +struct map_page { + u32 region_size:31; + u32 b_reserved:1; +}; + +/* Create the free list */ +static struct map_page *p_virt_mapping_tbl; +static u32 i_freeregion; /* The index of free region */ +static u32 i_freesize; +static u32 table_size;/* The size of virtual and physical pages tables */ +static u32 dyn_mem_map_begin; +struct mutex *dmm_lock; + +static struct map_page *get_free_region(u32 size); +static struct map_page *get_mapped_region(u32 addr); + +/* ======== dmm_create_tables ======== + * Purpose: + *Create table to hold information of the virtual memory that is reserved + *for DSP. + */ +int dmm_create_tables(u32 addr, u32 size) +{ + int status = 0; + + dmm_delete_tables(); + if (WARN_ON(mutex_lock_interruptible(dmm_lock)) < 0) { + status = -EFAULT; + goto func_exit; + } + dyn_mem_map_begin = addr; + table_size = (size/PAGE_SIZE) + 1; + /* Create the free list */ + p_virt_mapping_tbl = (struct map_page *)vmalloc( + table_size*sizeof(struct map_page)); + if (WARN_ON(p_virt_mapping_tbl == NULL)) + status = -ENOMEM; + /* On successful allocation, + * all entries are zero ('free') */ + i_freeregion = 0; + i_freesize = table_size*PAGE_SIZE; + p_virt_mapping_tbl[0].region_size = table_size; + mutex_unlock(dmm_lock); + +func_exit: + return status; +} + +/* + * ======== dmm_create ======== + * Purpose: + *Create a dynamic memory manager object. + */ +int dmm_create(void) +{ + int status = 0; + dmm_lock = kmalloc(sizeof(struct mutex), GFP_KERNEL); + if (WARN_ON(dmm_lock == NULL)) { + status = -EFAULT; + goto func_exit; + } + mutex_init(dmm_lock); +func_exit: + return status; +} + +/* + * ======== dmm_destroy ======== + * Purpose: + *Release the communication memory manager resources. + */ +void dmm_destroy(void) +{ + dmm_delete_tables(); + kfree(dmm_lock); +} + + +/* + * ======== dmm_delete_tables ======== + * Purpose: + *Delete DMM Tables. + */ +void dmm_delete_tables(void) +{ + /* Delete all DMM tables */ + WARN_ON(mutex_lock_interruptible(dmm_lock)); + if (p_virt_mapping_tbl != NULL) { + vfree(p_virt_mapping_tbl); + p_virt_mapping_tbl = NULL; + } + mutex_unlock(dmm_lock); +} + +/* + * ======== dmm_init ======== + * Purpose: + *Initializes private state of DMM module. + */ +void dmm_init(void) +{ + p_virt_mapping_tbl = NULL ; + table_size = 0; + return; +} + +/* + * ======== dmm_reserve_memory ======== + * Purpose: + *Reserve a chunk of virtually contiguous DSP/IVA address space. + */ +int dmm_reserve_memory(u32 size, u32 *p_rsv_addr) +{ + int status = 0; + struct map_page *node; + u32 rsv_addr = 0; + u32 rsv_size = 0; + + if (WARN_ON(mutex_lock_interruptible(dmm_lock)) < 0) { + status = -EFAULT; + goto func_exit; + } + + /* Try to get a DSP chunk from the free list */ + node = get_free_region(size); + if (node != NULL) { + /* DSP chunk of given size is available. */ + DMM_ADDR_VIRTUAL(rsv_addr, node); + /* Calculate the number entries to use */ + rsv_size = size/PAGE_SIZE; + if (rsv_size < node->region_size) { + /* Mark remainder of free region */ + node[rsv_size].b_reserved = false; + node[rsv_size].region_size = + node->region_size - rsv_size; + } + /* get_region will return first fit chunk. But we only use what + is requested. */ + node->b_reserved = true; + node->region_size = rsv_size; + /* Return the chunk's starting address */ + *p_rsv_addr = rsv_addr; + } else + /*dSP chunk of given size is not available */ + status = -ENOMEM; + + mutex_unlock(dmm_lock); +func_exit: + return status; +} + + +/* + * ======== dmm_unreserve_memory ======== + * Purpose: + *Free a chunk of reserved DSP/IVA address space. + */ +int dmm_unreserve_memory(u32 rsv_addr, u32 *psize) +{ + struct map_page *chunk; + int status = 0; + + WARN_ON(mutex_lock_interruptible(dmm_lock)); + + /* Find the chunk containing the reserved address */ + chunk = get_mapped_region(rsv_addr); + if (chunk == NULL) + status = -ENXIO; + WARN_ON(status < 0); + if (status == 0) { + chunk->b_reserved = false; + *psize = chunk->region_size * PAGE_SIZE; + /* NOTE: We do NOT coalesce free regions here. + * Free regions are coalesced in get_region(), as it traverses + *the whole mapping table + */ + } + mutex_unlock(dmm_lock); + return status; +} + +/* + * ======== get_free_region ======== + * Purpose: + * Returns the requested free region + */ +static struct map_page *get_free_region(u32 size) +{ + struct map_page *curr_region = NULL; + u32 i = 0; + u32 region_size = 0; + u32 next_i = 0; + + if (p_virt_mapping_tbl == NULL) + return curr_region; + if (size > i_freesize) { + /* Find the largest free region + * (coalesce during the traversal) */ + while (i < table_size) { + region_size = p_virt_mapping_tbl[i].region_size; + next_i = i+region_size; + if (p_virt_mapping_tbl[i].b_reserved == false) { + /* Coalesce, if possible */ + if (next_i < table_size && + p_virt_mapping_tbl[next_i].b_reserved + == false) { + p_virt_mapping_tbl[i].region_size += + p_virt_mapping_tbl[next_i].region_size; + continue; + } + region_size *= PAGE_SIZE; + if (region_size > i_freesize) { + i_freeregion = i; + i_freesize = region_size; + } + } + i = next_i; + } + } + if (size <= i_freesize) { + curr_region = p_virt_mapping_tbl + i_freeregion; + i_freeregion += (size / PAGE_SIZE); + i_freesize -= size; + } + return curr_region; +} + +/* + * ======== get_mapped_region ======== + * Purpose: + * Returns the requestedmapped region + */ +static struct map_page *get_mapped_region(u32 addr) +{ + u32 i = 0; + struct map_page *curr_region = NULL; + + if (p_virt_mapping_tbl == NULL) + return curr_region; + + DMM_ADDR_TO_INDEX(i, addr); + if (i < table_size && (p_virt_mapping_tbl[i].b_reserved)) + curr_region = p_virt_mapping_tbl + i; + return curr_region; +} + +#ifdef DSP_DMM_DEBUG +int dmm_mem_map_dump(void) +{ + struct map_page *curNode = NULL; + u32 i; + u32 freemem = 0; + u32 bigsize = 0; + + WARN_ON(mutex_lock_interruptible(dmm_lock)); + + if (p_virt_mapping_tbl != NULL) { + for (i = 0; i < table_size; i += + p_virt_mapping_tbl[i].region_size) { + curNode = p_virt_mapping_tbl + i; + if (curNode->b_reserved == true) { + /*printk("RESERVED size = 0x%x, " + "Map size = 0x%x\n", + (curNode->region_size * PAGE_SIZE), + (curNode->b_mapped == false) ? 0 : + (curNode->mapped_size * PAGE_SIZE)); +*/ + } else { +/* printk("UNRESERVED size = 0x%x\n", + (curNode->region_size * PAGE_SIZE)); +*/ + freemem += (curNode->region_size * PAGE_SIZE); + if (curNode->region_size > bigsize) + bigsize = curNode->region_size; + } + } + } + printk(KERN_INFO "Total DSP VA FREE memory = %d Mbytes\n", + freemem/(1024*1024)); + printk(KERN_INFO "Total DSP VA USED memory= %d Mbytes \n", + (((table_size * PAGE_SIZE)-freemem))/(1024*1024)); + printk(KERN_INFO "DSP VA - Biggest FREE block = %d Mbytes \n\n", + (bigsize*PAGE_SIZE/(1024*1024))); + mutex_unlock(dmm_lock); + + return 0; +} +#endif diff --git a/drivers/dsp/syslink/procmgr/proc4430/dmm4430.h b/drivers/dsp/syslink/procmgr/proc4430/dmm4430.h new file mode 100755 index 000000000000..7d879f4fe123 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/dmm4430.h @@ -0,0 +1,50 @@ +/* + * dmm.h + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Copyright (C) 2005-2006 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +/* + * ======== dmm.h ======== + * Purpose: + *The Dynamic Memory Mapping(DMM) module manages the DSP Virtual address + *space that can be directly mapped to any MPU buffer or memory region + * + * Public Functions: + * + */ + +#ifndef DMM_4430_ +#define DMM_4430_ + +#include <linux/types.h> + +int dmm_reserve_memory(u32 size, u32 *p_rsv_addr); + +int dmm_unreserve_memory(u32 rsv_addr, u32 *psize); + +void dmm_destroy(void); + +void dmm_delete_tables(void); + +int dmm_create(void); + +void dmm_init(void); + +int dmm_create_tables(u32 addr, u32 size); + +#ifdef DSP_DMM_DEBUG +int dmm_mem_map_dump(void); +#endif +#endif/* DMM_4430_ */ diff --git a/drivers/dsp/syslink/procmgr/proc4430/ducatienabler.c b/drivers/dsp/syslink/procmgr/proc4430/ducatienabler.c new file mode 100644 index 000000000000..b416d20fdc90 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/ducatienabler.c @@ -0,0 +1,1378 @@ +/* + * ducatienabler.c + * + * Syslink driver support for TI OMAP processors. + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/gfp.h> +#include <linux/io.h> +#include <linux/module.h> +#include <asm/page.h> +#include <linux/kernel.h> +#include <linux/pagemap.h> + + +#include <generated/autoconf.h> +#include <asm/system.h> +#include <asm/atomic.h> +#include <linux/semaphore.h> +#include <linux/uaccess.h> +#include <asm/irq.h> +#include <linux/io.h> +#include <linux/syscalls.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/stddef.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/mm.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pagemap.h> +#include <asm/cacheflush.h> +#include <linux/dma-mapping.h> + +#include <linux/interrupt.h> +#include <plat/irqs.h> + +#include <syslink/ducatienabler.h> +#include <syslink/MMUAccInt.h> + + +#ifdef DEBUG_DUCATI_IPC +#define DPRINTK(fmt, args...) printk(KERN_INFO "%s: " fmt, __func__, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + + +#define base_ducati_l2_mmuPhys 0x55082000 + +/* + * Macro to define the physical memory address for the + * Ducati Base image. The 74Mb memory is preallocated + * during the make menuconfig. + * + */ +/* #define DUCATI_BASEIMAGE_PHYSICAL_ADDRESS 0x87200000 */ +#define DUCATI_BASEIMAGE_PHYSICAL_ADDRESS 0x9CF00000 + + +/* Attributes used to manage the DSP MMU page tables */ +struct pg_table_attrs { + struct sync_cs_object *hcs_object;/* Critical section object handle */ + u32 l1_base_pa; /* Physical address of the L1 PT */ + u32 l1_base_va; /* Virtual address of the L1 PT */ + u32 l1_size; /* Size of the L1 PT */ + u32 l1_tbl_alloc_pa; + /* Physical address of Allocated mem for L1 table. May not be aligned */ + u32 l1_tbl_alloc_va; + /* Virtual address of Allocated mem for L1 table. May not be aligned */ + u32 l1_tbl_alloc_sz; + /* Size of consistent memory allocated for L1 table. + * May not be aligned */ + u32 l2_base_pa; /* Physical address of the L2 PT */ + u32 l2_base_va; /* Virtual address of the L2 PT */ + u32 l2_size; /* Size of the L2 PT */ + u32 l2_tbl_alloc_pa; + /* Physical address of Allocated mem for L2 table. May not be aligned */ + u32 l2_tbl_alloc_va; + /* Virtual address of Allocated mem for L2 table. May not be aligned */ + u32 ls_tbl_alloc_sz; + /* Size of consistent memory allocated for L2 table. + * May not be aligned */ + u32 l2_num_pages; /* Number of allocated L2 PT */ + struct page_info *pg_info; +}; + +/* Attributes of L2 page tables for DSP MMU.*/ +struct page_info { + /* Number of valid PTEs in the L2 PT*/ + u32 num_entries; +}; + +enum pagetype { + SECTION = 0, + LARGE_PAGE = 1, + SMALL_PAGE = 2, + SUPER_SECTION = 3 +}; + +static struct pg_table_attrs *p_pt_attrs; +static u32 mmu_index_next; +static u32 base_ducati_l2_mmu; + +static u32 shm_phys_addr; +static u32 shm_virt_addr; + +static void bad_page_dump(u32 pa, struct page *pg) +{ + pr_emerg("DSPBRIDGE: MAP function: COUNT 0 FOR PA 0x%x\n", pa); + pr_emerg("Bad page state in process '%s'\n", current->comm); + BUG(); +} + +/*============================================ + * Print the DSP MMU Table Entries + */ +void dbg_print_ptes(bool ashow_inv_entries, bool ashow_repeat_entries) +{ + u32 pte_val; + u32 pte_size; + u32 last_sect = 0; + u32 this_sect = 0; + u32 cur_l1_entry; + u32 cur_l2_entry; + u32 pg_tbl_va; + u32 l1_base_va; + u32 l2_base_va = 0; + u32 l2_base_pa = 0; + + l1_base_va = p_pt_attrs->l1_base_va; + pg_tbl_va = l1_base_va; + + DPRINTK("\n*** Currently programmed PTEs : Max possible L1 Entries" + "[%d] ***\n", (p_pt_attrs->l1_size / sizeof(u32))); + + /* Walk all L1 entries, dump out info. Dive into L2 if necessary */ + for (cur_l1_entry = 0; cur_l1_entry < + (p_pt_attrs->l1_size / sizeof(u32)); cur_l1_entry++) { + /*pte_val = pL1PgTbl[cur_l1_entry];*/ + pte_val = *((u32 *)(pg_tbl_va + (cur_l1_entry * sizeof(u32)))); + pte_size = hw_mmu_pte_sizel1(pte_val); + + if (pte_size == HW_PAGE_SIZE_16MB) { + this_sect = hw_mmu_pte_phyaddr(pte_val, pte_size); + if (this_sect != last_sect) { + last_sect = this_sect; + DPRINTK("PTE L1 [16 MB] -> VA = " + "0x%x PA = 0x%x\n", + cur_l1_entry << 24, this_sect); + + } else if (ashow_repeat_entries != false) + DPRINTK(" {REPEAT}\n"); + } else if (pte_size == HW_PAGE_SIZE_1MB) { + this_sect = hw_mmu_pte_phyaddr(pte_val, pte_size); + if (this_sect != last_sect) { + + last_sect = this_sect; + + DPRINTK("PTE L1 [1 MB ] -> VA = " + "0x%x PA = 0x%x\n", + cur_l1_entry << 20, this_sect); + + } else if (ashow_repeat_entries != false) + DPRINTK(" {REPEAT}\n"); + + } else if (pte_size == HW_MMU_COARSE_PAGE_SIZE) { + /* Get the L2 data for this */ + DPRINTK("PTE L1 [L2 ] -> VA = " + "0x%x\n", cur_l1_entry << 20); + /* Get the L2 PA from the L1 PTE, and find corresponding L2 VA*/ + l2_base_pa = hw_mmu_pte_coarsel1(pte_val); + l2_base_va = l2_base_pa - p_pt_attrs->l2_base_pa + + p_pt_attrs->l2_base_va; + for (cur_l2_entry = 0; + cur_l2_entry < (HW_MMU_COARSE_PAGE_SIZE / sizeof(u32)); + cur_l2_entry++) { + pte_val = *((u32 *)(l2_base_va + + (cur_l2_entry * sizeof(u32)))); + pte_size = hw_mmu_pte_sizel2(pte_val); + if ((pte_size == HW_PAGE_SIZE_64KB) || + (pte_size == HW_PAGE_SIZE_4KB)) { + this_sect = hw_mmu_pte_phyaddr + (pte_val, pte_size); + if (this_sect != last_sect) { + last_sect = this_sect; + DPRINTK("PTE L2 [%s KB] ->" + "VA = 0x%x PA = 0x%x\n", + (pte_size == + HW_PAGE_SIZE_64KB) ? + "64" : "4", + ((cur_l1_entry << 20) + | (cur_l2_entry << 12)), + this_sect); + } else if (ashow_repeat_entries + != false) + + DPRINTK("{REPEAT}"); + } else if (ashow_inv_entries != false) { + + DPRINTK("PTE L2 [INVALID] -> VA = " + "0x%x", + ((cur_l1_entry << 20) | + (cur_l2_entry << 12))); + continue; + } + } + } else if (ashow_inv_entries != false) { + /* Entry is invalid (not set), skip it */ + DPRINTK("PTE L1 [INVALID] -> VA = 0x%x", + cur_l1_entry << 20); + continue; + } + } + /* Dump the TLB entries as well */ + DPRINTK("\n*** Currently programmed TLBs ***\n"); + hw_mmu_tlb_dump(base_ducati_l2_mmu, true); + DPRINTK("*** DSP MMU DUMP COMPLETED ***\n"); +} + +/*============================================ + * This function calculates PTE address (MPU virtual) to be updated + * It also manages the L2 page tables + */ +static int pte_set(u32 pa, u32 va, u32 size, struct hw_mmu_map_attrs_t *attrs) +{ + u32 i; + u32 pte_val; + u32 pte_addr_l1; + u32 pte_size; + u32 pg_tbl_va; /* Base address of the PT that will be updated */ + u32 l1_base_va; + /* Compiler warns that the next three variables might be used + * uninitialized in this function. Doesn't seem so. Working around, + * anyways. */ + u32 l2_base_va = 0; + u32 l2_base_pa = 0; + u32 l2_page_num = 0; + struct pg_table_attrs *pt = p_pt_attrs; + int status = 0; + DPRINTK("> pte_set ppg_table_attrs %x, pa %x, va %x, " + "size %x, attrs %x\n", (u32)pt, pa, va, size, (u32)attrs); + l1_base_va = pt->l1_base_va; + pg_tbl_va = l1_base_va; + if ((size == HW_PAGE_SIZE_64KB) || (size == HW_PAGE_SIZE_4KB)) { + /* Find whether the L1 PTE points to a valid L2 PT */ + pte_addr_l1 = hw_mmu_pte_addr_l1(l1_base_va, va); + if (pte_addr_l1 <= (pt->l1_base_va + pt->l1_size)) { + pte_val = *(u32 *)pte_addr_l1; + pte_size = hw_mmu_pte_sizel1(pte_val); + } else { + return -EINVAL; + } + /* FIX ME */ + /* TODO: ADD synchronication element*/ + /* sync_enter_cs(pt->hcs_object);*/ + if (pte_size == HW_MMU_COARSE_PAGE_SIZE) { + /* Get the L2 PA from the L1 PTE, and find + * corresponding L2 VA */ + l2_base_pa = hw_mmu_pte_coarsel1(pte_val); + l2_base_va = l2_base_pa - pt->l2_base_pa + + pt->l2_base_va; + l2_page_num = (l2_base_pa - pt->l2_base_pa) / + HW_MMU_COARSE_PAGE_SIZE; + } else if (pte_size == 0) { + /* L1 PTE is invalid. Allocate a L2 PT and + * point the L1 PTE to it */ + /* Find a free L2 PT. */ + for (i = 0; (i < pt->l2_num_pages) && + (pt->pg_info[i].num_entries != 0); i++) + ;; + if (i < pt->l2_num_pages) { + l2_page_num = i; + l2_base_pa = pt->l2_base_pa + (l2_page_num * + HW_MMU_COARSE_PAGE_SIZE); + l2_base_va = pt->l2_base_va + (l2_page_num * + HW_MMU_COARSE_PAGE_SIZE); + /* Endianness attributes are ignored for + * HW_MMU_COARSE_PAGE_SIZE */ + status = + hw_mmu_pte_set(l1_base_va, l2_base_pa, va, + HW_MMU_COARSE_PAGE_SIZE, attrs); + } else { + status = -ENOMEM; + } + } else { + /* Found valid L1 PTE of another size. + * Should not overwrite it. */ + status = -EINVAL; + } + if (status == 0) { + pg_tbl_va = l2_base_va; + if (size == HW_PAGE_SIZE_64KB) + pt->pg_info[l2_page_num].num_entries += 16; + else + pt->pg_info[l2_page_num].num_entries++; + + DPRINTK("L2 BaseVa %x, BasePa %x, " + "PageNum %x num_entries %x\n", l2_base_va, + l2_base_pa, l2_page_num, + pt->pg_info[l2_page_num].num_entries); + } +/* sync_leave_cs(pt->hcs_object);*/ + } + if (status == 0) { + DPRINTK("PTE pg_tbl_va %x, pa %x, va %x, size %x\n", + pg_tbl_va, pa, va, size); + DPRINTK("PTE endianism %x, element_size %x, " + "mixedSize %x\n", attrs->endianism, + attrs->element_size, attrs->mixedSize); + status = hw_mmu_pte_set(pg_tbl_va, pa, va, size, attrs); + if (status == RET_OK) + status = 0; + } + DPRINTK("< pte_set status %x\n", status); + return status; +} + + +/*============================================= + * This function calculates the optimum page-aligned addresses and sizes + * Caller must pass page-aligned values + */ +static int pte_update(u32 pa, u32 va, u32 size, + struct hw_mmu_map_attrs_t *map_attrs) +{ + u32 i; + u32 all_bits; + u32 pa_curr = pa; + u32 va_curr = va; + u32 num_bytes = size; + int status = 0; + u32 pg_size[] = {HW_PAGE_SIZE_16MB, HW_PAGE_SIZE_1MB, + HW_PAGE_SIZE_64KB, HW_PAGE_SIZE_4KB}; + DPRINTK("> pte_update pa %x, va %x, " + "size %x, map_attrs %x\n", pa, va, size, (u32)map_attrs); + while (num_bytes && (status == 0)) { + /* To find the max. page size with which both PA & VA are + * aligned */ + all_bits = pa_curr | va_curr; + DPRINTK("all_bits %x, pa_curr %x, va_curr %x, " + "num_bytes %x\n ", + all_bits, pa_curr, va_curr, num_bytes); + + for (i = 0; i < 4; i++) { + if ((num_bytes >= pg_size[i]) && ((all_bits & + (pg_size[i] - 1)) == 0)) { + DPRINTK("pg_size %x\n", pg_size[i]); + status = pte_set(pa_curr, + va_curr, pg_size[i], map_attrs); + pa_curr += pg_size[i]; + va_curr += pg_size[i]; + num_bytes -= pg_size[i]; + /* Don't try smaller sizes. Hopefully we have + * reached an address aligned to a bigger page + * size */ + break; + } + } + } + DPRINTK("< pte_update status %x num_bytes %x\n", status, num_bytes); + return status; +} + +/* + * ======== ducati_mem_unmap ======== + * Invalidate the PTEs for the DSP VA block to be unmapped. + * + * PTEs of a mapped memory block are contiguous in any page table + * So, instead of looking up the PTE address for every 4K block, + * we clear consecutive PTEs until we unmap all the bytes + */ +int ducati_mem_unmap(u32 da, u32 num_bytes) +{ + u32 L1_base_va; + u32 L2_base_va; + u32 L2_base_pa; + u32 L2_page_num; + u32 pte_val; + u32 pte_size; + u32 pte_count; + u32 pte_addr_l1; + u32 pte_addr_l2 = 0; + u32 rem_bytes; + u32 rem_bytes_l2; + u32 vaCurr; + struct page *pg = NULL; + int status = 0; + u32 temp; + u32 patemp = 0; + u32 pAddr; + u32 numof4Kpages = 0; + DPRINTK("> ducati_mem_unmap da 0x%x, " + "NumBytes 0x%x\n", da, num_bytes); + vaCurr = da; + rem_bytes = num_bytes; + rem_bytes_l2 = 0; + L1_base_va = p_pt_attrs->l1_base_va; + pte_addr_l1 = hw_mmu_pte_addr_l1(L1_base_va, vaCurr); + while (rem_bytes) { + u32 vaCurrOrig = vaCurr; + /* Find whether the L1 PTE points to a valid L2 PT */ + pte_addr_l1 = hw_mmu_pte_addr_l1(L1_base_va, vaCurr); + pte_val = *(u32 *)pte_addr_l1; + pte_size = hw_mmu_pte_sizel1(pte_val); + if (pte_size == HW_MMU_COARSE_PAGE_SIZE) { + /* + * Get the L2 PA from the L1 PTE, and find + * corresponding L2 VA + */ + L2_base_pa = hw_mmu_pte_coarsel1(pte_val); + L2_base_va = L2_base_pa - p_pt_attrs->l2_base_pa + + p_pt_attrs->l2_base_va; + L2_page_num = (L2_base_pa - p_pt_attrs->l2_base_pa) / + HW_MMU_COARSE_PAGE_SIZE; + /* + * Find the L2 PTE address from which we will start + * clearing, the number of PTEs to be cleared on this + * page, and the size of VA space that needs to be + * cleared on this L2 page + */ + pte_addr_l2 = hw_mmu_pte_addr_l2(L2_base_va, vaCurr); + pte_count = pte_addr_l2 & (HW_MMU_COARSE_PAGE_SIZE - 1); + pte_count = (HW_MMU_COARSE_PAGE_SIZE - pte_count) / + sizeof(u32); + if (rem_bytes < (pte_count * PAGE_SIZE)) + pte_count = rem_bytes / PAGE_SIZE; + + rem_bytes_l2 = pte_count * PAGE_SIZE; + DPRINTK("ducati_mem_unmap L2_base_pa %x, " + "L2_base_va %x pte_addr_l2 %x," + "rem_bytes_l2 %x\n", L2_base_pa, L2_base_va, + pte_addr_l2, rem_bytes_l2); + /* + * Unmap the VA space on this L2 PT. A quicker way + * would be to clear pte_count entries starting from + * pte_addr_l2. However, below code checks that we don't + * clear invalid entries or less than 64KB for a 64KB + * entry. Similar checking is done for L1 PTEs too + * below + */ + while (rem_bytes_l2) { + pte_val = *(u32 *)pte_addr_l2; + pte_size = hw_mmu_pte_sizel2(pte_val); + /* vaCurr aligned to pte_size? */ + if ((pte_size != 0) && (rem_bytes_l2 + >= pte_size) && + !(vaCurr & (pte_size - 1))) { + /* Collect Physical addresses from VA */ + pAddr = (pte_val & ~(pte_size - 1)); + if (pte_size == HW_PAGE_SIZE_64KB) + numof4Kpages = 16; + else + numof4Kpages = 1; + temp = 0; + while (temp++ < numof4Kpages) { + if (pfn_valid + (__phys_to_pfn + (patemp))) { + pg = phys_to_page + (pAddr); + if (page_count + (pg) < 1) { + bad_page_dump + (pAddr, pg); + } + SetPageDirty(pg); + page_cache_release(pg); + } + pAddr += HW_PAGE_SIZE_4KB; + } + if (hw_mmu_pte_clear(pte_addr_l2, + vaCurr, pte_size) == RET_OK) { + rem_bytes_l2 -= pte_size; + vaCurr += pte_size; + pte_addr_l2 += (pte_size >> 12) + * sizeof(u32); + } else { + status = -EFAULT; + goto EXIT_LOOP; + } + } else + status = -EFAULT; + } + if (rem_bytes_l2 != 0) { + status = -EFAULT; + goto EXIT_LOOP; + } + p_pt_attrs->pg_info[L2_page_num].num_entries -= + pte_count; + if (p_pt_attrs->pg_info[L2_page_num].num_entries + == 0) { + /* + * Clear the L1 PTE pointing to the + * L2 PT + */ + if (RET_OK != hw_mmu_pte_clear(L1_base_va, + vaCurrOrig, HW_MMU_COARSE_PAGE_SIZE)) { + status = -EFAULT; + goto EXIT_LOOP; + } + } + rem_bytes -= pte_count * PAGE_SIZE; + DPRINTK("ducati_mem_unmap L2_page_num %x, " + "num_entries %x, pte_count %x, status: 0x%x\n", + L2_page_num, + p_pt_attrs->pg_info[L2_page_num].num_entries, + pte_count, status); + } else + /* vaCurr aligned to pte_size? */ + /* pte_size = 1 MB or 16 MB */ + if ((pte_size != 0) && (rem_bytes >= pte_size) && + !(vaCurr & (pte_size - 1))) { + if (pte_size == HW_PAGE_SIZE_1MB) + numof4Kpages = 256; + else + numof4Kpages = 4096; + temp = 0; + /* Collect Physical addresses from VA */ + pAddr = (pte_val & ~(pte_size - 1)); + while (temp++ < numof4Kpages) { + pg = phys_to_page(pAddr); + if (page_count(pg) < 1) + bad_page_dump(pAddr, pg); + SetPageDirty(pg); + page_cache_release(pg); + pAddr += HW_PAGE_SIZE_4KB; + } + if (hw_mmu_pte_clear(L1_base_va, vaCurr, + pte_size) == RET_OK) { + rem_bytes -= pte_size; + vaCurr += pte_size; + } else { + status = -EFAULT; + goto EXIT_LOOP; + } + } else { + status = -EFAULT; + } + } + /* + * It is better to flush the TLB here, so that any stale old entries + * get flushed + */ +EXIT_LOOP: + hw_mmu_tlb_flushAll(base_ducati_l2_mmu); + DPRINTK("ducati_mem_unmap vaCurr %x, pte_addr_l1 %x " + "pte_addr_l2 %x\n", vaCurr, pte_addr_l1, pte_addr_l2); + DPRINTK("< ducati_mem_unmap status %x rem_bytes %x, " + "rem_bytes_l2 %x\n", status, rem_bytes, rem_bytes_l2); + return status; +} + + +/* + * ======== ducati_mem_virtToPhys ======== + * This funciton provides the translation from + * Remote virtual address to Physical address + */ + +inline u32 ducati_mem_virtToPhys(u32 da) +{ +#if 0 + /* FIXME: temp work around till L2MMU issue + * is resolved + */ + u32 *iopgd = iopgd_offset(ducati_iommu_ptr, da); + u32 phyaddress; + + if (*iopgd & IOPGD_TABLE) { + u32 *iopte = iopte_offset(iopgd, da); + if (*iopte & IOPTE_LARGE) { + phyaddress = *iopte & IOLARGE_MASK; + phyaddress |= (da & (IOLARGE_SIZE - 1)); + } else + phyaddress = (*iopte) & IOPAGE_MASK; + } else { + if ((*iopgd & IOPGD_SUPER) == IOPGD_SUPER) { + phyaddress = *iopgd & IOSUPER_MASK; + phyaddress |= (da & (IOSUPER_SIZE - 1)); + } else { + phyaddress = (*iopgd) & IOPGD_MASK; + phyaddress |= (da & (IOPGD_SIZE - 1)); + } + } +#endif + return da; +} + + +/* + * ======== user_va2pa ======== + * Purpose: + * This function walks through the Linux page tables to convert a userland + * virtual address to physical address + */ +u32 user_va2pa(struct mm_struct *mm, u32 address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(mm, address); + if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { + pmd = pmd_offset(pgd, address); + if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { + ptep = pte_offset_map(pmd, address); + if (ptep) { + pte = *ptep; + if (pte_present(pte)) + return pte & PAGE_MASK; + } + } + } + + return 0; +} + +/*============================================ + * This function maps MPU buffer to the DSP address space. It performs +* linear to physical address translation if required. It translates each +* page since linear addresses can be physically non-contiguous +* All address & size arguments are assumed to be page aligned (in proc.c) + * + */ +int ducati_mem_map(u32 mpu_addr, u32 ul_virt_addr, + u32 num_bytes, u32 map_attr) +{ + u32 attrs; + int status = 0; + struct hw_mmu_map_attrs_t hw_attrs; + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + struct task_struct *curr_task = current; + u32 write = 0; + u32 da = ul_virt_addr; + u32 pa = 0; + int pg_i = 0; + int pg_num = 0; + struct page *mappedPage, *pg; + int num_usr_pages = 0; + + DPRINTK("> WMD_BRD_MemMap pa %x, va %x, " + "size %x, map_attr %x\n", mpu_addr, ul_virt_addr, + num_bytes, map_attr); + if (num_bytes == 0) + return -EINVAL; + if (map_attr != 0) { + attrs = map_attr; + attrs |= DSP_MAPELEMSIZE32; + } else { + /* Assign default attributes */ + attrs = DSP_MAPVIRTUALADDR | DSP_MAPELEMSIZE32; + } + /* Take mapping properties */ + if (attrs & DSP_MAPBIGENDIAN) + hw_attrs.endianism = HW_BIG_ENDIAN; + else + hw_attrs.endianism = HW_LITTLE_ENDIAN; + + hw_attrs.mixedSize = (enum hw_mmu_mixed_size_t) + ((attrs & DSP_MAPMIXEDELEMSIZE) >> 2); + /* Ignore element_size if mixedSize is enabled */ + if (hw_attrs.mixedSize == 0) { + if (attrs & DSP_MAPELEMSIZE8) { + /* Size is 8 bit */ + hw_attrs.element_size = HW_ELEM_SIZE_8BIT; + } else if (attrs & DSP_MAPELEMSIZE16) { + /* Size is 16 bit */ + hw_attrs.element_size = HW_ELEM_SIZE_16BIT; + } else if (attrs & DSP_MAPELEMSIZE32) { + /* Size is 32 bit */ + hw_attrs.element_size = HW_ELEM_SIZE_32BIT; + } else if (attrs & DSP_MAPELEMSIZE64) { + /* Size is 64 bit */ + hw_attrs.element_size = HW_ELEM_SIZE_64BIT; + } else { + /* Mixedsize isn't enabled, so size can't be + * zero here */ + DPRINTK("WMD_BRD_MemMap: MMU element size is zero\n"); + return -EINVAL; + } + } + /* + * Do OS-specific user-va to pa translation. + * Combine physically contiguous regions to reduce TLBs. + * Pass the translated pa to PteUpdate. + */ + if ((attrs & DSP_MAPPHYSICALADDR)) { + status = pte_update(mpu_addr, ul_virt_addr, num_bytes, + &hw_attrs); + goto func_cont; + } + /* + * Important Note: mpu_addr is mapped from user application process + * to current process - it must lie completely within the current + * virtual memory address space in order to be of use to us here! + */ + down_read(&mm->mmap_sem); + vma = find_vma(mm, mpu_addr); + /* + * It is observed that under some circumstances, the user buffer is + * spread across several VMAs. So loop through and check if the entire + * user buffer is covered + */ + while ((vma) && (mpu_addr + num_bytes > vma->vm_end)) { + /* jump to the next VMA region */ + vma = find_vma(mm, vma->vm_end + 1); + } + if (!vma) { + status = -EINVAL; + up_read(&mm->mmap_sem); + goto func_cont; + } + if (vma->vm_flags & VM_IO) { + num_usr_pages = num_bytes / PAGE_SIZE; + /* Get the physical addresses for user buffer */ + for (pg_i = 0; pg_i < num_usr_pages; pg_i++) { + pa = user_va2pa(mm, mpu_addr); + if (!pa) { + status = -EFAULT; + pr_err("DSPBRIDGE: VM_IO mapping physical" + "address is invalid\n"); + break; + } + if (pfn_valid(__phys_to_pfn(pa))) { + pg = phys_to_page(pa); + get_page(pg); + if (page_count(pg) < 1) { + pr_err("Bad page in VM_IO buffer\n"); + bad_page_dump(pa, pg); + } + } + status = pte_set(pa, da, HW_PAGE_SIZE_4KB, &hw_attrs); + if (WARN_ON(status < 0)) + break; + mpu_addr += HW_PAGE_SIZE_4KB; + da += HW_PAGE_SIZE_4KB; + } + } else { + num_usr_pages = num_bytes / PAGE_SIZE; + if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) + write = 1; + + for (pg_i = 0; pg_i < num_usr_pages; pg_i++) { + pg_num = get_user_pages(curr_task, mm, mpu_addr, 1, + write, 1, &mappedPage, NULL); + if (pg_num > 0) { + if (page_count(mappedPage) < 1) { + pr_err("Bad page count after doing" + "get_user_pages on" + "user buffer\n"); + bad_page_dump(page_to_phys(mappedPage), + mappedPage); + } + status = pte_set(page_to_phys(mappedPage), da, + HW_PAGE_SIZE_4KB, &hw_attrs); + if (WARN_ON(status < 0)) + break; + da += HW_PAGE_SIZE_4KB; + mpu_addr += HW_PAGE_SIZE_4KB; + } else { + pr_err("DSPBRIDGE: get_user_pages FAILED," + "MPU addr = 0x%x," + "vma->vm_flags = 0x%lx," + "get_user_pages Err" + "Value = %d, Buffer" + "size=0x%x\n", mpu_addr, + vma->vm_flags, pg_num, + num_bytes); + status = -EFAULT; + break; + } + } + } + up_read(&mm->mmap_sem); +func_cont: + /* Don't propogate Linux or HW status to upper layers */ + if (status < 0) { + /* + * Roll out the mapped pages incase it failed in middle of + * mapping + */ + if (pg_i) + ducati_mem_unmap(ul_virt_addr, (pg_i * PAGE_SIZE)); + } + /* In any case, flush the TLB + * This is called from here instead from pte_update to avoid unnecessary + * repetition while mapping non-contiguous physical regions of a virtual + * region */ + hw_mmu_tlb_flushAll(base_ducati_l2_mmu); + WARN_ON(status < 0); + DPRINTK("< WMD_BRD_MemMap status %x\n", status); + return status; +} + + /*========================================= + * Decides a TLB entry size + * + */ +static int get_mmu_entry_size(u32 phys_addr, u32 size, enum pagetype *size_tlb, + u32 *entry_size) +{ + int status = 0; + bool page_align_4kb = false; + bool page_align_64kb = false; + bool page_align_1mb = false; + bool page_align_16mb = false; + + /* First check the page alignment*/ + if ((phys_addr % PAGE_SIZE_4KB) == 0) + page_align_4kb = true; + if ((phys_addr % PAGE_SIZE_64KB) == 0) + page_align_64kb = true; + if ((phys_addr % PAGE_SIZE_1MB) == 0) + page_align_1mb = true; + if ((phys_addr % PAGE_SIZE_16MB) == 0) + page_align_16mb = true; + + if ((!page_align_64kb) && (!page_align_1mb) && (!page_align_4kb)) { + status = -EINVAL; + goto error_exit; + } + if (status == 0) { + /* Now decide the entry size */ + if (size >= PAGE_SIZE_16MB) { + if (page_align_16mb) { + *size_tlb = SUPER_SECTION; + *entry_size = PAGE_SIZE_16MB; + } else if (page_align_1mb) { + *size_tlb = SECTION; + *entry_size = PAGE_SIZE_1MB; + } else if (page_align_64kb) { + *size_tlb = LARGE_PAGE; + *entry_size = PAGE_SIZE_64KB; + } else if (page_align_4kb) { + *size_tlb = SMALL_PAGE; + *entry_size = PAGE_SIZE_4KB; + } else { + status = -EINVAL; + goto error_exit; + } + } else if (size >= PAGE_SIZE_1MB && size < PAGE_SIZE_16MB) { + if (page_align_1mb) { + *size_tlb = SECTION; + *entry_size = PAGE_SIZE_1MB; + } else if (page_align_64kb) { + *size_tlb = LARGE_PAGE; + *entry_size = PAGE_SIZE_64KB; + } else if (page_align_4kb) { + *size_tlb = SMALL_PAGE; + *entry_size = PAGE_SIZE_4KB; + } else { + status = -EINVAL; + goto error_exit; + } + } else if (size > PAGE_SIZE_4KB && + size < PAGE_SIZE_1MB) { + if (page_align_64kb) { + *size_tlb = LARGE_PAGE; + *entry_size = PAGE_SIZE_64KB; + } else if (page_align_4kb) { + *size_tlb = SMALL_PAGE; + *entry_size = PAGE_SIZE_4KB; + } else { + status = -EINVAL; + goto error_exit; + } + } else if (size == PAGE_SIZE_4KB) { + if (page_align_4kb) { + *size_tlb = SMALL_PAGE; + *entry_size = PAGE_SIZE_4KB; + } else { + status = -EINVAL; + goto error_exit; + } + } else { + status = -EINVAL; + goto error_exit; + } + } + DPRINTK("< GetMMUEntrySize status %x\n", status); + return 0; +error_exit: + DPRINTK("< GetMMUEntrySize FAILED !!!!!!\n"); + return status; +} + +/*========================================= + * Add DSP MMU entries corresponding to given MPU-Physical address + * and DSP-virtual address + */ +static int add_dsp_mmu_entry(u32 *phys_addr, u32 *dsp_addr, + u32 size) +{ + u32 mapped_size = 0; + enum pagetype size_tlb = SECTION; + u32 entry_size = 0; + int status = 0; + u32 page_size = HW_PAGE_SIZE_1MB; + struct hw_mmu_map_attrs_t map_attrs = { HW_LITTLE_ENDIAN, + HW_ELEM_SIZE_16BIT, + HW_MMU_CPUES }; + + DPRINTK("Entered add_dsp_mmu_entry phys_addr = " + "0x%x, dsp_addr = 0x%x,size = 0x%x\n", + *phys_addr, *dsp_addr, size); + + while ((mapped_size < size) && (status == 0)) { + status = get_mmu_entry_size(*phys_addr, + (size - mapped_size), &size_tlb, &entry_size); + + if (size_tlb == SUPER_SECTION) + page_size = HW_PAGE_SIZE_16MB; + + else if (size_tlb == SECTION) + page_size = HW_PAGE_SIZE_1MB; + + else if (size_tlb == LARGE_PAGE) + page_size = HW_PAGE_SIZE_64KB; + + else if (size_tlb == SMALL_PAGE) + page_size = HW_PAGE_SIZE_4KB; + if (status == 0) { + hw_mmu_tlb_add((base_ducati_l2_mmu), + *phys_addr, *dsp_addr, + page_size, mmu_index_next++, + &map_attrs, HW_SET, HW_SET); + mapped_size += entry_size; + *phys_addr += entry_size; + *dsp_addr += entry_size; + if (mmu_index_next > 32) { + status = -EINVAL; + break; + } + /* Lock the base counter*/ + hw_mmu_numlocked_set(base_ducati_l2_mmu, + mmu_index_next); + + hw_mmu_victim_numset(base_ducati_l2_mmu, + mmu_index_next); + } + } + DPRINTK("Exited add_dsp_mmu_entryphys_addr = \ + 0x%x, dsp_addr = 0x%x\n", + *phys_addr, *dsp_addr); + return status; + } + + +/*============================================= + * Add DSP MMU entries corresponding to given MPU-Physical address + * and DSP-virtual address + * + */ +#if 0 +static int add_entry_ext(u32 *phys_addr, u32 *dsp_addr, + u32 size) +{ + u32 mapped_size = 0; + enum pagetype size_tlb = SECTION; + u32 entry_size = 0; + int status = 0; + u32 page_size = HW_PAGE_SIZE_1MB; + u32 flags = 0; + + flags = (DSP_MAPELEMSIZE32 | DSP_MAPLITTLEENDIAN | + DSP_MAPPHYSICALADDR); + while ((mapped_size < size) && (status == 0)) { + + /* get_mmu_entry_size fills the size_tlb and entry_size + based on alignment and size of memory to map + to DSP - size */ + status = get_mmu_entry_size(*phys_addr, + (size - mapped_size), + &size_tlb, + &entry_size); + + if (size_tlb == SUPER_SECTION) + page_size = HW_PAGE_SIZE_16MB; + else if (size_tlb == SECTION) + page_size = HW_PAGE_SIZE_1MB; + else if (size_tlb == LARGE_PAGE) + page_size = HW_PAGE_SIZE_64KB; + else if (size_tlb == SMALL_PAGE) + page_size = HW_PAGE_SIZE_4KB; + + if (status == 0) { + + ducati_mem_map(*phys_addr, + *dsp_addr, page_size, flags); + mapped_size += entry_size; + *phys_addr += entry_size; + *dsp_addr += entry_size; + } + } + return status; +} +#endif +/*================================ + * Initialize the Ducati MMU. + */ +int ducati_mmu_init(u32 a_phy_addr) +{ + int ret_val = 0; + u32 ducati_mmu_linear_addr = base_ducati_l2_mmu; + u32 reg_value = 0; + u32 phys_addr = 0; + u32 num_l4_entries; + u32 i = 0; + u32 map_attrs; + u32 num_l3_mem_entries = 0; + u32 virt_addr = 0; +#if 0 + u32 tiler_mapbeg = 0; + u32 tiler_totalsize = 0; +#endif + + num_l4_entries = (sizeof(l4_map) / sizeof(struct mmu_entry)); + num_l3_mem_entries = sizeof(l3_memory_regions) / + sizeof(struct memory_entry); + + DPRINTK("\n Programming Ducati MMU using linear address [0x%x]", + ducati_mmu_linear_addr); + + /* Disable the MMU & TWL */ + hw_mmu_disable(base_ducati_l2_mmu); + hw_mmu_twl_disable(base_ducati_l2_mmu); + + mmu_index_next = 0; + phys_addr = a_phy_addr; + + printk(KERN_ALERT " Programming Ducati memory regions\n"); + printk(KERN_ALERT "=========================================\n"); + for (i = 0; i < num_l3_mem_entries; i++) { + + printk(KERN_ALERT "VA = [0x%x] of size [0x%x] at PA = [0x%x]\n", + l3_memory_regions[i].ul_virt_addr, + l3_memory_regions[i].ul_size, phys_addr); + +#if 0 + /* OMAP4430 original code */ + if (l3_memory_regions[i].ul_virt_addr == DUCATI_SHARED_IPC_ADDR) + shm_phys_addr = phys_addr; + */ +#endif + /* OMAP4430 SDC code */ + /* Adjust below logic if using cacheable shared memory */ + if (l3_memory_regions[i].ul_virt_addr == \ + DUCATI_MEM_IPC_HEAP0_ADDR) { + shm_phys_addr = phys_addr; + } + virt_addr = l3_memory_regions[i].ul_virt_addr; + ret_val = add_dsp_mmu_entry(&phys_addr, &virt_addr, + (l3_memory_regions[i].ul_size)); + + if (WARN_ON(ret_val < 0)) + goto error_exit; + } + +#if 0 + /* OMAP4430 original code */ + tiler_mapbeg = L3_TILER_VIEW0_ADDR; + tiler_totalsize = DUCATIVA_TILER_VIEW0_LEN; + phys_addr = L3_TILER_VIEW0_ADDR; + + printk(KERN_ALERT " Programming TILER memory region at " + "[VA = 0x%x] of size [0x%x] at [PA = 0x%x]\n", + tiler_mapbeg, tiler_totalsize, phys_addr); + ret_val = add_entry_ext(&phys_addr, &tiler_mapbeg, tiler_totalsize); + if (WARN_ON(ret_val < 0)) + goto error_exit; +#endif + + map_attrs = 0x00000000; + map_attrs |= DSP_MAPLITTLEENDIAN; + map_attrs |= DSP_MAPPHYSICALADDR; + map_attrs |= DSP_MAPELEMSIZE32; + printk(KERN_ALERT " Programming Ducati L4 peripherals\n"); + printk(KERN_ALERT "=========================================\n"); + for (i = 0; i < num_l4_entries; i++) { + printk(KERN_INFO "PA [0x%x] VA [0x%x] size [0x%x]\n", + l4_map[i].ul_phy_addr, l4_map[i].ul_virt_addr, + l4_map[i].ul_size); + virt_addr = l4_map[i].ul_virt_addr; + phys_addr = l4_map[i].ul_phy_addr; + ret_val = add_dsp_mmu_entry(&phys_addr, + &virt_addr, (l4_map[i].ul_size)); + if (WARN_ON(ret_val < 0)) { + + DPRINTK("**** Failed to map Peripheral ****"); + DPRINTK("Phys addr [0x%x] Virt addr [0x%x] size [0x%x]", + l4_map[i].ul_phy_addr, l4_map[i].ul_virt_addr, + l4_map[i].ul_size); + DPRINTK(" Status [0x%x]", ret_val); + goto error_exit; + } + } + + /* Set the TTB to point to the L1 page table's physical address */ + hw_mmu_ttbset(ducati_mmu_linear_addr, p_pt_attrs->l1_base_pa); + + hw_mmu_event_enable(ducati_mmu_linear_addr, + (MMU_MMU_IRQENABLE_TLBMiss_MASK | + MMU_MMU_IRQENABLE_EMUMiss_MASK | + MMU_MMU_IRQENABLE_MultiHitFault_MASK)); + + hw_mmu_enable(ducati_mmu_linear_addr); + + /* MMU Debug Statements */ + reg_value = *((REG u32 *)(ducati_mmu_linear_addr + 0x1C)); + DPRINTK(" Ducati Enable Status [0x%x]\n", reg_value); + + reg_value = *((REG u32 *)(ducati_mmu_linear_addr + 0x40)); + DPRINTK(" Ducati TWL Status [0x%x]\n", reg_value); + + reg_value = *((REG u32 *)(ducati_mmu_linear_addr + 0x4C)); + DPRINTK(" Ducati TTB Address [0x%x]\n", reg_value); + + reg_value = *((REG u32 *)(ducati_mmu_linear_addr + 0x44)); + DPRINTK(" Ducati MMU Status [0x%x]\n", reg_value); + + /* Dump the MMU Entries */ + dbg_print_ptes(false, false); + + return 0; +error_exit: + return ret_val; +} + + +/*======================================== + * This sets up the Ducati processor MMU Page tables + * + */ +int init_mmu_page_attribs(u32 l1_size, u32 l1_allign, u32 ls_num_of_pages) +{ + u32 pg_tbl_pa; + u32 pg_tbl_va; + u32 align_size; + int status = 0; + + base_ducati_l2_mmu = (u32)ioremap(base_ducati_l2_mmuPhys, 0x4000); + p_pt_attrs = kmalloc(sizeof(struct pg_table_attrs), GFP_ATOMIC); + if (p_pt_attrs) + memset(p_pt_attrs, 0, sizeof(struct pg_table_attrs)); + else { + status = -ENOMEM; + goto func_end; + } + p_pt_attrs->l1_size = l1_size; + align_size = p_pt_attrs->l1_size; + /* Align sizes are expected to be power of 2 */ + /* we like to get aligned on L1 table size */ + pg_tbl_va = (u32)__get_dma_pages(GFP_KERNEL, + get_order(p_pt_attrs->l1_size)); + if (pg_tbl_va == (u32)NULL) { + DPRINTK("dma_alloc_coherent failed 0x%x\n", pg_tbl_va); + status = -ENOMEM; + goto error_exit; + } + pg_tbl_pa = __pa(pg_tbl_va); + /* Check if the PA is aligned for us */ + if ((pg_tbl_pa) & (align_size-1)) { + /* PA not aligned to page table size ,*/ + /* try with more allocation and align */ + free_pages(pg_tbl_va, get_order(p_pt_attrs->l1_size)); + /* we like to get aligned on L1 table size */ + pg_tbl_va = (u32)__get_dma_pages(GFP_KERNEL, + get_order(p_pt_attrs->l1_size * 2)); + if (pg_tbl_va == (u32)NULL) { + DPRINTK("__get_dma_pages failed 0x%x\n", pg_tbl_va); + status = -ENOMEM; + goto error_exit; + } + pg_tbl_pa = __pa(pg_tbl_va); + /* We should be able to get aligned table now */ + p_pt_attrs->l1_tbl_alloc_pa = pg_tbl_pa; + p_pt_attrs->l1_tbl_alloc_va = pg_tbl_va; + p_pt_attrs->l1_tbl_alloc_sz = p_pt_attrs->l1_size * 2; + /* Align the PA to the next 'align' boundary */ + p_pt_attrs->l1_base_pa = ((pg_tbl_pa) + (align_size-1)) & + (~(align_size-1)); + p_pt_attrs->l1_base_va = pg_tbl_va + (p_pt_attrs->l1_base_pa - + pg_tbl_pa); + } else { + /* We got aligned PA, cool */ + p_pt_attrs->l1_tbl_alloc_pa = pg_tbl_pa; + p_pt_attrs->l1_tbl_alloc_va = pg_tbl_va; + p_pt_attrs->l1_tbl_alloc_sz = p_pt_attrs->l1_size; + p_pt_attrs->l1_base_pa = pg_tbl_pa; + p_pt_attrs->l1_base_va = pg_tbl_va; + } + if (p_pt_attrs->l1_base_va) + memset((u8 *)p_pt_attrs->l1_base_va, 0x00, p_pt_attrs->l1_size); + p_pt_attrs->l2_num_pages = ls_num_of_pages; + p_pt_attrs->l2_size = HW_MMU_COARSE_PAGE_SIZE * + p_pt_attrs->l2_num_pages; + align_size = 4; /* Make it u32 aligned */ + /* we like to get aligned on L1 table size */ + pg_tbl_va = (u32)__get_dma_pages(GFP_KERNEL, + get_order(p_pt_attrs->l2_size)); + if (pg_tbl_va == (u32)NULL) { + DPRINTK("dma_alloc_coherent failed 0x%x\n", pg_tbl_va); + status = -ENOMEM; + goto error_exit; + } + pg_tbl_pa = __pa(pg_tbl_va); + p_pt_attrs->l2_tbl_alloc_pa = pg_tbl_pa; + p_pt_attrs->l2_tbl_alloc_va = pg_tbl_va; + p_pt_attrs->ls_tbl_alloc_sz = p_pt_attrs->l2_size; + p_pt_attrs->l2_base_pa = pg_tbl_pa; + p_pt_attrs->l2_base_va = pg_tbl_va; + if (p_pt_attrs->l2_base_va) + memset((u8 *)p_pt_attrs->l2_base_va, 0x00, p_pt_attrs->l2_size); + + p_pt_attrs->pg_info = kmalloc(sizeof(struct page_info), GFP_ATOMIC); + if (p_pt_attrs->pg_info) + memset(p_pt_attrs->pg_info, 0, sizeof(struct page_info)); + else { + DPRINTK("memory allocation fails for p_pt_attrs->pg_info "); + status = -ENOMEM; + goto error_exit; + } + DPRINTK("L1 pa %x, va %x, size %x\n L2 pa %x, va " + "%x, size %x\n", p_pt_attrs->l1_base_pa, + p_pt_attrs->l1_base_va, p_pt_attrs->l1_size, + p_pt_attrs->l2_base_pa, p_pt_attrs->l2_base_va, + p_pt_attrs->l2_size); + DPRINTK("p_pt_attrs %x L2 NumPages %x pg_info %x\n", + (u32)p_pt_attrs, p_pt_attrs->l2_num_pages, + (u32)p_pt_attrs->pg_info); + return 0; + +error_exit: + kfree(p_pt_attrs->pg_info); + if (p_pt_attrs->l1_base_va) { + free_pages(p_pt_attrs->l1_base_va, + get_order(p_pt_attrs->l1_tbl_alloc_sz)); + } + if (p_pt_attrs->l2_base_va) { + free_pages(p_pt_attrs->l2_base_va, + get_order(p_pt_attrs->ls_tbl_alloc_sz)); + } + WARN_ON(1); +func_end: + printk(KERN_ALERT "init_mmu_page_attribs FAILED !!!!!\n"); + return status; +} + +static irqreturn_t ducati_fault_handler(int irq, void *data) +{ + u32 irq_status; + + printk(KERN_INFO "********** DUCATI MMU FAULT DUMP******* \n"); + printk(KERN_INFO "**Check the Ducati code for invalid memory" + "access*** \n"); + + + hw_mmu_event_status(base_ducati_l2_mmu, &irq_status); + printk(KERN_INFO "IRQ status = 0x%x\n", irq_status); + + hw_mmu_fault_dump(base_ducati_l2_mmu); + + hw_mmu_eventack(base_ducati_l2_mmu, irq_status); + printk(KERN_INFO "IRQ status acknowledged\n"); + printk(KERN_INFO "Disable Ducati Events and MMU \n"); + hw_mmu_event_disable(base_ducati_l2_mmu, irq_status); + hw_mmu_disable(base_ducati_l2_mmu); + printk(KERN_INFO "**** DUCATI MMU FAULT DUMP end******* \n"); + return 0; +} + + +/*======================================== + * This sets up the Ducati processor + * + */ +int ducati_setup(void) +{ + int ret_val = 0; + + ret_val = request_irq(INT_44XX_DUCATI_MMU_IRQ, ducati_fault_handler, 0, + "ducati_mmu_fault", NULL); + + if (ret_val == 0) + printk(KERN_INFO "DUCATI MMU fault handler setup completed\n"); + else { + printk(KERN_INFO "DUCATI MMU fault handler setup" + "failed = 0x%x\n", ret_val); + } + + ret_val = init_mmu_page_attribs(0x8000, 14, 128); + if (WARN_ON(ret_val < 0)) + goto error_exit; + ret_val = ducati_mmu_init(DUCATI_BASEIMAGE_PHYSICAL_ADDRESS); + if (WARN_ON(ret_val < 0)) + goto error_exit; + return 0; +error_exit: + WARN_ON(1); + printk(KERN_ERR "DUCATI SETUP FAILED !!!!!\n"); + return ret_val; +} +EXPORT_SYMBOL(ducati_setup); + +/*============================================ + * De-Initialize the Ducati MMU and free the + * memory allocation for L1 and L2 pages + * + */ +void ducati_destroy(void) +{ + DPRINTK(" Freeing memory allocated in mmu_de_init\n"); + if (p_pt_attrs) { + if (p_pt_attrs->l2_tbl_alloc_va) { + free_pages(p_pt_attrs->l2_tbl_alloc_va, + get_order(p_pt_attrs->ls_tbl_alloc_sz)); + } + if (p_pt_attrs->l1_tbl_alloc_va) { + free_pages(p_pt_attrs->l1_tbl_alloc_va, + get_order(p_pt_attrs->l1_tbl_alloc_sz)); + } + kfree((void *)p_pt_attrs); + } + iounmap((unsigned int *)base_ducati_l2_mmu); + free_irq(INT_44XX_DUCATI_MMU_IRQ, NULL); + return; +} +EXPORT_SYMBOL(ducati_destroy); + +/*============================================ + * Returns the ducati virtual address for IPC shared memory + * + */ +u32 get_ducati_virt_mem(void) +{ + /*shm_virt_addr = (u32)ioremap(shm_phys_addr, DUCATI_SHARED_IPC_LEN);*/ + shm_virt_addr = (u32)ioremap(shm_phys_addr, DUCATI_MEM_IPC_SHMEM_LEN); + return shm_virt_addr; +} +EXPORT_SYMBOL(get_ducati_virt_mem); + +/*============================================ + * Unmaps the ducati virtual address for IPC shared memory + * + */ +void unmap_ducati_virt_mem(u32 shm_virt_addr) +{ + iounmap((unsigned int *) shm_virt_addr); + return; +} +EXPORT_SYMBOL(unmap_ducati_virt_mem); diff --git a/drivers/dsp/syslink/procmgr/proc4430/hw_mmu.c b/drivers/dsp/syslink/procmgr/proc4430/hw_mmu.c new file mode 100755 index 000000000000..ba0547456ab3 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/hw_mmu.c @@ -0,0 +1,661 @@ +/* + * hw_mbox.c + * + * Syslink driver support for OMAP Processors. + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +#include<linux/kernel.h> +#include<linux/module.h> + +#include <syslink/GlobalTypes.h> +#include <syslink/MMURegAcM.h> +#include <syslink/hw_defs.h> +#include <syslink/hw_mmu.h> + +#define MMU_BASE_VAL_MASK 0xFC00 +#define MMU_PAGE_MAX 3 +#define MMU_ELEMENTSIZE_MAX 3 +#define MMU_ADDR_MASK 0xFFFFF000 +#define MMU_TTB_MASK 0xFFFFC000 +#define MMU_SECTION_ADDR_MASK 0xFFF00000 +#define MMU_SSECTION_ADDR_MASK 0xFF000000 +#define MMU_PAGE_TABLE_MASK 0xFFFFFC00 +#define MMU_LARGE_PAGE_MASK 0xFFFF0000 +#define MMU_SMALL_PAGE_MASK 0xFFFFF000 + +#define MMU_LOAD_TLB 0x00000001 +#define NUM_TLB_ENTRIES 32 + + + +/* +* type: hw_mmu_pgsiz_t +* +* desc: Enumerated Type used to specify the MMU Page Size(SLSS) +* +* +*/ +enum hw_mmu_pgsiz_t { + HW_MMU_SECTION, + HW_MMU_LARGE_PAGE, + HW_MMU_SMALL_PAGE, + HW_MMU_SUPERSECTION + +}; + +/* +* function : mmu_flsh_entry +*/ + +static hw_status mmu_flsh_entry(const u32 base_address); + + /* +* function : mme_set_cam_entry +* +*/ + +static hw_status mme_set_cam_entry(const u32 base_address, + const u32 page_size, + const u32 preserve_bit, + const u32 valid_bit, + const u32 virt_addr_tag); + +/* +* function : mmu_set_ram_entry +*/ +static hw_status mmu_set_ram_entry(const u32 base_address, + const u32 physical_addr, + enum hw_endianism_t endianism, + enum hw_elemnt_siz_t element_size, + enum hw_mmu_mixed_size_t mixedSize); + +/* +* hw functions +* +*/ + +hw_status hw_mmu_enable(const u32 base_address) +{ + hw_status status = RET_OK; + + MMUMMU_CNTLMMUEnableWrite32(base_address, HW_SET); + + return status; +} +EXPORT_SYMBOL(hw_mmu_enable); + +hw_status hw_mmu_disable(const u32 base_address) +{ + hw_status status = RET_OK; + + MMUMMU_CNTLMMUEnableWrite32(base_address, HW_CLEAR); + + return status; +} +EXPORT_SYMBOL(hw_mmu_disable); + +hw_status hw_mmu_autoidle_en(const u32 base_address) +{ + hw_status status; + + status = mmu_sisconf_auto_idle_set32(base_address, HW_SET); + status = RET_OK; + return status; +} +EXPORT_SYMBOL(hw_mmu_autoidle_en); + +hw_status hw_mmu_nulck_set(const u32 base_address, u32 *num_lcked_entries) +{ + hw_status status = RET_OK; + + *num_lcked_entries = MMUMMU_LOCKBaseValueRead32(base_address); + + return status; +} +EXPORT_SYMBOL(hw_mmu_nulck_set); + + +hw_status hw_mmu_numlocked_set(const u32 base_address, u32 num_lcked_entries) +{ + hw_status status = RET_OK; + + MMUMMU_LOCKBaseValueWrite32(base_address, num_lcked_entries); + + return status; +} +EXPORT_SYMBOL(hw_mmu_numlocked_set); + + +hw_status hw_mmu_vctm_numget(const u32 base_address, u32 *vctm_entry_num) +{ + hw_status status = RET_OK; + + *vctm_entry_num = MMUMMU_LOCKCurrentVictimRead32(base_address); + + return status; +} +EXPORT_SYMBOL(hw_mmu_vctm_numget); + + +hw_status hw_mmu_victim_numset(const u32 base_address, u32 vctm_entry_num) +{ + hw_status status = RET_OK; + + mmu_lck_crnt_vctmwite32(base_address, vctm_entry_num); + + return status; +} +EXPORT_SYMBOL(hw_mmu_victim_numset); + +hw_status hw_mmu_tlb_flushAll(const u32 base_address) +{ + hw_status status = RET_OK; + + MMUMMU_GFLUSHGlobalFlushWrite32(base_address, HW_SET); + + return status; +} +EXPORT_SYMBOL(hw_mmu_tlb_flushAll); + +hw_status hw_mmu_eventack(const u32 base_address, u32 irq_mask) +{ + hw_status status = RET_OK; + + MMUMMU_IRQSTATUSWriteRegister32(base_address, irq_mask); + + return status; +} +EXPORT_SYMBOL(hw_mmu_eventack); + +hw_status hw_mmu_event_disable(const u32 base_address, u32 irq_mask) +{ + hw_status status = RET_OK; + u32 irqReg; + irqReg = MMUMMU_IRQENABLEReadRegister32(base_address); + + MMUMMU_IRQENABLEWriteRegister32(base_address, irqReg & ~irq_mask); + + return status; +} +EXPORT_SYMBOL(hw_mmu_event_disable); + +hw_status hw_mmu_event_enable(const u32 base_address, u32 irq_mask) +{ + hw_status status = RET_OK; + u32 irqReg; + + irqReg = MMUMMU_IRQENABLEReadRegister32(base_address); + + MMUMMU_IRQENABLEWriteRegister32(base_address, irqReg | irq_mask); + + return status; +} +EXPORT_SYMBOL(hw_mmu_event_enable); + +hw_status hw_mmu_event_status(const u32 base_address, u32 *irq_mask) +{ + hw_status status = RET_OK; + + *irq_mask = MMUMMU_IRQSTATUSReadRegister32(base_address); + + return status; +} +EXPORT_SYMBOL(hw_mmu_event_status); + +hw_status hw_mmu_flt_adr_rd(const u32 base_address, u32 *addr) +{ + hw_status status = RET_OK; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + /* read values from register */ + *addr = MMUMMU_FAULT_ADReadRegister32(base_address); + + return status; +} +EXPORT_SYMBOL(hw_mmu_flt_adr_rd); + + +hw_status hw_mmu_ttbset(const u32 base_address, u32 ttb_phys_addr) +{ + hw_status status = RET_OK; + u32 loadTTB; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + + loadTTB = ttb_phys_addr & ~0x7FUL; + /* write values to register */ + MMUMMU_TTBWriteRegister32(base_address, loadTTB); + + return status; +} +EXPORT_SYMBOL(hw_mmu_ttbset); + +hw_status hw_mmu_twl_enable(const u32 base_address) +{ + hw_status status = RET_OK; + + MMUMMU_CNTLTWLEnableWrite32(base_address, HW_SET); + + return status; +} +EXPORT_SYMBOL(hw_mmu_twl_enable); + +hw_status hw_mmu_twl_disable(const u32 base_address) +{ + hw_status status = RET_OK; + + MMUMMU_CNTLTWLEnableWrite32(base_address, HW_CLEAR); + + return status; +} +EXPORT_SYMBOL(hw_mmu_twl_disable); + + +hw_status hw_mmu_tlb_flush(const u32 base_address, + u32 virtual_addr, + u32 page_size) +{ + hw_status status = RET_OK; + u32 virt_addr_tag; + enum hw_mmu_pgsiz_t pg_sizeBits; + + switch (page_size) { + case HW_PAGE_SIZE_4KB: + pg_sizeBits = HW_MMU_SMALL_PAGE; + break; + + case HW_PAGE_SIZE_64KB: + pg_sizeBits = HW_MMU_LARGE_PAGE; + break; + + case HW_PAGE_SIZE_1MB: + pg_sizeBits = HW_MMU_SECTION; + break; + + case HW_PAGE_SIZE_16MB: + pg_sizeBits = HW_MMU_SUPERSECTION; + break; + + default: + return RET_FAIL; + } + + /* Generate the 20-bit tag from virtual address */ + virt_addr_tag = ((virtual_addr & MMU_ADDR_MASK) >> 12); + + mme_set_cam_entry(base_address, pg_sizeBits, 0, 0, virt_addr_tag); + + mmu_flsh_entry(base_address); + + return status; +} +EXPORT_SYMBOL(hw_mmu_tlb_flush); + + +hw_status hw_mmu_tlb_add(const u32 base_address, + u32 physical_addr, + u32 virtual_addr, + u32 page_size, + u32 entryNum, + struct hw_mmu_map_attrs_t *map_attrs, + enum hw_set_clear_t preserve_bit, + enum hw_set_clear_t valid_bit) +{ + hw_status status = RET_OK; + u32 lockReg; + u32 virt_addr_tag; + enum hw_mmu_pgsiz_t mmu_pg_size; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + CHECK_INPUT_RANGE_MIN0(page_size, MMU_PAGE_MAX, RET_PARAM_OUT_OF_RANGE, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + CHECK_INPUT_RANGE_MIN0(map_attrs->element_size, + MMU_ELEMENTSIZE_MAX, RET_PARAM_OUT_OF_RANGE, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + + switch (page_size) { + case HW_PAGE_SIZE_4KB: + mmu_pg_size = HW_MMU_SMALL_PAGE; + break; + + case HW_PAGE_SIZE_64KB: + mmu_pg_size = HW_MMU_LARGE_PAGE; + break; + + case HW_PAGE_SIZE_1MB: + mmu_pg_size = HW_MMU_SECTION; + break; + + case HW_PAGE_SIZE_16MB: + mmu_pg_size = HW_MMU_SUPERSECTION; + break; + + default: + return RET_FAIL; + } + + lockReg = mmu_lckread_reg_32(base_address); + + /* Generate the 20-bit tag from virtual address */ + virt_addr_tag = ((virtual_addr & MMU_ADDR_MASK) >> 12); + + /* Write the fields in the CAM Entry Register */ + mme_set_cam_entry(base_address, mmu_pg_size, preserve_bit, valid_bit, + virt_addr_tag); + + /* Write the different fields of the RAM Entry Register */ + /* endianism of the page,Element Size of the page (8, 16, 32, 64 bit) */ + mmu_set_ram_entry(base_address, physical_addr, + map_attrs->endianism, map_attrs->element_size, map_attrs->mixedSize); + + /* Update the MMU Lock Register */ + /* currentVictim between lockedBaseValue and (MMU_Entries_Number - 1) */ + mmu_lck_crnt_vctmwite32(base_address, entryNum); + + /* Enable loading of an entry in TLB by writing 1 into LD_TLB_REG + register */ + mmu_ld_tlbwrt_reg32(base_address, MMU_LOAD_TLB); + + + mmu_lck_write_reg32(base_address, lockReg); + + return status; +} +EXPORT_SYMBOL(hw_mmu_tlb_add); + + + +hw_status hw_mmu_pte_set(const u32 pg_tbl_va, + u32 physical_addr, + u32 virtual_addr, + u32 page_size, + struct hw_mmu_map_attrs_t *map_attrs) +{ + hw_status status = RET_OK; + u32 pte_addr, pte_val; + long int num_entries = 1; + + switch (page_size) { + + case HW_PAGE_SIZE_4KB: + pte_addr = hw_mmu_pte_addr_l2(pg_tbl_va, virtual_addr & + MMU_SMALL_PAGE_MASK); + pte_val = ((physical_addr & MMU_SMALL_PAGE_MASK) | + (map_attrs->endianism << 9) | + (map_attrs->element_size << 4) | + (map_attrs->mixedSize << 11) | 2 + ); + break; + + case HW_PAGE_SIZE_64KB: + num_entries = 16; + pte_addr = hw_mmu_pte_addr_l2(pg_tbl_va, virtual_addr & + MMU_LARGE_PAGE_MASK); + pte_val = ((physical_addr & MMU_LARGE_PAGE_MASK) | + (map_attrs->endianism << 9) | + (map_attrs->element_size << 4) | + (map_attrs->mixedSize << 11) | 1 + ); + break; + + case HW_PAGE_SIZE_1MB: + pte_addr = hw_mmu_pte_addr_l1(pg_tbl_va, virtual_addr & + MMU_SECTION_ADDR_MASK); + pte_val = ((((physical_addr & MMU_SECTION_ADDR_MASK) | + (map_attrs->endianism << 15) | + (map_attrs->element_size << 10) | + (map_attrs->mixedSize << 17)) & + ~0x40000) | 0x2 + ); + break; + + case HW_PAGE_SIZE_16MB: + num_entries = 16; + pte_addr = hw_mmu_pte_addr_l1(pg_tbl_va, virtual_addr & + MMU_SSECTION_ADDR_MASK); + pte_val = (((physical_addr & MMU_SSECTION_ADDR_MASK) | + (map_attrs->endianism << 15) | + (map_attrs->element_size << 10) | + (map_attrs->mixedSize << 17) + ) | 0x40000 | 0x2 + ); + break; + + case HW_MMU_COARSE_PAGE_SIZE: + pte_addr = hw_mmu_pte_addr_l1(pg_tbl_va, virtual_addr & + MMU_SECTION_ADDR_MASK); + pte_val = (physical_addr & MMU_PAGE_TABLE_MASK) | 1; + break; + + default: + return RET_FAIL; + } + + while (--num_entries >= 0) + ((u32 *)pte_addr)[num_entries] = pte_val; + + + return status; +} +EXPORT_SYMBOL(hw_mmu_pte_set); + +hw_status hw_mmu_pte_clear(const u32 pg_tbl_va, + u32 virtual_addr, + u32 pg_size) +{ + hw_status status = RET_OK; + u32 pte_addr; + long int num_entries = 1; + + switch (pg_size) { + case HW_PAGE_SIZE_4KB: + pte_addr = hw_mmu_pte_addr_l2(pg_tbl_va, + virtual_addr & MMU_SMALL_PAGE_MASK); + break; + + case HW_PAGE_SIZE_64KB: + num_entries = 16; + pte_addr = hw_mmu_pte_addr_l2(pg_tbl_va, + virtual_addr & MMU_LARGE_PAGE_MASK); + break; + + case HW_PAGE_SIZE_1MB: + case HW_MMU_COARSE_PAGE_SIZE: + pte_addr = hw_mmu_pte_addr_l1(pg_tbl_va, + virtual_addr & MMU_SECTION_ADDR_MASK); + break; + + case HW_PAGE_SIZE_16MB: + num_entries = 16; + pte_addr = hw_mmu_pte_addr_l1(pg_tbl_va, + virtual_addr & MMU_SSECTION_ADDR_MASK); + break; + + default: + return RET_FAIL; + } + + while (--num_entries >= 0) + ((u32 *)pte_addr)[num_entries] = 0; + + return status; +} +EXPORT_SYMBOL(hw_mmu_pte_clear); + +/* +* function: mmu_flsh_entry +*/ +static hw_status mmu_flsh_entry(const u32 base_address) +{ + hw_status status = RET_OK; + u32 flushEntryData = 0x1; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + + /* write values to register */ + MMUMMU_FLUSH_ENTRYWriteRegister32(base_address, flushEntryData); + + return status; +} +EXPORT_SYMBOL(mmu_flsh_entry); +/* +* function : mme_set_cam_entry +*/ +static hw_status mme_set_cam_entry(const u32 base_address, + const u32 page_size, + const u32 preserve_bit, + const u32 valid_bit, + const u32 virt_addr_tag) +{ + hw_status status = RET_OK; + u32 mmuCamReg; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + + mmuCamReg = (virt_addr_tag << 12); + mmuCamReg = (mmuCamReg) | (page_size) | (valid_bit << 2) + | (preserve_bit << 3); + + /* write values to register */ + MMUMMU_CAMWriteRegister32(base_address, mmuCamReg); + + return status; +} +EXPORT_SYMBOL(mme_set_cam_entry); +/* +* function: mmu_set_ram_entry +*/ +static hw_status mmu_set_ram_entry(const u32 base_address, + const u32 physical_addr, + enum hw_endianism_t endianism, + enum hw_elemnt_siz_t element_size, + enum hw_mmu_mixed_size_t mixedSize) +{ + hw_status status = RET_OK; + u32 mmuRamReg; + + /*Check the input Parameters*/ + CHECK_INPUT_PARAM(base_address, 0, RET_BAD_NULL_PARAM, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + CHECK_INPUT_RANGE_MIN0(element_size, MMU_ELEMENTSIZE_MAX, + RET_PARAM_OUT_OF_RANGE, + RES_MMU_BASE + RES_INVALID_INPUT_PARAM); + + + mmuRamReg = (physical_addr & MMU_ADDR_MASK); + mmuRamReg = (mmuRamReg) | ((endianism << 9) | (element_size << 7) + | (mixedSize << 6)); + + /* write values to register */ + MMUMMU_RAMWriteRegister32(base_address, mmuRamReg); + + return status; + +} +EXPORT_SYMBOL(mmu_set_ram_entry); + +u32 hw_mmu_fault_dump(const u32 base_address) +{ + u32 reg; + + reg = MMUMMU_FAULT_ADReadRegister32(base_address); + printk(KERN_INFO "Fault Address Address = 0x%x\n", reg); + reg = MMUMMU_FAULT_PCReadRegister32(base_address); + printk(KERN_INFO "Fault PC Register Address = 0x%x\n", reg); + reg = MMUMMU_FAULT_STATUSReadRegister32(base_address); + printk(KERN_INFO "Fault PC address doesn't show right value in DUCATI" + "because of HW limitation\n"); + printk(KERN_INFO "Fault Status Register = 0x%x\n", reg); + reg = MMUMMU_FAULT_EMUAddressReadRegister32(base_address); + printk(KERN_INFO "Fault EMU Address = 0x%x\n", reg); + return 0; +} +EXPORT_SYMBOL(hw_mmu_fault_dump); + +long hw_mmu_tlb_dump(const u32 base_address, bool shw_inv_entries) +{ + u32 i; + u32 lockSave; + u32 cam; + u32 ram; + + + /* Save off the lock register contents, + we'll restore it when we are done */ + + lockSave = mmu_lckread_reg_32(base_address); + + printk(KERN_INFO "TLB locked entries = %u, current victim = %u\n", + ((lockSave & MMU_MMU_LOCK_BaseValue_MASK) + >> MMU_MMU_LOCK_BaseValue_OFFSET), + ((lockSave & MMU_MMU_LOCK_CurrentVictim_MASK) + >> MMU_MMU_LOCK_CurrentVictim_OFFSET)); + printk(KERN_INFO "=============================================\n"); + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + mmu_lck_crnt_vctmwite32(base_address, i); + cam = MMUMMU_CAMReadRegister32(base_address); + ram = MMUMMU_RAMReadRegister32(base_address); + + if ((cam & 0x4) != 0) { + printk(KERN_INFO "TLB Entry [0x%2x]: VA = 0x%8x " + "PA = 0x%8x Protected = 0x%1x\n", + i, (cam & MMU_ADDR_MASK), (ram & MMU_ADDR_MASK), + (cam & 0x8) ? 1 : 0); + + } else if (shw_inv_entries != false) + printk(KERN_ALERT "TLB Entry [0x%x]: <INVALID>\n", i); + } + mmu_lck_write_reg32(base_address, lockSave); + return RET_OK; +} +EXPORT_SYMBOL(hw_mmu_tlb_dump); + +u32 hw_mmu_pte_phyaddr(u32 pte_val, u32 pte_size) +{ + u32 ret_val = 0; + + switch (pte_size) { + + case HW_PAGE_SIZE_4KB: + ret_val = pte_val & MMU_SMALL_PAGE_MASK; + break; + case HW_PAGE_SIZE_64KB: + ret_val = pte_val & MMU_LARGE_PAGE_MASK; + break; + + case HW_PAGE_SIZE_1MB: + ret_val = pte_val & MMU_SECTION_ADDR_MASK; + break; + case HW_PAGE_SIZE_16MB: + ret_val = pte_val & MMU_SSECTION_ADDR_MASK; + break; + default: + /* Invalid */ + break; + + } + + return ret_val; +} +EXPORT_SYMBOL(hw_mmu_pte_phyaddr); diff --git a/drivers/dsp/syslink/procmgr/proc4430/proc4430.c b/drivers/dsp/syslink/procmgr/proc4430/proc4430.c new file mode 100644 index 000000000000..528a7aaf44a7 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/proc4430.c @@ -0,0 +1,1053 @@ +/* + * proc4430.c + * + * Syslink driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/delay.h> + +/* Module level headers */ +#include "../procdefs.h" +#include "../processor.h" +#include <procmgr.h> +#include "../procmgr_drvdefs.h" +#include "proc4430.h" +#include "dmm4430.h" +#include <syslink/multiproc.h> +#include <syslink/ducatienabler.h> +#include <syslink/platform_mem.h> +#include <syslink/atomic_linux.h> + +#define DUCATI_DMM_START_ADDR 0xa0000000 +#define DUCATI_DMM_POOL_SIZE 0x6000000 + +#define SYS_M3 2 +#define APP_M3 3 +#define CORE_PRM_BASE OMAP2_L4_IO_ADDRESS(0x4a306700) +#define CORE_CM2_DUCATI_CLKSTCTRL OMAP2_L4_IO_ADDRESS(0x4A008900) +#define CORE_CM2_DUCATI_CLKCTRL OMAP2_L4_IO_ADDRESS(0x4A008920) +#define RM_MPU_M3_RSTCTRL_OFFSET 0x210 +#define RM_MPU_M3_RSTST_OFFSET 0x214 +#define RM_MPU_M3_RST1 0x1 +#define RM_MPU_M3_RST2 0x2 +#define RM_MPU_M3_RST3 0x4 + +#define OMAP4430PROC_MODULEID (u16) 0xbbec + +/* Macro to make a correct module magic number with refCount */ +#define OMAP4430PROC_MAKE_MAGICSTAMP(x) ((OMAP4430PROC_MODULEID << 12u) | (x)) + +/*OMAP4430 Module state object */ +struct proc4430_module_object { + u32 config_size; + /* Size of configuration structure */ + struct proc4430_config cfg; + /* OMAP4430 configuration structure */ + struct proc4430_config def_cfg; + /* Default module configuration */ + struct proc4430_params def_inst_params; + /* Default parameters for the OMAP4430 instances */ + void *proc_handles[MULTIPROC_MAXPROCESSORS]; + /* Processor handle array. */ + struct mutex *gate_handle; + /* void * of gate to be used for local thread safety */ + atomic_t ref_count; +}; + +/* + OMAP4430 instance object. + */ +struct proc4430_object { + struct proc4430_params params; + /* Instance parameters (configuration values) */ +}; + + +/* ================================= + * Globals + * ================================= + */ +/* + OMAP4430 state object variable + */ + +static struct proc4430_module_object proc4430_state = { + .config_size = sizeof(struct proc4430_config), + .gate_handle = NULL, + .def_inst_params.num_mem_entries = 0u, + .def_inst_params.mem_entries = NULL, + .def_inst_params.reset_vector_mem_entry = 0 +}; + + +/* ================================= + * APIs directly called by applications + * ================================= + */ +/* + * Function to get the default configuration for the OMAP4430 + * module. + * + * This function can be called by the application to get their + * configuration parameter to proc4430_setup filled in by the + * OMAP4430 module with the default parameters. If the user + * does not wish to make any change in the default parameters, this + * API is not required to be called. + */ +void proc4430_get_config(struct proc4430_config *cfg) +{ + BUG_ON(cfg == NULL); + memcpy(cfg, &(proc4430_state.def_cfg), + sizeof(struct proc4430_config)); +} +EXPORT_SYMBOL(proc4430_get_config); + +/* + * Function to setup the OMAP4430 module. + * + * This function sets up the OMAP4430 module. This function + * must be called before any other instance-level APIs can be + * invoked. + * Module-level configuration needs to be provided to this + * function. If the user wishes to change some specific config + * parameters, then proc4430_get_config can be called to get the + * configuration filled with the default values. After this, only + * the required configuration values can be changed. If the user + * does not wish to make any change in the default parameters, the + * application can simply call proc4430_setup with NULL + * parameters. The default parameters would get automatically used. + */ +int proc4430_setup(struct proc4430_config *cfg) +{ + int retval = 0; + struct proc4430_config tmp_cfg; + atomic_cmpmask_and_set(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(0)); + + if (atomic_inc_return(&proc4430_state.ref_count) != + OMAP4430PROC_MAKE_MAGICSTAMP(1)) { + return 1; + } + + if (cfg == NULL) { + proc4430_get_config(&tmp_cfg); + cfg = &tmp_cfg; + } + + dmm_create(); + dmm_create_tables(DUCATI_DMM_START_ADDR, DUCATI_DMM_POOL_SIZE); + + /* Create a default gate handle for local module protection. */ + proc4430_state.gate_handle = + kmalloc(sizeof(struct mutex), GFP_KERNEL); + if (proc4430_state.gate_handle == NULL) { + retval = -ENOMEM; + goto error; + } + + mutex_init(proc4430_state.gate_handle); + + /* Initialize the name to handles mapping array. */ + memset(&proc4430_state.proc_handles, 0, + (sizeof(void *) * MULTIPROC_MAXPROCESSORS)); + + /* Copy the user provided values into the state object. */ + memcpy(&proc4430_state.cfg, cfg, + sizeof(struct proc4430_config)); + + return 0; + +error: + atomic_dec_return(&proc4430_state.ref_count); + dmm_delete_tables(); + dmm_destroy(); + + return retval; +} +EXPORT_SYMBOL(proc4430_setup); + +/* + * Function to destroy the OMAP4430 module. + * + * Once this function is called, other OMAP4430 module APIs, + * except for the proc4430_get_config API cannot be called + * anymore. + */ +int proc4430_destroy(void) +{ + int retval = 0; + u16 i; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + retval = -ENODEV; + goto exit; + } + if (!(atomic_dec_return(&proc4430_state.ref_count) + == OMAP4430PROC_MAKE_MAGICSTAMP(0))) { + + retval = 1; + goto exit; + } + + /* Check if any OMAP4430 instances have not been + * deleted so far. If not,delete them. + */ + + for (i = 0; i < MULTIPROC_MAXPROCESSORS; i++) { + if (proc4430_state.proc_handles[i] == NULL) + continue; + proc4430_delete(&(proc4430_state.proc_handles[i])); + } + + /* Check if the gate_handle was created internally. */ + if (proc4430_state.gate_handle != NULL) { + mutex_destroy(proc4430_state.gate_handle); + kfree(proc4430_state.gate_handle); + } + +exit: + return retval; +} +EXPORT_SYMBOL(proc4430_destroy); + +/*================================================= + * Function to initialize the parameters for this Processor + * instance. + */ +void proc4430_params_init(void *handle, struct proc4430_params *params) +{ + struct proc4430_object *proc_object = (struct proc4430_object *)handle; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_params_init failed " + "Module not initialized"); + return; + } + + if (WARN_ON(params == NULL)) { + printk(KERN_ERR "proc4430_params_init failed " + "Argument of type proc4430_params * " + "is NULL"); + return; + } + + if (handle == NULL) + memcpy(params, &(proc4430_state.def_inst_params), + sizeof(struct proc4430_params)); + else + memcpy(params, &(proc_object->params), + sizeof(struct proc4430_params)); +} +EXPORT_SYMBOL(proc4430_params_init); + +/*=================================================== + *Function to create an instance of this Processor. + * + */ +void *proc4430_create(u16 proc_id, const struct proc4430_params *params) +{ + struct processor_object *handle = NULL; + struct proc4430_object *object = NULL; + + BUG_ON(!IS_VALID_PROCID(proc_id)); + BUG_ON(params == NULL); + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_create failed " + "Module not initialized"); + goto error; + } + + /* Enter critical section protection. */ + WARN_ON(mutex_lock_interruptible(proc4430_state.gate_handle)); + if (proc4430_state.proc_handles[proc_id] != NULL) { + handle = proc4430_state.proc_handles[proc_id]; + goto func_end; + } else { + handle = (struct processor_object *) + vmalloc(sizeof(struct processor_object)); + if (WARN_ON(handle == NULL)) + goto func_end; + + handle->proc_fxn_table.attach = &proc4430_attach; + handle->proc_fxn_table.detach = &proc4430_detach; + handle->proc_fxn_table.start = &proc4430_start; + handle->proc_fxn_table.stop = &proc4430_stop; + handle->proc_fxn_table.read = &proc4430_read; + handle->proc_fxn_table.write = &proc4430_write; + handle->proc_fxn_table.control = &proc4430_control; + handle->proc_fxn_table.translateAddr = + &proc4430_translate_addr; + handle->proc_fxn_table.map = &proc4430_map; + handle->proc_fxn_table.unmap = &proc4430_unmap; + handle->proc_fxn_table.procinfo = &proc4430_proc_info; + handle->proc_fxn_table.virt_to_phys = &proc4430_virt_to_phys; + handle->state = PROC_MGR_STATE_UNKNOWN; + handle->object = vmalloc(sizeof(struct proc4430_object)); + handle->proc_id = proc_id; + object = (struct proc4430_object *)handle->object; + if (params != NULL) { + /* Copy params into instance object. */ + memcpy(&(object->params), (void *)params, + sizeof(struct proc4430_params)); + } + if ((params != NULL) && (params->mem_entries != NULL) + && (params->num_mem_entries > 0)) { + /* Allocate memory for, and copy mem_entries table*/ + object->params.mem_entries = vmalloc(sizeof(struct + proc4430_mem_entry) * + params->num_mem_entries); + memcpy(object->params.mem_entries, + params->mem_entries, + (sizeof(struct proc4430_mem_entry) * + params->num_mem_entries)); + } + handle->boot_mode = PROC_MGR_BOOTMODE_NOLOAD; + /* Set the handle in the state object. */ + proc4430_state.proc_handles[proc_id] = handle; + } + +func_end: + mutex_unlock(proc4430_state.gate_handle); +error: + return (void *)handle; +} +EXPORT_SYMBOL(proc4430_create); + +/*================================================= + * Function to delete an instance of this Processor. + * + * The user provided pointer to the handle is reset after + * successful completion of this function. + * + */ +int proc4430_delete(void **handle_ptr) +{ + int retval = 0; + struct proc4430_object *object = NULL; + struct processor_object *handle; + + BUG_ON(handle_ptr == NULL); + BUG_ON(*handle_ptr == NULL); + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_delete failed " + "Module not initialized"); + return -ENODEV; + } + + handle = (struct processor_object *)(*handle_ptr); + BUG_ON(!IS_VALID_PROCID(handle->proc_id)); + /* Enter critical section protection. */ + WARN_ON(mutex_lock_interruptible(proc4430_state.gate_handle)); + /* Reset handle in PwrMgr handle array. */ + proc4430_state.proc_handles[handle->proc_id] = NULL; + /* Free memory used for the OMAP4430 object. */ + if (handle->object != NULL) { + object = (struct proc4430_object *)handle->object; + if (object->params.mem_entries != NULL) { + vfree(object->params.mem_entries); + object->params.mem_entries = NULL; + } + vfree(handle->object); + handle->object = NULL; + } + /* Free memory used for the Processor object. */ + vfree(handle); + *handle_ptr = NULL; + /* Leave critical section protection. */ + mutex_unlock(proc4430_state.gate_handle); + return retval; +} +EXPORT_SYMBOL(proc4430_delete); + +/*=================================================== + * Function to open a handle to an instance of this Processor. This + * function is called when access to the Processor is required from + * a different process. + */ +int proc4430_open(void **handle_ptr, u16 proc_id) +{ + int retval = 0; + + BUG_ON(handle_ptr == NULL); + BUG_ON(!IS_VALID_PROCID(proc_id)); + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_open failed " + "Module not initialized"); + return -ENODEV; + } + + /* Initialize return parameter handle. */ + *handle_ptr = NULL; + + /* Check if the PwrMgr exists and return the handle if found. */ + if (proc4430_state.proc_handles[proc_id] == NULL) { + retval = -ENODEV; + goto func_exit; + } else + *handle_ptr = proc4430_state.proc_handles[proc_id]; +func_exit: + return retval; +} +EXPORT_SYMBOL(proc4430_open); + +/*=============================================== + * Function to close a handle to an instance of this Processor. + * + */ +int proc4430_close(void *handle) +{ + int retval = 0; + + BUG_ON(handle == NULL); + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_close failed " + "Module not initialized"); + return -ENODEV; + } + + /* nothing to be done for now */ + return retval; +} +EXPORT_SYMBOL(proc4430_close); + +/* ================================= + * APIs called by Processor module (part of function table interface) + * ================================= + */ +/*================================ + * Function to initialize the slave processor + * + */ +int proc4430_attach(void *handle, struct processor_attach_params *params) +{ + int retval = 0; + + struct processor_object *proc_handle = NULL; + struct proc4430_object *object = NULL; + u32 map_count = 0; + u32 i; + memory_map_info map_info; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_attach failed" + "Module not initialized"); + return -ENODEV; + } + + if (WARN_ON(handle == NULL)) { + printk(KERN_ERR "proc4430_attach failed" + "Driver handle is NULL"); + return -EINVAL; + } + + if (WARN_ON(params == NULL)) { + printk(KERN_ERR "proc4430_attach failed" + "Argument processor_attach_params * is NULL"); + return -EINVAL; + } + + proc_handle = (struct processor_object *)handle; + + object = (struct proc4430_object *)proc_handle->object; + /* Return memory information in params. */ + for (i = 0; (i < object->params.num_mem_entries); i++) { + /* If the configured master virtual address is invalid, get the + * actual address by mapping the physical address into master + * kernel memory space. + */ + if ((object->params.mem_entries[i].master_virt_addr == (u32)-1) + && (object->params.mem_entries[i].shared == true)) { + map_info.src = object->params.mem_entries[i].phys_addr; + map_info.size = object->params.mem_entries[i].size; + map_info.is_cached = false; + retval = platform_mem_map(&map_info); + if (retval != 0) { + printk(KERN_ERR "proc4430_attach failed\n"); + return -EFAULT; + } + map_count++; + object->params.mem_entries[i].master_virt_addr = + map_info.dst; + params->mem_entries[i].addr + [PROC_MGR_ADDRTYPE_MASTERKNLVIRT] = + map_info.dst; + params->mem_entries[i].addr + [PROC_MGR_ADDRTYPE_SLAVEVIRT] = + (object->params.mem_entries[i].slave_virt_addr); + /* User virtual will be filled by user side. For now, + fill in the physical address so that it can be used + by mmap to remap this region into user-space */ + params->mem_entries[i].addr + [PROC_MGR_ADDRTYPE_MASTERUSRVIRT] = \ + object->params.mem_entries[i].phys_addr; + params->mem_entries[i].size = + object->params.mem_entries[i].size; + } + } + params->num_mem_entries = map_count; + return retval; +} + + +/*========================================== + * Function to detach from the Processor. + * + */ +int proc4430_detach(void *handle) +{ + struct processor_object *proc_handle = NULL; + struct proc4430_object *object = NULL; + u32 i; + memory_unmap_info unmap_info; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + + printk(KERN_ERR "proc4430_detach failed " + "Module not initialized"); + return -ENODEV; + } + + if (WARN_ON(handle == NULL)) { + printk(KERN_ERR "proc4430_detach failed " + "Argument Driverhandle is NULL"); + return -EINVAL; + } + + proc_handle = (struct processor_object *)handle; + object = (struct proc4430_object *)proc_handle->object; + for (i = 0; (i < object->params.num_mem_entries); i++) { + if ((object->params.mem_entries[i].master_virt_addr > 0) + && (object->params.mem_entries[i].shared == true)) { + unmap_info.addr = + object->params.mem_entries[i].master_virt_addr; + unmap_info.size = object->params.mem_entries[i].size; + platform_mem_unmap(&unmap_info); + object->params.mem_entries[i].master_virt_addr = + (u32)-1; + } + } + return 0; +} + +/*========================================== + * Function to start the slave processor + * + * Start the slave processor running from its entry point. + * Depending on the boot mode, this involves configuring the boot + * address and releasing the slave from reset. + * + */ +int proc4430_start(void *handle, u32 entry_pt, + struct processor_start_params *start_params) +{ + u32 reg; + int counter = 10; + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + + printk(KERN_ERR "proc4430_start failed " + "Module not initialized"); + return -ENODEV; + } + + /*FIXME: Remove handle and entry_pt if not used */ + if (WARN_ON(start_params == NULL)) { + printk(KERN_ERR "proc4430_start failed " + "Argument processor_start_params * is NULL"); + return -EINVAL; + } + + reg = __raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET); + printk(KERN_INFO "proc4430_start: Reset Status [0x%x]", reg); + reg = __raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + printk(KERN_INFO "proc4430_start: Reset Control [0x%x]", reg); + + switch (start_params->params->proc_id) { + case SYS_M3: + /* Module is managed automatically by HW */ + __raw_writel(0x01, CORE_CM2_DUCATI_CLKCTRL); + /* Enable the M3 clock */ + __raw_writel(0x02, CORE_CM2_DUCATI_CLKSTCTRL); + do { + reg = __raw_readl(CORE_CM2_DUCATI_CLKSTCTRL); + if (reg & 0x100) { + printk(KERN_INFO "M3 clock enabled:" + "CORE_CM2_DUCATI_CLKSTCTRL = 0x%x\n", reg); + break; + } + msleep(1); + } while (--counter); + if (counter == 0) { + printk(KERN_ERR "FAILED TO ENABLE DUCATI M3 CLOCK !\n"); + return -EFAULT; + } + /* Check that releasing resets would indeed be effective */ + reg = __raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + if (reg != 7) { + printk(KERN_ERR "proc4430_start: Resets in not proper state!\n"); + __raw_writel(0x7, + CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + } + + /* De-assert RST3, and clear the Reset status */ + printk(KERN_INFO "De-assert RST3\n"); + __raw_writel(0x3, CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + while (!(__raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET) + & 0x4)) + ; + printk(KERN_INFO "RST3 released!"); + __raw_writel(0x4, CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET); + ducati_setup(); + + /* De-assert RST1, and clear the Reset status */ + printk(KERN_INFO "De-assert RST1\n"); + __raw_writel(0x2, CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + while (!(__raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET) + & 0x1)) + ; + printk(KERN_INFO "RST1 released!"); + __raw_writel(0x1, CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET); + break; + case APP_M3: + /* De-assert RST2, and clear the Reset status */ + printk(KERN_INFO "De-assert RST2\n"); + __raw_writel(0x0, CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + while (!(__raw_readl(CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET) + & 0x2)) + ; + printk(KERN_INFO "RST2 released!"); + __raw_writel(0x2, CORE_PRM_BASE + RM_MPU_M3_RSTST_OFFSET); + break; + default: + printk(KERN_ERR "proc4430_start: ERROR input\n"); + break; + } + return 0; +} + + +/* + * Function to stop the slave processor + * + * Stop the execution of the slave processor. Depending on the boot + * mode, this may result in placing the slave processor in reset. + * + * @param handle void * to the Processor instance + * + * @sa proc4430_start, OMAP3530_halResetCtrl + */ +int +proc4430_stop(void *handle, struct processor_stop_params *stop_params) +{ + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_stop failed " + "Module not initialized"); + return -ENODEV; + } + switch (stop_params->params->proc_id) { + case SYS_M3: + ducati_destroy(); + printk(KERN_INFO "Assert RST1 and RST2 and RST3\n"); + __raw_writel(0x7, CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + /* Disable the M3 clock */ + __raw_writel(0x01, CORE_CM2_DUCATI_CLKSTCTRL); + break; + case APP_M3: + printk(KERN_INFO "Assert RST2\n"); + __raw_writel(0x2, CORE_PRM_BASE + RM_MPU_M3_RSTCTRL_OFFSET); + break; + default: + printk(KERN_ERR "proc4430_stop: ERROR input\n"); + break; + } + return 0; +} + + +/*============================================== + * Function to read from the slave processor's memory. + * + * Read from the slave processor's memory and copy into the + * provided buffer. + */ +int proc4430_read(void *handle, u32 proc_addr, u32 *num_bytes, + void *buffer) +{ + int retval = 0; + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_read failed " + "Module not initialized"); + return -ENODEV; + } + + /* TODO */ + return retval; +} + + +/*============================================== + * Function to write into the slave processor's memory. + * + * Read from the provided buffer and copy into the slave + * processor's memory. + * + */ +int proc4430_write(void *handle, u32 proc_addr, u32 *num_bytes, + void *buffer) +{ + int retval = 0; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_write failed " + "Module not initialized"); + return -ENODEV; + } + + /* TODO */ + return retval; +} + + +/*========================================================= + * Function to perform device-dependent operations. + * + * Performs device-dependent control operations as exposed by this + * implementation of the Processor module. + */ +int proc4430_control(void *handle, int cmd, void *arg) +{ + int retval = 0; + + /*FIXME: Remove handle,etc if not used */ + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_control failed " + "Module not initialized"); + return -ENODEV; + } + + return retval; +} + + +/*===================================================== + * Function to translate between two types of address spaces. + * + * Translate between the specified address spaces. + */ +int proc4430_translate_addr(void *handle, + void **dst_addr, enum proc_mgr_addr_type dst_addr_type, + void *src_addr, enum proc_mgr_addr_type src_addr_type) +{ + int retval = 0; + struct processor_object *proc_handle = NULL; + struct proc4430_object *object = NULL; + struct proc4430_mem_entry *entry = NULL; + bool found = false; + u32 fm_addr_base = (u32)NULL; + u32 to_addr_base = (u32)NULL; + u32 i; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_translate_addr failed " + "Module not initialized"); + retval = -ENODEV; + goto error_exit; + } + + if (WARN_ON(handle == NULL)) { + retval = -EINVAL; + goto error_exit; + } + if (WARN_ON(dst_addr == NULL)) { + retval = -EINVAL; + goto error_exit; + } + if (WARN_ON(dst_addr_type > PROC_MGR_ADDRTYPE_ENDVALUE)) { + retval = -EINVAL; + goto error_exit; + } + if (WARN_ON(src_addr == NULL)) { + retval = -EINVAL; + goto error_exit; + } + if (WARN_ON(src_addr_type > PROC_MGR_ADDRTYPE_ENDVALUE)) { + retval = -EINVAL; + goto error_exit; + } + + proc_handle = (struct processor_object *)handle; + object = (struct proc4430_object *)proc_handle->object; + *dst_addr = NULL; + for (i = 0 ; i < object->params.num_mem_entries ; i++) { + entry = &(object->params.mem_entries[i]); + fm_addr_base = + (src_addr_type == PROC_MGR_ADDRTYPE_MASTERKNLVIRT) ? + entry->master_virt_addr : entry->slave_virt_addr; + to_addr_base = + (dst_addr_type == PROC_MGR_ADDRTYPE_MASTERKNLVIRT) ? + entry->master_virt_addr : entry->slave_virt_addr; + /* Determine whether which way to convert */ + if (((u32)src_addr < (fm_addr_base + entry->size)) && + ((u32)src_addr >= fm_addr_base)) { + found = true; + *dst_addr = (void *)(((u32)src_addr - fm_addr_base) + + to_addr_base); + break; + } + } + + /* This check must not be removed even with build optimize. */ + if (WARN_ON(found == false)) { + /*Failed to translate address. */ + retval = -ENXIO; + goto error_exit; + } + return 0; + +error_exit: + return retval; +} + + +/*================================================= + * Function to map slave address to host address space + * + * Map the provided slave address to master address space. This + * function also maps the specified address to slave MMU space. + */ +int proc4430_map(void *handle, u32 proc_addr, + u32 size, u32 *mapped_addr, u32 *mapped_size, u32 map_attribs) +{ + int retval = 0; + u32 da_align; + u32 da; + u32 va_align; + u32 size_align; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_map failed " + "Module not initialized"); + retval = -ENODEV; + goto error_exit; + } + + /*FIXME: Remove handle,etc if not used */ + + /* FIX ME: Temporary work around until the dynamic memory mapping + * for Tiler address space is available + */ + if ((map_attribs & DSP_MAPTILERADDR)) { + da_align = user_va2pa(current->mm, proc_addr); + *mapped_addr = (da_align | (proc_addr & (PAGE_SIZE - 1))); + printk(KERN_INFO "proc4430_map -tiler: user_va2pa: mapped_addr" + "= 0x%x\n", *mapped_addr); + return 0; + } + + /* Calculate the page-aligned PA, VA and size */ + va_align = PG_ALIGN_LOW(proc_addr, PAGE_SIZE); + size_align = PG_ALIGN_HIGH(size + (u32)proc_addr - va_align, PAGE_SIZE); + + dmm_reserve_memory(size_align, &da); + da_align = PG_ALIGN_LOW((u32)da, PAGE_SIZE); + retval = ducati_mem_map(va_align, da_align, size_align, map_attribs); + + /* Mapped address = MSB of DA | LSB of VA */ + *mapped_addr = (da_align | (proc_addr & (PAGE_SIZE - 1))); + +error_exit: + return retval; +} + +/*================================================= + * Function to unmap slave address to host address space + * + * UnMap the provided slave address to master address space. This + * function also unmaps the specified address to slave MMU space. + */ +int proc4430_unmap(void *handle, u32 mapped_addr) +{ + int da_align; + int ret_val = 0; + int size_align; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_map failed " + "Module not initialized"); + ret_val = -1; + goto error_exit; + } + + /*FIXME: Remove handle,etc if not used */ + + da_align = PG_ALIGN_LOW((u32)mapped_addr, PAGE_SIZE); + ret_val = dmm_unreserve_memory(da_align, &size_align); + if (WARN_ON(ret_val < 0)) + goto error_exit; + ret_val = ducati_mem_unmap(da_align, size_align); + if (WARN_ON(ret_val < 0)) + goto error_exit; + return 0; + +error_exit: + printk(KERN_WARNING "proc4430_unmap failed !!!!\n"); + return ret_val; +} + +/*================================================= + * Function to return list of translated mem entries + * + * This function takes the remote processor address as + * an input and returns the mapped Page entries in the + * buffer passed + */ +int proc4430_virt_to_phys(void *handle, u32 da, u32 *mapped_entries, + u32 num_of_entries) +{ + int da_align; + int i; + int ret_val = 0; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_virt_to_phys failed " + "Module not initialized"); + ret_val = -EFAULT; + goto error_exit; + } + + if (handle == NULL || mapped_entries == NULL || num_of_entries == 0) { + ret_val = -EFAULT; + goto error_exit; + } + da_align = PG_ALIGN_LOW((u32)da, PAGE_SIZE); + for (i = 0; i < num_of_entries; i++) { + mapped_entries[i] = ducati_mem_virtToPhys(da_align); + da_align += PAGE_SIZE; + } + return 0; + +error_exit: + printk(KERN_WARNING "proc4430_virtToPhys failed !!!!\n"); + return ret_val; +} + + +/*================================================= + * Function to return PROC4430 mem_entries info + * + */ +int proc4430_proc_info(void *handle, struct proc_mgr_proc_info *procinfo) +{ + struct processor_object *proc_handle = NULL; + struct proc4430_object *object = NULL; + struct proc4430_mem_entry *entry = NULL; + int i; + + if (atomic_cmpmask_and_lt(&proc4430_state.ref_count, + OMAP4430PROC_MAKE_MAGICSTAMP(0), + OMAP4430PROC_MAKE_MAGICSTAMP(1)) + == true) { + printk(KERN_ERR "proc4430_proc_info failed " + "Module not initialized"); + goto error_exit; + } + + if (WARN_ON(handle == NULL)) + goto error_exit; + if (WARN_ON(procinfo == NULL)) + goto error_exit; + + proc_handle = (struct processor_object *)handle; + + object = (struct proc4430_object *)proc_handle->object; + + for (i = 0 ; i < object->params.num_mem_entries ; i++) { + entry = &(object->params.mem_entries[i]); + procinfo->mem_entries[i].addr[PROC_MGR_ADDRTYPE_MASTERKNLVIRT] + = entry->master_virt_addr; + procinfo->mem_entries[i].addr[PROC_MGR_ADDRTYPE_SLAVEVIRT] + = entry->slave_virt_addr; + procinfo->mem_entries[i].size = entry->size; + } + procinfo->num_mem_entries = object->params.num_mem_entries; + procinfo->boot_mode = proc_handle->boot_mode; + return 0; + +error_exit: + printk(KERN_WARNING "proc4430_proc_info failed !!!!\n"); + return -EFAULT; +} diff --git a/drivers/dsp/syslink/procmgr/proc4430/proc4430.h b/drivers/dsp/syslink/procmgr/proc4430/proc4430.h new file mode 100755 index 000000000000..5903daeadaa3 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/proc4430.h @@ -0,0 +1,147 @@ +/* + * proc4430.h + * + * Syslink driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + + + +#ifndef _SYSLINK_PROC_4430_H_ +#define _SYSLINK_PROC_4430_H_ + + +/* Module headers */ +#include <procmgr.h> +#include "../procdefs.h" +#include <linux/types.h> + +/* + Module configuration structure. + */ +struct proc4430_config { + struct mutex *gate_handle; + /* void * of gate to be used for local thread safety */ +}; + +/* + Memory entry for slave memory map configuration + */ +struct proc4430_mem_entry { + char name[PROCMGR_MAX_STRLEN]; + /* Name identifying the memory region. */ + u32 phys_addr; + /* Physical address of the memory region. */ + u32 slave_virt_addr; + /* Slave virtual address of the memory region. */ + u32 master_virt_addr; + /* Master virtual address of the memory region. If specified as -1, + * the master virtual address is assumed to be invalid, and shall be + * set internally within the Processor module. */ + u32 size; + /* Size (in bytes) of the memory region. */ + bool shared; + /* Flag indicating whether the memory region is shared between master + * and slave. */ +}; + +/* + Configuration parameters specific to this processor. + */ +struct proc4430_params { + int num_mem_entries; + /* Number of memory regions to be configured. */ + struct proc4430_mem_entry *mem_entries; + /* Array of information structures for memory regions + * to be configured. */ + u32 reset_vector_mem_entry; + /* Index of the memory entry within the mem_entries array, + * which is the resetVector memory region. */ +}; + + +/* Function to initialize the slave processor */ +int proc4430_attach(void *handle, struct processor_attach_params *params); + +/* Function to finalize the slave processor */ +int proc4430_detach(void *handle); + +/* Function to start the slave processor */ +int proc4430_start(void *handle, u32 entry_pt, + struct processor_start_params *params); + +/* Function to start the stop processor */ +int proc4430_stop(void *handle, + struct processor_stop_params *params); + +/* Function to read from the slave processor's memory. */ +int proc4430_read(void *handle, u32 proc_addr, u32 *num_bytes, + void *buffer); + +/* Function to write into the slave processor's memory. */ +int proc4430_write(void *handle, u32 proc_addr, u32 *num_bytes, + void *buffer); + +/* Function to perform device-dependent operations. */ +int proc4430_control(void *handle, int cmd, void *arg); + +/* Function to translate between two types of address spaces. */ +int proc4430_translate_addr(void *handle, void **dst_addr, + enum proc_mgr_addr_type dst_addr_type, + void *src_addr, enum proc_mgr_addr_type src_addr_type); + +/* Function to map slave address to host address space */ +int proc4430_map(void *handle, u32 proc_addr, u32 size, u32 *mapped_addr, + u32 *mapped_size, u32 map_attribs); + +/* Function to unmap the slave address to host address space */ +int proc4430_unmap(void *handle, u32 mapped_addr); + +/* Function to retrive physical address translations */ +int proc4430_virt_to_phys(void *handle, u32 da, u32 *mapped_entries, + u32 num_of_entries); + +/* ================================================= + * APIs + * ================================================= + */ + +/* Function to get the default configuration for the OMAP4430PROC module */ +void proc4430_get_config(struct proc4430_config *cfg); + +/* Function to setup the OMAP4430PROC module. */ +int proc4430_setup(struct proc4430_config *cfg); + +/* Function to destroy the OMAP4430PROC module. */ +int proc4430_destroy(void); + +/* Function to initialize the parameters for this processor instance. */ +void proc4430_params_init(void *handle, + struct proc4430_params *params); + +/* Function to create an instance of this processor. */ +void *proc4430_create(u16 proc_id, const struct proc4430_params *params); + +/* Function to delete an instance of this processor. */ +int proc4430_delete(void **handle_ptr); + +/* Function to open an instance of this processor. */ +int proc4430_open(void **handle_ptr, u16 proc_id); + +/* Function to close an instance of this processor. */ +int proc4430_close(void *handle); + +/* Function to get the proc info */ +int proc4430_proc_info(void *handle, struct proc_mgr_proc_info *procinfo); + +#endif diff --git a/drivers/dsp/syslink/procmgr/proc4430/proc4430_drv.c b/drivers/dsp/syslink/procmgr/proc4430/proc4430_drv.c new file mode 100755 index 000000000000..f451ad0d5083 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/proc4430_drv.c @@ -0,0 +1,400 @@ +/* + * proc4430_drv.c + * + * Syslink driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <generated/autoconf.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> + + +/* Module headers */ +#include "proc4430.h" +#include "proc4430_drvdefs.h" + + + +/** ============================================================================ + * Macros and types + * ============================================================================ + */ +#define PROC4430_NAME "syslink-proc4430" + +static char *driver_name = PROC4430_NAME; + +static s32 driver_major; + +static s32 driver_minor; + +struct proc_4430_dev { + struct cdev cdev; +}; + +static struct proc_4430_dev *proc_4430_device; + +static struct class *proc_4430_class; + + + +/** ============================================================================ + * Forward declarations of internal functions + * ============================================================================ + */ +/* Linux driver function to open the driver object. */ +static int proc4430_drv_open(struct inode *inode, struct file *filp); + +/* Linux driver function to close the driver object. */ +static int proc4430_drv_release(struct inode *inode, struct file *filp); + +/* Linux driver function to invoke the APIs through ioctl. */ +static int proc4430_drv_ioctl(struct inode *inode, + struct file *filp, unsigned int cmd, + unsigned long args); + +/* Linux driver function to map memory regions to user space. */ +static int proc4430_drv_mmap(struct file *filp, + struct vm_area_struct *vma); + +/* Module initialization function for Linux driver. */ +static int __init proc4430_drv_initializeModule(void); + +/* Module finalization function for Linux driver. */ +static void __exit proc4430_drv_finalizeModule(void); + + + +/** ============================================================================ + * Globals + * ============================================================================ + */ + +/* + File operations table for PROC4430. + */ +static const struct file_operations proc_4430_fops = { + .open = proc4430_drv_open, + .release = proc4430_drv_release, + .ioctl = proc4430_drv_ioctl, + .mmap = proc4430_drv_mmap, +}; + +static int proc4430_drv_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int proc4430_drv_release(struct inode *inode, struct file *filp) +{ + return 0; +} + + +/* + Linux driver function to invoke the APIs through ioctl. + * + */ +static int proc4430_drv_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long args) +{ + int retval = 0; + struct proc_mgr_cmd_args *cmd_args = (struct proc_mgr_cmd_args *)args; + struct proc_mgr_cmd_args command_args; + + switch (cmd) { + case CMD_PROC4430_GETCONFIG: + { + struct proc4430_cmd_args_get_config *src_args = + (struct proc4430_cmd_args_get_config *)args; + struct proc4430_config cfg; + + /* copy_from_useris not needed for + * proc4430_get_config, since the + * user's config is not used. + */ + proc4430_get_config(&cfg); + + retval = copy_to_user((void *)(src_args->cfg), + (const void *)&cfg, + sizeof(struct proc4430_config)); + if (WARN_ON(retval < 0)) + goto func_exit; + } + break; + + case CMD_PROC4430_SETUP: + { + struct proc4430_cmd_args_setup *src_args = + (struct proc4430_cmd_args_setup *)args; + struct proc4430_config cfg; + + retval = copy_from_user((void *)&cfg, + (const void *)(src_args->cfg), + sizeof(struct proc4430_config)); + if (WARN_ON(retval < 0)) + goto func_exit; + proc4430_setup(&cfg); + } + break; + + case CMD_PROC4430_DESTROY: + { + proc4430_destroy(); + } + break; + + case CMD_PROC4430_PARAMS_INIT: + { + struct proc4430_cmd_args_params_init src_args; + struct proc4430_params params; + + /* Copy the full args from user-side. */ + retval = copy_from_user((void *)&src_args, + (const void *)(args), + sizeof(struct proc4430_cmd_args_params_init)); + if (WARN_ON(retval < 0)) + goto func_exit; + proc4430_params_init(src_args.handle, ¶ms); + retval = copy_to_user((void *)(src_args.params), + (const void *) ¶ms, + sizeof(struct proc4430_params)); + if (WARN_ON(retval < 0)) + goto func_exit; + } + break; + + case CMD_PROC4430_CREATE: + { + struct proc4430_cmd_args_create src_args; + struct proc4430_params params; + struct proc4430_mem_entry *entries = NULL; + + /* Copy the full args from user-side. */ + retval = copy_from_user((void *)&src_args, + (const void *)(args), + sizeof(struct proc4430_cmd_args_create)); + if (WARN_ON(retval < 0)) + goto func_exit; + retval = copy_from_user((void *) ¶ms, + (const void *)(src_args.params), + sizeof(struct proc4430_params)); + if (WARN_ON(retval < 0)) + goto func_exit; + /* Copy the contents of mem_entries from user-side */ + if (params.num_mem_entries) { + entries = vmalloc(params.num_mem_entries * \ + sizeof(struct proc4430_mem_entry)); + if (WARN_ON(!entries)) + goto func_exit; + retval = copy_from_user((void *) (entries), + (const void *)(params.mem_entries), + params.num_mem_entries * \ + sizeof(struct proc4430_mem_entry)); + if (WARN_ON(retval < 0)) { + vfree(entries); + goto func_exit; + } + params.mem_entries = entries; + } + src_args.handle = proc4430_create(src_args.proc_id, + ¶ms); + if (WARN_ON(src_args.handle == NULL)) + goto func_exit; + retval = copy_to_user((void *)(args), + (const void *)&src_args, + sizeof(struct proc4430_cmd_args_create)); + /* Free the memory created */ + if (params.num_mem_entries) + vfree(entries); + } + break; + + case CMD_PROC4430_DELETE: + { + struct proc4430_cmd_args_delete src_args; + + /* Copy the full args from user-side. */ + retval = copy_from_user((void *)&src_args, + (const void *)(args), + sizeof(struct proc4430_cmd_args_delete)); + if (WARN_ON(retval < 0)) + goto func_exit; + retval = proc4430_delete(&(src_args.handle)); + WARN_ON(retval < 0); + } + break; + + case CMD_PROC4430_OPEN: + { + struct proc4430_cmd_args_open src_args; + + /*Copy the full args from user-side. */ + retval = copy_from_user((void *)&src_args, + (const void *)(args), + sizeof(struct proc4430_cmd_args_open)); + if (WARN_ON(retval < 0)) + goto func_exit; + retval = proc4430_open(&(src_args.handle), + src_args.proc_id); + retval = copy_to_user((void *)(args), + (const void *)&src_args, + sizeof(struct proc4430_cmd_args_open)); + WARN_ON(retval < 0); + } + break; + + case CMD_PROC4430_CLOSE: + { + struct proc4430_cmd_args_close src_args; + + /*Copy the full args from user-side. */ + retval = copy_from_user((void *)&src_args, + (const void *)(args), + sizeof(struct proc4430_cmd_args_close)); + if (WARN_ON(retval < 0)) + goto func_exit; + retval = proc4430_close(src_args.handle); + WARN_ON(retval < 0); + } + break; + + default: + { + printk(KERN_ERR "unsupported ioctl\n"); + } + break; + } +func_exit: + /* Set the status and copy the common args to user-side. */ + command_args.api_status = retval; + retval = copy_to_user((void *) cmd_args, + (const void *) &command_args, + sizeof(struct proc_mgr_cmd_args)); + WARN_ON(retval < 0); + return retval; +} + + +/* + Linux driver function to map memory regions to user space. + * + */ +static int proc4430_drv_mmap(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + + +/** ================================================================== + * Functions required for multiple .ko modules configuration + * ================================================================== + */ +/* + Module initialization function for Linux driver. + */ +static int __init proc4430_drv_initializeModule(void) +{ + dev_t dev = 0 ; + int retval; + + /* Display the version info and created date/time */ + printk(KERN_INFO "proc4430_drv_initializeModule\n"); + + if (driver_major) { + dev = MKDEV(driver_major, driver_minor); + retval = register_chrdev_region(dev, 1, driver_name); + } else { + retval = alloc_chrdev_region(&dev, driver_minor, 1, + driver_name); + driver_major = MAJOR(dev); + } + + proc_4430_device = kmalloc(sizeof(struct proc_4430_dev), GFP_KERNEL); + if (!proc_4430_device) { + retval = -ENOMEM; + unregister_chrdev_region(dev, 1); + goto exit; + } + memset(proc_4430_device, 0, sizeof(struct proc_4430_dev)); + cdev_init(&proc_4430_device->cdev, &proc_4430_fops); + proc_4430_device->cdev.owner = THIS_MODULE; + proc_4430_device->cdev.ops = &proc_4430_fops; + + retval = cdev_add(&proc_4430_device->cdev, dev, 1); + + if (retval) { + printk(KERN_ERR "Failed to add the syslink proc_4430 device \n"); + goto exit; + } + + /* udev support */ + proc_4430_class = class_create(THIS_MODULE, "syslink-proc4430"); + + if (IS_ERR(proc_4430_class)) { + printk(KERN_ERR "Error creating bridge class \n"); + goto exit; + } + device_create(proc_4430_class, NULL, MKDEV(driver_major, driver_minor), + NULL, PROC4430_NAME); +exit: + return 0; +} + +/* + function to finalize the driver module. + */ +static void __exit proc4430_drv_finalizeModule(void) +{ + dev_t devno = 0; + + /* FIX ME: THIS MIGHT NOT BE THE RIGHT PLACE TO CALL THE SETUP */ + proc4430_destroy(); + + devno = MKDEV(driver_major, driver_minor); + if (proc_4430_device) { + cdev_del(&proc_4430_device->cdev); + kfree(proc_4430_device); + } + unregister_chrdev_region(devno, 1); + if (proc_4430_class) { + /* remove the device from sysfs */ + device_destroy(proc_4430_class, MKDEV(driver_major, + driver_minor)); + class_destroy(proc_4430_class); + } + return; +} + +/* + Macro calls that indicate initialization and finalization functions + * to the kernel. + */ +MODULE_LICENSE("GPL v2"); +module_init(proc4430_drv_initializeModule); +module_exit(proc4430_drv_finalizeModule); diff --git a/drivers/dsp/syslink/procmgr/proc4430/proc4430_drvdefs.h b/drivers/dsp/syslink/procmgr/proc4430/proc4430_drvdefs.h new file mode 100755 index 000000000000..4176d731f1d4 --- /dev/null +++ b/drivers/dsp/syslink/procmgr/proc4430/proc4430_drvdefs.h @@ -0,0 +1,169 @@ +/* + * proc4430_drvdefs.h + * + * Syslink driver support functions for TI OMAP processors. + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#ifndef _SYSLINK_PROC4430_H +#define _SYSLINK_PROC4430_H + + +/* Module headers */ +#include "../procmgr_drvdefs.h" +#include "proc4430.h" + + +/* ---------------------------------------------------------------------------- + * IOCTL command IDs for OMAP4430PROC + * ---------------------------------------------------------------------------- + */ +/* + * Base command ID for OMAP4430PROC + */ +#define PROC4430_BASE_CMD 0x200 + +/* + * Command for PROC4430_getConfig + */ +#define CMD_PROC4430_GETCONFIG (PROC4430_BASE_CMD + 1) + +/* + * Command for PROC4430_setup + */ +#define CMD_PROC4430_SETUP (PROC4430_BASE_CMD + 2) + +/* + * Command for PROC4430_setup + */ +#define CMD_PROC4430_DESTROY (PROC4430_BASE_CMD + 3) + +/* + * Command for PROC4430_destroy + */ +#define CMD_PROC4430_PARAMS_INIT (PROC4430_BASE_CMD + 4) + +/* + * Command for PROC4430_create + */ +#define CMD_PROC4430_CREATE (PROC4430_BASE_CMD + 5) + +/* + * Command for PROC4430_delete + */ +#define CMD_PROC4430_DELETE (PROC4430_BASE_CMD + 6) + +/* + * Command for PROC4430_open + */ +#define CMD_PROC4430_OPEN (PROC4430_BASE_CMD + 7) + +/* + * Command for PROC4430_close + */ +#define CMD_PROC4430_CLOSE (PROC4430_BASE_CMD + 8) + + +/* --------------------------------------------------- + * Command arguments for OMAP4430PROC + * --------------------------------------------------- + */ +/* + * Command arguments for PROC4430_getConfig + */ +struct proc4430_cmd_args_get_config { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + struct proc4430_config *cfg; + /* Pointer to the OMAP4430PROC module configuration structure + * in which the default config is to be returned. */ +}; + +/* + * Command arguments for PROC4430_setup + */ +struct proc4430_cmd_args_setup { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + struct proc4430_config *cfg; + /* Optional OMAP4430PROC module configuration. If provided as NULL, + * default configuration is used. */ +}; + +/* + * Command arguments for PROC4430_destroy + */ +struct proc4430_cmd_args_destroy { + struct proc_mgr_cmd_args command_args; + /* Common command args */ +}; + +/* + * Command arguments for struct struct proc4430_params_init + */ +struct proc4430_cmd_args_params_init { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + void *handle; + /* void * to the processor instance. */ + struct proc4430_params *params; + /* Configuration parameters. */ +}; + +/* + * Command arguments for PROC4430_create + */ +struct proc4430_cmd_args_create { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + u16 proc_id; + /* Processor ID for which this processor instance is required. */ + struct proc4430_params *params; + /*Configuration parameters. */ + void *handle; + /* void * to the created processor instance. */ +}; + +/* + * Command arguments for PROC4430_delete + */ +struct proc4430_cmd_args_delete { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + void *handle; + /* Pointer to handle to the processor instance */ +}; + +/* + * Command arguments for PROC4430_open + */ +struct proc4430_cmd_args_open { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + u16 proc_id; + /* Processor ID addressed by this OMAP4430PROC instance. */ + void *handle; + /* Return parameter: void * to the processor instance */ +}; + +/* + * Command arguments for PROC4430_close + */ +struct proc4430_cmd_args_close { + struct proc_mgr_cmd_args command_args; + /* Common command args */ + void *handle; + /* void * to the processor instance */ +}; + +#endif |