On the imx6q QuadPlus, hardware designers have improved the enet
clocking:
                          _
         anaclk3---------| \
                         |  |              _
                         |M |--enet_pad-->| \
                         |  |             |  |
         enet_ref---o--->|_/              |M |----enet_ptp
                    |                     |  |
                    o---------------------|_/

Where anaclk3 is an external clock fed into the imx6q via
a pad, typically RGMII_TX_CTL or GPIO_16, when these are
configured in mux mode ENET_REF_CLK.

Board designers can now change the clock tree (via the devicetree)
to choose between all three supported enet clocking methods.

See follow-up devicetree patch for details.

Signed-off-by: Sven Van Asbroeck <thesve...@gmail.com>
---

Tree: v5.8-rc4

To: Shawn Guo <shawn...@kernel.org>
To: Sascha Hauer <s.ha...@pengutronix.de>
Cc: Pengutronix Kernel Team <ker...@pengutronix.de>
Cc: Fabio Estevam <feste...@gmail.com>
Cc: NXP Linux Team <linux-...@nxp.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-ker...@lists.infradead.org
Cc: linux-...@vger.kernel.org

 drivers/clk/imx/clk-imx6q.c                 | 46 +++++++++++++++++++++
 include/linux/mfd/syscon/imx6q-iomuxc-gpr.h |  1 +
 2 files changed, 47 insertions(+)

diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index ba33c79158de..0edffa306a28 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -15,6 +15,8 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <soc/imx/revision.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <dt-bindings/clock/imx6qdl-clock.h>
 
 #include "clk.h"
@@ -87,6 +89,8 @@ static const char *pll4_bypass_sels[] = { "pll4", 
"pll4_bypass_src", };
 static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", };
 static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", };
 static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
+static const char * const enet_pad_sels[] = { "anaclk3", "enet_ref", };
+static const char * const enet_ptp_sels[] = { "enet_pad", "enet_ref", };
 
 static struct clk_hw **hws;
 static struct clk_hw_onecell_data *clk_hw_data;
@@ -436,6 +440,7 @@ static struct clk_hw * __init 
imx6q_obtain_fixed_clk_hw(struct device_node *np,
 
 static void __init imx6q_clocks_init(struct device_node *ccm_node)
 {
+       struct regmap *gpr;
        struct device_node *np;
        void __iomem *anatop_base, *base;
        int ret;
@@ -464,6 +469,9 @@ static void __init imx6q_clocks_init(struct device_node 
*ccm_node)
        WARN_ON(!base);
        of_node_put(np);
 
+       gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+       WARN_ON(IS_ERR_OR_NULL(gpr));
+
        /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */
        if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) {
                post_div_table[1].div = 1;
@@ -959,6 +967,44 @@ static void __init imx6q_clocks_init(struct device_node 
*ccm_node)
        /* Audio-related clocks configuration */
        clk_set_parent(hws[IMX6QDL_CLK_SPDIF_SEL]->clk, 
hws[IMX6QDL_CLK_PLL3_PFD3_454M]->clk);
 
+       /*
+        * The QuadPlus has improved enet clocking:
+        *                  _
+        * anaclk3---------| \
+        *                 |  |              _
+        *                 |M |--enet_pad-->| \
+        *                 |  |             |  |
+        * enet_ref---o--->|_/              |M |----enet_ptp
+        *            |                     |  |
+        *            o---------------------|_/
+        *
+        * On the plus, three options to generate the enet ptp clock:
+        *
+        * a) route enet_ref externally from pad to pad (the default):
+        *    enet_ptp from enet_pad
+        *    enet_pad from enet_ref
+        * b) route internally on SoC from enet_ref:
+        *    enet_ptp from enet_ref
+        * c) route external clock (from PHY or oscillator) via pad:
+        *    enet_ptp from enet_pad
+        *    enet_pad from anaclk3
+        *    anaclk3 from PHY or oscillator, add devicetree node
+        */
+       if (clk_on_imx6qp()) {
+               /*
+                * anaclk3: clock source from external clock via:
+                * - RGMII_TX_CTL PAD (mux mode ENET_REF_CLK), or
+                * - GPIO_16      PAD (mux mode ENET_REF_CLK)
+                */
+               hws[IMX6QDL_CLK_ANACLK3] = imx6q_obtain_fixed_clk_hw(ccm_node, 
"anaclk3", 0);
+               hws[IMX6QDL_CLK_ENET_PAD] = imx_clk_hw_mux_regmap("enet_pad", 
gpr, IOMUXC_GPR1, ffs(IMX6Q_GPR1_ENET_CLK_SEL_MASK) - 1,
+                                                                 1, 
enet_pad_sels, ARRAY_SIZE(enet_pad_sels));
+               hws[IMX6QDL_CLK_ENET_PTP] = imx_clk_hw_mux_regmap("enet_ptp", 
gpr, IOMUXC_GPR5, ffs(IMX6Q_GPR5_ENET_TXCLK_SEL_MASK) - 1,
+                                                                 1, 
enet_ptp_sels, ARRAY_SIZE(enet_ptp_sels));
+               clk_set_parent(hws[IMX6QDL_CLK_ENET_PAD]->clk, 
hws[IMX6QDL_CLK_ENET_REF]->clk);
+               clk_set_parent(hws[IMX6QDL_CLK_ENET_PTP]->clk, 
hws[IMX6QDL_CLK_ENET_PAD]->clk);
+       }
+
        /* All existing boards with PCIe use LVDS1 */
        if (IS_ENABLED(CONFIG_PCI_IMX6))
                clk_set_parent(hws[IMX6QDL_CLK_LVDS1_SEL]->clk, 
hws[IMX6QDL_CLK_SATA_REF_100M]->clk);
diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h 
b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
index d4b5e527a7a3..ac1bb3695933 100644
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -240,6 +240,7 @@
 #define IMX6Q_GPR4_IPU_RD_CACHE_CTL            BIT(0)
 
 #define IMX6Q_GPR5_L2_CLK_STOP                 BIT(8)
+#define IMX6Q_GPR5_ENET_TXCLK_SEL_MASK         BIT(9)
 #define IMX6Q_GPR5_SATA_SW_PD                  BIT(10)
 #define IMX6Q_GPR5_SATA_SW_RST                 BIT(11)
 
-- 
2.17.1

Reply via email to