[PATCH 02/14] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)

2018-08-13 Thread Sricharan R
From: Stephen Boyd 

HFPLLs are the main frequency source for Krait CPU clocks. Add
support for changing the rate of these PLLs.

Signed-off-by: Stephen Boyd 
---
 drivers/clk/qcom/Makefile|   1 +
 drivers/clk/qcom/clk-hfpll.c | 244 +++
 drivers/clk/qcom/clk-hfpll.h |  44 
 3 files changed, 289 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-hfpll.c
 create mode 100644 drivers/clk/qcom/clk-hfpll.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 762c011..1331ed7 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -11,6 +11,7 @@ clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
 clk-qcom-y += clk-regmap-mux-div.o
+clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
 
diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c
new file mode 100644
index 000..3c04805
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+#define PLL_OUTCTRLBIT(0)
+#define PLL_BYPASSNL   BIT(1)
+#define PLL_RESET_NBIT(2)
+
+/* Initialize a HFPLL at a given rate and enable it. */
+static void __clk_hfpll_init_once(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+
+   if (likely(h->init_done))
+   return;
+
+   /* Configure PLL parameters for integer mode. */
+   if (hd->config_val)
+   regmap_write(regmap, hd->config_reg, hd->config_val);
+   regmap_write(regmap, hd->m_reg, 0);
+   regmap_write(regmap, hd->n_reg, 1);
+
+   if (hd->user_reg) {
+   u32 regval = hd->user_val;
+   unsigned long rate;
+
+   rate = clk_hw_get_rate(hw);
+
+   /* Pick the right VCO. */
+   if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
+   regval |= hd->user_vco_mask;
+   regmap_write(regmap, hd->user_reg, regval);
+   }
+
+   if (hd->droop_reg)
+   regmap_write(regmap, hd->droop_reg, hd->droop_val);
+
+   h->init_done = true;
+}
+
+static void __clk_hfpll_enable(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+   u32 val;
+
+   __clk_hfpll_init_once(hw);
+
+   /* Disable PLL bypass mode. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
+
+   /*
+* H/W requires a 5us delay between disabling the bypass and
+* de-asserting the reset. Delay 10us just to be safe.
+*/
+   udelay(10);
+
+   /* De-assert active-low PLL reset. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
+
+   /* Wait for PLL to lock. */
+   if (hd->status_reg) {
+   do {
+   regmap_read(regmap, hd->status_reg, );
+   } while (!(val & BIT(hd->lock_bit)));
+   } else {
+   udelay(60);
+   }
+
+   /* Enable PLL output. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
+}
+
+/* Enable an already-configured HFPLL. */
+static int clk_hfpll_enable(struct clk_hw *hw)
+{
+   unsigned long flags;
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+   u32 mode;
+
+   spin_lock_irqsave(>lock, flags);
+   regmap_read(regmap, hd->mode_reg, );
+   if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
+   __clk_hfpll_enable(hw);
+   spin_unlock_irqrestore(>lock, flags);
+
+   return 0;
+}
+
+static void __clk_hfpll_disable(struct clk_hfpll *h)
+{
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+
+   /*
+* Disable the PLL output, disable test mode, enable the bypass mode,
+* and assert the reset.
+*/
+   regmap_update_bits(regmap, hd->mode_reg,
+  PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
+}
+
+static void clk_hfpll_disable(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   unsigned long flags;
+
+   spin_lock_irqsave(>lock, flags);
+   __clk_hfpll_disable(h);
+   spin_unlock_irqrestore(>lock, flags);
+}
+
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
+unsigned long *parent_rate)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   unsigned long rrate;
+
+   rate = 

[PATCH 02/14] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)

2018-08-13 Thread Sricharan R
From: Stephen Boyd 

HFPLLs are the main frequency source for Krait CPU clocks. Add
support for changing the rate of these PLLs.

Signed-off-by: Stephen Boyd 
---
 drivers/clk/qcom/Makefile|   1 +
 drivers/clk/qcom/clk-hfpll.c | 244 +++
 drivers/clk/qcom/clk-hfpll.h |  44 
 3 files changed, 289 insertions(+)
 create mode 100644 drivers/clk/qcom/clk-hfpll.c
 create mode 100644 drivers/clk/qcom/clk-hfpll.h

diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 762c011..1331ed7 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -11,6 +11,7 @@ clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
 clk-qcom-y += clk-regmap-mux-div.o
+clk-qcom-y += clk-hfpll.o
 clk-qcom-y += reset.o
 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
 
diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c
new file mode 100644
index 000..3c04805
--- /dev/null
+++ b/drivers/clk/qcom/clk-hfpll.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "clk-regmap.h"
+#include "clk-hfpll.h"
+
+#define PLL_OUTCTRLBIT(0)
+#define PLL_BYPASSNL   BIT(1)
+#define PLL_RESET_NBIT(2)
+
+/* Initialize a HFPLL at a given rate and enable it. */
+static void __clk_hfpll_init_once(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+
+   if (likely(h->init_done))
+   return;
+
+   /* Configure PLL parameters for integer mode. */
+   if (hd->config_val)
+   regmap_write(regmap, hd->config_reg, hd->config_val);
+   regmap_write(regmap, hd->m_reg, 0);
+   regmap_write(regmap, hd->n_reg, 1);
+
+   if (hd->user_reg) {
+   u32 regval = hd->user_val;
+   unsigned long rate;
+
+   rate = clk_hw_get_rate(hw);
+
+   /* Pick the right VCO. */
+   if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
+   regval |= hd->user_vco_mask;
+   regmap_write(regmap, hd->user_reg, regval);
+   }
+
+   if (hd->droop_reg)
+   regmap_write(regmap, hd->droop_reg, hd->droop_val);
+
+   h->init_done = true;
+}
+
+static void __clk_hfpll_enable(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+   u32 val;
+
+   __clk_hfpll_init_once(hw);
+
+   /* Disable PLL bypass mode. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);
+
+   /*
+* H/W requires a 5us delay between disabling the bypass and
+* de-asserting the reset. Delay 10us just to be safe.
+*/
+   udelay(10);
+
+   /* De-assert active-low PLL reset. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);
+
+   /* Wait for PLL to lock. */
+   if (hd->status_reg) {
+   do {
+   regmap_read(regmap, hd->status_reg, );
+   } while (!(val & BIT(hd->lock_bit)));
+   } else {
+   udelay(60);
+   }
+
+   /* Enable PLL output. */
+   regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);
+}
+
+/* Enable an already-configured HFPLL. */
+static int clk_hfpll_enable(struct clk_hw *hw)
+{
+   unsigned long flags;
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+   u32 mode;
+
+   spin_lock_irqsave(>lock, flags);
+   regmap_read(regmap, hd->mode_reg, );
+   if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
+   __clk_hfpll_enable(hw);
+   spin_unlock_irqrestore(>lock, flags);
+
+   return 0;
+}
+
+static void __clk_hfpll_disable(struct clk_hfpll *h)
+{
+   struct hfpll_data const *hd = h->d;
+   struct regmap *regmap = h->clkr.regmap;
+
+   /*
+* Disable the PLL output, disable test mode, enable the bypass mode,
+* and assert the reset.
+*/
+   regmap_update_bits(regmap, hd->mode_reg,
+  PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);
+}
+
+static void clk_hfpll_disable(struct clk_hw *hw)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   unsigned long flags;
+
+   spin_lock_irqsave(>lock, flags);
+   __clk_hfpll_disable(h);
+   spin_unlock_irqrestore(>lock, flags);
+}
+
+static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate,
+unsigned long *parent_rate)
+{
+   struct clk_hfpll *h = to_clk_hfpll(hw);
+   struct hfpll_data const *hd = h->d;
+   unsigned long rrate;
+
+   rate =