2013-04-03 16:26:57 +04:00
/*
* Sysctrl clock implementation for ux500 platform .
*
* Copyright ( C ) 2013 ST - Ericsson SA
* Author : Ulf Hansson < ulf . hansson @ linaro . org >
*
* License terms : GNU General Public License ( GPL ) version 2
*/
# include <linux/clk-provider.h>
# include <linux/mfd/abx500/ab8500-sysctrl.h>
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/err.h>
# include "clk.h"
# define SYSCTRL_MAX_NUM_PARENTS 4
# define to_clk_sysctrl(_hw) container_of(_hw, struct clk_sysctrl, hw)
struct clk_sysctrl {
struct clk_hw hw ;
struct device * dev ;
u8 parent_index ;
u16 reg_sel [ SYSCTRL_MAX_NUM_PARENTS ] ;
u8 reg_mask [ SYSCTRL_MAX_NUM_PARENTS ] ;
u8 reg_bits [ SYSCTRL_MAX_NUM_PARENTS ] ;
unsigned long rate ;
unsigned long enable_delay_us ;
} ;
/* Sysctrl clock operations. */
static int clk_sysctrl_prepare ( struct clk_hw * hw )
{
int ret ;
struct clk_sysctrl * clk = to_clk_sysctrl ( hw ) ;
ret = ab8500_sysctrl_write ( clk - > reg_sel [ 0 ] , clk - > reg_mask [ 0 ] ,
clk - > reg_bits [ 0 ] ) ;
if ( ! ret & & clk - > enable_delay_us )
usleep_range ( clk - > enable_delay_us , clk - > enable_delay_us ) ;
return ret ;
}
static void clk_sysctrl_unprepare ( struct clk_hw * hw )
{
struct clk_sysctrl * clk = to_clk_sysctrl ( hw ) ;
if ( ab8500_sysctrl_clear ( clk - > reg_sel [ 0 ] , clk - > reg_mask [ 0 ] ) )
dev_err ( clk - > dev , " clk_sysctrl: %s fail to clear %s. \n " ,
2015-08-12 21:42:23 +03:00
__func__ , clk_hw_get_name ( hw ) ) ;
2013-04-03 16:26:57 +04:00
}
static unsigned long clk_sysctrl_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_sysctrl * clk = to_clk_sysctrl ( hw ) ;
return clk - > rate ;
}
static int clk_sysctrl_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_sysctrl * clk = to_clk_sysctrl ( hw ) ;
u8 old_index = clk - > parent_index ;
int ret = 0 ;
if ( clk - > reg_sel [ old_index ] ) {
ret = ab8500_sysctrl_clear ( clk - > reg_sel [ old_index ] ,
clk - > reg_mask [ old_index ] ) ;
if ( ret )
return ret ;
}
if ( clk - > reg_sel [ index ] ) {
ret = ab8500_sysctrl_write ( clk - > reg_sel [ index ] ,
clk - > reg_mask [ index ] ,
clk - > reg_bits [ index ] ) ;
if ( ret ) {
if ( clk - > reg_sel [ old_index ] )
ab8500_sysctrl_write ( clk - > reg_sel [ old_index ] ,
clk - > reg_mask [ old_index ] ,
clk - > reg_bits [ old_index ] ) ;
return ret ;
}
}
clk - > parent_index = index ;
return ret ;
}
static u8 clk_sysctrl_get_parent ( struct clk_hw * hw )
{
struct clk_sysctrl * clk = to_clk_sysctrl ( hw ) ;
return clk - > parent_index ;
}
2017-08-28 10:02:40 +03:00
static const struct clk_ops clk_sysctrl_gate_ops = {
2013-04-03 16:26:57 +04:00
. prepare = clk_sysctrl_prepare ,
. unprepare = clk_sysctrl_unprepare ,
} ;
2017-08-28 10:02:40 +03:00
static const struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
2013-04-03 16:26:57 +04:00
. prepare = clk_sysctrl_prepare ,
. unprepare = clk_sysctrl_unprepare ,
. recalc_rate = clk_sysctrl_recalc_rate ,
} ;
2017-08-28 10:02:40 +03:00
static const struct clk_ops clk_sysctrl_set_parent_ops = {
2013-04-03 16:26:57 +04:00
. set_parent = clk_sysctrl_set_parent ,
. get_parent = clk_sysctrl_get_parent ,
} ;
static struct clk * clk_reg_sysctrl ( struct device * dev ,
const char * name ,
const char * * parent_names ,
u8 num_parents ,
u16 * reg_sel ,
u8 * reg_mask ,
u8 * reg_bits ,
unsigned long rate ,
unsigned long enable_delay_us ,
unsigned long flags ,
2017-08-28 10:02:40 +03:00
const struct clk_ops * clk_sysctrl_ops )
2013-04-03 16:26:57 +04:00
{
struct clk_sysctrl * clk ;
struct clk_init_data clk_sysctrl_init ;
struct clk * clk_reg ;
int i ;
if ( ! dev )
return ERR_PTR ( - EINVAL ) ;
if ( ! name | | ( num_parents > SYSCTRL_MAX_NUM_PARENTS ) ) {
dev_err ( dev , " clk_sysctrl: invalid arguments passed \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
clk = devm_kzalloc ( dev , sizeof ( struct clk_sysctrl ) , GFP_KERNEL ) ;
if ( ! clk ) {
dev_err ( dev , " clk_sysctrl: could not allocate clk \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2013-04-30 16:45:06 +04:00
/* set main clock registers */
clk - > reg_sel [ 0 ] = reg_sel [ 0 ] ;
clk - > reg_bits [ 0 ] = reg_bits [ 0 ] ;
clk - > reg_mask [ 0 ] = reg_mask [ 0 ] ;
/* handle clocks with more than one parent */
for ( i = 1 ; i < num_parents ; i + + ) {
2013-04-03 16:26:57 +04:00
clk - > reg_sel [ i ] = reg_sel [ i ] ;
clk - > reg_bits [ i ] = reg_bits [ i ] ;
clk - > reg_mask [ i ] = reg_mask [ i ] ;
}
clk - > parent_index = 0 ;
clk - > rate = rate ;
clk - > enable_delay_us = enable_delay_us ;
clk - > dev = dev ;
clk_sysctrl_init . name = name ;
clk_sysctrl_init . ops = clk_sysctrl_ops ;
clk_sysctrl_init . flags = flags ;
clk_sysctrl_init . parent_names = parent_names ;
clk_sysctrl_init . num_parents = num_parents ;
clk - > hw . init = & clk_sysctrl_init ;
clk_reg = devm_clk_register ( clk - > dev , & clk - > hw ) ;
if ( IS_ERR ( clk_reg ) )
dev_err ( dev , " clk_sysctrl: clk_register failed \n " ) ;
return clk_reg ;
}
struct clk * clk_reg_sysctrl_gate ( struct device * dev ,
const char * name ,
const char * parent_name ,
u16 reg_sel ,
u8 reg_mask ,
u8 reg_bits ,
unsigned long enable_delay_us ,
unsigned long flags )
{
const char * * parent_names = ( parent_name ? & parent_name : NULL ) ;
u8 num_parents = ( parent_name ? 1 : 0 ) ;
return clk_reg_sysctrl ( dev , name , parent_names , num_parents ,
& reg_sel , & reg_mask , & reg_bits , 0 , enable_delay_us ,
flags , & clk_sysctrl_gate_ops ) ;
}
struct clk * clk_reg_sysctrl_gate_fixed_rate ( struct device * dev ,
const char * name ,
const char * parent_name ,
u16 reg_sel ,
u8 reg_mask ,
u8 reg_bits ,
unsigned long rate ,
unsigned long enable_delay_us ,
unsigned long flags )
{
const char * * parent_names = ( parent_name ? & parent_name : NULL ) ;
u8 num_parents = ( parent_name ? 1 : 0 ) ;
return clk_reg_sysctrl ( dev , name , parent_names , num_parents ,
& reg_sel , & reg_mask , & reg_bits ,
rate , enable_delay_us , flags ,
& clk_sysctrl_gate_fixed_rate_ops ) ;
}
struct clk * clk_reg_sysctrl_set_parent ( struct device * dev ,
const char * name ,
const char * * parent_names ,
u8 num_parents ,
u16 * reg_sel ,
u8 * reg_mask ,
u8 * reg_bits ,
unsigned long flags )
{
return clk_reg_sysctrl ( dev , name , parent_names , num_parents ,
reg_sel , reg_mask , reg_bits , 0 , 0 , flags ,
& clk_sysctrl_set_parent_ops ) ;
}