/**
 * Setup a new frequency to the IOCLK domain.
 * @param index 0 or 1 for ioclk0 or ioclock1
 * @param nc New frequency in [Hz]
 *
 * The FRAC divider for the IOCLK must be between 18 (* 18/18) and 35 (* 18/35)
 *
 * ioclock0 is the shared clock source of SSP0/SSP1, ioclock1 the shared clock
 * source of SSP2/SSP3
 */
unsigned imx_set_ioclk(unsigned index, unsigned nc)
{
	uint32_t reg;
	unsigned div;

	nc /= 1000;
	div = (imx_get_mpllclk() / 1000) * 18;
	div = DIV_ROUND_CLOSEST(div, nc);
	if (div > 0x3f)
		div = 0x3f;

	switch (index) {
	case 0:
		reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0) &
				~(SET_IO0FRAC(0x3f));
		/* mask the current settings */
		writel(reg | SET_IO0FRAC(div), IMX_CCM_BASE + HW_CLKCTRL_FRAC0);
		/* enable the IO clock at its new frequency */
		writel(CLKCTRL_FRAC_CLKGATEIO0,
				IMX_CCM_BASE + HW_CLKCTRL_FRAC0 + BIT_CLR);
		break;
	case 1:
		reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0) &
				~(SET_IO1FRAC(0x3f));
		/* mask the current settings */
		writel(reg | SET_IO1FRAC(div), IMX_CCM_BASE + HW_CLKCTRL_FRAC0);
		/* enable the IO clock at its new frequency */
		writel(CLKCTRL_FRAC_CLKGATEIO1,
				IMX_CCM_BASE + HW_CLKCTRL_FRAC0 + BIT_CLR);
		break;
	}

	return imx_get_ioclk(index);
}
/*
 * Source of ssp, gpmi, ir
 * @param index 0 or 1 for ioclk0 or ioclock1
 */
unsigned imx_get_ioclk(unsigned index)
{
	uint32_t reg;
	unsigned rate = imx_get_mpllclk() / 1000;

	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0);
	switch (index) {
	case 0:
		if (reg & CLKCTRL_FRAC_CLKGATEIO0)
			return 0;	/* clock is off */

		rate *= 18;
		rate /= GET_IO0FRAC(reg);
		break;
	case 1:
		if (reg & CLKCTRL_FRAC_CLKGATEIO1)
			return 0;	/* clock is off */

		rate *= 18;
		rate /= GET_IO1FRAC(reg);
		break;
	}

	return rate * 1000;
}
unsigned long imx_get_armclk(void)
{
	unsigned long rate, cctl;

	cctl = readl(IMX_CCM_BASE + CCM_CCTL);
	rate = imx_get_mpllclk();

	if (cctl & (1 << 14)) {
		rate *= 3;
		rate >>= 2;
	}
void imx_dump_clocks(void)
{
	printf("mpll:    %10u kHz\n", imx_get_mpllclk() / 1000);
	printf("arm:     %10u kHz\n", imx_get_armclk() / 1000);
	printf("ioclk0:  %10u kHz\n", imx_get_ioclk(0) / 1000);
	printf("ioclk1:  %10u kHz\n", imx_get_ioclk(1) / 1000);
	printf("emiclk:  %10u kHz\n", imx_get_emiclk() / 1000);
	printf("hclk:    %10u kHz\n", imx_get_hclk() / 1000);
	printf("xclk:    %10u kHz\n", imx_get_xclk() / 1000);
	printf("ssp0:    %10u kHz\n", imx_get_sspclk(0) / 1000);
	printf("ssp1:    %10u kHz\n", imx_get_sspclk(1) / 1000);
	printf("ssp2:    %10u kHz\n", imx_get_sspclk(2) / 1000);
	printf("ssp3:    %10u kHz\n", imx_get_sspclk(3) / 1000);
}
/* this is CPU core clock */
unsigned imx_get_armclk(void)
{
	uint32_t reg;
	unsigned rate;

	if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_CPU)
		return imx_get_xtalclk() /
			GET_CPU_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU));

	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0);
	if (reg & CLKCTRL_FRAC_CLKGATECPU)
		return 0;	/* should not possible, shouldn't it? */

	rate = (imx_get_mpllclk() / 1000) * 18;
	rate /= GET_CPUFRAC(reg);

	return (rate / GET_CPU_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU)))
			* 1000;
}
Exemple #6
0
unsigned imx_get_lcdifclk(void)
{
	unsigned rate = (imx_get_mpllclk() / 1000) * 18U;
	unsigned div;

	div = GET_PIXFRAC(readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC));
	if (div != 0U) {
		rate /= div;
		div = GET_DIS_LCDIF_DIV(readl(IMX_CCM_BASE +
							HW_CLKCTRL_DIS_LCDIF));
		if (div != 0U)
			rate /= div;
		else
			pr_debug("LCDIF clock has divisor 0!\n");
	} else
		pr_debug("LCDIF clock has frac divisor 0!\n");

	return rate * 1000;
}
/* used for the SDRAM controller */
unsigned imx_get_emiclk(void)
{
	uint32_t reg;
	unsigned rate;

	if (readl(IMX_CCM_BASE + HW_CLKCTRL_EMI) & CLKCTRL_EMI_CLKGATE)
		return 0;	/* clock is off */

	if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_EMI)
		return imx_get_xtalclk() /
			GET_EMI_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI));

	rate = imx_get_mpllclk() / 1000;
	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0);
	if (!(reg & CLKCTRL_FRAC_CLKGATEEMI)) {
		rate *= 18;
		rate /= GET_EMIFRAC(reg);
	}

	return (rate / GET_EMI_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI)))
				* 1000;
}
Exemple #8
0
/*
 * The source of the pixel clock can be the external 24 MHz crystal or the
 * internal PLL running at 480 MHz. In order to support at least VGA sized
 * displays/resolutions this routine forces the PLL as the clock source.
 */
unsigned imx_set_lcdifclk(unsigned nc)
{
	unsigned frac, best_frac = 0, div, best_div = 0, result;
	int delta, best_delta = 0xffffff;
	unsigned i, parent_rate = imx_get_mpllclk() / 1000;
	uint32_t reg;

#define SH_DIV(NOM, DEN, LSH) ((((NOM) / (DEN)) << (LSH)) + \
			DIV_ROUND_CLOSEST(((NOM) % (DEN)) << (LSH), DEN))
#define SHIFT 4

	nc /= 1000;
	nc <<= SHIFT;

	for (frac = 18; frac <= 35; ++frac) {
		for (div = 1; div <= 255; ++div) {
			result = DIV_ROUND_CLOSEST(parent_rate *
						SH_DIV(18U, frac, SHIFT), div);
			delta = nc - result;
			if (abs(delta) < abs(best_delta)) {
				best_delta = delta;
				best_frac = frac;
				best_div = div;
			}
		}
	}

	if (best_delta == 0xffffff) {
		pr_debug("Unable to match the pixelclock\n");
		return 0;
	}

	pr_debug("Programming PFD=%u,DIV=%u ref_pix=%u MHz PIXCLK=%u kHz\n",
			best_frac, best_div, 480 * 18 / best_frac,
			480000 * 18 / best_frac / best_div);

	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC);
	reg &= ~SET_PIXFRAC(MASK_PIXFRAC);
	reg |= SET_PIXFRAC(best_frac);
	writel(reg, IMX_CCM_BASE + HW_CLKCTRL_FRAC);
	writel(reg & ~CLKCTRL_FRAC_CLKGATEPIX, IMX_CCM_BASE + HW_CLKCTRL_FRAC);

	reg = readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) & ~MASK_DIS_LCDIF_DIV;
	reg &= ~CLKCTRL_DIS_LCDIF_GATE;
	reg |= SET_DIS_LCDIF_DIV(best_div);
	writel(reg, IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF);

	/* Wait for divider update */
	for (i = 0; i < 10000; i++) {
		if (!(readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) &
						CLKCTRL_DIS_LCDIF_BUSY))
			break;
	}

	if (i >= 10000) {
		pr_debug("Setting LCD clock failed\n");
		return 0;
	}

	writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF,
		IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_CLR);

	return imx_get_lcdifclk();
}