blob: 242dd139999674a900057a8ba5182d215149d351 [file] [log] [blame]
/*
* 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;
}