2013-07-04 13:35:46 +04:00
/*
* Copyright ( C ) 2013 Freescale Semiconductor , Inc .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/slab.h>
# include "clk.h"
# define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
/**
* struct clk_fixup_mux - imx integer fixup multiplexer clock
* @ mux : the parent class
* @ ops : pointer to clk_ops of parent class
* @ fixup : a hook to fixup the write value
*
* The imx fixup multiplexer clock is a subclass of basic clk_mux
* with an addtional fixup hook .
*/
struct clk_fixup_mux {
struct clk_mux mux ;
const struct clk_ops * ops ;
void ( * fixup ) ( u32 * val ) ;
} ;
static inline struct clk_fixup_mux * to_clk_fixup_mux ( struct clk_hw * hw )
{
struct clk_mux * mux = to_clk_mux ( hw ) ;
return container_of ( mux , struct clk_fixup_mux , mux ) ;
}
static u8 clk_fixup_mux_get_parent ( struct clk_hw * hw )
{
struct clk_fixup_mux * fixup_mux = to_clk_fixup_mux ( hw ) ;
return fixup_mux - > ops - > get_parent ( & fixup_mux - > mux . hw ) ;
}
static int clk_fixup_mux_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_fixup_mux * fixup_mux = to_clk_fixup_mux ( hw ) ;
struct clk_mux * mux = to_clk_mux ( hw ) ;
unsigned long flags = 0 ;
u32 val ;
spin_lock_irqsave ( mux - > lock , flags ) ;
val = readl ( mux - > reg ) ;
val & = ~ ( mux - > mask < < mux - > shift ) ;
val | = index < < mux - > shift ;
fixup_mux - > fixup ( & val ) ;
writel ( val , mux - > reg ) ;
spin_unlock_irqrestore ( mux - > lock , flags ) ;
return 0 ;
}
static const struct clk_ops clk_fixup_mux_ops = {
. get_parent = clk_fixup_mux_get_parent ,
. set_parent = clk_fixup_mux_set_parent ,
} ;
struct clk * imx_clk_fixup_mux ( const char * name , void __iomem * reg ,
u8 shift , u8 width , const char * * parents ,
int num_parents , void ( * fixup ) ( u32 * val ) )
{
struct clk_fixup_mux * fixup_mux ;
struct clk * clk ;
struct clk_init_data init ;
if ( ! fixup )
return ERR_PTR ( - EINVAL ) ;
fixup_mux = kzalloc ( sizeof ( * fixup_mux ) , GFP_KERNEL ) ;
if ( ! fixup_mux )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & clk_fixup_mux_ops ;
init . parent_names = parents ;
init . num_parents = num_parents ;
2013-09-04 16:49:04 +04:00
init . flags = 0 ;
2013-07-04 13:35:46 +04:00
fixup_mux - > mux . reg = reg ;
fixup_mux - > mux . shift = shift ;
fixup_mux - > mux . mask = BIT ( width ) - 1 ;
fixup_mux - > mux . lock = & imx_ccm_lock ;
fixup_mux - > mux . hw . init = & init ;
fixup_mux - > ops = & clk_mux_ops ;
fixup_mux - > fixup = fixup ;
clk = clk_register ( NULL , & fixup_mux - > mux . hw ) ;
if ( IS_ERR ( clk ) )
kfree ( fixup_mux ) ;
return clk ;
}