/* * 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/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 *dev_obj; /* Device this PROC represents */ u32 process; /* Process owning this Processor */ struct mgr_object *mgr_obj; /* Manager Object Handle */ u32 attach_count; /* Processor attach count */ u32 processor_id; /* Processor number */ u32 timeout; /* Time out count */ enum dsp_procstate proc_state; /* Processor state */ u32 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 *bridge_context; /* Function interface to Bridge driver */ struct bridge_drv_interface *intf_fxns; char *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; struct drv_data *drv_datap = dev_get_drvdata(bridge); u8 dev_type; DBC_REQUIRE(refs > 0); DBC_REQUIRE(ph_processor != NULL); if (pr_ctxt->processor) { *ph_processor = pr_ctxt->processor; return status; } /* Get the Driver and Manager Object Handles */ if (!drv_datap || !drv_datap->drv_object || !drv_datap->mgr_object) { status = -ENODATA; pr_err("%s: Failed to get object handles\n", __func__); } else { hdrv_obj = drv_datap->drv_object; hmgr_obj = drv_datap->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->dev_obj = hdev_obj; p_proc_object->mgr_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->timeout = attr_in->timeout; else p_proc_object->timeout = 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->bridge_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 */ status = dev_insert_proc_object(p_proc_object->dev_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->processor = *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; struct drv_data *drv_datap = dev_get_drvdata(bridge); dev_get_dev_type(hdev_obj, (u8 *) &dev_type); if (!exec_file) return -EFAULT; if (dev_type == DSP_UNIT) { if (!drv_datap || !drv_datap->base_img) return -EFAULT; if (strlen(drv_datap->base_img) > size) return -EINVAL; strcpy(exec_file, drv_datap->base_img); } else if (dev_type == IVA_UNIT && iva_img) { len = strlen(iva_img); strncpy(exec_file, iva_img, len + 1); } else { return -ENOENT; } return 0; } /* * ======== 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; struct drv_data *drv_datap = dev_get_drvdata(bridge); u8 dev_type; DBC_REQUIRE(refs > 0); DBC_REQUIRE(dev_node_obj != NULL); DBC_REQUIRE(hdev_obj != NULL); /* Create a Dummy PROC Object */ if (!drv_datap || !drv_datap->mgr_object) { status = -ENODATA; pr_err("%s: Failed to retrieve the object handle\n", __func__); goto func_end; } else { hmgr_obj = drv_datap->mgr_object; } p_proc_object = kzalloc(sizeof(struct proc_object), GFP_KERNEL); if (p_proc_object == NULL) { status = -ENOMEM; goto func_end; } p_proc_object->dev_obj = hdev_obj; p_proc_object->mgr_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->bridge_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->last_coff); p_proc_object->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->dev_cntrl) (p_proc_object->bridge_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->processor; 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->last_coff); p_proc_object->last_coff = NULL; /* Remove the Proc from the DEV List */ (void)dev_remove_proc_object(p_proc_object->dev_obj, (u32) p_proc_object); /* Free the Processor Object */ kfree(p_proc_object); pr_ctxt->processor = 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->dev_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); mutex_lock(&proc_lock); /* 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 no_map; } 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; } no_map: mutex_unlock(&proc_lock); 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); mutex_lock(&proc_lock); /* 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 no_map; } 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; } no_map: mutex_unlock(&proc_lock); 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->dev_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->dev_obj, &hio_mgr); if (hio_mgr) status = p_proc_object->intf_fxns-> 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->dev_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->brd_status) (p_proc_object->bridge_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->dev_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->mgr_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->last_coff != NULL) { status = dcd_auto_unregister(hdcd_handle, p_proc_object-> last_coff); /* Regardless of auto unregister status, * free previously allocated * memory. */ kfree(p_proc_object->last_coff); p_proc_object->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->mgr_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->last_coff == NULL); /* Allocate memory for pszLastCoff */ p_proc_object->last_coff = kzalloc((strlen(user_args[0]) + 1), GFP_KERNEL); /* If memory allocated, save COFF file name */ if (p_proc_object->last_coff) { strncpy(p_proc_object->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->dev_obj, &hmsg_mgr); if (!hmsg_mgr) { status = msg_create(&hmsg_mgr, p_proc_object->dev_obj, (msg_onexit) node_on_exit); DBC_ASSERT(!status); dev_set_msg_mgr(p_proc_object->dev_obj, hmsg_mgr); } } if (!status) { /* Set the Device object's message manager */ status = dev_get_io_mgr(p_proc_object->dev_obj, &hio_mgr); if (hio_mgr) status = (*p_proc_object->intf_fxns->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->dev_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->brd_set_state) (p_proc_object->bridge_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->dev_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->brd_status) (p_proc_object->bridge_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->brd_mem_map) (p_proc_object->bridge_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->dev_obj, &hdeh_mgr); status = bridge_deh_register_notify(hdeh_mgr, event_mask, notify_type, hnotification); } } else { status = dev_get_deh_mgr(p_proc_object->dev_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->dev_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->brd_start) (p_proc_object->bridge_context, dw_dsp_addr); if (status) goto func_cont; /* Call dev_create2 */ status = dev_create2(p_proc_object->dev_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-> brd_stop) (p_proc_object->bridge_context); p_proc_object->proc_state = PROC_STOPPED; } func_cont: if (!status) { if (!((*p_proc_object->intf_fxns->brd_status) (p_proc_object->bridge_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->dev_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-> brd_stop) (p_proc_object->bridge_context); if (!status) { dev_dbg(bridge, "%s: processor in standby mode\n", __func__); p_proc_object->proc_state = PROC_STOPPED; /* Destroy the Node Manager, msg_ctrl Manager */ if (!(dev_destroy2(p_proc_object->dev_obj))) { /* Destroy the msg_ctrl by calling msg_delete */ dev_get_msg_mgr(p_proc_object->dev_obj, &hmsg_mgr); if (hmsg_mgr) { msg_delete(hmsg_mgr); dev_set_msg_mgr(p_proc_object->dev_obj, NULL); } if (!((*p_proc_object-> intf_fxns->brd_status) (p_proc_object-> bridge_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->brd_mem_un_map) (p_proc_object->bridge_context, va_align, size_align); } if (status) goto unmap_failed; /* * 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); unmap_failed: mutex_unlock(&proc_lock); 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' */ /* Destroy the Node Manager, msg_ctrl Manager */ if (!dev_destroy2(proc_obj->dev_obj)) { /* Destroy the msg_ctrl by calling msg_delete */ dev_get_msg_mgr(proc_obj->dev_obj, &hmsg_mgr); if (hmsg_mgr) { msg_delete(hmsg_mgr); dev_set_msg_mgr(proc_obj->dev_obj, NULL); } } /* Place the Board in the Monitor State */ if (!((*proc_obj->intf_fxns->brd_monitor) (proc_obj->bridge_context))) { status = 0; if (!((*proc_obj->intf_fxns->brd_status) (proc_obj->bridge_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->dev_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; }