I.MX6 PLL5 clock hakcing
/************************************************************************** * I.MX6 PLL5 clock hakcing * 说明: * 由于需要调整I.MX6的PLL5工作clock,所以需要了解I.MX6的PLL5 clock设置 * 细节。 * * 2016-7-12 深圳 南山平山村 曾剑锋 *************************************************************************/ cat arch/arm/mach-mx6/board-mx6q_sabresd.c /* * initialize __mach_desc_MX6Q_SABRESD data structure. */ MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board") /* Maintainer: Freescale Semiconductor, Inc. */ .boot_params = MX6_PHYS_OFFSET + 0x100, .fixup = fixup_mxc_board, .map_io = mx6_map_io, .init_irq = mx6_init_irq, .init_machine = mx6_sabresd_board_init, .timer = &mx6_sabresd_timer, --------------+ .reserve = mx6q_sabresd_reserve, | MACHINE_END | | static struct sys_timer mx6_sabresd_timer = { <-------+ .init = mx6_sabresd_timer_init, --------+ }; | | static void __init mx6_sabresd_timer_init(void) <-----+ { struct clk *uart_clk; #ifdef CONFIG_LOCAL_TIMERS twd_base = ioremap(LOCAL_TWD_ADDR, SZ_256); BUG_ON(!twd_base); #endif mx6_clocks_init(32768, 24000000, 0, 0); ----------------------+ | uart_clk = clk_get_sys("imx-uart.0", NULL); | early_console_setup(UART1_BASE_ADDR, uart_clk); | } | | int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, <----+ unsigned long ckih1, unsigned long ckih2) { __iomem void *base; int i, reg; u32 parent_rate, rate; unsigned long ipg_clk_rate, max_arm_wait_clk; external_low_reference = ckil; external_high_reference = ckih1; ckih2_reference = ckih2; oscillator_reference = osc; timer_base = ioremap(GPT_BASE_ADDR, SZ_4K); apll_base = ioremap(ANATOP_BASE_ADDR, SZ_4K); for (i = 0; i < ARRAY_SIZE(lookups); i++) { clkdev_add(&lookups[i]); clk_debug_register(lookups[i].clk); } /* Lower the ipg_perclk frequency to 22MHz. * I2C needs a minimum of 12.8MHz as its source * to acheive 400KHz speed. IPG_PERCLK sources * I2C. 22MHz when divided by the I2C divider gives the * freq closest to 400KHz. */ clk_set_rate(&ipg_perclk, 22000000); /* Timer needs to be initialized first as the * the WAIT routines use GPT counter as * a delay. */ if (mx6q_revision() == IMX_CHIP_REVISION_1_0) { gpt_clk[0].parent = &ipg_perclk; gpt_clk[0].get_rate = NULL; } else { /* Here we use OSC 24M as GPT's clock source, no need to enable gpt serial clock*/ gpt_clk[0].secondary = NULL; } mxc_timer_init(&gpt_clk[0], timer_base, MXC_INT_GPT); clk_tree_init(); #ifdef CONFIG_MX6_VPU_352M if (cpu_is_mx6q()) { clk_set_rate(&pll2_pfd_400M, 352000000); clk_set_parent(&vpu_clk[0], &pll2_pfd_400M); } #endif /* keep correct count. */ clk_enable(&cpu_clk); clk_enable(&periph_clk); /* Disable un-necessary PFDs & PLLs */ if (pll2_pfd_400M.usecount == 0 && cpu_is_mx6q()) pll2_pfd_400M.disable(&pll2_pfd_400M); #ifndef CONFIG_MX6_CLK_FOR_BOOTUI_TRANS /* * Bootloader may use pll2_pfd_352M to drive ldb_di1_clk * to support splashimage so we should not disable the * clock to keep the display running. */ pll2_pfd_352M.disable(&pll2_pfd_352M); #endif pll2_pfd_594M.disable(&pll2_pfd_594M); #if !defined(CONFIG_FEC_1588) pll3_pfd_454M.disable(&pll3_pfd_454M); pll3_pfd_508M.disable(&pll3_pfd_508M); pll3_pfd_720M.disable(&pll3_pfd_720M); if (cpu_is_mx6q()) { pll3_pfd_540M.disable(&pll3_pfd_540M); pll3_usb_otg_main_clk.disable(&pll3_usb_otg_main_clk); } else if (cpu_is_mx6dl()) { #ifndef CONFIG_MX6_CLK_FOR_BOOTUI_TRANS /* * Bootloader may use pll3_pfd_540M to drive ipu1_clk * to support splashimage so we should not disable the * clock to keep the display running. */ pll3_pfd_540M.disable(&pll3_pfd_540M); pll3_usb_otg_main_clk.disable(&pll3_usb_otg_main_clk); #endif } #endif pll4_audio_main_clk.disable(&pll4_audio_main_clk); pll5_video_main_clk.disable(&pll5_video_main_clk); pll6_mlb150_main_clk.disable(&pll6_mlb150_main_clk); pll7_usb_host_main_clk.disable(&pll7_usb_host_main_clk); pll8_enet_main_clk.disable(&pll8_enet_main_clk); sata_clk[0].disable(&sata_clk[0]); pcie_clk[0].disable(&pcie_clk[0]); /* Initialize Audio and Video PLLs to valid frequency. */ clk_set_rate(&pll4_audio_main_clk, 176000000); clk_set_rate(&pll5_video_main_clk, 650000000); ----------------------+ | /* | * We don't set ipu1_di_clk[1]'s parent clock to | * pll5_video_main_clk as bootloader may need | * the parent to be ldb_di1_clk to support LVDS | * panel splashimage. | */ | clk_set_parent(&ipu1_di_clk[0], &pll5_video_main_clk); | #ifndef CONFIG_MX6_CLK_FOR_BOOTUI_TRANS | clk_set_parent(&ipu1_di_clk[1], &pll5_video_main_clk); | #endif | clk_set_parent(&ipu2_di_clk[0], &pll5_video_main_clk); | clk_set_parent(&ipu2_di_clk[1], &pll5_video_main_clk); | | clk_set_parent(&emi_clk, &pll2_pfd_400M); | clk_set_rate(&emi_clk, 200000000); | | /* | * on mx6dl, 2d core clock sources from 3d shader core clock, | * but 3d shader clock multiplexer of mx6dl is different from | * mx6q. For instance the equivalent of pll2_pfd_594M on mc6q | * is pll2_pfd_528M on mx6dl. Make a note here. | */ | clk_set_parent(&gpu3d_shader_clk, &pll2_pfd_594M); | clk_set_rate(&gpu3d_shader_clk, 594000000); | if (cpu_is_mx6dl()) { | /*for mx6dl, change gpu3d core clk parant to 594_PFD */ | clk_set_parent(&gpu3d_core_clk[0], &pll2_pfd_594M); | clk_set_rate(&gpu3d_core_clk[0], 594000000); | | /*on mx6dl, 2d core clock sources from 3d shader core clock*/ | clk_set_parent(&gpu2d_core_clk[0], &gpu3d_shader_clk); | /* on mx6dl gpu3d_axi_clk source from mmdc0 directly */ | clk_set_parent(&gpu3d_axi_clk, &mmdc_ch0_axi_clk[0]); | /* on mx6dl gpu2d_axi_clk source from mmdc0 directly */ | clk_set_parent(&gpu2d_axi_clk, &mmdc_ch0_axi_clk[0]); | | clk_set_rate(&pll3_pfd_540M, 540000000); | | clk_set_parent(&ipu1_clk, &pll3_pfd_540M); | /* pxp & epdc */ | clk_set_parent(&ipu2_clk, &pll2_pfd_400M); | clk_set_rate(&ipu2_clk, 200000000); | clk_set_parent(&axi_clk, &pll3_pfd_540M); | } else if (cpu_is_mx6q()) { | clk_set_parent(&gpu3d_core_clk[0], &mmdc_ch0_axi_clk[0]); | clk_set_rate(&gpu3d_core_clk[0], 528000000); | clk_set_parent(&ipu2_clk, &mmdc_ch0_axi_clk[0]); | clk_set_parent(&ipu1_clk, &mmdc_ch0_axi_clk[0]); | clk_set_parent(&axi_clk, &periph_clk); | } | | /* Need to keep PLL3_PFD_540M enabled until AXI is sourced from it. */ | clk_enable(&axi_clk); | | if (cpu_is_mx6q()) | clk_set_parent(&gpu2d_core_clk[0], &pll3_usb_otg_main_clk); | | clk_set_parent(&ldb_di0_clk, &pll2_pfd_352M); | clk_set_parent(&ldb_di1_clk, &pll2_pfd_352M); | | /* PCLK camera - J5 */ | clk_set_parent(&clko2_clk, &osc_clk); | clk_set_rate(&clko2_clk, 2400000); | | clk_set_parent(&clko_clk, &pll4_audio_main_clk); | /* | * FIXME: asrc needs to use asrc_serial(spdif1) clock to do sample | * rate convertion and this clock frequency can not be too high, set | * it to the minimum value 7.5Mhz to make asrc work properly. | */ | clk_set_parent(&asrc_clk[1], &pll3_sw_clk); | clk_set_rate(&asrc_clk[1], 7500000); | | /* set the GPMI clock to default frequency : 20MHz */ | clk_set_parent(&enfc_clk, &pll2_pfd_400M); | clk_set_rate(&enfc_clk, enfc_clk.round_rate(&enfc_clk, 20000000)); | | mx6_cpu_op_init(); | cpu_op_tbl = get_cpu_op(&cpu_op_nr); | | /* Gate off all possible clocks */ | if (mxc_jtag_enabled) { | __raw_writel(3 << MXC_CCM_CCGRx_CG11_OFFSET | | 3 << MXC_CCM_CCGRx_CG2_OFFSET | | 3 << MXC_CCM_CCGRx_CG1_OFFSET | | 3 << MXC_CCM_CCGRx_CG0_OFFSET, MXC_CCM_CCGR0); | } else { | __raw_writel(1 << MXC_CCM_CCGRx_CG11_OFFSET | | 3 << MXC_CCM_CCGRx_CG2_OFFSET | | 3 << MXC_CCM_CCGRx_CG1_OFFSET | | 3 << MXC_CCM_CCGRx_CG0_OFFSET, MXC_CCM_CCGR0); | } | if (mx6q_revision() == IMX_CHIP_REVISION_1_0) | /* If GPT use ipg_perclk, we need to enable gpt serial clock */ | __raw_writel(3 << MXC_CCM_CCGRx_CG10_OFFSET | | 3 << MXC_CCM_CCGRx_CG11_OFFSET, MXC_CCM_CCGR1); | else | __raw_writel(3 << MXC_CCM_CCGRx_CG10_OFFSET, MXC_CCM_CCGR1); | __raw_writel(1 << MXC_CCM_CCGRx_CG12_OFFSET | | 1 << MXC_CCM_CCGRx_CG11_OFFSET | | 3 << MXC_CCM_CCGRx_CG10_OFFSET | | 3 << MXC_CCM_CCGRx_CG9_OFFSET | | 3 << MXC_CCM_CCGRx_CG8_OFFSET, MXC_CCM_CCGR2); | __raw_writel(1 << MXC_CCM_CCGRx_CG14_OFFSET | | 1 << MXC_CCM_CCGRx_CG13_OFFSET | | 3 << MXC_CCM_CCGRx_CG12_OFFSET | | 1 << MXC_CCM_CCGRx_CG11_OFFSET | | #ifdef CONFIG_MX6_CLK_FOR_BOOTUI_TRANS | /* | * We use IPU1 DI1 to do bootloader splashimage by | * default, so we need to enable the clocks to | * keep the display running. | */ | 3 << MXC_CCM_CCGRx_CG7_OFFSET | /* ldb_di1_clk */ | 3 << MXC_CCM_CCGRx_CG2_OFFSET | /* ipu1_di1_clk */ | 3 << MXC_CCM_CCGRx_CG0_OFFSET | /* ipu1_clk */ | #endif | 3 << MXC_CCM_CCGRx_CG10_OFFSET, MXC_CCM_CCGR3); | __raw_writel(3 << MXC_CCM_CCGRx_CG7_OFFSET | | #ifdef CONFIG_MX6_CLK_FOR_BOOTUI_TRANS | /* | * We use pwm1 to drive LVDS panel pwm backlight | * to support bootloader splashimage by default, | * so we need to enable the clock to keep the | * backlight on. | */ | (machine_is_mx6q_sabresd() ? | (3 << MXC_CCM_CCGRx_CG8_OFFSET) : 0) | /* pwm1_clk */ | #endif | 1 << MXC_CCM_CCGRx_CG6_OFFSET | | 1 << MXC_CCM_CCGRx_CG4_OFFSET, MXC_CCM_CCGR4); | __raw_writel(1 << MXC_CCM_CCGRx_CG0_OFFSET, MXC_CCM_CCGR5); | | __raw_writel(0, MXC_CCM_CCGR6); | | /* S/PDIF */ | clk_set_parent(&spdif0_clk[0], &pll3_pfd_454M); | | /* MLB150 SYS Clock */ | /* | * In Rigel validatioin, the MLB sys_clock isn't using the | * right frequency after boot. | * In arik, the register CBCMR controls gpu2d clock, not mlb clock, | * mlb is sourced from axi clock. | * But In rigel, the axi clock is lower than in mx6q, so mlb need to | * find a new clock root. | * The gpu2d clock is the root of mlb clock in rigel. | * Thus we need to add below code in mx6dl. | * */ | if (cpu_is_mx6dl()) | clk_set_parent(&mlb150_clk, &pll3_sw_clk); | | | if (cpu_is_mx6dl()) { | /* pxp & epdc */ | clk_set_parent(&ipu2_clk, &pll2_pfd_400M); | clk_set_rate(&ipu2_clk, 200000000); | if (epdc_enabled) | clk_set_parent(&ipu2_di_clk[1], &pll5_video_main_clk); | else | clk_set_parent(&ipu2_di_clk[1], &pll3_pfd_540M); | } | | lp_high_freq = 0; | lp_med_freq = 0; | lp_audio_freq = 0; | | /* Get current ARM_PODF value */ | rate = clk_get_rate(&cpu_clk); | parent_rate = clk_get_rate(&pll1_sw_clk); | cur_arm_podf = parent_rate / rate; | | /* Calculate the ARM_PODF to be applied when the system | * enters WAIT state. | * The max ARM clk is decided by the ipg_clk and has to | * follow the ratio of ARM_CLK:IPG_CLK of 12:5. | */ | ipg_clk_rate = clk_get_rate(&ipg_clk); | max_arm_wait_clk = (12 * ipg_clk_rate) / 5; | wait_mode_arm_podf = parent_rate / max_arm_wait_clk; | | /* Turn OFF all unnecessary PHYs. */ | if (cpu_is_mx6q()) { | /* Turn off SATA PHY. */ | base = ioremap(MX6Q_SATA_BASE_ADDR, SZ_8K); | reg = __raw_readl(base + PORT_PHY_CTL); | __raw_writel(reg | PORT_PHY_CTL_PDDQ_LOC, base + PORT_PHY_CTL); | } | | /* Turn off HDMI PHY. */ | base = ioremap(MX6Q_HDMI_ARB_BASE_ADDR, SZ_128K); | reg = __raw_readb(base + HDMI_PHY_CONF0); | __raw_writeb(reg | HDMI_PHY_CONF0_GEN2_PDDQ_MASK, base + HDMI_PHY_CONF0); | | reg = __raw_readb(base + HDMI_MC_PHYRSTZ); | __raw_writeb(reg | HDMI_MC_PHYRSTZ_DEASSERT, base + HDMI_MC_PHYRSTZ); | | iounmap(base); | | base = ioremap(MX6Q_IOMUXC_BASE_ADDR, SZ_4K); | /* Close PLL inside SATA PHY. */ | reg = __raw_readl(base + 0x34); | __raw_writel(reg | (1 << 1), base + 0x34); | | /* Close PCIE PHY. */ | reg = __raw_readl(base + 0x04); | reg |= (1 << 18); | __raw_writel(reg, base + 0x04); | iounmap(base); | | return 0; | | } | | static struct clk pll5_video_main_clk = { <---------------------+ __INIT_CLK_DEBUG(pll5_video_main_clk) .parent = &osc_clk, .enable = _clk_pll_enable, .disable = _clk_pll_disable, .set_rate = _clk_audio_video_set_rate, ----------+ .get_rate = _clk_audio_video_get_rate, | .round_rate = _clk_audio_video_round_rate, | .set_parent = _clk_audio_video_set_parent, | }; | v---------------------------------------+ static int _clk_audio_video_set_rate(struct clk *clk, unsigned long rate) { unsigned int reg, div; unsigned int mfn, mfd = 1000000; s64 temp64; unsigned int parent_rate = clk_get_rate(clk->parent); void __iomem *pllbase; unsigned long min_clk_rate, pre_div_rate; int rev = mx6q_revision(); u32 test_div_sel = 2; u32 control3 = 0; if ((rev < IMX_CHIP_REVISION_1_1) && !cpu_is_mx6dl()) min_clk_rate = AUDIO_VIDEO_MIN_CLK_FREQ; else if (clk == &pll4_audio_main_clk) min_clk_rate = AUDIO_VIDEO_MIN_CLK_FREQ / 4; else min_clk_rate = AUDIO_VIDEO_MIN_CLK_FREQ / 16; if ((rate < min_clk_rate) || (rate > AUDIO_VIDEO_MAX_CLK_FREQ)) return -EINVAL; if (clk == &pll4_audio_main_clk) pllbase = PLL4_AUDIO_BASE_ADDR; else pllbase = PLL5_VIDEO_BASE_ADDR; ----------------------------+ | pre_div_rate = rate; | if ((rev >= IMX_CHIP_REVISION_1_1) || cpu_is_mx6dl()) { | while (pre_div_rate < AUDIO_VIDEO_MIN_CLK_FREQ) { | pre_div_rate *= 2; | /* | * test_div_sel field values: | * 2 -> Divide by 1 | * 1 -> Divide by 2 | * 0 -> Divide by 4 | * | * control3 field values: | * 0 -> Divide by 1 | * 1 -> Divide by 2 | * 3 -> Divide by 4 | */ | if (test_div_sel != 0) | test_div_sel--; | else { | control3++; | if (control3 == 2) | control3++; | } | } | } | | div = pre_div_rate / parent_rate; | temp64 = (u64) (pre_div_rate - (div * parent_rate)); | temp64 *= mfd; | do_div(temp64, parent_rate); | mfn = temp64; | | reg = __raw_readl(pllbase) | & ~ANADIG_PLL_SYS_DIV_SELECT_MASK | & ~ANADIG_PLL_AV_TEST_DIV_SEL_MASK; | reg |= div | | (test_div_sel << ANADIG_PLL_AV_TEST_DIV_SEL_OFFSET); | __raw_writel(reg, pllbase); | __raw_writel(mfn, pllbase + PLL_NUM_DIV_OFFSET); | __raw_writel(mfd, pllbase + PLL_DENOM_DIV_OFFSET); | | if (rev >= IMX_CHIP_REVISION_1_1) { | reg = __raw_readl(ANA_MISC2_BASE_ADDR) | & ~ANADIG_ANA_MISC2_CONTROL3_MASK; | reg |= control3 << ANADIG_ANA_MISC2_CONTROL3_OFFSET; | __raw_writel(reg, ANA_MISC2_BASE_ADDR); | } | | return 0; | } | | /** | * 1. 参考文档:i.MX 6Solo/6DualLite Applications Processor Reference Manual | * 2. 计算代码中的寄存器地址: | * PLL5_VIDEO_BASE_ADDR = (MXC_PLL_BASE + 0xA0) | * PLL5_VIDEO_BASE_ADDR = (MX6_IO_ADDRESS(ANATOP_BASE_ADDR) + 0xA0) | * PLL5_VIDEO_BASE_ADDR = (MX6_IO_ADDRESS((AIPS1_OFF_BASE_ADDR + | * 0x48000)) + 0xA0) | * PLL5_VIDEO_BASE_ADDR = (MX6_IO_ADDRESS(((ATZ1_BASE_ADDR + 0x80000) | * + 0x48000)) + 0xA0) | * PLL5_VIDEO_BASE_ADDR = (MX6_IO_ADDRESS(((AIPS1_ARB_BASE_ADDR + | * 0x80000) + 0x48000)) + 0xA0) | * PLL5_VIDEO_BASE_ADDR = (MX6_IO_ADDRESS(((0x02000000 + 0x80000) + | * 0x48000)) + 0xA0) | * PLL5_VIDEO_BASE_ADDR = 0x02000000 + 0x80000 + 0x48000 + 0xA0 | * PLL5_VIDEO_BASE_ADDR = 0x020C80A0 | * 3. 参考文档Page 883: | * +----------+---------------------------------------------------------+| * | Absolute | Register name || * | address | || * | (hex) | || * +----------+---------------------------------------------------------+| * | 20C_80A0 | Analog Video PLL control Register (CCM_ANALOG_PLL_VIDEO)|| * +----------+---------------------------------------------------------+| */ | #define PLL5_VIDEO_BASE_ADDR (MXC_PLL_BASE + 0xA0) <----------+ #define MXC_PLL_BASE MX6_IO_ADDRESS(ANATOP_BASE_ADDR) #define ANATOP_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x48000) #define AIPS1_OFF_BASE_ADDR (ATZ1_BASE_ADDR + 0x80000) #define ATZ1_BASE_ADDR AIPS1_ARB_BASE_ADDR #define AIPS1_ARB_BASE_ADDR 0x02000000