mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-24 07:46:48 +00:00
ipq806x: drop upstream patch
This patchset has been merged upstream. Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
This commit is contained in:
parent
71eb7b057b
commit
eaceb85ebc
@ -1,115 +0,0 @@
|
||||
From 36d68f64c411e09788687d5919886aadeb92adca Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:20 +0530
|
||||
Subject: [PATCH 01/12] ARM: Add Krait L2 register accessor functions
|
||||
|
||||
Krait CPUs have a handful of L2 cache controller registers that
|
||||
live behind a cp15 based indirection register. First you program
|
||||
the indirection register (l2cpselr) to point the L2 'window'
|
||||
register (l2cpdr) at what you want to read/write. Then you
|
||||
read/write the 'window' register to do what you want. The
|
||||
l2cpselr register is not banked per-cpu so we must lock around
|
||||
accesses to it to prevent other CPUs from re-pointing l2cpdr
|
||||
underneath us.
|
||||
|
||||
Cc: Mark Rutland <mark.rutland@arm.com>
|
||||
Cc: Russell King <linux@arm.linux.org.uk>
|
||||
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
arch/arm/common/Kconfig | 3 ++
|
||||
arch/arm/common/Makefile | 1 +
|
||||
arch/arm/common/krait-l2-accessors.c | 48 +++++++++++++++++++++++
|
||||
arch/arm/include/asm/krait-l2-accessors.h | 9 +++++
|
||||
4 files changed, 61 insertions(+)
|
||||
create mode 100644 arch/arm/common/krait-l2-accessors.c
|
||||
create mode 100644 arch/arm/include/asm/krait-l2-accessors.h
|
||||
|
||||
--- a/arch/arm/common/Kconfig
|
||||
+++ b/arch/arm/common/Kconfig
|
||||
@@ -7,6 +7,9 @@ config DMABOUNCE
|
||||
bool
|
||||
select ZONE_DMA
|
||||
|
||||
+config KRAIT_L2_ACCESSORS
|
||||
+ bool
|
||||
+
|
||||
config SHARP_LOCOMO
|
||||
bool
|
||||
|
||||
--- a/arch/arm/common/Makefile
|
||||
+++ b/arch/arm/common/Makefile
|
||||
@@ -7,6 +7,7 @@ obj-y += firmware.o
|
||||
|
||||
obj-$(CONFIG_SA1111) += sa1111.o
|
||||
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
|
||||
+obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o
|
||||
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
|
||||
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
|
||||
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/common/krait-l2-accessors.c
|
||||
@@ -0,0 +1,48 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/export.h>
|
||||
+
|
||||
+#include <asm/barrier.h>
|
||||
+#include <asm/krait-l2-accessors.h>
|
||||
+
|
||||
+static DEFINE_RAW_SPINLOCK(krait_l2_lock);
|
||||
+
|
||||
+void krait_set_l2_indirect_reg(u32 addr, u32 val)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
||||
+ /*
|
||||
+ * Select the L2 window by poking l2cpselr, then write to the window
|
||||
+ * via l2cpdr.
|
||||
+ */
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
||||
+ isb();
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val));
|
||||
+ isb();
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
||||
+}
|
||||
+EXPORT_SYMBOL(krait_set_l2_indirect_reg);
|
||||
+
|
||||
+u32 krait_get_l2_indirect_reg(u32 addr)
|
||||
+{
|
||||
+ u32 val;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&krait_l2_lock, flags);
|
||||
+ /*
|
||||
+ * Select the L2 window by poking l2cpselr, then read from the window
|
||||
+ * via l2cpdr.
|
||||
+ */
|
||||
+ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr));
|
||||
+ isb();
|
||||
+ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val));
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&krait_l2_lock, flags);
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+EXPORT_SYMBOL(krait_get_l2_indirect_reg);
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/include/asm/krait-l2-accessors.h
|
||||
@@ -0,0 +1,9 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+
|
||||
+#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H
|
||||
+#define __ASMARM_KRAIT_L2_ACCESSORS_H
|
||||
+
|
||||
+extern void krait_set_l2_indirect_reg(u32 addr, u32 val);
|
||||
+extern u32 krait_get_l2_indirect_reg(u32 addr);
|
||||
+
|
||||
+#endif
|
@ -1,324 +0,0 @@
|
||||
From b3f2f10693aadeacf83ab5be03810941a4b77612 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:21 +0530
|
||||
Subject: [PATCH 02/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs)
|
||||
|
||||
HFPLLs are the main frequency source for Krait CPU clocks. Add
|
||||
support for changing the rate of these PLLs.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
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
|
||||
|
||||
--- 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
|
||||
|
||||
--- /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 <linux/kernel.h>
|
||||
+#include <linux/export.h>
|
||||
+#include <linux/regmap.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#include "clk-regmap.h"
|
||||
+#include "clk-hfpll.h"
|
||||
+
|
||||
+#define PLL_OUTCTRL BIT(0)
|
||||
+#define PLL_BYPASSNL BIT(1)
|
||||
+#define PLL_RESET_N BIT(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, &val);
|
||||
+ } 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(&h->lock, flags);
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))
|
||||
+ __clk_hfpll_enable(hw);
|
||||
+ spin_unlock_irqrestore(&h->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(&h->lock, flags);
|
||||
+ __clk_hfpll_disable(h);
|
||||
+ spin_unlock_irqrestore(&h->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 = clamp(rate, hd->min_rate, hd->max_rate);
|
||||
+
|
||||
+ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate;
|
||||
+ if (rrate > hd->max_rate)
|
||||
+ rrate -= *parent_rate;
|
||||
+
|
||||
+ return rrate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * For optimization reasons, assumes no downstream clocks are actively using
|
||||
+ * it.
|
||||
+ */
|
||||
+static int clk_hfpll_set_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;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ unsigned long flags;
|
||||
+ u32 l_val, val;
|
||||
+ bool enabled;
|
||||
+
|
||||
+ l_val = rate / parent_rate;
|
||||
+
|
||||
+ spin_lock_irqsave(&h->lock, flags);
|
||||
+
|
||||
+ enabled = __clk_is_enabled(hw->clk);
|
||||
+ if (enabled)
|
||||
+ __clk_hfpll_disable(h);
|
||||
+
|
||||
+ /* Pick the right VCO. */
|
||||
+ if (hd->user_reg && hd->user_vco_mask) {
|
||||
+ regmap_read(regmap, hd->user_reg, &val);
|
||||
+ if (rate <= hd->low_vco_max_rate)
|
||||
+ val &= ~hd->user_vco_mask;
|
||||
+ else
|
||||
+ val |= hd->user_vco_mask;
|
||||
+ regmap_write(regmap, hd->user_reg, val);
|
||||
+ }
|
||||
+
|
||||
+ regmap_write(regmap, hd->l_reg, l_val);
|
||||
+
|
||||
+ if (enabled)
|
||||
+ __clk_hfpll_enable(hw);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&h->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct clk_hfpll *h = to_clk_hfpll(hw);
|
||||
+ struct hfpll_data const *hd = h->d;
|
||||
+ struct regmap *regmap = h->clkr.regmap;
|
||||
+ u32 l_val;
|
||||
+
|
||||
+ regmap_read(regmap, hd->l_reg, &l_val);
|
||||
+
|
||||
+ return l_val * parent_rate;
|
||||
+}
|
||||
+
|
||||
+static void clk_hfpll_init(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 mode, status;
|
||||
+
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {
|
||||
+ __clk_hfpll_init_once(hw);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (hd->status_reg) {
|
||||
+ regmap_read(regmap, hd->status_reg, &status);
|
||||
+ if (!(status & BIT(hd->lock_bit))) {
|
||||
+ WARN(1, "HFPLL %s is ON, but not locked!\n",
|
||||
+ __clk_get_name(hw->clk));
|
||||
+ clk_hfpll_disable(hw);
|
||||
+ __clk_hfpll_init_once(hw);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int hfpll_is_enabled(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 mode;
|
||||
+
|
||||
+ regmap_read(regmap, hd->mode_reg, &mode);
|
||||
+ mode &= 0x7;
|
||||
+ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops clk_ops_hfpll = {
|
||||
+ .enable = clk_hfpll_enable,
|
||||
+ .disable = clk_hfpll_disable,
|
||||
+ .is_enabled = hfpll_is_enabled,
|
||||
+ .round_rate = clk_hfpll_round_rate,
|
||||
+ .set_rate = clk_hfpll_set_rate,
|
||||
+ .recalc_rate = clk_hfpll_recalc_rate,
|
||||
+ .init = clk_hfpll_init,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(clk_ops_hfpll);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-hfpll.h
|
||||
@@ -0,0 +1,44 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+
|
||||
+#ifndef __QCOM_CLK_HFPLL_H__
|
||||
+#define __QCOM_CLK_HFPLL_H__
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include "clk-regmap.h"
|
||||
+
|
||||
+struct hfpll_data {
|
||||
+ u32 mode_reg;
|
||||
+ u32 l_reg;
|
||||
+ u32 m_reg;
|
||||
+ u32 n_reg;
|
||||
+ u32 user_reg;
|
||||
+ u32 droop_reg;
|
||||
+ u32 config_reg;
|
||||
+ u32 status_reg;
|
||||
+ u8 lock_bit;
|
||||
+
|
||||
+ u32 droop_val;
|
||||
+ u32 config_val;
|
||||
+ u32 user_val;
|
||||
+ u32 user_vco_mask;
|
||||
+ unsigned long low_vco_max_rate;
|
||||
+
|
||||
+ unsigned long min_rate;
|
||||
+ unsigned long max_rate;
|
||||
+};
|
||||
+
|
||||
+struct clk_hfpll {
|
||||
+ struct hfpll_data const *d;
|
||||
+ int init_done;
|
||||
+
|
||||
+ struct clk_regmap clkr;
|
||||
+ spinlock_t lock;
|
||||
+};
|
||||
+
|
||||
+#define to_clk_hfpll(_hw) \
|
||||
+ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr)
|
||||
+
|
||||
+extern const struct clk_ops clk_ops_hfpll;
|
||||
+
|
||||
+#endif
|
@ -1,142 +0,0 @@
|
||||
From cb546b797a0da4dbb1a0c76a2a357921887b6189 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:22 +0530
|
||||
Subject: [PATCH 03/12] clk: qcom: Add HFPLL driver
|
||||
|
||||
On some devices (MSM8974 for example), the HFPLLs are
|
||||
instantiated within the Krait processor subsystem as separate
|
||||
register regions. Add a driver for these PLLs so that we can
|
||||
provide HFPLL clocks for use by the system.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/Kconfig | 8 ++++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/hfpll.c | 96 +++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 105 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/hfpll.c
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -272,3 +272,11 @@ config SPMI_PMIC_CLKDIV
|
||||
Technologies, Inc. SPMI PMIC. It configures the frequency of
|
||||
clkdiv outputs of the PMIC. These clocks are typically wired
|
||||
through alternate functions on GPIO pins.
|
||||
+
|
||||
+config QCOM_HFPLL
|
||||
+ tristate "High-Frequency PLL (HFPLL) Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM
|
||||
+ help
|
||||
+ Support for the high-frequency PLLs present on Qualcomm devices.
|
||||
+ Say Y if you want to support CPU frequency scaling on devices
|
||||
+ such as MSM8974, APQ8084, etc.
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -44,3 +44,4 @@ obj-$(CONFIG_SDM_DISPCC_845) += dispcc-s
|
||||
obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o
|
||||
obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
|
||||
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
|
||||
+obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/hfpll.c
|
||||
@@ -0,0 +1,96 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+#include "clk-regmap.h"
|
||||
+#include "clk-hfpll.h"
|
||||
+
|
||||
+static const struct hfpll_data hdata = {
|
||||
+ .mode_reg = 0x00,
|
||||
+ .l_reg = 0x04,
|
||||
+ .m_reg = 0x08,
|
||||
+ .n_reg = 0x0c,
|
||||
+ .user_reg = 0x10,
|
||||
+ .config_reg = 0x14,
|
||||
+ .config_val = 0x430405d,
|
||||
+ .status_reg = 0x1c,
|
||||
+ .lock_bit = 16,
|
||||
+
|
||||
+ .user_val = 0x8,
|
||||
+ .user_vco_mask = 0x100000,
|
||||
+ .low_vco_max_rate = 1248000000,
|
||||
+ .min_rate = 537600000UL,
|
||||
+ .max_rate = 2900000000UL,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id qcom_hfpll_match_table[] = {
|
||||
+ { .compatible = "qcom,hfpll" },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table);
|
||||
+
|
||||
+static const struct regmap_config hfpll_regmap_config = {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = 0x30,
|
||||
+ .fast_io = true,
|
||||
+};
|
||||
+
|
||||
+static int qcom_hfpll_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ void __iomem *base;
|
||||
+ struct regmap *regmap;
|
||||
+ struct clk_hfpll *h;
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = (const char *[]){ "xo" },
|
||||
+ .num_parents = 1,
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ };
|
||||
+
|
||||
+ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
|
||||
+ if (!h)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(dev, res);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config);
|
||||
+ if (IS_ERR(regmap))
|
||||
+ return PTR_ERR(regmap);
|
||||
+
|
||||
+ if (of_property_read_string_index(dev->of_node, "clock-output-names",
|
||||
+ 0, &init.name))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ h->d = &hdata;
|
||||
+ h->clkr.hw.init = &init;
|
||||
+ spin_lock_init(&h->lock);
|
||||
+
|
||||
+ return devm_clk_register_regmap(&pdev->dev, &h->clkr);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qcom_hfpll_driver = {
|
||||
+ .probe = qcom_hfpll_probe,
|
||||
+ .driver = {
|
||||
+ .name = "qcom-hfpll",
|
||||
+ .of_match_table = qcom_hfpll_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(qcom_hfpll_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("QCOM HFPLL Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:qcom-hfpll");
|
@ -1,81 +0,0 @@
|
||||
From 1f924faa8b1e4789ecc06ed0dd58ca3487c89012 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:23 +0530
|
||||
Subject: [PATCH 04/12] dt-bindings: clock: Document qcom,hfpll
|
||||
|
||||
Adds bindings document for qcom,hfpll instantiated within
|
||||
the Krait processor subsystem as separate register region.
|
||||
|
||||
Reviewed-by: Rob Herring <robh@kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
.../devicetree/bindings/clock/qcom,hfpll.txt | 60 +++++++++++++++++++
|
||||
1 file changed, 60 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt
|
||||
@@ -0,0 +1,60 @@
|
||||
+High-Frequency PLL (HFPLL)
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>:
|
||||
+ shall contain only one of the following. The generic
|
||||
+ compatible "qcom,hfpll" should be also included.
|
||||
+
|
||||
+ "qcom,hfpll-ipq8064", "qcom,hfpll"
|
||||
+ "qcom,hfpll-apq8064", "qcom,hfpll"
|
||||
+ "qcom,hfpll-msm8974", "qcom,hfpll"
|
||||
+ "qcom,hfpll-msm8960", "qcom,hfpll"
|
||||
+
|
||||
+- reg:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: address and size of HPLL registers. An optional second
|
||||
+ element specifies the address and size of the alias
|
||||
+ register region.
|
||||
+
|
||||
+- clocks:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: reference to the xo clock.
|
||||
+
|
||||
+- clock-names:
|
||||
+ Usage: required
|
||||
+ Value type: <stringlist>
|
||||
+ Definition: must be "xo".
|
||||
+
|
||||
+- clock-output-names:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the PLL. Typically hfpllX where X is a CPU number
|
||||
+ starting at 0. Otherwise hfpll_Y where Y is more specific
|
||||
+ such as "l2".
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+1) An HFPLL for the L2 cache.
|
||||
+
|
||||
+ clock-controller@f9016000 {
|
||||
+ compatible = "qcom,hfpll-ipq8064", "qcom,hfpll";
|
||||
+ reg = <0xf9016000 0x30>;
|
||||
+ clocks = <&xo_board>;
|
||||
+ clock-names = "xo";
|
||||
+ clock-output-names = "hfpll_l2";
|
||||
+ };
|
||||
+
|
||||
+2) An HFPLL for CPU0. This HFPLL has the alias register region.
|
||||
+
|
||||
+ clock-controller@f908a000 {
|
||||
+ compatible = "qcom,hfpll-ipq8064", "qcom,hfpll";
|
||||
+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
|
||||
+ clocks = <&xo_board>;
|
||||
+ clock-names = "xo";
|
||||
+ clock-output-names = "hfpll0";
|
||||
+ };
|
@ -1,236 +0,0 @@
|
||||
From 72ad7207954dd622a662ba884dc6c30a820123f2 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:24 +0530
|
||||
Subject: [PATCH 05/12] clk: qcom: Add MSM8960/APQ8064's HFPLLs
|
||||
|
||||
Describe the HFPLLs present on MSM8960 and APQ8064 devices.
|
||||
|
||||
Acked-by: Rob Herring <robh@kernel.org> (bindings)
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/gcc-msm8960.c | 172 +++++++++++++++++++
|
||||
include/dt-bindings/clock/qcom,gcc-msm8960.h | 2 +
|
||||
2 files changed, 174 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-msm8960.c
|
||||
+++ b/drivers/clk/qcom/gcc-msm8960.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
#include "clk-branch.h"
|
||||
+#include "clk-hfpll.h"
|
||||
#include "reset.h"
|
||||
|
||||
static struct clk_pll pll3 = {
|
||||
@@ -86,6 +87,164 @@ static struct clk_regmap pll8_vote = {
|
||||
},
|
||||
};
|
||||
|
||||
+static struct hfpll_data hfpll0_data = {
|
||||
+ .mode_reg = 0x3200,
|
||||
+ .l_reg = 0x3208,
|
||||
+ .m_reg = 0x320c,
|
||||
+ .n_reg = 0x3210,
|
||||
+ .config_reg = 0x3204,
|
||||
+ .status_reg = 0x321c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3214,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll0 = {
|
||||
+ .d = &hfpll0_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll0",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll1_8064_data = {
|
||||
+ .mode_reg = 0x3240,
|
||||
+ .l_reg = 0x3248,
|
||||
+ .m_reg = 0x324c,
|
||||
+ .n_reg = 0x3250,
|
||||
+ .config_reg = 0x3244,
|
||||
+ .status_reg = 0x325c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3254,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll1_data = {
|
||||
+ .mode_reg = 0x3300,
|
||||
+ .l_reg = 0x3308,
|
||||
+ .m_reg = 0x330c,
|
||||
+ .n_reg = 0x3310,
|
||||
+ .config_reg = 0x3304,
|
||||
+ .status_reg = 0x331c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll1 = {
|
||||
+ .d = &hfpll1_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll1",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll2_data = {
|
||||
+ .mode_reg = 0x3280,
|
||||
+ .l_reg = 0x3288,
|
||||
+ .m_reg = 0x328c,
|
||||
+ .n_reg = 0x3290,
|
||||
+ .config_reg = 0x3284,
|
||||
+ .status_reg = 0x329c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3294,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll2 = {
|
||||
+ .d = &hfpll2_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll2",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll2.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll3_data = {
|
||||
+ .mode_reg = 0x32c0,
|
||||
+ .l_reg = 0x32c8,
|
||||
+ .m_reg = 0x32cc,
|
||||
+ .n_reg = 0x32d0,
|
||||
+ .config_reg = 0x32c4,
|
||||
+ .status_reg = 0x32dc,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x32d4,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll3 = {
|
||||
+ .d = &hfpll3_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll3",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll3.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll_l2_8064_data = {
|
||||
+ .mode_reg = 0x3300,
|
||||
+ .l_reg = 0x3308,
|
||||
+ .m_reg = 0x330c,
|
||||
+ .n_reg = 0x3310,
|
||||
+ .config_reg = 0x3304,
|
||||
+ .status_reg = 0x331c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll_l2_data = {
|
||||
+ .mode_reg = 0x3400,
|
||||
+ .l_reg = 0x3408,
|
||||
+ .m_reg = 0x340c,
|
||||
+ .n_reg = 0x3410,
|
||||
+ .config_reg = 0x3404,
|
||||
+ .status_reg = 0x341c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3414,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll_l2 = {
|
||||
+ .d = &hfpll_l2_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll_l2",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
|
||||
+};
|
||||
+
|
||||
static struct clk_pll pll14 = {
|
||||
.l_reg = 0x31c4,
|
||||
.m_reg = 0x31c8,
|
||||
@@ -3107,6 +3266,9 @@ static struct clk_regmap *gcc_msm8960_cl
|
||||
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
|
||||
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
|
||||
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
|
||||
+ [PLL9] = &hfpll0.clkr,
|
||||
+ [PLL10] = &hfpll1.clkr,
|
||||
+ [PLL12] = &hfpll_l2.clkr,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map gcc_msm8960_resets[] = {
|
||||
@@ -3318,6 +3480,11 @@ static struct clk_regmap *gcc_apq8064_cl
|
||||
[PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
|
||||
[PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
|
||||
[RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
|
||||
+ [PLL9] = &hfpll0.clkr,
|
||||
+ [PLL10] = &hfpll1.clkr,
|
||||
+ [PLL12] = &hfpll_l2.clkr,
|
||||
+ [PLL16] = &hfpll2.clkr,
|
||||
+ [PLL17] = &hfpll3.clkr,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map gcc_apq8064_resets[] = {
|
||||
@@ -3477,6 +3644,11 @@ static int gcc_msm8960_probe(struct plat
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ if (match->data == &gcc_apq8064_desc) {
|
||||
+ hfpll1.d = &hfpll1_8064_data;
|
||||
+ hfpll_l2.d = &hfpll_l2_8064_data;
|
||||
+ }
|
||||
+
|
||||
tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1,
|
||||
NULL, 0);
|
||||
if (IS_ERR(tsens))
|
||||
--- a/include/dt-bindings/clock/qcom,gcc-msm8960.h
|
||||
+++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h
|
||||
@@ -319,5 +319,7 @@
|
||||
#define CE3_SRC 303
|
||||
#define CE3_CORE_CLK 304
|
||||
#define CE3_H_CLK 305
|
||||
+#define PLL16 306
|
||||
+#define PLL17 307
|
||||
|
||||
#endif
|
@ -1,120 +0,0 @@
|
||||
From 1f79131bfd512f322c16b58dca581ce39beafab9 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:25 +0530
|
||||
Subject: [PATCH 06/12] clk: qcom: Add IPQ806X's HFPLLs
|
||||
|
||||
Describe the HFPLLs present on IPQ806X devices.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/gcc-ipq806x.c | 82 ++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 82 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/gcc-ipq806x.c
|
||||
+++ b/drivers/clk/qcom/gcc-ipq806x.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
#include "clk-branch.h"
|
||||
+#include "clk-hfpll.h"
|
||||
#include "reset.h"
|
||||
|
||||
static struct clk_pll pll0 = {
|
||||
@@ -113,6 +114,84 @@ static struct clk_regmap pll8_vote = {
|
||||
},
|
||||
};
|
||||
|
||||
+static struct hfpll_data hfpll0_data = {
|
||||
+ .mode_reg = 0x3200,
|
||||
+ .l_reg = 0x3208,
|
||||
+ .m_reg = 0x320c,
|
||||
+ .n_reg = 0x3210,
|
||||
+ .config_reg = 0x3204,
|
||||
+ .status_reg = 0x321c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3214,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll0 = {
|
||||
+ .d = &hfpll0_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll0",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll1_data = {
|
||||
+ .mode_reg = 0x3240,
|
||||
+ .l_reg = 0x3248,
|
||||
+ .m_reg = 0x324c,
|
||||
+ .n_reg = 0x3250,
|
||||
+ .config_reg = 0x3244,
|
||||
+ .status_reg = 0x325c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll1 = {
|
||||
+ .d = &hfpll1_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll1",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock),
|
||||
+};
|
||||
+
|
||||
+static struct hfpll_data hfpll_l2_data = {
|
||||
+ .mode_reg = 0x3300,
|
||||
+ .l_reg = 0x3308,
|
||||
+ .m_reg = 0x330c,
|
||||
+ .n_reg = 0x3310,
|
||||
+ .config_reg = 0x3304,
|
||||
+ .status_reg = 0x331c,
|
||||
+ .config_val = 0x7845c665,
|
||||
+ .droop_reg = 0x3314,
|
||||
+ .droop_val = 0x0108c000,
|
||||
+ .min_rate = 600000000UL,
|
||||
+ .max_rate = 1800000000UL,
|
||||
+};
|
||||
+
|
||||
+static struct clk_hfpll hfpll_l2 = {
|
||||
+ .d = &hfpll_l2_data,
|
||||
+ .clkr.hw.init = &(struct clk_init_data){
|
||||
+ .parent_names = (const char *[]){ "pxo" },
|
||||
+ .num_parents = 1,
|
||||
+ .name = "hfpll_l2",
|
||||
+ .ops = &clk_ops_hfpll,
|
||||
+ .flags = CLK_IGNORE_UNUSED,
|
||||
+ },
|
||||
+ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock),
|
||||
+};
|
||||
+
|
||||
static struct clk_pll pll14 = {
|
||||
.l_reg = 0x31c4,
|
||||
.m_reg = 0x31c8,
|
||||
@@ -2798,6 +2877,9 @@ static struct clk_regmap *gcc_ipq806x_cl
|
||||
[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr,
|
||||
[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr,
|
||||
[NSSTCM_CLK] = &nss_tcm_clk.clkr,
|
||||
+ [PLL9] = &hfpll0.clkr,
|
||||
+ [PLL10] = &hfpll1.clkr,
|
||||
+ [PLL12] = &hfpll_l2.clkr,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map gcc_ipq806x_resets[] = {
|
@ -1,213 +0,0 @@
|
||||
From 4d7dc77babfef1d6cb8fd825e2f17dc3384c3272 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:26 +0530
|
||||
Subject: [PATCH 07/12] clk: qcom: Add support for Krait clocks
|
||||
|
||||
The Krait clocks are made up of a series of muxes and a divider
|
||||
that choose between a fixed rate clock and dedicated HFPLLs for
|
||||
each CPU. Instead of using mmio accesses to remux parents, the
|
||||
Krait implementation exposes the remux control via cp15
|
||||
registers. Support these clocks.
|
||||
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
[sboyd@kernel.org: Move hidden config to top outside of the visible qcom
|
||||
config zone so that menuconfig looks nice]
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/Kconfig | 4 ++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-krait.c | 124 +++++++++++++++++++++++++++++++++++
|
||||
drivers/clk/qcom/clk-krait.h | 37 +++++++++++
|
||||
4 files changed, 166 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/clk-krait.c
|
||||
create mode 100644 drivers/clk/qcom/clk-krait.h
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -1,3 +1,7 @@
|
||||
+config KRAIT_CLOCKS
|
||||
+ bool
|
||||
+ select KRAIT_L2_ACCESSORS
|
||||
+
|
||||
config QCOM_GDSC
|
||||
bool
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
--- 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-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
|
||||
clk-qcom-y += clk-hfpll.o
|
||||
clk-qcom-y += reset.o
|
||||
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-krait.c
|
||||
@@ -0,0 +1,124 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#include <asm/krait-l2-accessors.h>
|
||||
+
|
||||
+#include "clk-krait.h"
|
||||
+
|
||||
+/* Secondary and primary muxes share the same cp15 register */
|
||||
+static DEFINE_SPINLOCK(krait_clock_reg_lock);
|
||||
+
|
||||
+#define LPL_SHIFT 8
|
||||
+static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+ u32 regval;
|
||||
+
|
||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
||||
+ regval = krait_get_l2_indirect_reg(mux->offset);
|
||||
+ regval &= ~(mux->mask << mux->shift);
|
||||
+ regval |= (sel & mux->mask) << mux->shift;
|
||||
+ if (mux->lpl) {
|
||||
+ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
|
||||
+ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
|
||||
+ }
|
||||
+ krait_set_l2_indirect_reg(mux->offset, regval);
|
||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
||||
+
|
||||
+ /* Wait for switch to complete. */
|
||||
+ mb();
|
||||
+ udelay(1);
|
||||
+}
|
||||
+
|
||||
+static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+ u32 sel;
|
||||
+
|
||||
+ sel = clk_mux_reindex(index, mux->parent_map, 0);
|
||||
+ mux->en_mask = sel;
|
||||
+ /* Don't touch mux if CPU is off as it won't work */
|
||||
+ if (__clk_is_enabled(hw->clk))
|
||||
+ __krait_mux_set_sel(mux, sel);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static u8 krait_mux_get_parent(struct clk_hw *hw)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
+ u32 sel;
|
||||
+
|
||||
+ sel = krait_get_l2_indirect_reg(mux->offset);
|
||||
+ sel >>= mux->shift;
|
||||
+ sel &= mux->mask;
|
||||
+ mux->en_mask = sel;
|
||||
+
|
||||
+ return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops krait_mux_clk_ops = {
|
||||
+ .set_parent = krait_mux_set_parent,
|
||||
+ .get_parent = krait_mux_get_parent,
|
||||
+ .determine_rate = __clk_mux_determine_rate_closest,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
|
||||
+
|
||||
+/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
|
||||
+static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long *parent_rate)
|
||||
+{
|
||||
+ *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
|
||||
+ return DIV_ROUND_UP(*parent_rate, 2);
|
||||
+}
|
||||
+
|
||||
+static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
+ unsigned long parent_rate)
|
||||
+{
|
||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
||||
+ unsigned long flags;
|
||||
+ u32 val;
|
||||
+ u32 mask = BIT(d->width) - 1;
|
||||
+
|
||||
+ if (d->lpl)
|
||||
+ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
|
||||
+
|
||||
+ spin_lock_irqsave(&krait_clock_reg_lock, flags);
|
||||
+ val = krait_get_l2_indirect_reg(d->offset);
|
||||
+ val &= ~mask;
|
||||
+ krait_set_l2_indirect_reg(d->offset, val);
|
||||
+ spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned long
|
||||
+krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
+{
|
||||
+ struct krait_div2_clk *d = to_krait_div2_clk(hw);
|
||||
+ u32 mask = BIT(d->width) - 1;
|
||||
+ u32 div;
|
||||
+
|
||||
+ div = krait_get_l2_indirect_reg(d->offset);
|
||||
+ div >>= d->shift;
|
||||
+ div &= mask;
|
||||
+ div = (div + 1) * 2;
|
||||
+
|
||||
+ return DIV_ROUND_UP(parent_rate, div);
|
||||
+}
|
||||
+
|
||||
+const struct clk_ops krait_div2_clk_ops = {
|
||||
+ .round_rate = krait_div2_round_rate,
|
||||
+ .set_rate = krait_div2_set_rate,
|
||||
+ .recalc_rate = krait_div2_recalc_rate,
|
||||
+};
|
||||
+EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/clk-krait.h
|
||||
@@ -0,0 +1,37 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+
|
||||
+#ifndef __QCOM_CLK_KRAIT_H
|
||||
+#define __QCOM_CLK_KRAIT_H
|
||||
+
|
||||
+#include <linux/clk-provider.h>
|
||||
+
|
||||
+struct krait_mux_clk {
|
||||
+ unsigned int *parent_map;
|
||||
+ u32 offset;
|
||||
+ u32 mask;
|
||||
+ u32 shift;
|
||||
+ u32 en_mask;
|
||||
+ bool lpl;
|
||||
+
|
||||
+ struct clk_hw hw;
|
||||
+ struct notifier_block clk_nb;
|
||||
+};
|
||||
+
|
||||
+#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw)
|
||||
+
|
||||
+extern const struct clk_ops krait_mux_clk_ops;
|
||||
+
|
||||
+struct krait_div2_clk {
|
||||
+ u32 offset;
|
||||
+ u8 width;
|
||||
+ u32 shift;
|
||||
+ bool lpl;
|
||||
+
|
||||
+ struct clk_hw hw;
|
||||
+};
|
||||
+
|
||||
+#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw)
|
||||
+
|
||||
+extern const struct clk_ops krait_div2_clk_ops;
|
||||
+
|
||||
+#endif
|
@ -1,134 +0,0 @@
|
||||
From 3ddc3564d3c9f097986bd4ccbe34152413811335 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:27 +0530
|
||||
Subject: [PATCH 08/12] clk: qcom: Add KPSS ACC/GCC driver
|
||||
|
||||
The ACC and GCC regions present in KPSSv1 contain registers to
|
||||
control clocks and power to each Krait CPU and L2. For CPUfreq
|
||||
purposes probe these devices and expose a mux clock that chooses
|
||||
between PXO and PLL8.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/Kconfig | 8 ++++
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/kpss-xcc.c | 87 +++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 96 insertions(+)
|
||||
create mode 100644 drivers/clk/qcom/kpss-xcc.c
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -284,3 +284,11 @@ config QCOM_HFPLL
|
||||
Support for the high-frequency PLLs present on Qualcomm devices.
|
||||
Say Y if you want to support CPU frequency scaling on devices
|
||||
such as MSM8974, APQ8084, etc.
|
||||
+
|
||||
+config KPSS_XCC
|
||||
+ tristate "KPSS Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM
|
||||
+ help
|
||||
+ Support for the Krait ACC and GCC clock controllers. Say Y
|
||||
+ if you want to support CPU frequency scaling on devices such
|
||||
+ as MSM8960, APQ8064, etc.
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -45,4 +45,5 @@ obj-$(CONFIG_SDM_DISPCC_845) += dispcc-s
|
||||
obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o
|
||||
obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o
|
||||
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
|
||||
+obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/kpss-xcc.c
|
||||
@@ -0,0 +1,87 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+
|
||||
+static const char *aux_parents[] = {
|
||||
+ "pll8_vote",
|
||||
+ "pxo",
|
||||
+};
|
||||
+
|
||||
+static unsigned int aux_parent_map[] = {
|
||||
+ 3,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id kpss_xcc_match_table[] = {
|
||||
+ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL },
|
||||
+ { .compatible = "qcom,kpss-gcc" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, kpss_xcc_match_table);
|
||||
+
|
||||
+static int kpss_xcc_driver_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *id;
|
||||
+ struct clk *clk;
|
||||
+ struct resource *res;
|
||||
+ void __iomem *base;
|
||||
+ const char *name;
|
||||
+
|
||||
+ id = of_match_device(kpss_xcc_match_table, &pdev->dev);
|
||||
+ if (!id)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ if (id->data) {
|
||||
+ if (of_property_read_string_index(pdev->dev.of_node,
|
||||
+ "clock-output-names",
|
||||
+ 0, &name))
|
||||
+ return -ENODEV;
|
||||
+ base += 0x14;
|
||||
+ } else {
|
||||
+ name = "acpu_l2_aux";
|
||||
+ base += 0x28;
|
||||
+ }
|
||||
+
|
||||
+ clk = clk_register_mux_table(&pdev->dev, name, aux_parents,
|
||||
+ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3,
|
||||
+ 0, aux_parent_map, NULL);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, clk);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static int kpss_xcc_driver_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ clk_unregister_mux(platform_get_drvdata(pdev));
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver kpss_xcc_driver = {
|
||||
+ .probe = kpss_xcc_driver_probe,
|
||||
+ .remove = kpss_xcc_driver_remove,
|
||||
+ .driver = {
|
||||
+ .name = "kpss-xcc",
|
||||
+ .of_match_table = kpss_xcc_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(kpss_xcc_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:kpss-xcc");
|
@ -1,99 +0,0 @@
|
||||
From 40e5ddf4f84869815129551f4a8cfc2c223ebeae Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:28 +0530
|
||||
Subject: [PATCH 09/12] dt-bindings: arm: Document qcom,kpss-gcc
|
||||
|
||||
The ACC and GCC regions present in KPSSv1 contain registers to
|
||||
control clocks and power to each Krait CPU and L2. Documenting
|
||||
the bindings here.
|
||||
|
||||
Reviewed-by: Rob Herring <robh@kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
.../bindings/arm/msm/qcom,kpss-acc.txt | 19 ++++++++
|
||||
.../bindings/arm/msm/qcom,kpss-gcc.txt | 44 +++++++++++++++++++
|
||||
2 files changed, 63 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
||||
|
||||
--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt
|
||||
@@ -21,10 +21,29 @@ PROPERTIES
|
||||
the register region. An optional second element specifies
|
||||
the base address and size of the alias register region.
|
||||
|
||||
+- clocks:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: reference to the pll parents.
|
||||
+
|
||||
+- clock-names:
|
||||
+ Usage: required
|
||||
+ Value type: <stringlist>
|
||||
+ Definition: must be "pll8_vote", "pxo".
|
||||
+
|
||||
+- clock-output-names:
|
||||
+ Usage: optional
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the output clock. Typically acpuX_aux where X is a
|
||||
+ CPU number starting at 0.
|
||||
+
|
||||
Example:
|
||||
|
||||
clock-controller@2088000 {
|
||||
compatible = "qcom,kpss-acc-v2";
|
||||
reg = <0x02088000 0x1000>,
|
||||
<0x02008000 0x1000>;
|
||||
+ clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>;
|
||||
+ clock-names = "pll8_vote", "pxo";
|
||||
+ clock-output-names = "acpu0_aux";
|
||||
};
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt
|
||||
@@ -0,0 +1,44 @@
|
||||
+Krait Processor Sub-system (KPSS) Global Clock Controller (GCC)
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: should be one of the following. The generic compatible
|
||||
+ "qcom,kpss-gcc" should also be included.
|
||||
+ "qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc"
|
||||
+ "qcom,kpss-gcc-apq8064", "qcom,kpss-gcc"
|
||||
+ "qcom,kpss-gcc-msm8974", "qcom,kpss-gcc"
|
||||
+ "qcom,kpss-gcc-msm8960", "qcom,kpss-gcc"
|
||||
+
|
||||
+- reg:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: base address and size of the register region
|
||||
+
|
||||
+- clocks:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: reference to the pll parents.
|
||||
+
|
||||
+- clock-names:
|
||||
+ Usage: required
|
||||
+ Value type: <stringlist>
|
||||
+ Definition: must be "pll8_vote", "pxo".
|
||||
+
|
||||
+- clock-output-names:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: Name of the output clock. Typically acpu_l2_aux indicating
|
||||
+ an L2 cache auxiliary clock.
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ l2cc: clock-controller@2011000 {
|
||||
+ compatible = "qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc";
|
||||
+ reg = <0x2011000 0x1000>;
|
||||
+ clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>;
|
||||
+ clock-names = "pll8_vote", "pxo";
|
||||
+ clock-output-names = "acpu_l2_aux";
|
||||
+ };
|
@ -1,409 +0,0 @@
|
||||
From bb5c4a85051e5e0be39c775b6df85521f2ae807d Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:29 +0530
|
||||
Subject: [PATCH 10/12] clk: qcom: Add Krait clock controller driver
|
||||
|
||||
The Krait CPU clocks are made up of a primary mux and secondary
|
||||
mux for each CPU and the L2, controlled via cp15 accessors. For
|
||||
Kraits within KPSSv1 each secondary mux accepts a different aux
|
||||
source, but on KPSSv2 each secondary mux accepts the same aux
|
||||
source.
|
||||
|
||||
Cc: <devicetree@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/Kconfig | 8 +
|
||||
drivers/clk/qcom/Makefile | 1 +
|
||||
drivers/clk/qcom/clk-krait.c | 4 +-
|
||||
drivers/clk/qcom/krait-cc.c | 341 +++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 352 insertions(+), 2 deletions(-)
|
||||
create mode 100644 drivers/clk/qcom/krait-cc.c
|
||||
|
||||
--- a/drivers/clk/qcom/Kconfig
|
||||
+++ b/drivers/clk/qcom/Kconfig
|
||||
@@ -292,3 +292,11 @@ config KPSS_XCC
|
||||
Support for the Krait ACC and GCC clock controllers. Say Y
|
||||
if you want to support CPU frequency scaling on devices such
|
||||
as MSM8960, APQ8064, etc.
|
||||
+
|
||||
+config KRAITCC
|
||||
+ tristate "Krait Clock Controller"
|
||||
+ depends on COMMON_CLK_QCOM && ARM
|
||||
+ select KRAIT_CLOCKS
|
||||
+ help
|
||||
+ Support for the Krait CPU clocks on Qualcomm devices.
|
||||
+ Say Y if you want to support CPU frequency scaling.
|
||||
--- a/drivers/clk/qcom/Makefile
|
||||
+++ b/drivers/clk/qcom/Makefile
|
||||
@@ -47,3 +47,4 @@ obj-$(CONFIG_SDM_VIDEOCC_845) += videocc
|
||||
obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o
|
||||
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
|
||||
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
|
||||
+obj-$(CONFIG_KRAITCC) += krait-cc.o
|
||||
--- a/drivers/clk/qcom/clk-krait.c
|
||||
+++ b/drivers/clk/qcom/clk-krait.c
|
||||
@@ -44,7 +44,7 @@ static int krait_mux_set_parent(struct c
|
||||
struct krait_mux_clk *mux = to_krait_mux_clk(hw);
|
||||
u32 sel;
|
||||
|
||||
- sel = clk_mux_reindex(index, mux->parent_map, 0);
|
||||
+ sel = clk_mux_index_to_val(mux->parent_map, 0, index);
|
||||
mux->en_mask = sel;
|
||||
/* Don't touch mux if CPU is off as it won't work */
|
||||
if (__clk_is_enabled(hw->clk))
|
||||
@@ -63,7 +63,7 @@ static u8 krait_mux_get_parent(struct cl
|
||||
sel &= mux->mask;
|
||||
mux->en_mask = sel;
|
||||
|
||||
- return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
|
||||
+ return clk_mux_val_to_index(hw, mux->parent_map, 0, sel);
|
||||
}
|
||||
|
||||
const struct clk_ops krait_mux_clk_ops = {
|
||||
--- /dev/null
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -0,0 +1,341 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#include "clk-krait.h"
|
||||
+
|
||||
+static unsigned int sec_mux_map[] = {
|
||||
+ 2,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static unsigned int pri_mux_map[] = {
|
||||
+ 1,
|
||||
+ 2,
|
||||
+ 0,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
|
||||
+{
|
||||
+ struct krait_div2_clk *div;
|
||||
+ struct clk_init_data init = {
|
||||
+ .num_parents = 1,
|
||||
+ .ops = &krait_div2_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ const char *p_names[1];
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
||||
+ if (!div)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ div->width = 2;
|
||||
+ div->shift = 6;
|
||||
+ div->lpl = id >= 0;
|
||||
+ div->offset = offset;
|
||||
+ div->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
||||
+ if (!init.name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ init.parent_names = p_names;
|
||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
||||
+ if (!p_names[0]) {
|
||||
+ kfree(init.name);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &div->hw);
|
||||
+ kfree(p_names[0]);
|
||||
+ kfree(init.name);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+krait_add_sec_mux(struct device *dev, int id, const char *s,
|
||||
+ unsigned int offset, bool unique_aux)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux;
|
||||
+ static const char *sec_mux_list[] = {
|
||||
+ "acpu_aux",
|
||||
+ "qsb",
|
||||
+ };
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = sec_mux_list,
|
||||
+ .num_parents = ARRAY_SIZE(sec_mux_list),
|
||||
+ .ops = &krait_mux_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
+ if (!mux)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ mux->offset = offset;
|
||||
+ mux->lpl = id >= 0;
|
||||
+ mux->mask = 0x3;
|
||||
+ mux->shift = 2;
|
||||
+ mux->parent_map = sec_mux_map;
|
||||
+ mux->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
+ if (!init.name)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ if (unique_aux) {
|
||||
+ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s);
|
||||
+ if (!sec_mux_list[0]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_aux;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &mux->hw);
|
||||
+
|
||||
+ if (unique_aux)
|
||||
+ kfree(sec_mux_list[0]);
|
||||
+err_aux:
|
||||
+ kfree(init.name);
|
||||
+ return PTR_ERR_OR_ZERO(clk);
|
||||
+}
|
||||
+
|
||||
+static struct clk *
|
||||
+krait_add_pri_mux(struct device *dev, int id, const char *s,
|
||||
+ unsigned int offset)
|
||||
+{
|
||||
+ struct krait_mux_clk *mux;
|
||||
+ const char *p_names[3];
|
||||
+ struct clk_init_data init = {
|
||||
+ .parent_names = p_names,
|
||||
+ .num_parents = ARRAY_SIZE(p_names),
|
||||
+ .ops = &krait_mux_clk_ops,
|
||||
+ .flags = CLK_SET_RATE_PARENT,
|
||||
+ };
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
+ if (!mux)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ mux->mask = 0x3;
|
||||
+ mux->shift = 0;
|
||||
+ mux->offset = offset;
|
||||
+ mux->lpl = id >= 0;
|
||||
+ mux->parent_map = pri_mux_map;
|
||||
+ mux->hw.init = &init;
|
||||
+
|
||||
+ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
|
||||
+ if (!init.name)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s);
|
||||
+ if (!p_names[0]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p0;
|
||||
+ }
|
||||
+
|
||||
+ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s);
|
||||
+ if (!p_names[1]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p1;
|
||||
+ }
|
||||
+
|
||||
+ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
+ if (!p_names[2]) {
|
||||
+ clk = ERR_PTR(-ENOMEM);
|
||||
+ goto err_p2;
|
||||
+ }
|
||||
+
|
||||
+ clk = devm_clk_register(dev, &mux->hw);
|
||||
+
|
||||
+ kfree(p_names[2]);
|
||||
+err_p2:
|
||||
+ kfree(p_names[1]);
|
||||
+err_p1:
|
||||
+ kfree(p_names[0]);
|
||||
+err_p0:
|
||||
+ kfree(init.name);
|
||||
+ return clk;
|
||||
+}
|
||||
+
|
||||
+/* id < 0 for L2, otherwise id == physical CPU number */
|
||||
+static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux)
|
||||
+{
|
||||
+ int ret;
|
||||
+ unsigned int offset;
|
||||
+ void *p = NULL;
|
||||
+ const char *s;
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ if (id >= 0) {
|
||||
+ offset = 0x4501 + (0x1000 * id);
|
||||
+ s = p = kasprintf(GFP_KERNEL, "%d", id);
|
||||
+ if (!s)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+ } else {
|
||||
+ offset = 0x500;
|
||||
+ s = "_l2";
|
||||
+ }
|
||||
+
|
||||
+ ret = krait_add_div(dev, id, s, offset);
|
||||
+ if (ret) {
|
||||
+ clk = ERR_PTR(ret);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux);
|
||||
+ if (ret) {
|
||||
+ clk = ERR_PTR(ret);
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ clk = krait_add_pri_mux(dev, id, s, offset);
|
||||
+err:
|
||||
+ kfree(p);
|
||||
+ return clk;
|
||||
+}
|
||||
+
|
||||
+static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data)
|
||||
+{
|
||||
+ unsigned int idx = clkspec->args[0];
|
||||
+ struct clk **clks = data;
|
||||
+
|
||||
+ if (idx >= 5) {
|
||||
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+ }
|
||||
+
|
||||
+ return clks[idx] ? : ERR_PTR(-ENODEV);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id krait_cc_match_table[] = {
|
||||
+ { .compatible = "qcom,krait-cc-v1", (void *)1UL },
|
||||
+ { .compatible = "qcom,krait-cc-v2" },
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, krait_cc_match_table);
|
||||
+
|
||||
+static int krait_cc_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ const struct of_device_id *id;
|
||||
+ unsigned long cur_rate, aux_rate;
|
||||
+ int cpu;
|
||||
+ struct clk *clk;
|
||||
+ struct clk **clks;
|
||||
+ struct clk *l2_pri_mux_clk;
|
||||
+
|
||||
+ id = of_match_device(krait_cc_match_table, dev);
|
||||
+ if (!id)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */
|
||||
+ clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+
|
||||
+ if (!id->data) {
|
||||
+ clk = clk_register_fixed_factor(dev, "acpu_aux",
|
||||
+ "gpll0_vote", 0, 1, 2);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ /* Krait configurations have at most 4 CPUs and one L2 */
|
||||
+ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL);
|
||||
+ if (!clks)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ clk = krait_add_clks(dev, cpu, id->data);
|
||||
+ if (IS_ERR(clk))
|
||||
+ return PTR_ERR(clk);
|
||||
+ clks[cpu] = clk;
|
||||
+ }
|
||||
+
|
||||
+ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data);
|
||||
+ if (IS_ERR(l2_pri_mux_clk))
|
||||
+ return PTR_ERR(l2_pri_mux_clk);
|
||||
+ clks[4] = l2_pri_mux_clk;
|
||||
+
|
||||
+ /*
|
||||
+ * We don't want the CPU or L2 clocks to be turned off at late init
|
||||
+ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
|
||||
+ * refcount of these clocks. Any cpufreq/hotplug manager can assume
|
||||
+ * that the clocks have already been prepared and enabled by the time
|
||||
+ * they take over.
|
||||
+ */
|
||||
+ for_each_online_cpu(cpu) {
|
||||
+ clk_prepare_enable(l2_pri_mux_clk);
|
||||
+ WARN(clk_prepare_enable(clks[cpu]),
|
||||
+ "Unable to turn on CPU%d clock", cpu);
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Force reinit of HFPLLs and muxes to overwrite any potential
|
||||
+ * incorrect configuration of HFPLLs and muxes by the bootloader.
|
||||
+ * While at it, also make sure the cores are running at known rates
|
||||
+ * and print the current rate.
|
||||
+ *
|
||||
+ * The clocks are set to aux clock rate first to make sure the
|
||||
+ * secondary mux is not sourcing off of QSB. The rate is then set to
|
||||
+ * two different rates to force a HFPLL reinit under all
|
||||
+ * circumstances.
|
||||
+ */
|
||||
+ cur_rate = clk_get_rate(l2_pri_mux_clk);
|
||||
+ aux_rate = 384000000;
|
||||
+ if (cur_rate == 1) {
|
||||
+ pr_info("L2 @ QSB rate. Forcing new rate.\n");
|
||||
+ cur_rate = aux_rate;
|
||||
+ }
|
||||
+ clk_set_rate(l2_pri_mux_clk, aux_rate);
|
||||
+ clk_set_rate(l2_pri_mux_clk, 2);
|
||||
+ clk_set_rate(l2_pri_mux_clk, cur_rate);
|
||||
+ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000);
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ clk = clks[cpu];
|
||||
+ cur_rate = clk_get_rate(clk);
|
||||
+ if (cur_rate == 1) {
|
||||
+ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu);
|
||||
+ cur_rate = aux_rate;
|
||||
+ }
|
||||
+
|
||||
+ clk_set_rate(clk, aux_rate);
|
||||
+ clk_set_rate(clk, 2);
|
||||
+ clk_set_rate(clk, cur_rate);
|
||||
+ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000);
|
||||
+ }
|
||||
+
|
||||
+ of_clk_add_provider(dev->of_node, krait_of_get, clks);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver krait_cc_driver = {
|
||||
+ .probe = krait_cc_probe,
|
||||
+ .driver = {
|
||||
+ .name = "krait-cc",
|
||||
+ .of_match_table = krait_cc_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(krait_cc_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Krait CPU Clock Driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:krait-cc");
|
@ -1,55 +0,0 @@
|
||||
From bf4503ccf321811192cb07f9711556237c3cf668 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:30 +0530
|
||||
Subject: [PATCH 11/12] dt-bindings: clock: Document qcom,krait-cc
|
||||
|
||||
The Krait clock controller controls the krait CPU and the L2 clocks
|
||||
consisting a primary mux and secondary mux. Add document for that.
|
||||
|
||||
Reviewed-by: Rob Herring <robh@kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
.../bindings/clock/qcom,krait-cc.txt | 34 +++++++++++++++++++
|
||||
1 file changed, 34 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt
|
||||
@@ -0,0 +1,34 @@
|
||||
+Krait Clock Controller
|
||||
+
|
||||
+PROPERTIES
|
||||
+
|
||||
+- compatible:
|
||||
+ Usage: required
|
||||
+ Value type: <string>
|
||||
+ Definition: must be one of:
|
||||
+ "qcom,krait-cc-v1"
|
||||
+ "qcom,krait-cc-v2"
|
||||
+
|
||||
+- #clock-cells:
|
||||
+ Usage: required
|
||||
+ Value type: <u32>
|
||||
+ Definition: must be 1
|
||||
+
|
||||
+- clocks:
|
||||
+ Usage: required
|
||||
+ Value type: <prop-encoded-array>
|
||||
+ Definition: reference to the clock parents of hfpll, secondary muxes.
|
||||
+
|
||||
+- clock-names:
|
||||
+ Usage: required
|
||||
+ Value type: <stringlist>
|
||||
+ Definition: must be "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb".
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ kraitcc: clock-controller {
|
||||
+ compatible = "qcom,krait-cc-v1";
|
||||
+ clocks = <&hfpll0>, <&hfpll1>, <&acpu0_aux>, <&acpu1_aux>, <qsb>;
|
||||
+ clock-names = "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb";
|
||||
+ #clock-cells = <1>;
|
||||
+ };
|
@ -1,152 +0,0 @@
|
||||
From 77612720a2362230af726baa4149c40ec7a7fb05 Mon Sep 17 00:00:00 2001
|
||||
From: Sricharan R <sricharan@codeaurora.org>
|
||||
Date: Tue, 14 Aug 2018 17:42:31 +0530
|
||||
Subject: [PATCH 12/12] clk: qcom: Add safe switch hook for krait mux clocks
|
||||
|
||||
When the Hfplls are reprogrammed during the rate change,
|
||||
the primary muxes which are sourced from the same hfpll
|
||||
for higher frequencies, needs to be switched to the 'safe
|
||||
secondary mux' as the parent for that small window. This
|
||||
is done by registering a clk notifier for the muxes and
|
||||
switching to the safe parent in the PRE_RATE_CHANGE notifier
|
||||
and back to the original parent in the POST_RATE_CHANGE notifier.
|
||||
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
Tested-by: Craig Tatlor <ctatlor97@gmail.com>
|
||||
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
|
||||
---
|
||||
drivers/clk/qcom/clk-krait.c | 2 ++
|
||||
drivers/clk/qcom/clk-krait.h | 3 ++
|
||||
drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 61 insertions(+)
|
||||
|
||||
--- a/drivers/clk/qcom/clk-krait.c
|
||||
+++ b/drivers/clk/qcom/clk-krait.c
|
||||
@@ -50,6 +50,8 @@ static int krait_mux_set_parent(struct c
|
||||
if (__clk_is_enabled(hw->clk))
|
||||
__krait_mux_set_sel(mux, sel);
|
||||
|
||||
+ mux->reparent = true;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/drivers/clk/qcom/clk-krait.h
|
||||
+++ b/drivers/clk/qcom/clk-krait.h
|
||||
@@ -12,6 +12,9 @@ struct krait_mux_clk {
|
||||
u32 shift;
|
||||
u32 en_mask;
|
||||
bool lpl;
|
||||
+ u8 safe_sel;
|
||||
+ u8 old_index;
|
||||
+ bool reparent;
|
||||
|
||||
struct clk_hw hw;
|
||||
struct notifier_block clk_nb;
|
||||
--- a/drivers/clk/qcom/krait-cc.c
|
||||
+++ b/drivers/clk/qcom/krait-cc.c
|
||||
@@ -26,6 +26,49 @@ static unsigned int pri_mux_map[] = {
|
||||
0,
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * Notifier function for switching the muxes to safe parent
|
||||
+ * while the hfpll is getting reprogrammed.
|
||||
+ */
|
||||
+static int krait_notifier_cb(struct notifier_block *nb,
|
||||
+ unsigned long event,
|
||||
+ void *data)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk,
|
||||
+ clk_nb);
|
||||
+ /* Switch to safe parent */
|
||||
+ if (event == PRE_RATE_CHANGE) {
|
||||
+ mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw);
|
||||
+ ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel);
|
||||
+ mux->reparent = false;
|
||||
+ /*
|
||||
+ * By the time POST_RATE_CHANGE notifier is called,
|
||||
+ * clk framework itself would have changed the parent for the new rate.
|
||||
+ * Only otherwise, put back to the old parent.
|
||||
+ */
|
||||
+ } else if (event == POST_RATE_CHANGE) {
|
||||
+ if (!mux->reparent)
|
||||
+ ret = krait_mux_clk_ops.set_parent(&mux->hw,
|
||||
+ mux->old_index);
|
||||
+ }
|
||||
+
|
||||
+ return notifier_from_errno(ret);
|
||||
+}
|
||||
+
|
||||
+static int krait_notifier_register(struct device *dev, struct clk *clk,
|
||||
+ struct krait_mux_clk *mux)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mux->clk_nb.notifier_call = krait_notifier_cb;
|
||||
+ ret = clk_notifier_register(clk, &mux->clk_nb);
|
||||
+ if (ret)
|
||||
+ dev_err(dev, "failed to register clock notifier: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
krait_add_div(struct device *dev, int id, const char *s, unsigned int offset)
|
||||
{
|
||||
@@ -70,6 +113,7 @@ static int
|
||||
krait_add_sec_mux(struct device *dev, int id, const char *s,
|
||||
unsigned int offset, bool unique_aux)
|
||||
{
|
||||
+ int ret;
|
||||
struct krait_mux_clk *mux;
|
||||
static const char *sec_mux_list[] = {
|
||||
"acpu_aux",
|
||||
@@ -93,6 +137,7 @@ krait_add_sec_mux(struct device *dev, in
|
||||
mux->shift = 2;
|
||||
mux->parent_map = sec_mux_map;
|
||||
mux->hw.init = &init;
|
||||
+ mux->safe_sel = 0;
|
||||
|
||||
init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s);
|
||||
if (!init.name)
|
||||
@@ -108,6 +153,11 @@ krait_add_sec_mux(struct device *dev, in
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
|
||||
+ ret = krait_notifier_register(dev, clk, mux);
|
||||
+ if (ret)
|
||||
+ goto unique_aux;
|
||||
+
|
||||
+unique_aux:
|
||||
if (unique_aux)
|
||||
kfree(sec_mux_list[0]);
|
||||
err_aux:
|
||||
@@ -119,6 +169,7 @@ static struct clk *
|
||||
krait_add_pri_mux(struct device *dev, int id, const char *s,
|
||||
unsigned int offset)
|
||||
{
|
||||
+ int ret;
|
||||
struct krait_mux_clk *mux;
|
||||
const char *p_names[3];
|
||||
struct clk_init_data init = {
|
||||
@@ -139,6 +190,7 @@ krait_add_pri_mux(struct device *dev, in
|
||||
mux->lpl = id >= 0;
|
||||
mux->parent_map = pri_mux_map;
|
||||
mux->hw.init = &init;
|
||||
+ mux->safe_sel = 2;
|
||||
|
||||
init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s);
|
||||
if (!init.name)
|
||||
@@ -164,6 +216,10 @@ krait_add_pri_mux(struct device *dev, in
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
|
||||
+ ret = krait_notifier_register(dev, clk, mux);
|
||||
+ if (ret)
|
||||
+ goto err_p3;
|
||||
+err_p3:
|
||||
kfree(p_names[2]);
|
||||
err_p2:
|
||||
kfree(p_names[1]);
|
@ -1,25 +0,0 @@
|
||||
From 50c0b12f098fb3cfac7abcc0f2b5409f6bac5fa2 Mon Sep 17 00:00:00 2001
|
||||
From: Yangtao Li <tiny.windzz@gmail.com>
|
||||
Date: Mon, 4 Feb 2019 01:13:10 -0500
|
||||
Subject: [PATCH] cpufreq: qcom-kryo: make some variables static
|
||||
|
||||
The variables are local to the source and do not
|
||||
need to be in global scope, so make them static.
|
||||
|
||||
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
|
||||
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/qcom-cpufreq-kryo.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/cpufreq/qcom-cpufreq-kryo.c
|
||||
+++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
|
||||
@@ -42,7 +42,7 @@ enum _msm8996_version {
|
||||
NUM_OF_MSM8996_VERSIONS,
|
||||
};
|
||||
|
||||
-struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
|
||||
+static struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
|
||||
|
||||
static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void)
|
||||
{
|
@ -1,579 +0,0 @@
|
||||
From 7d12709544b8b3fb9727a34a664b8380e1e3493a Mon Sep 17 00:00:00 2001
|
||||
From: Sricharan R <sricharan@codeaurora.org>
|
||||
Date: Thu, 25 Jul 2019 12:41:31 +0200
|
||||
Subject: [PATCH] cpufreq: qcom: Re-organise kryo cpufreq to use it for other
|
||||
nvmem based qcom socs
|
||||
|
||||
The kryo cpufreq driver reads the nvmem cell and uses that data to
|
||||
populate the opps. There are other qcom cpufreq socs like krait which
|
||||
does similar thing. Except for the interpretation of the read data,
|
||||
rest of the driver is same for both the cases. So pull the common things
|
||||
out for reuse.
|
||||
|
||||
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
|
||||
[niklas.cassel@linaro.org: split dt-binding into a separate patch and
|
||||
do not rename the compatible string. Update MAINTAINERS file.]
|
||||
Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
|
||||
Reviewed-by: Ilia Lin <ilia.lin@kernel.org>
|
||||
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
|
||||
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
---
|
||||
MAINTAINERS | 4 +-
|
||||
drivers/cpufreq/Kconfig.arm | 4 +-
|
||||
drivers/cpufreq/Makefile | 2 +-
|
||||
...om-cpufreq-kryo.c => qcom-cpufreq-nvmem.c} | 122 +++++++++++-------
|
||||
4 files changed, 78 insertions(+), 54 deletions(-)
|
||||
rename drivers/cpufreq/{qcom-cpufreq-kryo.c => qcom-cpufreq-nvmem.c} (69%)
|
||||
|
||||
--- a/drivers/cpufreq/Kconfig.arm
|
||||
+++ b/drivers/cpufreq/Kconfig.arm
|
||||
@@ -110,8 +110,8 @@ config ARM_OMAP2PLUS_CPUFREQ
|
||||
depends on ARCH_OMAP2PLUS
|
||||
default ARCH_OMAP2PLUS
|
||||
|
||||
-config ARM_QCOM_CPUFREQ_KRYO
|
||||
- tristate "Qualcomm Kryo based CPUFreq"
|
||||
+config ARM_QCOM_CPUFREQ_NVMEM
|
||||
+ tristate "Qualcomm nvmem based CPUFreq"
|
||||
depends on ARM64
|
||||
depends on QCOM_QFPROM
|
||||
depends on QCOM_SMEM
|
||||
--- a/drivers/cpufreq/Makefile
|
||||
+++ b/drivers/cpufreq/Makefile
|
||||
@@ -64,7 +64,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cp
|
||||
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
|
||||
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
|
||||
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
|
||||
-obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
|
||||
+obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
|
||||
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
|
||||
--- a/drivers/cpufreq/qcom-cpufreq-kryo.c
|
||||
+++ /dev/null
|
||||
@@ -1,249 +0,0 @@
|
||||
-// SPDX-License-Identifier: GPL-2.0
|
||||
-/*
|
||||
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
- */
|
||||
-
|
||||
-/*
|
||||
- * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
|
||||
- * the CPU frequency subset and voltage value of each OPP varies
|
||||
- * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
|
||||
- * defines the voltage and frequency value based on the msm-id in SMEM
|
||||
- * and speedbin blown in the efuse combination.
|
||||
- * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
|
||||
- * to provide the OPP framework with required information.
|
||||
- * This is used to determine the voltage and frequency value for each OPP of
|
||||
- * operating-points-v2 table when it is parsed by the OPP framework.
|
||||
- */
|
||||
-
|
||||
-#include <linux/cpu.h>
|
||||
-#include <linux/err.h>
|
||||
-#include <linux/init.h>
|
||||
-#include <linux/kernel.h>
|
||||
-#include <linux/module.h>
|
||||
-#include <linux/nvmem-consumer.h>
|
||||
-#include <linux/of.h>
|
||||
-#include <linux/platform_device.h>
|
||||
-#include <linux/pm_opp.h>
|
||||
-#include <linux/slab.h>
|
||||
-#include <linux/soc/qcom/smem.h>
|
||||
-
|
||||
-#define MSM_ID_SMEM 137
|
||||
-
|
||||
-enum _msm_id {
|
||||
- MSM8996V3 = 0xF6ul,
|
||||
- APQ8096V3 = 0x123ul,
|
||||
- MSM8996SG = 0x131ul,
|
||||
- APQ8096SG = 0x138ul,
|
||||
-};
|
||||
-
|
||||
-enum _msm8996_version {
|
||||
- MSM8996_V3,
|
||||
- MSM8996_SG,
|
||||
- NUM_OF_MSM8996_VERSIONS,
|
||||
-};
|
||||
-
|
||||
-static struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
|
||||
-
|
||||
-static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void)
|
||||
-{
|
||||
- size_t len;
|
||||
- u32 *msm_id;
|
||||
- enum _msm8996_version version;
|
||||
-
|
||||
- msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
|
||||
- if (IS_ERR(msm_id))
|
||||
- return NUM_OF_MSM8996_VERSIONS;
|
||||
-
|
||||
- /* The first 4 bytes are format, next to them is the actual msm-id */
|
||||
- msm_id++;
|
||||
-
|
||||
- switch ((enum _msm_id)*msm_id) {
|
||||
- case MSM8996V3:
|
||||
- case APQ8096V3:
|
||||
- version = MSM8996_V3;
|
||||
- break;
|
||||
- case MSM8996SG:
|
||||
- case APQ8096SG:
|
||||
- version = MSM8996_SG;
|
||||
- break;
|
||||
- default:
|
||||
- version = NUM_OF_MSM8996_VERSIONS;
|
||||
- }
|
||||
-
|
||||
- return version;
|
||||
-}
|
||||
-
|
||||
-static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
|
||||
-{
|
||||
- struct opp_table **opp_tables;
|
||||
- enum _msm8996_version msm8996_version;
|
||||
- struct nvmem_cell *speedbin_nvmem;
|
||||
- struct device_node *np;
|
||||
- struct device *cpu_dev;
|
||||
- unsigned cpu;
|
||||
- u8 *speedbin;
|
||||
- u32 versions;
|
||||
- size_t len;
|
||||
- int ret;
|
||||
-
|
||||
- cpu_dev = get_cpu_device(0);
|
||||
- if (!cpu_dev)
|
||||
- return -ENODEV;
|
||||
-
|
||||
- msm8996_version = qcom_cpufreq_kryo_get_msm_id();
|
||||
- if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
|
||||
- dev_err(cpu_dev, "Not Snapdragon 820/821!");
|
||||
- return -ENODEV;
|
||||
- }
|
||||
-
|
||||
- np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
|
||||
- if (!np)
|
||||
- return -ENOENT;
|
||||
-
|
||||
- ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
|
||||
- if (!ret) {
|
||||
- of_node_put(np);
|
||||
- return -ENOENT;
|
||||
- }
|
||||
-
|
||||
- speedbin_nvmem = of_nvmem_cell_get(np, NULL);
|
||||
- of_node_put(np);
|
||||
- if (IS_ERR(speedbin_nvmem)) {
|
||||
- if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
|
||||
- dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
|
||||
- PTR_ERR(speedbin_nvmem));
|
||||
- return PTR_ERR(speedbin_nvmem);
|
||||
- }
|
||||
-
|
||||
- speedbin = nvmem_cell_read(speedbin_nvmem, &len);
|
||||
- nvmem_cell_put(speedbin_nvmem);
|
||||
- if (IS_ERR(speedbin))
|
||||
- return PTR_ERR(speedbin);
|
||||
-
|
||||
- switch (msm8996_version) {
|
||||
- case MSM8996_V3:
|
||||
- versions = 1 << (unsigned int)(*speedbin);
|
||||
- break;
|
||||
- case MSM8996_SG:
|
||||
- versions = 1 << ((unsigned int)(*speedbin) + 4);
|
||||
- break;
|
||||
- default:
|
||||
- BUG();
|
||||
- break;
|
||||
- }
|
||||
- kfree(speedbin);
|
||||
-
|
||||
- opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL);
|
||||
- if (!opp_tables)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- for_each_possible_cpu(cpu) {
|
||||
- cpu_dev = get_cpu_device(cpu);
|
||||
- if (NULL == cpu_dev) {
|
||||
- ret = -ENODEV;
|
||||
- goto free_opp;
|
||||
- }
|
||||
-
|
||||
- opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
|
||||
- &versions, 1);
|
||||
- if (IS_ERR(opp_tables[cpu])) {
|
||||
- ret = PTR_ERR(opp_tables[cpu]);
|
||||
- dev_err(cpu_dev, "Failed to set supported hardware\n");
|
||||
- goto free_opp;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
|
||||
- NULL, 0);
|
||||
- if (!IS_ERR(cpufreq_dt_pdev)) {
|
||||
- platform_set_drvdata(pdev, opp_tables);
|
||||
- return 0;
|
||||
- }
|
||||
-
|
||||
- ret = PTR_ERR(cpufreq_dt_pdev);
|
||||
- dev_err(cpu_dev, "Failed to register platform device\n");
|
||||
-
|
||||
-free_opp:
|
||||
- for_each_possible_cpu(cpu) {
|
||||
- if (IS_ERR_OR_NULL(opp_tables[cpu]))
|
||||
- break;
|
||||
- dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
- }
|
||||
- kfree(opp_tables);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-static int qcom_cpufreq_kryo_remove(struct platform_device *pdev)
|
||||
-{
|
||||
- struct opp_table **opp_tables = platform_get_drvdata(pdev);
|
||||
- unsigned int cpu;
|
||||
-
|
||||
- platform_device_unregister(cpufreq_dt_pdev);
|
||||
-
|
||||
- for_each_possible_cpu(cpu)
|
||||
- dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
-
|
||||
- kfree(opp_tables);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static struct platform_driver qcom_cpufreq_kryo_driver = {
|
||||
- .probe = qcom_cpufreq_kryo_probe,
|
||||
- .remove = qcom_cpufreq_kryo_remove,
|
||||
- .driver = {
|
||||
- .name = "qcom-cpufreq-kryo",
|
||||
- },
|
||||
-};
|
||||
-
|
||||
-static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
|
||||
- { .compatible = "qcom,apq8096", },
|
||||
- { .compatible = "qcom,msm8996", },
|
||||
- {}
|
||||
-};
|
||||
-
|
||||
-/*
|
||||
- * Since the driver depends on smem and nvmem drivers, which may
|
||||
- * return EPROBE_DEFER, all the real activity is done in the probe,
|
||||
- * which may be defered as well. The init here is only registering
|
||||
- * the driver and the platform device.
|
||||
- */
|
||||
-static int __init qcom_cpufreq_kryo_init(void)
|
||||
-{
|
||||
- struct device_node *np = of_find_node_by_path("/");
|
||||
- const struct of_device_id *match;
|
||||
- int ret;
|
||||
-
|
||||
- if (!np)
|
||||
- return -ENODEV;
|
||||
-
|
||||
- match = of_match_node(qcom_cpufreq_kryo_match_list, np);
|
||||
- of_node_put(np);
|
||||
- if (!match)
|
||||
- return -ENODEV;
|
||||
-
|
||||
- ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
|
||||
- if (unlikely(ret < 0))
|
||||
- return ret;
|
||||
-
|
||||
- kryo_cpufreq_pdev = platform_device_register_simple(
|
||||
- "qcom-cpufreq-kryo", -1, NULL, 0);
|
||||
- ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev);
|
||||
- if (0 == ret)
|
||||
- return 0;
|
||||
-
|
||||
- platform_driver_unregister(&qcom_cpufreq_kryo_driver);
|
||||
- return ret;
|
||||
-}
|
||||
-module_init(qcom_cpufreq_kryo_init);
|
||||
-
|
||||
-static void __exit qcom_cpufreq_kryo_exit(void)
|
||||
-{
|
||||
- platform_device_unregister(kryo_cpufreq_pdev);
|
||||
- platform_driver_unregister(&qcom_cpufreq_kryo_driver);
|
||||
-}
|
||||
-module_exit(qcom_cpufreq_kryo_exit);
|
||||
-
|
||||
-MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
|
||||
-MODULE_LICENSE("GPL v2");
|
||||
--- /dev/null
|
||||
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
|
||||
@@ -0,0 +1,273 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
|
||||
+ * the CPU frequency subset and voltage value of each OPP varies
|
||||
+ * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
|
||||
+ * defines the voltage and frequency value based on the msm-id in SMEM
|
||||
+ * and speedbin blown in the efuse combination.
|
||||
+ * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
|
||||
+ * to provide the OPP framework with required information.
|
||||
+ * This is used to determine the voltage and frequency value for each OPP of
|
||||
+ * operating-points-v2 table when it is parsed by the OPP framework.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/cpu.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pm_opp.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/soc/qcom/smem.h>
|
||||
+
|
||||
+#define MSM_ID_SMEM 137
|
||||
+
|
||||
+enum _msm_id {
|
||||
+ MSM8996V3 = 0xF6ul,
|
||||
+ APQ8096V3 = 0x123ul,
|
||||
+ MSM8996SG = 0x131ul,
|
||||
+ APQ8096SG = 0x138ul,
|
||||
+};
|
||||
+
|
||||
+enum _msm8996_version {
|
||||
+ MSM8996_V3,
|
||||
+ MSM8996_SG,
|
||||
+ NUM_OF_MSM8996_VERSIONS,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
|
||||
+
|
||||
+static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
|
||||
+{
|
||||
+ size_t len;
|
||||
+ u32 *msm_id;
|
||||
+ enum _msm8996_version version;
|
||||
+
|
||||
+ msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
|
||||
+ if (IS_ERR(msm_id))
|
||||
+ return NUM_OF_MSM8996_VERSIONS;
|
||||
+
|
||||
+ /* The first 4 bytes are format, next to them is the actual msm-id */
|
||||
+ msm_id++;
|
||||
+
|
||||
+ switch ((enum _msm_id)*msm_id) {
|
||||
+ case MSM8996V3:
|
||||
+ case APQ8096V3:
|
||||
+ version = MSM8996_V3;
|
||||
+ break;
|
||||
+ case MSM8996SG:
|
||||
+ case APQ8096SG:
|
||||
+ version = MSM8996_SG;
|
||||
+ break;
|
||||
+ default:
|
||||
+ version = NUM_OF_MSM8996_VERSIONS;
|
||||
+ }
|
||||
+
|
||||
+ return version;
|
||||
+}
|
||||
+
|
||||
+static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
|
||||
+ struct nvmem_cell *speedbin_nvmem,
|
||||
+ u32 *versions)
|
||||
+{
|
||||
+ size_t len;
|
||||
+ u8 *speedbin;
|
||||
+ enum _msm8996_version msm8996_version;
|
||||
+
|
||||
+ msm8996_version = qcom_cpufreq_get_msm_id();
|
||||
+ if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
|
||||
+ dev_err(cpu_dev, "Not Snapdragon 820/821!");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ speedbin = nvmem_cell_read(speedbin_nvmem, &len);
|
||||
+ if (IS_ERR(speedbin))
|
||||
+ return PTR_ERR(speedbin);
|
||||
+
|
||||
+ switch (msm8996_version) {
|
||||
+ case MSM8996_V3:
|
||||
+ *versions = 1 << (unsigned int)(*speedbin);
|
||||
+ break;
|
||||
+ case MSM8996_SG:
|
||||
+ *versions = 1 << ((unsigned int)(*speedbin) + 4);
|
||||
+ break;
|
||||
+ default:
|
||||
+ BUG();
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ kfree(speedbin);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct opp_table **opp_tables;
|
||||
+ int (*get_version)(struct device *cpu_dev,
|
||||
+ struct nvmem_cell *speedbin_nvmem,
|
||||
+ u32 *versions);
|
||||
+ struct nvmem_cell *speedbin_nvmem;
|
||||
+ struct device_node *np;
|
||||
+ struct device *cpu_dev;
|
||||
+ unsigned cpu;
|
||||
+ u32 versions;
|
||||
+ const struct of_device_id *match;
|
||||
+ int ret;
|
||||
+
|
||||
+ cpu_dev = get_cpu_device(0);
|
||||
+ if (!cpu_dev)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ match = pdev->dev.platform_data;
|
||||
+ get_version = match->data;
|
||||
+ if (!get_version)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
|
||||
+ if (!np)
|
||||
+ return -ENOENT;
|
||||
+
|
||||
+ ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
|
||||
+ if (!ret) {
|
||||
+ of_node_put(np);
|
||||
+ return -ENOENT;
|
||||
+ }
|
||||
+
|
||||
+ speedbin_nvmem = of_nvmem_cell_get(np, NULL);
|
||||
+ of_node_put(np);
|
||||
+ if (IS_ERR(speedbin_nvmem)) {
|
||||
+ if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
|
||||
+ dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
|
||||
+ PTR_ERR(speedbin_nvmem));
|
||||
+ return PTR_ERR(speedbin_nvmem);
|
||||
+ }
|
||||
+
|
||||
+ ret = get_version(cpu_dev, speedbin_nvmem, &versions);
|
||||
+ nvmem_cell_put(speedbin_nvmem);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL);
|
||||
+ if (!opp_tables)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ cpu_dev = get_cpu_device(cpu);
|
||||
+ if (NULL == cpu_dev) {
|
||||
+ ret = -ENODEV;
|
||||
+ goto free_opp;
|
||||
+ }
|
||||
+
|
||||
+ opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
|
||||
+ &versions, 1);
|
||||
+ if (IS_ERR(opp_tables[cpu])) {
|
||||
+ ret = PTR_ERR(opp_tables[cpu]);
|
||||
+ dev_err(cpu_dev, "Failed to set supported hardware\n");
|
||||
+ goto free_opp;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
|
||||
+ NULL, 0);
|
||||
+ if (!IS_ERR(cpufreq_dt_pdev)) {
|
||||
+ platform_set_drvdata(pdev, opp_tables);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ ret = PTR_ERR(cpufreq_dt_pdev);
|
||||
+ dev_err(cpu_dev, "Failed to register platform device\n");
|
||||
+
|
||||
+free_opp:
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ if (IS_ERR_OR_NULL(opp_tables[cpu]))
|
||||
+ break;
|
||||
+ dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
+ }
|
||||
+ kfree(opp_tables);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qcom_cpufreq_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct opp_table **opp_tables = platform_get_drvdata(pdev);
|
||||
+ unsigned int cpu;
|
||||
+
|
||||
+ platform_device_unregister(cpufreq_dt_pdev);
|
||||
+
|
||||
+ for_each_possible_cpu(cpu)
|
||||
+ dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
+
|
||||
+ kfree(opp_tables);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver qcom_cpufreq_driver = {
|
||||
+ .probe = qcom_cpufreq_probe,
|
||||
+ .remove = qcom_cpufreq_remove,
|
||||
+ .driver = {
|
||||
+ .name = "qcom-cpufreq-nvmem",
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
|
||||
+ { .compatible = "qcom,apq8096",
|
||||
+ .data = qcom_cpufreq_kryo_name_version },
|
||||
+ { .compatible = "qcom,msm8996",
|
||||
+ .data = qcom_cpufreq_kryo_name_version },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Since the driver depends on smem and nvmem drivers, which may
|
||||
+ * return EPROBE_DEFER, all the real activity is done in the probe,
|
||||
+ * which may be defered as well. The init here is only registering
|
||||
+ * the driver and the platform device.
|
||||
+ */
|
||||
+static int __init qcom_cpufreq_init(void)
|
||||
+{
|
||||
+ struct device_node *np = of_find_node_by_path("/");
|
||||
+ const struct of_device_id *match;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!np)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ match = of_match_node(qcom_cpufreq_match_list, np);
|
||||
+ of_node_put(np);
|
||||
+ if (!match)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ ret = platform_driver_register(&qcom_cpufreq_driver);
|
||||
+ if (unlikely(ret < 0))
|
||||
+ return ret;
|
||||
+
|
||||
+ cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
|
||||
+ -1, match, sizeof(*match));
|
||||
+ ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
|
||||
+ if (0 == ret)
|
||||
+ return 0;
|
||||
+
|
||||
+ platform_driver_unregister(&qcom_cpufreq_driver);
|
||||
+ return ret;
|
||||
+}
|
||||
+module_init(qcom_cpufreq_init);
|
||||
+
|
||||
+static void __exit qcom_cpufreq_exit(void)
|
||||
+{
|
||||
+ platform_device_unregister(cpufreq_pdev);
|
||||
+ platform_driver_unregister(&qcom_cpufreq_driver);
|
||||
+}
|
||||
+module_exit(qcom_cpufreq_exit);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
@ -1,241 +0,0 @@
|
||||
From 57f2f8b4aa0c6b41a284da82bfa40dc3b2abe9a5 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Cassel <niklas.cassel@linaro.org>
|
||||
Date: Thu, 25 Jul 2019 12:41:33 +0200
|
||||
Subject: [PATCH] cpufreq: qcom: Refactor the driver to make it easier to
|
||||
extend
|
||||
|
||||
Refactor the driver to make it easier to extend in a later commit.
|
||||
|
||||
Create a driver struct to collect all common resources, in order to make
|
||||
it easier to free up all common resources.
|
||||
Create a driver match_data struct to make it easier to extend the driver
|
||||
with support for new features that might only be supported on certain SoCs.
|
||||
|
||||
Co-developed-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
|
||||
Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
|
||||
Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
|
||||
Reviewed-by: Ilia Lin <ilia.lin@kernel.org>
|
||||
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
---
|
||||
drivers/cpufreq/qcom-cpufreq-nvmem.c | 123 +++++++++++++++++----------
|
||||
1 file changed, 79 insertions(+), 44 deletions(-)
|
||||
|
||||
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
|
||||
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
|
||||
@@ -43,6 +43,20 @@ enum _msm8996_version {
|
||||
NUM_OF_MSM8996_VERSIONS,
|
||||
};
|
||||
|
||||
+struct qcom_cpufreq_drv;
|
||||
+
|
||||
+struct qcom_cpufreq_match_data {
|
||||
+ int (*get_version)(struct device *cpu_dev,
|
||||
+ struct nvmem_cell *speedbin_nvmem,
|
||||
+ struct qcom_cpufreq_drv *drv);
|
||||
+};
|
||||
+
|
||||
+struct qcom_cpufreq_drv {
|
||||
+ struct opp_table **opp_tables;
|
||||
+ u32 versions;
|
||||
+ const struct qcom_cpufreq_match_data *data;
|
||||
+};
|
||||
+
|
||||
static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
|
||||
|
||||
static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
|
||||
@@ -76,7 +90,7 @@ static enum _msm8996_version qcom_cpufre
|
||||
|
||||
static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
|
||||
struct nvmem_cell *speedbin_nvmem,
|
||||
- u32 *versions)
|
||||
+ struct qcom_cpufreq_drv *drv)
|
||||
{
|
||||
size_t len;
|
||||
u8 *speedbin;
|
||||
@@ -94,10 +108,10 @@ static int qcom_cpufreq_kryo_name_versio
|
||||
|
||||
switch (msm8996_version) {
|
||||
case MSM8996_V3:
|
||||
- *versions = 1 << (unsigned int)(*speedbin);
|
||||
+ drv->versions = 1 << (unsigned int)(*speedbin);
|
||||
break;
|
||||
case MSM8996_SG:
|
||||
- *versions = 1 << ((unsigned int)(*speedbin) + 4);
|
||||
+ drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
@@ -108,17 +122,17 @@ static int qcom_cpufreq_kryo_name_versio
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static const struct qcom_cpufreq_match_data match_data_kryo = {
|
||||
+ .get_version = qcom_cpufreq_kryo_name_version,
|
||||
+};
|
||||
+
|
||||
static int qcom_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
- struct opp_table **opp_tables;
|
||||
- int (*get_version)(struct device *cpu_dev,
|
||||
- struct nvmem_cell *speedbin_nvmem,
|
||||
- u32 *versions);
|
||||
+ struct qcom_cpufreq_drv *drv;
|
||||
struct nvmem_cell *speedbin_nvmem;
|
||||
struct device_node *np;
|
||||
struct device *cpu_dev;
|
||||
unsigned cpu;
|
||||
- u32 versions;
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
@@ -126,11 +140,6 @@ static int qcom_cpufreq_probe(struct pla
|
||||
if (!cpu_dev)
|
||||
return -ENODEV;
|
||||
|
||||
- match = pdev->dev.platform_data;
|
||||
- get_version = match->data;
|
||||
- if (!get_version)
|
||||
- return -ENODEV;
|
||||
-
|
||||
np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
|
||||
if (!np)
|
||||
return -ENOENT;
|
||||
@@ -141,23 +150,43 @@ static int qcom_cpufreq_probe(struct pla
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
- speedbin_nvmem = of_nvmem_cell_get(np, NULL);
|
||||
- of_node_put(np);
|
||||
- if (IS_ERR(speedbin_nvmem)) {
|
||||
- if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
|
||||
- dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
|
||||
- PTR_ERR(speedbin_nvmem));
|
||||
- return PTR_ERR(speedbin_nvmem);
|
||||
+ drv = kzalloc(sizeof(*drv), GFP_KERNEL);
|
||||
+ if (!drv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ match = pdev->dev.platform_data;
|
||||
+ drv->data = match->data;
|
||||
+ if (!drv->data) {
|
||||
+ ret = -ENODEV;
|
||||
+ goto free_drv;
|
||||
}
|
||||
|
||||
- ret = get_version(cpu_dev, speedbin_nvmem, &versions);
|
||||
- nvmem_cell_put(speedbin_nvmem);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
+ if (drv->data->get_version) {
|
||||
+ speedbin_nvmem = of_nvmem_cell_get(np, NULL);
|
||||
+ if (IS_ERR(speedbin_nvmem)) {
|
||||
+ if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
|
||||
+ dev_err(cpu_dev,
|
||||
+ "Could not get nvmem cell: %ld\n",
|
||||
+ PTR_ERR(speedbin_nvmem));
|
||||
+ ret = PTR_ERR(speedbin_nvmem);
|
||||
+ goto free_drv;
|
||||
+ }
|
||||
|
||||
- opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL);
|
||||
- if (!opp_tables)
|
||||
- return -ENOMEM;
|
||||
+ ret = drv->data->get_version(cpu_dev, speedbin_nvmem, drv);
|
||||
+ if (ret) {
|
||||
+ nvmem_cell_put(speedbin_nvmem);
|
||||
+ goto free_drv;
|
||||
+ }
|
||||
+ nvmem_cell_put(speedbin_nvmem);
|
||||
+ }
|
||||
+ of_node_put(np);
|
||||
+
|
||||
+ drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!drv->opp_tables) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free_drv;
|
||||
+ }
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
@@ -166,19 +195,23 @@ static int qcom_cpufreq_probe(struct pla
|
||||
goto free_opp;
|
||||
}
|
||||
|
||||
- opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
|
||||
- &versions, 1);
|
||||
- if (IS_ERR(opp_tables[cpu])) {
|
||||
- ret = PTR_ERR(opp_tables[cpu]);
|
||||
- dev_err(cpu_dev, "Failed to set supported hardware\n");
|
||||
- goto free_opp;
|
||||
+ if (drv->data->get_version) {
|
||||
+ drv->opp_tables[cpu] =
|
||||
+ dev_pm_opp_set_supported_hw(cpu_dev,
|
||||
+ &drv->versions, 1);
|
||||
+ if (IS_ERR(drv->opp_tables[cpu])) {
|
||||
+ ret = PTR_ERR(drv->opp_tables[cpu]);
|
||||
+ dev_err(cpu_dev,
|
||||
+ "Failed to set supported hardware\n");
|
||||
+ goto free_opp;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
|
||||
NULL, 0);
|
||||
if (!IS_ERR(cpufreq_dt_pdev)) {
|
||||
- platform_set_drvdata(pdev, opp_tables);
|
||||
+ platform_set_drvdata(pdev, drv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -187,26 +220,30 @@ static int qcom_cpufreq_probe(struct pla
|
||||
|
||||
free_opp:
|
||||
for_each_possible_cpu(cpu) {
|
||||
- if (IS_ERR_OR_NULL(opp_tables[cpu]))
|
||||
+ if (IS_ERR_OR_NULL(drv->opp_tables[cpu]))
|
||||
break;
|
||||
- dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
+ dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);
|
||||
}
|
||||
- kfree(opp_tables);
|
||||
+ kfree(drv->opp_tables);
|
||||
+free_drv:
|
||||
+ kfree(drv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
- struct opp_table **opp_tables = platform_get_drvdata(pdev);
|
||||
+ struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev);
|
||||
unsigned int cpu;
|
||||
|
||||
platform_device_unregister(cpufreq_dt_pdev);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
- dev_pm_opp_put_supported_hw(opp_tables[cpu]);
|
||||
+ if (drv->opp_tables[cpu])
|
||||
+ dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]);
|
||||
|
||||
- kfree(opp_tables);
|
||||
+ kfree(drv->opp_tables);
|
||||
+ kfree(drv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -220,10 +257,8 @@ static struct platform_driver qcom_cpufr
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
|
||||
- { .compatible = "qcom,apq8096",
|
||||
- .data = qcom_cpufreq_kryo_name_version },
|
||||
- { .compatible = "qcom,msm8996",
|
||||
- .data = qcom_cpufreq_kryo_name_version },
|
||||
+ { .compatible = "qcom,apq8096", .data = &match_data_kryo },
|
||||
+ { .compatible = "qcom,msm8996", .data = &match_data_kryo },
|
||||
{},
|
||||
};
|
||||
|
@ -1,16 +0,0 @@
|
||||
Check for SCM availability before attempting to use SPM
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/drivers/soc/qcom/spm.c
|
||||
+++ b/drivers/soc/qcom/spm.c
|
||||
@@ -214,6 +214,9 @@ static int __init qcom_cpuidle_init(stru
|
||||
if (!qcom_scm_is_available())
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
+ if (!qcom_scm_is_available())
|
||||
+ return -EPROBE_DEFER;
|
||||
+
|
||||
for (i = 0; ; i++) {
|
||||
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
|
||||
if (!state_node)
|
Loading…
Reference in New Issue
Block a user