2008-09-30 03:00:48 +04:00
/* arch/arm/mach-msm/smd.c
*
* Copyright ( C ) 2007 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 .
*
*/
2010-11-13 00:49:27 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-09-30 03:00:48 +04:00
# include <linux/platform_device.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/cdev.h>
# include <linux/device.h>
# include <linux/wait.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/list.h>
# include <linux/slab.h>
# include <linux/debugfs.h>
# include <linux/delay.h>
# include <mach/msm_smd.h>
# include "smd_private.h"
# include "proc_comm.h"
2009-07-02 05:30:47 +04:00
# if defined(CONFIG_ARCH_QSD8X50)
# define CONFIG_QDSP6 1
# endif
2008-09-30 03:00:48 +04:00
# define MODULE_NAME "msm_smd"
enum {
MSM_SMD_DEBUG = 1U < < 0 ,
MSM_SMSM_DEBUG = 1U < < 0 ,
} ;
static int msm_smd_debug_mask ;
2009-07-02 04:58:37 +04:00
struct shared_info {
2009-04-27 05:38:49 +04:00
int ready ;
2012-09-15 00:20:43 +04:00
void __iomem * state ;
2009-04-27 05:38:49 +04:00
} ;
2009-05-21 03:52:36 +04:00
static unsigned dummy_state [ SMSM_STATE_COUNT ] ;
2009-04-27 05:38:49 +04:00
static struct shared_info smd_info = {
2012-09-15 00:20:43 +04:00
/* FIXME: not a real __iomem pointer */
. state = & dummy_state ,
2009-04-27 05:38:49 +04:00
} ;
2008-09-30 03:00:48 +04:00
module_param_named ( debug_mask , msm_smd_debug_mask ,
int , S_IRUGO | S_IWUSR | S_IWGRP ) ;
static unsigned last_heap_free = 0xffffffff ;
static inline void notify_other_smsm ( void )
{
2010-01-29 22:43:42 +03:00
msm_a2m_int ( 5 ) ;
2009-07-02 05:30:47 +04:00
# ifdef CONFIG_QDSP6
2010-01-29 22:43:42 +03:00
msm_a2m_int ( 8 ) ;
2009-07-02 05:30:47 +04:00
# endif
2008-09-30 03:00:48 +04:00
}
2009-04-27 05:38:49 +04:00
static inline void notify_modem_smd ( void )
2008-09-30 03:00:48 +04:00
{
2010-01-29 22:43:42 +03:00
msm_a2m_int ( 0 ) ;
2008-09-30 03:00:48 +04:00
}
2009-04-27 05:38:49 +04:00
static inline void notify_dsp_smd ( void )
{
2010-01-29 22:43:42 +03:00
msm_a2m_int ( 8 ) ;
2009-04-27 05:38:49 +04:00
}
2008-09-30 03:00:48 +04:00
static void smd_diag ( void )
{
char * x ;
x = smem_find ( ID_DIAG_ERR_MSG , SZ_DIAG_ERR_MSG ) ;
if ( x ! = 0 ) {
x [ SZ_DIAG_ERR_MSG - 1 ] = 0 ;
2010-11-13 00:49:27 +03:00
pr_debug ( " DIAG '%s' \n " , x ) ;
2008-09-30 03:00:48 +04:00
}
}
/* call when SMSM_RESET flag is set in the A9's smsm_state */
static void handle_modem_crash ( void )
{
pr_err ( " ARM9 has CRASHED \n " ) ;
smd_diag ( ) ;
/* in this case the modem or watchdog should reboot us */
for ( ; ; )
;
}
2009-05-21 03:52:36 +04:00
uint32_t raw_smsm_get_state ( enum smsm_state_item item )
{
return readl ( smd_info . state + item * 4 ) ;
}
2008-09-30 03:00:48 +04:00
static int check_for_modem_crash ( void )
{
2009-05-21 03:52:36 +04:00
if ( raw_smsm_get_state ( SMSM_STATE_MODEM ) & SMSM_RESET ) {
2008-09-30 03:00:48 +04:00
handle_modem_crash ( ) ;
return - 1 ;
}
2009-04-27 05:38:49 +04:00
return 0 ;
2008-09-30 03:00:48 +04:00
}
/* the spinlock is used to synchronize between the
2009-07-02 04:58:37 +04:00
* irq handler and code that mutates the channel
* list or fiddles with channel state
*/
DEFINE_SPINLOCK ( smd_lock ) ;
DEFINE_SPINLOCK ( smem_lock ) ;
2008-09-30 03:00:48 +04:00
/* the mutex is used during open() and close()
2009-07-02 04:58:37 +04:00
* operations to avoid races while creating or
* destroying smd_channel structures
*/
2008-09-30 03:00:48 +04:00
static DEFINE_MUTEX ( smd_creation_mutex ) ;
static int smd_initialized ;
2009-07-02 04:58:37 +04:00
LIST_HEAD ( smd_ch_closed_list ) ;
2009-07-02 05:30:47 +04:00
LIST_HEAD ( smd_ch_list_modem ) ;
LIST_HEAD ( smd_ch_list_dsp ) ;
2008-09-30 03:00:48 +04:00
static unsigned char smd_ch_allocated [ 64 ] ;
static struct work_struct probe_work ;
/* how many bytes are available for reading */
static int smd_stream_read_avail ( struct smd_channel * ch )
{
2009-04-27 05:38:49 +04:00
return ( ch - > recv - > head - ch - > recv - > tail ) & ch - > fifo_mask ;
2008-09-30 03:00:48 +04:00
}
/* how many bytes we are free to write */
static int smd_stream_write_avail ( struct smd_channel * ch )
{
2009-04-27 05:38:49 +04:00
return ch - > fifo_mask -
( ( ch - > send - > head - ch - > send - > tail ) & ch - > fifo_mask ) ;
2008-09-30 03:00:48 +04:00
}
static int smd_packet_read_avail ( struct smd_channel * ch )
{
if ( ch - > current_packet ) {
int n = smd_stream_read_avail ( ch ) ;
if ( n > ch - > current_packet )
n = ch - > current_packet ;
return n ;
} else {
return 0 ;
}
}
static int smd_packet_write_avail ( struct smd_channel * ch )
{
int n = smd_stream_write_avail ( ch ) ;
return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0 ;
}
static int ch_is_open ( struct smd_channel * ch )
{
return ( ch - > recv - > state = = SMD_SS_OPENED ) & &
( ch - > send - > state = = SMD_SS_OPENED ) ;
}
/* provide a pointer and length to readable data in the fifo */
static unsigned ch_read_buffer ( struct smd_channel * ch , void * * ptr )
{
unsigned head = ch - > recv - > head ;
unsigned tail = ch - > recv - > tail ;
2009-04-27 05:38:49 +04:00
* ptr = ( void * ) ( ch - > recv_data + tail ) ;
2008-09-30 03:00:48 +04:00
if ( tail < = head )
return head - tail ;
else
2009-04-27 05:38:49 +04:00
return ch - > fifo_size - tail ;
2008-09-30 03:00:48 +04:00
}
/* advance the fifo read pointer after data from ch_read_buffer is consumed */
static void ch_read_done ( struct smd_channel * ch , unsigned count )
{
BUG_ON ( count > smd_stream_read_avail ( ch ) ) ;
2009-04-27 05:38:49 +04:00
ch - > recv - > tail = ( ch - > recv - > tail + count ) & ch - > fifo_mask ;
2009-10-12 21:38:10 +04:00
ch - > send - > fTAIL = 1 ;
2008-09-30 03:00:48 +04:00
}
/* basic read interface to ch_read_{buffer,done} used
2009-07-02 04:58:37 +04:00
* by smd_ * _read ( ) and update_packet_state ( )
* will read - and - discard if the _data pointer is null
*/
2008-09-30 03:00:48 +04:00
static int ch_read ( struct smd_channel * ch , void * _data , int len )
{
void * ptr ;
unsigned n ;
unsigned char * data = _data ;
int orig_len = len ;
while ( len > 0 ) {
n = ch_read_buffer ( ch , & ptr ) ;
if ( n = = 0 )
break ;
if ( n > len )
n = len ;
if ( _data )
memcpy ( data , ptr , n ) ;
data + = n ;
len - = n ;
ch_read_done ( ch , n ) ;
}
return orig_len - len ;
}
static void update_stream_state ( struct smd_channel * ch )
{
/* streams have no special state requiring updating */
}
static void update_packet_state ( struct smd_channel * ch )
{
unsigned hdr [ 5 ] ;
int r ;
/* can't do anything if we're in the middle of a packet */
if ( ch - > current_packet ! = 0 )
return ;
/* don't bother unless we can get the full header */
if ( smd_stream_read_avail ( ch ) < SMD_HEADER_SIZE )
return ;
r = ch_read ( ch , hdr , SMD_HEADER_SIZE ) ;
BUG_ON ( r ! = SMD_HEADER_SIZE ) ;
ch - > current_packet = hdr [ 0 ] ;
}
/* provide a pointer and length to next free space in the fifo */
static unsigned ch_write_buffer ( struct smd_channel * ch , void * * ptr )
{
unsigned head = ch - > send - > head ;
unsigned tail = ch - > send - > tail ;
2009-04-27 05:38:49 +04:00
* ptr = ( void * ) ( ch - > send_data + head ) ;
2008-09-30 03:00:48 +04:00
if ( head < tail ) {
return tail - head - 1 ;
} else {
if ( tail = = 0 )
2009-04-27 05:38:49 +04:00
return ch - > fifo_size - head - 1 ;
2008-09-30 03:00:48 +04:00
else
2009-04-27 05:38:49 +04:00
return ch - > fifo_size - head ;
2008-09-30 03:00:48 +04:00
}
}
/* advace the fifo write pointer after freespace
* from ch_write_buffer is filled
*/
static void ch_write_done ( struct smd_channel * ch , unsigned count )
{
BUG_ON ( count > smd_stream_write_avail ( ch ) ) ;
2009-04-27 05:38:49 +04:00
ch - > send - > head = ( ch - > send - > head + count ) & ch - > fifo_mask ;
2008-09-30 03:00:48 +04:00
ch - > send - > fHEAD = 1 ;
}
2009-04-27 05:38:49 +04:00
static void ch_set_state ( struct smd_channel * ch , unsigned n )
2008-09-30 03:00:48 +04:00
{
if ( n = = SMD_SS_OPENED ) {
2009-04-27 05:38:49 +04:00
ch - > send - > fDSR = 1 ;
ch - > send - > fCTS = 1 ;
ch - > send - > fCD = 1 ;
2008-09-30 03:00:48 +04:00
} else {
2009-04-27 05:38:49 +04:00
ch - > send - > fDSR = 0 ;
ch - > send - > fCTS = 0 ;
ch - > send - > fCD = 0 ;
2008-09-30 03:00:48 +04:00
}
2009-04-27 05:38:49 +04:00
ch - > send - > state = n ;
ch - > send - > fSTATE = 1 ;
ch - > notify_other_cpu ( ) ;
2008-09-30 03:00:48 +04:00
}
static void do_smd_probe ( void )
{
struct smem_shared * shared = ( void * ) MSM_SHARED_RAM_BASE ;
if ( shared - > heap_info . free_offset ! = last_heap_free ) {
last_heap_free = shared - > heap_info . free_offset ;
schedule_work ( & probe_work ) ;
}
}
static void smd_state_change ( struct smd_channel * ch ,
unsigned last , unsigned next )
{
ch - > last_state = next ;
2010-11-13 00:49:27 +03:00
pr_debug ( " ch %d %d -> %d \n " , ch - > n , last , next ) ;
2008-09-30 03:00:48 +04:00
switch ( next ) {
case SMD_SS_OPENING :
ch - > recv - > tail = 0 ;
case SMD_SS_OPENED :
if ( ch - > send - > state ! = SMD_SS_OPENED )
2009-04-27 05:38:49 +04:00
ch_set_state ( ch , SMD_SS_OPENED ) ;
2008-09-30 03:00:48 +04:00
ch - > notify ( ch - > priv , SMD_EVENT_OPEN ) ;
break ;
case SMD_SS_FLUSHING :
case SMD_SS_RESET :
/* we should force them to close? */
default :
ch - > notify ( ch - > priv , SMD_EVENT_CLOSE ) ;
}
}
2009-04-27 05:38:49 +04:00
static void handle_smd_irq ( struct list_head * list , void ( * notify ) ( void ) )
2008-09-30 03:00:48 +04:00
{
unsigned long flags ;
struct smd_channel * ch ;
int do_notify = 0 ;
unsigned ch_flags ;
unsigned tmp ;
spin_lock_irqsave ( & smd_lock , flags ) ;
2009-04-27 05:38:49 +04:00
list_for_each_entry ( ch , list , ch_list ) {
2008-09-30 03:00:48 +04:00
ch_flags = 0 ;
if ( ch_is_open ( ch ) ) {
if ( ch - > recv - > fHEAD ) {
ch - > recv - > fHEAD = 0 ;
ch_flags | = 1 ;
do_notify | = 1 ;
}
if ( ch - > recv - > fTAIL ) {
ch - > recv - > fTAIL = 0 ;
ch_flags | = 2 ;
do_notify | = 1 ;
}
if ( ch - > recv - > fSTATE ) {
ch - > recv - > fSTATE = 0 ;
ch_flags | = 4 ;
do_notify | = 1 ;
}
}
tmp = ch - > recv - > state ;
if ( tmp ! = ch - > last_state )
smd_state_change ( ch , ch - > last_state , tmp ) ;
if ( ch_flags ) {
ch - > update_state ( ch ) ;
ch - > notify ( ch - > priv , SMD_EVENT_DATA ) ;
}
}
if ( do_notify )
2009-04-27 05:38:49 +04:00
notify ( ) ;
2008-09-30 03:00:48 +04:00
spin_unlock_irqrestore ( & smd_lock , flags ) ;
do_smd_probe ( ) ;
2009-04-27 05:38:49 +04:00
}
2009-07-02 05:30:47 +04:00
static irqreturn_t smd_modem_irq_handler ( int irq , void * data )
{
handle_smd_irq ( & smd_ch_list_modem , notify_modem_smd ) ;
return IRQ_HANDLED ;
}
2010-03-18 20:10:30 +03:00
# if defined(CONFIG_QDSP6)
2009-07-02 05:30:47 +04:00
static irqreturn_t smd_dsp_irq_handler ( int irq , void * data )
2009-04-27 05:38:49 +04:00
{
2009-07-02 05:30:47 +04:00
handle_smd_irq ( & smd_ch_list_dsp , notify_dsp_smd ) ;
2008-09-30 03:00:48 +04:00
return IRQ_HANDLED ;
}
2010-03-18 20:10:30 +03:00
# endif
2008-09-30 03:00:48 +04:00
static void smd_fake_irq_handler ( unsigned long arg )
{
2009-07-02 05:30:47 +04:00
handle_smd_irq ( & smd_ch_list_modem , notify_modem_smd ) ;
handle_smd_irq ( & smd_ch_list_dsp , notify_dsp_smd ) ;
2008-09-30 03:00:48 +04:00
}
static DECLARE_TASKLET ( smd_fake_irq_tasklet , smd_fake_irq_handler , 0 ) ;
2009-07-02 05:30:47 +04:00
static inline int smd_need_int ( struct smd_channel * ch )
{
if ( ch_is_open ( ch ) ) {
if ( ch - > recv - > fHEAD | | ch - > recv - > fTAIL | | ch - > recv - > fSTATE )
return 1 ;
if ( ch - > recv - > state ! = ch - > last_state )
return 1 ;
}
return 0 ;
}
2008-09-30 03:00:48 +04:00
void smd_sleep_exit ( void )
{
unsigned long flags ;
struct smd_channel * ch ;
int need_int = 0 ;
spin_lock_irqsave ( & smd_lock , flags ) ;
2009-07-02 05:30:47 +04:00
list_for_each_entry ( ch , & smd_ch_list_modem , ch_list ) {
if ( smd_need_int ( ch ) ) {
need_int = 1 ;
break ;
}
}
list_for_each_entry ( ch , & smd_ch_list_dsp , ch_list ) {
if ( smd_need_int ( ch ) ) {
need_int = 1 ;
break ;
2008-09-30 03:00:48 +04:00
}
}
spin_unlock_irqrestore ( & smd_lock , flags ) ;
do_smd_probe ( ) ;
2009-07-02 05:30:47 +04:00
2008-09-30 03:00:48 +04:00
if ( need_int ) {
if ( msm_smd_debug_mask & MSM_SMD_DEBUG )
pr_info ( " smd_sleep_exit need interrupt \n " ) ;
tasklet_schedule ( & smd_fake_irq_tasklet ) ;
}
}
void smd_kick ( smd_channel_t * ch )
{
unsigned long flags ;
unsigned tmp ;
spin_lock_irqsave ( & smd_lock , flags ) ;
ch - > update_state ( ch ) ;
tmp = ch - > recv - > state ;
if ( tmp ! = ch - > last_state ) {
ch - > last_state = tmp ;
if ( tmp = = SMD_SS_OPENED )
ch - > notify ( ch - > priv , SMD_EVENT_OPEN ) ;
else
ch - > notify ( ch - > priv , SMD_EVENT_CLOSE ) ;
}
ch - > notify ( ch - > priv , SMD_EVENT_DATA ) ;
2009-04-27 05:38:49 +04:00
ch - > notify_other_cpu ( ) ;
2008-09-30 03:00:48 +04:00
spin_unlock_irqrestore ( & smd_lock , flags ) ;
}
2009-04-27 05:38:49 +04:00
static int smd_is_packet ( int chn , unsigned type )
2008-09-30 03:00:48 +04:00
{
2009-04-27 05:38:49 +04:00
type & = SMD_KIND_MASK ;
if ( type = = SMD_KIND_PACKET )
return 1 ;
if ( type = = SMD_KIND_STREAM )
return 0 ;
/* older AMSS reports SMD_KIND_UNKNOWN always */
2008-09-30 03:00:48 +04:00
if ( ( chn > 4 ) | | ( chn = = 1 ) )
return 1 ;
else
return 0 ;
}
static int smd_stream_write ( smd_channel_t * ch , const void * _data , int len )
{
void * ptr ;
const unsigned char * buf = _data ;
unsigned xfer ;
int orig_len = len ;
if ( len < 0 )
return - EINVAL ;
while ( ( xfer = ch_write_buffer ( ch , & ptr ) ) ! = 0 ) {
if ( ! ch_is_open ( ch ) )
break ;
if ( xfer > len )
xfer = len ;
memcpy ( ptr , buf , xfer ) ;
ch_write_done ( ch , xfer ) ;
len - = xfer ;
buf + = xfer ;
if ( len = = 0 )
break ;
}
2009-04-27 05:38:49 +04:00
ch - > notify_other_cpu ( ) ;
2008-09-30 03:00:48 +04:00
return orig_len - len ;
}
static int smd_packet_write ( smd_channel_t * ch , const void * _data , int len )
{
unsigned hdr [ 5 ] ;
if ( len < 0 )
return - EINVAL ;
if ( smd_stream_write_avail ( ch ) < ( len + SMD_HEADER_SIZE ) )
return - ENOMEM ;
hdr [ 0 ] = len ;
hdr [ 1 ] = hdr [ 2 ] = hdr [ 3 ] = hdr [ 4 ] = 0 ;
smd_stream_write ( ch , hdr , sizeof ( hdr ) ) ;
smd_stream_write ( ch , _data , len ) ;
return len ;
}
static int smd_stream_read ( smd_channel_t * ch , void * data , int len )
{
int r ;
if ( len < 0 )
return - EINVAL ;
r = ch_read ( ch , data , len ) ;
if ( r > 0 )
2009-04-27 05:38:49 +04:00
ch - > notify_other_cpu ( ) ;
2008-09-30 03:00:48 +04:00
return r ;
}
static int smd_packet_read ( smd_channel_t * ch , void * data , int len )
{
unsigned long flags ;
int r ;
if ( len < 0 )
return - EINVAL ;
if ( len > ch - > current_packet )
len = ch - > current_packet ;
r = ch_read ( ch , data , len ) ;
if ( r > 0 )
2009-04-27 05:38:49 +04:00
ch - > notify_other_cpu ( ) ;
2008-09-30 03:00:48 +04:00
spin_lock_irqsave ( & smd_lock , flags ) ;
ch - > current_packet - = r ;
update_packet_state ( ch ) ;
spin_unlock_irqrestore ( & smd_lock , flags ) ;
return r ;
}
2009-10-31 02:22:05 +03:00
static int smd_alloc_channel ( const char * name , uint32_t cid , uint32_t type )
2009-04-27 05:38:49 +04:00
{
struct smd_channel * ch ;
2008-09-30 03:00:48 +04:00
ch = kzalloc ( sizeof ( struct smd_channel ) , GFP_KERNEL ) ;
if ( ch = = 0 ) {
pr_err ( " smd_alloc_channel() out of memory \n " ) ;
2009-10-31 02:22:05 +03:00
return - 1 ;
2008-09-30 03:00:48 +04:00
}
ch - > n = cid ;
2010-03-17 02:29:44 +03:00
if ( _smd_alloc_channel ( ch ) ) {
2009-04-27 05:38:49 +04:00
kfree ( ch ) ;
2009-10-31 02:22:05 +03:00
return - 1 ;
2009-04-27 05:38:49 +04:00
}
ch - > fifo_mask = ch - > fifo_size - 1 ;
ch - > type = type ;
if ( ( type & SMD_TYPE_MASK ) = = SMD_TYPE_APPS_MODEM )
ch - > notify_other_cpu = notify_modem_smd ;
else
ch - > notify_other_cpu = notify_dsp_smd ;
if ( smd_is_packet ( cid , type ) ) {
2008-09-30 03:00:48 +04:00
ch - > read = smd_packet_read ;
ch - > write = smd_packet_write ;
ch - > read_avail = smd_packet_read_avail ;
ch - > write_avail = smd_packet_write_avail ;
ch - > update_state = update_packet_state ;
} else {
ch - > read = smd_stream_read ;
ch - > write = smd_stream_write ;
ch - > read_avail = smd_stream_read_avail ;
ch - > write_avail = smd_stream_write_avail ;
ch - > update_state = update_stream_state ;
}
2009-04-27 05:38:49 +04:00
if ( ( type & 0xff ) = = 0 )
memcpy ( ch - > name , " SMD_ " , 4 ) ;
else
memcpy ( ch - > name , " DSP_ " , 4 ) ;
2008-09-30 03:00:48 +04:00
memcpy ( ch - > name + 4 , name , 20 ) ;
ch - > name [ 23 ] = 0 ;
ch - > pdev . name = ch - > name ;
ch - > pdev . id = - 1 ;
2010-11-13 00:49:27 +03:00
pr_debug ( " smd_alloc_channel() cid=%02d size=%05d '%s' \n " ,
2009-04-27 05:38:49 +04:00
ch - > n , ch - > fifo_size , ch - > name ) ;
2008-09-30 03:00:48 +04:00
mutex_lock ( & smd_creation_mutex ) ;
list_add ( & ch - > ch_list , & smd_ch_closed_list ) ;
mutex_unlock ( & smd_creation_mutex ) ;
platform_device_register ( & ch - > pdev ) ;
2009-10-31 02:22:05 +03:00
return 0 ;
2008-09-30 03:00:48 +04:00
}
2010-03-17 21:10:40 +03:00
static void smd_channel_probe_worker ( struct work_struct * work )
{
struct smd_alloc_elm * shared ;
unsigned ctype ;
unsigned type ;
unsigned n ;
shared = smem_find ( ID_CH_ALLOC_TBL , sizeof ( * shared ) * 64 ) ;
if ( ! shared ) {
2010-11-13 00:49:27 +03:00
pr_err ( " cannot find allocation table \n " ) ;
2010-03-17 21:10:40 +03:00
return ;
}
for ( n = 0 ; n < 64 ; n + + ) {
if ( smd_ch_allocated [ n ] )
continue ;
if ( ! shared [ n ] . ref_count )
continue ;
if ( ! shared [ n ] . name [ 0 ] )
continue ;
ctype = shared [ n ] . ctype ;
type = ctype & SMD_TYPE_MASK ;
/* DAL channels are stream but neither the modem,
* nor the DSP correctly indicate this . Fixup manually .
*/
if ( ! memcmp ( shared [ n ] . name , " DAL " , 3 ) )
ctype = ( ctype & ( ~ SMD_KIND_MASK ) ) | SMD_KIND_STREAM ;
type = shared [ n ] . ctype & SMD_TYPE_MASK ;
if ( ( type = = SMD_TYPE_APPS_MODEM ) | |
( type = = SMD_TYPE_APPS_DSP ) )
if ( ! smd_alloc_channel ( shared [ n ] . name , shared [ n ] . cid , ctype ) )
smd_ch_allocated [ n ] = 1 ;
}
}
2008-09-30 03:00:48 +04:00
static void do_nothing_notify ( void * priv , unsigned flags )
{
}
struct smd_channel * smd_get_channel ( const char * name )
{
struct smd_channel * ch ;
mutex_lock ( & smd_creation_mutex ) ;
list_for_each_entry ( ch , & smd_ch_closed_list , ch_list ) {
if ( ! strcmp ( name , ch - > name ) ) {
list_del ( & ch - > ch_list ) ;
mutex_unlock ( & smd_creation_mutex ) ;
return ch ;
}
}
mutex_unlock ( & smd_creation_mutex ) ;
return NULL ;
}
int smd_open ( const char * name , smd_channel_t * * _ch ,
void * priv , void ( * notify ) ( void * , unsigned ) )
{
struct smd_channel * ch ;
unsigned long flags ;
if ( smd_initialized = = 0 ) {
pr_info ( " smd_open() before smd_init() \n " ) ;
return - ENODEV ;
}
ch = smd_get_channel ( name ) ;
if ( ! ch )
return - ENODEV ;
if ( notify = = 0 )
notify = do_nothing_notify ;
ch - > notify = notify ;
ch - > current_packet = 0 ;
ch - > last_state = SMD_SS_CLOSED ;
ch - > priv = priv ;
* _ch = ch ;
spin_lock_irqsave ( & smd_lock , flags ) ;
2009-07-02 05:30:47 +04:00
if ( ( ch - > type & SMD_TYPE_MASK ) = = SMD_TYPE_APPS_MODEM )
list_add ( & ch - > ch_list , & smd_ch_list_modem ) ;
else
list_add ( & ch - > ch_list , & smd_ch_list_dsp ) ;
2008-09-30 03:00:48 +04:00
/* If the remote side is CLOSING, we need to get it to
* move to OPENING ( which we ' ll do by moving from CLOSED to
* OPENING ) and then get it to move from OPENING to
* OPENED ( by doing the same state change ourselves ) .
*
* Otherwise , it should be OPENING and we can move directly
* to OPENED so that it will follow .
*/
if ( ch - > recv - > state = = SMD_SS_CLOSING ) {
ch - > send - > head = 0 ;
2009-04-27 05:38:49 +04:00
ch_set_state ( ch , SMD_SS_OPENING ) ;
2008-09-30 03:00:48 +04:00
} else {
2009-04-27 05:38:49 +04:00
ch_set_state ( ch , SMD_SS_OPENED ) ;
2008-09-30 03:00:48 +04:00
}
spin_unlock_irqrestore ( & smd_lock , flags ) ;
smd_kick ( ch ) ;
return 0 ;
}
int smd_close ( smd_channel_t * ch )
{
unsigned long flags ;
if ( ch = = 0 )
return - 1 ;
spin_lock_irqsave ( & smd_lock , flags ) ;
ch - > notify = do_nothing_notify ;
list_del ( & ch - > ch_list ) ;
2009-04-27 05:38:49 +04:00
ch_set_state ( ch , SMD_SS_CLOSED ) ;
2008-09-30 03:00:48 +04:00
spin_unlock_irqrestore ( & smd_lock , flags ) ;
mutex_lock ( & smd_creation_mutex ) ;
list_add ( & ch - > ch_list , & smd_ch_closed_list ) ;
mutex_unlock ( & smd_creation_mutex ) ;
return 0 ;
}
int smd_read ( smd_channel_t * ch , void * data , int len )
{
return ch - > read ( ch , data , len ) ;
}
int smd_write ( smd_channel_t * ch , const void * data , int len )
{
return ch - > write ( ch , data , len ) ;
}
2009-12-08 02:28:08 +03:00
int smd_write_atomic ( smd_channel_t * ch , const void * data , int len )
{
unsigned long flags ;
int res ;
spin_lock_irqsave ( & smd_lock , flags ) ;
res = ch - > write ( ch , data , len ) ;
spin_unlock_irqrestore ( & smd_lock , flags ) ;
return res ;
}
2008-09-30 03:00:48 +04:00
int smd_read_avail ( smd_channel_t * ch )
{
return ch - > read_avail ( ch ) ;
}
int smd_write_avail ( smd_channel_t * ch )
{
return ch - > write_avail ( ch ) ;
}
int smd_wait_until_readable ( smd_channel_t * ch , int bytes )
{
return - 1 ;
}
int smd_wait_until_writable ( smd_channel_t * ch , int bytes )
{
return - 1 ;
}
int smd_cur_packet_size ( smd_channel_t * ch )
{
return ch - > current_packet ;
}
/* ------------------------------------------------------------------------- */
void * smem_alloc ( unsigned id , unsigned size )
{
return smem_find ( id , size ) ;
}
2012-09-15 00:20:43 +04:00
void __iomem * smem_item ( unsigned id , unsigned * size )
2008-09-30 03:00:48 +04:00
{
struct smem_shared * shared = ( void * ) MSM_SHARED_RAM_BASE ;
struct smem_heap_entry * toc = shared - > heap_toc ;
if ( id > = SMEM_NUM_ITEMS )
2012-09-15 00:20:43 +04:00
return NULL ;
2008-09-30 03:00:48 +04:00
if ( toc [ id ] . allocated ) {
* size = toc [ id ] . size ;
2012-09-15 00:20:43 +04:00
return ( MSM_SHARED_RAM_BASE + toc [ id ] . offset ) ;
2009-04-27 05:38:49 +04:00
} else {
* size = 0 ;
2008-09-30 03:00:48 +04:00
}
2012-09-15 00:20:43 +04:00
return NULL ;
2008-09-30 03:00:48 +04:00
}
void * smem_find ( unsigned id , unsigned size_in )
{
unsigned size ;
void * ptr ;
2009-04-27 05:38:49 +04:00
ptr = smem_item ( id , & size ) ;
2008-09-30 03:00:48 +04:00
if ( ! ptr )
return 0 ;
size_in = ALIGN ( size_in , 8 ) ;
if ( size_in ! = size ) {
pr_err ( " smem_find(%d, %d): wrong size %d \n " ,
id , size_in , size ) ;
return 0 ;
}
return ptr ;
}
static irqreturn_t smsm_irq_handler ( int irq , void * data )
{
unsigned long flags ;
2009-04-27 05:38:49 +04:00
unsigned apps , modm ;
2008-09-30 03:00:48 +04:00
spin_lock_irqsave ( & smem_lock , flags ) ;
2009-05-21 03:52:36 +04:00
apps = raw_smsm_get_state ( SMSM_STATE_APPS ) ;
modm = raw_smsm_get_state ( SMSM_STATE_MODEM ) ;
2008-09-30 03:00:48 +04:00
2009-04-27 05:38:49 +04:00
if ( msm_smd_debug_mask & MSM_SMSM_DEBUG )
pr_info ( " <SM %08x %08x> \n " , apps , modm ) ;
2010-03-17 01:20:07 +03:00
if ( modm & SMSM_RESET )
2009-04-27 05:38:49 +04:00
handle_modem_crash ( ) ;
2010-03-17 01:20:07 +03:00
2009-04-27 05:38:49 +04:00
do_smd_probe ( ) ;
2008-09-30 03:00:48 +04:00
spin_unlock_irqrestore ( & smem_lock , flags ) ;
return IRQ_HANDLED ;
}
2009-05-21 03:52:36 +04:00
int smsm_change_state ( enum smsm_state_item item ,
uint32_t clear_mask , uint32_t set_mask )
2008-09-30 03:00:48 +04:00
{
2012-09-15 00:20:43 +04:00
void __iomem * addr = smd_info . state + item * 4 ;
2008-09-30 03:00:48 +04:00
unsigned long flags ;
2009-04-27 05:38:49 +04:00
unsigned state ;
if ( ! smd_info . ready )
return - EIO ;
2008-09-30 03:00:48 +04:00
spin_lock_irqsave ( & smem_lock , flags ) ;
2009-05-21 03:52:36 +04:00
if ( raw_smsm_get_state ( SMSM_STATE_MODEM ) & SMSM_RESET )
2009-04-27 05:38:49 +04:00
handle_modem_crash ( ) ;
2009-05-21 03:52:36 +04:00
state = ( readl ( addr ) & ~ clear_mask ) | set_mask ;
writel ( state , addr ) ;
2009-04-27 05:38:49 +04:00
if ( msm_smd_debug_mask & MSM_SMSM_DEBUG )
2009-05-21 03:52:36 +04:00
pr_info ( " smsm_change_state %d %x \n " , item , state ) ;
2009-04-27 05:38:49 +04:00
notify_other_smsm ( ) ;
2008-09-30 03:00:48 +04:00
spin_unlock_irqrestore ( & smem_lock , flags ) ;
return 0 ;
}
2009-05-21 03:52:36 +04:00
uint32_t smsm_get_state ( enum smsm_state_item item )
2008-09-30 03:00:48 +04:00
{
unsigned long flags ;
uint32_t rv ;
spin_lock_irqsave ( & smem_lock , flags ) ;
2009-05-21 03:52:36 +04:00
rv = readl ( smd_info . state + item * 4 ) ;
2008-09-30 03:00:48 +04:00
2009-05-21 03:52:36 +04:00
if ( item = = SMSM_STATE_MODEM & & ( rv & SMSM_RESET ) )
2008-09-30 03:00:48 +04:00
handle_modem_crash ( ) ;
spin_unlock_irqrestore ( & smem_lock , flags ) ;
return rv ;
}
2009-06-17 01:48:21 +04:00
# ifdef CONFIG_ARCH_MSM_SCORPION
2008-09-30 03:00:48 +04:00
int smsm_set_sleep_duration ( uint32_t delay )
{
2009-07-02 04:58:37 +04:00
struct msm_dem_slave_data * ptr ;
ptr = smem_find ( SMEM_APPS_DEM_SLAVE_DATA , sizeof ( * ptr ) ) ;
2008-09-30 03:00:48 +04:00
if ( ptr = = NULL ) {
2009-06-17 01:48:21 +04:00
pr_err ( " smsm_set_sleep_duration <SM NO APPS_DEM_SLAVE_DATA> \n " ) ;
2008-09-30 03:00:48 +04:00
return - EIO ;
}
if ( msm_smd_debug_mask & MSM_SMSM_DEBUG )
pr_info ( " smsm_set_sleep_duration %d -> %d \n " ,
2009-06-17 01:48:21 +04:00
ptr - > sleep_time , delay ) ;
ptr - > sleep_time = delay ;
2008-09-30 03:00:48 +04:00
return 0 ;
}
2009-06-17 01:48:21 +04:00
# else
int smsm_set_sleep_duration ( uint32_t delay )
2008-09-30 03:00:48 +04:00
{
2009-06-17 01:48:21 +04:00
uint32_t * ptr ;
2008-09-30 03:00:48 +04:00
2009-07-02 04:58:37 +04:00
ptr = smem_find ( SMEM_SMSM_SLEEP_DELAY , sizeof ( * ptr ) ) ;
2008-09-30 03:00:48 +04:00
if ( ptr = = NULL ) {
2009-06-17 01:48:21 +04:00
pr_err ( " smsm_set_sleep_duration <SM NO SLEEP_DELAY> \n " ) ;
2008-09-30 03:00:48 +04:00
return - EIO ;
}
if ( msm_smd_debug_mask & MSM_SMSM_DEBUG )
2009-06-17 01:48:21 +04:00
pr_info ( " smsm_set_sleep_duration %d -> %d \n " ,
* ptr , delay ) ;
* ptr = delay ;
2008-09-30 03:00:48 +04:00
return 0 ;
}
2009-06-17 01:48:21 +04:00
# endif
2008-09-30 03:00:48 +04:00
int smd_core_init ( void )
{
int r ;
2009-04-27 05:38:49 +04:00
/* wait for essential items to be initialized */
for ( ; ; ) {
unsigned size ;
2012-09-15 00:20:43 +04:00
void __iomem * state ;
2009-04-27 05:38:49 +04:00
state = smem_item ( SMEM_SMSM_SHARED_STATE , & size ) ;
2009-05-21 03:52:36 +04:00
if ( size = = SMSM_V1_SIZE | | size = = SMSM_V2_SIZE ) {
2012-09-15 00:20:43 +04:00
smd_info . state = state ;
2009-04-27 05:38:49 +04:00
break ;
}
}
smd_info . ready = 1 ;
2009-07-02 05:30:47 +04:00
r = request_irq ( INT_A9_M2A_0 , smd_modem_irq_handler ,
2008-09-30 03:00:48 +04:00
IRQF_TRIGGER_RISING , " smd_dev " , 0 ) ;
if ( r < 0 )
return r ;
r = enable_irq_wake ( INT_A9_M2A_0 ) ;
if ( r < 0 )
pr_err ( " smd_core_init: enable_irq_wake failed for A9_M2A_0 \n " ) ;
r = request_irq ( INT_A9_M2A_5 , smsm_irq_handler ,
IRQF_TRIGGER_RISING , " smsm_dev " , 0 ) ;
if ( r < 0 ) {
free_irq ( INT_A9_M2A_0 , 0 ) ;
return r ;
}
r = enable_irq_wake ( INT_A9_M2A_5 ) ;
if ( r < 0 )
pr_err ( " smd_core_init: enable_irq_wake failed for A9_M2A_5 \n " ) ;
2009-07-02 05:30:47 +04:00
# if defined(CONFIG_QDSP6)
r = request_irq ( INT_ADSP_A11 , smd_dsp_irq_handler ,
IRQF_TRIGGER_RISING , " smd_dsp " , 0 ) ;
if ( r < 0 ) {
free_irq ( INT_A9_M2A_0 , 0 ) ;
free_irq ( INT_A9_M2A_5 , 0 ) ;
return r ;
}
# endif
2009-04-27 05:38:49 +04:00
/* check for any SMD channels that may already exist */
do_smd_probe ( ) ;
/* indicate that we're up and running */
2009-05-21 03:52:36 +04:00
smsm_change_state ( SMSM_STATE_APPS ,
2009-06-17 01:48:21 +04:00
~ 0 , SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT | SMSM_RUN ) ;
# ifdef CONFIG_ARCH_MSM_SCORPION
smsm_change_state ( SMSM_STATE_APPS_DEM , ~ 0 , 0 ) ;
# endif
2008-09-30 03:00:48 +04:00
return 0 ;
}
2012-12-22 02:02:24 +04:00
static int msm_smd_probe ( struct platform_device * pdev )
2008-09-30 03:00:48 +04:00
{
2010-03-18 22:31:08 +03:00
/*
* If we haven ' t waited for the ARM9 to boot up till now ,
* then we need to wait here . Otherwise this should just
* return immediately .
*/
proc_comm_boot_wait ( ) ;
2008-09-30 03:00:48 +04:00
INIT_WORK ( & probe_work , smd_channel_probe_worker ) ;
if ( smd_core_init ( ) ) {
pr_err ( " smd_core_init() failed \n " ) ;
return - 1 ;
}
do_smd_probe ( ) ;
msm_check_for_modem_crash = check_for_modem_crash ;
2009-11-16 05:16:43 +03:00
msm_init_last_radio_log ( THIS_MODULE ) ;
2008-09-30 03:00:48 +04:00
smd_initialized = 1 ;
return 0 ;
}
static struct platform_driver msm_smd_driver = {
. probe = msm_smd_probe ,
. driver = {
. name = MODULE_NAME ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init msm_smd_init ( void )
{
return platform_driver_register ( & msm_smd_driver ) ;
}
module_init ( msm_smd_init ) ;
MODULE_DESCRIPTION ( " MSM Shared Memory Core " ) ;
MODULE_AUTHOR ( " Brian Swetland <swetland@google.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;