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.h>
# 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
*/
# define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
static u8 clk_mux_get_parent ( struct clk_hw * hw )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
u32 val ;
/*
* FIXME need a mux - specific flag to determine if val is bitwise or numeric
* e . g . sys_clkin_ck ' s clksel field is 3 bits wide , but ranges from 0x1
* to 0x7 ( index starts at one )
* OTOH , pmd_trace_clk_mux_ck uses a separate bit for each clock , so
* val = 0x4 really means " bit 2, index starts at bit 0 "
*/
val = readl ( mux - > reg ) > > mux - > shift ;
val & = ( 1 < < mux - > width ) - 1 ;
if ( val & & ( mux - > flags & CLK_MUX_INDEX_BIT ) )
val = ffs ( val ) - 1 ;
if ( val & & ( mux - > flags & CLK_MUX_INDEX_ONE ) )
val - - ;
if ( val > = __clk_get_num_parents ( hw - > clk ) )
return - EINVAL ;
return val ;
}
static int clk_mux_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
u32 val ;
unsigned long flags = 0 ;
if ( mux - > flags & CLK_MUX_INDEX_BIT )
index = ( 1 < < ffs ( index ) ) ;
if ( mux - > flags & CLK_MUX_INDEX_ONE )
index + + ;
if ( mux - > lock )
spin_lock_irqsave ( mux - > lock , flags ) ;
val = readl ( mux - > reg ) ;
val & = ~ ( ( ( 1 < < mux - > width ) - 1 ) < < mux - > shift ) ;
val | = index < < mux - > shift ;
writel ( val , mux - > reg ) ;
if ( mux - > lock )
spin_unlock_irqrestore ( mux - > lock , flags ) ;
return 0 ;
}
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 ,
} ;
EXPORT_SYMBOL_GPL ( clk_mux_ops ) ;
struct clk * clk_register_mux ( struct device * dev , const char * name ,
2012-03-22 00:01:20 +04:00
const char * * parent_names , u8 num_parents , unsigned long flags ,
2012-03-16 10:11:20 +04:00
void __iomem * reg , u8 shift , u8 width ,
u8 clk_mux_flags , spinlock_t * lock )
{
struct clk_mux * mux ;
2012-03-27 04:51:03 +04:00
struct clk * clk ;
2012-04-26 09:58:56 +04:00
struct clk_init_data init ;
2012-03-16 10:11:20 +04:00
2012-03-27 04:51:03 +04:00
/* allocate the mux */
2012-03-27 11:23:20 +04:00
mux = kzalloc ( sizeof ( struct clk_mux ) , GFP_KERNEL ) ;
2012-03-16 10:11:20 +04:00
if ( ! mux ) {
pr_err ( " %s: could not allocate mux clk \n " , __func__ ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2012-04-26 09:58:56 +04:00
init . name = name ;
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 ;
mux - > width = width ;
mux - > flags = clk_mux_flags ;
mux - > lock = lock ;
2012-05-07 05:48:11 +04:00
mux - > hw . init = & init ;
2012-03-16 10:11:20 +04:00
2012-04-26 09:58:56 +04:00
clk = clk_register ( dev , & mux - > hw ) ;
2012-03-27 04:51:03 +04:00
if ( IS_ERR ( clk ) )
kfree ( mux ) ;
return clk ;
2012-03-16 10:11:20 +04:00
}