2012-03-16 10:11:20 +04:00
/*
* Copyright ( C ) 2011 Sascha Hauer , Pengutronix < s . hauer @ pengutronix . de >
* Copyright ( C ) 2011 Richard Zhao , Linaro < richard . zhao @ linaro . org >
* Copyright ( C ) 2011 - 2012 Mike Turquette , Linaro Ltd < mturquette @ linaro . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Simple multiplexer clock implementation
*/
# include <linux/clk-provider.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/err.h>
/*
* DOC : basic adjustable multiplexer clock that cannot gate
*
* Traits of this clock :
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is only affected by parent switching . No clk_set_rate support
* parent - parent is adjustable through clk_set_parent
*/
2018-02-14 16:43:34 +03:00
int clk_mux_val_to_index ( struct clk_hw * hw , u32 * table , unsigned int flags ,
unsigned int val )
2012-03-16 10:11:20 +04:00
{
2015-06-26 02:53:23 +03:00
int num_parents = clk_hw_get_num_parents ( hw ) ;
2012-03-16 10:11:20 +04:00
2018-02-14 16:43:34 +03:00
if ( table ) {
2013-03-22 16:07:53 +04:00
int i ;
for ( i = 0 ; i < num_parents ; i + + )
2018-02-14 16:43:34 +03:00
if ( table [ i ] = = val )
2013-03-22 16:07:53 +04:00
return i ;
return - EINVAL ;
}
2012-03-16 10:11:20 +04:00
2018-02-14 16:43:34 +03:00
if ( val & & ( flags & CLK_MUX_INDEX_BIT ) )
2012-03-16 10:11:20 +04:00
val = ffs ( val ) - 1 ;
2018-02-14 16:43:34 +03:00
if ( val & & ( flags & CLK_MUX_INDEX_ONE ) )
2012-03-16 10:11:20 +04:00
val - - ;
2013-03-22 16:07:53 +04:00
if ( val > = num_parents )
2012-03-16 10:11:20 +04:00
return - EINVAL ;
return val ;
}
2018-02-14 16:43:34 +03:00
EXPORT_SYMBOL_GPL ( clk_mux_val_to_index ) ;
2012-03-16 10:11:20 +04:00
2018-02-14 16:43:34 +03:00
unsigned int clk_mux_index_to_val ( u32 * table , unsigned int flags , u8 index )
2012-03-16 10:11:20 +04:00
{
2018-02-14 16:43:34 +03:00
unsigned int val = index ;
2012-03-16 10:11:20 +04:00
2018-02-14 16:43:34 +03:00
if ( table ) {
val = table [ index ] ;
2015-11-05 11:59:39 +03:00
} else {
2018-02-14 16:43:34 +03:00
if ( flags & CLK_MUX_INDEX_BIT )
val = 1 < < index ;
2013-03-22 16:07:53 +04:00
2018-02-14 16:43:34 +03:00
if ( flags & CLK_MUX_INDEX_ONE )
val + + ;
2013-03-22 16:07:53 +04:00
}
2012-03-16 10:11:20 +04:00
2018-02-14 16:43:34 +03:00
return val ;
}
EXPORT_SYMBOL_GPL ( clk_mux_index_to_val ) ;
static u8 clk_mux_get_parent ( struct clk_hw * hw )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
u32 val ;
val = clk_readl ( mux - > reg ) > > mux - > shift ;
val & = mux - > mask ;
return clk_mux_val_to_index ( hw , mux - > table , mux - > flags , val ) ;
}
static int clk_mux_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
u32 val = clk_mux_index_to_val ( mux - > table , mux - > flags , index ) ;
unsigned long flags = 0 ;
u32 reg ;
2012-03-16 10:11:20 +04:00
if ( mux - > lock )
spin_lock_irqsave ( mux - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__acquire ( mux - > lock ) ;
2012-03-16 10:11:20 +04:00
2013-06-08 18:47:17 +04:00
if ( mux - > flags & CLK_MUX_HIWORD_MASK ) {
2018-02-14 16:43:34 +03:00
reg = mux - > mask < < ( mux - > shift + 16 ) ;
2013-06-08 18:47:17 +04:00
} else {
2018-02-14 16:43:34 +03:00
reg = clk_readl ( mux - > reg ) ;
reg & = ~ ( mux - > mask < < mux - > shift ) ;
2013-06-08 18:47:17 +04:00
}
2018-02-14 16:43:34 +03:00
val = val < < mux - > shift ;
reg | = val ;
clk_writel ( reg , mux - > reg ) ;
2012-03-16 10:11:20 +04:00
if ( mux - > lock )
spin_unlock_irqrestore ( mux - > lock , flags ) ;
2015-07-24 22:21:12 +03:00
else
__release ( mux - > lock ) ;
2012-03-16 10:11:20 +04:00
return 0 ;
}
2018-04-09 16:59:20 +03:00
static int clk_mux_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
return clk_mux_determine_rate_flags ( hw , req , mux - > flags ) ;
}
2012-03-27 11:23:22 +04:00
const struct clk_ops clk_mux_ops = {
2012-03-16 10:11:20 +04:00
. get_parent = clk_mux_get_parent ,
. set_parent = clk_mux_set_parent ,
2018-04-09 16:59:20 +03:00
. determine_rate = clk_mux_determine_rate ,
2012-03-16 10:11:20 +04:00
} ;
EXPORT_SYMBOL_GPL ( clk_mux_ops ) ;
2013-07-23 03:49:18 +04:00
const struct clk_ops clk_mux_ro_ops = {
. get_parent = clk_mux_get_parent ,
} ;
EXPORT_SYMBOL_GPL ( clk_mux_ro_ops ) ;
2016-02-07 11:05:48 +03:00
struct clk_hw * clk_hw_register_mux_table ( struct device * dev , const char * name ,
2015-03-31 21:16:52 +03:00
const char * const * parent_names , u8 num_parents ,
unsigned long flags ,
2013-03-22 16:07:53 +04:00
void __iomem * reg , u8 shift , u32 mask ,
u8 clk_mux_flags , u32 * table , spinlock_t * lock )
2012-03-16 10:11:20 +04:00
{
struct clk_mux * mux ;
2016-02-07 11:05:48 +03:00
struct clk_hw * hw ;
2012-04-26 09:58:56 +04:00
struct clk_init_data init ;
2013-06-08 18:47:17 +04:00
u8 width = 0 ;
2016-02-07 11:05:48 +03:00
int ret ;
2013-06-08 18:47:17 +04:00
if ( clk_mux_flags & CLK_MUX_HIWORD_MASK ) {
width = fls ( mask ) - ffs ( mask ) + 1 ;
if ( width + shift > 16 ) {
pr_err ( " mux value exceeds LOWORD field \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
}
2012-03-16 10:11:20 +04:00
2012-03-27 04:51:03 +04:00
/* allocate the mux */
2017-09-26 18:30:06 +03:00
mux = kzalloc ( sizeof ( * mux ) , GFP_KERNEL ) ;
2017-09-26 18:23:04 +03:00
if ( ! mux )
2012-03-16 10:11:20 +04:00
return ERR_PTR ( - ENOMEM ) ;
2012-04-26 09:58:56 +04:00
init . name = name ;
2013-07-23 03:49:18 +04:00
if ( clk_mux_flags & CLK_MUX_READ_ONLY )
init . ops = & clk_mux_ro_ops ;
else
init . ops = & clk_mux_ops ;
2012-06-01 12:32:47 +04:00
init . flags = flags | CLK_IS_BASIC ;
2012-04-26 09:58:56 +04:00
init . parent_names = parent_names ;
init . num_parents = num_parents ;
2012-03-16 10:11:20 +04:00
/* struct clk_mux assignments */
mux - > reg = reg ;
mux - > shift = shift ;
2013-03-22 16:07:53 +04:00
mux - > mask = mask ;
2012-03-16 10:11:20 +04:00
mux - > flags = clk_mux_flags ;
mux - > lock = lock ;
2013-03-22 16:07:53 +04:00
mux - > table = table ;
2012-05-07 05:48:11 +04:00
mux - > hw . init = & init ;
2012-03-16 10:11:20 +04:00
2016-02-07 11:05:48 +03:00
hw = & mux - > hw ;
ret = clk_hw_register ( dev , hw ) ;
if ( ret ) {
2012-03-27 04:51:03 +04:00
kfree ( mux ) ;
2016-02-07 11:05:48 +03:00
hw = ERR_PTR ( ret ) ;
}
2012-03-27 04:51:03 +04:00
2016-02-07 11:05:48 +03:00
return hw ;
}
EXPORT_SYMBOL_GPL ( clk_hw_register_mux_table ) ;
struct clk * clk_register_mux_table ( struct device * dev , const char * name ,
const char * const * parent_names , u8 num_parents ,
unsigned long flags ,
void __iomem * reg , u8 shift , u32 mask ,
u8 clk_mux_flags , u32 * table , spinlock_t * lock )
{
struct clk_hw * hw ;
hw = clk_hw_register_mux_table ( dev , name , parent_names , num_parents ,
flags , reg , shift , mask , clk_mux_flags ,
table , lock ) ;
if ( IS_ERR ( hw ) )
return ERR_CAST ( hw ) ;
return hw - > clk ;
2012-03-16 10:11:20 +04:00
}
2013-08-16 06:06:29 +04:00
EXPORT_SYMBOL_GPL ( clk_register_mux_table ) ;
2013-03-22 16:07:53 +04:00
struct clk * clk_register_mux ( struct device * dev , const char * name ,
2015-03-31 21:16:52 +03:00
const char * const * parent_names , u8 num_parents ,
unsigned long flags ,
2013-03-22 16:07:53 +04:00
void __iomem * reg , u8 shift , u8 width ,
u8 clk_mux_flags , spinlock_t * lock )
{
u32 mask = BIT ( width ) - 1 ;
return clk_register_mux_table ( dev , name , parent_names , num_parents ,
flags , reg , shift , mask , clk_mux_flags ,
NULL , lock ) ;
}
2013-08-16 06:06:29 +04:00
EXPORT_SYMBOL_GPL ( clk_register_mux ) ;
2015-01-05 12:52:40 +03:00
2016-02-07 11:05:48 +03:00
struct clk_hw * clk_hw_register_mux ( struct device * dev , const char * name ,
const char * const * parent_names , u8 num_parents ,
unsigned long flags ,
void __iomem * reg , u8 shift , u8 width ,
u8 clk_mux_flags , spinlock_t * lock )
{
u32 mask = BIT ( width ) - 1 ;
return clk_hw_register_mux_table ( dev , name , parent_names , num_parents ,
flags , reg , shift , mask , clk_mux_flags ,
NULL , lock ) ;
}
EXPORT_SYMBOL_GPL ( clk_hw_register_mux ) ;
2015-01-05 12:52:40 +03:00
void clk_unregister_mux ( struct clk * clk )
{
struct clk_mux * mux ;
struct clk_hw * hw ;
hw = __clk_get_hw ( clk ) ;
if ( ! hw )
return ;
mux = to_clk_mux ( hw ) ;
clk_unregister ( clk ) ;
kfree ( mux ) ;
}
EXPORT_SYMBOL_GPL ( clk_unregister_mux ) ;
2016-02-07 11:05:48 +03:00
void clk_hw_unregister_mux ( struct clk_hw * hw )
{
struct clk_mux * mux ;
mux = to_clk_mux ( hw ) ;
clk_hw_unregister ( hw ) ;
kfree ( mux ) ;
}
EXPORT_SYMBOL_GPL ( clk_hw_unregister_mux ) ;