/** * 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; }
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; }
/* * 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(); }