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