/**************************************************************************
* 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