diff options
Diffstat (limited to 'arch/arm/plat-spear')
| -rw-r--r-- | arch/arm/plat-spear/Makefile | 4 | ||||
| -rw-r--r-- | arch/arm/plat-spear/clock.c | 844 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/clock.h | 166 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/debug-macro.S | 2 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/hardware.h | 23 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/memory.h | 2 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/system.h | 4 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/uncompress.h | 6 | ||||
| -rw-r--r-- | arch/arm/plat-spear/include/plat/vmalloc.h | 2 | ||||
| -rw-r--r-- | arch/arm/plat-spear/time.c | 26 | 
10 files changed, 893 insertions, 186 deletions
| diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile index eb89540aeda9..b4f340b8f1f1 100644 --- a/arch/arm/plat-spear/Makefile +++ b/arch/arm/plat-spear/Makefile @@ -3,6 +3,6 @@  #  # Common support -obj-y	:= clock.o padmux.o time.o +obj-y	:= clock.o time.o -obj-$(CONFIG_ARCH_SPEAR3XX)	+= shirq.o +obj-$(CONFIG_ARCH_SPEAR3XX)	+= shirq.o padmux.o diff --git a/arch/arm/plat-spear/clock.c b/arch/arm/plat-spear/clock.c index ee4f90e534d8..bdbd7ec9cb6b 100644 --- a/arch/arm/plat-spear/clock.c +++ b/arch/arm/plat-spear/clock.c @@ -12,18 +12,25 @@   */  #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 <mach/misc_regs.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 list_head *); +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)  { @@ -65,6 +72,104 @@ static struct clkops generic_clkops = {  	.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 @@ -78,17 +183,9 @@ int clk_enable(struct clk *clk)  	unsigned long flags;  	int ret = 0; -	if (!clk || IS_ERR(clk)) -		return -EFAULT; -  	spin_lock_irqsave(&clocks_lock, flags); -	if (clk->usage_count == 0) { -		if (clk->ops && clk->ops->enable) -			ret = clk->ops->enable(clk); -	} -	clk->usage_count++; +	ret = do_clk_enable(clk);  	spin_unlock_irqrestore(&clocks_lock, flags); -  	return ret;  }  EXPORT_SYMBOL(clk_enable); @@ -109,17 +206,8 @@ void clk_disable(struct clk *clk)  {  	unsigned long flags; -	if (!clk || IS_ERR(clk)) -		return; - -	WARN_ON(clk->usage_count == 0); -  	spin_lock_irqsave(&clocks_lock, flags); -	clk->usage_count--; -	if (clk->usage_count == 0) { -		if (clk->ops && clk->ops->disable) -			clk->ops->disable(clk); -	} +	do_clk_disable(clk);  	spin_unlock_irqrestore(&clocks_lock, flags);  }  EXPORT_SYMBOL(clk_disable); @@ -153,15 +241,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)  	int i, found = 0, val = 0;  	unsigned long flags; -	if (!clk || IS_ERR(clk) || !parent || IS_ERR(parent)) +	if (!clk || !parent)  		return -EFAULT; -	if (clk->usage_count) -		return -EBUSY; -	if (!clk->pclk_sel) -		return -EPERM;  	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; @@ -176,25 +263,58 @@ int clk_set_parent(struct clk *clk, struct clk *parent)  	/* 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_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->recalc(clk); -	propagate_rate(&clk->children); +	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 = cl->clk; +	struct clk *clk;  	unsigned long flags; -	if (!clk || IS_ERR(clk)) +	if (!cl || !cl->clk)  		return; +	clk = cl->clk;  	spin_lock_irqsave(&clocks_lock, flags); @@ -207,71 +327,173 @@ void clk_register(struct clk_lookup *cl)  	/* root clock don't have any parents */  	if (!clk->pclk && !clk->pclk_sel) {  		list_add(&clk->sibling, &root_clks); -		/* add clocks with only one parent to parent's children list */  	} 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 { -		/* add clocks with > 1 parent to 1st parent's children list */ -		list_add(&clk->sibling, -			 &clk->pclk_sel->pclk_info[0].pclk->children); +		/* 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 in list head + * 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 root clocks in list head, which if the clock's .recalc is - * set correctly, should also propagate their rates. + * Recalculates all children clocks   */ -static void propagate_rate(struct list_head *lhead) +void propagate_rate(struct clk *pclk, int on_init)  { -	struct clk *clkp, *_temp; +	struct clk *clk, *_temp; +	int ret = 0; -	list_for_each_entry_safe(clkp, _temp, lhead, sibling) { -		if (clkp->recalc) -			clkp->recalc(clkp); -		propagate_rate(&clkp->children); +	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);  	}  } -/* returns current programmed clocks clock info structure */ -static struct pclk_info *pclk_info_get(struct clk *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 int mask, i; -	unsigned long flags; -	struct pclk_info *info = NULL; +	unsigned long tmp = 0, prev_rate = 0; +	int index; -	spin_lock_irqsave(&clocks_lock, flags); -	mask = (readl(clk->pclk_sel->pclk_sel_reg) >> clk->pclk_sel_shift) -			& clk->pclk_sel->pclk_sel_mask; +	if (!clk->calc_rate) +		return -EFAULT; -	for (i = 0; i < clk->pclk_sel->pclk_count; i++) { -		if (clk->pclk_sel->pclk_info[i].pclk_mask == mask) -			info = &clk->pclk_sel->pclk_info[i]; +	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; +		}  	} -	spin_unlock_irqrestore(&clocks_lock, flags); +	/* 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 info; +	return index;  } -/* - * Set pclk as cclk's parent and add clock sibling node to current parents - * children list +/** + * 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.   */ -static void change_parent(struct clk *cclk, struct clk *pclk) +long clk_round_rate(struct clk *clk, unsigned long drate)  { -	unsigned long flags; +	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; +	} -	spin_lock_irqsave(&clocks_lock, flags); -	list_del(&cclk->sibling); -	list_add(&cclk->sibling, &pclk->children); +	index = round_rate_index(clk, drate, &rate); +	if (index >= 0) +		return rate; +	else +		return index; +} +EXPORT_SYMBOL(clk_round_rate); -	cclk->pclk = pclk; -	spin_unlock_irqrestore(&clocks_lock, flags); +/*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;  }  /* @@ -283,47 +505,146 @@ static void change_parent(struct clk *cclk, struct clk *pclk)   * In Dithered mode   * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P)   */ -void pll1_clk_recalc(struct clk *clk) +int pll_clk_recalc(struct clk *clk)  {  	struct pll_clk_config *config = clk->private_data;  	unsigned int num = 2, den = 0, val, mode = 0; -	unsigned long flags; -	spin_lock_irqsave(&clocks_lock, flags); -	mode = (readl(config->mode_reg) >> PLL_MODE_SHIFT) & -		PLL_MODE_MASK; +	mode = (readl(config->mode_reg) >> config->masks->mode_shift) & +		config->masks->mode_mask;  	val = readl(config->cfg_reg);  	/* calculate denominator */ -	den = (val >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; +	den = (val >> config->masks->div_p_shift) & config->masks->div_p_mask;  	den = 1 << den; -	den *= (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; +	den *= (val >> config->masks->div_n_shift) & config->masks->div_n_mask;  	/* calculate numerator & denominator */  	if (!mode) {  		/* Normal mode */ -		num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; +		num *= (val >> config->masks->norm_fdbk_m_shift) & +			config->masks->norm_fdbk_m_mask;  	} else {  		/* Dithered mode */ -		num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; +		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; -	spin_unlock_irqrestore(&clocks_lock, flags); +	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 */ -void bus_clk_recalc(struct clk *clk) +int bus_clk_recalc(struct clk *clk)  {  	struct bus_clk_config *config = clk->private_data;  	unsigned int div; -	unsigned long flags; -	spin_lock_irqsave(&clocks_lock, flags); -	div = ((readl(config->reg) >> config->shift) & config->mask) + 1; +	div = ((readl(config->reg) >> config->masks->shift) & +			config->masks->mask) + 1; + +	if (!div) +		return -EINVAL; +  	clk->rate = (unsigned long)clk->pclk->rate / div; -	spin_unlock_irqrestore(&clocks_lock, flags); +	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;  }  /* @@ -336,44 +657,76 @@ void bus_clk_recalc(struct clk *clk)   *   * Selection of eqn 1 or 2 is programmed in register   */ -void aux_clk_recalc(struct clk *clk) +int aux_clk_recalc(struct clk *clk)  {  	struct aux_clk_config *config = clk->private_data; -	struct pclk_info *pclk_info = NULL;  	unsigned int num = 1, den = 1, val, eqn; -	unsigned long flags; -	/* get current programmed parent */ -	pclk_info = pclk_info_get(clk); -	if (!pclk_info) { -		spin_lock_irqsave(&clocks_lock, flags); -		clk->pclk = NULL; -		clk->rate = 0; -		spin_unlock_irqrestore(&clocks_lock, flags); -		return; -	} +	val = readl(config->synth_reg); -	change_parent(clk, pclk_info->pclk); +	eqn = (val >> config->masks->eq_sel_shift) & +		config->masks->eq_sel_mask; +	if (eqn == config->masks->eq1_mask) +		den *= 2; -	spin_lock_irqsave(&clocks_lock, flags); -	if (pclk_info->scalable) { -		val = readl(config->synth_reg); +	/* calculate numerator */ +	num = (val >> config->masks->xscale_sel_shift) & +		config->masks->xscale_sel_mask; -		eqn = (val >> AUX_EQ_SEL_SHIFT) & AUX_EQ_SEL_MASK; -		if (eqn == AUX_EQ1_SEL) -			den *= 2; +	/* calculate denominator */ +	den *= (val >> config->masks->yscale_sel_shift) & +		config->masks->yscale_sel_mask; -		/* calculate numerator */ -		num = (val >> AUX_XSCALE_SHIFT) & AUX_XSCALE_MASK; +	if (!den) +		return -EINVAL; -		/* calculate denominator */ -		den *= (val >> AUX_YSCALE_SHIFT) & AUX_YSCALE_MASK; -		val = (((clk->pclk->rate/10000) * num) / den) * 10000; -	} else -		val = clk->pclk->rate; +	clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000; +	return 0; +} -	clk->rate = val; -	spin_unlock_irqrestore(&clocks_lock, flags); +/* 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));  }  /* @@ -381,46 +734,142 @@ void aux_clk_recalc(struct clk *clk)   * Fout from synthesizer can be given from below equations:   * Fout= Fin/((2 ^ (N+1)) * (M+1))   */ -void gpt_clk_recalc(struct clk *clk) +int gpt_clk_recalc(struct clk *clk)  { -	struct aux_clk_config *config = clk->private_data; -	struct pclk_info *pclk_info = NULL; +	struct gpt_clk_config *config = clk->private_data;  	unsigned int div = 1, val; -	unsigned long flags; -	pclk_info = pclk_info_get(clk); -	if (!pclk_info) { -		spin_lock_irqsave(&clocks_lock, flags); -		clk->pclk = NULL; -		clk->rate = 0; -		spin_unlock_irqrestore(&clocks_lock, flags); -		return; -	} - -	change_parent(clk, pclk_info->pclk); +	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); -	spin_lock_irqsave(&clocks_lock, flags); -	if (pclk_info->scalable) { -		val = readl(config->synth_reg); -		div += (val >> GPT_MSCALE_SHIFT) & GPT_MSCALE_MASK; -		div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); -	} +	if (!div) +		return -EINVAL;  	clk->rate = (unsigned long)clk->pclk->rate / div; -	spin_unlock_irqrestore(&clocks_lock, flags); +	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;  }  /* - * Used for clocks that always have same value as the parent clock divided by a + * 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   */ -void follow_parent(struct clk *clk) +int follow_parent(struct clk *clk)  { -	unsigned long flags; +	unsigned int div_factor = (clk->div_factor < 1) ? 1 : clk->div_factor; -	spin_lock_irqsave(&clocks_lock, flags); -	clk->rate = clk->pclk->rate; -	spin_unlock_irqrestore(&clocks_lock, flags); +	clk->rate = clk->pclk->rate/div_factor; +	return 0;  }  /** @@ -431,5 +880,124 @@ void follow_parent(struct clk *clk)   */  void recalc_root_clocks(void)  { -	propagate_rate(&root_clks); +	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); +} + +#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 */ diff --git a/arch/arm/plat-spear/include/plat/clock.h b/arch/arm/plat-spear/include/plat/clock.h index 2572260f990f..2ae6606930a6 100644 --- a/arch/arm/plat-spear/include/plat/clock.h +++ b/arch/arm/plat-spear/include/plat/clock.h @@ -21,6 +21,7 @@  /* clk structure flags */  #define	ALWAYS_ENABLED		(1 << 0) /* clock always enabled */  #define	RESET_TO_ENABLE		(1 << 1) /* reset register bit to enable clk */ +#define	ENABLED_ON_INIT		(1 << 2) /* clocks enabled at init */  /**   * struct clkops - clock operations @@ -35,13 +36,11 @@ struct clkops {  /**   * struct pclk_info - parents info   * @pclk: pointer to parent clk - * @pclk_mask: value to be written for selecting this parent - * @scalable: Is parent scalable (1 - YES, 0 - NO) + * @pclk_val: value to be written for selecting this parent   */  struct pclk_info {  	struct clk *pclk; -	u8 pclk_mask; -	u8 scalable; +	u8 pclk_val;  };  /** @@ -54,11 +53,23 @@ struct pclk_info {  struct pclk_sel {  	struct pclk_info *pclk_info;  	u8 pclk_count; -	unsigned int *pclk_sel_reg; +	void __iomem *pclk_sel_reg;  	unsigned int pclk_sel_mask;  };  /** + * struct rate_config - clk rate configurations + * @tbls: array of device specific clk rate tables, in ascending order of rates + * @count: size of tbls array + * @default_index: default setting when originally disabled + */ +struct rate_config { +	void *tbls; +	u8 count; +	u8 default_index; +}; + +/**   * struct clk - clock structure   * @usage_count: num of users who enabled this clock   * @flags: flags for clock properties @@ -67,21 +78,32 @@ struct pclk_sel {   * @en_reg_bit: clk enable/disable bit   * @ops: clk enable/disable ops - generic_clkops selected if NULL   * @recalc: pointer to clock rate recalculate function + * @set_rate: pointer to clock set rate function + * @calc_rate: pointer to clock get rate function for index + * @rate_config: rate configuration information, used by set_rate + * @div_factor: division factor to parent clock.   * @pclk: current parent clk   * @pclk_sel: pointer to parent selection structure   * @pclk_sel_shift: register shift for selecting parent of this clock   * @children: list for childrens or this clock   * @sibling: node for list of clocks having same parents   * @private_data: clock specific private data + * @node: list to maintain clocks linearly + * @cl: clocklook up assoicated with this clock + * @dent: object for debugfs   */  struct clk {  	unsigned int usage_count;  	unsigned int flags;  	unsigned long rate; -	unsigned int *en_reg; +	void __iomem *en_reg;  	u8 en_reg_bit;  	const struct clkops *ops; -	void (*recalc) (struct clk *); +	int (*recalc) (struct clk *); +	int (*set_rate) (struct clk *, unsigned long rate); +	unsigned long (*calc_rate)(struct clk *, int index); +	struct rate_config rate_config; +	unsigned int div_factor;  	struct clk *pclk;  	struct pclk_sel *pclk_sel; @@ -90,37 +112,137 @@ struct clk {  	struct list_head children;  	struct list_head sibling;  	void *private_data; +#ifdef CONFIG_DEBUG_FS +	struct list_head node; +	struct clk_lookup *cl; +	struct dentry *dent; +#endif  };  /* pll configuration structure */ +struct pll_clk_masks { +	u32 mode_mask; +	u32 mode_shift; + +	u32 norm_fdbk_m_mask; +	u32 norm_fdbk_m_shift; +	u32 dith_fdbk_m_mask; +	u32 dith_fdbk_m_shift; +	u32 div_p_mask; +	u32 div_p_shift; +	u32 div_n_mask; +	u32 div_n_shift; +}; +  struct pll_clk_config { -	unsigned int *mode_reg; -	unsigned int *cfg_reg; +	void __iomem *mode_reg; +	void __iomem *cfg_reg; +	struct pll_clk_masks *masks; +}; + +/* pll clk rate config structure */ +struct pll_rate_tbl { +	u8 mode; +	u16 m; +	u8 n; +	u8 p;  };  /* ahb and apb bus configuration structure */ +struct bus_clk_masks { +	u32 mask; +	u32 shift; +}; +  struct bus_clk_config { -	unsigned int *reg; -	unsigned int mask; -	unsigned int shift; +	void __iomem *reg; +	struct bus_clk_masks *masks; +}; + +/* ahb and apb clk bus rate config structure */ +struct bus_rate_tbl { +	u8 div; +}; + +/* Aux clk configuration structure: applicable to UART and FIRDA */ +struct aux_clk_masks { +	u32 eq_sel_mask; +	u32 eq_sel_shift; +	u32 eq1_mask; +	u32 eq2_mask; +	u32 xscale_sel_mask; +	u32 xscale_sel_shift; +	u32 yscale_sel_mask; +	u32 yscale_sel_shift;  }; -/* - * Aux clk configuration structure: applicable to GPT, UART and FIRDA - */  struct aux_clk_config { -	unsigned int *synth_reg; +	void __iomem *synth_reg; +	struct aux_clk_masks *masks; +}; + +/* aux clk rate config structure */ +struct aux_rate_tbl { +	u16 xscale; +	u16 yscale; +	u8 eq; +}; + +/* GPT clk configuration structure */ +struct gpt_clk_masks { +	u32 mscale_sel_mask; +	u32 mscale_sel_shift; +	u32 nscale_sel_mask; +	u32 nscale_sel_shift; +}; + +struct gpt_clk_config { +	void __iomem *synth_reg; +	struct gpt_clk_masks *masks; +}; + +/* gpt clk rate config structure */ +struct gpt_rate_tbl { +	u16 mscale; +	u16 nscale; +}; + +/* clcd clk configuration structure */ +struct clcd_synth_masks { +	u32 div_factor_mask; +	u32 div_factor_shift; +}; + +struct clcd_clk_config { +	void __iomem *synth_reg; +	struct clcd_synth_masks *masks; +}; + +/* clcd clk rate config structure */ +struct clcd_rate_tbl { +	u16 div;  };  /* platform specific clock functions */  void clk_register(struct clk_lookup *cl);  void recalc_root_clocks(void); -/* clock recalc functions */ -void follow_parent(struct clk *clk); -void pll1_clk_recalc(struct clk *clk); -void bus_clk_recalc(struct clk *clk); -void gpt_clk_recalc(struct clk *clk); -void aux_clk_recalc(struct clk *clk); +/* clock recalc & set rate functions */ +int follow_parent(struct clk *clk); +unsigned long pll_calc_rate(struct clk *clk, int index); +int pll_clk_recalc(struct clk *clk); +int pll_clk_set_rate(struct clk *clk, unsigned long desired_rate); +unsigned long bus_calc_rate(struct clk *clk, int index); +int bus_clk_recalc(struct clk *clk); +int bus_clk_set_rate(struct clk *clk, unsigned long desired_rate); +unsigned long gpt_calc_rate(struct clk *clk, int index); +int gpt_clk_recalc(struct clk *clk); +int gpt_clk_set_rate(struct clk *clk, unsigned long desired_rate); +unsigned long aux_calc_rate(struct clk *clk, int index); +int aux_clk_recalc(struct clk *clk); +int aux_clk_set_rate(struct clk *clk, unsigned long desired_rate); +unsigned long clcd_calc_rate(struct clk *clk, int index); +int clcd_clk_recalc(struct clk *clk); +int clcd_clk_set_rate(struct clk *clk, unsigned long desired_rate);  #endif /* __PLAT_CLOCK_H */ diff --git a/arch/arm/plat-spear/include/plat/debug-macro.S b/arch/arm/plat-spear/include/plat/debug-macro.S index e91270e4f640..8501bbf2c092 100644 --- a/arch/arm/plat-spear/include/plat/debug-macro.S +++ b/arch/arm/plat-spear/include/plat/debug-macro.S @@ -12,7 +12,7 @@   */  #include <linux/amba/serial.h> -#include <mach/spear.h> +#include <mach/hardware.h>  		.macro	addruart, rp, rv  		mov	\rp, #SPEAR_DBG_UART_BASE		@ Physical base diff --git a/arch/arm/plat-spear/include/plat/hardware.h b/arch/arm/plat-spear/include/plat/hardware.h new file mode 100644 index 000000000000..66d677225d15 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/hardware.h @@ -0,0 +1,23 @@ +/* + * arch/arm/plat-spear/include/plat/hardware.h + * + * Hardware definitions for SPEAr + * + * Copyright (C) 2010 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. + */ + +#ifndef __PLAT_HARDWARE_H +#define __PLAT_HARDWARE_H + +#ifndef __ASSEMBLY__ +#define IOMEM(x)	((void __iomem __force *)(x)) +#else +#define IOMEM(x)	(x) +#endif + +#endif /* __PLAT_HARDWARE_H */ diff --git a/arch/arm/plat-spear/include/plat/memory.h b/arch/arm/plat-spear/include/plat/memory.h index 27a4aba77343..7e3599e1104e 100644 --- a/arch/arm/plat-spear/include/plat/memory.h +++ b/arch/arm/plat-spear/include/plat/memory.h @@ -15,6 +15,6 @@  #define __PLAT_MEMORY_H  /* Physical DRAM offset */ -#define PHYS_OFFSET		UL(0x00000000) +#define PLAT_PHYS_OFFSET		UL(0x00000000)  #endif /* __PLAT_MEMORY_H */ diff --git a/arch/arm/plat-spear/include/plat/system.h b/arch/arm/plat-spear/include/plat/system.h index 55a4e405d578..a235fa0ca777 100644 --- a/arch/arm/plat-spear/include/plat/system.h +++ b/arch/arm/plat-spear/include/plat/system.h @@ -14,9 +14,9 @@  #ifndef __PLAT_SYSTEM_H  #define __PLAT_SYSTEM_H -#include <asm/hardware/sp810.h>  #include <linux/io.h> -#include <mach/spear.h> +#include <asm/hardware/sp810.h> +#include <mach/hardware.h>  static inline void arch_idle(void)  { diff --git a/arch/arm/plat-spear/include/plat/uncompress.h b/arch/arm/plat-spear/include/plat/uncompress.h index 99ba6789cc97..1bf84527aee4 100644 --- a/arch/arm/plat-spear/include/plat/uncompress.h +++ b/arch/arm/plat-spear/include/plat/uncompress.h @@ -13,7 +13,7 @@  #include <linux/io.h>  #include <linux/amba/serial.h> -#include <mach/spear.h> +#include <mach/hardware.h>  #ifndef __PLAT_UNCOMPRESS_H  #define __PLAT_UNCOMPRESS_H @@ -24,10 +24,10 @@ static inline void putc(int c)  {  	void __iomem *base = (void __iomem *)SPEAR_DBG_UART_BASE; -	while (readl(base + UART01x_FR) & UART01x_FR_TXFF) +	while (readl_relaxed(base + UART01x_FR) & UART01x_FR_TXFF)  		barrier(); -	writel(c, base + UART01x_DR); +	writel_relaxed(c, base + UART01x_DR);  }  static inline void flush(void) diff --git a/arch/arm/plat-spear/include/plat/vmalloc.h b/arch/arm/plat-spear/include/plat/vmalloc.h index 09e9372aea21..8c8b24d07046 100644 --- a/arch/arm/plat-spear/include/plat/vmalloc.h +++ b/arch/arm/plat-spear/include/plat/vmalloc.h @@ -14,6 +14,6 @@  #ifndef __PLAT_VMALLOC_H  #define __PLAT_VMALLOC_H -#define VMALLOC_END		0xF0000000 +#define VMALLOC_END		0xF0000000UL  #endif /* __PLAT_VMALLOC_H */ diff --git a/arch/arm/plat-spear/time.c b/arch/arm/plat-spear/time.c index 839c88df9994..dbb6e4fff79d 100644 --- a/arch/arm/plat-spear/time.c +++ b/arch/arm/plat-spear/time.c @@ -1,7 +1,7 @@  /*   * arch/arm/plat-spear/time.c   * - * Copyright (C) 2009 ST Microelectronics + * Copyright (C) 2010 ST Microelectronics   * Shiraz Hashim<shiraz.hashim@st.com>   *   * This file is licensed under the terms of the GNU General Public @@ -20,10 +20,9 @@  #include <linux/time.h>  #include <linux/irq.h>  #include <asm/mach/time.h> -#include <mach/irqs.h> -#include <mach/hardware.h> -#include <mach/spear.h>  #include <mach/generic.h> +#include <mach/hardware.h> +#include <mach/irqs.h>  /*   * We would use TIMER0 and TIMER1 as clockevent and clocksource. @@ -211,7 +210,7 @@ static void __init spear_clockevent_init(void)  void __init spear_setup_timer(void)  { -	struct clk *pll3_clk; +	int ret;  	if (!request_mem_region(SPEAR_GPT0_BASE, SZ_1K, "gpt0")) {  		pr_err("%s:cannot get IO addr\n", __func__); @@ -230,26 +229,21 @@ void __init spear_setup_timer(void)  		goto err_iomap;  	} -	pll3_clk = clk_get(NULL, "pll3_48m_clk"); -	if (!pll3_clk) { -		pr_err("%s:couldn't get PLL3 as parent for gpt\n", __func__); -		goto err_iomap; +	ret = clk_enable(gpt_clk); +	if (ret < 0) { +		pr_err("%s:couldn't enable gpt clock\n", __func__); +		goto err_clk;  	} -	clk_set_parent(gpt_clk, pll3_clk); -  	spear_clockevent_init();  	spear_clocksource_init();  	return; +err_clk: +	clk_put(gpt_clk);  err_iomap:  	iounmap(gpt_base); -  err_mem:  	release_mem_region(SPEAR_GPT0_BASE, SZ_1K);  } - -struct sys_timer spear_sys_timer = { -	.init = spear_setup_timer, -}; | 
