![Tejun Heo](/assets/img/avatar_default.png)
idr_get_new*() and friends are about to be deprecated. Convert to the new idr_alloc() interface. There are some peculiarities and possible bugs in the converted functions. This patch preserves those. * drv_insert_node_res_element() returns -ENOMEM on alloc failure, -EFAULT if id space is exhausted. -EFAULT is at best misleading. * drv_proc_insert_strm_res_element() is even weirder. It returns -EFAULT if kzalloc() fails, -ENOMEM if idr preloading fails and -EPERM if id space is exhausted. What's going on here? * drv_proc_insert_strm_res_element() doesn't free *pstrm_res after failure. Only compile tested. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Víctor Manuel Jáquez Leal <vjaquez@igalia.com> Cc: Rene Sapiens <rene.sapiens@ti.com> Cc: Armando Uribe <x0095078@ti.com> Cc: Omar Ramirez Luna <omar.ramirez@ti.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
817 lines
21 KiB
C
817 lines
21 KiB
C
/*
|
|
* drv.c
|
|
*
|
|
* DSP-BIOS Bridge driver support functions for TI OMAP processors.
|
|
*
|
|
* DSP/BIOS Bridge resource allocation module.
|
|
*
|
|
* 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>
|
|
#include <linux/list.h>
|
|
|
|
/* ----------------------------------- Host OS */
|
|
#include <dspbridge/host_os.h>
|
|
|
|
/* ----------------------------------- DSP/BIOS Bridge */
|
|
#include <dspbridge/dbdefs.h>
|
|
|
|
/* ----------------------------------- This */
|
|
#include <dspbridge/drv.h>
|
|
#include <dspbridge/dev.h>
|
|
|
|
#include <dspbridge/node.h>
|
|
#include <dspbridge/proc.h>
|
|
#include <dspbridge/strm.h>
|
|
#include <dspbridge/nodepriv.h>
|
|
#include <dspbridge/dspchnl.h>
|
|
#include <dspbridge/resourcecleanup.h>
|
|
|
|
/* ----------------------------------- Defines, Data Structures, Typedefs */
|
|
struct drv_object {
|
|
struct list_head dev_list;
|
|
struct list_head dev_node_string;
|
|
};
|
|
|
|
/*
|
|
* This is the Device Extension. Named with the Prefix
|
|
* DRV_ since it is living in this module
|
|
*/
|
|
struct drv_ext {
|
|
struct list_head link;
|
|
char sz_string[MAXREGPATHLENGTH];
|
|
};
|
|
|
|
/* ----------------------------------- Globals */
|
|
static bool ext_phys_mem_pool_enabled;
|
|
struct ext_phys_mem_pool {
|
|
u32 phys_mem_base;
|
|
u32 phys_mem_size;
|
|
u32 virt_mem_base;
|
|
u32 next_phys_alloc_ptr;
|
|
};
|
|
static struct ext_phys_mem_pool ext_mem_pool;
|
|
|
|
/* ----------------------------------- Function Prototypes */
|
|
static int request_bridge_resources(struct cfg_hostres *res);
|
|
|
|
|
|
/* GPP PROCESS CLEANUP CODE */
|
|
|
|
static int drv_proc_free_node_res(int id, void *p, void *data);
|
|
|
|
/* Allocate and add a node resource element
|
|
* This function is called from .Node_Allocate. */
|
|
int drv_insert_node_res_element(void *hnode, void *node_resource,
|
|
void *process_ctxt)
|
|
{
|
|
struct node_res_object **node_res_obj =
|
|
(struct node_res_object **)node_resource;
|
|
struct process_context *ctxt = (struct process_context *)process_ctxt;
|
|
int retval;
|
|
|
|
*node_res_obj = kzalloc(sizeof(struct node_res_object), GFP_KERNEL);
|
|
if (!*node_res_obj)
|
|
return -ENOMEM;
|
|
|
|
(*node_res_obj)->node = hnode;
|
|
retval = idr_alloc(ctxt->node_id, *node_res_obj, 0, 0, GFP_KERNEL);
|
|
if (retval >= 0) {
|
|
(*node_res_obj)->id = retval;
|
|
return 0;
|
|
}
|
|
|
|
kfree(*node_res_obj);
|
|
|
|
if (retval == -ENOSPC) {
|
|
pr_err("%s: FAILED, IDR is FULL\n", __func__);
|
|
return -EFAULT;
|
|
} else {
|
|
pr_err("%s: OUT OF MEMORY\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
/* Release all Node resources and its context
|
|
* Actual Node De-Allocation */
|
|
static int drv_proc_free_node_res(int id, void *p, void *data)
|
|
{
|
|
struct process_context *ctxt = data;
|
|
int status;
|
|
struct node_res_object *node_res_obj = p;
|
|
u32 node_state;
|
|
|
|
if (node_res_obj->node_allocated) {
|
|
node_state = node_get_state(node_res_obj->node);
|
|
if (node_state <= NODE_DELETING) {
|
|
if ((node_state == NODE_RUNNING) ||
|
|
(node_state == NODE_PAUSED) ||
|
|
(node_state == NODE_TERMINATING))
|
|
node_terminate
|
|
(node_res_obj->node, &status);
|
|
|
|
node_delete(node_res_obj, ctxt);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Release all Mapped and Reserved DMM resources */
|
|
int drv_remove_all_dmm_res_elements(void *process_ctxt)
|
|
{
|
|
struct process_context *ctxt = (struct process_context *)process_ctxt;
|
|
int status = 0;
|
|
struct dmm_map_object *temp_map, *map_obj;
|
|
struct dmm_rsv_object *temp_rsv, *rsv_obj;
|
|
|
|
/* Free DMM mapped memory resources */
|
|
list_for_each_entry_safe(map_obj, temp_map, &ctxt->dmm_map_list, link) {
|
|
status = proc_un_map(ctxt->processor,
|
|
(void *)map_obj->dsp_addr, ctxt);
|
|
if (status)
|
|
pr_err("%s: proc_un_map failed!"
|
|
" status = 0x%xn", __func__, status);
|
|
}
|
|
|
|
/* Free DMM reserved memory resources */
|
|
list_for_each_entry_safe(rsv_obj, temp_rsv, &ctxt->dmm_rsv_list, link) {
|
|
status = proc_un_reserve_memory(ctxt->processor, (void *)
|
|
rsv_obj->dsp_reserved_addr,
|
|
ctxt);
|
|
if (status)
|
|
pr_err("%s: proc_un_reserve_memory failed!"
|
|
" status = 0x%xn", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* Update Node allocation status */
|
|
void drv_proc_node_update_status(void *node_resource, s32 status)
|
|
{
|
|
struct node_res_object *node_res_obj =
|
|
(struct node_res_object *)node_resource;
|
|
node_res_obj->node_allocated = status;
|
|
}
|
|
|
|
/* Update Node Heap status */
|
|
void drv_proc_node_update_heap_status(void *node_resource, s32 status)
|
|
{
|
|
struct node_res_object *node_res_obj =
|
|
(struct node_res_object *)node_resource;
|
|
node_res_obj->heap_allocated = status;
|
|
}
|
|
|
|
/* Release all Node resources and its context
|
|
* This is called from .bridge_release.
|
|
*/
|
|
int drv_remove_all_node_res_elements(void *process_ctxt)
|
|
{
|
|
struct process_context *ctxt = process_ctxt;
|
|
|
|
idr_for_each(ctxt->node_id, drv_proc_free_node_res, ctxt);
|
|
idr_destroy(ctxt->node_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Allocate the STRM resource element
|
|
* This is called after the actual resource is allocated
|
|
*/
|
|
int drv_proc_insert_strm_res_element(void *stream_obj,
|
|
void *strm_res, void *process_ctxt)
|
|
{
|
|
struct strm_res_object **pstrm_res =
|
|
(struct strm_res_object **)strm_res;
|
|
struct process_context *ctxt = (struct process_context *)process_ctxt;
|
|
int retval;
|
|
|
|
*pstrm_res = kzalloc(sizeof(struct strm_res_object), GFP_KERNEL);
|
|
if (*pstrm_res == NULL)
|
|
return -EFAULT;
|
|
|
|
(*pstrm_res)->stream = stream_obj;
|
|
retval = idr_alloc(ctxt->stream_id, *pstrm_res, 0, 0, GFP_KERNEL);
|
|
if (retval >= 0) {
|
|
(*pstrm_res)->id = retval;
|
|
return 0;
|
|
}
|
|
|
|
if (retval == -ENOSPC) {
|
|
pr_err("%s: FAILED, IDR is FULL\n", __func__);
|
|
return -EPERM;
|
|
} else {
|
|
pr_err("%s: OUT OF MEMORY\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
static int drv_proc_free_strm_res(int id, void *p, void *process_ctxt)
|
|
{
|
|
struct process_context *ctxt = process_ctxt;
|
|
struct strm_res_object *strm_res = p;
|
|
struct stream_info strm_info;
|
|
struct dsp_streaminfo user;
|
|
u8 **ap_buffer = NULL;
|
|
u8 *buf_ptr;
|
|
u32 ul_bytes;
|
|
u32 dw_arg;
|
|
s32 ul_buf_size;
|
|
|
|
if (strm_res->num_bufs) {
|
|
ap_buffer = kmalloc((strm_res->num_bufs *
|
|
sizeof(u8 *)), GFP_KERNEL);
|
|
if (ap_buffer) {
|
|
strm_free_buffer(strm_res,
|
|
ap_buffer,
|
|
strm_res->num_bufs,
|
|
ctxt);
|
|
kfree(ap_buffer);
|
|
}
|
|
}
|
|
strm_info.user_strm = &user;
|
|
user.number_bufs_in_stream = 0;
|
|
strm_get_info(strm_res->stream, &strm_info, sizeof(strm_info));
|
|
while (user.number_bufs_in_stream--)
|
|
strm_reclaim(strm_res->stream, &buf_ptr, &ul_bytes,
|
|
(u32 *) &ul_buf_size, &dw_arg);
|
|
strm_close(strm_res, ctxt);
|
|
return 0;
|
|
}
|
|
|
|
/* Release all Stream resources and its context
|
|
* This is called from .bridge_release.
|
|
*/
|
|
int drv_remove_all_strm_res_elements(void *process_ctxt)
|
|
{
|
|
struct process_context *ctxt = process_ctxt;
|
|
|
|
idr_for_each(ctxt->stream_id, drv_proc_free_strm_res, ctxt);
|
|
idr_destroy(ctxt->stream_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Updating the stream resource element */
|
|
int drv_proc_update_strm_res(u32 num_bufs, void *strm_resources)
|
|
{
|
|
int status = 0;
|
|
struct strm_res_object **strm_res =
|
|
(struct strm_res_object **)strm_resources;
|
|
|
|
(*strm_res)->num_bufs = num_bufs;
|
|
return status;
|
|
}
|
|
|
|
/* GPP PROCESS CLEANUP CODE END */
|
|
|
|
/*
|
|
* ======== = drv_create ======== =
|
|
* Purpose:
|
|
* DRV Object gets created only once during Driver Loading.
|
|
*/
|
|
int drv_create(struct drv_object **drv_obj)
|
|
{
|
|
int status = 0;
|
|
struct drv_object *pdrv_object = NULL;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
pdrv_object = kzalloc(sizeof(struct drv_object), GFP_KERNEL);
|
|
if (pdrv_object) {
|
|
/* Create and Initialize List of device objects */
|
|
INIT_LIST_HEAD(&pdrv_object->dev_list);
|
|
INIT_LIST_HEAD(&pdrv_object->dev_node_string);
|
|
} else {
|
|
status = -ENOMEM;
|
|
}
|
|
/* Store the DRV Object in the driver data */
|
|
if (!status) {
|
|
if (drv_datap) {
|
|
drv_datap->drv_object = (void *)pdrv_object;
|
|
} else {
|
|
status = -EPERM;
|
|
pr_err("%s: Failed to store DRV object\n", __func__);
|
|
}
|
|
}
|
|
|
|
if (!status) {
|
|
*drv_obj = pdrv_object;
|
|
} else {
|
|
/* Free the DRV Object */
|
|
kfree(pdrv_object);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== = drv_destroy ======== =
|
|
* purpose:
|
|
* Invoked during bridge de-initialization
|
|
*/
|
|
int drv_destroy(struct drv_object *driver_obj)
|
|
{
|
|
int status = 0;
|
|
struct drv_object *pdrv_object = (struct drv_object *)driver_obj;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
kfree(pdrv_object);
|
|
/* Update the DRV Object in the driver data */
|
|
if (drv_datap) {
|
|
drv_datap->drv_object = NULL;
|
|
} else {
|
|
status = -EPERM;
|
|
pr_err("%s: Failed to store DRV object\n", __func__);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_get_dev_object ========
|
|
* Purpose:
|
|
* Given a index, returns a handle to DevObject from the list.
|
|
*/
|
|
int drv_get_dev_object(u32 index, struct drv_object *hdrv_obj,
|
|
struct dev_object **device_obj)
|
|
{
|
|
int status = 0;
|
|
struct dev_object *dev_obj;
|
|
u32 i;
|
|
|
|
dev_obj = (struct dev_object *)drv_get_first_dev_object();
|
|
for (i = 0; i < index; i++) {
|
|
dev_obj =
|
|
(struct dev_object *)drv_get_next_dev_object((u32) dev_obj);
|
|
}
|
|
if (dev_obj) {
|
|
*device_obj = (struct dev_object *)dev_obj;
|
|
} else {
|
|
*device_obj = NULL;
|
|
status = -EPERM;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_get_first_dev_object ========
|
|
* Purpose:
|
|
* Retrieve the first Device Object handle from an internal linked list of
|
|
* of DEV_OBJECTs maintained by DRV.
|
|
*/
|
|
u32 drv_get_first_dev_object(void)
|
|
{
|
|
u32 dw_dev_object = 0;
|
|
struct drv_object *pdrv_obj;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
if (drv_datap && drv_datap->drv_object) {
|
|
pdrv_obj = drv_datap->drv_object;
|
|
if (!list_empty(&pdrv_obj->dev_list))
|
|
dw_dev_object = (u32) pdrv_obj->dev_list.next;
|
|
} else {
|
|
pr_err("%s: Failed to retrieve the object handle\n", __func__);
|
|
}
|
|
|
|
return dw_dev_object;
|
|
}
|
|
|
|
/*
|
|
* ======== DRV_GetFirstDevNodeString ========
|
|
* Purpose:
|
|
* Retrieve the first Device Extension from an internal linked list of
|
|
* of Pointer to dev_node Strings maintained by DRV.
|
|
*/
|
|
u32 drv_get_first_dev_extension(void)
|
|
{
|
|
u32 dw_dev_extension = 0;
|
|
struct drv_object *pdrv_obj;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
if (drv_datap && drv_datap->drv_object) {
|
|
pdrv_obj = drv_datap->drv_object;
|
|
if (!list_empty(&pdrv_obj->dev_node_string)) {
|
|
dw_dev_extension =
|
|
(u32) pdrv_obj->dev_node_string.next;
|
|
}
|
|
} else {
|
|
pr_err("%s: Failed to retrieve the object handle\n", __func__);
|
|
}
|
|
|
|
return dw_dev_extension;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_get_next_dev_object ========
|
|
* Purpose:
|
|
* Retrieve the next Device Object handle from an internal linked list of
|
|
* of DEV_OBJECTs maintained by DRV, after having previously called
|
|
* drv_get_first_dev_object() and zero or more DRV_GetNext.
|
|
*/
|
|
u32 drv_get_next_dev_object(u32 hdev_obj)
|
|
{
|
|
u32 dw_next_dev_object = 0;
|
|
struct drv_object *pdrv_obj;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
struct list_head *curr;
|
|
|
|
if (drv_datap && drv_datap->drv_object) {
|
|
pdrv_obj = drv_datap->drv_object;
|
|
if (!list_empty(&pdrv_obj->dev_list)) {
|
|
curr = (struct list_head *)hdev_obj;
|
|
if (list_is_last(curr, &pdrv_obj->dev_list))
|
|
return 0;
|
|
dw_next_dev_object = (u32) curr->next;
|
|
}
|
|
} else {
|
|
pr_err("%s: Failed to retrieve the object handle\n", __func__);
|
|
}
|
|
|
|
return dw_next_dev_object;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_get_next_dev_extension ========
|
|
* Purpose:
|
|
* Retrieve the next Device Extension from an internal linked list of
|
|
* of pointer to DevNodeString maintained by DRV, after having previously
|
|
* called drv_get_first_dev_extension() and zero or more
|
|
* drv_get_next_dev_extension().
|
|
*/
|
|
u32 drv_get_next_dev_extension(u32 dev_extension)
|
|
{
|
|
u32 dw_dev_extension = 0;
|
|
struct drv_object *pdrv_obj;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
struct list_head *curr;
|
|
|
|
if (drv_datap && drv_datap->drv_object) {
|
|
pdrv_obj = drv_datap->drv_object;
|
|
if (!list_empty(&pdrv_obj->dev_node_string)) {
|
|
curr = (struct list_head *)dev_extension;
|
|
if (list_is_last(curr, &pdrv_obj->dev_node_string))
|
|
return 0;
|
|
dw_dev_extension = (u32) curr->next;
|
|
}
|
|
} else {
|
|
pr_err("%s: Failed to retrieve the object handle\n", __func__);
|
|
}
|
|
|
|
return dw_dev_extension;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_insert_dev_object ========
|
|
* Purpose:
|
|
* Insert a DevObject into the list of Manager object.
|
|
*/
|
|
int drv_insert_dev_object(struct drv_object *driver_obj,
|
|
struct dev_object *hdev_obj)
|
|
{
|
|
struct drv_object *pdrv_object = (struct drv_object *)driver_obj;
|
|
|
|
list_add_tail((struct list_head *)hdev_obj, &pdrv_object->dev_list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_remove_dev_object ========
|
|
* Purpose:
|
|
* Search for and remove a DeviceObject from the given list of DRV
|
|
* objects.
|
|
*/
|
|
int drv_remove_dev_object(struct drv_object *driver_obj,
|
|
struct dev_object *hdev_obj)
|
|
{
|
|
int status = -EPERM;
|
|
struct drv_object *pdrv_object = (struct drv_object *)driver_obj;
|
|
struct list_head *cur_elem;
|
|
|
|
/* Search list for p_proc_object: */
|
|
list_for_each(cur_elem, &pdrv_object->dev_list) {
|
|
/* If found, remove it. */
|
|
if ((struct dev_object *)cur_elem == hdev_obj) {
|
|
list_del(cur_elem);
|
|
status = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_request_resources ========
|
|
* Purpose:
|
|
* Requests resources from the OS.
|
|
*/
|
|
int drv_request_resources(u32 dw_context, u32 *dev_node_strg)
|
|
{
|
|
int status = 0;
|
|
struct drv_object *pdrv_object;
|
|
struct drv_ext *pszdev_node;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
/*
|
|
* Allocate memory to hold the string. This will live until
|
|
* it is freed in the Release resources. Update the driver object
|
|
* list.
|
|
*/
|
|
|
|
if (!drv_datap || !drv_datap->drv_object)
|
|
status = -ENODATA;
|
|
else
|
|
pdrv_object = drv_datap->drv_object;
|
|
|
|
if (!status) {
|
|
pszdev_node = kzalloc(sizeof(struct drv_ext), GFP_KERNEL);
|
|
if (pszdev_node) {
|
|
strncpy(pszdev_node->sz_string,
|
|
(char *)dw_context, MAXREGPATHLENGTH - 1);
|
|
pszdev_node->sz_string[MAXREGPATHLENGTH - 1] = '\0';
|
|
/* Update the Driver Object List */
|
|
*dev_node_strg = (u32) pszdev_node->sz_string;
|
|
list_add_tail(&pszdev_node->link,
|
|
&pdrv_object->dev_node_string);
|
|
} else {
|
|
status = -ENOMEM;
|
|
*dev_node_strg = 0;
|
|
}
|
|
} else {
|
|
dev_dbg(bridge, "%s: Failed to get Driver Object from Registry",
|
|
__func__);
|
|
*dev_node_strg = 0;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_release_resources ========
|
|
* Purpose:
|
|
* Releases resources from the OS.
|
|
*/
|
|
int drv_release_resources(u32 dw_context, struct drv_object *hdrv_obj)
|
|
{
|
|
int status = 0;
|
|
struct drv_ext *pszdev_node;
|
|
|
|
/*
|
|
* Irrespective of the status go ahead and clean it
|
|
* The following will over write the status.
|
|
*/
|
|
for (pszdev_node = (struct drv_ext *)drv_get_first_dev_extension();
|
|
pszdev_node != NULL; pszdev_node = (struct drv_ext *)
|
|
drv_get_next_dev_extension((u32) pszdev_node)) {
|
|
if ((u32) pszdev_node == dw_context) {
|
|
/* Found it */
|
|
/* Delete from the Driver object list */
|
|
list_del(&pszdev_node->link);
|
|
kfree(pszdev_node);
|
|
break;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* ======== request_bridge_resources ========
|
|
* Purpose:
|
|
* Reserves shared memory for bridge.
|
|
*/
|
|
static int request_bridge_resources(struct cfg_hostres *res)
|
|
{
|
|
struct cfg_hostres *host_res = res;
|
|
|
|
/* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */
|
|
host_res->num_mem_windows = 2;
|
|
|
|
/* First window is for DSP internal memory */
|
|
dev_dbg(bridge, "mem_base[0] 0x%x\n", host_res->mem_base[0]);
|
|
dev_dbg(bridge, "mem_base[3] 0x%x\n", host_res->mem_base[3]);
|
|
dev_dbg(bridge, "dmmu_base %p\n", host_res->dmmu_base);
|
|
|
|
/* for 24xx base port is not mapping the mamory for DSP
|
|
* internal memory TODO Do a ioremap here */
|
|
/* Second window is for DSP external memory shared with MPU */
|
|
|
|
/* These are hard-coded values */
|
|
host_res->birq_registers = 0;
|
|
host_res->birq_attrib = 0;
|
|
host_res->offset_for_monitor = 0;
|
|
host_res->chnl_offset = 0;
|
|
/* CHNL_MAXCHANNELS */
|
|
host_res->num_chnls = CHNL_MAXCHANNELS;
|
|
host_res->chnl_buf_size = 0x400;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ======== drv_request_bridge_res_dsp ========
|
|
* Purpose:
|
|
* Reserves shared memory for bridge.
|
|
*/
|
|
int drv_request_bridge_res_dsp(void **phost_resources)
|
|
{
|
|
int status = 0;
|
|
struct cfg_hostres *host_res;
|
|
u32 dw_buff_size;
|
|
u32 dma_addr;
|
|
u32 shm_size;
|
|
struct drv_data *drv_datap = dev_get_drvdata(bridge);
|
|
|
|
dw_buff_size = sizeof(struct cfg_hostres);
|
|
|
|
host_res = kzalloc(dw_buff_size, GFP_KERNEL);
|
|
|
|
if (host_res != NULL) {
|
|
request_bridge_resources(host_res);
|
|
/* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */
|
|
host_res->num_mem_windows = 4;
|
|
|
|
host_res->mem_base[0] = 0;
|
|
host_res->mem_base[2] = (u32) ioremap(OMAP_DSP_MEM1_BASE,
|
|
OMAP_DSP_MEM1_SIZE);
|
|
host_res->mem_base[3] = (u32) ioremap(OMAP_DSP_MEM2_BASE,
|
|
OMAP_DSP_MEM2_SIZE);
|
|
host_res->mem_base[4] = (u32) ioremap(OMAP_DSP_MEM3_BASE,
|
|
OMAP_DSP_MEM3_SIZE);
|
|
host_res->per_base = ioremap(OMAP_PER_CM_BASE,
|
|
OMAP_PER_CM_SIZE);
|
|
host_res->per_pm_base = ioremap(OMAP_PER_PRM_BASE,
|
|
OMAP_PER_PRM_SIZE);
|
|
host_res->core_pm_base = ioremap(OMAP_CORE_PRM_BASE,
|
|
OMAP_CORE_PRM_SIZE);
|
|
host_res->dmmu_base = ioremap(OMAP_DMMU_BASE,
|
|
OMAP_DMMU_SIZE);
|
|
|
|
dev_dbg(bridge, "mem_base[0] 0x%x\n",
|
|
host_res->mem_base[0]);
|
|
dev_dbg(bridge, "mem_base[1] 0x%x\n",
|
|
host_res->mem_base[1]);
|
|
dev_dbg(bridge, "mem_base[2] 0x%x\n",
|
|
host_res->mem_base[2]);
|
|
dev_dbg(bridge, "mem_base[3] 0x%x\n",
|
|
host_res->mem_base[3]);
|
|
dev_dbg(bridge, "mem_base[4] 0x%x\n",
|
|
host_res->mem_base[4]);
|
|
dev_dbg(bridge, "dmmu_base %p\n", host_res->dmmu_base);
|
|
|
|
shm_size = drv_datap->shm_size;
|
|
if (shm_size >= 0x10000) {
|
|
/* Allocate Physically contiguous,
|
|
* non-cacheable memory */
|
|
host_res->mem_base[1] =
|
|
(u32) mem_alloc_phys_mem(shm_size, 0x100000,
|
|
&dma_addr);
|
|
if (host_res->mem_base[1] == 0) {
|
|
status = -ENOMEM;
|
|
pr_err("shm reservation Failed\n");
|
|
} else {
|
|
host_res->mem_length[1] = shm_size;
|
|
host_res->mem_phys[1] = dma_addr;
|
|
|
|
dev_dbg(bridge, "%s: Bridge shm address 0x%x "
|
|
"dma_addr %x size %x\n", __func__,
|
|
host_res->mem_base[1],
|
|
dma_addr, shm_size);
|
|
}
|
|
}
|
|
if (!status) {
|
|
/* These are hard-coded values */
|
|
host_res->birq_registers = 0;
|
|
host_res->birq_attrib = 0;
|
|
host_res->offset_for_monitor = 0;
|
|
host_res->chnl_offset = 0;
|
|
/* CHNL_MAXCHANNELS */
|
|
host_res->num_chnls = CHNL_MAXCHANNELS;
|
|
host_res->chnl_buf_size = 0x400;
|
|
dw_buff_size = sizeof(struct cfg_hostres);
|
|
}
|
|
*phost_resources = host_res;
|
|
}
|
|
/* End Mem alloc */
|
|
return status;
|
|
}
|
|
|
|
void mem_ext_phys_pool_init(u32 pool_phys_base, u32 pool_size)
|
|
{
|
|
u32 pool_virt_base;
|
|
|
|
/* get the virtual address for the physical memory pool passed */
|
|
pool_virt_base = (u32) ioremap(pool_phys_base, pool_size);
|
|
|
|
if ((void **)pool_virt_base == NULL) {
|
|
pr_err("%s: external physical memory map failed\n", __func__);
|
|
ext_phys_mem_pool_enabled = false;
|
|
} else {
|
|
ext_mem_pool.phys_mem_base = pool_phys_base;
|
|
ext_mem_pool.phys_mem_size = pool_size;
|
|
ext_mem_pool.virt_mem_base = pool_virt_base;
|
|
ext_mem_pool.next_phys_alloc_ptr = pool_phys_base;
|
|
ext_phys_mem_pool_enabled = true;
|
|
}
|
|
}
|
|
|
|
void mem_ext_phys_pool_release(void)
|
|
{
|
|
if (ext_phys_mem_pool_enabled) {
|
|
iounmap((void *)(ext_mem_pool.virt_mem_base));
|
|
ext_phys_mem_pool_enabled = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== mem_ext_phys_mem_alloc ========
|
|
* Purpose:
|
|
* Allocate physically contiguous, uncached memory from external memory pool
|
|
*/
|
|
|
|
static void *mem_ext_phys_mem_alloc(u32 bytes, u32 align, u32 * phys_addr)
|
|
{
|
|
u32 new_alloc_ptr;
|
|
u32 offset;
|
|
u32 virt_addr;
|
|
|
|
if (align == 0)
|
|
align = 1;
|
|
|
|
if (bytes > ((ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size)
|
|
- ext_mem_pool.next_phys_alloc_ptr)) {
|
|
phys_addr = NULL;
|
|
return NULL;
|
|
} else {
|
|
offset = (ext_mem_pool.next_phys_alloc_ptr & (align - 1));
|
|
if (offset == 0)
|
|
new_alloc_ptr = ext_mem_pool.next_phys_alloc_ptr;
|
|
else
|
|
new_alloc_ptr = (ext_mem_pool.next_phys_alloc_ptr) +
|
|
(align - offset);
|
|
if ((new_alloc_ptr + bytes) <=
|
|
(ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size)) {
|
|
/* we can allocate */
|
|
*phys_addr = new_alloc_ptr;
|
|
ext_mem_pool.next_phys_alloc_ptr =
|
|
new_alloc_ptr + bytes;
|
|
virt_addr =
|
|
ext_mem_pool.virt_mem_base + (new_alloc_ptr -
|
|
ext_mem_pool.
|
|
phys_mem_base);
|
|
return (void *)virt_addr;
|
|
} else {
|
|
*phys_addr = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ======== mem_alloc_phys_mem ========
|
|
* Purpose:
|
|
* Allocate physically contiguous, uncached memory
|
|
*/
|
|
void *mem_alloc_phys_mem(u32 byte_size, u32 align_mask,
|
|
u32 *physical_address)
|
|
{
|
|
void *va_mem = NULL;
|
|
dma_addr_t pa_mem;
|
|
|
|
if (byte_size > 0) {
|
|
if (ext_phys_mem_pool_enabled) {
|
|
va_mem = mem_ext_phys_mem_alloc(byte_size, align_mask,
|
|
(u32 *) &pa_mem);
|
|
} else
|
|
va_mem = dma_alloc_coherent(NULL, byte_size, &pa_mem,
|
|
GFP_KERNEL);
|
|
if (va_mem == NULL)
|
|
*physical_address = 0;
|
|
else
|
|
*physical_address = pa_mem;
|
|
}
|
|
return va_mem;
|
|
}
|
|
|
|
/*
|
|
* ======== mem_free_phys_mem ========
|
|
* Purpose:
|
|
* Free the given block of physically contiguous memory.
|
|
*/
|
|
void mem_free_phys_mem(void *virtual_address, u32 physical_address,
|
|
u32 byte_size)
|
|
{
|
|
if (!ext_phys_mem_pool_enabled)
|
|
dma_free_coherent(NULL, byte_size, virtual_address,
|
|
physical_address);
|
|
}
|