2018-02-28 07:54:54 +03:00
// SPDX-License-Identifier: GPL-2.0
2021-05-22 13:12:54 +03:00
// Copyright (c) 2017-2018 HiSilicon Limited.
2018-02-28 07:54:54 +03:00
// Copyright (c) 2017-2018 Linaro Limited.
# include <linux/bitops.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/mailbox_controller.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "mailbox.h"
# define MBOX_CHAN_MAX 32
# define MBOX_RX 0x0
# define MBOX_TX 0x1
# define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40))
# define MBOX_SRC_REG 0x00
# define MBOX_DST_REG 0x04
# define MBOX_DCLR_REG 0x08
# define MBOX_DSTAT_REG 0x0c
# define MBOX_MODE_REG 0x10
# define MBOX_IMASK_REG 0x14
# define MBOX_ICLR_REG 0x18
# define MBOX_SEND_REG 0x1c
# define MBOX_DATA_REG 0x20
# define MBOX_IPC_LOCK_REG 0xa00
# define MBOX_IPC_UNLOCK 0x1acce551
# define MBOX_AUTOMATIC_ACK 1
# define MBOX_STATE_IDLE BIT(4)
2018-12-03 06:55:09 +03:00
# define MBOX_STATE_READY BIT(5)
2018-02-28 07:54:54 +03:00
# define MBOX_STATE_ACK BIT(7)
# define MBOX_MSG_LEN 8
/**
* Hi3660 mailbox channel information
*
* A channel can be used for TX or RX , it can trigger remote
* processor interrupt to notify remote processor and can receive
* interrupt if has incoming message .
*
* @ dst_irq : Interrupt vector for remote processor
* @ ack_irq : Interrupt vector for local processor
*/
struct hi3660_chan_info {
unsigned int dst_irq ;
unsigned int ack_irq ;
} ;
/**
* Hi3660 mailbox controller data
*
* Mailbox controller includes 32 channels and can allocate
* channel for message transferring .
*
* @ dev : Device to which it is attached
* @ base : Base address of the register mapping region
* @ chan : Representation of channels in mailbox controller
* @ mchan : Representation of channel info
* @ controller : Representation of a communication channel controller
*/
struct hi3660_mbox {
struct device * dev ;
void __iomem * base ;
struct mbox_chan chan [ MBOX_CHAN_MAX ] ;
struct hi3660_chan_info mchan [ MBOX_CHAN_MAX ] ;
struct mbox_controller controller ;
} ;
static struct hi3660_mbox * to_hi3660_mbox ( struct mbox_controller * mbox )
{
return container_of ( mbox , struct hi3660_mbox , controller ) ;
}
static int hi3660_mbox_check_state ( struct mbox_chan * chan )
{
unsigned long ch = ( unsigned long ) chan - > con_priv ;
struct hi3660_mbox * mbox = to_hi3660_mbox ( chan - > mbox ) ;
struct hi3660_chan_info * mchan = & mbox - > mchan [ ch ] ;
void __iomem * base = MBOX_BASE ( mbox , ch ) ;
unsigned long val ;
unsigned int ret ;
2018-12-03 06:55:09 +03:00
/* Mailbox is ready to use */
if ( readl ( base + MBOX_MODE_REG ) & MBOX_STATE_READY )
2018-02-28 07:54:54 +03:00
return 0 ;
/* Wait for acknowledge from remote */
ret = readx_poll_timeout_atomic ( readl , base + MBOX_MODE_REG ,
val , ( val & MBOX_STATE_ACK ) , 1000 , 300000 ) ;
if ( ret ) {
dev_err ( mbox - > dev , " %s: timeout for receiving ack \n " , __func__ ) ;
return ret ;
}
2018-12-03 06:55:09 +03:00
/* clear ack state, mailbox will get back to ready state */
writel ( BIT ( mchan - > ack_irq ) , base + MBOX_ICLR_REG ) ;
2018-02-28 07:54:54 +03:00
return 0 ;
}
static int hi3660_mbox_unlock ( struct mbox_chan * chan )
{
struct hi3660_mbox * mbox = to_hi3660_mbox ( chan - > mbox ) ;
unsigned int val , retry = 3 ;
do {
writel ( MBOX_IPC_UNLOCK , mbox - > base + MBOX_IPC_LOCK_REG ) ;
val = readl ( mbox - > base + MBOX_IPC_LOCK_REG ) ;
if ( ! val )
break ;
udelay ( 10 ) ;
} while ( retry - - ) ;
if ( val )
dev_err ( mbox - > dev , " %s: failed to unlock mailbox \n " , __func__ ) ;
return ( ! val ) ? 0 : - ETIMEDOUT ;
}
static int hi3660_mbox_acquire_channel ( struct mbox_chan * chan )
{
unsigned long ch = ( unsigned long ) chan - > con_priv ;
struct hi3660_mbox * mbox = to_hi3660_mbox ( chan - > mbox ) ;
struct hi3660_chan_info * mchan = & mbox - > mchan [ ch ] ;
void __iomem * base = MBOX_BASE ( mbox , ch ) ;
unsigned int val , retry ;
for ( retry = 10 ; retry ; retry - - ) {
/* Check if channel is in idle state */
if ( readl ( base + MBOX_MODE_REG ) & MBOX_STATE_IDLE ) {
writel ( BIT ( mchan - > ack_irq ) , base + MBOX_SRC_REG ) ;
/* Check ack bit has been set successfully */
val = readl ( base + MBOX_SRC_REG ) ;
if ( val & BIT ( mchan - > ack_irq ) )
break ;
}
}
if ( ! retry )
dev_err ( mbox - > dev , " %s: failed to acquire channel \n " , __func__ ) ;
return retry ? 0 : - ETIMEDOUT ;
}
static int hi3660_mbox_startup ( struct mbox_chan * chan )
{
int ret ;
ret = hi3660_mbox_unlock ( chan ) ;
if ( ret )
return ret ;
ret = hi3660_mbox_acquire_channel ( chan ) ;
if ( ret )
return ret ;
return 0 ;
}
static int hi3660_mbox_send_data ( struct mbox_chan * chan , void * msg )
{
unsigned long ch = ( unsigned long ) chan - > con_priv ;
struct hi3660_mbox * mbox = to_hi3660_mbox ( chan - > mbox ) ;
struct hi3660_chan_info * mchan = & mbox - > mchan [ ch ] ;
void __iomem * base = MBOX_BASE ( mbox , ch ) ;
u32 * buf = msg ;
unsigned int i ;
2018-12-03 06:55:09 +03:00
int ret ;
2018-02-28 07:54:54 +03:00
2018-12-03 06:55:09 +03:00
ret = hi3660_mbox_check_state ( chan ) ;
if ( ret )
return ret ;
2018-02-28 07:54:54 +03:00
/* Clear mask for destination interrupt */
writel_relaxed ( ~ BIT ( mchan - > dst_irq ) , base + MBOX_IMASK_REG ) ;
/* Config destination for interrupt vector */
writel_relaxed ( BIT ( mchan - > dst_irq ) , base + MBOX_DST_REG ) ;
/* Automatic acknowledge mode */
writel_relaxed ( MBOX_AUTOMATIC_ACK , base + MBOX_MODE_REG ) ;
/* Fill message data */
for ( i = 0 ; i < MBOX_MSG_LEN ; i + + )
writel_relaxed ( buf [ i ] , base + MBOX_DATA_REG + i * 4 ) ;
/* Trigger data transferring */
writel ( BIT ( mchan - > ack_irq ) , base + MBOX_SEND_REG ) ;
return 0 ;
}
2018-11-02 18:11:55 +03:00
static const struct mbox_chan_ops hi3660_mbox_ops = {
2018-02-28 07:54:54 +03:00
. startup = hi3660_mbox_startup ,
. send_data = hi3660_mbox_send_data ,
} ;
static struct mbox_chan * hi3660_mbox_xlate ( struct mbox_controller * controller ,
const struct of_phandle_args * spec )
{
struct hi3660_mbox * mbox = to_hi3660_mbox ( controller ) ;
struct hi3660_chan_info * mchan ;
unsigned int ch = spec - > args [ 0 ] ;
if ( ch > = MBOX_CHAN_MAX ) {
dev_err ( mbox - > dev , " Invalid channel idx %d \n " , ch ) ;
return ERR_PTR ( - EINVAL ) ;
}
mchan = & mbox - > mchan [ ch ] ;
mchan - > dst_irq = spec - > args [ 1 ] ;
mchan - > ack_irq = spec - > args [ 2 ] ;
return & mbox - > chan [ ch ] ;
}
static const struct of_device_id hi3660_mbox_of_match [ ] = {
{ . compatible = " hisilicon,hi3660-mbox " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , hi3660_mbox_of_match ) ;
static int hi3660_mbox_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct hi3660_mbox * mbox ;
struct mbox_chan * chan ;
struct resource * res ;
unsigned long ch ;
int err ;
mbox = devm_kzalloc ( dev , sizeof ( * mbox ) , GFP_KERNEL ) ;
if ( ! mbox )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
mbox - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( mbox - > base ) )
return PTR_ERR ( mbox - > base ) ;
mbox - > dev = dev ;
mbox - > controller . dev = dev ;
mbox - > controller . chans = mbox - > chan ;
mbox - > controller . num_chans = MBOX_CHAN_MAX ;
mbox - > controller . ops = & hi3660_mbox_ops ;
mbox - > controller . of_xlate = hi3660_mbox_xlate ;
/* Initialize mailbox channel data */
chan = mbox - > chan ;
for ( ch = 0 ; ch < MBOX_CHAN_MAX ; ch + + )
chan [ ch ] . con_priv = ( void * ) ch ;
2018-12-20 20:19:49 +03:00
err = devm_mbox_controller_register ( dev , & mbox - > controller ) ;
2018-02-28 07:54:54 +03:00
if ( err ) {
dev_err ( dev , " Failed to register mailbox %d \n " , err ) ;
return err ;
}
platform_set_drvdata ( pdev , mbox ) ;
dev_info ( dev , " Mailbox enabled \n " ) ;
return 0 ;
}
static struct platform_driver hi3660_mbox_driver = {
. probe = hi3660_mbox_probe ,
. driver = {
. name = " hi3660-mbox " ,
. of_match_table = hi3660_mbox_of_match ,
} ,
} ;
static int __init hi3660_mbox_init ( void )
{
return platform_driver_register ( & hi3660_mbox_driver ) ;
}
core_initcall ( hi3660_mbox_init ) ;
static void __exit hi3660_mbox_exit ( void )
{
platform_driver_unregister ( & hi3660_mbox_driver ) ;
}
module_exit ( hi3660_mbox_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Hisilicon Hi3660 Mailbox Controller " ) ;
MODULE_AUTHOR ( " Leo Yan <leo.yan@linaro.org> " ) ;