2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-06-09 14:01:58 +02:00
/*
* Copyright ( C ) 2016 Free Electrons
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
# include <linux/clk.h>
# include <linux/component.h>
# include <linux/module.h>
2018-06-19 22:47:28 -07:00
# include <linux/mod_devicetable.h>
2016-06-09 14:01:58 +02:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/reset.h>
struct sun6i_drc {
struct clk * bus_clk ;
struct clk * mod_clk ;
struct reset_control * reset ;
} ;
static int sun6i_drc_bind ( struct device * dev , struct device * master ,
void * data )
{
struct sun6i_drc * drc ;
int ret ;
drc = devm_kzalloc ( dev , sizeof ( * drc ) , GFP_KERNEL ) ;
if ( ! drc )
return - ENOMEM ;
dev_set_drvdata ( dev , drc ) ;
drc - > reset = devm_reset_control_get ( dev , NULL ) ;
if ( IS_ERR ( drc - > reset ) ) {
dev_err ( dev , " Couldn't get our reset line \n " ) ;
return PTR_ERR ( drc - > reset ) ;
}
ret = reset_control_deassert ( drc - > reset ) ;
if ( ret ) {
dev_err ( dev , " Couldn't deassert our reset line \n " ) ;
return ret ;
}
drc - > bus_clk = devm_clk_get ( dev , " ahb " ) ;
if ( IS_ERR ( drc - > bus_clk ) ) {
dev_err ( dev , " Couldn't get our bus clock \n " ) ;
ret = PTR_ERR ( drc - > bus_clk ) ;
goto err_assert_reset ;
}
clk_prepare_enable ( drc - > bus_clk ) ;
drc - > mod_clk = devm_clk_get ( dev , " mod " ) ;
if ( IS_ERR ( drc - > mod_clk ) ) {
dev_err ( dev , " Couldn't get our mod clock \n " ) ;
ret = PTR_ERR ( drc - > mod_clk ) ;
goto err_disable_bus_clk ;
}
2020-01-07 17:59:57 +01:00
ret = clk_set_rate_exclusive ( drc - > mod_clk , 300000000 ) ;
if ( ret ) {
dev_err ( dev , " Couldn't set the module clock frequency \n " ) ;
goto err_disable_bus_clk ;
}
2016-06-09 14:01:58 +02:00
clk_prepare_enable ( drc - > mod_clk ) ;
return 0 ;
err_disable_bus_clk :
clk_disable_unprepare ( drc - > bus_clk ) ;
err_assert_reset :
reset_control_assert ( drc - > reset ) ;
return ret ;
}
static void sun6i_drc_unbind ( struct device * dev , struct device * master ,
void * data )
{
struct sun6i_drc * drc = dev_get_drvdata ( dev ) ;
2020-01-07 17:59:57 +01:00
clk_rate_exclusive_put ( drc - > mod_clk ) ;
2016-06-09 14:01:58 +02:00
clk_disable_unprepare ( drc - > mod_clk ) ;
clk_disable_unprepare ( drc - > bus_clk ) ;
reset_control_assert ( drc - > reset ) ;
}
2016-11-12 18:19:58 +01:00
static const struct component_ops sun6i_drc_ops = {
2016-06-09 14:01:58 +02:00
. bind = sun6i_drc_bind ,
. unbind = sun6i_drc_unbind ,
} ;
static int sun6i_drc_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & sun6i_drc_ops ) ;
}
static int sun6i_drc_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & sun6i_drc_ops ) ;
return 0 ;
}
static const struct of_device_id sun6i_drc_of_table [ ] = {
2016-10-20 11:43:38 +08:00
{ . compatible = " allwinner,sun6i-a31-drc " } ,
{ . compatible = " allwinner,sun6i-a31s-drc " } ,
2019-01-25 11:23:09 +08:00
{ . compatible = " allwinner,sun8i-a23-drc " } ,
2016-06-09 14:01:58 +02:00
{ . compatible = " allwinner,sun8i-a33-drc " } ,
2018-03-15 19:41:33 +08:00
{ . compatible = " allwinner,sun9i-a80-drc " } ,
2016-06-09 14:01:58 +02:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , sun6i_drc_of_table ) ;
static struct platform_driver sun6i_drc_platform_driver = {
. probe = sun6i_drc_probe ,
. remove = sun6i_drc_remove ,
. driver = {
. name = " sun6i-drc " ,
. of_match_table = sun6i_drc_of_table ,
} ,
} ;
module_platform_driver ( sun6i_drc_platform_driver ) ;
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Allwinner A31 Dynamic Range Control (DRC) Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;