2013-10-11 13:41:41 +04:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . com >
*
* 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 ; either version 2 of the License , or
* ( at your option ) any later version .
*
*/
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
2014-09-07 10:14:29 +04:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2013-10-11 13:41:41 +04:00
# include "pmc.h"
# define SYSTEM_MAX_ID 31
# define SYSTEM_MAX_NAME_SZ 32
# define to_clk_system(hw) container_of(hw, struct clk_system, hw)
struct clk_system {
struct clk_hw hw ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:41:41 +04:00
u8 id ;
} ;
2014-03-11 13:00:34 +04:00
static inline int is_pck ( int id )
{
return ( id > = 8 ) & & ( id < = 15 ) ;
}
2014-09-07 10:14:29 +04:00
static inline bool clk_system_ready ( struct regmap * regmap , int id )
{
unsigned int status ;
regmap_read ( regmap , AT91_PMC_SR , & status ) ;
return status & ( 1 < < id ) ? 1 : 0 ;
}
2014-03-11 13:00:34 +04:00
static int clk_system_prepare ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
2014-09-07 10:14:29 +04:00
regmap_write ( sys - > regmap , AT91_PMC_SCER , 1 < < sys - > id ) ;
2014-03-11 13:00:34 +04:00
if ( ! is_pck ( sys - > id ) )
return 0 ;
2015-09-17 00:47:39 +03:00
while ( ! clk_system_ready ( sys - > regmap , sys - > id ) )
cpu_relax ( ) ;
2013-10-11 13:41:41 +04:00
return 0 ;
}
2014-03-11 13:00:34 +04:00
static void clk_system_unprepare ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
2014-09-07 10:14:29 +04:00
regmap_write ( sys - > regmap , AT91_PMC_SCDR , 1 < < sys - > id ) ;
2013-10-11 13:41:41 +04:00
}
2014-03-11 13:00:34 +04:00
static int clk_system_is_prepared ( struct clk_hw * hw )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys = to_clk_system ( hw ) ;
2014-09-07 10:14:29 +04:00
unsigned int status ;
regmap_read ( sys - > regmap , AT91_PMC_SCSR , & status ) ;
2013-10-11 13:41:41 +04:00
2014-09-07 10:14:29 +04:00
if ( ! ( status & ( 1 < < sys - > id ) ) )
2014-03-11 13:00:34 +04:00
return 0 ;
if ( ! is_pck ( sys - > id ) )
return 1 ;
2014-09-07 10:14:29 +04:00
regmap_read ( sys - > regmap , AT91_PMC_SR , & status ) ;
return status & ( 1 < < sys - > id ) ? 1 : 0 ;
2013-10-11 13:41:41 +04:00
}
static const struct clk_ops system_ops = {
2014-03-11 13:00:34 +04:00
. prepare = clk_system_prepare ,
. unprepare = clk_system_unprepare ,
. is_prepared = clk_system_is_prepared ,
2013-10-11 13:41:41 +04:00
} ;
2016-06-02 00:31:22 +03:00
static struct clk_hw * __init
2014-09-07 10:14:29 +04:00
at91_clk_register_system ( struct regmap * regmap , const char * name ,
2015-09-17 00:47:39 +03:00
const char * parent_name , u8 id )
2013-10-11 13:41:41 +04:00
{
struct clk_system * sys ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
2013-10-11 13:41:41 +04:00
struct clk_init_data init ;
2016-06-02 00:31:22 +03:00
int ret ;
2013-10-11 13:41:41 +04:00
if ( ! parent_name | | id > SYSTEM_MAX_ID )
return ERR_PTR ( - EINVAL ) ;
sys = kzalloc ( sizeof ( * sys ) , GFP_KERNEL ) ;
if ( ! sys )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & system_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2014-07-08 20:21:16 +04:00
init . flags = CLK_SET_RATE_PARENT ;
2013-10-11 13:41:41 +04:00
sys - > id = id ;
sys - > hw . init = & init ;
2014-09-07 10:14:29 +04:00
sys - > regmap = regmap ;
2013-10-11 13:41:41 +04:00
2016-06-02 00:31:22 +03:00
hw = & sys - > hw ;
ret = clk_hw_register ( NULL , & sys - > hw ) ;
if ( ret ) {
2013-10-11 13:41:41 +04:00
kfree ( sys ) ;
2016-06-02 00:31:22 +03:00
hw = ERR_PTR ( ret ) ;
}
2013-10-11 13:41:41 +04:00
2016-06-02 00:31:22 +03:00
return hw ;
2013-10-11 13:41:41 +04:00
}
2014-09-07 10:14:29 +04:00
static void __init of_at91rm9200_clk_sys_setup ( struct device_node * np )
2013-10-11 13:41:41 +04:00
{
int num ;
u32 id ;
2016-06-02 00:31:22 +03:00
struct clk_hw * hw ;
2013-10-11 13:41:41 +04:00
const char * name ;
struct device_node * sysclknp ;
const char * parent_name ;
2014-09-07 10:14:29 +04:00
struct regmap * regmap ;
2013-10-11 13:41:41 +04:00
num = of_get_child_count ( np ) ;
if ( num > ( SYSTEM_MAX_ID + 1 ) )
return ;
2014-09-07 10:14:29 +04:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2013-10-11 13:41:41 +04:00
for_each_child_of_node ( np , sysclknp ) {
if ( of_property_read_u32 ( sysclknp , " reg " , & id ) )
continue ;
if ( of_property_read_string ( np , " clock-output-names " , & name ) )
name = sysclknp - > name ;
parent_name = of_clk_get_parent_name ( sysclknp , 0 ) ;
2016-06-02 00:31:22 +03:00
hw = at91_clk_register_system ( regmap , name , parent_name , id ) ;
if ( IS_ERR ( hw ) )
2013-10-11 13:41:41 +04:00
continue ;
2016-06-02 00:31:22 +03:00
of_clk_add_hw_provider ( sysclknp , of_clk_hw_simple_get , hw ) ;
2013-10-11 13:41:41 +04:00
}
}
2014-09-07 10:14:29 +04:00
CLK_OF_DECLARE ( at91rm9200_clk_sys , " atmel,at91rm9200-clk-system " ,
of_at91rm9200_clk_sys_setup ) ;