[PATCH v12 3/5] clk: imx: Add SCCG PLL type

2018-11-07 Thread Abel Vesa
From: Lucas Stach 

The SCCG is a new PLL type introduced on i.MX8.

The description of this SCCG clock can be found here:

https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834

Signed-off-by: Lucas Stach 
Signed-off-by: Abel Vesa 
Reviewed-by: Sascha Hauer 
---
 drivers/clk/imx/Makefile   |   3 +-
 drivers/clk/imx/clk-sccg-pll.c | 256 +
 drivers/clk/imx/clk.h  |   9 ++
 3 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/imx/clk-sccg-pll.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 4893c1f..b87513c 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -12,7 +12,8 @@ obj-y += \
clk-pllv1.o \
clk-pllv2.o \
clk-pllv3.o \
-   clk-pfd.o
+   clk-pfd.o \
+   clk-sccg-pll.o
 
 obj-$(CONFIG_SOC_IMX1)   += clk-imx1.o
 obj-$(CONFIG_SOC_IMX21)  += clk-imx21.o
diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c
new file mode 100644
index 000..4666b96
--- /dev/null
+++ b/drivers/clk/imx/clk-sccg-pll.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright 2018 NXP.
+ *
+ * This driver supports the SCCG plls found in the imx8m SOCs
+ *
+ * Documentation for this SCCG pll can be found at:
+ *   https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "clk.h"
+
+/* PLL CFGs */
+#define PLL_CFG0   0x0
+#define PLL_CFG1   0x4
+#define PLL_CFG2   0x8
+
+#define PLL_DIVF1_MASK GENMASK(18, 13)
+#define PLL_DIVF2_MASK GENMASK(12, 7)
+#define PLL_DIVR1_MASK GENMASK(27, 25)
+#define PLL_DIVR2_MASK GENMASK(24, 19)
+#define PLL_REF_MASK   GENMASK(2, 0)
+
+#define PLL_LOCK_MASK  BIT(31)
+#define PLL_PD_MASKBIT(7)
+
+#define OSC_25M2500
+#define OSC_27M2700
+
+#define PLL_SCCG_LOCK_TIMEOUT  70
+
+struct clk_sccg_pll {
+   struct clk_hw   hw;
+   void __iomem*base;
+};
+
+#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
+
+static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
+{
+   u32 val;
+
+   return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
+   PLL_SCCG_LOCK_TIMEOUT);
+}
+
+static int clk_pll1_is_prepared(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   return (val & PLL_PD_MASK) ? 0 : 1;
+}
+
+static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
+unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val, divf;
+
+   val = readl_relaxed(pll->base + PLL_CFG2);
+   divf = FIELD_GET(PLL_DIVF1_MASK, val);
+
+   return parent_rate * 2 * (divf + 1);
+}
+
+static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
+  unsigned long *prate)
+{
+   unsigned long parent_rate = *prate;
+   u32 div;
+
+   if (!parent_rate)
+   return 0;
+
+   div = rate / (parent_rate * 2);
+
+   return parent_rate * div * 2;
+}
+
+static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+   u32 divf;
+
+   if (!parent_rate)
+   return -EINVAL;
+
+   divf = rate / (parent_rate * 2);
+
+   val = readl_relaxed(pll->base + PLL_CFG2);
+   val &= ~PLL_DIVF1_MASK;
+   val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
+   writel_relaxed(val, pll->base + PLL_CFG2);
+
+   return clk_pll_wait_lock(pll);
+}
+
+static int clk_pll1_prepare(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   val &= ~PLL_PD_MASK;
+   writel_relaxed(val, pll->base + PLL_CFG0);
+
+   return clk_pll_wait_lock(pll);
+}
+
+static void clk_pll1_unprepare(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   val |= PLL_PD_MASK;
+   writel_relaxed(val, pll->base + PLL_CFG0);
+
+}
+
+static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
+unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val, ref, divr1, divf1, divr2, divf2;
+   u64 temp64;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   switch (FIELD_GET(PLL_REF_MASK, val)) {
+   case 0:
+   ref = OSC_25M;
+   break;
+   case 1:
+   ref = OSC_27M;
+   

[PATCH v12 3/5] clk: imx: Add SCCG PLL type

2018-11-07 Thread Abel Vesa
From: Lucas Stach 

The SCCG is a new PLL type introduced on i.MX8.

The description of this SCCG clock can be found here:

https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834

Signed-off-by: Lucas Stach 
Signed-off-by: Abel Vesa 
Reviewed-by: Sascha Hauer 
---
 drivers/clk/imx/Makefile   |   3 +-
 drivers/clk/imx/clk-sccg-pll.c | 256 +
 drivers/clk/imx/clk.h  |   9 ++
 3 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/imx/clk-sccg-pll.c

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 4893c1f..b87513c 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -12,7 +12,8 @@ obj-y += \
clk-pllv1.o \
clk-pllv2.o \
clk-pllv3.o \
-   clk-pfd.o
+   clk-pfd.o \
+   clk-sccg-pll.o
 
 obj-$(CONFIG_SOC_IMX1)   += clk-imx1.o
 obj-$(CONFIG_SOC_IMX21)  += clk-imx21.o
diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c
new file mode 100644
index 000..4666b96
--- /dev/null
+++ b/drivers/clk/imx/clk-sccg-pll.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright 2018 NXP.
+ *
+ * This driver supports the SCCG plls found in the imx8m SOCs
+ *
+ * Documentation for this SCCG pll can be found at:
+ *   https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "clk.h"
+
+/* PLL CFGs */
+#define PLL_CFG0   0x0
+#define PLL_CFG1   0x4
+#define PLL_CFG2   0x8
+
+#define PLL_DIVF1_MASK GENMASK(18, 13)
+#define PLL_DIVF2_MASK GENMASK(12, 7)
+#define PLL_DIVR1_MASK GENMASK(27, 25)
+#define PLL_DIVR2_MASK GENMASK(24, 19)
+#define PLL_REF_MASK   GENMASK(2, 0)
+
+#define PLL_LOCK_MASK  BIT(31)
+#define PLL_PD_MASKBIT(7)
+
+#define OSC_25M2500
+#define OSC_27M2700
+
+#define PLL_SCCG_LOCK_TIMEOUT  70
+
+struct clk_sccg_pll {
+   struct clk_hw   hw;
+   void __iomem*base;
+};
+
+#define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
+
+static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
+{
+   u32 val;
+
+   return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
+   PLL_SCCG_LOCK_TIMEOUT);
+}
+
+static int clk_pll1_is_prepared(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   return (val & PLL_PD_MASK) ? 0 : 1;
+}
+
+static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
+unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val, divf;
+
+   val = readl_relaxed(pll->base + PLL_CFG2);
+   divf = FIELD_GET(PLL_DIVF1_MASK, val);
+
+   return parent_rate * 2 * (divf + 1);
+}
+
+static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
+  unsigned long *prate)
+{
+   unsigned long parent_rate = *prate;
+   u32 div;
+
+   if (!parent_rate)
+   return 0;
+
+   div = rate / (parent_rate * 2);
+
+   return parent_rate * div * 2;
+}
+
+static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
+   unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+   u32 divf;
+
+   if (!parent_rate)
+   return -EINVAL;
+
+   divf = rate / (parent_rate * 2);
+
+   val = readl_relaxed(pll->base + PLL_CFG2);
+   val &= ~PLL_DIVF1_MASK;
+   val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
+   writel_relaxed(val, pll->base + PLL_CFG2);
+
+   return clk_pll_wait_lock(pll);
+}
+
+static int clk_pll1_prepare(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   val &= ~PLL_PD_MASK;
+   writel_relaxed(val, pll->base + PLL_CFG0);
+
+   return clk_pll_wait_lock(pll);
+}
+
+static void clk_pll1_unprepare(struct clk_hw *hw)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   val |= PLL_PD_MASK;
+   writel_relaxed(val, pll->base + PLL_CFG0);
+
+}
+
+static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
+unsigned long parent_rate)
+{
+   struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+   u32 val, ref, divr1, divf1, divr2, divf2;
+   u64 temp64;
+
+   val = readl_relaxed(pll->base + PLL_CFG0);
+   switch (FIELD_GET(PLL_REF_MASK, val)) {
+   case 0:
+   ref = OSC_25M;
+   break;
+   case 1:
+   ref = OSC_27M;
+