2008-09-09 20:36:50 +04:00
/* arch/arm/mach-msm/proc_comm.c
*
* Copyright ( C ) 2007 - 2008 Google , Inc .
* Author : Brian Swetland < swetland @ google . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/spinlock.h>
# include <mach/msm_iomap.h>
# include "proc_comm.h"
2010-01-29 22:43:42 +03:00
static inline void msm_a2m_int ( uint32_t irq )
{
# if defined(CONFIG_ARCH_MSM7X30)
writel ( 1 < < irq , MSM_GCC_BASE + 0x8 ) ;
# else
writel ( 1 , MSM_CSR_BASE + 0x400 + ( irq * 4 ) ) ;
# endif
}
2008-09-09 20:36:50 +04:00
static inline void notify_other_proc_comm ( void )
{
2010-01-29 22:43:42 +03:00
msm_a2m_int ( 6 ) ;
2008-09-09 20:36:50 +04:00
}
# define APP_COMMAND 0x00
# define APP_STATUS 0x04
# define APP_DATA1 0x08
# define APP_DATA2 0x0C
# define MDM_COMMAND 0x10
# define MDM_STATUS 0x14
# define MDM_DATA1 0x18
# define MDM_DATA2 0x1C
static DEFINE_SPINLOCK ( proc_comm_lock ) ;
/* The higher level SMD support will install this to
* provide a way to check for and handle modem restart .
*/
int ( * msm_check_for_modem_crash ) ( void ) ;
/* Poll for a state change, checking for possible
* modem crashes along the way ( so we don ' t wait
* forever while the ARM9 is blowing up ) .
*
* Return an error in the event of a modem crash and
* restart so the msm_proc_comm ( ) routine can restart
* the operation from the beginning .
*/
static int proc_comm_wait_for ( void __iomem * addr , unsigned value )
{
for ( ; ; ) {
if ( readl ( addr ) = = value )
return 0 ;
if ( msm_check_for_modem_crash )
if ( msm_check_for_modem_crash ( ) )
return - EAGAIN ;
}
}
int msm_proc_comm ( unsigned cmd , unsigned * data1 , unsigned * data2 )
{
void __iomem * base = MSM_SHARED_RAM_BASE ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & proc_comm_lock , flags ) ;
for ( ; ; ) {
if ( proc_comm_wait_for ( base + MDM_STATUS , PCOM_READY ) )
continue ;
writel ( cmd , base + APP_COMMAND ) ;
writel ( data1 ? * data1 : 0 , base + APP_DATA1 ) ;
writel ( data2 ? * data2 : 0 , base + APP_DATA2 ) ;
notify_other_proc_comm ( ) ;
if ( proc_comm_wait_for ( base + APP_COMMAND , PCOM_CMD_DONE ) )
continue ;
if ( readl ( base + APP_STATUS ) ! = PCOM_CMD_FAIL ) {
if ( data1 )
* data1 = readl ( base + APP_DATA1 ) ;
if ( data2 )
* data2 = readl ( base + APP_DATA2 ) ;
ret = 0 ;
} else {
ret = - EIO ;
}
break ;
}
writel ( PCOM_CMD_IDLE , base + APP_COMMAND ) ;
spin_unlock_irqrestore ( & proc_comm_lock , flags ) ;
return ret ;
}
2010-03-18 22:31:08 +03:00
/*
* We need to wait for the ARM9 to at least partially boot
* up before we can continue . Since the ARM9 does resource
* allocation , if we dont ' wait we could end up crashing or in
* and unknown state . This function should be called early to
* wait on the ARM9 .
*/
2012-12-22 02:02:24 +04:00
void proc_comm_boot_wait ( void )
2010-03-18 22:31:08 +03:00
{
void __iomem * base = MSM_SHARED_RAM_BASE ;
proc_comm_wait_for ( base + MDM_STATUS , PCOM_READY ) ;
}