2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-05-28 02:14:04 +03:00
/*
* Copyright ( c ) 2017 , Linaro Ltd
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/of.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
2017-12-05 18:46:56 +03:00
# include <linux/regmap.h>
2017-05-28 02:14:04 +03:00
# include <linux/mailbox_controller.h>
# define QCOM_APCS_IPC_BITS 32
struct qcom_apcs_ipc {
struct mbox_controller mbox ;
struct mbox_chan mbox_chans [ QCOM_APCS_IPC_BITS ] ;
2017-12-05 18:46:56 +03:00
struct regmap * regmap ;
2017-05-28 02:14:04 +03:00
unsigned long offset ;
2017-12-05 18:46:57 +03:00
struct platform_device * clk ;
2017-05-28 02:14:04 +03:00
} ;
2017-12-05 18:46:56 +03:00
static const struct regmap_config apcs_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
2019-09-09 12:08:50 +03:00
. max_register = 0xFFC ,
2017-12-05 18:46:56 +03:00
. fast_io = true ,
} ;
2017-05-28 02:14:04 +03:00
static int qcom_apcs_ipc_send_data ( struct mbox_chan * chan , void * data )
{
struct qcom_apcs_ipc * apcs = container_of ( chan - > mbox ,
struct qcom_apcs_ipc , mbox ) ;
unsigned long idx = ( unsigned long ) chan - > con_priv ;
2017-12-05 18:46:56 +03:00
return regmap_write ( apcs - > regmap , apcs - > offset , BIT ( idx ) ) ;
2017-05-28 02:14:04 +03:00
}
static const struct mbox_chan_ops qcom_apcs_ipc_ops = {
. send_data = qcom_apcs_ipc_send_data ,
} ;
static int qcom_apcs_ipc_probe ( struct platform_device * pdev )
{
struct qcom_apcs_ipc * apcs ;
2017-12-05 18:46:56 +03:00
struct regmap * regmap ;
2017-05-28 02:14:04 +03:00
struct resource * res ;
unsigned long offset ;
void __iomem * base ;
unsigned long i ;
int ret ;
2019-08-29 11:27:58 +03:00
const struct of_device_id apcs_clk_match_table [ ] = {
{ . compatible = " qcom,msm8916-apcs-kpss-global " , } ,
{ . compatible = " qcom,qcs404-apcs-apps-global " , } ,
{ }
} ;
2017-05-28 02:14:04 +03:00
apcs = devm_kzalloc ( & pdev - > dev , sizeof ( * apcs ) , GFP_KERNEL ) ;
if ( ! apcs )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2017-12-05 18:46:56 +03:00
regmap = devm_regmap_init_mmio ( & pdev - > dev , base , & apcs_regmap_config ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
2017-05-28 02:14:04 +03:00
offset = ( unsigned long ) of_device_get_match_data ( & pdev - > dev ) ;
2017-12-05 18:46:56 +03:00
apcs - > regmap = regmap ;
apcs - > offset = offset ;
2017-05-28 02:14:04 +03:00
/* Initialize channel identifiers */
for ( i = 0 ; i < ARRAY_SIZE ( apcs - > mbox_chans ) ; i + + )
apcs - > mbox_chans [ i ] . con_priv = ( void * ) i ;
apcs - > mbox . dev = & pdev - > dev ;
apcs - > mbox . ops = & qcom_apcs_ipc_ops ;
apcs - > mbox . chans = apcs - > mbox_chans ;
apcs - > mbox . num_chans = ARRAY_SIZE ( apcs - > mbox_chans ) ;
2018-12-20 20:19:59 +03:00
ret = devm_mbox_controller_register ( & pdev - > dev , & apcs - > mbox ) ;
2017-05-28 02:14:04 +03:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register APCS IPC controller \n " ) ;
return ret ;
}
2019-08-29 11:27:58 +03:00
if ( of_match_device ( apcs_clk_match_table , & pdev - > dev ) ) {
2017-12-05 18:46:57 +03:00
apcs - > clk = platform_device_register_data ( & pdev - > dev ,
" qcom-apcs-msm8916-clk " ,
2019-08-29 11:27:59 +03:00
PLATFORM_DEVID_NONE ,
NULL , 0 ) ;
2017-12-05 18:46:57 +03:00
if ( IS_ERR ( apcs - > clk ) )
dev_err ( & pdev - > dev , " failed to register APCS clk \n " ) ;
}
2017-05-28 02:14:04 +03:00
platform_set_drvdata ( pdev , apcs ) ;
return 0 ;
}
static int qcom_apcs_ipc_remove ( struct platform_device * pdev )
{
struct qcom_apcs_ipc * apcs = platform_get_drvdata ( pdev ) ;
2017-12-05 18:46:57 +03:00
struct platform_device * clk = apcs - > clk ;
2017-05-28 02:14:04 +03:00
2017-12-05 18:46:57 +03:00
platform_device_unregister ( clk ) ;
2017-05-28 02:14:04 +03:00
return 0 ;
}
/* .data is the offset of the ipc register within the global block */
static const struct of_device_id qcom_apcs_ipc_of_match [ ] = {
{ . compatible = " qcom,msm8916-apcs-kpss-global " , . data = ( void * ) 8 } ,
{ . compatible = " qcom,msm8996-apcs-hmss-global " , . data = ( void * ) 16 } ,
2018-03-27 18:54:26 +03:00
{ . compatible = " qcom,msm8998-apcs-hmss-global " , . data = ( void * ) 8 } ,
2018-09-20 04:44:08 +03:00
{ . compatible = " qcom,qcs404-apcs-apps-global " , . data = ( void * ) 8 } ,
2019-08-07 10:09:55 +03:00
{ . compatible = " qcom,sc7180-apss-shared " , . data = ( void * ) 12 } ,
2018-04-25 17:38:02 +03:00
{ . compatible = " qcom,sdm845-apss-shared " , . data = ( void * ) 12 } ,
2019-08-07 10:09:55 +03:00
{ . compatible = " qcom,sm8150-apss-shared " , . data = ( void * ) 12 } ,
2019-09-13 14:56:08 +03:00
{ . compatible = " qcom,ipq8074-apcs-apps-global " , . data = ( void * ) 8 } ,
2017-05-28 02:14:04 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , qcom_apcs_ipc_of_match ) ;
static struct platform_driver qcom_apcs_ipc_driver = {
. probe = qcom_apcs_ipc_probe ,
. remove = qcom_apcs_ipc_remove ,
. driver = {
. name = " qcom_apcs_ipc " ,
. of_match_table = qcom_apcs_ipc_of_match ,
} ,
} ;
static int __init qcom_apcs_ipc_init ( void )
{
return platform_driver_register ( & qcom_apcs_ipc_driver ) ;
}
postcore_initcall ( qcom_apcs_ipc_init ) ;
static void __exit qcom_apcs_ipc_exit ( void )
{
platform_driver_unregister ( & qcom_apcs_ipc_driver ) ;
}
module_exit ( qcom_apcs_ipc_exit ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Qualcomm APCS IPC driver " ) ;