diff options
Diffstat (limited to 'drivers/clk/imx/clk-imx6q.c')
-rw-r--r-- | drivers/clk/imx/clk-imx6q.c | 93 |
1 files changed, 81 insertions, 12 deletions
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index bbe0c60f4d09..716eac3136b4 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -225,6 +225,41 @@ static void of_assigned_ldb_sels(struct device_node *node, } } +static bool pll6_bypassed(struct device_node *node) +{ + int index, ret, num_clocks; + struct of_phandle_args clkspec; + + num_clocks = of_count_phandle_with_args(node, "assigned-clocks", + "#clock-cells"); + if (num_clocks < 0) + return false; + + for (index = 0; index < num_clocks; index++) { + ret = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, + &clkspec); + if (ret < 0) + return false; + + if (clkspec.np == node && + clkspec.args[0] == IMX6QDL_PLL6_BYPASS) + break; + } + + /* PLL6 bypass is not part of the assigned clock list */ + if (index == num_clocks) + return false; + + ret = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + + if (clkspec.args[0] != IMX6QDL_CLK_PLL6) + return true; + + return false; +} + #define CCM_CCDR 0x04 #define CCM_CCSR 0x0c #define CCM_CS2CDR 0x2c @@ -414,12 +449,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) int ret; clk[IMX6QDL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); - clk[IMX6QDL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); - clk[IMX6QDL_CLK_CKIH] = imx_obtain_fixed_clock("ckih1", 0); - clk[IMX6QDL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); + clk[IMX6QDL_CLK_CKIL] = of_clk_get_by_name(ccm_node, "ckil"); + if (IS_ERR(clk[IMX6QDL_CLK_CKIL])) + clk[IMX6QDL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); + clk[IMX6QDL_CLK_CKIH] = of_clk_get_by_name(ccm_node, "ckih1"); + if (IS_ERR(clk[IMX6QDL_CLK_CKIH])) + clk[IMX6QDL_CLK_CKIH] = imx_obtain_fixed_clock("ckih1", 0); + clk[IMX6QDL_CLK_OSC] = of_clk_get_by_name(ccm_node, "osc"); + if (IS_ERR(clk[IMX6QDL_CLK_OSC])) + clk[IMX6QDL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); + /* Clock source from external clock via CLK1/2 PADs */ - clk[IMX6QDL_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0); - clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0); + clk[IMX6QDL_CLK_ANACLK1] = of_clk_get_by_name(ccm_node, "anaclk1"); + if (IS_ERR(clk[IMX6QDL_CLK_ANACLK1])) + clk[IMX6QDL_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0); + + clk[IMX6QDL_CLK_ANACLK2] = of_clk_get_by_name(ccm_node, "anaclk2"); + if (IS_ERR(clk[IMX6QDL_CLK_ANACLK2])) + clk[IMX6QDL_CLK_ANACLK2] = imx_obtain_fixed_clock("anaclk2", 0); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = base = of_iomap(np, 0); @@ -491,16 +538,32 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); clk[IMX6QDL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); - clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); - clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + /* + * The ENET PLL is special in that is has multiple outputs with + * different post-dividers that are all affected by the single bypass + * bit, so a single mux bit affects 3 independent branches of the clock + * tree. There is no good way to model this in the clock framework and + * dynamically changing the bypass bit, will yield unexpected results. + * So we treat any configuration that bypasses the ENET PLL as + * essentially static with the divider ratios reflecting the bypass + * status. + * + */ + if (!pll6_bypassed(ccm_node)) { + clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); + clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + clk[IMX6QDL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + } else { + clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 1); + clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 1); + clk[IMX6QDL_CLK_ENET_REF] = imx_clk_fixed_factor("enet_ref", "pll6_enet", 1, 1); + } clk[IMX6QDL_CLK_SATA_REF_100M] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); clk[IMX6QDL_CLK_PCIE_REF_125M] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); - clk[IMX6QDL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, - base + 0xe0, 0, 2, 0, clk_enet_ref_table, - &imx_ccm_lock); - clk[IMX6QDL_CLK_LVDS1_SEL] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); clk[IMX6QDL_CLK_LVDS2_SEL] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); @@ -508,8 +571,12 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * lvds1_gate and lvds2_gate are pseudo-gates. Both can be * independently configured as clock inputs or outputs. We treat * the "output_enable" bit as a gate, even though it's really just - * enabling clock output. + * enabling clock output. Initially the gate bits are cleared, as + * otherwise the exclusive configuration gets locked in the setup done + * by software running before the clock driver, with no way to change + * it. */ + writel(readl(base + 0x160) & ~0x3c00, base + 0x160); clk[IMX6QDL_CLK_LVDS1_GATE] = imx_clk_gate_exclusive("lvds1_gate", "lvds1_sel", base + 0x160, 10, BIT(12)); clk[IMX6QDL_CLK_LVDS2_GATE] = imx_clk_gate_exclusive("lvds2_gate", "lvds2_sel", base + 0x160, 11, BIT(13)); @@ -737,6 +804,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); clk[IMX6QDL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); clk[IMX6QDL_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_root", base + 0x68, 20); + clk[IMX6QDL_CLK_DCIC1] = imx_clk_gate2("dcic1", "ipu1_podf", base + 0x68, 24); + clk[IMX6QDL_CLK_DCIC2] = imx_clk_gate2("dcic2", "ipu2_podf", base + 0x68, 26); clk[IMX6QDL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); clk[IMX6QDL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); clk[IMX6QDL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); |