2019-08-01 10:54:52 +03:00
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2016-05-30 16:27:15 +03:00
/*
2017-02-08 03:18:46 +03:00
* Amlogic Meson Reset Controller driver
*
2016-05-30 16:27:15 +03:00
* Copyright ( c ) 2016 BayLibre , SAS .
* Author : Neil Armstrong < narmstrong @ baylibre . com >
*/
# include <linux/err.h>
2017-02-08 03:18:46 +03:00
# include <linux/init.h>
2016-05-30 16:27:15 +03:00
# include <linux/io.h>
# include <linux/of.h>
2020-10-19 17:48:09 +03:00
# include <linux/module.h>
2016-05-30 16:27:15 +03:00
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
# include <linux/slab.h>
# include <linux/types.h>
2017-10-17 13:19:18 +03:00
# include <linux/of_device.h>
2016-05-30 16:27:15 +03:00
# define BITS_PER_REG 32
2019-09-29 09:24:15 +03:00
struct meson_reset_param {
int reg_count ;
int level_offset ;
} ;
2016-05-30 16:27:15 +03:00
struct meson_reset {
void __iomem * reg_base ;
2019-09-29 09:24:15 +03:00
const struct meson_reset_param * param ;
2016-05-30 16:27:15 +03:00
struct reset_controller_dev rcdev ;
2017-10-17 13:19:18 +03:00
spinlock_t lock ;
2016-05-30 16:27:15 +03:00
} ;
static int meson_reset_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct meson_reset * data =
container_of ( rcdev , struct meson_reset , rcdev ) ;
unsigned int bank = id / BITS_PER_REG ;
unsigned int offset = id % BITS_PER_REG ;
void __iomem * reg_addr = data - > reg_base + ( bank < < 2 ) ;
writel ( BIT ( offset ) , reg_addr ) ;
return 0 ;
}
2017-10-17 13:19:18 +03:00
static int meson_reset_level ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct meson_reset * data =
container_of ( rcdev , struct meson_reset , rcdev ) ;
unsigned int bank = id / BITS_PER_REG ;
unsigned int offset = id % BITS_PER_REG ;
2019-09-29 09:24:15 +03:00
void __iomem * reg_addr ;
2017-10-17 13:19:18 +03:00
unsigned long flags ;
u32 reg ;
2019-09-29 09:24:15 +03:00
reg_addr = data - > reg_base + data - > param - > level_offset + ( bank < < 2 ) ;
2017-10-17 13:19:18 +03:00
spin_lock_irqsave ( & data - > lock , flags ) ;
reg = readl ( reg_addr ) ;
if ( assert )
writel ( reg & ~ BIT ( offset ) , reg_addr ) ;
else
writel ( reg | BIT ( offset ) , reg_addr ) ;
spin_unlock_irqrestore ( & data - > lock , flags ) ;
return 0 ;
}
static int meson_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return meson_reset_level ( rcdev , id , true ) ;
}
static int meson_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return meson_reset_level ( rcdev , id , false ) ;
}
2017-12-26 14:50:41 +03:00
static const struct reset_control_ops meson_reset_ops = {
2017-10-17 13:19:18 +03:00
. reset = meson_reset_reset ,
. assert = meson_reset_assert ,
. deassert = meson_reset_deassert ,
} ;
2019-09-29 09:24:15 +03:00
static const struct meson_reset_param meson8b_param = {
. reg_count = 8 ,
. level_offset = 0x7c ,
} ;
static const struct meson_reset_param meson_a1_param = {
. reg_count = 3 ,
. level_offset = 0x40 ,
} ;
2016-05-30 16:27:15 +03:00
static const struct of_device_id meson_reset_dt_ids [ ] = {
2019-09-29 09:24:15 +03:00
{ . compatible = " amlogic,meson8b-reset " , . data = & meson8b_param } ,
{ . compatible = " amlogic,meson-gxbb-reset " , . data = & meson8b_param } ,
{ . compatible = " amlogic,meson-axg-reset " , . data = & meson8b_param } ,
{ . compatible = " amlogic,meson-a1-reset " , . data = & meson_a1_param } ,
2016-05-30 16:27:15 +03:00
{ /* sentinel */ } ,
} ;
2020-10-19 17:48:09 +03:00
MODULE_DEVICE_TABLE ( of , meson_reset_dt_ids ) ;
2016-05-30 16:27:15 +03:00
static int meson_reset_probe ( struct platform_device * pdev )
{
struct meson_reset * data ;
struct resource * res ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
data - > reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( data - > reg_base ) )
return PTR_ERR ( data - > reg_base ) ;
2019-09-29 09:24:15 +03:00
data - > param = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! data - > param )
return - ENODEV ;
2016-05-30 16:27:15 +03:00
platform_set_drvdata ( pdev , data ) ;
2017-10-17 13:19:18 +03:00
spin_lock_init ( & data - > lock ) ;
2016-05-30 16:27:15 +03:00
data - > rcdev . owner = THIS_MODULE ;
2019-09-29 09:24:15 +03:00
data - > rcdev . nr_resets = data - > param - > reg_count * BITS_PER_REG ;
2017-12-26 14:50:41 +03:00
data - > rcdev . ops = & meson_reset_ops ;
2016-05-30 16:27:15 +03:00
data - > rcdev . of_node = pdev - > dev . of_node ;
return devm_reset_controller_register ( & pdev - > dev , & data - > rcdev ) ;
}
static struct platform_driver meson_reset_driver = {
. probe = meson_reset_probe ,
. driver = {
. name = " meson_reset " ,
. of_match_table = meson_reset_dt_ids ,
} ,
} ;
2020-10-19 17:48:09 +03:00
module_platform_driver ( meson_reset_driver ) ;
MODULE_DESCRIPTION ( " Amlogic Meson Reset Controller driver " ) ;
MODULE_AUTHOR ( " Neil Armstrong <narmstrong@baylibre.com> " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;