* [PATCH v2 2/3] clk: imx6q: optionally get CCM inputs via standard clock handles
2018-11-15 14:30 [PATCH v2 1/3] clk: imx6q: reset exclusive gates on init Lucas Stach
@ 2018-11-15 14:30 ` Lucas Stach
2018-12-10 19:41 ` Stephen Boyd
2018-11-15 14:30 ` [PATCH v2 3/3] clk: imx6q: handle ENET PLL bypass Lucas Stach
2018-12-10 19:41 ` [PATCH v2 1/3] clk: imx6q: reset exclusive gates on init Stephen Boyd
2 siblings, 1 reply; 6+ messages in thread
From: Lucas Stach @ 2018-11-15 14:30 UTC (permalink / raw)
To: linux-arm-kernel
When specifying external clock inputs to the CCM the current code
requires the clocks to be in a "clocks" child node of the DT root.
This is not really conformant with DT best practices.
To avoid the need to deviate from those best practices, allow the
clock inputs to be specified via standard clock handles. This is
in line with how drivers of the later CCM driver revisions on
newer i.MX SoCs handle this.
As we can't retroactively change the DT binding, allow this as an
option with a fallback to the old way of how this has been handled.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
---
v2: correct third clock name, as per review from Dong Aisheng
---
.../devicetree/bindings/clock/imx6q-clock.txt | 3 +++
drivers/clk/imx/clk-imx6q.c | 22 ++++++++++++++-----
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
index e1308346e00d..13d36d4c6991 100644
--- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt
@@ -13,6 +13,9 @@ Optional properties:
management IC (PMIC) triggered via PMIC_STBY_REQ signal.
Boards that are designed to initiate poweroff on PMIC_ON_REQ signal should
be using "syscon-poweroff" driver instead.
+- clocks: list of clock specifiers, must contain an entry for each entry
+ in clock-names
+- clock-names: valid names are "osc", "ckil", "ckih1", "anaclk1" and "anaclk2"
The clock consumer should specify the desired clock by having the clock
ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6qdl-clock.h
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index 59f6a3e087db..bd53c403bcc1 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -414,12 +414,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);
--
2.19.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/3] clk: imx6q: handle ENET PLL bypass
2018-11-15 14:30 [PATCH v2 1/3] clk: imx6q: reset exclusive gates on init Lucas Stach
2018-11-15 14:30 ` [PATCH v2 2/3] clk: imx6q: optionally get CCM inputs via standard clock handles Lucas Stach
@ 2018-11-15 14:30 ` Lucas Stach
2018-12-10 19:41 ` Stephen Boyd
2018-12-10 19:41 ` [PATCH v2 1/3] clk: imx6q: reset exclusive gates on init Stephen Boyd
2 siblings, 1 reply; 6+ messages in thread
From: Lucas Stach @ 2018-11-15 14:30 UTC (permalink / raw)
To: linux-arm-kernel
The ENET PLL is different from the other i.MX6 PLLs, as it has
multiple outputs with different post-dividers, which are all
bypassed if the single bypass bit is activated. The hardware setup
looks something like this:
_
refclk-o---PLL---o----DIV1-----| \
| | |M |----OUT1
o-----------------------|_/
| | _
| o----DIV2-----| \
| | |M |----OUT2
o-----------------------|_/
| | _
| `----DIV3-----| \
| |M |----OUT3
`-----------------------|_/
The bypass bit not only bypasses the PLL, but also the attached
post-dividers. This would be reasonbly straight forward to model
with a single output, or with different bypass bits for each output,
but sadly the HW guys decided that it would be good to actuate all
3 muxes with a single bit.
So the need to have the PLL bypassed for one of the outputs always
affects 2 other (in our model) independent branches of the clock
tree.
This means the decision to bypass this PLL is a system wide design
choice and should not be changed on-the-fly, so we can treat any
bapass configuration as static. As such we can just register the
post-dividiers with a ratio that reflects the bypass status, which
allows us to bypass the PLL without breaking our abstraction model
and with it DT stability.
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
drivers/clk/imx/clk-imx6q.c | 63 +++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index bd53c403bcc1..bf988912a5ed 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
@@ -503,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 refelcting 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));
--
2.19.1
^ permalink raw reply related [flat|nested] 6+ messages in thread