2019-05-28 09:57:24 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2014-05-15 10:55:11 +02:00
/*
* Copyright ( C ) 2014 Free Electrons
*
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*
* Allwinner A31 APB0 clock gates driver
*/
# include <linux/clk-provider.h>
2016-07-04 17:12:18 -04:00
# include <linux/init.h>
2014-05-15 10:55:11 +02:00
# include <linux/of.h>
# include <linux/platform_device.h>
# define SUN6I_APB0_GATES_MAX_SIZE 32
2014-07-09 15:54:34 +08:00
struct gates_data {
DECLARE_BITMAP ( mask , SUN6I_APB0_GATES_MAX_SIZE ) ;
} ;
static const struct gates_data sun6i_a31_apb0_gates __initconst = {
. mask = { 0x7F } ,
} ;
2014-07-09 15:54:35 +08:00
static const struct gates_data sun8i_a23_apb0_gates __initconst = {
. mask = { 0x5D } ,
} ;
2014-07-28 00:49:43 -03:00
static const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids [ ] = {
2014-07-09 15:54:34 +08:00
{ . compatible = " allwinner,sun6i-a31-apb0-gates-clk " , . data = & sun6i_a31_apb0_gates } ,
2014-07-09 15:54:35 +08:00
{ . compatible = " allwinner,sun8i-a23-apb0-gates-clk " , . data = & sun8i_a23_apb0_gates } ,
2014-07-09 15:54:34 +08:00
{ /* sentinel */ }
} ;
2014-05-15 10:55:11 +02:00
static int sun6i_a31_apb0_gates_clk_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct clk_onecell_data * clk_data ;
2014-07-09 15:54:34 +08:00
const struct gates_data * data ;
2014-05-15 10:55:11 +02:00
const char * clk_parent ;
const char * clk_name ;
void __iomem * reg ;
int ngates ;
int i ;
2014-07-09 15:54:34 +08:00
int j = 0 ;
if ( ! np )
return - ENODEV ;
2019-11-10 16:35:20 +00:00
data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! data )
2014-07-09 15:54:34 +08:00
return - ENODEV ;
2014-05-15 10:55:11 +02:00
2021-09-07 16:53:04 +08:00
reg = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-06-28 22:53:55 +05:30
if ( IS_ERR ( reg ) )
2014-05-15 10:55:11 +02:00
return PTR_ERR ( reg ) ;
clk_parent = of_clk_get_parent_name ( np , 0 ) ;
if ( ! clk_parent )
return - EINVAL ;
clk_data = devm_kzalloc ( & pdev - > dev , sizeof ( struct clk_onecell_data ) ,
GFP_KERNEL ) ;
if ( ! clk_data )
return - ENOMEM ;
2014-07-09 15:54:34 +08:00
/* Worst-case size approximation and memory allocation */
ngates = find_last_bit ( data - > mask , SUN6I_APB0_GATES_MAX_SIZE ) ;
clk_data - > clks = devm_kcalloc ( & pdev - > dev , ( ngates + 1 ) ,
sizeof ( struct clk * ) , GFP_KERNEL ) ;
2014-05-15 10:55:11 +02:00
if ( ! clk_data - > clks )
return - ENOMEM ;
2014-07-09 15:54:34 +08:00
for_each_set_bit ( i , data - > mask , SUN6I_APB0_GATES_MAX_SIZE ) {
2014-05-15 10:55:11 +02:00
of_property_read_string_index ( np , " clock-output-names " ,
2014-07-09 15:54:34 +08:00
j , & clk_name ) ;
2014-05-15 10:55:11 +02:00
2014-07-09 15:54:34 +08:00
clk_data - > clks [ i ] = clk_register_gate ( & pdev - > dev , clk_name ,
clk_parent , 0 , reg , i ,
0 , NULL ) ;
WARN_ON ( IS_ERR ( clk_data - > clks [ i ] ) ) ;
2014-05-15 10:55:11 +02:00
2014-07-09 15:54:34 +08:00
j + + ;
2014-05-15 10:55:11 +02:00
}
2014-07-09 15:54:34 +08:00
clk_data - > clk_num = ngates + 1 ;
2014-05-15 10:55:11 +02:00
return of_clk_add_provider ( np , of_clk_src_onecell_get , clk_data ) ;
}
static struct platform_driver sun6i_a31_apb0_gates_clk_driver = {
. driver = {
. name = " sun6i-a31-apb0-gates-clk " ,
. of_match_table = sun6i_a31_apb0_gates_clk_dt_ids ,
} ,
. probe = sun6i_a31_apb0_gates_clk_probe ,
} ;
2016-07-04 17:12:18 -04:00
builtin_platform_driver ( sun6i_a31_apb0_gates_clk_driver ) ;