2010-09-28 01:11:57 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2010
* Author : Stefan Nilsson < stefan . xk . nilsson @ stericsson . com > for ST - Ericsson .
* Author : Martin Persson < martin . persson @ stericsson . com > for ST - Ericsson .
* License terms : GNU General Public License ( GPL ) , version 2.
*/
/*
* Mailbox nomenclature :
*
* APE MODEM
* mbox pairX
* . . . . . . . . . . . . . . . . . . . . . . . . . .
* . .
* . peer .
* . send - - - - .
* . - - > | | .
* . | | .
* . - - - - .
* . .
* . local .
* . rec - - - - .
* . | | < - - .
* . | | .
* . - - - - .
* . . . . . . . . . . . . . . . . . . . . . . . . .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/platform_device.h>
# include <linux/debugfs.h>
# include <linux/seq_file.h>
# include <linux/completion.h>
2010-12-17 23:16:23 +03:00
# include <mach/mbox-db5500.h>
2010-09-28 01:11:57 +04:00
# define MBOX_NAME "mbox"
# define MBOX_FIFO_DATA 0x000
# define MBOX_FIFO_ADD 0x004
# define MBOX_FIFO_REMOVE 0x008
# define MBOX_FIFO_THRES_FREE 0x00C
# define MBOX_FIFO_THRES_OCCUP 0x010
# define MBOX_FIFO_STATUS 0x014
# define MBOX_DISABLE_IRQ 0x4
# define MBOX_ENABLE_IRQ 0x0
# define MBOX_LATCH 1
/* Global list of all mailboxes */
static struct list_head mboxs = LIST_HEAD_INIT ( mboxs ) ;
static struct mbox * get_mbox_with_id ( u8 id )
{
u8 i ;
struct list_head * pos = & mboxs ;
for ( i = 0 ; i < = id ; i + + )
pos = pos - > next ;
return ( struct mbox * ) list_entry ( pos , struct mbox , list ) ;
}
int mbox_send ( struct mbox * mbox , u32 mbox_msg , bool block )
{
int res = 0 ;
spin_lock ( & mbox - > lock ) ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" About to buffer 0x%X to mailbox 0x%X. "
" ri = %d, wi = %d \n " ,
mbox_msg , ( u32 ) mbox , mbox - > read_index ,
mbox - > write_index ) ;
/* Check if write buffer is full */
while ( ( ( mbox - > write_index + 1 ) % MBOX_BUF_SIZE ) = = mbox - > read_index ) {
if ( ! block ) {
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Buffer full in non-blocking call! "
" Returning -ENOMEM! \n " ) ;
res = - ENOMEM ;
goto exit ;
}
spin_unlock ( & mbox - > lock ) ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Buffer full in blocking call! Sleeping... \n " ) ;
mbox - > client_blocked = 1 ;
wait_for_completion ( & mbox - > buffer_available ) ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Blocking send was woken up! Trying again... \n " ) ;
spin_lock ( & mbox - > lock ) ;
}
mbox - > buffer [ mbox - > write_index ] = mbox_msg ;
mbox - > write_index = ( mbox - > write_index + 1 ) % MBOX_BUF_SIZE ;
/*
* Indicate that we want an IRQ as soon as there is a slot
* in the FIFO
*/
writel ( MBOX_ENABLE_IRQ , mbox - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ;
exit :
spin_unlock ( & mbox - > lock ) ;
return res ;
}
EXPORT_SYMBOL ( mbox_send ) ;
# if defined(CONFIG_DEBUG_FS)
/*
* Expected input : < value > < nbr sends >
* Example : " echo 0xdeadbeef 4 > mbox-node " sends 0xdeadbeef 4 times
*/
static ssize_t mbox_write_fifo ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
unsigned long mbox_mess ;
unsigned long nbr_sends ;
unsigned long i ;
char int_buf [ 16 ] ;
char * token ;
char * val ;
struct mbox * mbox = ( struct mbox * ) dev - > platform_data ;
strncpy ( ( char * ) & int_buf , buf , sizeof ( int_buf ) ) ;
token = ( char * ) & int_buf ;
/* Parse message */
val = strsep ( & token , " " ) ;
if ( ( val = = NULL ) | | ( strict_strtoul ( val , 16 , & mbox_mess ) ! = 0 ) )
mbox_mess = 0xDEADBEEF ;
val = strsep ( & token , " " ) ;
if ( ( val = = NULL ) | | ( strict_strtoul ( val , 10 , & nbr_sends ) ! = 0 ) )
nbr_sends = 1 ;
dev_dbg ( dev , " Will write 0x%lX %ld times using data struct at 0x%X \n " ,
mbox_mess , nbr_sends , ( u32 ) mbox ) ;
for ( i = 0 ; i < nbr_sends ; i + + )
mbox_send ( mbox , mbox_mess , true ) ;
return count ;
}
static ssize_t mbox_read_fifo ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
int mbox_value ;
struct mbox * mbox = ( struct mbox * ) dev - > platform_data ;
if ( ( readl ( mbox - > virtbase_local + MBOX_FIFO_STATUS ) & 0x7 ) < = 0 )
return sprintf ( buf , " Mailbox is empty \n " ) ;
mbox_value = readl ( mbox - > virtbase_local + MBOX_FIFO_DATA ) ;
writel ( MBOX_LATCH , ( mbox - > virtbase_local + MBOX_FIFO_REMOVE ) ) ;
return sprintf ( buf , " 0x%X \n " , mbox_value ) ;
}
static DEVICE_ATTR ( fifo , S_IWUGO | S_IRUGO , mbox_read_fifo , mbox_write_fifo ) ;
static int mbox_show ( struct seq_file * s , void * data )
{
struct list_head * pos ;
u8 mbox_index = 0 ;
list_for_each ( pos , & mboxs ) {
struct mbox * m =
( struct mbox * ) list_entry ( pos , struct mbox , list ) ;
if ( m = = NULL ) {
seq_printf ( s ,
" Unable to retrieve mailbox %d \n " ,
mbox_index ) ;
continue ;
}
spin_lock ( & m - > lock ) ;
if ( ( m - > virtbase_peer = = NULL ) | | ( m - > virtbase_local = = NULL ) ) {
seq_printf ( s , " MAILBOX %d not setup or corrupt \n " ,
mbox_index ) ;
spin_unlock ( & m - > lock ) ;
continue ;
}
seq_printf ( s ,
" =========================== \n "
" MAILBOX %d \n "
" PEER MAILBOX DUMP \n "
" --------------------------- \n "
" FIFO: 0x%X (%d) \n "
" Free Threshold: 0x%.2X (%d) \n "
" Occupied Threshold: 0x%.2X (%d) \n "
" Status: 0x%.2X (%d) \n "
" Free spaces (ot): %d (%d) \n "
" Occup spaces (ot): %d (%d) \n "
" =========================== \n "
" LOCAL MAILBOX DUMP \n "
" --------------------------- \n "
" FIFO: 0x%.X (%d) \n "
" Free Threshold: 0x%.2X (%d) \n "
" Occupied Threshold: 0x%.2X (%d) \n "
" Status: 0x%.2X (%d) \n "
" Free spaces (ot): %d (%d) \n "
" Occup spaces (ot): %d (%d) \n "
" =========================== \n "
" write_index: %d \n "
" read_index : %d \n "
" =========================== \n "
" \n " ,
mbox_index ,
readl ( m - > virtbase_peer + MBOX_FIFO_DATA ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_DATA ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_THRES_OCCUP ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_THRES_OCCUP ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) ,
readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) ,
( readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) > > 4 ) & 0x7 ,
( readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) > > 7 ) & 0x1 ,
( readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) > > 0 ) & 0x7 ,
( readl ( m - > virtbase_peer + MBOX_FIFO_STATUS ) > > 3 ) & 0x1 ,
readl ( m - > virtbase_local + MBOX_FIFO_DATA ) ,
readl ( m - > virtbase_local + MBOX_FIFO_DATA ) ,
readl ( m - > virtbase_local + MBOX_FIFO_THRES_FREE ) ,
readl ( m - > virtbase_local + MBOX_FIFO_THRES_FREE ) ,
readl ( m - > virtbase_local + MBOX_FIFO_THRES_OCCUP ) ,
readl ( m - > virtbase_local + MBOX_FIFO_THRES_OCCUP ) ,
readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) ,
readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) ,
( readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) > > 4 ) & 0x7 ,
( readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) > > 7 ) & 0x1 ,
( readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) > > 0 ) & 0x7 ,
( readl ( m - > virtbase_local + MBOX_FIFO_STATUS ) > > 3 ) & 0x1 ,
m - > write_index , m - > read_index ) ;
mbox_index + + ;
spin_unlock ( & m - > lock ) ;
}
return 0 ;
}
static int mbox_open ( struct inode * inode , struct file * file )
{
return single_open ( file , mbox_show , NULL ) ;
}
static const struct file_operations mbox_operations = {
. owner = THIS_MODULE ,
. open = mbox_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
# endif
static irqreturn_t mbox_irq ( int irq , void * arg )
{
u32 mbox_value ;
int nbr_occup ;
int nbr_free ;
struct mbox * mbox = ( struct mbox * ) arg ;
spin_lock ( & mbox - > lock ) ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" mbox IRQ [%d] received. ri = %d, wi = %d \n " ,
irq , mbox - > read_index , mbox - > write_index ) ;
/*
* Check if we have any outgoing messages , and if there is space for
* them in the FIFO .
*/
if ( mbox - > read_index ! = mbox - > write_index ) {
/*
* Check by reading FREE for LOCAL since that indicates
* OCCUP for PEER
*/
nbr_free = ( readl ( mbox - > virtbase_local + MBOX_FIFO_STATUS )
> > 4 ) & 0x7 ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Status indicates %d empty spaces in the FIFO! \n " ,
nbr_free ) ;
while ( ( nbr_free > 0 ) & &
( mbox - > read_index ! = mbox - > write_index ) ) {
/* Write the message and latch it into the FIFO */
writel ( mbox - > buffer [ mbox - > read_index ] ,
( mbox - > virtbase_peer + MBOX_FIFO_DATA ) ) ;
writel ( MBOX_LATCH ,
( mbox - > virtbase_peer + MBOX_FIFO_ADD ) ) ;
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Wrote message 0x%X to addr 0x%X \n " ,
mbox - > buffer [ mbox - > read_index ] ,
( u32 ) ( mbox - > virtbase_peer + MBOX_FIFO_DATA ) ) ;
nbr_free - - ;
mbox - > read_index =
( mbox - > read_index + 1 ) % MBOX_BUF_SIZE ;
}
/*
* Check if we still want IRQ : s when there is free
* space to send
*/
if ( mbox - > read_index ! = mbox - > write_index ) {
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Still have messages to send, but FIFO full. "
" Request IRQ again! \n " ) ;
writel ( MBOX_ENABLE_IRQ ,
mbox - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ;
} else {
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" No more messages to send. "
" Do not request IRQ again! \n " ) ;
writel ( MBOX_DISABLE_IRQ ,
mbox - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ;
}
/*
* Check if we can signal any blocked clients that it is OK to
* start buffering again
*/
if ( mbox - > client_blocked & &
( ( ( mbox - > write_index + 1 ) % MBOX_BUF_SIZE )
! = mbox - > read_index ) ) {
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Waking up blocked client \n " ) ;
complete ( & mbox - > buffer_available ) ;
mbox - > client_blocked = 0 ;
}
}
/* Check if we have any incoming messages */
nbr_occup = readl ( mbox - > virtbase_local + MBOX_FIFO_STATUS ) & 0x7 ;
if ( nbr_occup = = 0 )
goto exit ;
if ( mbox - > cb = = NULL ) {
dev_dbg ( & ( mbox - > pdev - > dev ) , " No receive callback registered, "
" leaving %d incoming messages in fifo! \n " , nbr_occup ) ;
goto exit ;
}
/* Read and acknowledge the message */
mbox_value = readl ( mbox - > virtbase_local + MBOX_FIFO_DATA ) ;
writel ( MBOX_LATCH , ( mbox - > virtbase_local + MBOX_FIFO_REMOVE ) ) ;
/* Notify consumer of new mailbox message */
dev_dbg ( & ( mbox - > pdev - > dev ) , " Calling callback for message 0x%X! \n " ,
mbox_value ) ;
mbox - > cb ( mbox_value , mbox - > client_data ) ;
exit :
dev_dbg ( & ( mbox - > pdev - > dev ) , " Exit mbox IRQ. ri = %d, wi = %d \n " ,
mbox - > read_index , mbox - > write_index ) ;
spin_unlock ( & mbox - > lock ) ;
return IRQ_HANDLED ;
}
/* Setup is executed once for each mbox pair */
struct mbox * mbox_setup ( u8 mbox_id , mbox_recv_cb_t * mbox_cb , void * priv )
{
struct resource * resource ;
int irq ;
int res ;
struct mbox * mbox ;
mbox = get_mbox_with_id ( mbox_id ) ;
if ( mbox = = NULL ) {
dev_err ( & ( mbox - > pdev - > dev ) , " Incorrect mailbox id: %d! \n " ,
mbox_id ) ;
goto exit ;
}
/*
* Check if mailbox has been allocated to someone else ,
* otherwise allocate it
*/
if ( mbox - > allocated ) {
dev_err ( & ( mbox - > pdev - > dev ) , " Mailbox number %d is busy! \n " ,
mbox_id ) ;
mbox = NULL ;
goto exit ;
}
mbox - > allocated = true ;
dev_dbg ( & ( mbox - > pdev - > dev ) , " Initiating mailbox number %d: 0x%X... \n " ,
mbox_id , ( u32 ) mbox ) ;
mbox - > client_data = priv ;
mbox - > cb = mbox_cb ;
/* Get addr for peer mailbox and ioremap it */
resource = platform_get_resource_byname ( mbox - > pdev ,
IORESOURCE_MEM ,
" mbox_peer " ) ;
if ( resource = = NULL ) {
dev_err ( & ( mbox - > pdev - > dev ) ,
" Unable to retrieve mbox peer resource \n " ) ;
mbox = NULL ;
goto exit ;
}
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Resource name: %s start: 0x%X, end: 0x%X \n " ,
resource - > name , resource - > start , resource - > end ) ;
mbox - > virtbase_peer =
ioremap ( resource - > start , resource - > end - resource - > start ) ;
if ( ! mbox - > virtbase_peer ) {
dev_err ( & ( mbox - > pdev - > dev ) , " Unable to ioremap peer mbox \n " ) ;
mbox = NULL ;
goto exit ;
}
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X \n " ,
resource - > start , resource - > end , ( u32 ) mbox - > virtbase_peer ) ;
/* Get addr for local mailbox and ioremap it */
resource = platform_get_resource_byname ( mbox - > pdev ,
IORESOURCE_MEM ,
" mbox_local " ) ;
if ( resource = = NULL ) {
dev_err ( & ( mbox - > pdev - > dev ) ,
" Unable to retrieve mbox local resource \n " ) ;
mbox = NULL ;
goto exit ;
}
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" Resource name: %s start: 0x%X, end: 0x%X \n " ,
resource - > name , resource - > start , resource - > end ) ;
mbox - > virtbase_local =
ioremap ( resource - > start , resource - > end - resource - > start ) ;
if ( ! mbox - > virtbase_local ) {
dev_err ( & ( mbox - > pdev - > dev ) , " Unable to ioremap local mbox \n " ) ;
mbox = NULL ;
goto exit ;
}
dev_dbg ( & ( mbox - > pdev - > dev ) ,
" ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X \n " ,
resource - > start , resource - > end , ( u32 ) mbox - > virtbase_peer ) ;
init_completion ( & mbox - > buffer_available ) ;
mbox - > client_blocked = 0 ;
/* Get IRQ for mailbox and allocate it */
irq = platform_get_irq_byname ( mbox - > pdev , " mbox_irq " ) ;
if ( irq < 0 ) {
dev_err ( & ( mbox - > pdev - > dev ) ,
" Unable to retrieve mbox irq resource \n " ) ;
mbox = NULL ;
goto exit ;
}
dev_dbg ( & ( mbox - > pdev - > dev ) , " Allocating irq %d... \n " , irq ) ;
res = request_irq ( irq , mbox_irq , 0 , mbox - > name , ( void * ) mbox ) ;
if ( res < 0 ) {
dev_err ( & ( mbox - > pdev - > dev ) ,
" Unable to allocate mbox irq %d \n " , irq ) ;
mbox = NULL ;
goto exit ;
}
/* Set up mailbox to not launch IRQ on free space in mailbox */
writel ( MBOX_DISABLE_IRQ , mbox - > virtbase_peer + MBOX_FIFO_THRES_FREE ) ;
/*
* Set up mailbox to launch IRQ on new message if we have
* a callback set . If not , do not raise IRQ , but keep message
* in FIFO for manual retrieval
*/
if ( mbox_cb ! = NULL )
writel ( MBOX_ENABLE_IRQ ,
mbox - > virtbase_local + MBOX_FIFO_THRES_OCCUP ) ;
else
writel ( MBOX_DISABLE_IRQ ,
mbox - > virtbase_local + MBOX_FIFO_THRES_OCCUP ) ;
# if defined(CONFIG_DEBUG_FS)
res = device_create_file ( & ( mbox - > pdev - > dev ) , & dev_attr_fifo ) ;
if ( res ! = 0 )
dev_warn ( & ( mbox - > pdev - > dev ) ,
" Unable to create mbox sysfs entry " ) ;
( void ) debugfs_create_file ( " mbox " , S_IFREG | S_IRUGO , NULL ,
NULL , & mbox_operations ) ;
# endif
dev_info ( & ( mbox - > pdev - > dev ) ,
2011-01-28 22:47:20 +03:00
" Mailbox driver with index %d initiated! \n " , mbox_id ) ;
2010-09-28 01:11:57 +04:00
exit :
return mbox ;
}
EXPORT_SYMBOL ( mbox_setup ) ;
int __init mbox_probe ( struct platform_device * pdev )
{
struct mbox local_mbox ;
struct mbox * mbox ;
int res = 0 ;
dev_dbg ( & ( pdev - > dev ) , " Probing mailbox (pdev = 0x%X)... \n " , ( u32 ) pdev ) ;
memset ( & local_mbox , 0x0 , sizeof ( struct mbox ) ) ;
/* Associate our mbox data with the platform device */
res = platform_device_add_data ( pdev ,
( void * ) & local_mbox ,
sizeof ( struct mbox ) ) ;
if ( res ! = 0 ) {
dev_err ( & ( pdev - > dev ) ,
" Unable to allocate driver platform data! \n " ) ;
goto exit ;
}
mbox = ( struct mbox * ) pdev - > dev . platform_data ;
mbox - > pdev = pdev ;
mbox - > write_index = 0 ;
mbox - > read_index = 0 ;
INIT_LIST_HEAD ( & ( mbox - > list ) ) ;
list_add_tail ( & ( mbox - > list ) , & mboxs ) ;
sprintf ( mbox - > name , " %s " , MBOX_NAME ) ;
spin_lock_init ( & mbox - > lock ) ;
dev_info ( & ( pdev - > dev ) , " Mailbox driver loaded \n " ) ;
exit :
return res ;
}
static struct platform_driver mbox_driver = {
. driver = {
. name = MBOX_NAME ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init mbox_init ( void )
{
return platform_driver_probe ( & mbox_driver , mbox_probe ) ;
}
module_init ( mbox_init ) ;
void __exit mbox_exit ( void )
{
platform_driver_unregister ( & mbox_driver ) ;
}
module_exit ( mbox_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " MBOX driver " ) ;