2015-07-31 04:13:56 +03:00
/*
* Copyright ( c ) 2015 , National Instruments Corp .
*
* Xilinx Zynq Reset controller driver
*
2016-06-13 21:03:37 +03:00
* Author : Moritz Fischer < moritz . fischer @ ettus . com >
*
2015-07-31 04:13:56 +03:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* 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 .
*/
# include <linux/err.h>
# include <linux/io.h>
2016-06-13 21:03:37 +03:00
# include <linux/init.h>
2015-07-31 04:13:56 +03:00
# include <linux/mfd/syscon.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
# include <linux/regmap.h>
# include <linux/types.h>
struct zynq_reset_data {
struct regmap * slcr ;
struct reset_controller_dev rcdev ;
u32 offset ;
} ;
# define to_zynq_reset_data(p) \
container_of ( ( p ) , struct zynq_reset_data , rcdev )
static int zynq_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct zynq_reset_data * priv = to_zynq_reset_data ( rcdev ) ;
int bank = id / BITS_PER_LONG ;
int offset = id % BITS_PER_LONG ;
pr_debug ( " %s: %s reset bank %u offset %u \n " , KBUILD_MODNAME , __func__ ,
bank , offset ) ;
return regmap_update_bits ( priv - > slcr ,
priv - > offset + ( bank * 4 ) ,
BIT ( offset ) ,
BIT ( offset ) ) ;
}
static int zynq_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct zynq_reset_data * priv = to_zynq_reset_data ( rcdev ) ;
int bank = id / BITS_PER_LONG ;
int offset = id % BITS_PER_LONG ;
pr_debug ( " %s: %s reset bank %u offset %u \n " , KBUILD_MODNAME , __func__ ,
bank , offset ) ;
return regmap_update_bits ( priv - > slcr ,
priv - > offset + ( bank * 4 ) ,
BIT ( offset ) ,
~ BIT ( offset ) ) ;
}
static int zynq_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct zynq_reset_data * priv = to_zynq_reset_data ( rcdev ) ;
int bank = id / BITS_PER_LONG ;
int offset = id % BITS_PER_LONG ;
int ret ;
u32 reg ;
pr_debug ( " %s: %s reset bank %u offset %u \n " , KBUILD_MODNAME , __func__ ,
bank , offset ) ;
ret = regmap_read ( priv - > slcr , priv - > offset + ( bank * 4 ) , & reg ) ;
if ( ret )
return ret ;
return ! ! ( reg & BIT ( offset ) ) ;
}
2016-01-17 17:14:52 +03:00
static const struct reset_control_ops zynq_reset_ops = {
2015-07-31 04:13:56 +03:00
. assert = zynq_reset_assert ,
. deassert = zynq_reset_deassert ,
. status = zynq_reset_status ,
} ;
static int zynq_reset_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct zynq_reset_data * priv ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
priv - > slcr = syscon_regmap_lookup_by_phandle ( pdev - > dev . of_node ,
" syscon " ) ;
if ( IS_ERR ( priv - > slcr ) ) {
dev_err ( & pdev - > dev , " unable to get zynq-slcr regmap " ) ;
return PTR_ERR ( priv - > slcr ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " missing IO resource \n " ) ;
return - ENODEV ;
}
priv - > offset = res - > start ;
priv - > rcdev . owner = THIS_MODULE ;
priv - > rcdev . nr_resets = resource_size ( res ) / 4 * BITS_PER_LONG ;
priv - > rcdev . ops = & zynq_reset_ops ;
priv - > rcdev . of_node = pdev - > dev . of_node ;
2016-05-01 13:37:03 +03:00
return devm_reset_controller_register ( & pdev - > dev , & priv - > rcdev ) ;
2015-07-31 04:13:56 +03:00
}
static const struct of_device_id zynq_reset_dt_ids [ ] = {
{ . compatible = " xlnx,zynq-reset " , } ,
{ /* sentinel */ } ,
} ;
static struct platform_driver zynq_reset_driver = {
. probe = zynq_reset_probe ,
. driver = {
. name = KBUILD_MODNAME ,
. of_match_table = zynq_reset_dt_ids ,
} ,
} ;
2016-06-13 21:03:37 +03:00
builtin_platform_driver ( zynq_reset_driver ) ;