2012-04-05 01:48:31 +04:00
/*
* Register map access API - MMIO support
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2013-02-14 20:39:08 +04:00
# include <linux/clk.h>
2012-04-05 01:48:31 +04:00
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/slab.h>
2016-03-29 22:30:44 +03:00
# include "internal.h"
2012-04-05 01:48:31 +04:00
struct regmap_mmio_context {
void __iomem * regs ;
unsigned val_bytes ;
2018-02-21 12:20:25 +03:00
bool attached_clk ;
2013-02-14 20:39:08 +04:00
struct clk * clk ;
2012-04-05 01:48:31 +04:00
2016-01-27 07:50:07 +03:00
void ( * reg_write ) ( struct regmap_mmio_context * ctx ,
unsigned int reg , unsigned int val ) ;
unsigned int ( * reg_read ) ( struct regmap_mmio_context * ctx ,
unsigned int reg ) ;
} ;
2014-03-27 08:42:42 +04:00
2014-03-28 09:12:56 +04:00
static int regmap_mmio_regbits_check ( size_t reg_bits )
{
switch ( reg_bits ) {
case 8 :
case 16 :
case 32 :
# ifdef CONFIG_64BIT
case 64 :
# endif
return 0 ;
default :
return - EINVAL ;
}
}
2015-12-03 08:27:21 +03:00
static int regmap_mmio_get_min_stride ( size_t val_bits )
{
int min_stride ;
switch ( val_bits ) {
case 8 :
/* The core treats 0 as 1 */
min_stride = 0 ;
return 0 ;
case 16 :
min_stride = 2 ;
break ;
case 32 :
min_stride = 4 ;
break ;
# ifdef CONFIG_64BIT
case 64 :
min_stride = 8 ;
break ;
# endif
default :
return - EINVAL ;
}
return min_stride ;
}
2016-01-27 07:50:07 +03:00
static void regmap_mmio_write8 ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
{
writeb ( val , ctx - > regs + reg ) ;
}
static void regmap_mmio_write16le ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
2014-03-27 08:42:42 +04:00
{
2016-01-27 07:50:07 +03:00
writew ( val , ctx - > regs + reg ) ;
2014-03-27 08:42:42 +04:00
}
2016-01-27 07:50:07 +03:00
static void regmap_mmio_write16be ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
2014-04-02 06:20:17 +04:00
{
2016-01-27 07:50:07 +03:00
iowrite16be ( val , ctx - > regs + reg ) ;
}
static void regmap_mmio_write32le ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
{
writel ( val , ctx - > regs + reg ) ;
}
static void regmap_mmio_write32be ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
{
iowrite32be ( val , ctx - > regs + reg ) ;
}
2014-04-02 06:20:17 +04:00
# ifdef CONFIG_64BIT
2016-01-27 07:50:07 +03:00
static void regmap_mmio_write64le ( struct regmap_mmio_context * ctx ,
unsigned int reg ,
unsigned int val )
{
writeq ( val , ctx - > regs + reg ) ;
2014-03-27 08:42:42 +04:00
}
2016-01-27 07:50:07 +03:00
# endif
2014-03-27 08:42:42 +04:00
2016-01-27 07:50:07 +03:00
static int regmap_mmio_write ( void * context , unsigned int reg , unsigned int val )
2012-04-05 01:48:31 +04:00
{
struct regmap_mmio_context * ctx = context ;
2013-02-14 20:39:08 +04:00
int ret ;
2012-04-05 01:48:31 +04:00
2013-11-26 02:12:47 +04:00
if ( ! IS_ERR ( ctx - > clk ) ) {
2013-02-14 20:39:08 +04:00
ret = clk_enable ( ctx - > clk ) ;
if ( ret < 0 )
return ret ;
}
2016-01-27 07:50:07 +03:00
ctx - > reg_write ( ctx , reg , val ) ;
2012-04-05 01:48:31 +04:00
2013-11-26 02:12:47 +04:00
if ( ! IS_ERR ( ctx - > clk ) )
2013-02-14 20:39:08 +04:00
clk_disable ( ctx - > clk ) ;
2012-04-05 01:48:31 +04:00
return 0 ;
}
2016-01-27 07:50:07 +03:00
static unsigned int regmap_mmio_read8 ( struct regmap_mmio_context * ctx ,
unsigned int reg )
2012-04-05 01:48:31 +04:00
{
2016-01-27 07:50:07 +03:00
return readb ( ctx - > regs + reg ) ;
}
static unsigned int regmap_mmio_read16le ( struct regmap_mmio_context * ctx ,
unsigned int reg )
{
return readw ( ctx - > regs + reg ) ;
}
static unsigned int regmap_mmio_read16be ( struct regmap_mmio_context * ctx ,
unsigned int reg )
{
return ioread16be ( ctx - > regs + reg ) ;
}
static unsigned int regmap_mmio_read32le ( struct regmap_mmio_context * ctx ,
unsigned int reg )
{
return readl ( ctx - > regs + reg ) ;
}
2014-03-27 08:42:43 +04:00
2016-01-27 07:50:07 +03:00
static unsigned int regmap_mmio_read32be ( struct regmap_mmio_context * ctx ,
unsigned int reg )
{
return ioread32be ( ctx - > regs + reg ) ;
}
2012-04-07 01:17:32 +04:00
2016-01-27 07:50:07 +03:00
# ifdef CONFIG_64BIT
static unsigned int regmap_mmio_read64le ( struct regmap_mmio_context * ctx ,
unsigned int reg )
{
return readq ( ctx - > regs + reg ) ;
2012-04-05 01:48:31 +04:00
}
2016-01-27 07:50:07 +03:00
# endif
2012-04-05 01:48:31 +04:00
2016-01-27 07:50:07 +03:00
static int regmap_mmio_read ( void * context , unsigned int reg , unsigned int * val )
2012-04-05 01:48:31 +04:00
{
struct regmap_mmio_context * ctx = context ;
2013-02-14 20:39:08 +04:00
int ret ;
2012-04-05 01:48:31 +04:00
2013-11-26 02:12:47 +04:00
if ( ! IS_ERR ( ctx - > clk ) ) {
2013-02-14 20:39:08 +04:00
ret = clk_enable ( ctx - > clk ) ;
if ( ret < 0 )
return ret ;
}
2016-01-27 07:50:07 +03:00
* val = ctx - > reg_read ( ctx , reg ) ;
2012-04-05 01:48:31 +04:00
2013-11-26 02:12:47 +04:00
if ( ! IS_ERR ( ctx - > clk ) )
2013-02-14 20:39:08 +04:00
clk_disable ( ctx - > clk ) ;
2012-04-05 01:48:31 +04:00
return 0 ;
}
static void regmap_mmio_free_context ( void * context )
{
2013-02-14 20:39:08 +04:00
struct regmap_mmio_context * ctx = context ;
2013-11-26 02:12:47 +04:00
if ( ! IS_ERR ( ctx - > clk ) ) {
2013-02-14 20:39:08 +04:00
clk_unprepare ( ctx - > clk ) ;
clk_put ( ctx - > clk ) ;
}
2012-04-05 01:48:31 +04:00
kfree ( context ) ;
}
2016-01-27 07:50:07 +03:00
static const struct regmap_bus regmap_mmio = {
2012-04-05 01:48:31 +04:00
. fast_io = true ,
2016-01-27 07:50:07 +03:00
. reg_write = regmap_mmio_write ,
. reg_read = regmap_mmio_read ,
2012-04-05 01:48:31 +04:00
. free_context = regmap_mmio_free_context ,
2016-03-31 20:18:09 +03:00
. val_format_endian_default = REGMAP_ENDIAN_LITTLE ,
2012-04-05 01:48:31 +04:00
} ;
2013-02-14 20:39:08 +04:00
static struct regmap_mmio_context * regmap_mmio_gen_context ( struct device * dev ,
const char * clk_id ,
void __iomem * regs ,
2012-04-05 01:48:31 +04:00
const struct regmap_config * config )
{
struct regmap_mmio_context * ctx ;
2012-04-09 23:40:24 +04:00
int min_stride ;
2013-02-14 20:39:08 +04:00
int ret ;
2012-04-05 01:48:31 +04:00
2014-03-28 09:12:56 +04:00
ret = regmap_mmio_regbits_check ( config - > reg_bits ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2012-04-05 01:48:31 +04:00
if ( config - > pad_bits )
return ERR_PTR ( - EINVAL ) ;
2015-12-03 08:27:21 +03:00
min_stride = regmap_mmio_get_min_stride ( config - > val_bits ) ;
if ( min_stride < 0 )
return ERR_PTR ( min_stride ) ;
2012-04-05 01:48:31 +04:00
2012-04-09 23:40:24 +04:00
if ( config - > reg_stride < min_stride )
return ERR_PTR ( - EINVAL ) ;
2012-07-18 17:17:23 +04:00
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
2012-04-05 01:48:31 +04:00
if ( ! ctx )
return ERR_PTR ( - ENOMEM ) ;
ctx - > regs = regs ;
ctx - > val_bytes = config - > val_bits / 8 ;
2013-11-26 02:12:47 +04:00
ctx - > clk = ERR_PTR ( - ENODEV ) ;
2012-04-05 01:48:31 +04:00
2016-03-29 22:30:44 +03:00
switch ( regmap_get_val_endian ( dev , & regmap_mmio , config ) ) {
2016-01-27 07:50:07 +03:00
case REGMAP_ENDIAN_DEFAULT :
case REGMAP_ENDIAN_LITTLE :
# ifdef __LITTLE_ENDIAN
case REGMAP_ENDIAN_NATIVE :
# endif
switch ( config - > val_bits ) {
case 8 :
ctx - > reg_read = regmap_mmio_read8 ;
ctx - > reg_write = regmap_mmio_write8 ;
break ;
case 16 :
ctx - > reg_read = regmap_mmio_read16le ;
ctx - > reg_write = regmap_mmio_write16le ;
break ;
case 32 :
ctx - > reg_read = regmap_mmio_read32le ;
ctx - > reg_write = regmap_mmio_write32le ;
break ;
# ifdef CONFIG_64BIT
case 64 :
ctx - > reg_read = regmap_mmio_read64le ;
ctx - > reg_write = regmap_mmio_write64le ;
break ;
# endif
default :
ret = - EINVAL ;
goto err_free ;
}
break ;
case REGMAP_ENDIAN_BIG :
# ifdef __BIG_ENDIAN
case REGMAP_ENDIAN_NATIVE :
# endif
switch ( config - > val_bits ) {
case 8 :
ctx - > reg_read = regmap_mmio_read8 ;
ctx - > reg_write = regmap_mmio_write8 ;
break ;
case 16 :
ctx - > reg_read = regmap_mmio_read16be ;
ctx - > reg_write = regmap_mmio_write16be ;
break ;
case 32 :
ctx - > reg_read = regmap_mmio_read32be ;
ctx - > reg_write = regmap_mmio_write32be ;
break ;
default :
ret = - EINVAL ;
goto err_free ;
}
break ;
default :
ret = - EINVAL ;
goto err_free ;
}
2013-02-14 20:39:08 +04:00
if ( clk_id = = NULL )
return ctx ;
ctx - > clk = clk_get ( dev , clk_id ) ;
if ( IS_ERR ( ctx - > clk ) ) {
ret = PTR_ERR ( ctx - > clk ) ;
goto err_free ;
}
ret = clk_prepare ( ctx - > clk ) ;
if ( ret < 0 ) {
clk_put ( ctx - > clk ) ;
goto err_free ;
}
2012-04-05 01:48:31 +04:00
return ctx ;
2013-02-14 20:39:08 +04:00
err_free :
kfree ( ctx ) ;
return ERR_PTR ( ret ) ;
2012-04-05 01:48:31 +04:00
}
2015-07-08 09:30:18 +03:00
struct regmap * __regmap_init_mmio_clk ( struct device * dev , const char * clk_id ,
void __iomem * regs ,
const struct regmap_config * config ,
struct lock_class_key * lock_key ,
const char * lock_name )
2012-04-05 01:48:31 +04:00
{
struct regmap_mmio_context * ctx ;
2013-02-14 20:39:08 +04:00
ctx = regmap_mmio_gen_context ( dev , clk_id , regs , config ) ;
2012-04-05 01:48:31 +04:00
if ( IS_ERR ( ctx ) )
return ERR_CAST ( ctx ) ;
2015-07-08 09:30:18 +03:00
return __regmap_init ( dev , & regmap_mmio , ctx , config ,
lock_key , lock_name ) ;
2012-04-05 01:48:31 +04:00
}
2015-07-08 09:30:18 +03:00
EXPORT_SYMBOL_GPL ( __regmap_init_mmio_clk ) ;
2012-04-05 01:48:31 +04:00
2015-07-08 09:30:18 +03:00
struct regmap * __devm_regmap_init_mmio_clk ( struct device * dev ,
const char * clk_id ,
void __iomem * regs ,
const struct regmap_config * config ,
struct lock_class_key * lock_key ,
const char * lock_name )
2012-04-05 01:48:31 +04:00
{
struct regmap_mmio_context * ctx ;
2013-02-14 20:39:08 +04:00
ctx = regmap_mmio_gen_context ( dev , clk_id , regs , config ) ;
2012-04-05 01:48:31 +04:00
if ( IS_ERR ( ctx ) )
return ERR_CAST ( ctx ) ;
2015-07-08 09:30:18 +03:00
return __devm_regmap_init ( dev , & regmap_mmio , ctx , config ,
lock_key , lock_name ) ;
2012-04-05 01:48:31 +04:00
}
2015-07-08 09:30:18 +03:00
EXPORT_SYMBOL_GPL ( __devm_regmap_init_mmio_clk ) ;
2012-04-05 01:48:31 +04:00
2018-02-21 12:20:25 +03:00
int regmap_mmio_attach_clk ( struct regmap * map , struct clk * clk )
{
struct regmap_mmio_context * ctx = map - > bus_context ;
ctx - > clk = clk ;
ctx - > attached_clk = true ;
return clk_prepare ( ctx - > clk ) ;
}
EXPORT_SYMBOL_GPL ( regmap_mmio_attach_clk ) ;
void regmap_mmio_detach_clk ( struct regmap * map )
{
struct regmap_mmio_context * ctx = map - > bus_context ;
clk_unprepare ( ctx - > clk ) ;
ctx - > attached_clk = false ;
ctx - > clk = NULL ;
}
EXPORT_SYMBOL_GPL ( regmap_mmio_detach_clk ) ;
2012-04-05 01:48:31 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;