aboutsummaryrefslogblamecommitdiffstats
path: root/arch/arm/plat-spear/clock.c
blob: 6fa474cb398e1c33da5a24c3b528fbf72875ff49 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                  
                      
                          




                           



                                    


                         
 
                                                      


                                              








































                                                   






























                                                                              



                                  






























































                                                                        












                                                                          
                                               
                                 
                                                    



















                                                                               
                                               
                            
































                                                                           
                            
                               

                                

                              
 
                                                             













                                                                      
                                                                           



                                                    


                                                        



                              








                                                       


















                                                                 


                            


                                                 
                        

                            
                            
                       
                      











                                                    
                                                 
                                                                               

                                                              











                                                                            
         
 

                                                    





                                      




                                                 


                                                                    
  
                                   
   
                                                  
 
















                                                                                
 


                                                                   


         













                                                                               
 

                                             
 

                               
 














                                                                           
         









                                                            
 
                     

 





                                                                       
   
                                                         
 














                                                                               
 






                                                    
 



















                                                                               










                                             
                                   


                                                          
 

                                                                       


                                     
                                                                              
                       
                                                                               



                                               

                                                                  

                                   

                                                                  


                           


                               
                                                                    






















































                                                                           


                                                          
                                   


                                                          
 

                                                             



                               
                                                         







































                                                                           











                                                               
                                   

                                                          
                                                
 
                                       
 



                                                    
 


                                                        
 





















































                                                                             






                                                           
                                   
 
                                                          
                                  
 




                                                                     
 

                               

                                                         












































































































                                                                               


  
                                                                          

                
                                  
 
                                                                              
 
                                               
                 









                                                                       




















                                                                                
 
 




                             

































































































                                                                            
/*
 * arch/arm/plat-spear/clock.c
 *
 * Clock framework for SPEAr platform
 *
 * Copyright (C) 2009 ST Microelectronics
 * Viresh Kumar<viresh.kumar@st.com>
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#include <linux/bug.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <plat/clock.h>

static DEFINE_SPINLOCK(clocks_lock);
static LIST_HEAD(root_clks);
#ifdef CONFIG_DEBUG_FS
static LIST_HEAD(clocks);
#endif

static void propagate_rate(struct clk *, int on_init);
#ifdef CONFIG_DEBUG_FS
static int clk_debugfs_reparent(struct clk *);
#endif

static int generic_clk_enable(struct clk *clk)
{
	unsigned int val;

	if (!clk->en_reg)
		return -EFAULT;

	val = readl(clk->en_reg);
	if (unlikely(clk->flags & RESET_TO_ENABLE))
		val &= ~(1 << clk->en_reg_bit);
	else
		val |= 1 << clk->en_reg_bit;

	writel(val, clk->en_reg);

	return 0;
}

static void generic_clk_disable(struct clk *clk)
{
	unsigned int val;

	if (!clk->en_reg)
		return;

	val = readl(clk->en_reg);
	if (unlikely(clk->flags & RESET_TO_ENABLE))
		val |= 1 << clk->en_reg_bit;
	else
		val &= ~(1 << clk->en_reg_bit);

	writel(val, clk->en_reg);
}

/* generic clk ops */
static struct clkops generic_clkops = {
	.enable = generic_clk_enable,
	.disable = generic_clk_disable,
};

/* returns current programmed clocks clock info structure */
static struct pclk_info *pclk_info_get(struct clk *clk)
{
	unsigned int val, i;
	struct pclk_info *info = NULL;

	val = (readl(clk->pclk_sel->pclk_sel_reg) >> clk->pclk_sel_shift)
		& clk->pclk_sel->pclk_sel_mask;

	for (i = 0; i < clk->pclk_sel->pclk_count; i++) {
		if (clk->pclk_sel->pclk_info[i].pclk_val == val)
			info = &clk->pclk_sel->pclk_info[i];
	}

	return info;
}

/*
 * Set Update pclk, and pclk_info of clk and add clock sibling node to current
 * parents children list
 */
static void clk_reparent(struct clk *clk, struct pclk_info *pclk_info)
{
	unsigned long flags;

	spin_lock_irqsave(&clocks_lock, flags);
	list_del(&clk->sibling);
	list_add(&clk->sibling, &pclk_info->pclk->children);

	clk->pclk = pclk_info->pclk;
	spin_unlock_irqrestore(&clocks_lock, flags);

#ifdef CONFIG_DEBUG_FS
	clk_debugfs_reparent(clk);
#endif
}

static void do_clk_disable(struct clk *clk)
{
	if (!clk)
		return;

	if (!clk->usage_count) {
		WARN_ON(1);
		return;
	}

	clk->usage_count--;

	if (clk->usage_count == 0) {
		/*
		 * Surely, there are no active childrens or direct users
		 * of this clock
		 */
		if (clk->pclk)
			do_clk_disable(clk->pclk);

		if (clk->ops && clk->ops->disable)
			clk->ops->disable(clk);
	}
}

static int do_clk_enable(struct clk *clk)
{
	int ret = 0;

	if (!clk)
		return -EFAULT;

	if (clk->usage_count == 0) {
		if (clk->pclk) {
			ret = do_clk_enable(clk->pclk);
			if (ret)
				goto err;
		}
		if (clk->ops && clk->ops->enable) {
			ret = clk->ops->enable(clk);
			if (ret) {
				if (clk->pclk)
					do_clk_disable(clk->pclk);
				goto err;
			}
		}
		/*
		 * Since the clock is going to be used for the first
		 * time please reclac
		 */
		if (clk->recalc) {
			ret = clk->recalc(clk);
			if (ret)
				goto err;
		}
	}
	clk->usage_count++;
err:
	return ret;
}

/*
 * clk_enable - inform the system when the clock source should be running.
 * @clk: clock source
 *
 * If the clock can not be enabled/disabled, this should return success.
 *
 * Returns success (0) or negative errno.
 */
int clk_enable(struct clk *clk)
{
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(&clocks_lock, flags);
	ret = do_clk_enable(clk);
	spin_unlock_irqrestore(&clocks_lock, flags);
	return ret;
}
EXPORT_SYMBOL(clk_enable);

/*
 * clk_disable - inform the system when the clock source is no longer required.
 * @clk: clock source
 *
 * Inform the system that a clock source is no longer required by
 * a driver and may be shut down.
 *
 * Implementation detail: if the clock source is shared between
 * multiple drivers, clk_enable() calls must be balanced by the
 * same number of clk_disable() calls for the clock source to be
 * disabled.
 */
void clk_disable(struct clk *clk)
{
	unsigned long flags;

	spin_lock_irqsave(&clocks_lock, flags);
	do_clk_disable(clk);
	spin_unlock_irqrestore(&clocks_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

/**
 * clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
 *		 This is only valid once the clock source has been enabled.
 * @clk: clock source
 */
unsigned long clk_get_rate(struct clk *clk)
{
	unsigned long flags, rate;

	spin_lock_irqsave(&clocks_lock, flags);
	rate = clk->rate;
	spin_unlock_irqrestore(&clocks_lock, flags);

	return rate;
}
EXPORT_SYMBOL(clk_get_rate);

/**
 * clk_set_parent - set the parent clock source for this clock
 * @clk: clock source
 * @parent: parent clock source
 *
 * Returns success (0) or negative errno.
 */
int clk_set_parent(struct clk *clk, struct clk *parent)
{
	int i, found = 0, val = 0;
	unsigned long flags;

	if (!clk || !parent)
		return -EFAULT;
	if (clk->pclk == parent)
		return 0;
	if (!clk->pclk_sel)
		return -EPERM;

	/* check if requested parent is in clk parent list */
	for (i = 0; i < clk->pclk_sel->pclk_count; i++) {
		if (clk->pclk_sel->pclk_info[i].pclk == parent) {
			found = 1;
			break;
		}
	}

	if (!found)
		return -EINVAL;

	spin_lock_irqsave(&clocks_lock, flags);
	/* reflect parent change in hardware */
	val = readl(clk->pclk_sel->pclk_sel_reg);
	val &= ~(clk->pclk_sel->pclk_sel_mask << clk->pclk_sel_shift);
	val |= clk->pclk_sel->pclk_info[i].pclk_val << clk->pclk_sel_shift;
	writel(val, clk->pclk_sel->pclk_sel_reg);
	spin_unlock_irqrestore(&clocks_lock, flags);

	/* reflect parent change in software */
	clk_reparent(clk, &clk->pclk_sel->pclk_info[i]);

	propagate_rate(clk, 0);
	return 0;
}
EXPORT_SYMBOL(clk_set_parent);

/**
 * clk_set_rate - set the clock rate for a clock source
 * @clk: clock source
 * @rate: desired clock rate in Hz
 *
 * Returns success (0) or negative errno.
 */
int clk_set_rate(struct clk *clk, unsigned long rate)
{
	unsigned long flags;
	int ret = -EINVAL;

	if (!clk || !rate)
		return -EFAULT;

	if (clk->set_rate) {
		spin_lock_irqsave(&clocks_lock, flags);
		ret = clk->set_rate(clk, rate);
		if (!ret)
			/* if successful -> propagate */
			propagate_rate(clk, 0);
		spin_unlock_irqrestore(&clocks_lock, flags);
	} else if (clk->pclk) {
		u32 mult = clk->div_factor ? clk->div_factor : 1;
		ret = clk_set_rate(clk->pclk, mult * rate);
	}

	return ret;
}
EXPORT_SYMBOL(clk_set_rate);

/* registers clock in platform clock framework */
void clk_register(struct clk_lookup *cl)
{
	struct clk *clk;
	unsigned long flags;

	if (!cl || !cl->clk)
		return;
	clk = cl->clk;

	spin_lock_irqsave(&clocks_lock, flags);

	INIT_LIST_HEAD(&clk->children);
	if (clk->flags & ALWAYS_ENABLED)
		clk->ops = NULL;
	else if (!clk->ops)
		clk->ops = &generic_clkops;

	/* root clock don't have any parents */
	if (!clk->pclk && !clk->pclk_sel) {
		list_add(&clk->sibling, &root_clks);
	} else if (clk->pclk && !clk->pclk_sel) {
		/* add clocks with only one parent to parent's children list */
		list_add(&clk->sibling, &clk->pclk->children);
	} else {
		/* clocks with more than one parent */
		struct pclk_info *pclk_info;

		pclk_info = pclk_info_get(clk);
		if (!pclk_info) {
			pr_err("CLKDEV: invalid pclk info of clk with"
					" %s dev_id and %s con_id\n",
					cl->dev_id, cl->con_id);
		} else {
			clk->pclk = pclk_info->pclk;
			list_add(&clk->sibling, &pclk_info->pclk->children);
		}
	}

	spin_unlock_irqrestore(&clocks_lock, flags);

	/* debugfs specific */
#ifdef CONFIG_DEBUG_FS
	list_add(&clk->node, &clocks);
	clk->cl = cl;
#endif

	/* add clock to arm clockdev framework */
	clkdev_add(cl);
}

/**
 * propagate_rate - recalculate and propagate all clocks to children
 * @pclk: parent clock required to be propogated
 * @on_init: flag for enabling clocks which are ENABLED_ON_INIT.
 *
 * Recalculates all children clocks
 */
void propagate_rate(struct clk *pclk, int on_init)
{
	struct clk *clk, *_temp;
	int ret = 0;

	list_for_each_entry_safe(clk, _temp, &pclk->children, sibling) {
		if (clk->recalc) {
			ret = clk->recalc(clk);
			/*
			 * recalc will return error if clk out is not programmed
			 * In this case configure default rate.
			 */
			if (ret && clk->set_rate)
				clk->set_rate(clk, 0);
		}
		propagate_rate(clk, on_init);

		if (!on_init)
			continue;

		/* Enable clks enabled on init, in software view */
		if (clk->flags & ENABLED_ON_INIT)
			do_clk_enable(clk);
	}
}

/**
 * round_rate_index - return closest programmable rate index in rate_config tbl
 * @clk: ptr to clock structure
 * @drate: desired rate
 * @rate: final rate will be returned in this variable only.
 *
 * Finds index in rate_config for highest clk rate which is less than
 * requested rate. If there is no clk rate lesser than requested rate then
 * -EINVAL is returned. This routine assumes that rate_config is written
 * in incrementing order of clk rates.
 * If drate passed is zero then default rate is programmed.
 */
static int
round_rate_index(struct clk *clk, unsigned long drate, unsigned long *rate)
{
	unsigned long tmp = 0, prev_rate = 0;
	int index;

	if (!clk->calc_rate)
		return -EFAULT;

	if (!drate)
		return -EINVAL;

	/*
	 * This loops ends on two conditions:
	 * - as soon as clk is found with rate greater than requested rate.
	 * - if all clks in rate_config are smaller than requested rate.
	 */
	for (index = 0; index < clk->rate_config.count; index++) {
		prev_rate = tmp;
		tmp = clk->calc_rate(clk, index);
		if (drate < tmp) {
			index--;
			break;
		}
	}
	/* return if can't find suitable clock */
	if (index < 0) {
		index = -EINVAL;
		*rate = 0;
	} else if (index == clk->rate_config.count) {
		/* program with highest clk rate possible */
		index = clk->rate_config.count - 1;
		*rate = tmp;
	} else
		*rate = prev_rate;

	return index;
}

/**
 * clk_round_rate - adjust a rate to the exact rate a clock can provide
 * @clk: clock source
 * @rate: desired clock rate in Hz
 *
 * Returns rounded clock rate in Hz, or negative errno.
 */
long clk_round_rate(struct clk *clk, unsigned long drate)
{
	long rate = 0;
	int index;

	/*
	 * propagate call to parent who supports calc_rate. Similar approach is
	 * used in clk_set_rate.
	 */
	if (!clk->calc_rate) {
		u32 mult;
		if (!clk->pclk)
			return clk->rate;

		mult = clk->div_factor ? clk->div_factor : 1;
		return clk_round_rate(clk->pclk, mult * drate) / mult;
	}

	index = round_rate_index(clk, drate, &rate);
	if (index >= 0)
		return rate;
	else
		return index;
}
EXPORT_SYMBOL(clk_round_rate);

/*All below functions are called with lock held */

/*
 * Calculates pll clk rate for specific value of mode, m, n and p
 *
 * In normal mode
 * rate = (2 * M[15:8] * Fin)/(N * 2^P)
 *
 * In Dithered mode
 * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P)
 */
unsigned long pll_calc_rate(struct clk *clk, int index)
{
	unsigned long rate = clk->pclk->rate;
	struct pll_rate_tbl *tbls = clk->rate_config.tbls;
	unsigned int mode;

	mode = tbls[index].mode ? 256 : 1;
	return (((2 * rate / 10000) * tbls[index].m) /
			(mode * tbls[index].n * (1 << tbls[index].p))) * 10000;
}

/*
 * calculates current programmed rate of pll1
 *
 * In normal mode
 * rate = (2 * M[15:8] * Fin)/(N * 2^P)
 *
 * In Dithered mode
 * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P)
 */
int pll_clk_recalc(struct clk *clk)
{
	struct pll_clk_config *config = clk->private_data;
	unsigned int num = 2, den = 0, val, mode = 0;

	mode = (readl(config->mode_reg) >> config->masks->mode_shift) &
		config->masks->mode_mask;

	val = readl(config->cfg_reg);
	/* calculate denominator */
	den = (val >> config->masks->div_p_shift) & config->masks->div_p_mask;
	den = 1 << den;
	den *= (val >> config->masks->div_n_shift) & config->masks->div_n_mask;

	/* calculate numerator & denominator */
	if (!mode) {
		/* Normal mode */
		num *= (val >> config->masks->norm_fdbk_m_shift) &
			config->masks->norm_fdbk_m_mask;
	} else {
		/* Dithered mode */
		num *= (val >> config->masks->dith_fdbk_m_shift) &
			config->masks->dith_fdbk_m_mask;
		den *= 256;
	}

	if (!den)
		return -EINVAL;

	clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
	return 0;
}

/*
 * Configures new clock rate of pll
 */
int pll_clk_set_rate(struct clk *clk, unsigned long desired_rate)
{
	struct pll_rate_tbl *tbls = clk->rate_config.tbls;
	struct pll_clk_config *config = clk->private_data;
	unsigned long val, rate;
	int i;

	i = round_rate_index(clk, desired_rate, &rate);
	if (i < 0)
		return i;

	val = readl(config->mode_reg) &
		~(config->masks->mode_mask << config->masks->mode_shift);
	val |= (tbls[i].mode & config->masks->mode_mask) <<
		config->masks->mode_shift;
	writel(val, config->mode_reg);

	val = readl(config->cfg_reg) &
		~(config->masks->div_p_mask << config->masks->div_p_shift);
	val |= (tbls[i].p & config->masks->div_p_mask) <<
		config->masks->div_p_shift;
	val &= ~(config->masks->div_n_mask << config->masks->div_n_shift);
	val |= (tbls[i].n & config->masks->div_n_mask) <<
		config->masks->div_n_shift;
	val &= ~(config->masks->dith_fdbk_m_mask <<
			config->masks->dith_fdbk_m_shift);
	if (tbls[i].mode)
		val |= (tbls[i].m & config->masks->dith_fdbk_m_mask) <<
			config->masks->dith_fdbk_m_shift;
	else
		val |= (tbls[i].m & config->masks->norm_fdbk_m_mask) <<
			config->masks->norm_fdbk_m_shift;

	writel(val, config->cfg_reg);

	clk->rate = rate;

	return 0;
}

/*
 * Calculates ahb, apb clk rate for specific value of div
 */
unsigned long bus_calc_rate(struct clk *clk, int index)
{
	unsigned long rate = clk->pclk->rate;
	struct bus_rate_tbl *tbls = clk->rate_config.tbls;

	return rate / (tbls[index].div + 1);
}

/* calculates current programmed rate of ahb or apb bus */
int bus_clk_recalc(struct clk *clk)
{
	struct bus_clk_config *config = clk->private_data;
	unsigned int div;

	div = ((readl(config->reg) >> config->masks->shift) &
			config->masks->mask) + 1;

	if (!div)
		return -EINVAL;

	clk->rate = (unsigned long)clk->pclk->rate / div;
	return 0;
}

/* Configures new clock rate of AHB OR APB bus */
int bus_clk_set_rate(struct clk *clk, unsigned long desired_rate)
{
	struct bus_rate_tbl *tbls = clk->rate_config.tbls;
	struct bus_clk_config *config = clk->private_data;
	unsigned long val, rate;
	int i;

	i = round_rate_index(clk, desired_rate, &rate);
	if (i < 0)
		return i;

	val = readl(config->reg) &
		~(config->masks->mask << config->masks->shift);
	val |= (tbls[i].div & config->masks->mask) << config->masks->shift;
	writel(val, config->reg);

	clk->rate = rate;

	return 0;
}

/*
 * gives rate for different values of eq, x and y
 *
 * Fout from synthesizer can be given from two equations:
 * Fout1 = (Fin * X/Y)/2		EQ1
 * Fout2 = Fin * X/Y			EQ2
 */
unsigned long aux_calc_rate(struct clk *clk, int index)
{
	unsigned long rate = clk->pclk->rate;
	struct aux_rate_tbl *tbls = clk->rate_config.tbls;
	u8 eq = tbls[index].eq ? 1 : 2;

	return (((rate/10000) * tbls[index].xscale) /
			(tbls[index].yscale * eq)) * 10000;
}

/*
 * calculates current programmed rate of auxiliary synthesizers
 * used by: UART, FIRDA
 *
 * Fout from synthesizer can be given from two equations:
 * Fout1 = (Fin * X/Y)/2
 * Fout2 = Fin * X/Y
 *
 * Selection of eqn 1 or 2 is programmed in register
 */
int aux_clk_recalc(struct clk *clk)
{
	struct aux_clk_config *config = clk->private_data;
	unsigned int num = 1, den = 1, val, eqn;

	val = readl(config->synth_reg);

	eqn = (val >> config->masks->eq_sel_shift) &
		config->masks->eq_sel_mask;
	if (eqn == config->masks->eq1_mask)
		den *= 2;

	/* calculate numerator */
	num = (val >> config->masks->xscale_sel_shift) &
		config->masks->xscale_sel_mask;

	/* calculate denominator */
	den *= (val >> config->masks->yscale_sel_shift) &
		config->masks->yscale_sel_mask;

	if (!den)
		return -EINVAL;

	clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
	return 0;
}

/* Configures new clock rate of auxiliary synthesizers used by: UART, FIRDA*/
int aux_clk_set_rate(struct clk *clk, unsigned long desired_rate)
{
	struct aux_rate_tbl *tbls = clk->rate_config.tbls;
	struct aux_clk_config *config = clk->private_data;
	unsigned long val, rate;
	int i;

	i = round_rate_index(clk, desired_rate, &rate);
	if (i < 0)
		return i;

	val = readl(config->synth_reg) &
		~(config->masks->eq_sel_mask << config->masks->eq_sel_shift);
	val |= (tbls[i].eq & config->masks->eq_sel_mask) <<
		config->masks->eq_sel_shift;
	val &= ~(config->masks->xscale_sel_mask <<
			config->masks->xscale_sel_shift);
	val |= (tbls[i].xscale & config->masks->xscale_sel_mask) <<
		config->masks->xscale_sel_shift;
	val &= ~(config->masks->yscale_sel_mask <<
			config->masks->yscale_sel_shift);
	val |= (tbls[i].yscale & config->masks->yscale_sel_mask) <<
		config->masks->yscale_sel_shift;
	writel(val, config->synth_reg);

	clk->rate = rate;

	return 0;
}

/*
 * Calculates gpt clk rate for different values of mscale and nscale
 *
 * Fout= Fin/((2 ^ (N+1)) * (M+1))
 */
unsigned long gpt_calc_rate(struct clk *clk, int index)
{
	unsigned long rate = clk->pclk->rate;
	struct gpt_rate_tbl *tbls = clk->rate_config.tbls;

	return rate / ((1 << (tbls[index].nscale + 1)) *
			(tbls[index].mscale + 1));
}

/*
 * calculates current programmed rate of gpt synthesizers
 * Fout from synthesizer can be given from below equations:
 * Fout= Fin/((2 ^ (N+1)) * (M+1))
 */
int gpt_clk_recalc(struct clk *clk)
{
	struct gpt_clk_config *config = clk->private_data;
	unsigned int div = 1, val;

	val = readl(config->synth_reg);
	div += (val >> config->masks->mscale_sel_shift) &
		config->masks->mscale_sel_mask;
	div *= 1 << (((val >> config->masks->nscale_sel_shift) &
				config->masks->nscale_sel_mask) + 1);

	if (!div)
		return -EINVAL;

	clk->rate = (unsigned long)clk->pclk->rate / div;
	return 0;
}

/* Configures new clock rate of gptiliary synthesizers used by: UART, FIRDA*/
int gpt_clk_set_rate(struct clk *clk, unsigned long desired_rate)
{
	struct gpt_rate_tbl *tbls = clk->rate_config.tbls;
	struct gpt_clk_config *config = clk->private_data;
	unsigned long val, rate;
	int i;

	i = round_rate_index(clk, desired_rate, &rate);
	if (i < 0)
		return i;

	val = readl(config->synth_reg) & ~(config->masks->mscale_sel_mask <<
			config->masks->mscale_sel_shift);
	val |= (tbls[i].mscale & config->masks->mscale_sel_mask) <<
		config->masks->mscale_sel_shift;
	val &= ~(config->masks->nscale_sel_mask <<
			config->masks->nscale_sel_shift);
	val |= (tbls[i].nscale & config->masks->nscale_sel_mask) <<
		config->masks->nscale_sel_shift;
	writel(val, config->synth_reg);

	clk->rate = rate;

	return 0;
}

/*
 * Calculates clcd clk rate for different values of div
 *
 * Fout from synthesizer can be given from below equation:
 * Fout= Fin/2*div (division factor)
 * div is 17 bits:-
 *	0-13 (fractional part)
 *	14-16 (integer part)
 * To calculate Fout we left shift val by 14 bits and divide Fin by
 * complete div (including fractional part) and then right shift the
 * result by 14 places.
 */
unsigned long clcd_calc_rate(struct clk *clk, int index)
{
	unsigned long rate = clk->pclk->rate;
	struct clcd_rate_tbl *tbls = clk->rate_config.tbls;

	rate /= 1000;
	rate <<= 12;
	rate /= (2 * tbls[index].div);
	rate >>= 12;
	rate *= 1000;

	return rate;
}

/*
 * calculates current programmed rate of clcd synthesizer
 * Fout from synthesizer can be given from below equation:
 * Fout= Fin/2*div (division factor)
 * div is 17 bits:-
 *	0-13 (fractional part)
 *	14-16 (integer part)
 * To calculate Fout we left shift val by 14 bits and divide Fin by
 * complete div (including fractional part) and then right shift the
 * result by 14 places.
 */
int clcd_clk_recalc(struct clk *clk)
{
	struct clcd_clk_config *config = clk->private_data;
	unsigned int div = 1;
	unsigned long prate;
	unsigned int val;

	val = readl(config->synth_reg);
	div = (val >> config->masks->div_factor_shift) &
		config->masks->div_factor_mask;

	if (!div)
		return -EINVAL;

	prate = clk->pclk->rate / 1000; /* first level division, make it KHz */

	clk->rate = (((unsigned long)prate << 12) / (2 * div)) >> 12;
	clk->rate *= 1000;
	return 0;
}

/* Configures new clock rate of auxiliary synthesizers used by: UART, FIRDA*/
int clcd_clk_set_rate(struct clk *clk, unsigned long desired_rate)
{
	struct clcd_rate_tbl *tbls = clk->rate_config.tbls;
	struct clcd_clk_config *config = clk->private_data;
	unsigned long val, rate;
	int i;

	i = round_rate_index(clk, desired_rate, &rate);
	if (i < 0)
		return i;

	val = readl(config->synth_reg) & ~(config->masks->div_factor_mask <<
			config->masks->div_factor_shift);
	val |= (tbls[i].div & config->masks->div_factor_mask) <<
		config->masks->div_factor_shift;
	writel(val, config->synth_reg);

	clk->rate = rate;

	return 0;
}

/*
 * Used for clocks that always have value as the parent clock divided by a
 * fixed divisor
 */
int follow_parent(struct clk *clk)
{
	unsigned int div_factor = (clk->div_factor < 1) ? 1 : clk->div_factor;

	clk->rate = clk->pclk->rate/div_factor;
	return 0;
}

/**
 * recalc_root_clocks - recalculate and propagate all root clocks
 *
 * Recalculates all root clocks (clocks with no parent), which if the
 * clock's .recalc is set correctly, should also propagate their rates.
 */
void recalc_root_clocks(void)
{
	struct clk *pclk;
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(&clocks_lock, flags);
	list_for_each_entry(pclk, &root_clks, sibling) {
		if (pclk->recalc) {
			ret = pclk->recalc(pclk);
			/*
			 * recalc will return error if clk out is not programmed
			 * In this case configure default clock.
			 */
			if (ret && pclk->set_rate)
				pclk->set_rate(pclk, 0);
		}
		propagate_rate(pclk, 1);
		/* Enable clks enabled on init, in software view */
		if (pclk->flags & ENABLED_ON_INIT)
			do_clk_enable(pclk);
	}
	spin_unlock_irqrestore(&clocks_lock, flags);
}

void __init clk_init(void)
{
	recalc_root_clocks();
}

#ifdef CONFIG_DEBUG_FS
/*
 *	debugfs support to trace clock tree hierarchy and attributes
 */
static struct dentry *clk_debugfs_root;
static int clk_debugfs_register_one(struct clk *c)
{
	int err;
	struct dentry *d, *child;
	struct clk *pa = c->pclk;
	char s[255];
	char *p = s;

	if (c) {
		if (c->cl->con_id)
			p += sprintf(p, "%s", c->cl->con_id);
		if (c->cl->dev_id)
			p += sprintf(p, "%s", c->cl->dev_id);
	}
	d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
	if (!d)
		return -ENOMEM;
	c->dent = d;

	d = debugfs_create_u32("usage_count", S_IRUGO, c->dent,
			(u32 *)&c->usage_count);
	if (!d) {
		err = -ENOMEM;
		goto err_out;
	}
	d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
	if (!d) {
		err = -ENOMEM;
		goto err_out;
	}
	d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
	if (!d) {
		err = -ENOMEM;
		goto err_out;
	}
	return 0;

err_out:
	d = c->dent;
	list_for_each_entry(child, &d->d_subdirs, d_u.d_child)
		debugfs_remove(child);
	debugfs_remove(c->dent);
	return err;
}

static int clk_debugfs_register(struct clk *c)
{
	int err;
	struct clk *pa = c->pclk;

	if (pa && !pa->dent) {
		err = clk_debugfs_register(pa);
		if (err)
			return err;
	}

	if (!c->dent) {
		err = clk_debugfs_register_one(c);
		if (err)
			return err;
	}
	return 0;
}

static int __init clk_debugfs_init(void)
{
	struct clk *c;
	struct dentry *d;
	int err;

	d = debugfs_create_dir("clock", NULL);
	if (!d)
		return -ENOMEM;
	clk_debugfs_root = d;

	list_for_each_entry(c, &clocks, node) {
		err = clk_debugfs_register(c);
		if (err)
			goto err_out;
	}
	return 0;
err_out:
	debugfs_remove_recursive(clk_debugfs_root);
	return err;
}
late_initcall(clk_debugfs_init);

static int clk_debugfs_reparent(struct clk *c)
{
	debugfs_remove(c->dent);
	return clk_debugfs_register_one(c);
}
#endif /* CONFIG_DEBUG_FS */