2018-05-11 12:03:20 -05:00
// SPDX-License-Identifier: GPL-2.0
2011-09-08 22:47:40 +03:00
/*
* u8500 HWSEM driver
*
* Copyright ( C ) 2010 - 2011 ST - Ericsson
*
* Implements u8500 semaphore handling for protocol 1 , no interrupts .
*
* Author : Mathieu Poirier < mathieu . poirier @ linaro . org >
* Heavily borrowed from the work of :
* Simon Que < sque @ ti . com >
* Hari Kanigeri < h - kanigeri2 @ ti . com >
* Ohad Ben - Cohen < ohad @ wizery . com >
*/
2011-11-06 21:16:51 +08:00
# include <linux/module.h>
2011-09-08 22:47:40 +03:00
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/pm_runtime.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/hwspinlock.h>
# include <linux/platform_device.h>
# include "hwspinlock_internal.h"
/*
* Implementation of STE ' s HSem protocol 1 without interrutps .
* The only masterID we allow is ' 0x01 ' to force people to use
* HSems for synchronisation between processors rather than processes
* on the ARM core .
*/
# define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */
# define RESET_SEMAPHORE (0) /* free */
/*
* CPU ID for master running u8500 kernel .
* Hswpinlocks should only be used to synchonise operations
* between the Cortex A9 core and the other CPUs . Hence
* forcing the masterID to a preset value .
*/
# define HSEM_MASTER_ID 0x01
# define HSEM_REGISTER_OFFSET 0x08
# define HSEM_CTRL_REG 0x00
# define HSEM_ICRALL 0x90
# define HSEM_PROTOCOL_1 0x01
static int u8500_hsem_trylock ( struct hwspinlock * lock )
{
void __iomem * lock_addr = lock - > priv ;
writel ( HSEM_MASTER_ID , lock_addr ) ;
/* get only first 4 bit and compare to masterID.
* if equal , we have the semaphore , otherwise
* someone else has it .
*/
return ( HSEM_MASTER_ID = = ( 0x0F & readl ( lock_addr ) ) ) ;
}
static void u8500_hsem_unlock ( struct hwspinlock * lock )
{
void __iomem * lock_addr = lock - > priv ;
/* release the lock by writing 0 to it */
writel ( RESET_SEMAPHORE , lock_addr ) ;
}
/*
* u8500 : what value is recommended here ?
*/
static void u8500_hsem_relax ( struct hwspinlock * lock )
{
ndelay ( 50 ) ;
}
static const struct hwspinlock_ops u8500_hwspinlock_ops = {
. trylock = u8500_hsem_trylock ,
. unlock = u8500_hsem_unlock ,
. relax = u8500_hsem_relax ,
} ;
2012-11-19 13:23:22 -05:00
static int u8500_hsem_probe ( struct platform_device * pdev )
2011-09-08 22:47:40 +03:00
{
struct hwspinlock_pdata * pdata = pdev - > dev . platform_data ;
struct hwspinlock_device * bank ;
struct hwspinlock * hwlock ;
struct resource * res ;
void __iomem * io_base ;
int i , ret , num_locks = U8500_MAX_SEMAPHORE ;
ulong val ;
if ( ! pdata )
return - ENODEV ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
io_base = ioremap ( res - > start , resource_size ( res ) ) ;
2011-11-06 21:14:16 +08:00
if ( ! io_base )
return - ENOMEM ;
2011-09-08 22:47:40 +03:00
/* make sure protocol 1 is selected */
val = readl ( io_base + HSEM_CTRL_REG ) ;
writel ( ( val & ~ HSEM_PROTOCOL_1 ) , io_base + HSEM_CTRL_REG ) ;
/* clear all interrupts */
writel ( 0xFFFF , io_base + HSEM_ICRALL ) ;
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 13:45:50 -07:00
bank = kzalloc ( struct_size ( bank , lock , num_locks ) , GFP_KERNEL ) ;
2011-09-08 22:47:40 +03:00
if ( ! bank ) {
ret = - ENOMEM ;
goto iounmap_base ;
}
platform_set_drvdata ( pdev , bank ) ;
for ( i = 0 , hwlock = & bank - > lock [ 0 ] ; i < num_locks ; i + + , hwlock + + )
hwlock - > priv = io_base + HSEM_REGISTER_OFFSET + sizeof ( u32 ) * i ;
/* no pm needed for HSem but required to comply with hwspilock core */
pm_runtime_enable ( & pdev - > dev ) ;
ret = hwspin_lock_register ( bank , & pdev - > dev , & u8500_hwspinlock_ops ,
pdata - > base_id , num_locks ) ;
if ( ret )
goto reg_fail ;
return 0 ;
reg_fail :
pm_runtime_disable ( & pdev - > dev ) ;
kfree ( bank ) ;
iounmap_base :
iounmap ( io_base ) ;
return ret ;
}
2012-11-19 13:25:52 -05:00
static int u8500_hsem_remove ( struct platform_device * pdev )
2011-09-08 22:47:40 +03:00
{
struct hwspinlock_device * bank = platform_get_drvdata ( pdev ) ;
void __iomem * io_base = bank - > lock [ 0 ] . priv - HSEM_REGISTER_OFFSET ;
int ret ;
/* clear all interrupts */
writel ( 0xFFFF , io_base + HSEM_ICRALL ) ;
ret = hwspin_lock_unregister ( bank ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s failed: %d \n " , __func__ , ret ) ;
return ret ;
}
pm_runtime_disable ( & pdev - > dev ) ;
iounmap ( io_base ) ;
kfree ( bank ) ;
return 0 ;
}
static struct platform_driver u8500_hsem_driver = {
. probe = u8500_hsem_probe ,
2012-11-19 13:20:13 -05:00
. remove = u8500_hsem_remove ,
2011-09-08 22:47:40 +03:00
. driver = {
. name = " u8500_hsem " ,
} ,
} ;
static int __init u8500_hsem_init ( void )
{
return platform_driver_register ( & u8500_hsem_driver ) ;
}
/* board init code might need to reserve hwspinlocks for predefined purposes */
postcore_initcall ( u8500_hsem_init ) ;
static void __exit u8500_hsem_exit ( void )
{
platform_driver_unregister ( & u8500_hsem_driver ) ;
}
module_exit ( u8500_hsem_exit ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Hardware Spinlock driver for u8500 " ) ;
MODULE_AUTHOR ( " Mathieu Poirier <mathieu.poirier@linaro.org> " ) ;