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>
2023-07-14 20:47:27 +03:00
# include <linux/platform_device.h>
2019-03-12 01:18:03 +03:00
# include <linux/regmap.h>
# include <linux/slab.h>
/**
* struct altr_sysmgr - Altera SOCFPGA System Manager
* @ regmap : the regmap used for System Manager accesses .
*/
struct altr_sysmgr {
struct regmap * regmap ;
} ;
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 ,
} ;
/**
* 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 .
2020-06-24 14:56:11 +03:00
*
* @ np : Pointer to device ' s Device Tree node
* @ property : Device Tree property name which references the sysmgr
2019-03-12 01:18:03 +03:00
*/
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 ) ;
2019-07-24 01:18:33 +03:00
dev = driver_find_device_by_of_node ( & altr_sysmgr_driver . driver ,
( void * ) sysmgr_np ) ;
2024-02-20 14:50:12 +03:00
if ( property )
of_node_put ( sysmgr_np ) ;
2019-03-12 01:18:03 +03:00
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 ;
2020-06-23 11:55:05 +03:00
void __iomem * base ;
2019-03-12 01:18:03 +03:00
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 " ) ) {
sysmgr_config . reg_read = s10_protected_reg_read ;
sysmgr_config . reg_write = s10_protected_reg_write ;
2020-06-23 11:55:05 +03:00
/* Need physical address for SMCC call */
mfd: altera-sysmgr: Fix physical address storing more
A recent fix improved the way the resource gets passed to
the low-level accessors, but left one warning that appears
in configurations with a resource_size_t that is wider than
a pointer:
In file included from drivers/mfd/altera-sysmgr.c:19:
drivers/mfd/altera-sysmgr.c: In function 'sysmgr_probe':
drivers/mfd/altera-sysmgr.c:148:40: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
148 | regmap = devm_regmap_init(dev, NULL, (void *)res->start,
| ^
include/linux/regmap.h:646:6: note: in definition of macro '__regmap_lockdep_wrapper'
646 | fn(__VA_ARGS__, &_key, \
| ^~~~~~~~~~~
drivers/mfd/altera-sysmgr.c:148:12: note: in expansion of macro 'devm_regmap_init'
148 | regmap = devm_regmap_init(dev, NULL, (void *)res->start,
| ^~~~~~~~~~~~~~~~
I had tried a different approach that would store the address
in the private data as a phys_addr_t, but the easiest solution
now seems to be to add a double cast to shut up the warning.
As the address is passed to an inline assembly, it is guaranteed
to not be wider than a register anyway.
Fixes: d9ca7801b6e5 ("mfd: altera-sysmgr: Fix physical address storing hacks")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
2020-12-04 01:52:48 +03:00
regmap = devm_regmap_init ( dev , NULL ,
( void * ) ( uintptr_t ) res - > start ,
2019-03-12 01:18:03 +03:00
& sysmgr_config ) ;
} else {
2020-06-23 11:55:05 +03:00
base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! base )
2019-03-12 01:18:03 +03:00
return - ENOMEM ;
2021-10-06 17:19:26 +03:00
sysmgr_config . max_register = resource_size ( res ) - 4 ;
2020-06-23 11:55:05 +03:00
regmap = devm_regmap_init_mmio ( dev , base , & sysmgr_config ) ;
2019-03-12 01:18:03 +03:00
}
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 " ) ;