diff options
Diffstat (limited to 'drivers/staging/tidspbridge/rmgr/proc.c')
-rw-r--r-- | drivers/staging/tidspbridge/rmgr/proc.c | 1936 |
1 files changed, 1936 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c new file mode 100644 index 000000000000..44c26e11fc4a --- /dev/null +++ b/drivers/staging/tidspbridge/rmgr/proc.c @@ -0,0 +1,1936 @@ +/* + * proc.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Processor interface at the driver level. + * + * 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. + */ + +#include <linux/types.h> +/* ------------------------------------ Host OS */ +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <dspbridge/host_os.h> + +/* ----------------------------------- DSP/BIOS Bridge */ +#include <dspbridge/dbdefs.h> + +/* ----------------------------------- Trace & Debug */ +#include <dspbridge/dbc.h> + +/* ----------------------------------- OS Adaptation Layer */ +#include <dspbridge/cfg.h> +#include <dspbridge/list.h> +#include <dspbridge/ntfy.h> +#include <dspbridge/sync.h> +/* ----------------------------------- Bridge Driver */ +#include <dspbridge/dspdefs.h> +#include <dspbridge/dspdeh.h> +/* ----------------------------------- Platform Manager */ +#include <dspbridge/cod.h> +#include <dspbridge/dev.h> +#include <dspbridge/procpriv.h> +#include <dspbridge/dmm.h> + +/* ----------------------------------- Resource Manager */ +#include <dspbridge/mgr.h> +#include <dspbridge/node.h> +#include <dspbridge/nldr.h> +#include <dspbridge/rmm.h> + +/* ----------------------------------- Others */ +#include <dspbridge/dbdcd.h> +#include <dspbridge/msg.h> +#include <dspbridge/dspioctl.h> +#include <dspbridge/drv.h> + +/* ----------------------------------- This */ +#include <dspbridge/proc.h> +#include <dspbridge/pwr.h> + +#include <dspbridge/resourcecleanup.h> +/* ----------------------------------- Defines, Data Structures, Typedefs */ +#define MAXCMDLINELEN 255 +#define PROC_ENVPROCID "PROC_ID=%d" +#define MAXPROCIDLEN (8 + 5) +#define PROC_DFLT_TIMEOUT 10000 /* Time out in milliseconds */ +#define PWR_TIMEOUT 500 /* Sleep/wake timout in msec */ +#define EXTEND "_EXT_END" /* Extmem end addr in DSP binary */ + +#define DSP_CACHE_LINE 128 + +#define BUFMODE_MASK (3 << 14) + +/* Buffer modes from DSP perspective */ +#define RBUF 0x4000 /* Input buffer */ +#define WBUF 0x8000 /* Output Buffer */ + +extern struct device *bridge; + +/* ----------------------------------- Globals */ + +/* The proc_object structure. */ +struct proc_object { + struct list_head link; /* Link to next proc_object */ + struct dev_object *hdev_obj; /* Device this PROC represents */ + u32 process; /* Process owning this Processor */ + struct mgr_object *hmgr_obj; /* Manager Object Handle */ + u32 attach_count; /* Processor attach count */ + u32 processor_id; /* Processor number */ + u32 utimeout; /* Time out count */ + enum dsp_procstate proc_state; /* Processor state */ + u32 ul_unit; /* DDSP unit number */ + bool is_already_attached; /* + * True if the Device below has + * GPP Client attached + */ + struct ntfy_object *ntfy_obj; /* Manages notifications */ + /* Bridge Context Handle */ + struct bridge_dev_context *hbridge_context; + /* Function interface to Bridge driver */ + struct bridge_drv_interface *intf_fxns; + char *psz_last_coff; + struct list_head proc_list; +}; + +static u32 refs; + +DEFINE_MUTEX(proc_lock); /* For critical sections */ + +/* ----------------------------------- Function Prototypes */ +static int proc_monitor(struct proc_object *proc_obj); +static s32 get_envp_count(char **envp); +static char **prepend_envp(char **new_envp, char **envp, s32 envp_elems, + s32 cnew_envp, char *sz_var); + +/* remember mapping information */ +static struct dmm_map_object *add_mapping_info(struct process_context *pr_ctxt, + u32 mpu_addr, u32 dsp_addr, u32 size) +{ + struct dmm_map_object *map_obj; + + u32 num_usr_pgs = size / PG_SIZE4K; + + pr_debug("%s: adding map info: mpu_addr 0x%x virt 0x%x size 0x%x\n", + __func__, mpu_addr, + dsp_addr, size); + + map_obj = kzalloc(sizeof(struct dmm_map_object), GFP_KERNEL); + if (!map_obj) { + pr_err("%s: kzalloc failed\n", __func__); + return NULL; + } + INIT_LIST_HEAD(&map_obj->link); + + map_obj->pages = kcalloc(num_usr_pgs, sizeof(struct page *), + GFP_KERNEL); + if (!map_obj->pages) { + pr_err("%s: kzalloc failed\n", __func__); + kfree(map_obj); + return NULL; + } + + map_obj->mpu_addr = mpu_addr; + map_obj->dsp_addr = dsp_addr; + map_obj->size = size; + map_obj->num_usr_pgs = num_usr_pgs; + + spin_lock(&pr_ctxt->dmm_map_lock); + list_add(&map_obj->link, &pr_ctxt->dmm_map_list); + spin_unlock(&pr_ctxt->dmm_map_lock); + + return map_obj; +} + +static int match_exact_map_obj(struct dmm_map_object *map_obj, + u32 dsp_addr, u32 size) +{ + if (map_obj->dsp_addr == dsp_addr && map_obj->size != size) + pr_err("%s: addr match (0x%x), size don't (0x%x != 0x%x)\n", + __func__, dsp_addr, map_obj->size, size); + + return map_obj->dsp_addr == dsp_addr && + map_obj->size == size; +} + +static void remove_mapping_information(struct process_context *pr_ctxt, + u32 dsp_addr, u32 size) +{ + struct dmm_map_object *map_obj; + + pr_debug("%s: looking for virt 0x%x size 0x%x\n", __func__, + dsp_addr, size); + + spin_lock(&pr_ctxt->dmm_map_lock); + list_for_each_entry(map_obj, &pr_ctxt->dmm_map_list, link) { + pr_debug("%s: candidate: mpu_addr 0x%x virt 0x%x size 0x%x\n", + __func__, + map_obj->mpu_addr, + map_obj->dsp_addr, + map_obj->size); + + if (match_exact_map_obj(map_obj, dsp_addr, size)) { + pr_debug("%s: match, deleting map info\n", __func__); + list_del(&map_obj->link); + kfree(map_obj->dma_info.sg); + kfree(map_obj->pages); + kfree(map_obj); + goto out; + } + pr_debug("%s: candidate didn't match\n", __func__); + } + + pr_err("%s: failed to find given map info\n", __func__); +out: + spin_unlock(&pr_ctxt->dmm_map_lock); +} + +static int match_containing_map_obj(struct dmm_map_object *map_obj, + u32 mpu_addr, u32 size) +{ + u32 map_obj_end = map_obj->mpu_addr + map_obj->size; + + return mpu_addr >= map_obj->mpu_addr && + mpu_addr + size <= map_obj_end; +} + +static struct dmm_map_object *find_containing_mapping( + struct process_context *pr_ctxt, + u32 mpu_addr, u32 size) +{ + struct dmm_map_object *map_obj; + pr_debug("%s: looking for mpu_addr 0x%x size 0x%x\n", __func__, + mpu_addr, size); + + spin_lock(&pr_ctxt->dmm_map_lock); + list_for_each_entry(map_obj, &pr_ctxt->dmm_map_list, link) { + pr_debug("%s: candidate: mpu_addr 0x%x virt 0x%x size 0x%x\n", + __func__, + map_obj->mpu_addr, + map_obj->dsp_addr, + map_obj->size); + if (match_containing_map_obj(map_obj, mpu_addr, size)) { + pr_debug("%s: match!\n", __func__); + goto out; + } + + pr_debug("%s: no match!\n", __func__); + } + + map_obj = NULL; +out: + spin_unlock(&pr_ctxt->dmm_map_lock); + return map_obj; +} + +static int find_first_page_in_cache(struct dmm_map_object *map_obj, + unsigned long mpu_addr) +{ + u32 mapped_base_page = map_obj->mpu_addr >> PAGE_SHIFT; + u32 requested_base_page = mpu_addr >> PAGE_SHIFT; + int pg_index = requested_base_page - mapped_base_page; + + if (pg_index < 0 || pg_index >= map_obj->num_usr_pgs) { + pr_err("%s: failed (got %d)\n", __func__, pg_index); + return -1; + } + + pr_debug("%s: first page is %d\n", __func__, pg_index); + return pg_index; +} + +static inline struct page *get_mapping_page(struct dmm_map_object *map_obj, + int pg_i) +{ + pr_debug("%s: looking for pg_i %d, num_usr_pgs: %d\n", __func__, + pg_i, map_obj->num_usr_pgs); + + if (pg_i < 0 || pg_i >= map_obj->num_usr_pgs) { + pr_err("%s: requested pg_i %d is out of mapped range\n", + __func__, pg_i); + return NULL; + } + + return map_obj->pages[pg_i]; +} + +/* + * ======== proc_attach ======== + * Purpose: + * Prepare for communication with a particular DSP processor, and return + * a handle to the processor object. + */ +int +proc_attach(u32 processor_id, + const struct dsp_processorattrin *attr_in, + void **ph_processor, struct process_context *pr_ctxt) +{ + int status = 0; + struct dev_object *hdev_obj; + struct proc_object *p_proc_object = NULL; + struct mgr_object *hmgr_obj = NULL; + struct drv_object *hdrv_obj = NULL; + u8 dev_type; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(ph_processor != NULL); + + if (pr_ctxt->hprocessor) { + *ph_processor = pr_ctxt->hprocessor; + return status; + } + + /* Get the Driver and Manager Object Handles */ + status = cfg_get_object((u32 *) &hdrv_obj, REG_DRV_OBJECT); + if (!status) + status = cfg_get_object((u32 *) &hmgr_obj, REG_MGR_OBJECT); + + if (!status) { + /* Get the Device Object */ + status = drv_get_dev_object(processor_id, hdrv_obj, &hdev_obj); + } + if (!status) + status = dev_get_dev_type(hdev_obj, &dev_type); + + if (status) + goto func_end; + + /* If we made it this far, create the Proceesor object: */ + p_proc_object = kzalloc(sizeof(struct proc_object), GFP_KERNEL); + /* Fill out the Processor Object: */ + if (p_proc_object == NULL) { + status = -ENOMEM; + goto func_end; + } + p_proc_object->hdev_obj = hdev_obj; + p_proc_object->hmgr_obj = hmgr_obj; + p_proc_object->processor_id = dev_type; + /* Store TGID instead of process handle */ + p_proc_object->process = current->tgid; + + INIT_LIST_HEAD(&p_proc_object->proc_list); + + if (attr_in) + p_proc_object->utimeout = attr_in->utimeout; + else + p_proc_object->utimeout = PROC_DFLT_TIMEOUT; + + status = dev_get_intf_fxns(hdev_obj, &p_proc_object->intf_fxns); + if (!status) { + status = dev_get_bridge_context(hdev_obj, + &p_proc_object->hbridge_context); + if (status) + kfree(p_proc_object); + } else + kfree(p_proc_object); + + if (status) + goto func_end; + + /* Create the Notification Object */ + /* This is created with no event mask, no notify mask + * and no valid handle to the notification. They all get + * filled up when proc_register_notify is called */ + p_proc_object->ntfy_obj = kmalloc(sizeof(struct ntfy_object), + GFP_KERNEL); + if (p_proc_object->ntfy_obj) + ntfy_init(p_proc_object->ntfy_obj); + else + status = -ENOMEM; + + if (!status) { + /* Insert the Processor Object into the DEV List. + * Return handle to this Processor Object: + * Find out if the Device is already attached to a + * Processor. If so, return AlreadyAttached status */ + lst_init_elem(&p_proc_object->link); + status = dev_insert_proc_object(p_proc_object->hdev_obj, + (u32) p_proc_object, + &p_proc_object-> + is_already_attached); + if (!status) { + if (p_proc_object->is_already_attached) + status = 0; + } else { + if (p_proc_object->ntfy_obj) { + ntfy_delete(p_proc_object->ntfy_obj); + kfree(p_proc_object->ntfy_obj); + } + + kfree(p_proc_object); + } + if (!status) { + *ph_processor = (void *)p_proc_object; + pr_ctxt->hprocessor = *ph_processor; + (void)proc_notify_clients(p_proc_object, + DSP_PROCESSORATTACH); + } + } else { + /* Don't leak memory if status is failed */ + kfree(p_proc_object); + } +func_end: + DBC_ENSURE((status == -EPERM && *ph_processor == NULL) || + (!status && p_proc_object) || + (status == 0 && p_proc_object)); + + return status; +} + +static int get_exec_file(struct cfg_devnode *dev_node_obj, + struct dev_object *hdev_obj, + u32 size, char *exec_file) +{ + u8 dev_type; + s32 len; + + dev_get_dev_type(hdev_obj, (u8 *) &dev_type); + if (dev_type == DSP_UNIT) { + return cfg_get_exec_file(dev_node_obj, size, exec_file); + } else if (dev_type == IVA_UNIT) { + if (iva_img) { + len = strlen(iva_img); + strncpy(exec_file, iva_img, len + 1); + return 0; + } + } + return -ENOENT; +} + +/* + * ======== proc_auto_start ======== = + * Purpose: + * A Particular device gets loaded with the default image + * if the AutoStart flag is set. + * Parameters: + * hdev_obj: Handle to the Device + * Returns: + * 0: On Successful Loading + * -EPERM General Failure + * Requires: + * hdev_obj != NULL + * Ensures: + */ +int proc_auto_start(struct cfg_devnode *dev_node_obj, + struct dev_object *hdev_obj) +{ + int status = -EPERM; + struct proc_object *p_proc_object; + char sz_exec_file[MAXCMDLINELEN]; + char *argv[2]; + struct mgr_object *hmgr_obj = NULL; + u8 dev_type; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(dev_node_obj != NULL); + DBC_REQUIRE(hdev_obj != NULL); + + /* Create a Dummy PROC Object */ + status = cfg_get_object((u32 *) &hmgr_obj, REG_MGR_OBJECT); + if (status) + goto func_end; + + p_proc_object = kzalloc(sizeof(struct proc_object), GFP_KERNEL); + if (p_proc_object == NULL) { + status = -ENOMEM; + goto func_end; + } + p_proc_object->hdev_obj = hdev_obj; + p_proc_object->hmgr_obj = hmgr_obj; + status = dev_get_intf_fxns(hdev_obj, &p_proc_object->intf_fxns); + if (!status) + status = dev_get_bridge_context(hdev_obj, + &p_proc_object->hbridge_context); + if (status) + goto func_cont; + + /* Stop the Device, put it into standby mode */ + status = proc_stop(p_proc_object); + + if (status) + goto func_cont; + + /* Get the default executable for this board... */ + dev_get_dev_type(hdev_obj, (u8 *) &dev_type); + p_proc_object->processor_id = dev_type; + status = get_exec_file(dev_node_obj, hdev_obj, sizeof(sz_exec_file), + sz_exec_file); + if (!status) { + argv[0] = sz_exec_file; + argv[1] = NULL; + /* ...and try to load it: */ + status = proc_load(p_proc_object, 1, (const char **)argv, NULL); + if (!status) + status = proc_start(p_proc_object); + } + kfree(p_proc_object->psz_last_coff); + p_proc_object->psz_last_coff = NULL; +func_cont: + kfree(p_proc_object); +func_end: + return status; +} + +/* + * ======== proc_ctrl ======== + * Purpose: + * Pass control information to the GPP device driver managing the + * DSP processor. + * + * This will be an OEM-only function, and not part of the DSP/BIOS Bridge + * application developer's API. + * Call the bridge_dev_ctrl fxn with the Argument. This is a Synchronous + * Operation. arg can be null. + */ +int proc_ctrl(void *hprocessor, u32 dw_cmd, struct dsp_cbdata * arg) +{ + int status = 0; + struct proc_object *p_proc_object = hprocessor; + u32 timeout = 0; + + DBC_REQUIRE(refs > 0); + + if (p_proc_object) { + /* intercept PWR deep sleep command */ + if (dw_cmd == BRDIOCTL_DEEPSLEEP) { + timeout = arg->cb_data; + status = pwr_sleep_dsp(PWR_DEEPSLEEP, timeout); + } + /* intercept PWR emergency sleep command */ + else if (dw_cmd == BRDIOCTL_EMERGENCYSLEEP) { + timeout = arg->cb_data; + status = pwr_sleep_dsp(PWR_EMERGENCYDEEPSLEEP, timeout); + } else if (dw_cmd == PWR_DEEPSLEEP) { + /* timeout = arg->cb_data; */ + status = pwr_sleep_dsp(PWR_DEEPSLEEP, timeout); + } + /* intercept PWR wake commands */ + else if (dw_cmd == BRDIOCTL_WAKEUP) { + timeout = arg->cb_data; + status = pwr_wake_dsp(timeout); + } else if (dw_cmd == PWR_WAKEUP) { + /* timeout = arg->cb_data; */ + status = pwr_wake_dsp(timeout); + } else + if (!((*p_proc_object->intf_fxns->pfn_dev_cntrl) + (p_proc_object->hbridge_context, dw_cmd, + arg))) { + status = 0; + } else { + status = -EPERM; + } + } else { + status = -EFAULT; + } + + return status; +} + +/* + * ======== proc_detach ======== + * Purpose: + * Destroys the Processor Object. Removes the notification from the Dev + * List. + */ +int proc_detach(struct process_context *pr_ctxt) +{ + int status = 0; + struct proc_object *p_proc_object = NULL; + + DBC_REQUIRE(refs > 0); + + p_proc_object = (struct proc_object *)pr_ctxt->hprocessor; + + if (p_proc_object) { + /* Notify the Client */ + ntfy_notify(p_proc_object->ntfy_obj, DSP_PROCESSORDETACH); + /* Remove the notification memory */ + if (p_proc_object->ntfy_obj) { + ntfy_delete(p_proc_object->ntfy_obj); + kfree(p_proc_object->ntfy_obj); + } + + kfree(p_proc_object->psz_last_coff); + p_proc_object->psz_last_coff = NULL; + /* Remove the Proc from the DEV List */ + (void)dev_remove_proc_object(p_proc_object->hdev_obj, + (u32) p_proc_object); + /* Free the Processor Object */ + kfree(p_proc_object); + pr_ctxt->hprocessor = NULL; + } else { + status = -EFAULT; + } + + return status; +} + +/* + * ======== proc_enum_nodes ======== + * Purpose: + * Enumerate and get configuration information about nodes allocated + * on a DSP processor. + */ +int proc_enum_nodes(void *hprocessor, void **node_tab, + u32 node_tab_size, u32 *pu_num_nodes, + u32 *pu_allocated) +{ + int status = -EPERM; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct node_mgr *hnode_mgr = NULL; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(node_tab != NULL || node_tab_size == 0); + DBC_REQUIRE(pu_num_nodes != NULL); + DBC_REQUIRE(pu_allocated != NULL); + + if (p_proc_object) { + if (!(dev_get_node_manager(p_proc_object->hdev_obj, + &hnode_mgr))) { + if (hnode_mgr) { + status = node_enum_nodes(hnode_mgr, node_tab, + node_tab_size, + pu_num_nodes, + pu_allocated); + } + } + } else { + status = -EFAULT; + } + + return status; +} + +/* Cache operation against kernel address instead of users */ +static int build_dma_sg(struct dmm_map_object *map_obj, unsigned long start, + ssize_t len, int pg_i) +{ + struct page *page; + unsigned long offset; + ssize_t rest; + int ret = 0, i = 0; + struct scatterlist *sg = map_obj->dma_info.sg; + + while (len) { + page = get_mapping_page(map_obj, pg_i); + if (!page) { + pr_err("%s: no page for %08lx\n", __func__, start); + ret = -EINVAL; + goto out; + } else if (IS_ERR(page)) { + pr_err("%s: err page for %08lx(%lu)\n", __func__, start, + PTR_ERR(page)); + ret = PTR_ERR(page); + goto out; + } + + offset = start & ~PAGE_MASK; + rest = min_t(ssize_t, PAGE_SIZE - offset, len); + + sg_set_page(&sg[i], page, rest, offset); + + len -= rest; + start += rest; + pg_i++, i++; + } + + if (i != map_obj->dma_info.num_pages) { + pr_err("%s: bad number of sg iterations\n", __func__); + ret = -EFAULT; + goto out; + } + +out: + return ret; +} + +static int memory_regain_ownership(struct dmm_map_object *map_obj, + unsigned long start, ssize_t len, enum dma_data_direction dir) +{ + int ret = 0; + unsigned long first_data_page = start >> PAGE_SHIFT; + unsigned long last_data_page = ((u32)(start + len - 1) >> PAGE_SHIFT); + /* calculating the number of pages this area spans */ + unsigned long num_pages = last_data_page - first_data_page + 1; + struct bridge_dma_map_info *dma_info = &map_obj->dma_info; + + if (!dma_info->sg) + goto out; + + if (dma_info->dir != dir || dma_info->num_pages != num_pages) { + pr_err("%s: dma info doesn't match given params\n", __func__); + return -EINVAL; + } + + dma_unmap_sg(bridge, dma_info->sg, num_pages, dma_info->dir); + + pr_debug("%s: dma_map_sg unmapped\n", __func__); + + kfree(dma_info->sg); + + map_obj->dma_info.sg = NULL; + +out: + return ret; +} + +/* Cache operation against kernel address instead of users */ +static int memory_give_ownership(struct dmm_map_object *map_obj, + unsigned long start, ssize_t len, enum dma_data_direction dir) +{ + int pg_i, ret, sg_num; + struct scatterlist *sg; + unsigned long first_data_page = start >> PAGE_SHIFT; + unsigned long last_data_page = ((u32)(start + len - 1) >> PAGE_SHIFT); + /* calculating the number of pages this area spans */ + unsigned long num_pages = last_data_page - first_data_page + 1; + + pg_i = find_first_page_in_cache(map_obj, start); + if (pg_i < 0) { + pr_err("%s: failed to find first page in cache\n", __func__); + ret = -EINVAL; + goto out; + } + + sg = kcalloc(num_pages, sizeof(*sg), GFP_KERNEL); + if (!sg) { + pr_err("%s: kcalloc failed\n", __func__); + ret = -ENOMEM; + goto out; + } + + sg_init_table(sg, num_pages); + + /* cleanup a previous sg allocation */ + /* this may happen if application doesn't signal for e/o DMA */ + kfree(map_obj->dma_info.sg); + + map_obj->dma_info.sg = sg; + map_obj->dma_info.dir = dir; + map_obj->dma_info.num_pages = num_pages; + + ret = build_dma_sg(map_obj, start, len, pg_i); + if (ret) + goto kfree_sg; + + sg_num = dma_map_sg(bridge, sg, num_pages, dir); + if (sg_num < 1) { + pr_err("%s: dma_map_sg failed: %d\n", __func__, sg_num); + ret = -EFAULT; + goto kfree_sg; + } + + pr_debug("%s: dma_map_sg mapped %d elements\n", __func__, sg_num); + map_obj->dma_info.sg_num = sg_num; + + return 0; + +kfree_sg: + kfree(sg); + map_obj->dma_info.sg = NULL; +out: + return ret; +} + +int proc_begin_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, + enum dma_data_direction dir) +{ + /* Keep STATUS here for future additions to this function */ + int status = 0; + struct process_context *pr_ctxt = (struct process_context *) hprocessor; + struct dmm_map_object *map_obj; + + DBC_REQUIRE(refs > 0); + + if (!pr_ctxt) { + status = -EFAULT; + goto err_out; + } + + pr_debug("%s: addr 0x%x, size 0x%x, type %d\n", __func__, + (u32)pmpu_addr, + ul_size, dir); + + /* find requested memory are in cached mapping information */ + map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); + if (!map_obj) { + pr_err("%s: find_containing_mapping failed\n", __func__); + status = -EFAULT; + goto err_out; + } + + if (memory_give_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { + pr_err("%s: InValid address parameters %p %x\n", + __func__, pmpu_addr, ul_size); + status = -EFAULT; + } + +err_out: + + return status; +} + +int proc_end_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, + enum dma_data_direction dir) +{ + /* Keep STATUS here for future additions to this function */ + int status = 0; + struct process_context *pr_ctxt = (struct process_context *) hprocessor; + struct dmm_map_object *map_obj; + + DBC_REQUIRE(refs > 0); + + if (!pr_ctxt) { + status = -EFAULT; + goto err_out; + } + + pr_debug("%s: addr 0x%x, size 0x%x, type %d\n", __func__, + (u32)pmpu_addr, + ul_size, dir); + + /* find requested memory are in cached mapping information */ + map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); + if (!map_obj) { + pr_err("%s: find_containing_mapping failed\n", __func__); + status = -EFAULT; + goto err_out; + } + + if (memory_regain_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { + pr_err("%s: InValid address parameters %p %x\n", + __func__, pmpu_addr, ul_size); + status = -EFAULT; + goto err_out; + } + +err_out: + return status; +} + +/* + * ======== proc_flush_memory ======== + * Purpose: + * Flush cache + */ +int proc_flush_memory(void *hprocessor, void *pmpu_addr, + u32 ul_size, u32 ul_flags) +{ + enum dma_data_direction dir = DMA_BIDIRECTIONAL; + + return proc_begin_dma(hprocessor, pmpu_addr, ul_size, dir); +} + +/* + * ======== proc_invalidate_memory ======== + * Purpose: + * Invalidates the memory specified + */ +int proc_invalidate_memory(void *hprocessor, void *pmpu_addr, u32 size) +{ + enum dma_data_direction dir = DMA_FROM_DEVICE; + + return proc_begin_dma(hprocessor, pmpu_addr, size, dir); +} + +/* + * ======== proc_get_resource_info ======== + * Purpose: + * Enumerate the resources currently available on a processor. + */ +int proc_get_resource_info(void *hprocessor, u32 resource_type, + struct dsp_resourceinfo *resource_info, + u32 resource_info_size) +{ + int status = -EPERM; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct node_mgr *hnode_mgr = NULL; + struct nldr_object *nldr_obj = NULL; + struct rmm_target_obj *rmm = NULL; + struct io_mgr *hio_mgr = NULL; /* IO manager handle */ + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(resource_info != NULL); + DBC_REQUIRE(resource_info_size >= sizeof(struct dsp_resourceinfo)); + + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + switch (resource_type) { + case DSP_RESOURCE_DYNDARAM: + case DSP_RESOURCE_DYNSARAM: + case DSP_RESOURCE_DYNEXTERNAL: + case DSP_RESOURCE_DYNSRAM: + status = dev_get_node_manager(p_proc_object->hdev_obj, + &hnode_mgr); + if (!hnode_mgr) { + status = -EFAULT; + goto func_end; + } + + status = node_get_nldr_obj(hnode_mgr, &nldr_obj); + if (!status) { + status = nldr_get_rmm_manager(nldr_obj, &rmm); + if (rmm) { + if (!rmm_stat(rmm, + (enum dsp_memtype)resource_type, + (struct dsp_memstat *) + &(resource_info->result. + mem_stat))) + status = -EINVAL; + } else { + status = -EFAULT; + } + } + break; + case DSP_RESOURCE_PROCLOAD: + status = dev_get_io_mgr(p_proc_object->hdev_obj, &hio_mgr); + if (hio_mgr) + status = + p_proc_object->intf_fxns-> + pfn_io_get_proc_load(hio_mgr, + (struct dsp_procloadstat *) + &(resource_info->result. + proc_load_stat)); + else + status = -EFAULT; + break; + default: + status = -EPERM; + break; + } +func_end: + return status; +} + +/* + * ======== proc_exit ======== + * Purpose: + * Decrement reference count, and free resources when reference count is + * 0. + */ +void proc_exit(void) +{ + DBC_REQUIRE(refs > 0); + + refs--; + + DBC_ENSURE(refs >= 0); +} + +/* + * ======== proc_get_dev_object ======== + * Purpose: + * Return the Dev Object handle for a given Processor. + * + */ +int proc_get_dev_object(void *hprocessor, + struct dev_object **device_obj) +{ + int status = -EPERM; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(device_obj != NULL); + + if (p_proc_object) { + *device_obj = p_proc_object->hdev_obj; + status = 0; + } else { + *device_obj = NULL; + status = -EFAULT; + } + + DBC_ENSURE((!status && *device_obj != NULL) || + (status && *device_obj == NULL)); + + return status; +} + +/* + * ======== proc_get_state ======== + * Purpose: + * Report the state of the specified DSP processor. + */ +int proc_get_state(void *hprocessor, + struct dsp_processorstate *proc_state_obj, + u32 state_info_size) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + int brd_status; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(proc_state_obj != NULL); + DBC_REQUIRE(state_info_size >= sizeof(struct dsp_processorstate)); + + if (p_proc_object) { + /* First, retrieve BRD state information */ + status = (*p_proc_object->intf_fxns->pfn_brd_status) + (p_proc_object->hbridge_context, &brd_status); + if (!status) { + switch (brd_status) { + case BRD_STOPPED: + proc_state_obj->proc_state = PROC_STOPPED; + break; + case BRD_SLEEP_TRANSITION: + case BRD_DSP_HIBERNATION: + /* Fall through */ + case BRD_RUNNING: + proc_state_obj->proc_state = PROC_RUNNING; + break; + case BRD_LOADED: + proc_state_obj->proc_state = PROC_LOADED; + break; + case BRD_ERROR: + proc_state_obj->proc_state = PROC_ERROR; + break; + default: + proc_state_obj->proc_state = 0xFF; + status = -EPERM; + break; + } + } + } else { + status = -EFAULT; + } + dev_dbg(bridge, "%s, results: status: 0x%x proc_state_obj: 0x%x\n", + __func__, status, proc_state_obj->proc_state); + return status; +} + +/* + * ======== proc_get_trace ======== + * Purpose: + * Retrieve the current contents of the trace buffer, located on the + * Processor. Predefined symbols for the trace buffer must have been + * configured into the DSP executable. + * Details: + * We support using the symbols SYS_PUTCBEG and SYS_PUTCEND to define a + * trace buffer, only. Treat it as an undocumented feature. + * This call is destructive, meaning the processor is placed in the monitor + * state as a result of this function. + */ +int proc_get_trace(void *hprocessor, u8 * pbuf, u32 max_size) +{ + int status; + status = -ENOSYS; + return status; +} + +/* + * ======== proc_init ======== + * Purpose: + * Initialize PROC's private state, keeping a reference count on each call + */ +bool proc_init(void) +{ + bool ret = true; + + DBC_REQUIRE(refs >= 0); + + if (ret) + refs++; + + DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs >= 0))); + + return ret; +} + +/* + * ======== proc_load ======== + * Purpose: + * Reset a processor and load a new base program image. + * This will be an OEM-only function, and not part of the DSP/BIOS Bridge + * application developer's API. + */ +int proc_load(void *hprocessor, const s32 argc_index, + const char **user_args, const char **user_envp) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct io_mgr *hio_mgr; /* IO manager handle */ + struct msg_mgr *hmsg_mgr; + struct cod_manager *cod_mgr; /* Code manager handle */ + char *pargv0; /* temp argv[0] ptr */ + char **new_envp; /* Updated envp[] array. */ + char sz_proc_id[MAXPROCIDLEN]; /* Size of "PROC_ID=<n>" */ + s32 envp_elems; /* Num elements in envp[]. */ + s32 cnew_envp; /* " " in new_envp[] */ + s32 nproc_id = 0; /* Anticipate MP version. */ + struct dcd_manager *hdcd_handle; + struct dmm_object *dmm_mgr; + u32 dw_ext_end; + u32 proc_id; + int brd_state; + struct drv_data *drv_datap = dev_get_drvdata(bridge); + +#ifdef OPT_LOAD_TIME_INSTRUMENTATION + struct timeval tv1; + struct timeval tv2; +#endif + +#if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) + struct dspbridge_platform_data *pdata = + omap_dspbridge_dev->dev.platform_data; +#endif + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(argc_index > 0); + DBC_REQUIRE(user_args != NULL); + +#ifdef OPT_LOAD_TIME_INSTRUMENTATION + do_gettimeofday(&tv1); +#endif + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + dev_get_cod_mgr(p_proc_object->hdev_obj, &cod_mgr); + if (!cod_mgr) { + status = -EPERM; + goto func_end; + } + status = proc_stop(hprocessor); + if (status) + goto func_end; + + /* Place the board in the monitor state. */ + status = proc_monitor(hprocessor); + if (status) + goto func_end; + + /* Save ptr to original argv[0]. */ + pargv0 = (char *)user_args[0]; + /*Prepend "PROC_ID=<nproc_id>"to envp array for target. */ + envp_elems = get_envp_count((char **)user_envp); + cnew_envp = (envp_elems ? (envp_elems + 1) : (envp_elems + 2)); + new_envp = kzalloc(cnew_envp * sizeof(char **), GFP_KERNEL); + if (new_envp) { + status = snprintf(sz_proc_id, MAXPROCIDLEN, PROC_ENVPROCID, + nproc_id); + if (status == -1) { + dev_dbg(bridge, "%s: Proc ID string overflow\n", + __func__); + status = -EPERM; + } else { + new_envp = + prepend_envp(new_envp, (char **)user_envp, + envp_elems, cnew_envp, sz_proc_id); + /* Get the DCD Handle */ + status = mgr_get_dcd_handle(p_proc_object->hmgr_obj, + (u32 *) &hdcd_handle); + if (!status) { + /* Before proceeding with new load, + * check if a previously registered COFF + * exists. + * If yes, unregister nodes in previously + * registered COFF. If any error occurred, + * set previously registered COFF to NULL. */ + if (p_proc_object->psz_last_coff != NULL) { + status = + dcd_auto_unregister(hdcd_handle, + p_proc_object-> + psz_last_coff); + /* Regardless of auto unregister status, + * free previously allocated + * memory. */ + kfree(p_proc_object->psz_last_coff); + p_proc_object->psz_last_coff = NULL; + } + } + /* On success, do cod_open_base() */ + status = cod_open_base(cod_mgr, (char *)user_args[0], + COD_SYMB); + } + } else { + status = -ENOMEM; + } + if (!status) { + /* Auto-register data base */ + /* Get the DCD Handle */ + status = mgr_get_dcd_handle(p_proc_object->hmgr_obj, + (u32 *) &hdcd_handle); + if (!status) { + /* Auto register nodes in specified COFF + * file. If registration did not fail, + * (status = 0 or -EACCES) + * save the name of the COFF file for + * de-registration in the future. */ + status = + dcd_auto_register(hdcd_handle, + (char *)user_args[0]); + if (status == -EACCES) + status = 0; + + if (status) { + status = -EPERM; + } else { + DBC_ASSERT(p_proc_object->psz_last_coff == + NULL); + /* Allocate memory for pszLastCoff */ + p_proc_object->psz_last_coff = + kzalloc((strlen(user_args[0]) + + 1), GFP_KERNEL); + /* If memory allocated, save COFF file name */ + if (p_proc_object->psz_last_coff) { + strncpy(p_proc_object->psz_last_coff, + (char *)user_args[0], + (strlen((char *)user_args[0]) + + 1)); + } + } + } + } + /* Update shared memory address and size */ + if (!status) { + /* Create the message manager. This must be done + * before calling the IOOnLoaded function. */ + dev_get_msg_mgr(p_proc_object->hdev_obj, &hmsg_mgr); + if (!hmsg_mgr) { + status = msg_create(&hmsg_mgr, p_proc_object->hdev_obj, + (msg_onexit) node_on_exit); + DBC_ASSERT(!status); + dev_set_msg_mgr(p_proc_object->hdev_obj, hmsg_mgr); + } + } + if (!status) { + /* Set the Device object's message manager */ + status = dev_get_io_mgr(p_proc_object->hdev_obj, &hio_mgr); + if (hio_mgr) + status = (*p_proc_object->intf_fxns->pfn_io_on_loaded) + (hio_mgr); + else + status = -EFAULT; + } + if (!status) { + /* Now, attempt to load an exec: */ + + /* Boost the OPP level to Maximum level supported by baseport */ +#if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) + if (pdata->cpu_set_freq) + (*pdata->cpu_set_freq) (pdata->mpu_speed[VDD1_OPP5]); +#endif + status = cod_load_base(cod_mgr, argc_index, (char **)user_args, + dev_brd_write_fxn, + p_proc_object->hdev_obj, NULL); + if (status) { + if (status == -EBADF) { + dev_dbg(bridge, "%s: Failure to Load the EXE\n", + __func__); + } + if (status == -ESPIPE) { + pr_err("%s: Couldn't parse the file\n", + __func__); + } + } + /* Requesting the lowest opp supported */ +#if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) + if (pdata->cpu_set_freq) + (*pdata->cpu_set_freq) (pdata->mpu_speed[VDD1_OPP1]); +#endif + + } + if (!status) { + /* Update the Processor status to loaded */ + status = (*p_proc_object->intf_fxns->pfn_brd_set_state) + (p_proc_object->hbridge_context, BRD_LOADED); + if (!status) { + p_proc_object->proc_state = PROC_LOADED; + if (p_proc_object->ntfy_obj) + proc_notify_clients(p_proc_object, + DSP_PROCESSORSTATECHANGE); + } + } + if (!status) { + status = proc_get_processor_id(hprocessor, &proc_id); + if (proc_id == DSP_UNIT) { + /* Use all available DSP address space after EXTMEM + * for DMM */ + if (!status) + status = cod_get_sym_value(cod_mgr, EXTEND, + &dw_ext_end); + + /* Reset DMM structs and add an initial free chunk */ + if (!status) { + status = + dev_get_dmm_mgr(p_proc_object->hdev_obj, + &dmm_mgr); + if (dmm_mgr) { + /* Set dw_ext_end to DMM START u8 + * address */ + dw_ext_end = + (dw_ext_end + 1) * DSPWORDSIZE; + /* DMM memory is from EXT_END */ + status = dmm_create_tables(dmm_mgr, + dw_ext_end, + DMMPOOLSIZE); + } else { + status = -EFAULT; + } + } + } + } + /* Restore the original argv[0] */ + kfree(new_envp); + user_args[0] = pargv0; + if (!status) { + if (!((*p_proc_object->intf_fxns->pfn_brd_status) + (p_proc_object->hbridge_context, &brd_state))) { + pr_info("%s: Processor Loaded %s\n", __func__, pargv0); + kfree(drv_datap->base_img); + drv_datap->base_img = kmalloc(strlen(pargv0) + 1, + GFP_KERNEL); + if (drv_datap->base_img) + strncpy(drv_datap->base_img, pargv0, + strlen(pargv0) + 1); + else + status = -ENOMEM; + DBC_ASSERT(brd_state == BRD_LOADED); + } + } + +func_end: + if (status) { + pr_err("%s: Processor failed to load\n", __func__); + proc_stop(p_proc_object); + } + DBC_ENSURE((!status + && p_proc_object->proc_state == PROC_LOADED) + || status); +#ifdef OPT_LOAD_TIME_INSTRUMENTATION + do_gettimeofday(&tv2); + if (tv2.tv_usec < tv1.tv_usec) { + tv2.tv_usec += 1000000; + tv2.tv_sec--; + } + dev_dbg(bridge, "%s: time to load %d sec and %d usec\n", __func__, + tv2.tv_sec - tv1.tv_sec, tv2.tv_usec - tv1.tv_usec); +#endif + return status; +} + +/* + * ======== proc_map ======== + * Purpose: + * Maps a MPU buffer to DSP address space. + */ +int proc_map(void *hprocessor, void *pmpu_addr, u32 ul_size, + void *req_addr, void **pp_map_addr, u32 ul_map_attr, + struct process_context *pr_ctxt) +{ + u32 va_align; + u32 pa_align; + struct dmm_object *dmm_mgr; + u32 size_align; + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct dmm_map_object *map_obj; + u32 tmp_addr = 0; + +#ifdef CONFIG_TIDSPBRIDGE_CACHE_LINE_CHECK + if ((ul_map_attr & BUFMODE_MASK) != RBUF) { + if (!IS_ALIGNED((u32)pmpu_addr, DSP_CACHE_LINE) || + !IS_ALIGNED(ul_size, DSP_CACHE_LINE)) { + pr_err("%s: not aligned: 0x%x (%d)\n", __func__, + (u32)pmpu_addr, ul_size); + return -EFAULT; + } + } +#endif + + /* Calculate the page-aligned PA, VA and size */ + va_align = PG_ALIGN_LOW((u32) req_addr, PG_SIZE4K); + pa_align = PG_ALIGN_LOW((u32) pmpu_addr, PG_SIZE4K); + size_align = PG_ALIGN_HIGH(ul_size + (u32) pmpu_addr - pa_align, + PG_SIZE4K); + + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + /* Critical section */ + mutex_lock(&proc_lock); + dmm_get_handle(p_proc_object, &dmm_mgr); + if (dmm_mgr) + status = dmm_map_memory(dmm_mgr, va_align, size_align); + else + status = -EFAULT; + + /* Add mapping to the page tables. */ + if (!status) { + + /* Mapped address = MSB of VA | LSB of PA */ + tmp_addr = (va_align | ((u32) pmpu_addr & (PG_SIZE4K - 1))); + /* mapped memory resource tracking */ + map_obj = add_mapping_info(pr_ctxt, pa_align, tmp_addr, + size_align); + if (!map_obj) + status = -ENOMEM; + else + status = (*p_proc_object->intf_fxns->pfn_brd_mem_map) + (p_proc_object->hbridge_context, pa_align, va_align, + size_align, ul_map_attr, map_obj->pages); + } + if (!status) { + /* Mapped address = MSB of VA | LSB of PA */ + *pp_map_addr = (void *) tmp_addr; + } else { + remove_mapping_information(pr_ctxt, tmp_addr, size_align); + dmm_un_map_memory(dmm_mgr, va_align, &size_align); + } + mutex_unlock(&proc_lock); + + if (status) + goto func_end; + +func_end: + dev_dbg(bridge, "%s: hprocessor %p, pmpu_addr %p, ul_size %x, " + "req_addr %p, ul_map_attr %x, pp_map_addr %p, va_align %x, " + "pa_align %x, size_align %x status 0x%x\n", __func__, + hprocessor, pmpu_addr, ul_size, req_addr, ul_map_attr, + pp_map_addr, va_align, pa_align, size_align, status); + + return status; +} + +/* + * ======== proc_register_notify ======== + * Purpose: + * Register to be notified of specific processor events. + */ +int proc_register_notify(void *hprocessor, u32 event_mask, + u32 notify_type, struct dsp_notification + * hnotification) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct deh_mgr *hdeh_mgr; + + DBC_REQUIRE(hnotification != NULL); + DBC_REQUIRE(refs > 0); + + /* Check processor handle */ + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + /* Check if event mask is a valid processor related event */ + if (event_mask & ~(DSP_PROCESSORSTATECHANGE | DSP_PROCESSORATTACH | + DSP_PROCESSORDETACH | DSP_PROCESSORRESTART | + DSP_MMUFAULT | DSP_SYSERROR | DSP_PWRERROR | + DSP_WDTOVERFLOW)) + status = -EINVAL; + + /* Check if notify type is valid */ + if (notify_type != DSP_SIGNALEVENT) + status = -EINVAL; + + if (!status) { + /* If event mask is not DSP_SYSERROR, DSP_MMUFAULT, + * or DSP_PWRERROR then register event immediately. */ + if (event_mask & + ~(DSP_SYSERROR | DSP_MMUFAULT | DSP_PWRERROR | + DSP_WDTOVERFLOW)) { + status = ntfy_register(p_proc_object->ntfy_obj, + hnotification, event_mask, + notify_type); + /* Special case alert, special case alert! + * If we're trying to *deregister* (i.e. event_mask + * is 0), a DSP_SYSERROR or DSP_MMUFAULT notification, + * we have to deregister with the DEH manager. + * There's no way to know, based on event_mask which + * manager the notification event was registered with, + * so if we're trying to deregister and ntfy_register + * failed, we'll give the deh manager a shot. + */ + if ((event_mask == 0) && status) { + status = + dev_get_deh_mgr(p_proc_object->hdev_obj, + &hdeh_mgr); + status = + bridge_deh_register_notify(hdeh_mgr, + event_mask, + notify_type, + hnotification); + } + } else { + status = dev_get_deh_mgr(p_proc_object->hdev_obj, + &hdeh_mgr); + status = + bridge_deh_register_notify(hdeh_mgr, + event_mask, + notify_type, + hnotification); + + } + } +func_end: + return status; +} + +/* + * ======== proc_reserve_memory ======== + * Purpose: + * Reserve a virtually contiguous region of DSP address space. + */ +int proc_reserve_memory(void *hprocessor, u32 ul_size, + void **pp_rsv_addr, + struct process_context *pr_ctxt) +{ + struct dmm_object *dmm_mgr; + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct dmm_rsv_object *rsv_obj; + + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + + status = dmm_get_handle(p_proc_object, &dmm_mgr); + if (!dmm_mgr) { + status = -EFAULT; + goto func_end; + } + + status = dmm_reserve_memory(dmm_mgr, ul_size, (u32 *) pp_rsv_addr); + if (status != 0) + goto func_end; + + /* + * A successful reserve should be followed by insertion of rsv_obj + * into dmm_rsv_list, so that reserved memory resource tracking + * remains uptodate + */ + rsv_obj = kmalloc(sizeof(struct dmm_rsv_object), GFP_KERNEL); + if (rsv_obj) { + rsv_obj->dsp_reserved_addr = (u32) *pp_rsv_addr; + spin_lock(&pr_ctxt->dmm_rsv_lock); + list_add(&rsv_obj->link, &pr_ctxt->dmm_rsv_list); + spin_unlock(&pr_ctxt->dmm_rsv_lock); + } + +func_end: + dev_dbg(bridge, "%s: hprocessor: 0x%p ul_size: 0x%x pp_rsv_addr: 0x%p " + "status 0x%x\n", __func__, hprocessor, + ul_size, pp_rsv_addr, status); + return status; +} + +/* + * ======== proc_start ======== + * Purpose: + * Start a processor running. + */ +int proc_start(void *hprocessor) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct cod_manager *cod_mgr; /* Code manager handle */ + u32 dw_dsp_addr; /* Loaded code's entry point. */ + int brd_state; + + DBC_REQUIRE(refs > 0); + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + /* Call the bridge_brd_start */ + if (p_proc_object->proc_state != PROC_LOADED) { + status = -EBADR; + goto func_end; + } + status = dev_get_cod_mgr(p_proc_object->hdev_obj, &cod_mgr); + if (!cod_mgr) { + status = -EFAULT; + goto func_cont; + } + + status = cod_get_entry(cod_mgr, &dw_dsp_addr); + if (status) + goto func_cont; + + status = (*p_proc_object->intf_fxns->pfn_brd_start) + (p_proc_object->hbridge_context, dw_dsp_addr); + if (status) + goto func_cont; + + /* Call dev_create2 */ + status = dev_create2(p_proc_object->hdev_obj); + if (!status) { + p_proc_object->proc_state = PROC_RUNNING; + /* Deep sleep switces off the peripheral clocks. + * we just put the DSP CPU in idle in the idle loop. + * so there is no need to send a command to DSP */ + + if (p_proc_object->ntfy_obj) { + proc_notify_clients(p_proc_object, + DSP_PROCESSORSTATECHANGE); + } + } else { + /* Failed to Create Node Manager and DISP Object + * Stop the Processor from running. Put it in STOPPED State */ + (void)(*p_proc_object->intf_fxns-> + pfn_brd_stop) (p_proc_object->hbridge_context); + p_proc_object->proc_state = PROC_STOPPED; + } +func_cont: + if (!status) { + if (!((*p_proc_object->intf_fxns->pfn_brd_status) + (p_proc_object->hbridge_context, &brd_state))) { + pr_info("%s: dsp in running state\n", __func__); + DBC_ASSERT(brd_state != BRD_HIBERNATION); + } + } else { + pr_err("%s: Failed to start the dsp\n", __func__); + proc_stop(p_proc_object); + } + +func_end: + DBC_ENSURE((!status && p_proc_object->proc_state == + PROC_RUNNING) || status); + return status; +} + +/* + * ======== proc_stop ======== + * Purpose: + * Stop a processor running. + */ +int proc_stop(void *hprocessor) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct msg_mgr *hmsg_mgr; + struct node_mgr *hnode_mgr; + void *hnode; + u32 node_tab_size = 1; + u32 num_nodes = 0; + u32 nodes_allocated = 0; + int brd_state; + + DBC_REQUIRE(refs > 0); + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + /* check if there are any running nodes */ + status = dev_get_node_manager(p_proc_object->hdev_obj, &hnode_mgr); + if (!status && hnode_mgr) { + status = node_enum_nodes(hnode_mgr, &hnode, node_tab_size, + &num_nodes, &nodes_allocated); + if ((status == -EINVAL) || (nodes_allocated > 0)) { + pr_err("%s: Can't stop device, active nodes = %d \n", + __func__, nodes_allocated); + return -EBADR; + } + } + /* Call the bridge_brd_stop */ + /* It is OK to stop a device that does n't have nodes OR not started */ + status = + (*p_proc_object->intf_fxns-> + pfn_brd_stop) (p_proc_object->hbridge_context); + if (!status) { + dev_dbg(bridge, "%s: processor in standby mode\n", __func__); + p_proc_object->proc_state = PROC_STOPPED; + /* Destory the Node Manager, msg_ctrl Manager */ + if (!(dev_destroy2(p_proc_object->hdev_obj))) { + /* Destroy the msg_ctrl by calling msg_delete */ + dev_get_msg_mgr(p_proc_object->hdev_obj, &hmsg_mgr); + if (hmsg_mgr) { + msg_delete(hmsg_mgr); + dev_set_msg_mgr(p_proc_object->hdev_obj, NULL); + } + if (!((*p_proc_object-> + intf_fxns->pfn_brd_status) (p_proc_object-> + hbridge_context, + &brd_state))) + DBC_ASSERT(brd_state == BRD_STOPPED); + } + } else { + pr_err("%s: Failed to stop the processor\n", __func__); + } +func_end: + + return status; +} + +/* + * ======== proc_un_map ======== + * Purpose: + * Removes a MPU buffer mapping from the DSP address space. + */ +int proc_un_map(void *hprocessor, void *map_addr, + struct process_context *pr_ctxt) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct dmm_object *dmm_mgr; + u32 va_align; + u32 size_align; + + va_align = PG_ALIGN_LOW((u32) map_addr, PG_SIZE4K); + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + + status = dmm_get_handle(hprocessor, &dmm_mgr); + if (!dmm_mgr) { + status = -EFAULT; + goto func_end; + } + + /* Critical section */ + mutex_lock(&proc_lock); + /* + * Update DMM structures. Get the size to unmap. + * This function returns error if the VA is not mapped + */ + status = dmm_un_map_memory(dmm_mgr, (u32) va_align, &size_align); + /* Remove mapping from the page tables. */ + if (!status) { + status = (*p_proc_object->intf_fxns->pfn_brd_mem_un_map) + (p_proc_object->hbridge_context, va_align, size_align); + } + + mutex_unlock(&proc_lock); + if (status) + goto func_end; + + /* + * A successful unmap should be followed by removal of map_obj + * from dmm_map_list, so that mapped memory resource tracking + * remains uptodate + */ + remove_mapping_information(pr_ctxt, (u32) map_addr, size_align); + +func_end: + dev_dbg(bridge, "%s: hprocessor: 0x%p map_addr: 0x%p status: 0x%x\n", + __func__, hprocessor, map_addr, status); + return status; +} + +/* + * ======== proc_un_reserve_memory ======== + * Purpose: + * Frees a previously reserved region of DSP address space. + */ +int proc_un_reserve_memory(void *hprocessor, void *prsv_addr, + struct process_context *pr_ctxt) +{ + struct dmm_object *dmm_mgr; + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)hprocessor; + struct dmm_rsv_object *rsv_obj; + + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + + status = dmm_get_handle(p_proc_object, &dmm_mgr); + if (!dmm_mgr) { + status = -EFAULT; + goto func_end; + } + + status = dmm_un_reserve_memory(dmm_mgr, (u32) prsv_addr); + if (status != 0) + goto func_end; + + /* + * A successful unreserve should be followed by removal of rsv_obj + * from dmm_rsv_list, so that reserved memory resource tracking + * remains uptodate + */ + spin_lock(&pr_ctxt->dmm_rsv_lock); + list_for_each_entry(rsv_obj, &pr_ctxt->dmm_rsv_list, link) { + if (rsv_obj->dsp_reserved_addr == (u32) prsv_addr) { + list_del(&rsv_obj->link); + kfree(rsv_obj); + break; + } + } + spin_unlock(&pr_ctxt->dmm_rsv_lock); + +func_end: + dev_dbg(bridge, "%s: hprocessor: 0x%p prsv_addr: 0x%p status: 0x%x\n", + __func__, hprocessor, prsv_addr, status); + return status; +} + +/* + * ======== = proc_monitor ======== == + * Purpose: + * Place the Processor in Monitor State. This is an internal + * function and a requirement before Processor is loaded. + * This does a bridge_brd_stop, dev_destroy2 and bridge_brd_monitor. + * In dev_destroy2 we delete the node manager. + * Parameters: + * p_proc_object: Pointer to Processor Object + * Returns: + * 0: Processor placed in monitor mode. + * !0: Failed to place processor in monitor mode. + * Requires: + * Valid Processor Handle + * Ensures: + * Success: ProcObject state is PROC_IDLE + */ +static int proc_monitor(struct proc_object *proc_obj) +{ + int status = -EPERM; + struct msg_mgr *hmsg_mgr; + int brd_state; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(proc_obj); + + /* This is needed only when Device is loaded when it is + * already 'ACTIVE' */ + /* Destory the Node Manager, msg_ctrl Manager */ + if (!dev_destroy2(proc_obj->hdev_obj)) { + /* Destroy the msg_ctrl by calling msg_delete */ + dev_get_msg_mgr(proc_obj->hdev_obj, &hmsg_mgr); + if (hmsg_mgr) { + msg_delete(hmsg_mgr); + dev_set_msg_mgr(proc_obj->hdev_obj, NULL); + } + } + /* Place the Board in the Monitor State */ + if (!((*proc_obj->intf_fxns->pfn_brd_monitor) + (proc_obj->hbridge_context))) { + status = 0; + if (!((*proc_obj->intf_fxns->pfn_brd_status) + (proc_obj->hbridge_context, &brd_state))) + DBC_ASSERT(brd_state == BRD_IDLE); + } + + DBC_ENSURE((!status && brd_state == BRD_IDLE) || + status); + return status; +} + +/* + * ======== get_envp_count ======== + * Purpose: + * Return the number of elements in the envp array, including the + * terminating NULL element. + */ +static s32 get_envp_count(char **envp) +{ + s32 ret = 0; + if (envp) { + while (*envp++) + ret++; + + ret += 1; /* Include the terminating NULL in the count. */ + } + + return ret; +} + +/* + * ======== prepend_envp ======== + * Purpose: + * Prepend an environment variable=value pair to the new envp array, and + * copy in the existing var=value pairs in the old envp array. + */ +static char **prepend_envp(char **new_envp, char **envp, s32 envp_elems, + s32 cnew_envp, char *sz_var) +{ + char **pp_envp = new_envp; + + DBC_REQUIRE(new_envp); + + /* Prepend new environ var=value string */ + *new_envp++ = sz_var; + + /* Copy user's environment into our own. */ + while (envp_elems--) + *new_envp++ = *envp++; + + /* Ensure NULL terminates the new environment strings array. */ + if (envp_elems == 0) + *new_envp = NULL; + + return pp_envp; +} + +/* + * ======== proc_notify_clients ======== + * Purpose: + * Notify the processor the events. + */ +int proc_notify_clients(void *proc, u32 events) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)proc; + + DBC_REQUIRE(p_proc_object); + DBC_REQUIRE(is_valid_proc_event(events)); + DBC_REQUIRE(refs > 0); + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + + ntfy_notify(p_proc_object->ntfy_obj, events); +func_end: + return status; +} + +/* + * ======== proc_notify_all_clients ======== + * Purpose: + * Notify the processor the events. This includes notifying all clients + * attached to a particulat DSP. + */ +int proc_notify_all_clients(void *proc, u32 events) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)proc; + + DBC_REQUIRE(is_valid_proc_event(events)); + DBC_REQUIRE(refs > 0); + + if (!p_proc_object) { + status = -EFAULT; + goto func_end; + } + + dev_notify_clients(p_proc_object->hdev_obj, events); + +func_end: + return status; +} + +/* + * ======== proc_get_processor_id ======== + * Purpose: + * Retrieves the processor ID. + */ +int proc_get_processor_id(void *proc, u32 * proc_id) +{ + int status = 0; + struct proc_object *p_proc_object = (struct proc_object *)proc; + + if (p_proc_object) + *proc_id = p_proc_object->processor_id; + else + status = -EFAULT; + + return status; +} |