|
|
|
|
@@ -1217,6 +1217,157 @@ void enable_ipu_clock(void)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) || defined(CONFIG_MX6DL) || \
|
|
|
|
|
defined(CONFIG_MX6S)
|
|
|
|
|
static void disable_ldb_di_clock_sources(void)
|
|
|
|
|
{
|
|
|
|
|
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
|
|
|
|
|
int reg;
|
|
|
|
|
|
|
|
|
|
/* Make sure PFDs are disabled at boot. */
|
|
|
|
|
reg = readl(&mxc_ccm->analog_pfd_528);
|
|
|
|
|
/* Cannot disable pll2_pfd2_396M, as it is the MMDC clock in iMX6DL */
|
|
|
|
|
if (is_cpu_type(MXC_CPU_MX6DL))
|
|
|
|
|
reg |= 0x80008080;
|
|
|
|
|
else
|
|
|
|
|
reg |= 0x80808080;
|
|
|
|
|
writel(reg, &mxc_ccm->analog_pfd_528);
|
|
|
|
|
|
|
|
|
|
/* Disable PLL3 PFDs */
|
|
|
|
|
reg = readl(&mxc_ccm->analog_pfd_480);
|
|
|
|
|
reg |= 0x80808080;
|
|
|
|
|
writel(reg, &mxc_ccm->analog_pfd_480);
|
|
|
|
|
|
|
|
|
|
/* Disable PLL5 */
|
|
|
|
|
reg = readl(&mxc_ccm->analog_pll_video);
|
|
|
|
|
reg &= ~(1 << 13);
|
|
|
|
|
writel(reg, &mxc_ccm->analog_pll_video);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void enable_ldb_di_clock_sources(void)
|
|
|
|
|
{
|
|
|
|
|
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
|
|
|
|
|
int reg;
|
|
|
|
|
|
|
|
|
|
reg = readl(&mxc_ccm->analog_pfd_528);
|
|
|
|
|
if (is_cpu_type(MXC_CPU_MX6DL))
|
|
|
|
|
reg &= ~(0x80008080);
|
|
|
|
|
else
|
|
|
|
|
reg &= ~(0x80808080);
|
|
|
|
|
writel(reg, &mxc_ccm->analog_pfd_528);
|
|
|
|
|
|
|
|
|
|
reg = readl(&mxc_ccm->analog_pfd_480);
|
|
|
|
|
reg &= ~(0x80808080);
|
|
|
|
|
writel(reg, &mxc_ccm->analog_pfd_480);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Try call this function as early in the boot process as possible since the
|
|
|
|
|
* function temporarily disables PLL2 PFD's, PLL3 PFD's and PLL5.
|
|
|
|
|
*/
|
|
|
|
|
void select_ldb_di_clock_source(enum ldb_di_clock clk)
|
|
|
|
|
{
|
|
|
|
|
struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
|
|
|
|
|
int reg;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Need to follow a strict procedure when changing the LDB
|
|
|
|
|
* clock, else we can introduce a glitch. Things to keep in
|
|
|
|
|
* mind:
|
|
|
|
|
* 1. The current and new parent clocks must be disabled.
|
|
|
|
|
* 2. The default clock for ldb_dio_clk is mmdc_ch1 which has
|
|
|
|
|
* no CG bit.
|
|
|
|
|
* 3. In the RTL implementation of the LDB_DI_CLK_SEL mux
|
|
|
|
|
* the top four options are in one mux and the PLL3 option along
|
|
|
|
|
* with another option is in the second mux. There is third mux
|
|
|
|
|
* used to decide between the first and second mux.
|
|
|
|
|
* The code below switches the parent to the bottom mux first
|
|
|
|
|
* and then manipulates the top mux. This ensures that no glitch
|
|
|
|
|
* will enter the divider.
|
|
|
|
|
*
|
|
|
|
|
* Need to disable MMDC_CH1 clock manually as there is no CG bit
|
|
|
|
|
* for this clock. The only way to disable this clock is to move
|
|
|
|
|
* it to pll3_sw_clk and then to disable pll3_sw_clk
|
|
|
|
|
* Make sure periph2_clk2_sel is set to pll3_sw_clk
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Disable all ldb_di clock parents */
|
|
|
|
|
disable_ldb_di_clock_sources();
|
|
|
|
|
|
|
|
|
|
/* Set MMDC_CH1 mask bit */
|
|
|
|
|
reg = readl(&mxc_ccm->ccdr);
|
|
|
|
|
reg |= MXC_CCM_CCDR_MMDC_CH1_HS_MASK;
|
|
|
|
|
writel(reg, &mxc_ccm->ccdr);
|
|
|
|
|
|
|
|
|
|
/* Set periph2_clk2_sel to be sourced from PLL3_sw_clk */
|
|
|
|
|
reg = readl(&mxc_ccm->cbcmr);
|
|
|
|
|
reg &= ~MXC_CCM_CBCMR_PERIPH2_CLK2_SEL;
|
|
|
|
|
writel(reg, &mxc_ccm->cbcmr);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set the periph2_clk_sel to the top mux so that
|
|
|
|
|
* mmdc_ch1 is from pll3_sw_clk.
|
|
|
|
|
*/
|
|
|
|
|
reg = readl(&mxc_ccm->cbcdr);
|
|
|
|
|
reg |= MXC_CCM_CBCDR_PERIPH2_CLK_SEL;
|
|
|
|
|
writel(reg, &mxc_ccm->cbcdr);
|
|
|
|
|
|
|
|
|
|
/* Wait for the clock switch */
|
|
|
|
|
while (readl(&mxc_ccm->cdhipr))
|
|
|
|
|
;
|
|
|
|
|
/* Disable pll3_sw_clk by selecting bypass clock source */
|
|
|
|
|
reg = readl(&mxc_ccm->ccsr);
|
|
|
|
|
reg |= MXC_CCM_CCSR_PLL3_SW_CLK_SEL;
|
|
|
|
|
writel(reg, &mxc_ccm->ccsr);
|
|
|
|
|
|
|
|
|
|
/* Set the ldb_di0_clk and ldb_di1_clk to 111b */
|
|
|
|
|
reg = readl(&mxc_ccm->cs2cdr);
|
|
|
|
|
reg |= ((7 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
|
|
|
|
|
| (7 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
|
|
|
|
|
writel(reg, &mxc_ccm->cs2cdr);
|
|
|
|
|
|
|
|
|
|
/* Set the ldb_di0_clk and ldb_di1_clk to 100b */
|
|
|
|
|
reg = readl(&mxc_ccm->cs2cdr);
|
|
|
|
|
reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK
|
|
|
|
|
| MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK);
|
|
|
|
|
reg |= ((4 << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
|
|
|
|
|
| (4 << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
|
|
|
|
|
writel(reg, &mxc_ccm->cs2cdr);
|
|
|
|
|
|
|
|
|
|
/* Set the ldb_di0_clk and ldb_di1_clk to desired source */
|
|
|
|
|
reg = readl(&mxc_ccm->cs2cdr);
|
|
|
|
|
reg &= ~(MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK
|
|
|
|
|
| MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK);
|
|
|
|
|
reg |= ((clk << MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET)
|
|
|
|
|
| (clk << MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_OFFSET));
|
|
|
|
|
writel(reg, &mxc_ccm->cs2cdr);
|
|
|
|
|
|
|
|
|
|
/* Unbypass pll3_sw_clk */
|
|
|
|
|
reg = readl(&mxc_ccm->ccsr);
|
|
|
|
|
reg &= ~MXC_CCM_CCSR_PLL3_SW_CLK_SEL;
|
|
|
|
|
writel(reg, &mxc_ccm->ccsr);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set the periph2_clk_sel back to the bottom mux so that
|
|
|
|
|
* mmdc_ch1 is from its original parent.
|
|
|
|
|
*/
|
|
|
|
|
reg = readl(&mxc_ccm->cbcdr);
|
|
|
|
|
reg &= ~MXC_CCM_CBCDR_PERIPH2_CLK_SEL;
|
|
|
|
|
writel(reg, &mxc_ccm->cbcdr);
|
|
|
|
|
|
|
|
|
|
/* Wait for the clock switch */
|
|
|
|
|
while (readl(&mxc_ccm->cdhipr))
|
|
|
|
|
;
|
|
|
|
|
/* Clear MMDC_CH1 mask bit */
|
|
|
|
|
reg = readl(&mxc_ccm->ccdr);
|
|
|
|
|
reg &= ~MXC_CCM_CCDR_MMDC_CH1_HS_MASK;
|
|
|
|
|
writel(reg, &mxc_ccm->ccdr);
|
|
|
|
|
|
|
|
|
|
enable_ldb_di_clock_sources();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/***************************************************/
|
|
|
|
|
|
|
|
|
|
U_BOOT_CMD(
|
|
|
|
|
|