2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-04-23 13:49:31 +04:00
/*
*
* 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 ;
2018-12-03 17:52:19 +03:00
if ( WARN_ON ( index > = func - > num_templates ) )
2014-04-23 13:49:31 +04:00
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 ;
}
treewide: Use struct_size() for kmalloc()-family
One of the more common cases of allocation size calculations is finding
the size of a structure that has a zero-sized array at the end, along
with memory for some number of elements for that array. For example:
struct foo {
int stuff;
void *entry[];
};
instance = kmalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL);
Instead of leaving these open-coded and prone to type mistakes, we can
now use the new struct_size() helper:
instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL);
This patch makes the changes for kmalloc()-family (and kvmalloc()-family)
uses. It was done via automatic conversion with manual review for the
"CHECKME" non-standard cases noted below, using the following Coccinelle
script:
// pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
// sizeof *pkey_cache->table, GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(*VAR->ELEMENT), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// mr = kzalloc(sizeof(*mr) + m * sizeof(mr->map[0]), GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(VAR->ELEMENT[0]), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// Same pattern, but can't trivially locate the trailing element name,
// or variable name.
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
expression SOMETHING, COUNT, ELEMENT;
@@
- alloc(sizeof(SOMETHING) + COUNT * sizeof(ELEMENT), GFP)
+ alloc(CHECKME_struct_size(&SOMETHING, ELEMENT, COUNT), GFP)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-05-08 23:45:50 +03:00
func = kzalloc ( struct_size ( func , template , num ) , GFP_KERNEL ) ;
2014-04-23 13:49:31 +04:00
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 ) ;
2018-07-11 18:41:10 +03:00
syscfg - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( syscfg - > base ) )
return PTR_ERR ( syscfg - > base ) ;
2014-04-23 13:49:31 +04:00
/* 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 ) ;