2015-05-24 16:11:35 +01:00
/*
* Ingenic SoC CGU driver
*
* Copyright ( c ) 2013 - 2015 Imagination Technologies
* Author : Paul Burton < paul . burton @ imgtec . 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/bitops.h>
2015-05-01 16:09:33 -07:00
# include <linux/clk.h>
2015-05-24 16:11:35 +01:00
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/delay.h>
# include <linux/math64.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include "cgu.h"
# define MHZ (1000 * 1000)
/**
* ingenic_cgu_gate_get ( ) - get the value of clock gate register bit
* @ cgu : reference to the CGU whose registers should be read
* @ info : info struct describing the gate bit
*
* Retrieves the state of the clock gate bit described by info . The
* caller must hold cgu - > lock .
*
* Return : true if the gate bit is set , else false .
*/
static inline bool
ingenic_cgu_gate_get ( struct ingenic_cgu * cgu ,
const struct ingenic_cgu_gate_info * info )
{
return readl ( cgu - > base + info - > reg ) & BIT ( info - > bit ) ;
}
/**
* ingenic_cgu_gate_set ( ) - set the value of clock gate register bit
* @ cgu : reference to the CGU whose registers should be modified
* @ info : info struct describing the gate bit
* @ val : non - zero to gate a clock , otherwise zero
*
* Sets the given gate bit in order to gate or ungate a clock .
*
* The caller must hold cgu - > lock .
*/
static inline void
ingenic_cgu_gate_set ( struct ingenic_cgu * cgu ,
const struct ingenic_cgu_gate_info * info , bool val )
{
u32 clkgr = readl ( cgu - > base + info - > reg ) ;
if ( val )
clkgr | = BIT ( info - > bit ) ;
else
clkgr & = ~ BIT ( info - > bit ) ;
writel ( clkgr , cgu - > base + info - > reg ) ;
}
/*
* PLL operations
*/
static unsigned long
ingenic_pll_recalc_rate ( struct clk_hw * hw , unsigned long parent_rate )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
const struct ingenic_cgu_pll_info * pll_info ;
unsigned m , n , od_enc , od ;
bool bypass , enable ;
unsigned long flags ;
u32 ctl ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
BUG_ON ( clk_info - > type ! = CGU_CLK_PLL ) ;
pll_info = & clk_info - > pll ;
spin_lock_irqsave ( & cgu - > lock , flags ) ;
ctl = readl ( cgu - > base + pll_info - > reg ) ;
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
m = ( ctl > > pll_info - > m_shift ) & GENMASK ( pll_info - > m_bits - 1 , 0 ) ;
m + = pll_info - > m_offset ;
n = ( ctl > > pll_info - > n_shift ) & GENMASK ( pll_info - > n_bits - 1 , 0 ) ;
n + = pll_info - > n_offset ;
od_enc = ctl > > pll_info - > od_shift ;
od_enc & = GENMASK ( pll_info - > od_bits - 1 , 0 ) ;
bypass = ! ! ( ctl & BIT ( pll_info - > bypass_bit ) ) ;
enable = ! ! ( ctl & BIT ( pll_info - > enable_bit ) ) ;
if ( bypass )
return parent_rate ;
if ( ! enable )
return 0 ;
for ( od = 0 ; od < pll_info - > od_max ; od + + ) {
if ( pll_info - > od_encoding [ od ] = = od_enc )
break ;
}
BUG_ON ( od = = pll_info - > od_max ) ;
od + + ;
return div_u64 ( ( u64 ) parent_rate * m , n * od ) ;
}
static unsigned long
ingenic_pll_calc ( const struct ingenic_cgu_clk_info * clk_info ,
unsigned long rate , unsigned long parent_rate ,
unsigned * pm , unsigned * pn , unsigned * pod )
{
const struct ingenic_cgu_pll_info * pll_info ;
unsigned m , n , od ;
pll_info = & clk_info - > pll ;
od = 1 ;
/*
* The frequency after the input divider must be between 10 and 50 MHz .
* The highest divider yields the best resolution .
*/
n = parent_rate / ( 10 * MHZ ) ;
n = min_t ( unsigned , n , 1 < < clk_info - > pll . n_bits ) ;
n = max_t ( unsigned , n , pll_info - > n_offset ) ;
m = ( rate / MHZ ) * od * n / ( parent_rate / MHZ ) ;
m = min_t ( unsigned , m , 1 < < clk_info - > pll . m_bits ) ;
m = max_t ( unsigned , m , pll_info - > m_offset ) ;
if ( pm )
* pm = m ;
if ( pn )
* pn = n ;
if ( pod )
* pod = od ;
return div_u64 ( ( u64 ) parent_rate * m , n * od ) ;
}
static long
ingenic_pll_round_rate ( struct clk_hw * hw , unsigned long req_rate ,
unsigned long * prate )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
BUG_ON ( clk_info - > type ! = CGU_CLK_PLL ) ;
return ingenic_pll_calc ( clk_info , req_rate , * prate , NULL , NULL , NULL ) ;
}
static int
ingenic_pll_set_rate ( struct clk_hw * hw , unsigned long req_rate ,
unsigned long parent_rate )
{
const unsigned timeout = 100 ;
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
const struct ingenic_cgu_pll_info * pll_info ;
unsigned long rate , flags ;
unsigned m , n , od , i ;
u32 ctl ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
BUG_ON ( clk_info - > type ! = CGU_CLK_PLL ) ;
pll_info = & clk_info - > pll ;
rate = ingenic_pll_calc ( clk_info , req_rate , parent_rate ,
& m , & n , & od ) ;
if ( rate ! = req_rate )
pr_info ( " ingenic-cgu: request '%s' rate %luHz, actual %luHz \n " ,
clk_info - > name , req_rate , rate ) ;
spin_lock_irqsave ( & cgu - > lock , flags ) ;
ctl = readl ( cgu - > base + pll_info - > reg ) ;
ctl & = ~ ( GENMASK ( pll_info - > m_bits - 1 , 0 ) < < pll_info - > m_shift ) ;
ctl | = ( m - pll_info - > m_offset ) < < pll_info - > m_shift ;
ctl & = ~ ( GENMASK ( pll_info - > n_bits - 1 , 0 ) < < pll_info - > n_shift ) ;
ctl | = ( n - pll_info - > n_offset ) < < pll_info - > n_shift ;
ctl & = ~ ( GENMASK ( pll_info - > od_bits - 1 , 0 ) < < pll_info - > od_shift ) ;
ctl | = pll_info - > od_encoding [ od - 1 ] < < pll_info - > od_shift ;
ctl & = ~ BIT ( pll_info - > bypass_bit ) ;
ctl | = BIT ( pll_info - > enable_bit ) ;
writel ( ctl , cgu - > base + pll_info - > reg ) ;
/* wait for the PLL to stabilise */
for ( i = 0 ; i < timeout ; i + + ) {
ctl = readl ( cgu - > base + pll_info - > reg ) ;
if ( ctl & BIT ( pll_info - > stable_bit ) )
break ;
mdelay ( 1 ) ;
}
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
if ( i = = timeout )
return - EBUSY ;
return 0 ;
}
static const struct clk_ops ingenic_pll_ops = {
. recalc_rate = ingenic_pll_recalc_rate ,
. round_rate = ingenic_pll_round_rate ,
. set_rate = ingenic_pll_set_rate ,
} ;
/*
* Operations for all non - PLL clocks
*/
static u8 ingenic_clk_get_parent ( struct clk_hw * hw )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
u32 reg ;
u8 i , hw_idx , idx = 0 ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_MUX ) {
reg = readl ( cgu - > base + clk_info - > mux . reg ) ;
hw_idx = ( reg > > clk_info - > mux . shift ) &
GENMASK ( clk_info - > mux . bits - 1 , 0 ) ;
/*
* Convert the hardware index to the parent index by skipping
* over any - 1 ' s in the parents array .
*/
for ( i = 0 ; i < hw_idx ; i + + ) {
if ( clk_info - > parents [ i ] ! = - 1 )
idx + + ;
}
}
return idx ;
}
static int ingenic_clk_set_parent ( struct clk_hw * hw , u8 idx )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
unsigned long flags ;
u8 curr_idx , hw_idx , num_poss ;
u32 reg , mask ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_MUX ) {
/*
* Convert the parent index to the hardware index by adding
* 1 for any - 1 in the parents array preceding the given
* index . That is , we want the index of idx ' th entry in
* clk_info - > parents which does not equal - 1.
*/
hw_idx = curr_idx = 0 ;
num_poss = 1 < < clk_info - > mux . bits ;
for ( ; hw_idx < num_poss ; hw_idx + + ) {
if ( clk_info - > parents [ hw_idx ] = = - 1 )
continue ;
if ( curr_idx = = idx )
break ;
curr_idx + + ;
}
/* idx should always be a valid parent */
BUG_ON ( curr_idx ! = idx ) ;
mask = GENMASK ( clk_info - > mux . bits - 1 , 0 ) ;
mask < < = clk_info - > mux . shift ;
spin_lock_irqsave ( & cgu - > lock , flags ) ;
/* write the register */
reg = readl ( cgu - > base + clk_info - > mux . reg ) ;
reg & = ~ mask ;
reg | = hw_idx < < clk_info - > mux . shift ;
writel ( reg , cgu - > base + clk_info - > mux . reg ) ;
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
return 0 ;
}
return idx ? - EINVAL : 0 ;
}
static unsigned long
ingenic_clk_recalc_rate ( struct clk_hw * hw , unsigned long parent_rate )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
unsigned long rate = parent_rate ;
u32 div_reg , div ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_DIV ) {
div_reg = readl ( cgu - > base + clk_info - > div . reg ) ;
div = ( div_reg > > clk_info - > div . shift ) &
GENMASK ( clk_info - > div . bits - 1 , 0 ) ;
div + = 1 ;
rate / = div ;
}
return rate ;
}
static unsigned
ingenic_clk_calc_div ( const struct ingenic_cgu_clk_info * clk_info ,
unsigned long parent_rate , unsigned long req_rate )
{
unsigned div ;
/* calculate the divide */
div = DIV_ROUND_UP ( parent_rate , req_rate ) ;
/* and impose hardware constraints */
div = min_t ( unsigned , div , 1 < < clk_info - > div . bits ) ;
div = max_t ( unsigned , div , 1 ) ;
return div ;
}
static long
ingenic_clk_round_rate ( struct clk_hw * hw , unsigned long req_rate ,
unsigned long * parent_rate )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
long rate = * parent_rate ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_DIV )
rate / = ingenic_clk_calc_div ( clk_info , * parent_rate , req_rate ) ;
else if ( clk_info - > type & CGU_CLK_FIXDIV )
rate / = clk_info - > fixdiv . div ;
return rate ;
}
static int
ingenic_clk_set_rate ( struct clk_hw * hw , unsigned long req_rate ,
unsigned long parent_rate )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
const unsigned timeout = 100 ;
unsigned long rate , flags ;
unsigned div , i ;
u32 reg , mask ;
int ret = 0 ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_DIV ) {
div = ingenic_clk_calc_div ( clk_info , parent_rate , req_rate ) ;
rate = parent_rate / div ;
if ( rate ! = req_rate )
return - EINVAL ;
spin_lock_irqsave ( & cgu - > lock , flags ) ;
reg = readl ( cgu - > base + clk_info - > div . reg ) ;
/* update the divide */
mask = GENMASK ( clk_info - > div . bits - 1 , 0 ) ;
reg & = ~ ( mask < < clk_info - > div . shift ) ;
reg | = ( div - 1 ) < < clk_info - > div . shift ;
/* clear the stop bit */
if ( clk_info - > div . stop_bit ! = - 1 )
reg & = ~ BIT ( clk_info - > div . stop_bit ) ;
/* set the change enable bit */
if ( clk_info - > div . ce_bit ! = - 1 )
reg | = BIT ( clk_info - > div . ce_bit ) ;
/* update the hardware */
writel ( reg , cgu - > base + clk_info - > div . reg ) ;
/* wait for the change to take effect */
if ( clk_info - > div . busy_bit ! = - 1 ) {
for ( i = 0 ; i < timeout ; i + + ) {
reg = readl ( cgu - > base + clk_info - > div . reg ) ;
if ( ! ( reg & BIT ( clk_info - > div . busy_bit ) ) )
break ;
mdelay ( 1 ) ;
}
if ( i = = timeout )
ret = - EBUSY ;
}
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
return ret ;
}
return - EINVAL ;
}
static int ingenic_clk_enable ( struct clk_hw * hw )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
unsigned long flags ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_GATE ) {
/* ungate the clock */
spin_lock_irqsave ( & cgu - > lock , flags ) ;
ingenic_cgu_gate_set ( cgu , & clk_info - > gate , false ) ;
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
}
return 0 ;
}
static void ingenic_clk_disable ( struct clk_hw * hw )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
unsigned long flags ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_GATE ) {
/* gate the clock */
spin_lock_irqsave ( & cgu - > lock , flags ) ;
ingenic_cgu_gate_set ( cgu , & clk_info - > gate , true ) ;
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
}
}
static int ingenic_clk_is_enabled ( struct clk_hw * hw )
{
struct ingenic_clk * ingenic_clk = to_ingenic_clk ( hw ) ;
struct ingenic_cgu * cgu = ingenic_clk - > cgu ;
const struct ingenic_cgu_clk_info * clk_info ;
unsigned long flags ;
int enabled = 1 ;
clk_info = & cgu - > clock_info [ ingenic_clk - > idx ] ;
if ( clk_info - > type & CGU_CLK_GATE ) {
spin_lock_irqsave ( & cgu - > lock , flags ) ;
enabled = ! ingenic_cgu_gate_get ( cgu , & clk_info - > gate ) ;
spin_unlock_irqrestore ( & cgu - > lock , flags ) ;
}
return enabled ;
}
static const struct clk_ops ingenic_clk_ops = {
. get_parent = ingenic_clk_get_parent ,
. set_parent = ingenic_clk_set_parent ,
. recalc_rate = ingenic_clk_recalc_rate ,
. round_rate = ingenic_clk_round_rate ,
. set_rate = ingenic_clk_set_rate ,
. enable = ingenic_clk_enable ,
. disable = ingenic_clk_disable ,
. is_enabled = ingenic_clk_is_enabled ,
} ;
/*
* Setup functions .
*/
static int ingenic_register_clock ( struct ingenic_cgu * cgu , unsigned idx )
{
const struct ingenic_cgu_clk_info * clk_info = & cgu - > clock_info [ idx ] ;
struct clk_init_data clk_init ;
struct ingenic_clk * ingenic_clk = NULL ;
struct clk * clk , * parent ;
const char * parent_names [ 4 ] ;
unsigned caps , i , num_possible ;
int err = - EINVAL ;
BUILD_BUG_ON ( ARRAY_SIZE ( clk_info - > parents ) > ARRAY_SIZE ( parent_names ) ) ;
if ( clk_info - > type = = CGU_CLK_EXT ) {
clk = of_clk_get_by_name ( cgu - > np , clk_info - > name ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: no external clock '%s' provided \n " ,
__func__ , clk_info - > name ) ;
err = - ENODEV ;
goto out ;
}
err = clk_register_clkdev ( clk , clk_info - > name , NULL ) ;
if ( err ) {
clk_put ( clk ) ;
goto out ;
}
cgu - > clocks . clks [ idx ] = clk ;
return 0 ;
}
if ( ! clk_info - > type ) {
pr_err ( " %s: no clock type specified for '%s' \n " , __func__ ,
clk_info - > name ) ;
goto out ;
}
ingenic_clk = kzalloc ( sizeof ( * ingenic_clk ) , GFP_KERNEL ) ;
if ( ! ingenic_clk ) {
err = - ENOMEM ;
goto out ;
}
ingenic_clk - > hw . init = & clk_init ;
ingenic_clk - > cgu = cgu ;
ingenic_clk - > idx = idx ;
clk_init . name = clk_info - > name ;
clk_init . flags = 0 ;
clk_init . parent_names = parent_names ;
caps = clk_info - > type ;
if ( caps & ( CGU_CLK_MUX | CGU_CLK_CUSTOM ) ) {
clk_init . num_parents = 0 ;
if ( caps & CGU_CLK_MUX )
num_possible = 1 < < clk_info - > mux . bits ;
else
num_possible = ARRAY_SIZE ( clk_info - > parents ) ;
for ( i = 0 ; i < num_possible ; i + + ) {
if ( clk_info - > parents [ i ] = = - 1 )
continue ;
parent = cgu - > clocks . clks [ clk_info - > parents [ i ] ] ;
parent_names [ clk_init . num_parents ] =
__clk_get_name ( parent ) ;
clk_init . num_parents + + ;
}
BUG_ON ( ! clk_init . num_parents ) ;
BUG_ON ( clk_init . num_parents > ARRAY_SIZE ( parent_names ) ) ;
} else {
BUG_ON ( clk_info - > parents [ 0 ] = = - 1 ) ;
clk_init . num_parents = 1 ;
parent = cgu - > clocks . clks [ clk_info - > parents [ 0 ] ] ;
parent_names [ 0 ] = __clk_get_name ( parent ) ;
}
if ( caps & CGU_CLK_CUSTOM ) {
clk_init . ops = clk_info - > custom . clk_ops ;
caps & = ~ CGU_CLK_CUSTOM ;
if ( caps ) {
pr_err ( " %s: custom clock may not be combined with type 0x%x \n " ,
__func__ , caps ) ;
goto out ;
}
} else if ( caps & CGU_CLK_PLL ) {
clk_init . ops = & ingenic_pll_ops ;
caps & = ~ CGU_CLK_PLL ;
if ( caps ) {
pr_err ( " %s: PLL may not be combined with type 0x%x \n " ,
__func__ , caps ) ;
goto out ;
}
} else {
clk_init . ops = & ingenic_clk_ops ;
}
/* nothing to do for gates or fixed dividers */
caps & = ~ ( CGU_CLK_GATE | CGU_CLK_FIXDIV ) ;
if ( caps & CGU_CLK_MUX ) {
if ( ! ( caps & CGU_CLK_MUX_GLITCHFREE ) )
clk_init . flags | = CLK_SET_PARENT_GATE ;
caps & = ~ ( CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE ) ;
}
if ( caps & CGU_CLK_DIV ) {
caps & = ~ CGU_CLK_DIV ;
} else {
/* pass rate changes to the parent clock */
clk_init . flags | = CLK_SET_RATE_PARENT ;
}
if ( caps ) {
pr_err ( " %s: unknown clock type 0x%x \n " , __func__ , caps ) ;
goto out ;
}
clk = clk_register ( NULL , & ingenic_clk - > hw ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " %s: failed to register clock '%s' \n " , __func__ ,
clk_info - > name ) ;
err = PTR_ERR ( clk ) ;
goto out ;
}
err = clk_register_clkdev ( clk , clk_info - > name , NULL ) ;
if ( err )
goto out ;
cgu - > clocks . clks [ idx ] = clk ;
out :
if ( err )
kfree ( ingenic_clk ) ;
return err ;
}
struct ingenic_cgu *
ingenic_cgu_new ( const struct ingenic_cgu_clk_info * clock_info ,
unsigned num_clocks , struct device_node * np )
{
struct ingenic_cgu * cgu ;
cgu = kzalloc ( sizeof ( * cgu ) , GFP_KERNEL ) ;
if ( ! cgu )
goto err_out ;
cgu - > base = of_iomap ( np , 0 ) ;
if ( ! cgu - > base ) {
pr_err ( " %s: failed to map CGU registers \n " , __func__ ) ;
goto err_out_free ;
}
cgu - > np = np ;
cgu - > clock_info = clock_info ;
cgu - > clocks . clk_num = num_clocks ;
spin_lock_init ( & cgu - > lock ) ;
return cgu ;
err_out_free :
kfree ( cgu ) ;
err_out :
return NULL ;
}
int ingenic_cgu_register_clocks ( struct ingenic_cgu * cgu )
{
unsigned i ;
int err ;
cgu - > clocks . clks = kcalloc ( cgu - > clocks . clk_num , sizeof ( struct clk * ) ,
GFP_KERNEL ) ;
if ( ! cgu - > clocks . clks ) {
err = - ENOMEM ;
goto err_out ;
}
for ( i = 0 ; i < cgu - > clocks . clk_num ; i + + ) {
err = ingenic_register_clock ( cgu , i ) ;
if ( err )
goto err_out_unregister ;
}
err = of_clk_add_provider ( cgu - > np , of_clk_src_onecell_get ,
& cgu - > clocks ) ;
if ( err )
goto err_out_unregister ;
return 0 ;
err_out_unregister :
for ( i = 0 ; i < cgu - > clocks . clk_num ; i + + ) {
if ( ! cgu - > clocks . clks [ i ] )
continue ;
if ( cgu - > clock_info [ i ] . type & CGU_CLK_EXT )
clk_put ( cgu - > clocks . clks [ i ] ) ;
else
clk_unregister ( cgu - > clocks . clks [ i ] ) ;
}
kfree ( cgu - > clocks . clks ) ;
err_out :
return err ;
}