1c824e7faf
fixes the following errors ERROR: "foo * bar" should be "foo *bar" +int proc_ctrl(void *hprocessor, u32 dw_cmd, struct dsp_cbdata * arg) ERROR: "foo * bar" should be "foo *bar" +int proc_get_trace(void *hprocessor, u8 * pbuf, u32 max_size) ERROR: space prohibited after that '*' (ctx:ExW) + * hnotification) ^ ERROR: "foo * bar" should be "foo *bar" +int proc_get_processor_id(void *proc, u32 * proc_id) Signed-off-by: Devendra Naga <devendra.aaru@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1834 lines
48 KiB
C
1834 lines
48 KiB
C
/*
|
|
* 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>
|
|
|
|
/* ----------------------------------- 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;
|
|
};
|
|
|
|
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)
|
|
return NULL;
|
|
|
|
INIT_LIST_HEAD(&map_obj->link);
|
|
|
|
map_obj->pages = kcalloc(num_usr_pgs, sizeof(struct page *),
|
|
GFP_KERNEL);
|
|
if (!map_obj->pages) {
|
|
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;
|
|
|
|
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 Processor 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:
|
|
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;
|
|
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 {
|
|
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;
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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) {
|
|
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;
|
|
|
|
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;
|
|
|
|
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 */
|
|
|
|
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_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;
|
|
|
|
if (p_proc_object) {
|
|
*device_obj = p_proc_object->dev_obj;
|
|
status = 0;
|
|
} else {
|
|
*device_obj = NULL;
|
|
status = -EFAULT;
|
|
}
|
|
|
|
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;
|
|
|
|
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_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
|
|
|
|
#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 {
|
|
/* 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);
|
|
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 = kstrdup(pargv0, GFP_KERNEL);
|
|
if (!drv_datap->base_img)
|
|
status = -ENOMEM;
|
|
}
|
|
}
|
|
|
|
func_end:
|
|
if (status) {
|
|
pr_err("%s: Processor failed to load\n", __func__);
|
|
proc_stop(p_proc_object);
|
|
}
|
|
#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;
|
|
|
|
/* 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;
|
|
|
|
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__);
|
|
}
|
|
} else {
|
|
pr_err("%s: Failed to start the dsp\n", __func__);
|
|
proc_stop(p_proc_object);
|
|
}
|
|
|
|
func_end:
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
} 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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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;
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|