summaryrefslogtreecommitdiffstats
path: root/drivers/clk/tegra
diff options
context:
space:
mode:
authorMiguel Ojeda <ojeda@kernel.org>2026-08-16 15:32:33 +0200
committerMiguel Ojeda <ojeda@kernel.org>2026-08-23 21:27:17 +0200
commit5febf432df1cfa5b25d99b54c32103fafbdd0eb9 (patch)
tree93a1ed1d337bc53adf1e572c2586d347703b286e /drivers/clk/tegra
downloadlinux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.tar.gz
linux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.zip
kbuild: rust: keep Rust objects out of Clang LTO with inline helpersgrafted
Under `CONFIG_LTO_CLANG` + `CONFIG_RUST_INLINE_HELPERS`, one may hit `objtool` errors such as: vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a: can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfd The reason is that in such builds, the Clang invocation that compiles the combined Rust plus helpers bitcode emits LLVM bitcode (again) -- the final code generation happens in the linker's LTO step, which the `-mllvm` trap options passed to Clang do not reach. This, in turn, means that unreachable traps are missing, and the impossible paths do not merely fallthrough to the next symbol, but past the end of their own section, since LTO builds place each function in its own section. Thus filter `CC_FLAGS_LTO` out of the Clang invocation, so that it always emits machine code directly, with the traps in place. Assisted-by: LLM Cc: Gary Guo <gary@garyguo.net> Cc: Boqun Feng <boqun@kernel.org> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Matthew Maurer <mmaurer@google.com> Cc: Josh Poimboeuf <jpoimboe@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: stable@vger.kernel.org Fixes: 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust") Acked-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260816133233.197500-2-ojeda@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Diffstat (limited to 'drivers/clk/tegra')
-rw-r--r--drivers/clk/tegra/Kconfig12
-rw-r--r--drivers/clk/tegra/Makefile32
-rw-r--r--drivers/clk/tegra/clk-audio-sync.c75
-rw-r--r--drivers/clk/tegra/clk-bpmp.c705
-rw-r--r--drivers/clk/tegra/clk-device.c209
-rw-r--r--drivers/clk/tegra/clk-dfll.c2108
-rw-r--r--drivers/clk/tegra/clk-dfll.h48
-rw-r--r--drivers/clk/tegra/clk-divider.c194
-rw-r--r--drivers/clk/tegra/clk-id.h325
-rw-r--r--drivers/clk/tegra/clk-periph-fixed.c110
-rw-r--r--drivers/clk/tegra/clk-periph-gate.c176
-rw-r--r--drivers/clk/tegra/clk-periph.c235
-rw-r--r--drivers/clk/tegra/clk-pll-out.c120
-rw-r--r--drivers/clk/tegra/clk-pll.c2740
-rw-r--r--drivers/clk/tegra/clk-sdmmc-mux.c278
-rw-r--r--drivers/clk/tegra/clk-super.c275
-rw-r--r--drivers/clk/tegra/clk-tegra-audio.c248
-rw-r--r--drivers/clk/tegra/clk-tegra-fixed.c118
-rw-r--r--drivers/clk/tegra/clk-tegra-periph.c1030
-rw-r--r--drivers/clk/tegra/clk-tegra-super-cclk.c233
-rw-r--r--drivers/clk/tegra/clk-tegra-super-gen4.c269
-rw-r--r--drivers/clk/tegra/clk-tegra114.c1381
-rw-r--r--drivers/clk/tegra/clk-tegra124-dfll-fcpu.c749
-rw-r--r--drivers/clk/tegra/clk-tegra124-emc.c576
-rw-r--r--drivers/clk/tegra/clk-tegra124.c1597
-rw-r--r--drivers/clk/tegra/clk-tegra20-emc.c296
-rw-r--r--drivers/clk/tegra/clk-tegra20.c1206
-rw-r--r--drivers/clk/tegra/clk-tegra210-emc.c379
-rw-r--r--drivers/clk/tegra/clk-tegra210.c3821
-rw-r--r--drivers/clk/tegra/clk-tegra30.c1438
-rw-r--r--drivers/clk/tegra/clk-utils.c43
-rw-r--r--drivers/clk/tegra/clk.c451
-rw-r--r--drivers/clk/tegra/clk.h929
-rw-r--r--drivers/clk/tegra/cvb.c142
-rw-r--r--drivers/clk/tegra/cvb.h60
35 files changed, 22608 insertions, 0 deletions
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
new file mode 100644
index 000000000..62147a069
--- /dev/null
+++ b/drivers/clk/tegra/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CLK_TEGRA_BPMP
+ def_bool y
+ depends on TEGRA_BPMP
+
+config TEGRA_CLK_DFLL
+ depends on ARCH_TEGRA_114_SOC || ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC
+ select PM_OPP
+ def_bool y
+
+config TEGRA124_CLK_EMC
+ bool
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
new file mode 100644
index 000000000..a0715cdfc
--- /dev/null
+++ b/drivers/clk/tegra/Makefile
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += clk.o
+obj-y += clk-audio-sync.o
+obj-y += clk-device.o
+obj-y += clk-dfll.o
+obj-y += clk-divider.o
+obj-y += clk-periph.o
+obj-y += clk-periph-fixed.o
+obj-y += clk-periph-gate.o
+obj-y += clk-pll.o
+obj-y += clk-pll-out.o
+obj-y += clk-sdmmc-mux.o
+obj-y += clk-super.o
+obj-y += clk-tegra-audio.o
+obj-y += clk-tegra-periph.o
+obj-y += clk-tegra-fixed.o
+obj-y += clk-tegra-super-cclk.o
+obj-y += clk-tegra-super-gen4.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
+obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
+obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
+obj-$(CONFIG_TEGRA124_CLK_EMC) += clk-tegra124-emc.o
+obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o
+obj-y += cvb.o
+obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o
+obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210-emc.o
+obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o
+obj-y += clk-utils.o
diff --git a/drivers/clk/tegra/clk-audio-sync.c b/drivers/clk/tegra/clk-audio-sync.c
new file mode 100644
index 000000000..0bbfcf46b
--- /dev/null
+++ b/drivers/clk/tegra/clk-audio-sync.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "clk.h"
+
+static unsigned long clk_sync_source_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ return sync->rate;
+}
+
+static int clk_sync_source_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ if (req->rate > sync->max_rate)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+static int clk_sync_source_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_sync_source *sync = to_clk_sync_source(hw);
+
+ sync->rate = rate;
+ return 0;
+}
+
+const struct clk_ops tegra_clk_sync_source_ops = {
+ .determine_rate = clk_sync_source_determine_rate,
+ .set_rate = clk_sync_source_set_rate,
+ .recalc_rate = clk_sync_source_recalc_rate,
+};
+
+struct clk *tegra_clk_register_sync_source(const char *name,
+ unsigned long max_rate)
+{
+ struct tegra_clk_sync_source *sync;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ sync = kzalloc_obj(*sync);
+ if (!sync) {
+ pr_err("%s: could not allocate sync source clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sync->max_rate = max_rate;
+
+ init.ops = &tegra_clk_sync_source_ops;
+ init.name = name;
+ init.flags = 0;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ sync->hw.init = &init;
+
+ clk = clk_register(NULL, &sync->hw);
+ if (IS_ERR(clk))
+ kfree(sync);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
new file mode 100644
index 000000000..f6d2b9342
--- /dev/null
+++ b/drivers/clk/tegra/clk-bpmp.c
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2022 NVIDIA Corporation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/seq_buf.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+#define TEGRA_BPMP_DUMP_CLOCK_INFO 0
+
+#define TEGRA_BPMP_CLK_HAS_MUX BIT(0)
+#define TEGRA_BPMP_CLK_HAS_SET_RATE BIT(1)
+#define TEGRA_BPMP_CLK_IS_ROOT BIT(2)
+
+struct tegra_bpmp_clk_info {
+ unsigned int id;
+ char name[MRQ_CLK_NAME_MAXLEN];
+ unsigned int parents[MRQ_CLK_MAX_PARENTS];
+ unsigned int num_parents;
+ unsigned long flags;
+};
+
+struct tegra_bpmp_clk {
+ struct clk_hw hw;
+
+ struct tegra_bpmp *bpmp;
+ unsigned int id;
+
+ unsigned int num_parents;
+ unsigned int *parents;
+};
+
+static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct tegra_bpmp_clk, hw);
+}
+
+struct tegra_bpmp_clk_message {
+ unsigned int cmd;
+ unsigned int id;
+
+ struct {
+ const void *data;
+ size_t size;
+ } tx;
+
+ struct {
+ void *data;
+ size_t size;
+ int ret;
+ } rx;
+};
+
+static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
+ const struct tegra_bpmp_clk_message *clk)
+{
+ struct mrq_clk_request request;
+ struct tegra_bpmp_message msg;
+ void *req = &request;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd_and_id = (clk->cmd << 24) | clk->id;
+
+ /*
+ * The mrq_clk_request structure has an anonymous union at offset 4
+ * that contains all possible sub-command structures. Copy the data
+ * to that union. Ideally we'd be able to refer to it by name, but
+ * doing so would require changing the ABI header and increase the
+ * maintenance burden.
+ */
+ memcpy(req + 4, clk->tx.data, clk->tx.size);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_CLK;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = clk->rx.data;
+ msg.rx.size = clk->rx.size;
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err < 0)
+ return err;
+ else if (msg.rx.ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_bpmp_clk_prepare(struct clk_hw *hw)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct tegra_bpmp_clk_message msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_ENABLE;
+ msg.id = clk->id;
+
+ return tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+}
+
+static void tegra_bpmp_clk_unprepare(struct clk_hw *hw)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct tegra_bpmp_clk_message msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_DISABLE;
+ msg.id = clk->id;
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0)
+ dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",
+ clk_hw_get_name(hw), err);
+}
+
+static int tegra_bpmp_clk_is_prepared(struct clk_hw *hw)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_is_enabled_response response;
+ struct tegra_bpmp_clk_message msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_IS_ENABLED;
+ msg.id = clk->id;
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ return response.state;
+}
+
+static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_get_rate_response response;
+ struct cmd_clk_get_rate_request request;
+ struct tegra_bpmp_clk_message msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_GET_RATE;
+ msg.id = clk->id;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0)
+ return 0;
+
+ return response.rate;
+}
+
+static int tegra_bpmp_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *rate_req)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_round_rate_response response;
+ struct cmd_clk_round_rate_request request;
+ struct tegra_bpmp_clk_message msg;
+ unsigned long rate;
+ int err;
+
+ rate = clamp(rate_req->rate, rate_req->min_rate, rate_req->max_rate);
+
+ memset(&request, 0, sizeof(request));
+ request.rate = min_t(u64, rate, S64_MAX);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_ROUND_RATE;
+ msg.id = clk->id;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ rate_req->rate = (unsigned long)response.rate;
+
+ return 0;
+}
+
+static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_set_parent_response response;
+ struct cmd_clk_set_parent_request request;
+ struct tegra_bpmp_clk_message msg;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.parent_id = clk->parents[index];
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_SET_PARENT;
+ msg.id = clk->id;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ /* XXX check parent ID in response */
+
+ return 0;
+}
+
+static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_get_parent_response response;
+ struct tegra_bpmp_clk_message msg;
+ unsigned int i;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_GET_PARENT;
+ msg.id = clk->id;
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+ if (err < 0) {
+ dev_err(clk->bpmp->dev, "failed to get parent for %s: %d\n",
+ clk_hw_get_name(hw), err);
+ return U8_MAX;
+ }
+
+ for (i = 0; i < clk->num_parents; i++)
+ if (clk->parents[i] == response.parent_id)
+ return i;
+
+ return U8_MAX;
+}
+
+static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+ struct cmd_clk_set_rate_response response;
+ struct cmd_clk_set_rate_request request;
+ struct tegra_bpmp_clk_message msg;
+
+ memset(&request, 0, sizeof(request));
+ request.rate = min_t(u64, rate, S64_MAX);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_SET_RATE;
+ msg.id = clk->id;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ return tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+}
+
+static const struct clk_ops tegra_bpmp_clk_gate_ops = {
+ .prepare = tegra_bpmp_clk_prepare,
+ .unprepare = tegra_bpmp_clk_unprepare,
+ .is_prepared = tegra_bpmp_clk_is_prepared,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_ops = {
+ .prepare = tegra_bpmp_clk_prepare,
+ .unprepare = tegra_bpmp_clk_unprepare,
+ .is_prepared = tegra_bpmp_clk_is_prepared,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .set_parent = tegra_bpmp_clk_set_parent,
+ .get_parent = tegra_bpmp_clk_get_parent,
+};
+
+static const struct clk_ops tegra_bpmp_clk_rate_ops = {
+ .prepare = tegra_bpmp_clk_prepare,
+ .unprepare = tegra_bpmp_clk_unprepare,
+ .is_prepared = tegra_bpmp_clk_is_prepared,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+ .determine_rate = tegra_bpmp_clk_determine_rate,
+ .set_rate = tegra_bpmp_clk_set_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
+ .prepare = tegra_bpmp_clk_prepare,
+ .unprepare = tegra_bpmp_clk_unprepare,
+ .is_prepared = tegra_bpmp_clk_is_prepared,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+ .determine_rate = tegra_bpmp_clk_determine_rate,
+ .set_parent = tegra_bpmp_clk_set_parent,
+ .get_parent = tegra_bpmp_clk_get_parent,
+ .set_rate = tegra_bpmp_clk_set_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_read_only_ops = {
+ .get_parent = tegra_bpmp_clk_get_parent,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_read_only_ops = {
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_gate_mux_read_only_ops = {
+ .prepare = tegra_bpmp_clk_prepare,
+ .unprepare = tegra_bpmp_clk_unprepare,
+ .is_prepared = tegra_bpmp_clk_is_prepared,
+ .recalc_rate = tegra_bpmp_clk_recalc_rate,
+ .get_parent = tegra_bpmp_clk_get_parent,
+};
+
+static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
+{
+ struct cmd_clk_get_max_clk_id_response response;
+ struct tegra_bpmp_clk_message msg;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ if (response.max_id > INT_MAX)
+ return -E2BIG;
+
+ return response.max_id;
+}
+
+static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
+ struct tegra_bpmp_clk_info *info)
+{
+ struct cmd_clk_get_all_info_response response;
+ struct tegra_bpmp_clk_message msg;
+ unsigned int i;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = CMD_CLK_GET_ALL_INFO;
+ msg.id = id;
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_clk_transfer(bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
+ info->num_parents = response.num_parents;
+
+ for (i = 0; i < info->num_parents; i++)
+ info->parents[i] = response.parents[i];
+
+ info->flags = response.flags;
+
+ return 0;
+}
+
+static void tegra_bpmp_clk_info_dump(struct tegra_bpmp *bpmp,
+ const char *level,
+ const struct tegra_bpmp_clk_info *info)
+{
+ const char *prefix = "";
+ struct seq_buf buf;
+ unsigned int i;
+ char flags[64];
+
+ seq_buf_init(&buf, flags, sizeof(flags));
+
+ if (info->flags)
+ seq_buf_printf(&buf, "(");
+
+ if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+ seq_buf_printf(&buf, "%smux", prefix);
+ prefix = ", ";
+ }
+
+ if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
+ seq_buf_printf(&buf, "%sfixed", prefix);
+ prefix = ", ";
+ }
+
+ if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
+ seq_buf_printf(&buf, "%sroot", prefix);
+ prefix = ", ";
+ }
+
+ if (info->flags)
+ seq_buf_printf(&buf, ")");
+
+ dev_printk(level, bpmp->dev, "%03u: %s\n", info->id, info->name);
+ dev_printk(level, bpmp->dev, " flags: %lx %s\n", info->flags, flags);
+ dev_printk(level, bpmp->dev, " parents: %u\n", info->num_parents);
+
+ for (i = 0; i < info->num_parents; i++)
+ dev_printk(level, bpmp->dev, " %03u\n", info->parents[i]);
+}
+
+static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_clk_info **clocksp)
+{
+ struct tegra_bpmp_clk_info *clocks;
+ unsigned int max_id, id, count = 0;
+ unsigned int holes = 0;
+ int err;
+
+ err = tegra_bpmp_clk_get_max_id(bpmp);
+ if (err < 0)
+ return err;
+
+ max_id = err;
+
+ dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
+
+ clocks = kzalloc_objs(*clocks, max_id + 1);
+ if (!clocks)
+ return -ENOMEM;
+
+ for (id = 0; id <= max_id; id++) {
+ struct tegra_bpmp_clk_info *info = &clocks[count];
+
+ err = tegra_bpmp_clk_get_info(bpmp, id, info);
+ if (err < 0)
+ continue;
+
+ if (info->num_parents >= U8_MAX) {
+ dev_err(bpmp->dev,
+ "clock %u has too many parents (%u, max: %u)\n",
+ id, info->num_parents, U8_MAX);
+ continue;
+ }
+
+ /* clock not exposed by BPMP */
+ if (info->name[0] == '\0') {
+ holes++;
+ continue;
+ }
+
+ info->id = id;
+ count++;
+
+ if (TEGRA_BPMP_DUMP_CLOCK_INFO)
+ tegra_bpmp_clk_info_dump(bpmp, KERN_DEBUG, info);
+ }
+
+ dev_dbg(bpmp->dev, "holes: %u\n", holes);
+ *clocksp = clocks;
+
+ return count;
+}
+
+static unsigned int
+tegra_bpmp_clk_id_to_index(const struct tegra_bpmp_clk_info *clocks,
+ unsigned int num_clocks, unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_clocks; i++)
+ if (clocks[i].id == id)
+ return i;
+
+ return UINT_MAX;
+}
+
+static const struct tegra_bpmp_clk_info *
+tegra_bpmp_clk_find(const struct tegra_bpmp_clk_info *clocks,
+ unsigned int num_clocks, unsigned int id)
+{
+ unsigned int i;
+
+ i = tegra_bpmp_clk_id_to_index(clocks, num_clocks, id);
+
+ if (i < num_clocks)
+ return &clocks[i];
+
+ return NULL;
+}
+
+static struct tegra_bpmp_clk *
+tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
+ const struct tegra_bpmp_clk_info *info,
+ const struct tegra_bpmp_clk_info *clocks,
+ unsigned int num_clocks)
+{
+ struct tegra_bpmp_clk *clk;
+ struct clk_init_data init;
+ const char **parents;
+ unsigned int i;
+ int err;
+
+ clk = devm_kzalloc(bpmp->dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ clk->id = info->id;
+ clk->bpmp = bpmp;
+
+ clk->parents = devm_kcalloc(bpmp->dev, info->num_parents,
+ sizeof(*clk->parents), GFP_KERNEL);
+ if (!clk->parents)
+ return ERR_PTR(-ENOMEM);
+
+ clk->num_parents = info->num_parents;
+
+ /* hardware clock initialization */
+ memset(&init, 0, sizeof(init));
+ init.name = info->name;
+ clk->hw.init = &init;
+ if (info->flags & BPMP_CLK_STATE_CHANGE_DENIED) {
+ if ((info->flags & BPMP_CLK_RATE_PARENT_CHANGE_DENIED) == 0) {
+ dev_WARN(bpmp->dev,
+ "Firmware bug! Inconsistent permission bits for clock %s. State and parent/rate changes disabled.",
+ init.name);
+ }
+ if (info->flags & TEGRA_BPMP_CLK_HAS_MUX)
+ init.ops = &tegra_bpmp_clk_mux_read_only_ops;
+ else
+ init.ops = &tegra_bpmp_clk_read_only_ops;
+ } else if (info->flags & BPMP_CLK_RATE_PARENT_CHANGE_DENIED) {
+ if (info->flags & TEGRA_BPMP_CLK_HAS_MUX)
+ init.ops = &tegra_bpmp_clk_gate_mux_read_only_ops;
+ else
+ init.ops = &tegra_bpmp_clk_gate_ops;
+ } else if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+ if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+ init.ops = &tegra_bpmp_clk_mux_rate_ops;
+ else
+ init.ops = &tegra_bpmp_clk_mux_ops;
+ } else {
+ if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+ init.ops = &tegra_bpmp_clk_rate_ops;
+ else
+ init.ops = &tegra_bpmp_clk_gate_ops;
+ }
+
+ init.num_parents = info->num_parents;
+
+ parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
+ if (!parents)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < info->num_parents; i++) {
+ const struct tegra_bpmp_clk_info *parent;
+
+ /* keep a private copy of the ID to parent index map */
+ clk->parents[i] = info->parents[i];
+
+ parent = tegra_bpmp_clk_find(clocks, num_clocks,
+ info->parents[i]);
+ if (!parent) {
+ dev_err(bpmp->dev, "no parent %u found for %u\n",
+ info->parents[i], info->id);
+ continue;
+ }
+
+ parents[i] = parent->name;
+ }
+
+ init.parent_names = parents;
+
+ err = devm_clk_hw_register(bpmp->dev, &clk->hw);
+
+ kfree(parents);
+
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return clk;
+}
+
+static void tegra_bpmp_register_clocks_one(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_clk_info *infos,
+ unsigned int i,
+ unsigned int count)
+{
+ unsigned int j;
+ struct tegra_bpmp_clk_info *info;
+ struct tegra_bpmp_clk *clk;
+
+ if (bpmp->clocks[i]) {
+ /* already registered */
+ return;
+ }
+
+ info = &infos[i];
+ for (j = 0; j < info->num_parents; ++j) {
+ unsigned int p_id = info->parents[j];
+ unsigned int p_i = tegra_bpmp_clk_id_to_index(infos, count,
+ p_id);
+ if (p_i < count)
+ tegra_bpmp_register_clocks_one(bpmp, infos, p_i, count);
+ }
+
+ clk = tegra_bpmp_clk_register(bpmp, info, infos, count);
+ if (IS_ERR(clk)) {
+ dev_err(bpmp->dev,
+ "failed to register clock %u (%s): %ld\n",
+ info->id, info->name, PTR_ERR(clk));
+ /* intentionally store the error pointer to
+ * bpmp->clocks[i] to avoid re-attempting the
+ * registration later
+ */
+ }
+
+ bpmp->clocks[i] = clk;
+}
+
+static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_clk_info *infos,
+ unsigned int count)
+{
+ unsigned int i;
+
+ bpmp->num_clocks = count;
+
+ bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(*bpmp->clocks), GFP_KERNEL);
+ if (!bpmp->clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ tegra_bpmp_register_clocks_one(bpmp, infos, i, count);
+ }
+
+ return 0;
+}
+
+static void tegra_bpmp_unregister_clocks(struct tegra_bpmp *bpmp)
+{
+ unsigned int i;
+
+ for (i = 0; i < bpmp->num_clocks; i++)
+ clk_hw_unregister(&bpmp->clocks[i]->hw);
+}
+
+static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
+ void *data)
+{
+ unsigned int id = clkspec->args[0], i;
+ struct tegra_bpmp *bpmp = data;
+
+ for (i = 0; i < bpmp->num_clocks; i++) {
+ struct tegra_bpmp_clk *clk = bpmp->clocks[i];
+
+ if (!clk)
+ continue;
+
+ if (clk->id == id)
+ return &clk->hw;
+ }
+
+ return NULL;
+}
+
+int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
+{
+ struct tegra_bpmp_clk_info *clocks;
+ unsigned int count;
+ int err;
+
+ err = tegra_bpmp_probe_clocks(bpmp, &clocks);
+ if (err < 0)
+ return err;
+
+ count = err;
+
+ dev_dbg(bpmp->dev, "%u clocks probed\n", count);
+
+ err = tegra_bpmp_register_clocks(bpmp, clocks, count);
+ if (err < 0)
+ goto free;
+
+ err = of_clk_add_hw_provider(bpmp->dev->of_node,
+ tegra_bpmp_clk_of_xlate,
+ bpmp);
+ if (err < 0) {
+ tegra_bpmp_unregister_clocks(bpmp);
+ goto free;
+ }
+
+free:
+ kfree(clocks);
+ return err;
+}
diff --git a/drivers/clk/tegra/clk-device.c b/drivers/clk/tegra/clk-device.c
new file mode 100644
index 000000000..a75f71462
--- /dev/null
+++ b/drivers/clk/tegra/clk-device.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/common.h>
+
+#include "clk.h"
+
+/*
+ * This driver manages performance state of the core power domain for the
+ * independent PLLs and system clocks. We created a virtual clock device
+ * for such clocks, see tegra_clk_dev_register().
+ */
+
+struct tegra_clk_device {
+ struct notifier_block clk_nb;
+ struct device *dev;
+ struct clk_hw *hw;
+ struct mutex lock;
+};
+
+static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev,
+ unsigned long rate)
+{
+ struct device *dev = clk_dev->dev;
+ struct dev_pm_opp *opp;
+ unsigned int pstate;
+
+ opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+ if (opp == ERR_PTR(-ERANGE)) {
+ /*
+ * Some clocks may be unused by a particular board and they
+ * may have uninitiated clock rate that is overly high. In
+ * this case clock is expected to be disabled, but still we
+ * need to set up performance state of the power domain and
+ * not error out clk initialization. A typical example is
+ * a PCIe clock on Android tablets.
+ */
+ dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate);
+ opp = dev_pm_opp_find_freq_floor(dev, &rate);
+ }
+
+ if (IS_ERR(opp)) {
+ dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp);
+ return PTR_ERR(opp);
+ }
+
+ pstate = dev_pm_opp_get_required_pstate(opp, 0);
+ dev_pm_opp_put(opp);
+
+ return dev_pm_genpd_set_performance_state(dev, pstate);
+}
+
+static int tegra_clock_change_notify(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct tegra_clk_device *clk_dev;
+ int err = 0;
+
+ clk_dev = container_of(nb, struct tegra_clk_device, clk_nb);
+
+ mutex_lock(&clk_dev->lock);
+ switch (msg) {
+ case PRE_RATE_CHANGE:
+ if (cnd->new_rate > cnd->old_rate)
+ err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
+ break;
+
+ case ABORT_RATE_CHANGE:
+ err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate);
+ break;
+
+ case POST_RATE_CHANGE:
+ if (cnd->new_rate < cnd->old_rate)
+ err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(&clk_dev->lock);
+
+ return notifier_from_errno(err);
+}
+
+static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev)
+{
+ unsigned long rate;
+ int ret;
+
+ mutex_lock(&clk_dev->lock);
+
+ rate = clk_hw_get_rate(clk_dev->hw);
+ ret = tegra_clock_set_pd_state(clk_dev, rate);
+
+ mutex_unlock(&clk_dev->lock);
+
+ return ret;
+}
+
+static int tegra_clock_probe(struct platform_device *pdev)
+{
+ struct tegra_core_opp_params opp_params = {};
+ struct tegra_clk_device *clk_dev;
+ struct device *dev = &pdev->dev;
+ struct clk *clk;
+ int err;
+
+ if (!dev->pm_domain)
+ return -EINVAL;
+
+ clk_dev = devm_kzalloc(dev, sizeof(*clk_dev), GFP_KERNEL);
+ if (!clk_dev)
+ return -ENOMEM;
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ clk_dev->dev = dev;
+ clk_dev->hw = __clk_get_hw(clk);
+ clk_dev->clk_nb.notifier_call = tegra_clock_change_notify;
+ mutex_init(&clk_dev->lock);
+
+ platform_set_drvdata(pdev, clk_dev);
+
+ /*
+ * Runtime PM was already enabled for this device by the parent clk
+ * driver and power domain state should be synced under clk_dev lock,
+ * hence we don't use the common OPP helper that initializes OPP
+ * state. For some clocks common OPP helper may fail to find ceil
+ * rate, it's handled by this driver.
+ */
+ err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
+ if (err)
+ return err;
+
+ err = clk_notifier_register(clk, &clk_dev->clk_nb);
+ if (err) {
+ dev_err(dev, "failed to register clk notifier: %d\n", err);
+ return err;
+ }
+
+ /*
+ * The driver is attaching to a potentially active/resumed clock, hence
+ * we need to sync the power domain performance state in a accordance to
+ * the clock rate if clock is resumed.
+ */
+ err = tegra_clock_sync_pd_state(clk_dev);
+ if (err)
+ goto unreg_clk;
+
+ return 0;
+
+unreg_clk:
+ clk_notifier_unregister(clk, &clk_dev->clk_nb);
+
+ return err;
+}
+
+/*
+ * Tegra GENPD driver enables clocks during NOIRQ phase. It can't be done
+ * for clocks served by this driver because runtime PM is unavailable in
+ * NOIRQ phase. We will keep clocks resumed during suspend to mitigate this
+ * problem. In practice this makes no difference from a power management
+ * perspective since voltage is kept at a nominal level during suspend anyways.
+ */
+static inline int tegra_clock_suspend(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_clock_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_clock_suspend, NULL)
+};
+
+static const struct of_device_id tegra_clock_match[] = {
+ { .compatible = "nvidia,tegra20-sclk" },
+ { .compatible = "nvidia,tegra30-sclk" },
+ { .compatible = "nvidia,tegra30-pllc" },
+ { .compatible = "nvidia,tegra30-plle" },
+ { .compatible = "nvidia,tegra30-pllm" },
+ { }
+};
+
+static struct platform_driver tegra_clock_driver = {
+ .driver = {
+ .name = "tegra-clock",
+ .of_match_table = tegra_clock_match,
+ .pm = &tegra_clock_pm,
+ .suppress_bind_attrs = true,
+ },
+ .probe = tegra_clock_probe,
+};
+builtin_platform_driver(tegra_clock_driver);
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
new file mode 100644
index 000000000..22dc29432
--- /dev/null
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -0,0 +1,2108 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * clk-dfll.c - Tegra DFLL clock source common code
+ *
+ * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved.
+ *
+ * Aleksandr Frid <afrid@nvidia.com>
+ * Paul Walmsley <pwalmsley@nvidia.com>
+ *
+ * This library is for the DVCO and DFLL IP blocks on the Tegra124
+ * SoC. These IP blocks together are also known at NVIDIA as
+ * "CL-DVFS". To try to avoid confusion, this code refers to them
+ * collectively as the "DFLL."
+ *
+ * The DFLL is a root clocksource which tolerates some amount of
+ * supply voltage noise. Tegra124 uses it to clock the fast CPU
+ * complex when the target CPU speed is above a particular rate. The
+ * DFLL can be operated in either open-loop mode or closed-loop mode.
+ * In open-loop mode, the DFLL generates an output clock appropriate
+ * to the supply voltage. In closed-loop mode, when configured with a
+ * target frequency, the DFLL minimizes supply voltage while
+ * delivering an average frequency equal to the target.
+ *
+ * Devices clocked by the DFLL must be able to tolerate frequency
+ * variation. In the case of the CPU, it's important to note that the
+ * CPU cycle time will vary. This has implications for
+ * performance-measurement code and any code that relies on the CPU
+ * cycle time to delay for a certain length of time.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/seq_file.h>
+
+#include "clk-dfll.h"
+#include "cvb.h"
+
+/*
+ * DFLL control registers - access via dfll_{readl,writel}
+ */
+
+/* DFLL_CTRL: DFLL control register */
+#define DFLL_CTRL 0x00
+#define DFLL_CTRL_MODE_MASK 0x03
+
+/* DFLL_CONFIG: DFLL sample rate control */
+#define DFLL_CONFIG 0x04
+#define DFLL_CONFIG_DIV_MASK 0xff
+#define DFLL_CONFIG_DIV_PRESCALE 32
+
+/* DFLL_PARAMS: tuning coefficients for closed loop integrator */
+#define DFLL_PARAMS 0x08
+#define DFLL_PARAMS_CG_SCALE (0x1 << 24)
+#define DFLL_PARAMS_FORCE_MODE_SHIFT 22
+#define DFLL_PARAMS_FORCE_MODE_MASK (0x3 << DFLL_PARAMS_FORCE_MODE_SHIFT)
+#define DFLL_PARAMS_CF_PARAM_SHIFT 16
+#define DFLL_PARAMS_CF_PARAM_MASK (0x3f << DFLL_PARAMS_CF_PARAM_SHIFT)
+#define DFLL_PARAMS_CI_PARAM_SHIFT 8
+#define DFLL_PARAMS_CI_PARAM_MASK (0x7 << DFLL_PARAMS_CI_PARAM_SHIFT)
+#define DFLL_PARAMS_CG_PARAM_SHIFT 0
+#define DFLL_PARAMS_CG_PARAM_MASK (0xff << DFLL_PARAMS_CG_PARAM_SHIFT)
+
+/* DFLL_TUNE0: delay line configuration register 0 */
+#define DFLL_TUNE0 0x0c
+
+/* DFLL_TUNE1: delay line configuration register 1 */
+#define DFLL_TUNE1 0x10
+
+/* DFLL_FREQ_REQ: target DFLL frequency control */
+#define DFLL_FREQ_REQ 0x14
+#define DFLL_FREQ_REQ_FORCE_ENABLE (0x1 << 28)
+#define DFLL_FREQ_REQ_FORCE_SHIFT 16
+#define DFLL_FREQ_REQ_FORCE_MASK (0xfff << DFLL_FREQ_REQ_FORCE_SHIFT)
+#define FORCE_MAX 2047
+#define FORCE_MIN -2048
+#define DFLL_FREQ_REQ_SCALE_SHIFT 8
+#define DFLL_FREQ_REQ_SCALE_MASK (0xff << DFLL_FREQ_REQ_SCALE_SHIFT)
+#define DFLL_FREQ_REQ_SCALE_MAX 256
+#define DFLL_FREQ_REQ_FREQ_VALID (0x1 << 7)
+#define DFLL_FREQ_REQ_MULT_SHIFT 0
+#define DFLL_FREQ_REG_MULT_MASK (0x7f << DFLL_FREQ_REQ_MULT_SHIFT)
+#define FREQ_MAX 127
+
+/* DFLL_DROOP_CTRL: droop prevention control */
+#define DFLL_DROOP_CTRL 0x1c
+
+/* DFLL_OUTPUT_CFG: closed loop mode control registers */
+/* NOTE: access via dfll_i2c_{readl,writel} */
+#define DFLL_OUTPUT_CFG 0x20
+#define DFLL_OUTPUT_CFG_I2C_ENABLE (0x1 << 30)
+#define OUT_MASK 0x3f
+#define DFLL_OUTPUT_CFG_SAFE_SHIFT 24
+#define DFLL_OUTPUT_CFG_SAFE_MASK \
+ (OUT_MASK << DFLL_OUTPUT_CFG_SAFE_SHIFT)
+#define DFLL_OUTPUT_CFG_MAX_SHIFT 16
+#define DFLL_OUTPUT_CFG_MAX_MASK \
+ (OUT_MASK << DFLL_OUTPUT_CFG_MAX_SHIFT)
+#define DFLL_OUTPUT_CFG_MIN_SHIFT 8
+#define DFLL_OUTPUT_CFG_MIN_MASK \
+ (OUT_MASK << DFLL_OUTPUT_CFG_MIN_SHIFT)
+#define DFLL_OUTPUT_CFG_PWM_DELTA (0x1 << 7)
+#define DFLL_OUTPUT_CFG_PWM_ENABLE (0x1 << 6)
+#define DFLL_OUTPUT_CFG_PWM_DIV_SHIFT 0
+#define DFLL_OUTPUT_CFG_PWM_DIV_MASK \
+ (OUT_MASK << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+
+/* DFLL_OUTPUT_FORCE: closed loop mode voltage forcing control */
+#define DFLL_OUTPUT_FORCE 0x24
+#define DFLL_OUTPUT_FORCE_ENABLE (0x1 << 6)
+#define DFLL_OUTPUT_FORCE_VALUE_SHIFT 0
+#define DFLL_OUTPUT_FORCE_VALUE_MASK \
+ (OUT_MASK << DFLL_OUTPUT_FORCE_VALUE_SHIFT)
+
+/* DFLL_MONITOR_CTRL: internal monitor data source control */
+#define DFLL_MONITOR_CTRL 0x28
+#define DFLL_MONITOR_CTRL_FREQ 6
+
+/* DFLL_MONITOR_DATA: internal monitor data output */
+#define DFLL_MONITOR_DATA 0x2c
+#define DFLL_MONITOR_DATA_NEW_MASK (0x1 << 16)
+#define DFLL_MONITOR_DATA_VAL_SHIFT 0
+#define DFLL_MONITOR_DATA_VAL_MASK (0xFFFF << DFLL_MONITOR_DATA_VAL_SHIFT)
+
+/*
+ * I2C output control registers - access via dfll_i2c_{readl,writel}
+ */
+
+/* DFLL_I2C_CFG: I2C controller configuration register */
+#define DFLL_I2C_CFG 0x40
+#define DFLL_I2C_CFG_ARB_ENABLE (0x1 << 20)
+#define DFLL_I2C_CFG_HS_CODE_SHIFT 16
+#define DFLL_I2C_CFG_HS_CODE_MASK (0x7 << DFLL_I2C_CFG_HS_CODE_SHIFT)
+#define DFLL_I2C_CFG_PACKET_ENABLE (0x1 << 15)
+#define DFLL_I2C_CFG_SIZE_SHIFT 12
+#define DFLL_I2C_CFG_SIZE_MASK (0x7 << DFLL_I2C_CFG_SIZE_SHIFT)
+#define DFLL_I2C_CFG_SLAVE_ADDR_10 (0x1 << 10)
+#define DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_7BIT 1
+#define DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_10BIT 0
+
+/* DFLL_I2C_VDD_REG_ADDR: PMIC I2C address for closed loop mode */
+#define DFLL_I2C_VDD_REG_ADDR 0x44
+
+/* DFLL_I2C_STS: I2C controller status */
+#define DFLL_I2C_STS 0x48
+#define DFLL_I2C_STS_I2C_LAST_SHIFT 1
+#define DFLL_I2C_STS_I2C_REQ_PENDING 0x1
+
+/* DFLL_INTR_STS: DFLL interrupt status register */
+#define DFLL_INTR_STS 0x5c
+
+/* DFLL_INTR_EN: DFLL interrupt enable register */
+#define DFLL_INTR_EN 0x60
+#define DFLL_INTR_MIN_MASK 0x1
+#define DFLL_INTR_MAX_MASK 0x2
+
+/*
+ * Integrated I2C controller registers - relative to td->i2c_controller_base
+ */
+
+/* DFLL_I2C_CLK_DIVISOR: I2C controller clock divisor */
+#define DFLL_I2C_CLK_DIVISOR 0x6c
+#define DFLL_I2C_CLK_DIVISOR_MASK 0xffff
+#define DFLL_I2C_CLK_DIVISOR_FS_SHIFT 16
+#define DFLL_I2C_CLK_DIVISOR_HS_SHIFT 0
+#define DFLL_I2C_CLK_DIVISOR_PREDIV 8
+#define DFLL_I2C_CLK_DIVISOR_HSMODE_PREDIV 12
+
+/*
+ * Other constants
+ */
+
+/* MAX_DFLL_VOLTAGES: number of LUT entries in the DFLL IP block */
+#define MAX_DFLL_VOLTAGES 33
+
+/*
+ * REF_CLK_CYC_PER_DVCO_SAMPLE: the number of ref_clk cycles that the hardware
+ * integrates the DVCO counter over - used for debug rate monitoring and
+ * droop control
+ */
+#define REF_CLK_CYC_PER_DVCO_SAMPLE 4
+
+/*
+ * REF_CLOCK_RATE: the DFLL reference clock rate currently supported by this
+ * driver, in Hz
+ */
+#define REF_CLOCK_RATE 51000000UL
+
+#define DVCO_RATE_TO_MULT(rate, ref_rate) ((rate) / ((ref_rate) / 2))
+#define MULT_TO_DVCO_RATE(mult, ref_rate) ((mult) * ((ref_rate) / 2))
+
+/**
+ * enum dfll_ctrl_mode - DFLL hardware operating mode
+ * @DFLL_UNINITIALIZED: (uninitialized state - not in hardware bitfield)
+ * @DFLL_DISABLED: DFLL not generating an output clock
+ * @DFLL_OPEN_LOOP: DVCO running, but DFLL not adjusting voltage
+ * @DFLL_CLOSED_LOOP: DVCO running, and DFLL adjusting voltage to match
+ * the requested rate
+ *
+ * The integer corresponding to the last two states, minus one, is
+ * written to the DFLL hardware to change operating modes.
+ */
+enum dfll_ctrl_mode {
+ DFLL_UNINITIALIZED = 0,
+ DFLL_DISABLED = 1,
+ DFLL_OPEN_LOOP = 2,
+ DFLL_CLOSED_LOOP = 3,
+};
+
+/**
+ * enum dfll_tune_range - voltage range that the driver believes it's in
+ * @DFLL_TUNE_UNINITIALIZED: DFLL tuning not yet programmed
+ * @DFLL_TUNE_LOW: DFLL in the low-voltage range (or open-loop mode)
+ *
+ * Some DFLL tuning parameters may need to change depending on the
+ * DVCO's voltage; these states represent the ranges that the driver
+ * supports. These are software states; these values are never
+ * written into registers.
+ */
+enum dfll_tune_range {
+ DFLL_TUNE_UNINITIALIZED = 0,
+ DFLL_TUNE_LOW = 1,
+};
+
+
+enum tegra_dfll_pmu_if {
+ TEGRA_DFLL_PMU_I2C = 0,
+ TEGRA_DFLL_PMU_PWM = 1,
+};
+
+/**
+ * struct dfll_rate_req - target DFLL rate request data
+ * @rate: target frequency, after the postscaling
+ * @dvco_target_rate: target frequency, after the postscaling
+ * @lut_index: LUT index at which voltage the dvco_target_rate will be reached
+ * @mult_bits: value to program to the MULT bits of the DFLL_FREQ_REQ register
+ * @scale_bits: value to program to the SCALE bits of the DFLL_FREQ_REQ register
+ */
+struct dfll_rate_req {
+ unsigned long rate;
+ unsigned long dvco_target_rate;
+ int lut_index;
+ u8 mult_bits;
+ u8 scale_bits;
+};
+
+struct tegra_dfll {
+ struct device *dev;
+ struct tegra_dfll_soc_data *soc;
+
+ void __iomem *base;
+ void __iomem *i2c_base;
+ void __iomem *i2c_controller_base;
+ void __iomem *lut_base;
+
+ struct regulator *vdd_reg;
+ struct clk *soc_clk;
+ struct clk *ref_clk;
+ struct clk *i2c_clk;
+ struct clk *dfll_clk;
+ struct reset_control *dfll_rst;
+ struct reset_control *dvco_rst;
+ unsigned long ref_rate;
+ unsigned long i2c_clk_rate;
+ unsigned long dvco_rate_min;
+
+ enum dfll_ctrl_mode mode;
+ enum dfll_tune_range tune_range;
+ struct dentry *debugfs_dir;
+ struct clk_hw dfll_clk_hw;
+ const char *output_clock_name;
+ struct dfll_rate_req last_req;
+ unsigned long last_unrounded_rate;
+
+ /* Parameters from DT */
+ u32 droop_ctrl;
+ u32 sample_rate;
+ u32 force_mode;
+ u32 cf;
+ u32 ci;
+ u32 cg;
+ bool cg_scale;
+
+ /* I2C interface parameters */
+ u32 i2c_fs_rate;
+ u32 i2c_reg;
+ u32 i2c_slave_addr;
+
+ /* lut array entries are regulator framework selectors or PWM values*/
+ unsigned lut[MAX_DFLL_VOLTAGES];
+ unsigned long lut_uv[MAX_DFLL_VOLTAGES];
+ int lut_size;
+ u8 lut_bottom, lut_min, lut_max, lut_safe;
+
+ /* PWM interface */
+ enum tegra_dfll_pmu_if pmu_if;
+ unsigned long pwm_rate;
+ struct pinctrl *pwm_pin;
+ struct pinctrl_state *pwm_enable_state;
+ struct pinctrl_state *pwm_disable_state;
+ u32 reg_init_uV;
+};
+
+#define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
+
+/* mode_name: map numeric DFLL modes to names for friendly console messages */
+static const char * const mode_name[] = {
+ [DFLL_UNINITIALIZED] = "uninitialized",
+ [DFLL_DISABLED] = "disabled",
+ [DFLL_OPEN_LOOP] = "open_loop",
+ [DFLL_CLOSED_LOOP] = "closed_loop",
+};
+
+/*
+ * Register accessors
+ */
+
+static inline u32 dfll_readl(struct tegra_dfll *td, u32 offs)
+{
+ return __raw_readl(td->base + offs);
+}
+
+static inline void dfll_writel(struct tegra_dfll *td, u32 val, u32 offs)
+{
+ WARN_ON(offs >= DFLL_I2C_CFG);
+ __raw_writel(val, td->base + offs);
+}
+
+static inline void dfll_wmb(struct tegra_dfll *td)
+{
+ dfll_readl(td, DFLL_CTRL);
+}
+
+/* I2C output control registers - for addresses above DFLL_I2C_CFG */
+
+static inline u32 dfll_i2c_readl(struct tegra_dfll *td, u32 offs)
+{
+ return __raw_readl(td->i2c_base + offs);
+}
+
+static inline void dfll_i2c_writel(struct tegra_dfll *td, u32 val, u32 offs)
+{
+ __raw_writel(val, td->i2c_base + offs);
+}
+
+static inline void dfll_i2c_wmb(struct tegra_dfll *td)
+{
+ dfll_i2c_readl(td, DFLL_I2C_CFG);
+}
+
+/**
+ * dfll_is_running - is the DFLL currently generating a clock?
+ * @td: DFLL instance
+ *
+ * If the DFLL is currently generating an output clock signal, return
+ * true; otherwise return false.
+ */
+static bool dfll_is_running(struct tegra_dfll *td)
+{
+ return td->mode >= DFLL_OPEN_LOOP;
+}
+
+/*
+ * Runtime PM suspend/resume callbacks
+ */
+
+/**
+ * tegra_dfll_runtime_resume - enable all clocks needed by the DFLL
+ * @dev: DFLL device *
+ *
+ * Enable all clocks needed by the DFLL. Assumes that clk_prepare()
+ * has already been called on all the clocks.
+ *
+ * XXX Should also handle context restore when returning from off.
+ */
+int tegra_dfll_runtime_resume(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(td->ref_clk);
+ if (ret) {
+ dev_err(dev, "could not enable ref clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(td->soc_clk);
+ if (ret) {
+ dev_err(dev, "could not enable register clock: %d\n", ret);
+ clk_disable(td->ref_clk);
+ return ret;
+ }
+
+ ret = clk_enable(td->i2c_clk);
+ if (ret) {
+ dev_err(dev, "could not enable i2c clock: %d\n", ret);
+ clk_disable(td->soc_clk);
+ clk_disable(td->ref_clk);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_runtime_resume);
+
+/**
+ * tegra_dfll_runtime_suspend - disable all clocks needed by the DFLL
+ * @dev: DFLL device *
+ *
+ * Disable all clocks needed by the DFLL. Assumes that other code
+ * will later call clk_unprepare().
+ */
+int tegra_dfll_runtime_suspend(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+
+ clk_disable(td->ref_clk);
+ clk_disable(td->soc_clk);
+ clk_disable(td->i2c_clk);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_runtime_suspend);
+
+/*
+ * DFLL tuning operations (per-voltage-range tuning settings)
+ */
+
+/**
+ * dfll_tune_low - tune to DFLL and CPU settings valid for any voltage
+ * @td: DFLL instance
+ *
+ * Tune the DFLL oscillator parameters and the CPU clock shaper for
+ * the low-voltage range. These settings are valid for any voltage,
+ * but may not be optimal.
+ */
+static void dfll_tune_low(struct tegra_dfll *td)
+{
+ td->tune_range = DFLL_TUNE_LOW;
+
+ dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune0_low, DFLL_TUNE0);
+ dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune1, DFLL_TUNE1);
+ dfll_wmb(td);
+
+ if (td->soc->set_clock_trimmers_low)
+ td->soc->set_clock_trimmers_low();
+}
+
+/*
+ * Output clock scaler helpers
+ */
+
+/**
+ * dfll_scale_dvco_rate - calculate scaled rate from the DVCO rate
+ * @scale_bits: clock scaler value (bits in the DFLL_FREQ_REQ_SCALE field)
+ * @dvco_rate: the DVCO rate
+ *
+ * Apply the same scaling formula that the DFLL hardware uses to scale
+ * the DVCO rate.
+ */
+static unsigned long dfll_scale_dvco_rate(int scale_bits,
+ unsigned long dvco_rate)
+{
+ return (u64)dvco_rate * (scale_bits + 1) / DFLL_FREQ_REQ_SCALE_MAX;
+}
+
+/*
+ * DFLL mode switching
+ */
+
+/**
+ * dfll_set_mode - change the DFLL control mode
+ * @td: DFLL instance
+ * @mode: DFLL control mode (see enum dfll_ctrl_mode)
+ *
+ * Change the DFLL's operating mode between disabled, open-loop mode,
+ * and closed-loop mode, or vice versa.
+ */
+static void dfll_set_mode(struct tegra_dfll *td,
+ enum dfll_ctrl_mode mode)
+{
+ td->mode = mode;
+ dfll_writel(td, mode - 1, DFLL_CTRL);
+ dfll_wmb(td);
+}
+
+/*
+ * DVCO rate control
+ */
+
+static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
+{
+ struct dev_pm_opp *opp;
+ unsigned long rate, prev_rate;
+ unsigned long uv, min_uv;
+
+ min_uv = td->lut_uv[out_min];
+ for (rate = 0, prev_rate = 0; ; rate++) {
+ opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
+ if (IS_ERR(opp))
+ break;
+
+ uv = dev_pm_opp_get_voltage(opp);
+ dev_pm_opp_put(opp);
+
+ if (uv && uv > min_uv)
+ return prev_rate;
+
+ prev_rate = rate;
+ }
+
+ return prev_rate;
+}
+
+/*
+ * DFLL-to-I2C controller interface
+ */
+
+/**
+ * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the I2C voltage requests
+ *
+ * Set the master enable control for I2C control value updates. If disabled,
+ * then I2C control messages are inhibited, regardless of the DFLL mode.
+ */
+static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+ u32 val;
+
+ val = dfll_i2c_readl(td, DFLL_OUTPUT_CFG);
+
+ if (enable)
+ val |= DFLL_OUTPUT_CFG_I2C_ENABLE;
+ else
+ val &= ~DFLL_OUTPUT_CFG_I2C_ENABLE;
+
+ dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_i2c_wmb(td);
+
+ return 0;
+}
+
+
+/*
+ * DFLL-to-PWM controller interface
+ */
+
+/**
+ * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the PWM voltage requests
+ *
+ * Set the master enable control for PWM control value updates. If disabled,
+ * then the PWM signal is not driven. Also configure the PWM output pad
+ * to the appropriate state.
+ */
+static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+ int ret;
+ u32 val, div;
+
+ if (enable) {
+ ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
+ if (ret < 0) {
+ dev_err(td->dev, "setting enable state failed\n");
+ return -EINVAL;
+ }
+ val = dfll_readl(td, DFLL_OUTPUT_CFG);
+ val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+ div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
+ val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+ & DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+ dfll_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+
+ val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
+ dfll_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+ } else {
+ ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
+ if (ret < 0)
+ dev_warn(td->dev, "setting disable state failed\n");
+
+ val = dfll_readl(td, DFLL_OUTPUT_CFG);
+ val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
+ dfll_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+ }
+
+ return 0;
+}
+
+/**
+ * dfll_set_force_output_value - set fixed value for force output
+ * @td: DFLL instance
+ * @out_val: value to force output
+ *
+ * Set the fixed value for force output, DFLL will output this value when
+ * force output is enabled.
+ */
+static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
+{
+ u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+ val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);
+ dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+ dfll_wmb(td);
+
+ return dfll_readl(td, DFLL_OUTPUT_FORCE);
+}
+
+/**
+ * dfll_set_force_output_enabled - enable/disable force output
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the force output
+ *
+ * Set the enable control for fouce output with fixed value.
+ */
+static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
+{
+ u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+ if (enable)
+ val |= DFLL_OUTPUT_FORCE_ENABLE;
+ else
+ val &= ~DFLL_OUTPUT_FORCE_ENABLE;
+
+ dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+ dfll_wmb(td);
+}
+
+/**
+ * dfll_force_output - force output a fixed value
+ * @td: DFLL instance
+ * @out_sel: value to force output
+ *
+ * Set the fixed value for force output, DFLL will output this value.
+ */
+static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
+{
+ u32 val;
+
+ if (out_sel > OUT_MASK)
+ return -EINVAL;
+
+ val = dfll_set_force_output_value(td, out_sel);
+ if ((td->mode < DFLL_CLOSED_LOOP) &&
+ !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
+ dfll_set_force_output_enabled(td, true);
+ }
+
+ return 0;
+}
+
+/**
+ * dfll_load_i2c_lut - load the voltage lookup table
+ * @td: struct tegra_dfll *
+ *
+ * Load the voltage-to-PMIC register value lookup table into the DFLL
+ * IP block memory. Look-up tables can be loaded at any time.
+ */
+static void dfll_load_i2c_lut(struct tegra_dfll *td)
+{
+ int i, lut_index;
+ u32 val;
+
+ for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
+ if (i < td->lut_min)
+ lut_index = td->lut_min;
+ else if (i > td->lut_max)
+ lut_index = td->lut_max;
+ else
+ lut_index = i;
+
+ val = regulator_list_hardware_vsel(td->vdd_reg,
+ td->lut[lut_index]);
+ __raw_writel(val, td->lut_base + i * 4);
+ }
+
+ dfll_i2c_wmb(td);
+}
+
+/**
+ * dfll_init_i2c_if - set up the DFLL's DFLL-I2C interface
+ * @td: DFLL instance
+ *
+ * During DFLL driver initialization, program the DFLL-I2C interface
+ * with the PMU slave address, vdd register offset, and transfer mode.
+ * This data is used by the DFLL to automatically construct I2C
+ * voltage-set commands, which are then passed to the DFLL's internal
+ * I2C controller.
+ */
+static void dfll_init_i2c_if(struct tegra_dfll *td)
+{
+ u32 val;
+
+ if (td->i2c_slave_addr > 0x7f) {
+ val = td->i2c_slave_addr << DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_10BIT;
+ val |= DFLL_I2C_CFG_SLAVE_ADDR_10;
+ } else {
+ val = td->i2c_slave_addr << DFLL_I2C_CFG_SLAVE_ADDR_SHIFT_7BIT;
+ }
+ val |= DFLL_I2C_CFG_SIZE_MASK;
+ val |= DFLL_I2C_CFG_ARB_ENABLE;
+ dfll_i2c_writel(td, val, DFLL_I2C_CFG);
+
+ dfll_i2c_writel(td, td->i2c_reg, DFLL_I2C_VDD_REG_ADDR);
+
+ val = DIV_ROUND_UP(td->i2c_clk_rate, td->i2c_fs_rate * 8);
+ BUG_ON(!val || (val > DFLL_I2C_CLK_DIVISOR_MASK));
+ val = (val - 1) << DFLL_I2C_CLK_DIVISOR_FS_SHIFT;
+
+ /* default hs divisor just in case */
+ val |= 1 << DFLL_I2C_CLK_DIVISOR_HS_SHIFT;
+ __raw_writel(val, td->i2c_controller_base + DFLL_I2C_CLK_DIVISOR);
+ dfll_i2c_wmb(td);
+}
+
+/**
+ * dfll_init_out_if - prepare DFLL-to-PMIC interface
+ * @td: DFLL instance
+ *
+ * During DFLL driver initialization or resume from context loss,
+ * disable the I2C command output to the PMIC, set safe voltage and
+ * output limits, and disable and clear limit interrupts.
+ */
+static void dfll_init_out_if(struct tegra_dfll *td)
+{
+ u32 val;
+
+ td->lut_min = td->lut_bottom;
+ td->lut_max = td->lut_size - 1;
+ td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
+
+ /* clear DFLL_OUTPUT_CFG before setting new value */
+ dfll_writel(td, 0, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+
+ val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+ (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+ (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+ dfll_writel(td, val, DFLL_OUTPUT_CFG);
+ dfll_wmb(td);
+
+ dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+ dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+ dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+ DFLL_INTR_STS);
+
+ if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
+ u32 vinit = td->reg_init_uV;
+ int vstep = td->soc->alignment.step_uv;
+ unsigned long vmin = td->lut_uv[0];
+
+ /* set initial voltage */
+ if ((vinit >= vmin) && vstep) {
+ unsigned int vsel;
+
+ vsel = DIV_ROUND_UP((vinit - vmin), vstep);
+ dfll_force_output(td, vsel);
+ }
+ } else {
+ dfll_load_i2c_lut(td);
+ dfll_init_i2c_if(td);
+ }
+}
+
+/*
+ * Set/get the DFLL's targeted output clock rate
+ */
+
+/**
+ * find_lut_index_for_rate - determine I2C LUT index for given DFLL rate
+ * @td: DFLL instance
+ * @rate: clock rate
+ *
+ * Determines the index of a I2C LUT entry for a voltage that approximately
+ * produces the given DFLL clock rate. This is used when forcing a value
+ * to the integrator during rate changes. Returns -ENOENT if a suitable
+ * LUT index is not found.
+ */
+static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
+{
+ struct dev_pm_opp *opp;
+ int i, align_step;
+
+ opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ align_step = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
+ dev_pm_opp_put(opp);
+
+ for (i = td->lut_bottom; i < td->lut_size; i++) {
+ if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= align_step)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * dfll_calculate_rate_request - calculate DFLL parameters for a given rate
+ * @td: DFLL instance
+ * @req: DFLL-rate-request structure
+ * @rate: the desired DFLL rate
+ *
+ * Populate the DFLL-rate-request record @req fields with the scale_bits
+ * and mult_bits fields, based on the target input rate. Returns 0 upon
+ * success, or -EINVAL if the requested rate in req->rate is too high
+ * or low for the DFLL to generate.
+ */
+static int dfll_calculate_rate_request(struct tegra_dfll *td,
+ struct dfll_rate_req *req,
+ unsigned long rate)
+{
+ u32 val;
+
+ /*
+ * If requested rate is below the minimum DVCO rate, active the scaler.
+ * In the future the DVCO minimum voltage should be selected based on
+ * chip temperature and the actual minimum rate should be calibrated
+ * at runtime.
+ */
+ req->scale_bits = DFLL_FREQ_REQ_SCALE_MAX - 1;
+ if (rate < td->dvco_rate_min) {
+ int scale;
+
+ scale = DIV_ROUND_CLOSEST(rate / 1000 * DFLL_FREQ_REQ_SCALE_MAX,
+ td->dvco_rate_min / 1000);
+ if (!scale) {
+ dev_err(td->dev, "%s: Rate %lu is too low\n",
+ __func__, rate);
+ return -EINVAL;
+ }
+ req->scale_bits = scale - 1;
+ rate = td->dvco_rate_min;
+ }
+
+ /* Convert requested rate into frequency request and scale settings */
+ val = DVCO_RATE_TO_MULT(rate, td->ref_rate);
+ if (val > FREQ_MAX) {
+ dev_err(td->dev, "%s: Rate %lu is above dfll range\n",
+ __func__, rate);
+ return -EINVAL;
+ }
+ req->mult_bits = val;
+ req->dvco_target_rate = MULT_TO_DVCO_RATE(req->mult_bits, td->ref_rate);
+ req->rate = dfll_scale_dvco_rate(req->scale_bits,
+ req->dvco_target_rate);
+ req->lut_index = find_lut_index_for_rate(td, req->dvco_target_rate);
+ if (req->lut_index < 0)
+ return req->lut_index;
+
+ return 0;
+}
+
+/**
+ * dfll_set_frequency_request - start the frequency change operation
+ * @td: DFLL instance
+ * @req: rate request structure
+ *
+ * Tell the DFLL to try to change its output frequency to the
+ * frequency represented by @req. DFLL must be in closed-loop mode.
+ */
+static void dfll_set_frequency_request(struct tegra_dfll *td,
+ struct dfll_rate_req *req)
+{
+ u32 val = 0;
+ int force_val;
+ int coef = 128; /* FIXME: td->cg_scale? */
+
+ force_val = (req->lut_index - td->lut_safe) * coef / td->cg;
+ force_val = clamp(force_val, FORCE_MIN, FORCE_MAX);
+
+ val |= req->mult_bits << DFLL_FREQ_REQ_MULT_SHIFT;
+ val |= req->scale_bits << DFLL_FREQ_REQ_SCALE_SHIFT;
+ val |= ((u32)force_val << DFLL_FREQ_REQ_FORCE_SHIFT) &
+ DFLL_FREQ_REQ_FORCE_MASK;
+ val |= DFLL_FREQ_REQ_FREQ_VALID | DFLL_FREQ_REQ_FORCE_ENABLE;
+
+ dfll_writel(td, val, DFLL_FREQ_REQ);
+ dfll_wmb(td);
+}
+
+/**
+ * dfll_request_rate - set the next rate for the DFLL to tune to
+ * @td: DFLL instance
+ * @rate: clock rate to target
+ *
+ * Convert the requested clock rate @rate into the DFLL control logic
+ * settings. In closed-loop mode, update new settings immediately to
+ * adjust DFLL output rate accordingly. Otherwise, just save them
+ * until the next switch to closed loop. Returns 0 upon success,
+ * -EPERM if the DFLL driver has not yet been initialized, or -EINVAL
+ * if @rate is outside the DFLL's tunable range.
+ */
+static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate)
+{
+ int ret;
+ struct dfll_rate_req req;
+
+ if (td->mode == DFLL_UNINITIALIZED) {
+ dev_err(td->dev, "%s: Cannot set DFLL rate in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+
+ ret = dfll_calculate_rate_request(td, &req, rate);
+ if (ret)
+ return ret;
+
+ td->last_unrounded_rate = rate;
+ td->last_req = req;
+
+ if (td->mode == DFLL_CLOSED_LOOP)
+ dfll_set_frequency_request(td, &td->last_req);
+
+ return 0;
+}
+
+/*
+ * DFLL enable/disable & open-loop <-> closed-loop transitions
+ */
+
+/**
+ * dfll_disable - switch from open-loop mode to disabled mode
+ * @td: DFLL instance
+ *
+ * Switch from OPEN_LOOP state to DISABLED state. Returns 0 upon success
+ * or -EPERM if the DFLL is not currently in open-loop mode.
+ */
+static int dfll_disable(struct tegra_dfll *td)
+{
+ if (td->mode != DFLL_OPEN_LOOP) {
+ dev_err(td->dev, "cannot disable DFLL in %s mode\n",
+ mode_name[td->mode]);
+ return -EINVAL;
+ }
+
+ dfll_set_mode(td, DFLL_DISABLED);
+ pm_runtime_put_sync(td->dev);
+
+ return 0;
+}
+
+/**
+ * dfll_enable - switch a disabled DFLL to open-loop mode
+ * @td: DFLL instance
+ *
+ * Switch from DISABLED state to OPEN_LOOP state. Returns 0 upon success
+ * or -EPERM if the DFLL is not currently disabled.
+ */
+static int dfll_enable(struct tegra_dfll *td)
+{
+ if (td->mode != DFLL_DISABLED) {
+ dev_err(td->dev, "cannot enable DFLL in %s mode\n",
+ mode_name[td->mode]);
+ return -EPERM;
+ }
+
+ pm_runtime_get_sync(td->dev);
+ dfll_set_mode(td, DFLL_OPEN_LOOP);
+
+ return 0;
+}
+
+/**
+ * dfll_set_open_loop_config - prepare to switch to open-loop mode
+ * @td: DFLL instance
+ *
+ * Prepare to switch the DFLL to open-loop mode. This switches the
+ * DFLL to the low-voltage tuning range, ensures that I2C output
+ * forcing is disabled, and disables the output clock rate scaler.
+ * The DFLL's low-voltage tuning range parameters must be
+ * characterized to keep the downstream device stable at any DVCO
+ * input voltage. No return value.
+ */
+static void dfll_set_open_loop_config(struct tegra_dfll *td)
+{
+ u32 val;
+
+ /* always tune low (safe) in open loop */
+ if (td->tune_range != DFLL_TUNE_LOW)
+ dfll_tune_low(td);
+
+ val = dfll_readl(td, DFLL_FREQ_REQ);
+ val |= DFLL_FREQ_REQ_SCALE_MASK;
+ val &= ~DFLL_FREQ_REQ_FORCE_ENABLE;
+ dfll_writel(td, val, DFLL_FREQ_REQ);
+ dfll_wmb(td);
+}
+
+/**
+ * dfll_lock - switch from open-loop to closed-loop mode
+ * @td: DFLL instance
+ *
+ * Switch from OPEN_LOOP state to CLOSED_LOOP state. Returns 0 upon success,
+ * -EINVAL if the DFLL's target rate hasn't been set yet, or -EPERM if the
+ * DFLL is not currently in open-loop mode.
+ */
+static int dfll_lock(struct tegra_dfll *td)
+{
+ struct dfll_rate_req *req = &td->last_req;
+
+ switch (td->mode) {
+ case DFLL_CLOSED_LOOP:
+ return 0;
+
+ case DFLL_OPEN_LOOP:
+ if (req->rate == 0) {
+ dev_err(td->dev, "%s: Cannot lock DFLL at rate 0\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+ dfll_pwm_set_output_enabled(td, true);
+ else
+ dfll_i2c_set_output_enabled(td, true);
+
+ dfll_set_mode(td, DFLL_CLOSED_LOOP);
+ dfll_set_frequency_request(td, req);
+ dfll_set_force_output_enabled(td, false);
+ return 0;
+
+ default:
+ BUG_ON(td->mode > DFLL_CLOSED_LOOP);
+ dev_err(td->dev, "%s: Cannot lock DFLL in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+}
+
+/**
+ * dfll_unlock - switch from closed-loop to open-loop mode
+ * @td: DFLL instance
+ *
+ * Switch from CLOSED_LOOP state to OPEN_LOOP state. Returns 0 upon success,
+ * or -EPERM if the DFLL is not currently in open-loop mode.
+ */
+static int dfll_unlock(struct tegra_dfll *td)
+{
+ switch (td->mode) {
+ case DFLL_CLOSED_LOOP:
+ dfll_set_open_loop_config(td);
+ dfll_set_mode(td, DFLL_OPEN_LOOP);
+ if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+ dfll_pwm_set_output_enabled(td, false);
+ else
+ dfll_i2c_set_output_enabled(td, false);
+ return 0;
+
+ case DFLL_OPEN_LOOP:
+ return 0;
+
+ default:
+ BUG_ON(td->mode > DFLL_CLOSED_LOOP);
+ dev_err(td->dev, "%s: Cannot unlock DFLL in %s mode\n",
+ __func__, mode_name[td->mode]);
+ return -EPERM;
+ }
+}
+
+/*
+ * Clock framework integration
+ *
+ * When the DFLL is being controlled by the CCF, always enter closed loop
+ * mode when the clk is enabled. This requires that a DFLL rate request
+ * has been set beforehand, which implies that a clk_set_rate() call is
+ * always required before a clk_enable().
+ */
+
+static int dfll_clk_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+
+ return dfll_is_running(td);
+}
+
+static int dfll_clk_enable(struct clk_hw *hw)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ int ret;
+
+ ret = dfll_enable(td);
+ if (ret)
+ return ret;
+
+ ret = dfll_lock(td);
+ if (ret)
+ dfll_disable(td);
+
+ return ret;
+}
+
+static void dfll_clk_disable(struct clk_hw *hw)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ int ret;
+
+ ret = dfll_unlock(td);
+ if (!ret)
+ dfll_disable(td);
+}
+
+static unsigned long dfll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+
+ return td->last_unrounded_rate;
+}
+
+/* Must use determine_rate since it allows for rates exceeding 2^31-1 */
+static int dfll_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *clk_req)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+ struct dfll_rate_req req;
+ int ret;
+
+ ret = dfll_calculate_rate_request(td, &req, clk_req->rate);
+ if (ret)
+ return ret;
+
+ /*
+ * Don't set the rounded rate, since it doesn't really matter as
+ * the output rate will be voltage controlled anyway, and cpufreq
+ * freaks out if any rounding happens.
+ */
+
+ return 0;
+}
+
+static int dfll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_dfll *td = clk_hw_to_dfll(hw);
+
+ return dfll_request_rate(td, rate);
+}
+
+static const struct clk_ops dfll_clk_ops = {
+ .is_enabled = dfll_clk_is_enabled,
+ .enable = dfll_clk_enable,
+ .disable = dfll_clk_disable,
+ .recalc_rate = dfll_clk_recalc_rate,
+ .determine_rate = dfll_clk_determine_rate,
+ .set_rate = dfll_clk_set_rate,
+};
+
+static struct clk_init_data dfll_clk_init_data = {
+ .ops = &dfll_clk_ops,
+ .num_parents = 0,
+};
+
+/**
+ * dfll_register_clk - register the DFLL output clock with the clock framework
+ * @td: DFLL instance
+ *
+ * Register the DFLL's output clock with the Linux clock framework and register
+ * the DFLL driver as an OF clock provider. Returns 0 upon success or -EINVAL
+ * or -ENOMEM upon failure.
+ */
+static int dfll_register_clk(struct tegra_dfll *td)
+{
+ int ret;
+
+ dfll_clk_init_data.name = td->output_clock_name;
+ td->dfll_clk_hw.init = &dfll_clk_init_data;
+
+ td->dfll_clk = clk_register(td->dev, &td->dfll_clk_hw);
+ if (IS_ERR(td->dfll_clk)) {
+ dev_err(td->dev, "DFLL clock registration error\n");
+ return -EINVAL;
+ }
+
+ ret = of_clk_add_provider(td->dev->of_node, of_clk_src_simple_get,
+ td->dfll_clk);
+ if (ret) {
+ dev_err(td->dev, "of_clk_add_provider() failed\n");
+
+ clk_unregister(td->dfll_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * dfll_unregister_clk - unregister the DFLL output clock
+ * @td: DFLL instance
+ *
+ * Unregister the DFLL's output clock from the Linux clock framework
+ * and from clkdev. No return value.
+ */
+static void dfll_unregister_clk(struct tegra_dfll *td)
+{
+ of_clk_del_provider(td->dev->of_node);
+ clk_unregister(td->dfll_clk);
+ td->dfll_clk = NULL;
+}
+
+/*
+ * Debugfs interface
+ */
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Monitor control
+ */
+
+/**
+ * dfll_calc_monitored_rate - convert DFLL_MONITOR_DATA_VAL rate into real freq
+ * @monitor_data: value read from the DFLL_MONITOR_DATA_VAL bitfield
+ * @ref_rate: DFLL reference clock rate
+ *
+ * Convert @monitor_data from DFLL_MONITOR_DATA_VAL units into cycles
+ * per second. Returns the converted value.
+ */
+static u64 dfll_calc_monitored_rate(u32 monitor_data,
+ unsigned long ref_rate)
+{
+ return monitor_data * (ref_rate / REF_CLK_CYC_PER_DVCO_SAMPLE);
+}
+
+/**
+ * dfll_read_monitor_rate - return the DFLL's output rate from internal monitor
+ * @td: DFLL instance
+ *
+ * If the DFLL is enabled, return the last rate reported by the DFLL's
+ * internal monitoring hardware. This works in both open-loop and
+ * closed-loop mode, and takes the output scaler setting into account.
+ * Assumes that the monitor was programmed to monitor frequency before
+ * the sample period started. If the driver believes that the DFLL is
+ * currently uninitialized or disabled, it will return 0, since
+ * otherwise the DFLL monitor data register will return the last
+ * measured rate from when the DFLL was active.
+ */
+static u64 dfll_read_monitor_rate(struct tegra_dfll *td)
+{
+ u32 v, s;
+ u64 pre_scaler_rate, post_scaler_rate;
+
+ if (!dfll_is_running(td))
+ return 0;
+
+ v = dfll_readl(td, DFLL_MONITOR_DATA);
+ v = (v & DFLL_MONITOR_DATA_VAL_MASK) >> DFLL_MONITOR_DATA_VAL_SHIFT;
+ pre_scaler_rate = dfll_calc_monitored_rate(v, td->ref_rate);
+
+ s = dfll_readl(td, DFLL_FREQ_REQ);
+ s = (s & DFLL_FREQ_REQ_SCALE_MASK) >> DFLL_FREQ_REQ_SCALE_SHIFT;
+ post_scaler_rate = dfll_scale_dvco_rate(s, pre_scaler_rate);
+
+ return post_scaler_rate;
+}
+
+static int attr_enable_get(void *data, u64 *val)
+{
+ struct tegra_dfll *td = data;
+
+ *val = dfll_is_running(td);
+
+ return 0;
+}
+static int attr_enable_set(void *data, u64 val)
+{
+ struct tegra_dfll *td = data;
+
+ return val ? dfll_enable(td) : dfll_disable(td);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(enable_fops, attr_enable_get, attr_enable_set,
+ "%llu\n");
+
+static int attr_lock_get(void *data, u64 *val)
+{
+ struct tegra_dfll *td = data;
+
+ *val = (td->mode == DFLL_CLOSED_LOOP);
+
+ return 0;
+}
+static int attr_lock_set(void *data, u64 val)
+{
+ struct tegra_dfll *td = data;
+
+ return val ? dfll_lock(td) : dfll_unlock(td);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(lock_fops, attr_lock_get, attr_lock_set, "%llu\n");
+
+static int attr_rate_get(void *data, u64 *val)
+{
+ struct tegra_dfll *td = data;
+
+ *val = dfll_read_monitor_rate(td);
+
+ return 0;
+}
+
+static int attr_rate_set(void *data, u64 val)
+{
+ struct tegra_dfll *td = data;
+
+ return dfll_request_rate(td, val);
+}
+DEFINE_DEBUGFS_ATTRIBUTE(rate_fops, attr_rate_get, attr_rate_set, "%llu\n");
+
+static int attr_registers_show(struct seq_file *s, void *data)
+{
+ u32 val, offs;
+ struct tegra_dfll *td = s->private;
+
+ seq_puts(s, "CONTROL REGISTERS:\n");
+ for (offs = 0; offs <= DFLL_MONITOR_DATA; offs += 4) {
+ if (offs == DFLL_OUTPUT_CFG)
+ val = dfll_i2c_readl(td, offs);
+ else
+ val = dfll_readl(td, offs);
+ seq_printf(s, "[0x%02x] = 0x%08x\n", offs, val);
+ }
+
+ seq_puts(s, "\nI2C and INTR REGISTERS:\n");
+ for (offs = DFLL_I2C_CFG; offs <= DFLL_I2C_STS; offs += 4)
+ seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+ dfll_i2c_readl(td, offs));
+ for (offs = DFLL_INTR_STS; offs <= DFLL_INTR_EN; offs += 4)
+ seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+ dfll_i2c_readl(td, offs));
+
+ if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
+ seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
+ offs = DFLL_I2C_CLK_DIVISOR;
+ seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+ __raw_readl(td->i2c_controller_base + offs));
+
+ seq_puts(s, "\nLUT:\n");
+ for (offs = 0; offs < 4 * MAX_DFLL_VOLTAGES; offs += 4)
+ seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+ __raw_readl(td->lut_base + offs));
+ }
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(attr_registers);
+
+static void dfll_debug_init(struct tegra_dfll *td)
+{
+ struct dentry *root;
+
+ if (!td || (td->mode == DFLL_UNINITIALIZED))
+ return;
+
+ root = debugfs_create_dir("tegra_dfll_fcpu", NULL);
+ td->debugfs_dir = root;
+
+ debugfs_create_file_unsafe("enable", 0644, root, td,
+ &enable_fops);
+ debugfs_create_file_unsafe("lock", 0444, root, td, &lock_fops);
+ debugfs_create_file_unsafe("rate", 0444, root, td, &rate_fops);
+ debugfs_create_file("registers", 0444, root, td, &attr_registers_fops);
+}
+
+#else
+static inline void dfll_debug_init(struct tegra_dfll *td) { }
+#endif /* CONFIG_DEBUG_FS */
+
+/*
+ * DFLL initialization
+ */
+
+/**
+ * dfll_set_default_params - program non-output related DFLL parameters
+ * @td: DFLL instance
+ *
+ * During DFLL driver initialization or resume from context loss,
+ * program parameters for the closed loop integrator, DVCO tuning,
+ * voltage droop control and monitor control.
+ */
+static void dfll_set_default_params(struct tegra_dfll *td)
+{
+ u32 val;
+
+ val = DIV_ROUND_UP(td->ref_rate, td->sample_rate * 32);
+ BUG_ON(val > DFLL_CONFIG_DIV_MASK);
+ dfll_writel(td, val, DFLL_CONFIG);
+
+ val = (td->force_mode << DFLL_PARAMS_FORCE_MODE_SHIFT) |
+ (td->cf << DFLL_PARAMS_CF_PARAM_SHIFT) |
+ (td->ci << DFLL_PARAMS_CI_PARAM_SHIFT) |
+ (td->cg << DFLL_PARAMS_CG_PARAM_SHIFT) |
+ (td->cg_scale ? DFLL_PARAMS_CG_SCALE : 0);
+ dfll_writel(td, val, DFLL_PARAMS);
+
+ dfll_tune_low(td);
+ dfll_writel(td, td->droop_ctrl, DFLL_DROOP_CTRL);
+ dfll_writel(td, DFLL_MONITOR_CTRL_FREQ, DFLL_MONITOR_CTRL);
+}
+
+/**
+ * dfll_init_clks - clk_get() the DFLL source clocks
+ * @td: DFLL instance
+ *
+ * Call clk_get() on the DFLL source clocks and save the pointers for later
+ * use. Returns 0 upon success or error (see devm_clk_get) if one or more
+ * of the clocks couldn't be looked up.
+ */
+static int dfll_init_clks(struct tegra_dfll *td)
+{
+ td->ref_clk = devm_clk_get(td->dev, "ref");
+ if (IS_ERR(td->ref_clk)) {
+ dev_err(td->dev, "missing ref clock\n");
+ return PTR_ERR(td->ref_clk);
+ }
+
+ td->soc_clk = devm_clk_get(td->dev, "soc");
+ if (IS_ERR(td->soc_clk)) {
+ dev_err(td->dev, "missing soc clock\n");
+ return PTR_ERR(td->soc_clk);
+ }
+
+ td->i2c_clk = devm_clk_get(td->dev, "i2c");
+ if (IS_ERR(td->i2c_clk)) {
+ dev_err(td->dev, "missing i2c clock\n");
+ return PTR_ERR(td->i2c_clk);
+ }
+ td->i2c_clk_rate = clk_get_rate(td->i2c_clk);
+
+ return 0;
+}
+
+/**
+ * dfll_init - Prepare the DFLL IP block for use
+ * @td: DFLL instance
+ *
+ * Do everything necessary to prepare the DFLL IP block for use. The
+ * DFLL will be left in DISABLED state. Called by dfll_probe().
+ * Returns 0 upon success, or passes along the error from whatever
+ * function returned it.
+ */
+static int dfll_init(struct tegra_dfll *td)
+{
+ int ret;
+
+ td->ref_rate = clk_get_rate(td->ref_clk);
+ if (td->ref_rate != REF_CLOCK_RATE) {
+ dev_err(td->dev, "unexpected ref clk rate %lu, expecting %lu",
+ td->ref_rate, REF_CLOCK_RATE);
+ return -EINVAL;
+ }
+
+ reset_control_deassert(td->dfll_rst);
+ reset_control_deassert(td->dvco_rst);
+
+ ret = clk_prepare(td->ref_clk);
+ if (ret) {
+ dev_err(td->dev, "failed to prepare ref_clk\n");
+ return ret;
+ }
+
+ ret = clk_prepare(td->soc_clk);
+ if (ret) {
+ dev_err(td->dev, "failed to prepare soc_clk\n");
+ goto di_err1;
+ }
+
+ ret = clk_prepare(td->i2c_clk);
+ if (ret) {
+ dev_err(td->dev, "failed to prepare i2c_clk\n");
+ goto di_err2;
+ }
+
+ td->last_unrounded_rate = 0;
+
+ pm_runtime_enable(td->dev);
+ pm_runtime_get_sync(td->dev);
+
+ dfll_set_mode(td, DFLL_DISABLED);
+ dfll_set_default_params(td);
+
+ if (td->soc->init_clock_trimmers)
+ td->soc->init_clock_trimmers();
+
+ dfll_set_open_loop_config(td);
+
+ dfll_init_out_if(td);
+
+ pm_runtime_put_sync(td->dev);
+
+ return 0;
+
+di_err2:
+ clk_unprepare(td->soc_clk);
+di_err1:
+ clk_unprepare(td->ref_clk);
+
+ reset_control_assert(td->dvco_rst);
+ reset_control_assert(td->dfll_rst);
+
+ return ret;
+}
+
+/**
+ * tegra_dfll_suspend - check DFLL is disabled
+ * @dev: DFLL instance
+ *
+ * DFLL clock should be disabled by the CPUFreq driver. So, make
+ * sure it is disabled and disable all clocks needed by the DFLL.
+ */
+int tegra_dfll_suspend(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+
+ if (dfll_is_running(td)) {
+ dev_err(td->dev, "DFLL still enabled while suspending\n");
+ return -EBUSY;
+ }
+
+ reset_control_assert(td->dvco_rst);
+ reset_control_assert(td->dfll_rst);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_suspend);
+
+/**
+ * tegra_dfll_resume - reinitialize DFLL on resume
+ * @dev: DFLL instance
+ *
+ * DFLL is disabled and reset during suspend and resume.
+ * So, reinitialize the DFLL IP block back for use.
+ * DFLL clock is enabled later in closed loop mode by CPUFreq
+ * driver before switching its clock source to DFLL output.
+ */
+int tegra_dfll_resume(struct device *dev)
+{
+ struct tegra_dfll *td = dev_get_drvdata(dev);
+
+ reset_control_deassert(td->dfll_rst);
+ reset_control_deassert(td->dvco_rst);
+
+ pm_runtime_get_sync(td->dev);
+
+ dfll_set_mode(td, DFLL_DISABLED);
+ dfll_set_default_params(td);
+
+ if (td->soc->init_clock_trimmers)
+ td->soc->init_clock_trimmers();
+
+ dfll_set_open_loop_config(td);
+
+ dfll_init_out_if(td);
+
+ pm_runtime_put_sync(td->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_resume);
+
+/*
+ * DT data fetch
+ */
+
+/*
+ * Find a PMIC voltage register-to-voltage mapping for the given voltage.
+ * An exact voltage match is required.
+ */
+static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
+{
+ int i, n_voltages, reg_uV,reg_volt_id, align_step;
+
+ if (WARN_ON(td->pmu_if == TEGRA_DFLL_PMU_PWM))
+ return -EINVAL;
+
+ align_step = uV / td->soc->alignment.step_uv;
+ n_voltages = regulator_count_voltages(td->vdd_reg);
+ for (i = 0; i < n_voltages; i++) {
+ reg_uV = regulator_list_voltage(td->vdd_reg, i);
+ if (reg_uV < 0)
+ break;
+
+ reg_volt_id = reg_uV / td->soc->alignment.step_uv;
+
+ if (align_step == reg_volt_id)
+ return i;
+ }
+
+ dev_err(td->dev, "no voltage map entry for %d uV\n", uV);
+ return -EINVAL;
+}
+
+/*
+ * Find a PMIC voltage register-to-voltage mapping for the given voltage,
+ * rounding up to the closest supported voltage.
+ * */
+static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
+{
+ int i, n_voltages, reg_uV, reg_volt_id, align_step;
+
+ if (WARN_ON(td->pmu_if == TEGRA_DFLL_PMU_PWM))
+ return -EINVAL;
+
+ align_step = uV / td->soc->alignment.step_uv;
+ n_voltages = regulator_count_voltages(td->vdd_reg);
+ for (i = 0; i < n_voltages; i++) {
+ reg_uV = regulator_list_voltage(td->vdd_reg, i);
+ if (reg_uV < 0)
+ break;
+
+ reg_volt_id = reg_uV / td->soc->alignment.step_uv;
+
+ if (align_step <= reg_volt_id)
+ return i;
+ }
+
+ dev_err(td->dev, "no voltage map entry rounding to %d uV\n", uV);
+ return -EINVAL;
+}
+
+/*
+ * dfll_build_pwm_lut - build the PWM regulator lookup table
+ * @td: DFLL instance
+ * @v_max: Vmax from OPP table
+ *
+ * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
+ * In this case closed loop output is controlling duty cycle directly. The s/w
+ * look-up that maps PWM duty cycle to voltage is still built by this function.
+ */
+static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max)
+{
+ int i;
+ unsigned long rate, reg_volt;
+ u8 lut_bottom = MAX_DFLL_VOLTAGES;
+ int v_min = td->soc->cvb->min_millivolts * 1000;
+
+ for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
+ reg_volt = td->lut_uv[i];
+
+ /* since opp voltage is exact mv */
+ reg_volt = (reg_volt / 1000) * 1000;
+ if (reg_volt > v_max)
+ break;
+
+ td->lut[i] = i;
+ if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
+ lut_bottom = i;
+ }
+
+ /* determine voltage boundaries */
+ td->lut_size = i;
+ if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
+ (lut_bottom + 1 >= td->lut_size)) {
+ dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
+ td->soc->cvb->min_millivolts);
+ return -EINVAL;
+ }
+ td->lut_bottom = lut_bottom;
+
+ /* determine rate boundaries */
+ rate = get_dvco_rate_below(td, td->lut_bottom);
+ if (!rate) {
+ dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
+ td->soc->cvb->min_millivolts);
+ return -EINVAL;
+ }
+ td->dvco_rate_min = rate;
+
+ return 0;
+}
+
+/**
+ * dfll_build_i2c_lut - build the I2C voltage register lookup table
+ * @td: DFLL instance
+ * @v_max: Vmax from OPP table
+ *
+ * The DFLL hardware has 33 bytes of look-up table RAM that must be filled with
+ * PMIC voltage register values that span the entire DFLL operating range.
+ * This function builds the look-up table based on the OPP table provided by
+ * the soc-specific platform driver (td->soc->opp_dev) and the PMIC
+ * register-to-voltage mapping queried from the regulator framework.
+ *
+ * On success, fills in td->lut and returns 0, or -err on failure.
+ */
+static int dfll_build_i2c_lut(struct tegra_dfll *td, unsigned long v_max)
+{
+ unsigned long rate, v, v_opp;
+ int ret = -EINVAL;
+ int j, selector, lut;
+
+ v = td->soc->cvb->min_millivolts * 1000;
+ lut = find_vdd_map_entry_exact(td, v);
+ if (lut < 0)
+ goto out;
+ td->lut[0] = lut;
+ td->lut_bottom = 0;
+
+ for (j = 1, rate = 0; ; rate++) {
+ struct dev_pm_opp *opp;
+
+ opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
+ if (IS_ERR(opp))
+ break;
+ v_opp = dev_pm_opp_get_voltage(opp);
+
+ if (v_opp <= td->soc->cvb->min_millivolts * 1000)
+ td->dvco_rate_min = dev_pm_opp_get_freq(opp);
+
+ dev_pm_opp_put(opp);
+
+ for (;;) {
+ v += max(1UL, (v_max - v) / (MAX_DFLL_VOLTAGES - j));
+ if (v >= v_opp)
+ break;
+
+ selector = find_vdd_map_entry_min(td, v);
+ if (selector < 0)
+ goto out;
+ if (selector != td->lut[j - 1])
+ td->lut[j++] = selector;
+ }
+
+ v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
+ selector = find_vdd_map_entry_exact(td, v);
+ if (selector < 0)
+ goto out;
+ if (selector != td->lut[j - 1])
+ td->lut[j++] = selector;
+
+ if (v >= v_max)
+ break;
+ }
+ td->lut_size = j;
+
+ if (!td->dvco_rate_min)
+ dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
+ td->soc->cvb->min_millivolts);
+ else {
+ ret = 0;
+ for (j = 0; j < td->lut_size; j++)
+ td->lut_uv[j] =
+ regulator_list_voltage(td->vdd_reg,
+ td->lut[j]);
+ }
+
+out:
+ return ret;
+}
+
+static int dfll_build_lut(struct tegra_dfll *td)
+{
+ unsigned long rate, v_max;
+ struct dev_pm_opp *opp;
+
+ rate = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
+ if (IS_ERR(opp)) {
+ dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
+ return -EINVAL;
+ }
+ v_max = dev_pm_opp_get_voltage(opp);
+ dev_pm_opp_put(opp);
+
+ if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+ return dfll_build_pwm_lut(td, v_max);
+ else
+ return dfll_build_i2c_lut(td, v_max);
+}
+
+/**
+ * read_dt_param - helper function for reading required parameters from the DT
+ * @td: DFLL instance
+ * @param: DT property name
+ * @dest: output pointer for the value read
+ *
+ * Read a required numeric parameter from the DFLL device node, or complain
+ * if the property doesn't exist. Returns a boolean indicating success for
+ * easy chaining of multiple calls to this function.
+ */
+static bool read_dt_param(struct tegra_dfll *td, const char *param, u32 *dest)
+{
+ int err = of_property_read_u32(td->dev->of_node, param, dest);
+
+ if (err < 0) {
+ dev_err(td->dev, "failed to read DT parameter %s: %d\n",
+ param, err);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * dfll_fetch_i2c_params - query PMIC I2C params from DT & regulator subsystem
+ * @td: DFLL instance
+ *
+ * Read all the parameters required for operation in I2C mode. The parameters
+ * can originate from the device tree or the regulator subsystem.
+ * Returns 0 on success or -err on failure.
+ */
+static int dfll_fetch_i2c_params(struct tegra_dfll *td)
+{
+ struct regmap *regmap;
+ struct device *i2c_dev;
+ struct i2c_client *i2c_client;
+ int vsel_reg, vsel_mask;
+ int ret;
+
+ if (!read_dt_param(td, "nvidia,i2c-fs-rate", &td->i2c_fs_rate))
+ return -EINVAL;
+
+ regmap = regulator_get_regmap(td->vdd_reg);
+ i2c_dev = regmap_get_device(regmap);
+ i2c_client = to_i2c_client(i2c_dev);
+
+ td->i2c_slave_addr = i2c_client->addr;
+
+ ret = regulator_get_hardware_vsel_register(td->vdd_reg,
+ &vsel_reg,
+ &vsel_mask);
+ if (ret < 0) {
+ dev_err(td->dev,
+ "regulator unsuitable for DFLL I2C operation\n");
+ return -EINVAL;
+ }
+ td->i2c_reg = vsel_reg;
+
+ return 0;
+}
+
+static int dfll_fetch_pwm_params(struct tegra_dfll *td)
+{
+ int ret, i;
+ u32 pwm_period;
+
+ if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
+ dev_err(td->dev,
+ "Missing step or alignment info for PWM regulator");
+ return -EINVAL;
+ }
+ for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
+ td->lut_uv[i] = td->soc->alignment.offset_uv +
+ i * td->soc->alignment.step_uv;
+
+ ret = read_dt_param(td, "nvidia,pwm-tristate-microvolts",
+ &td->reg_init_uV);
+ if (!ret) {
+ dev_err(td->dev, "couldn't get initialized voltage\n");
+ return -EINVAL;
+ }
+
+ ret = read_dt_param(td, "nvidia,pwm-period-nanoseconds", &pwm_period);
+ if (!ret) {
+ dev_err(td->dev, "couldn't get PWM period\n");
+ return -EINVAL;
+ }
+ td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
+
+ td->pwm_pin = devm_pinctrl_get(td->dev);
+ if (IS_ERR(td->pwm_pin)) {
+ dev_err(td->dev, "DT: missing pinctrl device\n");
+ return PTR_ERR(td->pwm_pin);
+ }
+
+ td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
+ "dvfs_pwm_enable");
+ if (IS_ERR(td->pwm_enable_state)) {
+ dev_err(td->dev, "DT: missing pwm enabled state\n");
+ return PTR_ERR(td->pwm_enable_state);
+ }
+
+ td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
+ "dvfs_pwm_disable");
+ if (IS_ERR(td->pwm_disable_state)) {
+ dev_err(td->dev, "DT: missing pwm disabled state\n");
+ return PTR_ERR(td->pwm_disable_state);
+ }
+
+ return 0;
+}
+
+/**
+ * dfll_fetch_common_params - read DFLL parameters from the device tree
+ * @td: DFLL instance
+ *
+ * Read all the DT parameters that are common to both I2C and PWM operation.
+ * Returns 0 on success or -EINVAL on any failure.
+ */
+static int dfll_fetch_common_params(struct tegra_dfll *td)
+{
+ bool ok = true;
+
+ ok &= read_dt_param(td, "nvidia,droop-ctrl", &td->droop_ctrl);
+ ok &= read_dt_param(td, "nvidia,sample-rate", &td->sample_rate);
+ ok &= read_dt_param(td, "nvidia,force-mode", &td->force_mode);
+ ok &= read_dt_param(td, "nvidia,cf", &td->cf);
+ ok &= read_dt_param(td, "nvidia,ci", &td->ci);
+ ok &= read_dt_param(td, "nvidia,cg", &td->cg);
+ td->cg_scale = of_property_read_bool(td->dev->of_node,
+ "nvidia,cg-scale");
+
+ if (of_property_read_string(td->dev->of_node, "clock-output-names",
+ &td->output_clock_name)) {
+ dev_err(td->dev, "missing clock-output-names property\n");
+ ok = false;
+ }
+
+ return ok ? 0 : -EINVAL;
+}
+
+/*
+ * API exported to per-SoC platform drivers
+ */
+
+/**
+ * tegra_dfll_register - probe a Tegra DFLL device
+ * @pdev: DFLL platform_device *
+ * @soc: Per-SoC integration and characterization data for this DFLL instance
+ *
+ * Probe and initialize a DFLL device instance. Intended to be called
+ * by a SoC-specific shim driver that passes in per-SoC integration
+ * and configuration data via @soc. Returns 0 on success or -err on failure.
+ */
+int tegra_dfll_register(struct platform_device *pdev,
+ struct tegra_dfll_soc_data *soc)
+{
+ struct resource *mem;
+ struct tegra_dfll *td;
+ int ret;
+
+ if (!soc) {
+ dev_err(&pdev->dev, "no tegra_dfll_soc_data provided\n");
+ return -EINVAL;
+ }
+
+ td = devm_kzalloc(&pdev->dev, sizeof(*td), GFP_KERNEL);
+ if (!td)
+ return -ENOMEM;
+ td->dev = &pdev->dev;
+ platform_set_drvdata(pdev, td);
+
+ td->soc = soc;
+
+ td->dfll_rst = devm_reset_control_get_optional(td->dev, "dfll");
+ if (IS_ERR(td->dfll_rst)) {
+ dev_err(td->dev, "couldn't get dfll reset\n");
+ return PTR_ERR(td->dfll_rst);
+ }
+
+ td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
+ if (IS_ERR(td->dvco_rst)) {
+ dev_err(td->dev, "couldn't get dvco reset\n");
+ return PTR_ERR(td->dvco_rst);
+ }
+
+ ret = dfll_fetch_common_params(td);
+ if (ret) {
+ dev_err(td->dev, "couldn't parse device tree parameters\n");
+ return ret;
+ }
+
+ if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
+ td->pmu_if = TEGRA_DFLL_PMU_PWM;
+ ret = dfll_fetch_pwm_params(td);
+ } else {
+ td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
+ if (IS_ERR(td->vdd_reg)) {
+ dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
+ return PTR_ERR(td->vdd_reg);
+ }
+ td->pmu_if = TEGRA_DFLL_PMU_I2C;
+ ret = dfll_fetch_i2c_params(td);
+ }
+ if (ret)
+ return ret;
+
+ ret = dfll_build_lut(td);
+ if (ret) {
+ dev_err(td->dev, "couldn't build LUT\n");
+ return ret;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(td->dev, "no control register resource\n");
+ return -ENODEV;
+ }
+
+ td->base = devm_ioremap(td->dev, mem->start, resource_size(mem));
+ if (!td->base) {
+ dev_err(td->dev, "couldn't ioremap DFLL control registers\n");
+ return -ENODEV;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem) {
+ dev_err(td->dev, "no i2c_base resource\n");
+ return -ENODEV;
+ }
+
+ td->i2c_base = devm_ioremap(td->dev, mem->start, resource_size(mem));
+ if (!td->i2c_base) {
+ dev_err(td->dev, "couldn't ioremap i2c_base resource\n");
+ return -ENODEV;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!mem) {
+ dev_err(td->dev, "no i2c_controller_base resource\n");
+ return -ENODEV;
+ }
+
+ td->i2c_controller_base = devm_ioremap(td->dev, mem->start,
+ resource_size(mem));
+ if (!td->i2c_controller_base) {
+ dev_err(td->dev,
+ "couldn't ioremap i2c_controller_base resource\n");
+ return -ENODEV;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (!mem) {
+ dev_err(td->dev, "no lut_base resource\n");
+ return -ENODEV;
+ }
+
+ td->lut_base = devm_ioremap(td->dev, mem->start, resource_size(mem));
+ if (!td->lut_base) {
+ dev_err(td->dev,
+ "couldn't ioremap lut_base resource\n");
+ return -ENODEV;
+ }
+
+ ret = dfll_init_clks(td);
+ if (ret) {
+ dev_err(&pdev->dev, "DFLL clock init error\n");
+ return ret;
+ }
+
+ /* Enable the clocks and set the device up */
+ ret = dfll_init(td);
+ if (ret)
+ return ret;
+
+ ret = dfll_register_clk(td);
+ if (ret) {
+ dev_err(&pdev->dev, "DFLL clk registration failed\n");
+ return ret;
+ }
+
+ dfll_debug_init(td);
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dfll_register);
+
+/**
+ * tegra_dfll_unregister - release all of the DFLL driver resources for a device
+ * @pdev: DFLL platform_device *
+ *
+ * Unbind this driver from the DFLL hardware device represented by
+ * @pdev. The DFLL must be disabled for this to succeed. Returns a
+ * soc pointer upon success or -EBUSY if the DFLL is still active.
+ */
+struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev)
+{
+ struct tegra_dfll *td = platform_get_drvdata(pdev);
+
+ /*
+ * Note that exiting early here doesn't prevent unbinding the driver.
+ * Exiting early here only leaks some resources.
+ */
+ if (td->mode != DFLL_DISABLED) {
+ dev_err(&pdev->dev,
+ "must disable DFLL before removing driver\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ debugfs_remove_recursive(td->debugfs_dir);
+
+ dfll_unregister_clk(td);
+ pm_runtime_disable(&pdev->dev);
+
+ clk_unprepare(td->ref_clk);
+ clk_unprepare(td->soc_clk);
+ clk_unprepare(td->i2c_clk);
+
+ reset_control_assert(td->dvco_rst);
+ reset_control_assert(td->dfll_rst);
+
+ return td->soc;
+}
+EXPORT_SYMBOL(tegra_dfll_unregister);
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
new file mode 100644
index 000000000..fb209eb5f
--- /dev/null
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * clk-dfll.h - prototypes and macros for the Tegra DFLL clocksource driver
+ * Copyright (C) 2013-2019 NVIDIA Corporation. All rights reserved.
+ *
+ * Aleksandr Frid <afrid@nvidia.com>
+ * Paul Walmsley <pwalmsley@nvidia.com>
+ */
+
+#ifndef __DRIVERS_CLK_TEGRA_CLK_DFLL_H
+#define __DRIVERS_CLK_TEGRA_CLK_DFLL_H
+
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+
+#include "cvb.h"
+
+/**
+ * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
+ * @dev: struct device * that holds the OPP table for the DFLL
+ * @max_freq: maximum frequency supported on this SoC
+ * @cvb: CPU frequency table for this SoC
+ * @alignment: parameters of the regulator step and offset
+ * @init_clock_trimmers: callback to initialize clock trimmers
+ * @set_clock_trimmers_high: callback to tune clock trimmers for high voltage
+ * @set_clock_trimmers_low: callback to tune clock trimmers for low voltage
+ */
+struct tegra_dfll_soc_data {
+ struct device *dev;
+ unsigned long max_freq;
+ const struct cvb_table *cvb;
+ struct rail_alignment alignment;
+
+ void (*init_clock_trimmers)(void);
+ void (*set_clock_trimmers_high)(void);
+ void (*set_clock_trimmers_low)(void);
+};
+
+int tegra_dfll_register(struct platform_device *pdev,
+ struct tegra_dfll_soc_data *soc);
+struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
+int tegra_dfll_runtime_suspend(struct device *dev);
+int tegra_dfll_runtime_resume(struct device *dev);
+int tegra_dfll_suspend(struct device *dev);
+int tegra_dfll_resume(struct device *dev);
+
+#endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */
diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c
new file mode 100644
index 000000000..27f43c3da
--- /dev/null
+++ b/drivers/clk/tegra/clk-divider.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define pll_out_override(p) (BIT((p->shift - 6)))
+#define div_mask(d) ((1 << (d->width)) - 1)
+#define get_mul(d) (1 << d->frac_width)
+#define get_max_div(d) div_mask(d)
+
+#define PERIPH_CLK_UART_DIV_ENB BIT(24)
+
+static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int div;
+
+ div = div_frac_get(rate, parent_rate, divider->width,
+ divider->frac_width, divider->flags);
+
+ if (div < 0)
+ return 0;
+
+ return div;
+}
+
+static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ u32 reg;
+ int div, mul;
+ u64 rate = parent_rate;
+
+ reg = readl_relaxed(divider->reg);
+
+ if ((divider->flags & TEGRA_DIVIDER_UART) &&
+ !(reg & PERIPH_CLK_UART_DIV_ENB))
+ return rate;
+
+ div = (reg >> divider->shift) & div_mask(divider);
+
+ mul = get_mul(divider);
+ div += mul;
+
+ rate *= mul;
+ rate += div - 1;
+ do_div(rate, div);
+
+ return rate;
+}
+
+static int clk_frac_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ int div, mul;
+ unsigned long output_rate = req->best_parent_rate;
+
+ if (!req->rate) {
+ req->rate = output_rate;
+
+ return 0;
+ }
+
+ div = get_div(divider, req->rate, output_rate);
+ if (div < 0) {
+ req->rate = req->best_parent_rate;
+
+ return 0;
+ }
+
+ mul = get_mul(divider);
+
+ req->rate = DIV_ROUND_UP(output_rate * mul, div + mul);
+
+ return 0;
+}
+
+static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_frac_div *divider = to_clk_frac_div(hw);
+ int div;
+ unsigned long flags = 0;
+ u32 val;
+
+ div = get_div(divider, rate, parent_rate);
+ if (div < 0)
+ return div;
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl_relaxed(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= div << divider->shift;
+
+ if (divider->flags & TEGRA_DIVIDER_UART) {
+ if (div)
+ val |= PERIPH_CLK_UART_DIV_ENB;
+ else
+ val &= ~PERIPH_CLK_UART_DIV_ENB;
+ }
+
+ if (divider->flags & TEGRA_DIVIDER_FIXED)
+ val |= pll_out_override(divider);
+
+ writel_relaxed(val, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return 0;
+}
+
+static void clk_divider_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long rate = clk_hw_get_rate(hw);
+
+ if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0)
+ WARN_ON(1);
+}
+
+const struct clk_ops tegra_clk_frac_div_ops = {
+ .recalc_rate = clk_frac_div_recalc_rate,
+ .set_rate = clk_frac_div_set_rate,
+ .determine_rate = clk_frac_div_determine_rate,
+ .restore_context = clk_divider_restore_context,
+};
+
+struct clk *tegra_clk_register_divider(const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
+ u8 frac_width, spinlock_t *lock)
+{
+ struct tegra_clk_frac_div *divider;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ divider = kzalloc_obj(*divider);
+ if (!divider) {
+ pr_err("%s: could not allocate fractional divider clk\n",
+ __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &tegra_clk_frac_div_ops;
+ init.flags = flags;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+
+ divider->reg = reg;
+ divider->shift = shift;
+ divider->width = width;
+ divider->frac_width = frac_width;
+ divider->lock = lock;
+ divider->flags = clk_divider_flags;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ divider->hw.init = &init;
+
+ clk = clk_register(NULL, &divider->hw);
+ if (IS_ERR(clk))
+ kfree(divider);
+
+ return clk;
+}
+
+static const struct clk_div_table mc_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 1 },
+ { .val = 0, .div = 0 },
+};
+
+struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
+ void __iomem *reg, spinlock_t *lock)
+{
+ return clk_register_divider_table(NULL, name, parent_name,
+ CLK_IS_CRITICAL,
+ reg, 16, 1, CLK_DIVIDER_READ_ONLY,
+ mc_div_table, lock);
+}
diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h
new file mode 100644
index 000000000..24413812e
--- /dev/null
+++ b/drivers/clk/tegra/clk-id.h
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides IDs for clocks common between several Tegra SoCs
+ */
+#ifndef _TEGRA_CLK_ID_H
+#define _TEGRA_CLK_ID_H
+
+enum clk_id {
+ tegra_clk_actmon,
+ tegra_clk_adx,
+ tegra_clk_adx1,
+ tegra_clk_afi,
+ tegra_clk_amx,
+ tegra_clk_amx1,
+ tegra_clk_apb2ape,
+ tegra_clk_ahbdma,
+ tegra_clk_apbdma,
+ tegra_clk_apbif,
+ tegra_clk_ape,
+ tegra_clk_audio0,
+ tegra_clk_audio0_2x,
+ tegra_clk_audio0_mux,
+ tegra_clk_audio1,
+ tegra_clk_audio1_2x,
+ tegra_clk_audio1_mux,
+ tegra_clk_audio2,
+ tegra_clk_audio2_2x,
+ tegra_clk_audio2_mux,
+ tegra_clk_audio3,
+ tegra_clk_audio3_2x,
+ tegra_clk_audio3_mux,
+ tegra_clk_audio4,
+ tegra_clk_audio4_2x,
+ tegra_clk_audio4_mux,
+ tegra_clk_bsea,
+ tegra_clk_bsev,
+ tegra_clk_cclk_g,
+ tegra_clk_cclk_lp,
+ tegra_clk_cilab,
+ tegra_clk_cilcd,
+ tegra_clk_cile,
+ tegra_clk_clk_32k,
+ tegra_clk_clk72Mhz,
+ tegra_clk_clk72Mhz_8,
+ tegra_clk_clk_m,
+ tegra_clk_osc,
+ tegra_clk_osc_div2,
+ tegra_clk_osc_div4,
+ tegra_clk_cml0,
+ tegra_clk_cml1,
+ tegra_clk_csi,
+ tegra_clk_csite,
+ tegra_clk_csite_8,
+ tegra_clk_csus,
+ tegra_clk_cve,
+ tegra_clk_dam0,
+ tegra_clk_dam1,
+ tegra_clk_dam2,
+ tegra_clk_d_audio,
+ tegra_clk_dbgapb,
+ tegra_clk_dds,
+ tegra_clk_dfll_ref,
+ tegra_clk_dfll_soc,
+ tegra_clk_disp1,
+ tegra_clk_disp1_8,
+ tegra_clk_disp2,
+ tegra_clk_disp2_8,
+ tegra_clk_dp2,
+ tegra_clk_dpaux,
+ tegra_clk_dpaux1,
+ tegra_clk_dsialp,
+ tegra_clk_dsia_mux,
+ tegra_clk_dsiblp,
+ tegra_clk_dsib_mux,
+ tegra_clk_dtv,
+ tegra_clk_emc,
+ tegra_clk_entropy,
+ tegra_clk_entropy_8,
+ tegra_clk_epp,
+ tegra_clk_epp_8,
+ tegra_clk_extern1,
+ tegra_clk_extern2,
+ tegra_clk_extern3,
+ tegra_clk_fuse,
+ tegra_clk_fuse_burn,
+ tegra_clk_gpu,
+ tegra_clk_gr2d,
+ tegra_clk_gr2d_8,
+ tegra_clk_gr3d,
+ tegra_clk_gr3d_8,
+ tegra_clk_hclk,
+ tegra_clk_hda,
+ tegra_clk_hda_8,
+ tegra_clk_hda2codec_2x,
+ tegra_clk_hda2codec_2x_8,
+ tegra_clk_hda2hdmi,
+ tegra_clk_hdmi,
+ tegra_clk_hdmi_audio,
+ tegra_clk_host1x,
+ tegra_clk_host1x_8,
+ tegra_clk_host1x_9,
+ tegra_clk_hsic_trk,
+ tegra_clk_i2c1,
+ tegra_clk_i2c2,
+ tegra_clk_i2c3,
+ tegra_clk_i2c4,
+ tegra_clk_i2c5,
+ tegra_clk_i2c6,
+ tegra_clk_i2cslow,
+ tegra_clk_i2s0,
+ tegra_clk_i2s0_sync,
+ tegra_clk_i2s1,
+ tegra_clk_i2s1_sync,
+ tegra_clk_i2s2,
+ tegra_clk_i2s2_sync,
+ tegra_clk_i2s3,
+ tegra_clk_i2s3_sync,
+ tegra_clk_i2s4,
+ tegra_clk_i2s4_sync,
+ tegra_clk_isp,
+ tegra_clk_isp_8,
+ tegra_clk_isp_9,
+ tegra_clk_ispb,
+ tegra_clk_kbc,
+ tegra_clk_kfuse,
+ tegra_clk_la,
+ tegra_clk_maud,
+ tegra_clk_mipi,
+ tegra_clk_mipibif,
+ tegra_clk_mipi_cal,
+ tegra_clk_mpe,
+ tegra_clk_mselect,
+ tegra_clk_msenc,
+ tegra_clk_ndflash,
+ tegra_clk_ndflash_8,
+ tegra_clk_ndspeed,
+ tegra_clk_ndspeed_8,
+ tegra_clk_nor,
+ tegra_clk_nvdec,
+ tegra_clk_nvenc,
+ tegra_clk_nvjpg,
+ tegra_clk_owr,
+ tegra_clk_owr_8,
+ tegra_clk_pcie,
+ tegra_clk_pclk,
+ tegra_clk_pll_a,
+ tegra_clk_pll_a_out0,
+ tegra_clk_pll_a1,
+ tegra_clk_pll_c,
+ tegra_clk_pll_c2,
+ tegra_clk_pll_c3,
+ tegra_clk_pll_c4,
+ tegra_clk_pll_c4_out0,
+ tegra_clk_pll_c4_out1,
+ tegra_clk_pll_c4_out2,
+ tegra_clk_pll_c4_out3,
+ tegra_clk_pll_c_out1,
+ tegra_clk_pll_d,
+ tegra_clk_pll_d2,
+ tegra_clk_pll_d2_out0,
+ tegra_clk_pll_d_out0,
+ tegra_clk_pll_dp,
+ tegra_clk_pll_e_out0,
+ tegra_clk_pll_g_ref,
+ tegra_clk_pll_m,
+ tegra_clk_pll_m_out1,
+ tegra_clk_pll_mb,
+ tegra_clk_pll_p,
+ tegra_clk_pll_p_out1,
+ tegra_clk_pll_p_out2,
+ tegra_clk_pll_p_out2_int,
+ tegra_clk_pll_p_out3,
+ tegra_clk_pll_p_out4,
+ tegra_clk_pll_p_out4_cpu,
+ tegra_clk_pll_p_out5,
+ tegra_clk_pll_p_out_hsio,
+ tegra_clk_pll_p_out_xusb,
+ tegra_clk_pll_p_out_cpu,
+ tegra_clk_pll_p_out_adsp,
+ tegra_clk_pll_ref,
+ tegra_clk_pll_re_out,
+ tegra_clk_pll_re_vco,
+ tegra_clk_pll_u,
+ tegra_clk_pll_u_out,
+ tegra_clk_pll_u_out1,
+ tegra_clk_pll_u_out2,
+ tegra_clk_pll_u_12m,
+ tegra_clk_pll_u_480m,
+ tegra_clk_pll_u_48m,
+ tegra_clk_pll_u_60m,
+ tegra_clk_pll_x,
+ tegra_clk_pll_x_out0,
+ tegra_clk_pwm,
+ tegra_clk_qspi,
+ tegra_clk_rtc,
+ tegra_clk_sata,
+ tegra_clk_sata_8,
+ tegra_clk_sata_cold,
+ tegra_clk_sata_oob,
+ tegra_clk_sata_oob_8,
+ tegra_clk_sbc1,
+ tegra_clk_sbc1_8,
+ tegra_clk_sbc1_9,
+ tegra_clk_sbc2,
+ tegra_clk_sbc2_8,
+ tegra_clk_sbc2_9,
+ tegra_clk_sbc3,
+ tegra_clk_sbc3_8,
+ tegra_clk_sbc3_9,
+ tegra_clk_sbc4,
+ tegra_clk_sbc4_8,
+ tegra_clk_sbc4_9,
+ tegra_clk_sbc5,
+ tegra_clk_sbc5_8,
+ tegra_clk_sbc6,
+ tegra_clk_sbc6_8,
+ tegra_clk_sclk,
+ tegra_clk_sdmmc_legacy,
+ tegra_clk_sdmmc1,
+ tegra_clk_sdmmc1_8,
+ tegra_clk_sdmmc1_9,
+ tegra_clk_sdmmc2,
+ tegra_clk_sdmmc2_8,
+ tegra_clk_sdmmc3,
+ tegra_clk_sdmmc3_8,
+ tegra_clk_sdmmc3_9,
+ tegra_clk_sdmmc4,
+ tegra_clk_sdmmc4_8,
+ tegra_clk_se,
+ tegra_clk_se_10,
+ tegra_clk_soc_therm,
+ tegra_clk_soc_therm_8,
+ tegra_clk_sor0,
+ tegra_clk_sor0_out,
+ tegra_clk_sor1,
+ tegra_clk_sor1_out,
+ tegra_clk_spdif,
+ tegra_clk_spdif_2x,
+ tegra_clk_spdif_in,
+ tegra_clk_spdif_in_8,
+ tegra_clk_spdif_in_sync,
+ tegra_clk_spdif_mux,
+ tegra_clk_spdif_out,
+ tegra_clk_timer,
+ tegra_clk_trace,
+ tegra_clk_tsec,
+ tegra_clk_tsec_8,
+ tegra_clk_tsecb,
+ tegra_clk_tsensor,
+ tegra_clk_tvdac,
+ tegra_clk_tvo,
+ tegra_clk_uarta,
+ tegra_clk_uarta_8,
+ tegra_clk_uartb,
+ tegra_clk_uartb_8,
+ tegra_clk_uartc,
+ tegra_clk_uartc_8,
+ tegra_clk_uartd,
+ tegra_clk_uartd_8,
+ tegra_clk_uarte,
+ tegra_clk_uarte_8,
+ tegra_clk_uartape,
+ tegra_clk_usb2,
+ tegra_clk_usb2_hsic_trk,
+ tegra_clk_usb2_trk,
+ tegra_clk_usb3,
+ tegra_clk_usbd,
+ tegra_clk_vcp,
+ tegra_clk_vde,
+ tegra_clk_vde_8,
+ tegra_clk_vfir,
+ tegra_clk_vi,
+ tegra_clk_vi_8,
+ tegra_clk_vi_9,
+ tegra_clk_vi_10,
+ tegra_clk_vi_i2c,
+ tegra_clk_vic03,
+ tegra_clk_vic03_8,
+ tegra_clk_vim2_clk,
+ tegra_clk_vimclk_sync,
+ tegra_clk_vi_sensor,
+ tegra_clk_vi_sensor_8,
+ tegra_clk_vi_sensor_9,
+ tegra_clk_vi_sensor2,
+ tegra_clk_vi_sensor2_8,
+ tegra_clk_xusb_dev,
+ tegra_clk_xusb_dev_src,
+ tegra_clk_xusb_dev_src_8,
+ tegra_clk_xusb_falcon_src,
+ tegra_clk_xusb_falcon_src_8,
+ tegra_clk_xusb_fs_src,
+ tegra_clk_xusb_gate,
+ tegra_clk_xusb_host,
+ tegra_clk_xusb_host_src,
+ tegra_clk_xusb_host_src_8,
+ tegra_clk_xusb_hs_src,
+ tegra_clk_xusb_hs_src_4,
+ tegra_clk_xusb_ss,
+ tegra_clk_xusb_ss_src,
+ tegra_clk_xusb_ss_src_8,
+ tegra_clk_xusb_ss_div2,
+ tegra_clk_xusb_ssp_src,
+ tegra_clk_sclk_mux,
+ tegra_clk_sor_safe,
+ tegra_clk_cec,
+ tegra_clk_ispa,
+ tegra_clk_dmic1,
+ tegra_clk_dmic2,
+ tegra_clk_dmic3,
+ tegra_clk_dmic1_sync_clk,
+ tegra_clk_dmic2_sync_clk,
+ tegra_clk_dmic3_sync_clk,
+ tegra_clk_dmic1_sync_clk_mux,
+ tegra_clk_dmic2_sync_clk_mux,
+ tegra_clk_dmic3_sync_clk_mux,
+ tegra_clk_iqc1,
+ tegra_clk_iqc2,
+ tegra_clk_pll_a_out_adsp,
+ tegra_clk_pll_a_out0_out_adsp,
+ tegra_clk_adsp,
+ tegra_clk_adsp_neon,
+ tegra_clk_max,
+};
+
+#endif /* _TEGRA_CLK_ID_H */
diff --git a/drivers/clk/tegra/clk-periph-fixed.c b/drivers/clk/tegra/clk-periph-fixed.c
new file mode 100644
index 000000000..bb33e32ca
--- /dev/null
+++ b/drivers/clk/tegra/clk-periph-fixed.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+
+#include "clk.h"
+
+static inline struct tegra_clk_periph_fixed *
+to_tegra_clk_periph_fixed(struct clk_hw *hw)
+{
+ return container_of(hw, struct tegra_clk_periph_fixed, hw);
+}
+
+static int tegra_clk_periph_fixed_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+ u32 mask = 1 << (fixed->num % 32), value;
+
+ value = readl(fixed->base + fixed->regs->enb_reg);
+ if (value & mask) {
+ value = readl(fixed->base + fixed->regs->rst_reg);
+ if ((value & mask) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tegra_clk_periph_fixed_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+ u32 mask = 1 << (fixed->num % 32);
+
+ writel(mask, fixed->base + fixed->regs->enb_set_reg);
+
+ return 0;
+}
+
+static void tegra_clk_periph_fixed_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+ u32 mask = 1 << (fixed->num % 32);
+
+ writel(mask, fixed->base + fixed->regs->enb_clr_reg);
+}
+
+static unsigned long
+tegra_clk_periph_fixed_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_periph_fixed *fixed = to_tegra_clk_periph_fixed(hw);
+ unsigned long long rate;
+
+ rate = (unsigned long long)parent_rate * fixed->mul;
+ do_div(rate, fixed->div);
+
+ return (unsigned long)rate;
+}
+
+static const struct clk_ops tegra_clk_periph_fixed_ops = {
+ .is_enabled = tegra_clk_periph_fixed_is_enabled,
+ .enable = tegra_clk_periph_fixed_enable,
+ .disable = tegra_clk_periph_fixed_disable,
+ .recalc_rate = tegra_clk_periph_fixed_recalc_rate,
+};
+
+struct clk *tegra_clk_register_periph_fixed(const char *name,
+ const char *parent,
+ unsigned long flags,
+ void __iomem *base,
+ unsigned int mul,
+ unsigned int div,
+ unsigned int num)
+{
+ const struct tegra_clk_periph_regs *regs;
+ struct tegra_clk_periph_fixed *fixed;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ regs = get_reg_bank(num);
+ if (!regs)
+ return ERR_PTR(-EINVAL);
+
+ fixed = kzalloc_obj(*fixed);
+ if (!fixed)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = parent ? &parent : NULL;
+ init.num_parents = parent ? 1 : 0;
+ init.ops = &tegra_clk_periph_fixed_ops;
+
+ fixed->base = base;
+ fixed->regs = regs;
+ fixed->mul = mul;
+ fixed->div = div;
+ fixed->num = num;
+
+ fixed->hw.init = &init;
+
+ clk = clk_register(NULL, &fixed->hw);
+ if (IS_ERR(clk))
+ kfree(fixed);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c
new file mode 100644
index 000000000..fa39d6487
--- /dev/null
+++ b/drivers/clk/tegra/clk-periph-gate.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "clk.h"
+
+static DEFINE_SPINLOCK(periph_ref_lock);
+
+/* Macros to assist peripheral gate clock */
+#define read_enb(gate) \
+ readl_relaxed(gate->clk_base + (gate->regs->enb_reg))
+#define write_enb_set(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->enb_set_reg))
+#define write_enb_clr(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->enb_clr_reg))
+
+#define read_rst(gate) \
+ readl_relaxed(gate->clk_base + (gate->regs->rst_reg))
+#define write_rst_clr(val, gate) \
+ writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
+
+#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))
+
+#define LVL2_CLK_GATE_OVRE 0x554
+
+/* Peripheral gate clock ops */
+static int clk_periph_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ int state = 1;
+
+ if (!(read_enb(gate) & periph_clk_to_bit(gate)))
+ state = 0;
+
+ if (!(gate->flags & TEGRA_PERIPH_NO_RESET))
+ if (read_rst(gate) & periph_clk_to_bit(gate))
+ state = 0;
+
+ return state;
+}
+
+static void clk_periph_enable_locked(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+
+ write_enb_set(periph_clk_to_bit(gate), gate);
+ udelay(2);
+
+ if (gate->flags & TEGRA_PERIPH_WAR_1005168) {
+ writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
+ udelay(1);
+ writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
+ }
+}
+
+static void clk_periph_disable_locked(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+
+ /*
+ * If peripheral is in the APB bus then read the APB bus to
+ * flush the write operation in apb bus. This will avoid the
+ * peripheral access after disabling clock
+ */
+ if (gate->flags & TEGRA_PERIPH_ON_APB)
+ tegra_read_chipid();
+
+ write_enb_clr(periph_clk_to_bit(gate), gate);
+}
+
+static int clk_periph_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&periph_ref_lock, flags);
+
+ if (!gate->enable_refcnt[gate->clk_num]++)
+ clk_periph_enable_locked(hw);
+
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+
+ return 0;
+}
+
+static void clk_periph_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&periph_ref_lock, flags);
+
+ WARN_ON(!gate->enable_refcnt[gate->clk_num]);
+
+ if (--gate->enable_refcnt[gate->clk_num] == 0)
+ clk_periph_disable_locked(hw);
+
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+}
+
+static void clk_periph_disable_unused(struct clk_hw *hw)
+{
+ struct tegra_clk_periph_gate *gate = to_clk_periph_gate(hw);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&periph_ref_lock, flags);
+
+ /*
+ * Some clocks are duplicated and some of them are marked as critical,
+ * like fuse and fuse_burn for example, thus the enable_refcnt will
+ * be non-zero here if the "unused" duplicate is disabled by CCF.
+ */
+ if (!gate->enable_refcnt[gate->clk_num])
+ clk_periph_disable_locked(hw);
+
+ spin_unlock_irqrestore(&periph_ref_lock, flags);
+}
+
+const struct clk_ops tegra_clk_periph_gate_ops = {
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+ .disable_unused = clk_periph_disable_unused,
+};
+
+struct clk *tegra_clk_register_periph_gate(const char *name,
+ const char *parent_name, u8 gate_flags, void __iomem *clk_base,
+ unsigned long flags, int clk_num, int *enable_refcnt)
+{
+ struct tegra_clk_periph_gate *gate;
+ struct clk *clk;
+ struct clk_init_data init;
+ const struct tegra_clk_periph_regs *pregs;
+
+ pregs = get_reg_bank(clk_num);
+ if (!pregs)
+ return ERR_PTR(-EINVAL);
+
+ gate = kzalloc_obj(*gate);
+ if (!gate) {
+ pr_err("%s: could not allocate periph gate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.ops = &tegra_clk_periph_gate_ops;
+
+ gate->magic = TEGRA_CLK_PERIPH_GATE_MAGIC;
+ gate->clk_base = clk_base;
+ gate->clk_num = clk_num;
+ gate->flags = gate_flags;
+ gate->enable_refcnt = enable_refcnt;
+ gate->regs = pregs;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ gate->hw.init = &init;
+
+ clk = clk_register(NULL, &gate->hw);
+ if (IS_ERR(clk))
+ kfree(gate);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
new file mode 100644
index 000000000..6ebeaa7cb
--- /dev/null
+++ b/drivers/clk/tegra/clk-periph.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "clk.h"
+
+static u8 clk_periph_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *mux_ops = periph->mux_ops;
+ struct clk_hw *mux_hw = &periph->mux.hw;
+
+ __clk_hw_set_clk(mux_hw, hw);
+
+ return mux_ops->get_parent(mux_hw);
+}
+
+static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *mux_ops = periph->mux_ops;
+ struct clk_hw *mux_hw = &periph->mux.hw;
+
+ __clk_hw_set_clk(mux_hw, hw);
+
+ return mux_ops->set_parent(mux_hw, index);
+}
+
+static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return div_ops->recalc_rate(div_hw, parent_rate);
+}
+
+static int clk_periph_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return div_ops->determine_rate(div_hw, req);
+}
+
+static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return div_ops->set_rate(div_hw, rate, parent_rate);
+}
+
+static int clk_periph_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ __clk_hw_set_clk(gate_hw, hw);
+
+ return gate_ops->is_enabled(gate_hw);
+}
+
+static int clk_periph_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ __clk_hw_set_clk(gate_hw, hw);
+
+ return gate_ops->enable(gate_hw);
+}
+
+static void clk_periph_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ gate_ops->disable(gate_hw);
+}
+
+static void clk_periph_disable_unused(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *gate_ops = periph->gate_ops;
+ struct clk_hw *gate_hw = &periph->gate.hw;
+
+ gate_ops->disable_unused(gate_hw);
+}
+
+static void clk_periph_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+ int parent_id;
+
+ parent_id = clk_hw_get_parent_index(hw);
+ if (WARN_ON(parent_id < 0))
+ return;
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+ div_ops->restore_context(div_hw);
+
+ clk_periph_set_parent(hw, parent_id);
+}
+
+static const struct clk_ops tegra_clk_periph_ops = {
+ .get_parent = clk_periph_get_parent,
+ .set_parent = clk_periph_set_parent,
+ .recalc_rate = clk_periph_recalc_rate,
+ .determine_rate = clk_periph_determine_rate,
+ .set_rate = clk_periph_set_rate,
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+ .disable_unused = clk_periph_disable_unused,
+ .restore_context = clk_periph_restore_context,
+};
+
+static const struct clk_ops tegra_clk_periph_nodiv_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .get_parent = clk_periph_get_parent,
+ .set_parent = clk_periph_set_parent,
+ .is_enabled = clk_periph_is_enabled,
+ .enable = clk_periph_enable,
+ .disable = clk_periph_disable,
+ .disable_unused = clk_periph_disable_unused,
+ .restore_context = clk_periph_restore_context,
+};
+
+static const struct clk_ops tegra_clk_periph_no_gate_ops = {
+ .get_parent = clk_periph_get_parent,
+ .set_parent = clk_periph_set_parent,
+ .recalc_rate = clk_periph_recalc_rate,
+ .determine_rate = clk_periph_determine_rate,
+ .set_rate = clk_periph_set_rate,
+ .restore_context = clk_periph_restore_context,
+};
+
+static struct clk *_tegra_clk_register_periph(const char *name,
+ const char * const *parent_names, int num_parents,
+ struct tegra_clk_periph *periph,
+ void __iomem *clk_base, u32 offset,
+ unsigned long flags)
+{
+ struct clk *clk;
+ struct clk_init_data init;
+ const struct tegra_clk_periph_regs *bank;
+ bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
+
+ if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
+ flags |= CLK_SET_RATE_PARENT;
+ init.ops = &tegra_clk_periph_nodiv_ops;
+ } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
+ init.ops = &tegra_clk_periph_no_gate_ops;
+ else
+ init.ops = &tegra_clk_periph_ops;
+
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ bank = get_reg_bank(periph->gate.clk_num);
+ if (!bank)
+ return ERR_PTR(-EINVAL);
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ periph->hw.init = &init;
+ periph->magic = TEGRA_CLK_PERIPH_MAGIC;
+ periph->mux.reg = clk_base + offset;
+ periph->divider.reg = div ? (clk_base + offset) : NULL;
+ periph->gate.clk_base = clk_base;
+ periph->gate.regs = bank;
+ periph->gate.enable_refcnt = periph_clk_enb_refcnt;
+
+ clk = clk_register(NULL, &periph->hw);
+ if (IS_ERR(clk))
+ return clk;
+
+ periph->mux.hw.clk = clk;
+ periph->divider.hw.clk = div ? clk : NULL;
+ periph->gate.hw.clk = clk;
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_periph(const char *name,
+ const char * const *parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset, unsigned long flags)
+{
+ return _tegra_clk_register_periph(name, parent_names, num_parents,
+ periph, clk_base, offset, flags);
+}
+
+struct clk *tegra_clk_register_periph_nodiv(const char *name,
+ const char * const *parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset)
+{
+ periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
+ return _tegra_clk_register_periph(name, parent_names, num_parents,
+ periph, clk_base, offset, CLK_SET_RATE_PARENT);
+}
+
+struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
+ struct tegra_periph_init_data *init)
+{
+ return _tegra_clk_register_periph(init->name, init->p.parent_names,
+ init->num_parents, &init->periph,
+ clk_base, init->offset, init->flags);
+}
diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c
new file mode 100644
index 000000000..2299545fc
--- /dev/null
+++ b/drivers/clk/tegra/clk-pll-out.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define pll_out_enb(p) (BIT(p->enb_bit_idx))
+#define pll_out_rst(p) (BIT(p->rst_bit_idx))
+
+static int clk_pll_out_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ u32 val = readl_relaxed(pll_out->reg);
+ int state;
+
+ state = (val & pll_out_enb(pll_out)) ? 1 : 0;
+ if (!(val & (pll_out_rst(pll_out))))
+ state = 0;
+ return state;
+}
+
+static int clk_pll_out_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll_out->lock)
+ spin_lock_irqsave(pll_out->lock, flags);
+
+ val = readl_relaxed(pll_out->reg);
+
+ val |= (pll_out_enb(pll_out) | pll_out_rst(pll_out));
+
+ writel_relaxed(val, pll_out->reg);
+ udelay(2);
+
+ if (pll_out->lock)
+ spin_unlock_irqrestore(pll_out->lock, flags);
+
+ return 0;
+}
+
+static void clk_pll_out_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll_out *pll_out = to_clk_pll_out(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll_out->lock)
+ spin_lock_irqsave(pll_out->lock, flags);
+
+ val = readl_relaxed(pll_out->reg);
+
+ val &= ~(pll_out_enb(pll_out) | pll_out_rst(pll_out));
+
+ writel_relaxed(val, pll_out->reg);
+ udelay(2);
+
+ if (pll_out->lock)
+ spin_unlock_irqrestore(pll_out->lock, flags);
+}
+
+static void tegra_clk_pll_out_restore_context(struct clk_hw *hw)
+{
+ if (!__clk_get_enable_count(hw->clk))
+ clk_pll_out_disable(hw);
+ else
+ clk_pll_out_enable(hw);
+}
+
+const struct clk_ops tegra_clk_pll_out_ops = {
+ .is_enabled = clk_pll_out_is_enabled,
+ .enable = clk_pll_out_enable,
+ .disable = clk_pll_out_disable,
+ .restore_context = tegra_clk_pll_out_restore_context,
+};
+
+struct clk *tegra_clk_register_pll_out(const char *name,
+ const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
+ u8 rst_bit_idx, unsigned long flags, u8 pll_out_flags,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll_out *pll_out;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll_out = kzalloc_obj(*pll_out);
+ if (!pll_out)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &tegra_clk_pll_out_ops;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ init.flags = flags;
+
+ pll_out->reg = reg;
+ pll_out->enb_bit_idx = enb_bit_idx;
+ pll_out->rst_bit_idx = rst_bit_idx;
+ pll_out->flags = pll_out_flags;
+ pll_out->lock = lock;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ pll_out->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_out->hw);
+ if (IS_ERR(clk))
+ kfree(pll_out);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
new file mode 100644
index 000000000..d86003b6d
--- /dev/null
+++ b/drivers/clk/tegra/clk-pll.c
@@ -0,0 +1,2740 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define PLL_BASE_BYPASS BIT(31)
+#define PLL_BASE_ENABLE BIT(30)
+#define PLL_BASE_REF_ENABLE BIT(29)
+#define PLL_BASE_OVERRIDE BIT(28)
+
+#define PLL_BASE_DIVP_SHIFT 20
+#define PLL_BASE_DIVP_WIDTH 3
+#define PLL_BASE_DIVN_SHIFT 8
+#define PLL_BASE_DIVN_WIDTH 10
+#define PLL_BASE_DIVM_SHIFT 0
+#define PLL_BASE_DIVM_WIDTH 5
+#define PLLU_POST_DIVP_MASK 0x1
+
+#define PLL_MISC_DCCON_SHIFT 20
+#define PLL_MISC_CPCON_SHIFT 8
+#define PLL_MISC_CPCON_WIDTH 4
+#define PLL_MISC_CPCON_MASK ((1 << PLL_MISC_CPCON_WIDTH) - 1)
+#define PLL_MISC_LFCON_SHIFT 4
+#define PLL_MISC_LFCON_WIDTH 4
+#define PLL_MISC_LFCON_MASK ((1 << PLL_MISC_LFCON_WIDTH) - 1)
+#define PLL_MISC_VCOCON_SHIFT 0
+#define PLL_MISC_VCOCON_WIDTH 4
+#define PLL_MISC_VCOCON_MASK ((1 << PLL_MISC_VCOCON_WIDTH) - 1)
+
+#define OUT_OF_TABLE_CPCON 8
+
+#define PMC_PLLP_WB0_OVERRIDE 0xf8
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE BIT(12)
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE BIT(11)
+
+#define PLL_POST_LOCK_DELAY 50
+
+#define PLLDU_LFCON_SET_DIVN 600
+
+#define PLLE_BASE_DIVCML_SHIFT 24
+#define PLLE_BASE_DIVCML_MASK 0xf
+#define PLLE_BASE_DIVP_SHIFT 16
+#define PLLE_BASE_DIVP_WIDTH 6
+#define PLLE_BASE_DIVN_SHIFT 8
+#define PLLE_BASE_DIVN_WIDTH 8
+#define PLLE_BASE_DIVM_SHIFT 0
+#define PLLE_BASE_DIVM_WIDTH 8
+#define PLLE_BASE_ENABLE BIT(31)
+
+#define PLLE_MISC_SETUP_BASE_SHIFT 16
+#define PLLE_MISC_SETUP_BASE_MASK (0xffff << PLLE_MISC_SETUP_BASE_SHIFT)
+#define PLLE_MISC_LOCK_ENABLE BIT(9)
+#define PLLE_MISC_READY BIT(15)
+#define PLLE_MISC_SETUP_EX_SHIFT 2
+#define PLLE_MISC_SETUP_EX_MASK (3 << PLLE_MISC_SETUP_EX_SHIFT)
+#define PLLE_MISC_SETUP_MASK (PLLE_MISC_SETUP_BASE_MASK | \
+ PLLE_MISC_SETUP_EX_MASK)
+#define PLLE_MISC_SETUP_VALUE (7 << PLLE_MISC_SETUP_BASE_SHIFT)
+
+#define PLLE_SS_CTRL 0x68
+#define PLLE_SS_CNTL_BYPASS_SS BIT(10)
+#define PLLE_SS_CNTL_INTERP_RESET BIT(11)
+#define PLLE_SS_CNTL_SSC_BYP BIT(12)
+#define PLLE_SS_CNTL_CENTER BIT(14)
+#define PLLE_SS_CNTL_INVERT BIT(15)
+#define PLLE_SS_DISABLE (PLLE_SS_CNTL_BYPASS_SS | PLLE_SS_CNTL_INTERP_RESET |\
+ PLLE_SS_CNTL_SSC_BYP)
+#define PLLE_SS_MAX_MASK 0x1ff
+#define PLLE_SS_MAX_VAL_TEGRA114 0x25
+#define PLLE_SS_MAX_VAL_TEGRA210 0x21
+#define PLLE_SS_INC_MASK (0xff << 16)
+#define PLLE_SS_INC_VAL (0x1 << 16)
+#define PLLE_SS_INCINTRV_MASK (0x3f << 24)
+#define PLLE_SS_INCINTRV_VAL_TEGRA114 (0x20 << 24)
+#define PLLE_SS_INCINTRV_VAL_TEGRA210 (0x23 << 24)
+#define PLLE_SS_COEFFICIENTS_MASK \
+ (PLLE_SS_MAX_MASK | PLLE_SS_INC_MASK | PLLE_SS_INCINTRV_MASK)
+#define PLLE_SS_COEFFICIENTS_VAL_TEGRA114 \
+ (PLLE_SS_MAX_VAL_TEGRA114 | PLLE_SS_INC_VAL |\
+ PLLE_SS_INCINTRV_VAL_TEGRA114)
+#define PLLE_SS_COEFFICIENTS_VAL_TEGRA210 \
+ (PLLE_SS_MAX_VAL_TEGRA210 | PLLE_SS_INC_VAL |\
+ PLLE_SS_INCINTRV_VAL_TEGRA210)
+
+#define PLLE_AUX_PLLP_SEL BIT(2)
+#define PLLE_AUX_USE_LOCKDET BIT(3)
+#define PLLE_AUX_ENABLE_SWCTL BIT(4)
+#define PLLE_AUX_SS_SWCTL BIT(6)
+#define PLLE_AUX_SEQ_ENABLE BIT(24)
+#define PLLE_AUX_SEQ_START_STATE BIT(25)
+#define PLLE_AUX_PLLRE_SEL BIT(28)
+#define PLLE_AUX_SS_SEQ_INCLUDE BIT(31)
+
+#define XUSBIO_PLL_CFG0 0x51c
+#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
+#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6)
+#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24)
+#define XUSBIO_PLL_CFG0_SEQ_START_STATE BIT(25)
+
+#define SATA_PLL_CFG0 0x490
+#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
+#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2)
+#define SATA_PLL_CFG0_SEQ_ENABLE BIT(24)
+#define SATA_PLL_CFG0_SEQ_START_STATE BIT(25)
+
+#define PLLE_MISC_PLLE_PTS BIT(8)
+#define PLLE_MISC_IDDQ_SW_VALUE BIT(13)
+#define PLLE_MISC_IDDQ_SW_CTRL BIT(14)
+#define PLLE_MISC_VREG_BG_CTRL_SHIFT 4
+#define PLLE_MISC_VREG_BG_CTRL_MASK (3 << PLLE_MISC_VREG_BG_CTRL_SHIFT)
+#define PLLE_MISC_VREG_CTRL_SHIFT 2
+#define PLLE_MISC_VREG_CTRL_MASK (2 << PLLE_MISC_VREG_CTRL_SHIFT)
+
+#define PLLCX_MISC_STROBE BIT(31)
+#define PLLCX_MISC_RESET BIT(30)
+#define PLLCX_MISC_SDM_DIV_SHIFT 28
+#define PLLCX_MISC_SDM_DIV_MASK (0x3 << PLLCX_MISC_SDM_DIV_SHIFT)
+#define PLLCX_MISC_FILT_DIV_SHIFT 26
+#define PLLCX_MISC_FILT_DIV_MASK (0x3 << PLLCX_MISC_FILT_DIV_SHIFT)
+#define PLLCX_MISC_ALPHA_SHIFT 18
+#define PLLCX_MISC_DIV_LOW_RANGE \
+ ((0x1 << PLLCX_MISC_SDM_DIV_SHIFT) | \
+ (0x1 << PLLCX_MISC_FILT_DIV_SHIFT))
+#define PLLCX_MISC_DIV_HIGH_RANGE \
+ ((0x2 << PLLCX_MISC_SDM_DIV_SHIFT) | \
+ (0x2 << PLLCX_MISC_FILT_DIV_SHIFT))
+#define PLLCX_MISC_COEF_LOW_RANGE \
+ ((0x14 << PLLCX_MISC_KA_SHIFT) | (0x38 << PLLCX_MISC_KB_SHIFT))
+#define PLLCX_MISC_KA_SHIFT 2
+#define PLLCX_MISC_KB_SHIFT 9
+#define PLLCX_MISC_DEFAULT (PLLCX_MISC_COEF_LOW_RANGE | \
+ (0x19 << PLLCX_MISC_ALPHA_SHIFT) | \
+ PLLCX_MISC_DIV_LOW_RANGE | \
+ PLLCX_MISC_RESET)
+#define PLLCX_MISC1_DEFAULT 0x000d2308
+#define PLLCX_MISC2_DEFAULT 0x30211200
+#define PLLCX_MISC3_DEFAULT 0x200
+
+#define PMC_SATA_PWRGT 0x1ac
+#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
+#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4)
+
+#define PLLSS_MISC_KCP 0
+#define PLLSS_MISC_KVCO 0
+#define PLLSS_MISC_SETUP 0
+#define PLLSS_EN_SDM 0
+#define PLLSS_EN_SSC 0
+#define PLLSS_EN_DITHER2 0
+#define PLLSS_EN_DITHER 1
+#define PLLSS_SDM_RESET 0
+#define PLLSS_CLAMP 0
+#define PLLSS_SDM_SSC_MAX 0
+#define PLLSS_SDM_SSC_MIN 0
+#define PLLSS_SDM_SSC_STEP 0
+#define PLLSS_SDM_DIN 0
+#define PLLSS_MISC_DEFAULT ((PLLSS_MISC_KCP << 25) | \
+ (PLLSS_MISC_KVCO << 24) | \
+ PLLSS_MISC_SETUP)
+#define PLLSS_CFG_DEFAULT ((PLLSS_EN_SDM << 31) | \
+ (PLLSS_EN_SSC << 30) | \
+ (PLLSS_EN_DITHER2 << 29) | \
+ (PLLSS_EN_DITHER << 28) | \
+ (PLLSS_SDM_RESET) << 27 | \
+ (PLLSS_CLAMP << 22))
+#define PLLSS_CTRL1_DEFAULT \
+ ((PLLSS_SDM_SSC_MAX << 16) | PLLSS_SDM_SSC_MIN)
+#define PLLSS_CTRL2_DEFAULT \
+ ((PLLSS_SDM_SSC_STEP << 16) | PLLSS_SDM_DIN)
+#define PLLSS_LOCK_OVERRIDE BIT(24)
+#define PLLSS_REF_SRC_SEL_SHIFT 25
+#define PLLSS_REF_SRC_SEL_MASK (3 << PLLSS_REF_SRC_SEL_SHIFT)
+
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17)
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP BIT(1)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP BIT(3)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERUP BIT(5)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN BIT(24)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP BIT(25)
+#define UTMIP_PLL_CFG2_PHY_XTAL_CLOCKEN BIT(30)
+
+#define UTMIPLL_HW_PWRDN_CFG0 0x52c
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1)
+#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5)
+#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25)
+
+#define PLLU_HW_PWRDN_CFG0 0x530
+#define PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL BIT(0)
+#define PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define PLLU_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
+#define PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT BIT(7)
+#define PLLU_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
+#define PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE BIT(28)
+
+#define XUSB_PLL_CFG0 0x534
+#define XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY 0x3ff
+#define XUSB_PLL_CFG0_PLLU_LOCK_DLY (0x3ff << 14)
+
+#define PLLU_BASE_CLKENABLE_USB BIT(21)
+#define PLLU_BASE_OVERRIDE BIT(24)
+
+#define pll_readl(offset, p) readl_relaxed(p->clk_base + offset)
+#define pll_readl_base(p) pll_readl(p->params->base_reg, p)
+#define pll_readl_misc(p) pll_readl(p->params->misc_reg, p)
+#define pll_override_readl(offset, p) readl_relaxed(p->pmc + offset)
+#define pll_readl_sdm_din(p) pll_readl(p->params->sdm_din_reg, p)
+#define pll_readl_sdm_ctrl(p) pll_readl(p->params->sdm_ctrl_reg, p)
+
+#define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset)
+#define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p)
+#define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p)
+#define pll_override_writel(val, offset, p) writel(val, p->pmc + offset)
+#define pll_writel_sdm_din(val, p) pll_writel(val, p->params->sdm_din_reg, p)
+#define pll_writel_sdm_ctrl(val, p) pll_writel(val, p->params->sdm_ctrl_reg, p)
+
+#define mask(w) ((1 << (w)) - 1)
+#define divm_mask(p) mask(p->params->div_nmp->divm_width)
+#define divn_mask(p) mask(p->params->div_nmp->divn_width)
+#define divp_mask(p) (p->params->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK :\
+ mask(p->params->div_nmp->divp_width))
+#define sdm_din_mask(p) p->params->sdm_din_mask
+#define sdm_en_mask(p) p->params->sdm_ctrl_en_mask
+
+#define divm_shift(p) (p)->params->div_nmp->divm_shift
+#define divn_shift(p) (p)->params->div_nmp->divn_shift
+#define divp_shift(p) (p)->params->div_nmp->divp_shift
+
+#define divm_mask_shifted(p) (divm_mask(p) << divm_shift(p))
+#define divn_mask_shifted(p) (divn_mask(p) << divn_shift(p))
+#define divp_mask_shifted(p) (divp_mask(p) << divp_shift(p))
+
+#define divm_max(p) (divm_mask(p))
+#define divn_max(p) (divn_mask(p))
+#define divp_max(p) (1 << (divp_mask(p)))
+
+#define sdin_din_to_data(din) ((u16)((din) ? : 0xFFFFU))
+#define sdin_data_to_din(dat) (((dat) == 0xFFFFU) ? 0 : (s16)dat)
+
+static struct div_nmp default_nmp = {
+ .divn_shift = PLL_BASE_DIVN_SHIFT,
+ .divn_width = PLL_BASE_DIVN_WIDTH,
+ .divm_shift = PLL_BASE_DIVM_SHIFT,
+ .divm_width = PLL_BASE_DIVM_WIDTH,
+ .divp_shift = PLL_BASE_DIVP_SHIFT,
+ .divp_width = PLL_BASE_DIVP_WIDTH,
+};
+
+static void clk_pll_enable_lock(struct tegra_clk_pll *pll)
+{
+ u32 val;
+
+ if (!(pll->params->flags & TEGRA_PLL_USE_LOCK))
+ return;
+
+ if (!(pll->params->flags & TEGRA_PLL_HAS_LOCK_ENABLE))
+ return;
+
+ val = pll_readl_misc(pll);
+ val |= BIT(pll->params->lock_enable_bit_idx);
+ pll_writel_misc(val, pll);
+}
+
+static int clk_pll_wait_for_lock(struct tegra_clk_pll *pll)
+{
+ int i;
+ u32 val, lock_mask;
+ void __iomem *lock_addr;
+
+ if (!(pll->params->flags & TEGRA_PLL_USE_LOCK)) {
+ udelay(pll->params->lock_delay);
+ return 0;
+ }
+
+ lock_addr = pll->clk_base;
+ if (pll->params->flags & TEGRA_PLL_LOCK_MISC)
+ lock_addr += pll->params->misc_reg;
+ else
+ lock_addr += pll->params->base_reg;
+
+ lock_mask = pll->params->lock_mask;
+
+ for (i = 0; i < pll->params->lock_delay; i++) {
+ val = readl_relaxed(lock_addr);
+ if ((val & lock_mask) == lock_mask) {
+ udelay(PLL_POST_LOCK_DELAY);
+ return 0;
+ }
+ udelay(2); /* timeout = 2 * lock time */
+ }
+
+ pr_err("%s: Timed out waiting for pll %s lock\n", __func__,
+ clk_hw_get_name(&pll->hw));
+
+ return -1;
+}
+
+int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll)
+{
+ return clk_pll_wait_for_lock(pll);
+}
+
+static bool pllm_clk_is_gated_by_pmc(struct tegra_clk_pll *pll)
+{
+ u32 val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+
+ return (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) &&
+ !(val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ /*
+ * Power Management Controller (PMC) can override the PLLM clock
+ * settings, including the enable-state. The PLLM is enabled when
+ * PLLM's CaR state is ON and when PLLM isn't gated by PMC.
+ */
+ if ((pll->params->flags & TEGRA_PLLM) && pllm_clk_is_gated_by_pmc(pll))
+ return 0;
+
+ val = pll_readl_base(pll);
+
+ return val & PLL_BASE_ENABLE ? 1 : 0;
+}
+
+static void _clk_pll_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ if (pll->params->iddq_reg) {
+ val = pll_readl(pll->params->iddq_reg, pll);
+ val &= ~BIT(pll->params->iddq_bit_idx);
+ pll_writel(val, pll->params->iddq_reg, pll);
+ udelay(5);
+ }
+
+ if (pll->params->reset_reg) {
+ val = pll_readl(pll->params->reset_reg, pll);
+ val &= ~BIT(pll->params->reset_bit_idx);
+ pll_writel(val, pll->params->reset_reg, pll);
+ }
+
+ clk_pll_enable_lock(pll);
+
+ val = pll_readl_base(pll);
+ if (pll->params->flags & TEGRA_PLL_BYPASS)
+ val &= ~PLL_BASE_BYPASS;
+ val |= PLL_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ if (pll->params->flags & TEGRA_PLLM) {
+ val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ }
+}
+
+static void _clk_pll_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ val = pll_readl_base(pll);
+ if (pll->params->flags & TEGRA_PLL_BYPASS)
+ val &= ~PLL_BASE_BYPASS;
+ val &= ~PLL_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ if (pll->params->flags & TEGRA_PLLM) {
+ val = readl_relaxed(pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
+ writel_relaxed(val, pll->pmc + PMC_PLLP_WB0_OVERRIDE);
+ }
+
+ if (pll->params->reset_reg) {
+ val = pll_readl(pll->params->reset_reg, pll);
+ val |= BIT(pll->params->reset_bit_idx);
+ pll_writel(val, pll->params->reset_reg, pll);
+ }
+
+ if (pll->params->iddq_reg) {
+ val = pll_readl(pll->params->iddq_reg, pll);
+ val |= BIT(pll->params->iddq_bit_idx);
+ pll_writel(val, pll->params->iddq_reg, pll);
+ udelay(2);
+ }
+}
+
+static void pll_clk_start_ss(struct tegra_clk_pll *pll)
+{
+ if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) {
+ u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll);
+
+ val |= pll->params->ssc_ctrl_en_mask;
+ pll_writel(val, pll->params->ssc_ctrl_reg, pll);
+ }
+}
+
+static void pll_clk_stop_ss(struct tegra_clk_pll *pll)
+{
+ if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) {
+ u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll);
+
+ val &= ~pll->params->ssc_ctrl_en_mask;
+ pll_writel(val, pll->params->ssc_ctrl_reg, pll);
+ }
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int ret;
+
+ if (clk_pll_is_enabled(hw))
+ return 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_enable(hw);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ pll_clk_start_ss(pll);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ pll_clk_stop_ss(pll);
+
+ _clk_pll_disable(hw);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int _p_div_to_hw(struct clk_hw *hw, u8 p_div)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ const struct pdiv_map *p_tohw = pll->params->pdiv_tohw;
+
+ if (p_tohw) {
+ while (p_tohw->pdiv) {
+ if (p_div <= p_tohw->pdiv)
+ return p_tohw->hw_val;
+ p_tohw++;
+ }
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div)
+{
+ return _p_div_to_hw(&pll->hw, p_div);
+}
+
+static int _hw_to_p_div(struct clk_hw *hw, u8 p_div_hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ const struct pdiv_map *p_tohw = pll->params->pdiv_tohw;
+
+ if (p_tohw) {
+ while (p_tohw->pdiv) {
+ if (p_div_hw == p_tohw->hw_val)
+ return p_tohw->pdiv;
+ p_tohw++;
+ }
+ return -EINVAL;
+ }
+
+ return 1 << p_div_hw;
+}
+
+static int _get_table_rate(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table *sel;
+ int p;
+
+ for (sel = pll->params->freq_table; sel->input_rate != 0; sel++)
+ if (sel->input_rate == parent_rate &&
+ sel->output_rate == rate)
+ break;
+
+ if (sel->input_rate == 0)
+ return -EINVAL;
+
+ if (pll->params->pdiv_tohw) {
+ p = _p_div_to_hw(hw, sel->p);
+ if (p < 0)
+ return p;
+ } else {
+ p = ilog2(sel->p);
+ }
+
+ cfg->input_rate = sel->input_rate;
+ cfg->output_rate = sel->output_rate;
+ cfg->m = sel->m;
+ cfg->n = sel->n;
+ cfg->p = p;
+ cfg->cpcon = sel->cpcon;
+ cfg->sdm_data = sel->sdm_data;
+
+ return 0;
+}
+
+static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long cfreq;
+ u32 p_div = 0;
+ int ret;
+
+ if (!rate)
+ return -EINVAL;
+
+ switch (parent_rate) {
+ case 12000000:
+ case 26000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
+ break;
+ case 13000000:
+ cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
+ break;
+ case 16800000:
+ case 19200000:
+ cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
+ break;
+ case 9600000:
+ case 28800000:
+ /*
+ * PLL_P_OUT1 rate is not listed in PLLA table
+ */
+ cfreq = parent_rate / (parent_rate / 1000000);
+ break;
+ default:
+ pr_err("%s Unexpected reference rate %lu\n",
+ __func__, parent_rate);
+ BUG();
+ }
+
+ /* Raise VCO to guarantee 0.5% accuracy */
+ for (cfg->output_rate = rate; cfg->output_rate < 200 * cfreq;
+ cfg->output_rate <<= 1)
+ p_div++;
+
+ cfg->m = parent_rate / cfreq;
+ cfg->n = cfg->output_rate / cfreq;
+ cfg->cpcon = OUT_OF_TABLE_CPCON;
+
+ if (cfg->m == 0 || cfg->m > divm_max(pll) ||
+ cfg->n > divn_max(pll) || (1 << p_div) > divp_max(pll) ||
+ cfg->output_rate > pll->params->vco_max) {
+ return -EINVAL;
+ }
+
+ cfg->output_rate = cfg->n * DIV_ROUND_UP(parent_rate, cfg->m);
+ cfg->output_rate >>= p_div;
+
+ if (pll->params->pdiv_tohw) {
+ ret = _p_div_to_hw(hw, 1 << p_div);
+ if (ret < 0)
+ return ret;
+ else
+ cfg->p = ret;
+ } else
+ cfg->p = p_div;
+
+ return 0;
+}
+
+/*
+ * SDM (Sigma Delta Modulator) divisor is 16-bit 2's complement signed number
+ * within (-2^12 ... 2^12-1) range. Represented in PLL data structure as
+ * unsigned 16-bit value, with "0" divisor mapped to 0xFFFF. Data "0" is used
+ * to indicate that SDM is disabled.
+ *
+ * Effective ndiv value when SDM is enabled: ndiv + 1/2 + sdm_din/2^13
+ */
+static void clk_pll_set_sdm_data(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+ bool enabled;
+
+ if (!pll->params->sdm_din_reg)
+ return;
+
+ if (cfg->sdm_data) {
+ val = pll_readl_sdm_din(pll) & (~sdm_din_mask(pll));
+ val |= sdin_data_to_din(cfg->sdm_data) & sdm_din_mask(pll);
+ pll_writel_sdm_din(val, pll);
+ }
+
+ val = pll_readl_sdm_ctrl(pll);
+ enabled = (val & sdm_en_mask(pll));
+
+ if (cfg->sdm_data == 0 && enabled)
+ val &= ~pll->params->sdm_ctrl_en_mask;
+
+ if (cfg->sdm_data != 0 && !enabled)
+ val |= pll->params->sdm_ctrl_en_mask;
+
+ pll_writel_sdm_ctrl(val, pll);
+}
+
+static void _update_pll_mnp(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ u32 val;
+ struct tegra_clk_pll_params *params = pll->params;
+ struct div_nmp *div_nmp = params->div_nmp;
+
+ if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) &&
+ (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) &
+ PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) {
+ val = pll_override_readl(params->pmc_divp_reg, pll);
+ val &= ~(divp_mask(pll) << div_nmp->override_divp_shift);
+ val |= cfg->p << div_nmp->override_divp_shift;
+ pll_override_writel(val, params->pmc_divp_reg, pll);
+
+ val = pll_override_readl(params->pmc_divnm_reg, pll);
+ val &= ~((divm_mask(pll) << div_nmp->override_divm_shift) |
+ (divn_mask(pll) << div_nmp->override_divn_shift));
+ val |= (cfg->m << div_nmp->override_divm_shift) |
+ (cfg->n << div_nmp->override_divn_shift);
+ pll_override_writel(val, params->pmc_divnm_reg, pll);
+ } else {
+ val = pll_readl_base(pll);
+
+ val &= ~(divm_mask_shifted(pll) | divn_mask_shifted(pll) |
+ divp_mask_shifted(pll));
+
+ val |= (cfg->m << divm_shift(pll)) |
+ (cfg->n << divn_shift(pll)) |
+ (cfg->p << divp_shift(pll));
+
+ pll_writel_base(val, pll);
+
+ clk_pll_set_sdm_data(&pll->hw, cfg);
+ }
+}
+
+static void _get_pll_mnp(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ u32 val;
+ struct tegra_clk_pll_params *params = pll->params;
+ struct div_nmp *div_nmp = params->div_nmp;
+
+ *cfg = (struct tegra_clk_pll_freq_table) { };
+
+ if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) &&
+ (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) &
+ PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) {
+ val = pll_override_readl(params->pmc_divp_reg, pll);
+ cfg->p = (val >> div_nmp->override_divp_shift) & divp_mask(pll);
+
+ val = pll_override_readl(params->pmc_divnm_reg, pll);
+ cfg->m = (val >> div_nmp->override_divm_shift) & divm_mask(pll);
+ cfg->n = (val >> div_nmp->override_divn_shift) & divn_mask(pll);
+ } else {
+ val = pll_readl_base(pll);
+
+ cfg->m = (val >> div_nmp->divm_shift) & divm_mask(pll);
+ cfg->n = (val >> div_nmp->divn_shift) & divn_mask(pll);
+ cfg->p = (val >> div_nmp->divp_shift) & divp_mask(pll);
+
+ if (pll->params->sdm_din_reg) {
+ if (sdm_en_mask(pll) & pll_readl_sdm_ctrl(pll)) {
+ val = pll_readl_sdm_din(pll);
+ val &= sdm_din_mask(pll);
+ cfg->sdm_data = sdin_din_to_data(val);
+ }
+ }
+ }
+}
+
+static void _update_pll_cpcon(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate)
+{
+ u32 val;
+
+ val = pll_readl_misc(pll);
+
+ val &= ~(PLL_MISC_CPCON_MASK << PLL_MISC_CPCON_SHIFT);
+ val |= cfg->cpcon << PLL_MISC_CPCON_SHIFT;
+
+ if (pll->params->flags & TEGRA_PLL_SET_LFCON) {
+ val &= ~(PLL_MISC_LFCON_MASK << PLL_MISC_LFCON_SHIFT);
+ if (cfg->n >= PLLDU_LFCON_SET_DIVN)
+ val |= 1 << PLL_MISC_LFCON_SHIFT;
+ } else if (pll->params->flags & TEGRA_PLL_SET_DCCON) {
+ val &= ~(1 << PLL_MISC_DCCON_SHIFT);
+ if (rate >= (pll->params->vco_max >> 1))
+ val |= 1 << PLL_MISC_DCCON_SHIFT;
+ }
+
+ pll_writel_misc(val, pll);
+}
+
+static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table old_cfg;
+ int state, ret = 0;
+
+ state = clk_pll_is_enabled(hw);
+
+ if (state && pll->params->pre_rate_change) {
+ ret = pll->params->pre_rate_change();
+ if (WARN_ON(ret))
+ return ret;
+ }
+
+ _get_pll_mnp(pll, &old_cfg);
+
+ if (state && pll->params->defaults_set && pll->params->dyn_ramp &&
+ (cfg->m == old_cfg.m) && (cfg->p == old_cfg.p)) {
+ ret = pll->params->dyn_ramp(pll, cfg);
+ if (!ret)
+ goto done;
+ }
+
+ if (state) {
+ pll_clk_stop_ss(pll);
+ _clk_pll_disable(hw);
+ }
+
+ if (!pll->params->defaults_set && pll->params->set_defaults)
+ pll->params->set_defaults(pll);
+
+ _update_pll_mnp(pll, cfg);
+
+ if (pll->params->flags & TEGRA_PLL_HAS_CPCON)
+ _update_pll_cpcon(pll, cfg, rate);
+
+ if (state) {
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+ pll_clk_start_ss(pll);
+ }
+
+done:
+ if (state && pll->params->post_rate_change)
+ pll->params->post_rate_change();
+
+ return ret;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ if (pll->params->flags & TEGRA_PLL_FIXED) {
+ if (rate != pll->params->fixed_rate) {
+ pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
+ __func__, clk_hw_get_name(hw),
+ pll->params->fixed_rate, rate);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (_get_table_rate(hw, &cfg, rate, parent_rate) &&
+ pll->params->calc_rate(hw, &cfg, rate, parent_rate)) {
+ pr_err("%s: Failed to set %s rate %lu\n", __func__,
+ clk_hw_get_name(hw), rate);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _get_pll_mnp(pll, &old_cfg);
+ if (pll->params->flags & TEGRA_PLL_VCO_OUT)
+ cfg.p = old_cfg.p;
+
+ if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p ||
+ old_cfg.sdm_data != cfg.sdm_data)
+ ret = _program_pll(hw, &cfg, rate);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static int clk_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg;
+
+ if (pll->params->flags & TEGRA_PLL_FIXED) {
+ /* PLLM/MB are used for memory; we do not change rate */
+ if (pll->params->flags & (TEGRA_PLLM | TEGRA_PLLMB))
+ req->rate = clk_hw_get_rate(hw);
+ else
+ req->rate = pll->params->fixed_rate;
+
+ return 0;
+ }
+
+ if (_get_table_rate(hw, &cfg, req->rate, req->best_parent_rate) &&
+ pll->params->calc_rate(hw, &cfg, req->rate, req->best_parent_rate))
+ return -EINVAL;
+
+ req->rate = cfg.output_rate;
+
+ return 0;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg;
+ u32 val;
+ u64 rate = parent_rate;
+ int pdiv;
+
+ val = pll_readl_base(pll);
+
+ if ((pll->params->flags & TEGRA_PLL_BYPASS) && (val & PLL_BASE_BYPASS))
+ return parent_rate;
+
+ if ((pll->params->flags & TEGRA_PLL_FIXED) &&
+ !(pll->params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) &&
+ !(val & PLL_BASE_OVERRIDE)) {
+ struct tegra_clk_pll_freq_table sel;
+ if (_get_table_rate(hw, &sel, pll->params->fixed_rate,
+ parent_rate)) {
+ pr_err("Clock %s has unknown fixed frequency\n",
+ clk_hw_get_name(hw));
+ BUG();
+ }
+ return pll->params->fixed_rate;
+ }
+
+ _get_pll_mnp(pll, &cfg);
+
+ if (pll->params->flags & TEGRA_PLL_VCO_OUT) {
+ pdiv = 1;
+ } else {
+ pdiv = _hw_to_p_div(hw, cfg.p);
+ if (pdiv < 0) {
+ WARN(1, "Clock %s has invalid pdiv value : 0x%x\n",
+ clk_hw_get_name(hw), cfg.p);
+ pdiv = 1;
+ }
+ }
+
+ if (pll->params->set_gain)
+ pll->params->set_gain(&cfg);
+
+ cfg.m *= pdiv;
+
+ rate *= cfg.n;
+ do_div(rate, cfg.m);
+
+ return rate;
+}
+
+static int clk_plle_training(struct tegra_clk_pll *pll)
+{
+ u32 val;
+ unsigned long timeout;
+
+ if (!pll->pmc)
+ return -ENOSYS;
+
+ /*
+ * PLLE is already disabled, and setup cleared;
+ * create falling edge on PLLE IDDQ input.
+ */
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = readl(pll->pmc + PMC_SATA_PWRGT);
+ val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
+ writel(val, pll->pmc + PMC_SATA_PWRGT);
+
+ val = pll_readl_misc(pll);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (1) {
+ val = pll_readl_misc(pll);
+ if (val & PLLE_MISC_READY)
+ break;
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: timeout waiting for PLLE\n", __func__);
+ return -EBUSY;
+ }
+ udelay(300);
+ }
+
+ return 0;
+}
+
+static int clk_plle_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table sel;
+ unsigned long input_rate;
+ u32 val;
+ int err;
+
+ if (clk_pll_is_enabled(hw))
+ return 0;
+
+ input_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+
+ if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate))
+ return -EINVAL;
+
+ clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
+ pll_writel_misc(val, pll);
+
+ val = pll_readl_misc(pll);
+ if (!(val & PLLE_MISC_READY)) {
+ err = clk_plle_training(pll);
+ if (err)
+ return err;
+ }
+
+ if (pll->params->flags & TEGRA_PLLE_CONFIGURE) {
+ /* configure dividers */
+ val = pll_readl_base(pll);
+ val &= ~(divp_mask_shifted(pll) | divn_mask_shifted(pll) |
+ divm_mask_shifted(pll));
+ val &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << divm_shift(pll);
+ val |= sel.n << divn_shift(pll);
+ val |= sel.p << divp_shift(pll);
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ }
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_SETUP_VALUE;
+ val |= PLLE_MISC_LOCK_ENABLE;
+ pll_writel_misc(val, pll);
+
+ val = readl(pll->clk_base + PLLE_SS_CTRL);
+ val &= ~PLLE_SS_COEFFICIENTS_MASK;
+ val |= PLLE_SS_DISABLE;
+ writel(val, pll->clk_base + PLLE_SS_CTRL);
+
+ val = pll_readl_base(pll);
+ val |= (PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+ pll_writel_base(val, pll);
+
+ clk_pll_wait_for_lock(pll);
+
+ return 0;
+}
+
+static unsigned long clk_plle_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val = pll_readl_base(pll);
+ u32 divn = 0, divm = 0, divp = 0;
+ u64 rate = parent_rate;
+
+ divp = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll));
+ divn = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll));
+ divm = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll));
+ divm *= divp;
+
+ rate *= divn;
+ do_div(rate, divm);
+ return rate;
+}
+
+static void tegra_clk_pll_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long rate = clk_hw_get_rate(hw);
+
+ if (clk_pll_is_enabled(hw))
+ return;
+
+ if (pll->params->set_defaults)
+ pll->params->set_defaults(pll);
+
+ clk_pll_set_rate(hw, rate, parent_rate);
+
+ if (!__clk_get_enable_count(hw->clk))
+ clk_pll_disable(hw);
+ else
+ clk_pll_enable(hw);
+}
+
+const struct clk_ops tegra_clk_pll_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
+ .restore_context = tegra_clk_pll_restore_context,
+};
+
+const struct clk_ops tegra_clk_plle_ops = {
+ .recalc_rate = clk_plle_recalc_rate,
+ .is_enabled = clk_pll_is_enabled,
+ .disable = clk_pll_disable,
+ .enable = clk_plle_enable,
+};
+
+/*
+ * Structure defining the fields for USB UTMI clocks Parameters.
+ */
+struct utmi_clk_param {
+ /* Oscillator Frequency in Hz */
+ u32 osc_frequency;
+ /* UTMIP PLL Enable Delay Count */
+ u8 enable_delay_count;
+ /* UTMIP PLL Stable count */
+ u8 stable_count;
+ /* UTMIP PLL Active delay count */
+ u8 active_delay_count;
+ /* UTMIP PLL Xtal frequency count */
+ u8 xtal_freq_count;
+};
+
+static const struct utmi_clk_param utmi_parameters[] = {
+ {
+ .osc_frequency = 13000000, .enable_delay_count = 0x02,
+ .stable_count = 0x33, .active_delay_count = 0x05,
+ .xtal_freq_count = 0x7f
+ }, {
+ .osc_frequency = 19200000, .enable_delay_count = 0x03,
+ .stable_count = 0x4b, .active_delay_count = 0x06,
+ .xtal_freq_count = 0xbb
+ }, {
+ .osc_frequency = 12000000, .enable_delay_count = 0x02,
+ .stable_count = 0x2f, .active_delay_count = 0x04,
+ .xtal_freq_count = 0x76
+ }, {
+ .osc_frequency = 26000000, .enable_delay_count = 0x04,
+ .stable_count = 0x66, .active_delay_count = 0x09,
+ .xtal_freq_count = 0xfe
+ }, {
+ .osc_frequency = 16800000, .enable_delay_count = 0x03,
+ .stable_count = 0x41, .active_delay_count = 0x0a,
+ .xtal_freq_count = 0xa4
+ }, {
+ .osc_frequency = 38400000, .enable_delay_count = 0x0,
+ .stable_count = 0x0, .active_delay_count = 0x6,
+ .xtal_freq_count = 0x80
+ },
+};
+
+static int clk_pllu_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct clk_hw *pll_ref = clk_hw_get_parent(hw);
+ struct clk_hw *osc = clk_hw_get_parent(pll_ref);
+ const struct utmi_clk_param *params = NULL;
+ unsigned long flags = 0, input_rate;
+ unsigned int i;
+ int ret = 0;
+ u32 value;
+
+ if (!osc) {
+ pr_err("%s: failed to get OSC clock\n", __func__);
+ return -EINVAL;
+ }
+
+ input_rate = clk_hw_get_rate(osc);
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (!clk_pll_is_enabled(hw))
+ _clk_pll_enable(hw);
+
+ ret = clk_pll_wait_for_lock(pll);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (input_rate == utmi_parameters[i].osc_frequency) {
+ params = &utmi_parameters[i];
+ break;
+ }
+ }
+
+ if (!params) {
+ pr_err("%s: unexpected input rate %lu Hz\n", __func__,
+ input_rate);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ value = pll_readl_base(pll);
+ value &= ~PLLU_BASE_OVERRIDE;
+ pll_writel_base(value, pll);
+
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
+ /* Program UTMIP PLL stable and active counts */
+ value &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ value |= UTMIP_PLL_CFG2_STABLE_COUNT(params->stable_count);
+ value &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+ value |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(params->active_delay_count);
+ /* Remove power downs from UTMIP PLL control bits */
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
+
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ value &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+ value |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(params->enable_delay_count);
+ value &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ value |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(params->xtal_freq_count);
+ /* Remove power downs from UTMIP PLL control bits */
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static const struct clk_ops tegra_clk_pllu_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pllu_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_determine_rate,
+ .set_rate = clk_pll_set_rate,
+};
+
+static int _pll_fixed_mdiv(struct tegra_clk_pll_params *pll_params,
+ unsigned long parent_rate)
+{
+ u16 mdiv = parent_rate / pll_params->cf_min;
+
+ if (pll_params->flags & TEGRA_MDIV_NEW)
+ return (!pll_params->mdiv_default ? mdiv :
+ min(mdiv, pll_params->mdiv_default));
+
+ if (pll_params->mdiv_default)
+ return pll_params->mdiv_default;
+
+ if (parent_rate > pll_params->cf_max)
+ return 2;
+ else
+ return 1;
+}
+
+static int _calc_dynamic_ramp_rate(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned int p;
+ int p_div;
+
+ if (!rate)
+ return -EINVAL;
+
+ p = DIV_ROUND_UP(pll->params->vco_min, rate);
+ cfg->m = _pll_fixed_mdiv(pll->params, parent_rate);
+ cfg->output_rate = rate * p;
+ cfg->n = cfg->output_rate * cfg->m / parent_rate;
+ cfg->input_rate = parent_rate;
+
+ p_div = _p_div_to_hw(hw, p);
+ if (p_div < 0)
+ return p_div;
+
+ cfg->p = p_div;
+
+ if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max)
+ return -EINVAL;
+
+ return 0;
+}
+
+#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_124_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_132_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_210_SOC)
+
+u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+ return (u16)_pll_fixed_mdiv(pll->params, input_rate);
+}
+
+static unsigned long _clip_vco_min(unsigned long vco_min,
+ unsigned long parent_rate)
+{
+ return DIV_ROUND_UP(vco_min, parent_rate) * parent_rate;
+}
+
+static int _setup_dynamic_ramp(struct tegra_clk_pll_params *pll_params,
+ void __iomem *clk_base,
+ unsigned long parent_rate)
+{
+ u32 val;
+ u32 step_a, step_b;
+
+ switch (parent_rate) {
+ case 12000000:
+ case 13000000:
+ case 26000000:
+ step_a = 0x2B;
+ step_b = 0x0B;
+ break;
+ case 16800000:
+ step_a = 0x1A;
+ step_b = 0x09;
+ break;
+ case 19200000:
+ step_a = 0x12;
+ step_b = 0x08;
+ break;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, parent_rate);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ val = step_a << pll_params->stepa_shift;
+ val |= step_b << pll_params->stepb_shift;
+ writel_relaxed(val, clk_base + pll_params->dyn_ramp_reg);
+
+ return 0;
+}
+
+static int _pll_ramp_calc_pll(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ int err = 0;
+
+ err = _get_table_rate(hw, cfg, rate, parent_rate);
+ if (err < 0)
+ err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate);
+ else {
+ if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) {
+ WARN_ON(1);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (cfg->p > pll->params->max_p)
+ err = -EINVAL;
+
+out:
+ return err;
+}
+
+static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ unsigned long flags = 0;
+ int ret;
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate);
+ if (ret < 0)
+ return ret;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _get_pll_mnp(pll, &old_cfg);
+ if (pll->params->flags & TEGRA_PLL_VCO_OUT)
+ cfg.p = old_cfg.p;
+
+ if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p)
+ ret = _program_pll(hw, &cfg, rate);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static int clk_pll_ramp_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table cfg;
+ int ret, p_div;
+ u64 output_rate = req->best_parent_rate;
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, req->rate, req->best_parent_rate);
+ if (ret < 0)
+ return ret;
+
+ p_div = _hw_to_p_div(hw, cfg.p);
+ if (p_div < 0)
+ return p_div;
+
+ if (pll->params->set_gain)
+ pll->params->set_gain(&cfg);
+
+ output_rate *= cfg.n;
+ do_div(output_rate, cfg.m * p_div);
+
+ req->rate = output_rate;
+
+ return 0;
+}
+
+static void _pllcx_strobe(struct tegra_clk_pll *pll)
+{
+ u32 val;
+
+ val = pll_readl_misc(pll);
+ val |= PLLCX_MISC_STROBE;
+ pll_writel_misc(val, pll);
+ udelay(2);
+
+ val &= ~PLLCX_MISC_STROBE;
+ pll_writel_misc(val, pll);
+}
+
+static int clk_pllc_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+ int ret;
+ unsigned long flags = 0;
+
+ if (clk_pll_is_enabled(hw))
+ return 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_enable(hw);
+ udelay(2);
+
+ val = pll_readl_misc(pll);
+ val &= ~PLLCX_MISC_RESET;
+ pll_writel_misc(val, pll);
+ udelay(2);
+
+ _pllcx_strobe(pll);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void _clk_pllc_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ _clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val |= PLLCX_MISC_RESET;
+ pll_writel_misc(val, pll);
+ udelay(2);
+}
+
+static void clk_pllc_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pllc_disable(hw);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll,
+ unsigned long input_rate, u32 n)
+{
+ u32 val, n_threshold;
+
+ switch (input_rate) {
+ case 12000000:
+ n_threshold = 70;
+ break;
+ case 13000000:
+ case 26000000:
+ n_threshold = 71;
+ break;
+ case 16800000:
+ n_threshold = 55;
+ break;
+ case 19200000:
+ n_threshold = 48;
+ break;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, input_rate);
+ return -EINVAL;
+ }
+
+ val = pll_readl_misc(pll);
+ val &= ~(PLLCX_MISC_SDM_DIV_MASK | PLLCX_MISC_FILT_DIV_MASK);
+ val |= n <= n_threshold ?
+ PLLCX_MISC_DIV_LOW_RANGE : PLLCX_MISC_DIV_HIGH_RANGE;
+ pll_writel_misc(val, pll);
+
+ return 0;
+}
+
+static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int state, ret = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate);
+ if (ret < 0)
+ goto out;
+
+ _get_pll_mnp(pll, &old_cfg);
+
+ if (cfg.m != old_cfg.m) {
+ WARN_ON(1);
+ goto out;
+ }
+
+ if (old_cfg.n == cfg.n && old_cfg.p == cfg.p)
+ goto out;
+
+ state = clk_pll_is_enabled(hw);
+ if (state)
+ _clk_pllc_disable(hw);
+
+ ret = _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n);
+ if (ret < 0)
+ goto out;
+
+ _update_pll_mnp(pll, &cfg);
+
+ if (state)
+ ret = clk_pllc_enable(hw);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static long _pllre_calc_rate(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate)
+{
+ u16 m, n;
+ u64 output_rate = parent_rate;
+
+ m = _pll_fixed_mdiv(pll->params, parent_rate);
+ n = rate * m / parent_rate;
+
+ output_rate *= n;
+ do_div(output_rate, m);
+
+ if (cfg) {
+ cfg->m = m;
+ cfg->n = n;
+ }
+
+ return output_rate;
+}
+
+static int clk_pllre_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg, old_cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ int state, ret = 0;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _pllre_calc_rate(pll, &cfg, rate, parent_rate);
+ _get_pll_mnp(pll, &old_cfg);
+ cfg.p = old_cfg.p;
+
+ if (cfg.m != old_cfg.m || cfg.n != old_cfg.n) {
+ state = clk_pll_is_enabled(hw);
+ if (state)
+ _clk_pll_disable(hw);
+
+ _update_pll_mnp(pll, &cfg);
+
+ if (state) {
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+ }
+ }
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static unsigned long clk_pllre_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_pll_freq_table cfg;
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u64 rate = parent_rate;
+
+ _get_pll_mnp(pll, &cfg);
+
+ rate *= cfg.n;
+ do_div(rate, cfg.m);
+
+ return rate;
+}
+
+static int clk_pllre_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+ req->rate = _pllre_calc_rate(pll, NULL, req->rate,
+ req->best_parent_rate);
+
+ return 0;
+}
+
+static int clk_plle_tegra114_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int ret;
+ unsigned long flags = 0;
+ unsigned long input_rate;
+
+ input_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+
+ if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate))
+ return -EINVAL;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ val = pll_readl_base(pll);
+ val &= ~BIT(29); /* Disable lock override */
+ pll_writel_base(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= PLLE_AUX_ENABLE_SWCTL;
+ val &= ~PLLE_AUX_SEQ_ENABLE;
+ pll_writel(val, pll->params->aux_reg, pll);
+ udelay(1);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_LOCK_ENABLE;
+ val |= PLLE_MISC_IDDQ_SW_CTRL;
+ val &= ~PLLE_MISC_IDDQ_SW_VALUE;
+ val |= PLLE_MISC_PLLE_PTS;
+ val &= ~(PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK);
+ pll_writel_misc(val, pll);
+ udelay(5);
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val |= PLLE_SS_DISABLE;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+
+ val = pll_readl_base(pll);
+ val &= ~(divp_mask_shifted(pll) | divn_mask_shifted(pll) |
+ divm_mask_shifted(pll));
+ val &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << divm_shift(pll);
+ val |= sel.n << divn_shift(pll);
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ udelay(1);
+
+ _clk_pll_enable(hw);
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (ret < 0)
+ goto out;
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_CENTER | PLLE_SS_CNTL_INVERT);
+ val &= ~PLLE_SS_COEFFICIENTS_MASK;
+ val |= PLLE_SS_COEFFICIENTS_VAL_TEGRA114;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_SSC_BYP | PLLE_SS_CNTL_BYPASS_SS);
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+ val &= ~PLLE_SS_CNTL_INTERP_RESET;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+
+ /* Enable HW control of XUSB brick PLL */
+ val = pll_readl_misc(pll);
+ val &= ~PLLE_MISC_IDDQ_SW_CTRL;
+ pll_writel_misc(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SEQ_START_STATE);
+ val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+ pll_writel(val, pll->params->aux_reg, pll);
+ udelay(1);
+ val |= PLLE_AUX_SEQ_ENABLE;
+ pll_writel(val, pll->params->aux_reg, pll);
+
+ val = pll_readl(XUSBIO_PLL_CFG0, pll);
+ val |= (XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET |
+ XUSBIO_PLL_CFG0_SEQ_START_STATE);
+ val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL |
+ XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL);
+ pll_writel(val, XUSBIO_PLL_CFG0, pll);
+ udelay(1);
+ val |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
+ pll_writel(val, XUSBIO_PLL_CFG0, pll);
+
+ /* Enable HW control of SATA PLL */
+ val = pll_readl(SATA_PLL_CFG0, pll);
+ val &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL;
+ val |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET;
+ val |= SATA_PLL_CFG0_SEQ_START_STATE;
+ pll_writel(val, SATA_PLL_CFG0, pll);
+
+ udelay(1);
+
+ val = pll_readl(SATA_PLL_CFG0, pll);
+ val |= SATA_PLL_CFG0_SEQ_ENABLE;
+ pll_writel(val, SATA_PLL_CFG0, pll);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void clk_plle_tegra114_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ _clk_pll_disable(hw);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE;
+ pll_writel_misc(val, pll);
+ udelay(1);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static int clk_pllu_tegra114_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ const struct utmi_clk_param *params = NULL;
+ struct clk *osc = __clk_lookup("osc");
+ unsigned long flags = 0, input_rate;
+ unsigned int i;
+ int ret = 0;
+ u32 value;
+
+ if (!osc) {
+ pr_err("%s: failed to get OSC clock\n", __func__);
+ return -EINVAL;
+ }
+
+ input_rate = clk_hw_get_rate(__clk_get_hw(osc));
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ if (!clk_pll_is_enabled(hw))
+ _clk_pll_enable(hw);
+
+ ret = clk_pll_wait_for_lock(pll);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (input_rate == utmi_parameters[i].osc_frequency) {
+ params = &utmi_parameters[i];
+ break;
+ }
+ }
+
+ if (!params) {
+ pr_err("%s: unexpected input rate %lu Hz\n", __func__,
+ input_rate);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ value = pll_readl_base(pll);
+ value &= ~PLLU_BASE_OVERRIDE;
+ pll_writel_base(value, pll);
+
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG2);
+ /* Program UTMIP PLL stable and active counts */
+ value &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ value |= UTMIP_PLL_CFG2_STABLE_COUNT(params->stable_count);
+ value &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+ value |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(params->active_delay_count);
+ /* Remove power downs from UTMIP PLL control bits */
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG2);
+
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ value &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+ value |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(params->enable_delay_count);
+ value &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ value |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(params->xtal_freq_count);
+ /* Remove power downs from UTMIP PLL control bits */
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
+
+ /* Setup HW control of UTMIPLL */
+ value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ value |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
+ value &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
+ value |= UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE;
+ writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ value = readl_relaxed(pll->clk_base + UTMIP_PLL_CFG1);
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+ value &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ writel_relaxed(value, pll->clk_base + UTMIP_PLL_CFG1);
+
+ udelay(1);
+
+ /*
+ * Setup SW override of UTMIPLL assuming USB2.0 ports are assigned
+ * to USB2
+ */
+ value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ value |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL;
+ value &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ udelay(1);
+
+ /* Enable HW control of UTMIPLL */
+ value = readl_relaxed(pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ value |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
+ writel_relaxed(value, pll->clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void _clk_plle_tegra_init_parent(struct tegra_clk_pll *pll)
+{
+ u32 val, val_aux;
+
+ /* ensure parent is set to pll_ref */
+ val = pll_readl_base(pll);
+ val_aux = pll_readl(pll->params->aux_reg, pll);
+
+ if (val & PLL_BASE_ENABLE) {
+ if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
+ (val_aux & PLLE_AUX_PLLP_SEL))
+ WARN(1, "pll_e enabled with unsupported parent %s\n",
+ (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
+ "pll_re_vco");
+ } else {
+ val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
+ pll_writel(val_aux, pll->params->aux_reg, pll);
+ fence_udelay(1, pll->clk_base);
+ }
+}
+#endif
+
+static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
+ void __iomem *pmc, struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+
+ pll = kzalloc_obj(*pll);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ pll->clk_base = clk_base;
+ pll->pmc = pmc;
+
+ pll->params = pll_params;
+ pll->lock = lock;
+
+ if (!pll_params->div_nmp)
+ pll_params->div_nmp = &default_nmp;
+
+ return pll;
+}
+
+static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
+ const char *name, const char *parent_name, unsigned long flags,
+ const struct clk_ops *ops)
+{
+ struct clk_init_data init;
+
+ init.name = name;
+ init.ops = ops;
+ init.flags = flags;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ /* Default to _calc_rate if unspecified */
+ if (!pll->params->calc_rate) {
+ if (pll->params->flags & TEGRA_PLLM)
+ pll->params->calc_rate = _calc_dynamic_ramp_rate;
+ else
+ pll->params->calc_rate = _calc_rate;
+ }
+
+ if (pll->params->set_defaults)
+ pll->params->set_defaults(pll);
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ pll->hw.init = &init;
+
+ return tegra_clk_dev_register(&pll->hw);
+}
+
+struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+static struct div_nmp pll_e_nmp = {
+ .divn_shift = PLLE_BASE_DIVN_SHIFT,
+ .divn_width = PLLE_BASE_DIVN_WIDTH,
+ .divm_shift = PLLE_BASE_DIVM_SHIFT,
+ .divm_width = PLLE_BASE_DIVM_WIDTH,
+ .divp_shift = PLLE_BASE_DIVP_SHIFT,
+ .divp_width = PLLE_BASE_DIVP_WIDTH,
+};
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+
+ if (!pll_params->div_nmp)
+ pll_params->div_nmp = &pll_e_nmp;
+
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_plle_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllu(const char *name, const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params, spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->flags |= TEGRA_PLLU;
+
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllu_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_124_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_132_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_210_SOC)
+static const struct clk_ops tegra_clk_pllxc_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_ramp_determine_rate,
+ .set_rate = clk_pllxc_set_rate,
+};
+
+static const struct clk_ops tegra_clk_pllc_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pllc_enable,
+ .disable = clk_pllc_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_ramp_determine_rate,
+ .set_rate = clk_pllc_set_rate,
+};
+
+static const struct clk_ops tegra_clk_pllre_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pllre_recalc_rate,
+ .determine_rate = clk_pllre_determine_rate,
+ .set_rate = clk_pllre_set_rate,
+};
+
+static const struct clk_ops tegra_clk_plle_tegra114_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_plle_tegra114_enable,
+ .disable = clk_plle_tegra114_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+static const struct clk_ops tegra_clk_pllu_tegra114_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pllu_tegra114_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+};
+
+struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk, *parent;
+ unsigned long parent_rate;
+ u32 val, val_iddq;
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ parent_name, name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!pll_params->pdiv_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ /*
+ * If the pll has a set_defaults callback, it will take care of
+ * configuring dynamic ramping and setting IDDQ in that path.
+ */
+ if (!pll_params->set_defaults) {
+ int err;
+
+ err = _setup_dynamic_ramp(pll_params, clk_base, parent_rate);
+ if (err)
+ return ERR_PTR(err);
+
+ val = readl_relaxed(clk_base + pll_params->base_reg);
+ val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg);
+
+ if (val & PLL_BASE_ENABLE)
+ WARN_ON(val_iddq & BIT(pll_params->iddq_bit_idx));
+ else {
+ val_iddq |= BIT(pll_params->iddq_bit_idx);
+ writel_relaxed(val_iddq,
+ clk_base + pll_params->iddq_reg);
+ }
+ }
+
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllxc_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock, unsigned long parent_rate)
+{
+ u32 val;
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ /* program minimum rate by default */
+
+ val = pll_readl_base(pll);
+ if (val & PLL_BASE_ENABLE)
+ WARN_ON(readl_relaxed(clk_base + pll_params->iddq_reg) &
+ BIT(pll_params->iddq_bit_idx));
+ else {
+ int m;
+
+ m = _pll_fixed_mdiv(pll_params, parent_rate);
+ val = m << divm_shift(pll);
+ val |= (pll_params->vco_min / parent_rate) << divn_shift(pll);
+ pll_writel_base(val, pll);
+ }
+
+ /* disable lock override */
+
+ val = pll_readl_misc(pll);
+ val &= ~BIT(29);
+ pll_writel_misc(val, pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllre_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk, *parent;
+ unsigned long parent_rate;
+
+ if (!pll_params->pdiv_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ parent_name, name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+ pll_params->flags |= TEGRA_PLLM;
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct clk *parent, *clk;
+ const struct pdiv_map *p_tohw = pll_params->pdiv_tohw;
+ struct tegra_clk_pll *pll;
+ struct tegra_clk_pll_freq_table cfg;
+ unsigned long parent_rate;
+
+ if (!p_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ parent_name, name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ /*
+ * Most of PLLC register fields are shadowed, and can not be read
+ * directly from PLL h/w. Hence, actual PLLC boot state is unknown.
+ * Initialize PLL to default state: disabled, reset; shadow registers
+ * loaded with default parameters; dividers are preset for half of
+ * minimum VCO rate (the latter assured that shadowed divider settings
+ * are within supported range).
+ */
+
+ cfg.m = _pll_fixed_mdiv(pll_params, parent_rate);
+ cfg.n = cfg.m * pll_params->vco_min / parent_rate;
+
+ while (p_tohw->pdiv) {
+ if (p_tohw->pdiv == 2) {
+ cfg.p = p_tohw->hw_val;
+ break;
+ }
+ p_tohw++;
+ }
+
+ if (!p_tohw->pdiv) {
+ WARN_ON(1);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pll_writel_base(0, pll);
+ _update_pll_mnp(pll, &cfg);
+
+ pll_writel_misc(PLLCX_MISC_DEFAULT, pll);
+ pll_writel(PLLCX_MISC1_DEFAULT, pll_params->ext_misc_reg[0], pll);
+ pll_writel(PLLCX_MISC2_DEFAULT, pll_params->ext_misc_reg[1], pll);
+ pll_writel(PLLCX_MISC3_DEFAULT, pll_params->ext_misc_reg[2], pll);
+
+ _pllcx_update_dynamic_coef(pll, parent_rate, cfg.n);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllc_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ _clk_plle_tegra_init_parent(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_plle_tegra114_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *
+tegra_clk_register_pllu_tegra114(const char *name, const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->flags |= TEGRA_PLLU;
+
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllu_tegra114_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) || defined(CONFIG_ARCH_TEGRA_210_SOC)
+static const struct clk_ops tegra_clk_pllss_ops = {
+ .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .determine_rate = clk_pll_ramp_determine_rate,
+ .set_rate = clk_pllxc_set_rate,
+ .restore_context = tegra_clk_pll_restore_context,
+};
+
+struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk, *parent;
+ struct tegra_clk_pll_freq_table cfg;
+ unsigned long parent_rate;
+ u32 val, val_iddq;
+ int i;
+
+ if (!pll_params->div_nmp)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ parent_name, name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ val = pll_readl_base(pll);
+ val &= ~PLLSS_REF_SRC_SEL_MASK;
+ pll_writel_base(val, pll);
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ /* initialize PLL to minimum rate */
+
+ cfg.m = _pll_fixed_mdiv(pll_params, parent_rate);
+ cfg.n = cfg.m * pll_params->vco_min / parent_rate;
+
+ for (i = 0; pll_params->pdiv_tohw[i].pdiv; i++)
+ ;
+ if (!i) {
+ kfree(pll);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cfg.p = pll_params->pdiv_tohw[i-1].hw_val;
+
+ _update_pll_mnp(pll, &cfg);
+
+ pll_writel_misc(PLLSS_MISC_DEFAULT, pll);
+ pll_writel(PLLSS_CFG_DEFAULT, pll_params->ext_misc_reg[0], pll);
+ pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[1], pll);
+ pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[2], pll);
+
+ val = pll_readl_base(pll);
+ val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg);
+ if (val & PLL_BASE_ENABLE) {
+ if (val_iddq & BIT(pll_params->iddq_bit_idx)) {
+ WARN(1, "%s is on but IDDQ set\n", name);
+ kfree(pll);
+ return ERR_PTR(-EINVAL);
+ }
+ } else {
+ val_iddq |= BIT(pll_params->iddq_bit_idx);
+ writel_relaxed(val_iddq, clk_base + pll_params->iddq_reg);
+ }
+
+ val &= ~PLLSS_LOCK_OVERRIDE;
+ pll_writel_base(val, pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pllss_ops);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_210_SOC)
+struct clk *tegra_clk_register_pllre_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ void __iomem *pmc, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock, unsigned long parent_rate)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+static int clk_plle_tegra210_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ u32 val;
+
+ val = pll_readl_base(pll);
+
+ return val & PLLE_BASE_ENABLE ? 1 : 0;
+}
+
+static int clk_plle_tegra210_enable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_freq_table sel;
+ u32 val;
+ int ret = 0;
+ unsigned long flags = 0;
+ unsigned long input_rate;
+
+ if (clk_plle_tegra210_is_enabled(hw))
+ return 0;
+
+ input_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+
+ if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate))
+ return -EINVAL;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ if (val & PLLE_AUX_SEQ_ENABLE)
+ goto out;
+
+ val = pll_readl_base(pll);
+ val &= ~BIT(30); /* Disable lock override */
+ pll_writel_base(val, pll);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_LOCK_ENABLE;
+ val |= PLLE_MISC_IDDQ_SW_CTRL;
+ val &= ~PLLE_MISC_IDDQ_SW_VALUE;
+ val |= PLLE_MISC_PLLE_PTS;
+ val &= ~(PLLE_MISC_VREG_BG_CTRL_MASK | PLLE_MISC_VREG_CTRL_MASK);
+ pll_writel_misc(val, pll);
+ udelay(5);
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val |= PLLE_SS_DISABLE;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+
+ val = pll_readl_base(pll);
+ val &= ~(divp_mask_shifted(pll) | divn_mask_shifted(pll) |
+ divm_mask_shifted(pll));
+ val &= ~(PLLE_BASE_DIVCML_MASK << PLLE_BASE_DIVCML_SHIFT);
+ val |= sel.m << divm_shift(pll);
+ val |= sel.n << divn_shift(pll);
+ val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT;
+ pll_writel_base(val, pll);
+ udelay(1);
+
+ val = pll_readl_base(pll);
+ val |= PLLE_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ ret = clk_pll_wait_for_lock(pll);
+
+ if (ret < 0)
+ goto out;
+
+ val = pll_readl(PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_CENTER | PLLE_SS_CNTL_INVERT);
+ val &= ~PLLE_SS_COEFFICIENTS_MASK;
+ val |= PLLE_SS_COEFFICIENTS_VAL_TEGRA210;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ val &= ~(PLLE_SS_CNTL_SSC_BYP | PLLE_SS_CNTL_BYPASS_SS);
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+ val &= ~PLLE_SS_CNTL_INTERP_RESET;
+ pll_writel(val, PLLE_SS_CTRL, pll);
+ udelay(1);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return ret;
+}
+
+static void clk_plle_tegra210_disable(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+ u32 val;
+
+ if (pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ /* If PLLE HW sequencer is enabled, SW should not disable PLLE */
+ val = pll_readl(pll->params->aux_reg, pll);
+ if (val & PLLE_AUX_SEQ_ENABLE)
+ goto out;
+
+ val = pll_readl_base(pll);
+ val &= ~PLLE_BASE_ENABLE;
+ pll_writel_base(val, pll);
+
+ val = pll_readl(pll->params->aux_reg, pll);
+ val |= PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL;
+ pll_writel(val, pll->params->aux_reg, pll);
+
+ val = pll_readl_misc(pll);
+ val |= PLLE_MISC_IDDQ_SW_CTRL | PLLE_MISC_IDDQ_SW_VALUE;
+ pll_writel_misc(val, pll);
+ udelay(1);
+
+out:
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+}
+
+static void tegra_clk_plle_t210_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+ _clk_plle_tegra_init_parent(pll);
+}
+
+static const struct clk_ops tegra_clk_plle_tegra210_ops = {
+ .is_enabled = clk_plle_tegra210_is_enabled,
+ .enable = clk_plle_tegra210_enable,
+ .disable = clk_plle_tegra210_disable,
+ .recalc_rate = clk_pll_recalc_rate,
+ .restore_context = tegra_clk_plle_t210_restore_context,
+};
+
+struct clk *tegra_clk_register_plle_tegra210(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk;
+
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ _clk_plle_tegra_init_parent(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_plle_tegra210_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllc_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ void __iomem *pmc, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct clk *parent, *clk;
+ const struct pdiv_map *p_tohw = pll_params->pdiv_tohw;
+ struct tegra_clk_pll *pll;
+ unsigned long parent_rate;
+
+ if (!p_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ name, parent_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllss_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk, *parent;
+ unsigned long parent_rate;
+ u32 val;
+
+ if (!pll_params->div_nmp)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ name, parent_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ val = readl_relaxed(clk_base + pll_params->base_reg);
+ if (val & PLLSS_REF_SRC_SEL_MASK) {
+ WARN(1, "not supported reference clock for %s\n", name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+ pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_pllmb(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock)
+{
+ struct tegra_clk_pll *pll;
+ struct clk *clk, *parent;
+ unsigned long parent_rate;
+
+ if (!pll_params->pdiv_tohw)
+ return ERR_PTR(-EINVAL);
+
+ parent = __clk_lookup(parent_name);
+ if (!parent) {
+ WARN(1, "parent clk %s of %s must be registered first\n",
+ parent_name, name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent_rate = clk_get_rate(parent);
+
+ pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate);
+
+ if (pll_params->adjust_vco)
+ pll_params->vco_min = pll_params->adjust_vco(pll_params,
+ parent_rate);
+
+ pll_params->flags |= TEGRA_PLL_BYPASS;
+ pll_params->flags |= TEGRA_PLLMB;
+ pll = _tegra_init_pll(clk_base, pmc, pll_params, lock);
+ if (IS_ERR(pll))
+ return ERR_CAST(pll);
+
+ clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
+ &tegra_clk_pll_ops);
+ if (IS_ERR(clk))
+ kfree(pll);
+
+ return clk;
+}
+
+#endif
diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
new file mode 100644
index 000000000..14b5439a1
--- /dev/null
+++ b/drivers/clk/tegra/clk-sdmmc-mux.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 NVIDIA CORPORATION. All rights reserved.
+ *
+ * based on clk-mux.c
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+#define DIV_MASK GENMASK(7, 0)
+#define MUX_SHIFT 29
+#define MUX_MASK GENMASK(MUX_SHIFT + 2, MUX_SHIFT)
+#define SDMMC_MUL 2
+
+#define get_max_div(d) DIV_MASK
+#define get_div_field(val) ((val) & DIV_MASK)
+#define get_mux_field(val) (((val) & MUX_MASK) >> MUX_SHIFT)
+
+static const char * const mux_sdmmc_parents[] = {
+ "pll_p", "pll_c4_out2", "pll_c4_out0", "pll_c4_out1", "clk_m"
+};
+
+static const u8 mux_lj_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 5, [4] = 6
+};
+
+static const u8 mux_non_lj_idx[] = {
+ [0] = 0, [1] = 3, [2] = 7, [3] = 4, [4] = 6
+};
+
+static u8 clk_sdmmc_mux_get_parent(struct clk_hw *hw)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ int num_parents, i;
+ u32 src, val;
+ const u8 *mux_idx;
+
+ num_parents = clk_hw_get_num_parents(hw);
+
+ val = readl_relaxed(sdmmc_mux->reg);
+ src = get_mux_field(val);
+ if (get_div_field(val))
+ mux_idx = mux_non_lj_idx;
+ else
+ mux_idx = mux_lj_idx;
+
+ for (i = 0; i < num_parents; i++) {
+ if (mux_idx[i] == src)
+ return i;
+ }
+
+ WARN(1, "Unknown parent selector %d\n", src);
+
+ return 0;
+}
+
+static int clk_sdmmc_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ u32 val;
+
+
+ val = readl_relaxed(sdmmc_mux->reg);
+ if (get_div_field(val))
+ index = mux_non_lj_idx[index];
+ else
+ index = mux_lj_idx[index];
+
+ val &= ~MUX_MASK;
+ val |= index << MUX_SHIFT;
+
+ writel(val, sdmmc_mux->reg);
+
+ return 0;
+}
+
+static unsigned long clk_sdmmc_mux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ u32 val;
+ int div;
+ u64 rate = parent_rate;
+
+ val = readl_relaxed(sdmmc_mux->reg);
+ div = get_div_field(val);
+
+ div += SDMMC_MUL;
+
+ rate *= SDMMC_MUL;
+ rate += div - 1;
+ do_div(rate, div);
+
+ return rate;
+}
+
+static int clk_sdmmc_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ int div;
+ unsigned long output_rate = req->best_parent_rate;
+
+ req->rate = max(req->rate, req->min_rate);
+ req->rate = min(req->rate, req->max_rate);
+
+ if (!req->rate)
+ return output_rate;
+
+ div = div_frac_get(req->rate, output_rate, 8, 1, sdmmc_mux->div_flags);
+ if (div < 0)
+ div = 0;
+
+ if (sdmmc_mux->div_flags & TEGRA_DIVIDER_ROUND_UP)
+ req->rate = DIV_ROUND_UP(output_rate * SDMMC_MUL,
+ div + SDMMC_MUL);
+ else
+ req->rate = output_rate * SDMMC_MUL / (div + SDMMC_MUL);
+
+ return 0;
+}
+
+static int clk_sdmmc_mux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ int div;
+ unsigned long flags = 0;
+ u32 val;
+ u8 src;
+
+ div = div_frac_get(rate, parent_rate, 8, 1, sdmmc_mux->div_flags);
+ if (div < 0)
+ return div;
+
+ if (sdmmc_mux->lock)
+ spin_lock_irqsave(sdmmc_mux->lock, flags);
+
+ src = clk_sdmmc_mux_get_parent(hw);
+ if (div)
+ src = mux_non_lj_idx[src];
+ else
+ src = mux_lj_idx[src];
+
+ val = src << MUX_SHIFT;
+ val |= div;
+ writel(val, sdmmc_mux->reg);
+ fence_udelay(2, sdmmc_mux->reg);
+
+ if (sdmmc_mux->lock)
+ spin_unlock_irqrestore(sdmmc_mux->lock, flags);
+
+ return 0;
+}
+
+static int clk_sdmmc_mux_is_enabled(struct clk_hw *hw)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+ struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+
+ __clk_hw_set_clk(gate_hw, hw);
+
+ return gate_ops->is_enabled(gate_hw);
+}
+
+static int clk_sdmmc_mux_enable(struct clk_hw *hw)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+ struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+
+ __clk_hw_set_clk(gate_hw, hw);
+
+ return gate_ops->enable(gate_hw);
+}
+
+static void clk_sdmmc_mux_disable(struct clk_hw *hw)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+ struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+
+ gate_ops->disable(gate_hw);
+}
+
+static void clk_sdmmc_mux_disable_unused(struct clk_hw *hw)
+{
+ struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw);
+ const struct clk_ops *gate_ops = sdmmc_mux->gate_ops;
+ struct clk_hw *gate_hw = &sdmmc_mux->gate.hw;
+
+ gate_ops->disable_unused(gate_hw);
+}
+
+static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long rate = clk_hw_get_rate(hw);
+ int parent_id;
+
+ parent_id = clk_hw_get_parent_index(hw);
+ if (WARN_ON(parent_id < 0))
+ return;
+
+ clk_sdmmc_mux_set_parent(hw, parent_id);
+ clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
+}
+
+static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
+ .get_parent = clk_sdmmc_mux_get_parent,
+ .set_parent = clk_sdmmc_mux_set_parent,
+ .determine_rate = clk_sdmmc_mux_determine_rate,
+ .recalc_rate = clk_sdmmc_mux_recalc_rate,
+ .set_rate = clk_sdmmc_mux_set_rate,
+ .is_enabled = clk_sdmmc_mux_is_enabled,
+ .enable = clk_sdmmc_mux_enable,
+ .disable = clk_sdmmc_mux_disable,
+ .disable_unused = clk_sdmmc_mux_disable_unused,
+ .restore_context = clk_sdmmc_mux_restore_context,
+};
+
+struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
+ void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags,
+ unsigned long flags, void *lock)
+{
+ struct clk *clk;
+ struct clk_init_data init;
+ const struct tegra_clk_periph_regs *bank;
+ struct tegra_sdmmc_mux *sdmmc_mux;
+
+ init.ops = &tegra_clk_sdmmc_mux_ops;
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = mux_sdmmc_parents;
+ init.num_parents = ARRAY_SIZE(mux_sdmmc_parents);
+
+ bank = get_reg_bank(clk_num);
+ if (!bank)
+ return ERR_PTR(-EINVAL);
+
+ sdmmc_mux = kzalloc_obj(*sdmmc_mux);
+ if (!sdmmc_mux)
+ return ERR_PTR(-ENOMEM);
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ sdmmc_mux->hw.init = &init;
+ sdmmc_mux->reg = clk_base + offset;
+ sdmmc_mux->lock = lock;
+ sdmmc_mux->gate.clk_base = clk_base;
+ sdmmc_mux->gate.regs = bank;
+ sdmmc_mux->gate.enable_refcnt = periph_clk_enb_refcnt;
+ sdmmc_mux->gate.clk_num = clk_num;
+ sdmmc_mux->gate.flags = TEGRA_PERIPH_ON_APB;
+ sdmmc_mux->div_flags = div_flags;
+ sdmmc_mux->gate_ops = &tegra_clk_periph_gate_ops;
+
+ clk = clk_register(NULL, &sdmmc_mux->hw);
+ if (IS_ERR(clk)) {
+ kfree(sdmmc_mux);
+ return clk;
+ }
+
+ sdmmc_mux->gate.hw.clk = clk;
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
new file mode 100644
index 000000000..370445e3d
--- /dev/null
+++ b/drivers/clk/tegra/clk-super.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define SUPER_STATE_IDLE 0
+#define SUPER_STATE_RUN 1
+#define SUPER_STATE_IRQ 2
+#define SUPER_STATE_FIQ 3
+
+#define SUPER_STATE_SHIFT 28
+#define SUPER_STATE_MASK ((BIT(SUPER_STATE_IDLE) | BIT(SUPER_STATE_RUN) | \
+ BIT(SUPER_STATE_IRQ) | BIT(SUPER_STATE_FIQ)) \
+ << SUPER_STATE_SHIFT)
+
+#define SUPER_LP_DIV2_BYPASS (1 << 16)
+
+#define super_state(s) (BIT(s) << SUPER_STATE_SHIFT)
+#define super_state_to_src_shift(m, s) ((m->width * s))
+#define super_state_to_src_mask(m) (((1 << m->width) - 1))
+
+#define CCLK_SRC_PLLP_OUT0 4
+#define CCLK_SRC_PLLP_OUT4 5
+
+static u8 clk_super_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+ u32 val, state;
+ u8 source, shift;
+
+ val = readl_relaxed(mux->reg);
+
+ state = val & SUPER_STATE_MASK;
+
+ BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
+ (state != super_state(SUPER_STATE_IDLE)));
+ shift = (state == super_state(SUPER_STATE_IDLE)) ?
+ super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
+ super_state_to_src_shift(mux, SUPER_STATE_RUN);
+
+ source = (val >> shift) & super_state_to_src_mask(mux);
+
+ /*
+ * If LP_DIV2_BYPASS is not set and PLLX is current parent then
+ * PLLX/2 is the input source to CCLKLP.
+ */
+ if ((mux->flags & TEGRA_DIVIDER_2) && !(val & SUPER_LP_DIV2_BYPASS) &&
+ (source == mux->pllx_index))
+ source = mux->div2_index;
+
+ return source;
+}
+
+static int clk_super_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
+ u32 val, state;
+ int err = 0;
+ u8 parent_index, shift;
+ unsigned long flags = 0;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ val = readl_relaxed(mux->reg);
+ state = val & SUPER_STATE_MASK;
+ BUG_ON((state != super_state(SUPER_STATE_RUN)) &&
+ (state != super_state(SUPER_STATE_IDLE)));
+ shift = (state == super_state(SUPER_STATE_IDLE)) ?
+ super_state_to_src_shift(mux, SUPER_STATE_IDLE) :
+ super_state_to_src_shift(mux, SUPER_STATE_RUN);
+
+ /*
+ * For LP mode super-clock switch between PLLX direct
+ * and divided-by-2 outputs is allowed only when other
+ * than PLLX clock source is current parent.
+ */
+ if ((mux->flags & TEGRA_DIVIDER_2) && ((index == mux->div2_index) ||
+ (index == mux->pllx_index))) {
+ parent_index = clk_super_get_parent(hw);
+ if ((parent_index == mux->div2_index) ||
+ (parent_index == mux->pllx_index)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ val ^= SUPER_LP_DIV2_BYPASS;
+ writel_relaxed(val, mux->reg);
+ udelay(2);
+
+ if (index == mux->div2_index)
+ index = mux->pllx_index;
+ }
+
+ /* enable PLLP branches to CPU before selecting PLLP source */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
+ tegra_clk_set_pllp_out_cpu(true);
+
+ val &= ~((super_state_to_src_mask(mux)) << shift);
+ val |= (index & (super_state_to_src_mask(mux))) << shift;
+
+ writel_relaxed(val, mux->reg);
+ udelay(2);
+
+ /* disable PLLP branches to CPU if not used */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
+ tegra_clk_set_pllp_out_cpu(false);
+
+out:
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return err;
+}
+
+static void clk_super_mux_restore_context(struct clk_hw *hw)
+{
+ int parent_id;
+
+ parent_id = clk_hw_get_parent_index(hw);
+ if (WARN_ON(parent_id < 0))
+ return;
+
+ clk_super_set_parent(hw, parent_id);
+}
+
+static const struct clk_ops tegra_clk_super_mux_ops = {
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+ .get_parent = clk_super_get_parent,
+ .set_parent = clk_super_set_parent,
+ .restore_context = clk_super_mux_restore_context,
+};
+
+static int clk_super_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return super->div_ops->determine_rate(div_hw, req);
+}
+
+static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return super->div_ops->recalc_rate(div_hw, parent_rate);
+}
+
+static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+
+ __clk_hw_set_clk(div_hw, hw);
+
+ return super->div_ops->set_rate(div_hw, rate, parent_rate);
+}
+
+static void clk_super_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+ int parent_id;
+
+ parent_id = clk_hw_get_parent_index(hw);
+ if (WARN_ON(parent_id < 0))
+ return;
+
+ super->div_ops->restore_context(div_hw);
+ clk_super_set_parent(hw, parent_id);
+}
+
+const struct clk_ops tegra_clk_super_ops = {
+ .get_parent = clk_super_get_parent,
+ .set_parent = clk_super_set_parent,
+ .set_rate = clk_super_set_rate,
+ .determine_rate = clk_super_determine_rate,
+ .recalc_rate = clk_super_recalc_rate,
+ .restore_context = clk_super_restore_context,
+};
+
+struct clk *tegra_clk_register_super_mux(const char *name,
+ const char **parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock)
+{
+ struct tegra_clk_super_mux *super;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ super = kzalloc_obj(*super);
+ if (!super)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &tegra_clk_super_mux_ops;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ super->reg = reg;
+ super->pllx_index = pllx_index;
+ super->div2_index = div2_index;
+ super->lock = lock;
+ super->width = width;
+ super->flags = clk_super_flags;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ super->hw.init = &init;
+
+ clk = tegra_clk_dev_register(&super->hw);
+ if (IS_ERR(clk))
+ kfree(super);
+
+ return clk;
+}
+
+struct clk *tegra_clk_register_super_clk(const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ spinlock_t *lock)
+{
+ struct tegra_clk_super_mux *super;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ super = kzalloc_obj(*super);
+ if (!super)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &tegra_clk_super_ops;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ super->reg = reg;
+ super->lock = lock;
+ super->width = 4;
+ super->flags = clk_super_flags;
+ super->frac_div.reg = reg + 4;
+ super->frac_div.shift = 16;
+ super->frac_div.width = 8;
+ super->frac_div.frac_width = 1;
+ super->frac_div.lock = lock;
+ super->div_ops = &tegra_clk_frac_div_ops;
+
+ /* Data in .init is copied by clk_register(), so stack variable OK */
+ super->hw.init = &init;
+
+ clk = clk_register(NULL, &super->hw);
+ if (IS_ERR(clk))
+ kfree(super);
+
+ return clk;
+}
diff --git a/drivers/clk/tegra/clk-tegra-audio.c b/drivers/clk/tegra/clk-tegra-audio.c
new file mode 100644
index 000000000..c99e34d69
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra-audio.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define AUDIO_SYNC_CLK_I2S0 0x4a0
+#define AUDIO_SYNC_CLK_I2S1 0x4a4
+#define AUDIO_SYNC_CLK_I2S2 0x4a8
+#define AUDIO_SYNC_CLK_I2S3 0x4ac
+#define AUDIO_SYNC_CLK_I2S4 0x4b0
+#define AUDIO_SYNC_CLK_SPDIF 0x4b4
+#define AUDIO_SYNC_CLK_DMIC1 0x560
+#define AUDIO_SYNC_CLK_DMIC2 0x564
+#define AUDIO_SYNC_CLK_DMIC3 0x6b8
+
+#define AUDIO_SYNC_DOUBLER 0x49c
+
+#define PLLA_OUT 0xb4
+
+struct tegra_sync_source_initdata {
+ char *name;
+ unsigned long rate;
+ unsigned long max_rate;
+ int clk_id;
+};
+
+#define SYNC(_name) \
+ {\
+ .name = #_name,\
+ .clk_id = tegra_clk_ ## _name,\
+ }
+
+struct tegra_audio_clk_initdata {
+ char *gate_name;
+ char *mux_name;
+ u32 offset;
+ int gate_clk_id;
+ int mux_clk_id;
+};
+
+#define AUDIO(_name, _offset) \
+ {\
+ .gate_name = #_name,\
+ .mux_name = #_name"_mux",\
+ .offset = _offset,\
+ .gate_clk_id = tegra_clk_ ## _name,\
+ .mux_clk_id = tegra_clk_ ## _name ## _mux,\
+ }
+
+struct tegra_audio2x_clk_initdata {
+ char *parent;
+ char *gate_name;
+ char *name_2x;
+ char *div_name;
+ int clk_id;
+ int clk_num;
+ u8 div_offset;
+};
+
+#define AUDIO2X(_name, _num, _offset) \
+ {\
+ .parent = #_name,\
+ .gate_name = #_name"_2x",\
+ .name_2x = #_name"_doubler",\
+ .div_name = #_name"_div",\
+ .clk_id = tegra_clk_ ## _name ## _2x,\
+ .clk_num = _num,\
+ .div_offset = _offset,\
+ }
+
+static DEFINE_SPINLOCK(clk_doubler_lock);
+
+static const char * const mux_audio_sync_clk[] = { "spdif_in_sync",
+ "i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync",
+ "pll_a_out0", "vimclk_sync",
+};
+
+static const char * const mux_dmic_sync_clk[] = { "unused", "i2s0_sync",
+ "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "pll_a_out0",
+ "vimclk_sync",
+};
+
+static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
+ SYNC(spdif_in_sync),
+ SYNC(i2s0_sync),
+ SYNC(i2s1_sync),
+ SYNC(i2s2_sync),
+ SYNC(i2s3_sync),
+ SYNC(i2s4_sync),
+ SYNC(vimclk_sync),
+};
+
+static struct tegra_audio_clk_initdata audio_clks[] = {
+ AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
+ AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
+ AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
+ AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
+ AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
+ AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
+};
+
+static struct tegra_audio_clk_initdata dmic_clks[] = {
+ AUDIO(dmic1_sync_clk, AUDIO_SYNC_CLK_DMIC1),
+ AUDIO(dmic2_sync_clk, AUDIO_SYNC_CLK_DMIC2),
+ AUDIO(dmic3_sync_clk, AUDIO_SYNC_CLK_DMIC3),
+};
+
+static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
+ AUDIO2X(audio0, 113, 24),
+ AUDIO2X(audio1, 114, 25),
+ AUDIO2X(audio2, 115, 26),
+ AUDIO2X(audio3, 116, 27),
+ AUDIO2X(audio4, 117, 28),
+ AUDIO2X(spdif, 118, 29),
+};
+
+static void __init tegra_audio_sync_clk_init(void __iomem *clk_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_audio_clk_initdata *sync,
+ int num_sync_clks,
+ const char * const *mux_names,
+ int num_mux_inputs)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+ struct tegra_audio_clk_initdata *data;
+ int i;
+
+ for (i = 0, data = sync; i < num_sync_clks; i++, data++) {
+ dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = clk_register_mux(NULL, data->mux_name, mux_names,
+ num_mux_inputs,
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + data->offset, 0, 3, 0,
+ NULL);
+ *dt_clk = clk;
+
+ dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
+ 0, clk_base + data->offset, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ *dt_clk = clk;
+ }
+}
+
+void __init tegra_audio_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base, struct tegra_clk *tegra_clks,
+ struct tegra_audio_clk_info *audio_info,
+ unsigned int num_plls, unsigned long sync_max_rate)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+ int i;
+
+ if (!audio_info || num_plls < 1) {
+ pr_err("No audio data passed to tegra_audio_clk_init\n");
+ WARN_ON(1);
+ return;
+ }
+
+ for (i = 0; i < num_plls; i++) {
+ struct tegra_audio_clk_info *info = &audio_info[i];
+
+ dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
+ if (dt_clk) {
+ clk = tegra_clk_register_pll(info->name, info->parent,
+ clk_base, pmc_base, 0, info->pll_params,
+ NULL);
+ *dt_clk = clk;
+ }
+ }
+
+ /* PLLA_OUT0 */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
+ if (dt_clk) {
+ clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
+ clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
+ clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ *dt_clk = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
+ struct tegra_sync_source_initdata *data;
+
+ data = &sync_source_clks[i];
+
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = tegra_clk_register_sync_source(data->name, sync_max_rate);
+ *dt_clk = clk;
+ }
+
+ tegra_audio_sync_clk_init(clk_base, tegra_clks, audio_clks,
+ ARRAY_SIZE(audio_clks), mux_audio_sync_clk,
+ ARRAY_SIZE(mux_audio_sync_clk));
+
+ /* make sure the DMIC sync clocks have a valid parent */
+ for (i = 0; i < ARRAY_SIZE(dmic_clks); i++)
+ writel_relaxed(1, clk_base + dmic_clks[i].offset);
+
+ tegra_audio_sync_clk_init(clk_base, tegra_clks, dmic_clks,
+ ARRAY_SIZE(dmic_clks), mux_dmic_sync_clk,
+ ARRAY_SIZE(mux_dmic_sync_clk));
+
+ for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
+ struct tegra_audio2x_clk_initdata *data;
+
+ data = &audio2x_clks[i];
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = clk_register_fixed_factor(NULL, data->name_2x,
+ data->parent, CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_divider(data->div_name,
+ data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
+ 0, 0, data->div_offset, 1, 0,
+ &clk_doubler_lock);
+ clk = tegra_clk_register_periph_gate(data->gate_name,
+ data->div_name, TEGRA_PERIPH_NO_RESET,
+ clk_base, CLK_SET_RATE_PARENT, data->clk_num,
+ periph_clk_enb_refcnt);
+ *dt_clk = clk;
+ }
+}
+
diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
new file mode 100644
index 000000000..77c22cef5
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra-fixed.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_SHIFT 28
+#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
+#define OSC_CTRL_MASK (0x3f2 | \
+ (0xf << OSC_CTRL_OSC_FREQ_SHIFT))
+
+static u32 osc_ctrl_ctx;
+
+int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
+ unsigned long *input_freqs, unsigned int num,
+ unsigned int clk_m_div, unsigned long *osc_freq,
+ unsigned long *pll_ref_freq)
+{
+ struct clk *clk, *osc;
+ struct clk **dt_clk;
+ u32 val, pll_ref_div;
+ unsigned osc_idx;
+
+ val = readl_relaxed(clk_base + OSC_CTRL);
+ osc_ctrl_ctx = val & OSC_CTRL_MASK;
+ osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
+
+ if (osc_idx < num)
+ *osc_freq = input_freqs[osc_idx];
+ else
+ *osc_freq = 0;
+
+ if (!*osc_freq) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_osc, clks);
+ if (!dt_clk)
+ return 0;
+
+ osc = clk_register_fixed_rate(NULL, "osc", NULL, 0, *osc_freq);
+ *dt_clk = osc;
+
+ /* osc_div2 */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_osc_div2, clks);
+ if (dt_clk) {
+ clk = clk_register_fixed_factor(NULL, "osc_div2", "osc",
+ 0, 1, 2);
+ *dt_clk = clk;
+ }
+
+ /* osc_div4 */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_osc_div4, clks);
+ if (dt_clk) {
+ clk = clk_register_fixed_factor(NULL, "osc_div4", "osc",
+ 0, 1, 4);
+ *dt_clk = clk;
+ }
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, clks);
+ if (!dt_clk)
+ return 0;
+
+ clk = clk_register_fixed_factor(NULL, "clk_m", "osc",
+ 0, 1, clk_m_div);
+ *dt_clk = clk;
+
+ /* pll_ref */
+ val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
+ pll_ref_div = 1 << val;
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, clks);
+ if (!dt_clk)
+ return 0;
+
+ clk = clk_register_fixed_factor(NULL, "pll_ref", "osc",
+ 0, 1, pll_ref_div);
+ *dt_clk = clk;
+
+ if (pll_ref_freq)
+ *pll_ref_freq = *osc_freq / pll_ref_div;
+
+ return 0;
+}
+
+void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ /* clk_32k */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_clk_32k, tegra_clks);
+ if (dt_clk) {
+ clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, 0, 32768);
+ *dt_clk = clk;
+ }
+}
+
+void tegra_clk_osc_resume(void __iomem *clk_base)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
+ val |= osc_ctrl_ctx;
+ writel_relaxed(val, clk_base + OSC_CTRL);
+ fence_udelay(2, clk_base);
+}
diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c
new file mode 100644
index 000000000..4dcf7f7cb
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra-periph.c
@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define CLK_SOURCE_I2S0 0x1d8
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_NDFLASH 0x160
+#define CLK_SOURCE_I2S3 0x3bc
+#define CLK_SOURCE_I2S4 0x3c0
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_SPDIF_IN 0x10c
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_ADX 0x638
+#define CLK_SOURCE_ADX1 0x670
+#define CLK_SOURCE_AMX 0x63c
+#define CLK_SOURCE_AMX1 0x674
+#define CLK_SOURCE_HDA 0x428
+#define CLK_SOURCE_HDA2CODEC_2X 0x3e4
+#define CLK_SOURCE_SBC1 0x134
+#define CLK_SOURCE_SBC2 0x118
+#define CLK_SOURCE_SBC3 0x11c
+#define CLK_SOURCE_SBC4 0x1b4
+#define CLK_SOURCE_SBC5 0x3c8
+#define CLK_SOURCE_SBC6 0x3cc
+#define CLK_SOURCE_SATA_OOB 0x420
+#define CLK_SOURCE_SATA 0x424
+#define CLK_SOURCE_NDSPEED 0x3f8
+#define CLK_SOURCE_VFIR 0x168
+#define CLK_SOURCE_SDMMC1 0x150
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC3 0x1bc
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_CVE 0x140
+#define CLK_SOURCE_TVO 0x188
+#define CLK_SOURCE_TVDAC 0x194
+#define CLK_SOURCE_VDE 0x1c8
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_LA 0x1f8
+#define CLK_SOURCE_TRACE 0x634
+#define CLK_SOURCE_OWR 0x1cc
+#define CLK_SOURCE_NOR 0x1d0
+#define CLK_SOURCE_MIPI 0x174
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_I2C4 0x3c4
+#define CLK_SOURCE_I2C5 0x128
+#define CLK_SOURCE_I2C6 0x65c
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_UARTE 0x1c4
+#define CLK_SOURCE_3D 0x158
+#define CLK_SOURCE_2D 0x15c
+#define CLK_SOURCE_MPE 0x170
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+#define CLK_SOURCE_VI 0x148
+#define CLK_SOURCE_EPP 0x16c
+#define CLK_SOURCE_MSENC 0x1f0
+#define CLK_SOURCE_TSEC 0x1f4
+#define CLK_SOURCE_HOST1X 0x180
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_CILAB 0x614
+#define CLK_SOURCE_CILCD 0x618
+#define CLK_SOURCE_CILE 0x61c
+#define CLK_SOURCE_DSIALP 0x620
+#define CLK_SOURCE_DSIBLP 0x624
+#define CLK_SOURCE_TSENSOR 0x3b8
+#define CLK_SOURCE_D_AUDIO 0x3d0
+#define CLK_SOURCE_DAM0 0x3d8
+#define CLK_SOURCE_DAM1 0x3dc
+#define CLK_SOURCE_DAM2 0x3e0
+#define CLK_SOURCE_ACTMON 0x3e8
+#define CLK_SOURCE_EXTERN1 0x3ec
+#define CLK_SOURCE_EXTERN2 0x3f0
+#define CLK_SOURCE_EXTERN3 0x3f4
+#define CLK_SOURCE_I2CSLOW 0x3fc
+#define CLK_SOURCE_SE 0x42c
+#define CLK_SOURCE_MSELECT 0x3b4
+#define CLK_SOURCE_DFLL_REF 0x62c
+#define CLK_SOURCE_DFLL_SOC 0x630
+#define CLK_SOURCE_SOC_THERM 0x644
+#define CLK_SOURCE_XUSB_HOST_SRC 0x600
+#define CLK_SOURCE_XUSB_FALCON_SRC 0x604
+#define CLK_SOURCE_XUSB_FS_SRC 0x608
+#define CLK_SOURCE_XUSB_SS_SRC 0x610
+#define CLK_SOURCE_XUSB_DEV_SRC 0x60c
+#define CLK_SOURCE_ISP 0x144
+#define CLK_SOURCE_SOR0 0x414
+#define CLK_SOURCE_DPAUX 0x418
+#define CLK_SOURCE_ENTROPY 0x628
+#define CLK_SOURCE_VI_SENSOR2 0x658
+#define CLK_SOURCE_HDMI_AUDIO 0x668
+#define CLK_SOURCE_VIC03 0x678
+#define CLK_SOURCE_CLK72MHZ 0x66c
+#define CLK_SOURCE_DBGAPB 0x718
+#define CLK_SOURCE_NVENC 0x6a0
+#define CLK_SOURCE_NVDEC 0x698
+#define CLK_SOURCE_NVJPG 0x69c
+#define CLK_SOURCE_APE 0x6c0
+#define CLK_SOURCE_SDMMC_LEGACY 0x694
+#define CLK_SOURCE_QSPI 0x6c4
+#define CLK_SOURCE_VI_I2C 0x6c8
+#define CLK_SOURCE_MIPIBIF 0x660
+#define CLK_SOURCE_UARTAPE 0x710
+#define CLK_SOURCE_TSECB 0x6d8
+#define CLK_SOURCE_MAUD 0x6d4
+#define CLK_SOURCE_USB2_HSIC_TRK 0x6cc
+#define CLK_SOURCE_DMIC1 0x64c
+#define CLK_SOURCE_DMIC2 0x650
+#define CLK_SOURCE_DMIC3 0x6bc
+
+#define MASK(x) (BIT(x) - 1)
+
+#define MUX(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
+ _clk_num, _gate_flags, _clk_id, _parents##_idx, 0,\
+ NULL)
+
+#define MUX_FLAGS(_name, _parents, _offset,\
+ _clk_num, _gate_flags, _clk_id, flags)\
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ _clk_num, _gate_flags, _clk_id, _parents##_idx, flags,\
+ NULL)
+
+#define MUX8(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ _clk_num, _gate_flags, _clk_id, _parents##_idx, 0,\
+ NULL)
+
+#define MUX8_NOGATE_LOCK(_name, _parents, _offset, _clk_id, _lock) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ 0, TEGRA_PERIPH_NO_GATE, _clk_id,\
+ _parents##_idx, 0, _lock)
+
+#define MUX8_NOGATE(_name, _parents, _offset, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ 0, TEGRA_PERIPH_NO_GATE, _clk_id,\
+ _parents##_idx, 0, NULL)
+
+#define INT(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
+ _clk_id, _parents##_idx, 0, NULL)
+
+#define INT_FLAGS(_name, _parents, _offset,\
+ _clk_num, _gate_flags, _clk_id, flags)\
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
+ _clk_id, _parents##_idx, flags, NULL)
+
+#define INT8(_name, _parents, _offset,\
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
+ _clk_id, _parents##_idx, 0, NULL)
+
+#define UART(_name, _parents, _offset,\
+ _clk_num, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 16, 1, TEGRA_DIVIDER_UART| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, 0, _clk_id,\
+ _parents##_idx, 0, NULL)
+
+#define UART8(_name, _parents, _offset,\
+ _clk_num, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 29, MASK(3), 0, 0, 16, 1, TEGRA_DIVIDER_UART| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, 0, _clk_id,\
+ _parents##_idx, 0, NULL)
+
+#define I2C(_name, _parents, _offset,\
+ _clk_num, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 30, MASK(2), 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP,\
+ _clk_num, TEGRA_PERIPH_ON_APB, _clk_id, \
+ _parents##_idx, 0, NULL)
+
+#define XUSB(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_INT| \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags,\
+ _clk_id, _parents##_idx, 0, NULL)
+
+#define AUDIO(_name, _offset, _clk_num,\
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, mux_d_audio_clk, \
+ _offset, 16, 0xE01F, 0, 0, 8, 1, \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, _gate_flags, \
+ _clk_id, mux_d_audio_clk_idx, 0, NULL)
+
+#define NODIV(_name, _parents, _offset, \
+ _mux_shift, _mux_mask, _clk_num, \
+ _gate_flags, _clk_id, _lock) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ _mux_shift, _mux_mask, 0, 0, 0, 0, 0,\
+ _clk_num, (_gate_flags) | TEGRA_PERIPH_NO_DIV,\
+ _clk_id, _parents##_idx, 0, _lock)
+
+#define GATE(_name, _parent_name, \
+ _clk_num, _gate_flags, _clk_id, _flags) \
+ { \
+ .name = _name, \
+ .clk_id = _clk_id, \
+ .p.parent_name = _parent_name, \
+ .periph = TEGRA_CLK_PERIPH(0, 0, 0, 0, 0, 0, 0, \
+ _clk_num, _gate_flags, NULL, NULL), \
+ .flags = _flags \
+ }
+
+#define DIV8(_name, _parent_name, _offset, _clk_id, _flags) \
+ { \
+ .name = _name, \
+ .clk_id = _clk_id, \
+ .p.parent_name = _parent_name, \
+ .periph = TEGRA_CLK_PERIPH(0, 0, 0, 0, 8, 1, \
+ TEGRA_DIVIDER_ROUND_UP, 0, 0, \
+ NULL, NULL), \
+ .offset = _offset, \
+ .flags = _flags, \
+ }
+
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLP_MISC1 0x680
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLP_OUTC 0x67c
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLL_MISC_LOCK_ENABLE 18
+
+static DEFINE_SPINLOCK(PLLP_OUTA_lock);
+static DEFINE_SPINLOCK(PLLP_OUTB_lock);
+static DEFINE_SPINLOCK(PLLP_OUTC_lock);
+
+#define MUX_I2S_SPDIF(_id) \
+static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \
+ #_id, "pll_p",\
+ "clk_m"};
+MUX_I2S_SPDIF(audio0)
+MUX_I2S_SPDIF(audio1)
+MUX_I2S_SPDIF(audio2)
+MUX_I2S_SPDIF(audio3)
+MUX_I2S_SPDIF(audio4)
+MUX_I2S_SPDIF(audio)
+
+#define mux_pllaout0_audio0_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio1_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio2_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio3_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio4_2x_pllp_clkm_idx NULL
+#define mux_pllaout0_audio_2x_pllp_clkm_idx NULL
+
+static const char *mux_pllp_pllc_pllm_clkm[] = {
+ "pll_p", "pll_c", "pll_m", "clk_m"
+};
+#define mux_pllp_pllc_pllm_clkm_idx NULL
+
+static const char *mux_pllp_pllc_pllm[] = { "pll_p", "pll_c", "pll_m" };
+#define mux_pllp_pllc_pllm_idx NULL
+
+static const char *mux_pllp_pllc_clk32_clkm[] = {
+ "pll_p", "pll_c", "clk_32k", "clk_m"
+};
+#define mux_pllp_pllc_clk32_clkm_idx NULL
+
+static const char *mux_plla_pllc_pllp_clkm[] = {
+ "pll_a_out0", "pll_c", "pll_p", "clk_m"
+};
+#define mux_plla_pllc_pllp_clkm_idx mux_pllp_pllc_pllm_clkm_idx
+
+static const char *mux_pllp_pllc2_c_c3_pllm_clkm[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_m", "clk_m"
+};
+static u32 mux_pllp_pllc2_c_c3_pllm_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6,
+};
+
+static const char *mux_pllp_clkm[] = {
+ "pll_p", "clk_m"
+};
+static u32 mux_pllp_clkm_idx[] = {
+ [0] = 0, [1] = 3,
+};
+
+static const char *mux_pllp_clkm_2[] = {
+ "pll_p", "clk_m"
+};
+static u32 mux_pllp_clkm_2_idx[] = {
+ [0] = 2, [1] = 6,
+};
+
+static const char *mux_pllc2_c_c3_pllp_plla1_clkm[] = {
+ "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a1", "clk_m"
+};
+static u32 mux_pllc2_c_c3_pllp_plla1_clkm_idx[] = {
+ [0] = 1, [1] = 2, [2] = 3, [3] = 4, [4] = 6, [5] = 7,
+};
+
+static const char *
+mux_pllc4_out1_pllc_pllc4_out2_pllp_clkm_plla_pllc4_out0[] = {
+ "pll_c4_out1", "pll_c", "pll_c4_out2", "pll_p", "clk_m",
+ "pll_a_out0", "pll_c4_out0"
+};
+static u32 mux_pllc4_out1_pllc_pllc4_out2_pllp_clkm_plla_pllc4_out0_idx[] = {
+ [0] = 0, [1] = 2, [2] = 3, [3] = 4, [4] = 5, [5] = 6, [6] = 7,
+};
+
+static const char *mux_pllc_pllp_plla[] = {
+ "pll_c", "pll_p", "pll_a_out0"
+};
+static u32 mux_pllc_pllp_plla_idx[] = {
+ [0] = 1, [1] = 2, [2] = 3,
+};
+
+static const char *mux_clkm_pllc_pllp_plla[] = {
+ "clk_m", "pll_c", "pll_p", "pll_a_out0"
+};
+#define mux_clkm_pllc_pllp_plla_idx NULL
+
+static const char *mux_pllc_pllp_plla1_pllc2_c3_clkm[] = {
+ "pll_c", "pll_p", "pll_a1", "pll_c2", "pll_c3", "clk_m"
+};
+static u32 mux_pllc_pllp_plla1_pllc2_c3_clkm_idx[] = {
+ [0] = 1, [1] = 2, [2] = 3, [3] = 4, [4] = 5, [5] = 6,
+};
+
+static const char *mux_pllc2_c_c3_pllp_clkm_plla1_pllc4[] = {
+ "pll_c2", "pll_c", "pll_c3", "pll_p", "clk_m", "pll_a1", "pll_c4_out0",
+};
+static u32 mux_pllc2_c_c3_pllp_clkm_plla1_pllc4_idx[] = {
+ [0] = 1, [1] = 2, [2] = 3, [3] = 4, [4] = 5, [5] = 6, [6] = 7,
+};
+
+static const char *mux_pllc_pllp_plla1_pllc2_c3_clkm_pllc4[] = {
+ "pll_c", "pll_p", "pll_a1", "pll_c2", "pll_c3", "clk_m", "pll_c4_out0",
+};
+#define mux_pllc_pllp_plla1_pllc2_c3_clkm_pllc4_idx \
+ mux_pllc2_c_c3_pllp_clkm_plla1_pllc4_idx
+
+static const char *
+mux_plla_pllc4_out0_pllc_pllc4_out1_pllp_pllc4_out2_clkm[] = {
+ "pll_a_out0", "pll_c4_out0", "pll_c", "pll_c4_out1", "pll_p",
+ "pll_c4_out2", "clk_m"
+};
+#define mux_plla_pllc4_out0_pllc_pllc4_out1_pllp_pllc4_out2_clkm_idx NULL
+
+static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = {
+ "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0"
+};
+#define mux_pllm_pllc2_c_c3_pllp_plla_idx mux_pllp_pllc2_c_c3_pllm_clkm_idx
+
+static const char *mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = {
+ "pll_p", "pll_m", "pll_d_out0", "pll_a_out0", "pll_c",
+ "pll_d2_out0", "clk_m"
+};
+#define mux_pllp_pllm_plld_plla_pllc_plld2_clkm_idx NULL
+
+static const char *mux_pllm_pllc_pllp_plla[] = {
+ "pll_m", "pll_c", "pll_p", "pll_a_out0"
+};
+#define mux_pllm_pllc_pllp_plla_idx mux_pllp_pllc_pllm_clkm_idx
+
+static const char *mux_pllp_pllc_clkm[] = {
+ "pll_p", "pll_c", "clk_m"
+};
+static u32 mux_pllp_pllc_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3,
+};
+
+static const char *mux_pllp_pllc_clkm_1[] = {
+ "pll_p", "pll_c", "clk_m"
+};
+static u32 mux_pllp_pllc_clkm_1_idx[] = {
+ [0] = 0, [1] = 2, [2] = 5,
+};
+
+static const char *mux_pllp_pllc_plla_clkm[] = {
+ "pll_p", "pll_c", "pll_a_out0", "clk_m"
+};
+static u32 mux_pllp_pllc_plla_clkm_idx[] = {
+ [0] = 0, [1] = 2, [2] = 4, [3] = 6,
+};
+
+static const char *mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2[] = {
+ "pll_p", "pll_c", "pll_c4_out0", "pll_c4_out1", "clk_m", "pll_c4_out2"
+};
+static u32 mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2_idx[] = {
+ [0] = 0, [1] = 2, [2] = 3, [3] = 5, [4] = 6, [5] = 7,
+};
+
+static const char *
+mux_pllp_pllc_pllc_out1_pllc4_out2_pllc4_out1_clkm_pllc4_out0[] = {
+ "pll_p", "pll_c_out1", "pll_c", "pll_c4_out2", "pll_c4_out1",
+ "clk_m", "pll_c4_out0"
+};
+static u32
+mux_pllp_pllc_pllc_out1_pllc4_out2_pllc4_out1_clkm_pllc4_out0_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 4, [4] = 5, [5] = 6, [6] = 7,
+};
+
+static const char *mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0[] = {
+ "pll_p", "pll_c4_out2", "pll_c4_out1", "clk_m", "pll_c4_out0"
+};
+static u32 mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0_idx[] = {
+ [0] = 0, [1] = 3, [2] = 4, [3] = 6, [4] = 7,
+};
+
+static const char *mux_pllp_pllc2_c_c3_clkm[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "clk_m"
+};
+static u32 mux_pllp_pllc2_c_c3_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 6,
+};
+
+static const char *mux_pllp_clkm_clk32_plle[] = {
+ "pll_p", "clk_m", "clk_32k", "pll_e"
+};
+static u32 mux_pllp_clkm_clk32_plle_idx[] = {
+ [0] = 0, [1] = 2, [2] = 4, [3] = 6,
+};
+
+static const char *mux_pllp_pllp_out3_clkm_clk32k_plla[] = {
+ "pll_p", "pll_p_out3", "clk_m", "clk_32k", "pll_a_out0"
+};
+#define mux_pllp_pllp_out3_clkm_clk32k_plla_idx NULL
+
+static const char *mux_pllp_out3_clkm_pllp_pllc4[] = {
+ "pll_p_out3", "clk_m", "pll_p", "pll_c4_out0", "pll_c4_out1",
+ "pll_c4_out2"
+};
+static u32 mux_pllp_out3_clkm_pllp_pllc4_idx[] = {
+ [0] = 0, [1] = 3, [2] = 4, [3] = 5, [4] = 6, [5] = 7,
+};
+
+static const char *mux_clkm_pllp_pllre[] = {
+ "clk_m", "pll_p_out_xusb", "pll_re_out"
+};
+static u32 mux_clkm_pllp_pllre_idx[] = {
+ [0] = 0, [1] = 1, [2] = 5,
+};
+
+static const char *mux_pllp_pllc_clkm_clk32[] = {
+ "pll_p", "pll_c", "clk_m", "clk_32k"
+};
+#define mux_pllp_pllc_clkm_clk32_idx NULL
+
+static const char *mux_plla_clk32_pllp_clkm_plle[] = {
+ "pll_a_out0", "clk_32k", "pll_p", "clk_m", "pll_e_out0"
+};
+#define mux_plla_clk32_pllp_clkm_plle_idx NULL
+
+static const char *mux_clkm_pllp_pllc_pllre[] = {
+ "clk_m", "pll_p", "pll_c", "pll_re_out"
+};
+static u32 mux_clkm_pllp_pllc_pllre_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3, [3] = 5,
+};
+
+static const char *mux_clkm_48M_pllp_480M[] = {
+ "clk_m", "pll_u_48M", "pll_p", "pll_u_480M"
+};
+static u32 mux_clkm_48M_pllp_480M_idx[] = {
+ [0] = 0, [1] = 2, [2] = 4, [3] = 6,
+};
+
+static const char *mux_clkm_pllre_clk32_480M[] = {
+ "clk_m", "pll_re_out", "clk_32k", "pll_u_480M"
+};
+#define mux_clkm_pllre_clk32_480M_idx NULL
+
+static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = {
+ "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref"
+};
+static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7,
+};
+
+static const char *mux_pllp_out3_pllp_pllc_clkm[] = {
+ "pll_p_out3", "pll_p", "pll_c", "clk_m"
+};
+static u32 mux_pllp_out3_pllp_pllc_clkm_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 6,
+};
+
+static const char *mux_ss_div2_60M[] = {
+ "xusb_ss_div2", "pll_u_60M"
+};
+#define mux_ss_div2_60M_idx NULL
+
+static const char *mux_ss_div2_60M_ss[] = {
+ "xusb_ss_div2", "pll_u_60M", "xusb_ss_src"
+};
+#define mux_ss_div2_60M_ss_idx NULL
+
+static const char *mux_ss_clkm[] = {
+ "xusb_ss_src", "clk_m"
+};
+#define mux_ss_clkm_idx NULL
+
+static const char *mux_d_audio_clk[] = {
+ "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync",
+ "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
+};
+static u32 mux_d_audio_clk_idx[] = {
+ [0] = 0, [1] = 0x8000, [2] = 0xc000, [3] = 0xE000, [4] = 0xE001,
+ [5] = 0xE002, [6] = 0xE003, [7] = 0xE004, [8] = 0xE005, [9] = 0xE007,
+};
+
+static const char *mux_pllp_plld_pllc_clkm[] = {
+ "pll_p", "pll_d_out0", "pll_c", "clk_m"
+};
+#define mux_pllp_plld_pllc_clkm_idx NULL
+static const char *mux_pllm_pllc_pllp_plla_clkm_pllc4[] = {
+ "pll_m", "pll_c", "pll_p", "pll_a_out0", "clk_m", "pll_c4",
+};
+static u32 mux_pllm_pllc_pllp_plla_clkm_pllc4_idx[] = {
+ [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 6, [5] = 7,
+};
+
+static const char *mux_pllp_clkm1[] = {
+ "pll_p", "clk_m",
+};
+#define mux_pllp_clkm1_idx NULL
+
+static const char *mux_pllp3_pllc_clkm[] = {
+ "pll_p_out3", "pll_c", "pll_c2", "clk_m",
+};
+#define mux_pllp3_pllc_clkm_idx NULL
+
+static const char *mux_pllm_pllc_pllp_plla_pllc2_c3_clkm[] = {
+ "pll_m", "pll_c", "pll_p", "pll_a", "pll_c2", "pll_c3", "clk_m"
+};
+#define mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx NULL
+
+static const char *mux_pllm_pllc2_c_c3_pllp_plla_pllc4[] = {
+ "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0", "pll_c4",
+};
+static u32 mux_pllm_pllc2_c_c3_pllp_plla_pllc4_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, [6] = 7,
+};
+
+/* SOR1 mux'es */
+static const char *mux_pllp_plld_plld2_clkm[] = {
+ "pll_p", "pll_d_out0", "pll_d2_out0", "clk_m"
+};
+static u32 mux_pllp_plld_plld2_clkm_idx[] = {
+ [0] = 0, [1] = 2, [2] = 5, [3] = 6
+};
+
+static const char *mux_pllp_pllre_clkm[] = {
+ "pll_p", "pll_re_out1", "clk_m"
+};
+
+static u32 mux_pllp_pllre_clkm_idx[] = {
+ [0] = 0, [1] = 2, [2] = 3,
+};
+
+static const char * const mux_dmic1[] = {
+ "pll_a_out0", "dmic1_sync_clk", "pll_p", "clk_m"
+};
+#define mux_dmic1_idx NULL
+
+static const char * const mux_dmic2[] = {
+ "pll_a_out0", "dmic2_sync_clk", "pll_p", "clk_m"
+};
+#define mux_dmic2_idx NULL
+
+static const char * const mux_dmic3[] = {
+ "pll_a_out0", "dmic3_sync_clk", "pll_p", "clk_m"
+};
+#define mux_dmic3_idx NULL
+
+static struct tegra_periph_init_data periph_clks[] = {
+ AUDIO("d_audio", CLK_SOURCE_D_AUDIO, 106, TEGRA_PERIPH_ON_APB, tegra_clk_d_audio),
+ AUDIO("dam0", CLK_SOURCE_DAM0, 108, TEGRA_PERIPH_ON_APB, tegra_clk_dam0),
+ AUDIO("dam1", CLK_SOURCE_DAM1, 109, TEGRA_PERIPH_ON_APB, tegra_clk_dam1),
+ AUDIO("dam2", CLK_SOURCE_DAM2, 110, TEGRA_PERIPH_ON_APB, tegra_clk_dam2),
+ I2C("i2c1", mux_pllp_clkm, CLK_SOURCE_I2C1, 12, tegra_clk_i2c1),
+ I2C("i2c2", mux_pllp_clkm, CLK_SOURCE_I2C2, 54, tegra_clk_i2c2),
+ I2C("i2c3", mux_pllp_clkm, CLK_SOURCE_I2C3, 67, tegra_clk_i2c3),
+ I2C("i2c4", mux_pllp_clkm, CLK_SOURCE_I2C4, 103, tegra_clk_i2c4),
+ I2C("i2c5", mux_pllp_clkm, CLK_SOURCE_I2C5, 47, tegra_clk_i2c5),
+ I2C("i2c6", mux_pllp_clkm, CLK_SOURCE_I2C6, 166, tegra_clk_i2c6),
+ INT("vde", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VDE, 61, 0, tegra_clk_vde),
+ INT("vi", mux_pllm_pllc_pllp_plla, CLK_SOURCE_VI, 20, 0, tegra_clk_vi),
+ INT("epp", mux_pllm_pllc_pllp_plla, CLK_SOURCE_EPP, 19, 0, tegra_clk_epp),
+ INT("host1x", mux_pllm_pllc_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x),
+ INT("mpe", mux_pllm_pllc_pllp_plla, CLK_SOURCE_MPE, 60, 0, tegra_clk_mpe),
+ INT("2d", mux_pllm_pllc_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d),
+ INT("3d", mux_pllm_pllc_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d),
+ INT8("vde", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_VDE, 61, 0, tegra_clk_vde_8),
+ INT8("vi", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, 0, tegra_clk_vi_8),
+ INT8("vi", mux_pllm_pllc2_c_c3_pllp_plla_pllc4, CLK_SOURCE_VI, 20, 0, tegra_clk_vi_9),
+ INT8("vi", mux_pllc2_c_c3_pllp_clkm_plla1_pllc4, CLK_SOURCE_VI, 20, 0, tegra_clk_vi_10),
+ INT8("epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, 0, tegra_clk_epp_8),
+ INT8("msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, TEGRA_PERIPH_WAR_1005168, tegra_clk_msenc),
+ INT8("tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, 0, tegra_clk_tsec),
+ INT("tsec", mux_pllp_pllc_clkm, CLK_SOURCE_TSEC, 83, 0, tegra_clk_tsec_8),
+ INT8("host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_8),
+ INT8("host1x", mux_pllc4_out1_pllc_pllc4_out2_pllp_clkm_plla_pllc4_out0, CLK_SOURCE_HOST1X, 28, 0, tegra_clk_host1x_9),
+ INT8("se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se),
+ INT8("se", mux_pllp_pllc2_c_c3_clkm, CLK_SOURCE_SE, 127, TEGRA_PERIPH_ON_APB, tegra_clk_se_10),
+ INT8("2d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_2D, 21, 0, tegra_clk_gr2d_8),
+ INT8("3d", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_3D, 24, 0, tegra_clk_gr3d_8),
+ INT8("vic03", mux_pllm_pllc_pllp_plla_pllc2_c3_clkm, CLK_SOURCE_VIC03, 178, 0, tegra_clk_vic03),
+ INT8("vic03", mux_pllc_pllp_plla1_pllc2_c3_clkm, CLK_SOURCE_VIC03, 178, 0, tegra_clk_vic03_8),
+ INT_FLAGS("mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, 0, tegra_clk_mselect, CLK_IGNORE_UNUSED),
+ MUX("i2s0", mux_pllaout0_audio0_2x_pllp_clkm, CLK_SOURCE_I2S0, 30, TEGRA_PERIPH_ON_APB, tegra_clk_i2s0),
+ MUX("i2s1", mux_pllaout0_audio1_2x_pllp_clkm, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, tegra_clk_i2s1),
+ MUX("i2s2", mux_pllaout0_audio2_2x_pllp_clkm, CLK_SOURCE_I2S2, 18, TEGRA_PERIPH_ON_APB, tegra_clk_i2s2),
+ MUX("i2s3", mux_pllaout0_audio3_2x_pllp_clkm, CLK_SOURCE_I2S3, 101, TEGRA_PERIPH_ON_APB, tegra_clk_i2s3),
+ MUX("i2s4", mux_pllaout0_audio4_2x_pllp_clkm, CLK_SOURCE_I2S4, 102, TEGRA_PERIPH_ON_APB, tegra_clk_i2s4),
+ MUX("spdif_out", mux_pllaout0_audio_2x_pllp_clkm, CLK_SOURCE_SPDIF_OUT, 10, TEGRA_PERIPH_ON_APB, tegra_clk_spdif_out),
+ MUX("spdif_in", mux_pllp_pllc_pllm, CLK_SOURCE_SPDIF_IN, 10, TEGRA_PERIPH_ON_APB, tegra_clk_spdif_in),
+ MUX8("spdif_in", mux_pllp_pllc_clkm_1, CLK_SOURCE_SPDIF_IN, 10, TEGRA_PERIPH_ON_APB, tegra_clk_spdif_in_8),
+ MUX("pwm", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_PWM, 17, TEGRA_PERIPH_ON_APB, tegra_clk_pwm),
+ MUX("adx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX, 154, TEGRA_PERIPH_ON_APB, tegra_clk_adx),
+ MUX("amx", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX, 153, TEGRA_PERIPH_ON_APB, tegra_clk_amx),
+ MUX("hda", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA, 125, TEGRA_PERIPH_ON_APB, tegra_clk_hda),
+ MUX("hda", mux_pllp_pllc_clkm, CLK_SOURCE_HDA, 125, TEGRA_PERIPH_ON_APB, tegra_clk_hda_8),
+ MUX("hda2codec_2x", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, TEGRA_PERIPH_ON_APB, tegra_clk_hda2codec_2x),
+ MUX8("hda2codec_2x", mux_pllp_pllc_plla_clkm, CLK_SOURCE_HDA2CODEC_2X, 111, TEGRA_PERIPH_ON_APB, tegra_clk_hda2codec_2x_8),
+ MUX("vfir", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_VFIR, 7, TEGRA_PERIPH_ON_APB, tegra_clk_vfir),
+ MUX("sdmmc1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC1, 14, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc1),
+ MUX("sdmmc2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC2, 9, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc2),
+ MUX("sdmmc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3),
+ MUX("sdmmc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4),
+ MUX8("sdmmc1", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC1, 14, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc1_9),
+ MUX8("sdmmc3", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3_9),
+ MUX("la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, TEGRA_PERIPH_ON_APB, tegra_clk_la),
+ MUX("trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, TEGRA_PERIPH_ON_APB, tegra_clk_trace),
+ MUX("owr", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, TEGRA_PERIPH_ON_APB, tegra_clk_owr),
+ MUX("owr", mux_pllp_pllc_clkm, CLK_SOURCE_OWR, 71, TEGRA_PERIPH_ON_APB, tegra_clk_owr_8),
+ MUX("nor", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NOR, 42, 0, tegra_clk_nor),
+ MUX("mipi", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_MIPI, 50, TEGRA_PERIPH_ON_APB, tegra_clk_mipi),
+ MUX("vi_sensor", mux_pllm_pllc_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor),
+ MUX("vi_sensor", mux_pllc_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor_9),
+ MUX("cilab", mux_pllp_pllc_clkm, CLK_SOURCE_CILAB, 144, 0, tegra_clk_cilab),
+ MUX("cilcd", mux_pllp_pllc_clkm, CLK_SOURCE_CILCD, 145, 0, tegra_clk_cilcd),
+ MUX("cile", mux_pllp_pllc_clkm, CLK_SOURCE_CILE, 146, 0, tegra_clk_cile),
+ MUX("dsialp", mux_pllp_pllc_clkm, CLK_SOURCE_DSIALP, 147, 0, tegra_clk_dsialp),
+ MUX("dsiblp", mux_pllp_pllc_clkm, CLK_SOURCE_DSIBLP, 148, 0, tegra_clk_dsiblp),
+ MUX("tsensor", mux_pllp_pllc_clkm_clk32, CLK_SOURCE_TSENSOR, 100, TEGRA_PERIPH_ON_APB, tegra_clk_tsensor),
+ MUX("actmon", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_ACTMON, 119, 0, tegra_clk_actmon),
+ MUX("dfll_ref", mux_pllp_clkm, CLK_SOURCE_DFLL_REF, 155, TEGRA_PERIPH_ON_APB, tegra_clk_dfll_ref),
+ MUX("dfll_soc", mux_pllp_clkm, CLK_SOURCE_DFLL_SOC, 155, TEGRA_PERIPH_ON_APB, tegra_clk_dfll_soc),
+ MUX("i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, TEGRA_PERIPH_ON_APB, tegra_clk_i2cslow),
+ MUX("sbc1", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1),
+ MUX("sbc2", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2),
+ MUX("sbc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3),
+ MUX("sbc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC4, 68, TEGRA_PERIPH_ON_APB, tegra_clk_sbc4),
+ MUX("sbc5", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC5, 104, TEGRA_PERIPH_ON_APB, tegra_clk_sbc5),
+ MUX("sbc6", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SBC6, 105, TEGRA_PERIPH_ON_APB, tegra_clk_sbc6),
+ MUX("cve", mux_pllp_plld_pllc_clkm, CLK_SOURCE_CVE, 49, 0, tegra_clk_cve),
+ MUX("tvo", mux_pllp_plld_pllc_clkm, CLK_SOURCE_TVO, 49, 0, tegra_clk_tvo),
+ MUX("tvdac", mux_pllp_plld_pllc_clkm, CLK_SOURCE_TVDAC, 53, 0, tegra_clk_tvdac),
+ MUX("ndflash", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NDFLASH, 13, TEGRA_PERIPH_ON_APB, tegra_clk_ndflash),
+ MUX("ndspeed", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_NDSPEED, 80, TEGRA_PERIPH_ON_APB, tegra_clk_ndspeed),
+ MUX("sata_oob", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SATA_OOB, 123, TEGRA_PERIPH_ON_APB, tegra_clk_sata_oob),
+ MUX("sata_oob", mux_pllp_pllc_clkm, CLK_SOURCE_SATA_OOB, 123, TEGRA_PERIPH_ON_APB, tegra_clk_sata_oob_8),
+ MUX("sata", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SATA, 124, TEGRA_PERIPH_ON_APB, tegra_clk_sata),
+ MUX("sata", mux_pllp_pllc_clkm, CLK_SOURCE_SATA, 124, TEGRA_PERIPH_ON_APB, tegra_clk_sata_8),
+ MUX("adx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX1, 180, TEGRA_PERIPH_ON_APB, tegra_clk_adx1),
+ MUX("amx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX1, 185, TEGRA_PERIPH_ON_APB, tegra_clk_amx1),
+ MUX("vi_sensor2", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR2, 165, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor2),
+ MUX("vi_sensor2", mux_pllc_pllp_plla, CLK_SOURCE_VI_SENSOR2, 165, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor2_8),
+ MUX8("sdmmc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC1, 14, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc1_8),
+ MUX8("sdmmc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC2, 9, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc2_8),
+ MUX8("sdmmc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3_8),
+ MUX8("sdmmc4", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4_8),
+ MUX8("sbc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1_8),
+ MUX8("sbc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2_8),
+ MUX8("sbc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3_8),
+ MUX8("sbc4", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC4, 68, TEGRA_PERIPH_ON_APB, tegra_clk_sbc4_8),
+ MUX8("sbc5", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC5, 104, TEGRA_PERIPH_ON_APB, tegra_clk_sbc5_8),
+ MUX8("sbc6", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC6, 105, TEGRA_PERIPH_ON_APB, tegra_clk_sbc6_8),
+ MUX("sbc1", mux_pllp_pllc_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1_9),
+ MUX("sbc2", mux_pllp_pllc_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2_9),
+ MUX("sbc3", mux_pllp_pllc_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3_9),
+ MUX("sbc4", mux_pllp_pllc_clkm, CLK_SOURCE_SBC4, 68, TEGRA_PERIPH_ON_APB, tegra_clk_sbc4_9),
+ MUX8("ndflash", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDFLASH, 13, TEGRA_PERIPH_ON_APB, tegra_clk_ndflash_8),
+ MUX8("ndspeed", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_NDSPEED, 80, TEGRA_PERIPH_ON_APB, tegra_clk_ndspeed_8),
+ MUX8("hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, 0, tegra_clk_hdmi),
+ MUX8("extern1", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN1, 120, TEGRA_PERIPH_NO_RESET, tegra_clk_extern1),
+ MUX8("extern2", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN2, 121, TEGRA_PERIPH_NO_RESET, tegra_clk_extern2),
+ MUX8("extern3", mux_plla_clk32_pllp_clkm_plle, CLK_SOURCE_EXTERN3, 122, TEGRA_PERIPH_NO_RESET, tegra_clk_extern3),
+ MUX8("soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, TEGRA_PERIPH_ON_APB, tegra_clk_soc_therm),
+ MUX8("soc_therm", mux_clkm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, TEGRA_PERIPH_ON_APB, tegra_clk_soc_therm_8),
+ MUX8("vi_sensor", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 164, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor_8),
+ MUX8("isp", mux_pllm_pllc_pllp_plla_clkm_pllc4, CLK_SOURCE_ISP, 23, TEGRA_PERIPH_ON_APB, tegra_clk_isp_8),
+ MUX8_NOGATE("isp", mux_pllc_pllp_plla1_pllc2_c3_clkm_pllc4, CLK_SOURCE_ISP, tegra_clk_isp_9),
+ MUX8("entropy", mux_pllp_clkm1, CLK_SOURCE_ENTROPY, 149, 0, tegra_clk_entropy),
+ MUX8("entropy", mux_pllp_clkm_clk32_plle, CLK_SOURCE_ENTROPY, 149, 0, tegra_clk_entropy_8),
+ MUX8("hdmi_audio", mux_pllp3_pllc_clkm, CLK_SOURCE_HDMI_AUDIO, 176, TEGRA_PERIPH_NO_RESET, tegra_clk_hdmi_audio),
+ MUX8("clk72mhz", mux_pllp3_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz),
+ MUX8("clk72mhz", mux_pllp_out3_pllp_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz_8),
+ MUX_FLAGS("csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite, CLK_IGNORE_UNUSED),
+ MUX_FLAGS("csite", mux_pllp_pllre_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite_8, CLK_IGNORE_UNUSED),
+ NODIV("disp1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1, NULL),
+ NODIV("disp1", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1_8, NULL),
+ NODIV("disp2", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2, NULL),
+ NODIV("disp2", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2_8, NULL),
+ UART("uarta", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, tegra_clk_uarta),
+ UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb),
+ UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc),
+ UART("uartd", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, tegra_clk_uartd),
+ UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 66, tegra_clk_uarte),
+ UART8("uarta", mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2, CLK_SOURCE_UARTA, 6, tegra_clk_uarta_8),
+ UART8("uartb", mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2, CLK_SOURCE_UARTB, 7, tegra_clk_uartb_8),
+ UART8("uartc", mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2, CLK_SOURCE_UARTC, 55, tegra_clk_uartc_8),
+ UART8("uartd", mux_pllp_pllc_pllc4_out0_pllc4_out1_clkm_pllc4_out2, CLK_SOURCE_UARTD, 65, tegra_clk_uartd_8),
+ XUSB("xusb_host_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_host_src),
+ XUSB("xusb_host_src", mux_clkm_pllp_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_host_src_8),
+ XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src),
+ XUSB("xusb_falcon_src", mux_clkm_pllp_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src_8),
+ XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src),
+ XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src),
+ XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src_8),
+ NODIV("xusb_hs_src", mux_ss_div2_60M, CLK_SOURCE_XUSB_SS_SRC, 25, MASK(1), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_hs_src, NULL),
+ NODIV("xusb_hs_src", mux_ss_div2_60M_ss, CLK_SOURCE_XUSB_SS_SRC, 25, MASK(2), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_hs_src_4, NULL),
+ NODIV("xusb_ssp_src", mux_ss_clkm, CLK_SOURCE_XUSB_SS_SRC, 24, MASK(1), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ssp_src, NULL),
+ XUSB("xusb_dev_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src),
+ XUSB("xusb_dev_src", mux_clkm_pllp_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src_8),
+ MUX8("dbgapb", mux_pllp_clkm_2, CLK_SOURCE_DBGAPB, 185, TEGRA_PERIPH_NO_RESET, tegra_clk_dbgapb),
+ MUX8("nvenc", mux_pllc2_c_c3_pllp_plla1_clkm, CLK_SOURCE_NVENC, 219, 0, tegra_clk_nvenc),
+ MUX8("nvdec", mux_pllc2_c_c3_pllp_plla1_clkm, CLK_SOURCE_NVDEC, 194, 0, tegra_clk_nvdec),
+ MUX8("nvjpg", mux_pllc2_c_c3_pllp_plla1_clkm, CLK_SOURCE_NVJPG, 195, 0, tegra_clk_nvjpg),
+ MUX8("ape", mux_plla_pllc4_out0_pllc_pllc4_out1_pllp_pllc4_out2_clkm, CLK_SOURCE_APE, 198, TEGRA_PERIPH_ON_APB, tegra_clk_ape),
+ MUX8("sdmmc_legacy", mux_pllp_out3_clkm_pllp_pllc4, CLK_SOURCE_SDMMC_LEGACY, 193, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_sdmmc_legacy),
+ MUX8("qspi", mux_pllp_pllc_pllc_out1_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_QSPI, 211, TEGRA_PERIPH_ON_APB, tegra_clk_qspi),
+ I2C("vii2c", mux_pllp_pllc_clkm, CLK_SOURCE_VI_I2C, 208, tegra_clk_vi_i2c),
+ MUX("mipibif", mux_pllp_clkm, CLK_SOURCE_MIPIBIF, 173, TEGRA_PERIPH_ON_APB, tegra_clk_mipibif),
+ MUX("uartape", mux_pllp_pllc_clkm, CLK_SOURCE_UARTAPE, 212, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_uartape),
+ MUX8("tsecb", mux_pllp_pllc2_c_c3_clkm, CLK_SOURCE_TSECB, 206, 0, tegra_clk_tsecb),
+ MUX8("maud", mux_pllp_pllp_out3_clkm_clk32k_plla, CLK_SOURCE_MAUD, 202, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_maud),
+ MUX8("dmic1", mux_dmic1, CLK_SOURCE_DMIC1, 161, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_dmic1),
+ MUX8("dmic2", mux_dmic2, CLK_SOURCE_DMIC2, 162, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_dmic2),
+ MUX8("dmic3", mux_dmic3, CLK_SOURCE_DMIC3, 197, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_dmic3),
+};
+
+static struct tegra_periph_init_data gate_clks[] = {
+ GATE("rtc", "clk_32k", 4, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_rtc, 0),
+ GATE("timer", "clk_m", 5, 0, tegra_clk_timer, CLK_IS_CRITICAL),
+ GATE("isp", "clk_m", 23, 0, tegra_clk_isp, 0),
+ GATE("vcp", "clk_m", 29, 0, tegra_clk_vcp, 0),
+ GATE("ahbdma", "hclk", 33, 0, tegra_clk_ahbdma, 0),
+ GATE("apbdma", "pclk", 34, 0, tegra_clk_apbdma, 0),
+ GATE("kbc", "clk_32k", 36, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_kbc, 0),
+ GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0),
+ GATE("fuse_burn", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse_burn, 0),
+ GATE("kfuse", "clk_m", 40, TEGRA_PERIPH_ON_APB, tegra_clk_kfuse, 0),
+ GATE("apbif", "clk_m", 107, TEGRA_PERIPH_ON_APB, tegra_clk_apbif, 0),
+ GATE("hda2hdmi", "clk_m", 128, TEGRA_PERIPH_ON_APB, tegra_clk_hda2hdmi, 0),
+ GATE("bsea", "clk_m", 62, 0, tegra_clk_bsea, 0),
+ GATE("bsev", "clk_m", 63, 0, tegra_clk_bsev, 0),
+ GATE("mipi-cal", "clk72mhz", 56, 0, tegra_clk_mipi_cal, 0),
+ GATE("usbd", "clk_m", 22, 0, tegra_clk_usbd, 0),
+ GATE("usb2", "clk_m", 58, 0, tegra_clk_usb2, 0),
+ GATE("usb3", "clk_m", 59, 0, tegra_clk_usb3, 0),
+ GATE("csi", "pll_p_out3", 52, 0, tegra_clk_csi, 0),
+ GATE("afi", "mselect", 72, 0, tegra_clk_afi, 0),
+ GATE("csus", "clk_m", 92, TEGRA_PERIPH_NO_RESET, tegra_clk_csus, 0),
+ GATE("dds", "clk_m", 150, TEGRA_PERIPH_ON_APB, tegra_clk_dds, 0),
+ GATE("dp2", "clk_m", 152, TEGRA_PERIPH_ON_APB, tegra_clk_dp2, 0),
+ GATE("dtv", "clk_m", 79, TEGRA_PERIPH_ON_APB, tegra_clk_dtv, 0),
+ GATE("xusb_host", "xusb_host_src", 89, 0, tegra_clk_xusb_host, 0),
+ GATE("xusb_ss", "xusb_ss_src", 156, 0, tegra_clk_xusb_ss, 0),
+ GATE("xusb_dev", "xusb_dev_src", 95, 0, tegra_clk_xusb_dev, 0),
+ GATE("emc", "emc_mux", 57, 0, tegra_clk_emc, CLK_IS_CRITICAL),
+ GATE("sata_cold", "clk_m", 129, TEGRA_PERIPH_ON_APB, tegra_clk_sata_cold, 0),
+ GATE("ispa", "isp", 23, 0, tegra_clk_ispa, 0),
+ GATE("ispb", "isp", 3, 0, tegra_clk_ispb, 0),
+ GATE("vim2_clk", "clk_m", 11, 0, tegra_clk_vim2_clk, 0),
+ GATE("pcie", "clk_m", 70, 0, tegra_clk_pcie, 0),
+ GATE("gpu", "pll_ref", 184, 0, tegra_clk_gpu, 0),
+ GATE("pllg_ref", "pll_ref", 189, 0, tegra_clk_pll_g_ref, 0),
+ GATE("hsic_trk", "usb2_hsic_trk", 209, TEGRA_PERIPH_NO_RESET, tegra_clk_hsic_trk, 0),
+ GATE("usb2_trk", "usb2_hsic_trk", 210, TEGRA_PERIPH_NO_RESET, tegra_clk_usb2_trk, 0),
+ GATE("xusb_gate", "osc", 143, 0, tegra_clk_xusb_gate, 0),
+ GATE("pll_p_out_cpu", "pll_p", 223, 0, tegra_clk_pll_p_out_cpu, 0),
+ GATE("pll_p_out_adsp", "pll_p", 187, 0, tegra_clk_pll_p_out_adsp, 0),
+ GATE("apb2ape", "clk_m", 107, 0, tegra_clk_apb2ape, 0),
+ GATE("cec", "pclk", 136, 0, tegra_clk_cec, 0),
+ GATE("iqc1", "clk_m", 221, 0, tegra_clk_iqc1, 0),
+ GATE("iqc2", "clk_m", 220, 0, tegra_clk_iqc1, 0),
+ GATE("pll_a_out_adsp", "pll_a", 188, 0, tegra_clk_pll_a_out_adsp, 0),
+ GATE("pll_a_out0_out_adsp", "pll_a", 188, 0, tegra_clk_pll_a_out0_out_adsp, 0),
+ GATE("adsp", "aclk", 199, 0, tegra_clk_adsp, 0),
+ GATE("adsp_neon", "aclk", 218, 0, tegra_clk_adsp_neon, 0),
+};
+
+static struct tegra_periph_init_data div_clks[] = {
+ DIV8("usb2_hsic_trk", "osc", CLK_SOURCE_USB2_HSIC_TRK, tegra_clk_usb2_hsic_trk, 0),
+};
+
+struct pll_out_data {
+ char *div_name;
+ char *pll_out_name;
+ u32 offset;
+ int clk_id;
+ u8 div_shift;
+ u8 div_flags;
+ u8 rst_shift;
+ spinlock_t *lock;
+};
+
+#define PLL_OUT(_num, _offset, _div_shift, _div_flags, _rst_shift, _id) \
+ {\
+ .div_name = "pll_p_out" #_num "_div",\
+ .pll_out_name = "pll_p_out" #_num,\
+ .offset = _offset,\
+ .div_shift = _div_shift,\
+ .div_flags = _div_flags | TEGRA_DIVIDER_FIXED |\
+ TEGRA_DIVIDER_ROUND_UP,\
+ .rst_shift = _rst_shift,\
+ .clk_id = tegra_clk_ ## _id,\
+ .lock = &_offset ##_lock,\
+ }
+
+static struct pll_out_data pllp_out_clks[] = {
+ PLL_OUT(1, PLLP_OUTA, 8, 0, 0, pll_p_out1),
+ PLL_OUT(2, PLLP_OUTA, 24, 0, 16, pll_p_out2),
+ PLL_OUT(2, PLLP_OUTA, 24, TEGRA_DIVIDER_INT, 16, pll_p_out2_int),
+ PLL_OUT(3, PLLP_OUTB, 8, 0, 0, pll_p_out3),
+ PLL_OUT(4, PLLP_OUTB, 24, 0, 16, pll_p_out4),
+ PLL_OUT(5, PLLP_OUTC, 24, 0, 16, pll_p_out5),
+};
+
+static void __init periph_clk_init(void __iomem *clk_base,
+ struct tegra_clk *tegra_clks)
+{
+ int i;
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ for (i = 0; i < ARRAY_SIZE(periph_clks); i++) {
+ const struct tegra_clk_periph_regs *bank;
+ struct tegra_periph_init_data *data;
+
+ data = periph_clks + i;
+
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ bank = get_reg_bank(data->periph.gate.clk_num);
+ if (!bank)
+ continue;
+
+ data->periph.gate.regs = bank;
+ clk = tegra_clk_register_periph_data(clk_base, data);
+ *dt_clk = clk;
+ }
+}
+
+static void __init gate_clk_init(void __iomem *clk_base,
+ struct tegra_clk *tegra_clks)
+{
+ int i;
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ for (i = 0; i < ARRAY_SIZE(gate_clks); i++) {
+ struct tegra_periph_init_data *data;
+
+ data = gate_clks + i;
+
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = tegra_clk_register_periph_gate(data->name,
+ data->p.parent_name, data->periph.gate.flags,
+ clk_base, data->flags,
+ data->periph.gate.clk_num,
+ periph_clk_enb_refcnt);
+ *dt_clk = clk;
+ }
+}
+
+static void __init div_clk_init(void __iomem *clk_base,
+ struct tegra_clk *tegra_clks)
+{
+ int i;
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ for (i = 0; i < ARRAY_SIZE(div_clks); i++) {
+ struct tegra_periph_init_data *data;
+
+ data = div_clks + i;
+
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = tegra_clk_register_divider(data->name,
+ data->p.parent_name, clk_base + data->offset,
+ data->flags, data->periph.divider.flags,
+ data->periph.divider.shift,
+ data->periph.divider.width,
+ data->periph.divider.frac_width,
+ data->periph.divider.lock);
+ *dt_clk = clk;
+ }
+}
+
+static void __init init_pllp(void __iomem *clk_base, void __iomem *pmc_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *pll_params)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+ int i;
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p, tegra_clks);
+ if (dt_clk) {
+ /* PLLP */
+ clk = tegra_clk_register_pll("pll_p", "pll_ref", clk_base,
+ pmc_base, 0, pll_params, NULL);
+ clk_register_clkdev(clk, "pll_p", NULL);
+ *dt_clk = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pllp_out_clks); i++) {
+ struct pll_out_data *data;
+
+ data = pllp_out_clks + i;
+
+ dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
+ if (!dt_clk)
+ continue;
+
+ clk = tegra_clk_register_divider(data->div_name, "pll_p",
+ clk_base + data->offset, 0, data->div_flags,
+ data->div_shift, 8, 1, data->lock);
+ clk = tegra_clk_register_pll_out(data->pll_out_name,
+ data->div_name, clk_base + data->offset,
+ data->rst_shift + 1, data->rst_shift,
+ CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0,
+ data->lock);
+ *dt_clk = clk;
+ }
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p_out_cpu,
+ tegra_clks);
+ if (dt_clk) {
+ /*
+ * Tegra210 has control on enabling/disabling PLLP branches to
+ * CPU, register a gate clock "pll_p_out_cpu" for this gating
+ * function and parent "pll_p_out4" to it, so when we are
+ * re-parenting CPU off from "pll_p_out4" the PLLP branching to
+ * CPU can be disabled automatically.
+ */
+ clk = tegra_clk_register_divider("pll_p_out4_div",
+ "pll_p_out_cpu", clk_base + PLLP_OUTB, 0, 0, 24,
+ 8, 1, &PLLP_OUTB_lock);
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p_out4_cpu, tegra_clks);
+ if (dt_clk) {
+ clk = tegra_clk_register_pll_out("pll_p_out4",
+ "pll_p_out4_div", clk_base + PLLP_OUTB,
+ 17, 16, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0,
+ &PLLP_OUTB_lock);
+ *dt_clk = clk;
+ }
+ }
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p_out_hsio, tegra_clks);
+ if (dt_clk) {
+ /* PLLP_OUT_HSIO */
+ clk = clk_register_gate(NULL, "pll_p_out_hsio", "pll_p",
+ CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ clk_base + PLLP_MISC1, 29, 0, NULL);
+ *dt_clk = clk;
+ }
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_p_out_xusb, tegra_clks);
+ if (dt_clk) {
+ /* PLLP_OUT_XUSB */
+ clk = clk_register_gate(NULL, "pll_p_out_xusb",
+ "pll_p_out_hsio", CLK_SET_RATE_PARENT |
+ CLK_IGNORE_UNUSED, clk_base + PLLP_MISC1, 28, 0,
+ NULL);
+ clk_register_clkdev(clk, "pll_p_out_xusb", NULL);
+ *dt_clk = clk;
+ }
+}
+
+void __init tegra_periph_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base, struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *pll_params)
+{
+ init_pllp(clk_base, pmc_base, tegra_clks, pll_params);
+ periph_clk_init(clk_base, tegra_clks);
+ gate_clk_init(clk_base, tegra_clks);
+ div_clk_init(clk_base, tegra_clks);
+}
diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
new file mode 100644
index 000000000..e7aeb6760
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on clk-super.c
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Based on older tegra20-cpufreq driver by Colin Cross <ccross@google.com>
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+#define PLLP_INDEX 4
+#define PLLX_INDEX 8
+
+#define SUPER_CDIV_ENB BIT(31)
+
+#define TSENSOR_SLOWDOWN BIT(23)
+
+static struct tegra_clk_super_mux *cclk_super;
+static bool cclk_on_pllx;
+
+static u8 cclk_super_get_parent(struct clk_hw *hw)
+{
+ return tegra_clk_super_ops.get_parent(hw);
+}
+
+static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
+{
+ return tegra_clk_super_ops.set_parent(hw, index);
+}
+
+static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
+}
+
+static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ u32 val = readl_relaxed(super->reg);
+ unsigned int div2;
+
+ /* check whether thermal throttling is active */
+ if (val & TSENSOR_SLOWDOWN)
+ div2 = 1;
+ else
+ div2 = 0;
+
+ if (cclk_super_get_parent(hw) == PLLX_INDEX)
+ return parent_rate >> div2;
+
+ return tegra_clk_super_ops.recalc_rate(hw, parent_rate) >> div2;
+}
+
+static int cclk_super_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
+ struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ unsigned long pllp_rate;
+ long rate = req->rate;
+
+ if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
+ return -EINVAL;
+
+ /*
+ * Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
+ * PLLX will be disabled in this case, saving some power.
+ */
+ pllp_rate = clk_hw_get_rate(pllp_hw);
+
+ if (rate <= pllp_rate) {
+ if (super->flags & TEGRA20_SUPER_CLK)
+ rate = pllp_rate;
+ else {
+ struct clk_rate_request parent = {
+ .rate = req->rate,
+ .best_parent_rate = pllp_rate,
+ };
+
+ clk_hw_get_rate_range(hw, &parent.min_rate,
+ &parent.max_rate);
+ tegra_clk_super_ops.determine_rate(hw, &parent);
+ pllp_rate = parent.best_parent_rate;
+ rate = parent.rate;
+ }
+
+ req->best_parent_rate = pllp_rate;
+ req->best_parent_hw = pllp_hw;
+ req->rate = rate;
+ } else {
+ rate = clk_hw_round_rate(pllx_hw, rate);
+ req->best_parent_rate = rate;
+ req->best_parent_hw = pllx_hw;
+ req->rate = rate;
+ }
+
+ if (WARN_ON_ONCE(rate <= 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct clk_ops tegra_cclk_super_ops = {
+ .get_parent = cclk_super_get_parent,
+ .set_parent = cclk_super_set_parent,
+ .set_rate = cclk_super_set_rate,
+ .recalc_rate = cclk_super_recalc_rate,
+ .determine_rate = cclk_super_determine_rate,
+};
+
+static const struct clk_ops tegra_cclk_super_mux_ops = {
+ .get_parent = cclk_super_get_parent,
+ .set_parent = cclk_super_set_parent,
+ .determine_rate = cclk_super_determine_rate,
+};
+
+struct clk *tegra_clk_register_super_cclk(const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ spinlock_t *lock)
+{
+ struct tegra_clk_super_mux *super;
+ struct clk *clk;
+ struct clk_init_data init;
+ u32 val;
+
+ if (WARN_ON(cclk_super))
+ return ERR_PTR(-EBUSY);
+
+ super = kzalloc_obj(*super);
+ if (!super)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.flags = flags;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ super->reg = reg;
+ super->lock = lock;
+ super->width = 4;
+ super->flags = clk_super_flags;
+ super->hw.init = &init;
+
+ if (super->flags & TEGRA20_SUPER_CLK) {
+ init.ops = &tegra_cclk_super_mux_ops;
+ } else {
+ init.ops = &tegra_cclk_super_ops;
+
+ super->frac_div.reg = reg + 4;
+ super->frac_div.shift = 16;
+ super->frac_div.width = 8;
+ super->frac_div.frac_width = 1;
+ super->frac_div.lock = lock;
+ super->div_ops = &tegra_clk_frac_div_ops;
+ }
+
+ /*
+ * Tegra30+ has the following CPUG clock topology:
+ *
+ * +---+ +-------+ +-+ +-+ +-+
+ * PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
+ * | | +-------+ | | | +---+ | | | | |
+ * PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
+ * ... | | | | | | K | | | | +-------+ | |
+ * PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
+ * +---+ +++ | P | +++ |SKIPPER| +++
+ * ^ | P | ^ +-------+ ^
+ * | | E | | |
+ * PLLX_SEL+--+ | R | | OVERHEAT+--+
+ * +---+ |
+ * |
+ * SUPER_CDIV_ENB+--+
+ *
+ * Tegra20 is similar, but simpler. It doesn't have the divider and
+ * thermal DIV2 skipper.
+ *
+ * At least for now we're not going to use clock-skipper, hence let's
+ * ensure that it is disabled.
+ */
+ val = readl_relaxed(reg + 4);
+ val &= ~SUPER_CDIV_ENB;
+ writel_relaxed(val, reg + 4);
+
+ clk = clk_register(NULL, &super->hw);
+ if (IS_ERR(clk))
+ kfree(super);
+ else
+ cclk_super = super;
+
+ return clk;
+}
+
+int tegra_cclk_pre_pllx_rate_change(void)
+{
+ if (IS_ERR_OR_NULL(cclk_super))
+ return -EINVAL;
+
+ if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
+ cclk_on_pllx = true;
+ else
+ cclk_on_pllx = false;
+
+ /*
+ * CPU needs to be temporarily re-parented away from PLLX if PLLX
+ * changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
+ */
+ if (cclk_on_pllx)
+ cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
+
+ return 0;
+}
+
+void tegra_cclk_post_pllx_rate_change(void)
+{
+ if (cclk_on_pllx)
+ cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
+}
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
new file mode 100644
index 000000000..5760c978b
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+
+#define CCLKG_BURST_POLICY 0x368
+#define CCLKLP_BURST_POLICY 0x370
+#define SCLK_BURST_POLICY 0x028
+#define SYSTEM_CLK_RATE 0x030
+#define SCLK_DIVIDER 0x2c
+
+static DEFINE_SPINLOCK(sysrate_lock);
+
+enum tegra_super_gen {
+ gen4 = 4,
+ gen5,
+};
+
+struct tegra_super_gen_info {
+ enum tegra_super_gen gen;
+ const char **sclk_parents;
+ const char **cclk_g_parents;
+ const char **cclk_lp_parents;
+ int num_sclk_parents;
+ int num_cclk_g_parents;
+ int num_cclk_lp_parents;
+};
+
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p", "pll_p_out2", "unused",
+ "clk_32k", "pll_m_out1" };
+
+static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x", "unused", "unused",
+ "unused", "unused", "unused", "unused",
+ "dfllCPU_out" };
+
+static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x", "pll_x_out0" };
+
+static const struct tegra_super_gen_info tegra_super_gen_info_gen4 = {
+ .gen = gen4,
+ .sclk_parents = sclk_parents,
+ .cclk_g_parents = cclk_g_parents,
+ .cclk_lp_parents = cclk_lp_parents,
+ .num_sclk_parents = ARRAY_SIZE(sclk_parents),
+ .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents),
+ .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents),
+};
+
+static const char *sclk_parents_gen5[] = { "clk_m", "pll_c_out1", "pll_c4_out3",
+ "pll_p", "pll_p_out2", "pll_c4_out1",
+ "clk_32k", "pll_c4_out2" };
+
+static const char *cclk_g_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x", "unused", "unused",
+ "unused", "unused", "unused", "unused",
+ "dfllCPU_out" };
+
+static const char *cclk_lp_parents_gen5[] = { "clk_m", "unused", "clk_32k", "unused",
+ "pll_p", "pll_p_out4", "unused",
+ "unused", "pll_x", "unused", "unused",
+ "unused", "unused", "unused", "unused",
+ "dfllCPU_out" };
+
+static const struct tegra_super_gen_info tegra_super_gen_info_gen5 = {
+ .gen = gen5,
+ .sclk_parents = sclk_parents_gen5,
+ .cclk_g_parents = cclk_g_parents_gen5,
+ .cclk_lp_parents = cclk_lp_parents_gen5,
+ .num_sclk_parents = ARRAY_SIZE(sclk_parents_gen5),
+ .num_cclk_g_parents = ARRAY_SIZE(cclk_g_parents_gen5),
+ .num_cclk_lp_parents = ARRAY_SIZE(cclk_lp_parents_gen5),
+};
+
+static void __init tegra_sclk_init(void __iomem *clk_base,
+ struct tegra_clk *tegra_clks,
+ const struct tegra_super_gen_info *gen_info)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ /* SCLK_MUX */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_sclk_mux, tegra_clks);
+ if (dt_clk) {
+ clk = tegra_clk_register_super_mux("sclk_mux",
+ gen_info->sclk_parents,
+ gen_info->num_sclk_parents,
+ CLK_SET_RATE_PARENT,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ *dt_clk = clk;
+
+
+ /* SCLK */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
+ if (dt_clk) {
+ clk = clk_register_divider(NULL, "sclk", "sclk_mux",
+ CLK_IS_CRITICAL,
+ clk_base + SCLK_DIVIDER, 0, 8,
+ 0, &sysrate_lock);
+ *dt_clk = clk;
+ }
+ } else {
+ /* SCLK */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_sclk, tegra_clks);
+ if (dt_clk) {
+ clk = tegra_clk_register_super_mux("sclk",
+ gen_info->sclk_parents,
+ gen_info->num_sclk_parents,
+ CLK_SET_RATE_PARENT |
+ CLK_IS_CRITICAL,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ *dt_clk = clk;
+ }
+ }
+
+ /* HCLK */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_hclk, tegra_clks);
+ if (dt_clk) {
+ clk = clk_register_divider(NULL, "hclk_div", "sclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 4, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "hclk", "hclk_div",
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ clk_base + SYSTEM_CLK_RATE,
+ 7, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ *dt_clk = clk;
+ }
+
+ /* PCLK */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pclk, tegra_clks);
+ if (!dt_clk)
+ return;
+
+ clk = clk_register_divider(NULL, "pclk_div", "hclk", 0,
+ clk_base + SYSTEM_CLK_RATE, 0, 2, 0,
+ &sysrate_lock);
+ clk = clk_register_gate(NULL, "pclk", "pclk_div", CLK_SET_RATE_PARENT |
+ CLK_IS_CRITICAL, clk_base + SYSTEM_CLK_RATE,
+ 3, CLK_GATE_SET_TO_DISABLE, &sysrate_lock);
+ *dt_clk = clk;
+}
+
+static void __init tegra_super_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *params,
+ const struct tegra_super_gen_info *gen_info)
+{
+ struct clk *clk;
+ struct clk **dt_clk;
+
+ /* CCLKG */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_g, tegra_clks);
+ if (dt_clk) {
+ if (gen_info->gen == gen5) {
+ clk = tegra_clk_register_super_mux("cclk_g",
+ gen_info->cclk_g_parents,
+ gen_info->num_cclk_g_parents,
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKG_BURST_POLICY,
+ TEGRA210_CPU_CLK, 4, 8, 0, NULL);
+ } else {
+ clk = tegra_clk_register_super_mux("cclk_g",
+ gen_info->cclk_g_parents,
+ gen_info->num_cclk_g_parents,
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKG_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ }
+ *dt_clk = clk;
+ }
+
+ /* CCLKLP */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
+ if (dt_clk) {
+ if (gen_info->gen == gen5) {
+ /*
+ * TEGRA210_CPU_CLK flag is not needed for cclk_lp as
+ * cluster switching is not currently supported on
+ * Tegra210 and also cpu_lp is not used.
+ */
+ clk = tegra_clk_register_super_mux("cclk_lp",
+ gen_info->cclk_lp_parents,
+ gen_info->num_cclk_lp_parents,
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKLP_BURST_POLICY,
+ 0, 4, 8, 0, NULL);
+ } else {
+ clk = tegra_clk_register_super_mux("cclk_lp",
+ gen_info->cclk_lp_parents,
+ gen_info->num_cclk_lp_parents,
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKLP_BURST_POLICY,
+ TEGRA_DIVIDER_2, 4, 8, 9, NULL);
+ }
+ *dt_clk = clk;
+ }
+
+ tegra_sclk_init(clk_base, tegra_clks, gen_info);
+
+#if defined(CONFIG_ARCH_TEGRA_114_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_124_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_210_SOC)
+ /* PLLX */
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x, tegra_clks);
+ if (!dt_clk)
+ return;
+
+#if defined(CONFIG_ARCH_TEGRA_210_SOC)
+ if (gen_info->gen == gen5)
+ clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref",
+ clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL);
+ else
+#endif
+ clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base,
+ pmc_base, CLK_IGNORE_UNUSED, params, NULL);
+
+ *dt_clk = clk;
+
+ /* PLLX_OUT0 */
+
+ dt_clk = tegra_lookup_dt_id(tegra_clk_pll_x_out0, tegra_clks);
+ if (!dt_clk)
+ return;
+ clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
+ CLK_SET_RATE_PARENT, 1, 2);
+ *dt_clk = clk;
+#endif
+}
+
+void __init tegra_super_clk_gen4_init(void __iomem *clk_base,
+ void __iomem *pmc_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *params)
+{
+ tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params,
+ &tegra_super_gen_info_gen4);
+}
+
+void __init tegra_super_clk_gen5_init(void __iomem *clk_base,
+ void __iomem *pmc_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *params)
+{
+ tegra_super_clk_init(clk_base, pmc_base, tegra_clks, params,
+ &tegra_super_gen_info_gen5);
+}
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
new file mode 100644
index 000000000..a4f40533c
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -0,0 +1,1381 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+#include <dt-bindings/clock/tegra114-car.h>
+#include <dt-bindings/reset/nvidia,tegra114-car.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define RST_DFLL_DVCO 0x2F4
+#define CPU_FINETRIM_SELECT 0x4d4 /* override default prop dlys */
+#define CPU_FINETRIM_DR 0x4d8 /* rise->rise prop dly A */
+#define CPU_FINETRIM_R 0x4e4 /* rise->rise prop dly inc A */
+
+/* RST_DFLL_DVCO bitfields */
+#define DVFS_DFLL_RESET_SHIFT 0
+
+/* CPU_FINETRIM_SELECT and CPU_FINETRIM_DR bitfields */
+#define CPU_FINETRIM_1_FCPU_1 BIT(0) /* fcpu0 */
+#define CPU_FINETRIM_1_FCPU_2 BIT(1) /* fcpu1 */
+#define CPU_FINETRIM_1_FCPU_3 BIT(2) /* fcpu2 */
+#define CPU_FINETRIM_1_FCPU_4 BIT(3) /* fcpu3 */
+#define CPU_FINETRIM_1_FCPU_5 BIT(4) /* fl2 */
+#define CPU_FINETRIM_1_FCPU_6 BIT(5) /* ftop */
+
+/* CPU_FINETRIM_R bitfields */
+#define CPU_FINETRIM_R_FCPU_1_SHIFT 0 /* fcpu0 */
+#define CPU_FINETRIM_R_FCPU_1_MASK (0x3 << CPU_FINETRIM_R_FCPU_1_SHIFT)
+#define CPU_FINETRIM_R_FCPU_2_SHIFT 2 /* fcpu1 */
+#define CPU_FINETRIM_R_FCPU_2_MASK (0x3 << CPU_FINETRIM_R_FCPU_2_SHIFT)
+#define CPU_FINETRIM_R_FCPU_3_SHIFT 4 /* fcpu2 */
+#define CPU_FINETRIM_R_FCPU_3_MASK (0x3 << CPU_FINETRIM_R_FCPU_3_SHIFT)
+#define CPU_FINETRIM_R_FCPU_4_SHIFT 6 /* fcpu3 */
+#define CPU_FINETRIM_R_FCPU_4_MASK (0x3 << CPU_FINETRIM_R_FCPU_4_SHIFT)
+#define CPU_FINETRIM_R_FCPU_5_SHIFT 8 /* fl2 */
+#define CPU_FINETRIM_R_FCPU_5_MASK (0x3 << CPU_FINETRIM_R_FCPU_5_SHIFT)
+#define CPU_FINETRIM_R_FCPU_6_SHIFT 10 /* ftop */
+#define CPU_FINETRIM_R_FCPU_6_MASK (0x3 << CPU_FINETRIM_R_FCPU_6_SHIFT)
+
+#define TEGRA114_CLK_PERIPH_BANKS 5
+
+#define PLLC_BASE 0x80
+#define PLLC_MISC2 0x88
+#define PLLC_MISC 0x8c
+#define PLLC2_BASE 0x4e8
+#define PLLC2_MISC 0x4ec
+#define PLLC3_BASE 0x4fc
+#define PLLC3_MISC 0x500
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+#define PLLRE_BASE 0x4c4
+#define PLLRE_MISC 0x4c8
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLC_MISC_LOCK_ENABLE 24
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+#define PLLRE_MISC_LOCK_ENABLE 30
+
+#define PLLC_IDDQ_BIT 26
+#define PLLX_IDDQ_BIT 3
+#define PLLRE_IDDQ_BIT 16
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+#define PLLRE_MISC_LOCK BIT(24)
+#define PLLCX_BASE_LOCK (BIT(26)|BIT(27))
+
+#define PLLE_AUX 0x48c
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_SHIFT 28
+#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
+
+#define PLLXC_SW_MAX_P 6
+
+#define CCLKG_BURST_POLICY 0x368
+
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_EMC 0x19c
+
+/* PLLM override registers */
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
+
+/* Tegra CPU clock and reset control regs */
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
+
+#define MUX8(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ _clk_num, _gate_flags, _clk_id, _parents##_idx, 0,\
+ NULL)
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 clk_csite_src;
+ u32 cclkg_burst;
+ u32 cclkg_divider;
+} tegra114_cpu_clk_sctx;
+#endif
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_d2_lock);
+static DEFINE_SPINLOCK(pll_u_lock);
+static DEFINE_SPINLOCK(pll_re_lock);
+static DEFINE_SPINLOCK(emc_lock);
+
+static struct div_nmp pllxc_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 4,
+};
+
+static const struct pdiv_map pllxc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 624000000, 104, 1, 2, 0 },
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLC_MISC,
+ .iddq_bit_idx = PLLC_IDDQ_BIT,
+ .max_p = PLLXC_SW_MAX_P,
+ .dyn_ramp_reg = PLLC_MISC2,
+ .stepa_shift = 17,
+ .stepb_shift = 9,
+ .pdiv_tohw = pllxc_p,
+ .div_nmp = &pllxc_nmp,
+ .freq_table = pll_c_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct div_nmp pllcx_nmp = {
+ .divm_shift = 0,
+ .divm_width = 2,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static const struct pdiv_map pllc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 8, .hw_val = 5 },
+ { .pdiv = 16, .hw_val = 7 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = {
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c2_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC2_BASE,
+ .misc_reg = PLLC2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .div_nmp = &pllcx_nmp,
+ .max_p = 7,
+ .ext_misc_reg[0] = 0x4f0,
+ .ext_misc_reg[1] = 0x4f4,
+ .ext_misc_reg[2] = 0x4f8,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+};
+
+static struct tegra_clk_pll_params pll_c3_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC3_BASE,
+ .misc_reg = PLLC3_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .div_nmp = &pllcx_nmp,
+ .max_p = 7,
+ .ext_misc_reg[0] = 0x504,
+ .ext_misc_reg[1] = 0x508,
+ .ext_misc_reg[2] = 0x50c,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+};
+
+static struct div_nmp pllm_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .override_divm_shift = 0,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .override_divn_shift = 8,
+ .divp_shift = 20,
+ .divp_width = 1,
+ .override_divp_shift = 27,
+};
+
+static const struct pdiv_map pllm_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 800000000, 66, 1, 1, 0 }, /* actual: 792.0 MHz */
+ { 13000000, 800000000, 61, 1, 1, 0 }, /* actual: 793.0 MHz */
+ { 16800000, 800000000, 47, 1, 1, 0 }, /* actual: 789.6 MHz */
+ { 19200000, 800000000, 41, 1, 1, 0 }, /* actual: 787.2 MHz */
+ { 26000000, 800000000, 61, 2, 1, 0 }, /* actual: 793.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 12000000,
+ .input_max = 500000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 400000000,
+ .vco_max = 1066000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .max_p = 2,
+ .pdiv_tohw = pllm_p,
+ .div_nmp = &pllm_nmp,
+ .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE,
+ .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE |
+ TEGRA_PLL_FIXED,
+};
+
+static struct div_nmp pllp_nmp = {
+ .divm_shift = 0,
+ .divm_width = 5,
+ .divn_shift = 8,
+ .divn_width = 10,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 2, 8 },
+ { 13000000, 216000000, 432, 13, 2, 8 },
+ { 16800000, 216000000, 360, 14, 2, 8 },
+ { 19200000, 216000000, 360, 16, 2, 8 },
+ { 26000000, 216000000, 432, 26, 2, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_p_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 408000000,
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 9600000, 282240000, 147, 5, 1, 4 },
+ { 9600000, 368640000, 192, 5, 1, 4 },
+ { 9600000, 240000000, 200, 8, 1, 8 },
+ { 28800000, 282240000, 245, 25, 1, 8 },
+ { 28800000, 368640000, 320, 25, 1, 8 },
+ { 28800000, 240000000, 200, 24, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_a_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 864, 12, 4, 12 },
+ { 13000000, 216000000, 864, 13, 4, 12 },
+ { 16800000, 216000000, 720, 14, 4, 12 },
+ { 19200000, 216000000, 720, 16, 4, 12 },
+ { 26000000, 216000000, 864, 26, 4, 12 },
+ { 12000000, 594000000, 594, 12, 1, 12 },
+ { 13000000, 594000000, 594, 13, 1, 12 },
+ { 16800000, 594000000, 495, 14, 1, 12 },
+ { 19200000, 594000000, 495, 16, 1, 12 },
+ { 26000000, 594000000, 594, 26, 1, 12 },
+ { 12000000, 1000000000, 1000, 12, 1, 12 },
+ { 13000000, 1000000000, 1000, 13, 1, 12 },
+ { 19200000, 1000000000, 625, 12, 1, 12 },
+ { 26000000, 1000000000, 1000, 26, 1, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_d2_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static const struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct div_nmp pllu_nmp = {
+ .divm_shift = 0,
+ .divm_width = 5,
+ .divn_shift = 8,
+ .divn_width = 10,
+ .divp_shift = 20,
+ .divp_width = 1,
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 2, 12 },
+ { 13000000, 480000000, 960, 13, 2, 12 },
+ { 16800000, 480000000, 400, 7, 2, 5 },
+ { 19200000, 480000000, 200, 4, 2, 3 },
+ { 26000000, 480000000, 960, 26, 2, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+ .div_nmp = &pllu_nmp,
+ .freq_table = pll_u_freq_table,
+ .flags = TEGRA_PLLU | TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 83, 1, 1, 0 }, /* actual: 996.0 MHz */
+ { 13000000, 1000000000, 76, 1, 1, 0 }, /* actual: 988.0 MHz */
+ { 16800000, 1000000000, 59, 1, 1, 0 }, /* actual: 991.2 MHz */
+ { 19200000, 1000000000, 52, 1, 1, 0 }, /* actual: 998.4 MHz */
+ { 26000000, 1000000000, 76, 2, 1, 0 }, /* actual: 988.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 700000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLX_MISC3,
+ .iddq_bit_idx = PLLX_IDDQ_BIT,
+ .max_p = PLLXC_SW_MAX_P,
+ .dyn_ramp_reg = PLLX_MISC2,
+ .stepa_shift = 16,
+ .stepb_shift = 24,
+ .pdiv_tohw = pllxc_p,
+ .div_nmp = &pllxc_nmp,
+ .freq_table = pll_x_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 336000000, 100000000, 100, 21, 16, 11 },
+ { 312000000, 100000000, 200, 26, 24, 13 },
+ { 12000000, 100000000, 200, 1, 24, 13 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct pdiv_map plle_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 }
+};
+
+static struct div_nmp plle_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 24,
+ .divp_width = 4,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 75000000,
+ .vco_min = 1600000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .aux_reg = PLLE_AUX,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = plle_p,
+ .div_nmp = &plle_nmp,
+ .freq_table = pll_e_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 100000000,
+};
+
+static struct div_nmp pllre_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 16,
+ .divp_width = 4,
+};
+
+static struct tegra_clk_pll_params pll_re_vco_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 300000000,
+ .vco_max = 600000000,
+ .base_reg = PLLRE_BASE,
+ .misc_reg = PLLRE_MISC,
+ .lock_mask = PLLRE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLRE_MISC,
+ .iddq_bit_idx = PLLRE_IDDQ_BIT,
+ .div_nmp = &pllre_nmp,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE |
+ TEGRA_PLL_LOCK_MISC,
+};
+
+/* possible OSC frequencies in Hz */
+static unsigned long tegra114_input_freq[] = {
+ [ 0] = 13000000,
+ [ 1] = 16800000,
+ [ 4] = 19200000,
+ [ 5] = 38400000,
+ [ 8] = 12000000,
+ [ 9] = 48000000,
+ [12] = 26000000,
+};
+
+#define MASK(x) (BIT(x) - 1)
+
+/* peripheral mux definitions */
+
+static const char *mux_plld_out0_plld2_out0[] = {
+ "pll_d_out0", "pll_d2_out0",
+};
+#define mux_plld_out0_plld2_out0_idx NULL
+
+static const char *mux_pllmcp_clkm[] = {
+ "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud",
+};
+
+static const struct clk_div_table pll_re_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 5 },
+ { .val = 5, .div = 6 },
+ { .val = 0, .div = 0 },
+};
+
+static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = {
+ [tegra_clk_rtc] = { .dt_id = TEGRA114_CLK_RTC, .present = true },
+ [tegra_clk_timer] = { .dt_id = TEGRA114_CLK_TIMER, .present = true },
+ [tegra_clk_uarta] = { .dt_id = TEGRA114_CLK_UARTA, .present = true },
+ [tegra_clk_uartd] = { .dt_id = TEGRA114_CLK_UARTD, .present = true },
+ [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA114_CLK_SDMMC2, .present = true },
+ [tegra_clk_i2s1] = { .dt_id = TEGRA114_CLK_I2S1, .present = true },
+ [tegra_clk_i2c1] = { .dt_id = TEGRA114_CLK_I2C1, .present = true },
+ [tegra_clk_ndflash] = { .dt_id = TEGRA114_CLK_NDFLASH, .present = true },
+ [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA114_CLK_SDMMC1, .present = true },
+ [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA114_CLK_SDMMC4, .present = true },
+ [tegra_clk_pwm] = { .dt_id = TEGRA114_CLK_PWM, .present = true },
+ [tegra_clk_i2s0] = { .dt_id = TEGRA114_CLK_I2S0, .present = true },
+ [tegra_clk_i2s2] = { .dt_id = TEGRA114_CLK_I2S2, .present = true },
+ [tegra_clk_epp_8] = { .dt_id = TEGRA114_CLK_EPP, .present = true },
+ [tegra_clk_gr2d_8] = { .dt_id = TEGRA114_CLK_GR2D, .present = true },
+ [tegra_clk_usbd] = { .dt_id = TEGRA114_CLK_USBD, .present = true },
+ [tegra_clk_isp] = { .dt_id = TEGRA114_CLK_ISP, .present = true },
+ [tegra_clk_gr3d_8] = { .dt_id = TEGRA114_CLK_GR3D, .present = true },
+ [tegra_clk_disp2] = { .dt_id = TEGRA114_CLK_DISP2, .present = true },
+ [tegra_clk_disp1] = { .dt_id = TEGRA114_CLK_DISP1, .present = true },
+ [tegra_clk_host1x_8] = { .dt_id = TEGRA114_CLK_HOST1X, .present = true },
+ [tegra_clk_vcp] = { .dt_id = TEGRA114_CLK_VCP, .present = true },
+ [tegra_clk_apbdma] = { .dt_id = TEGRA114_CLK_APBDMA, .present = true },
+ [tegra_clk_kbc] = { .dt_id = TEGRA114_CLK_KBC, .present = true },
+ [tegra_clk_kfuse] = { .dt_id = TEGRA114_CLK_KFUSE, .present = true },
+ [tegra_clk_sbc1_8] = { .dt_id = TEGRA114_CLK_SBC1, .present = true },
+ [tegra_clk_nor] = { .dt_id = TEGRA114_CLK_NOR, .present = true },
+ [tegra_clk_sbc2_8] = { .dt_id = TEGRA114_CLK_SBC2, .present = true },
+ [tegra_clk_sbc3_8] = { .dt_id = TEGRA114_CLK_SBC3, .present = true },
+ [tegra_clk_i2c5] = { .dt_id = TEGRA114_CLK_I2C5, .present = true },
+ [tegra_clk_mipi] = { .dt_id = TEGRA114_CLK_MIPI, .present = true },
+ [tegra_clk_hdmi] = { .dt_id = TEGRA114_CLK_HDMI, .present = true },
+ [tegra_clk_csi] = { .dt_id = TEGRA114_CLK_CSI, .present = true },
+ [tegra_clk_i2c2] = { .dt_id = TEGRA114_CLK_I2C2, .present = true },
+ [tegra_clk_uartc] = { .dt_id = TEGRA114_CLK_UARTC, .present = true },
+ [tegra_clk_emc] = { .dt_id = TEGRA114_CLK_EMC, .present = true },
+ [tegra_clk_usb2] = { .dt_id = TEGRA114_CLK_USB2, .present = true },
+ [tegra_clk_usb3] = { .dt_id = TEGRA114_CLK_USB3, .present = true },
+ [tegra_clk_vde_8] = { .dt_id = TEGRA114_CLK_VDE, .present = true },
+ [tegra_clk_bsea] = { .dt_id = TEGRA114_CLK_BSEA, .present = true },
+ [tegra_clk_bsev] = { .dt_id = TEGRA114_CLK_BSEV, .present = true },
+ [tegra_clk_i2c3] = { .dt_id = TEGRA114_CLK_I2C3, .present = true },
+ [tegra_clk_sbc4_8] = { .dt_id = TEGRA114_CLK_SBC4, .present = true },
+ [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA114_CLK_SDMMC3, .present = true },
+ [tegra_clk_owr] = { .dt_id = TEGRA114_CLK_OWR, .present = true },
+ [tegra_clk_csite] = { .dt_id = TEGRA114_CLK_CSITE, .present = true },
+ [tegra_clk_la] = { .dt_id = TEGRA114_CLK_LA, .present = true },
+ [tegra_clk_trace] = { .dt_id = TEGRA114_CLK_TRACE, .present = true },
+ [tegra_clk_soc_therm] = { .dt_id = TEGRA114_CLK_SOC_THERM, .present = true },
+ [tegra_clk_dtv] = { .dt_id = TEGRA114_CLK_DTV, .present = true },
+ [tegra_clk_ndspeed] = { .dt_id = TEGRA114_CLK_NDSPEED, .present = true },
+ [tegra_clk_i2cslow] = { .dt_id = TEGRA114_CLK_I2CSLOW, .present = true },
+ [tegra_clk_tsec] = { .dt_id = TEGRA114_CLK_TSEC, .present = true },
+ [tegra_clk_xusb_host] = { .dt_id = TEGRA114_CLK_XUSB_HOST, .present = true },
+ [tegra_clk_msenc] = { .dt_id = TEGRA114_CLK_MSENC, .present = true },
+ [tegra_clk_mselect] = { .dt_id = TEGRA114_CLK_MSELECT, .present = true },
+ [tegra_clk_tsensor] = { .dt_id = TEGRA114_CLK_TSENSOR, .present = true },
+ [tegra_clk_i2s3] = { .dt_id = TEGRA114_CLK_I2S3, .present = true },
+ [tegra_clk_i2s4] = { .dt_id = TEGRA114_CLK_I2S4, .present = true },
+ [tegra_clk_i2c4] = { .dt_id = TEGRA114_CLK_I2C4, .present = true },
+ [tegra_clk_sbc5_8] = { .dt_id = TEGRA114_CLK_SBC5, .present = true },
+ [tegra_clk_sbc6_8] = { .dt_id = TEGRA114_CLK_SBC6, .present = true },
+ [tegra_clk_d_audio] = { .dt_id = TEGRA114_CLK_D_AUDIO, .present = true },
+ [tegra_clk_apbif] = { .dt_id = TEGRA114_CLK_APBIF, .present = true },
+ [tegra_clk_dam0] = { .dt_id = TEGRA114_CLK_DAM0, .present = true },
+ [tegra_clk_dam1] = { .dt_id = TEGRA114_CLK_DAM1, .present = true },
+ [tegra_clk_dam2] = { .dt_id = TEGRA114_CLK_DAM2, .present = true },
+ [tegra_clk_hda2codec_2x] = { .dt_id = TEGRA114_CLK_HDA2CODEC_2X, .present = true },
+ [tegra_clk_audio0_2x] = { .dt_id = TEGRA114_CLK_AUDIO0_2X, .present = true },
+ [tegra_clk_audio1_2x] = { .dt_id = TEGRA114_CLK_AUDIO1_2X, .present = true },
+ [tegra_clk_audio2_2x] = { .dt_id = TEGRA114_CLK_AUDIO2_2X, .present = true },
+ [tegra_clk_audio3_2x] = { .dt_id = TEGRA114_CLK_AUDIO3_2X, .present = true },
+ [tegra_clk_audio4_2x] = { .dt_id = TEGRA114_CLK_AUDIO4_2X, .present = true },
+ [tegra_clk_spdif_2x] = { .dt_id = TEGRA114_CLK_SPDIF_2X, .present = true },
+ [tegra_clk_actmon] = { .dt_id = TEGRA114_CLK_ACTMON, .present = true },
+ [tegra_clk_extern1] = { .dt_id = TEGRA114_CLK_EXTERN1, .present = true },
+ [tegra_clk_extern2] = { .dt_id = TEGRA114_CLK_EXTERN2, .present = true },
+ [tegra_clk_extern3] = { .dt_id = TEGRA114_CLK_EXTERN3, .present = true },
+ [tegra_clk_hda] = { .dt_id = TEGRA114_CLK_HDA, .present = true },
+ [tegra_clk_se] = { .dt_id = TEGRA114_CLK_SE, .present = true },
+ [tegra_clk_hda2hdmi] = { .dt_id = TEGRA114_CLK_HDA2HDMI, .present = true },
+ [tegra_clk_cilab] = { .dt_id = TEGRA114_CLK_CILAB, .present = true },
+ [tegra_clk_cilcd] = { .dt_id = TEGRA114_CLK_CILCD, .present = true },
+ [tegra_clk_cile] = { .dt_id = TEGRA114_CLK_CILE, .present = true },
+ [tegra_clk_dsialp] = { .dt_id = TEGRA114_CLK_DSIALP, .present = true },
+ [tegra_clk_dsiblp] = { .dt_id = TEGRA114_CLK_DSIBLP, .present = true },
+ [tegra_clk_dds] = { .dt_id = TEGRA114_CLK_DDS, .present = true },
+ [tegra_clk_dp2] = { .dt_id = TEGRA114_CLK_DP2, .present = true },
+ [tegra_clk_amx] = { .dt_id = TEGRA114_CLK_AMX, .present = true },
+ [tegra_clk_adx] = { .dt_id = TEGRA114_CLK_ADX, .present = true },
+ [tegra_clk_xusb_ss] = { .dt_id = TEGRA114_CLK_XUSB_SS, .present = true },
+ [tegra_clk_uartb] = { .dt_id = TEGRA114_CLK_UARTB, .present = true },
+ [tegra_clk_vfir] = { .dt_id = TEGRA114_CLK_VFIR, .present = true },
+ [tegra_clk_spdif_in] = { .dt_id = TEGRA114_CLK_SPDIF_IN, .present = true },
+ [tegra_clk_spdif_out] = { .dt_id = TEGRA114_CLK_SPDIF_OUT, .present = true },
+ [tegra_clk_vi_8] = { .dt_id = TEGRA114_CLK_VI, .present = true },
+ [tegra_clk_fuse] = { .dt_id = TEGRA114_CLK_FUSE, .present = true },
+ [tegra_clk_fuse_burn] = { .dt_id = TEGRA114_CLK_FUSE_BURN, .present = true },
+ [tegra_clk_clk_32k] = { .dt_id = TEGRA114_CLK_CLK_32K, .present = true },
+ [tegra_clk_clk_m] = { .dt_id = TEGRA114_CLK_CLK_M, .present = true },
+ [tegra_clk_osc] = { .dt_id = TEGRA114_CLK_OSC, .present = true },
+ [tegra_clk_osc_div2] = { .dt_id = TEGRA114_CLK_OSC_DIV2, .present = true },
+ [tegra_clk_osc_div4] = { .dt_id = TEGRA114_CLK_OSC_DIV4, .present = true },
+ [tegra_clk_pll_ref] = { .dt_id = TEGRA114_CLK_PLL_REF, .present = true },
+ [tegra_clk_pll_c] = { .dt_id = TEGRA114_CLK_PLL_C, .present = true },
+ [tegra_clk_pll_c_out1] = { .dt_id = TEGRA114_CLK_PLL_C_OUT1, .present = true },
+ [tegra_clk_pll_c2] = { .dt_id = TEGRA114_CLK_PLL_C2, .present = true },
+ [tegra_clk_pll_c3] = { .dt_id = TEGRA114_CLK_PLL_C3, .present = true },
+ [tegra_clk_pll_m] = { .dt_id = TEGRA114_CLK_PLL_M, .present = true },
+ [tegra_clk_pll_m_out1] = { .dt_id = TEGRA114_CLK_PLL_M_OUT1, .present = true },
+ [tegra_clk_pll_p] = { .dt_id = TEGRA114_CLK_PLL_P, .present = true },
+ [tegra_clk_pll_p_out1] = { .dt_id = TEGRA114_CLK_PLL_P_OUT1, .present = true },
+ [tegra_clk_pll_p_out2_int] = { .dt_id = TEGRA114_CLK_PLL_P_OUT2, .present = true },
+ [tegra_clk_pll_p_out3] = { .dt_id = TEGRA114_CLK_PLL_P_OUT3, .present = true },
+ [tegra_clk_pll_p_out4] = { .dt_id = TEGRA114_CLK_PLL_P_OUT4, .present = true },
+ [tegra_clk_pll_a] = { .dt_id = TEGRA114_CLK_PLL_A, .present = true },
+ [tegra_clk_pll_a_out0] = { .dt_id = TEGRA114_CLK_PLL_A_OUT0, .present = true },
+ [tegra_clk_pll_d] = { .dt_id = TEGRA114_CLK_PLL_D, .present = true },
+ [tegra_clk_pll_d_out0] = { .dt_id = TEGRA114_CLK_PLL_D_OUT0, .present = true },
+ [tegra_clk_pll_d2] = { .dt_id = TEGRA114_CLK_PLL_D2, .present = true },
+ [tegra_clk_pll_d2_out0] = { .dt_id = TEGRA114_CLK_PLL_D2_OUT0, .present = true },
+ [tegra_clk_pll_u] = { .dt_id = TEGRA114_CLK_PLL_U, .present = true },
+ [tegra_clk_pll_u_480m] = { .dt_id = TEGRA114_CLK_PLL_U_480M, .present = true },
+ [tegra_clk_pll_u_60m] = { .dt_id = TEGRA114_CLK_PLL_U_60M, .present = true },
+ [tegra_clk_pll_u_48m] = { .dt_id = TEGRA114_CLK_PLL_U_48M, .present = true },
+ [tegra_clk_pll_u_12m] = { .dt_id = TEGRA114_CLK_PLL_U_12M, .present = true },
+ [tegra_clk_pll_x] = { .dt_id = TEGRA114_CLK_PLL_X, .present = true },
+ [tegra_clk_pll_x_out0] = { .dt_id = TEGRA114_CLK_PLL_X_OUT0, .present = true },
+ [tegra_clk_pll_re_vco] = { .dt_id = TEGRA114_CLK_PLL_RE_VCO, .present = true },
+ [tegra_clk_pll_re_out] = { .dt_id = TEGRA114_CLK_PLL_RE_OUT, .present = true },
+ [tegra_clk_pll_e_out0] = { .dt_id = TEGRA114_CLK_PLL_E_OUT0, .present = true },
+ [tegra_clk_spdif_in_sync] = { .dt_id = TEGRA114_CLK_SPDIF_IN_SYNC, .present = true },
+ [tegra_clk_i2s0_sync] = { .dt_id = TEGRA114_CLK_I2S0_SYNC, .present = true },
+ [tegra_clk_i2s1_sync] = { .dt_id = TEGRA114_CLK_I2S1_SYNC, .present = true },
+ [tegra_clk_i2s2_sync] = { .dt_id = TEGRA114_CLK_I2S2_SYNC, .present = true },
+ [tegra_clk_i2s3_sync] = { .dt_id = TEGRA114_CLK_I2S3_SYNC, .present = true },
+ [tegra_clk_i2s4_sync] = { .dt_id = TEGRA114_CLK_I2S4_SYNC, .present = true },
+ [tegra_clk_vimclk_sync] = { .dt_id = TEGRA114_CLK_VIMCLK_SYNC, .present = true },
+ [tegra_clk_audio0] = { .dt_id = TEGRA114_CLK_AUDIO0, .present = true },
+ [tegra_clk_audio1] = { .dt_id = TEGRA114_CLK_AUDIO1, .present = true },
+ [tegra_clk_audio2] = { .dt_id = TEGRA114_CLK_AUDIO2, .present = true },
+ [tegra_clk_audio3] = { .dt_id = TEGRA114_CLK_AUDIO3, .present = true },
+ [tegra_clk_audio4] = { .dt_id = TEGRA114_CLK_AUDIO4, .present = true },
+ [tegra_clk_spdif] = { .dt_id = TEGRA114_CLK_SPDIF, .present = true },
+ [tegra_clk_xusb_host_src] = { .dt_id = TEGRA114_CLK_XUSB_HOST_SRC, .present = true },
+ [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA114_CLK_XUSB_FALCON_SRC, .present = true },
+ [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA114_CLK_XUSB_FS_SRC, .present = true },
+ [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA114_CLK_XUSB_SS_SRC, .present = true },
+ [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA114_CLK_XUSB_SS_DIV2, .present = true},
+ [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA114_CLK_XUSB_DEV_SRC, .present = true },
+ [tegra_clk_xusb_dev] = { .dt_id = TEGRA114_CLK_XUSB_DEV, .present = true },
+ [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA114_CLK_XUSB_HS_SRC, .present = true },
+ [tegra_clk_sclk] = { .dt_id = TEGRA114_CLK_SCLK, .present = true },
+ [tegra_clk_hclk] = { .dt_id = TEGRA114_CLK_HCLK, .present = true },
+ [tegra_clk_pclk] = { .dt_id = TEGRA114_CLK_PCLK, .present = true },
+ [tegra_clk_cclk_g] = { .dt_id = TEGRA114_CLK_CCLK_G, .present = true },
+ [tegra_clk_cclk_lp] = { .dt_id = TEGRA114_CLK_CCLK_LP, .present = true },
+ [tegra_clk_dfll_ref] = { .dt_id = TEGRA114_CLK_DFLL_REF, .present = true },
+ [tegra_clk_dfll_soc] = { .dt_id = TEGRA114_CLK_DFLL_SOC, .present = true },
+ [tegra_clk_audio0_mux] = { .dt_id = TEGRA114_CLK_AUDIO0_MUX, .present = true },
+ [tegra_clk_audio1_mux] = { .dt_id = TEGRA114_CLK_AUDIO1_MUX, .present = true },
+ [tegra_clk_audio2_mux] = { .dt_id = TEGRA114_CLK_AUDIO2_MUX, .present = true },
+ [tegra_clk_audio3_mux] = { .dt_id = TEGRA114_CLK_AUDIO3_MUX, .present = true },
+ [tegra_clk_audio4_mux] = { .dt_id = TEGRA114_CLK_AUDIO4_MUX, .present = true },
+ [tegra_clk_spdif_mux] = { .dt_id = TEGRA114_CLK_SPDIF_MUX, .present = true },
+ [tegra_clk_dsia_mux] = { .dt_id = TEGRA114_CLK_DSIA_MUX, .present = true },
+ [tegra_clk_dsib_mux] = { .dt_id = TEGRA114_CLK_DSIB_MUX, .present = true },
+ [tegra_clk_cec] = { .dt_id = TEGRA114_CLK_CEC, .present = true },
+};
+
+static struct tegra_devclk devclks[] __initdata = {
+ { .con_id = "clk_m", .dt_id = TEGRA114_CLK_CLK_M },
+ { .con_id = "pll_ref", .dt_id = TEGRA114_CLK_PLL_REF },
+ { .con_id = "clk_32k", .dt_id = TEGRA114_CLK_CLK_32K },
+ { .con_id = "osc", .dt_id = TEGRA114_CLK_OSC },
+ { .con_id = "osc_div2", .dt_id = TEGRA114_CLK_OSC_DIV2 },
+ { .con_id = "osc_div4", .dt_id = TEGRA114_CLK_OSC_DIV4 },
+ { .con_id = "pll_c", .dt_id = TEGRA114_CLK_PLL_C },
+ { .con_id = "pll_c_out1", .dt_id = TEGRA114_CLK_PLL_C_OUT1 },
+ { .con_id = "pll_c2", .dt_id = TEGRA114_CLK_PLL_C2 },
+ { .con_id = "pll_c3", .dt_id = TEGRA114_CLK_PLL_C3 },
+ { .con_id = "pll_p", .dt_id = TEGRA114_CLK_PLL_P },
+ { .con_id = "pll_p_out1", .dt_id = TEGRA114_CLK_PLL_P_OUT1 },
+ { .con_id = "pll_p_out2", .dt_id = TEGRA114_CLK_PLL_P_OUT2 },
+ { .con_id = "pll_p_out3", .dt_id = TEGRA114_CLK_PLL_P_OUT3 },
+ { .con_id = "pll_p_out4", .dt_id = TEGRA114_CLK_PLL_P_OUT4 },
+ { .con_id = "pll_m", .dt_id = TEGRA114_CLK_PLL_M },
+ { .con_id = "pll_m_out1", .dt_id = TEGRA114_CLK_PLL_M_OUT1 },
+ { .con_id = "pll_x", .dt_id = TEGRA114_CLK_PLL_X },
+ { .con_id = "pll_x_out0", .dt_id = TEGRA114_CLK_PLL_X_OUT0 },
+ { .con_id = "pll_u", .dt_id = TEGRA114_CLK_PLL_U },
+ { .con_id = "pll_u_480M", .dt_id = TEGRA114_CLK_PLL_U_480M },
+ { .con_id = "pll_u_60M", .dt_id = TEGRA114_CLK_PLL_U_60M },
+ { .con_id = "pll_u_48M", .dt_id = TEGRA114_CLK_PLL_U_48M },
+ { .con_id = "pll_u_12M", .dt_id = TEGRA114_CLK_PLL_U_12M },
+ { .con_id = "pll_d", .dt_id = TEGRA114_CLK_PLL_D },
+ { .con_id = "pll_d_out0", .dt_id = TEGRA114_CLK_PLL_D_OUT0 },
+ { .con_id = "pll_d2", .dt_id = TEGRA114_CLK_PLL_D2 },
+ { .con_id = "pll_d2_out0", .dt_id = TEGRA114_CLK_PLL_D2_OUT0 },
+ { .con_id = "pll_a", .dt_id = TEGRA114_CLK_PLL_A },
+ { .con_id = "pll_a_out0", .dt_id = TEGRA114_CLK_PLL_A_OUT0 },
+ { .con_id = "pll_re_vco", .dt_id = TEGRA114_CLK_PLL_RE_VCO },
+ { .con_id = "pll_re_out", .dt_id = TEGRA114_CLK_PLL_RE_OUT },
+ { .con_id = "pll_e_out0", .dt_id = TEGRA114_CLK_PLL_E_OUT0 },
+ { .con_id = "spdif_in_sync", .dt_id = TEGRA114_CLK_SPDIF_IN_SYNC },
+ { .con_id = "i2s0_sync", .dt_id = TEGRA114_CLK_I2S0_SYNC },
+ { .con_id = "i2s1_sync", .dt_id = TEGRA114_CLK_I2S1_SYNC },
+ { .con_id = "i2s2_sync", .dt_id = TEGRA114_CLK_I2S2_SYNC },
+ { .con_id = "i2s3_sync", .dt_id = TEGRA114_CLK_I2S3_SYNC },
+ { .con_id = "i2s4_sync", .dt_id = TEGRA114_CLK_I2S4_SYNC },
+ { .con_id = "vimclk_sync", .dt_id = TEGRA114_CLK_VIMCLK_SYNC },
+ { .con_id = "audio0", .dt_id = TEGRA114_CLK_AUDIO0 },
+ { .con_id = "audio1", .dt_id = TEGRA114_CLK_AUDIO1 },
+ { .con_id = "audio2", .dt_id = TEGRA114_CLK_AUDIO2 },
+ { .con_id = "audio3", .dt_id = TEGRA114_CLK_AUDIO3 },
+ { .con_id = "audio4", .dt_id = TEGRA114_CLK_AUDIO4 },
+ { .con_id = "spdif", .dt_id = TEGRA114_CLK_SPDIF },
+ { .con_id = "audio0_2x", .dt_id = TEGRA114_CLK_AUDIO0_2X },
+ { .con_id = "audio1_2x", .dt_id = TEGRA114_CLK_AUDIO1_2X },
+ { .con_id = "audio2_2x", .dt_id = TEGRA114_CLK_AUDIO2_2X },
+ { .con_id = "audio3_2x", .dt_id = TEGRA114_CLK_AUDIO3_2X },
+ { .con_id = "audio4_2x", .dt_id = TEGRA114_CLK_AUDIO4_2X },
+ { .con_id = "spdif_2x", .dt_id = TEGRA114_CLK_SPDIF_2X },
+ { .con_id = "extern1", .dt_id = TEGRA114_CLK_EXTERN1 },
+ { .con_id = "extern2", .dt_id = TEGRA114_CLK_EXTERN2 },
+ { .con_id = "extern3", .dt_id = TEGRA114_CLK_EXTERN3 },
+ { .con_id = "cclk_g", .dt_id = TEGRA114_CLK_CCLK_G },
+ { .con_id = "cclk_lp", .dt_id = TEGRA114_CLK_CCLK_LP },
+ { .con_id = "sclk", .dt_id = TEGRA114_CLK_SCLK },
+ { .con_id = "hclk", .dt_id = TEGRA114_CLK_HCLK },
+ { .con_id = "pclk", .dt_id = TEGRA114_CLK_PCLK },
+ { .con_id = "fuse", .dt_id = TEGRA114_CLK_FUSE },
+ { .dev_id = "rtc-tegra", .dt_id = TEGRA114_CLK_RTC },
+ { .dev_id = "timer", .dt_id = TEGRA114_CLK_TIMER },
+};
+
+static const char *mux_pllm_pllc2_c_c3_pllp_plla[] = {
+ "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0"
+};
+static u32 mux_pllm_pllc2_c_c3_pllp_plla_idx[] = {
+ [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6,
+};
+
+static struct tegra_audio_clk_info tegra114_audio_plls[] = {
+ { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
+};
+
+static struct clk **clks;
+
+static unsigned long osc_freq;
+static unsigned long pll_ref_freq;
+
+static void __init tegra114_fixed_clk_init(void __iomem *clk_base)
+{
+ struct clk *clk;
+
+ /* clk_32k */
+ clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, 0, 32768);
+ clks[TEGRA114_CLK_CLK_32K] = clk;
+}
+
+static void __init tegra114_pll_init(void __iomem *clk_base,
+ void __iomem *pmc)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base,
+ pmc, 0, &pll_c_params, NULL);
+ clks[TEGRA114_CLK_PLL_C] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA114_CLK_PLL_C_OUT1] = clk;
+
+ /* PLLC2 */
+ clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0,
+ &pll_c2_params, NULL);
+ clks[TEGRA114_CLK_PLL_C2] = clk;
+
+ /* PLLC3 */
+ clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0,
+ &pll_c3_params, NULL);
+ clks[TEGRA114_CLK_PLL_C3] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clks[TEGRA114_CLK_PLL_M] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA114_CLK_PLL_M_OUT1] = clk;
+
+ /* PLLM_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
+ CLK_SET_RATE_PARENT, 1, 1);
+
+ /* PLLU */
+ clk = tegra_clk_register_pllu_tegra114("pll_u", "pll_ref", clk_base, 0,
+ &pll_u_params, &pll_u_lock);
+ clks[TEGRA114_CLK_PLL_U] = clk;
+
+ /* PLLU_480M */
+ clk = clk_register_gate(NULL, "pll_u_480M", "pll_u",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 22, 0, &pll_u_lock);
+ clks[TEGRA114_CLK_PLL_U_480M] = clk;
+
+ /* PLLU_60M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 8);
+ clks[TEGRA114_CLK_PLL_U_60M] = clk;
+
+ /* PLLU_48M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 10);
+ clks[TEGRA114_CLK_PLL_U_48M] = clk;
+
+ /* PLLU_12M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 40);
+ clks[TEGRA114_CLK_PLL_U_12M] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0,
+ &pll_d_params, &pll_d_lock);
+ clks[TEGRA114_CLK_PLL_D] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA114_CLK_PLL_D_OUT0] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc, 0,
+ &pll_d2_params, &pll_d2_lock);
+ clks[TEGRA114_CLK_PLL_D2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA114_CLK_PLL_D2_OUT0] = clk;
+
+ /* PLLRE */
+ clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc,
+ 0, &pll_re_vco_params, &pll_re_lock, pll_ref_freq);
+ clks[TEGRA114_CLK_PLL_RE_VCO] = clk;
+
+ clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0,
+ clk_base + PLLRE_BASE, 16, 4, 0,
+ pll_re_div_table, &pll_re_lock);
+ clks[TEGRA114_CLK_PLL_RE_OUT] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle_tegra114("pll_e_out0", "pll_ref",
+ clk_base, 0, &pll_e_params, NULL);
+ clks[TEGRA114_CLK_PLL_E_OUT0] = clk;
+}
+
+#define CLK_SOURCE_VI_SENSOR 0x1a8
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ MUX8("vi_sensor", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, TEGRA_PERIPH_NO_RESET, TEGRA114_CLK_VI_SENSOR),
+};
+
+static __init void tegra114_periph_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base)
+{
+ struct clk *clk;
+ struct tegra_periph_init_data *data;
+ unsigned int i;
+
+ /* xusb_ss_div2 */
+ clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
+ 1, 2);
+ clks[TEGRA114_CLK_XUSB_SS_DIV2] = clk;
+
+ /* dsia mux */
+ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock);
+ clks[TEGRA114_CLK_DSIA_MUX] = clk;
+
+ /* dsib mux */
+ clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock);
+ clks[TEGRA114_CLK_DSIB_MUX] = clk;
+
+ clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base,
+ 0, 48, periph_clk_enb_refcnt);
+ clks[TEGRA114_CLK_DSIA] = clk;
+
+ clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base,
+ 0, 82, periph_clk_enb_refcnt);
+ clks[TEGRA114_CLK_DSIB] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
+ clk_base, 0, TEGRA114_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA114_CLK_CSUS] = clk;
+
+ /* emc mux */
+ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+ ARRAY_SIZE(mux_pllmcp_clkm),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + CLK_SOURCE_EMC,
+ 29, 3, 0, &emc_lock);
+
+ clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+ &emc_lock);
+ clks[TEGRA114_CLK_MC] = clk;
+
+ clk = tegra_clk_register_periph_gate("mipi-cal", "clk_m", 0, clk_base,
+ CLK_SET_RATE_PARENT, 56,
+ periph_clk_enb_refcnt);
+ clks[TEGRA114_CLK_MIPI_CAL] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph_data(clk_base, data);
+ clks[data->clk_id] = clk;
+ }
+
+ tegra_periph_clk_init(clk_base, pmc_base, tegra114_clks,
+ &pll_p_params);
+}
+
+/* Tegra114 CPU clock and reset control functions */
+static void tegra114_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base + CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+}
+
+static void tegra114_disable_cpu_clock(u32 cpu)
+{
+ /* flow controller would take care in the power sequence. */
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void tegra114_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra114_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_SOURCE_CSITE);
+ writel(3 << 30, clk_base + CLK_SOURCE_CSITE);
+
+ tegra114_cpu_clk_sctx.cclkg_burst =
+ readl(clk_base + CCLKG_BURST_POLICY);
+ tegra114_cpu_clk_sctx.cclkg_divider =
+ readl(clk_base + CCLKG_BURST_POLICY + 4);
+}
+
+static void tegra114_cpu_clock_resume(void)
+{
+ writel(tegra114_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_SOURCE_CSITE);
+
+ writel(tegra114_cpu_clk_sctx.cclkg_burst,
+ clk_base + CCLKG_BURST_POLICY);
+ writel(tegra114_cpu_clk_sctx.cclkg_divider,
+ clk_base + CCLKG_BURST_POLICY + 4);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra114_cpu_car_ops = {
+ .wait_for_reset = tegra114_wait_cpu_in_reset,
+ .disable_clock = tegra114_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra114_cpu_clock_suspend,
+ .resume = tegra114_cpu_clock_resume,
+#endif
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra114-pmc" },
+ { },
+};
+
+/*
+ * dfll_soc/dfll_ref apparently must be kept enabled, otherwise I2C5
+ * breaks
+ */
+static struct tegra_clk_init_table init_table[] __initdata = {
+ { TEGRA114_CLK_UARTA, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ { TEGRA114_CLK_UARTB, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ { TEGRA114_CLK_UARTC, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ { TEGRA114_CLK_UARTD, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ { TEGRA114_CLK_PLL_A, TEGRA114_CLK_CLK_MAX, 564480000, 0 },
+ { TEGRA114_CLK_PLL_A_OUT0, TEGRA114_CLK_CLK_MAX, 11289600, 0 },
+ { TEGRA114_CLK_I2S0, TEGRA114_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA114_CLK_I2S1, TEGRA114_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA114_CLK_I2S2, TEGRA114_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA114_CLK_I2S3, TEGRA114_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA114_CLK_I2S4, TEGRA114_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA114_CLK_HOST1X, TEGRA114_CLK_PLL_P, 136000000, 0 },
+ { TEGRA114_CLK_DFLL_SOC, TEGRA114_CLK_PLL_P, 51000000, 1 },
+ { TEGRA114_CLK_DFLL_REF, TEGRA114_CLK_PLL_P, 51000000, 1 },
+ { TEGRA114_CLK_DISP1, TEGRA114_CLK_PLL_P, 0, 0 },
+ { TEGRA114_CLK_DISP2, TEGRA114_CLK_PLL_P, 0, 0 },
+ { TEGRA114_CLK_GR2D, TEGRA114_CLK_PLL_C2, 300000000, 0 },
+ { TEGRA114_CLK_GR3D, TEGRA114_CLK_PLL_C2, 300000000, 0 },
+ { TEGRA114_CLK_DSIALP, TEGRA114_CLK_PLL_P, 68000000, 0 },
+ { TEGRA114_CLK_DSIBLP, TEGRA114_CLK_PLL_P, 68000000, 0 },
+ { TEGRA114_CLK_PLL_RE_VCO, TEGRA114_CLK_CLK_MAX, 612000000, 0 },
+ { TEGRA114_CLK_XUSB_SS_SRC, TEGRA114_CLK_PLL_RE_OUT, 122400000, 0 },
+ { TEGRA114_CLK_XUSB_FS_SRC, TEGRA114_CLK_PLL_U_48M, 48000000, 0 },
+ { TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0 },
+ { TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0 },
+ { TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0 },
+ { TEGRA114_CLK_VDE, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ { TEGRA114_CLK_SPDIF_IN_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_I2S0_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_I2S1_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_I2S2_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_I2S3_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_I2S4_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_VIMCLK_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA114_CLK_PWM, TEGRA114_CLK_PLL_P, 408000000, 0 },
+ /* must be the last entry */
+ { TEGRA114_CLK_CLK_MAX, TEGRA114_CLK_CLK_MAX, 0, 0 },
+};
+
+static void __init tegra114_clock_apply_init_table(void)
+{
+ tegra_init_from_table(init_table, clks, TEGRA114_CLK_CLK_MAX);
+}
+
+/**
+ * tegra114_car_barrier - wait for pending writes to the CAR to complete
+ *
+ * Wait for any outstanding writes to the CAR MMIO space from this CPU
+ * to complete before continuing execution. No return value.
+ */
+static void tegra114_car_barrier(void)
+{
+ wmb(); /* probably unnecessary */
+ readl_relaxed(clk_base + CPU_FINETRIM_SELECT);
+}
+
+/**
+ * tegra114_clock_tune_cpu_trimmers_high - use high-voltage propagation delays
+ *
+ * When the CPU rail voltage is in the high-voltage range, use the
+ * built-in hardwired clock propagation delays in the CPU clock
+ * shaper. No return value.
+ */
+void tegra114_clock_tune_cpu_trimmers_high(void)
+{
+ u32 select = 0;
+
+ /* Use hardwired rise->rise & fall->fall clock propagation delays */
+ select |= ~(CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 |
+ CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 |
+ CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6);
+ writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT);
+
+ tegra114_car_barrier();
+}
+EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_high);
+
+/**
+ * tegra114_clock_tune_cpu_trimmers_low - use low-voltage propagation delays
+ *
+ * When the CPU rail voltage is in the low-voltage range, use the
+ * extended clock propagation delays set by
+ * tegra114_clock_tune_cpu_trimmers_init(). The intention is to
+ * maintain the input clock duty cycle that the FCPU subsystem
+ * expects. No return value.
+ */
+void tegra114_clock_tune_cpu_trimmers_low(void)
+{
+ u32 select = 0;
+
+ /*
+ * Use software-specified rise->rise & fall->fall clock
+ * propagation delays (from
+ * tegra114_clock_tune_cpu_trimmers_init()
+ */
+ select |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 |
+ CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 |
+ CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6);
+ writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT);
+
+ tegra114_car_barrier();
+}
+EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_low);
+
+/**
+ * tegra114_clock_tune_cpu_trimmers_init - set up and enable clk prop delays
+ *
+ * Program extended clock propagation delays into the FCPU clock
+ * shaper and enable them. XXX Define the purpose - peak current
+ * reduction? No return value.
+ */
+/* XXX Initial voltage rail state assumption issues? */
+void tegra114_clock_tune_cpu_trimmers_init(void)
+{
+ u32 dr = 0, r = 0;
+
+ /* Increment the rise->rise clock delay by four steps */
+ r |= (CPU_FINETRIM_R_FCPU_1_MASK | CPU_FINETRIM_R_FCPU_2_MASK |
+ CPU_FINETRIM_R_FCPU_3_MASK | CPU_FINETRIM_R_FCPU_4_MASK |
+ CPU_FINETRIM_R_FCPU_5_MASK | CPU_FINETRIM_R_FCPU_6_MASK);
+ writel_relaxed(r, clk_base + CPU_FINETRIM_R);
+
+ /*
+ * Use the rise->rise clock propagation delay specified in the
+ * r field
+ */
+ dr |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 |
+ CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 |
+ CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6);
+ writel_relaxed(dr, clk_base + CPU_FINETRIM_DR);
+
+ tegra114_clock_tune_cpu_trimmers_low();
+}
+EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_init);
+
+/**
+ * tegra114_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
+ *
+ * Assert the reset line of the DFLL's DVCO. No return value.
+ */
+static void tegra114_clock_assert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v |= (1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra114_car_barrier();
+}
+
+/**
+ * tegra114_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
+ *
+ * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
+ * operate. No return value.
+ */
+static void tegra114_clock_deassert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra114_car_barrier();
+}
+
+static int tegra114_reset_assert(unsigned long id)
+{
+ if (id == TEGRA114_RST_DFLL_DVCO)
+ tegra114_clock_assert_dfll_dvco_reset();
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra114_reset_deassert(unsigned long id)
+{
+ if (id == TEGRA114_RST_DFLL_DVCO)
+ tegra114_clock_deassert_dfll_dvco_reset();
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __init tegra114_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra114 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ WARN_ON(1);
+ return;
+ }
+
+ pmc_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ WARN_ON(1);
+ return;
+ }
+
+ clks = tegra_clk_init(clk_base, TEGRA114_CLK_CLK_MAX,
+ TEGRA114_CLK_PERIPH_BANKS);
+ if (!clks)
+ return;
+
+ if (tegra_osc_clk_init(clk_base, tegra114_clks, tegra114_input_freq,
+ ARRAY_SIZE(tegra114_input_freq), 1, &osc_freq,
+ &pll_ref_freq) < 0)
+ return;
+
+ tegra114_fixed_clk_init(clk_base);
+ tegra114_pll_init(clk_base, pmc_base);
+ tegra114_periph_clk_init(clk_base, pmc_base);
+ tegra_audio_clk_init(clk_base, pmc_base, tegra114_clks,
+ tegra114_audio_plls,
+ ARRAY_SIZE(tegra114_audio_plls), 24000000);
+ tegra_super_clk_gen4_init(clk_base, pmc_base, tegra114_clks,
+ &pll_x_params);
+
+ tegra_init_special_resets(1, tegra114_reset_assert,
+ tegra114_reset_deassert);
+
+ tegra_add_of_provider(np, of_clk_src_onecell_get);
+ tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+
+ tegra_clk_apply_init_table = tegra114_clock_apply_init_table;
+
+ tegra_cpu_car_ops = &tegra114_cpu_car_ops;
+}
+CLK_OF_DECLARE(tegra114, "nvidia,tegra114-car", tegra114_clock_init);
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
new file mode 100644
index 000000000..457a77c5b
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -0,0 +1,749 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Tegra124 DFLL FCPU clock source driver
+ *
+ * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved.
+ *
+ * Aleksandr Frid <afrid@nvidia.com>
+ * Paul Walmsley <pwalmsley@nvidia.com>
+ */
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <soc/tegra/fuse.h>
+
+#include "clk.h"
+#include "clk-dfll.h"
+#include "cvb.h"
+
+struct dfll_fcpu_data {
+ const unsigned long *cpu_max_freq_table;
+ unsigned int cpu_max_freq_table_size;
+ const struct cvb_table *cpu_cvb_tables;
+ unsigned int cpu_cvb_tables_size;
+};
+
+/* Maximum CPU frequency, indexed by CPU speedo id */
+static const unsigned long tegra114_cpu_max_freq_table[] = {
+ [0] = 2040000000UL,
+ [1] = 1810500000UL,
+ [2] = 1912500000UL,
+ [3] = 1810500000UL,
+};
+
+#define T114_CPU_CVB_TABLE \
+ .min_millivolts = 1000, \
+ .max_millivolts = 1320, \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 306000000UL, { 2190643, -141851, 3576 } }, \
+ { 408000000UL, { 2250968, -144331, 3576 } }, \
+ { 510000000UL, { 2313333, -146811, 3576 } }, \
+ { 612000000UL, { 2377738, -149291, 3576 } }, \
+ { 714000000UL, { 2444183, -151771, 3576 } }, \
+ { 816000000UL, { 2512669, -154251, 3576 } }, \
+ { 918000000UL, { 2583194, -156731, 3576 } }, \
+ { 1020000000UL, { 2655759, -159211, 3576 } }, \
+ { 1122000000UL, { 2730365, -161691, 3576 } }, \
+ { 1224000000UL, { 2807010, -164171, 3576 } }, \
+ { 1326000000UL, { 2885696, -166651, 3576 } }, \
+ { 1428000000UL, { 2966422, -169131, 3576 } }, \
+ { 1530000000UL, { 3049183, -171601, 3576 } }, \
+ { 1606500000UL, { 3112179, -173451, 3576 } }, \
+ { 1708500000UL, { 3198504, -175931, 3576 } }, \
+ { 1810500000UL, { 3304747, -179126, 3576 } }, \
+ { 1912500000UL, { 3395401, -181606, 3576 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }, \
+ .cpu_dfll_data = { \
+ .tune0_low = 0x00b0039d, \
+ .tune0_high = 0x00b0009d, \
+ .tune1 = 0x0000001f, \
+ .tune_high_min_millivolts = 1050, \
+ }
+
+static const struct cvb_table tegra114_cpu_cvb_tables[] = {
+ {
+ .speedo_id = 0,
+ .process_id = -1,
+ .min_millivolts = 1000,
+ .max_millivolts = 1250,
+ .speedo_scale = 100,
+ .voltage_scale = 100,
+ .entries = {
+ { 306000000UL, { 107330, -1569, 0 } },
+ { 408000000UL, { 111250, -1666, 0 } },
+ { 510000000UL, { 110000, -1460, 0 } },
+ { 612000000UL, { 117290, -1745, 0 } },
+ { 714000000UL, { 122700, -1910, 0 } },
+ { 816000000UL, { 125620, -1945, 0 } },
+ { 918000000UL, { 130560, -2076, 0 } },
+ { 1020000000UL, { 137280, -2303, 0 } },
+ { 1122000000UL, { 146440, -2660, 0 } },
+ { 1224000000UL, { 152190, -2825, 0 } },
+ { 1326000000UL, { 157520, -2953, 0 } },
+ { 1428000000UL, { 166100, -3261, 0 } },
+ { 1530000000UL, { 176410, -3647, 0 } },
+ { 1632000000UL, { 189620, -4186, 0 } },
+ { 1734000000UL, { 203190, -4725, 0 } },
+ { 1836000000UL, { 222670, -5573, 0 } },
+ { 1938000000UL, { 256210, -7165, 0 } },
+ { 2040000000UL, { 250050, -6544, 0 } },
+ { 0UL, { 0, 0, 0 } },
+ },
+ .cpu_dfll_data = {
+ .tune0_low = 0x00b0019d,
+ .tune0_high = 0x00b0019d,
+ .tune1 = 0x0000001f,
+ .tune_high_min_millivolts = 1000,
+ }
+ },
+ {
+ .speedo_id = 1,
+ .process_id = -1,
+ T114_CPU_CVB_TABLE
+ },
+ {
+ .speedo_id = 2,
+ .process_id = -1,
+ T114_CPU_CVB_TABLE
+ },
+ {
+ .speedo_id = 3,
+ .process_id = -1,
+ T114_CPU_CVB_TABLE
+ },
+};
+
+/* Maximum CPU frequency, indexed by CPU speedo id */
+static const unsigned long tegra124_cpu_max_freq_table[] = {
+ [0] = 2014500000UL,
+ [1] = 2320500000UL,
+ [2] = 2116500000UL,
+ [3] = 2524500000UL,
+};
+
+static const struct cvb_table tegra124_cpu_cvb_tables[] = {
+ {
+ .speedo_id = -1,
+ .process_id = -1,
+ .min_millivolts = 900,
+ .max_millivolts = 1260,
+ .speedo_scale = 100,
+ .voltage_scale = 1000,
+ .entries = {
+ { 204000000UL, { 1112619, -29295, 402 } },
+ { 306000000UL, { 1150460, -30585, 402 } },
+ { 408000000UL, { 1190122, -31865, 402 } },
+ { 510000000UL, { 1231606, -33155, 402 } },
+ { 612000000UL, { 1274912, -34435, 402 } },
+ { 714000000UL, { 1320040, -35725, 402 } },
+ { 816000000UL, { 1366990, -37005, 402 } },
+ { 918000000UL, { 1415762, -38295, 402 } },
+ { 1020000000UL, { 1466355, -39575, 402 } },
+ { 1122000000UL, { 1518771, -40865, 402 } },
+ { 1224000000UL, { 1573009, -42145, 402 } },
+ { 1326000000UL, { 1629068, -43435, 402 } },
+ { 1428000000UL, { 1686950, -44715, 402 } },
+ { 1530000000UL, { 1746653, -46005, 402 } },
+ { 1632000000UL, { 1808179, -47285, 402 } },
+ { 1734000000UL, { 1871526, -48575, 402 } },
+ { 1836000000UL, { 1936696, -49855, 402 } },
+ { 1938000000UL, { 2003687, -51145, 402 } },
+ { 2014500000UL, { 2054787, -52095, 402 } },
+ { 2116500000UL, { 2124957, -53385, 402 } },
+ { 2218500000UL, { 2196950, -54665, 402 } },
+ { 2320500000UL, { 2270765, -55955, 402 } },
+ { 2422500000UL, { 2346401, -57235, 402 } },
+ { 2524500000UL, { 2437299, -58535, 402 } },
+ { 0UL, { 0, 0, 0 } },
+ },
+ .cpu_dfll_data = {
+ .tune0_low = 0x005020ff,
+ .tune0_high = 0x005040ff,
+ .tune1 = 0x00000060,
+ }
+ },
+};
+
+static const unsigned long tegra210_cpu_max_freq_table[] = {
+ [0] = 1912500000UL,
+ [1] = 1912500000UL,
+ [2] = 2218500000UL,
+ [3] = 1785000000UL,
+ [4] = 1632000000UL,
+ [5] = 1912500000UL,
+ [6] = 2014500000UL,
+ [7] = 1734000000UL,
+ [8] = 1683000000UL,
+ [9] = 1555500000UL,
+ [10] = 1504500000UL,
+};
+
+#define TEGRA210_CPU_CVB_TABLE \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 1007452, -23865, 370 } }, \
+ { 306000000UL, { 1052709, -24875, 370 } }, \
+ { 408000000UL, { 1099069, -25895, 370 } }, \
+ { 510000000UL, { 1146534, -26905, 370 } }, \
+ { 612000000UL, { 1195102, -27915, 370 } }, \
+ { 714000000UL, { 1244773, -28925, 370 } }, \
+ { 816000000UL, { 1295549, -29935, 370 } }, \
+ { 918000000UL, { 1347428, -30955, 370 } }, \
+ { 1020000000UL, { 1400411, -31965, 370 } }, \
+ { 1122000000UL, { 1454497, -32975, 370 } }, \
+ { 1224000000UL, { 1509687, -33985, 370 } }, \
+ { 1326000000UL, { 1565981, -35005, 370 } }, \
+ { 1428000000UL, { 1623379, -36015, 370 } }, \
+ { 1530000000UL, { 1681880, -37025, 370 } }, \
+ { 1632000000UL, { 1741485, -38035, 370 } }, \
+ { 1734000000UL, { 1802194, -39055, 370 } }, \
+ { 1836000000UL, { 1864006, -40065, 370 } }, \
+ { 1912500000UL, { 1910780, -40815, 370 } }, \
+ { 2014500000UL, { 1227000, 0, 0 } }, \
+ { 2218500000UL, { 1227000, 0, 0 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+#define TEGRA210_CPU_CVB_TABLE_XA \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 1250024, -39785, 565 } }, \
+ { 306000000UL, { 1297556, -41145, 565 } }, \
+ { 408000000UL, { 1346718, -42505, 565 } }, \
+ { 510000000UL, { 1397511, -43855, 565 } }, \
+ { 612000000UL, { 1449933, -45215, 565 } }, \
+ { 714000000UL, { 1503986, -46575, 565 } }, \
+ { 816000000UL, { 1559669, -47935, 565 } }, \
+ { 918000000UL, { 1616982, -49295, 565 } }, \
+ { 1020000000UL, { 1675926, -50645, 565 } }, \
+ { 1122000000UL, { 1736500, -52005, 565 } }, \
+ { 1224000000UL, { 1798704, -53365, 565 } }, \
+ { 1326000000UL, { 1862538, -54725, 565 } }, \
+ { 1428000000UL, { 1928003, -56085, 565 } }, \
+ { 1530000000UL, { 1995097, -57435, 565 } }, \
+ { 1606500000UL, { 2046149, -58445, 565 } }, \
+ { 1632000000UL, { 2063822, -58795, 565 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+#define TEGRA210_CPU_CVB_TABLE_EUCM1 \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 734429, 0, 0 } }, \
+ { 306000000UL, { 768191, 0, 0 } }, \
+ { 408000000UL, { 801953, 0, 0 } }, \
+ { 510000000UL, { 835715, 0, 0 } }, \
+ { 612000000UL, { 869477, 0, 0 } }, \
+ { 714000000UL, { 903239, 0, 0 } }, \
+ { 816000000UL, { 937001, 0, 0 } }, \
+ { 918000000UL, { 970763, 0, 0 } }, \
+ { 1020000000UL, { 1004525, 0, 0 } }, \
+ { 1122000000UL, { 1038287, 0, 0 } }, \
+ { 1224000000UL, { 1072049, 0, 0 } }, \
+ { 1326000000UL, { 1105811, 0, 0 } }, \
+ { 1428000000UL, { 1130000, 0, 0 } }, \
+ { 1555500000UL, { 1130000, 0, 0 } }, \
+ { 1632000000UL, { 1170000, 0, 0 } }, \
+ { 1734000000UL, { 1227500, 0, 0 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+#define TEGRA210_CPU_CVB_TABLE_EUCM2 \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 742283, 0, 0 } }, \
+ { 306000000UL, { 776249, 0, 0 } }, \
+ { 408000000UL, { 810215, 0, 0 } }, \
+ { 510000000UL, { 844181, 0, 0 } }, \
+ { 612000000UL, { 878147, 0, 0 } }, \
+ { 714000000UL, { 912113, 0, 0 } }, \
+ { 816000000UL, { 946079, 0, 0 } }, \
+ { 918000000UL, { 980045, 0, 0 } }, \
+ { 1020000000UL, { 1014011, 0, 0 } }, \
+ { 1122000000UL, { 1047977, 0, 0 } }, \
+ { 1224000000UL, { 1081943, 0, 0 } }, \
+ { 1326000000UL, { 1090000, 0, 0 } }, \
+ { 1479000000UL, { 1090000, 0, 0 } }, \
+ { 1555500000UL, { 1162000, 0, 0 } }, \
+ { 1683000000UL, { 1195000, 0, 0 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+#define TEGRA210_CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 742283, 0, 0 } }, \
+ { 306000000UL, { 776249, 0, 0 } }, \
+ { 408000000UL, { 810215, 0, 0 } }, \
+ { 510000000UL, { 844181, 0, 0 } }, \
+ { 612000000UL, { 878147, 0, 0 } }, \
+ { 714000000UL, { 912113, 0, 0 } }, \
+ { 816000000UL, { 946079, 0, 0 } }, \
+ { 918000000UL, { 980045, 0, 0 } }, \
+ { 1020000000UL, { 1014011, 0, 0 } }, \
+ { 1122000000UL, { 1047977, 0, 0 } }, \
+ { 1224000000UL, { 1081943, 0, 0 } }, \
+ { 1326000000UL, { 1090000, 0, 0 } }, \
+ { 1479000000UL, { 1090000, 0, 0 } }, \
+ { 1504500000UL, { 1120000, 0, 0 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+#define TEGRA210_CPU_CVB_TABLE_ODN \
+ .speedo_scale = 100, \
+ .voltage_scale = 1000, \
+ .entries = { \
+ { 204000000UL, { 721094, 0, 0 } }, \
+ { 306000000UL, { 754040, 0, 0 } }, \
+ { 408000000UL, { 786986, 0, 0 } }, \
+ { 510000000UL, { 819932, 0, 0 } }, \
+ { 612000000UL, { 852878, 0, 0 } }, \
+ { 714000000UL, { 885824, 0, 0 } }, \
+ { 816000000UL, { 918770, 0, 0 } }, \
+ { 918000000UL, { 915716, 0, 0 } }, \
+ { 1020000000UL, { 984662, 0, 0 } }, \
+ { 1122000000UL, { 1017608, 0, 0 } }, \
+ { 1224000000UL, { 1050554, 0, 0 } }, \
+ { 1326000000UL, { 1083500, 0, 0 } }, \
+ { 1428000000UL, { 1116446, 0, 0 } }, \
+ { 1581000000UL, { 1130000, 0, 0 } }, \
+ { 1683000000UL, { 1168000, 0, 0 } }, \
+ { 1785000000UL, { 1227500, 0, 0 } }, \
+ { 0UL, { 0, 0, 0 } }, \
+ }
+
+static struct cvb_table tegra210_cpu_cvb_tables[] = {
+ {
+ .speedo_id = 10,
+ .process_id = 0,
+ .min_millivolts = 840,
+ .max_millivolts = 1120,
+ TEGRA210_CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 10,
+ .process_id = 1,
+ .min_millivolts = 840,
+ .max_millivolts = 1120,
+ TEGRA210_CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 9,
+ .process_id = 0,
+ .min_millivolts = 900,
+ .max_millivolts = 1162,
+ TEGRA210_CPU_CVB_TABLE_EUCM2,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 9,
+ .process_id = 1,
+ .min_millivolts = 900,
+ .max_millivolts = 1162,
+ TEGRA210_CPU_CVB_TABLE_EUCM2,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 8,
+ .process_id = 0,
+ .min_millivolts = 900,
+ .max_millivolts = 1195,
+ TEGRA210_CPU_CVB_TABLE_EUCM2,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 8,
+ .process_id = 1,
+ .min_millivolts = 900,
+ .max_millivolts = 1195,
+ TEGRA210_CPU_CVB_TABLE_EUCM2,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 7,
+ .process_id = 0,
+ .min_millivolts = 841,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE_EUCM1,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 7,
+ .process_id = 1,
+ .min_millivolts = 841,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE_EUCM1,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 6,
+ .process_id = 0,
+ .min_millivolts = 870,
+ .max_millivolts = 1150,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 6,
+ .process_id = 1,
+ .min_millivolts = 870,
+ .max_millivolts = 1150,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ }
+ },
+ {
+ .speedo_id = 5,
+ .process_id = 0,
+ .min_millivolts = 818,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 5,
+ .process_id = 1,
+ .min_millivolts = 818,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 4,
+ .process_id = -1,
+ .min_millivolts = 918,
+ .max_millivolts = 1113,
+ TEGRA210_CPU_CVB_TABLE_XA,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune1 = 0x17711BD,
+ }
+ },
+ {
+ .speedo_id = 3,
+ .process_id = 0,
+ .min_millivolts = 825,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE_ODN,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 3,
+ .process_id = 1,
+ .min_millivolts = 825,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE_ODN,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 2,
+ .process_id = 0,
+ .min_millivolts = 870,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ }
+ },
+ {
+ .speedo_id = 2,
+ .process_id = 1,
+ .min_millivolts = 870,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ }
+ },
+ {
+ .speedo_id = 1,
+ .process_id = 0,
+ .min_millivolts = 837,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 1,
+ .process_id = 1,
+ .min_millivolts = 837,
+ .max_millivolts = 1227,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 0,
+ .process_id = 0,
+ .min_millivolts = 850,
+ .max_millivolts = 1170,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x20091d9,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+ {
+ .speedo_id = 0,
+ .process_id = 1,
+ .min_millivolts = 850,
+ .max_millivolts = 1170,
+ TEGRA210_CPU_CVB_TABLE,
+ .cpu_dfll_data = {
+ .tune0_low = 0xffead0ff,
+ .tune0_high = 0xffead0ff,
+ .tune1 = 0x25501d0,
+ .tune_high_min_millivolts = 864,
+ }
+ },
+};
+
+static const struct dfll_fcpu_data tegra114_dfll_fcpu_data = {
+ .cpu_max_freq_table = tegra114_cpu_max_freq_table,
+ .cpu_max_freq_table_size = ARRAY_SIZE(tegra114_cpu_max_freq_table),
+ .cpu_cvb_tables = tegra114_cpu_cvb_tables,
+ .cpu_cvb_tables_size = ARRAY_SIZE(tegra114_cpu_cvb_tables)
+};
+
+static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
+ .cpu_max_freq_table = tegra124_cpu_max_freq_table,
+ .cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
+ .cpu_cvb_tables = tegra124_cpu_cvb_tables,
+ .cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
+};
+
+static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
+ .cpu_max_freq_table = tegra210_cpu_max_freq_table,
+ .cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
+ .cpu_cvb_tables = tegra210_cpu_cvb_tables,
+ .cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
+};
+
+static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
+ {
+ .compatible = "nvidia,tegra114-dfll",
+ .data = &tegra114_dfll_fcpu_data,
+ },
+ {
+ .compatible = "nvidia,tegra124-dfll",
+ .data = &tegra124_dfll_fcpu_data,
+ },
+ {
+ .compatible = "nvidia,tegra210-dfll",
+ .data = &tegra210_dfll_fcpu_data
+ },
+ { },
+};
+
+static void get_alignment_from_dt(struct device *dev,
+ struct rail_alignment *align)
+{
+ if (of_property_read_u32(dev->of_node,
+ "nvidia,pwm-voltage-step-microvolts",
+ &align->step_uv))
+ align->step_uv = 0;
+
+ if (of_property_read_u32(dev->of_node,
+ "nvidia,pwm-min-microvolts",
+ &align->offset_uv))
+ align->offset_uv = 0;
+}
+
+static int get_alignment_from_regulator(struct device *dev,
+ struct rail_alignment *align)
+{
+ struct regulator *reg = regulator_get(dev, "vdd-cpu");
+
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ align->offset_uv = regulator_list_voltage(reg, 0);
+ align->step_uv = regulator_get_linear_step(reg);
+
+ regulator_put(reg);
+
+ return 0;
+}
+
+static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
+{
+ int process_id, speedo_id, speedo_value, err;
+ struct tegra_dfll_soc_data *soc;
+ const struct dfll_fcpu_data *fcpu_data;
+ struct rail_alignment align;
+
+ fcpu_data = of_device_get_match_data(&pdev->dev);
+ if (!fcpu_data)
+ return -ENODEV;
+
+ process_id = tegra_sku_info.cpu_process_id;
+ speedo_id = tegra_sku_info.cpu_speedo_id;
+ speedo_value = tegra_sku_info.cpu_speedo_value;
+
+ if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
+ dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
+ speedo_id);
+ return -ENODEV;
+ }
+
+ soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
+ if (!soc)
+ return -ENOMEM;
+
+ soc->dev = get_cpu_device(0);
+ if (!soc->dev) {
+ dev_err(&pdev->dev, "no CPU0 device\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "nvidia,pwm-to-pmic")) {
+ get_alignment_from_dt(&pdev->dev, &align);
+ } else {
+ err = get_alignment_from_regulator(&pdev->dev, &align);
+ if (err)
+ return err;
+ }
+
+ soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
+
+ soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
+ fcpu_data->cpu_cvb_tables_size,
+ &align, process_id, speedo_id,
+ speedo_value, soc->max_freq);
+ soc->alignment = align;
+
+ if (IS_ERR(soc->cvb)) {
+ dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
+ PTR_ERR(soc->cvb));
+ return PTR_ERR(soc->cvb);
+ }
+
+ err = tegra_dfll_register(pdev, soc);
+ if (err < 0) {
+ tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tegra124_dfll_fcpu_remove(struct platform_device *pdev)
+{
+ struct tegra_dfll_soc_data *soc;
+
+ /*
+ * Note that exiting early here is dangerous as after this function
+ * returns *soc is freed.
+ */
+ soc = tegra_dfll_unregister(pdev);
+ if (IS_ERR(soc))
+ return;
+
+ tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
+}
+
+static const struct dev_pm_ops tegra124_dfll_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
+ tegra_dfll_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume)
+};
+
+static struct platform_driver tegra124_dfll_fcpu_driver = {
+ .probe = tegra124_dfll_fcpu_probe,
+ .remove = tegra124_dfll_fcpu_remove,
+ .driver = {
+ .name = "tegra124-dfll",
+ .of_match_table = tegra124_dfll_fcpu_of_match,
+ .pm = &tegra124_dfll_pm_ops,
+ },
+};
+builtin_platform_driver(tegra124_dfll_fcpu_driver);
diff --git a/drivers/clk/tegra/clk-tegra124-emc.c b/drivers/clk/tegra/clk-tegra124-emc.c
new file mode 100644
index 000000000..f3b2c96fd
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra124-emc.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/clk/tegra/clk-emc.c
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author:
+ * Mikko Perttunen <mperttunen@nvidia.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk/tegra.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "clk.h"
+
+#define CLK_SOURCE_EMC 0x19c
+
+#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT 0
+#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK 0xff
+#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK) << \
+ CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT)
+
+#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT 29
+#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK 0x7
+#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \
+ CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT)
+
+static const char * const emc_parent_clk_names[] = {
+ "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud",
+ "pll_c2", "pll_c3", "pll_c_ud"
+};
+
+/*
+ * List of clock sources for various parents the EMC clock can have.
+ * When we change the timing to a timing with a parent that has the same
+ * clock source as the current parent, we must first change to a backup
+ * timing that has a different clock source.
+ */
+
+#define EMC_SRC_PLL_M 0
+#define EMC_SRC_PLL_C 1
+#define EMC_SRC_PLL_P 2
+#define EMC_SRC_CLK_M 3
+#define EMC_SRC_PLL_C2 4
+#define EMC_SRC_PLL_C3 5
+
+static const char emc_parent_clk_sources[] = {
+ EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M,
+ EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C
+};
+
+struct emc_timing {
+ unsigned long rate, parent_rate;
+ u8 parent_index;
+ struct clk *parent;
+ u32 ram_code;
+};
+
+struct tegra_clk_emc {
+ struct clk_hw hw;
+ void __iomem *clk_regs;
+ struct clk *prev_parent;
+ bool changing_timing;
+
+ struct device_node *emc_node;
+ struct tegra_emc *emc;
+
+ int num_timings;
+ struct emc_timing *timings;
+ spinlock_t *lock;
+
+ tegra124_emc_prepare_timing_change_cb *prepare_timing_change;
+ tegra124_emc_complete_timing_change_cb *complete_timing_change;
+};
+
+/* Common clock framework callback implementations */
+
+static unsigned long emc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_emc *tegra;
+ u32 val, div;
+
+ tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ /*
+ * CCF wrongly assumes that the parent won't change during set_rate,
+ * so get the parent rate explicitly.
+ */
+ parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+
+ val = readl(tegra->clk_regs + CLK_SOURCE_EMC);
+ div = val & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK;
+
+ return parent_rate / (div + 2) * 2;
+}
+
+/*
+ * Rounds up unless no higher rate exists, in which case down. This way is
+ * safer since things have EMC rate floors. Also don't touch parent_rate
+ * since we don't want the CCF to play with our parent clocks.
+ */
+static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct tegra_clk_emc *tegra;
+ u8 ram_code = tegra_read_ram_code();
+ struct emc_timing *timing = NULL;
+ int i, k, t;
+
+ tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ for (k = 0; k < tegra->num_timings; k++) {
+ if (tegra->timings[k].ram_code == ram_code)
+ break;
+ }
+
+ for (t = k; t < tegra->num_timings; t++) {
+ if (tegra->timings[t].ram_code != ram_code)
+ break;
+ }
+
+ for (i = k; i < t; i++) {
+ timing = tegra->timings + i;
+
+ if (timing->rate < req->rate && i != t - 1)
+ continue;
+
+ if (timing->rate > req->max_rate) {
+ i = max(i, k + 1);
+ req->rate = tegra->timings[i - 1].rate;
+ return 0;
+ }
+
+ if (timing->rate < req->min_rate)
+ continue;
+
+ req->rate = timing->rate;
+ return 0;
+ }
+
+ if (timing) {
+ req->rate = timing->rate;
+ return 0;
+ }
+
+ req->rate = clk_hw_get_rate(hw);
+ return 0;
+}
+
+static u8 emc_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_emc *tegra;
+ u32 val;
+
+ tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ val = readl(tegra->clk_regs + CLK_SOURCE_EMC);
+
+ return (val >> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT)
+ & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK;
+}
+
+static struct tegra_emc *emc_ensure_emc_driver(struct tegra_clk_emc *tegra)
+{
+ struct platform_device *pdev;
+
+ if (tegra->emc)
+ return tegra->emc;
+
+ if (!tegra->prepare_timing_change || !tegra->complete_timing_change)
+ return NULL;
+
+ if (!tegra->emc_node)
+ return NULL;
+
+ pdev = of_find_device_by_node(tegra->emc_node);
+ if (!pdev) {
+ pr_err("%s: could not get external memory controller\n",
+ __func__);
+ return NULL;
+ }
+
+ of_node_put(tegra->emc_node);
+ tegra->emc_node = NULL;
+
+ tegra->emc = platform_get_drvdata(pdev);
+ put_device(&pdev->dev);
+ if (!tegra->emc) {
+ pr_err("%s: cannot find EMC driver\n", __func__);
+ return NULL;
+ }
+
+ return tegra->emc;
+}
+
+static int emc_set_timing(struct tegra_clk_emc *tegra,
+ struct emc_timing *timing)
+{
+ int err;
+ u8 div;
+ u32 car_value;
+ unsigned long flags = 0;
+ struct tegra_emc *emc = emc_ensure_emc_driver(tegra);
+
+ if (!emc)
+ return -ENOENT;
+
+ pr_debug("going to rate %ld prate %ld p %s\n", timing->rate,
+ timing->parent_rate, __clk_get_name(timing->parent));
+
+ if (emc_get_parent(&tegra->hw) == timing->parent_index &&
+ clk_get_rate(timing->parent) != timing->parent_rate) {
+ WARN_ONCE(1, "parent %s rate mismatch %lu %lu\n",
+ __clk_get_name(timing->parent),
+ clk_get_rate(timing->parent),
+ timing->parent_rate);
+ return -EINVAL;
+ }
+
+ tegra->changing_timing = true;
+
+ err = clk_set_rate(timing->parent, timing->parent_rate);
+ if (err) {
+ pr_err("cannot change parent %s rate to %ld: %d\n",
+ __clk_get_name(timing->parent), timing->parent_rate,
+ err);
+
+ return err;
+ }
+
+ err = clk_prepare_enable(timing->parent);
+ if (err) {
+ pr_err("cannot enable parent clock: %d\n", err);
+ return err;
+ }
+
+ div = timing->parent_rate / (timing->rate / 2) - 2;
+
+ err = tegra->prepare_timing_change(emc, timing->rate);
+ if (err) {
+ clk_disable_unprepare(timing->parent);
+ return err;
+ }
+
+ spin_lock_irqsave(tegra->lock, flags);
+
+ car_value = readl(tegra->clk_regs + CLK_SOURCE_EMC);
+
+ car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_SRC(~0);
+ car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_SRC(timing->parent_index);
+
+ car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(~0);
+ car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(div);
+
+ writel(car_value, tegra->clk_regs + CLK_SOURCE_EMC);
+
+ spin_unlock_irqrestore(tegra->lock, flags);
+
+ tegra->complete_timing_change(emc, timing->rate);
+
+ clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent));
+ clk_disable_unprepare(tegra->prev_parent);
+
+ tegra->prev_parent = timing->parent;
+ tegra->changing_timing = false;
+
+ return 0;
+}
+
+/*
+ * Get backup timing to use as an intermediate step when a change between
+ * two timings with the same clock source has been requested. First try to
+ * find a timing with a higher clock rate to avoid a rate below any set rate
+ * floors. If that is not possible, find a lower rate.
+ */
+static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra,
+ int timing_index)
+{
+ int i;
+ u32 ram_code = tegra_read_ram_code();
+ struct emc_timing *timing;
+
+ for (i = timing_index+1; i < tegra->num_timings; i++) {
+ timing = tegra->timings + i;
+ if (timing->ram_code != ram_code)
+ break;
+
+ if (emc_parent_clk_sources[timing->parent_index] !=
+ emc_parent_clk_sources[
+ tegra->timings[timing_index].parent_index])
+ return timing;
+ }
+
+ for (i = timing_index-1; i >= 0; --i) {
+ timing = tegra->timings + i;
+ if (timing->ram_code != ram_code)
+ break;
+
+ if (emc_parent_clk_sources[timing->parent_index] !=
+ emc_parent_clk_sources[
+ tegra->timings[timing_index].parent_index])
+ return timing;
+ }
+
+ return NULL;
+}
+
+static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_emc *tegra;
+ struct emc_timing *timing = NULL;
+ int i, err;
+ u32 ram_code = tegra_read_ram_code();
+
+ tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ if (clk_hw_get_rate(hw) == rate)
+ return 0;
+
+ /*
+ * When emc_set_timing changes the parent rate, CCF will propagate
+ * that downward to us, so ignore any set_rate calls while a rate
+ * change is already going on.
+ */
+ if (tegra->changing_timing)
+ return 0;
+
+ for (i = 0; i < tegra->num_timings; i++) {
+ if (tegra->timings[i].rate == rate &&
+ tegra->timings[i].ram_code == ram_code) {
+ timing = tegra->timings + i;
+ break;
+ }
+ }
+
+ if (!timing) {
+ pr_err("cannot switch to rate %ld without emc table\n", rate);
+ return -EINVAL;
+ }
+
+ if (emc_parent_clk_sources[emc_get_parent(hw)] ==
+ emc_parent_clk_sources[timing->parent_index] &&
+ clk_get_rate(timing->parent) != timing->parent_rate) {
+ /*
+ * Parent clock source not changed but parent rate has changed,
+ * need to temporarily switch to another parent
+ */
+
+ struct emc_timing *backup_timing;
+
+ backup_timing = get_backup_timing(tegra, i);
+ if (!backup_timing) {
+ pr_err("cannot find backup timing\n");
+ return -EINVAL;
+ }
+
+ pr_debug("using %ld as backup rate when going to %ld\n",
+ backup_timing->rate, rate);
+
+ err = emc_set_timing(tegra, backup_timing);
+ if (err) {
+ pr_err("cannot set backup timing: %d\n", err);
+ return err;
+ }
+ }
+
+ return emc_set_timing(tegra, timing);
+}
+
+/* Initialization and deinitialization */
+
+static int load_one_timing_from_dt(struct tegra_clk_emc *tegra,
+ struct emc_timing *timing,
+ struct device_node *node)
+{
+ int err, i;
+ u32 tmp;
+
+ err = of_property_read_u32(node, "clock-frequency", &tmp);
+ if (err) {
+ pr_err("timing %pOF: failed to read rate\n", node);
+ return err;
+ }
+
+ timing->rate = tmp;
+
+ err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp);
+ if (err) {
+ pr_err("timing %pOF: failed to read parent rate\n", node);
+ return err;
+ }
+
+ timing->parent_rate = tmp;
+
+ timing->parent = of_clk_get_by_name(node, "emc-parent");
+ if (IS_ERR(timing->parent)) {
+ pr_err("timing %pOF: failed to get parent clock\n", node);
+ return PTR_ERR(timing->parent);
+ }
+
+ timing->parent_index = 0xff;
+ i = match_string(emc_parent_clk_names, ARRAY_SIZE(emc_parent_clk_names),
+ __clk_get_name(timing->parent));
+ if (i < 0) {
+ pr_err("timing %pOF: %s is not a valid parent\n",
+ node, __clk_get_name(timing->parent));
+ clk_put(timing->parent);
+ return -EINVAL;
+ }
+
+ timing->parent_index = i;
+ return 0;
+}
+
+static int cmp_timings(const void *_a, const void *_b)
+{
+ const struct emc_timing *a = _a;
+ const struct emc_timing *b = _b;
+
+ if (a->rate < b->rate)
+ return -1;
+ else if (a->rate == b->rate)
+ return 0;
+ else
+ return 1;
+}
+
+static int load_timings_from_dt(struct tegra_clk_emc *tegra,
+ struct device_node *node,
+ u32 ram_code)
+{
+ struct emc_timing *timings_ptr;
+ int child_count = of_get_child_count(node);
+ int i = 0, err;
+ size_t size;
+
+ size = (tegra->num_timings + child_count) * sizeof(struct emc_timing);
+
+ tegra->timings = krealloc(tegra->timings, size, GFP_KERNEL);
+ if (!tegra->timings)
+ return -ENOMEM;
+
+ timings_ptr = tegra->timings + tegra->num_timings;
+ tegra->num_timings += child_count;
+
+ for_each_child_of_node_scoped(node, child) {
+ struct emc_timing *timing = timings_ptr + (i++);
+
+ err = load_one_timing_from_dt(tegra, timing, child);
+ if (err) {
+ kfree(tegra->timings);
+ return err;
+ }
+
+ timing->ram_code = ram_code;
+ }
+
+ sort(timings_ptr, child_count, sizeof(struct emc_timing),
+ cmp_timings, NULL);
+
+ return 0;
+}
+
+static const struct clk_ops tegra_clk_emc_ops = {
+ .recalc_rate = emc_recalc_rate,
+ .determine_rate = emc_determine_rate,
+ .set_rate = emc_set_rate,
+ .get_parent = emc_get_parent,
+};
+
+struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
+ spinlock_t *lock)
+{
+ struct tegra_clk_emc *tegra;
+ struct clk_init_data init;
+ struct device_node *node;
+ u32 node_ram_code;
+ struct clk *clk;
+ int err;
+
+ tegra = kzalloc_objs(*tegra, 1);
+ if (!tegra)
+ return ERR_PTR(-ENOMEM);
+
+ tegra->clk_regs = base;
+ tegra->lock = lock;
+
+ tegra->num_timings = 0;
+
+ for_each_child_of_node(np, node) {
+ err = of_property_read_u32(node, "nvidia,ram-code",
+ &node_ram_code);
+ if (err)
+ continue;
+
+ /*
+ * Store timings for all ram codes as we cannot read the
+ * fuses until the apbmisc driver is loaded.
+ */
+ err = load_timings_from_dt(tegra, node, node_ram_code);
+ if (err) {
+ of_node_put(node);
+ kfree(tegra);
+ return ERR_PTR(err);
+ }
+ }
+
+ if (tegra->num_timings == 0)
+ pr_warn("%s: no memory timings registered\n", __func__);
+
+ tegra->emc_node = of_parse_phandle(np,
+ "nvidia,external-memory-controller", 0);
+ if (!tegra->emc_node)
+ pr_warn("%s: couldn't find node for EMC driver\n", __func__);
+
+ init.name = "emc";
+ init.ops = &tegra_clk_emc_ops;
+ init.flags = CLK_IS_CRITICAL;
+ init.parent_names = emc_parent_clk_names;
+ init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
+
+ tegra->hw.init = &init;
+
+ clk = clk_register(NULL, &tegra->hw);
+ if (IS_ERR(clk)) {
+ kfree(tegra);
+ return clk;
+ }
+
+ tegra->prev_parent = clk_hw_get_parent_by_index(
+ &tegra->hw, emc_get_parent(&tegra->hw))->clk;
+ tegra->changing_timing = false;
+
+ /* Allow debugging tools to see the EMC clock */
+ clk_register_clkdev(clk, "emc", "tegra-clk-debug");
+
+ return clk;
+};
+
+void tegra124_clk_set_emc_callbacks(tegra124_emc_prepare_timing_change_cb *prep_cb,
+ tegra124_emc_complete_timing_change_cb *complete_cb)
+{
+ struct clk *clk = __clk_lookup("emc");
+ struct tegra_clk_emc *tegra;
+ struct clk_hw *hw;
+
+ if (clk) {
+ hw = __clk_get_hw(clk);
+ tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ tegra->prepare_timing_change = prep_cb;
+ tegra->complete_timing_change = complete_cb;
+ }
+}
+EXPORT_SYMBOL_GPL(tegra124_clk_set_emc_callbacks);
+
+bool tegra124_clk_emc_driver_available(struct clk_hw *hw)
+{
+ struct tegra_clk_emc *tegra = container_of(hw, struct tegra_clk_emc, hw);
+
+ return tegra->prepare_timing_change && tegra->complete_timing_change;
+}
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
new file mode 100644
index 000000000..6c46592d7
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -0,0 +1,1597 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2014 NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clk/tegra.h>
+#include <dt-bindings/clock/tegra124-car.h>
+#include <dt-bindings/reset/tegra124-car.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+/*
+ * TEGRA124_CAR_BANK_COUNT: the number of peripheral clock register
+ * banks present in the Tegra124/132 CAR IP block. The banks are
+ * identified by single letters, e.g.: L, H, U, V, W, X. See
+ * periph_regs[] in drivers/clk/tegra/clk.c
+ */
+#define TEGRA124_CAR_BANK_COUNT 6
+
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_EMC 0x19c
+#define CLK_SOURCE_SOR0 0x414
+
+#define RST_DFLL_DVCO 0x2f4
+#define DVFS_DFLL_RESET_SHIFT 0
+
+#define PLLC_BASE 0x80
+#define PLLC_OUT 0x84
+#define PLLC_MISC2 0x88
+#define PLLC_MISC 0x8c
+#define PLLC2_BASE 0x4e8
+#define PLLC2_MISC 0x4ec
+#define PLLC3_BASE 0x4fc
+#define PLLC3_MISC 0x500
+#define PLLM_BASE 0x90
+#define PLLM_OUT 0x94
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define PLLE_AUX 0x48c
+#define PLLRE_BASE 0x4c4
+#define PLLRE_MISC 0x4c8
+#define PLLDP_BASE 0x590
+#define PLLDP_MISC 0x594
+#define PLLC4_BASE 0x5a4
+#define PLLC4_MISC 0x5a8
+
+#define PLLC_IDDQ_BIT 26
+#define PLLRE_IDDQ_BIT 16
+#define PLLSS_IDDQ_BIT 19
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+#define PLLRE_MISC_LOCK BIT(24)
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLC_MISC_LOCK_ENABLE 24
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+#define PLLRE_MISC_LOCK_ENABLE 30
+#define PLLSS_MISC_LOCK_ENABLE 30
+
+#define PLLXC_SW_MAX_P 6
+
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
+
+#define CCLKG_BURST_POLICY 0x368
+
+/* Tegra CPU clock and reset control regs */
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
+
+#define MASK(x) (BIT(x) - 1)
+
+#define MUX8_NOGATE_LOCK(_name, _parents, _offset, _clk_id, _lock) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \
+ 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\
+ 0, TEGRA_PERIPH_NO_GATE, _clk_id,\
+ _parents##_idx, 0, _lock)
+
+#define NODIV(_name, _parents, _offset, \
+ _mux_shift, _mux_mask, _clk_num, \
+ _gate_flags, _clk_id, _lock) \
+ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\
+ _mux_shift, _mux_mask, 0, 0, 0, 0, 0,\
+ _clk_num, (_gate_flags) | TEGRA_PERIPH_NO_DIV,\
+ _clk_id, _parents##_idx, 0, _lock)
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 clk_csite_src;
+ u32 cclkg_burst;
+ u32 cclkg_divider;
+} tegra124_cpu_clk_sctx;
+#endif
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+
+static unsigned long osc_freq;
+static unsigned long pll_ref_freq;
+
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_e_lock);
+static DEFINE_SPINLOCK(pll_re_lock);
+static DEFINE_SPINLOCK(pll_u_lock);
+static DEFINE_SPINLOCK(emc_lock);
+static DEFINE_SPINLOCK(sor0_lock);
+
+/* possible OSC frequencies in Hz */
+static unsigned long tegra124_input_freq[] = {
+ [ 0] = 13000000,
+ [ 1] = 16800000,
+ [ 4] = 19200000,
+ [ 5] = 38400000,
+ [ 8] = 12000000,
+ [ 9] = 48000000,
+ [12] = 26000000,
+};
+
+static struct div_nmp pllxc_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 4,
+};
+
+static const struct pdiv_map pllxc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 83, 1, 1, 0 }, /* actual: 996.0 MHz */
+ { 13000000, 1000000000, 76, 1, 1, 0 }, /* actual: 988.0 MHz */
+ { 16800000, 1000000000, 59, 1, 1, 0 }, /* actual: 991.2 MHz */
+ { 19200000, 1000000000, 52, 1, 1, 0 }, /* actual: 998.4 MHz */
+ { 26000000, 1000000000, 76, 2, 1, 0 }, /* actual: 988.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 700000000,
+ .vco_max = 3000000000UL,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLX_MISC3,
+ .iddq_bit_idx = 3,
+ .max_p = 6,
+ .dyn_ramp_reg = PLLX_MISC2,
+ .stepa_shift = 16,
+ .stepb_shift = 24,
+ .pdiv_tohw = pllxc_p,
+ .div_nmp = &pllxc_nmp,
+ .freq_table = pll_x_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 624000000, 104, 1, 2, 0 },
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLC_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLC_MISC,
+ .iddq_bit_idx = PLLC_IDDQ_BIT,
+ .max_p = PLLXC_SW_MAX_P,
+ .dyn_ramp_reg = PLLC_MISC2,
+ .stepa_shift = 17,
+ .stepb_shift = 9,
+ .pdiv_tohw = pllxc_p,
+ .div_nmp = &pllxc_nmp,
+ .freq_table = pll_c_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct div_nmp pllcx_nmp = {
+ .divm_shift = 0,
+ .divm_width = 2,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static const struct pdiv_map pllc_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 6, .hw_val = 4 },
+ { .pdiv = 8, .hw_val = 5 },
+ { .pdiv = 12, .hw_val = 6 },
+ { .pdiv = 16, .hw_val = 7 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = {
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c2_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC2_BASE,
+ .misc_reg = PLLC2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .div_nmp = &pllcx_nmp,
+ .max_p = 7,
+ .ext_misc_reg[0] = 0x4f0,
+ .ext_misc_reg[1] = 0x4f4,
+ .ext_misc_reg[2] = 0x4f8,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+};
+
+static struct tegra_clk_pll_params pll_c3_params = {
+ .input_min = 12000000,
+ .input_max = 48000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC3_BASE,
+ .misc_reg = PLLC3_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = pllc_p,
+ .div_nmp = &pllcx_nmp,
+ .max_p = 7,
+ .ext_misc_reg[0] = 0x504,
+ .ext_misc_reg[1] = 0x508,
+ .ext_misc_reg[2] = 0x50c,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+};
+
+static struct div_nmp pllss_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 4,
+};
+
+static const struct pdiv_map pll12g_ssd_esd_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_c4_freq_table[] = {
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c4_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC4_BASE,
+ .misc_reg = PLLC4_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLSS_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLC4_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .pdiv_tohw = pll12g_ssd_esd_p,
+ .div_nmp = &pllss_nmp,
+ .ext_misc_reg[0] = 0x5ac,
+ .ext_misc_reg[1] = 0x5b0,
+ .ext_misc_reg[2] = 0x5b4,
+ .freq_table = pll_c4_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static const struct pdiv_map pllm_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 800000000, 66, 1, 1, 0 }, /* actual: 792.0 MHz */
+ { 13000000, 800000000, 61, 1, 1, 0 }, /* actual: 793.0 MHz */
+ { 16800000, 800000000, 47, 1, 1, 0 }, /* actual: 789.6 MHz */
+ { 19200000, 800000000, 41, 1, 1, 0 }, /* actual: 787.2 MHz */
+ { 26000000, 800000000, 61, 2, 1, 0 }, /* actual: 793.0 MHz */
+ { 0, 0, 0, 0, 0, 0},
+};
+
+static struct div_nmp pllm_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .override_divm_shift = 0,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .override_divn_shift = 8,
+ .divp_shift = 20,
+ .divp_width = 1,
+ .override_divp_shift = 27,
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 12000000,
+ .input_max = 500000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 50 MHz */
+ .vco_min = 400000000,
+ .vco_max = 1066000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .max_p = 5,
+ .pdiv_tohw = pllm_p,
+ .div_nmp = &pllm_nmp,
+ .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE,
+ .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 336000000, 100000000, 100, 21, 16, 11 },
+ { 312000000, 100000000, 200, 26, 24, 13 },
+ { 13000000, 100000000, 200, 1, 26, 13 },
+ { 12000000, 100000000, 200, 1, 24, 13 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct pdiv_map plle_p[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 10, .hw_val = 7 },
+ { .pdiv = 12, .hw_val = 8 },
+ { .pdiv = 16, .hw_val = 9 },
+ { .pdiv = 12, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 20, .hw_val = 12 },
+ { .pdiv = 24, .hw_val = 13 },
+ { .pdiv = 32, .hw_val = 14 },
+ { .pdiv = 1, .hw_val = 0 },
+};
+
+static struct div_nmp plle_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 24,
+ .divp_width = 4,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 75000000,
+ .vco_min = 1600000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .aux_reg = PLLE_AUX,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = plle_p,
+ .div_nmp = &plle_nmp,
+ .freq_table = pll_e_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 100000000,
+};
+
+static const struct clk_div_table pll_re_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 5 },
+ { .val = 5, .div = 6 },
+ { .val = 0, .div = 0 },
+};
+
+static struct div_nmp pllre_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 16,
+ .divp_width = 4,
+};
+
+static struct tegra_clk_pll_params pll_re_vco_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 300000000,
+ .vco_max = 600000000,
+ .base_reg = PLLRE_BASE,
+ .misc_reg = PLLRE_MISC,
+ .lock_mask = PLLRE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLRE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLRE_MISC,
+ .iddq_bit_idx = PLLRE_IDDQ_BIT,
+ .div_nmp = &pllre_nmp,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE |
+ TEGRA_PLL_LOCK_MISC,
+};
+
+static struct div_nmp pllp_nmp = {
+ .divm_shift = 0,
+ .divm_width = 5,
+ .divn_shift = 8,
+ .divn_width = 10,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 408000000, 408, 12, 1, 8 },
+ { 13000000, 408000000, 408, 13, 1, 8 },
+ { 16800000, 408000000, 340, 14, 1, 8 },
+ { 19200000, 408000000, 340, 16, 1, 8 },
+ { 26000000, 408000000, 408, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_p_freq_table,
+ .fixed_rate = 408000000,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 9600000, 282240000, 147, 5, 1, 4 },
+ { 9600000, 368640000, 192, 5, 1, 4 },
+ { 9600000, 240000000, 200, 8, 1, 8 },
+ { 28800000, 282240000, 245, 25, 1, 8 },
+ { 28800000, 368640000, 320, 25, 1, 8 },
+ { 28800000, 240000000, 200, 24, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 200000000,
+ .vco_max = 700000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_a_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct div_nmp plld_nmp = {
+ .divm_shift = 0,
+ .divm_width = 5,
+ .divn_shift = 8,
+ .divn_width = 11,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 864, 12, 4, 12 },
+ { 13000000, 216000000, 864, 13, 4, 12 },
+ { 16800000, 216000000, 720, 14, 4, 12 },
+ { 19200000, 216000000, 720, 16, 4, 12 },
+ { 26000000, 216000000, 864, 26, 4, 12 },
+ { 12000000, 594000000, 594, 12, 1, 12 },
+ { 13000000, 594000000, 594, 13, 1, 12 },
+ { 16800000, 594000000, 495, 14, 1, 12 },
+ { 19200000, 594000000, 495, 16, 1, 12 },
+ { 26000000, 594000000, 594, 26, 1, 12 },
+ { 12000000, 1000000000, 1000, 12, 1, 12 },
+ { 13000000, 1000000000, 1000, 13, 1, 12 },
+ { 19200000, 1000000000, 625, 12, 1, 12 },
+ { 26000000, 1000000000, 1000, 26, 1, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .div_nmp = &plld_nmp,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table tegra124_pll_d2_freq_table[] = {
+ { 12000000, 594000000, 99, 1, 2, 0 },
+ { 13000000, 594000000, 91, 1, 2, 0 }, /* actual: 591.5 MHz */
+ { 16800000, 594000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 594000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 594000000, 91, 2, 2, 0 }, /* actual: 591.5 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params tegra124_pll_d2_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLSS_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLD2_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .pdiv_tohw = pll12g_ssd_esd_p,
+ .div_nmp = &pllss_nmp,
+ .ext_misc_reg[0] = 0x570,
+ .ext_misc_reg[1] = 0x574,
+ .ext_misc_reg[2] = 0x578,
+ .max_p = 15,
+ .freq_table = tegra124_pll_d2_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_freq_table pll_dp_freq_table[] = {
+ { 12000000, 600000000, 100, 1, 2, 0 },
+ { 13000000, 600000000, 92, 1, 2, 0 }, /* actual: 598.0 MHz */
+ { 16800000, 600000000, 71, 1, 2, 0 }, /* actual: 596.4 MHz */
+ { 19200000, 600000000, 62, 1, 2, 0 }, /* actual: 595.2 MHz */
+ { 26000000, 600000000, 92, 2, 2, 0 }, /* actual: 598.0 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_dp_params = {
+ .input_min = 12000000,
+ .input_max = 1000000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000, /* s/w policy, h/w capability 38 MHz */
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLDP_BASE,
+ .misc_reg = PLLDP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLSS_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLDP_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .pdiv_tohw = pll12g_ssd_esd_p,
+ .div_nmp = &pllss_nmp,
+ .ext_misc_reg[0] = 0x598,
+ .ext_misc_reg[1] = 0x59c,
+ .ext_misc_reg[2] = 0x5a0,
+ .max_p = 5,
+ .freq_table = pll_dp_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static const struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct div_nmp pllu_nmp = {
+ .divm_shift = 0,
+ .divm_width = 5,
+ .divn_shift = 8,
+ .divn_width = 10,
+ .divp_shift = 20,
+ .divp_width = 1,
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 2, 12 },
+ { 13000000, 480000000, 960, 13, 2, 12 },
+ { 16800000, 480000000, 400, 7, 2, 5 },
+ { 19200000, 480000000, 200, 4, 2, 3 },
+ { 26000000, 480000000, 960, 26, 2, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+ .div_nmp = &pllu_nmp,
+ .freq_table = pll_u_freq_table,
+ .flags = TEGRA_PLLU | TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = {
+ [tegra_clk_ispb] = { .dt_id = TEGRA124_CLK_ISPB, .present = true },
+ [tegra_clk_rtc] = { .dt_id = TEGRA124_CLK_RTC, .present = true },
+ [tegra_clk_timer] = { .dt_id = TEGRA124_CLK_TIMER, .present = true },
+ [tegra_clk_uarta] = { .dt_id = TEGRA124_CLK_UARTA, .present = true },
+ [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA124_CLK_SDMMC2, .present = true },
+ [tegra_clk_i2s1] = { .dt_id = TEGRA124_CLK_I2S1, .present = true },
+ [tegra_clk_i2c1] = { .dt_id = TEGRA124_CLK_I2C1, .present = true },
+ [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA124_CLK_SDMMC1, .present = true },
+ [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA124_CLK_SDMMC4, .present = true },
+ [tegra_clk_pwm] = { .dt_id = TEGRA124_CLK_PWM, .present = true },
+ [tegra_clk_i2s2] = { .dt_id = TEGRA124_CLK_I2S2, .present = true },
+ [tegra_clk_usbd] = { .dt_id = TEGRA124_CLK_USBD, .present = true },
+ [tegra_clk_isp_8] = { .dt_id = TEGRA124_CLK_ISP, .present = true },
+ [tegra_clk_disp2] = { .dt_id = TEGRA124_CLK_DISP2, .present = true },
+ [tegra_clk_disp1] = { .dt_id = TEGRA124_CLK_DISP1, .present = true },
+ [tegra_clk_host1x_8] = { .dt_id = TEGRA124_CLK_HOST1X, .present = true },
+ [tegra_clk_vcp] = { .dt_id = TEGRA124_CLK_VCP, .present = true },
+ [tegra_clk_i2s0] = { .dt_id = TEGRA124_CLK_I2S0, .present = true },
+ [tegra_clk_apbdma] = { .dt_id = TEGRA124_CLK_APBDMA, .present = true },
+ [tegra_clk_kbc] = { .dt_id = TEGRA124_CLK_KBC, .present = true },
+ [tegra_clk_kfuse] = { .dt_id = TEGRA124_CLK_KFUSE, .present = true },
+ [tegra_clk_sbc1] = { .dt_id = TEGRA124_CLK_SBC1, .present = true },
+ [tegra_clk_nor] = { .dt_id = TEGRA124_CLK_NOR, .present = true },
+ [tegra_clk_sbc2] = { .dt_id = TEGRA124_CLK_SBC2, .present = true },
+ [tegra_clk_sbc3] = { .dt_id = TEGRA124_CLK_SBC3, .present = true },
+ [tegra_clk_i2c5] = { .dt_id = TEGRA124_CLK_I2C5, .present = true },
+ [tegra_clk_mipi] = { .dt_id = TEGRA124_CLK_MIPI, .present = true },
+ [tegra_clk_hdmi] = { .dt_id = TEGRA124_CLK_HDMI, .present = true },
+ [tegra_clk_csi] = { .dt_id = TEGRA124_CLK_CSI, .present = true },
+ [tegra_clk_i2c2] = { .dt_id = TEGRA124_CLK_I2C2, .present = true },
+ [tegra_clk_uartc] = { .dt_id = TEGRA124_CLK_UARTC, .present = true },
+ [tegra_clk_mipi_cal] = { .dt_id = TEGRA124_CLK_MIPI_CAL, .present = true },
+ [tegra_clk_usb2] = { .dt_id = TEGRA124_CLK_USB2, .present = true },
+ [tegra_clk_usb3] = { .dt_id = TEGRA124_CLK_USB3, .present = true },
+ [tegra_clk_vde_8] = { .dt_id = TEGRA124_CLK_VDE, .present = true },
+ [tegra_clk_bsea] = { .dt_id = TEGRA124_CLK_BSEA, .present = true },
+ [tegra_clk_bsev] = { .dt_id = TEGRA124_CLK_BSEV, .present = true },
+ [tegra_clk_uartd] = { .dt_id = TEGRA124_CLK_UARTD, .present = true },
+ [tegra_clk_i2c3] = { .dt_id = TEGRA124_CLK_I2C3, .present = true },
+ [tegra_clk_sbc4] = { .dt_id = TEGRA124_CLK_SBC4, .present = true },
+ [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA124_CLK_SDMMC3, .present = true },
+ [tegra_clk_pcie] = { .dt_id = TEGRA124_CLK_PCIE, .present = true },
+ [tegra_clk_owr] = { .dt_id = TEGRA124_CLK_OWR, .present = true },
+ [tegra_clk_afi] = { .dt_id = TEGRA124_CLK_AFI, .present = true },
+ [tegra_clk_csite] = { .dt_id = TEGRA124_CLK_CSITE, .present = true },
+ [tegra_clk_la] = { .dt_id = TEGRA124_CLK_LA, .present = true },
+ [tegra_clk_trace] = { .dt_id = TEGRA124_CLK_TRACE, .present = true },
+ [tegra_clk_soc_therm] = { .dt_id = TEGRA124_CLK_SOC_THERM, .present = true },
+ [tegra_clk_dtv] = { .dt_id = TEGRA124_CLK_DTV, .present = true },
+ [tegra_clk_i2cslow] = { .dt_id = TEGRA124_CLK_I2CSLOW, .present = true },
+ [tegra_clk_tsec] = { .dt_id = TEGRA124_CLK_TSEC, .present = true },
+ [tegra_clk_xusb_host] = { .dt_id = TEGRA124_CLK_XUSB_HOST, .present = true },
+ [tegra_clk_msenc] = { .dt_id = TEGRA124_CLK_MSENC, .present = true },
+ [tegra_clk_csus] = { .dt_id = TEGRA124_CLK_CSUS, .present = true },
+ [tegra_clk_mselect] = { .dt_id = TEGRA124_CLK_MSELECT, .present = true },
+ [tegra_clk_tsensor] = { .dt_id = TEGRA124_CLK_TSENSOR, .present = true },
+ [tegra_clk_i2s3] = { .dt_id = TEGRA124_CLK_I2S3, .present = true },
+ [tegra_clk_i2s4] = { .dt_id = TEGRA124_CLK_I2S4, .present = true },
+ [tegra_clk_i2c4] = { .dt_id = TEGRA124_CLK_I2C4, .present = true },
+ [tegra_clk_sbc5] = { .dt_id = TEGRA124_CLK_SBC5, .present = true },
+ [tegra_clk_sbc6] = { .dt_id = TEGRA124_CLK_SBC6, .present = true },
+ [tegra_clk_d_audio] = { .dt_id = TEGRA124_CLK_D_AUDIO, .present = true },
+ [tegra_clk_apbif] = { .dt_id = TEGRA124_CLK_APBIF, .present = true },
+ [tegra_clk_dam0] = { .dt_id = TEGRA124_CLK_DAM0, .present = true },
+ [tegra_clk_dam1] = { .dt_id = TEGRA124_CLK_DAM1, .present = true },
+ [tegra_clk_dam2] = { .dt_id = TEGRA124_CLK_DAM2, .present = true },
+ [tegra_clk_hda2codec_2x] = { .dt_id = TEGRA124_CLK_HDA2CODEC_2X, .present = true },
+ [tegra_clk_audio0_2x] = { .dt_id = TEGRA124_CLK_AUDIO0_2X, .present = true },
+ [tegra_clk_audio1_2x] = { .dt_id = TEGRA124_CLK_AUDIO1_2X, .present = true },
+ [tegra_clk_audio2_2x] = { .dt_id = TEGRA124_CLK_AUDIO2_2X, .present = true },
+ [tegra_clk_audio3_2x] = { .dt_id = TEGRA124_CLK_AUDIO3_2X, .present = true },
+ [tegra_clk_audio4_2x] = { .dt_id = TEGRA124_CLK_AUDIO4_2X, .present = true },
+ [tegra_clk_spdif_2x] = { .dt_id = TEGRA124_CLK_SPDIF_2X, .present = true },
+ [tegra_clk_actmon] = { .dt_id = TEGRA124_CLK_ACTMON, .present = true },
+ [tegra_clk_extern1] = { .dt_id = TEGRA124_CLK_EXTERN1, .present = true },
+ [tegra_clk_extern2] = { .dt_id = TEGRA124_CLK_EXTERN2, .present = true },
+ [tegra_clk_extern3] = { .dt_id = TEGRA124_CLK_EXTERN3, .present = true },
+ [tegra_clk_sata_oob] = { .dt_id = TEGRA124_CLK_SATA_OOB, .present = true },
+ [tegra_clk_sata] = { .dt_id = TEGRA124_CLK_SATA, .present = true },
+ [tegra_clk_hda] = { .dt_id = TEGRA124_CLK_HDA, .present = true },
+ [tegra_clk_se] = { .dt_id = TEGRA124_CLK_SE, .present = true },
+ [tegra_clk_hda2hdmi] = { .dt_id = TEGRA124_CLK_HDA2HDMI, .present = true },
+ [tegra_clk_sata_cold] = { .dt_id = TEGRA124_CLK_SATA_COLD, .present = true },
+ [tegra_clk_cilab] = { .dt_id = TEGRA124_CLK_CILAB, .present = true },
+ [tegra_clk_cilcd] = { .dt_id = TEGRA124_CLK_CILCD, .present = true },
+ [tegra_clk_cile] = { .dt_id = TEGRA124_CLK_CILE, .present = true },
+ [tegra_clk_dsialp] = { .dt_id = TEGRA124_CLK_DSIALP, .present = true },
+ [tegra_clk_dsiblp] = { .dt_id = TEGRA124_CLK_DSIBLP, .present = true },
+ [tegra_clk_entropy] = { .dt_id = TEGRA124_CLK_ENTROPY, .present = true },
+ [tegra_clk_dds] = { .dt_id = TEGRA124_CLK_DDS, .present = true },
+ [tegra_clk_dp2] = { .dt_id = TEGRA124_CLK_DP2, .present = true },
+ [tegra_clk_amx] = { .dt_id = TEGRA124_CLK_AMX, .present = true },
+ [tegra_clk_adx] = { .dt_id = TEGRA124_CLK_ADX, .present = true },
+ [tegra_clk_xusb_ss] = { .dt_id = TEGRA124_CLK_XUSB_SS, .present = true },
+ [tegra_clk_i2c6] = { .dt_id = TEGRA124_CLK_I2C6, .present = true },
+ [tegra_clk_vim2_clk] = { .dt_id = TEGRA124_CLK_VIM2_CLK, .present = true },
+ [tegra_clk_hdmi_audio] = { .dt_id = TEGRA124_CLK_HDMI_AUDIO, .present = true },
+ [tegra_clk_clk72Mhz] = { .dt_id = TEGRA124_CLK_CLK72MHZ, .present = true },
+ [tegra_clk_vic03] = { .dt_id = TEGRA124_CLK_VIC03, .present = true },
+ [tegra_clk_adx1] = { .dt_id = TEGRA124_CLK_ADX1, .present = true },
+ [tegra_clk_dpaux] = { .dt_id = TEGRA124_CLK_DPAUX, .present = true },
+ [tegra_clk_sor0] = { .dt_id = TEGRA124_CLK_SOR0, .present = true },
+ [tegra_clk_sor0_out] = { .dt_id = TEGRA124_CLK_SOR0_OUT, .present = true },
+ [tegra_clk_gpu] = { .dt_id = TEGRA124_CLK_GPU, .present = true },
+ [tegra_clk_amx1] = { .dt_id = TEGRA124_CLK_AMX1, .present = true },
+ [tegra_clk_uartb] = { .dt_id = TEGRA124_CLK_UARTB, .present = true },
+ [tegra_clk_vfir] = { .dt_id = TEGRA124_CLK_VFIR, .present = true },
+ [tegra_clk_spdif_in] = { .dt_id = TEGRA124_CLK_SPDIF_IN, .present = true },
+ [tegra_clk_spdif_out] = { .dt_id = TEGRA124_CLK_SPDIF_OUT, .present = true },
+ [tegra_clk_vi_9] = { .dt_id = TEGRA124_CLK_VI, .present = true },
+ [tegra_clk_vi_sensor_8] = { .dt_id = TEGRA124_CLK_VI_SENSOR, .present = true },
+ [tegra_clk_fuse] = { .dt_id = TEGRA124_CLK_FUSE, .present = true },
+ [tegra_clk_fuse_burn] = { .dt_id = TEGRA124_CLK_FUSE_BURN, .present = true },
+ [tegra_clk_clk_32k] = { .dt_id = TEGRA124_CLK_CLK_32K, .present = true },
+ [tegra_clk_clk_m] = { .dt_id = TEGRA124_CLK_CLK_M, .present = true },
+ [tegra_clk_osc] = { .dt_id = TEGRA124_CLK_OSC, .present = true },
+ [tegra_clk_osc_div2] = { .dt_id = TEGRA124_CLK_OSC_DIV2, .present = true },
+ [tegra_clk_osc_div4] = { .dt_id = TEGRA124_CLK_OSC_DIV4, .present = true },
+ [tegra_clk_pll_ref] = { .dt_id = TEGRA124_CLK_PLL_REF, .present = true },
+ [tegra_clk_pll_c] = { .dt_id = TEGRA124_CLK_PLL_C, .present = true },
+ [tegra_clk_pll_c_out1] = { .dt_id = TEGRA124_CLK_PLL_C_OUT1, .present = true },
+ [tegra_clk_pll_c2] = { .dt_id = TEGRA124_CLK_PLL_C2, .present = true },
+ [tegra_clk_pll_c3] = { .dt_id = TEGRA124_CLK_PLL_C3, .present = true },
+ [tegra_clk_pll_m] = { .dt_id = TEGRA124_CLK_PLL_M, .present = true },
+ [tegra_clk_pll_m_out1] = { .dt_id = TEGRA124_CLK_PLL_M_OUT1, .present = true },
+ [tegra_clk_pll_p] = { .dt_id = TEGRA124_CLK_PLL_P, .present = true },
+ [tegra_clk_pll_p_out1] = { .dt_id = TEGRA124_CLK_PLL_P_OUT1, .present = true },
+ [tegra_clk_pll_p_out2] = { .dt_id = TEGRA124_CLK_PLL_P_OUT2, .present = true },
+ [tegra_clk_pll_p_out3] = { .dt_id = TEGRA124_CLK_PLL_P_OUT3, .present = true },
+ [tegra_clk_pll_p_out4] = { .dt_id = TEGRA124_CLK_PLL_P_OUT4, .present = true },
+ [tegra_clk_pll_a] = { .dt_id = TEGRA124_CLK_PLL_A, .present = true },
+ [tegra_clk_pll_a_out0] = { .dt_id = TEGRA124_CLK_PLL_A_OUT0, .present = true },
+ [tegra_clk_pll_d] = { .dt_id = TEGRA124_CLK_PLL_D, .present = true },
+ [tegra_clk_pll_d_out0] = { .dt_id = TEGRA124_CLK_PLL_D_OUT0, .present = true },
+ [tegra_clk_pll_d2] = { .dt_id = TEGRA124_CLK_PLL_D2, .present = true },
+ [tegra_clk_pll_d2_out0] = { .dt_id = TEGRA124_CLK_PLL_D2_OUT0, .present = true },
+ [tegra_clk_pll_u] = { .dt_id = TEGRA124_CLK_PLL_U, .present = true },
+ [tegra_clk_pll_u_480m] = { .dt_id = TEGRA124_CLK_PLL_U_480M, .present = true },
+ [tegra_clk_pll_u_60m] = { .dt_id = TEGRA124_CLK_PLL_U_60M, .present = true },
+ [tegra_clk_pll_u_48m] = { .dt_id = TEGRA124_CLK_PLL_U_48M, .present = true },
+ [tegra_clk_pll_u_12m] = { .dt_id = TEGRA124_CLK_PLL_U_12M, .present = true },
+ [tegra_clk_pll_x] = { .dt_id = TEGRA124_CLK_PLL_X, .present = true },
+ [tegra_clk_pll_x_out0] = { .dt_id = TEGRA124_CLK_PLL_X_OUT0, .present = true },
+ [tegra_clk_pll_re_vco] = { .dt_id = TEGRA124_CLK_PLL_RE_VCO, .present = true },
+ [tegra_clk_pll_re_out] = { .dt_id = TEGRA124_CLK_PLL_RE_OUT, .present = true },
+ [tegra_clk_spdif_in_sync] = { .dt_id = TEGRA124_CLK_SPDIF_IN_SYNC, .present = true },
+ [tegra_clk_i2s0_sync] = { .dt_id = TEGRA124_CLK_I2S0_SYNC, .present = true },
+ [tegra_clk_i2s1_sync] = { .dt_id = TEGRA124_CLK_I2S1_SYNC, .present = true },
+ [tegra_clk_i2s2_sync] = { .dt_id = TEGRA124_CLK_I2S2_SYNC, .present = true },
+ [tegra_clk_i2s3_sync] = { .dt_id = TEGRA124_CLK_I2S3_SYNC, .present = true },
+ [tegra_clk_i2s4_sync] = { .dt_id = TEGRA124_CLK_I2S4_SYNC, .present = true },
+ [tegra_clk_vimclk_sync] = { .dt_id = TEGRA124_CLK_VIMCLK_SYNC, .present = true },
+ [tegra_clk_audio0] = { .dt_id = TEGRA124_CLK_AUDIO0, .present = true },
+ [tegra_clk_audio1] = { .dt_id = TEGRA124_CLK_AUDIO1, .present = true },
+ [tegra_clk_audio2] = { .dt_id = TEGRA124_CLK_AUDIO2, .present = true },
+ [tegra_clk_audio3] = { .dt_id = TEGRA124_CLK_AUDIO3, .present = true },
+ [tegra_clk_audio4] = { .dt_id = TEGRA124_CLK_AUDIO4, .present = true },
+ [tegra_clk_spdif] = { .dt_id = TEGRA124_CLK_SPDIF, .present = true },
+ [tegra_clk_xusb_host_src] = { .dt_id = TEGRA124_CLK_XUSB_HOST_SRC, .present = true },
+ [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA124_CLK_XUSB_FALCON_SRC, .present = true },
+ [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA124_CLK_XUSB_FS_SRC, .present = true },
+ [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA124_CLK_XUSB_SS_SRC, .present = true },
+ [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA124_CLK_XUSB_SS_DIV2, .present = true },
+ [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA124_CLK_XUSB_DEV_SRC, .present = true },
+ [tegra_clk_xusb_dev] = { .dt_id = TEGRA124_CLK_XUSB_DEV, .present = true },
+ [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA124_CLK_XUSB_HS_SRC, .present = true },
+ [tegra_clk_sclk] = { .dt_id = TEGRA124_CLK_SCLK, .present = true },
+ [tegra_clk_hclk] = { .dt_id = TEGRA124_CLK_HCLK, .present = true },
+ [tegra_clk_pclk] = { .dt_id = TEGRA124_CLK_PCLK, .present = true },
+ [tegra_clk_cclk_g] = { .dt_id = TEGRA124_CLK_CCLK_G, .present = true },
+ [tegra_clk_cclk_lp] = { .dt_id = TEGRA124_CLK_CCLK_LP, .present = true },
+ [tegra_clk_dfll_ref] = { .dt_id = TEGRA124_CLK_DFLL_REF, .present = true },
+ [tegra_clk_dfll_soc] = { .dt_id = TEGRA124_CLK_DFLL_SOC, .present = true },
+ [tegra_clk_vi_sensor2] = { .dt_id = TEGRA124_CLK_VI_SENSOR2, .present = true },
+ [tegra_clk_pll_p_out5] = { .dt_id = TEGRA124_CLK_PLL_P_OUT5, .present = true },
+ [tegra_clk_pll_c4] = { .dt_id = TEGRA124_CLK_PLL_C4, .present = true },
+ [tegra_clk_pll_dp] = { .dt_id = TEGRA124_CLK_PLL_DP, .present = true },
+ [tegra_clk_audio0_mux] = { .dt_id = TEGRA124_CLK_AUDIO0_MUX, .present = true },
+ [tegra_clk_audio1_mux] = { .dt_id = TEGRA124_CLK_AUDIO1_MUX, .present = true },
+ [tegra_clk_audio2_mux] = { .dt_id = TEGRA124_CLK_AUDIO2_MUX, .present = true },
+ [tegra_clk_audio3_mux] = { .dt_id = TEGRA124_CLK_AUDIO3_MUX, .present = true },
+ [tegra_clk_audio4_mux] = { .dt_id = TEGRA124_CLK_AUDIO4_MUX, .present = true },
+ [tegra_clk_spdif_mux] = { .dt_id = TEGRA124_CLK_SPDIF_MUX, .present = true },
+ [tegra_clk_cec] = { .dt_id = TEGRA124_CLK_CEC, .present = true },
+};
+
+static struct tegra_devclk devclks[] __initdata = {
+ { .con_id = "clk_m", .dt_id = TEGRA124_CLK_CLK_M },
+ { .con_id = "pll_ref", .dt_id = TEGRA124_CLK_PLL_REF },
+ { .con_id = "clk_32k", .dt_id = TEGRA124_CLK_CLK_32K },
+ { .con_id = "osc", .dt_id = TEGRA124_CLK_OSC },
+ { .con_id = "osc_div2", .dt_id = TEGRA124_CLK_OSC_DIV2 },
+ { .con_id = "osc_div4", .dt_id = TEGRA124_CLK_OSC_DIV4 },
+ { .con_id = "pll_c", .dt_id = TEGRA124_CLK_PLL_C },
+ { .con_id = "pll_c_out1", .dt_id = TEGRA124_CLK_PLL_C_OUT1 },
+ { .con_id = "pll_c2", .dt_id = TEGRA124_CLK_PLL_C2 },
+ { .con_id = "pll_c3", .dt_id = TEGRA124_CLK_PLL_C3 },
+ { .con_id = "pll_p", .dt_id = TEGRA124_CLK_PLL_P },
+ { .con_id = "pll_p_out1", .dt_id = TEGRA124_CLK_PLL_P_OUT1 },
+ { .con_id = "pll_p_out2", .dt_id = TEGRA124_CLK_PLL_P_OUT2 },
+ { .con_id = "pll_p_out3", .dt_id = TEGRA124_CLK_PLL_P_OUT3 },
+ { .con_id = "pll_p_out4", .dt_id = TEGRA124_CLK_PLL_P_OUT4 },
+ { .con_id = "pll_m", .dt_id = TEGRA124_CLK_PLL_M },
+ { .con_id = "pll_m_out1", .dt_id = TEGRA124_CLK_PLL_M_OUT1 },
+ { .con_id = "pll_x", .dt_id = TEGRA124_CLK_PLL_X },
+ { .con_id = "pll_x_out0", .dt_id = TEGRA124_CLK_PLL_X_OUT0 },
+ { .con_id = "pll_u", .dt_id = TEGRA124_CLK_PLL_U },
+ { .con_id = "pll_u_480M", .dt_id = TEGRA124_CLK_PLL_U_480M },
+ { .con_id = "pll_u_60M", .dt_id = TEGRA124_CLK_PLL_U_60M },
+ { .con_id = "pll_u_48M", .dt_id = TEGRA124_CLK_PLL_U_48M },
+ { .con_id = "pll_u_12M", .dt_id = TEGRA124_CLK_PLL_U_12M },
+ { .con_id = "pll_d", .dt_id = TEGRA124_CLK_PLL_D },
+ { .con_id = "pll_d_out0", .dt_id = TEGRA124_CLK_PLL_D_OUT0 },
+ { .con_id = "pll_d2", .dt_id = TEGRA124_CLK_PLL_D2 },
+ { .con_id = "pll_d2_out0", .dt_id = TEGRA124_CLK_PLL_D2_OUT0 },
+ { .con_id = "pll_a", .dt_id = TEGRA124_CLK_PLL_A },
+ { .con_id = "pll_a_out0", .dt_id = TEGRA124_CLK_PLL_A_OUT0 },
+ { .con_id = "pll_re_vco", .dt_id = TEGRA124_CLK_PLL_RE_VCO },
+ { .con_id = "pll_re_out", .dt_id = TEGRA124_CLK_PLL_RE_OUT },
+ { .con_id = "spdif_in_sync", .dt_id = TEGRA124_CLK_SPDIF_IN_SYNC },
+ { .con_id = "i2s0_sync", .dt_id = TEGRA124_CLK_I2S0_SYNC },
+ { .con_id = "i2s1_sync", .dt_id = TEGRA124_CLK_I2S1_SYNC },
+ { .con_id = "i2s2_sync", .dt_id = TEGRA124_CLK_I2S2_SYNC },
+ { .con_id = "i2s3_sync", .dt_id = TEGRA124_CLK_I2S3_SYNC },
+ { .con_id = "i2s4_sync", .dt_id = TEGRA124_CLK_I2S4_SYNC },
+ { .con_id = "vimclk_sync", .dt_id = TEGRA124_CLK_VIMCLK_SYNC },
+ { .con_id = "audio0", .dt_id = TEGRA124_CLK_AUDIO0 },
+ { .con_id = "audio1", .dt_id = TEGRA124_CLK_AUDIO1 },
+ { .con_id = "audio2", .dt_id = TEGRA124_CLK_AUDIO2 },
+ { .con_id = "audio3", .dt_id = TEGRA124_CLK_AUDIO3 },
+ { .con_id = "audio4", .dt_id = TEGRA124_CLK_AUDIO4 },
+ { .con_id = "spdif", .dt_id = TEGRA124_CLK_SPDIF },
+ { .con_id = "audio0_2x", .dt_id = TEGRA124_CLK_AUDIO0_2X },
+ { .con_id = "audio1_2x", .dt_id = TEGRA124_CLK_AUDIO1_2X },
+ { .con_id = "audio2_2x", .dt_id = TEGRA124_CLK_AUDIO2_2X },
+ { .con_id = "audio3_2x", .dt_id = TEGRA124_CLK_AUDIO3_2X },
+ { .con_id = "audio4_2x", .dt_id = TEGRA124_CLK_AUDIO4_2X },
+ { .con_id = "spdif_2x", .dt_id = TEGRA124_CLK_SPDIF_2X },
+ { .con_id = "extern1", .dt_id = TEGRA124_CLK_EXTERN1 },
+ { .con_id = "extern2", .dt_id = TEGRA124_CLK_EXTERN2 },
+ { .con_id = "extern3", .dt_id = TEGRA124_CLK_EXTERN3 },
+ { .con_id = "cclk_g", .dt_id = TEGRA124_CLK_CCLK_G },
+ { .con_id = "cclk_lp", .dt_id = TEGRA124_CLK_CCLK_LP },
+ { .con_id = "sclk", .dt_id = TEGRA124_CLK_SCLK },
+ { .con_id = "hclk", .dt_id = TEGRA124_CLK_HCLK },
+ { .con_id = "pclk", .dt_id = TEGRA124_CLK_PCLK },
+ { .con_id = "fuse", .dt_id = TEGRA124_CLK_FUSE },
+ { .dev_id = "rtc-tegra", .dt_id = TEGRA124_CLK_RTC },
+ { .dev_id = "timer", .dt_id = TEGRA124_CLK_TIMER },
+ { .con_id = "hda", .dt_id = TEGRA124_CLK_HDA },
+ { .con_id = "hda2codec_2x", .dt_id = TEGRA124_CLK_HDA2CODEC_2X },
+ { .con_id = "hda2hdmi", .dt_id = TEGRA124_CLK_HDA2HDMI },
+};
+
+static const char * const sor0_parents[] = {
+ "pll_p_out0", "pll_m_out0", "pll_d_out0", "pll_a_out0", "pll_c_out0",
+ "pll_d2_out0", "clk_m",
+};
+
+static const char * const sor0_out_parents[] = {
+ "clk_m", "sor0_pad_clkout",
+};
+
+static struct tegra_periph_init_data tegra124_periph[] = {
+ TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents,
+ CLK_SOURCE_SOR0, 29, 0x7, 0, 0, 0, 0,
+ 0, 182, 0, tegra_clk_sor0, NULL, 0,
+ &sor0_lock),
+ TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents,
+ CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0,
+ 0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out,
+ NULL, 0, &sor0_lock),
+};
+
+static struct clk **clks;
+
+static __init void tegra124_periph_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ /* xusb_ss_div2 */
+ clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
+ 1, 2);
+ clks[TEGRA124_CLK_XUSB_SS_DIV2] = clk;
+
+ clk = tegra_clk_register_periph_fixed("dpaux", "pll_p", 0, clk_base,
+ 1, 17, 181);
+ clks[TEGRA124_CLK_DPAUX] = clk;
+
+ clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0,
+ clk_base + PLLD_MISC, 30, 0, &pll_d_lock);
+ clks[TEGRA124_CLK_PLL_D_DSI_OUT] = clk;
+
+ clk = tegra_clk_register_periph_gate("dsia", "pll_d_dsi_out", 0,
+ clk_base, 0, 48,
+ periph_clk_enb_refcnt);
+ clks[TEGRA124_CLK_DSIA] = clk;
+
+ clk = tegra_clk_register_periph_gate("dsib", "pll_d_dsi_out", 0,
+ clk_base, 0, 82,
+ periph_clk_enb_refcnt);
+ clks[TEGRA124_CLK_DSIB] = clk;
+
+ clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
+ &emc_lock);
+ clks[TEGRA124_CLK_MC] = clk;
+
+ /* cml0 */
+ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
+ 0, 0, &pll_e_lock);
+ clk_register_clkdev(clk, "cml0", NULL);
+ clks[TEGRA124_CLK_CML0] = clk;
+
+ /* cml1 */
+ clk = clk_register_gate(NULL, "cml1", "pll_e", 0, clk_base + PLLE_AUX,
+ 1, 0, &pll_e_lock);
+ clk_register_clkdev(clk, "cml1", NULL);
+ clks[TEGRA124_CLK_CML1] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra124_periph); i++) {
+ struct tegra_periph_init_data *init = &tegra124_periph[i];
+ struct clk **clkp;
+
+ clkp = tegra_lookup_dt_id(init->clk_id, tegra124_clks);
+ if (!clkp) {
+ pr_warn("clock %u not found\n", init->clk_id);
+ continue;
+ }
+
+ clk = tegra_clk_register_periph_data(clk_base, init);
+ *clkp = clk;
+ }
+
+ tegra_periph_clk_init(clk_base, pmc_base, tegra124_clks, &pll_p_params);
+}
+
+static void __init tegra124_pll_init(void __iomem *clk_base,
+ void __iomem *pmc)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pllxc("pll_c", "pll_ref", clk_base,
+ pmc, 0, &pll_c_params, NULL);
+ clk_register_clkdev(clk, "pll_c", NULL);
+ clks[TEGRA124_CLK_PLL_C] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_c_out1", NULL);
+ clks[TEGRA124_CLK_PLL_C_OUT1] = clk;
+
+ /* PLLC_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_c_ud", NULL);
+ clks[TEGRA124_CLK_PLL_C_UD] = clk;
+
+ /* PLLC2 */
+ clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0,
+ &pll_c2_params, NULL);
+ clk_register_clkdev(clk, "pll_c2", NULL);
+ clks[TEGRA124_CLK_PLL_C2] = clk;
+
+ /* PLLC3 */
+ clk = tegra_clk_register_pllc("pll_c3", "pll_ref", clk_base, pmc, 0,
+ &pll_c3_params, NULL);
+ clk_register_clkdev(clk, "pll_c3", NULL);
+ clks[TEGRA124_CLK_PLL_C3] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pllm("pll_m", "pll_ref", clk_base, pmc,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clk_register_clkdev(clk, "pll_m", NULL);
+ clks[TEGRA124_CLK_PLL_M] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_m_out1", NULL);
+ clks[TEGRA124_CLK_PLL_M_OUT1] = clk;
+
+ /* PLLM_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_m_ud", NULL);
+ clks[TEGRA124_CLK_PLL_M_UD] = clk;
+
+ /* PLLU */
+ clk = tegra_clk_register_pllu_tegra114("pll_u", "pll_ref", clk_base, 0,
+ &pll_u_params, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u", NULL);
+ clks[TEGRA124_CLK_PLL_U] = clk;
+
+ /* PLLU_480M */
+ clk = clk_register_gate(NULL, "pll_u_480M", "pll_u",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 22, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_480M", NULL);
+ clks[TEGRA124_CLK_PLL_U_480M] = clk;
+
+ /* PLLU_60M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_60M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 8);
+ clk_register_clkdev(clk, "pll_u_60M", NULL);
+ clks[TEGRA124_CLK_PLL_U_60M] = clk;
+
+ /* PLLU_48M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_48M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 10);
+ clk_register_clkdev(clk, "pll_u_48M", NULL);
+ clks[TEGRA124_CLK_PLL_U_48M] = clk;
+
+ /* PLLU_12M */
+ clk = clk_register_fixed_factor(NULL, "pll_u_12M", "pll_u",
+ CLK_SET_RATE_PARENT, 1, 40);
+ clk_register_clkdev(clk, "pll_u_12M", NULL);
+ clks[TEGRA124_CLK_PLL_U_12M] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0,
+ &pll_d_params, &pll_d_lock);
+ clk_register_clkdev(clk, "pll_d", NULL);
+ clks[TEGRA124_CLK_PLL_D] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d_out0", NULL);
+ clks[TEGRA124_CLK_PLL_D_OUT0] = clk;
+
+ /* PLLRE */
+ clk = tegra_clk_register_pllre("pll_re_vco", "pll_ref", clk_base, pmc,
+ 0, &pll_re_vco_params, &pll_re_lock, pll_ref_freq);
+ clk_register_clkdev(clk, "pll_re_vco", NULL);
+ clks[TEGRA124_CLK_PLL_RE_VCO] = clk;
+
+ clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0,
+ clk_base + PLLRE_BASE, 16, 4, 0,
+ pll_re_div_table, &pll_re_lock);
+ clk_register_clkdev(clk, "pll_re_out", NULL);
+ clks[TEGRA124_CLK_PLL_RE_OUT] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle_tegra114("pll_e", "pll_ref",
+ clk_base, 0, &pll_e_params, NULL);
+ clk_register_clkdev(clk, "pll_e", NULL);
+ clks[TEGRA124_CLK_PLL_E] = clk;
+
+ /* PLLC4 */
+ clk = tegra_clk_register_pllss("pll_c4", "pll_ref", clk_base, 0,
+ &pll_c4_params, NULL);
+ clk_register_clkdev(clk, "pll_c4", NULL);
+ clks[TEGRA124_CLK_PLL_C4] = clk;
+
+ /* PLLDP */
+ clk = tegra_clk_register_pllss("pll_dp", "pll_ref", clk_base, 0,
+ &pll_dp_params, NULL);
+ clk_register_clkdev(clk, "pll_dp", NULL);
+ clks[TEGRA124_CLK_PLL_DP] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pllss("pll_d2", "pll_ref", clk_base, 0,
+ &tegra124_pll_d2_params, NULL);
+ clk_register_clkdev(clk, "pll_d2", NULL);
+ clks[TEGRA124_CLK_PLL_D2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_d2_out0", NULL);
+ clks[TEGRA124_CLK_PLL_D2_OUT0] = clk;
+
+}
+
+/* Tegra124 CPU clock and reset control functions */
+static void tegra124_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base + CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+}
+
+static void tegra124_disable_cpu_clock(u32 cpu)
+{
+ /* flow controller would take care in the power sequence. */
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void tegra124_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra124_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_SOURCE_CSITE);
+ writel(3 << 30, clk_base + CLK_SOURCE_CSITE);
+
+ tegra124_cpu_clk_sctx.cclkg_burst =
+ readl(clk_base + CCLKG_BURST_POLICY);
+ tegra124_cpu_clk_sctx.cclkg_divider =
+ readl(clk_base + CCLKG_BURST_POLICY + 4);
+}
+
+static void tegra124_cpu_clock_resume(void)
+{
+ writel(tegra124_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_SOURCE_CSITE);
+
+ writel(tegra124_cpu_clk_sctx.cclkg_burst,
+ clk_base + CCLKG_BURST_POLICY);
+ writel(tegra124_cpu_clk_sctx.cclkg_divider,
+ clk_base + CCLKG_BURST_POLICY + 4);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra124_cpu_car_ops = {
+ .wait_for_reset = tegra124_wait_cpu_in_reset,
+ .disable_clock = tegra124_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra124_cpu_clock_suspend,
+ .resume = tegra124_cpu_clock_resume,
+#endif
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra124-pmc" },
+ { },
+};
+
+static struct tegra_clk_init_table common_init_table[] __initdata = {
+ { TEGRA124_CLK_UARTA, TEGRA124_CLK_PLL_P, 408000000, 0 },
+ { TEGRA124_CLK_UARTB, TEGRA124_CLK_PLL_P, 408000000, 0 },
+ { TEGRA124_CLK_UARTC, TEGRA124_CLK_PLL_P, 408000000, 0 },
+ { TEGRA124_CLK_UARTD, TEGRA124_CLK_PLL_P, 408000000, 0 },
+ { TEGRA124_CLK_PLL_A, TEGRA124_CLK_CLK_MAX, 282240000, 0 },
+ { TEGRA124_CLK_PLL_A_OUT0, TEGRA124_CLK_CLK_MAX, 11289600, 0 },
+ { TEGRA124_CLK_I2S0, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA124_CLK_I2S1, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA124_CLK_I2S2, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA124_CLK_I2S3, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA124_CLK_I2S4, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA124_CLK_VDE, TEGRA124_CLK_PLL_C3, 600000000, 0 },
+ { TEGRA124_CLK_HOST1X, TEGRA124_CLK_PLL_P, 136000000, 1 },
+ { TEGRA124_CLK_DSIALP, TEGRA124_CLK_PLL_P, 68000000, 0 },
+ { TEGRA124_CLK_DSIBLP, TEGRA124_CLK_PLL_P, 68000000, 0 },
+ { TEGRA124_CLK_SCLK, TEGRA124_CLK_PLL_P_OUT2, 102000000, 0 },
+ { TEGRA124_CLK_DFLL_SOC, TEGRA124_CLK_PLL_P, 51000000, 1 },
+ { TEGRA124_CLK_DFLL_REF, TEGRA124_CLK_PLL_P, 51000000, 1 },
+ { TEGRA124_CLK_PLL_C, TEGRA124_CLK_CLK_MAX, 768000000, 0 },
+ { TEGRA124_CLK_PLL_C_OUT1, TEGRA124_CLK_CLK_MAX, 100000000, 0 },
+ { TEGRA124_CLK_SBC4, TEGRA124_CLK_PLL_P, 12000000, 1 },
+ { TEGRA124_CLK_TSEC, TEGRA124_CLK_PLL_C3, 0, 0 },
+ { TEGRA124_CLK_MSENC, TEGRA124_CLK_PLL_C3, 0, 0 },
+ { TEGRA124_CLK_PLL_RE_VCO, TEGRA124_CLK_CLK_MAX, 672000000, 0 },
+ { TEGRA124_CLK_XUSB_SS_SRC, TEGRA124_CLK_PLL_U_480M, 120000000, 0 },
+ { TEGRA124_CLK_XUSB_FS_SRC, TEGRA124_CLK_PLL_U_48M, 48000000, 0 },
+ { TEGRA124_CLK_XUSB_HS_SRC, TEGRA124_CLK_PLL_U_60M, 60000000, 0 },
+ { TEGRA124_CLK_XUSB_FALCON_SRC, TEGRA124_CLK_PLL_RE_OUT, 224000000, 0 },
+ { TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0 },
+ { TEGRA124_CLK_SATA, TEGRA124_CLK_PLL_P, 104000000, 0 },
+ { TEGRA124_CLK_SATA_OOB, TEGRA124_CLK_PLL_P, 204000000, 0 },
+ { TEGRA124_CLK_MSELECT, TEGRA124_CLK_CLK_MAX, 0, 1 },
+ { TEGRA124_CLK_CSITE, TEGRA124_CLK_CLK_MAX, 0, 1 },
+ { TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0 },
+ { TEGRA124_CLK_VIC03, TEGRA124_CLK_PLL_C3, 0, 0 },
+ { TEGRA124_CLK_SPDIF_IN_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_I2S0_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_I2S1_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_I2S2_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_I2S3_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_I2S4_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_VIMCLK_SYNC, TEGRA124_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA124_CLK_PWM, TEGRA124_CLK_PLL_P, 408000000, 0 },
+ /* must be the last entry */
+ { TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0 },
+};
+
+static struct tegra_clk_init_table tegra124_init_table[] __initdata = {
+ { TEGRA124_CLK_SOC_THERM, TEGRA124_CLK_PLL_P, 51000000, 0 },
+ { TEGRA124_CLK_CCLK_G, TEGRA124_CLK_CLK_MAX, 0, 1 },
+ { TEGRA124_CLK_HDA, TEGRA124_CLK_PLL_P, 102000000, 0 },
+ { TEGRA124_CLK_HDA2CODEC_2X, TEGRA124_CLK_PLL_P, 48000000, 0 },
+ /* must be the last entry */
+ { TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0 },
+};
+
+/* Tegra132 requires the SOC_THERM clock to remain active */
+static struct tegra_clk_init_table tegra132_init_table[] __initdata = {
+ { TEGRA124_CLK_SOC_THERM, TEGRA124_CLK_PLL_P, 51000000, 1 },
+ /* must be the last entry */
+ { TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0 },
+};
+
+static struct tegra_audio_clk_info tegra124_audio_plls[] = {
+ { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
+};
+
+/**
+ * tegra124_clock_apply_init_table - initialize clocks on Tegra124 SoCs
+ *
+ * Program an initial clock rate and enable or disable clocks needed
+ * by the rest of the kernel, for Tegra124 SoCs. It is intended to be
+ * called by assigning a pointer to it to tegra_clk_apply_init_table -
+ * this will be called as an arch_initcall. No return value.
+ */
+static void __init tegra124_clock_apply_init_table(void)
+{
+ tegra_init_from_table(common_init_table, clks, TEGRA124_CLK_CLK_MAX);
+ tegra_init_from_table(tegra124_init_table, clks, TEGRA124_CLK_CLK_MAX);
+}
+
+/**
+ * tegra124_car_barrier - wait for pending writes to the CAR to complete
+ *
+ * Wait for any outstanding writes to the CAR MMIO space from this CPU
+ * to complete before continuing execution. No return value.
+ */
+static void tegra124_car_barrier(void)
+{
+ readl_relaxed(clk_base + RST_DFLL_DVCO);
+}
+
+/**
+ * tegra124_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
+ *
+ * Assert the reset line of the DFLL's DVCO. No return value.
+ */
+static void tegra124_clock_assert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v |= (1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra124_car_barrier();
+}
+
+/**
+ * tegra124_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
+ *
+ * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
+ * operate. No return value.
+ */
+static void tegra124_clock_deassert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra124_car_barrier();
+}
+
+static int tegra124_reset_assert(unsigned long id)
+{
+ if (id == TEGRA124_RST_DFLL_DVCO)
+ tegra124_clock_assert_dfll_dvco_reset();
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra124_reset_deassert(unsigned long id)
+{
+ if (id == TEGRA124_RST_DFLL_DVCO)
+ tegra124_clock_deassert_dfll_dvco_reset();
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * tegra132_clock_apply_init_table - initialize clocks on Tegra132 SoCs
+ *
+ * Program an initial clock rate and enable or disable clocks needed
+ * by the rest of the kernel, for Tegra132 SoCs. It is intended to be
+ * called by assigning a pointer to it to tegra_clk_apply_init_table -
+ * this will be called as an arch_initcall. No return value.
+ */
+static void __init tegra132_clock_apply_init_table(void)
+{
+ tegra_init_from_table(common_init_table, clks, TEGRA124_CLK_CLK_MAX);
+ tegra_init_from_table(tegra132_init_table, clks, TEGRA124_CLK_CLK_MAX);
+}
+
+/**
+ * tegra124_132_clock_init_pre - clock initialization preamble for T124/T132
+ * @np: struct device_node * of the DT node for the SoC CAR IP block
+ *
+ * Register most of the clocks controlled by the CAR IP block.
+ * Everything in this function should be common to Tegra124 and Tegra132.
+ * No return value.
+ */
+static void __init tegra124_132_clock_init_pre(struct device_node *np)
+{
+ struct device_node *node;
+ u32 plld_base;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra124/tegra132 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ WARN_ON(1);
+ return;
+ }
+
+ pmc_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ WARN_ON(1);
+ return;
+ }
+
+ clks = tegra_clk_init(clk_base, TEGRA124_CLK_CLK_MAX,
+ TEGRA124_CAR_BANK_COUNT);
+ if (!clks)
+ return;
+
+ if (tegra_osc_clk_init(clk_base, tegra124_clks, tegra124_input_freq,
+ ARRAY_SIZE(tegra124_input_freq), 1, &osc_freq,
+ &pll_ref_freq) < 0)
+ return;
+
+ tegra_fixed_clk_init(tegra124_clks);
+ tegra124_pll_init(clk_base, pmc_base);
+ tegra124_periph_clk_init(clk_base, pmc_base);
+ tegra_audio_clk_init(clk_base, pmc_base, tegra124_clks,
+ tegra124_audio_plls,
+ ARRAY_SIZE(tegra124_audio_plls), 24576000);
+
+ /* For Tegra124 & Tegra132, PLLD is the only source for DSIA & DSIB */
+ plld_base = readl(clk_base + PLLD_BASE);
+ plld_base &= ~BIT(25);
+ writel(plld_base, clk_base + PLLD_BASE);
+}
+
+static struct clk *tegra124_clk_src_onecell_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+
+ clk = of_clk_src_onecell_get(clkspec, data);
+ if (IS_ERR(clk))
+ return clk;
+
+ hw = __clk_get_hw(clk);
+
+ if (clkspec->args[0] == TEGRA124_CLK_EMC) {
+ if (!tegra124_clk_emc_driver_available(hw))
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return clk;
+}
+
+/**
+ * tegra124_132_clock_init_post - clock initialization postamble for T124/T132
+ * @np: struct device_node * of the DT node for the SoC CAR IP block
+ *
+ * Register most of the clocks controlled by the CAR IP block.
+ * Everything in this function should be common to Tegra124
+ * and Tegra132. This function must be called after
+ * tegra124_132_clock_init_pre(), otherwise clk_base will not be set.
+ * No return value.
+ */
+static void __init tegra124_132_clock_init_post(struct device_node *np)
+{
+ tegra_super_clk_gen4_init(clk_base, pmc_base, tegra124_clks,
+ &pll_x_params);
+ tegra_init_special_resets(1, tegra124_reset_assert,
+ tegra124_reset_deassert);
+ tegra_add_of_provider(np, tegra124_clk_src_onecell_get);
+
+ clks[TEGRA124_CLK_EMC] = tegra124_clk_register_emc(clk_base, np,
+ &emc_lock);
+
+ tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+
+ tegra_cpu_car_ops = &tegra124_cpu_car_ops;
+}
+
+/**
+ * tegra124_clock_init - Tegra124-specific clock initialization
+ * @np: struct device_node * of the DT node for the SoC CAR IP block
+ *
+ * Register most SoC clocks for the Tegra124 system-on-chip. Most of
+ * this code is shared between the Tegra124 and Tegra132 SoCs,
+ * although some of the initial clock settings and CPU clocks differ.
+ * Intended to be called by the OF init code when a DT node with the
+ * "nvidia,tegra124-car" string is encountered, and declared with
+ * CLK_OF_DECLARE. No return value.
+ */
+static void __init tegra124_clock_init(struct device_node *np)
+{
+ tegra124_132_clock_init_pre(np);
+ tegra_clk_apply_init_table = tegra124_clock_apply_init_table;
+ tegra124_132_clock_init_post(np);
+}
+
+/**
+ * tegra132_clock_init - Tegra132-specific clock initialization
+ * @np: struct device_node * of the DT node for the SoC CAR IP block
+ *
+ * Register most SoC clocks for the Tegra132 system-on-chip. Most of
+ * this code is shared between the Tegra124 and Tegra132 SoCs,
+ * although some of the initial clock settings and CPU clocks differ.
+ * Intended to be called by the OF init code when a DT node with the
+ * "nvidia,tegra132-car" string is encountered, and declared with
+ * CLK_OF_DECLARE. No return value.
+ */
+static void __init tegra132_clock_init(struct device_node *np)
+{
+ tegra124_132_clock_init_pre(np);
+
+ /*
+ * On Tegra132, these clocks are controlled by the
+ * CLUSTER_clocks IP block, located in the CPU complex
+ */
+ tegra124_clks[tegra_clk_cclk_g].present = false;
+ tegra124_clks[tegra_clk_cclk_lp].present = false;
+ tegra124_clks[tegra_clk_pll_x].present = false;
+ tegra124_clks[tegra_clk_pll_x_out0].present = false;
+
+ tegra_clk_apply_init_table = tegra132_clock_apply_init_table;
+ tegra124_132_clock_init_post(np);
+}
+CLK_OF_DECLARE(tegra124, "nvidia,tegra124-car", tegra124_clock_init);
+CLK_OF_DECLARE(tegra132, "nvidia,tegra132-car", tegra132_clock_init);
diff --git a/drivers/clk/tegra/clk-tegra20-emc.c b/drivers/clk/tegra/clk-tegra20-emc.c
new file mode 100644
index 000000000..44465726a
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra20-emc.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Based on drivers/clk/tegra/clk-emc.c
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#define pr_fmt(fmt) "tegra-emc-clk: " fmt
+
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/tegra.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0)
+#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30)
+#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30
+
+#define MC_EMC_SAME_FREQ BIT(16)
+#define USE_PLLM_UD BIT(29)
+
+#define EMC_SRC_PLL_M 0
+#define EMC_SRC_PLL_C 1
+#define EMC_SRC_PLL_P 2
+#define EMC_SRC_CLK_M 3
+
+static const char * const emc_parent_clk_names[] = {
+ "pll_m", "pll_c", "pll_p", "clk_m",
+};
+
+struct tegra_clk_emc {
+ struct clk_hw hw;
+ void __iomem *reg;
+ bool mc_same_freq;
+ bool want_low_jitter;
+
+ tegra20_clk_emc_round_cb *round_cb;
+ void *cb_arg;
+};
+
+static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
+{
+ return container_of(hw, struct tegra_clk_emc, hw);
+}
+
+static unsigned long emc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+ u32 val, div;
+
+ val = readl_relaxed(emc->reg);
+ div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+
+ return DIV_ROUND_UP(parent_rate * 2, div + 2);
+}
+
+static u8 emc_get_parent(struct clk_hw *hw)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+
+ return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+}
+
+static int emc_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+ u32 val, div;
+
+ val = readl_relaxed(emc->reg);
+ val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
+ val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+ div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+
+ if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+ val |= USE_PLLM_UD;
+ else
+ val &= ~USE_PLLM_UD;
+
+ if (emc->mc_same_freq)
+ val |= MC_EMC_SAME_FREQ;
+ else
+ val &= ~MC_EMC_SAME_FREQ;
+
+ writel_relaxed(val, emc->reg);
+
+ fence_udelay(1, emc->reg);
+
+ return 0;
+}
+
+static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+ unsigned int index;
+ u32 val, div;
+
+ div = div_frac_get(rate, parent_rate, 8, 1, 0);
+
+ val = readl_relaxed(emc->reg);
+ val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+ val |= div;
+
+ index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+ if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+ val |= USE_PLLM_UD;
+ else
+ val &= ~USE_PLLM_UD;
+
+ if (emc->mc_same_freq)
+ val |= MC_EMC_SAME_FREQ;
+ else
+ val &= ~MC_EMC_SAME_FREQ;
+
+ writel_relaxed(val, emc->reg);
+
+ fence_udelay(1, emc->reg);
+
+ return 0;
+}
+
+static int emc_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate,
+ u8 index)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+ u32 val, div;
+
+ div = div_frac_get(rate, parent_rate, 8, 1, 0);
+
+ val = readl_relaxed(emc->reg);
+
+ val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
+ val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+ val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+ val |= div;
+
+ if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+ val |= USE_PLLM_UD;
+ else
+ val &= ~USE_PLLM_UD;
+
+ if (emc->mc_same_freq)
+ val |= MC_EMC_SAME_FREQ;
+ else
+ val &= ~MC_EMC_SAME_FREQ;
+
+ writel_relaxed(val, emc->reg);
+
+ fence_udelay(1, emc->reg);
+
+ return 0;
+}
+
+static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+ struct clk_hw *parent_hw;
+ unsigned long divided_rate;
+ unsigned long parent_rate;
+ unsigned int i;
+ long emc_rate;
+ int div;
+
+ emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
+ emc->cb_arg);
+ if (emc_rate < 0)
+ return emc_rate;
+
+ for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
+ parent_hw = clk_hw_get_parent_by_index(hw, i);
+
+ if (req->best_parent_hw == parent_hw)
+ parent_rate = req->best_parent_rate;
+ else
+ parent_rate = clk_hw_get_rate(parent_hw);
+
+ if (emc_rate > parent_rate)
+ continue;
+
+ div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
+ divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
+
+ if (divided_rate != emc_rate)
+ continue;
+
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent_hw;
+ req->rate = emc_rate;
+ break;
+ }
+
+ if (i == ARRAY_SIZE(emc_parent_clk_names)) {
+ pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
+ req->rate, emc_rate);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct clk_ops tegra_clk_emc_ops = {
+ .recalc_rate = emc_recalc_rate,
+ .get_parent = emc_get_parent,
+ .set_parent = emc_set_parent,
+ .set_rate = emc_set_rate,
+ .set_rate_and_parent = emc_set_rate_and_parent,
+ .determine_rate = emc_determine_rate,
+};
+
+void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
+ void *cb_arg)
+{
+ struct clk *clk = __clk_lookup("emc");
+ struct tegra_clk_emc *emc;
+ struct clk_hw *hw;
+
+ if (clk) {
+ hw = __clk_get_hw(clk);
+ emc = to_tegra_clk_emc(hw);
+
+ emc->round_cb = round_cb;
+ emc->cb_arg = cb_arg;
+ }
+}
+EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
+
+bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
+{
+ return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
+}
+
+struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
+{
+ struct tegra_clk_emc *emc;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ emc = kzalloc_obj(*emc);
+ if (!emc)
+ return NULL;
+
+ /*
+ * EMC stands for External Memory Controller.
+ *
+ * We don't want EMC clock to be disabled ever by gating its
+ * parent and whatnot because system is busted immediately in that
+ * case, hence the clock is marked as critical.
+ */
+ init.name = "emc";
+ init.ops = &tegra_clk_emc_ops;
+ init.flags = CLK_IS_CRITICAL;
+ init.parent_names = emc_parent_clk_names;
+ init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
+
+ emc->reg = ioaddr;
+ emc->hw.init = &init;
+ emc->want_low_jitter = low_jitter;
+
+ clk = clk_register(NULL, &emc->hw);
+ if (IS_ERR(clk)) {
+ kfree(emc);
+ return NULL;
+ }
+
+ return clk;
+}
+
+int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
+{
+ struct tegra_clk_emc *emc;
+ struct clk_hw *hw;
+
+ if (!emc_clk)
+ return -EINVAL;
+
+ hw = __clk_get_hw(emc_clk);
+ emc = to_tegra_clk_emc(hw);
+ emc->mc_same_freq = same;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
new file mode 100644
index 000000000..9da82dd79
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -0,0 +1,1206 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/clk/tegra.h>
+#include <linux/delay.h>
+#include <dt-bindings/clock/tegra20-car.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define MISC_CLK_ENB 0x48
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_MASK (3u<<30)
+#define OSC_CTRL_OSC_FREQ_13MHZ (0u<<30)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ (1u<<30)
+#define OSC_CTRL_OSC_FREQ_12MHZ (2u<<30)
+#define OSC_CTRL_OSC_FREQ_26MHZ (3u<<30)
+#define OSC_CTRL_MASK (0x3f2u | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK (3u<<28)
+#define OSC_CTRL_PLL_REF_DIV_1 (0u<<28)
+#define OSC_CTRL_PLL_REF_DIV_2 (1u<<28)
+#define OSC_CTRL_PLL_REF_DIV_4 (2u<<28)
+
+#define OSC_FREQ_DET 0x58
+#define OSC_FREQ_DET_TRIG (1u<<31)
+
+#define OSC_FREQ_DET_STATUS 0x5c
+#define OSC_FREQ_DET_BUSYu (1<<31)
+#define OSC_FREQ_DET_CNT_MASK 0xFFFFu
+
+#define TEGRA20_CLK_PERIPH_BANKS 3
+
+#define PLLS_BASE 0xf0
+#define PLLS_MISC 0xf4
+#define PLLC_BASE 0x80
+#define PLLC_MISC 0x8c
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLA_OUT 0xb4
+
+#define CCLK_BURST_POLICY 0x20
+#define SUPER_CCLK_DIVIDER 0x24
+#define SCLK_BURST_POLICY 0x28
+#define SUPER_SCLK_DIVIDER 0x2c
+#define CLK_SYSTEM_RATE 0x30
+
+#define CCLK_BURST_POLICY_SHIFT 28
+#define CCLK_RUN_POLICY_SHIFT 4
+#define CCLK_IDLE_POLICY_SHIFT 0
+#define CCLK_IDLE_POLICY 1
+#define CCLK_RUN_POLICY 2
+#define CCLK_BURST_POLICY_PLLX 8
+
+#define CLK_SOURCE_I2S1 0x100
+#define CLK_SOURCE_I2S2 0x104
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_SPI 0x114
+#define CLK_SOURCE_XIO 0x120
+#define CLK_SOURCE_TWC 0x12c
+#define CLK_SOURCE_IDE 0x144
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_DISP1 0x138
+#define CLK_SOURCE_DISP2 0x13c
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_I2C1 0x124
+#define CLK_SOURCE_I2C2 0x198
+#define CLK_SOURCE_I2C3 0x1b8
+#define CLK_SOURCE_DVC 0x128
+#define CLK_SOURCE_UARTA 0x178
+#define CLK_SOURCE_UARTB 0x17c
+#define CLK_SOURCE_UARTC 0x1a0
+#define CLK_SOURCE_UARTD 0x1c0
+#define CLK_SOURCE_UARTE 0x1c4
+#define CLK_SOURCE_EMC 0x19c
+
+#define AUDIO_SYNC_CLK 0x38
+
+/* Tegra CPU clock and reset control regs */
+#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
+
+#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
+#define CPU_RESET(cpu) (0x1111ul << (cpu))
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra20_cpu_clk_sctx;
+#endif
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+
+#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
+ _clk_num, \
+ _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_DIV16(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ 30, 2, 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP, \
+ _clk_num, _gate_flags, \
+ _clk_id)
+
+#define TEGRA_INIT_DATA_NODIV(_name, _parents, _offset, \
+ _mux_shift, _mux_width, _clk_num, \
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ _mux_shift, _mux_width, 0, 0, 0, 0, 0, \
+ _clk_num, _gate_flags, \
+ _clk_id)
+
+static struct clk **clks;
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 600000000, 600, 12, 1, 8 },
+ { 13000000, 600000000, 600, 13, 1, 8 },
+ { 19200000, 600000000, 500, 16, 1, 6 },
+ { 26000000, 600000000, 600, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 1, 8 },
+ { 13000000, 666000000, 666, 13, 1, 8 },
+ { 19200000, 666000000, 555, 16, 1, 8 },
+ { 26000000, 666000000, 666, 26, 1, 8 },
+ { 12000000, 600000000, 600, 12, 1, 8 },
+ { 13000000, 600000000, 600, 13, 1, 8 },
+ { 19200000, 600000000, 375, 12, 1, 6 },
+ { 26000000, 600000000, 600, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 2, 8 },
+ { 13000000, 216000000, 432, 13, 2, 8 },
+ { 19200000, 216000000, 90, 4, 2, 1 },
+ { 26000000, 216000000, 432, 26, 2, 8 },
+ { 12000000, 432000000, 432, 12, 1, 8 },
+ { 13000000, 432000000, 432, 13, 1, 8 },
+ { 19200000, 432000000, 90, 4, 1, 1 },
+ { 26000000, 432000000, 432, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 28800000, 56448000, 49, 25, 1, 1 },
+ { 28800000, 73728000, 64, 25, 1, 1 },
+ { 28800000, 24000000, 5, 6, 1, 1 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 1, 4 },
+ { 13000000, 216000000, 216, 13, 1, 4 },
+ { 19200000, 216000000, 135, 12, 1, 3 },
+ { 26000000, 216000000, 216, 26, 1, 4 },
+ { 12000000, 594000000, 594, 12, 1, 8 },
+ { 13000000, 594000000, 594, 13, 1, 8 },
+ { 19200000, 594000000, 495, 16, 1, 8 },
+ { 26000000, 594000000, 594, 26, 1, 8 },
+ { 12000000, 1000000000, 1000, 12, 1, 12 },
+ { 13000000, 1000000000, 1000, 13, 1, 12 },
+ { 19200000, 1000000000, 625, 12, 1, 8 },
+ { 26000000, 1000000000, 1000, 26, 1, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 1, 0 },
+ { 13000000, 480000000, 960, 13, 1, 0 },
+ { 19200000, 480000000, 200, 4, 1, 0 },
+ { 26000000, 480000000, 960, 26, 1, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 1, 12 },
+ { 13000000, 1000000000, 1000, 13, 1, 12 },
+ { 19200000, 1000000000, 625, 12, 1, 8 },
+ { 26000000, 1000000000, 1000, 26, 1, 12 },
+ /* 912 MHz */
+ { 12000000, 912000000, 912, 12, 1, 12 },
+ { 13000000, 912000000, 912, 13, 1, 12 },
+ { 19200000, 912000000, 760, 16, 1, 8 },
+ { 26000000, 912000000, 912, 26, 1, 12 },
+ /* 816 MHz */
+ { 12000000, 816000000, 816, 12, 1, 12 },
+ { 13000000, 816000000, 816, 13, 1, 12 },
+ { 19200000, 816000000, 680, 16, 1, 8 },
+ { 26000000, 816000000, 816, 26, 1, 12 },
+ /* 760 MHz */
+ { 12000000, 760000000, 760, 12, 1, 12 },
+ { 13000000, 760000000, 760, 13, 1, 12 },
+ { 19200000, 760000000, 950, 24, 1, 8 },
+ { 26000000, 760000000, 760, 26, 1, 12 },
+ /* 750 MHz */
+ { 12000000, 750000000, 750, 12, 1, 12 },
+ { 13000000, 750000000, 750, 13, 1, 12 },
+ { 19200000, 750000000, 625, 16, 1, 8 },
+ { 26000000, 750000000, 750, 26, 1, 12 },
+ /* 608 MHz */
+ { 12000000, 608000000, 608, 12, 1, 12 },
+ { 13000000, 608000000, 608, 13, 1, 12 },
+ { 19200000, 608000000, 380, 12, 1, 8 },
+ { 26000000, 608000000, 608, 26, 1, 12 },
+ /* 456 MHz */
+ { 12000000, 456000000, 456, 12, 1, 12 },
+ { 13000000, 456000000, 456, 13, 1, 12 },
+ { 19200000, 456000000, 380, 16, 1, 8 },
+ { 26000000, 456000000, 456, 26, 1, 12 },
+ /* 312 MHz */
+ { 12000000, 312000000, 312, 12, 1, 12 },
+ { 13000000, 312000000, 312, 13, 1, 12 },
+ { 19200000, 312000000, 260, 16, 1, 8 },
+ { 26000000, 312000000, 312, 26, 1, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct pdiv_map plle_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ { 12000000, 100000000, 200, 24, 1, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+/* PLL parameters */
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_c_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_p_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 216000000,
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_a_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static const struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_params pll_u_params = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 48000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+ .freq_table = pll_u_freq_table,
+ .flags = TEGRA_PLLU | TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_x_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .pre_rate_change = tegra_cclk_pre_pllx_rate_change,
+ .post_rate_change = tegra_cclk_post_pllx_rate_change,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 12000000,
+ .cf_min = 0,
+ .cf_max = 0,
+ .vco_min = 0,
+ .vco_max = 0,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 0,
+ .pdiv_tohw = plle_p,
+ .freq_table = pll_e_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_LOCK_MISC |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 100000000,
+};
+
+static struct tegra_devclk devclks[] = {
+ { .con_id = "pll_c", .dt_id = TEGRA20_CLK_PLL_C },
+ { .con_id = "pll_c_out1", .dt_id = TEGRA20_CLK_PLL_C_OUT1 },
+ { .con_id = "pll_p", .dt_id = TEGRA20_CLK_PLL_P },
+ { .con_id = "pll_p_out1", .dt_id = TEGRA20_CLK_PLL_P_OUT1 },
+ { .con_id = "pll_p_out2", .dt_id = TEGRA20_CLK_PLL_P_OUT2 },
+ { .con_id = "pll_p_out3", .dt_id = TEGRA20_CLK_PLL_P_OUT3 },
+ { .con_id = "pll_p_out4", .dt_id = TEGRA20_CLK_PLL_P_OUT4 },
+ { .con_id = "pll_m", .dt_id = TEGRA20_CLK_PLL_M },
+ { .con_id = "pll_m_out1", .dt_id = TEGRA20_CLK_PLL_M_OUT1 },
+ { .con_id = "pll_x", .dt_id = TEGRA20_CLK_PLL_X },
+ { .con_id = "pll_u", .dt_id = TEGRA20_CLK_PLL_U },
+ { .con_id = "pll_d", .dt_id = TEGRA20_CLK_PLL_D },
+ { .con_id = "pll_d_out0", .dt_id = TEGRA20_CLK_PLL_D_OUT0 },
+ { .con_id = "pll_a", .dt_id = TEGRA20_CLK_PLL_A },
+ { .con_id = "pll_a_out0", .dt_id = TEGRA20_CLK_PLL_A_OUT0 },
+ { .con_id = "pll_e", .dt_id = TEGRA20_CLK_PLL_E },
+ { .con_id = "cclk", .dt_id = TEGRA20_CLK_CCLK },
+ { .con_id = "sclk", .dt_id = TEGRA20_CLK_SCLK },
+ { .con_id = "hclk", .dt_id = TEGRA20_CLK_HCLK },
+ { .con_id = "pclk", .dt_id = TEGRA20_CLK_PCLK },
+ { .con_id = "fuse", .dt_id = TEGRA20_CLK_FUSE },
+ { .con_id = "twd", .dt_id = TEGRA20_CLK_TWD },
+ { .con_id = "audio", .dt_id = TEGRA20_CLK_AUDIO },
+ { .con_id = "audio_2x", .dt_id = TEGRA20_CLK_AUDIO_2X },
+ { .dev_id = "tegra20-ac97", .dt_id = TEGRA20_CLK_AC97 },
+ { .dev_id = "tegra-apbdma", .dt_id = TEGRA20_CLK_APBDMA },
+ { .dev_id = "rtc-tegra", .dt_id = TEGRA20_CLK_RTC },
+ { .dev_id = "timer", .dt_id = TEGRA20_CLK_TIMER },
+ { .dev_id = "tegra-kbc", .dt_id = TEGRA20_CLK_KBC },
+ { .con_id = "csus", .dev_id = "tegra_camera", .dt_id = TEGRA20_CLK_CSUS },
+ { .con_id = "vcp", .dev_id = "tegra-avp", .dt_id = TEGRA20_CLK_VCP },
+ { .con_id = "bsea", .dev_id = "tegra-avp", .dt_id = TEGRA20_CLK_BSEA },
+ { .con_id = "bsev", .dev_id = "tegra-aes", .dt_id = TEGRA20_CLK_BSEV },
+ { .con_id = "emc", .dt_id = TEGRA20_CLK_EMC },
+ { .dev_id = "fsl-tegra-udc", .dt_id = TEGRA20_CLK_USBD },
+ { .dev_id = "tegra-ehci.1", .dt_id = TEGRA20_CLK_USB2 },
+ { .dev_id = "tegra-ehci.2", .dt_id = TEGRA20_CLK_USB3 },
+ { .dev_id = "dsi", .dt_id = TEGRA20_CLK_DSI },
+ { .con_id = "csi", .dev_id = "tegra_camera", .dt_id = TEGRA20_CLK_CSI },
+ { .con_id = "isp", .dev_id = "tegra_camera", .dt_id = TEGRA20_CLK_ISP },
+ { .con_id = "pex", .dt_id = TEGRA20_CLK_PEX },
+ { .con_id = "afi", .dt_id = TEGRA20_CLK_AFI },
+ { .con_id = "cdev1", .dt_id = TEGRA20_CLK_CDEV1 },
+ { .con_id = "cdev2", .dt_id = TEGRA20_CLK_CDEV2 },
+ { .con_id = "clk_32k", .dt_id = TEGRA20_CLK_CLK_32K },
+ { .con_id = "clk_m", .dt_id = TEGRA20_CLK_CLK_M },
+ { .con_id = "pll_ref", .dt_id = TEGRA20_CLK_PLL_REF },
+ { .dev_id = "tegra20-i2s.0", .dt_id = TEGRA20_CLK_I2S1 },
+ { .dev_id = "tegra20-i2s.1", .dt_id = TEGRA20_CLK_I2S2 },
+ { .con_id = "spdif_out", .dev_id = "tegra20-spdif", .dt_id = TEGRA20_CLK_SPDIF_OUT },
+ { .con_id = "spdif_in", .dev_id = "tegra20-spdif", .dt_id = TEGRA20_CLK_SPDIF_IN },
+ { .dev_id = "spi_tegra.0", .dt_id = TEGRA20_CLK_SBC1 },
+ { .dev_id = "spi_tegra.1", .dt_id = TEGRA20_CLK_SBC2 },
+ { .dev_id = "spi_tegra.2", .dt_id = TEGRA20_CLK_SBC3 },
+ { .dev_id = "spi_tegra.3", .dt_id = TEGRA20_CLK_SBC4 },
+ { .dev_id = "spi", .dt_id = TEGRA20_CLK_SPI },
+ { .dev_id = "xio", .dt_id = TEGRA20_CLK_XIO },
+ { .dev_id = "twc", .dt_id = TEGRA20_CLK_TWC },
+ { .dev_id = "ide", .dt_id = TEGRA20_CLK_IDE },
+ { .dev_id = "tegra_nand", .dt_id = TEGRA20_CLK_NDFLASH },
+ { .dev_id = "vfir", .dt_id = TEGRA20_CLK_VFIR },
+ { .dev_id = "csite", .dt_id = TEGRA20_CLK_CSITE },
+ { .dev_id = "la", .dt_id = TEGRA20_CLK_LA },
+ { .dev_id = "tegra_w1", .dt_id = TEGRA20_CLK_OWR },
+ { .dev_id = "mipi", .dt_id = TEGRA20_CLK_MIPI },
+ { .dev_id = "vde", .dt_id = TEGRA20_CLK_VDE },
+ { .con_id = "vi", .dev_id = "tegra_camera", .dt_id = TEGRA20_CLK_VI },
+ { .dev_id = "epp", .dt_id = TEGRA20_CLK_EPP },
+ { .dev_id = "mpe", .dt_id = TEGRA20_CLK_MPE },
+ { .dev_id = "host1x", .dt_id = TEGRA20_CLK_HOST1X },
+ { .dev_id = "3d", .dt_id = TEGRA20_CLK_GR3D },
+ { .dev_id = "2d", .dt_id = TEGRA20_CLK_GR2D },
+ { .dev_id = "tegra-nor", .dt_id = TEGRA20_CLK_NOR },
+ { .dev_id = "sdhci-tegra.0", .dt_id = TEGRA20_CLK_SDMMC1 },
+ { .dev_id = "sdhci-tegra.1", .dt_id = TEGRA20_CLK_SDMMC2 },
+ { .dev_id = "sdhci-tegra.2", .dt_id = TEGRA20_CLK_SDMMC3 },
+ { .dev_id = "sdhci-tegra.3", .dt_id = TEGRA20_CLK_SDMMC4 },
+ { .dev_id = "cve", .dt_id = TEGRA20_CLK_CVE },
+ { .dev_id = "tvo", .dt_id = TEGRA20_CLK_TVO },
+ { .dev_id = "tvdac", .dt_id = TEGRA20_CLK_TVDAC },
+ { .con_id = "vi_sensor", .dev_id = "tegra_camera", .dt_id = TEGRA20_CLK_VI_SENSOR },
+ { .dev_id = "hdmi", .dt_id = TEGRA20_CLK_HDMI },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.0", .dt_id = TEGRA20_CLK_I2C1 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.1", .dt_id = TEGRA20_CLK_I2C2 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.2", .dt_id = TEGRA20_CLK_I2C3 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.3", .dt_id = TEGRA20_CLK_DVC },
+ { .dev_id = "tegra-pwm", .dt_id = TEGRA20_CLK_PWM },
+ { .dev_id = "tegra_uart.0", .dt_id = TEGRA20_CLK_UARTA },
+ { .dev_id = "tegra_uart.1", .dt_id = TEGRA20_CLK_UARTB },
+ { .dev_id = "tegra_uart.2", .dt_id = TEGRA20_CLK_UARTC },
+ { .dev_id = "tegra_uart.3", .dt_id = TEGRA20_CLK_UARTD },
+ { .dev_id = "tegra_uart.4", .dt_id = TEGRA20_CLK_UARTE },
+ { .dev_id = "tegradc.0", .dt_id = TEGRA20_CLK_DISP1 },
+ { .dev_id = "tegradc.1", .dt_id = TEGRA20_CLK_DISP2 },
+};
+
+static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
+ [tegra_clk_ahbdma] = { .dt_id = TEGRA20_CLK_AHBDMA, .present = true },
+ [tegra_clk_apbdma] = { .dt_id = TEGRA20_CLK_APBDMA, .present = true },
+ [tegra_clk_spdif_out] = { .dt_id = TEGRA20_CLK_SPDIF_OUT, .present = true },
+ [tegra_clk_spdif_in] = { .dt_id = TEGRA20_CLK_SPDIF_IN, .present = true },
+ [tegra_clk_sdmmc1] = { .dt_id = TEGRA20_CLK_SDMMC1, .present = true },
+ [tegra_clk_sdmmc2] = { .dt_id = TEGRA20_CLK_SDMMC2, .present = true },
+ [tegra_clk_sdmmc3] = { .dt_id = TEGRA20_CLK_SDMMC3, .present = true },
+ [tegra_clk_sdmmc4] = { .dt_id = TEGRA20_CLK_SDMMC4, .present = true },
+ [tegra_clk_la] = { .dt_id = TEGRA20_CLK_LA, .present = true },
+ [tegra_clk_csite] = { .dt_id = TEGRA20_CLK_CSITE, .present = true },
+ [tegra_clk_vfir] = { .dt_id = TEGRA20_CLK_VFIR, .present = true },
+ [tegra_clk_mipi] = { .dt_id = TEGRA20_CLK_MIPI, .present = true },
+ [tegra_clk_nor] = { .dt_id = TEGRA20_CLK_NOR, .present = true },
+ [tegra_clk_rtc] = { .dt_id = TEGRA20_CLK_RTC, .present = true },
+ [tegra_clk_timer] = { .dt_id = TEGRA20_CLK_TIMER, .present = true },
+ [tegra_clk_kbc] = { .dt_id = TEGRA20_CLK_KBC, .present = true },
+ [tegra_clk_vcp] = { .dt_id = TEGRA20_CLK_VCP, .present = true },
+ [tegra_clk_bsea] = { .dt_id = TEGRA20_CLK_BSEA, .present = true },
+ [tegra_clk_bsev] = { .dt_id = TEGRA20_CLK_BSEV, .present = true },
+ [tegra_clk_usbd] = { .dt_id = TEGRA20_CLK_USBD, .present = true },
+ [tegra_clk_usb2] = { .dt_id = TEGRA20_CLK_USB2, .present = true },
+ [tegra_clk_usb3] = { .dt_id = TEGRA20_CLK_USB3, .present = true },
+ [tegra_clk_csi] = { .dt_id = TEGRA20_CLK_CSI, .present = true },
+ [tegra_clk_isp] = { .dt_id = TEGRA20_CLK_ISP, .present = true },
+ [tegra_clk_clk_32k] = { .dt_id = TEGRA20_CLK_CLK_32K, .present = true },
+ [tegra_clk_hclk] = { .dt_id = TEGRA20_CLK_HCLK, .present = true },
+ [tegra_clk_pclk] = { .dt_id = TEGRA20_CLK_PCLK, .present = true },
+ [tegra_clk_pll_p_out1] = { .dt_id = TEGRA20_CLK_PLL_P_OUT1, .present = true },
+ [tegra_clk_pll_p_out2] = { .dt_id = TEGRA20_CLK_PLL_P_OUT2, .present = true },
+ [tegra_clk_pll_p_out3] = { .dt_id = TEGRA20_CLK_PLL_P_OUT3, .present = true },
+ [tegra_clk_pll_p_out4] = { .dt_id = TEGRA20_CLK_PLL_P_OUT4, .present = true },
+ [tegra_clk_pll_p] = { .dt_id = TEGRA20_CLK_PLL_P, .present = true },
+ [tegra_clk_owr] = { .dt_id = TEGRA20_CLK_OWR, .present = true },
+ [tegra_clk_sbc1] = { .dt_id = TEGRA20_CLK_SBC1, .present = true },
+ [tegra_clk_sbc2] = { .dt_id = TEGRA20_CLK_SBC2, .present = true },
+ [tegra_clk_sbc3] = { .dt_id = TEGRA20_CLK_SBC3, .present = true },
+ [tegra_clk_sbc4] = { .dt_id = TEGRA20_CLK_SBC4, .present = true },
+ [tegra_clk_vde] = { .dt_id = TEGRA20_CLK_VDE, .present = true },
+ [tegra_clk_vi] = { .dt_id = TEGRA20_CLK_VI, .present = true },
+ [tegra_clk_epp] = { .dt_id = TEGRA20_CLK_EPP, .present = true },
+ [tegra_clk_mpe] = { .dt_id = TEGRA20_CLK_MPE, .present = true },
+ [tegra_clk_host1x] = { .dt_id = TEGRA20_CLK_HOST1X, .present = true },
+ [tegra_clk_gr2d] = { .dt_id = TEGRA20_CLK_GR2D, .present = true },
+ [tegra_clk_gr3d] = { .dt_id = TEGRA20_CLK_GR3D, .present = true },
+ [tegra_clk_ndflash] = { .dt_id = TEGRA20_CLK_NDFLASH, .present = true },
+ [tegra_clk_cve] = { .dt_id = TEGRA20_CLK_CVE, .present = true },
+ [tegra_clk_tvo] = { .dt_id = TEGRA20_CLK_TVO, .present = true },
+ [tegra_clk_tvdac] = { .dt_id = TEGRA20_CLK_TVDAC, .present = true },
+ [tegra_clk_vi_sensor] = { .dt_id = TEGRA20_CLK_VI_SENSOR, .present = true },
+ [tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true },
+ [tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true },
+ [tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },
+};
+
+static unsigned long tegra20_clk_measure_input_freq(void)
+{
+ u32 osc_ctrl = readl_relaxed(clk_base + OSC_CTRL);
+ u32 auto_clk_control = osc_ctrl & OSC_CTRL_OSC_FREQ_MASK;
+ u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
+ unsigned long input_freq;
+
+ switch (auto_clk_control) {
+ case OSC_CTRL_OSC_FREQ_12MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 12000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_13MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 13000000;
+ break;
+ case OSC_CTRL_OSC_FREQ_19_2MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 19200000;
+ break;
+ case OSC_CTRL_OSC_FREQ_26MHZ:
+ BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+ input_freq = 26000000;
+ break;
+ default:
+ pr_err("Unexpected clock autodetect value %d",
+ auto_clk_control);
+ BUG();
+ return 0;
+ }
+
+ return input_freq;
+}
+
+static unsigned int tegra20_get_pll_ref_div(void)
+{
+ u32 pll_ref_div = readl_relaxed(clk_base + OSC_CTRL) &
+ OSC_CTRL_PLL_REF_DIV_MASK;
+
+ switch (pll_ref_div) {
+ case OSC_CTRL_PLL_REF_DIV_1:
+ return 1;
+ case OSC_CTRL_PLL_REF_DIV_2:
+ return 2;
+ case OSC_CTRL_PLL_REF_DIV_4:
+ return 4;
+ default:
+ pr_err("Invalid pll ref divider %d\n", pll_ref_div);
+ BUG();
+ }
+ return 0;
+}
+
+static void tegra20_pll_init(void)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, NULL, 0,
+ &pll_c_params, NULL);
+ clks[TEGRA20_CLK_PLL_C] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT,
+ 0, NULL);
+ clks[TEGRA20_CLK_PLL_C_OUT1] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, NULL,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clks[TEGRA20_CLK_PLL_M] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA20_CLK_PLL_M_OUT1] = clk;
+
+ /* PLLX */
+ clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, NULL, 0,
+ &pll_x_params, NULL);
+ clks[TEGRA20_CLK_PLL_X] = clk;
+
+ /* PLLU */
+ clk = tegra_clk_register_pll("pll_u", "pll_ref", clk_base, NULL, 0,
+ &pll_u_params, NULL);
+ clks[TEGRA20_CLK_PLL_U] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, NULL, 0,
+ &pll_d_params, NULL);
+ clks[TEGRA20_CLK_PLL_D] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA20_CLK_PLL_D_OUT0] = clk;
+
+ /* PLLA */
+ clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, NULL, 0,
+ &pll_a_params, NULL);
+ clks[TEGRA20_CLK_PLL_A] = clk;
+
+ /* PLLA_OUT0 */
+ clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
+ clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
+ clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA20_CLK_PLL_A_OUT0] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle("pll_e", "pll_ref", clk_base, pmc_base,
+ 0, &pll_e_params, NULL);
+ clks[TEGRA20_CLK_PLL_E] = clk;
+}
+
+static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p", "pll_p_out4",
+ "pll_p_out3", "clk_d", "pll_x" };
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p_out3", "pll_p_out2", "clk_d",
+ "clk_32k", "pll_m_out1" };
+
+static void tegra20_super_clk_init(void)
+{
+ struct clk *clk;
+
+ /* CCLK */
+ clk = tegra_clk_register_super_cclk("cclk", cclk_parents,
+ ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT,
+ clk_base + CCLK_BURST_POLICY, TEGRA20_SUPER_CLK,
+ NULL);
+ clks[TEGRA20_CLK_CCLK] = clk;
+
+ /* twd */
+ clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4);
+ clks[TEGRA20_CLK_TWD] = clk;
+}
+
+static const char *audio_parents[] = { "spdif_in", "i2s1", "i2s2", "unused",
+ "pll_a_out0", "unused", "unused",
+ "unused" };
+
+static void __init tegra20_audio_clk_init(void)
+{
+ struct clk *clk;
+
+ /* audio */
+ clk = clk_register_mux(NULL, "audio_mux", audio_parents,
+ ARRAY_SIZE(audio_parents),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL);
+ clk = clk_register_gate(NULL, "audio", "audio_mux", 0,
+ clk_base + AUDIO_SYNC_CLK, 4,
+ CLK_GATE_SET_TO_DISABLE, NULL);
+ clks[TEGRA20_CLK_AUDIO] = clk;
+
+ /* audio_2x */
+ clk = clk_register_fixed_factor(NULL, "audio_doubler", "audio",
+ CLK_SET_RATE_PARENT, 2, 1);
+ clk = tegra_clk_register_periph_gate("audio_2x", "audio_doubler",
+ TEGRA_PERIPH_NO_RESET, clk_base,
+ CLK_SET_RATE_PARENT, 89,
+ periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_AUDIO_2X] = clk;
+}
+
+static const char *i2s1_parents[] = { "pll_a_out0", "audio_2x", "pll_p",
+ "clk_m" };
+static const char *i2s2_parents[] = { "pll_a_out0", "audio_2x", "pll_p",
+ "clk_m" };
+static const char *pwm_parents[] = { "pll_p", "pll_c", "audio", "clk_m",
+ "clk_32k" };
+static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
+static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c",
+ "clk_m" };
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1),
+ TEGRA_INIT_DATA_MUX("i2s2", i2s2_parents, CLK_SOURCE_I2S2, 18, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S2),
+ TEGRA_INIT_DATA_MUX("spi", mux_pllpcm_clkm, CLK_SOURCE_SPI, 43, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_SPI),
+ TEGRA_INIT_DATA_MUX("xio", mux_pllpcm_clkm, CLK_SOURCE_XIO, 45, 0, TEGRA20_CLK_XIO),
+ TEGRA_INIT_DATA_MUX("twc", mux_pllpcm_clkm, CLK_SOURCE_TWC, 16, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_TWC),
+ TEGRA_INIT_DATA_MUX("ide", mux_pllpcm_clkm, CLK_SOURCE_XIO, 25, 0, TEGRA20_CLK_IDE),
+ TEGRA_INIT_DATA_DIV16("dvc", mux_pllpcm_clkm, CLK_SOURCE_DVC, 47, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_DVC),
+ TEGRA_INIT_DATA_DIV16("i2c1", mux_pllpcm_clkm, CLK_SOURCE_I2C1, 12, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2C1),
+ TEGRA_INIT_DATA_DIV16("i2c2", mux_pllpcm_clkm, CLK_SOURCE_I2C2, 54, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2C2),
+ TEGRA_INIT_DATA_DIV16("i2c3", mux_pllpcm_clkm, CLK_SOURCE_I2C3, 67, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2C3),
+ TEGRA_INIT_DATA_MUX("hdmi", mux_pllpdc_clkm, CLK_SOURCE_HDMI, 51, 0, TEGRA20_CLK_HDMI),
+ TEGRA_INIT_DATA("pwm", NULL, NULL, pwm_parents, CLK_SOURCE_PWM, 28, 3, 0, 0, 8, 1, 0, 17, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_PWM),
+};
+
+static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
+ TEGRA_INIT_DATA_NODIV("uarta", mux_pllpcm_clkm, CLK_SOURCE_UARTA, 30, 2, 6, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_UARTA),
+ TEGRA_INIT_DATA_NODIV("uartb", mux_pllpcm_clkm, CLK_SOURCE_UARTB, 30, 2, 7, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_UARTB),
+ TEGRA_INIT_DATA_NODIV("uartc", mux_pllpcm_clkm, CLK_SOURCE_UARTC, 30, 2, 55, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_UARTC),
+ TEGRA_INIT_DATA_NODIV("uartd", mux_pllpcm_clkm, CLK_SOURCE_UARTD, 30, 2, 65, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_UARTD),
+ TEGRA_INIT_DATA_NODIV("uarte", mux_pllpcm_clkm, CLK_SOURCE_UARTE, 30, 2, 66, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_UARTE),
+ TEGRA_INIT_DATA_NODIV("disp1", mux_pllpdc_clkm, CLK_SOURCE_DISP1, 30, 2, 27, 0, TEGRA20_CLK_DISP1),
+ TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2),
+};
+
+static void __init tegra20_periph_clk_init(void)
+{
+ struct tegra_periph_init_data *data;
+ struct clk *clk;
+ unsigned int i;
+
+ /* ac97 */
+ clk = tegra_clk_register_periph_gate("ac97", "pll_a_out0",
+ TEGRA_PERIPH_ON_APB,
+ clk_base, 0, 3, periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_AC97] = clk;
+
+ /* emc */
+ clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false);
+
+ clks[TEGRA20_CLK_EMC] = clk;
+
+ clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
+ NULL);
+ clks[TEGRA20_CLK_MC] = clk;
+
+ /* dsi */
+ clk = tegra_clk_register_periph_gate("dsi", "pll_d_out0", 0,
+ clk_base, 0, TEGRA20_CLK_DSI,
+ periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_DSI] = clk;
+
+ /* pex */
+ clk = tegra_clk_register_periph_gate("pex", "clk_m", 0, clk_base, 0, 70,
+ periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_PEX] = clk;
+
+ /* dev1 OSC divider */
+ clk_register_divider(NULL, "dev1_osc_div", "clk_m",
+ 0, clk_base + MISC_CLK_ENB, 22, 2,
+ CLK_DIVIDER_POWER_OF_TWO | CLK_DIVIDER_READ_ONLY,
+ NULL);
+
+ /* dev2 OSC divider */
+ clk_register_divider(NULL, "dev2_osc_div", "clk_m",
+ 0, clk_base + MISC_CLK_ENB, 20, 2,
+ CLK_DIVIDER_POWER_OF_TWO | CLK_DIVIDER_READ_ONLY,
+ NULL);
+
+ /* cdev1 */
+ clk = tegra_clk_register_periph_gate("cdev1", "cdev1_mux", 0,
+ clk_base, 0, 94, periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_CDEV1] = clk;
+
+ /* cdev2 */
+ clk = tegra_clk_register_periph_gate("cdev2", "cdev2_mux", 0,
+ clk_base, 0, 93, periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_CDEV2] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "csus_mux", 0,
+ clk_base, 0, TEGRA20_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_CSUS] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph_data(clk_base, data);
+ clks[data->clk_id] = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) {
+ data = &tegra_periph_nodiv_clk_list[i];
+ clk = tegra_clk_register_periph_nodiv(data->name,
+ data->p.parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset);
+ clks[data->clk_id] = clk;
+ }
+
+ tegra_periph_clk_init(clk_base, pmc_base, tegra20_clks, &pll_p_params);
+}
+
+static void __init tegra20_osc_clk_init(void)
+{
+ struct clk *clk;
+ unsigned long input_freq;
+ unsigned int pll_ref_div;
+
+ input_freq = tegra20_clk_measure_input_freq();
+
+ /* clk_m */
+ clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IGNORE_UNUSED,
+ input_freq);
+ clks[TEGRA20_CLK_CLK_M] = clk;
+
+ /* pll_ref */
+ pll_ref_div = tegra20_get_pll_ref_div();
+ clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",
+ CLK_SET_RATE_PARENT, 1, pll_ref_div);
+ clks[TEGRA20_CLK_PLL_REF] = clk;
+}
+
+/* Tegra20 CPU clock and reset control functions */
+static void tegra20_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base +
+ TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+
+ return;
+}
+
+static void tegra20_put_cpu_in_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ dmb();
+}
+
+static void tegra20_cpu_out_of_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+ wmb();
+}
+
+static void tegra20_enable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg & ~CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ barrier();
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+static void tegra20_disable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg | CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static bool tegra20_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+
+ cpu_rst_status = readl(clk_base +
+ TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+
+ return !!(cpu_rst_status & 0x2);
+}
+
+static void tegra20_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra20_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_SOURCE_CSITE);
+ writel(3<<30, clk_base + CLK_SOURCE_CSITE);
+
+ tegra20_cpu_clk_sctx.cpu_burst =
+ readl(clk_base + CCLK_BURST_POLICY);
+ tegra20_cpu_clk_sctx.pllx_base =
+ readl(clk_base + PLLX_BASE);
+ tegra20_cpu_clk_sctx.pllx_misc =
+ readl(clk_base + PLLX_MISC);
+ tegra20_cpu_clk_sctx.cclk_divider =
+ readl(clk_base + SUPER_CCLK_DIVIDER);
+}
+
+static void tegra20_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+ u32 misc, base;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(clk_base + CCLK_BURST_POLICY);
+ policy = (reg >> CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CCLK_IDLE_POLICY)
+ reg = (reg >> CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CCLK_RUN_POLICY)
+ reg = (reg >> CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CCLK_BURST_POLICY_PLLX) {
+ misc = readl_relaxed(clk_base + PLLX_MISC);
+ base = readl_relaxed(clk_base + PLLX_BASE);
+
+ if (misc != tegra20_cpu_clk_sctx.pllx_misc ||
+ base != tegra20_cpu_clk_sctx.pllx_base) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra20_cpu_clk_sctx.pllx_misc,
+ clk_base + PLLX_MISC);
+ writel(tegra20_cpu_clk_sctx.pllx_base,
+ clk_base + PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra20_cpu_clk_sctx.cclk_divider,
+ clk_base + SUPER_CCLK_DIVIDER);
+ writel(tegra20_cpu_clk_sctx.cpu_burst,
+ clk_base + CCLK_BURST_POLICY);
+
+ writel(tegra20_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_SOURCE_CSITE);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra20_cpu_car_ops = {
+ .wait_for_reset = tegra20_wait_cpu_in_reset,
+ .put_in_reset = tegra20_put_cpu_in_reset,
+ .out_of_reset = tegra20_cpu_out_of_reset,
+ .enable_clock = tegra20_enable_cpu_clock,
+ .disable_clock = tegra20_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra20_cpu_rail_off_ready,
+ .suspend = tegra20_cpu_clock_suspend,
+ .resume = tegra20_cpu_clock_resume,
+#endif
+};
+
+static struct tegra_clk_init_table init_table[] = {
+ { TEGRA20_CLK_PLL_P, TEGRA20_CLK_CLK_MAX, 216000000, 1 },
+ { TEGRA20_CLK_PLL_P_OUT1, TEGRA20_CLK_CLK_MAX, 28800000, 1 },
+ { TEGRA20_CLK_PLL_P_OUT2, TEGRA20_CLK_CLK_MAX, 48000000, 1 },
+ { TEGRA20_CLK_PLL_P_OUT3, TEGRA20_CLK_CLK_MAX, 72000000, 1 },
+ { TEGRA20_CLK_PLL_P_OUT4, TEGRA20_CLK_CLK_MAX, 24000000, 1 },
+ { TEGRA20_CLK_PLL_C, TEGRA20_CLK_CLK_MAX, 600000000, 0 },
+ { TEGRA20_CLK_PLL_C_OUT1, TEGRA20_CLK_CLK_MAX, 120000000, 0 },
+ { TEGRA20_CLK_SCLK, TEGRA20_CLK_PLL_C_OUT1, 120000000, 0 },
+ { TEGRA20_CLK_HCLK, TEGRA20_CLK_CLK_MAX, 120000000, 0 },
+ { TEGRA20_CLK_PCLK, TEGRA20_CLK_CLK_MAX, 60000000, 0 },
+ { TEGRA20_CLK_CSITE, TEGRA20_CLK_CLK_MAX, 0, 1 },
+ { TEGRA20_CLK_CCLK, TEGRA20_CLK_CLK_MAX, 0, 1 },
+ { TEGRA20_CLK_UARTA, TEGRA20_CLK_PLL_P, 0, 0 },
+ { TEGRA20_CLK_UARTB, TEGRA20_CLK_PLL_P, 0, 0 },
+ { TEGRA20_CLK_UARTC, TEGRA20_CLK_PLL_P, 0, 0 },
+ { TEGRA20_CLK_UARTD, TEGRA20_CLK_PLL_P, 0, 0 },
+ { TEGRA20_CLK_UARTE, TEGRA20_CLK_PLL_P, 0, 0 },
+ { TEGRA20_CLK_PLL_A, TEGRA20_CLK_CLK_MAX, 56448000, 0 },
+ { TEGRA20_CLK_PLL_A_OUT0, TEGRA20_CLK_CLK_MAX, 11289600, 0 },
+ { TEGRA20_CLK_I2S1, TEGRA20_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA20_CLK_I2S2, TEGRA20_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA20_CLK_SDMMC1, TEGRA20_CLK_PLL_P, 48000000, 0 },
+ { TEGRA20_CLK_SDMMC3, TEGRA20_CLK_PLL_P, 48000000, 0 },
+ { TEGRA20_CLK_SDMMC4, TEGRA20_CLK_PLL_P, 48000000, 0 },
+ { TEGRA20_CLK_SPI, TEGRA20_CLK_PLL_P, 20000000, 0 },
+ { TEGRA20_CLK_SBC1, TEGRA20_CLK_PLL_P, 100000000, 0 },
+ { TEGRA20_CLK_SBC2, TEGRA20_CLK_PLL_P, 100000000, 0 },
+ { TEGRA20_CLK_SBC3, TEGRA20_CLK_PLL_P, 100000000, 0 },
+ { TEGRA20_CLK_SBC4, TEGRA20_CLK_PLL_P, 100000000, 0 },
+ { TEGRA20_CLK_HOST1X, TEGRA20_CLK_PLL_C, 150000000, 0 },
+ { TEGRA20_CLK_GR2D, TEGRA20_CLK_PLL_C, 300000000, 0 },
+ { TEGRA20_CLK_GR3D, TEGRA20_CLK_PLL_C, 300000000, 0 },
+ { TEGRA20_CLK_VDE, TEGRA20_CLK_PLL_C, 300000000, 0 },
+ { TEGRA20_CLK_PWM, TEGRA20_CLK_PLL_P, 48000000, 0 },
+ /* must be the last entry */
+ { TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0 },
+};
+
+/*
+ * Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+static struct tegra_clk_duplicate tegra_clk_duplicates[] = {
+ TEGRA_CLK_DUPLICATE(TEGRA20_CLK_USBD, "utmip-pad", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA20_CLK_USBD, "tegra-ehci.0", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA20_CLK_USBD, "tegra-otg", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA20_CLK_CCLK, NULL, "cpu"),
+ /* must be the last entry */
+ TEGRA_CLK_DUPLICATE(TEGRA20_CLK_CLK_MAX, NULL, NULL),
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra20-pmc" },
+ { },
+};
+
+static bool tegra20_car_initialized;
+
+static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct clk_hw *parent_hw;
+ struct clk_hw *hw;
+ struct clk *clk;
+
+ /*
+ * Timer clocks are needed early, the rest of the clocks shouldn't be
+ * available to device drivers until clock tree is fully initialized.
+ */
+ if (clkspec->args[0] != TEGRA20_CLK_RTC &&
+ clkspec->args[0] != TEGRA20_CLK_TWD &&
+ clkspec->args[0] != TEGRA20_CLK_TIMER &&
+ !tegra20_car_initialized)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ clk = of_clk_src_onecell_get(clkspec, data);
+ if (IS_ERR(clk))
+ return clk;
+
+ hw = __clk_get_hw(clk);
+
+ /*
+ * Tegra20 CDEV1, CDEV2 and CSUS clocks are a bit special case, their
+ * parent clock is created by the pinctrl driver. It is possible for
+ * clk user to request these clocks before pinctrl driver got probed
+ * and hence user will get an orphaned clock. That might be undesirable
+ * because user may expect parent clock to be enabled by the child.
+ */
+ if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
+ clkspec->args[0] == TEGRA20_CLK_CDEV2 ||
+ clkspec->args[0] == TEGRA20_CLK_CSUS) {
+ parent_hw = clk_hw_get_parent(hw);
+ if (!parent_hw)
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ if (clkspec->args[0] == TEGRA20_CLK_EMC) {
+ if (!tegra20_clk_emc_driver_available(hw))
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return clk;
+}
+
+static void __init tegra20_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("Can't map CAR registers\n");
+ BUG();
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ BUG();
+ }
+
+ pmc_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ BUG();
+ }
+
+ clks = tegra_clk_init(clk_base, TEGRA20_CLK_CLK_MAX,
+ TEGRA20_CLK_PERIPH_BANKS);
+ if (!clks)
+ return;
+
+ tegra20_osc_clk_init();
+ tegra_fixed_clk_init(tegra20_clks);
+ tegra20_pll_init();
+ tegra20_super_clk_init();
+ tegra_super_clk_gen4_init(clk_base, pmc_base, tegra20_clks, NULL);
+ tegra20_periph_clk_init();
+ tegra20_audio_clk_init();
+
+ tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX);
+
+ tegra_add_of_provider(np, tegra20_clk_src_onecell_get);
+
+ tegra_cpu_car_ops = &tegra20_cpu_car_ops;
+}
+CLK_OF_DECLARE_DRIVER(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
+
+/*
+ * Clocks that use runtime PM can't be created at the tegra20_clock_init
+ * time because drivers' base isn't initialized yet, and thus platform
+ * devices can't be created for the clocks. Hence we need to split the
+ * registration of the clocks into two phases. The first phase registers
+ * essential clocks which don't require RPM and are actually used during
+ * early boot. The second phase registers clocks which use RPM and this
+ * is done when device drivers' core API is ready.
+ */
+static int tegra20_car_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
+ clks[TEGRA20_CLK_SCLK] = clk;
+
+ tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+ tegra_init_from_table(init_table, clks, TEGRA20_CLK_CLK_MAX);
+ tegra20_car_initialized = true;
+
+ return 0;
+}
+
+static const struct of_device_id tegra20_car_match[] = {
+ { .compatible = "nvidia,tegra20-car" },
+ { }
+};
+
+static struct platform_driver tegra20_car_driver = {
+ .driver = {
+ .name = "tegra20-car",
+ .of_match_table = tegra20_car_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = tegra20_car_probe,
+};
+builtin_platform_driver(tegra20_car_driver);
diff --git a/drivers/clk/tegra/clk-tegra210-emc.c b/drivers/clk/tegra/clk-tegra210-emc.c
new file mode 100644
index 000000000..9f140d94b
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra210-emc.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/tegra.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define CLK_SOURCE_EMC 0x19c
+#define CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29)
+#define CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16)
+#define CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0)
+
+#define CLK_SRC_PLLM 0
+#define CLK_SRC_PLLC 1
+#define CLK_SRC_PLLP 2
+#define CLK_SRC_CLK_M 3
+#define CLK_SRC_PLLM_UD 4
+#define CLK_SRC_PLLMB_UD 5
+#define CLK_SRC_PLLMB 6
+#define CLK_SRC_PLLP_UD 7
+
+struct tegra210_clk_emc {
+ struct clk_hw hw;
+ void __iomem *regs;
+
+ struct tegra210_clk_emc_provider *provider;
+
+ struct clk *parents[8];
+};
+
+static inline struct tegra210_clk_emc *
+to_tegra210_clk_emc(struct clk_hw *hw)
+{
+ return container_of(hw, struct tegra210_clk_emc, hw);
+}
+
+static const char *tegra210_clk_emc_parents[] = {
+ "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud",
+ "pll_mb", "pll_p_ud",
+};
+
+static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw)
+{
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
+ u32 value;
+ u8 src;
+
+ value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
+ src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value);
+
+ return src;
+}
+
+static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
+ u32 value, div;
+
+ /*
+ * CCF assumes that neither the parent nor its rate will change during
+ * ->set_rate(), so the parent rate passed in here was cached from the
+ * parent before the ->set_rate() call.
+ *
+ * This can lead to wrong results being reported for the EMC clock if
+ * the parent and/or parent rate have changed as part of the EMC rate
+ * change sequence. Fix this by overriding the parent clock with what
+ * we know to be the correct value after the rate change.
+ */
+ parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+
+ value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
+
+ div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value);
+ div += 2;
+
+ return DIV_ROUND_UP(parent_rate * 2, div);
+}
+
+static int tegra210_clk_emc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
+ struct tegra210_clk_emc_provider *provider = emc->provider;
+ unsigned int i;
+
+ if (!provider || !provider->configs || provider->num_configs == 0) {
+ req->rate = clk_hw_get_rate(hw);
+
+ return 0;
+ }
+
+ for (i = 0; i < provider->num_configs; i++) {
+ if (provider->configs[i].rate >= req->rate) {
+ req->rate = provider->configs[i].rate;
+
+ return 0;
+ }
+ }
+
+ req->rate = provider->configs[i - 1].rate;
+
+ return 0;
+}
+
+static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc,
+ u8 index)
+{
+ struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index);
+ const char *name = clk_hw_get_name(parent);
+
+ /* XXX implement cache? */
+
+ return __clk_lookup(name);
+}
+
+static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
+ struct tegra210_clk_emc_provider *provider = emc->provider;
+ struct tegra210_clk_emc_config *config;
+ struct device *dev = provider->dev;
+ struct clk_hw *old, *new, *parent;
+ u8 old_idx, new_idx, index;
+ struct clk *clk;
+ unsigned int i;
+ int err;
+
+ if (!provider->configs || provider->num_configs == 0)
+ return -EINVAL;
+
+ for (i = 0; i < provider->num_configs; i++) {
+ if (provider->configs[i].rate >= rate) {
+ config = &provider->configs[i];
+ break;
+ }
+ }
+
+ if (i == provider->num_configs)
+ config = &provider->configs[i - 1];
+
+ old_idx = tegra210_clk_emc_get_parent(hw);
+ new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
+
+ old = clk_hw_get_parent_by_index(hw, old_idx);
+ new = clk_hw_get_parent_by_index(hw, new_idx);
+
+ /* if the rate has changed... */
+ if (config->parent_rate != clk_hw_get_rate(old)) {
+ /* ... but the clock source remains the same ... */
+ if (new_idx == old_idx) {
+ /* ... switch to the alternative clock source. */
+ switch (new_idx) {
+ case CLK_SRC_PLLM:
+ new_idx = CLK_SRC_PLLMB;
+ break;
+
+ case CLK_SRC_PLLM_UD:
+ new_idx = CLK_SRC_PLLMB_UD;
+ break;
+
+ case CLK_SRC_PLLMB_UD:
+ new_idx = CLK_SRC_PLLM_UD;
+ break;
+
+ case CLK_SRC_PLLMB:
+ new_idx = CLK_SRC_PLLM;
+ break;
+ }
+
+ /*
+ * This should never happen because we can't deal with
+ * it.
+ */
+ if (WARN_ON(new_idx == old_idx))
+ return -EINVAL;
+
+ new = clk_hw_get_parent_by_index(hw, new_idx);
+ }
+
+ index = new_idx;
+ parent = new;
+ } else {
+ index = old_idx;
+ parent = old;
+ }
+
+ clk = tegra210_clk_emc_find_parent(emc, index);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ dev_err(dev, "failed to get parent clock for index %u: %d\n",
+ index, err);
+ return err;
+ }
+
+ /* set the new parent clock to the required rate */
+ if (clk_get_rate(clk) != config->parent_rate) {
+ err = clk_set_rate(clk, config->parent_rate);
+ if (err < 0) {
+ dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n",
+ config->parent_rate, clk, err);
+ return err;
+ }
+ }
+
+ /* enable the new parent clock */
+ if (parent != old) {
+ err = clk_prepare_enable(clk);
+ if (err < 0) {
+ dev_err(dev, "failed to enable parent clock %pC: %d\n",
+ clk, err);
+ return err;
+ }
+ }
+
+ /* update the EMC source configuration to reflect the new parent */
+ config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC;
+ config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index);
+
+ /*
+ * Finally, switch the EMC programming with both old and new parent
+ * clocks enabled.
+ */
+ err = provider->set_rate(dev, config);
+ if (err < 0) {
+ dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate,
+ err);
+
+ /*
+ * If we're unable to switch to the new EMC frequency, we no
+ * longer need the new parent to be enabled.
+ */
+ if (parent != old)
+ clk_disable_unprepare(clk);
+
+ return err;
+ }
+
+ /* reparent to new parent clock and disable the old parent clock */
+ if (parent != old) {
+ clk = tegra210_clk_emc_find_parent(emc, old_idx);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ dev_err(dev,
+ "failed to get parent clock for index %u: %d\n",
+ old_idx, err);
+ return err;
+ }
+
+ clk_hw_reparent(hw, parent);
+ clk_disable_unprepare(clk);
+ }
+
+ return err;
+}
+
+static const struct clk_ops tegra210_clk_emc_ops = {
+ .get_parent = tegra210_clk_emc_get_parent,
+ .recalc_rate = tegra210_clk_emc_recalc_rate,
+ .determine_rate = tegra210_clk_emc_determine_rate,
+ .set_rate = tegra210_clk_emc_set_rate,
+};
+
+struct clk *tegra210_clk_register_emc(struct device_node *np,
+ void __iomem *regs)
+{
+ struct tegra210_clk_emc *emc;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ emc = kzalloc_obj(*emc);
+ if (!emc)
+ return ERR_PTR(-ENOMEM);
+
+ emc->regs = regs;
+
+ init.name = "emc";
+ init.ops = &tegra210_clk_emc_ops;
+ init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE;
+ init.parent_names = tegra210_clk_emc_parents;
+ init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents);
+ emc->hw.init = &init;
+
+ clk = clk_register(NULL, &emc->hw);
+ if (IS_ERR(clk)) {
+ kfree(emc);
+ return clk;
+ }
+
+ return clk;
+}
+
+int tegra210_clk_emc_attach(struct clk *clk,
+ struct tegra210_clk_emc_provider *provider)
+{
+ struct clk_hw *hw = __clk_get_hw(clk);
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
+ struct device *dev = provider->dev;
+ unsigned int i;
+ int err;
+
+ if (!try_module_get(provider->owner))
+ return -ENODEV;
+
+ for (i = 0; i < provider->num_configs; i++) {
+ struct tegra210_clk_emc_config *config = &provider->configs[i];
+ struct clk_hw *parent;
+ bool same_freq;
+ u8 div, src;
+
+ div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value);
+ src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
+
+ /* do basic sanity checking on the EMC timings */
+ if (div & 0x1) {
+ dev_err(dev, "invalid odd divider %u for rate %lu Hz\n",
+ div, config->rate);
+ err = -EINVAL;
+ goto put;
+ }
+
+ same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
+
+ if (same_freq != config->same_freq) {
+ dev_err(dev,
+ "ambiguous EMC to MC ratio for rate %lu Hz\n",
+ config->rate);
+ err = -EINVAL;
+ goto put;
+ }
+
+ parent = clk_hw_get_parent_by_index(hw, src);
+ config->parent = src;
+
+ if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) {
+ config->parent_rate = config->rate * (1 + div / 2);
+ } else {
+ unsigned long rate = config->rate * (1 + div / 2);
+
+ config->parent_rate = clk_hw_get_rate(parent);
+
+ if (config->parent_rate != rate) {
+ dev_err(dev,
+ "rate %lu Hz does not match input\n",
+ config->rate);
+ err = -EINVAL;
+ goto put;
+ }
+ }
+ }
+
+ emc->provider = provider;
+
+ return 0;
+
+put:
+ module_put(provider->owner);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach);
+
+void tegra210_clk_emc_detach(struct clk *clk)
+{
+ struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk));
+
+ module_put(emc->provider->owner);
+ emc->provider = NULL;
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach);
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
new file mode 100644
index 000000000..0c86cff71
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -0,0 +1,3821 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2020 NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/clk/tegra.h>
+#include <dt-bindings/clock/tegra210-car.h>
+#include <dt-bindings/reset/tegra210-car.h>
+#include <linux/sizes.h>
+#include <soc/tegra/pmc.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+/*
+ * TEGRA210_CAR_BANK_COUNT: the number of peripheral clock register
+ * banks present in the Tegra210 CAR IP block. The banks are
+ * identified by single letters, e.g.: L, H, U, V, W, X, Y. See
+ * periph_regs[] in drivers/clk/tegra/clk.c
+ */
+#define TEGRA210_CAR_BANK_COUNT 7
+
+#define CLK_SOURCE_CSITE 0x1d4
+#define CLK_SOURCE_EMC 0x19c
+#define CLK_SOURCE_SOR1 0x410
+#define CLK_SOURCE_SOR0 0x414
+#define CLK_SOURCE_LA 0x1f8
+#define CLK_SOURCE_SDMMC2 0x154
+#define CLK_SOURCE_SDMMC4 0x164
+#define CLK_SOURCE_EMC_DLL 0x664
+
+#define PLLC_BASE 0x80
+#define PLLC_OUT 0x84
+#define PLLC_MISC0 0x88
+#define PLLC_MISC1 0x8c
+#define PLLC_MISC2 0x5d0
+#define PLLC_MISC3 0x5d4
+
+#define PLLC2_BASE 0x4e8
+#define PLLC2_MISC0 0x4ec
+#define PLLC2_MISC1 0x4f0
+#define PLLC2_MISC2 0x4f4
+#define PLLC2_MISC3 0x4f8
+
+#define PLLC3_BASE 0x4fc
+#define PLLC3_MISC0 0x500
+#define PLLC3_MISC1 0x504
+#define PLLC3_MISC2 0x508
+#define PLLC3_MISC3 0x50c
+
+#define PLLM_BASE 0x90
+#define PLLM_MISC1 0x98
+#define PLLM_MISC2 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC0 0xac
+#define PLLP_MISC1 0x680
+#define PLLA_BASE 0xb0
+#define PLLA_MISC0 0xbc
+#define PLLA_MISC1 0xb8
+#define PLLA_MISC2 0x5d8
+#define PLLD_BASE 0xd0
+#define PLLD_MISC0 0xdc
+#define PLLD_MISC1 0xd8
+#define PLLU_BASE 0xc0
+#define PLLU_OUTA 0xc4
+#define PLLU_MISC0 0xcc
+#define PLLU_MISC1 0xc8
+#define PLLX_BASE 0xe0
+#define PLLX_MISC0 0xe4
+#define PLLX_MISC1 0x510
+#define PLLX_MISC2 0x514
+#define PLLX_MISC3 0x518
+#define PLLX_MISC4 0x5f0
+#define PLLX_MISC5 0x5f4
+#define PLLE_BASE 0xe8
+#define PLLE_MISC0 0xec
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC0 0x4bc
+#define PLLD2_MISC1 0x570
+#define PLLD2_MISC2 0x574
+#define PLLD2_MISC3 0x578
+#define PLLE_AUX 0x48c
+#define PLLRE_BASE 0x4c4
+#define PLLRE_MISC0 0x4c8
+#define PLLRE_OUT1 0x4cc
+#define PLLDP_BASE 0x590
+#define PLLDP_MISC 0x594
+
+#define PLLC4_BASE 0x5a4
+#define PLLC4_MISC0 0x5a8
+#define PLLC4_OUT 0x5e4
+#define PLLMB_BASE 0x5e8
+#define PLLMB_MISC1 0x5ec
+#define PLLA1_BASE 0x6a4
+#define PLLA1_MISC0 0x6a8
+#define PLLA1_MISC1 0x6ac
+#define PLLA1_MISC2 0x6b0
+#define PLLA1_MISC3 0x6b4
+
+#define PLLU_IDDQ_BIT 31
+#define PLLCX_IDDQ_BIT 27
+#define PLLRE_IDDQ_BIT 24
+#define PLLA_IDDQ_BIT 25
+#define PLLD_IDDQ_BIT 20
+#define PLLSS_IDDQ_BIT 18
+#define PLLM_IDDQ_BIT 5
+#define PLLMB_IDDQ_BIT 17
+#define PLLXP_IDDQ_BIT 3
+
+#define PLLCX_RESET_BIT 30
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLCX_BASE_LOCK BIT(26)
+#define PLLE_MISC_LOCK BIT(11)
+#define PLLRE_MISC_LOCK BIT(27)
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLC_MISC_LOCK_ENABLE 24
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLU_MISC_LOCK_ENABLE 29
+#define PLLE_MISC_LOCK_ENABLE 9
+#define PLLRE_MISC_LOCK_ENABLE 30
+#define PLLSS_MISC_LOCK_ENABLE 30
+#define PLLP_MISC_LOCK_ENABLE 18
+#define PLLM_MISC_LOCK_ENABLE 4
+#define PLLMB_MISC_LOCK_ENABLE 16
+#define PLLA_MISC_LOCK_ENABLE 28
+#define PLLU_MISC_LOCK_ENABLE 29
+#define PLLD_MISC_LOCK_ENABLE 18
+
+#define PLLA_SDM_DIN_MASK 0xffff
+#define PLLA_SDM_EN_MASK BIT(26)
+
+#define PLLD_SDM_EN_MASK BIT(16)
+
+#define PLLD2_SDM_EN_MASK BIT(31)
+#define PLLD2_SSC_EN_MASK 0
+
+#define PLLDP_SS_CFG 0x598
+#define PLLDP_SDM_EN_MASK BIT(31)
+#define PLLDP_SSC_EN_MASK BIT(30)
+#define PLLDP_SS_CTRL1 0x59c
+#define PLLDP_SS_CTRL2 0x5a0
+
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0
+
+#define UTMIP_PLL_CFG2 0x488
+#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN BIT(0)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP BIT(1)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN BIT(2)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP BIT(3)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN BIT(4)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERUP BIT(5)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN BIT(24)
+#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP BIT(25)
+
+#define UTMIP_PLL_CFG1 0x484
+#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
+#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERUP BIT(17)
+#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN BIT(16)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP BIT(15)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
+#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
+
+#define SATA_PLL_CFG0 0x490
+#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
+#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2)
+#define SATA_PLL_CFG0_SATA_SEQ_IN_SWCTL BIT(4)
+#define SATA_PLL_CFG0_SATA_SEQ_RESET_INPUT_VALUE BIT(5)
+#define SATA_PLL_CFG0_SATA_SEQ_LANE_PD_INPUT_VALUE BIT(6)
+#define SATA_PLL_CFG0_SATA_SEQ_PADPLL_PD_INPUT_VALUE BIT(7)
+
+#define SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13)
+#define SATA_PLL_CFG0_SEQ_ENABLE BIT(24)
+
+#define XUSBIO_PLL_CFG0 0x51c
+#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
+#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6)
+#define XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13)
+#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24)
+
+#define UTMIPLL_HW_PWRDN_CFG0 0x52c
+#define UTMIPLL_HW_PWRDN_CFG0_UTMIPLL_LOCK BIT(31)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE BIT(7)
+#define UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_RESET_INPUT_VALUE BIT(5)
+#define UTMIPLL_HW_PWRDN_CFG0_SEQ_IN_SWCTL BIT(4)
+#define UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE BIT(1)
+#define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0)
+
+#define PLLU_HW_PWRDN_CFG0 0x530
+#define PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE BIT(28)
+#define PLLU_HW_PWRDN_CFG0_SEQ_ENABLE BIT(24)
+#define PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT BIT(7)
+#define PLLU_HW_PWRDN_CFG0_USE_LOCKDET BIT(6)
+#define PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL BIT(2)
+#define PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL BIT(0)
+
+#define XUSB_PLL_CFG0 0x534
+#define XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY 0x3ff
+#define XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK (0x3ff << 14)
+
+#define SPARE_REG0 0x55c
+#define CLK_M_DIVISOR_SHIFT 2
+#define CLK_M_DIVISOR_MASK 0x3
+
+#define CLK_MASK_ARM 0x44
+#define MISC_CLK_ENB 0x48
+
+#define RST_DFLL_DVCO 0x2f4
+#define DVFS_DFLL_RESET_SHIFT 0
+
+#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X_SET 0x284
+#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X_CLR 0x288
+#define CLK_OUT_ENB_X_CLK_ENB_EMC_DLL BIT(14)
+
+#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
+#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+#define CPU_SOFTRST_CTRL 0x380
+
+#define LVL2_CLK_GATE_OVRA 0xf8
+#define LVL2_CLK_GATE_OVRC 0x3a0
+#define LVL2_CLK_GATE_OVRD 0x3a4
+#define LVL2_CLK_GATE_OVRE 0x554
+
+/* I2S registers to handle during APE MBIST WAR */
+#define TEGRA210_I2S_BASE 0x1000
+#define TEGRA210_I2S_SIZE 0x100
+#define TEGRA210_I2S_CTRLS 5
+#define TEGRA210_I2S_CG 0x88
+#define TEGRA210_I2S_CTRL 0xa0
+
+/* DISPA registers to handle during MBIST WAR */
+#define DC_CMD_DISPLAY_COMMAND 0xc8
+#define DC_COM_DSC_TOP_CTL 0xcf8
+
+/* VIC register to handle during MBIST WAR */
+#define NV_PVIC_THI_SLCG_OVERRIDE_LOW 0x8c
+
+/* APE, DISPA and VIC base addresses needed for MBIST WAR */
+#define TEGRA210_AHUB_BASE 0x702d0000
+#define TEGRA210_DISPA_BASE 0x54200000
+#define TEGRA210_VIC_BASE 0x54340000
+
+/*
+ * SDM fractional divisor is 16-bit 2's complement signed number within
+ * (-2^12 ... 2^12-1) range. Represented in PLL data structure as unsigned
+ * 16-bit value, with "0" divisor mapped to 0xFFFF. Data "0" is used to
+ * indicate that SDM is disabled.
+ *
+ * Effective ndiv value when SDM is enabled: ndiv + 1/2 + sdm_din/2^13
+ */
+#define PLL_SDM_COEFF BIT(13)
+#define sdin_din_to_data(din) ((u16)((din) ? : 0xFFFFU))
+#define sdin_data_to_din(dat) (((dat) == 0xFFFFU) ? 0 : (s16)dat)
+/* This macro returns ndiv effective scaled to SDM range */
+#define sdin_get_n_eff(cfg) ((cfg)->n * PLL_SDM_COEFF + ((cfg)->sdm_data ? \
+ (PLL_SDM_COEFF/2 + sdin_data_to_din((cfg)->sdm_data)) : 0))
+
+/* Tegra CPU clock and reset control regs */
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 clk_csite_src;
+} tegra210_cpu_clk_sctx;
+#endif
+
+struct tegra210_domain_mbist_war {
+ void (*handle_lvl2_ovr)(struct tegra210_domain_mbist_war *mbist);
+ const u32 lvl2_offset;
+ const u32 lvl2_mask;
+ const unsigned int num_clks;
+ const unsigned int *clk_init_data;
+ struct clk_bulk_data *clks;
+};
+
+static struct clk **clks;
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+static void __iomem *ahub_base;
+static void __iomem *dispa_base;
+static void __iomem *vic_base;
+
+static unsigned long osc_freq;
+static unsigned long pll_ref_freq;
+
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_e_lock);
+static DEFINE_SPINLOCK(pll_re_lock);
+static DEFINE_SPINLOCK(pll_u_lock);
+static DEFINE_SPINLOCK(sor0_lock);
+static DEFINE_SPINLOCK(sor1_lock);
+static DEFINE_SPINLOCK(emc_lock);
+static DEFINE_MUTEX(lvl2_ovr_lock);
+
+/* possible OSC frequencies in Hz */
+static unsigned long tegra210_input_freq[] = {
+ [5] = 38400000,
+ [8] = 12000000,
+};
+
+#define PLL_ENABLE (1 << 30)
+
+#define PLLCX_MISC1_IDDQ (1 << 27)
+#define PLLCX_MISC0_RESET (1 << 30)
+
+#define PLLCX_MISC0_DEFAULT_VALUE 0x40080000
+#define PLLCX_MISC0_WRITE_MASK 0x400ffffb
+#define PLLCX_MISC1_DEFAULT_VALUE 0x08000000
+#define PLLCX_MISC1_WRITE_MASK 0x08003cff
+#define PLLCX_MISC2_DEFAULT_VALUE 0x1f720f05
+#define PLLCX_MISC2_WRITE_MASK 0xffffff17
+#define PLLCX_MISC3_DEFAULT_VALUE 0x000000c4
+#define PLLCX_MISC3_WRITE_MASK 0x00ffffff
+
+/* PLLA */
+#define PLLA_BASE_IDDQ (1 << 25)
+#define PLLA_BASE_LOCK (1 << 27)
+
+#define PLLA_MISC0_LOCK_ENABLE (1 << 28)
+#define PLLA_MISC0_LOCK_OVERRIDE (1 << 27)
+
+#define PLLA_MISC2_EN_SDM (1 << 26)
+#define PLLA_MISC2_EN_DYNRAMP (1 << 25)
+
+#define PLLA_MISC0_DEFAULT_VALUE 0x12000020
+#define PLLA_MISC0_WRITE_MASK 0x7fffffff
+#define PLLA_MISC2_DEFAULT_VALUE 0x0
+#define PLLA_MISC2_WRITE_MASK 0x06ffffff
+
+/* PLLD */
+#define PLLD_BASE_CSI_CLKSOURCE (1 << 23)
+
+#define PLLD_MISC0_EN_SDM (1 << 16)
+#define PLLD_MISC0_LOCK_OVERRIDE (1 << 17)
+#define PLLD_MISC0_LOCK_ENABLE (1 << 18)
+#define PLLD_MISC0_IDDQ (1 << 20)
+#define PLLD_MISC0_DSI_CLKENABLE (1 << 21)
+
+#define PLLD_MISC0_DEFAULT_VALUE 0x00140000
+#define PLLD_MISC0_WRITE_MASK 0x3ff7ffff
+#define PLLD_MISC1_DEFAULT_VALUE 0x20
+#define PLLD_MISC1_WRITE_MASK 0x00ffffff
+
+/* PLLD2 and PLLDP and PLLC4 */
+#define PLLDSS_BASE_LOCK (1 << 27)
+#define PLLDSS_BASE_LOCK_OVERRIDE (1 << 24)
+#define PLLDSS_BASE_IDDQ (1 << 18)
+#define PLLDSS_BASE_REF_SEL_SHIFT 25
+#define PLLDSS_BASE_REF_SEL_MASK (0x3 << PLLDSS_BASE_REF_SEL_SHIFT)
+
+#define PLLDSS_MISC0_LOCK_ENABLE (1 << 30)
+
+#define PLLDSS_MISC1_CFG_EN_SDM (1 << 31)
+#define PLLDSS_MISC1_CFG_EN_SSC (1 << 30)
+
+#define PLLD2_MISC0_DEFAULT_VALUE 0x40000020
+#define PLLD2_MISC1_CFG_DEFAULT_VALUE 0x10000000
+#define PLLD2_MISC2_CTRL1_DEFAULT_VALUE 0x0
+#define PLLD2_MISC3_CTRL2_DEFAULT_VALUE 0x0
+
+#define PLLDP_MISC0_DEFAULT_VALUE 0x40000020
+#define PLLDP_MISC1_CFG_DEFAULT_VALUE 0xc0000000
+#define PLLDP_MISC2_CTRL1_DEFAULT_VALUE 0xf400f0da
+#define PLLDP_MISC3_CTRL2_DEFAULT_VALUE 0x2004f400
+
+#define PLLDSS_MISC0_WRITE_MASK 0x47ffffff
+#define PLLDSS_MISC1_CFG_WRITE_MASK 0xf8000000
+#define PLLDSS_MISC2_CTRL1_WRITE_MASK 0xffffffff
+#define PLLDSS_MISC3_CTRL2_WRITE_MASK 0xffffffff
+
+#define PLLC4_MISC0_DEFAULT_VALUE 0x40000000
+
+/* PLLRE */
+#define PLLRE_MISC0_LOCK_ENABLE (1 << 30)
+#define PLLRE_MISC0_LOCK_OVERRIDE (1 << 29)
+#define PLLRE_MISC0_LOCK (1 << 27)
+#define PLLRE_MISC0_IDDQ (1 << 24)
+
+#define PLLRE_BASE_DEFAULT_VALUE 0x0
+#define PLLRE_MISC0_DEFAULT_VALUE 0x41000000
+
+#define PLLRE_BASE_DEFAULT_MASK 0x1c000000
+#define PLLRE_MISC0_WRITE_MASK 0x67ffffff
+
+/* PLLE */
+#define PLLE_MISC_IDDQ_SW_CTRL (1 << 14)
+#define PLLE_AUX_USE_LOCKDET (1 << 3)
+#define PLLE_AUX_SS_SEQ_INCLUDE (1 << 31)
+#define PLLE_AUX_ENABLE_SWCTL (1 << 4)
+#define PLLE_AUX_SS_SWCTL (1 << 6)
+#define PLLE_AUX_SEQ_ENABLE (1 << 24)
+
+/* PLLX */
+#define PLLX_USE_DYN_RAMP 1
+#define PLLX_BASE_LOCK (1 << 27)
+
+#define PLLX_MISC0_FO_G_DISABLE (0x1 << 28)
+#define PLLX_MISC0_LOCK_ENABLE (0x1 << 18)
+
+#define PLLX_MISC2_DYNRAMP_STEPB_SHIFT 24
+#define PLLX_MISC2_DYNRAMP_STEPB_MASK (0xFF << PLLX_MISC2_DYNRAMP_STEPB_SHIFT)
+#define PLLX_MISC2_DYNRAMP_STEPA_SHIFT 16
+#define PLLX_MISC2_DYNRAMP_STEPA_MASK (0xFF << PLLX_MISC2_DYNRAMP_STEPA_SHIFT)
+#define PLLX_MISC2_NDIV_NEW_SHIFT 8
+#define PLLX_MISC2_NDIV_NEW_MASK (0xFF << PLLX_MISC2_NDIV_NEW_SHIFT)
+#define PLLX_MISC2_LOCK_OVERRIDE (0x1 << 4)
+#define PLLX_MISC2_DYNRAMP_DONE (0x1 << 2)
+#define PLLX_MISC2_EN_DYNRAMP (0x1 << 0)
+
+#define PLLX_MISC3_IDDQ (0x1 << 3)
+
+#define PLLX_MISC0_DEFAULT_VALUE PLLX_MISC0_LOCK_ENABLE
+#define PLLX_MISC0_WRITE_MASK 0x10c40000
+#define PLLX_MISC1_DEFAULT_VALUE 0x20
+#define PLLX_MISC1_WRITE_MASK 0x00ffffff
+#define PLLX_MISC2_DEFAULT_VALUE 0x0
+#define PLLX_MISC2_WRITE_MASK 0xffffff11
+#define PLLX_MISC3_DEFAULT_VALUE PLLX_MISC3_IDDQ
+#define PLLX_MISC3_WRITE_MASK 0x01ff0f0f
+#define PLLX_MISC4_DEFAULT_VALUE 0x0
+#define PLLX_MISC4_WRITE_MASK 0x8000ffff
+#define PLLX_MISC5_DEFAULT_VALUE 0x0
+#define PLLX_MISC5_WRITE_MASK 0x0000ffff
+
+#define PLLX_HW_CTRL_CFG 0x548
+#define PLLX_HW_CTRL_CFG_SWCTRL (0x1 << 0)
+
+/* PLLMB */
+#define PLLMB_BASE_LOCK (1 << 27)
+
+#define PLLMB_MISC1_LOCK_OVERRIDE (1 << 18)
+#define PLLMB_MISC1_IDDQ (1 << 17)
+#define PLLMB_MISC1_LOCK_ENABLE (1 << 16)
+
+#define PLLMB_MISC1_DEFAULT_VALUE 0x00030000
+#define PLLMB_MISC1_WRITE_MASK 0x0007ffff
+
+/* PLLP */
+#define PLLP_BASE_OVERRIDE (1 << 28)
+#define PLLP_BASE_LOCK (1 << 27)
+
+#define PLLP_MISC0_LOCK_ENABLE (1 << 18)
+#define PLLP_MISC0_LOCK_OVERRIDE (1 << 17)
+#define PLLP_MISC0_IDDQ (1 << 3)
+
+#define PLLP_MISC1_HSIO_EN_SHIFT 29
+#define PLLP_MISC1_HSIO_EN (1 << PLLP_MISC1_HSIO_EN_SHIFT)
+#define PLLP_MISC1_XUSB_EN_SHIFT 28
+#define PLLP_MISC1_XUSB_EN (1 << PLLP_MISC1_XUSB_EN_SHIFT)
+
+#define PLLP_MISC0_DEFAULT_VALUE 0x00040008
+#define PLLP_MISC1_DEFAULT_VALUE 0x0
+
+#define PLLP_MISC0_WRITE_MASK 0xdc6000f
+#define PLLP_MISC1_WRITE_MASK 0x70ffffff
+
+/* PLLU */
+#define PLLU_BASE_LOCK (1 << 27)
+#define PLLU_BASE_OVERRIDE (1 << 24)
+#define PLLU_BASE_CLKENABLE_USB (1 << 21)
+#define PLLU_BASE_CLKENABLE_HSIC (1 << 22)
+#define PLLU_BASE_CLKENABLE_ICUSB (1 << 23)
+#define PLLU_BASE_CLKENABLE_48M (1 << 25)
+#define PLLU_BASE_CLKENABLE_ALL (PLLU_BASE_CLKENABLE_USB |\
+ PLLU_BASE_CLKENABLE_HSIC |\
+ PLLU_BASE_CLKENABLE_ICUSB |\
+ PLLU_BASE_CLKENABLE_48M)
+
+#define PLLU_MISC0_IDDQ (1 << 31)
+#define PLLU_MISC0_LOCK_ENABLE (1 << 29)
+#define PLLU_MISC1_LOCK_OVERRIDE (1 << 0)
+
+#define PLLU_MISC0_DEFAULT_VALUE 0xa0000000
+#define PLLU_MISC1_DEFAULT_VALUE 0x0
+
+#define PLLU_MISC0_WRITE_MASK 0xbfffffff
+#define PLLU_MISC1_WRITE_MASK 0x00000007
+
+bool tegra210_plle_hw_sequence_is_enabled(void)
+{
+ u32 value;
+
+ value = readl_relaxed(clk_base + PLLE_AUX);
+ if (value & PLLE_AUX_SEQ_ENABLE)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_is_enabled);
+
+int tegra210_plle_hw_sequence_start(void)
+{
+ u32 value;
+
+ if (tegra210_plle_hw_sequence_is_enabled())
+ return 0;
+
+ /* skip if PLLE is not enabled yet */
+ value = readl_relaxed(clk_base + PLLE_MISC0);
+ if (!(value & PLLE_MISC_LOCK))
+ return -EIO;
+
+ value &= ~PLLE_MISC_IDDQ_SW_CTRL;
+ writel_relaxed(value, clk_base + PLLE_MISC0);
+
+ value = readl_relaxed(clk_base + PLLE_AUX);
+ value |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SS_SEQ_INCLUDE);
+ value &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL);
+ writel_relaxed(value, clk_base + PLLE_AUX);
+
+ fence_udelay(1, clk_base);
+
+ value |= PLLE_AUX_SEQ_ENABLE;
+ writel_relaxed(value, clk_base + PLLE_AUX);
+
+ fence_udelay(1, clk_base);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra210_plle_hw_sequence_start);
+
+void tegra210_xusb_pll_hw_control_enable(void)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0);
+ val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL |
+ XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL);
+ val |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET |
+ XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ;
+ writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_control_enable);
+
+void tegra210_xusb_pll_hw_sequence_start(void)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0);
+ val |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
+ writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_sequence_start);
+
+void tegra210_sata_pll_hw_control_enable(void)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + SATA_PLL_CFG0);
+ val &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL;
+ val |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET |
+ SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ;
+ writel_relaxed(val, clk_base + SATA_PLL_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_control_enable);
+
+void tegra210_sata_pll_hw_sequence_start(void)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + SATA_PLL_CFG0);
+ val |= SATA_PLL_CFG0_SEQ_ENABLE;
+ writel_relaxed(val, clk_base + SATA_PLL_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_sequence_start);
+
+void tegra210_set_sata_pll_seq_sw(bool state)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + SATA_PLL_CFG0);
+ if (state) {
+ val |= SATA_PLL_CFG0_SATA_SEQ_IN_SWCTL;
+ val |= SATA_PLL_CFG0_SATA_SEQ_RESET_INPUT_VALUE;
+ val |= SATA_PLL_CFG0_SATA_SEQ_LANE_PD_INPUT_VALUE;
+ val |= SATA_PLL_CFG0_SATA_SEQ_PADPLL_PD_INPUT_VALUE;
+ } else {
+ val &= ~SATA_PLL_CFG0_SATA_SEQ_IN_SWCTL;
+ val &= ~SATA_PLL_CFG0_SATA_SEQ_RESET_INPUT_VALUE;
+ val &= ~SATA_PLL_CFG0_SATA_SEQ_LANE_PD_INPUT_VALUE;
+ val &= ~SATA_PLL_CFG0_SATA_SEQ_PADPLL_PD_INPUT_VALUE;
+ }
+ writel_relaxed(val, clk_base + SATA_PLL_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_set_sata_pll_seq_sw);
+
+void tegra210_clk_emc_dll_enable(bool flag)
+{
+ u32 offset = flag ? CLK_RST_CONTROLLER_CLK_OUT_ENB_X_SET :
+ CLK_RST_CONTROLLER_CLK_OUT_ENB_X_CLR;
+
+ writel_relaxed(CLK_OUT_ENB_X_CLK_ENB_EMC_DLL, clk_base + offset);
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_dll_enable);
+
+void tegra210_clk_emc_dll_update_setting(u32 emc_dll_src_value)
+{
+ writel_relaxed(emc_dll_src_value, clk_base + CLK_SOURCE_EMC_DLL);
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_dll_update_setting);
+
+void tegra210_clk_emc_update_setting(u32 emc_src_value)
+{
+ writel_relaxed(emc_src_value, clk_base + CLK_SOURCE_EMC);
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_update_setting);
+
+static void tegra210_generic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + mbist->lvl2_offset);
+ writel_relaxed(val | mbist->lvl2_mask, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+ writel_relaxed(val, clk_base + mbist->lvl2_offset);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_venc_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 csi_src, ovra, ovre;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pll_d_lock, flags);
+
+ csi_src = readl_relaxed(clk_base + PLLD_BASE);
+ writel_relaxed(csi_src | PLLD_BASE_CSI_CLKSOURCE, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(15), clk_base + LVL2_CLK_GATE_OVRA);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(3), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(csi_src, clk_base + PLLD_BASE);
+ fence_udelay(1, clk_base);
+
+ spin_unlock_irqrestore(&pll_d_lock, flags);
+}
+
+static void tegra210_disp_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovra, dsc_top_ctrl;
+
+ ovra = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRA);
+ writel_relaxed(ovra | BIT(1), clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+
+ dsc_top_ctrl = readl_relaxed(dispa_base + DC_COM_DSC_TOP_CTL);
+ writel_relaxed(dsc_top_ctrl | BIT(2), dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+ writel_relaxed(dsc_top_ctrl, dispa_base + DC_COM_DSC_TOP_CTL);
+ readl_relaxed(dispa_base + DC_CMD_DISPLAY_COMMAND);
+
+ writel_relaxed(ovra, clk_base + LVL2_CLK_GATE_OVRA);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_vic_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ u32 ovre, val;
+
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovre | BIT(5), clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ val = readl_relaxed(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ writel_relaxed(val | BIT(0) | GENMASK(7, 2) | BIT(24),
+ vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ fence_udelay(1, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(val, vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+ readl(vic_base + NV_PVIC_THI_SLCG_OVERRIDE_LOW);
+
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
+static void tegra210_ape_mbist_war(struct tegra210_domain_mbist_war *mbist)
+{
+ void __iomem *i2s_base;
+ unsigned int i;
+ u32 ovrc, ovre;
+
+ ovrc = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRC);
+ ovre = readl_relaxed(clk_base + LVL2_CLK_GATE_OVRE);
+ writel_relaxed(ovrc | BIT(1), clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre | BIT(10) | BIT(11),
+ clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+
+ i2s_base = ahub_base + TEGRA210_I2S_BASE;
+
+ for (i = 0; i < TEGRA210_I2S_CTRLS; i++) {
+ u32 i2s_ctrl;
+
+ i2s_ctrl = readl_relaxed(i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(i2s_ctrl | BIT(10),
+ i2s_base + TEGRA210_I2S_CTRL);
+ writel_relaxed(0, i2s_base + TEGRA210_I2S_CG);
+ readl(i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(1, i2s_base + TEGRA210_I2S_CG);
+ writel_relaxed(i2s_ctrl, i2s_base + TEGRA210_I2S_CTRL);
+ readl(i2s_base + TEGRA210_I2S_CTRL);
+
+ i2s_base += TEGRA210_I2S_SIZE;
+ }
+
+ writel_relaxed(ovrc, clk_base + LVL2_CLK_GATE_OVRC);
+ writel_relaxed(ovre, clk_base + LVL2_CLK_GATE_OVRE);
+ fence_udelay(1, clk_base);
+}
+
+static inline void _pll_misc_chk_default(void __iomem *base,
+ struct tegra_clk_pll_params *params,
+ u8 misc_num, u32 default_val, u32 mask)
+{
+ u32 boot_val = readl_relaxed(base + params->ext_misc_reg[misc_num]);
+
+ boot_val &= mask;
+ default_val &= mask;
+ if (boot_val != default_val) {
+ pr_warn("boot misc%d 0x%x: expected 0x%x\n",
+ misc_num, boot_val, default_val);
+ pr_warn(" (comparison mask = 0x%x)\n", mask);
+ params->defaults_set = false;
+ }
+}
+
+/*
+ * PLLCX: PLLC, PLLC2, PLLC3, PLLA1
+ * Hybrid PLLs with dynamic ramp. Dynamic ramp is allowed for any transition
+ * that changes NDIV only, while PLL is already locked.
+ */
+static void pllcx_check_defaults(struct tegra_clk_pll_params *params)
+{
+ u32 default_val;
+
+ default_val = PLLCX_MISC0_DEFAULT_VALUE & (~PLLCX_MISC0_RESET);
+ _pll_misc_chk_default(clk_base, params, 0, default_val,
+ PLLCX_MISC0_WRITE_MASK);
+
+ default_val = PLLCX_MISC1_DEFAULT_VALUE & (~PLLCX_MISC1_IDDQ);
+ _pll_misc_chk_default(clk_base, params, 1, default_val,
+ PLLCX_MISC1_WRITE_MASK);
+
+ default_val = PLLCX_MISC2_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, params, 2, default_val,
+ PLLCX_MISC2_WRITE_MASK);
+
+ default_val = PLLCX_MISC3_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, params, 3, default_val,
+ PLLCX_MISC3_WRITE_MASK);
+}
+
+static void tegra210_pllcx_set_defaults(const char *name,
+ struct tegra_clk_pll *pllcx)
+{
+ pllcx->params->defaults_set = true;
+
+ if (readl_relaxed(clk_base + pllcx->params->base_reg) & PLL_ENABLE) {
+ /* PLL is ON: only check if defaults already set */
+ pllcx_check_defaults(pllcx->params);
+ if (!pllcx->params->defaults_set)
+ pr_warn("%s already enabled. Postponing set full defaults\n",
+ name);
+ return;
+ }
+
+ /* Defaults assert PLL reset, and set IDDQ */
+ writel_relaxed(PLLCX_MISC0_DEFAULT_VALUE,
+ clk_base + pllcx->params->ext_misc_reg[0]);
+ writel_relaxed(PLLCX_MISC1_DEFAULT_VALUE,
+ clk_base + pllcx->params->ext_misc_reg[1]);
+ writel_relaxed(PLLCX_MISC2_DEFAULT_VALUE,
+ clk_base + pllcx->params->ext_misc_reg[2]);
+ writel_relaxed(PLLCX_MISC3_DEFAULT_VALUE,
+ clk_base + pllcx->params->ext_misc_reg[3]);
+ udelay(1);
+}
+
+static void _pllc_set_defaults(struct tegra_clk_pll *pllcx)
+{
+ tegra210_pllcx_set_defaults("PLL_C", pllcx);
+}
+
+static void _pllc2_set_defaults(struct tegra_clk_pll *pllcx)
+{
+ tegra210_pllcx_set_defaults("PLL_C2", pllcx);
+}
+
+static void _pllc3_set_defaults(struct tegra_clk_pll *pllcx)
+{
+ tegra210_pllcx_set_defaults("PLL_C3", pllcx);
+}
+
+static void _plla1_set_defaults(struct tegra_clk_pll *pllcx)
+{
+ tegra210_pllcx_set_defaults("PLL_A1", pllcx);
+}
+
+/*
+ * PLLA
+ * PLL with dynamic ramp and fractional SDM. Dynamic ramp is not used.
+ * Fractional SDM is allowed to provide exact audio rates.
+ */
+static void tegra210_plla_set_defaults(struct tegra_clk_pll *plla)
+{
+ u32 mask;
+ u32 val = readl_relaxed(clk_base + plla->params->base_reg);
+
+ plla->params->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ if (val & PLLA_BASE_IDDQ) {
+ pr_warn("PLL_A boot enabled with IDDQ set\n");
+ plla->params->defaults_set = false;
+ }
+
+ pr_warn("PLL_A already enabled. Postponing set full defaults\n");
+
+ val = PLLA_MISC0_DEFAULT_VALUE; /* ignore lock enable */
+ mask = PLLA_MISC0_LOCK_ENABLE | PLLA_MISC0_LOCK_OVERRIDE;
+ _pll_misc_chk_default(clk_base, plla->params, 0, val,
+ ~mask & PLLA_MISC0_WRITE_MASK);
+
+ val = PLLA_MISC2_DEFAULT_VALUE; /* ignore all but control bit */
+ _pll_misc_chk_default(clk_base, plla->params, 2, val,
+ PLLA_MISC2_EN_DYNRAMP);
+
+ /* Enable lock detect */
+ val = readl_relaxed(clk_base + plla->params->ext_misc_reg[0]);
+ val &= ~mask;
+ val |= PLLA_MISC0_DEFAULT_VALUE & mask;
+ writel_relaxed(val, clk_base + plla->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect, disable dynamic ramp and SDM */
+ val |= PLLA_BASE_IDDQ;
+ writel_relaxed(val, clk_base + plla->params->base_reg);
+ writel_relaxed(PLLA_MISC0_DEFAULT_VALUE,
+ clk_base + plla->params->ext_misc_reg[0]);
+ writel_relaxed(PLLA_MISC2_DEFAULT_VALUE,
+ clk_base + plla->params->ext_misc_reg[2]);
+ udelay(1);
+}
+
+/*
+ * PLLD
+ * PLL with fractional SDM.
+ */
+static void tegra210_plld_set_defaults(struct tegra_clk_pll *plld)
+{
+ u32 val;
+ u32 mask = 0xffff;
+
+ plld->params->defaults_set = true;
+
+ if (readl_relaxed(clk_base + plld->params->base_reg) &
+ PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ val = PLLD_MISC1_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, plld->params, 1,
+ val, PLLD_MISC1_WRITE_MASK);
+
+ /* ignore lock, DSI and SDM controls, make sure IDDQ not set */
+ val = PLLD_MISC0_DEFAULT_VALUE & (~PLLD_MISC0_IDDQ);
+ mask |= PLLD_MISC0_DSI_CLKENABLE | PLLD_MISC0_LOCK_ENABLE |
+ PLLD_MISC0_LOCK_OVERRIDE | PLLD_MISC0_EN_SDM;
+ _pll_misc_chk_default(clk_base, plld->params, 0, val,
+ ~mask & PLLD_MISC0_WRITE_MASK);
+
+ if (!plld->params->defaults_set)
+ pr_warn("PLL_D already enabled. Postponing set full defaults\n");
+
+ /* Enable lock detect */
+ mask = PLLD_MISC0_LOCK_ENABLE | PLLD_MISC0_LOCK_OVERRIDE;
+ val = readl_relaxed(clk_base + plld->params->ext_misc_reg[0]);
+ val &= ~mask;
+ val |= PLLD_MISC0_DEFAULT_VALUE & mask;
+ writel_relaxed(val, clk_base + plld->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ val = readl_relaxed(clk_base + plld->params->ext_misc_reg[0]);
+ val &= PLLD_MISC0_DSI_CLKENABLE;
+ val |= PLLD_MISC0_DEFAULT_VALUE;
+ /* set IDDQ, enable lock detect, disable SDM */
+ writel_relaxed(val, clk_base + plld->params->ext_misc_reg[0]);
+ writel_relaxed(PLLD_MISC1_DEFAULT_VALUE, clk_base +
+ plld->params->ext_misc_reg[1]);
+ udelay(1);
+}
+
+/*
+ * PLLD2, PLLDP
+ * PLL with fractional SDM and Spread Spectrum (SDM is a must if SSC is used).
+ */
+static void plldss_defaults(const char *pll_name, struct tegra_clk_pll *plldss,
+ u32 misc0_val, u32 misc1_val, u32 misc2_val, u32 misc3_val)
+{
+ u32 default_val;
+ u32 val = readl_relaxed(clk_base + plldss->params->base_reg);
+
+ plldss->params->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ if (val & PLLDSS_BASE_IDDQ) {
+ pr_warn("plldss boot enabled with IDDQ set\n");
+ plldss->params->defaults_set = false;
+ }
+
+ /* ignore lock enable */
+ default_val = misc0_val;
+ _pll_misc_chk_default(clk_base, plldss->params, 0, default_val,
+ PLLDSS_MISC0_WRITE_MASK &
+ (~PLLDSS_MISC0_LOCK_ENABLE));
+
+ /*
+ * If SSC is used, check all settings, otherwise just confirm
+ * that SSC is not used on boot as well. Do nothing when using
+ * this function for PLLC4 that has only MISC0.
+ */
+ if (plldss->params->ssc_ctrl_en_mask) {
+ default_val = misc1_val;
+ _pll_misc_chk_default(clk_base, plldss->params, 1,
+ default_val, PLLDSS_MISC1_CFG_WRITE_MASK);
+ default_val = misc2_val;
+ _pll_misc_chk_default(clk_base, plldss->params, 2,
+ default_val, PLLDSS_MISC2_CTRL1_WRITE_MASK);
+ default_val = misc3_val;
+ _pll_misc_chk_default(clk_base, plldss->params, 3,
+ default_val, PLLDSS_MISC3_CTRL2_WRITE_MASK);
+ } else if (plldss->params->ext_misc_reg[1]) {
+ default_val = misc1_val;
+ _pll_misc_chk_default(clk_base, plldss->params, 1,
+ default_val, PLLDSS_MISC1_CFG_WRITE_MASK &
+ (~PLLDSS_MISC1_CFG_EN_SDM));
+ }
+
+ if (!plldss->params->defaults_set)
+ pr_warn("%s already enabled. Postponing set full defaults\n",
+ pll_name);
+
+ /* Enable lock detect */
+ if (val & PLLDSS_BASE_LOCK_OVERRIDE) {
+ val &= ~PLLDSS_BASE_LOCK_OVERRIDE;
+ writel_relaxed(val, clk_base +
+ plldss->params->base_reg);
+ }
+
+ val = readl_relaxed(clk_base + plldss->params->ext_misc_reg[0]);
+ val &= ~PLLDSS_MISC0_LOCK_ENABLE;
+ val |= misc0_val & PLLDSS_MISC0_LOCK_ENABLE;
+ writel_relaxed(val, clk_base + plldss->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect, configure SDM/SSC */
+ val |= PLLDSS_BASE_IDDQ;
+ val &= ~PLLDSS_BASE_LOCK_OVERRIDE;
+ writel_relaxed(val, clk_base + plldss->params->base_reg);
+
+ /* When using this function for PLLC4 exit here */
+ if (!plldss->params->ext_misc_reg[1]) {
+ writel_relaxed(misc0_val, clk_base +
+ plldss->params->ext_misc_reg[0]);
+ udelay(1);
+ return;
+ }
+
+ writel_relaxed(misc0_val, clk_base +
+ plldss->params->ext_misc_reg[0]);
+ /* if SSC used set by 1st enable */
+ writel_relaxed(misc1_val & (~PLLDSS_MISC1_CFG_EN_SSC),
+ clk_base + plldss->params->ext_misc_reg[1]);
+ writel_relaxed(misc2_val, clk_base + plldss->params->ext_misc_reg[2]);
+ writel_relaxed(misc3_val, clk_base + plldss->params->ext_misc_reg[3]);
+ udelay(1);
+}
+
+static void tegra210_plld2_set_defaults(struct tegra_clk_pll *plld2)
+{
+ plldss_defaults("PLL_D2", plld2, PLLD2_MISC0_DEFAULT_VALUE,
+ PLLD2_MISC1_CFG_DEFAULT_VALUE,
+ PLLD2_MISC2_CTRL1_DEFAULT_VALUE,
+ PLLD2_MISC3_CTRL2_DEFAULT_VALUE);
+}
+
+static void tegra210_plldp_set_defaults(struct tegra_clk_pll *plldp)
+{
+ plldss_defaults("PLL_DP", plldp, PLLDP_MISC0_DEFAULT_VALUE,
+ PLLDP_MISC1_CFG_DEFAULT_VALUE,
+ PLLDP_MISC2_CTRL1_DEFAULT_VALUE,
+ PLLDP_MISC3_CTRL2_DEFAULT_VALUE);
+}
+
+/*
+ * PLLC4
+ * Base and misc0 layout is the same as PLLD2/PLLDP, but no SDM/SSC support.
+ * VCO is exposed to the clock tree via fixed 1/3 and 1/5 dividers.
+ */
+static void tegra210_pllc4_set_defaults(struct tegra_clk_pll *pllc4)
+{
+ plldss_defaults("PLL_C4", pllc4, PLLC4_MISC0_DEFAULT_VALUE, 0, 0, 0);
+}
+
+/*
+ * PLLRE
+ * VCO is exposed to the clock tree directly along with post-divider output
+ */
+static void tegra210_pllre_set_defaults(struct tegra_clk_pll *pllre)
+{
+ u32 mask;
+ u32 val = readl_relaxed(clk_base + pllre->params->base_reg);
+
+ pllre->params->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ val &= PLLRE_BASE_DEFAULT_MASK;
+ if (val != PLLRE_BASE_DEFAULT_VALUE) {
+ pr_warn("pllre boot base 0x%x : expected 0x%x\n",
+ val, PLLRE_BASE_DEFAULT_VALUE);
+ pr_warn("(comparison mask = 0x%x)\n",
+ PLLRE_BASE_DEFAULT_MASK);
+ pllre->params->defaults_set = false;
+ }
+
+ /* Ignore lock enable */
+ val = PLLRE_MISC0_DEFAULT_VALUE & (~PLLRE_MISC0_IDDQ);
+ mask = PLLRE_MISC0_LOCK_ENABLE | PLLRE_MISC0_LOCK_OVERRIDE;
+ _pll_misc_chk_default(clk_base, pllre->params, 0, val,
+ ~mask & PLLRE_MISC0_WRITE_MASK);
+
+ /* The PLL doesn't work if it's in IDDQ. */
+ val = readl_relaxed(clk_base + pllre->params->ext_misc_reg[0]);
+ if (val & PLLRE_MISC0_IDDQ)
+ pr_warn("unexpected IDDQ bit set for enabled clock\n");
+
+ /* Enable lock detect */
+ val &= ~mask;
+ val |= PLLRE_MISC0_DEFAULT_VALUE & mask;
+ writel_relaxed(val, clk_base + pllre->params->ext_misc_reg[0]);
+ udelay(1);
+
+ if (!pllre->params->defaults_set)
+ pr_warn("PLL_RE already enabled. Postponing set full defaults\n");
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect */
+ val &= ~PLLRE_BASE_DEFAULT_MASK;
+ val |= PLLRE_BASE_DEFAULT_VALUE & PLLRE_BASE_DEFAULT_MASK;
+ writel_relaxed(val, clk_base + pllre->params->base_reg);
+ writel_relaxed(PLLRE_MISC0_DEFAULT_VALUE,
+ clk_base + pllre->params->ext_misc_reg[0]);
+ udelay(1);
+}
+
+static void pllx_get_dyn_steps(struct clk_hw *hw, u32 *step_a, u32 *step_b)
+{
+ unsigned long input_rate;
+
+ /* cf rate */
+ if (!IS_ERR_OR_NULL(hw->clk))
+ input_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
+ else
+ input_rate = 38400000;
+
+ input_rate /= tegra_pll_get_fixed_mdiv(hw, input_rate);
+
+ switch (input_rate) {
+ case 12000000:
+ case 12800000:
+ case 13000000:
+ *step_a = 0x2B;
+ *step_b = 0x0B;
+ return;
+ case 19200000:
+ *step_a = 0x12;
+ *step_b = 0x08;
+ return;
+ case 38400000:
+ *step_a = 0x04;
+ *step_b = 0x05;
+ return;
+ default:
+ pr_err("%s: Unexpected reference rate %lu\n",
+ __func__, input_rate);
+ BUG();
+ }
+}
+
+static void pllx_check_defaults(struct tegra_clk_pll *pll)
+{
+ u32 default_val;
+
+ default_val = PLLX_MISC0_DEFAULT_VALUE;
+ /* ignore lock enable */
+ _pll_misc_chk_default(clk_base, pll->params, 0, default_val,
+ PLLX_MISC0_WRITE_MASK & (~PLLX_MISC0_LOCK_ENABLE));
+
+ default_val = PLLX_MISC1_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, pll->params, 1, default_val,
+ PLLX_MISC1_WRITE_MASK);
+
+ /* ignore all but control bit */
+ default_val = PLLX_MISC2_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, pll->params, 2,
+ default_val, PLLX_MISC2_EN_DYNRAMP);
+
+ default_val = PLLX_MISC3_DEFAULT_VALUE & (~PLLX_MISC3_IDDQ);
+ _pll_misc_chk_default(clk_base, pll->params, 3, default_val,
+ PLLX_MISC3_WRITE_MASK);
+
+ default_val = PLLX_MISC4_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, pll->params, 4, default_val,
+ PLLX_MISC4_WRITE_MASK);
+
+ default_val = PLLX_MISC5_DEFAULT_VALUE;
+ _pll_misc_chk_default(clk_base, pll->params, 5, default_val,
+ PLLX_MISC5_WRITE_MASK);
+}
+
+static void tegra210_pllx_set_defaults(struct tegra_clk_pll *pllx)
+{
+ u32 val;
+ u32 step_a, step_b;
+
+ pllx->params->defaults_set = true;
+
+ /* Get ready dyn ramp state machine settings */
+ pllx_get_dyn_steps(&pllx->hw, &step_a, &step_b);
+ val = PLLX_MISC2_DEFAULT_VALUE & (~PLLX_MISC2_DYNRAMP_STEPA_MASK) &
+ (~PLLX_MISC2_DYNRAMP_STEPB_MASK);
+ val |= step_a << PLLX_MISC2_DYNRAMP_STEPA_SHIFT;
+ val |= step_b << PLLX_MISC2_DYNRAMP_STEPB_SHIFT;
+
+ if (readl_relaxed(clk_base + pllx->params->base_reg) & PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ pllx_check_defaults(pllx);
+
+ if (!pllx->params->defaults_set)
+ pr_warn("PLL_X already enabled. Postponing set full defaults\n");
+ /* Configure dyn ramp, disable lock override */
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[2]);
+
+ /* Enable lock detect */
+ val = readl_relaxed(clk_base + pllx->params->ext_misc_reg[0]);
+ val &= ~PLLX_MISC0_LOCK_ENABLE;
+ val |= PLLX_MISC0_DEFAULT_VALUE & PLLX_MISC0_LOCK_ENABLE;
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ /* Enable lock detect and CPU output */
+ writel_relaxed(PLLX_MISC0_DEFAULT_VALUE, clk_base +
+ pllx->params->ext_misc_reg[0]);
+
+ /* Setup */
+ writel_relaxed(PLLX_MISC1_DEFAULT_VALUE, clk_base +
+ pllx->params->ext_misc_reg[1]);
+
+ /* Configure dyn ramp state machine, disable lock override */
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[2]);
+
+ /* Set IDDQ */
+ writel_relaxed(PLLX_MISC3_DEFAULT_VALUE, clk_base +
+ pllx->params->ext_misc_reg[3]);
+
+ /* Disable SDM */
+ writel_relaxed(PLLX_MISC4_DEFAULT_VALUE, clk_base +
+ pllx->params->ext_misc_reg[4]);
+ writel_relaxed(PLLX_MISC5_DEFAULT_VALUE, clk_base +
+ pllx->params->ext_misc_reg[5]);
+ udelay(1);
+}
+
+/* PLLMB */
+static void tegra210_pllmb_set_defaults(struct tegra_clk_pll *pllmb)
+{
+ u32 mask, val = readl_relaxed(clk_base + pllmb->params->base_reg);
+
+ pllmb->params->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ val = PLLMB_MISC1_DEFAULT_VALUE & (~PLLMB_MISC1_IDDQ);
+ mask = PLLMB_MISC1_LOCK_ENABLE | PLLMB_MISC1_LOCK_OVERRIDE;
+ _pll_misc_chk_default(clk_base, pllmb->params, 0, val,
+ ~mask & PLLMB_MISC1_WRITE_MASK);
+
+ if (!pllmb->params->defaults_set)
+ pr_warn("PLL_MB already enabled. Postponing set full defaults\n");
+ /* Enable lock detect */
+ val = readl_relaxed(clk_base + pllmb->params->ext_misc_reg[0]);
+ val &= ~mask;
+ val |= PLLMB_MISC1_DEFAULT_VALUE & mask;
+ writel_relaxed(val, clk_base + pllmb->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect */
+ writel_relaxed(PLLMB_MISC1_DEFAULT_VALUE,
+ clk_base + pllmb->params->ext_misc_reg[0]);
+ udelay(1);
+}
+
+/*
+ * PLLP
+ * VCO is exposed to the clock tree directly along with post-divider output.
+ * Both VCO and post-divider output rates are fixed at 408MHz and 204MHz,
+ * respectively.
+ */
+static void pllp_check_defaults(struct tegra_clk_pll *pll, bool enabled)
+{
+ u32 val, mask;
+
+ /* Ignore lock enable (will be set), make sure not in IDDQ if enabled */
+ val = PLLP_MISC0_DEFAULT_VALUE & (~PLLP_MISC0_IDDQ);
+ mask = PLLP_MISC0_LOCK_ENABLE | PLLP_MISC0_LOCK_OVERRIDE;
+ if (!enabled)
+ mask |= PLLP_MISC0_IDDQ;
+ _pll_misc_chk_default(clk_base, pll->params, 0, val,
+ ~mask & PLLP_MISC0_WRITE_MASK);
+
+ /* Ignore branch controls */
+ val = PLLP_MISC1_DEFAULT_VALUE;
+ mask = PLLP_MISC1_HSIO_EN | PLLP_MISC1_XUSB_EN;
+ _pll_misc_chk_default(clk_base, pll->params, 1, val,
+ ~mask & PLLP_MISC1_WRITE_MASK);
+}
+
+static void tegra210_pllp_set_defaults(struct tegra_clk_pll *pllp)
+{
+ u32 mask;
+ u32 val = readl_relaxed(clk_base + pllp->params->base_reg);
+
+ pllp->params->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ pllp_check_defaults(pllp, true);
+ if (!pllp->params->defaults_set)
+ pr_warn("PLL_P already enabled. Postponing set full defaults\n");
+
+ /* Enable lock detect */
+ val = readl_relaxed(clk_base + pllp->params->ext_misc_reg[0]);
+ mask = PLLP_MISC0_LOCK_ENABLE | PLLP_MISC0_LOCK_OVERRIDE;
+ val &= ~mask;
+ val |= PLLP_MISC0_DEFAULT_VALUE & mask;
+ writel_relaxed(val, clk_base + pllp->params->ext_misc_reg[0]);
+ udelay(1);
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect */
+ writel_relaxed(PLLP_MISC0_DEFAULT_VALUE,
+ clk_base + pllp->params->ext_misc_reg[0]);
+
+ /* Preserve branch control */
+ val = readl_relaxed(clk_base + pllp->params->ext_misc_reg[1]);
+ mask = PLLP_MISC1_HSIO_EN | PLLP_MISC1_XUSB_EN;
+ val &= mask;
+ val |= ~mask & PLLP_MISC1_DEFAULT_VALUE;
+ writel_relaxed(val, clk_base + pllp->params->ext_misc_reg[1]);
+ udelay(1);
+}
+
+/*
+ * PLLU
+ * VCO is exposed to the clock tree directly along with post-divider output.
+ * Both VCO and post-divider output rates are fixed at 480MHz and 240MHz,
+ * respectively.
+ */
+static void pllu_check_defaults(struct tegra_clk_pll_params *params,
+ bool hw_control)
+{
+ u32 val, mask;
+
+ /* Ignore lock enable (will be set) and IDDQ if under h/w control */
+ val = PLLU_MISC0_DEFAULT_VALUE & (~PLLU_MISC0_IDDQ);
+ mask = PLLU_MISC0_LOCK_ENABLE | (hw_control ? PLLU_MISC0_IDDQ : 0);
+ _pll_misc_chk_default(clk_base, params, 0, val,
+ ~mask & PLLU_MISC0_WRITE_MASK);
+
+ val = PLLU_MISC1_DEFAULT_VALUE;
+ mask = PLLU_MISC1_LOCK_OVERRIDE;
+ _pll_misc_chk_default(clk_base, params, 1, val,
+ ~mask & PLLU_MISC1_WRITE_MASK);
+}
+
+static void tegra210_pllu_set_defaults(struct tegra_clk_pll_params *pllu)
+{
+ u32 val = readl_relaxed(clk_base + pllu->base_reg);
+
+ pllu->defaults_set = true;
+
+ if (val & PLL_ENABLE) {
+
+ /*
+ * PLL is ON: check if defaults already set, then set those
+ * that can be updated in flight.
+ */
+ pllu_check_defaults(pllu, false);
+ if (!pllu->defaults_set)
+ pr_warn("PLL_U already enabled. Postponing set full defaults\n");
+
+ /* Enable lock detect */
+ val = readl_relaxed(clk_base + pllu->ext_misc_reg[0]);
+ val &= ~PLLU_MISC0_LOCK_ENABLE;
+ val |= PLLU_MISC0_DEFAULT_VALUE & PLLU_MISC0_LOCK_ENABLE;
+ writel_relaxed(val, clk_base + pllu->ext_misc_reg[0]);
+
+ val = readl_relaxed(clk_base + pllu->ext_misc_reg[1]);
+ val &= ~PLLU_MISC1_LOCK_OVERRIDE;
+ val |= PLLU_MISC1_DEFAULT_VALUE & PLLU_MISC1_LOCK_OVERRIDE;
+ writel_relaxed(val, clk_base + pllu->ext_misc_reg[1]);
+ udelay(1);
+
+ return;
+ }
+
+ /* set IDDQ, enable lock detect */
+ writel_relaxed(PLLU_MISC0_DEFAULT_VALUE,
+ clk_base + pllu->ext_misc_reg[0]);
+ writel_relaxed(PLLU_MISC1_DEFAULT_VALUE,
+ clk_base + pllu->ext_misc_reg[1]);
+ udelay(1);
+}
+
+#define mask(w) ((1 << (w)) - 1)
+#define divm_mask(p) mask(p->params->div_nmp->divm_width)
+#define divn_mask(p) mask(p->params->div_nmp->divn_width)
+#define divp_mask(p) (p->params->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK :\
+ mask(p->params->div_nmp->divp_width))
+
+#define divm_shift(p) ((p)->params->div_nmp->divm_shift)
+#define divn_shift(p) ((p)->params->div_nmp->divn_shift)
+#define divp_shift(p) ((p)->params->div_nmp->divp_shift)
+
+#define divm_mask_shifted(p) (divm_mask(p) << divm_shift(p))
+#define divn_mask_shifted(p) (divn_mask(p) << divn_shift(p))
+#define divp_mask_shifted(p) (divp_mask(p) << divp_shift(p))
+
+#define PLL_LOCKDET_DELAY 2 /* Lock detection safety delays */
+static int tegra210_wait_for_mask(struct tegra_clk_pll *pll,
+ u32 reg, u32 mask)
+{
+ int i;
+ u32 val = 0;
+
+ for (i = 0; i < pll->params->lock_delay / PLL_LOCKDET_DELAY + 1; i++) {
+ udelay(PLL_LOCKDET_DELAY);
+ val = readl_relaxed(clk_base + reg);
+ if ((val & mask) == mask) {
+ udelay(PLL_LOCKDET_DELAY);
+ return 0;
+ }
+ }
+ return -ETIMEDOUT;
+}
+
+static int tegra210_pllx_dyn_ramp(struct tegra_clk_pll *pllx,
+ struct tegra_clk_pll_freq_table *cfg)
+{
+ u32 val, base, ndiv_new_mask;
+
+ ndiv_new_mask = (divn_mask(pllx) >> pllx->params->div_nmp->divn_shift)
+ << PLLX_MISC2_NDIV_NEW_SHIFT;
+
+ val = readl_relaxed(clk_base + pllx->params->ext_misc_reg[2]);
+ val &= (~ndiv_new_mask);
+ val |= cfg->n << PLLX_MISC2_NDIV_NEW_SHIFT;
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[2]);
+ udelay(1);
+
+ val = readl_relaxed(clk_base + pllx->params->ext_misc_reg[2]);
+ val |= PLLX_MISC2_EN_DYNRAMP;
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[2]);
+ udelay(1);
+
+ tegra210_wait_for_mask(pllx, pllx->params->ext_misc_reg[2],
+ PLLX_MISC2_DYNRAMP_DONE);
+
+ base = readl_relaxed(clk_base + pllx->params->base_reg) &
+ (~divn_mask_shifted(pllx));
+ base |= cfg->n << pllx->params->div_nmp->divn_shift;
+ writel_relaxed(base, clk_base + pllx->params->base_reg);
+ udelay(1);
+
+ val &= ~PLLX_MISC2_EN_DYNRAMP;
+ writel_relaxed(val, clk_base + pllx->params->ext_misc_reg[2]);
+ udelay(1);
+
+ pr_debug("%s: dynamic ramp to m = %u n = %u p = %u, Fout = %lu kHz\n",
+ __clk_get_name(pllx->hw.clk), cfg->m, cfg->n, cfg->p,
+ cfg->input_rate / cfg->m * cfg->n /
+ pllx->params->pdiv_tohw[cfg->p].pdiv / 1000);
+
+ return 0;
+}
+
+/*
+ * Common configuration for PLLs with fixed input divider policy:
+ * - always set fixed M-value based on the reference rate
+ * - always set P-value value 1:1 for output rates above VCO minimum, and
+ * choose minimum necessary P-value for output rates below VCO maximum
+ * - calculate N-value based on selected M and P
+ * - calculate SDM_DIN fractional part
+ */
+static int tegra210_pll_fixed_mdiv_cfg(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long input_rate)
+{
+ struct tegra_clk_pll *pll = to_clk_pll(hw);
+ struct tegra_clk_pll_params *params = pll->params;
+ int p;
+ unsigned long cf, p_rate;
+ u32 pdiv;
+
+ if (!rate)
+ return -EINVAL;
+
+ if (!(params->flags & TEGRA_PLL_VCO_OUT)) {
+ p = DIV_ROUND_UP(params->vco_min, rate);
+ p = params->round_p_to_pdiv(p, &pdiv);
+ } else {
+ p = rate >= params->vco_min ? 1 : -EINVAL;
+ }
+
+ if (p < 0)
+ return -EINVAL;
+
+ cfg->m = tegra_pll_get_fixed_mdiv(hw, input_rate);
+ cfg->p = p;
+
+ /* Store P as HW value, as that is what is expected */
+ cfg->p = tegra_pll_p_div_to_hw(pll, cfg->p);
+
+ p_rate = rate * p;
+ if (p_rate > params->vco_max)
+ p_rate = params->vco_max;
+ cf = input_rate / cfg->m;
+ cfg->n = p_rate / cf;
+
+ cfg->sdm_data = 0;
+ cfg->output_rate = input_rate;
+ if (params->sdm_ctrl_reg) {
+ unsigned long rem = p_rate - cf * cfg->n;
+ /* If ssc is enabled SDM enabled as well, even for integer n */
+ if (rem || params->ssc_ctrl_reg) {
+ u64 s = rem * PLL_SDM_COEFF;
+
+ do_div(s, cf);
+ s -= PLL_SDM_COEFF / 2;
+ cfg->sdm_data = sdin_din_to_data(s);
+ }
+ cfg->output_rate *= sdin_get_n_eff(cfg);
+ cfg->output_rate /= p * cfg->m * PLL_SDM_COEFF;
+ } else {
+ cfg->output_rate *= cfg->n;
+ cfg->output_rate /= p * cfg->m;
+ }
+
+ cfg->input_rate = input_rate;
+
+ return 0;
+}
+
+/*
+ * clk_pll_set_gain - set gain to m, n to calculate correct VCO rate
+ *
+ * @cfg: struct tegra_clk_pll_freq_table * cfg
+ *
+ * For Normal mode:
+ * Fvco = Fref * NDIV / MDIV
+ *
+ * For fractional mode:
+ * Fvco = Fref * (NDIV + 0.5 + SDM_DIN / PLL_SDM_COEFF) / MDIV
+ */
+static void tegra210_clk_pll_set_gain(struct tegra_clk_pll_freq_table *cfg)
+{
+ cfg->n = sdin_get_n_eff(cfg);
+ cfg->m *= PLL_SDM_COEFF;
+}
+
+static unsigned long
+tegra210_clk_adjust_vco_min(struct tegra_clk_pll_params *params,
+ unsigned long parent_rate)
+{
+ unsigned long vco_min = params->vco_min;
+
+ params->vco_min += DIV_ROUND_UP(parent_rate, PLL_SDM_COEFF);
+ vco_min = min(vco_min, params->vco_min);
+
+ return vco_min;
+}
+
+static struct div_nmp pllx_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+};
+/*
+ * PLL post divider maps - two types: quasi-linear and exponential
+ * post divider.
+ */
+#define PLL_QLIN_PDIV_MAX 16
+static const struct pdiv_map pll_qlin_pdiv_to_hw[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 3, .hw_val = 2 },
+ { .pdiv = 4, .hw_val = 3 },
+ { .pdiv = 5, .hw_val = 4 },
+ { .pdiv = 6, .hw_val = 5 },
+ { .pdiv = 8, .hw_val = 6 },
+ { .pdiv = 9, .hw_val = 7 },
+ { .pdiv = 10, .hw_val = 8 },
+ { .pdiv = 12, .hw_val = 9 },
+ { .pdiv = 15, .hw_val = 10 },
+ { .pdiv = 16, .hw_val = 11 },
+ { .pdiv = 18, .hw_val = 12 },
+ { .pdiv = 20, .hw_val = 13 },
+ { .pdiv = 24, .hw_val = 14 },
+ { .pdiv = 30, .hw_val = 15 },
+ { .pdiv = 32, .hw_val = 16 },
+};
+
+static u32 pll_qlin_p_to_pdiv(u32 p, u32 *pdiv)
+{
+ int i;
+
+ if (p) {
+ for (i = 0; i <= PLL_QLIN_PDIV_MAX; i++) {
+ if (p <= pll_qlin_pdiv_to_hw[i].pdiv) {
+ if (pdiv)
+ *pdiv = i;
+ return pll_qlin_pdiv_to_hw[i].pdiv;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+#define PLL_EXPO_PDIV_MAX 7
+static const struct pdiv_map pll_expo_pdiv_to_hw[] = {
+ { .pdiv = 1, .hw_val = 0 },
+ { .pdiv = 2, .hw_val = 1 },
+ { .pdiv = 4, .hw_val = 2 },
+ { .pdiv = 8, .hw_val = 3 },
+ { .pdiv = 16, .hw_val = 4 },
+ { .pdiv = 32, .hw_val = 5 },
+ { .pdiv = 64, .hw_val = 6 },
+ { .pdiv = 128, .hw_val = 7 },
+};
+
+static u32 pll_expo_p_to_pdiv(u32 p, u32 *pdiv)
+{
+ if (p) {
+ u32 i = fls(p);
+
+ if (i == ffs(p))
+ i--;
+
+ if (i <= PLL_EXPO_PDIV_MAX) {
+ if (pdiv)
+ *pdiv = i;
+ return 1 << i;
+ }
+ }
+ return -EINVAL;
+}
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 166, 1, 2, 0 }, /* actual: 996.0 MHz */
+ { 13000000, 1000000000, 153, 1, 2, 0 }, /* actual: 994.0 MHz */
+ { 38400000, 1000000000, 156, 3, 2, 0 }, /* actual: 998.4 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_x_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 38400000,
+ .vco_min = 1350000000,
+ .vco_max = 3000000000UL,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .ext_misc_reg[0] = PLLX_MISC0,
+ .ext_misc_reg[1] = PLLX_MISC1,
+ .ext_misc_reg[2] = PLLX_MISC2,
+ .ext_misc_reg[3] = PLLX_MISC3,
+ .ext_misc_reg[4] = PLLX_MISC4,
+ .ext_misc_reg[5] = PLLX_MISC5,
+ .iddq_reg = PLLX_MISC3,
+ .iddq_bit_idx = PLLXP_IDDQ_BIT,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .mdiv_default = 2,
+ .dyn_ramp_reg = PLLX_MISC2,
+ .stepa_shift = 16,
+ .stepb_shift = 24,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllx_nmp,
+ .freq_table = pll_x_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .dyn_ramp = tegra210_pllx_dyn_ramp,
+ .set_defaults = tegra210_pllx_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct div_nmp pllc_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 10,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_freq_table pll_cx_freq_table[] = {
+ { 12000000, 510000000, 85, 1, 2, 0 },
+ { 13000000, 510000000, 78, 1, 2, 0 }, /* actual: 507.0 MHz */
+ { 38400000, 510000000, 79, 3, 2, 0 }, /* actual: 505.6 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_c_params = {
+ .input_min = 12000000,
+ .input_max = 700000000,
+ .cf_min = 12000000,
+ .cf_max = 50000000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLC_MISC1,
+ .iddq_bit_idx = PLLCX_IDDQ_BIT,
+ .reset_reg = PLLC_MISC0,
+ .reset_bit_idx = PLLCX_RESET_BIT,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLC_MISC0,
+ .ext_misc_reg[1] = PLLC_MISC1,
+ .ext_misc_reg[2] = PLLC_MISC2,
+ .ext_misc_reg[3] = PLLC_MISC3,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .mdiv_default = 3,
+ .div_nmp = &pllc_nmp,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .set_defaults = _pllc_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct div_nmp pllcx_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 10,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_params pll_c2_params = {
+ .input_min = 12000000,
+ .input_max = 700000000,
+ .cf_min = 12000000,
+ .cf_max = 50000000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC2_BASE,
+ .misc_reg = PLLC2_MISC0,
+ .iddq_reg = PLLC2_MISC1,
+ .iddq_bit_idx = PLLCX_IDDQ_BIT,
+ .reset_reg = PLLC2_MISC0,
+ .reset_bit_idx = PLLCX_RESET_BIT,
+ .lock_mask = PLLCX_BASE_LOCK,
+ .lock_delay = 300,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .mdiv_default = 3,
+ .div_nmp = &pllcx_nmp,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLC2_MISC0,
+ .ext_misc_reg[1] = PLLC2_MISC1,
+ .ext_misc_reg[2] = PLLC2_MISC2,
+ .ext_misc_reg[3] = PLLC2_MISC3,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .set_defaults = _pllc2_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct tegra_clk_pll_params pll_c3_params = {
+ .input_min = 12000000,
+ .input_max = 700000000,
+ .cf_min = 12000000,
+ .cf_max = 50000000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLC3_BASE,
+ .misc_reg = PLLC3_MISC0,
+ .lock_mask = PLLCX_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLC3_MISC1,
+ .iddq_bit_idx = PLLCX_IDDQ_BIT,
+ .reset_reg = PLLC3_MISC0,
+ .reset_bit_idx = PLLCX_RESET_BIT,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .mdiv_default = 3,
+ .div_nmp = &pllcx_nmp,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLC3_MISC0,
+ .ext_misc_reg[1] = PLLC3_MISC1,
+ .ext_misc_reg[2] = PLLC3_MISC2,
+ .ext_misc_reg[3] = PLLC3_MISC3,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .set_defaults = _pllc3_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct div_nmp pllss_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 19,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_freq_table pll_c4_vco_freq_table[] = {
+ { 12000000, 600000000, 50, 1, 1, 0 },
+ { 13000000, 600000000, 46, 1, 1, 0 }, /* actual: 598.0 MHz */
+ { 38400000, 600000000, 62, 4, 1, 0 }, /* actual: 595.2 MHz */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct clk_div_table pll_vco_post_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 5 },
+ { .val = 5, .div = 6 },
+ { .val = 6, .div = 8 },
+ { .val = 7, .div = 10 },
+ { .val = 8, .div = 12 },
+ { .val = 9, .div = 16 },
+ { .val = 10, .div = 12 },
+ { .val = 11, .div = 16 },
+ { .val = 12, .div = 20 },
+ { .val = 13, .div = 24 },
+ { .val = 14, .div = 32 },
+ { .val = 0, .div = 0 },
+};
+
+static struct tegra_clk_pll_params pll_c4_vco_params = {
+ .input_min = 9600000,
+ .input_max = 800000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 500000000,
+ .vco_max = 1080000000,
+ .base_reg = PLLC4_BASE,
+ .misc_reg = PLLC4_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLC4_MISC0,
+ .iddq_reg = PLLC4_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .mdiv_default = 3,
+ .div_nmp = &pllss_nmp,
+ .freq_table = pll_c4_vco_freq_table,
+ .set_defaults = tegra210_pllc4_set_defaults,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_VCO_OUT,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 800000000, 66, 1, 1, 0 }, /* actual: 792.0 MHz */
+ { 13000000, 800000000, 61, 1, 1, 0 }, /* actual: 793.0 MHz */
+ { 38400000, 297600000, 93, 4, 3, 0 },
+ { 38400000, 400000000, 125, 4, 3, 0 },
+ { 38400000, 532800000, 111, 4, 2, 0 },
+ { 38400000, 665600000, 104, 3, 2, 0 },
+ { 38400000, 800000000, 125, 3, 2, 0 },
+ { 38400000, 931200000, 97, 4, 1, 0 },
+ { 38400000, 1065600000, 111, 4, 1, 0 },
+ { 38400000, 1200000000, 125, 4, 1, 0 },
+ { 38400000, 1331200000, 104, 3, 1, 0 },
+ { 38400000, 1459200000, 76, 2, 1, 0 },
+ { 38400000, 1600000000, 125, 3, 1, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct div_nmp pllm_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .override_divm_shift = 0,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .override_divn_shift = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+ .override_divp_shift = 27,
+};
+
+static struct tegra_clk_pll_params pll_m_params = {
+ .input_min = 9600000,
+ .input_max = 500000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 800000000,
+ .vco_max = 1866000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC2,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLM_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .iddq_reg = PLLM_MISC2,
+ .iddq_bit_idx = PLLM_IDDQ_BIT,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLM_MISC2,
+ .ext_misc_reg[1] = PLLM_MISC1,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllm_nmp,
+ .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE,
+ .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct tegra_clk_pll_params pll_mb_params = {
+ .input_min = 9600000,
+ .input_max = 500000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 800000000,
+ .vco_max = 1866000000,
+ .base_reg = PLLMB_BASE,
+ .misc_reg = PLLMB_MISC1,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLMB_MISC1,
+ .iddq_bit_idx = PLLMB_IDDQ_BIT,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLMB_MISC1,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllm_nmp,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .set_defaults = tegra210_pllmb_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 672000000, 100000000, 125, 42, 0, 13 },
+ { 624000000, 100000000, 125, 39, 0, 13 },
+ { 336000000, 100000000, 125, 21, 0, 13 },
+ { 312000000, 100000000, 200, 26, 0, 14 },
+ { 38400000, 100000000, 125, 2, 0, 14 },
+ { 12000000, 100000000, 200, 1, 0, 14 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct div_nmp plle_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 24,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_params pll_e_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 38400000,
+ .vco_min = 1600000000,
+ .vco_max = 2500000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC0,
+ .aux_reg = PLLE_AUX,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &plle_nmp,
+ .freq_table = pll_e_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_LOCK_MISC | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 100000000,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct tegra_clk_pll_freq_table pll_re_vco_freq_table[] = {
+ { 12000000, 672000000, 56, 1, 1, 0 },
+ { 13000000, 672000000, 51, 1, 1, 0 }, /* actual: 663.0 MHz */
+ { 38400000, 672000000, 70, 4, 1, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct div_nmp pllre_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 16,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_params pll_re_vco_params = {
+ .input_min = 9600000,
+ .input_max = 800000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 350000000,
+ .vco_max = 700000000,
+ .base_reg = PLLRE_BASE,
+ .misc_reg = PLLRE_MISC0,
+ .lock_mask = PLLRE_MISC_LOCK,
+ .lock_delay = 300,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .ext_misc_reg[0] = PLLRE_MISC0,
+ .iddq_reg = PLLRE_MISC0,
+ .iddq_bit_idx = PLLRE_IDDQ_BIT,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllre_nmp,
+ .freq_table = pll_re_vco_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_LOCK_MISC | TEGRA_PLL_VCO_OUT,
+ .set_defaults = tegra210_pllre_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct div_nmp pllp_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 10,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 408000000, 34, 1, 1, 0 },
+ { 38400000, 408000000, 85, 8, 1, 0 }, /* cf = 4.8MHz, allowed exception */
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_p_params = {
+ .input_min = 9600000,
+ .input_max = 800000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 350000000,
+ .vco_max = 700000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLP_MISC0,
+ .iddq_bit_idx = PLLXP_IDDQ_BIT,
+ .ext_misc_reg[0] = PLLP_MISC0,
+ .ext_misc_reg[1] = PLLP_MISC1,
+ .div_nmp = &pllp_nmp,
+ .freq_table = pll_p_freq_table,
+ .fixed_rate = 408000000,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_USE_LOCK | TEGRA_PLL_VCO_OUT,
+ .set_defaults = tegra210_pllp_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct tegra_clk_pll_params pll_a1_params = {
+ .input_min = 12000000,
+ .input_max = 700000000,
+ .cf_min = 12000000,
+ .cf_max = 50000000,
+ .vco_min = 600000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLA1_BASE,
+ .misc_reg = PLLA1_MISC0,
+ .lock_mask = PLLCX_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLA1_MISC1,
+ .iddq_bit_idx = PLLCX_IDDQ_BIT,
+ .reset_reg = PLLA1_MISC0,
+ .reset_bit_idx = PLLCX_RESET_BIT,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllc_nmp,
+ .ext_misc_reg[0] = PLLA1_MISC0,
+ .ext_misc_reg[1] = PLLA1_MISC1,
+ .ext_misc_reg[2] = PLLA1_MISC2,
+ .ext_misc_reg[3] = PLLA1_MISC3,
+ .freq_table = pll_cx_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .set_defaults = _plla1_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+};
+
+static struct div_nmp plla_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 12000000, 282240000, 47, 1, 2, 1, 0xf148 }, /* actual: 282240234 */
+ { 12000000, 368640000, 61, 1, 2, 1, 0xfe15 }, /* actual: 368640381 */
+ { 12000000, 240000000, 60, 1, 3, 1, 0 },
+ { 13000000, 282240000, 43, 1, 2, 1, 0xfd7d }, /* actual: 282239807 */
+ { 13000000, 368640000, 56, 1, 2, 1, 0x06d8 }, /* actual: 368640137 */
+ { 13000000, 240000000, 55, 1, 3, 1, 0 }, /* actual: 238.3 MHz */
+ { 38400000, 282240000, 44, 3, 2, 1, 0xf333 }, /* actual: 282239844 */
+ { 38400000, 368640000, 57, 3, 2, 1, 0x0333 }, /* actual: 368639844 */
+ { 38400000, 240000000, 75, 3, 3, 1, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 19200000,
+ .vco_min = 500000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .iddq_reg = PLLA_BASE,
+ .iddq_bit_idx = PLLA_IDDQ_BIT,
+ .div_nmp = &plla_nmp,
+ .sdm_din_reg = PLLA_MISC1,
+ .sdm_din_mask = PLLA_SDM_DIN_MASK,
+ .sdm_ctrl_reg = PLLA_MISC2,
+ .sdm_ctrl_en_mask = PLLA_SDM_EN_MASK,
+ .ext_misc_reg[0] = PLLA_MISC0,
+ .ext_misc_reg[1] = PLLA_MISC1,
+ .ext_misc_reg[2] = PLLA_MISC2,
+ .freq_table = pll_a_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK | TEGRA_MDIV_NEW,
+ .set_defaults = tegra210_plla_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+ .set_gain = tegra210_clk_pll_set_gain,
+ .adjust_vco = tegra210_clk_adjust_vco_min,
+};
+
+static struct div_nmp plld_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 11,
+ .divn_width = 8,
+ .divp_shift = 20,
+ .divp_width = 3,
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 594000000, 99, 1, 2, 0, 0 },
+ { 13000000, 594000000, 91, 1, 2, 0, 0xfc4f }, /* actual: 594000183 */
+ { 38400000, 594000000, 30, 1, 2, 0, 0x0e00 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_d_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 38400000,
+ .vco_min = 750000000,
+ .vco_max = 1500000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 1000,
+ .iddq_reg = PLLD_MISC0,
+ .iddq_bit_idx = PLLD_IDDQ_BIT,
+ .round_p_to_pdiv = pll_expo_p_to_pdiv,
+ .pdiv_tohw = pll_expo_pdiv_to_hw,
+ .div_nmp = &plld_nmp,
+ .sdm_din_reg = PLLD_MISC0,
+ .sdm_din_mask = PLLA_SDM_DIN_MASK,
+ .sdm_ctrl_reg = PLLD_MISC0,
+ .sdm_ctrl_en_mask = PLLD_SDM_EN_MASK,
+ .ext_misc_reg[0] = PLLD_MISC0,
+ .ext_misc_reg[1] = PLLD_MISC1,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .mdiv_default = 1,
+ .set_defaults = tegra210_plld_set_defaults,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+ .set_gain = tegra210_clk_pll_set_gain,
+ .adjust_vco = tegra210_clk_adjust_vco_min,
+};
+
+static struct tegra_clk_pll_freq_table tegra210_pll_d2_freq_table[] = {
+ { 12000000, 594000000, 99, 1, 2, 0, 0xf000 },
+ { 13000000, 594000000, 91, 1, 2, 0, 0xfc4f }, /* actual: 594000183 */
+ { 38400000, 594000000, 30, 1, 2, 0, 0x0e00 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* s/w policy, always tegra_pll_ref */
+static struct tegra_clk_pll_params pll_d2_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 38400000,
+ .vco_min = 750000000,
+ .vco_max = 1500000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLD2_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .sdm_din_reg = PLLD2_MISC3,
+ .sdm_din_mask = PLLA_SDM_DIN_MASK,
+ .sdm_ctrl_reg = PLLD2_MISC1,
+ .sdm_ctrl_en_mask = PLLD2_SDM_EN_MASK,
+ /* disable spread-spectrum for pll_d2 */
+ .ssc_ctrl_reg = 0,
+ .ssc_ctrl_en_mask = 0,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllss_nmp,
+ .ext_misc_reg[0] = PLLD2_MISC0,
+ .ext_misc_reg[1] = PLLD2_MISC1,
+ .ext_misc_reg[2] = PLLD2_MISC2,
+ .ext_misc_reg[3] = PLLD2_MISC3,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .mdiv_default = 1,
+ .freq_table = tegra210_pll_d2_freq_table,
+ .set_defaults = tegra210_plld2_set_defaults,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+ .set_gain = tegra210_clk_pll_set_gain,
+ .adjust_vco = tegra210_clk_adjust_vco_min,
+};
+
+static struct tegra_clk_pll_freq_table pll_dp_freq_table[] = {
+ { 12000000, 270000000, 90, 1, 4, 0, 0xf000 },
+ { 13000000, 270000000, 83, 1, 4, 0, 0xf000 }, /* actual: 269.8 MHz */
+ { 38400000, 270000000, 28, 1, 4, 0, 0xf400 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_dp_params = {
+ .input_min = 12000000,
+ .input_max = 800000000,
+ .cf_min = 12000000,
+ .cf_max = 38400000,
+ .vco_min = 750000000,
+ .vco_max = 1500000000,
+ .base_reg = PLLDP_BASE,
+ .misc_reg = PLLDP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 300,
+ .iddq_reg = PLLDP_BASE,
+ .iddq_bit_idx = PLLSS_IDDQ_BIT,
+ .sdm_din_reg = PLLDP_SS_CTRL2,
+ .sdm_din_mask = PLLA_SDM_DIN_MASK,
+ .sdm_ctrl_reg = PLLDP_SS_CFG,
+ .sdm_ctrl_en_mask = PLLDP_SDM_EN_MASK,
+ .ssc_ctrl_reg = PLLDP_SS_CFG,
+ .ssc_ctrl_en_mask = PLLDP_SSC_EN_MASK,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllss_nmp,
+ .ext_misc_reg[0] = PLLDP_MISC,
+ .ext_misc_reg[1] = PLLDP_SS_CFG,
+ .ext_misc_reg[2] = PLLDP_SS_CTRL1,
+ .ext_misc_reg[3] = PLLDP_SS_CTRL2,
+ .max_p = PLL_QLIN_PDIV_MAX,
+ .mdiv_default = 1,
+ .freq_table = pll_dp_freq_table,
+ .set_defaults = tegra210_plldp_set_defaults,
+ .flags = TEGRA_PLL_USE_LOCK,
+ .calc_rate = tegra210_pll_fixed_mdiv_cfg,
+ .set_gain = tegra210_clk_pll_set_gain,
+ .adjust_vco = tegra210_clk_adjust_vco_min,
+};
+
+static struct div_nmp pllu_nmp = {
+ .divm_shift = 0,
+ .divm_width = 8,
+ .divn_shift = 8,
+ .divn_width = 8,
+ .divp_shift = 16,
+ .divp_width = 5,
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 40, 1, 1, 0 },
+ { 13000000, 480000000, 36, 1, 1, 0 }, /* actual: 468.0 MHz */
+ { 38400000, 480000000, 25, 2, 1, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_params pll_u_vco_params = {
+ .input_min = 9600000,
+ .input_max = 800000000,
+ .cf_min = 9600000,
+ .cf_max = 19200000,
+ .vco_min = 350000000,
+ .vco_max = 700000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC0,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_delay = 1000,
+ .iddq_reg = PLLU_MISC0,
+ .iddq_bit_idx = PLLU_IDDQ_BIT,
+ .ext_misc_reg[0] = PLLU_MISC0,
+ .ext_misc_reg[1] = PLLU_MISC1,
+ .round_p_to_pdiv = pll_qlin_p_to_pdiv,
+ .pdiv_tohw = pll_qlin_pdiv_to_hw,
+ .div_nmp = &pllu_nmp,
+ .freq_table = pll_u_freq_table,
+ .flags = TEGRA_PLLU | TEGRA_PLL_USE_LOCK | TEGRA_PLL_VCO_OUT,
+};
+
+struct utmi_clk_param {
+ /* Oscillator Frequency in KHz */
+ u32 osc_frequency;
+ /* UTMIP PLL Enable Delay Count */
+ u8 enable_delay_count;
+ /* UTMIP PLL Stable count */
+ u16 stable_count;
+ /* UTMIP PLL Active delay count */
+ u8 active_delay_count;
+ /* UTMIP PLL Xtal frequency count */
+ u16 xtal_freq_count;
+};
+
+static const struct utmi_clk_param utmi_parameters[] = {
+ {
+ .osc_frequency = 38400000, .enable_delay_count = 0x0,
+ .stable_count = 0x0, .active_delay_count = 0x6,
+ .xtal_freq_count = 0x80
+ }, {
+ .osc_frequency = 13000000, .enable_delay_count = 0x02,
+ .stable_count = 0x33, .active_delay_count = 0x05,
+ .xtal_freq_count = 0x7f
+ }, {
+ .osc_frequency = 19200000, .enable_delay_count = 0x03,
+ .stable_count = 0x4b, .active_delay_count = 0x06,
+ .xtal_freq_count = 0xbb
+ }, {
+ .osc_frequency = 12000000, .enable_delay_count = 0x02,
+ .stable_count = 0x2f, .active_delay_count = 0x08,
+ .xtal_freq_count = 0x76
+ }, {
+ .osc_frequency = 26000000, .enable_delay_count = 0x04,
+ .stable_count = 0x66, .active_delay_count = 0x09,
+ .xtal_freq_count = 0xfe
+ }, {
+ .osc_frequency = 16800000, .enable_delay_count = 0x03,
+ .stable_count = 0x41, .active_delay_count = 0x0a,
+ .xtal_freq_count = 0xa4
+ },
+};
+
+static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = {
+ [tegra_clk_ispb] = { .dt_id = TEGRA210_CLK_ISPB, .present = true },
+ [tegra_clk_rtc] = { .dt_id = TEGRA210_CLK_RTC, .present = true },
+ [tegra_clk_timer] = { .dt_id = TEGRA210_CLK_TIMER, .present = true },
+ [tegra_clk_uarta_8] = { .dt_id = TEGRA210_CLK_UARTA, .present = true },
+ [tegra_clk_i2s1] = { .dt_id = TEGRA210_CLK_I2S1, .present = true },
+ [tegra_clk_i2c1] = { .dt_id = TEGRA210_CLK_I2C1, .present = true },
+ [tegra_clk_sdmmc1_9] = { .dt_id = TEGRA210_CLK_SDMMC1, .present = true },
+ [tegra_clk_pwm] = { .dt_id = TEGRA210_CLK_PWM, .present = true },
+ [tegra_clk_i2s2] = { .dt_id = TEGRA210_CLK_I2S2, .present = true },
+ [tegra_clk_usbd] = { .dt_id = TEGRA210_CLK_USBD, .present = true },
+ [tegra_clk_isp_9] = { .dt_id = TEGRA210_CLK_ISP, .present = true },
+ [tegra_clk_disp2_8] = { .dt_id = TEGRA210_CLK_DISP2, .present = true },
+ [tegra_clk_disp1_8] = { .dt_id = TEGRA210_CLK_DISP1, .present = true },
+ [tegra_clk_host1x_9] = { .dt_id = TEGRA210_CLK_HOST1X, .present = true },
+ [tegra_clk_i2s0] = { .dt_id = TEGRA210_CLK_I2S0, .present = true },
+ [tegra_clk_apbdma] = { .dt_id = TEGRA210_CLK_APBDMA, .present = true },
+ [tegra_clk_kfuse] = { .dt_id = TEGRA210_CLK_KFUSE, .present = true },
+ [tegra_clk_sbc1_9] = { .dt_id = TEGRA210_CLK_SBC1, .present = true },
+ [tegra_clk_sbc2_9] = { .dt_id = TEGRA210_CLK_SBC2, .present = true },
+ [tegra_clk_sbc3_9] = { .dt_id = TEGRA210_CLK_SBC3, .present = true },
+ [tegra_clk_i2c5] = { .dt_id = TEGRA210_CLK_I2C5, .present = true },
+ [tegra_clk_csi] = { .dt_id = TEGRA210_CLK_CSI, .present = true },
+ [tegra_clk_i2c2] = { .dt_id = TEGRA210_CLK_I2C2, .present = true },
+ [tegra_clk_uartc_8] = { .dt_id = TEGRA210_CLK_UARTC, .present = true },
+ [tegra_clk_mipi_cal] = { .dt_id = TEGRA210_CLK_MIPI_CAL, .present = true },
+ [tegra_clk_usb2] = { .dt_id = TEGRA210_CLK_USB2, .present = true },
+ [tegra_clk_bsev] = { .dt_id = TEGRA210_CLK_BSEV, .present = true },
+ [tegra_clk_uartd_8] = { .dt_id = TEGRA210_CLK_UARTD, .present = true },
+ [tegra_clk_i2c3] = { .dt_id = TEGRA210_CLK_I2C3, .present = true },
+ [tegra_clk_sbc4_9] = { .dt_id = TEGRA210_CLK_SBC4, .present = true },
+ [tegra_clk_sdmmc3_9] = { .dt_id = TEGRA210_CLK_SDMMC3, .present = true },
+ [tegra_clk_pcie] = { .dt_id = TEGRA210_CLK_PCIE, .present = true },
+ [tegra_clk_owr_8] = { .dt_id = TEGRA210_CLK_OWR, .present = true },
+ [tegra_clk_afi] = { .dt_id = TEGRA210_CLK_AFI, .present = true },
+ [tegra_clk_csite_8] = { .dt_id = TEGRA210_CLK_CSITE, .present = true },
+ [tegra_clk_soc_therm_8] = { .dt_id = TEGRA210_CLK_SOC_THERM, .present = true },
+ [tegra_clk_dtv] = { .dt_id = TEGRA210_CLK_DTV, .present = true },
+ [tegra_clk_i2cslow] = { .dt_id = TEGRA210_CLK_I2CSLOW, .present = true },
+ [tegra_clk_tsec_8] = { .dt_id = TEGRA210_CLK_TSEC, .present = true },
+ [tegra_clk_xusb_host] = { .dt_id = TEGRA210_CLK_XUSB_HOST, .present = true },
+ [tegra_clk_csus] = { .dt_id = TEGRA210_CLK_CSUS, .present = true },
+ [tegra_clk_mselect] = { .dt_id = TEGRA210_CLK_MSELECT, .present = true },
+ [tegra_clk_tsensor] = { .dt_id = TEGRA210_CLK_TSENSOR, .present = true },
+ [tegra_clk_i2s3] = { .dt_id = TEGRA210_CLK_I2S3, .present = true },
+ [tegra_clk_i2s4] = { .dt_id = TEGRA210_CLK_I2S4, .present = true },
+ [tegra_clk_i2c4] = { .dt_id = TEGRA210_CLK_I2C4, .present = true },
+ [tegra_clk_d_audio] = { .dt_id = TEGRA210_CLK_D_AUDIO, .present = true },
+ [tegra_clk_hda2codec_2x_8] = { .dt_id = TEGRA210_CLK_HDA2CODEC_2X, .present = true },
+ [tegra_clk_spdif_2x] = { .dt_id = TEGRA210_CLK_SPDIF_2X, .present = true },
+ [tegra_clk_actmon] = { .dt_id = TEGRA210_CLK_ACTMON, .present = true },
+ [tegra_clk_extern1] = { .dt_id = TEGRA210_CLK_EXTERN1, .present = true },
+ [tegra_clk_extern2] = { .dt_id = TEGRA210_CLK_EXTERN2, .present = true },
+ [tegra_clk_extern3] = { .dt_id = TEGRA210_CLK_EXTERN3, .present = true },
+ [tegra_clk_sata_oob_8] = { .dt_id = TEGRA210_CLK_SATA_OOB, .present = true },
+ [tegra_clk_sata_8] = { .dt_id = TEGRA210_CLK_SATA, .present = true },
+ [tegra_clk_hda_8] = { .dt_id = TEGRA210_CLK_HDA, .present = true },
+ [tegra_clk_hda2hdmi] = { .dt_id = TEGRA210_CLK_HDA2HDMI, .present = true },
+ [tegra_clk_cilab] = { .dt_id = TEGRA210_CLK_CILAB, .present = true },
+ [tegra_clk_cilcd] = { .dt_id = TEGRA210_CLK_CILCD, .present = true },
+ [tegra_clk_cile] = { .dt_id = TEGRA210_CLK_CILE, .present = true },
+ [tegra_clk_dsialp] = { .dt_id = TEGRA210_CLK_DSIALP, .present = true },
+ [tegra_clk_dsiblp] = { .dt_id = TEGRA210_CLK_DSIBLP, .present = true },
+ [tegra_clk_entropy_8] = { .dt_id = TEGRA210_CLK_ENTROPY, .present = true },
+ [tegra_clk_xusb_ss] = { .dt_id = TEGRA210_CLK_XUSB_SS, .present = true },
+ [tegra_clk_i2c6] = { .dt_id = TEGRA210_CLK_I2C6, .present = true },
+ [tegra_clk_vim2_clk] = { .dt_id = TEGRA210_CLK_VIM2_CLK, .present = true },
+ [tegra_clk_clk72Mhz_8] = { .dt_id = TEGRA210_CLK_CLK72MHZ, .present = true },
+ [tegra_clk_vic03_8] = { .dt_id = TEGRA210_CLK_VIC03, .present = true },
+ [tegra_clk_dpaux] = { .dt_id = TEGRA210_CLK_DPAUX, .present = true },
+ [tegra_clk_dpaux1] = { .dt_id = TEGRA210_CLK_DPAUX1, .present = true },
+ [tegra_clk_sor0] = { .dt_id = TEGRA210_CLK_SOR0, .present = true },
+ [tegra_clk_sor0_out] = { .dt_id = TEGRA210_CLK_SOR0_OUT, .present = true },
+ [tegra_clk_sor1] = { .dt_id = TEGRA210_CLK_SOR1, .present = true },
+ [tegra_clk_sor1_out] = { .dt_id = TEGRA210_CLK_SOR1_OUT, .present = true },
+ [tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true },
+ [tegra_clk_pll_g_ref] = { .dt_id = TEGRA210_CLK_PLL_G_REF, .present = true, },
+ [tegra_clk_uartb_8] = { .dt_id = TEGRA210_CLK_UARTB, .present = true },
+ [tegra_clk_spdif_in_8] = { .dt_id = TEGRA210_CLK_SPDIF_IN, .present = true },
+ [tegra_clk_spdif_out] = { .dt_id = TEGRA210_CLK_SPDIF_OUT, .present = true },
+ [tegra_clk_vi_10] = { .dt_id = TEGRA210_CLK_VI, .present = true },
+ [tegra_clk_vi_sensor_8] = { .dt_id = TEGRA210_CLK_VI_SENSOR, .present = true },
+ [tegra_clk_fuse] = { .dt_id = TEGRA210_CLK_FUSE, .present = true },
+ [tegra_clk_fuse_burn] = { .dt_id = TEGRA210_CLK_FUSE_BURN, .present = true },
+ [tegra_clk_clk_32k] = { .dt_id = TEGRA210_CLK_CLK_32K, .present = true },
+ [tegra_clk_clk_m] = { .dt_id = TEGRA210_CLK_CLK_M, .present = true },
+ [tegra_clk_osc] = { .dt_id = TEGRA210_CLK_OSC, .present = true },
+ [tegra_clk_osc_div2] = { .dt_id = TEGRA210_CLK_OSC_DIV2, .present = true },
+ [tegra_clk_osc_div4] = { .dt_id = TEGRA210_CLK_OSC_DIV4, .present = true },
+ [tegra_clk_pll_ref] = { .dt_id = TEGRA210_CLK_PLL_REF, .present = true },
+ [tegra_clk_pll_c] = { .dt_id = TEGRA210_CLK_PLL_C, .present = true },
+ [tegra_clk_pll_c_out1] = { .dt_id = TEGRA210_CLK_PLL_C_OUT1, .present = true },
+ [tegra_clk_pll_c2] = { .dt_id = TEGRA210_CLK_PLL_C2, .present = true },
+ [tegra_clk_pll_c3] = { .dt_id = TEGRA210_CLK_PLL_C3, .present = true },
+ [tegra_clk_pll_m] = { .dt_id = TEGRA210_CLK_PLL_M, .present = true },
+ [tegra_clk_pll_p] = { .dt_id = TEGRA210_CLK_PLL_P, .present = true },
+ [tegra_clk_pll_p_out1] = { .dt_id = TEGRA210_CLK_PLL_P_OUT1, .present = true },
+ [tegra_clk_pll_p_out3] = { .dt_id = TEGRA210_CLK_PLL_P_OUT3, .present = true },
+ [tegra_clk_pll_p_out4_cpu] = { .dt_id = TEGRA210_CLK_PLL_P_OUT4, .present = true },
+ [tegra_clk_pll_p_out_hsio] = { .dt_id = TEGRA210_CLK_PLL_P_OUT_HSIO, .present = true },
+ [tegra_clk_pll_p_out_xusb] = { .dt_id = TEGRA210_CLK_PLL_P_OUT_XUSB, .present = true },
+ [tegra_clk_pll_p_out_cpu] = { .dt_id = TEGRA210_CLK_PLL_P_OUT_CPU, .present = true },
+ [tegra_clk_pll_p_out_adsp] = { .dt_id = TEGRA210_CLK_PLL_P_OUT_ADSP, .present = true },
+ [tegra_clk_pll_a] = { .dt_id = TEGRA210_CLK_PLL_A, .present = true },
+ [tegra_clk_pll_a_out0] = { .dt_id = TEGRA210_CLK_PLL_A_OUT0, .present = true },
+ [tegra_clk_pll_d] = { .dt_id = TEGRA210_CLK_PLL_D, .present = true },
+ [tegra_clk_pll_d_out0] = { .dt_id = TEGRA210_CLK_PLL_D_OUT0, .present = true },
+ [tegra_clk_pll_d2] = { .dt_id = TEGRA210_CLK_PLL_D2, .present = true },
+ [tegra_clk_pll_d2_out0] = { .dt_id = TEGRA210_CLK_PLL_D2_OUT0, .present = true },
+ [tegra_clk_pll_u] = { .dt_id = TEGRA210_CLK_PLL_U, .present = true },
+ [tegra_clk_pll_u_out] = { .dt_id = TEGRA210_CLK_PLL_U_OUT, .present = true },
+ [tegra_clk_pll_u_out1] = { .dt_id = TEGRA210_CLK_PLL_U_OUT1, .present = true },
+ [tegra_clk_pll_u_out2] = { .dt_id = TEGRA210_CLK_PLL_U_OUT2, .present = true },
+ [tegra_clk_pll_u_480m] = { .dt_id = TEGRA210_CLK_PLL_U_480M, .present = true },
+ [tegra_clk_pll_u_60m] = { .dt_id = TEGRA210_CLK_PLL_U_60M, .present = true },
+ [tegra_clk_pll_u_48m] = { .dt_id = TEGRA210_CLK_PLL_U_48M, .present = true },
+ [tegra_clk_pll_x] = { .dt_id = TEGRA210_CLK_PLL_X, .present = true },
+ [tegra_clk_pll_x_out0] = { .dt_id = TEGRA210_CLK_PLL_X_OUT0, .present = true },
+ [tegra_clk_pll_re_vco] = { .dt_id = TEGRA210_CLK_PLL_RE_VCO, .present = true },
+ [tegra_clk_pll_re_out] = { .dt_id = TEGRA210_CLK_PLL_RE_OUT, .present = true },
+ [tegra_clk_spdif_in_sync] = { .dt_id = TEGRA210_CLK_SPDIF_IN_SYNC, .present = true },
+ [tegra_clk_i2s0_sync] = { .dt_id = TEGRA210_CLK_I2S0_SYNC, .present = true },
+ [tegra_clk_i2s1_sync] = { .dt_id = TEGRA210_CLK_I2S1_SYNC, .present = true },
+ [tegra_clk_i2s2_sync] = { .dt_id = TEGRA210_CLK_I2S2_SYNC, .present = true },
+ [tegra_clk_i2s3_sync] = { .dt_id = TEGRA210_CLK_I2S3_SYNC, .present = true },
+ [tegra_clk_i2s4_sync] = { .dt_id = TEGRA210_CLK_I2S4_SYNC, .present = true },
+ [tegra_clk_vimclk_sync] = { .dt_id = TEGRA210_CLK_VIMCLK_SYNC, .present = true },
+ [tegra_clk_audio0] = { .dt_id = TEGRA210_CLK_AUDIO0, .present = true },
+ [tegra_clk_audio1] = { .dt_id = TEGRA210_CLK_AUDIO1, .present = true },
+ [tegra_clk_audio2] = { .dt_id = TEGRA210_CLK_AUDIO2, .present = true },
+ [tegra_clk_audio3] = { .dt_id = TEGRA210_CLK_AUDIO3, .present = true },
+ [tegra_clk_audio4] = { .dt_id = TEGRA210_CLK_AUDIO4, .present = true },
+ [tegra_clk_spdif] = { .dt_id = TEGRA210_CLK_SPDIF, .present = true },
+ [tegra_clk_xusb_gate] = { .dt_id = TEGRA210_CLK_XUSB_GATE, .present = true },
+ [tegra_clk_xusb_host_src_8] = { .dt_id = TEGRA210_CLK_XUSB_HOST_SRC, .present = true },
+ [tegra_clk_xusb_falcon_src_8] = { .dt_id = TEGRA210_CLK_XUSB_FALCON_SRC, .present = true },
+ [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA210_CLK_XUSB_FS_SRC, .present = true },
+ [tegra_clk_xusb_ss_src_8] = { .dt_id = TEGRA210_CLK_XUSB_SS_SRC, .present = true },
+ [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA210_CLK_XUSB_SS_DIV2, .present = true },
+ [tegra_clk_xusb_dev_src_8] = { .dt_id = TEGRA210_CLK_XUSB_DEV_SRC, .present = true },
+ [tegra_clk_xusb_dev] = { .dt_id = TEGRA210_CLK_XUSB_DEV, .present = true },
+ [tegra_clk_xusb_hs_src_4] = { .dt_id = TEGRA210_CLK_XUSB_HS_SRC, .present = true },
+ [tegra_clk_xusb_ssp_src] = { .dt_id = TEGRA210_CLK_XUSB_SSP_SRC, .present = true },
+ [tegra_clk_usb2_hsic_trk] = { .dt_id = TEGRA210_CLK_USB2_HSIC_TRK, .present = true },
+ [tegra_clk_hsic_trk] = { .dt_id = TEGRA210_CLK_HSIC_TRK, .present = true },
+ [tegra_clk_usb2_trk] = { .dt_id = TEGRA210_CLK_USB2_TRK, .present = true },
+ [tegra_clk_sclk] = { .dt_id = TEGRA210_CLK_SCLK, .present = true },
+ [tegra_clk_sclk_mux] = { .dt_id = TEGRA210_CLK_SCLK_MUX, .present = true },
+ [tegra_clk_hclk] = { .dt_id = TEGRA210_CLK_HCLK, .present = true },
+ [tegra_clk_pclk] = { .dt_id = TEGRA210_CLK_PCLK, .present = true },
+ [tegra_clk_cclk_g] = { .dt_id = TEGRA210_CLK_CCLK_G, .present = true },
+ [tegra_clk_cclk_lp] = { .dt_id = TEGRA210_CLK_CCLK_LP, .present = true },
+ [tegra_clk_dfll_ref] = { .dt_id = TEGRA210_CLK_DFLL_REF, .present = true },
+ [tegra_clk_dfll_soc] = { .dt_id = TEGRA210_CLK_DFLL_SOC, .present = true },
+ [tegra_clk_vi_sensor2_8] = { .dt_id = TEGRA210_CLK_VI_SENSOR2, .present = true },
+ [tegra_clk_pll_p_out5] = { .dt_id = TEGRA210_CLK_PLL_P_OUT5, .present = true },
+ [tegra_clk_pll_c4] = { .dt_id = TEGRA210_CLK_PLL_C4, .present = true },
+ [tegra_clk_pll_dp] = { .dt_id = TEGRA210_CLK_PLL_DP, .present = true },
+ [tegra_clk_audio0_mux] = { .dt_id = TEGRA210_CLK_AUDIO0_MUX, .present = true },
+ [tegra_clk_audio1_mux] = { .dt_id = TEGRA210_CLK_AUDIO1_MUX, .present = true },
+ [tegra_clk_audio2_mux] = { .dt_id = TEGRA210_CLK_AUDIO2_MUX, .present = true },
+ [tegra_clk_audio3_mux] = { .dt_id = TEGRA210_CLK_AUDIO3_MUX, .present = true },
+ [tegra_clk_audio4_mux] = { .dt_id = TEGRA210_CLK_AUDIO4_MUX, .present = true },
+ [tegra_clk_spdif_mux] = { .dt_id = TEGRA210_CLK_SPDIF_MUX, .present = true },
+ [tegra_clk_maud] = { .dt_id = TEGRA210_CLK_MAUD, .present = true },
+ [tegra_clk_mipibif] = { .dt_id = TEGRA210_CLK_MIPIBIF, .present = true },
+ [tegra_clk_qspi] = { .dt_id = TEGRA210_CLK_QSPI, .present = true },
+ [tegra_clk_sdmmc_legacy] = { .dt_id = TEGRA210_CLK_SDMMC_LEGACY, .present = true },
+ [tegra_clk_tsecb] = { .dt_id = TEGRA210_CLK_TSECB, .present = true },
+ [tegra_clk_uartape] = { .dt_id = TEGRA210_CLK_UARTAPE, .present = true },
+ [tegra_clk_vi_i2c] = { .dt_id = TEGRA210_CLK_VI_I2C, .present = true },
+ [tegra_clk_ape] = { .dt_id = TEGRA210_CLK_APE, .present = true },
+ [tegra_clk_dbgapb] = { .dt_id = TEGRA210_CLK_DBGAPB, .present = true },
+ [tegra_clk_nvdec] = { .dt_id = TEGRA210_CLK_NVDEC, .present = true },
+ [tegra_clk_nvenc] = { .dt_id = TEGRA210_CLK_NVENC, .present = true },
+ [tegra_clk_nvjpg] = { .dt_id = TEGRA210_CLK_NVJPG, .present = true },
+ [tegra_clk_pll_c4_out0] = { .dt_id = TEGRA210_CLK_PLL_C4_OUT0, .present = true },
+ [tegra_clk_pll_c4_out1] = { .dt_id = TEGRA210_CLK_PLL_C4_OUT1, .present = true },
+ [tegra_clk_pll_c4_out2] = { .dt_id = TEGRA210_CLK_PLL_C4_OUT2, .present = true },
+ [tegra_clk_pll_c4_out3] = { .dt_id = TEGRA210_CLK_PLL_C4_OUT3, .present = true },
+ [tegra_clk_apb2ape] = { .dt_id = TEGRA210_CLK_APB2APE, .present = true },
+ [tegra_clk_pll_a1] = { .dt_id = TEGRA210_CLK_PLL_A1, .present = true },
+ [tegra_clk_ispa] = { .dt_id = TEGRA210_CLK_ISPA, .present = true },
+ [tegra_clk_cec] = { .dt_id = TEGRA210_CLK_CEC, .present = true },
+ [tegra_clk_dmic1] = { .dt_id = TEGRA210_CLK_DMIC1, .present = true },
+ [tegra_clk_dmic2] = { .dt_id = TEGRA210_CLK_DMIC2, .present = true },
+ [tegra_clk_dmic3] = { .dt_id = TEGRA210_CLK_DMIC3, .present = true },
+ [tegra_clk_dmic1_sync_clk] = { .dt_id = TEGRA210_CLK_DMIC1_SYNC_CLK, .present = true },
+ [tegra_clk_dmic2_sync_clk] = { .dt_id = TEGRA210_CLK_DMIC2_SYNC_CLK, .present = true },
+ [tegra_clk_dmic3_sync_clk] = { .dt_id = TEGRA210_CLK_DMIC3_SYNC_CLK, .present = true },
+ [tegra_clk_dmic1_sync_clk_mux] = { .dt_id = TEGRA210_CLK_DMIC1_SYNC_CLK_MUX, .present = true },
+ [tegra_clk_dmic2_sync_clk_mux] = { .dt_id = TEGRA210_CLK_DMIC2_SYNC_CLK_MUX, .present = true },
+ [tegra_clk_dmic3_sync_clk_mux] = { .dt_id = TEGRA210_CLK_DMIC3_SYNC_CLK_MUX, .present = true },
+ [tegra_clk_dp2] = { .dt_id = TEGRA210_CLK_DP2, .present = true },
+ [tegra_clk_iqc1] = { .dt_id = TEGRA210_CLK_IQC1, .present = true },
+ [tegra_clk_iqc2] = { .dt_id = TEGRA210_CLK_IQC2, .present = true },
+ [tegra_clk_pll_a_out_adsp] = { .dt_id = TEGRA210_CLK_PLL_A_OUT_ADSP, .present = true },
+ [tegra_clk_pll_a_out0_out_adsp] = { .dt_id = TEGRA210_CLK_PLL_A_OUT0_OUT_ADSP, .present = true },
+ [tegra_clk_adsp] = { .dt_id = TEGRA210_CLK_ADSP, .present = true },
+ [tegra_clk_adsp_neon] = { .dt_id = TEGRA210_CLK_ADSP_NEON, .present = true },
+};
+
+static struct tegra_devclk devclks[] __initdata = {
+ { .con_id = "clk_m", .dt_id = TEGRA210_CLK_CLK_M },
+ { .con_id = "pll_ref", .dt_id = TEGRA210_CLK_PLL_REF },
+ { .con_id = "clk_32k", .dt_id = TEGRA210_CLK_CLK_32K },
+ { .con_id = "osc", .dt_id = TEGRA210_CLK_OSC },
+ { .con_id = "osc_div2", .dt_id = TEGRA210_CLK_OSC_DIV2 },
+ { .con_id = "osc_div4", .dt_id = TEGRA210_CLK_OSC_DIV4 },
+ { .con_id = "pll_c", .dt_id = TEGRA210_CLK_PLL_C },
+ { .con_id = "pll_c_out1", .dt_id = TEGRA210_CLK_PLL_C_OUT1 },
+ { .con_id = "pll_c2", .dt_id = TEGRA210_CLK_PLL_C2 },
+ { .con_id = "pll_c3", .dt_id = TEGRA210_CLK_PLL_C3 },
+ { .con_id = "pll_p", .dt_id = TEGRA210_CLK_PLL_P },
+ { .con_id = "pll_p_out1", .dt_id = TEGRA210_CLK_PLL_P_OUT1 },
+ { .con_id = "pll_p_out2", .dt_id = TEGRA210_CLK_PLL_P_OUT2 },
+ { .con_id = "pll_p_out3", .dt_id = TEGRA210_CLK_PLL_P_OUT3 },
+ { .con_id = "pll_p_out4", .dt_id = TEGRA210_CLK_PLL_P_OUT4 },
+ { .con_id = "pll_m", .dt_id = TEGRA210_CLK_PLL_M },
+ { .con_id = "pll_x", .dt_id = TEGRA210_CLK_PLL_X },
+ { .con_id = "pll_x_out0", .dt_id = TEGRA210_CLK_PLL_X_OUT0 },
+ { .con_id = "pll_u", .dt_id = TEGRA210_CLK_PLL_U },
+ { .con_id = "pll_u_out", .dt_id = TEGRA210_CLK_PLL_U_OUT },
+ { .con_id = "pll_u_out1", .dt_id = TEGRA210_CLK_PLL_U_OUT1 },
+ { .con_id = "pll_u_out2", .dt_id = TEGRA210_CLK_PLL_U_OUT2 },
+ { .con_id = "pll_u_480M", .dt_id = TEGRA210_CLK_PLL_U_480M },
+ { .con_id = "pll_u_60M", .dt_id = TEGRA210_CLK_PLL_U_60M },
+ { .con_id = "pll_u_48M", .dt_id = TEGRA210_CLK_PLL_U_48M },
+ { .con_id = "pll_d", .dt_id = TEGRA210_CLK_PLL_D },
+ { .con_id = "pll_d_out0", .dt_id = TEGRA210_CLK_PLL_D_OUT0 },
+ { .con_id = "pll_d2", .dt_id = TEGRA210_CLK_PLL_D2 },
+ { .con_id = "pll_d2_out0", .dt_id = TEGRA210_CLK_PLL_D2_OUT0 },
+ { .con_id = "pll_a", .dt_id = TEGRA210_CLK_PLL_A },
+ { .con_id = "pll_a_out0", .dt_id = TEGRA210_CLK_PLL_A_OUT0 },
+ { .con_id = "pll_re_vco", .dt_id = TEGRA210_CLK_PLL_RE_VCO },
+ { .con_id = "pll_re_out", .dt_id = TEGRA210_CLK_PLL_RE_OUT },
+ { .con_id = "spdif_in_sync", .dt_id = TEGRA210_CLK_SPDIF_IN_SYNC },
+ { .con_id = "i2s0_sync", .dt_id = TEGRA210_CLK_I2S0_SYNC },
+ { .con_id = "i2s1_sync", .dt_id = TEGRA210_CLK_I2S1_SYNC },
+ { .con_id = "i2s2_sync", .dt_id = TEGRA210_CLK_I2S2_SYNC },
+ { .con_id = "i2s3_sync", .dt_id = TEGRA210_CLK_I2S3_SYNC },
+ { .con_id = "i2s4_sync", .dt_id = TEGRA210_CLK_I2S4_SYNC },
+ { .con_id = "vimclk_sync", .dt_id = TEGRA210_CLK_VIMCLK_SYNC },
+ { .con_id = "audio0", .dt_id = TEGRA210_CLK_AUDIO0 },
+ { .con_id = "audio1", .dt_id = TEGRA210_CLK_AUDIO1 },
+ { .con_id = "audio2", .dt_id = TEGRA210_CLK_AUDIO2 },
+ { .con_id = "audio3", .dt_id = TEGRA210_CLK_AUDIO3 },
+ { .con_id = "audio4", .dt_id = TEGRA210_CLK_AUDIO4 },
+ { .con_id = "spdif", .dt_id = TEGRA210_CLK_SPDIF },
+ { .con_id = "spdif_2x", .dt_id = TEGRA210_CLK_SPDIF_2X },
+ { .con_id = "extern1", .dt_id = TEGRA210_CLK_EXTERN1 },
+ { .con_id = "extern2", .dt_id = TEGRA210_CLK_EXTERN2 },
+ { .con_id = "extern3", .dt_id = TEGRA210_CLK_EXTERN3 },
+ { .con_id = "cclk_g", .dt_id = TEGRA210_CLK_CCLK_G },
+ { .con_id = "cclk_lp", .dt_id = TEGRA210_CLK_CCLK_LP },
+ { .con_id = "sclk", .dt_id = TEGRA210_CLK_SCLK },
+ { .con_id = "hclk", .dt_id = TEGRA210_CLK_HCLK },
+ { .con_id = "pclk", .dt_id = TEGRA210_CLK_PCLK },
+ { .con_id = "fuse", .dt_id = TEGRA210_CLK_FUSE },
+ { .dev_id = "rtc-tegra", .dt_id = TEGRA210_CLK_RTC },
+ { .dev_id = "timer", .dt_id = TEGRA210_CLK_TIMER },
+ { .con_id = "pll_c4_out0", .dt_id = TEGRA210_CLK_PLL_C4_OUT0 },
+ { .con_id = "pll_c4_out1", .dt_id = TEGRA210_CLK_PLL_C4_OUT1 },
+ { .con_id = "pll_c4_out2", .dt_id = TEGRA210_CLK_PLL_C4_OUT2 },
+ { .con_id = "pll_c4_out3", .dt_id = TEGRA210_CLK_PLL_C4_OUT3 },
+ { .con_id = "dpaux", .dt_id = TEGRA210_CLK_DPAUX },
+};
+
+static struct tegra_audio_clk_info tegra210_audio_plls[] = {
+ { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_ref" },
+ { "pll_a1", &pll_a1_params, tegra_clk_pll_a1, "pll_ref" },
+};
+
+static const char * const aclk_parents[] = {
+ "pll_a1", "pll_c", "pll_p", "pll_a_out0", "pll_c2", "pll_c3",
+ "clk_m"
+};
+
+static const unsigned int nvjpg_slcg_clkids[] = { TEGRA210_CLK_NVDEC };
+static const unsigned int nvdec_slcg_clkids[] = { TEGRA210_CLK_NVJPG };
+static const unsigned int sor_slcg_clkids[] = { TEGRA210_CLK_HDA2CODEC_2X,
+ TEGRA210_CLK_HDA2HDMI, TEGRA210_CLK_DISP1, TEGRA210_CLK_DISP2 };
+static const unsigned int disp_slcg_clkids[] = { TEGRA210_CLK_LA,
+ TEGRA210_CLK_HOST1X};
+static const unsigned int xusba_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_DEV };
+static const unsigned int xusbb_slcg_clkids[] = { TEGRA210_CLK_XUSB_HOST,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int xusbc_slcg_clkids[] = { TEGRA210_CLK_XUSB_DEV,
+ TEGRA210_CLK_XUSB_SS };
+static const unsigned int venc_slcg_clkids[] = { TEGRA210_CLK_HOST1X,
+ TEGRA210_CLK_PLL_D };
+static const unsigned int ape_slcg_clkids[] = { TEGRA210_CLK_ACLK,
+ TEGRA210_CLK_I2S0, TEGRA210_CLK_I2S1, TEGRA210_CLK_I2S2,
+ TEGRA210_CLK_I2S3, TEGRA210_CLK_I2S4, TEGRA210_CLK_SPDIF_OUT,
+ TEGRA210_CLK_D_AUDIO };
+static const unsigned int vic_slcg_clkids[] = { TEGRA210_CLK_HOST1X };
+
+static struct tegra210_domain_mbist_war tegra210_pg_mbist_war[] = {
+ [TEGRA_POWERGATE_VENC] = {
+ .handle_lvl2_ovr = tegra210_venc_mbist_war,
+ .num_clks = ARRAY_SIZE(venc_slcg_clkids),
+ .clk_init_data = venc_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_SATA] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(0) | BIT(17) | BIT(19),
+ },
+ [TEGRA_POWERGATE_MPE] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
+ .lvl2_mask = BIT(29),
+ },
+ [TEGRA_POWERGATE_SOR] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .num_clks = ARRAY_SIZE(sor_slcg_clkids),
+ .clk_init_data = sor_slcg_clkids,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(1) | BIT(2),
+ },
+ [TEGRA_POWERGATE_DIS] = {
+ .handle_lvl2_ovr = tegra210_disp_mbist_war,
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ },
+ [TEGRA_POWERGATE_DISB] = {
+ .num_clks = ARRAY_SIZE(disp_slcg_clkids),
+ .clk_init_data = disp_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRA,
+ .lvl2_mask = BIT(2),
+ },
+ [TEGRA_POWERGATE_XUSBA] = {
+ .num_clks = ARRAY_SIZE(xusba_slcg_clkids),
+ .clk_init_data = xusba_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBB] = {
+ .num_clks = ARRAY_SIZE(xusbb_slcg_clkids),
+ .clk_init_data = xusbb_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_XUSBC] = {
+ .num_clks = ARRAY_SIZE(xusbc_slcg_clkids),
+ .clk_init_data = xusbc_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRC,
+ .lvl2_mask = BIT(30) | BIT(31),
+ },
+ [TEGRA_POWERGATE_VIC] = {
+ .num_clks = ARRAY_SIZE(vic_slcg_clkids),
+ .clk_init_data = vic_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_vic_mbist_war,
+ },
+ [TEGRA_POWERGATE_NVDEC] = {
+ .num_clks = ARRAY_SIZE(nvdec_slcg_clkids),
+ .clk_init_data = nvdec_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_NVJPG] = {
+ .num_clks = ARRAY_SIZE(nvjpg_slcg_clkids),
+ .clk_init_data = nvjpg_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRE,
+ .lvl2_mask = BIT(9) | BIT(31),
+ },
+ [TEGRA_POWERGATE_AUD] = {
+ .num_clks = ARRAY_SIZE(ape_slcg_clkids),
+ .clk_init_data = ape_slcg_clkids,
+ .handle_lvl2_ovr = tegra210_ape_mbist_war,
+ },
+ [TEGRA_POWERGATE_VE2] = {
+ .handle_lvl2_ovr = tegra210_generic_mbist_war,
+ .lvl2_offset = LVL2_CLK_GATE_OVRD,
+ .lvl2_mask = BIT(22),
+ },
+};
+
+int tegra210_clk_handle_mbist_war(unsigned int id)
+{
+ int err;
+ struct tegra210_domain_mbist_war *mbist_war;
+
+ if (id >= ARRAY_SIZE(tegra210_pg_mbist_war)) {
+ WARN(1, "unknown domain id in MBIST WAR handler\n");
+ return -EINVAL;
+ }
+
+ mbist_war = &tegra210_pg_mbist_war[id];
+ if (!mbist_war->handle_lvl2_ovr)
+ return 0;
+
+ if (mbist_war->num_clks && !mbist_war->clks)
+ return -ENODEV;
+
+ err = clk_bulk_prepare_enable(mbist_war->num_clks, mbist_war->clks);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&lvl2_ovr_lock);
+
+ mbist_war->handle_lvl2_ovr(mbist_war);
+
+ mutex_unlock(&lvl2_ovr_lock);
+
+ clk_bulk_disable_unprepare(mbist_war->num_clks, mbist_war->clks);
+
+ return 0;
+}
+
+void tegra210_put_utmipll_in_iddq(void)
+{
+ u32 reg;
+
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ if (reg & UTMIPLL_HW_PWRDN_CFG0_UTMIPLL_LOCK) {
+ pr_err("trying to assert IDDQ while UTMIPLL is locked\n");
+ return;
+ }
+
+ reg |= UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_put_utmipll_in_iddq);
+
+void tegra210_put_utmipll_out_iddq(void)
+{
+ u32 reg;
+
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+}
+EXPORT_SYMBOL_GPL(tegra210_put_utmipll_out_iddq);
+
+static void tegra210_utmi_param_configure(void)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
+ if (osc_freq == utmi_parameters[i].osc_frequency)
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(utmi_parameters)) {
+ pr_err("%s: Unexpected oscillator freq %lu\n", __func__,
+ osc_freq);
+ return;
+ }
+
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_IDDQ_OVERRIDE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ udelay(10);
+
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL stable and active counts */
+ /* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */
+ reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
+ reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count);
+
+ reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
+ reg |=
+ UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i].active_delay_count);
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2);
+
+ /* Program UTMIP PLL delay and oscillator frequency counts */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+
+ reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
+ reg |=
+ UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i].enable_delay_count);
+
+ reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
+ reg |=
+ UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(utmi_parameters[i].xtal_freq_count);
+
+ reg |= UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+
+ /* Remove power downs from UTMIP PLL control bits */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+
+ udelay(20);
+
+ /* Enable samplers for SNPS, XUSB_HOST, XUSB_DEV */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2);
+ reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERUP;
+ reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERUP;
+ reg |= UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERUP;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_D_POWERDOWN;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2);
+
+ /* Setup HW control of UTMIPLL */
+ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1);
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
+ reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP;
+ writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1);
+
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_USE_LOCKDET;
+ reg &= ~UTMIPLL_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+
+ udelay(1);
+
+ reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
+ reg &= ~XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY;
+ writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
+
+ udelay(1);
+
+ /* Enable HW control UTMIPLL */
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ reg |= UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE;
+ writel_relaxed(reg, clk_base + UTMIPLL_HW_PWRDN_CFG0);
+}
+
+static int tegra210_enable_pllu(void)
+{
+ struct tegra_clk_pll_freq_table *fentry;
+ struct tegra_clk_pll pllu;
+ u32 reg;
+ int ret;
+
+ for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) {
+ if (fentry->input_rate == pll_ref_freq)
+ break;
+ }
+
+ if (!fentry->input_rate) {
+ pr_err("Unknown PLL_U reference frequency %lu\n", pll_ref_freq);
+ return -EINVAL;
+ }
+
+ /* clear IDDQ bit */
+ pllu.params = &pll_u_vco_params;
+ reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
+ reg &= ~BIT(pllu.params->iddq_bit_idx);
+ writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
+ fence_udelay(5, clk_base);
+
+ reg = readl_relaxed(clk_base + PLLU_BASE);
+ reg &= ~GENMASK(20, 0);
+ reg |= fentry->m;
+ reg |= fentry->n << 8;
+ reg |= fentry->p << 16;
+ writel(reg, clk_base + PLLU_BASE);
+ fence_udelay(1, clk_base);
+ reg |= PLL_ENABLE;
+ writel(reg, clk_base + PLLU_BASE);
+
+ /*
+ * During clocks resume, same PLLU init and enable sequence get
+ * executed. So, readx_poll_timeout_atomic can't be used here as it
+ * uses ktime_get() and timekeeping resume doesn't happen by that
+ * time. So, using tegra210_wait_for_mask for PLL LOCK.
+ */
+ ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK);
+ if (ret) {
+ pr_err("Timed out waiting for PLL_U to lock\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int tegra210_init_pllu(void)
+{
+ u32 reg;
+ int err;
+
+ tegra210_pllu_set_defaults(&pll_u_vco_params);
+ /* skip initialization when pllu is in hw controlled mode */
+ reg = readl_relaxed(clk_base + PLLU_BASE);
+ if (reg & PLLU_BASE_OVERRIDE) {
+ if (!(reg & PLL_ENABLE)) {
+ err = tegra210_enable_pllu();
+ if (err < 0) {
+ WARN_ON(1);
+ return err;
+ }
+ }
+ /* enable hw controlled mode */
+ reg = readl_relaxed(clk_base + PLLU_BASE);
+ reg &= ~PLLU_BASE_OVERRIDE;
+ writel(reg, clk_base + PLLU_BASE);
+
+ reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
+ reg |= PLLU_HW_PWRDN_CFG0_IDDQ_PD_INCLUDE |
+ PLLU_HW_PWRDN_CFG0_USE_SWITCH_DETECT |
+ PLLU_HW_PWRDN_CFG0_USE_LOCKDET;
+ reg &= ~(PLLU_HW_PWRDN_CFG0_CLK_ENABLE_SWCTL |
+ PLLU_HW_PWRDN_CFG0_CLK_SWITCH_SWCTL);
+ writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
+
+ reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
+ reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
+ writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
+ fence_udelay(1, clk_base);
+
+ reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
+ reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
+ writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
+ fence_udelay(1, clk_base);
+
+ reg = readl_relaxed(clk_base + PLLU_BASE);
+ reg &= ~PLLU_BASE_CLKENABLE_USB;
+ writel_relaxed(reg, clk_base + PLLU_BASE);
+ }
+
+ /* enable UTMIPLL hw control if not yet done by the bootloader */
+ reg = readl_relaxed(clk_base + UTMIPLL_HW_PWRDN_CFG0);
+ if (!(reg & UTMIPLL_HW_PWRDN_CFG0_SEQ_ENABLE))
+ tegra210_utmi_param_configure();
+
+ return 0;
+}
+
+/*
+ * The SOR hardware blocks are driven by two clocks: a module clock that is
+ * used to access registers and a pixel clock that is sourced from the same
+ * pixel clock that also drives the head attached to the SOR. The module
+ * clock is typically called sorX (with X being the SOR instance) and the
+ * pixel clock is called sorX_out. The source for the SOR pixel clock is
+ * referred to as the "parent" clock.
+ *
+ * On Tegra186 and newer, clocks are provided by the BPMP. Unfortunately the
+ * BPMP implementation for the SOR clocks doesn't exactly match the above in
+ * some aspects. For example, the SOR module is really clocked by the pad or
+ * sor_safe clocks, but BPMP models the sorX clock as being sourced by the
+ * pixel clocks. Conversely the sorX_out clock is sourced by the sor_safe or
+ * pad clocks on BPMP.
+ *
+ * In order to allow the display driver to deal with all SoC generations in
+ * a unified way, implement the BPMP semantics in this driver.
+ */
+
+static const char * const sor0_parents[] = {
+ "pll_d_out0",
+};
+
+static const char * const sor0_out_parents[] = {
+ "sor_safe", "sor0_pad_clkout",
+};
+
+static const char * const sor1_parents[] = {
+ "pll_p", "pll_d_out0", "pll_d2_out0", "clk_m",
+};
+
+static u32 sor1_parents_idx[] = { 0, 2, 5, 6 };
+
+static const struct clk_div_table mc_div_table_tegra210[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 4 },
+ { .val = 2, .div = 1 },
+ { .val = 3, .div = 2 },
+ { .val = 0, .div = 0 },
+};
+
+static void tegra210_clk_register_mc(const char *name,
+ const char *parent_name)
+{
+ struct clk *clk;
+
+ clk = clk_register_divider_table(NULL, name, parent_name,
+ CLK_IS_CRITICAL,
+ clk_base + CLK_SOURCE_EMC,
+ 15, 2, CLK_DIVIDER_READ_ONLY,
+ mc_div_table_tegra210, &emc_lock);
+ clks[TEGRA210_CLK_MC] = clk;
+}
+
+static const char * const sor1_out_parents[] = {
+ /*
+ * Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so
+ * the sor1_pad_clkout parent appears twice in the list below. This is
+ * merely to support clk_get_parent() if firmware happened to set
+ * these bits to 0b11. While not an invalid setting, code should
+ * always set the bits to 0b01 to select sor1_pad_clkout.
+ */
+ "sor_safe", "sor1_pad_clkout", "sor1_out", "sor1_pad_clkout",
+};
+
+static struct tegra_periph_init_data tegra210_periph[] = {
+ /*
+ * On Tegra210, the sor0 clock doesn't have a mux it bitfield 31:29,
+ * but it is hardwired to the pll_d_out0 clock.
+ */
+ TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents,
+ CLK_SOURCE_SOR0, 29, 0x0, 0, 0, 0, 0,
+ 0, 182, 0, tegra_clk_sor0, NULL, 0,
+ &sor0_lock),
+ TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents,
+ CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0,
+ 0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out,
+ NULL, 0, &sor0_lock),
+ TEGRA_INIT_DATA_TABLE("sor1", NULL, NULL, sor1_parents,
+ CLK_SOURCE_SOR1, 29, 0x7, 0, 0, 8, 1,
+ TEGRA_DIVIDER_ROUND_UP, 183, 0,
+ tegra_clk_sor1, sor1_parents_idx, 0,
+ &sor1_lock),
+ TEGRA_INIT_DATA_TABLE("sor1_out", NULL, NULL, sor1_out_parents,
+ CLK_SOURCE_SOR1, 14, 0x3, 0, 0, 0, 0,
+ 0, 0, TEGRA_PERIPH_NO_GATE,
+ tegra_clk_sor1_out, NULL, 0, &sor1_lock),
+};
+
+static const char * const la_parents[] = {
+ "pll_p", "pll_c2", "pll_c", "pll_c3", "pll_re_out1", "pll_a1", "clk_m", "pll_c4_out0"
+};
+
+static struct tegra_clk_periph tegra210_la =
+ TEGRA_CLK_PERIPH(29, 7, 9, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, 76, 0, NULL, NULL);
+
+static __init void tegra210_periph_clk_init(struct device_node *np,
+ void __iomem *clk_base,
+ void __iomem *pmc_base)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ /* xusb_ss_div2 */
+ clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0,
+ 1, 2);
+ clks[TEGRA210_CLK_XUSB_SS_DIV2] = clk;
+
+ clk = tegra_clk_register_periph_fixed("sor_safe", "pll_p", 0, clk_base,
+ 1, 17, 222);
+ clks[TEGRA210_CLK_SOR_SAFE] = clk;
+
+ clk = tegra_clk_register_periph_fixed("dpaux", "sor_safe", 0, clk_base,
+ 1, 17, 181);
+ clks[TEGRA210_CLK_DPAUX] = clk;
+
+ clk = tegra_clk_register_periph_fixed("dpaux1", "sor_safe", 0, clk_base,
+ 1, 17, 207);
+ clks[TEGRA210_CLK_DPAUX1] = clk;
+
+ /* pll_d_dsi_out */
+ clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0,
+ clk_base + PLLD_MISC0, 21, 0, &pll_d_lock);
+ clks[TEGRA210_CLK_PLL_D_DSI_OUT] = clk;
+
+ /* dsia */
+ clk = tegra_clk_register_periph_gate("dsia", "pll_d_dsi_out", 0,
+ clk_base, 0, 48,
+ periph_clk_enb_refcnt);
+ clks[TEGRA210_CLK_DSIA] = clk;
+
+ /* dsib */
+ clk = tegra_clk_register_periph_gate("dsib", "pll_d_dsi_out", 0,
+ clk_base, 0, 82,
+ periph_clk_enb_refcnt);
+ clks[TEGRA210_CLK_DSIB] = clk;
+
+ /* csi_tpg */
+ clk = clk_register_gate(NULL, "csi_tpg", "pll_d",
+ CLK_SET_RATE_PARENT, clk_base + PLLD_BASE,
+ 23, 0, &pll_d_lock);
+ clk_register_clkdev(clk, "csi_tpg", NULL);
+ clks[TEGRA210_CLK_CSI_TPG] = clk;
+
+ /* la */
+ clk = tegra_clk_register_periph("la", la_parents,
+ ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
+ CLK_SOURCE_LA, 0);
+ clks[TEGRA210_CLK_LA] = clk;
+
+ /* cml0 */
+ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
+ 0, 0, &pll_e_lock);
+ clk_register_clkdev(clk, "cml0", NULL);
+ clks[TEGRA210_CLK_CML0] = clk;
+
+ /* cml1 */
+ clk = clk_register_gate(NULL, "cml1", "pll_e", 0, clk_base + PLLE_AUX,
+ 1, 0, &pll_e_lock);
+ clk_register_clkdev(clk, "cml1", NULL);
+ clks[TEGRA210_CLK_CML1] = clk;
+
+ clk = tegra_clk_register_super_clk("aclk", aclk_parents,
+ ARRAY_SIZE(aclk_parents), 0, clk_base + 0x6e0,
+ 0, NULL);
+ clks[TEGRA210_CLK_ACLK] = clk;
+
+ clk = tegra_clk_register_sdmmc_mux_div("sdmmc2", clk_base,
+ CLK_SOURCE_SDMMC2, 9,
+ TEGRA_DIVIDER_ROUND_UP, 0, NULL);
+ clks[TEGRA210_CLK_SDMMC2] = clk;
+
+ clk = tegra_clk_register_sdmmc_mux_div("sdmmc4", clk_base,
+ CLK_SOURCE_SDMMC4, 15,
+ TEGRA_DIVIDER_ROUND_UP, 0, NULL);
+ clks[TEGRA210_CLK_SDMMC4] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra210_periph); i++) {
+ struct tegra_periph_init_data *init = &tegra210_periph[i];
+ struct clk **clkp;
+
+ clkp = tegra_lookup_dt_id(init->clk_id, tegra210_clks);
+ if (!clkp) {
+ pr_warn("clock %u not found\n", init->clk_id);
+ continue;
+ }
+
+ clk = tegra_clk_register_periph_data(clk_base, init);
+ *clkp = clk;
+ }
+
+ tegra_periph_clk_init(clk_base, pmc_base, tegra210_clks, &pll_p_params);
+
+ /* emc */
+ clk = tegra210_clk_register_emc(np, clk_base);
+ clks[TEGRA210_CLK_EMC] = clk;
+
+ /* mc */
+ tegra210_clk_register_mc("mc", "emc");
+}
+
+static void __init tegra210_pll_init(void __iomem *clk_base,
+ void __iomem *pmc)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pllc_tegra210("pll_c", "pll_ref", clk_base,
+ pmc, 0, &pll_c_params, NULL);
+ if (!WARN_ON(IS_ERR(clk)))
+ clk_register_clkdev(clk, "pll_c", NULL);
+ clks[TEGRA210_CLK_PLL_C] = clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_c_out1", NULL);
+ clks[TEGRA210_CLK_PLL_C_OUT1] = clk;
+
+ /* PLLC_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_c_ud", NULL);
+ clks[TEGRA210_CLK_PLL_C_UD] = clk;
+
+ /* PLLC2 */
+ clk = tegra_clk_register_pllc_tegra210("pll_c2", "pll_ref", clk_base,
+ pmc, 0, &pll_c2_params, NULL);
+ clk_register_clkdev(clk, "pll_c2", NULL);
+ clks[TEGRA210_CLK_PLL_C2] = clk;
+
+ /* PLLC3 */
+ clk = tegra_clk_register_pllc_tegra210("pll_c3", "pll_ref", clk_base,
+ pmc, 0, &pll_c3_params, NULL);
+ clk_register_clkdev(clk, "pll_c3", NULL);
+ clks[TEGRA210_CLK_PLL_C3] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pllm("pll_m", "osc", clk_base, pmc,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clk_register_clkdev(clk, "pll_m", NULL);
+ clks[TEGRA210_CLK_PLL_M] = clk;
+
+ /* PLLMB */
+ clk = tegra_clk_register_pllmb("pll_mb", "osc", clk_base, pmc,
+ CLK_SET_RATE_GATE, &pll_mb_params, NULL);
+ clk_register_clkdev(clk, "pll_mb", NULL);
+ clks[TEGRA210_CLK_PLL_MB] = clk;
+
+ /* PLLM_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_m_ud", NULL);
+ clks[TEGRA210_CLK_PLL_M_UD] = clk;
+
+ /* PLLMB_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_mb_ud", "pll_mb",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_mb_ud", NULL);
+ clks[TEGRA210_CLK_PLL_MB_UD] = clk;
+
+ /* PLLP_UD */
+ clk = clk_register_fixed_factor(NULL, "pll_p_ud", "pll_p",
+ 0, 1, 1);
+ clks[TEGRA210_CLK_PLL_P_UD] = clk;
+
+ /* PLLU_VCO */
+ if (!tegra210_init_pllu()) {
+ clk = clk_register_fixed_rate(NULL, "pll_u_vco", "pll_ref", 0,
+ 480*1000*1000);
+ clk_register_clkdev(clk, "pll_u_vco", NULL);
+ clks[TEGRA210_CLK_PLL_U] = clk;
+ }
+
+ /* PLLU_OUT */
+ clk = clk_register_divider_table(NULL, "pll_u_out", "pll_u_vco", 0,
+ clk_base + PLLU_BASE, 16, 4, 0,
+ pll_vco_post_div_table, NULL);
+ clk_register_clkdev(clk, "pll_u_out", NULL);
+ clks[TEGRA210_CLK_PLL_U_OUT] = clk;
+
+ /* PLLU_OUT1 */
+ clk = tegra_clk_register_divider("pll_u_out1_div", "pll_u_out",
+ clk_base + PLLU_OUTA, 0,
+ TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, &pll_u_lock);
+ clk = tegra_clk_register_pll_out("pll_u_out1", "pll_u_out1_div",
+ clk_base + PLLU_OUTA, 1, 0,
+ CLK_SET_RATE_PARENT, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_out1", NULL);
+ clks[TEGRA210_CLK_PLL_U_OUT1] = clk;
+
+ /* PLLU_OUT2 */
+ clk = tegra_clk_register_divider("pll_u_out2_div", "pll_u_out",
+ clk_base + PLLU_OUTA, 0,
+ TEGRA_DIVIDER_ROUND_UP,
+ 24, 8, 1, &pll_u_lock);
+ clk = tegra_clk_register_pll_out("pll_u_out2", "pll_u_out2_div",
+ clk_base + PLLU_OUTA, 17, 16,
+ CLK_SET_RATE_PARENT, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_out2", NULL);
+ clks[TEGRA210_CLK_PLL_U_OUT2] = clk;
+
+ /* PLLU_480M */
+ clk = clk_register_gate(NULL, "pll_u_480M", "pll_u_vco",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 22, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_480M", NULL);
+ clks[TEGRA210_CLK_PLL_U_480M] = clk;
+
+ /* PLLU_60M */
+ clk = clk_register_gate(NULL, "pll_u_60M", "pll_u_out2",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 23, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_60M", NULL);
+ clks[TEGRA210_CLK_PLL_U_60M] = clk;
+
+ /* PLLU_48M */
+ clk = clk_register_gate(NULL, "pll_u_48M", "pll_u_out1",
+ CLK_SET_RATE_PARENT, clk_base + PLLU_BASE,
+ 25, 0, &pll_u_lock);
+ clk_register_clkdev(clk, "pll_u_48M", NULL);
+ clks[TEGRA210_CLK_PLL_U_48M] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc, 0,
+ &pll_d_params, &pll_d_lock);
+ clk_register_clkdev(clk, "pll_d", NULL);
+ clks[TEGRA210_CLK_PLL_D] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_d_out0", NULL);
+ clks[TEGRA210_CLK_PLL_D_OUT0] = clk;
+
+ /* PLLRE */
+ clk = tegra_clk_register_pllre_tegra210("pll_re_vco", "pll_ref",
+ clk_base, pmc, 0,
+ &pll_re_vco_params,
+ &pll_re_lock, pll_ref_freq);
+ clk_register_clkdev(clk, "pll_re_vco", NULL);
+ clks[TEGRA210_CLK_PLL_RE_VCO] = clk;
+
+ clk = clk_register_divider_table(NULL, "pll_re_out", "pll_re_vco", 0,
+ clk_base + PLLRE_BASE, 16, 5, 0,
+ pll_vco_post_div_table, &pll_re_lock);
+ clk_register_clkdev(clk, "pll_re_out", NULL);
+ clks[TEGRA210_CLK_PLL_RE_OUT] = clk;
+
+ clk = tegra_clk_register_divider("pll_re_out1_div", "pll_re_vco",
+ clk_base + PLLRE_OUT1, 0,
+ TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_re_out1", "pll_re_out1_div",
+ clk_base + PLLRE_OUT1, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA210_CLK_PLL_RE_OUT1] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle_tegra210("pll_e", "pll_ref",
+ clk_base, 0, &pll_e_params, NULL);
+ clk_register_clkdev(clk, "pll_e", NULL);
+ clks[TEGRA210_CLK_PLL_E] = clk;
+
+ /* PLLC4 */
+ clk = tegra_clk_register_pllre("pll_c4_vco", "pll_ref", clk_base, pmc,
+ 0, &pll_c4_vco_params, NULL, pll_ref_freq);
+ clk_register_clkdev(clk, "pll_c4_vco", NULL);
+ clks[TEGRA210_CLK_PLL_C4] = clk;
+
+ /* PLLC4_OUT0 */
+ clk = clk_register_divider_table(NULL, "pll_c4_out0", "pll_c4_vco", 0,
+ clk_base + PLLC4_BASE, 19, 4, 0,
+ pll_vco_post_div_table, NULL);
+ clk_register_clkdev(clk, "pll_c4_out0", NULL);
+ clks[TEGRA210_CLK_PLL_C4_OUT0] = clk;
+
+ /* PLLC4_OUT1 */
+ clk = clk_register_fixed_factor(NULL, "pll_c4_out1", "pll_c4_vco",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll_c4_out1", NULL);
+ clks[TEGRA210_CLK_PLL_C4_OUT1] = clk;
+
+ /* PLLC4_OUT2 */
+ clk = clk_register_fixed_factor(NULL, "pll_c4_out2", "pll_c4_vco",
+ CLK_SET_RATE_PARENT, 1, 5);
+ clk_register_clkdev(clk, "pll_c4_out2", NULL);
+ clks[TEGRA210_CLK_PLL_C4_OUT2] = clk;
+
+ /* PLLC4_OUT3 */
+ clk = tegra_clk_register_divider("pll_c4_out3_div", "pll_c4_out0",
+ clk_base + PLLC4_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c4_out3", "pll_c4_out3_div",
+ clk_base + PLLC4_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clk_register_clkdev(clk, "pll_c4_out3", NULL);
+ clks[TEGRA210_CLK_PLL_C4_OUT3] = clk;
+
+ /* PLLDP */
+ clk = tegra_clk_register_pllss_tegra210("pll_dp", "pll_ref", clk_base,
+ 0, &pll_dp_params, NULL);
+ clk_register_clkdev(clk, "pll_dp", NULL);
+ clks[TEGRA210_CLK_PLL_DP] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pllss_tegra210("pll_d2", "pll_ref", clk_base,
+ 0, &pll_d2_params, NULL);
+ clk_register_clkdev(clk, "pll_d2", NULL);
+ clks[TEGRA210_CLK_PLL_D2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 1);
+ clk_register_clkdev(clk, "pll_d2_out0", NULL);
+ clks[TEGRA210_CLK_PLL_D2_OUT0] = clk;
+
+ /* PLLP_OUT2 */
+ clk = clk_register_fixed_factor(NULL, "pll_p_out2", "pll_p",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll_p_out2", NULL);
+ clks[TEGRA210_CLK_PLL_P_OUT2] = clk;
+
+}
+
+/* Tegra210 CPU clock and reset control functions */
+static void tegra210_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base + CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+}
+
+static void tegra210_disable_cpu_clock(u32 cpu)
+{
+ /* flow controller would take care in the power sequence. */
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4))
+#define car_writel(_val, _base, _off) \
+ writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
+
+static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx;
+static u32 cpu_softrst_ctx[3];
+
+static int tegra210_clk_suspend(void *data)
+{
+ unsigned int i;
+
+ clk_save_context();
+
+ /*
+ * Save the bootloader configured clock registers SPARE_REG0,
+ * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL.
+ */
+ spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0);
+ misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB);
+ clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM);
+
+ for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+ cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
+
+ tegra_clk_periph_suspend();
+ return 0;
+}
+
+static void tegra210_clk_resume(void *data)
+{
+ unsigned int i;
+
+ tegra_clk_osc_resume(clk_base);
+
+ /*
+ * Restore the bootloader configured clock registers SPARE_REG0,
+ * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context.
+ */
+ writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0);
+ writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB);
+ writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM);
+
+ for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+ car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
+
+ /*
+ * Tegra clock programming sequence recommends peripheral clock to
+ * be enabled prior to changing its clock source and divider to
+ * prevent glitchless frequency switch.
+ * So, enable all peripheral clocks before restoring their source
+ * and dividers.
+ */
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_L, clk_base + CLK_OUT_ENB_L);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_H, clk_base + CLK_OUT_ENB_H);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_U, clk_base + CLK_OUT_ENB_U);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_V, clk_base + CLK_OUT_ENB_V);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_W, clk_base + CLK_OUT_ENB_W);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_X, clk_base + CLK_OUT_ENB_X);
+ writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_Y, clk_base + CLK_OUT_ENB_Y);
+
+ /* wait for all writes to happen to have all the clocks enabled */
+ fence_udelay(2, clk_base);
+
+ /* restore PLLs and all peripheral clock rates */
+ tegra210_init_pllu();
+ clk_restore_context();
+
+ /* restore saved context of peripheral clocks and reset state */
+ tegra_clk_periph_resume();
+}
+
+static void tegra210_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra210_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_SOURCE_CSITE);
+ writel(3 << 30, clk_base + CLK_SOURCE_CSITE);
+}
+
+static void tegra210_cpu_clock_resume(void)
+{
+ writel(tegra210_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_SOURCE_CSITE);
+}
+#endif
+
+static const struct syscore_ops tegra_clk_syscore_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra210_clk_suspend,
+ .resume = tegra210_clk_resume,
+#endif
+};
+
+static struct syscore tegra_clk_syscore = {
+ .ops = &tegra_clk_syscore_ops,
+};
+
+static struct tegra_cpu_car_ops tegra210_cpu_car_ops = {
+ .wait_for_reset = tegra210_wait_cpu_in_reset,
+ .disable_clock = tegra210_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .suspend = tegra210_cpu_clock_suspend,
+ .resume = tegra210_cpu_clock_resume,
+#endif
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra210-pmc" },
+ { },
+};
+
+static struct tegra_clk_init_table init_table[] __initdata = {
+ { TEGRA210_CLK_UARTA, TEGRA210_CLK_PLL_P, 408000000, 0 },
+ { TEGRA210_CLK_UARTB, TEGRA210_CLK_PLL_P, 408000000, 0 },
+ { TEGRA210_CLK_UARTC, TEGRA210_CLK_PLL_P, 408000000, 0 },
+ { TEGRA210_CLK_UARTD, TEGRA210_CLK_PLL_P, 408000000, 0 },
+ { TEGRA210_CLK_PLL_A, TEGRA210_CLK_CLK_MAX, 564480000, 0 },
+ { TEGRA210_CLK_PLL_A_OUT0, TEGRA210_CLK_CLK_MAX, 11289600, 0 },
+ { TEGRA210_CLK_I2S0, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA210_CLK_I2S1, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA210_CLK_I2S2, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA210_CLK_I2S3, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA210_CLK_I2S4, TEGRA210_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA210_CLK_HOST1X, TEGRA210_CLK_PLL_P, 136000000, 1 },
+ { TEGRA210_CLK_SCLK_MUX, TEGRA210_CLK_PLL_P, 0, 1 },
+ { TEGRA210_CLK_SCLK, TEGRA210_CLK_CLK_MAX, 102000000, 0 },
+ { TEGRA210_CLK_DFLL_SOC, TEGRA210_CLK_PLL_P, 51000000, 1 },
+ { TEGRA210_CLK_DFLL_REF, TEGRA210_CLK_PLL_P, 51000000, 1 },
+ { TEGRA210_CLK_SBC4, TEGRA210_CLK_PLL_P, 12000000, 1 },
+ { TEGRA210_CLK_PLL_U_OUT1, TEGRA210_CLK_CLK_MAX, 48000000, 1 },
+ { TEGRA210_CLK_XUSB_GATE, TEGRA210_CLK_CLK_MAX, 0, 1 },
+ { TEGRA210_CLK_XUSB_SS_SRC, TEGRA210_CLK_PLL_U_480M, 120000000, 0 },
+ { TEGRA210_CLK_XUSB_FS_SRC, TEGRA210_CLK_PLL_U_48M, 48000000, 0 },
+ { TEGRA210_CLK_XUSB_HS_SRC, TEGRA210_CLK_XUSB_SS_SRC, 120000000, 0 },
+ { TEGRA210_CLK_XUSB_SSP_SRC, TEGRA210_CLK_XUSB_SS_SRC, 120000000, 0 },
+ { TEGRA210_CLK_XUSB_FALCON_SRC, TEGRA210_CLK_PLL_P_OUT_XUSB, 204000000, 0 },
+ { TEGRA210_CLK_XUSB_HOST_SRC, TEGRA210_CLK_PLL_P_OUT_XUSB, 102000000, 0 },
+ { TEGRA210_CLK_XUSB_DEV_SRC, TEGRA210_CLK_PLL_P_OUT_XUSB, 102000000, 0 },
+ { TEGRA210_CLK_SATA, TEGRA210_CLK_PLL_P, 104000000, 0 },
+ { TEGRA210_CLK_SATA_OOB, TEGRA210_CLK_PLL_P, 204000000, 0 },
+ { TEGRA210_CLK_MSELECT, TEGRA210_CLK_CLK_MAX, 0, 1 },
+ { TEGRA210_CLK_CSITE, TEGRA210_CLK_CLK_MAX, 0, 1 },
+ /* TODO find a way to enable this on-demand */
+ { TEGRA210_CLK_DBGAPB, TEGRA210_CLK_CLK_MAX, 0, 1 },
+ { TEGRA210_CLK_TSENSOR, TEGRA210_CLK_CLK_M, 400000, 0 },
+ { TEGRA210_CLK_I2C1, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_I2C2, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_I2C3, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_I2C4, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_I2C5, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_I2C6, TEGRA210_CLK_PLL_P, 0, 0 },
+ { TEGRA210_CLK_PLL_DP, TEGRA210_CLK_CLK_MAX, 270000000, 0 },
+ { TEGRA210_CLK_SOC_THERM, TEGRA210_CLK_PLL_P, 51000000, 0 },
+ { TEGRA210_CLK_CCLK_G, TEGRA210_CLK_CLK_MAX, 0, 1 },
+ { TEGRA210_CLK_PLL_U_OUT2, TEGRA210_CLK_CLK_MAX, 60000000, 1 },
+ { TEGRA210_CLK_SPDIF_IN_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_I2S0_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_I2S1_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_I2S2_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_I2S3_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_I2S4_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_VIMCLK_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
+ { TEGRA210_CLK_HDA, TEGRA210_CLK_PLL_P, 51000000, 0 },
+ { TEGRA210_CLK_HDA2CODEC_2X, TEGRA210_CLK_PLL_P, 48000000, 0 },
+ { TEGRA210_CLK_PWM, TEGRA210_CLK_PLL_P, 48000000, 0 },
+ /* This MUST be the last entry. */
+ { TEGRA210_CLK_CLK_MAX, TEGRA210_CLK_CLK_MAX, 0, 0 },
+};
+
+/**
+ * tegra210_clock_apply_init_table - initialize clocks on Tegra210 SoCs
+ *
+ * Program an initial clock rate and enable or disable clocks needed
+ * by the rest of the kernel, for Tegra210 SoCs. It is intended to be
+ * called by assigning a pointer to it to tegra_clk_apply_init_table -
+ * this will be called as an arch_initcall. No return value.
+ */
+static void __init tegra210_clock_apply_init_table(void)
+{
+ tegra_init_from_table(init_table, clks, TEGRA210_CLK_CLK_MAX);
+}
+
+/**
+ * tegra210_car_barrier - wait for pending writes to the CAR to complete
+ *
+ * Wait for any outstanding writes to the CAR MMIO space from this CPU
+ * to complete before continuing execution. No return value.
+ */
+static void tegra210_car_barrier(void)
+{
+ readl_relaxed(clk_base + RST_DFLL_DVCO);
+}
+
+/**
+ * tegra210_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
+ *
+ * Assert the reset line of the DFLL's DVCO. No return value.
+ */
+static void tegra210_clock_assert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v |= (1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra210_car_barrier();
+}
+
+/**
+ * tegra210_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
+ *
+ * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
+ * operate. No return value.
+ */
+static void tegra210_clock_deassert_dfll_dvco_reset(void)
+{
+ u32 v;
+
+ v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+ v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
+ writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+ tegra210_car_barrier();
+}
+
+static int tegra210_reset_assert(unsigned long id)
+{
+ if (id == TEGRA210_RST_DFLL_DVCO)
+ tegra210_clock_assert_dfll_dvco_reset();
+ else if (id == TEGRA210_RST_ADSP)
+ writel(GENMASK(26, 21) | BIT(7),
+ clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_SET);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra210_reset_deassert(unsigned long id)
+{
+ if (id == TEGRA210_RST_DFLL_DVCO)
+ tegra210_clock_deassert_dfll_dvco_reset();
+ else if (id == TEGRA210_RST_ADSP) {
+ writel(BIT(21), clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_CLR);
+ /*
+ * Considering adsp cpu clock (min: 12.5MHZ, max: 1GHz)
+ * a delay of 5us ensures that it's at least
+ * 6 * adsp_cpu_cycle_period long.
+ */
+ udelay(5);
+ writel(GENMASK(26, 22) | BIT(7),
+ clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_CLR);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void tegra210_mbist_clk_init(void)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(tegra210_pg_mbist_war); i++) {
+ unsigned int num_clks = tegra210_pg_mbist_war[i].num_clks;
+ struct clk_bulk_data *clk_data;
+
+ if (!num_clks)
+ continue;
+
+ clk_data = kmalloc_objs(*clk_data, num_clks);
+ if (WARN_ON(!clk_data))
+ return;
+
+ tegra210_pg_mbist_war[i].clks = clk_data;
+ for (j = 0; j < num_clks; j++) {
+ int clk_id = tegra210_pg_mbist_war[i].clk_init_data[j];
+ struct clk *clk = clks[clk_id];
+
+ if (WARN(IS_ERR(clk), "clk_id: %d\n", clk_id)) {
+ kfree(clk_data);
+ tegra210_pg_mbist_war[i].clks = NULL;
+ break;
+ }
+ clk_data[j].clk = clk;
+ }
+ }
+}
+
+/**
+ * tegra210_clock_init - Tegra210-specific clock initialization
+ * @np: struct device_node * of the DT node for the SoC CAR IP block
+ *
+ * Register most SoC clocks for the Tegra210 system-on-chip. Intended
+ * to be called by the OF init code when a DT node with the
+ * "nvidia,tegra210-car" string is encountered, and declared with
+ * CLK_OF_DECLARE. No return value.
+ */
+static void __init tegra210_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+ u32 value, clk_m_div;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra210 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ WARN_ON(1);
+ return;
+ }
+
+ pmc_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ WARN_ON(1);
+ return;
+ }
+
+ ahub_base = ioremap(TEGRA210_AHUB_BASE, SZ_64K);
+ if (!ahub_base) {
+ pr_err("ioremap tegra210 APE failed\n");
+ return;
+ }
+
+ dispa_base = ioremap(TEGRA210_DISPA_BASE, SZ_256K);
+ if (!dispa_base) {
+ pr_err("ioremap tegra210 DISPA failed\n");
+ return;
+ }
+
+ vic_base = ioremap(TEGRA210_VIC_BASE, SZ_256K);
+ if (!vic_base) {
+ pr_err("ioremap tegra210 VIC failed\n");
+ return;
+ }
+
+ clks = tegra_clk_init(clk_base, TEGRA210_CLK_CLK_MAX,
+ TEGRA210_CAR_BANK_COUNT);
+ if (!clks)
+ return;
+
+ value = readl(clk_base + SPARE_REG0) >> CLK_M_DIVISOR_SHIFT;
+ clk_m_div = (value & CLK_M_DIVISOR_MASK) + 1;
+
+ if (tegra_osc_clk_init(clk_base, tegra210_clks, tegra210_input_freq,
+ ARRAY_SIZE(tegra210_input_freq), clk_m_div,
+ &osc_freq, &pll_ref_freq) < 0)
+ return;
+
+ tegra_fixed_clk_init(tegra210_clks);
+ tegra210_pll_init(clk_base, pmc_base);
+ tegra210_periph_clk_init(np, clk_base, pmc_base);
+ tegra_audio_clk_init(clk_base, pmc_base, tegra210_clks,
+ tegra210_audio_plls,
+ ARRAY_SIZE(tegra210_audio_plls), 24576000);
+
+ /* For Tegra210, PLLD is the only source for DSIA & DSIB */
+ value = readl(clk_base + PLLD_BASE);
+ value &= ~BIT(25);
+ writel(value, clk_base + PLLD_BASE);
+
+ tegra_clk_apply_init_table = tegra210_clock_apply_init_table;
+
+ tegra_super_clk_gen5_init(clk_base, pmc_base, tegra210_clks,
+ &pll_x_params);
+ tegra_init_special_resets(2, tegra210_reset_assert,
+ tegra210_reset_deassert);
+
+ tegra_add_of_provider(np, of_clk_src_onecell_get);
+ tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+
+ tegra210_mbist_clk_init();
+
+ tegra_cpu_car_ops = &tegra210_cpu_car_ops;
+
+ register_syscore(&tegra_clk_syscore);
+}
+CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
new file mode 100644
index 000000000..61fe527ee
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -0,0 +1,1438 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/clk/tegra.h>
+
+#include <soc/tegra/pmc.h>
+
+#include <dt-bindings/clock/tegra30-car.h>
+
+#include "clk.h"
+#include "clk-id.h"
+
+#define OSC_CTRL 0x50
+#define OSC_CTRL_OSC_FREQ_MASK (0xF<<28)
+#define OSC_CTRL_OSC_FREQ_13MHZ (0X0<<28)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ (0X4<<28)
+#define OSC_CTRL_OSC_FREQ_12MHZ (0X8<<28)
+#define OSC_CTRL_OSC_FREQ_26MHZ (0XC<<28)
+#define OSC_CTRL_OSC_FREQ_16_8MHZ (0X1<<28)
+#define OSC_CTRL_OSC_FREQ_38_4MHZ (0X5<<28)
+#define OSC_CTRL_OSC_FREQ_48MHZ (0X9<<28)
+#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK (3<<26)
+#define OSC_CTRL_PLL_REF_DIV_1 (0<<26)
+#define OSC_CTRL_PLL_REF_DIV_2 (1<<26)
+#define OSC_CTRL_PLL_REF_DIV_4 (2<<26)
+
+#define OSC_FREQ_DET 0x58
+#define OSC_FREQ_DET_TRIG BIT(31)
+
+#define OSC_FREQ_DET_STATUS 0x5c
+#define OSC_FREQ_DET_BUSY BIT(31)
+#define OSC_FREQ_DET_CNT_MASK 0xffff
+
+#define CCLKG_BURST_POLICY 0x368
+#define SUPER_CCLKG_DIVIDER 0x36c
+#define CCLKLP_BURST_POLICY 0x370
+#define SUPER_CCLKLP_DIVIDER 0x374
+#define SCLK_BURST_POLICY 0x028
+#define SUPER_SCLK_DIVIDER 0x02c
+
+#define SYSTEM_CLK_RATE 0x030
+
+#define TEGRA30_CLK_PERIPH_BANKS 5
+#define TEGRA30_CLK_CLK_MAX 311
+
+#define PLLC_BASE 0x80
+#define PLLC_MISC 0x8c
+#define PLLM_BASE 0x90
+#define PLLM_MISC 0x9c
+#define PLLP_BASE 0xa0
+#define PLLP_MISC 0xac
+#define PLLX_BASE 0xe0
+#define PLLX_MISC 0xe4
+#define PLLD_BASE 0xd0
+#define PLLD_MISC 0xdc
+#define PLLD2_BASE 0x4b8
+#define PLLD2_MISC 0x4bc
+#define PLLE_BASE 0xe8
+#define PLLE_MISC 0xec
+#define PLLA_BASE 0xb0
+#define PLLA_MISC 0xbc
+#define PLLU_BASE 0xc0
+#define PLLU_MISC 0xcc
+
+#define PLL_MISC_LOCK_ENABLE 18
+#define PLLDU_MISC_LOCK_ENABLE 22
+#define PLLE_MISC_LOCK_ENABLE 9
+
+#define PLL_BASE_LOCK BIT(27)
+#define PLLE_MISC_LOCK BIT(11)
+
+#define PLLE_AUX 0x48c
+#define PLLC_OUT 0x84
+#define PLLM_OUT 0x94
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
+#define PLLA_OUT 0xb4
+
+#define AUDIO_SYNC_CLK_I2S0 0x4a0
+#define AUDIO_SYNC_CLK_I2S1 0x4a4
+#define AUDIO_SYNC_CLK_I2S2 0x4a8
+#define AUDIO_SYNC_CLK_I2S3 0x4ac
+#define AUDIO_SYNC_CLK_I2S4 0x4b0
+#define AUDIO_SYNC_CLK_SPDIF 0x4b4
+
+#define CLK_SOURCE_SPDIF_OUT 0x108
+#define CLK_SOURCE_PWM 0x110
+#define CLK_SOURCE_D_AUDIO 0x3d0
+#define CLK_SOURCE_DAM0 0x3d8
+#define CLK_SOURCE_DAM1 0x3dc
+#define CLK_SOURCE_DAM2 0x3e0
+#define CLK_SOURCE_3D2 0x3b0
+#define CLK_SOURCE_2D 0x15c
+#define CLK_SOURCE_HDMI 0x18c
+#define CLK_SOURCE_DSIB 0xd0
+#define CLK_SOURCE_SE 0x42c
+#define CLK_SOURCE_EMC 0x19c
+
+#define AUDIO_SYNC_DOUBLER 0x49c
+
+/* Tegra CPU clock and reset control regs */
+#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340
+#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
+#define TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR 0x34c
+#define TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470
+
+#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
+#define CPU_RESET(cpu) (0x1111ul << (cpu))
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_SOURCE_CSITE 0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4
+#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0
+#define CLK_RESET_CCLK_IDLE_POLICY 1
+#define CLK_RESET_CCLK_RUN_POLICY 2
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8
+
+/* PLLM override registers */
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra30_cpu_clk_sctx;
+#endif
+
+static void __iomem *clk_base;
+static void __iomem *pmc_base;
+static unsigned long input_freq;
+
+static DEFINE_SPINLOCK(cml_lock);
+static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_d2_lock);
+
+#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
+ _clk_num, _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_MUX8(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ 29, 3, 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP, \
+ _clk_num, _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_INT(_name, _parents, _offset, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ 30, 2, 0, 0, 8, 1, TEGRA_DIVIDER_INT | \
+ TEGRA_DIVIDER_ROUND_UP, _clk_num, \
+ _gate_flags, _clk_id)
+
+#define TEGRA_INIT_DATA_NODIV(_name, _parents, _offset, \
+ _mux_shift, _mux_width, _clk_num, \
+ _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
+ _mux_shift, _mux_width, 0, 0, 0, 0, 0,\
+ _clk_num, _gate_flags, \
+ _clk_id)
+
+static struct clk **clks;
+
+static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
+ { 12000000, 1040000000, 520, 6, 1, 8 },
+ { 13000000, 1040000000, 480, 6, 1, 8 },
+ { 16800000, 1040000000, 495, 8, 1, 8 }, /* actual: 1039.5 MHz */
+ { 19200000, 1040000000, 325, 6, 1, 6 },
+ { 26000000, 1040000000, 520, 13, 1, 8 },
+ { 12000000, 832000000, 416, 6, 1, 8 },
+ { 13000000, 832000000, 832, 13, 1, 8 },
+ { 16800000, 832000000, 396, 8, 1, 8 }, /* actual: 831.6 MHz */
+ { 19200000, 832000000, 260, 6, 1, 8 },
+ { 26000000, 832000000, 416, 13, 1, 8 },
+ { 12000000, 624000000, 624, 12, 1, 8 },
+ { 13000000, 624000000, 624, 13, 1, 8 },
+ { 16800000, 600000000, 520, 14, 1, 8 },
+ { 19200000, 624000000, 520, 16, 1, 8 },
+ { 26000000, 624000000, 624, 26, 1, 8 },
+ { 12000000, 600000000, 600, 12, 1, 8 },
+ { 13000000, 600000000, 600, 13, 1, 8 },
+ { 16800000, 600000000, 500, 14, 1, 8 },
+ { 19200000, 600000000, 375, 12, 1, 6 },
+ { 26000000, 600000000, 600, 26, 1, 8 },
+ { 12000000, 520000000, 520, 12, 1, 8 },
+ { 13000000, 520000000, 520, 13, 1, 8 },
+ { 16800000, 520000000, 495, 16, 1, 8 }, /* actual: 519.75 MHz */
+ { 19200000, 520000000, 325, 12, 1, 6 },
+ { 26000000, 520000000, 520, 26, 1, 8 },
+ { 12000000, 416000000, 416, 12, 1, 8 },
+ { 13000000, 416000000, 416, 13, 1, 8 },
+ { 16800000, 416000000, 396, 16, 1, 8 }, /* actual: 415.8 MHz */
+ { 19200000, 416000000, 260, 12, 1, 6 },
+ { 26000000, 416000000, 416, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 1, 8 },
+ { 13000000, 666000000, 666, 13, 1, 8 },
+ { 16800000, 666000000, 555, 14, 1, 8 },
+ { 19200000, 666000000, 555, 16, 1, 8 },
+ { 26000000, 666000000, 666, 26, 1, 8 },
+ { 12000000, 600000000, 600, 12, 1, 8 },
+ { 13000000, 600000000, 600, 13, 1, 8 },
+ { 16800000, 600000000, 500, 14, 1, 8 },
+ { 19200000, 600000000, 375, 12, 1, 6 },
+ { 26000000, 600000000, 600, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 2, 8 },
+ { 13000000, 216000000, 432, 13, 2, 8 },
+ { 16800000, 216000000, 360, 14, 2, 8 },
+ { 19200000, 216000000, 360, 16, 2, 8 },
+ { 26000000, 216000000, 432, 26, 2, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
+ { 9600000, 564480000, 294, 5, 1, 4 },
+ { 9600000, 552960000, 288, 5, 1, 4 },
+ { 9600000, 24000000, 5, 2, 1, 1 },
+ { 28800000, 56448000, 49, 25, 1, 1 },
+ { 28800000, 73728000, 64, 25, 1, 1 },
+ { 28800000, 24000000, 5, 6, 1, 1 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 1, 4 },
+ { 13000000, 216000000, 216, 13, 1, 4 },
+ { 16800000, 216000000, 180, 14, 1, 4 },
+ { 19200000, 216000000, 180, 16, 1, 4 },
+ { 26000000, 216000000, 216, 26, 1, 4 },
+ { 12000000, 594000000, 594, 12, 1, 8 },
+ { 13000000, 594000000, 594, 13, 1, 8 },
+ { 16800000, 594000000, 495, 14, 1, 8 },
+ { 19200000, 594000000, 495, 16, 1, 8 },
+ { 26000000, 594000000, 594, 26, 1, 8 },
+ { 12000000, 1000000000, 1000, 12, 1, 12 },
+ { 13000000, 1000000000, 1000, 13, 1, 12 },
+ { 19200000, 1000000000, 625, 12, 1, 8 },
+ { 26000000, 1000000000, 1000, 26, 1, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct pdiv_map pllu_p[] = {
+ { .pdiv = 1, .hw_val = 1 },
+ { .pdiv = 2, .hw_val = 0 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 2, 12 },
+ { 13000000, 480000000, 960, 13, 2, 12 },
+ { 16800000, 480000000, 400, 7, 2, 5 },
+ { 19200000, 480000000, 200, 4, 2, 3 },
+ { 26000000, 480000000, 960, 26, 2, 12 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
+ /* 1.7 GHz */
+ { 12000000, 1700000000, 850, 6, 1, 8 },
+ { 13000000, 1700000000, 915, 7, 1, 8 }, /* actual: 1699.2 MHz */
+ { 16800000, 1700000000, 708, 7, 1, 8 }, /* actual: 1699.2 MHz */
+ { 19200000, 1700000000, 885, 10, 1, 8 }, /* actual: 1699.2 MHz */
+ { 26000000, 1700000000, 850, 13, 1, 8 },
+ /* 1.6 GHz */
+ { 12000000, 1600000000, 800, 6, 1, 8 },
+ { 13000000, 1600000000, 738, 6, 1, 8 }, /* actual: 1599.0 MHz */
+ { 16800000, 1600000000, 857, 9, 1, 8 }, /* actual: 1599.7 MHz */
+ { 19200000, 1600000000, 500, 6, 1, 8 },
+ { 26000000, 1600000000, 800, 13, 1, 8 },
+ /* 1.5 GHz */
+ { 12000000, 1500000000, 750, 6, 1, 8 },
+ { 13000000, 1500000000, 923, 8, 1, 8 }, /* actual: 1499.8 MHz */
+ { 16800000, 1500000000, 625, 7, 1, 8 },
+ { 19200000, 1500000000, 625, 8, 1, 8 },
+ { 26000000, 1500000000, 750, 13, 1, 8 },
+ /* 1.4 GHz */
+ { 12000000, 1400000000, 700, 6, 1, 8 },
+ { 13000000, 1400000000, 969, 9, 1, 8 }, /* actual: 1399.7 MHz */
+ { 16800000, 1400000000, 1000, 12, 1, 8 },
+ { 19200000, 1400000000, 875, 12, 1, 8 },
+ { 26000000, 1400000000, 700, 13, 1, 8 },
+ /* 1.3 GHz */
+ { 12000000, 1300000000, 975, 9, 1, 8 },
+ { 13000000, 1300000000, 1000, 10, 1, 8 },
+ { 16800000, 1300000000, 928, 12, 1, 8 }, /* actual: 1299.2 MHz */
+ { 19200000, 1300000000, 812, 12, 1, 8 }, /* actual: 1299.2 MHz */
+ { 26000000, 1300000000, 650, 13, 1, 8 },
+ /* 1.2 GHz */
+ { 12000000, 1200000000, 1000, 10, 1, 8 },
+ { 13000000, 1200000000, 923, 10, 1, 8 }, /* actual: 1199.9 MHz */
+ { 16800000, 1200000000, 1000, 14, 1, 8 },
+ { 19200000, 1200000000, 1000, 16, 1, 8 },
+ { 26000000, 1200000000, 600, 13, 1, 8 },
+ /* 1.1 GHz */
+ { 12000000, 1100000000, 825, 9, 1, 8 },
+ { 13000000, 1100000000, 846, 10, 1, 8 }, /* actual: 1099.8 MHz */
+ { 16800000, 1100000000, 982, 15, 1, 8 }, /* actual: 1099.8 MHz */
+ { 19200000, 1100000000, 859, 15, 1, 8 }, /* actual: 1099.5 MHz */
+ { 26000000, 1100000000, 550, 13, 1, 8 },
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 1, 8 },
+ { 13000000, 1000000000, 1000, 13, 1, 8 },
+ { 16800000, 1000000000, 833, 14, 1, 8 }, /* actual: 999.6 MHz */
+ { 19200000, 1000000000, 625, 12, 1, 8 },
+ { 26000000, 1000000000, 1000, 26, 1, 8 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static const struct pdiv_map plle_p[] = {
+ { .pdiv = 18, .hw_val = 18 },
+ { .pdiv = 24, .hw_val = 24 },
+ { .pdiv = 0, .hw_val = 0 },
+};
+
+static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
+ /* PLLE special case: use cpcon field to store cml divider value */
+ { 12000000, 100000000, 150, 1, 18, 11 },
+ { 216000000, 100000000, 200, 18, 24, 13 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+/* PLL parameters */
+static struct tegra_clk_pll_params pll_c_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLC_BASE,
+ .misc_reg = PLLC_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_c_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct div_nmp pllm_nmp = {
+ .divn_shift = 8,
+ .divn_width = 10,
+ .override_divn_shift = 5,
+ .divm_shift = 0,
+ .divm_width = 5,
+ .override_divm_shift = 0,
+ .divp_shift = 20,
+ .divp_width = 3,
+ .override_divp_shift = 15,
+};
+
+static struct tegra_clk_pll_params pll_m_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .base_reg = PLLM_BASE,
+ .misc_reg = PLLM_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .div_nmp = &pllm_nmp,
+ .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE,
+ .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE,
+ .freq_table = pll_m_freq_table,
+ .flags = TEGRA_PLLM | TEGRA_PLL_HAS_CPCON |
+ TEGRA_PLL_SET_DCCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_FIXED,
+};
+
+static struct tegra_clk_pll_params pll_p_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLP_BASE,
+ .misc_reg = PLLP_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_p_freq_table,
+ .flags = TEGRA_PLL_FIXED | TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+ .fixed_rate = 408000000,
+};
+
+static struct tegra_clk_pll_params pll_a_params = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .base_reg = PLLA_BASE,
+ .misc_reg = PLLA_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_a_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_d_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD_BASE,
+ .misc_reg = PLLD_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_d2_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .base_reg = PLLD2_BASE,
+ .misc_reg = PLLD2_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .freq_table = pll_d_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_u_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 48000000,
+ .vco_max = 960000000,
+ .base_reg = PLLU_BASE,
+ .misc_reg = PLLU_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
+ .lock_delay = 1000,
+ .pdiv_tohw = pllu_p,
+ .freq_table = pll_u_freq_table,
+ .flags = TEGRA_PLLU | TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON |
+ TEGRA_PLL_HAS_LOCK_ENABLE,
+};
+
+static struct tegra_clk_pll_params pll_x_params __ro_after_init = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1700000000,
+ .base_reg = PLLX_BASE,
+ .misc_reg = PLLX_MISC,
+ .lock_mask = PLL_BASE_LOCK,
+ .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .freq_table = pll_x_freq_table,
+ .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_DCCON |
+ TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE,
+ .pre_rate_change = tegra_cclk_pre_pllx_rate_change,
+ .post_rate_change = tegra_cclk_post_pllx_rate_change,
+};
+
+static struct tegra_clk_pll_params pll_e_params __ro_after_init = {
+ .input_min = 12000000,
+ .input_max = 216000000,
+ .cf_min = 12000000,
+ .cf_max = 12000000,
+ .vco_min = 1200000000,
+ .vco_max = 2400000000U,
+ .base_reg = PLLE_BASE,
+ .misc_reg = PLLE_MISC,
+ .lock_mask = PLLE_MISC_LOCK,
+ .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
+ .lock_delay = 300,
+ .pdiv_tohw = plle_p,
+ .freq_table = pll_e_freq_table,
+ .flags = TEGRA_PLLE_CONFIGURE | TEGRA_PLL_FIXED |
+ TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC,
+ .fixed_rate = 100000000,
+};
+
+static unsigned long tegra30_input_freq[] = {
+ [ 0] = 13000000,
+ [ 1] = 16800000,
+ [ 4] = 19200000,
+ [ 5] = 38400000,
+ [ 8] = 12000000,
+ [ 9] = 48000000,
+ [12] = 26000000,
+};
+
+static struct tegra_devclk devclks[] = {
+ { .con_id = "pll_c", .dt_id = TEGRA30_CLK_PLL_C },
+ { .con_id = "pll_c_out1", .dt_id = TEGRA30_CLK_PLL_C_OUT1 },
+ { .con_id = "pll_p", .dt_id = TEGRA30_CLK_PLL_P },
+ { .con_id = "pll_p_out1", .dt_id = TEGRA30_CLK_PLL_P_OUT1 },
+ { .con_id = "pll_p_out2", .dt_id = TEGRA30_CLK_PLL_P_OUT2 },
+ { .con_id = "pll_p_out3", .dt_id = TEGRA30_CLK_PLL_P_OUT3 },
+ { .con_id = "pll_p_out4", .dt_id = TEGRA30_CLK_PLL_P_OUT4 },
+ { .con_id = "pll_m", .dt_id = TEGRA30_CLK_PLL_M },
+ { .con_id = "pll_m_out1", .dt_id = TEGRA30_CLK_PLL_M_OUT1 },
+ { .con_id = "pll_x", .dt_id = TEGRA30_CLK_PLL_X },
+ { .con_id = "pll_x_out0", .dt_id = TEGRA30_CLK_PLL_X_OUT0 },
+ { .con_id = "pll_u", .dt_id = TEGRA30_CLK_PLL_U },
+ { .con_id = "pll_d", .dt_id = TEGRA30_CLK_PLL_D },
+ { .con_id = "pll_d_out0", .dt_id = TEGRA30_CLK_PLL_D_OUT0 },
+ { .con_id = "pll_d2", .dt_id = TEGRA30_CLK_PLL_D2 },
+ { .con_id = "pll_d2_out0", .dt_id = TEGRA30_CLK_PLL_D2_OUT0 },
+ { .con_id = "pll_a", .dt_id = TEGRA30_CLK_PLL_A },
+ { .con_id = "pll_a_out0", .dt_id = TEGRA30_CLK_PLL_A_OUT0 },
+ { .con_id = "pll_e", .dt_id = TEGRA30_CLK_PLL_E },
+ { .con_id = "spdif_in_sync", .dt_id = TEGRA30_CLK_SPDIF_IN_SYNC },
+ { .con_id = "i2s0_sync", .dt_id = TEGRA30_CLK_I2S0_SYNC },
+ { .con_id = "i2s1_sync", .dt_id = TEGRA30_CLK_I2S1_SYNC },
+ { .con_id = "i2s2_sync", .dt_id = TEGRA30_CLK_I2S2_SYNC },
+ { .con_id = "i2s3_sync", .dt_id = TEGRA30_CLK_I2S3_SYNC },
+ { .con_id = "i2s4_sync", .dt_id = TEGRA30_CLK_I2S4_SYNC },
+ { .con_id = "vimclk_sync", .dt_id = TEGRA30_CLK_VIMCLK_SYNC },
+ { .con_id = "audio0", .dt_id = TEGRA30_CLK_AUDIO0 },
+ { .con_id = "audio1", .dt_id = TEGRA30_CLK_AUDIO1 },
+ { .con_id = "audio2", .dt_id = TEGRA30_CLK_AUDIO2 },
+ { .con_id = "audio3", .dt_id = TEGRA30_CLK_AUDIO3 },
+ { .con_id = "audio4", .dt_id = TEGRA30_CLK_AUDIO4 },
+ { .con_id = "spdif", .dt_id = TEGRA30_CLK_SPDIF },
+ { .con_id = "audio0_2x", .dt_id = TEGRA30_CLK_AUDIO0_2X },
+ { .con_id = "audio1_2x", .dt_id = TEGRA30_CLK_AUDIO1_2X },
+ { .con_id = "audio2_2x", .dt_id = TEGRA30_CLK_AUDIO2_2X },
+ { .con_id = "audio3_2x", .dt_id = TEGRA30_CLK_AUDIO3_2X },
+ { .con_id = "audio4_2x", .dt_id = TEGRA30_CLK_AUDIO4_2X },
+ { .con_id = "spdif_2x", .dt_id = TEGRA30_CLK_SPDIF_2X },
+ { .con_id = "extern1", .dt_id = TEGRA30_CLK_EXTERN1 },
+ { .con_id = "extern2", .dt_id = TEGRA30_CLK_EXTERN2 },
+ { .con_id = "extern3", .dt_id = TEGRA30_CLK_EXTERN3 },
+ { .con_id = "cclk_g", .dt_id = TEGRA30_CLK_CCLK_G },
+ { .con_id = "cclk_lp", .dt_id = TEGRA30_CLK_CCLK_LP },
+ { .con_id = "sclk", .dt_id = TEGRA30_CLK_SCLK },
+ { .con_id = "hclk", .dt_id = TEGRA30_CLK_HCLK },
+ { .con_id = "pclk", .dt_id = TEGRA30_CLK_PCLK },
+ { .con_id = "twd", .dt_id = TEGRA30_CLK_TWD },
+ { .con_id = "emc", .dt_id = TEGRA30_CLK_EMC },
+ { .con_id = "clk_32k", .dt_id = TEGRA30_CLK_CLK_32K },
+ { .con_id = "osc", .dt_id = TEGRA30_CLK_OSC },
+ { .con_id = "osc_div2", .dt_id = TEGRA30_CLK_OSC_DIV2 },
+ { .con_id = "osc_div4", .dt_id = TEGRA30_CLK_OSC_DIV4 },
+ { .con_id = "cml0", .dt_id = TEGRA30_CLK_CML0 },
+ { .con_id = "cml1", .dt_id = TEGRA30_CLK_CML1 },
+ { .con_id = "clk_m", .dt_id = TEGRA30_CLK_CLK_M },
+ { .con_id = "pll_ref", .dt_id = TEGRA30_CLK_PLL_REF },
+ { .con_id = "csus", .dev_id = "tengra_camera", .dt_id = TEGRA30_CLK_CSUS },
+ { .con_id = "vcp", .dev_id = "tegra-avp", .dt_id = TEGRA30_CLK_VCP },
+ { .con_id = "bsea", .dev_id = "tegra-avp", .dt_id = TEGRA30_CLK_BSEA },
+ { .con_id = "bsev", .dev_id = "tegra-aes", .dt_id = TEGRA30_CLK_BSEV },
+ { .con_id = "dsia", .dev_id = "tegradc.0", .dt_id = TEGRA30_CLK_DSIA },
+ { .con_id = "csi", .dev_id = "tegra_camera", .dt_id = TEGRA30_CLK_CSI },
+ { .con_id = "isp", .dev_id = "tegra_camera", .dt_id = TEGRA30_CLK_ISP },
+ { .con_id = "pcie", .dev_id = "tegra-pcie", .dt_id = TEGRA30_CLK_PCIE },
+ { .con_id = "afi", .dev_id = "tegra-pcie", .dt_id = TEGRA30_CLK_AFI },
+ { .con_id = "fuse", .dt_id = TEGRA30_CLK_FUSE },
+ { .con_id = "fuse_burn", .dev_id = "fuse-tegra", .dt_id = TEGRA30_CLK_FUSE_BURN },
+ { .con_id = "apbif", .dev_id = "tegra30-ahub", .dt_id = TEGRA30_CLK_APBIF },
+ { .con_id = "hda2hdmi", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2HDMI },
+ { .dev_id = "tegra-apbdma", .dt_id = TEGRA30_CLK_APBDMA },
+ { .dev_id = "rtc-tegra", .dt_id = TEGRA30_CLK_RTC },
+ { .dev_id = "timer", .dt_id = TEGRA30_CLK_TIMER },
+ { .dev_id = "tegra-kbc", .dt_id = TEGRA30_CLK_KBC },
+ { .dev_id = "fsl-tegra-udc", .dt_id = TEGRA30_CLK_USBD },
+ { .dev_id = "tegra-ehci.1", .dt_id = TEGRA30_CLK_USB2 },
+ { .dev_id = "tegra-ehci.2", .dt_id = TEGRA30_CLK_USB2 },
+ { .dev_id = "kfuse-tegra", .dt_id = TEGRA30_CLK_KFUSE },
+ { .dev_id = "tegra_sata_cold", .dt_id = TEGRA30_CLK_SATA_COLD },
+ { .dev_id = "dtv", .dt_id = TEGRA30_CLK_DTV },
+ { .dev_id = "tegra30-i2s.0", .dt_id = TEGRA30_CLK_I2S0 },
+ { .dev_id = "tegra30-i2s.1", .dt_id = TEGRA30_CLK_I2S1 },
+ { .dev_id = "tegra30-i2s.2", .dt_id = TEGRA30_CLK_I2S2 },
+ { .dev_id = "tegra30-i2s.3", .dt_id = TEGRA30_CLK_I2S3 },
+ { .dev_id = "tegra30-i2s.4", .dt_id = TEGRA30_CLK_I2S4 },
+ { .con_id = "spdif_out", .dev_id = "tegra30-spdif", .dt_id = TEGRA30_CLK_SPDIF_OUT },
+ { .con_id = "spdif_in", .dev_id = "tegra30-spdif", .dt_id = TEGRA30_CLK_SPDIF_IN },
+ { .con_id = "d_audio", .dev_id = "tegra30-ahub", .dt_id = TEGRA30_CLK_D_AUDIO },
+ { .dev_id = "tegra30-dam.0", .dt_id = TEGRA30_CLK_DAM0 },
+ { .dev_id = "tegra30-dam.1", .dt_id = TEGRA30_CLK_DAM1 },
+ { .dev_id = "tegra30-dam.2", .dt_id = TEGRA30_CLK_DAM2 },
+ { .con_id = "hda", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA },
+ { .con_id = "hda2codec_2x", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X },
+ { .dev_id = "spi_tegra.0", .dt_id = TEGRA30_CLK_SBC1 },
+ { .dev_id = "spi_tegra.1", .dt_id = TEGRA30_CLK_SBC2 },
+ { .dev_id = "spi_tegra.2", .dt_id = TEGRA30_CLK_SBC3 },
+ { .dev_id = "spi_tegra.3", .dt_id = TEGRA30_CLK_SBC4 },
+ { .dev_id = "spi_tegra.4", .dt_id = TEGRA30_CLK_SBC5 },
+ { .dev_id = "spi_tegra.5", .dt_id = TEGRA30_CLK_SBC6 },
+ { .dev_id = "tegra_sata_oob", .dt_id = TEGRA30_CLK_SATA_OOB },
+ { .dev_id = "tegra_sata", .dt_id = TEGRA30_CLK_SATA },
+ { .dev_id = "tegra_nand", .dt_id = TEGRA30_CLK_NDFLASH },
+ { .dev_id = "tegra_nand_speed", .dt_id = TEGRA30_CLK_NDSPEED },
+ { .dev_id = "vfir", .dt_id = TEGRA30_CLK_VFIR },
+ { .dev_id = "csite", .dt_id = TEGRA30_CLK_CSITE },
+ { .dev_id = "la", .dt_id = TEGRA30_CLK_LA },
+ { .dev_id = "tegra_w1", .dt_id = TEGRA30_CLK_OWR },
+ { .dev_id = "mipi", .dt_id = TEGRA30_CLK_MIPI },
+ { .dev_id = "tegra-tsensor", .dt_id = TEGRA30_CLK_TSENSOR },
+ { .dev_id = "i2cslow", .dt_id = TEGRA30_CLK_I2CSLOW },
+ { .dev_id = "vde", .dt_id = TEGRA30_CLK_VDE },
+ { .con_id = "vi", .dev_id = "tegra_camera", .dt_id = TEGRA30_CLK_VI },
+ { .dev_id = "epp", .dt_id = TEGRA30_CLK_EPP },
+ { .dev_id = "mpe", .dt_id = TEGRA30_CLK_MPE },
+ { .dev_id = "host1x", .dt_id = TEGRA30_CLK_HOST1X },
+ { .dev_id = "3d", .dt_id = TEGRA30_CLK_GR3D },
+ { .dev_id = "3d2", .dt_id = TEGRA30_CLK_GR3D2 },
+ { .dev_id = "2d", .dt_id = TEGRA30_CLK_GR2D },
+ { .dev_id = "se", .dt_id = TEGRA30_CLK_SE },
+ { .dev_id = "mselect", .dt_id = TEGRA30_CLK_MSELECT },
+ { .dev_id = "tegra-nor", .dt_id = TEGRA30_CLK_NOR },
+ { .dev_id = "sdhci-tegra.0", .dt_id = TEGRA30_CLK_SDMMC1 },
+ { .dev_id = "sdhci-tegra.1", .dt_id = TEGRA30_CLK_SDMMC2 },
+ { .dev_id = "sdhci-tegra.2", .dt_id = TEGRA30_CLK_SDMMC3 },
+ { .dev_id = "sdhci-tegra.3", .dt_id = TEGRA30_CLK_SDMMC4 },
+ { .dev_id = "cve", .dt_id = TEGRA30_CLK_CVE },
+ { .dev_id = "tvo", .dt_id = TEGRA30_CLK_TVO },
+ { .dev_id = "tvdac", .dt_id = TEGRA30_CLK_TVDAC },
+ { .dev_id = "actmon", .dt_id = TEGRA30_CLK_ACTMON },
+ { .con_id = "vi_sensor", .dev_id = "tegra_camera", .dt_id = TEGRA30_CLK_VI_SENSOR },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.0", .dt_id = TEGRA30_CLK_I2C1 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.1", .dt_id = TEGRA30_CLK_I2C2 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.2", .dt_id = TEGRA30_CLK_I2C3 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.3", .dt_id = TEGRA30_CLK_I2C4 },
+ { .con_id = "div-clk", .dev_id = "tegra-i2c.4", .dt_id = TEGRA30_CLK_I2C5 },
+ { .dev_id = "tegra_uart.0", .dt_id = TEGRA30_CLK_UARTA },
+ { .dev_id = "tegra_uart.1", .dt_id = TEGRA30_CLK_UARTB },
+ { .dev_id = "tegra_uart.2", .dt_id = TEGRA30_CLK_UARTC },
+ { .dev_id = "tegra_uart.3", .dt_id = TEGRA30_CLK_UARTD },
+ { .dev_id = "tegra_uart.4", .dt_id = TEGRA30_CLK_UARTE },
+ { .dev_id = "hdmi", .dt_id = TEGRA30_CLK_HDMI },
+ { .dev_id = "extern1", .dt_id = TEGRA30_CLK_EXTERN1 },
+ { .dev_id = "extern2", .dt_id = TEGRA30_CLK_EXTERN2 },
+ { .dev_id = "extern3", .dt_id = TEGRA30_CLK_EXTERN3 },
+ { .dev_id = "pwm", .dt_id = TEGRA30_CLK_PWM },
+ { .dev_id = "tegradc.0", .dt_id = TEGRA30_CLK_DISP1 },
+ { .dev_id = "tegradc.1", .dt_id = TEGRA30_CLK_DISP2 },
+ { .dev_id = "tegradc.1", .dt_id = TEGRA30_CLK_DSIB },
+};
+
+static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
+ [tegra_clk_clk_32k] = { .dt_id = TEGRA30_CLK_CLK_32K, .present = true },
+ [tegra_clk_clk_m] = { .dt_id = TEGRA30_CLK_CLK_M, .present = true },
+ [tegra_clk_osc] = { .dt_id = TEGRA30_CLK_OSC, .present = true },
+ [tegra_clk_osc_div2] = { .dt_id = TEGRA30_CLK_OSC_DIV2, .present = true },
+ [tegra_clk_osc_div4] = { .dt_id = TEGRA30_CLK_OSC_DIV4, .present = true },
+ [tegra_clk_pll_ref] = { .dt_id = TEGRA30_CLK_PLL_REF, .present = true },
+ [tegra_clk_spdif_in_sync] = { .dt_id = TEGRA30_CLK_SPDIF_IN_SYNC, .present = true },
+ [tegra_clk_i2s0_sync] = { .dt_id = TEGRA30_CLK_I2S0_SYNC, .present = true },
+ [tegra_clk_i2s1_sync] = { .dt_id = TEGRA30_CLK_I2S1_SYNC, .present = true },
+ [tegra_clk_i2s2_sync] = { .dt_id = TEGRA30_CLK_I2S2_SYNC, .present = true },
+ [tegra_clk_i2s3_sync] = { .dt_id = TEGRA30_CLK_I2S3_SYNC, .present = true },
+ [tegra_clk_i2s4_sync] = { .dt_id = TEGRA30_CLK_I2S4_SYNC, .present = true },
+ [tegra_clk_vimclk_sync] = { .dt_id = TEGRA30_CLK_VIMCLK_SYNC, .present = true },
+ [tegra_clk_audio0] = { .dt_id = TEGRA30_CLK_AUDIO0, .present = true },
+ [tegra_clk_audio1] = { .dt_id = TEGRA30_CLK_AUDIO1, .present = true },
+ [tegra_clk_audio2] = { .dt_id = TEGRA30_CLK_AUDIO2, .present = true },
+ [tegra_clk_audio3] = { .dt_id = TEGRA30_CLK_AUDIO3, .present = true },
+ [tegra_clk_audio4] = { .dt_id = TEGRA30_CLK_AUDIO4, .present = true },
+ [tegra_clk_spdif] = { .dt_id = TEGRA30_CLK_SPDIF, .present = true },
+ [tegra_clk_audio0_mux] = { .dt_id = TEGRA30_CLK_AUDIO0_MUX, .present = true },
+ [tegra_clk_audio1_mux] = { .dt_id = TEGRA30_CLK_AUDIO1_MUX, .present = true },
+ [tegra_clk_audio2_mux] = { .dt_id = TEGRA30_CLK_AUDIO2_MUX, .present = true },
+ [tegra_clk_audio3_mux] = { .dt_id = TEGRA30_CLK_AUDIO3_MUX, .present = true },
+ [tegra_clk_audio4_mux] = { .dt_id = TEGRA30_CLK_AUDIO4_MUX, .present = true },
+ [tegra_clk_spdif_mux] = { .dt_id = TEGRA30_CLK_SPDIF_MUX, .present = true },
+ [tegra_clk_audio0_2x] = { .dt_id = TEGRA30_CLK_AUDIO0_2X, .present = true },
+ [tegra_clk_audio1_2x] = { .dt_id = TEGRA30_CLK_AUDIO1_2X, .present = true },
+ [tegra_clk_audio2_2x] = { .dt_id = TEGRA30_CLK_AUDIO2_2X, .present = true },
+ [tegra_clk_audio3_2x] = { .dt_id = TEGRA30_CLK_AUDIO3_2X, .present = true },
+ [tegra_clk_audio4_2x] = { .dt_id = TEGRA30_CLK_AUDIO4_2X, .present = true },
+ [tegra_clk_spdif_2x] = { .dt_id = TEGRA30_CLK_SPDIF_2X, .present = true },
+ [tegra_clk_hclk] = { .dt_id = TEGRA30_CLK_HCLK, .present = true },
+ [tegra_clk_pclk] = { .dt_id = TEGRA30_CLK_PCLK, .present = true },
+ [tegra_clk_i2s0] = { .dt_id = TEGRA30_CLK_I2S0, .present = true },
+ [tegra_clk_i2s1] = { .dt_id = TEGRA30_CLK_I2S1, .present = true },
+ [tegra_clk_i2s2] = { .dt_id = TEGRA30_CLK_I2S2, .present = true },
+ [tegra_clk_i2s3] = { .dt_id = TEGRA30_CLK_I2S3, .present = true },
+ [tegra_clk_i2s4] = { .dt_id = TEGRA30_CLK_I2S4, .present = true },
+ [tegra_clk_spdif_in] = { .dt_id = TEGRA30_CLK_SPDIF_IN, .present = true },
+ [tegra_clk_hda] = { .dt_id = TEGRA30_CLK_HDA, .present = true },
+ [tegra_clk_hda2codec_2x] = { .dt_id = TEGRA30_CLK_HDA2CODEC_2X, .present = true },
+ [tegra_clk_sbc1] = { .dt_id = TEGRA30_CLK_SBC1, .present = true },
+ [tegra_clk_sbc2] = { .dt_id = TEGRA30_CLK_SBC2, .present = true },
+ [tegra_clk_sbc3] = { .dt_id = TEGRA30_CLK_SBC3, .present = true },
+ [tegra_clk_sbc4] = { .dt_id = TEGRA30_CLK_SBC4, .present = true },
+ [tegra_clk_sbc5] = { .dt_id = TEGRA30_CLK_SBC5, .present = true },
+ [tegra_clk_sbc6] = { .dt_id = TEGRA30_CLK_SBC6, .present = true },
+ [tegra_clk_ndflash] = { .dt_id = TEGRA30_CLK_NDFLASH, .present = true },
+ [tegra_clk_ndspeed] = { .dt_id = TEGRA30_CLK_NDSPEED, .present = true },
+ [tegra_clk_vfir] = { .dt_id = TEGRA30_CLK_VFIR, .present = true },
+ [tegra_clk_la] = { .dt_id = TEGRA30_CLK_LA, .present = true },
+ [tegra_clk_csite] = { .dt_id = TEGRA30_CLK_CSITE, .present = true },
+ [tegra_clk_owr] = { .dt_id = TEGRA30_CLK_OWR, .present = true },
+ [tegra_clk_mipi] = { .dt_id = TEGRA30_CLK_MIPI, .present = true },
+ [tegra_clk_tsensor] = { .dt_id = TEGRA30_CLK_TSENSOR, .present = true },
+ [tegra_clk_i2cslow] = { .dt_id = TEGRA30_CLK_I2CSLOW, .present = true },
+ [tegra_clk_vde] = { .dt_id = TEGRA30_CLK_VDE, .present = true },
+ [tegra_clk_vi] = { .dt_id = TEGRA30_CLK_VI, .present = true },
+ [tegra_clk_epp] = { .dt_id = TEGRA30_CLK_EPP, .present = true },
+ [tegra_clk_mpe] = { .dt_id = TEGRA30_CLK_MPE, .present = true },
+ [tegra_clk_host1x] = { .dt_id = TEGRA30_CLK_HOST1X, .present = true },
+ [tegra_clk_gr2d] = { .dt_id = TEGRA30_CLK_GR2D, .present = true },
+ [tegra_clk_gr3d] = { .dt_id = TEGRA30_CLK_GR3D, .present = true },
+ [tegra_clk_mselect] = { .dt_id = TEGRA30_CLK_MSELECT, .present = true },
+ [tegra_clk_nor] = { .dt_id = TEGRA30_CLK_NOR, .present = true },
+ [tegra_clk_sdmmc1] = { .dt_id = TEGRA30_CLK_SDMMC1, .present = true },
+ [tegra_clk_sdmmc2] = { .dt_id = TEGRA30_CLK_SDMMC2, .present = true },
+ [tegra_clk_sdmmc3] = { .dt_id = TEGRA30_CLK_SDMMC3, .present = true },
+ [tegra_clk_sdmmc4] = { .dt_id = TEGRA30_CLK_SDMMC4, .present = true },
+ [tegra_clk_cve] = { .dt_id = TEGRA30_CLK_CVE, .present = true },
+ [tegra_clk_tvo] = { .dt_id = TEGRA30_CLK_TVO, .present = true },
+ [tegra_clk_tvdac] = { .dt_id = TEGRA30_CLK_TVDAC, .present = true },
+ [tegra_clk_actmon] = { .dt_id = TEGRA30_CLK_ACTMON, .present = true },
+ [tegra_clk_vi_sensor] = { .dt_id = TEGRA30_CLK_VI_SENSOR, .present = true },
+ [tegra_clk_i2c1] = { .dt_id = TEGRA30_CLK_I2C1, .present = true },
+ [tegra_clk_i2c2] = { .dt_id = TEGRA30_CLK_I2C2, .present = true },
+ [tegra_clk_i2c3] = { .dt_id = TEGRA30_CLK_I2C3, .present = true },
+ [tegra_clk_i2c4] = { .dt_id = TEGRA30_CLK_I2C4, .present = true },
+ [tegra_clk_i2c5] = { .dt_id = TEGRA30_CLK_I2C5, .present = true },
+ [tegra_clk_uarta] = { .dt_id = TEGRA30_CLK_UARTA, .present = true },
+ [tegra_clk_uartb] = { .dt_id = TEGRA30_CLK_UARTB, .present = true },
+ [tegra_clk_uartc] = { .dt_id = TEGRA30_CLK_UARTC, .present = true },
+ [tegra_clk_uartd] = { .dt_id = TEGRA30_CLK_UARTD, .present = true },
+ [tegra_clk_uarte] = { .dt_id = TEGRA30_CLK_UARTE, .present = true },
+ [tegra_clk_extern1] = { .dt_id = TEGRA30_CLK_EXTERN1, .present = true },
+ [tegra_clk_extern2] = { .dt_id = TEGRA30_CLK_EXTERN2, .present = true },
+ [tegra_clk_extern3] = { .dt_id = TEGRA30_CLK_EXTERN3, .present = true },
+ [tegra_clk_disp1] = { .dt_id = TEGRA30_CLK_DISP1, .present = true },
+ [tegra_clk_disp2] = { .dt_id = TEGRA30_CLK_DISP2, .present = true },
+ [tegra_clk_ahbdma] = { .dt_id = TEGRA30_CLK_AHBDMA, .present = true },
+ [tegra_clk_apbdma] = { .dt_id = TEGRA30_CLK_APBDMA, .present = true },
+ [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true },
+ [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },
+ [tegra_clk_kbc] = { .dt_id = TEGRA30_CLK_KBC, .present = true },
+ [tegra_clk_vcp] = { .dt_id = TEGRA30_CLK_VCP, .present = true },
+ [tegra_clk_bsea] = { .dt_id = TEGRA30_CLK_BSEA, .present = true },
+ [tegra_clk_bsev] = { .dt_id = TEGRA30_CLK_BSEV, .present = true },
+ [tegra_clk_usbd] = { .dt_id = TEGRA30_CLK_USBD, .present = true },
+ [tegra_clk_usb2] = { .dt_id = TEGRA30_CLK_USB2, .present = true },
+ [tegra_clk_usb3] = { .dt_id = TEGRA30_CLK_USB3, .present = true },
+ [tegra_clk_csi] = { .dt_id = TEGRA30_CLK_CSI, .present = true },
+ [tegra_clk_isp] = { .dt_id = TEGRA30_CLK_ISP, .present = true },
+ [tegra_clk_kfuse] = { .dt_id = TEGRA30_CLK_KFUSE, .present = true },
+ [tegra_clk_fuse] = { .dt_id = TEGRA30_CLK_FUSE, .present = true },
+ [tegra_clk_fuse_burn] = { .dt_id = TEGRA30_CLK_FUSE_BURN, .present = true },
+ [tegra_clk_apbif] = { .dt_id = TEGRA30_CLK_APBIF, .present = true },
+ [tegra_clk_hda2hdmi] = { .dt_id = TEGRA30_CLK_HDA2HDMI, .present = true },
+ [tegra_clk_sata_cold] = { .dt_id = TEGRA30_CLK_SATA_COLD, .present = true },
+ [tegra_clk_sata_oob] = { .dt_id = TEGRA30_CLK_SATA_OOB, .present = true },
+ [tegra_clk_sata] = { .dt_id = TEGRA30_CLK_SATA, .present = true },
+ [tegra_clk_dtv] = { .dt_id = TEGRA30_CLK_DTV, .present = true },
+ [tegra_clk_pll_p] = { .dt_id = TEGRA30_CLK_PLL_P, .present = true },
+ [tegra_clk_pll_p_out1] = { .dt_id = TEGRA30_CLK_PLL_P_OUT1, .present = true },
+ [tegra_clk_pll_p_out2] = { .dt_id = TEGRA30_CLK_PLL_P_OUT2, .present = true },
+ [tegra_clk_pll_p_out3] = { .dt_id = TEGRA30_CLK_PLL_P_OUT3, .present = true },
+ [tegra_clk_pll_p_out4] = { .dt_id = TEGRA30_CLK_PLL_P_OUT4, .present = true },
+ [tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true },
+ [tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true },
+ [tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true },
+ [tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = false },
+};
+
+static const char *pll_e_parents[] = { "pll_ref", "pll_p" };
+
+static void __init tegra30_pll_init(void)
+{
+ struct clk *clk;
+
+ /* PLLC_OUT1 */
+ clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
+ clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_c_out1", "pll_c_out1_div",
+ clk_base + PLLC_OUT, 1, 0, CLK_SET_RATE_PARENT,
+ 0, NULL);
+ clks[TEGRA30_CLK_PLL_C_OUT1] = clk;
+
+ /* PLLM_OUT1 */
+ clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
+ clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
+ 8, 8, 1, NULL);
+ clk = tegra_clk_register_pll_out("pll_m_out1", "pll_m_out1_div",
+ clk_base + PLLM_OUT, 1, 0,
+ CLK_SET_RATE_PARENT, 0, NULL);
+ clks[TEGRA30_CLK_PLL_M_OUT1] = clk;
+
+ /* PLLX */
+ clk = tegra_clk_register_pll("pll_x", "pll_ref", clk_base, pmc_base, 0,
+ &pll_x_params, NULL);
+ clks[TEGRA30_CLK_PLL_X] = clk;
+
+ /* PLLX_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_x_out0", "pll_x",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA30_CLK_PLL_X_OUT0] = clk;
+
+ /* PLLU */
+ clk = tegra_clk_register_pllu("pll_u", "pll_ref", clk_base, 0,
+ &pll_u_params, NULL);
+ clks[TEGRA30_CLK_PLL_U] = clk;
+
+ /* PLLD */
+ clk = tegra_clk_register_pll("pll_d", "pll_ref", clk_base, pmc_base, 0,
+ &pll_d_params, &pll_d_lock);
+ clks[TEGRA30_CLK_PLL_D] = clk;
+
+ /* PLLD_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d_out0", "pll_d",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA30_CLK_PLL_D_OUT0] = clk;
+
+ /* PLLD2 */
+ clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc_base, 0,
+ &pll_d2_params, &pll_d2_lock);
+ clks[TEGRA30_CLK_PLL_D2] = clk;
+
+ /* PLLD2_OUT0 */
+ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA30_CLK_PLL_D2_OUT0] = clk;
+
+ /* PLLE */
+ clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents,
+ ARRAY_SIZE(pll_e_parents),
+ CLK_SET_RATE_NO_REPARENT,
+ clk_base + PLLE_AUX, 2, 1, 0, NULL);
+}
+
+static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p_cclkg", "pll_p_out4_cclkg",
+ "pll_p_out3_cclkg", "unused", "pll_x" };
+static const char *cclk_lp_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
+ "pll_p_cclklp", "pll_p_out4_cclklp",
+ "pll_p_out3_cclklp", "unused", "pll_x",
+ "pll_x_out0" };
+static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
+ "pll_p_out3", "pll_p_out2", "unused",
+ "clk_32k", "pll_m_out1" };
+
+static void __init tegra30_super_clk_init(void)
+{
+ struct clk *clk;
+
+ /*
+ * Clock input to cclk_g divided from pll_p using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_cclkg", "pll_p",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_cclkg", NULL);
+
+ /*
+ * Clock input to cclk_g divided from pll_p_out3 using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_out3_cclkg", "pll_p_out3",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out3_cclkg", NULL);
+
+ /*
+ * Clock input to cclk_g divided from pll_p_out4 using
+ * U71 divider of cclk_g.
+ */
+ clk = tegra_clk_register_divider("pll_p_out4_cclkg", "pll_p_out4",
+ clk_base + SUPER_CCLKG_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out4_cclkg", NULL);
+
+ /* CCLKG */
+ clk = tegra_clk_register_super_cclk("cclk_g", cclk_g_parents,
+ ARRAY_SIZE(cclk_g_parents),
+ CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ clk_base + CCLKG_BURST_POLICY,
+ 0, NULL);
+ clks[TEGRA30_CLK_CCLK_G] = clk;
+
+ /*
+ * Clock input to cclk_lp divided from pll_p using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_cclklp", "pll_p",
+ clk_base + SUPER_CCLKLP_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_cclklp", NULL);
+
+ /*
+ * Clock input to cclk_lp divided from pll_p_out3 using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_out3_cclklp", "pll_p_out3",
+ clk_base + SUPER_CCLKLP_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out3_cclklp", NULL);
+
+ /*
+ * Clock input to cclk_lp divided from pll_p_out4 using
+ * U71 divider of cclk_lp.
+ */
+ clk = tegra_clk_register_divider("pll_p_out4_cclklp", "pll_p_out4",
+ clk_base + SUPER_CCLKLP_DIVIDER, 0,
+ TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
+ clk_register_clkdev(clk, "pll_p_out4_cclklp", NULL);
+
+ /* CCLKLP */
+ clk = tegra_clk_register_super_mux("cclk_lp", cclk_lp_parents,
+ ARRAY_SIZE(cclk_lp_parents),
+ CLK_SET_RATE_PARENT,
+ clk_base + CCLKLP_BURST_POLICY,
+ TEGRA_DIVIDER_2, 4, 8, 9,
+ NULL);
+ clks[TEGRA30_CLK_CCLK_LP] = clk;
+
+ /* twd */
+ clk = clk_register_fixed_factor(NULL, "twd", "cclk_g",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clks[TEGRA30_CLK_TWD] = clk;
+
+ tegra_super_clk_gen4_init(clk_base, pmc_base, tegra30_clks, NULL);
+}
+
+static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p",
+ "clk_m" };
+static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
+static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p",
+ "clk_m" };
+static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" };
+static const char *mux_pllpmdacd2_clkm[] = { "pll_p", "pll_m", "pll_d_out0",
+ "pll_a_out0", "pll_c",
+ "pll_d2_out0", "clk_m" };
+static const char *mux_plld_out0_plld2_out0[] = { "pll_d_out0",
+ "pll_d2_out0" };
+static const char *pwm_parents[] = { "pll_p", "pll_c", "clk_32k", "clk_m" };
+
+static struct tegra_periph_init_data tegra_periph_clk_list[] = {
+ TEGRA_INIT_DATA_MUX("spdif_out", spdif_out_parents, CLK_SOURCE_SPDIF_OUT, 10, TEGRA_PERIPH_ON_APB, TEGRA30_CLK_SPDIF_OUT),
+ TEGRA_INIT_DATA_MUX("d_audio", mux_pllacp_clkm, CLK_SOURCE_D_AUDIO, 106, 0, TEGRA30_CLK_D_AUDIO),
+ TEGRA_INIT_DATA_MUX("dam0", mux_pllacp_clkm, CLK_SOURCE_DAM0, 108, 0, TEGRA30_CLK_DAM0),
+ TEGRA_INIT_DATA_MUX("dam1", mux_pllacp_clkm, CLK_SOURCE_DAM1, 109, 0, TEGRA30_CLK_DAM1),
+ TEGRA_INIT_DATA_MUX("dam2", mux_pllacp_clkm, CLK_SOURCE_DAM2, 110, 0, TEGRA30_CLK_DAM2),
+ TEGRA_INIT_DATA_INT("3d2", mux_pllmcpa, CLK_SOURCE_3D2, 98, 0, TEGRA30_CLK_GR3D2),
+ TEGRA_INIT_DATA_INT("se", mux_pllpcm_clkm, CLK_SOURCE_SE, 127, 0, TEGRA30_CLK_SE),
+ TEGRA_INIT_DATA_MUX8("hdmi", mux_pllpmdacd2_clkm, CLK_SOURCE_HDMI, 51, 0, TEGRA30_CLK_HDMI),
+ TEGRA_INIT_DATA("pwm", NULL, NULL, pwm_parents, CLK_SOURCE_PWM, 28, 2, 0, 0, 8, 1, 0, 17, TEGRA_PERIPH_ON_APB, TEGRA30_CLK_PWM),
+};
+
+static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
+ TEGRA_INIT_DATA_NODIV("dsib", mux_plld_out0_plld2_out0, CLK_SOURCE_DSIB, 25, 1, 82, 0, TEGRA30_CLK_DSIB),
+};
+
+static void __init tegra30_periph_clk_init(void)
+{
+ struct tegra_periph_init_data *data;
+ struct clk *clk;
+ unsigned int i;
+
+ /* dsia */
+ clk = tegra_clk_register_periph_gate("dsia", "pll_d_out0", 0, clk_base,
+ 0, 48, periph_clk_enb_refcnt);
+ clks[TEGRA30_CLK_DSIA] = clk;
+
+ /* csia_pad */
+ clk = clk_register_gate(NULL, "csia_pad", "pll_d", CLK_SET_RATE_PARENT,
+ clk_base + PLLD_BASE, 26, 0, &pll_d_lock);
+ clks[TEGRA30_CLK_CSIA_PAD] = clk;
+
+ /* csib_pad */
+ clk = clk_register_gate(NULL, "csib_pad", "pll_d2", CLK_SET_RATE_PARENT,
+ clk_base + PLLD2_BASE, 26, 0, &pll_d2_lock);
+ clks[TEGRA30_CLK_CSIB_PAD] = clk;
+
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
+ clk_base, 0, TEGRA30_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA30_CLK_CSUS] = clk;
+
+ /* pcie */
+ clk = tegra_clk_register_periph_gate("pcie", "clk_m", 0, clk_base, 0,
+ 70, periph_clk_enb_refcnt);
+ clks[TEGRA30_CLK_PCIE] = clk;
+
+ /* afi */
+ clk = tegra_clk_register_periph_gate("afi", "clk_m", 0, clk_base, 0, 72,
+ periph_clk_enb_refcnt);
+ clks[TEGRA30_CLK_AFI] = clk;
+
+ /* emc */
+ clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, true);
+
+ clks[TEGRA30_CLK_EMC] = clk;
+
+ clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
+ NULL);
+ clks[TEGRA30_CLK_MC] = clk;
+
+ /* cml0 */
+ clk = clk_register_gate(NULL, "cml0", "pll_e", 0, clk_base + PLLE_AUX,
+ 0, 0, &cml_lock);
+ clks[TEGRA30_CLK_CML0] = clk;
+
+ /* cml1 */
+ clk = clk_register_gate(NULL, "cml1", "pll_e", 0, clk_base + PLLE_AUX,
+ 1, 0, &cml_lock);
+ clks[TEGRA30_CLK_CML1] = clk;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
+ data = &tegra_periph_clk_list[i];
+ clk = tegra_clk_register_periph_data(clk_base, data);
+ clks[data->clk_id] = clk;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tegra_periph_nodiv_clk_list); i++) {
+ data = &tegra_periph_nodiv_clk_list[i];
+ clk = tegra_clk_register_periph_nodiv(data->name,
+ data->p.parent_names,
+ data->num_parents, &data->periph,
+ clk_base, data->offset);
+ clks[data->clk_id] = clk;
+ }
+
+ tegra_periph_clk_init(clk_base, pmc_base, tegra30_clks, &pll_p_params);
+}
+
+/* Tegra30 CPU clock and reset control functions */
+static void tegra30_wait_cpu_in_reset(u32 cpu)
+{
+ unsigned int reg;
+
+ do {
+ reg = readl(clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_relax();
+ } while (!(reg & (1 << cpu))); /* check CPU been reset or not */
+
+ return;
+}
+
+static void tegra30_put_cpu_in_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+ dmb();
+}
+
+static void tegra30_cpu_out_of_reset(u32 cpu)
+{
+ writel(CPU_RESET(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+ wmb();
+}
+
+static void tegra30_enable_cpu_clock(u32 cpu)
+{
+ writel(CPU_CLOCK(cpu),
+ clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ readl(clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+}
+
+static void tegra30_disable_cpu_clock(u32 cpu)
+{
+ unsigned int reg;
+
+ reg = readl(clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg | CPU_CLOCK(cpu),
+ clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static bool tegra30_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+ int cpu_pwr_status;
+
+ cpu_rst_status = readl(clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_pwr_status = tegra_pmc_cpu_is_powered(1) ||
+ tegra_pmc_cpu_is_powered(2) ||
+ tegra_pmc_cpu_is_powered(3);
+
+ if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status)
+ return false;
+
+ return true;
+}
+
+static void tegra30_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra30_cpu_clk_sctx.clk_csite_src =
+ readl(clk_base + CLK_RESET_SOURCE_CSITE);
+ writel(3 << 30, clk_base + CLK_RESET_SOURCE_CSITE);
+
+ tegra30_cpu_clk_sctx.cpu_burst =
+ readl(clk_base + CLK_RESET_CCLK_BURST);
+ tegra30_cpu_clk_sctx.pllx_base =
+ readl(clk_base + CLK_RESET_PLLX_BASE);
+ tegra30_cpu_clk_sctx.pllx_misc =
+ readl(clk_base + CLK_RESET_PLLX_MISC);
+ tegra30_cpu_clk_sctx.cclk_divider =
+ readl(clk_base + CLK_RESET_CCLK_DIVIDER);
+}
+
+static void tegra30_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+ u32 misc, base;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(clk_base + CLK_RESET_CCLK_BURST);
+ policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CLK_RESET_CCLK_IDLE_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CLK_RESET_CCLK_RUN_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) {
+ misc = readl_relaxed(clk_base + CLK_RESET_PLLX_MISC);
+ base = readl_relaxed(clk_base + CLK_RESET_PLLX_BASE);
+
+ if (misc != tegra30_cpu_clk_sctx.pllx_misc ||
+ base != tegra30_cpu_clk_sctx.pllx_base) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra30_cpu_clk_sctx.pllx_misc,
+ clk_base + CLK_RESET_PLLX_MISC);
+ writel(tegra30_cpu_clk_sctx.pllx_base,
+ clk_base + CLK_RESET_PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra30_cpu_clk_sctx.cclk_divider,
+ clk_base + CLK_RESET_CCLK_DIVIDER);
+ writel(tegra30_cpu_clk_sctx.cpu_burst,
+ clk_base + CLK_RESET_CCLK_BURST);
+
+ writel(tegra30_cpu_clk_sctx.clk_csite_src,
+ clk_base + CLK_RESET_SOURCE_CSITE);
+}
+#endif
+
+static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
+ .wait_for_reset = tegra30_wait_cpu_in_reset,
+ .put_in_reset = tegra30_put_cpu_in_reset,
+ .out_of_reset = tegra30_cpu_out_of_reset,
+ .enable_clock = tegra30_enable_cpu_clock,
+ .disable_clock = tegra30_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra30_cpu_rail_off_ready,
+ .suspend = tegra30_cpu_clock_suspend,
+ .resume = tegra30_cpu_clock_resume,
+#endif
+};
+
+static struct tegra_clk_init_table init_table[] = {
+ { TEGRA30_CLK_UARTA, TEGRA30_CLK_PLL_P, 408000000, 0 },
+ { TEGRA30_CLK_UARTB, TEGRA30_CLK_PLL_P, 408000000, 0 },
+ { TEGRA30_CLK_UARTC, TEGRA30_CLK_PLL_P, 408000000, 0 },
+ { TEGRA30_CLK_UARTD, TEGRA30_CLK_PLL_P, 408000000, 0 },
+ { TEGRA30_CLK_UARTE, TEGRA30_CLK_PLL_P, 408000000, 0 },
+ { TEGRA30_CLK_PLL_A, TEGRA30_CLK_CLK_MAX, 564480000, 0 },
+ { TEGRA30_CLK_PLL_A_OUT0, TEGRA30_CLK_CLK_MAX, 11289600, 0 },
+ { TEGRA30_CLK_I2S0, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA30_CLK_I2S1, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA30_CLK_I2S2, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA30_CLK_I2S3, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA30_CLK_I2S4, TEGRA30_CLK_PLL_A_OUT0, 11289600, 0 },
+ { TEGRA30_CLK_SDMMC1, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ { TEGRA30_CLK_SDMMC2, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ { TEGRA30_CLK_SDMMC3, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ { TEGRA30_CLK_CSITE, TEGRA30_CLK_CLK_MAX, 0, 1 },
+ { TEGRA30_CLK_MSELECT, TEGRA30_CLK_CLK_MAX, 0, 1 },
+ { TEGRA30_CLK_SBC1, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_SBC2, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_SBC3, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_SBC4, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_SBC5, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_SBC6, TEGRA30_CLK_PLL_P, 100000000, 0 },
+ { TEGRA30_CLK_PLL_C, TEGRA30_CLK_CLK_MAX, 600000000, 0 },
+ { TEGRA30_CLK_HOST1X, TEGRA30_CLK_PLL_C, 150000000, 0 },
+ { TEGRA30_CLK_TWD, TEGRA30_CLK_CLK_MAX, 0, 1 },
+ { TEGRA30_CLK_GR2D, TEGRA30_CLK_PLL_C, 300000000, 0 },
+ { TEGRA30_CLK_GR3D, TEGRA30_CLK_PLL_C, 300000000, 0 },
+ { TEGRA30_CLK_GR3D2, TEGRA30_CLK_PLL_C, 300000000, 0 },
+ { TEGRA30_CLK_PLL_U, TEGRA30_CLK_CLK_MAX, 480000000, 0 },
+ { TEGRA30_CLK_VDE, TEGRA30_CLK_PLL_C, 300000000, 0 },
+ { TEGRA30_CLK_SPDIF_IN_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_I2S0_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_I2S1_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_I2S2_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+ { TEGRA30_CLK_HDA, TEGRA30_CLK_PLL_P, 102000000, 0 },
+ { TEGRA30_CLK_HDA2CODEC_2X, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ { TEGRA30_CLK_PWM, TEGRA30_CLK_PLL_P, 48000000, 0 },
+ /* must be the last entry */
+ { TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
+};
+
+/*
+ * Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+static struct tegra_clk_duplicate tegra_clk_duplicates[] = {
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_USBD, "utmip-pad", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_USBD, "tegra-ehci.0", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_USBD, "tegra-otg", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_BSEV, "tegra-avp", "bsev"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_BSEV, "nvavp", "bsev"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_VDE, "tegra-aes", "vde"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_BSEA, "tegra-aes", "bsea"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_BSEA, "nvavp", "bsea"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_CML1, "tegra_sata_cml", NULL),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_CML0, "tegra_pcie", "cml"),
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_VCP, "nvavp", "vcp"),
+ /* must be the last entry */
+ TEGRA_CLK_DUPLICATE(TEGRA30_CLK_CLK_MAX, NULL, NULL),
+};
+
+static const struct of_device_id pmc_match[] __initconst = {
+ { .compatible = "nvidia,tegra30-pmc" },
+ { },
+};
+
+static struct tegra_audio_clk_info tegra30_audio_plls[] = {
+ { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
+};
+
+static bool tegra30_car_initialized;
+
+static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+
+ /*
+ * Timer clocks are needed early, the rest of the clocks shouldn't be
+ * available to device drivers until clock tree is fully initialized.
+ */
+ if (clkspec->args[0] != TEGRA30_CLK_RTC &&
+ clkspec->args[0] != TEGRA30_CLK_TWD &&
+ clkspec->args[0] != TEGRA30_CLK_TIMER &&
+ !tegra30_car_initialized)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ clk = of_clk_src_onecell_get(clkspec, data);
+ if (IS_ERR(clk))
+ return clk;
+
+ hw = __clk_get_hw(clk);
+
+ if (clkspec->args[0] == TEGRA30_CLK_EMC) {
+ if (!tegra20_clk_emc_driver_available(hw))
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return clk;
+}
+
+static void __init tegra30_clock_init(struct device_node *np)
+{
+ struct device_node *node;
+
+ clk_base = of_iomap(np, 0);
+ if (!clk_base) {
+ pr_err("ioremap tegra30 CAR failed\n");
+ return;
+ }
+
+ node = of_find_matching_node(NULL, pmc_match);
+ if (!node) {
+ pr_err("Failed to find pmc node\n");
+ BUG();
+ }
+
+ pmc_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!pmc_base) {
+ pr_err("Can't map pmc registers\n");
+ BUG();
+ }
+
+ clks = tegra_clk_init(clk_base, TEGRA30_CLK_CLK_MAX,
+ TEGRA30_CLK_PERIPH_BANKS);
+ if (!clks)
+ return;
+
+ if (tegra_osc_clk_init(clk_base, tegra30_clks, tegra30_input_freq,
+ ARRAY_SIZE(tegra30_input_freq), 1, &input_freq,
+ NULL) < 0)
+ return;
+
+ tegra_fixed_clk_init(tegra30_clks);
+ tegra30_pll_init();
+ tegra30_super_clk_init();
+ tegra30_periph_clk_init();
+ tegra_audio_clk_init(clk_base, pmc_base, tegra30_clks,
+ tegra30_audio_plls,
+ ARRAY_SIZE(tegra30_audio_plls), 24000000);
+
+ tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
+
+ tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
+
+ tegra_cpu_car_ops = &tegra30_cpu_car_ops;
+}
+CLK_OF_DECLARE_DRIVER(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
+
+/*
+ * Clocks that use runtime PM can't be created at the tegra30_clock_init
+ * time because drivers' base isn't initialized yet, and thus platform
+ * devices can't be created for the clocks. Hence we need to split the
+ * registration of the clocks into two phases. The first phase registers
+ * essential clocks which don't require RPM and are actually used during
+ * early boot. The second phase registers clocks which use RPM and this
+ * is done when device drivers' core API is ready.
+ */
+static int tegra30_car_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ /* PLLC */
+ clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
+ &pll_c_params, NULL);
+ clks[TEGRA30_CLK_PLL_C] = clk;
+
+ /* PLLE */
+ clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
+ CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
+ clks[TEGRA30_CLK_PLL_E] = clk;
+
+ /* PLLM */
+ clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
+ CLK_SET_RATE_GATE, &pll_m_params, NULL);
+ clks[TEGRA30_CLK_PLL_M] = clk;
+
+ /* SCLK */
+ clk = tegra_clk_register_super_mux("sclk", sclk_parents,
+ ARRAY_SIZE(sclk_parents),
+ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+ clk_base + SCLK_BURST_POLICY,
+ 0, 4, 0, 0, NULL);
+ clks[TEGRA30_CLK_SCLK] = clk;
+
+ tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
+ tegra_init_from_table(init_table, clks, TEGRA30_CLK_CLK_MAX);
+ tegra30_car_initialized = true;
+
+ return 0;
+}
+
+static const struct of_device_id tegra30_car_match[] = {
+ { .compatible = "nvidia,tegra30-car" },
+ { }
+};
+
+static struct platform_driver tegra30_car_driver = {
+ .driver = {
+ .name = "tegra30-car",
+ .of_match_table = tegra30_car_match,
+ .suppress_bind_attrs = true,
+ },
+ .probe = tegra30_car_probe,
+};
+
+/*
+ * Clock driver must be registered before memory controller driver,
+ * which doesn't support deferred probing for today and is registered
+ * from arch init-level.
+ */
+static int tegra30_car_init(void)
+{
+ return platform_driver_register(&tegra30_car_driver);
+}
+postcore_initcall(tegra30_car_init);
diff --git a/drivers/clk/tegra/clk-utils.c b/drivers/clk/tegra/clk-utils.c
new file mode 100644
index 000000000..1a5daae4e
--- /dev/null
+++ b/drivers/clk/tegra/clk-utils.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <asm/div64.h>
+
+#include "clk.h"
+
+#define div_mask(w) ((1 << (w)) - 1)
+
+int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
+ u8 frac_width, u8 flags)
+{
+ u64 divider_ux1 = parent_rate;
+ int mul;
+
+ if (!rate)
+ return 0;
+
+ mul = 1 << frac_width;
+
+ if (!(flags & TEGRA_DIVIDER_INT))
+ divider_ux1 *= mul;
+
+ if (flags & TEGRA_DIVIDER_ROUND_UP)
+ divider_ux1 += rate - 1;
+
+ do_div(divider_ux1, rate);
+
+ if (flags & TEGRA_DIVIDER_INT)
+ divider_ux1 *= mul;
+
+ if (divider_ux1 < mul)
+ return 0;
+
+ divider_ux1 -= mul;
+
+ if (divider_ux1 > div_mask(width))
+ return div_mask(width);
+
+ return divider_ux1;
+}
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
new file mode 100644
index 000000000..786554222
--- /dev/null
+++ b/drivers/clk/tegra/clk.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clk/tegra.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset-controller.h>
+#include <linux/string_helpers.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "clk.h"
+
+/* Global data of Tegra CPU CAR ops */
+static struct device_node *tegra_car_np;
+static struct tegra_cpu_car_ops dummy_car_ops;
+struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
+
+int *periph_clk_enb_refcnt;
+static int periph_banks;
+static u32 *periph_state_ctx;
+static struct clk **clks;
+static int clk_num;
+static struct clk_onecell_data clk_data;
+
+/* Handlers for SoC-specific reset lines */
+static int (*special_reset_assert)(unsigned long);
+static int (*special_reset_deassert)(unsigned long);
+static unsigned int num_special_reset;
+
+static const struct tegra_clk_periph_regs periph_regs[] = {
+ [0] = {
+ .enb_reg = CLK_OUT_ENB_L,
+ .enb_set_reg = CLK_OUT_ENB_SET_L,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_L,
+ .rst_reg = RST_DEVICES_L,
+ .rst_set_reg = RST_DEVICES_SET_L,
+ .rst_clr_reg = RST_DEVICES_CLR_L,
+ },
+ [1] = {
+ .enb_reg = CLK_OUT_ENB_H,
+ .enb_set_reg = CLK_OUT_ENB_SET_H,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_H,
+ .rst_reg = RST_DEVICES_H,
+ .rst_set_reg = RST_DEVICES_SET_H,
+ .rst_clr_reg = RST_DEVICES_CLR_H,
+ },
+ [2] = {
+ .enb_reg = CLK_OUT_ENB_U,
+ .enb_set_reg = CLK_OUT_ENB_SET_U,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_U,
+ .rst_reg = RST_DEVICES_U,
+ .rst_set_reg = RST_DEVICES_SET_U,
+ .rst_clr_reg = RST_DEVICES_CLR_U,
+ },
+ [3] = {
+ .enb_reg = CLK_OUT_ENB_V,
+ .enb_set_reg = CLK_OUT_ENB_SET_V,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_V,
+ .rst_reg = RST_DEVICES_V,
+ .rst_set_reg = RST_DEVICES_SET_V,
+ .rst_clr_reg = RST_DEVICES_CLR_V,
+ },
+ [4] = {
+ .enb_reg = CLK_OUT_ENB_W,
+ .enb_set_reg = CLK_OUT_ENB_SET_W,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_W,
+ .rst_reg = RST_DEVICES_W,
+ .rst_set_reg = RST_DEVICES_SET_W,
+ .rst_clr_reg = RST_DEVICES_CLR_W,
+ },
+ [5] = {
+ .enb_reg = CLK_OUT_ENB_X,
+ .enb_set_reg = CLK_OUT_ENB_SET_X,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_X,
+ .rst_reg = RST_DEVICES_X,
+ .rst_set_reg = RST_DEVICES_SET_X,
+ .rst_clr_reg = RST_DEVICES_CLR_X,
+ },
+ [6] = {
+ .enb_reg = CLK_OUT_ENB_Y,
+ .enb_set_reg = CLK_OUT_ENB_SET_Y,
+ .enb_clr_reg = CLK_OUT_ENB_CLR_Y,
+ .rst_reg = RST_DEVICES_Y,
+ .rst_set_reg = RST_DEVICES_SET_Y,
+ .rst_clr_reg = RST_DEVICES_CLR_Y,
+ },
+};
+
+static void __iomem *clk_base;
+
+static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ /*
+ * If peripheral is on the APB bus then we must read the APB bus to
+ * flush the write operation in apb bus. This will avoid peripheral
+ * access after disabling clock. Since the reset driver has no
+ * knowledge of which reset IDs represent which devices, simply do
+ * this all the time.
+ */
+ tegra_read_chipid();
+
+ if (id < periph_banks * 32) {
+ writel_relaxed(BIT(id % 32),
+ clk_base + periph_regs[id / 32].rst_set_reg);
+ return 0;
+ } else if (id < periph_banks * 32 + num_special_reset) {
+ return special_reset_assert(id);
+ }
+
+ return -EINVAL;
+}
+
+static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ if (id < periph_banks * 32) {
+ writel_relaxed(BIT(id % 32),
+ clk_base + periph_regs[id / 32].rst_clr_reg);
+ return 0;
+ } else if (id < periph_banks * 32 + num_special_reset) {
+ return special_reset_deassert(id);
+ }
+
+ return -EINVAL;
+}
+
+static int tegra_clk_rst_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int err;
+
+ err = tegra_clk_rst_assert(rcdev, id);
+ if (err)
+ return err;
+
+ udelay(1);
+
+ return tegra_clk_rst_deassert(rcdev, id);
+}
+
+const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
+{
+ int reg_bank = clkid / 32;
+
+ if (reg_bank < periph_banks)
+ return &periph_regs[reg_bank];
+ else {
+ WARN_ON(1);
+ return NULL;
+ }
+}
+
+void tegra_clk_set_pllp_out_cpu(bool enable)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
+ if (enable)
+ val |= CLK_ENB_PLLP_OUT_CPU;
+ else
+ val &= ~CLK_ENB_PLLP_OUT_CPU;
+
+ writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
+}
+
+void tegra_clk_periph_suspend(void)
+{
+ unsigned int i, idx;
+
+ idx = 0;
+ for (i = 0; i < periph_banks; i++, idx++)
+ periph_state_ctx[idx] =
+ readl_relaxed(clk_base + periph_regs[i].enb_reg);
+
+ for (i = 0; i < periph_banks; i++, idx++)
+ periph_state_ctx[idx] =
+ readl_relaxed(clk_base + periph_regs[i].rst_reg);
+}
+
+void tegra_clk_periph_resume(void)
+{
+ unsigned int i, idx;
+
+ idx = 0;
+ for (i = 0; i < periph_banks; i++, idx++)
+ writel_relaxed(periph_state_ctx[idx],
+ clk_base + periph_regs[i].enb_reg);
+ /*
+ * All non-boot peripherals will be in reset state on resume.
+ * Wait for 5us of reset propagation delay before de-asserting
+ * the peripherals based on the saved context.
+ */
+ fence_udelay(5, clk_base);
+
+ for (i = 0; i < periph_banks; i++, idx++)
+ writel_relaxed(periph_state_ctx[idx],
+ clk_base + periph_regs[i].rst_reg);
+
+ fence_udelay(2, clk_base);
+}
+
+static int tegra_clk_periph_ctx_init(int banks)
+{
+ periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx),
+ GFP_KERNEL);
+ if (!periph_state_ctx)
+ return -ENOMEM;
+
+ return 0;
+}
+
+struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
+{
+ clk_base = regs;
+
+ if (WARN_ON(banks > ARRAY_SIZE(periph_regs)))
+ return NULL;
+
+ periph_clk_enb_refcnt = kzalloc_objs(*periph_clk_enb_refcnt, 32 * banks);
+ if (!periph_clk_enb_refcnt)
+ return NULL;
+
+ periph_banks = banks;
+
+ clks = kzalloc_objs(struct clk *, num);
+ if (!clks) {
+ kfree(periph_clk_enb_refcnt);
+ return NULL;
+ }
+
+ clk_num = num;
+
+ if (IS_ENABLED(CONFIG_PM_SLEEP)) {
+ if (tegra_clk_periph_ctx_init(banks)) {
+ kfree(periph_clk_enb_refcnt);
+ kfree(clks);
+ return NULL;
+ }
+ }
+
+ return clks;
+}
+
+void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
+ struct clk *clks[], int clk_max)
+{
+ struct clk *clk;
+
+ for (; dup_list->clk_id < clk_max; dup_list++) {
+ clk = clks[dup_list->clk_id];
+ dup_list->lookup.clk = clk;
+ clkdev_add(&dup_list->lookup);
+ }
+}
+
+void tegra_init_from_table(struct tegra_clk_init_table *tbl,
+ struct clk *clks[], int clk_max)
+{
+ struct clk *clk;
+
+ for (; tbl->clk_id < clk_max; tbl++) {
+ clk = clks[tbl->clk_id];
+ if (IS_ERR_OR_NULL(clk)) {
+ pr_err("%s: invalid entry %ld in clks array for id %d\n",
+ __func__, PTR_ERR(clk), tbl->clk_id);
+ WARN_ON(1);
+
+ continue;
+ }
+
+ if (tbl->parent_id < clk_max) {
+ struct clk *parent = clks[tbl->parent_id];
+ if (clk_set_parent(clk, parent)) {
+ pr_err("%s: Failed to set parent %s of %s\n",
+ __func__, __clk_get_name(parent),
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+ }
+
+ if (tbl->rate)
+ if (clk_set_rate(clk, tbl->rate)) {
+ pr_err("%s: Failed to set rate %lu of %s\n",
+ __func__, tbl->rate,
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+
+ if (tbl->state)
+ if (clk_prepare_enable(clk)) {
+ pr_err("%s: Failed to enable %s\n", __func__,
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+ }
+}
+
+static const struct reset_control_ops rst_ops = {
+ .assert = tegra_clk_rst_assert,
+ .deassert = tegra_clk_rst_deassert,
+ .reset = tegra_clk_rst_reset,
+};
+
+static struct reset_controller_dev rst_ctlr = {
+ .ops = &rst_ops,
+ .owner = THIS_MODULE,
+ .of_reset_n_cells = 1,
+};
+
+void __init tegra_add_of_provider(struct device_node *np,
+ void *clk_src_onecell_get)
+{
+ int i;
+
+ tegra_car_np = np;
+
+ for (i = 0; i < clk_num; i++) {
+ if (IS_ERR(clks[i])) {
+ pr_err
+ ("Tegra clk %d: register failed with %ld\n",
+ i, PTR_ERR(clks[i]));
+ }
+ if (!clks[i])
+ clks[i] = ERR_PTR(-EINVAL);
+ }
+
+ clk_data.clks = clks;
+ clk_data.clk_num = clk_num;
+ of_clk_add_provider(np, clk_src_onecell_get, &clk_data);
+
+ rst_ctlr.of_node = np;
+ rst_ctlr.nr_resets = periph_banks * 32 + num_special_reset;
+ reset_controller_register(&rst_ctlr);
+}
+
+void __init tegra_init_special_resets(unsigned int num,
+ int (*assert)(unsigned long),
+ int (*deassert)(unsigned long))
+{
+ num_special_reset = num;
+ special_reset_assert = assert;
+ special_reset_deassert = deassert;
+}
+
+void tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, dev_clks++)
+ clk_register_clkdev(clks[dev_clks->dt_id], dev_clks->con_id,
+ dev_clks->dev_id);
+
+ for (i = 0; i < clk_num; i++) {
+ if (!IS_ERR_OR_NULL(clks[i]))
+ clk_register_clkdev(clks[i], __clk_get_name(clks[i]),
+ "tegra-clk-debug");
+ }
+}
+
+struct clk ** __init tegra_lookup_dt_id(int clk_id,
+ struct tegra_clk *tegra_clk)
+{
+ if (tegra_clk[clk_id].present)
+ return &clks[tegra_clk[clk_id].dt_id];
+ else
+ return NULL;
+}
+
+static struct device_node *tegra_clk_get_of_node(struct clk_hw *hw)
+{
+ struct device_node *np;
+ char *node_name;
+
+ node_name = kstrdup_and_replace(hw->init->name, '_', '-', GFP_KERNEL);
+ if (!node_name)
+ return NULL;
+
+ for_each_child_of_node(tegra_car_np, np) {
+ if (!strcmp(np->name, node_name))
+ break;
+ }
+
+ kfree(node_name);
+
+ return np;
+}
+
+struct clk *tegra_clk_dev_register(struct clk_hw *hw)
+{
+ struct platform_device *pdev, *parent;
+ const char *dev_name = NULL;
+ struct device *dev = NULL;
+ struct device_node *np;
+
+ np = tegra_clk_get_of_node(hw);
+
+ if (!of_device_is_available(np))
+ goto put_node;
+
+ dev_name = kasprintf(GFP_KERNEL, "tegra_clk_%s", hw->init->name);
+ if (!dev_name)
+ goto put_node;
+
+ parent = of_find_device_by_node(tegra_car_np);
+ if (parent) {
+ pdev = of_platform_device_create(np, dev_name, &parent->dev);
+ put_device(&parent->dev);
+
+ if (!pdev) {
+ pr_err("%s: failed to create device for %pOF\n",
+ __func__, np);
+ goto free_name;
+ }
+
+ dev = &pdev->dev;
+ pm_runtime_enable(dev);
+ } else {
+ WARN(1, "failed to find device for %pOF\n", tegra_car_np);
+ }
+
+free_name:
+ kfree(dev_name);
+put_node:
+ of_node_put(np);
+
+ return clk_register(dev, hw);
+}
+
+tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
+
+static int __init tegra_clocks_apply_init_table(void)
+{
+ if (!tegra_clk_apply_init_table)
+ return 0;
+
+ tegra_clk_apply_init_table();
+
+ return 0;
+}
+arch_initcall(tegra_clocks_apply_init_table);
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
new file mode 100644
index 000000000..73efd2ff3
--- /dev/null
+++ b/drivers/clk/tegra/clk.h
@@ -0,0 +1,929 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+ /*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#ifndef __TEGRA_CLK_H
+#define __TEGRA_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+
+#define CLK_OUT_ENB_L 0x010
+#define CLK_OUT_ENB_H 0x014
+#define CLK_OUT_ENB_U 0x018
+#define CLK_OUT_ENB_V 0x360
+#define CLK_OUT_ENB_W 0x364
+#define CLK_OUT_ENB_X 0x280
+#define CLK_OUT_ENB_Y 0x298
+#define CLK_ENB_PLLP_OUT_CPU BIT(31)
+#define CLK_OUT_ENB_SET_L 0x320
+#define CLK_OUT_ENB_CLR_L 0x324
+#define CLK_OUT_ENB_SET_H 0x328
+#define CLK_OUT_ENB_CLR_H 0x32c
+#define CLK_OUT_ENB_SET_U 0x330
+#define CLK_OUT_ENB_CLR_U 0x334
+#define CLK_OUT_ENB_SET_V 0x440
+#define CLK_OUT_ENB_CLR_V 0x444
+#define CLK_OUT_ENB_SET_W 0x448
+#define CLK_OUT_ENB_CLR_W 0x44c
+#define CLK_OUT_ENB_SET_X 0x284
+#define CLK_OUT_ENB_CLR_X 0x288
+#define CLK_OUT_ENB_SET_Y 0x29c
+#define CLK_OUT_ENB_CLR_Y 0x2a0
+
+#define RST_DEVICES_L 0x004
+#define RST_DEVICES_H 0x008
+#define RST_DEVICES_U 0x00C
+#define RST_DEVICES_V 0x358
+#define RST_DEVICES_W 0x35C
+#define RST_DEVICES_X 0x28C
+#define RST_DEVICES_Y 0x2a4
+#define RST_DEVICES_SET_L 0x300
+#define RST_DEVICES_CLR_L 0x304
+#define RST_DEVICES_SET_H 0x308
+#define RST_DEVICES_CLR_H 0x30c
+#define RST_DEVICES_SET_U 0x310
+#define RST_DEVICES_CLR_U 0x314
+#define RST_DEVICES_SET_V 0x430
+#define RST_DEVICES_CLR_V 0x434
+#define RST_DEVICES_SET_W 0x438
+#define RST_DEVICES_CLR_W 0x43c
+#define RST_DEVICES_SET_X 0x290
+#define RST_DEVICES_CLR_X 0x294
+#define RST_DEVICES_SET_Y 0x2a8
+#define RST_DEVICES_CLR_Y 0x2ac
+
+/*
+ * Tegra CLK_OUT_ENB registers have some undefined bits which are not used and
+ * any accidental write of 1 to these bits can cause PSLVERR.
+ * So below are the valid mask defines for each CLK_OUT_ENB register used to
+ * turn ON only the valid clocks.
+ */
+#define TEGRA210_CLK_ENB_VLD_MSK_L 0xdcd7dff9
+#define TEGRA210_CLK_ENB_VLD_MSK_H 0x87d1f3e7
+#define TEGRA210_CLK_ENB_VLD_MSK_U 0xf3fed3fa
+#define TEGRA210_CLK_ENB_VLD_MSK_V 0xffc18cfb
+#define TEGRA210_CLK_ENB_VLD_MSK_W 0x793fb7ff
+#define TEGRA210_CLK_ENB_VLD_MSK_X 0x3fe66fff
+#define TEGRA210_CLK_ENB_VLD_MSK_Y 0xfc1fc7ff
+
+/**
+ * struct tegra_clk_sync_source - external clock source from codec
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @rate: input frequency from source
+ * @max_rate: max rate allowed
+ */
+struct tegra_clk_sync_source {
+ struct clk_hw hw;
+ unsigned long rate;
+ unsigned long max_rate;
+};
+
+#define to_clk_sync_source(_hw) \
+ container_of(_hw, struct tegra_clk_sync_source, hw)
+
+extern const struct clk_ops tegra_clk_sync_source_ops;
+extern int *periph_clk_enb_refcnt;
+
+struct clk *tegra_clk_register_sync_source(const char *name,
+ unsigned long max_rate);
+
+/**
+ * struct tegra_clk_frac_div - fractional divider clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register containing divider
+ * @flags: hardware-specific flags
+ * @shift: shift to the divider bit field
+ * @width: width of the divider bit field
+ * @frac_width: width of the fractional bit field
+ * @lock: register lock
+ *
+ * Flags:
+ * TEGRA_DIVIDER_ROUND_UP - This flags indicates to round up the divider value.
+ * TEGRA_DIVIDER_FIXED - Fixed rate PLL dividers has addition override bit, this
+ * flag indicates that this divider is for fixed rate PLL.
+ * TEGRA_DIVIDER_INT - Some modules can not cope with the duty cycle when
+ * fraction bit is set. This flags indicates to calculate divider for which
+ * fracton bit will be zero.
+ * TEGRA_DIVIDER_UART - UART module divider has additional enable bit which is
+ * set when divider value is not 0. This flags indicates that the divider
+ * is for UART module.
+ */
+struct tegra_clk_frac_div {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 flags;
+ u8 shift;
+ u8 width;
+ u8 frac_width;
+ spinlock_t *lock;
+};
+
+#define to_clk_frac_div(_hw) container_of(_hw, struct tegra_clk_frac_div, hw)
+
+#define TEGRA_DIVIDER_ROUND_UP BIT(0)
+#define TEGRA_DIVIDER_FIXED BIT(1)
+#define TEGRA_DIVIDER_INT BIT(2)
+#define TEGRA_DIVIDER_UART BIT(3)
+
+extern const struct clk_ops tegra_clk_frac_div_ops;
+struct clk *tegra_clk_register_divider(const char *name,
+ const char *parent_name, void __iomem *reg,
+ unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width,
+ u8 frac_width, spinlock_t *lock);
+struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
+ void __iomem *reg, spinlock_t *lock);
+
+/*
+ * Tegra PLL:
+ *
+ * In general, there are 3 requirements for each PLL
+ * that SW needs to be comply with.
+ * (1) Input frequency range (REF).
+ * (2) Comparison frequency range (CF). CF = REF/DIVM.
+ * (3) VCO frequency range (VCO). VCO = CF * DIVN.
+ *
+ * The final PLL output frequency (FO) = VCO >> DIVP.
+ */
+
+/**
+ * struct tegra_clk_pll_freq_table - PLL frequecy table
+ *
+ * @input_rate: input rate from source
+ * @output_rate: output rate from PLL for the input rate
+ * @n: feedback divider
+ * @m: input divider
+ * @p: post divider
+ * @cpcon: charge pump current
+ * @sdm_data: fraction divider setting (0 = disabled)
+ */
+struct tegra_clk_pll_freq_table {
+ unsigned long input_rate;
+ unsigned long output_rate;
+ u32 n;
+ u32 m;
+ u8 p;
+ u8 cpcon;
+ u16 sdm_data;
+};
+
+/**
+ * struct pdiv_map - map post divider to hw value
+ *
+ * @pdiv: post divider
+ * @hw_val: value to be written to the PLL hw
+ */
+struct pdiv_map {
+ u8 pdiv;
+ u8 hw_val;
+};
+
+/**
+ * struct div_nmp - offset and width of m,n and p fields
+ *
+ * @divn_shift: shift to the feedback divider bit field
+ * @divn_width: width of the feedback divider bit field
+ * @divm_shift: shift to the input divider bit field
+ * @divm_width: width of the input divider bit field
+ * @divp_shift: shift to the post divider bit field
+ * @divp_width: width of the post divider bit field
+ * @override_divn_shift: shift to the feedback divider bitfield in override reg
+ * @override_divm_shift: shift to the input divider bitfield in override reg
+ * @override_divp_shift: shift to the post divider bitfield in override reg
+ */
+struct div_nmp {
+ u8 divn_shift;
+ u8 divn_width;
+ u8 divm_shift;
+ u8 divm_width;
+ u8 divp_shift;
+ u8 divp_width;
+ u8 override_divn_shift;
+ u8 override_divm_shift;
+ u8 override_divp_shift;
+};
+
+#define MAX_PLL_MISC_REG_COUNT 6
+
+struct tegra_clk_pll;
+
+/**
+ * struct tegra_clk_pll_params - PLL parameters
+ *
+ * @input_min: Minimum input frequency
+ * @input_max: Maximum input frequency
+ * @cf_min: Minimum comparison frequency
+ * @cf_max: Maximum comparison frequency
+ * @vco_min: Minimum VCO frequency
+ * @vco_max: Maximum VCO frequency
+ * @base_reg: PLL base reg offset
+ * @misc_reg: PLL misc reg offset
+ * @lock_reg: PLL lock reg offset
+ * @lock_mask: Bitmask for PLL lock status
+ * @lock_enable_bit_idx: Bit index to enable PLL lock
+ * @iddq_reg: PLL IDDQ register offset
+ * @iddq_bit_idx: Bit index to enable PLL IDDQ
+ * @reset_reg: Register offset of where RESET bit is
+ * @reset_bit_idx: Shift of reset bit in reset_reg
+ * @sdm_din_reg: Register offset where SDM settings are
+ * @sdm_din_mask: Mask of SDM divider bits
+ * @sdm_ctrl_reg: Register offset where SDM enable is
+ * @sdm_ctrl_en_mask: Mask of SDM enable bit
+ * @ssc_ctrl_reg: Register offset where SSC settings are
+ * @ssc_ctrl_en_mask: Mask of SSC enable bit
+ * @aux_reg: AUX register offset
+ * @dyn_ramp_reg: Dynamic ramp control register offset
+ * @ext_misc_reg: Miscellaneous control register offsets
+ * @pmc_divnm_reg: n, m divider PMC override register offset (PLLM)
+ * @pmc_divp_reg: p divider PMC override register offset (PLLM)
+ * @flags: PLL flags
+ * @stepa_shift: Dynamic ramp step A field shift
+ * @stepb_shift: Dynamic ramp step B field shift
+ * @lock_delay: Delay in us if PLL lock is not used
+ * @max_p: maximum value for the p divider
+ * @defaults_set: Boolean signaling all reg defaults for PLL set.
+ * @pdiv_tohw: mapping of p divider to register values
+ * @div_nmp: offsets and widths on n, m and p fields
+ * @freq_table: array of frequencies supported by PLL
+ * @fixed_rate: PLL rate if it is fixed
+ * @mdiv_default: Default value for fixed mdiv for this PLL
+ * @round_p_to_pdiv: Callback used to round p to the closed pdiv
+ * @set_gain: Callback to adjust N div for SDM enabled
+ * PLL's based on fractional divider value.
+ * @calc_rate: Callback used to change how out of table
+ * rates (dividers and multipler) are calculated.
+ * @adjust_vco: Callback to adjust the programming range of the
+ * divider range (if SDM is present)
+ * @set_defaults: Callback which will try to initialize PLL
+ * registers to sane default values. This is first
+ * tried during PLL registration, but if the PLL
+ * is already enabled, it will be done the first
+ * time the rate is changed while the PLL is
+ * disabled.
+ * @dyn_ramp: Callback which can be used to define a custom
+ * dynamic ramp function for a given PLL.
+ * @pre_rate_change: Callback which is invoked just before changing
+ * PLL's rate.
+ * @post_rate_change: Callback which is invoked right after changing
+ * PLL's rate.
+ *
+ * Flags:
+ * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for
+ * PLL locking. If not set it will use lock_delay value to wait.
+ * TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs
+ * to be programmed to change output frequency of the PLL.
+ * TEGRA_PLLU - PLLU has inverted post divider. This flags indicated
+ * that it is PLLU and invert post divider value.
+ * TEGRA_PLLM - PLLM has additional override settings in PMC. This
+ * flag indicates that it is PLLM and use override settings.
+ * TEGRA_PLL_FIXED - We are not supposed to change output frequency
+ * of some plls.
+ * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling.
+ * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the
+ * base register.
+ * TEGRA_PLL_BYPASS - PLL has bypass bit
+ * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring
+ * TEGRA_MDIV_NEW - Switch to new method for calculating fixed mdiv
+ * it may be more accurate (especially if SDM present)
+ * TEGRA_PLLMB - PLLMB has should be treated similar to PLLM. This
+ * flag indicated that it is PLLMB.
+ * TEGRA_PLL_VCO_OUT - Used to indicate that the PLL has a VCO output
+ */
+struct tegra_clk_pll_params {
+ unsigned long input_min;
+ unsigned long input_max;
+ unsigned long cf_min;
+ unsigned long cf_max;
+ unsigned long vco_min;
+ unsigned long vco_max;
+
+ u32 base_reg;
+ u32 misc_reg;
+ u32 lock_reg;
+ u32 lock_mask;
+ u32 lock_enable_bit_idx;
+ u32 iddq_reg;
+ u32 iddq_bit_idx;
+ u32 reset_reg;
+ u32 reset_bit_idx;
+ u32 sdm_din_reg;
+ u32 sdm_din_mask;
+ u32 sdm_ctrl_reg;
+ u32 sdm_ctrl_en_mask;
+ u32 ssc_ctrl_reg;
+ u32 ssc_ctrl_en_mask;
+ u32 aux_reg;
+ u32 dyn_ramp_reg;
+ u32 ext_misc_reg[MAX_PLL_MISC_REG_COUNT];
+ u32 pmc_divnm_reg;
+ u32 pmc_divp_reg;
+ u32 flags;
+ int stepa_shift;
+ int stepb_shift;
+ int lock_delay;
+ int max_p;
+ bool defaults_set;
+ const struct pdiv_map *pdiv_tohw;
+ struct div_nmp *div_nmp;
+ struct tegra_clk_pll_freq_table *freq_table;
+ unsigned long fixed_rate;
+ u16 mdiv_default;
+ u32 (*round_p_to_pdiv)(u32 p, u32 *pdiv);
+ void (*set_gain)(struct tegra_clk_pll_freq_table *cfg);
+ int (*calc_rate)(struct clk_hw *hw,
+ struct tegra_clk_pll_freq_table *cfg,
+ unsigned long rate, unsigned long parent_rate);
+ unsigned long (*adjust_vco)(struct tegra_clk_pll_params *pll_params,
+ unsigned long parent_rate);
+ void (*set_defaults)(struct tegra_clk_pll *pll);
+ int (*dyn_ramp)(struct tegra_clk_pll *pll,
+ struct tegra_clk_pll_freq_table *cfg);
+ int (*pre_rate_change)(void);
+ void (*post_rate_change)(void);
+};
+
+#define TEGRA_PLL_USE_LOCK BIT(0)
+#define TEGRA_PLL_HAS_CPCON BIT(1)
+#define TEGRA_PLL_SET_LFCON BIT(2)
+#define TEGRA_PLL_SET_DCCON BIT(3)
+#define TEGRA_PLLU BIT(4)
+#define TEGRA_PLLM BIT(5)
+#define TEGRA_PLL_FIXED BIT(6)
+#define TEGRA_PLLE_CONFIGURE BIT(7)
+#define TEGRA_PLL_LOCK_MISC BIT(8)
+#define TEGRA_PLL_BYPASS BIT(9)
+#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10)
+#define TEGRA_MDIV_NEW BIT(11)
+#define TEGRA_PLLMB BIT(12)
+#define TEGRA_PLL_VCO_OUT BIT(13)
+
+/**
+ * struct tegra_clk_pll - Tegra PLL clock
+ *
+ * @hw: handle between common and hardware-specifix interfaces
+ * @clk_base: address of CAR controller
+ * @pmc: address of PMC, required to read override bits
+ * @lock: register lock
+ * @params: PLL parameters
+ */
+struct tegra_clk_pll {
+ struct clk_hw hw;
+ void __iomem *clk_base;
+ void __iomem *pmc;
+ spinlock_t *lock;
+ struct tegra_clk_pll_params *params;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw)
+
+/**
+ * struct tegra_audio_clk_info - Tegra Audio Clk Information
+ *
+ * @name: name for the audio pll
+ * @pll_params: pll_params for audio pll
+ * @clk_id: clk_ids for the audio pll
+ * @parent: name of the parent of the audio pll
+ */
+struct tegra_audio_clk_info {
+ char *name;
+ struct tegra_clk_pll_params *pll_params;
+ int clk_id;
+ char *parent;
+};
+
+extern const struct clk_ops tegra_clk_pll_ops;
+extern const struct clk_ops tegra_clk_plle_ops;
+struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags, struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock, unsigned long parent_rate);
+
+struct clk *tegra_clk_register_pllre_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ void __iomem *pmc, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock, unsigned long parent_rate);
+
+struct clk *tegra_clk_register_plle_tegra114(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_plle_tegra210(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllc_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ void __iomem *pmc, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllss_tegra210(const char *name,
+ const char *parent_name, void __iomem *clk_base,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllmb(const char *name, const char *parent_name,
+ void __iomem *clk_base, void __iomem *pmc,
+ unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllu(const char *name, const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllu_tegra114(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+struct clk *tegra_clk_register_pllu_tegra210(const char *name,
+ const char *parent_name,
+ void __iomem *clk_base, unsigned long flags,
+ struct tegra_clk_pll_params *pll_params,
+ spinlock_t *lock);
+
+/**
+ * struct tegra_clk_pll_out - PLL divider down clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register containing the PLL divider
+ * @enb_bit_idx: bit to enable/disable PLL divider
+ * @rst_bit_idx: bit to reset PLL divider
+ * @lock: register lock
+ * @flags: hardware-specific flags
+ */
+struct tegra_clk_pll_out {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 enb_bit_idx;
+ u8 rst_bit_idx;
+ spinlock_t *lock;
+ u8 flags;
+};
+
+#define to_clk_pll_out(_hw) container_of(_hw, struct tegra_clk_pll_out, hw)
+
+extern const struct clk_ops tegra_clk_pll_out_ops;
+struct clk *tegra_clk_register_pll_out(const char *name,
+ const char *parent_name, void __iomem *reg, u8 enb_bit_idx,
+ u8 rst_bit_idx, unsigned long flags, u8 pll_div_flags,
+ spinlock_t *lock);
+
+/**
+ * struct tegra_clk_periph_regs - Registers controlling peripheral clock
+ *
+ * @enb_reg: read the enable status
+ * @enb_set_reg: write 1 to enable clock
+ * @enb_clr_reg: write 1 to disable clock
+ * @rst_reg: read the reset status
+ * @rst_set_reg: write 1 to assert the reset of peripheral
+ * @rst_clr_reg: write 1 to deassert the reset of peripheral
+ */
+struct tegra_clk_periph_regs {
+ u32 enb_reg;
+ u32 enb_set_reg;
+ u32 enb_clr_reg;
+ u32 rst_reg;
+ u32 rst_set_reg;
+ u32 rst_clr_reg;
+};
+
+/**
+ * struct tegra_clk_periph_gate - peripheral gate clock
+ *
+ * @magic: magic number to validate type
+ * @hw: handle between common and hardware-specific interfaces
+ * @clk_base: address of CAR controller
+ * @regs: Registers to control the peripheral
+ * @flags: hardware-specific flags
+ * @clk_num: Clock number
+ * @enable_refcnt: array to maintain reference count of the clock
+ *
+ * Flags:
+ * TEGRA_PERIPH_NO_RESET - This flag indicates that reset is not allowed
+ * for this module.
+ * TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the
+ * bus to flush the write operation in apb bus. This flag indicates
+ * that this peripheral is in apb bus.
+ * TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug
+ */
+struct tegra_clk_periph_gate {
+ u32 magic;
+ struct clk_hw hw;
+ void __iomem *clk_base;
+ u8 flags;
+ int clk_num;
+ int *enable_refcnt;
+ const struct tegra_clk_periph_regs *regs;
+};
+
+#define to_clk_periph_gate(_hw) \
+ container_of(_hw, struct tegra_clk_periph_gate, hw)
+
+#define TEGRA_CLK_PERIPH_GATE_MAGIC 0x17760309
+
+#define TEGRA_PERIPH_NO_RESET BIT(0)
+#define TEGRA_PERIPH_ON_APB BIT(2)
+#define TEGRA_PERIPH_WAR_1005168 BIT(3)
+#define TEGRA_PERIPH_NO_DIV BIT(4)
+#define TEGRA_PERIPH_NO_GATE BIT(5)
+
+extern const struct clk_ops tegra_clk_periph_gate_ops;
+struct clk *tegra_clk_register_periph_gate(const char *name,
+ const char *parent_name, u8 gate_flags, void __iomem *clk_base,
+ unsigned long flags, int clk_num, int *enable_refcnt);
+
+struct tegra_clk_periph_fixed {
+ struct clk_hw hw;
+ void __iomem *base;
+ const struct tegra_clk_periph_regs *regs;
+ unsigned int mul;
+ unsigned int div;
+ unsigned int num;
+};
+
+struct clk *tegra_clk_register_periph_fixed(const char *name,
+ const char *parent,
+ unsigned long flags,
+ void __iomem *base,
+ unsigned int mul,
+ unsigned int div,
+ unsigned int num);
+
+/**
+ * struct clk-periph - peripheral clock
+ *
+ * @magic: magic number to validate type
+ * @hw: handle between common and hardware-specific interfaces
+ * @mux: mux clock
+ * @divider: divider clock
+ * @gate: gate clock
+ * @mux_ops: mux clock ops
+ * @div_ops: divider clock ops
+ * @gate_ops: gate clock ops
+ */
+struct tegra_clk_periph {
+ u32 magic;
+ struct clk_hw hw;
+ struct clk_mux mux;
+ struct tegra_clk_frac_div divider;
+ struct tegra_clk_periph_gate gate;
+
+ const struct clk_ops *mux_ops;
+ const struct clk_ops *div_ops;
+ const struct clk_ops *gate_ops;
+};
+
+#define to_clk_periph(_hw) container_of(_hw, struct tegra_clk_periph, hw)
+
+#define TEGRA_CLK_PERIPH_MAGIC 0x18221223
+
+struct clk *tegra_clk_register_periph(const char *name,
+ const char * const *parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset, unsigned long flags);
+struct clk *tegra_clk_register_periph_nodiv(const char *name,
+ const char * const *parent_names, int num_parents,
+ struct tegra_clk_periph *periph, void __iomem *clk_base,
+ u32 offset);
+
+#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \
+ _div_shift, _div_width, _div_frac_width, \
+ _div_flags, _clk_num,\
+ _gate_flags, _table, _lock) \
+ { \
+ .mux = { \
+ .flags = _mux_flags, \
+ .shift = _mux_shift, \
+ .mask = _mux_mask, \
+ .table = _table, \
+ .lock = _lock, \
+ }, \
+ .divider = { \
+ .flags = _div_flags, \
+ .shift = _div_shift, \
+ .width = _div_width, \
+ .frac_width = _div_frac_width, \
+ .lock = _lock, \
+ }, \
+ .gate = { \
+ .flags = _gate_flags, \
+ .clk_num = _clk_num, \
+ }, \
+ .mux_ops = &clk_mux_ops, \
+ .div_ops = &tegra_clk_frac_div_ops, \
+ .gate_ops = &tegra_clk_periph_gate_ops, \
+ }
+
+struct tegra_periph_init_data {
+ const char *name;
+ int clk_id;
+ union {
+ const char *const *parent_names;
+ const char *parent_name;
+ } p;
+ int num_parents;
+ struct tegra_clk_periph periph;
+ u32 offset;
+ const char *con_id;
+ const char *dev_id;
+ unsigned long flags;
+};
+
+#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, _mux_mask, _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, _div_flags, \
+ _clk_num, _gate_flags, _clk_id, _table, \
+ _flags, _lock) \
+ { \
+ .name = _name, \
+ .clk_id = _clk_id, \
+ .p.parent_names = _parent_names, \
+ .num_parents = ARRAY_SIZE(_parent_names), \
+ .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \
+ _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, \
+ _div_flags, _clk_num, \
+ _gate_flags, _table, _lock), \
+ .offset = _offset, \
+ .con_id = _con_id, \
+ .dev_id = _dev_id, \
+ .flags = _flags \
+ }
+
+#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, _mux_width, _mux_flags, _div_shift, \
+ _div_width, _div_frac_width, _div_flags, \
+ _clk_num, _gate_flags, _clk_id) \
+ TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
+ _mux_shift, BIT(_mux_width) - 1, _mux_flags, \
+ _div_shift, _div_width, _div_frac_width, _div_flags, \
+ _clk_num, _gate_flags, _clk_id,\
+ NULL, 0, NULL)
+
+struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
+ struct tegra_periph_init_data *init);
+
+/**
+ * struct clk_super_mux - super clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register controlling multiplexer
+ * @width: width of the multiplexer bit field
+ * @flags: hardware-specific flags
+ * @div2_index: bit controlling divide-by-2
+ * @pllx_index: PLLX index in the parent list
+ * @lock: register lock
+ *
+ * Flags:
+ * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
+ * that this is LP cluster clock.
+ * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
+ * super mux parent using PLLP branches. To use PLLP branches to CPU, need
+ * to configure additional bit PLLP_OUT_CPU in the clock registers.
+ * TEGRA20_SUPER_CLK - Tegra20 doesn't have a dedicated divider for Super
+ * clocks, it only has a clock-skipper.
+ */
+struct tegra_clk_super_mux {
+ struct clk_hw hw;
+ void __iomem *reg;
+ struct tegra_clk_frac_div frac_div;
+ const struct clk_ops *div_ops;
+ u8 width;
+ u8 flags;
+ u8 div2_index;
+ u8 pllx_index;
+ spinlock_t *lock;
+};
+
+#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
+
+#define TEGRA_DIVIDER_2 BIT(0)
+#define TEGRA210_CPU_CLK BIT(1)
+#define TEGRA20_SUPER_CLK BIT(2)
+
+extern const struct clk_ops tegra_clk_super_ops;
+struct clk *tegra_clk_register_super_mux(const char *name,
+ const char **parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ u8 width, u8 pllx_index, u8 div2_index, spinlock_t *lock);
+struct clk *tegra_clk_register_super_clk(const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ spinlock_t *lock);
+struct clk *tegra_clk_register_super_cclk(const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags, void __iomem *reg, u8 clk_super_flags,
+ spinlock_t *lock);
+int tegra_cclk_pre_pllx_rate_change(void);
+void tegra_cclk_post_pllx_rate_change(void);
+
+/**
+ * struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register controlling mux and divider
+ * @flags: hardware-specific flags
+ * @lock: optional register lock
+ * @gate: gate clock
+ * @gate_ops: gate clock ops
+ */
+struct tegra_sdmmc_mux {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t *lock;
+ const struct clk_ops *gate_ops;
+ struct tegra_clk_periph_gate gate;
+ u8 div_flags;
+};
+
+#define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw)
+
+struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
+ void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags,
+ unsigned long flags, void *lock);
+
+/**
+ * struct clk_init_table - clock initialization table
+ * @clk_id: clock id as mentioned in device tree bindings
+ * @parent_id: parent clock id as mentioned in device tree bindings
+ * @rate: rate to set
+ * @state: enable/disable
+ */
+struct tegra_clk_init_table {
+ unsigned int clk_id;
+ unsigned int parent_id;
+ unsigned long rate;
+ int state;
+};
+
+/**
+ * struct clk_duplicate - duplicate clocks
+ * @clk_id: clock id as mentioned in device tree bindings
+ * @lookup: duplicate lookup entry for the clock
+ */
+struct tegra_clk_duplicate {
+ int clk_id;
+ struct clk_lookup lookup;
+};
+
+#define TEGRA_CLK_DUPLICATE(_clk_id, _dev, _con) \
+ { \
+ .clk_id = _clk_id, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ }
+
+struct tegra_clk {
+ int dt_id;
+ bool present;
+};
+
+struct tegra_devclk {
+ int dt_id;
+ char *dev_id;
+ char *con_id;
+};
+
+void tegra_init_special_resets(unsigned int num, int (*assert)(unsigned long),
+ int (*deassert)(unsigned long));
+
+void tegra_init_from_table(struct tegra_clk_init_table *tbl,
+ struct clk *clks[], int clk_max);
+
+void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
+ struct clk *clks[], int clk_max);
+
+const struct tegra_clk_periph_regs *get_reg_bank(int clkid);
+struct clk **tegra_clk_init(void __iomem *clk_base, int num, int periph_banks);
+
+struct clk **tegra_lookup_dt_id(int clk_id, struct tegra_clk *tegra_clk);
+
+void tegra_add_of_provider(struct device_node *np, void *clk_src_onecell_get);
+void tegra_register_devclks(struct tegra_devclk *dev_clks, int num);
+
+void tegra_audio_clk_init(void __iomem *clk_base,
+ void __iomem *pmc_base, struct tegra_clk *tegra_clks,
+ struct tegra_audio_clk_info *audio_info,
+ unsigned int num_plls, unsigned long sync_max_rate);
+
+void tegra_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base,
+ struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *pll_params);
+
+void tegra_fixed_clk_init(struct tegra_clk *tegra_clks);
+int tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
+ unsigned long *input_freqs, unsigned int num,
+ unsigned int clk_m_div, unsigned long *osc_freq,
+ unsigned long *pll_ref_freq);
+void tegra_super_clk_gen4_init(void __iomem *clk_base,
+ void __iomem *pmc_base, struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *pll_params);
+void tegra_super_clk_gen5_init(void __iomem *clk_base,
+ void __iomem *pmc_base, struct tegra_clk *tegra_clks,
+ struct tegra_clk_pll_params *pll_params);
+
+#ifdef CONFIG_TEGRA124_CLK_EMC
+struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
+ spinlock_t *lock);
+bool tegra124_clk_emc_driver_available(struct clk_hw *emc_hw);
+#else
+static inline struct clk *
+tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
+ spinlock_t *lock)
+{
+ return NULL;
+}
+
+static inline bool tegra124_clk_emc_driver_available(struct clk_hw *emc_hw)
+{
+ return false;
+}
+#endif
+
+void tegra114_clock_tune_cpu_trimmers_high(void);
+void tegra114_clock_tune_cpu_trimmers_low(void);
+void tegra114_clock_tune_cpu_trimmers_init(void);
+
+typedef void (*tegra_clk_apply_init_table_func)(void);
+extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
+int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll);
+u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
+int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
+int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
+ u8 frac_width, u8 flags);
+void tegra_clk_osc_resume(void __iomem *clk_base);
+void tegra_clk_set_pllp_out_cpu(bool enable);
+void tegra_clk_periph_suspend(void);
+void tegra_clk_periph_resume(void);
+
+
+/* Combined read fence with delay */
+#define fence_udelay(delay, reg) \
+ do { \
+ readl(reg); \
+ udelay(delay); \
+ } while (0)
+
+bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw);
+struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
+
+struct clk *tegra210_clk_register_emc(struct device_node *np,
+ void __iomem *regs);
+
+struct clk *tegra_clk_dev_register(struct clk_hw *hw);
+
+#endif /* TEGRA_CLK_H */
diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
new file mode 100644
index 000000000..a7fdc7622
--- /dev/null
+++ b/drivers/clk/tegra/cvb.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Utility functions for parsing Tegra CVB voltage tables
+ *
+ * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved.
+ */
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/pm_opp.h>
+
+#include "cvb.h"
+
+/* cvb_mv = ((c2 * speedo / s_scale + c1) * speedo / s_scale + c0) */
+static inline int get_cvb_voltage(int speedo, int s_scale,
+ const struct cvb_coefficients *cvb)
+{
+ int mv;
+
+ /* apply only speedo scale: output mv = cvb_mv * v_scale */
+ mv = DIV_ROUND_CLOSEST(cvb->c2 * speedo, s_scale);
+ mv = DIV_ROUND_CLOSEST((mv + cvb->c1) * speedo, s_scale) + cvb->c0;
+ return mv;
+}
+
+static int round_cvb_voltage(int mv, int v_scale,
+ const struct rail_alignment *align)
+{
+ /* combined: apply voltage scale and round to cvb alignment step */
+ int uv;
+ int step = (align->step_uv ? : 1000) * v_scale;
+ int offset = align->offset_uv * v_scale;
+
+ uv = max(mv * 1000, offset) - offset;
+ uv = DIV_ROUND_UP(uv, step) * align->step_uv + align->offset_uv;
+ return uv / 1000;
+}
+
+enum {
+ DOWN,
+ UP
+};
+
+static int round_voltage(int mv, const struct rail_alignment *align, int up)
+{
+ if (align->step_uv) {
+ int uv;
+
+ uv = max(mv * 1000, align->offset_uv) - align->offset_uv;
+ uv = (uv + (up ? align->step_uv - 1 : 0)) / align->step_uv;
+ return (uv * align->step_uv + align->offset_uv) / 1000;
+ }
+ return mv;
+}
+
+static int build_opp_table(struct device *dev, const struct cvb_table *table,
+ struct rail_alignment *align,
+ int speedo_value, unsigned long max_freq)
+{
+ int i, ret, dfll_mv, min_mv, max_mv;
+
+ min_mv = round_voltage(table->min_millivolts, align, UP);
+ max_mv = round_voltage(table->max_millivolts, align, DOWN);
+
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ const struct cvb_table_freq_entry *entry = &table->entries[i];
+
+ if (!entry->freq || (entry->freq > max_freq))
+ break;
+
+ dfll_mv = get_cvb_voltage(speedo_value, table->speedo_scale,
+ &entry->coefficients);
+ dfll_mv = round_cvb_voltage(dfll_mv, table->voltage_scale,
+ align);
+ dfll_mv = clamp(dfll_mv, min_mv, max_mv);
+
+ ret = dev_pm_opp_add(dev, entry->freq, dfll_mv * 1000);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * tegra_cvb_add_opp_table - build OPP table from Tegra CVB tables
+ * @dev: the struct device * for which the OPP table is built
+ * @tables: array of CVB tables
+ * @count: size of the previously mentioned array
+ * @align: parameters of the regulator step and offset
+ * @process_id: process id of the HW module
+ * @speedo_id: speedo id of the HW module
+ * @speedo_value: speedo value of the HW module
+ * @max_freq: highest safe clock rate
+ *
+ * On Tegra, a CVB table encodes the relationship between operating voltage
+ * and safe maximal frequency for a given module (e.g. GPU or CPU). This
+ * function calculates the optimal voltage-frequency operating points
+ * for the given arguments and exports them via the OPP library for the
+ * given @dev. Returns a pointer to the struct cvb_table that matched
+ * or an ERR_PTR on failure.
+ */
+const struct cvb_table *
+tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
+ size_t count, struct rail_alignment *align,
+ int process_id, int speedo_id, int speedo_value,
+ unsigned long max_freq)
+{
+ size_t i;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ const struct cvb_table *table = &tables[i];
+
+ if (table->speedo_id != -1 && table->speedo_id != speedo_id)
+ continue;
+
+ if (table->process_id != -1 && table->process_id != process_id)
+ continue;
+
+ ret = build_opp_table(dev, table, align, speedo_value,
+ max_freq);
+ return ret ? ERR_PTR(ret) : table;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+void tegra_cvb_remove_opp_table(struct device *dev,
+ const struct cvb_table *table,
+ unsigned long max_freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_DVFS_FREQS; i++) {
+ const struct cvb_table_freq_entry *entry = &table->entries[i];
+
+ if (!entry->freq || (entry->freq > max_freq))
+ break;
+
+ dev_pm_opp_remove(dev, entry->freq);
+ }
+}
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
new file mode 100644
index 000000000..750095fe9
--- /dev/null
+++ b/drivers/clk/tegra/cvb.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Utility functions for parsing Tegra CVB voltage tables
+ */
+
+#ifndef __DRIVERS_CLK_TEGRA_CVB_H
+#define __DRIVERS_CLK_TEGRA_CVB_H
+
+#include <linux/types.h>
+
+struct device;
+
+#define MAX_DVFS_FREQS 40
+
+struct rail_alignment {
+ int offset_uv;
+ int step_uv;
+};
+
+struct cvb_coefficients {
+ int c0;
+ int c1;
+ int c2;
+};
+
+struct cvb_table_freq_entry {
+ unsigned long freq;
+ struct cvb_coefficients coefficients;
+};
+
+struct cvb_cpu_dfll_data {
+ u32 tune0_low;
+ u32 tune0_high;
+ u32 tune1;
+ unsigned int tune_high_min_millivolts;
+};
+
+struct cvb_table {
+ int speedo_id;
+ int process_id;
+
+ int min_millivolts;
+ int max_millivolts;
+
+ int speedo_scale;
+ int voltage_scale;
+ struct cvb_table_freq_entry entries[MAX_DVFS_FREQS];
+ struct cvb_cpu_dfll_data cpu_dfll_data;
+};
+
+const struct cvb_table *
+tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
+ size_t count, struct rail_alignment *align,
+ int process_id, int speedo_id, int speedo_value,
+ unsigned long max_freq);
+void tegra_cvb_remove_opp_table(struct device *dev,
+ const struct cvb_table *table,
+ unsigned long max_freq);
+
+#endif