int cpStart(int isReset)
{
	void __iomem *apcp_shmem;
	void __iomem *cp_boot_base;
	u32 reg_val;

	IPC_DEBUG(DBG_TRACE, "enter\n");

#ifdef CONFIG_BCM_MODEM_DEFER_CP_START
	CP_Boot();
#else
	if (isReset) {
		IPC_DEBUG(DBG_INFO, "call CP_Boot\n");
		CP_Boot();
	}
#endif

	apcp_shmem = ioremap_nocache(IPC_BASE, IPC_SIZE);
	if (!apcp_shmem) {
		IPC_DEBUG(DBG_ERROR, "IPC_BASE=0x%x, IPC_SIZE=0x%x\n",
			  IPC_BASE, IPC_SIZE);
		IPC_DEBUG(DBG_ERROR, "ioremap shmem failed\n");
		return -1;
	}
	/* clear first (9) 32-bit words in shared memory */
	memset(apcp_shmem, 0, IPC_SIZE);
	iounmap(apcp_shmem);
	IPC_DEBUG(DBG_TRACE, "cleared sh mem\n");

	cp_boot_base = ioremap_nocache(MODEM_DTCM_ADDRESS,
				       INIT_ADDRESS_OFFSET + RESERVED_HEADER);

	if (!cp_boot_base) {
		IPC_DEBUG(DBG_ERROR,
			  "DTCM Addr=0x%x, length=0x%x",
			  MODEM_DTCM_ADDRESS,
			  INIT_ADDRESS_OFFSET + RESERVED_HEADER);
		IPC_DEBUG(DBG_ERROR, "ioremap cp_boot_base error\n");
		return -1;
	}

	/* Start the CP:
	 *	- read main address
	 *	- write to init address
	 */
	reg_val = readl(cp_boot_base+MAIN_ADDRESS_OFFSET+RESERVED_HEADER);
	IPC_DEBUG(DBG_TRACE, "main addr 0x%x\n", reg_val);
	writel(reg_val, cp_boot_base+INIT_ADDRESS_OFFSET+RESERVED_HEADER);

	iounmap(cp_boot_base);
	IPC_DEBUG(DBG_TRACE, "modem (R4 COMMS) started ...\n");
	return 0;
}
/* registers client callback to be used for passing silent CP reset events */
int IPCAP_RegisterCPResetHandler(IPCAP_CPResetHandler_T inResetHandler)
{
	/* **FIXME** need to support multiple clients, or just RPC? */
	sCPResetHandler = inResetHandler;
	IPC_DEBUG(DBG_INFO, "cp reset handler registered\n");
	return 1;
}
示例#3
0
/** 
   @fn void ipcs_ipc_initialised(void);
*/
void ipcs_ipc_initialised(void)
{
   IPC_DEBUG(DBG_INFO,"IPC Initialization completed\n");
   g_ipc_info.ipc_state = 1;

   return;
}
示例#4
0
文件: ipc.c 项目: pombredanne/jos
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
// This function keeps trying until it succeeds.
// It should panic() on any error other than -E_IPC_NOT_RECV.
//
// Hint:
//   Use sys_yield() to be CPU-friendly.
//   If 'pg' is null, pass sys_ipc_try_send a value that it will understand
//   as meaning "no page".  (Zero is not the right value.)
void
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
{
	// LAB 4: Your code here.
	struct Env *env;
	int r;
	void *srcva;

	if (pg == NULL) {
		srcva = (void *)~0;
	} else {
		srcva = pg;
	}

	IPC_DEBUG("ipc_send: perm = %d\n", perm);
	
	while ((r = sys_ipc_try_send(to_env, val, srcva, perm)) != 0) {
		if (r != -E_IPC_NOT_RECV) {
			panic("ipc_send: sys_ipc_try_send erro %e\n", r);
		}
	}

	// I tested and found out a sys_yield() here actually made things slower!
	// sys_yield();

	return;

}
void ResetCP(void)
{
	void __iomem *cp_root_reset_base;
	void __iomem *cp_bmdm_reset_base;

	IPC_DEBUG(DBG_INFO, "resetting CP\n");

	/* reset CP - copy from cp_reset.cmm rxd from CP team */
	/*;CP reset, from AP
	 *
	 * D.S ZSD:0x35001F00 %LE %LONG 0xa5a501
	 * D.S ZSD:0x35001F08 %LE %LONG 0x3bd	;reset
	 * D.S ZSD:0x35001F08 %LE %LONG 0x3fd	;clear
	 */
	cp_root_reset_base = ioremap(ROOT_RST_BASE_ADDR,
				ROOT_RST_MGR_REG_PD_SOFT_RSTN_OFFSET+4);
	if (!cp_root_reset_base) {
		IPC_DEBUG(DBG_ERROR,
			"failed to remap ROOT_RST_BASE_ADDR, crashing\n");
		BUG();
	}
	writel(0xa5a501, cp_root_reset_base+ROOT_RST_MGR_REG_WR_ACCESS_OFFSET);
	writel(0x3bd, cp_root_reset_base+ROOT_RST_MGR_REG_PD_SOFT_RSTN_OFFSET);
	writel(0x3fd, cp_root_reset_base+ROOT_RST_MGR_REG_PD_SOFT_RSTN_OFFSET);

	/* reset R4 - copy from cp_reset.cmm rxd from CP team */
	/*;R4 reset
	*
	* D.S ZSD:0x3a055f00 %LE %LONG 0xa5a501
	* D.S ZSD:0x3a055f18 %LE %LONG 0x2 ;reset
	* D.S ZSD:0x3a055f18 %LE %LONG 0x3  ;clear
	*/
	cp_bmdm_reset_base = ioremap(BMDM_RST_BASE_ADDR,
				BMDM_RST_MGR_REG_CP_RSTN_OFFSET+4);
	if (!cp_bmdm_reset_base) {
		IPC_DEBUG(DBG_ERROR,
			"failed to remap BMDM_RST_BASE_ADDR, crashing\n");
		BUG();
	}
	writel(0xa5a501, cp_bmdm_reset_base+BMDM_RST_MGR_REG_WR_ACCESS_OFFSET);
	writel(0x2, cp_bmdm_reset_base+BMDM_RST_MGR_REG_CP_RSTN_OFFSET);
	writel(0x3, cp_bmdm_reset_base+BMDM_RST_MGR_REG_CP_RSTN_OFFSET);

	iounmap(cp_root_reset_base);
	iounmap(cp_bmdm_reset_base);

}
void ipcs_intr_workqueue_process(struct work_struct *work)
{
    static int first = 1;
    if (first)
    {
        first = 0;
        set_user_nice(current, -16);
    }


	if(IpcCPCrashCheck())
	{
		cp_crashed  = 1;
		//patch-s from Broadcom for csp#529767 reason:FALSE file=ripisr_cp.c line=342 code=-1/0xffffffff tastk=RIP_H TS=48744870/FN=4873
		//reviewed by yuan
				/* Dumping Camera registers. MobC00180667 */
		{
			int i = 0;
			unsigned int camera_addr_base = 0x08440000; // Receiver Status reg set
			printk("\n Dumping Camera registers\n");
			for(i = 0; i < 8; i ++) {
				printk("Addr: 0x%x Value: 0x%x\n", camera_addr_base, readl(io_p2v(camera_addr_base)));
				camera_addr_base += 4;
			}
			camera_addr_base = 0x08440080; // Debug register set
			for(i = 0; i < 9; i ++) {
				printk("Addr: 0x%x Value: 0x%x\n", camera_addr_base, readl(io_p2v(camera_addr_base)));
				camera_addr_base += 4;
			}
			camera_addr_base = 0x08440100; // Channel information set
			for(i = 0; i < 12; i ++) {
				printk("Addr: 0x%x Value: 0x%x\n", camera_addr_base, readl(io_p2v(camera_addr_base)));
				camera_addr_base += 4;
			}
			camera_addr_base = 0x08440400; // Ping pong buffer management
			printk("Addr: 0x%x Value: 0x%x\n", camera_addr_base, readl(io_p2v(camera_addr_base)));
		}
		/* End of Camera register dump */
		//patch-e from Broadcom for csp#529767 reason:FALSE file=ripisr_cp.c line=342 code=-1/0xffffffff tastk=RIP_H TS=48744870/FN=4873

		if( BCMLOG_CPCRASH_MTD == BCMLOG_GetCpCrashDumpDevice() )
		{
			/* we kill AP when CP crashes */
			IPC_DEBUG(DBG_INFO, "Crashing AP now...\n\n");
			abort();  
		} else 
		{
			ProcessCPCrashedDump(work);
		}

		IPC_ProcessEvents();
	}else
	{
		IPC_ProcessEvents();  
#ifdef CONFIG_HAS_WAKELOCK 
	       wake_unlock(&ipc_wake_lock);
#endif // CONFIG_HAS_WAKELOCK
	}
}
示例#7
0
/**
   unsigned int bcm_map_virt_to_phys(void *virt_addr);
*/
unsigned int bcm_map_virt_to_phys(void *virt_addr)
{
   //Note: since shared-mem is io-mem, these kernel call to map virt to phys does not work
   //return((unsigned int)virt_to_phys(virt_addr));

   IPC_DEBUG(DBG_ERROR, "%s: should not be used on this address\n", __FUNCTION__);
   return(0);
}
示例#8
0
/**
   void *bcm_map_phys_to_virt(unsigned int physical_addr);
*/
void *bcm_map_phys_to_virt(unsigned int physical_addr)
{
   //Note: since shared-mem is io-mem, these kernel call to map phys to virt does not work
   //return((void *)phys_to_virt((unsigned long)physical_addr));

   IPC_DEBUG(DBG_ERROR, "%s: should not be used on this address\n", __FUNCTION__);
   return(NULL);
}
/* callback from client indicating it is ready for CP reset */
void IPCAP_ReadyForReset(int inClientID)
{
	IPC_DEBUG(DBG_INFO, "ready for reset\n");

	/* get rid of the ack timeout timer */
	del_timer(&cp_reset_timer);

	cp_reset_clients_acked = 1;
}
/*************************************************
*   Reloads the CP images.
*
*****************************************************/
static void ReloadCP(void)
{
	int ret;
	int index;

	for (index = 0; (g_cp_imgs[index].img_name != NULL); index++) {
		IPC_DEBUG(DBG_INFO, "LoadFirmware for %s @ %p, size %d\n",
			  g_cp_imgs[index].img_name,
			  (void *)g_cp_imgs[index].ram_addr,
			  g_cp_imgs[index].img_size);
		ret = LoadFirmware(ipcs_get_drvdata(),
				   g_cp_imgs[index].img_name,
				   g_cp_imgs[index].ram_addr,
				   g_cp_imgs[index].img_size);
		IPC_DEBUG(DBG_INFO, "LoadFirmware for %s returned %d\n",
			  g_cp_imgs[index].img_name, ret);
	}
}
static IPC_ReturnCode_T EventDelete(void *Event)
{
	struct IPC_Evt_t *ipcEvt = (struct IPC_Evt_t *)Event;
	if (ipcEvt) {
		IPC_DEBUG(DBG_TRACE, "EventDelete: %p\n", ipcEvt);
		kfree(ipcEvt);
	}
	return IPC_OK;
}
int HandleRestartCP(void *data)
{
	IPC_DEBUG(DBG_INFO, "enter\n");

	IPC_DEBUG(DBG_INFO, "call local_irq_disable()\n");
	local_irq_disable();

	IPC_DEBUG(DBG_INFO, "resetting CP\n");
	ResetCP();

	/* reload CP */
	IPC_DEBUG(DBG_INFO, "reloading CP\n");
	ReloadCP();

	IPC_DEBUG(DBG_INFO, "rebooting CP\n");
	/* reboot CP; this will also wipe IPC shared memory */
	Comms_Start(1);

	IPC_DEBUG(DBG_INFO, "call local_irq_enable()\n");
	local_irq_enable();
	enable_irq(IRQ_IPC_C2A);

	IPC_DEBUG(DBG_INFO, "re-init IPC\n");
	/* reinitialize IPC, and wait for IPC sync with CP */
	if (ipcs_reinitialize_ipc()) {
		IPC_DEBUG(DBG_ERROR, "ipcs_reinitialize_ipc failed\n");
		/* CP didn't re-sync, so crash AP here */
		BUG();
	}

	/* give CP some time to boot; without this delay, we hang
	 * on the CAPI2_PhoneCtrlApi_ProcessPowerUpReq() from RIL
	 */
	msleep(2000);

	/* notify clients that we're back in business... */
	IPC_DEBUG(DBG_INFO, "notifying clients CP reset is complete\n");
	HandleCPResetDone();
	IPC_DEBUG(DBG_INFO, "notification done, exiting reset thread\n");

	/* done with thread */
	do_exit(0);
}
/**
   static int ipcs_init(void *smbase, unsigned int size)
 */
static int ipcs_init(void *smbase, unsigned int size, int isReset)
{
	int rc = 0;

	IPC_DEBUG(DBG_TRACE, "WaitForCpIpc\n");

	/* Wait for CP to initialize */
	WaitForCpIpc(smbase);
	IPC_DEBUG(DBG_TRACE, "Calling ipc_set_interrupt_mask()\n");
	ipc_set_interrupt_mask();
	IPC_DEBUG(DBG_TRACE, "Done ipc_set_interrupt_mask()\n");

	IPC_DEBUG(DBG_TRACE, "Calling ipc_set_interrupt_mask()\n");
	ipc_set_interrupt_mask();
	IPC_DEBUG(DBG_TRACE, "Done ipc_set_interrupt_mask()\n");


	/* Initialize OS specific callbacks with the IPC lib */
	rc = ipc_ipc_init(smbase, size);
	if (rc) {
		IPC_DEBUG(DBG_ERROR, "ipc_ipc_init() failed, ret[%d]\n", rc);
		return rc;
	}
	IPC_DEBUG(DBG_TRACE, "ipc_ipc_init done\n");

	/* Register Endpoints */
	rc = ipcs_ccb_init(isReset);
	if (rc) {
		IPC_DEBUG(DBG_ERROR, "ipcs_ccb_init() failed, ret[%d]\n", rc);
		return rc;
	}

	IPC_DEBUG(DBG_TRACE, "ipcs_ccb_init done\n");
	/* Let CP know that we are done registering endpoints */
	IPC_Configured();

	/* Wait for IPC initialized */
	IPC_DEBUG(DBG_TRACE, "IPC_Configured() invoked\n");

	return 0;
}
/*************************************************
*   Loads the image into RAM.
*
*	@param	len		(in) The size of the image.
*	@param	p_data		(in) Pointer to the image data.
*	@param	addr		(in) The RAM address to load the image into.
*
*	@return Returns 0 on success, non-zero otherwise.
*
*****************************************************/
static int DownloadFirmware(uint32_t len, const uint8_t *p_data, uint32_t addr)
{
	void __iomem *virtAddr;
	int ret = 0;

	IPC_DEBUG(DBG_INFO, "Downloading %d bytes from %p to address %p\n",
		  len, (void *)p_data, (void *)addr);

	virtAddr = ioremap_nocache(addr, len);
	if (NULL == virtAddr) {
		IPC_DEBUG(DBG_ERROR, "*** ERROR: ioremap_nocache failed\n");
		ret = -1;
	} else {
		IPC_DEBUG(DBG_INFO, "copying to virtual addr %p\n",
			  (void *)virtAddr);
		memcpy(virtAddr, p_data, len);
		iounmap(virtAddr);
	}

	return ret;
}
/**
 * unsigned int bcm_map_virt_to_phys(void *virt_addr);
 */
unsigned int bcm_map_virt_to_phys(void *virt_addr)
{
	/**
	 * Note: since shared-mem is io-mem, these kernel call to map
	 * virt to phys does not work
	 *
	 * return((unsigned int)virt_to_phys(virt_addr));
	 */
	IPC_DEBUG(DBG_TRACE, "should not be used on this address\n");

	return 0;
}
/**
 * void *bcm_map_phys_to_virt(unsigned int physical_addr);
 */
void *bcm_map_phys_to_virt(unsigned int physical_addr)
{
	/**
	 * Note: since shared-mem is io-mem, these kernel call to map
	 * phys to virt does not work
	 *
	 * return((void *)phys_to_virt((unsigned long)physical_addr));
	 */
	IPC_DEBUG(DBG_TRACE, "should not be used on this address\n");

	return NULL;
}
/*************************************************
*   Loads the image into RAM.
*
*	@param	len		(in) The size of the image.
*	@param	p_data		(in) Pointer to the image data.
*	@param	addr		(in) The RAM address to load the image into.
*
*	@return Returns 0 on success, non-zero otherwise.
*
*****************************************************/
static int DownloadFirmware(uint32_t len, const uint8_t *p_data, uint32_t addr)
{
	void __iomem *virtAddr;
	int ret = 0;
	int count;
	int chunk_size;

	IPC_DEBUG(DBG_INFO, "Downloading %d bytes from %p to address %p\n",
		len, (void *)p_data, (void *)addr);

	chunk_size = (VIRTUAL_MEM_CHUNK_SIZE > len) ? len
					: VIRTUAL_MEM_CHUNK_SIZE;
	for (count = 0; count < len; count += chunk_size) {
		if (count + chunk_size > len) {
			/* chunk size is too large for last segment */
			chunk_size = len - count;
			IPC_DEBUG(DBG_INFO,
				"*** chunk_size: truncated to %d\n",
				chunk_size);
		}
		virtAddr = ioremap_nocache(addr + count, chunk_size);
		if (NULL == virtAddr) {
			IPC_DEBUG(DBG_ERROR,
				"*** ERROR: ioremap_nocache failed\n");
			ret = -1;
			break;
		} else {
			IPC_DEBUG(DBG_INFO, "[%d] copying to virtual addr %p\n",
				count, (void *)virtAddr);
			memcpy(virtAddr, p_data + count, chunk_size);
			iounmap(virtAddr);
		}
	}

	return ret;
}
static void *EventCreate(void)
{
	struct IPC_Evt_t *ipcEvt;

	ipcEvt = kmalloc(sizeof(struct IPC_Evt_t), GFP_ATOMIC);
	if (!ipcEvt)
	{
		IPC_DEBUG(DBG_ERROR, "IPC event create fail\n");
		return NULL;
	}

	init_waitqueue_head(&(ipcEvt->evt_wait));
	ipcEvt->evt = 0;

	return ipcEvt;
}
/*********************************************************************
*
*   Retrieve string from physical address
*
*	@param inPhysAddr   (in) Physical address of string.
*	@param inStrBuf	    (in) Pointer to buffer to copy string into.
*	@param inStrBufLen	(in) Size of inStrBuf in bytes.
*	@return	Null terminated string from physical address is
*                       copied in to buffer pointed to by inStrBuf.
*
***********************************************************************/
void GetStringFromPA(UInt32 inPhysAddr, char *inStrBuf, UInt32 inStrBufLen)
{
	void __iomem *virtAddr;

	virtAddr = plat_ioremap_ns((unsigned long __force)
			get_vaddr(IPC_CP_STRING_MAP_AREA),
			IPC_CP_STRING_MAP_AREA_SZ,
			(phys_addr_t)inPhysAddr);
	if (!virtAddr) {
		IPC_DEBUG(DBG_ERROR,
			"ioremap failed in GetStringFromPA\n");
		return;
	}

	strncpy(inStrBuf, (char *)virtAddr, inStrBufLen);

	/* pad NULL in the end of the string */
	inStrBuf[inStrBufLen - 1] = '\0';
	plat_iounmap_ns(get_vaddr(IPC_CP_STRING_MAP_AREA),
			free_size_ipc(IPC_CP_STRING_MAP_AREA_SZ));
}
示例#20
0
static irqreturn_t ipcs_interrupt(int irq, void *dev_id)
{
   //IPC_DEBUG(DBG_INFO, "[ipc]: ipcs_interrupt\n");
   // check for early CP interrupt
   if( 1 == g_ipc_info.ap_ipc_init_done )
   {
#ifdef CONFIG_HAS_WAKELOCK
       wake_lock(&ipc_wake_lock);
#endif
       queue_work(g_ipc_info.intr_workqueue, &g_ipc_info.intr_work);
   }

   else
   {
        // if we're interrupted before IPC is setup, that 
        // means CP has had an early crash....
        IPC_DEBUG(DBG_INFO, "[ipc]:  abnormal CP interrupt\n");
        sEarlyCPInterrupt = 1;
   }

  return IRQ_HANDLED;
}
示例#21
0
文件: ipc.c 项目: pombredanne/jos
// Receive a value via IPC and return it.
// If 'pg' is nonnull, then any page sent by the sender will be mapped at
//	that address.
// If 'from_env_store' is nonnull, then store the IPC sender's envid in
//	*from_env_store.
// If 'perm_store' is nonnull, then store the IPC sender's page permission
//	in *perm_store (this is nonzero iff a page was successfully
//	transferred to 'pg').
// If the system call fails, then store 0 in *fromenv and *perm (if
//	they're nonnull) and return the error.
// Otherwise, return the value sent by the sender
//
// Hint:
//   Use 'thisenv' to discover the value and who sent it.
//   If 'pg' is null, pass sys_ipc_recv a value that it will understand
//   as meaning "no page".  (Zero is not the right value, since that's
//   a perfectly valid place to map a page.)
int32_t
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
{
	// LAB 4: Your code here.

	void *dstva;
	int r;
	
	if (pg == NULL) {
		dstva = (void *)~0;
	} else {
		dstva = pg;
	}
	
	IPC_DEBUG("ipc_recv: dstva = %p\n", dstva);

	r = sys_ipc_recv(dstva);
	if (r < 0) {
		if (from_env_store) {
			*from_env_store = 0;
		}
		if (perm_store) {
			*perm_store = 0;
		}
		return r;
	}

	if (from_env_store) {
		*from_env_store = thisenv->env_ipc_from;
	}
	if (perm_store) {
		*perm_store = thisenv->env_ipc_perm;
	}

	return thisenv->env_ipc_value;

}
示例#22
0
void ipcs_intr_workqueue_process(struct work_struct *work)
{
    static int first = 1;
    if (first)
    {
        first = 0;
        set_user_nice(current, -16);
    }


   if(IpcCPCrashCheck())
   {
		cp_crashed  = 1;
		if( BCMLOG_CPCRASH_MTD == BCMLOG_GetCpCrashDumpDevice() )
		{
		/* we kill AP when CP crashes */
			IPC_DEBUG(DBG_INFO, "Crashing AP now...\n\n");
#if defined(CONFIG_SEC_DEBUG)
    			cp_abort();
#else
    			abort();
#endif
		} else 
		{
		schedule_work(&g_ipc_info.cp_crash_dump_wq);
	}

		IPC_ProcessEvents();
	}else
	{
       IPC_ProcessEvents();  
#ifdef CONFIG_HAS_WAKELOCK 
       wake_unlock(&ipc_wake_lock);
#endif // CONFIG_HAS_WAKELOCK
   }
}
static int CP_Boot(void)
{
	int started = 0;
	void __iomem *cp_boot_itcm;
	void __iomem *cp_bmodem_r4cfg;
	unsigned int r4init;
	unsigned int jump_instruction = 0xEA000000;

#define BMODEM_SYSCFG_R4_CFG0  0x3a004000
#define CP_SYSCFG_BASE_SIZE    0x8
#define CP_ITCM_BASE_SIZE      0x1000     /* 1 4k page */

	IPC_DEBUG(DBG_TRACE, "enter\n");
	cp_bmodem_r4cfg = ioremap(BMODEM_SYSCFG_R4_CFG0, CP_SYSCFG_BASE_SIZE);
	if (!cp_bmodem_r4cfg) {
		IPC_DEBUG(DBG_ERROR,
		"BMODEM_SYSCFG_R4_CFG0=0x%x, CP_SYSCFG_BASE_SIZE=0x%x\n",
			BMODEM_SYSCFG_R4_CFG0, CP_SYSCFG_BASE_SIZE);
		IPC_DEBUG(DBG_ERROR, "ioremap cp_bmodem_r4cfg failed\n");
		return started;
	}

	r4init = *(unsigned int *)(cp_bmodem_r4cfg);

	/* check if the CP is already booted, and if not, then boot it */
	if ((0x5 != (r4init & 0x5))) {
		IPC_DEBUG(DBG_TRACE, "boot (R4 COMMS) - init code 0x%x ...\n",
			  r4init);

		/* Set the CP jump to address.
		   CP must jump to DTCM offset 0x400 */
		cp_boot_itcm = ioremap(MODEM_ITCM_ADDRESS, CP_ITCM_BASE_SIZE);
		if (!cp_boot_itcm) {
			IPC_DEBUG(DBG_ERROR,
			"MODEM_ITCM_ADDRESS=0x%x, CP_ITCM_BASE_SIZE=0x%x\n",
			MODEM_ITCM_ADDRESS, CP_ITCM_BASE_SIZE);
			IPC_DEBUG(DBG_ERROR, "ioremap cp_boot_itcm failed\n");
			return 0;
		}
		/* generate instruction for reset vector that jumps to start of
		 * cp_boot.img at 0x20400
		 */
		jump_instruction |=
		    (0x00FFFFFFUL & (((0x10000 + RESERVED_HEADER) / 4) - 2));

		IPC_DEBUG(DBG_TRACE, "cp_boot_itcm 0x%x jump_instruction 0x%x\n",
				(unsigned int)cp_boot_itcm,
				jump_instruction);
		/* write jump instruction to cp reset vector */
		*(unsigned int *)(cp_boot_itcm) = jump_instruction;

		iounmap(cp_boot_itcm);

		/* start CP - should jump to 0x20400 and spin there */
		*(unsigned int *)(cp_bmodem_r4cfg) = 0x5;
	}
	else {
		IPC_DEBUG(DBG_TRACE,
			"(R4 COMMS) already started - init code 0x%x ...\n",
			r4init);
	}

	iounmap(cp_bmodem_r4cfg);
	IPC_DEBUG(DBG_TRACE, "exit\n");

	return started;
}
void CPReset_Timer_Callback(unsigned long data)
{
	/* not all IPC/RPC clients ackd the reset in time, so crash AP */
	IPC_DEBUG(DBG_INFO, "cp reset timeout %ld jiffies\n", jiffies);
	BUG();
}
/****************************************************************
**
*   Utility function to retrieve full CP RAM dump log for crash log
*
*
*******************************************************************/
void DUMP_CPMemoryByList(struct T_RAMDUMP_BLOCK *mem_dump)
{
	UInt32 i, offset;
	void __iomem *RamDumpBlockVAddr = NULL;
	struct T_RAMDUMP_BLOCK *pBlockVAddr = NULL;

	RamDumpBlockVAddr = ioremap_nocache((UInt32)(mem_dump),
					    (MAX_RAMDUMP_BLOCKS *
					     sizeof(struct T_RAMDUMP_BLOCK)));
	if (NULL == RamDumpBlockVAddr) {
		IPC_DEBUG(DBG_ERROR, "failed to remap RAM dump block addr\n");
		return;
	}

	pBlockVAddr = (struct T_RAMDUMP_BLOCK *)RamDumpBlockVAddr;

	BCMLOG_LogCPCrashDumpString("===== COMMS PROCESSOR memory dump =====");

	i = 0;
	while (i < MAX_RAMDUMP_BLOCKS && pBlockVAddr[i].name[0] != '\0'
	       && pBlockVAddr[i].mem_size != 0) {
		if (pBlockVAddr[i].mem_start == SIM_DEBUG_DATA) {
			offset = (pBlockVAddr[i].name[4] << 24) +
				 (pBlockVAddr[i].name[5] << 16) +
				 (pBlockVAddr[i].name[6] << 8) +
				 pBlockVAddr[i].name[7];
			snprintf(assert_buf,
				 ASSERT_BUF_SIZE,
				 "FLASH DUMP: %8s, start=0x%08x, size=0x%08x, image_start=0x%08x, offset_in_image=0x%08x",
				 pBlockVAddr[i].name,
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size,
				 0,	/* MSP_IMAGE_ADDR, */
				 (int)offset);
		} else if (pBlockVAddr[i].mem_start == SIM_AP_DEBUG_DATA) {
			offset = (pBlockVAddr[i].name[4] << 24) +
				 (pBlockVAddr[i].name[5] << 16) +
				 (pBlockVAddr[i].name[6] << 8) +
				 pBlockVAddr[i].name[7];
			snprintf(assert_buf,
				 ASSERT_BUF_SIZE,
				 "FLASH DUMP: %8s, start=0x%08x, size=0x%08x, image_start=0x%08x, offset_in_image=0x%08x",
				 pBlockVAddr[i].name,
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size,
				 0,	/* AP_IMAGE_ADDR, */
				 (int)offset);
		} else {
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "RAM   DUMP: %8s, start=0x%08x, size=0x%08x, buffer_in_main=0x%08x",
				 pBlockVAddr[i].name,
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size,
				 pBlockVAddr[i].buffer_in_main);
		}
		BCMLOG_LogCPCrashDumpString(assert_buf);
		i++;
	}
	i = 0;
	while (i < MAX_RAMDUMP_BLOCKS && pBlockVAddr[i].name[0] != '\0'
	       && pBlockVAddr[i].mem_size != 0) {
		if (pBlockVAddr[i].mem_start == SIM_DEBUG_DATA) {
			offset = (pBlockVAddr[i].name[4] << 24) +
				 (pBlockVAddr[i].name[5] << 16) +
				 (pBlockVAddr[i].name[6] << 8) +
				 pBlockVAddr[i].name[7];
			BCMLOG_LogCPCrashDumpString(pBlockVAddr[i].name);
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "FLASH DUMP Begin: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);
			/* **FIXME** MAG - flash dump not supported yet... */
			/* DUMP_CompressedFlash(cpu, pBlockVAddr[i].mem_start,
			   pBlockVAddr[i].mem_size, MSP_IMAGE_ADDR, offset); */
			BCMLOG_LogCPCrashDumpString
			    ("*** FLASH DUMP NOT SUPPORTED YET ***");
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "FLASH DUMP End: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);
		} else if (pBlockVAddr[i].mem_start == SIM_AP_DEBUG_DATA) {
			offset = (pBlockVAddr[i].name[4] << 24) +
				 (pBlockVAddr[i].name[5] << 16) +
				 (pBlockVAddr[i].name[6] << 8) +
				 pBlockVAddr[i].name[7];
			BCMLOG_LogCPCrashDumpString(pBlockVAddr[i].name);
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "FLASH DUMP Begin: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);
			/* **FIXME** MAG - flash dump not supported yet... */
			BCMLOG_LogCPCrashDumpString
			    ("*** FLASH DUMP NOT SUPPORTED YET ***");
			/* DUMP_CompressedFlash(cpu, pBlockVAddr[i].mem_start,
			   pBlockVAddr[i].mem_size, AP_IMAGE_ADDR, offset); */
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "FLASH DUMP End: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);
		} else if (pBlockVAddr[i].buffer_in_main == 0xFFFFFFFF) {
			BCMLOG_LogCPCrashDumpString(pBlockVAddr[i].name);
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "RAM DUMP Begin: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);

			/* BCMLOG_HandleCpCrashMemDumpData takes
			   physical address... */
			BCMLOG_HandleCpCrashMemDumpData((const char *)
							pBlockVAddr
							[i].mem_start,
							pBlockVAddr
							[i].mem_size);
			snprintf(assert_buf, ASSERT_BUF_SIZE,
				 "RAM DUMP End: 0x%08x, 0x%08x",
				 pBlockVAddr[i].mem_start,
				 pBlockVAddr[i].mem_size);
			BCMLOG_LogCPCrashDumpString(assert_buf);
		}
		i++;
	}

	iounmap(RamDumpBlockVAddr);

}
/******************************************************************
*   Utility function to retrieve full crash log from CP via simple
*   handshake protocol.
*
*
********************************************************************/
void DUMP_CP_assert_log(void)
{
	UInt32 t1, i, size, retryCount;
	UInt8 *p;
	UInt32 packetCount = 0;
	void __iomem *AssertLogVAddr = NULL;
	struct file *sdDumpFile = NULL;
	int cpReset = SmLocalControl.SmControl->CrashCode ==
	    IPC_CP_SILENT_RESET_READY;

	/* put logging driver into crash dump mode; messages will be sent
	 * straight out to MTT via RNDIS (or dump file) instead of buffering
	 * in RING buffer (flood of crash dump info overloads ring buffer
	 * otherwise,and we lose a lot of crash dump info)
	 * NOTE: crash dump is put into SD by default; if SD file fails to open,
	 * then we'll try sending it out RNDIS */

	BCMLOG_StartCpCrashDump(sdDumpFile, cpReset);

	/* only grab RAM dump for CP reset case */
	if (!cpReset) {
		retryCount = 0;
		while (1) {
			t1 = TIMER_GetValue();

			/* signal to CP that we're ready to rx crash log... */
			SmLocalControl.SmControl->CrashCode =
			    IPC_AP_CLEAR_TO_SEND;

			/* wait for CP to "dump"; CrashCode field will be
			 * set to physical address of current assert buf */
			while (SmLocalControl.SmControl->CrashCode ==
			       IPC_AP_CLEAR_TO_SEND) {
				for (i = 0; i < 256; i++)
					;
				if (TIMER_GetValue() - t1 >
				    TICKS_ONE_SECOND * 20)
					break;
			}

			/* check for time out */
			if (SmLocalControl.SmControl->CrashCode ==
			    IPC_AP_CLEAR_TO_SEND) {
				if (retryCount < MAX_CP_DUMP_RETRIES) {
					retryCount++;
					IPC_DEBUG(
						DBG_TRACE,
						"timeout %d, trying again..\n",
						(int)retryCount);
					continue;
				} else {
					/* no response from CP,
					 * so get out of here
					 */
					IPC_DEBUG(
						DBG_ERROR,
						"Abort --- max retries %d reached\n",
						(int)retryCount);
					break;
				}
			}

			/* reset retry counter */
			retryCount = 0;

			/* get virtual address of CP assert buffer */
			AssertLogVAddr = ioremap_nocache(
					(UInt32)
					(SmLocalControl.SmControl->CrashCode),
					ASSERT_BUF_SIZE);
			if (NULL == AssertLogVAddr) {
				IPC_DEBUG(DBG_ERROR,
					  "ioremap_nocache failed in DUMP_CP_assert_log\n");
				break;
			}
			p = (UInt8 *)AssertLogVAddr;

			/* number of bytes in assert buffer */
			size = (p[0] << 8) + p[1];

			/* size of 0 means CP is done dumping assert log */
			if (size == 0) {
				IPC_DEBUG(DBG_ERROR,
					  "assert log size 0, exiting, packetCount:0x%x\n",
					  (int)packetCount);
				iounmap(AssertLogVAddr);
				AssertLogVAddr = NULL;
				break;
			}
			/* sanity check for too beaucoup... */
			if (size > ASSERT_BUF_SIZE - 2) {
				IPC_DEBUG(DBG_ERROR,
					  "Abort --- improper size [%08x]=%d\n",
					  SmLocalControl.SmControl->CrashCode,
					  (int)size);
				iounmap(AssertLogVAddr);
				AssertLogVAddr = NULL;
				break;
			}
			/* send packet out to log
			 * (MTT via RNDIS or crash dump file)
			 */
			BCMLOG_HandleCpCrashDumpData((const char *)(p + 2),
						     size);

			packetCount++;
			iounmap(AssertLogVAddr);
			AssertLogVAddr = NULL;

		}
	}

	RpcDbgDumpHistoryLogging(2, 1);

	IPC_DEBUG(DBG_ERROR, "Starting CP RAM dump - do not power down...\n");

	/* dump all CP memory to log */
	DUMP_CPMemoryByList(dumped_crash_summary_ptr->mem_dump);

	IPC_DEBUG(DBG_ERROR, "CP RAM dump complete\n");
	/* resume normal logging activities... */
	BCMLOG_EndCpCrashDump();

	if (BCMLOG_OUTDEV_SDCARD == BCMLOG_GetCpCrashLogDevice())
		sys_sync();

	IPC_DEBUG(DBG_ERROR, "CP crash dump complete\n");

	if ((BCMLOG_OUTDEV_SDCARD == BCMLOG_GetCpCrashLogDevice())
	    && cp_crashed == 1 && !cpReset)
		abort();

}
/*************************************************
*
*   Worker thread to dump CP crash log information.
*
*
*****************************************************/
void ProcessCPCrashedDump(struct work_struct *work)
{
	char crashReason[40] = { 0 };
	char crashFile[40] = { 0 };
	char crashThread[40] = { 0 };
	char outString[512] = { 0 };
	IPC_U32 *Dump;
	void __iomem *DumpVAddr;
	int cpReset = SmLocalControl.SmControl->CrashCode ==
	    IPC_CP_SILENT_RESET_READY;

#ifdef CONFIG_CDEBUGGER
	if (ramdump_enable
#ifdef CONFIG_BRCM_CP_CRASH_DUMP_EMMC
	    && ap_triggered == 0
#endif
	    ) {
		BCMLOG_SetCpCrashLogDevice(BCMLOG_OUTDEV_NONE);
		/* we kill AP when CP crashes */
		IPC_DEBUG(DBG_ERROR, "Crashing AP for ramdump ...\n\n");
		abort();
	}
#endif
	if ((BCMLOG_OUTDEV_PANIC == BCMLOG_GetCpCrashLogDevice() ||
	     (BCMLOG_OUTDEV_RNDIS == BCMLOG_GetCpCrashLogDevice() && !cpReset))
#ifdef CONFIG_BRCM_CP_CRASH_DUMP_EMMC
	    && !ap_triggered
#endif
	    ) {

#ifdef CONFIG_CRASH_DUMP_START_UI_DISPLAY
		if (!dump_start_ui_on) {
			display_crash_dump_start_ui();
			dump_start_ui_on = 1;
			msleep(100);
		}
#endif

		/* we kill AP when CP crashes */
		IPC_DEBUG(DBG_ERROR, "Crashing AP now ...\n\n");
		abort();
	}
#ifdef CONFIG_CRASH_DUMP_START_UI_DISPLAY
	if ((BCMLOG_OUTDEV_SDCARD == BCMLOG_GetCpCrashLogDevice())
	    && cp_crashed == 1 && !cpReset) {
		if (!dump_start_ui_on) {
			display_crash_dump_start_ui();
			dump_start_ui_on = 1;
		}
	}
#endif

	IPC_Dump();

	RpcDbgDumpHistoryLogging(0, 0);

#if defined(CONFIG_BRCM_CP_CRASH_DUMP) \
	|| defined(CONFIG_BRCM_CP_CRASH_DUMP_EMMC) \
	|| defined(CONFIG_BCM_AP_PANIC_ON_CPCRASH)
	while (SmLocalControl.SmControl->CrashDump == NULL)
		; /* No op */
#endif

	/* **NOTE** for now, continue doing simple dump out IPC_DEBUG so there
	 * is some indication of CP crash in console
	 * (in case user not running MTT) */
	Dump = (void *)SmLocalControl.SmControl->CrashDump;

	IPC_DEBUG(DBG_ERROR, "ioremap_nocache\n");
	DumpVAddr = ioremap_nocache((UInt32)Dump,
				    sizeof(struct T_CRASH_SUMMARY));
	if (NULL == DumpVAddr) {
		IPC_DEBUG(DBG_ERROR, "VirtualAlloc failed\n");
		goto cleanUp;
	}

	IPC_DEBUG(DBG_ERROR, "Crash Summary Virtual Addr: 0x%08X\n",
		  (unsigned int)DumpVAddr);

	dumped_crash_summary_ptr = (struct T_CRASH_SUMMARY *)DumpVAddr;

	IPC_DEBUG(DBG_ERROR, "===== COMMS_PROCESSOR crash summary =====\r\n");

	if (dumped_crash_summary_ptr->link_signature) {
		GetStringFromPA((UInt32)
				dumped_crash_summary_ptr->link_signature,
				outString, 128);
		IPC_DEBUG(DBG_ERROR, "%s\r\n", outString);
	}

	if (dumped_crash_summary_ptr->project_version) {
		GetStringFromPA((UInt32)
				dumped_crash_summary_ptr->project_version,
				outString, 128);
		IPC_DEBUG(DBG_ERROR, "%s\r\n", outString);
	}

	if (dumped_crash_summary_ptr->DSP_version) {
		GetStringFromPA((UInt32)dumped_crash_summary_ptr->DSP_version,
				outString, 128);
		IPC_DEBUG(DBG_ERROR, "%s\r\n", outString);
	}

	if (dumped_crash_summary_ptr->FW_version) {
		GetStringFromPA((UInt32)dumped_crash_summary_ptr->FW_version,
				outString, 128);
		IPC_DEBUG(DBG_ERROR, "%s\r\n", outString);
	}

	if (dumped_crash_summary_ptr->decoder_version) {
		GetStringFromPA((UInt32)
				dumped_crash_summary_ptr->decoder_version,
				outString, 128);
		IPC_DEBUG(DBG_ERROR, "%s\r\n", outString);
	}

	GetStringFromPA((UInt32)dumped_crash_summary_ptr->reason, crashReason,
			40);

	GetStringFromPA((UInt32)dumped_crash_summary_ptr->file, crashFile, 40);

	GetStringFromPA((UInt32)dumped_crash_summary_ptr->thread, crashThread,
			40);

	IPC_DEBUG(DBG_ERROR, "%s f=%s l=%d v=%d/0x%x t=%s TS=%d\r\n",
		  crashReason,
		  crashFile,
		  dumped_crash_summary_ptr->line,
		  dumped_crash_summary_ptr->value,
		  dumped_crash_summary_ptr->value,
		  crashThread, dumped_crash_summary_ptr->time);

	/* notify clients about CP reset */
	if (cpReset
#ifdef CONFIG_BRCM_CP_CRASH_DUMP_EMMC
	    && !ap_triggered
#endif
	    )
		HandleCPResetStart();


#ifndef CONFIG_BCM_AP_PANIC_ON_CPCRASH
	/* done with "simple" dump, so now pull the full assert
	 * log from CP and dump out to MTT */
	DUMP_CP_assert_log();
#endif

cleanUp:

	if (NULL != DumpVAddr)
		iounmap(DumpVAddr);

	/* crash dump is done, so trigger CP reset */
	if (cpReset
#ifdef CONFIG_BRCM_CP_CRASH_DUMP_EMMC
	    && !ap_triggered
#endif
	    ) {
		IPC_DEBUG(DBG_INFO, "waiting for clients to ack...\n");
		while (!cp_reset_clients_acked)
			msleep(300);
		cp_reset_clients_acked = 0;

		IPC_DEBUG(DBG_INFO, "starting cp_reset thread\n");
		kthread_run(HandleRestartCP, 0, "cp_reset");
	}
#ifdef CONFIG_HAS_WAKELOCK
	else
		wake_unlock(&ipc_wake_lock);
#endif

#ifdef CONFIG_BCM_AP_PANIC_ON_CPCRASH
	IPC_DEBUG(DBG_ERROR, "CP crashed, crashing AP now..\n");

#ifdef CONFIG_SEC_DEBUG
	cp_abort();
#else
	abort();
#endif /* CONFIG_SEC_DEBUG */

#endif /* CONFIG_AP_PANIC_ON_CPCRASH */

}
/*************************************************
*   Loads the image with the given name into RAM.
*
*	@param	p_device	(in) The kernel device.
*	@param	p_name		(in) The image name. This image file must be located
*							in /vendor/firmware.
*	@param	addr		(in) The RAM address to load the image into.
*	@param	expectedSize (in) The expected size of the image file, or 0
*                             if the size is to be calculated.
*
*****************************************************/
static int32_t LoadFirmware(struct device *p_device, const char *p_name,
			    int addr, int expectedSize)
{
	const struct firmware *fw;
	int32_t err;
	int imgSize;

	IPC_DEBUG(DBG_INFO, "calling request_firmware for %s, device=%p\n",
		  p_name, p_device);

	/** call kernel to start firmware load **/
	/* request_firmware(const struct firmware **fw,
	 *                  const char *name,
	 *                  struct device *device);
	 */
	err = request_firmware(&fw, p_name, p_device);
	if (err) {
		IPC_DEBUG(DBG_ERROR, "firmware request failed (%d)\n", err);
		return err;
	}

	if (fw)
		IPC_DEBUG(DBG_INFO, "fw->size=%d\n", fw->size);
	else {
		/*Coverity Complaint: FORWARD_NULL */
		IPC_DEBUG(DBG_INFO, "fw = NULL!\n");
		return err;
	}

	imgSize = fw->size;
	if (expectedSize == 0) {
		UInt8 *ptr;

		/* This is the main CP image */
		if (IsCommsImageValid(fw->data))
			IPC_DEBUG(DBG_INFO, "verified CP image\n");
		else
			IPC_DEBUG(DBG_ERROR, "failed to verify main image\n");

		ptr = ((UInt8 *)fw->data) + CP_IMAGE_SIZE_OFFSET;

		imgSize = (ptr[3] << 24) |
		    (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
		IPC_DEBUG(DBG_INFO, "calculated CP image size = 0x%x\n",
			  imgSize);
	} else if (expectedSize != imgSize) {
		if (imgSize > expectedSize) {
			IPC_DEBUG(DBG_ERROR,
				  "ERROR: fw->size > expected (0x%x > 0x%x)\n",
				  fw->size, expectedSize);
			imgSize = expectedSize;
		} else
			IPC_DEBUG(DBG_ERROR,
				  "ERROR: fw->size < expected (0x%x < 0x%x)\n",
				  fw->size, expectedSize);
	}

	/** download to chip **/
	err = DownloadFirmware(imgSize, fw->data, addr);

	/* Verify CP image @ RAM addr */
	if (expectedSize == 0) {
		void __iomem *virtAddr;

		virtAddr = ioremap_nocache(addr, fw->size);
		if (virtAddr) {
			int retval;
			/* This is the main CP image */
			if (IsCommsImageValid(virtAddr))
				IPC_DEBUG(DBG_INFO,
					  "verified CP image @ %p\n",
					  (void *)addr);
			else
				IPC_DEBUG(DBG_ERROR,
					  "failed to verify main image @ %p\n",
					  (void *)addr);
			retval = memcmp(fw->data, virtAddr, imgSize);
			IPC_DEBUG(DBG_INFO, "memcmp(%p, %p, 0x%x) = %d\n",
				  (void *)fw->data, virtAddr, imgSize, retval);
			iounmap(virtAddr);
		} else {
			IPC_DEBUG(DBG_ERROR,
				  "*** ERROR: ioremap_nocache FAILED for addr %p\n",
				  (void *)addr);
		}
	}

	/** free kernel structure */
	release_firmware(fw);

	return err;
}
示例#29
0
static int __init ipcs_module_init(void)
{
  int rc = 0;
  int readyChkCnt = 0;
  struct timespec startTime, endTime;
  
  IPC_DEBUG(DBG_INFO,"[ipc]: ipcs_module_init start..\n");
  
  init_MUTEX_LOCKED(&g_ipc_info.ipc_sem);

  g_ipc_info.ipc_state = 0;

  g_ipc_info.devnum = MKDEV(IPC_MAJOR, 0);
  
  rc = register_chrdev_region(g_ipc_info.devnum, 1, "bcm_fuse_ipc");
  if (rc < 0) 
  {
    IPC_DEBUG(DBG_ERROR,"Error registering the IPC device\n");
    goto out;
  }

  cdev_init(&g_ipc_info.cdev, &ipc_ops);
  
  g_ipc_info.cdev.owner = THIS_MODULE;

  rc = cdev_add(&g_ipc_info.cdev, g_ipc_info.devnum, 1);
  if (rc) 
  {
    IPC_DEBUG(DBG_ERROR,"[ipc]: cdev_add errpr\n");
    goto out_unregister;
  }

  IPC_DEBUG(DBG_INFO, "[ipc]: create_workqueue\n");
 
  INIT_WORK(&g_ipc_info.cp_crash_dump_wq, ProcessCPCrashedDump);
  INIT_WORK(&g_ipc_info.intr_work, ipcs_intr_workqueue_process);

  g_ipc_info.intr_workqueue = create_workqueue("ipc-wq");
  if (!g_ipc_info.intr_workqueue)
  {
    IPC_DEBUG(DBG_ERROR,"[ipc]: cannot create workqueue\n");
    goto out_unregister;
  } 


  IPC_DEBUG(DBG_INFO, "[ipc]: request_irq\n");
  rc = request_irq(IRQ_IPC_C2A, ipcs_interrupt, IRQF_NO_SUSPEND, "ipc-intr", &g_ipc_info);
  if (rc) 
  {
    IPC_DEBUG(DBG_ERROR,"[ipc]: request_irq error\n");
    goto out_del;
  }
  
  /**
     Make sure this is not cache'd because CP has to know about any changes
     we write to this memory immediately.
   */
  IPC_DEBUG(DBG_INFO, "[ipc]: ioremap_nocache IPC_BASE\n");
  g_ipc_info.apcp_shmem = ioremap_nocache(IPC_BASE, IPC_SIZE);
  if (!g_ipc_info.apcp_shmem) 
  {
    rc = -ENOMEM;
    IPC_DEBUG(DBG_ERROR,"[ipc]: Could not map shmem\n");
    goto out_del;
  }
#ifdef CONFIG_HAS_WAKELOCK
  wake_lock_init(&ipc_wake_lock, WAKE_LOCK_SUSPEND, "ipc_wake_lock");
#endif

  IPC_DEBUG(DBG_INFO, "[ipc]: ipcs_init\n");
  if (ipcs_init((void *)g_ipc_info.apcp_shmem, IPC_SIZE))
  {
    rc = -1;
    IPC_DEBUG(DBG_ERROR,"[ipc]: ipcs_init() failed\n");
    goto out_del;
  }
  
  if ( sEarlyCPInterrupt )
  {
    IPC_DEBUG(DBG_INFO,"[ipc]: early CP interrupt - doing crash dump...\n");
#ifdef CONFIG_HAS_WAKELOCK
    wake_lock(&ipc_wake_lock);
#endif
    schedule_work(&g_ipc_info.cp_crash_dump_wq);
  }
  
  // check for AP only boot mode
  if ( AP_ONLY_BOOT == get_ap_boot_mode() )
  {
      IPC_DEBUG(DBG_INFO,"[ipc]: AP only boot - not waiting for CP\n");
  }
  else
  {
  // wait for CP to have IPC setup as well; if we exit module init
  // before IPC is ready, RPC module will likely crash during its 
  // own init
  startTime = current_kernel_time();
  while ( !g_ipc_info.ipc_state )
  {
    IPC_DEBUG(DBG_INFO, "[ipc]: CP IPC not ready, sleeping...\n");
    msleep(20);
    readyChkCnt++;
    if ( readyChkCnt > 100 )
    {
        IPC_DEBUG(DBG_ERROR, "[ipc]: IPC init timeout - no response from CP\n");
        rc = -1;
        goto out_del;
    }
  }
  endTime = current_kernel_time();
  IPC_DEBUG(DBG_INFO,"readyChkCnt=%d time=%ldus\n", readyChkCnt,
        ((endTime.tv_sec - startTime.tv_sec)*1000000L+(endTime.tv_nsec - startTime.tv_nsec)/1000L));

  IPC_DEBUG(DBG_INFO,"[ipc]: ipcs_module_init ok\n");
  }
    
  return 0;

out_del:
  cdev_del(&g_ipc_info.cdev);
out_unregister:
  unregister_chrdev_region(g_ipc_info.devnum, 1);
out:
  IPC_DEBUG(DBG_ERROR,"IPC Driver Failed to initialise!\n");
  return rc;
}
static int __init ipcs_module_init(void)
{
	int rc = -1;
	struct proc_dir_entry *dir;

	dir =
	    create_proc_read_entry("driver/bcmipc", 0, NULL, ipcs_read_proc,
				   NULL);
	if (dir == NULL) {
		IPC_DEBUG(DBG_ERROR,
			  "ipcs_module_init: can't create /proc/driver/bcmipc\n");
		//return -1;
	}

	IPC_DEBUG(DBG_TRACE, "start ...\n");

	g_ipc_info.ipc_state = 0;

	g_ipc_info.mDriverClass = class_create(THIS_MODULE,
				BCM_KERNEL_IPC_NAME);
	if (IS_ERR(g_ipc_info.mDriverClass)) {
		IPC_DEBUG(DBG_ERROR, "driver class_create failed\n");
		goto out;
	}

	g_ipc_info.drvdata = device_create(g_ipc_info.mDriverClass, NULL,
				MKDEV(IPC_MAJOR, 0), NULL, BCM_KERNEL_IPC_NAME);
	if (IS_ERR(g_ipc_info.drvdata)) {
		IPC_DEBUG(DBG_ERROR, "device_create drvdata failed\n");
		goto out;
	}

	IPC_DEBUG(DBG_TRACE, "Allocate CP crash dump workqueue\n");
	g_ipc_info.crash_dump_workqueue = alloc_workqueue("dump-wq",
							  WQ_FREEZABLE |
							  WQ_MEM_RECLAIM, 0);

	if (!g_ipc_info.crash_dump_workqueue) {
		IPC_DEBUG(DBG_ERROR,
			  "Cannot allocate CP crash dump workqueue\n");
		goto out;
	}

	INIT_WORK(&g_ipc_info.cp_crash_dump_wq, ProcessCPCrashedDump);

	tasklet_init(&g_ipc_info.intr_tasklet, ipcs_intr_tasklet_handler, 0);

	/**
	 Make sure this is not cache'd because CP has to know about any changes
	 we write to this memory immediately.
	*/
	IPC_DEBUG(DBG_TRACE, "ioremap_nocache IPC_BASE\n");
	g_ipc_info.apcp_shmem = ioremap_nocache(IPC_BASE, IPC_SIZE);
	if (!g_ipc_info.apcp_shmem) {
		rc = -ENOMEM;
		IPC_DEBUG(DBG_ERROR, "Could not map shmem\n");
		goto out_shared_mem_fail;
	}

	IPC_DEBUG(DBG_TRACE, "ipcs_init\n");
	if (ipcs_init((void *)g_ipc_info.apcp_shmem, IPC_SIZE, 0)) {
		rc = -1;
		IPC_DEBUG(DBG_ERROR, "ipcs_init() failed\n");
		goto out_ipc_init_fail;
	}

	IPC_DEBUG(DBG_TRACE, "ok\n");

#ifdef CONFIG_HAS_WAKELOCK
	wake_lock_init(&ipc_wake_lock, WAKE_LOCK_SUSPEND, "ipc_wake_lock");
#endif

	IPC_DEBUG(DBG_TRACE, "request_irq\n");
	rc = request_irq(IRQ_IPC_C2A, ipcs_interrupt, IRQF_NO_SUSPEND,
			 "ipc-intr", &g_ipc_info);

	if (rc) {
		IPC_DEBUG(DBG_ERROR, "request_irq error\n");
		goto out_irq_req_fail;
	}

	IPC_DEBUG(DBG_TRACE, "IRQ Clear and Enable\n");

	return 0;

out_irq_req_fail:
	wake_lock_destroy(&ipc_wake_lock);

out_ipc_init_fail:
	iounmap(g_ipc_info.apcp_shmem);

out_shared_mem_fail:
	flush_workqueue(g_ipc_info.crash_dump_workqueue);
	destroy_workqueue(g_ipc_info.crash_dump_workqueue);

out:
	IPC_DEBUG(DBG_ERROR, "IPC Driver Failed to initialise!\n");
	return rc;
}