2006-12-08 02:43:59 +03:00
/*
2009-03-24 04:07:23 +03:00
* Mailbox reservation modules for OMAP2 / 3
2006-12-08 02:43:59 +03:00
*
2009-03-24 04:07:23 +03:00
* Copyright ( C ) 2006 - 2009 Nokia Corporation
2006-12-08 02:43:59 +03:00
* Written by : Hiroshi DOYU < Hiroshi . DOYU @ nokia . com >
2009-03-24 04:07:23 +03:00
* and Paul Mundt
2006-12-08 02:43:59 +03:00
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
2011-11-08 00:27:10 +04:00
# include <linux/module.h>
2013-01-29 03:21:58 +04:00
# include <linux/slab.h>
2006-12-08 02:43:59 +03:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/platform_device.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2011-02-24 23:51:33 +03:00
# include <linux/pm_runtime.h>
2013-01-29 03:21:58 +04:00
# include <linux/platform_data/mailbox-omap.h>
2012-08-28 04:43:01 +04:00
2013-03-13 02:55:29 +04:00
# include "omap-mbox.h"
2006-12-08 02:43:59 +03:00
2009-03-24 04:07:23 +03:00
# define MAILBOX_REVISION 0x000
# define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
# define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m))
# define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m))
# define MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u))
# define MAILBOX_IRQENABLE(u) (0x104 + 8 * (u))
2006-12-08 02:43:59 +03:00
2012-05-09 03:31:13 +04:00
# define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u))
# define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u))
# define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u))
2009-11-22 21:11:22 +03:00
# define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m)))
# define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1))
2006-12-08 02:43:59 +03:00
2009-03-24 04:07:26 +03:00
# define MBOX_REG_SIZE 0x120
2009-11-22 21:11:22 +03:00
# define OMAP4_MBOX_REG_SIZE 0x130
2009-03-24 04:07:26 +03:00
# define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32))
2009-11-22 21:11:22 +03:00
# define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32))
2009-03-24 04:07:26 +03:00
2009-03-24 04:07:23 +03:00
static void __iomem * mbox_base ;
2006-12-08 02:43:59 +03:00
struct omap_mbox2_fifo {
unsigned long msg ;
unsigned long fifo_stat ;
unsigned long msg_stat ;
} ;
struct omap_mbox2_priv {
struct omap_mbox2_fifo tx_fifo ;
struct omap_mbox2_fifo rx_fifo ;
unsigned long irqenable ;
unsigned long irqstatus ;
u32 newmsg_bit ;
u32 notfull_bit ;
2009-11-22 21:11:22 +03:00
u32 ctx [ OMAP4_MBOX_NR_REGS ] ;
unsigned long irqdisable ;
2013-01-29 03:21:58 +04:00
u32 intr_type ;
2006-12-08 02:43:59 +03:00
} ;
2009-03-24 04:07:23 +03:00
static inline unsigned int mbox_read_reg ( size_t ofs )
2006-12-08 02:43:59 +03:00
{
2009-03-24 04:07:23 +03:00
return __raw_readl ( mbox_base + ofs ) ;
2006-12-08 02:43:59 +03:00
}
2009-03-24 04:07:23 +03:00
static inline void mbox_write_reg ( u32 val , size_t ofs )
2006-12-08 02:43:59 +03:00
{
2009-03-24 04:07:23 +03:00
__raw_writel ( val , mbox_base + ofs ) ;
2006-12-08 02:43:59 +03:00
}
/* Mailbox H/W preparations */
2007-07-30 15:04:04 +04:00
static int omap2_mbox_startup ( struct omap_mbox * mbox )
2006-12-08 02:43:59 +03:00
{
2009-09-25 03:23:09 +04:00
u32 l ;
2006-12-08 02:43:59 +03:00
2011-02-24 23:51:33 +03:00
pm_runtime_enable ( mbox - > dev - > parent ) ;
pm_runtime_get_sync ( mbox - > dev - > parent ) ;
2009-09-25 03:23:09 +04:00
2009-03-24 04:07:24 +03:00
l = mbox_read_reg ( MAILBOX_REVISION ) ;
2010-06-11 19:51:37 +04:00
pr_debug ( " omap mailbox rev %d.%d \n " , ( l & 0xf0 ) > > 4 , ( l & 0x0f ) ) ;
2009-03-24 04:07:24 +03:00
2006-12-08 02:43:59 +03:00
return 0 ;
}
2007-07-30 15:04:04 +04:00
static void omap2_mbox_shutdown ( struct omap_mbox * mbox )
2006-12-08 02:43:59 +03:00
{
2011-02-24 23:51:33 +03:00
pm_runtime_put_sync ( mbox - > dev - > parent ) ;
pm_runtime_disable ( mbox - > dev - > parent ) ;
2006-12-08 02:43:59 +03:00
}
/* Mailbox FIFO handle functions */
2007-07-30 15:04:04 +04:00
static mbox_msg_t omap2_mbox_fifo_read ( struct omap_mbox * mbox )
2006-12-08 02:43:59 +03:00
{
struct omap_mbox2_fifo * fifo =
& ( ( struct omap_mbox2_priv * ) mbox - > priv ) - > rx_fifo ;
return ( mbox_msg_t ) mbox_read_reg ( fifo - > msg ) ;
}
2007-07-30 15:04:04 +04:00
static void omap2_mbox_fifo_write ( struct omap_mbox * mbox , mbox_msg_t msg )
2006-12-08 02:43:59 +03:00
{
struct omap_mbox2_fifo * fifo =
& ( ( struct omap_mbox2_priv * ) mbox - > priv ) - > tx_fifo ;
mbox_write_reg ( msg , fifo - > msg ) ;
}
2007-07-30 15:04:04 +04:00
static int omap2_mbox_fifo_empty ( struct omap_mbox * mbox )
2006-12-08 02:43:59 +03:00
{
struct omap_mbox2_fifo * fifo =
& ( ( struct omap_mbox2_priv * ) mbox - > priv ) - > rx_fifo ;
return ( mbox_read_reg ( fifo - > msg_stat ) = = 0 ) ;
}
2007-07-30 15:04:04 +04:00
static int omap2_mbox_fifo_full ( struct omap_mbox * mbox )
2006-12-08 02:43:59 +03:00
{
struct omap_mbox2_fifo * fifo =
& ( ( struct omap_mbox2_priv * ) mbox - > priv ) - > tx_fifo ;
2009-11-22 21:11:22 +03:00
return mbox_read_reg ( fifo - > fifo_stat ) ;
2006-12-08 02:43:59 +03:00
}
/* Mailbox IRQ handle functions */
2013-06-08 01:27:45 +04:00
static void omap2_mbox_enable_irq ( struct omap_mbox * mbox , omap_mbox_irq_t irq )
2006-12-08 02:43:59 +03:00
{
2010-09-28 06:04:32 +04:00
struct omap_mbox2_priv * p = mbox - > priv ;
2006-12-08 02:43:59 +03:00
u32 l , bit = ( irq = = IRQ_TX ) ? p - > notfull_bit : p - > newmsg_bit ;
l = mbox_read_reg ( p - > irqenable ) ;
l | = bit ;
mbox_write_reg ( l , p - > irqenable ) ;
}
2013-06-08 01:27:45 +04:00
static void omap2_mbox_disable_irq ( struct omap_mbox * mbox , omap_mbox_irq_t irq )
2006-12-08 02:43:59 +03:00
{
2010-09-28 06:04:32 +04:00
struct omap_mbox2_priv * p = mbox - > priv ;
2011-03-03 01:14:18 +03:00
u32 bit = ( irq = = IRQ_TX ) ? p - > notfull_bit : p - > newmsg_bit ;
2013-01-29 03:21:58 +04:00
/*
* Read and update the interrupt configuration register for pre - OMAP4 .
* OMAP4 and later SoCs have a dedicated interrupt disabling register .
*/
if ( ! p - > intr_type )
2011-03-03 01:14:18 +03:00
bit = mbox_read_reg ( p - > irqdisable ) & ~ bit ;
mbox_write_reg ( bit , p - > irqdisable ) ;
2006-12-08 02:43:59 +03:00
}
2013-06-08 01:27:45 +04:00
static void omap2_mbox_ack_irq ( struct omap_mbox * mbox , omap_mbox_irq_t irq )
2006-12-08 02:43:59 +03:00
{
2010-09-28 06:04:32 +04:00
struct omap_mbox2_priv * p = mbox - > priv ;
2006-12-08 02:43:59 +03:00
u32 bit = ( irq = = IRQ_TX ) ? p - > notfull_bit : p - > newmsg_bit ;
mbox_write_reg ( bit , p - > irqstatus ) ;
2009-09-25 03:23:10 +04:00
/* Flush posted write for irq status to avoid spurious interrupts */
mbox_read_reg ( p - > irqstatus ) ;
2006-12-08 02:43:59 +03:00
}
2013-06-08 01:27:45 +04:00
static int omap2_mbox_is_irq ( struct omap_mbox * mbox , omap_mbox_irq_t irq )
2006-12-08 02:43:59 +03:00
{
2010-09-28 06:04:32 +04:00
struct omap_mbox2_priv * p = mbox - > priv ;
2006-12-08 02:43:59 +03:00
u32 bit = ( irq = = IRQ_TX ) ? p - > notfull_bit : p - > newmsg_bit ;
u32 enable = mbox_read_reg ( p - > irqenable ) ;
u32 status = mbox_read_reg ( p - > irqstatus ) ;
2009-11-22 21:11:22 +03:00
return ( int ) ( enable & status & bit ) ;
2006-12-08 02:43:59 +03:00
}
2009-03-24 04:07:26 +03:00
static void omap2_mbox_save_ctx ( struct omap_mbox * mbox )
{
int i ;
struct omap_mbox2_priv * p = mbox - > priv ;
2009-11-22 21:11:22 +03:00
int nr_regs ;
2013-01-29 03:21:58 +04:00
if ( p - > intr_type )
2009-11-22 21:11:22 +03:00
nr_regs = OMAP4_MBOX_NR_REGS ;
else
nr_regs = MBOX_NR_REGS ;
for ( i = 0 ; i < nr_regs ; i + + ) {
2009-03-24 04:07:26 +03:00
p - > ctx [ i ] = mbox_read_reg ( i * sizeof ( u32 ) ) ;
dev_dbg ( mbox - > dev , " %s: [%02x] %08x \n " , __func__ ,
i , p - > ctx [ i ] ) ;
}
}
static void omap2_mbox_restore_ctx ( struct omap_mbox * mbox )
{
int i ;
struct omap_mbox2_priv * p = mbox - > priv ;
2009-11-22 21:11:22 +03:00
int nr_regs ;
2013-01-29 03:21:58 +04:00
if ( p - > intr_type )
2009-11-22 21:11:22 +03:00
nr_regs = OMAP4_MBOX_NR_REGS ;
else
nr_regs = MBOX_NR_REGS ;
for ( i = 0 ; i < nr_regs ; i + + ) {
2009-03-24 04:07:26 +03:00
mbox_write_reg ( p - > ctx [ i ] , i * sizeof ( u32 ) ) ;
dev_dbg ( mbox - > dev , " %s: [%02x] %08x \n " , __func__ ,
i , p - > ctx [ i ] ) ;
}
}
2006-12-08 02:43:59 +03:00
static struct omap_mbox_ops omap2_mbox_ops = {
. type = OMAP_MBOX_TYPE2 ,
. startup = omap2_mbox_startup ,
. shutdown = omap2_mbox_shutdown ,
. fifo_read = omap2_mbox_fifo_read ,
. fifo_write = omap2_mbox_fifo_write ,
. fifo_empty = omap2_mbox_fifo_empty ,
. fifo_full = omap2_mbox_fifo_full ,
. enable_irq = omap2_mbox_enable_irq ,
. disable_irq = omap2_mbox_disable_irq ,
. ack_irq = omap2_mbox_ack_irq ,
. is_irq = omap2_mbox_is_irq ,
2009-03-24 04:07:26 +03:00
. save_ctx = omap2_mbox_save_ctx ,
. restore_ctx = omap2_mbox_restore_ctx ,
2006-12-08 02:43:59 +03:00
} ;
2012-12-22 02:02:24 +04:00
static int omap2_mbox_probe ( struct platform_device * pdev )
2006-12-08 02:43:59 +03:00
{
2010-06-11 19:51:45 +04:00
struct resource * mem ;
2009-03-24 04:07:23 +03:00
int ret ;
2013-01-29 03:21:58 +04:00
struct omap_mbox * * list , * mbox , * mboxblk ;
struct omap_mbox2_priv * priv , * privblk ;
struct omap_mbox_pdata * pdata = pdev - > dev . platform_data ;
struct omap_mbox_dev_info * info ;
int i ;
2010-06-11 19:51:45 +04:00
2013-01-29 03:21:58 +04:00
if ( ! pdata | | ! pdata - > info_cnt | | ! pdata - > info ) {
pr_err ( " %s: platform not supported \n " , __func__ ) ;
return - ENODEV ;
2006-12-08 02:43:59 +03:00
}
2010-10-23 05:10:58 +04:00
2013-01-29 03:21:58 +04:00
/* allocate one extra for marking end of list */
list = kzalloc ( ( pdata - > info_cnt + 1 ) * sizeof ( * list ) , GFP_KERNEL ) ;
if ( ! list )
return - ENOMEM ;
2006-12-08 02:43:59 +03:00
2013-01-29 03:21:58 +04:00
mboxblk = mbox = kzalloc ( pdata - > info_cnt * sizeof ( * mbox ) , GFP_KERNEL ) ;
if ( ! mboxblk ) {
ret = - ENOMEM ;
goto free_list ;
2010-06-11 19:51:45 +04:00
}
2009-11-22 21:11:22 +03:00
2013-01-29 03:21:58 +04:00
privblk = priv = kzalloc ( pdata - > info_cnt * sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! privblk ) {
ret = - ENOMEM ;
goto free_mboxblk ;
2006-12-08 02:43:59 +03:00
}
2013-01-29 03:21:58 +04:00
info = pdata - > info ;
for ( i = 0 ; i < pdata - > info_cnt ; i + + , info + + , priv + + ) {
priv - > tx_fifo . msg = MAILBOX_MESSAGE ( info - > tx_id ) ;
priv - > tx_fifo . fifo_stat = MAILBOX_FIFOSTATUS ( info - > tx_id ) ;
priv - > rx_fifo . msg = MAILBOX_MESSAGE ( info - > rx_id ) ;
priv - > rx_fifo . msg_stat = MAILBOX_MSGSTATUS ( info - > rx_id ) ;
priv - > notfull_bit = MAILBOX_IRQ_NOTFULL ( info - > tx_id ) ;
priv - > newmsg_bit = MAILBOX_IRQ_NEWMSG ( info - > rx_id ) ;
if ( pdata - > intr_type ) {
priv - > irqenable = OMAP4_MAILBOX_IRQENABLE ( info - > usr_id ) ;
priv - > irqstatus = OMAP4_MAILBOX_IRQSTATUS ( info - > usr_id ) ;
priv - > irqdisable =
OMAP4_MAILBOX_IRQENABLE_CLR ( info - > usr_id ) ;
} else {
priv - > irqenable = MAILBOX_IRQENABLE ( info - > usr_id ) ;
priv - > irqstatus = MAILBOX_IRQSTATUS ( info - > usr_id ) ;
priv - > irqdisable = MAILBOX_IRQENABLE ( info - > usr_id ) ;
}
priv - > intr_type = pdata - > intr_type ;
mbox - > priv = priv ;
mbox - > name = info - > name ;
mbox - > ops = & omap2_mbox_ops ;
mbox - > irq = platform_get_irq ( pdev , info - > irq_id ) ;
if ( mbox - > irq < 0 ) {
ret = mbox - > irq ;
goto free_privblk ;
}
list [ i ] = mbox + + ;
2009-11-22 21:11:22 +03:00
}
2009-03-24 04:07:23 +03:00
2010-06-11 19:51:45 +04:00
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-29 03:21:58 +04:00
if ( ! mem ) {
ret = - ENOENT ;
goto free_privblk ;
}
2013-02-02 06:24:51 +04:00
2010-06-11 19:51:45 +04:00
mbox_base = ioremap ( mem - > start , resource_size ( mem ) ) ;
2013-01-29 03:21:58 +04:00
if ( ! mbox_base ) {
ret = - ENOMEM ;
goto free_privblk ;
}
2010-06-11 19:51:45 +04:00
2010-06-11 19:51:46 +04:00
ret = omap_mbox_register ( & pdev - > dev , list ) ;
2013-01-29 03:21:58 +04:00
if ( ret )
goto unmap_mbox ;
platform_set_drvdata ( pdev , list ) ;
2006-12-08 02:43:59 +03:00
2010-12-01 23:15:08 +03:00
return 0 ;
2013-01-29 03:21:58 +04:00
unmap_mbox :
iounmap ( mbox_base ) ;
free_privblk :
kfree ( privblk ) ;
free_mboxblk :
kfree ( mboxblk ) ;
free_list :
kfree ( list ) ;
return ret ;
2006-12-08 02:43:59 +03:00
}
2012-12-22 02:02:24 +04:00
static int omap2_mbox_remove ( struct platform_device * pdev )
2006-12-08 02:43:59 +03:00
{
2013-01-29 03:21:58 +04:00
struct omap_mbox2_priv * privblk ;
struct omap_mbox * * list = platform_get_drvdata ( pdev ) ;
struct omap_mbox * mboxblk = list [ 0 ] ;
privblk = mboxblk - > priv ;
2010-06-11 19:51:46 +04:00
omap_mbox_unregister ( ) ;
2009-03-24 04:07:23 +03:00
iounmap ( mbox_base ) ;
2013-01-29 03:21:58 +04:00
kfree ( privblk ) ;
kfree ( mboxblk ) ;
kfree ( list ) ;
2006-12-08 02:43:59 +03:00
return 0 ;
}
static struct platform_driver omap2_mbox_driver = {
2013-03-13 02:55:29 +04:00
. probe = omap2_mbox_probe ,
. remove = omap2_mbox_remove ,
. driver = {
2010-06-11 19:51:48 +04:00
. name = " omap-mailbox " ,
2006-12-08 02:43:59 +03:00
} ,
} ;
static int __init omap2_mbox_init ( void )
{
return platform_driver_register ( & omap2_mbox_driver ) ;
}
static void __exit omap2_mbox_exit ( void )
{
platform_driver_unregister ( & omap2_mbox_driver ) ;
}
2012-03-04 14:01:11 +04:00
module_init ( omap2_mbox_init ) ;
2006-12-08 02:43:59 +03:00
module_exit ( omap2_mbox_exit ) ;
2009-03-24 04:07:23 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2009-11-22 21:11:22 +03:00
MODULE_DESCRIPTION ( " omap mailbox: omap2/3/4 architecture specific functions " ) ;
2010-05-05 19:33:07 +04:00
MODULE_AUTHOR ( " Hiroshi DOYU <Hiroshi.DOYU@nokia.com> " ) ;
MODULE_AUTHOR ( " Paul Mundt " ) ;
2010-06-11 19:51:48 +04:00
MODULE_ALIAS ( " platform:omap2-mailbox " ) ;