2014-04-23 13:49:31 +04:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* Copyright ( C ) 2014 ARM Limited
*/
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/platform_device.h>
2017-02-02 21:15:33 +03:00
# include <linux/sched/signal.h>
2014-04-23 13:49:31 +04:00
# include <linux/slab.h>
# include <linux/syscore_ops.h>
# include <linux/vexpress.h>
# define SYS_CFGDATA 0x0
# define SYS_CFGCTRL 0x4
# define SYS_CFGCTRL_START (1 << 31)
# define SYS_CFGCTRL_WRITE (1 << 30)
# define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
# define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
# define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
# define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
# define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
# define SYS_CFGSTAT 0x8
# define SYS_CFGSTAT_ERR (1 << 1)
# define SYS_CFGSTAT_COMPLETE (1 << 0)
struct vexpress_syscfg {
struct device * dev ;
void __iomem * base ;
struct list_head funcs ;
} ;
struct vexpress_syscfg_func {
struct list_head list ;
struct vexpress_syscfg * syscfg ;
struct regmap * regmap ;
int num_templates ;
u32 template [ 0 ] ; /* Keep it last! */
} ;
static int vexpress_syscfg_exec ( struct vexpress_syscfg_func * func ,
int index , bool write , u32 * data )
{
struct vexpress_syscfg * syscfg = func - > syscfg ;
u32 command , status ;
int tries ;
long timeout ;
if ( WARN_ON ( index > func - > num_templates ) )
return - EINVAL ;
command = readl ( syscfg - > base + SYS_CFGCTRL ) ;
if ( WARN_ON ( command & SYS_CFGCTRL_START ) )
return - EBUSY ;
command = func - > template [ index ] ;
command | = SYS_CFGCTRL_START ;
command | = write ? SYS_CFGCTRL_WRITE : 0 ;
/* Use a canary for reads */
if ( ! write )
* data = 0xdeadbeef ;
dev_dbg ( syscfg - > dev , " func %p, command %x, data %x \n " ,
func , command , * data ) ;
writel ( * data , syscfg - > base + SYS_CFGDATA ) ;
writel ( 0 , syscfg - > base + SYS_CFGSTAT ) ;
writel ( command , syscfg - > base + SYS_CFGCTRL ) ;
mb ( ) ;
/* The operation can take ages... Go to sleep, 100us initially */
tries = 100 ;
timeout = 100 ;
do {
if ( ! irqs_disabled ( ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule_timeout ( usecs_to_jiffies ( timeout ) ) ;
if ( signal_pending ( current ) )
return - EINTR ;
} else {
udelay ( timeout ) ;
}
status = readl ( syscfg - > base + SYS_CFGSTAT ) ;
if ( status & SYS_CFGSTAT_ERR )
return - EFAULT ;
if ( timeout > 20 )
timeout - = 20 ;
} while ( - - tries & & ! ( status & SYS_CFGSTAT_COMPLETE ) ) ;
if ( WARN_ON_ONCE ( ! tries ) )
return - ETIMEDOUT ;
if ( ! write ) {
* data = readl ( syscfg - > base + SYS_CFGDATA ) ;
dev_dbg ( syscfg - > dev , " func %p, read data %x \n " , func , * data ) ;
}
return 0 ;
}
static int vexpress_syscfg_read ( void * context , unsigned int index ,
unsigned int * val )
{
struct vexpress_syscfg_func * func = context ;
return vexpress_syscfg_exec ( func , index , false , val ) ;
}
static int vexpress_syscfg_write ( void * context , unsigned int index ,
unsigned int val )
{
struct vexpress_syscfg_func * func = context ;
return vexpress_syscfg_exec ( func , index , true , & val ) ;
}
2014-07-20 09:25:39 +04:00
static struct regmap_config vexpress_syscfg_regmap_config = {
2014-04-23 13:49:31 +04:00
. lock = vexpress_config_lock ,
. unlock = vexpress_config_unlock ,
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_read = vexpress_syscfg_read ,
. reg_write = vexpress_syscfg_write ,
. reg_format_endian = REGMAP_ENDIAN_LITTLE ,
. val_format_endian = REGMAP_ENDIAN_LITTLE ,
} ;
static struct regmap * vexpress_syscfg_regmap_init ( struct device * dev ,
void * context )
{
2014-11-25 21:17:34 +03:00
int err ;
2014-04-23 13:49:31 +04:00
struct vexpress_syscfg * syscfg = context ;
struct vexpress_syscfg_func * func ;
struct property * prop ;
const __be32 * val = NULL ;
__be32 energy_quirk [ 4 ] ;
int num ;
u32 site , position , dcc ;
int i ;
2014-11-25 21:17:34 +03:00
err = vexpress_config_get_topo ( dev - > of_node , & site ,
2014-04-23 13:49:31 +04:00
& position , & dcc ) ;
2014-11-25 21:17:34 +03:00
if ( err )
return ERR_PTR ( err ) ;
2014-04-23 13:49:31 +04:00
2014-11-25 21:17:34 +03:00
prop = of_find_property ( dev - > of_node ,
" arm,vexpress-sysreg,func " , NULL ) ;
if ( ! prop )
return ERR_PTR ( - EINVAL ) ;
num = prop - > length / sizeof ( u32 ) / 2 ;
val = prop - > value ;
2014-04-23 13:49:31 +04:00
/*
* " arm,vexpress-energy " function used to be described
* by its first device only , now it requires both
*/
if ( num = = 1 & & of_device_is_compatible ( dev - > of_node ,
" arm,vexpress-energy " ) ) {
num = 2 ;
energy_quirk [ 0 ] = * val ;
energy_quirk [ 2 ] = * val + + ;
energy_quirk [ 1 ] = * val ;
energy_quirk [ 3 ] = cpu_to_be32 ( be32_to_cpup ( val ) + 1 ) ;
val = energy_quirk ;
}
func = kzalloc ( sizeof ( * func ) + sizeof ( * func - > template ) * num ,
GFP_KERNEL ) ;
if ( ! func )
2014-06-11 14:17:41 +04:00
return ERR_PTR ( - ENOMEM ) ;
2014-04-23 13:49:31 +04:00
func - > syscfg = syscfg ;
func - > num_templates = num ;
for ( i = 0 ; i < num ; i + + ) {
u32 function , device ;
2014-11-25 21:17:34 +03:00
function = be32_to_cpup ( val + + ) ;
device = be32_to_cpup ( val + + ) ;
2014-04-23 13:49:31 +04:00
dev_dbg ( dev , " func %p: %u/%u/%u/%u/%u \n " ,
func , site , position , dcc ,
function , device ) ;
func - > template [ i ] = SYS_CFGCTRL_DCC ( dcc ) ;
func - > template [ i ] | = SYS_CFGCTRL_SITE ( site ) ;
func - > template [ i ] | = SYS_CFGCTRL_POSITION ( position ) ;
func - > template [ i ] | = SYS_CFGCTRL_FUNC ( function ) ;
func - > template [ i ] | = SYS_CFGCTRL_DEVICE ( device ) ;
}
vexpress_syscfg_regmap_config . max_register = num - 1 ;
func - > regmap = regmap_init ( dev , NULL , func ,
& vexpress_syscfg_regmap_config ) ;
2014-06-11 14:17:41 +04:00
if ( IS_ERR ( func - > regmap ) ) {
void * err = func - > regmap ;
2014-04-23 13:49:31 +04:00
kfree ( func ) ;
2014-06-11 14:17:41 +04:00
return err ;
}
list_add ( & func - > list , & syscfg - > funcs ) ;
2014-04-23 13:49:31 +04:00
return func - > regmap ;
}
static void vexpress_syscfg_regmap_exit ( struct regmap * regmap , void * context )
{
struct vexpress_syscfg * syscfg = context ;
struct vexpress_syscfg_func * func , * tmp ;
regmap_exit ( regmap ) ;
list_for_each_entry_safe ( func , tmp , & syscfg - > funcs , list ) {
if ( func - > regmap = = regmap ) {
list_del ( & syscfg - > funcs ) ;
kfree ( func ) ;
break ;
}
}
}
static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
. regmap_init = vexpress_syscfg_regmap_init ,
. regmap_exit = vexpress_syscfg_regmap_exit ,
} ;
2014-07-20 09:25:39 +04:00
static int vexpress_syscfg_probe ( struct platform_device * pdev )
2014-04-23 13:49:31 +04:00
{
struct vexpress_syscfg * syscfg ;
struct resource * res ;
struct device * bridge ;
syscfg = devm_kzalloc ( & pdev - > dev , sizeof ( * syscfg ) , GFP_KERNEL ) ;
if ( ! syscfg )
return - ENOMEM ;
syscfg - > dev = & pdev - > dev ;
INIT_LIST_HEAD ( & syscfg - > funcs ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! devm_request_mem_region ( & pdev - > dev , res - > start ,
resource_size ( res ) , pdev - > name ) )
return - EBUSY ;
syscfg - > base = devm_ioremap ( & pdev - > dev , res - > start , resource_size ( res ) ) ;
if ( ! syscfg - > base )
return - EFAULT ;
/* Must use dev.parent (MFD), as that's where DT phandle points at... */
bridge = vexpress_config_bridge_register ( pdev - > dev . parent ,
& vexpress_syscfg_bridge_ops , syscfg ) ;
2017-11-28 18:20:43 +03:00
return PTR_ERR_OR_ZERO ( bridge ) ;
2014-04-23 13:49:31 +04:00
}
static const struct platform_device_id vexpress_syscfg_id_table [ ] = {
{ " vexpress-syscfg " , } ,
{ } ,
} ;
static struct platform_driver vexpress_syscfg_driver = {
. driver . name = " vexpress-syscfg " ,
. id_table = vexpress_syscfg_id_table ,
. probe = vexpress_syscfg_probe ,
} ;
static int __init vexpress_syscfg_init ( void )
{
return platform_driver_register ( & vexpress_syscfg_driver ) ;
}
core_initcall ( vexpress_syscfg_init ) ;