All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefano Babic <sbabic@denx.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH] mx5: Add clock config interface
Date: Mon, 19 Mar 2012 16:27:07 +0100	[thread overview]
Message-ID: <4F67504B.70303@denx.de> (raw)
In-Reply-To: <1331929300-27616-1-git-send-email-festevam@gmail.com>

On 16/03/2012 21:21, Fabio Estevam wrote:
> From: Jason Liu <jason.hui@linaro.org>
> 
> mx5: Add clock config interface

Hi Fabio,

> 
> Add clock config interface support, so that we
> can configure CPU or DDR clock in the later init
> 
> Signed-off-by: Jason Liu <jason.hui@linaro.org>
> Signed-off-by: Eric Miao <eric.miao@linaro.org>
> ---

My personal quote: this seems to introduce several changes as the clocks
are computed - my personal feeling is that this patch will go into the
-next branch.

> 
> diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c
> index e92f106..c7613f0 100644
> --- a/arch/arm/cpu/armv7/mx5/clock.c
> +++ b/arch/arm/cpu/armv7/mx5/clock.c
> @@ -24,6 +24,7 @@
>   */
>  
>  #include <common.h>
> +#include <div64.h>
>  #include <asm/io.h>
>  #include <asm/errno.h>
>  #include <asm/arch/imx-regs.h>
> @@ -48,6 +49,39 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = {
>  #endif
>  };
>  
> +#define AHB_CLK_ROOT    133333333
> +#define SZ_DEC_1M       1000000
> +#define PLL_PD_MAX      16      /* Actual pd+1 */
> +#define PLL_MFI_MAX     15
> +#define PLL_MFI_MIN     5
> +#define ARM_DIV_MAX     8
> +#define IPG_DIV_MAX     4
> +#define AHB_DIV_MAX     8
> +#define EMI_DIV_MAX     8
> +#define NFC_DIV_MAX     8
> +
> +struct fixed_pll_mfd {
> +	u32 ref_clk_hz;
> +	u32 mfd;
> +};
> +
> +const struct fixed_pll_mfd fixed_mfd[] = {
> +	{CONFIG_SYS_MX5_HCLK, 24 * 16},
> +};
> +
> +struct pll_param {
> +	u32 pd;
> +	u32 mfi;
> +	u32 mfn;
> +	u32 mfd;
> +};
> +
> +#define PLL_FREQ_MAX(ref_clk)  (4 * (ref_clk) * PLL_MFI_MAX)
> +#define PLL_FREQ_MIN(ref_clk) \
> +		((2 * (ref_clk) * (PLL_MFI_MIN - 1)) / PLL_PD_MAX)
> +#define MAX_DDR_CLK     420000000
> +#define NFC_CLK_MAX     34000000
> +
>  struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE;
>  
>  void set_usboh3_clk(void)
> @@ -212,20 +246,13 @@ static u32 get_periph_clk(void)
>  	/* NOTREACHED */
>  }
>  
> -/*
> - * Get the rate of ahb clock.
> - */
>  static u32 get_ahb_clk(void)
>  {
> -	uint32_t freq, div, reg;
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +	u32 pdf = (cbcdr & MXC_CCM_CBCDR_AHB_PODF_MASK) \
> +			>> MXC_CCM_CBCDR_AHB_PODF_OFFSET;
>  
> -	freq = get_periph_clk();
> -
> -	reg = __raw_readl(&mxc_ccm->cbcdr);
> -	div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
> -			MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
> -
> -	return freq / div;
> +	return  get_periph_clk() / (pdf + 1);

A good occasion to factorize with mx6 code. What about to move the
function into arch/arm/cpu/armv7/imx-common/ ?

>  }
>  
>  /*
> @@ -306,7 +333,7 @@ static u32 get_uart_clk(void)
>  /*
>   * This function returns the low power audio clock.
>   */
> -u32 get_lp_apm(void)
> +static u32 get_lp_apm(void)

Right, all internal functions should be static - thanks for fixing it.

>  
> +static u32 get_axi_a_clk(void)
> +{
> +	u32 cbcdr =  __raw_readl(&mxc_ccm->cbcdr);
> +	u32 pdf = (cbcdr & MXC_CCM_CBCDR_AXI_A_PODF_MASK) \
> +			>> MXC_CCM_CBCDR_AXI_A_PODF_OFFSET;
> +
> +	return  get_periph_clk() / (pdf + 1);
> +}
> +
> +static u32 get_axi_b_clk(void)
> +{
> +	u32 cbcdr =  __raw_readl(&mxc_ccm->cbcdr);
> +	u32 pdf = (cbcdr & MXC_CCM_CBCDR_AXI_B_PODF_MASK) \
> +			>> MXC_CCM_CBCDR_AXI_B_PODF_OFFSET;
> +
> +	return  get_periph_clk() / (pdf + 1);
> +}
> +
> +static u32 get_emi_slow_clk(void)
> +{
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +	u32 emi_clk_sel = cbcdr & MXC_CCM_CBCDR_EMI_CLK_SEL;
> +	u32 pdf = (cbcdr & MXC_CCM_CBCDR_EMI_PODF_MASK) \
> +			>> MXC_CCM_CBCDR_EMI_PODF_OFFSET;
> +
> +	if (emi_clk_sel)
> +		return  get_ahb_clk() / (pdf + 1);
> +
> +	return  get_periph_clk() / (pdf + 1);
> +}
> +
> +static u32 get_nfc_clk(void)
> +{
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +	u32 pdf = (cbcdr & MXC_CCM_CBCDR_NFC_PODF_MASK) \
> +			>> MXC_CCM_CBCDR_NFC_PODF_OFFSET;
> +
> +	return  get_emi_slow_clk() / (pdf + 1);
> +}
> +
> +static u32 get_ddr_clk(void)
> +{
> +	u32 ret_val = 0;
> +	u32 cbcmr = __raw_readl(&mxc_ccm->cbcmr);
> +	u32 ddr_clk_sel = (cbcmr & MXC_CCM_CBCMR_DDR_CLK_SEL_MASK) \
> +				>> MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET;
> +#ifdef CONFIG_MX51
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +	if (cbcdr & MXC_CCM_CBCDR_DDR_HIFREQ_SEL) {
> +		u32 ddr_clk_podf = (cbcdr & MXC_CCM_CBCDR_DDR_PODF_MASK) >> \
> +					MXC_CCM_CBCDR_DDR_PODF_OFFSET;
> +
> +		ret_val = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);
> +		ret_val /= ddr_clk_podf + 1;
> +
> +		return ret_val;
> +	}
> +#endif
> +	switch (ddr_clk_sel) {
> +	case 0:
> +		ret_val = get_axi_a_clk();
> +		break;
> +	case 1:
> +		ret_val = get_axi_b_clk();
> +		break;
> +	case 2:
> +		ret_val = get_emi_slow_clk();
> +		break;
> +	case 3:
> +		ret_val = get_ahb_clk();
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret_val;
> +}
> +
> +
>  /*
>   * The API of get mxc clockes.
>   */
> @@ -376,10 +482,12 @@ unsigned int mxc_get_clock(enum mxc_clock clk)
>  	case MXC_UART_CLK:
>  		return get_uart_clk();
>  	case MXC_CSPI_CLK:
> -		return imx_get_cspiclk();
> +		return get_cspi_clk();
>  	case MXC_FEC_CLK:
>  		return decode_pll(mxc_plls[PLL1_CLOCK],
>  				    CONFIG_SYS_MX5_HCLK);
> +	case MXC_DDR_CLK:
> +		return get_ddr_clk();
>  	default:
>  		break;
>  	}
> @@ -398,6 +506,424 @@ u32 imx_get_fecclk(void)
>  }
>  
>  /*
> + * Clock config code start here
> + */
> +
> +/* precondition: m>0 and n>0.  Let g=gcd(m,n). */
> +static int gcd(int m, int n)
> +{
> +	int t;
> +	while (m > 0) {
> +		if (n > m) {
> +			t = m;
> +			m = n;
> +			n = t;
> +		} /* swap */
> +		m -= n;
> +	}
> +	return n;
> +}
> +
> +/*
> + * This is to calculate various parameters based on reference clock and
> + * targeted clock based on the equation:
> + *      t_clk = 2*ref_freq*(mfi + mfn/(mfd+1))/(pd+1)
> + * This calculation is based on a fixed MFD value for simplicity.
> + *
> + * @param ref       reference clock freq in Hz
> + * @param target    targeted clock in Hz
> + * @param pll       pll_param structure.

We have not doxygen in all u-boot code and this function remains an
exception. So you can drop all @statements. This should be fixed globally.


> +	}
> +	/* Now got pd and mfi already */
> +	/*
> +	mfn = (((n_target * pd) / 4 - n_ref * mfi) * mfd) / n_ref;
> +	*/

Dead code ?

> +	t1 = n_target * pd;
> +	do_div(t1, 4);
> +	t1 -= n_ref * mfi;
> +	t1 *= mfd;
> +	do_div(t1, n_ref);
> +	mfn = t1;
> +#ifdef CMD_CLOCK_DEBUG
> +	printf("ref=%d, target=%d, pd=%d," "mfi=%d,mfn=%d, mfd=%d\n",
> +		ref, n_target, (u32)pd, (u32)mfi, (u32)mfn, (u32)mfd);

What is CMD_CLOCK_DEBUG ? I think it is enough if you replace printf()
with debug()

> +
> +static u32 calc_per_cbcdr_val(u32 per_clk)
> +{
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +	u32 tmp_clk = 0, div = 0, clk_sel = 0;
> +
> +	cbcdr &= ~MXC_CCM_CBCDR_PERIPH_CLK_SEL;
> +
> +	/* emi_slow_podf divider */
> +	tmp_clk = get_emi_slow_clk();
> +	clk_sel = cbcdr & MXC_CCM_CBCDR_EMI_CLK_SEL;
> +	if (clk_sel) {
> +		div = calc_div(tmp_clk, per_clk, 8);
> +		cbcdr &= ~MXC_CCM_CBCDR_EMI_PODF_MASK;
> +		cbcdr |= (div << MXC_CCM_CBCDR_EMI_PODF_OFFSET);
> +	}
> +
> +	/* axi_b_podf divider */
> +	tmp_clk = get_axi_b_clk();
> +	div = calc_div(tmp_clk, per_clk, 8);
> +	cbcdr &= ~MXC_CCM_CBCDR_AXI_B_PODF_MASK;
> +	cbcdr |= (div << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET);
> +
> +	/* axi_b_podf divider */
> +	tmp_clk = get_axi_a_clk();
> +	div = calc_div(tmp_clk, per_clk, 8);
> +	cbcdr &= ~MXC_CCM_CBCDR_AXI_A_PODF_MASK;
> +	cbcdr |= (div << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET);
> +
> +	/* ahb podf divider */
> +	tmp_clk = AHB_CLK_ROOT;
> +	div = calc_div(tmp_clk, per_clk, 8);
> +	cbcdr &= ~MXC_CCM_CBCDR_AHB_PODF_MASK;
> +	cbcdr |= (div << MXC_CCM_CBCDR_AHB_PODF_OFFSET);
> +
> +	return cbcdr;
> +}
> +
> +#define CHANGE_PLL_SETTINGS(pll, pd, fi, fn, fd) \
> +	{	\
> +		__raw_writel(0x1232, &pll->ctrl);		\
> +		__raw_writel(0x2, &pll->config);		\
> +		__raw_writel((((pd) - 1) << 0) | ((fi) << 4),	\
> +			&pll->op);				\
> +		__raw_writel(fn, &(pll->mfn));			\
> +		__raw_writel((fd) - 1, &pll->mfd);		\
> +		__raw_writel((((pd) - 1) << 0) | ((fi) << 4),	\
> +			&pll->hfs_op);				\
> +		__raw_writel(fn, &pll->hfs_mfn);		\
> +		__raw_writel((fd) - 1, &pll->hfs_mfd);		\
> +		__raw_writel(0x1232, &pll->ctrl);		\
> +		while (!__raw_readl(&pll->ctrl) & 0x1)		\
> +			;\
> +	}
> +
> +static int config_pll_clk(enum pll_clocks index, struct pll_param *pll_param)
> +{
> +	u32 ccsr = __raw_readl(&mxc_ccm->ccsr);
> +	struct mxc_pll_reg *pll = mxc_plls[index];
> +
> +	switch (index) {
> +	case PLL1_CLOCK:
> +		/* Switch ARM to PLL2 clock */
> +		__raw_writel(ccsr | 0x4, &mxc_ccm->ccsr);
> +		CHANGE_PLL_SETTINGS(pll, pll_param->pd,
> +					pll_param->mfi, pll_param->mfn,
> +					pll_param->mfd);
> +		/* Switch back */
> +		__raw_writel(ccsr & ~0x4, &mxc_ccm->ccsr);
> +		break;
> +	case PLL2_CLOCK:
> +		/* Switch to pll2 bypass clock */
> +		__raw_writel(ccsr | 0x2, &mxc_ccm->ccsr);
> +		CHANGE_PLL_SETTINGS(pll, pll_param->pd,
> +					pll_param->mfi, pll_param->mfn,
> +					pll_param->mfd);
> +		/* Switch back */
> +		__raw_writel(ccsr & ~0x2, &mxc_ccm->ccsr);
> +		break;
> +	case PLL3_CLOCK:
> +		/* Switch to pll3 bypass clock */
> +		__raw_writel(ccsr | 0x1, &mxc_ccm->ccsr);
> +		CHANGE_PLL_SETTINGS(pll, pll_param->pd,
> +					pll_param->mfi, pll_param->mfn,
> +					pll_param->mfd);
> +		/* Switch back */
> +		__raw_writel(ccsr & ~0x1, &mxc_ccm->ccsr);
> +		break;
> +	case PLL4_CLOCK:
> +		/* Switch to pll4 bypass clock */
> +		__raw_writel(ccsr | 0x20, &mxc_ccm->ccsr);
> +		CHANGE_PLL_SETTINGS(pll, pll_param->pd,
> +					pll_param->mfi, pll_param->mfn,
> +					pll_param->mfd);
> +		/* Switch back */
> +		__raw_writel(ccsr & ~0x20, &mxc_ccm->ccsr);
> +		break;
> +	default:
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Config CPU clock */
> +static int config_core_clk(u32 ref, u32 freq)
> +{
> +	int ret = 0;
> +	struct pll_param pll_param;
> +
> +	memset(&pll_param, 0, sizeof(struct pll_param));
> +
> +	/* The case that periph uses PLL1 is not considered here */
> +	ret = calc_pll_params(ref, freq, &pll_param);
> +	if (ret != 0) {
> +		printf("Error:Can't find pll parameters: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return config_pll_clk(PLL1_CLOCK, &pll_param);
> +}
> +
> +static int config_nfc_clk(u32 nfc_clk)
> +{
> +	u32 reg;
> +	u32 parent_rate = get_emi_slow_clk();
> +	u32 div = parent_rate / nfc_clk;
> +
> +	if (nfc_clk <= 0)
> +		return -1;
> +	if (div == 0)
> +		div++;
> +	if (parent_rate / div > NFC_CLK_MAX)
> +		div++;
> +	reg = __raw_readl(&mxc_ccm->cbcdr);
> +	reg &= ~MXC_CCM_CBCDR_NFC_PODF_MASK;
> +	reg |= (div - 1) << MXC_CCM_CBCDR_NFC_PODF_OFFSET;
> +	__raw_writel(reg, &mxc_ccm->cbcdr);
> +	while (__raw_readl(&mxc_ccm->cdhipr) != 0)
> +		;
> +	return 0;
> +}
> +
> +/* Config main_bus_clock for periphs */
> +static int config_periph_clk(u32 ref, u32 freq)
> +{
> +	int ret = 0;
> +	struct pll_param pll_param;
> +
> +	memset(&pll_param, 0, sizeof(struct pll_param));
> +
> +	if (__raw_readl(&mxc_ccm->cbcdr) & MXC_CCM_CBCDR_PERIPH_CLK_SEL) {
> +		ret = calc_pll_params(ref, freq, &pll_param);
> +		if (ret != 0) {
> +			printf("Error:Can't find pll parameters: %d\n",
> +				ret);
> +			return ret;
> +		}
> +		switch ((__raw_readl(&mxc_ccm->cbcmr) & \
> +			MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK) >> \
> +			MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET) {
> +		case 0:
> +			return config_pll_clk(PLL1_CLOCK, &pll_param);
> +			break;
> +		case 1:
> +			return config_pll_clk(PLL3_CLOCK, &pll_param);
> +			break;
> +		default:
> +			return -1;
> +		}
> +	} else {
> +		u32 old_cbcmr = __raw_readl(&mxc_ccm->cbcmr);
> +		u32 new_cbcdr = calc_per_cbcdr_val(freq);
> +		u32 old_nfc = get_nfc_clk();
> +
> +		/* Switch peripheral to PLL3 */
> +		__raw_writel(0x00015154, &mxc_ccm->cbcmr);
> +		__raw_writel(0x02888945, &mxc_ccm->cbcdr);

Can we set constants for these ?

> +
> +		/* Make sure change is effective */
> +		while (__raw_readl(&mxc_ccm->cdhipr) != 0)
> +			;
> +
> +		/* Setup PLL2 */
> +		ret = calc_pll_params(ref, freq, &pll_param);
> +		if (ret != 0) {
> +			printf("Error:Can't find pll parameters: %d\n",
> +				ret);
> +			return ret;
> +		}
> +		config_pll_clk(PLL2_CLOCK, &pll_param);
> +
> +		/* Switch peripheral back */
> +		__raw_writel(new_cbcdr, &mxc_ccm->cbcdr);
> +		__raw_writel(old_cbcmr, &mxc_ccm->cbcmr);
> +
> +		/* Make sure change is effective */
> +		while (__raw_readl(&mxc_ccm->cdhipr) != 0)
> +			;
> +		/* restore to old NFC clock */
> +		config_nfc_clk(old_nfc);
> +	}
> +
> +	return 0;
> +}
> +
> +static int config_ddr_clk(u32 emi_clk)
> +{
> +	u32 clk_src;
> +	s32 shift = 0, clk_sel, div = 1;
> +	u32 cbcmr = __raw_readl(&mxc_ccm->cbcmr);
> +	u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr);
> +
> +	if (emi_clk > MAX_DDR_CLK) {
> +		printf("Warning:DDR clock should not exceed %d MHz\n",
> +			MAX_DDR_CLK / SZ_DEC_1M);
> +		emi_clk = MAX_DDR_CLK;
> +	}
> +
> +	clk_src = get_periph_clk();
> +	/* Find DDR clock input */
> +	clk_sel = (cbcmr >> 10) & 0x3;
> +	switch (clk_sel) {
> +	case 0:
> +		shift = 16;
> +		break;
> +	case 1:
> +		shift = 19;
> +		break;
> +	case 2:
> +		shift = 22;
> +		break;
> +	case 3:
> +		shift = 10;
> +		break;
> +	default:
> +		return -1;
> +	}
> +
> +	if ((clk_src % emi_clk) < 10000000)
> +		div = clk_src / emi_clk;
> +	else
> +		div = (clk_src / emi_clk) + 1;
> +	if (div > 8)
> +		div = 8;
> +
> +	cbcdr = cbcdr & ~(0x7 << shift);
> +	cbcdr |= ((div - 1) << shift);
> +	__raw_writel(cbcdr, &mxc_ccm->cbcdr);
> +	while (__raw_readl(&mxc_ccm->cdhipr) != 0)
> +		;
> +	__raw_writel(0x0, &mxc_ccm->ccdr);
> +
> +	return 0;
> +}
> +
> +/*!
Drop the "!"

Best regards,
Stefano Babic

-- 
=====================================================================
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80  Email: office at denx.de
=====================================================================

      parent reply	other threads:[~2012-03-19 15:27 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-16 20:21 [U-Boot] [PATCH] mx5: Add clock config interface Fabio Estevam
2012-03-16 20:21 ` [U-Boot] [RFC] mx53loco: Add 1GHz support Fabio Estevam
2012-03-16 21:01   ` Fabio Estevam
2012-03-17  8:23     ` stefano babic
2012-03-17 14:30       ` Fabio Estevam
2012-03-17 15:42         ` stefano babic
2012-03-19 15:27 ` Stefano Babic [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4F67504B.70303@denx.de \
    --to=sbabic@denx.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.