2019-03-12 01:18:03 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2018 - 2019 , Intel Corporation .
* Copyright ( C ) 2012 Freescale Semiconductor , Inc .
* Copyright ( C ) 2012 Linaro Ltd .
*
* Based on syscon driver .
*/
# include <linux/arm-smccc.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/mfd/altera-sysmgr.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/regmap.h>
# include <linux/slab.h>
/**
* struct altr_sysmgr - Altera SOCFPGA System Manager
* @ regmap : the regmap used for System Manager accesses .
* @ base : the base address for the System Manager
*/
struct altr_sysmgr {
struct regmap * regmap ;
resource_size_t * base ;
} ;
static struct platform_driver altr_sysmgr_driver ;
/**
* s10_protected_reg_write
* Write to a protected SMC register .
* @ base : Base address of System Manager
* @ reg : Address offset of register
* @ val : Value to write
* Return : INTEL_SIP_SMC_STATUS_OK ( 0 ) on success
* INTEL_SIP_SMC_REG_ERROR on error
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
*/
static int s10_protected_reg_write ( void * base ,
unsigned int reg , unsigned int val )
{
struct arm_smccc_res result ;
unsigned long sysmgr_base = ( unsigned long ) base ;
arm_smccc_smc ( INTEL_SIP_SMC_REG_WRITE , sysmgr_base + reg ,
val , 0 , 0 , 0 , 0 , 0 , & result ) ;
return ( int ) result . a0 ;
}
/**
* s10_protected_reg_read
* Read the status of a protected SMC register
* @ base : Base address of System Manager .
* @ reg : Address of register
* @ val : Value read .
* Return : INTEL_SIP_SMC_STATUS_OK ( 0 ) on success
* INTEL_SIP_SMC_REG_ERROR on error
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
*/
static int s10_protected_reg_read ( void * base ,
unsigned int reg , unsigned int * val )
{
struct arm_smccc_res result ;
unsigned long sysmgr_base = ( unsigned long ) base ;
arm_smccc_smc ( INTEL_SIP_SMC_REG_READ , sysmgr_base + reg ,
0 , 0 , 0 , 0 , 0 , 0 , & result ) ;
* val = ( unsigned int ) result . a1 ;
return ( int ) result . a0 ;
}
static struct regmap_config altr_sysmgr_regmap_cfg = {
. name = " altr_sysmgr " ,
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. fast_io = true ,
. use_single_read = true ,
. use_single_write = true ,
} ;
/**
* sysmgr_match_phandle
* Matching function used by driver_find_device ( ) .
* Return : True if match is found , otherwise false .
*/
2019-06-14 20:54:00 +03:00
static int sysmgr_match_phandle ( struct device * dev , const void * data )
2019-03-12 01:18:03 +03:00
{
2019-06-14 20:54:00 +03:00
return dev - > of_node = = ( const struct device_node * ) data ;
2019-03-12 01:18:03 +03:00
}
/**
* altr_sysmgr_regmap_lookup_by_phandle
* Find the sysmgr previous configured in probe ( ) and return regmap property .
* Return : regmap if found or error if not found .
*/
struct regmap * altr_sysmgr_regmap_lookup_by_phandle ( struct device_node * np ,
const char * property )
{
struct device * dev ;
struct altr_sysmgr * sysmgr ;
struct device_node * sysmgr_np ;
if ( property )
sysmgr_np = of_parse_phandle ( np , property , 0 ) ;
else
sysmgr_np = np ;
if ( ! sysmgr_np )
return ERR_PTR ( - ENODEV ) ;
dev = driver_find_device ( & altr_sysmgr_driver . driver , NULL ,
( void * ) sysmgr_np , sysmgr_match_phandle ) ;
of_node_put ( sysmgr_np ) ;
if ( ! dev )
return ERR_PTR ( - EPROBE_DEFER ) ;
sysmgr = dev_get_drvdata ( dev ) ;
return sysmgr - > regmap ;
}
EXPORT_SYMBOL_GPL ( altr_sysmgr_regmap_lookup_by_phandle ) ;
static int sysmgr_probe ( struct platform_device * pdev )
{
struct altr_sysmgr * sysmgr ;
struct regmap * regmap ;
struct resource * res ;
struct regmap_config sysmgr_config = altr_sysmgr_regmap_cfg ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
sysmgr = devm_kzalloc ( dev , sizeof ( * sysmgr ) , GFP_KERNEL ) ;
if ( ! sysmgr )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENOENT ;
sysmgr_config . max_register = resource_size ( res ) -
sysmgr_config . reg_stride ;
if ( of_device_is_compatible ( np , " altr,sys-mgr-s10 " ) ) {
/* Need physical address for SMCC call */
sysmgr - > base = ( resource_size_t * ) res - > start ;
sysmgr_config . reg_read = s10_protected_reg_read ;
sysmgr_config . reg_write = s10_protected_reg_write ;
regmap = devm_regmap_init ( dev , NULL , sysmgr - > base ,
& sysmgr_config ) ;
} else {
sysmgr - > base = devm_ioremap ( dev , res - > start ,
resource_size ( res ) ) ;
if ( ! sysmgr - > base )
return - ENOMEM ;
sysmgr_config . max_register = res - > end - res - > start - 3 ;
regmap = devm_regmap_init_mmio ( dev , sysmgr - > base ,
& sysmgr_config ) ;
}
if ( IS_ERR ( regmap ) ) {
pr_err ( " regmap init failed \n " ) ;
return PTR_ERR ( regmap ) ;
}
sysmgr - > regmap = regmap ;
platform_set_drvdata ( pdev , sysmgr ) ;
return 0 ;
}
static const struct of_device_id altr_sysmgr_of_match [ ] = {
{ . compatible = " altr,sys-mgr " } ,
{ . compatible = " altr,sys-mgr-s10 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , altr_sysmgr_of_match ) ;
static struct platform_driver altr_sysmgr_driver = {
. probe = sysmgr_probe ,
. driver = {
. name = " altr,system_manager " ,
. of_match_table = altr_sysmgr_of_match ,
} ,
} ;
static int __init altr_sysmgr_init ( void )
{
return platform_driver_register ( & altr_sysmgr_driver ) ;
}
core_initcall ( altr_sysmgr_init ) ;
static void __exit altr_sysmgr_exit ( void )
{
platform_driver_unregister ( & altr_sysmgr_driver ) ;
}
module_exit ( altr_sysmgr_exit ) ;
MODULE_AUTHOR ( " Thor Thayer <> " ) ;
MODULE_DESCRIPTION ( " SOCFPGA System Manager driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;