diff --git a/drivers/gpu/nvgpu/Makefile.nvgpu-t18x b/drivers/gpu/nvgpu/Makefile.nvgpu-t18x
index c6b6f0d2a..cd4ce5ce7 100644
--- a/drivers/gpu/nvgpu/Makefile.nvgpu-t18x
+++ b/drivers/gpu/nvgpu/Makefile.nvgpu-t18x
@@ -38,7 +38,8 @@ nvgpu-y += \
$(nvgpu-t18x)/perf/vfe_var.o \
$(nvgpu-t18x)/perf/vfe_equ.o \
$(nvgpu-t18x)/perf/perf.o \
- $(nvgpu-t18x)/clk/clk.o
+ $(nvgpu-t18x)/clk/clk.o \
+ $(nvgpu-t18x)/gp106/clk_gp106.o
nvgpu-$(CONFIG_TEGRA_GK20A) += $(nvgpu-t18x)/gp10b/platform_gp10b_tegra.o
diff --git a/drivers/gpu/nvgpu/gp106/clk_gp106.c b/drivers/gpu/nvgpu/gp106/clk_gp106.c
new file mode 100644
index 000000000..4bf03661c
--- /dev/null
+++ b/drivers/gpu/nvgpu/gp106/clk_gp106.c
@@ -0,0 +1,226 @@
+/*
+ * GP106 Clocks
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include /* for mdelay */
+#include
+#include
+#include
+#include
+#include
+
+#include "gk20a/gk20a.h"
+#include "hw_trim_gp106.h"
+#include "clk_gp106.h"
+
+#define gk20a_dbg_clk(fmt, arg...) \
+ gk20a_dbg(gpu_dbg_clk, fmt, ##arg)
+
+#ifdef CONFIG_DEBUG_FS
+static int clk_gp106_debugfs_init(struct gk20a *g);
+#endif
+
+#define NUM_NAMEMAPS 4
+
+static int gp106_init_clk_support(struct gk20a *g) {
+ struct clk_gk20a *clk = &g->clk;
+ u32 err = 0;
+
+ gk20a_dbg_fn("");
+
+ mutex_init(&clk->clk_mutex);
+
+ clk->clk_namemap = (struct namemap_cfg *)
+ kzalloc(sizeof(struct namemap_cfg) * NUM_NAMEMAPS, GFP_KERNEL);
+
+ if (!clk->clk_namemap)
+ return -ENOMEM;
+
+ clk->clk_namemap[0] = (struct namemap_cfg) {
+ .namemap = CLK_NAMEMAP_INDEX_GPC2CLK,
+ .is_enable = 1,
+ .is_counter = 1,
+ .g = g,
+ .cntr.reg_ctrl_addr = trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_r(),
+ .cntr.reg_ctrl_idx =
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_source_gpc2clk_f(),
+ .cntr.reg_cntr_addr = trim_gpc_bcast_clk_cntr_ncgpcclk_cnt_r(),
+ .name = "gpc2clk"
+ };
+ clk->clk_namemap[1] = (struct namemap_cfg) {
+ .namemap = CLK_NAMEMAP_INDEX_SYS2CLK,
+ .is_enable = 1,
+ .is_counter = 1,
+ .g = g,
+ .cntr.reg_ctrl_addr = trim_sys_clk_cntr_ncsyspll_cfg_r(),
+ .cntr.reg_ctrl_idx = trim_sys_clk_cntr_ncsyspll_cfg_source_sys2clk_f(),
+ .cntr.reg_cntr_addr = trim_sys_clk_cntr_ncsyspll_cnt_r(),
+ .name = "sys2clk"
+ };
+ clk->clk_namemap[2] = (struct namemap_cfg) {
+ .namemap = CLK_NAMEMAP_INDEX_XBAR2CLK,
+ .is_enable = 1,
+ .is_counter = 1,
+ .g = g,
+ .cntr.reg_ctrl_addr = trim_sys_clk_cntr_ncltcpll_cfg_r(),
+ .cntr.reg_ctrl_idx = trim_sys_clk_cntr_ncltcpll_cfg_source_xbar2clk_f(),
+ .cntr.reg_cntr_addr = trim_sys_clk_cntr_ncltcpll_cnt_r(),
+ .name = "xbar2clk"
+ };
+ clk->clk_namemap[3] = (struct namemap_cfg) {
+ .namemap = CLK_NAMEMAP_INDEX_DRAMCLK,
+ .is_enable = 1,
+ .is_counter = 1,
+ .g = g,
+ .cntr.reg_ctrl_addr = trim_fbpa_bcast_clk_cntr_ncltcclk_cfg_r(),
+ .cntr.reg_ctrl_idx =
+ trim_fbpa_bcast_clk_cntr_ncltcclk_cfg_source_dramdiv4_rec_clk1_f(),
+ .cntr.reg_cntr_addr = trim_fbpa_bcast_clk_cntr_ncltcclk_cnt_r(),
+ .name = "dramdiv2_rec_clk1"
+ };
+
+ clk->namemap_num = NUM_NAMEMAPS;
+
+ clk->g = g;
+
+#ifdef CONFIG_DEBUG_FS
+ if (!clk->debugfs_set) {
+ if (!clk_gp106_debugfs_init(g))
+ clk->debugfs_set = true;
+ }
+#endif
+ return err;
+}
+
+#ifdef CONFIG_DEBUG_FS
+typedef struct namemap_cfg namemap_cfg_t;
+static u32 gp106_get_rate_cntr(struct gk20a *, struct namemap_cfg *);
+
+static u32 gp106_get_rate_cntr(struct gk20a *g, struct namemap_cfg *c) {
+ u32 save_reg;
+ u32 retries;
+ u32 cntr = 0;
+
+ struct clk_gk20a *clk = &g->clk;
+
+ if (!c || !c->cntr.reg_ctrl_addr || !c->cntr.reg_cntr_addr)
+ return 0;
+
+ mutex_lock(&clk->clk_mutex);
+
+ /* Save the register */
+ save_reg = gk20a_readl(g, c->cntr.reg_ctrl_addr);
+
+ /* Disable and reset the current clock */
+ gk20a_writel(g, c->cntr.reg_ctrl_addr,
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_deasserted_f());
+
+ /* Force wb() */
+ gk20a_readl(g, c->cntr.reg_ctrl_addr);
+
+ /* Wait for reset to happen */
+ retries = CLK_DEFAULT_CNTRL_SETTLE_RETRIES;
+ do {
+ udelay(CLK_DEFAULT_CNTRL_SETTLE_USECS);
+ } while ((--retries) && (cntr = gk20a_readl(g, c->cntr.reg_cntr_addr)));
+
+ if (!retries) {
+ gk20a_err(dev_from_gk20a(g),
+ "unable to settle counter reset, bailing");
+ goto read_err;
+ }
+ /* Program counter */
+ gk20a_writel(g, c->cntr.reg_ctrl_addr,
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_deasserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_noofipclks_f(XTAL_CNTR_CLKS) |
+ c->cntr.reg_ctrl_idx);
+ gk20a_readl(g, c->cntr.reg_ctrl_addr);
+
+ udelay(XTAL_CNTR_DELAY);
+
+ cntr = XTAL_SCALE_TO_KHZ * gk20a_readl(g, c->cntr.reg_cntr_addr);
+
+read_err:
+ /* reset and restore control register */
+ gk20a_writel(g, c->cntr.reg_ctrl_addr,
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_asserted_f() |
+ trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_deasserted_f());
+ gk20a_readl(g, c->cntr.reg_ctrl_addr);
+ gk20a_writel(g, c->cntr.reg_ctrl_addr, save_reg);
+ gk20a_readl(g, c->cntr.reg_ctrl_addr);
+ mutex_unlock(&clk->clk_mutex);
+
+ return cntr;
+
+}
+
+static int gp106_get_rate_show(void *data , u64 *val) {
+ struct namemap_cfg *c = (struct namemap_cfg *) data;
+ struct gk20a *g = c->g;
+
+ *val = c->is_counter ? gp106_get_rate_cntr(g, c) : 0 /* TODO PLL read */;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(get_rate_fops, gp106_get_rate_show, NULL, "%llu\n");
+
+
+static int clk_gp106_debugfs_init(struct gk20a *g) {
+ struct gk20a_platform *platform = dev_get_drvdata(g->dev);
+
+ struct dentry *gpu_root = platform->debugfs;
+ struct dentry *clocks_root;
+ struct dentry *d;
+ int i;
+
+ if (NULL == (clocks_root = debugfs_create_dir("clocks", gpu_root)))
+ return -ENOMEM;
+
+ gk20a_dbg(gpu_dbg_info, "g=%p", g);
+
+ for (i = 0; i < g->clk.namemap_num; i++) {
+ if (g->clk.clk_namemap[i].is_enable) {
+ d = debugfs_create_file(
+ g->clk.clk_namemap[i].name,
+ S_IRUGO,
+ clocks_root,
+ &g->clk.clk_namemap[i],
+ &get_rate_fops);
+ if (!d)
+ goto err_out;
+ }
+ }
+ return 0;
+
+err_out:
+ pr_err("%s: Failed to make debugfs node\n", __func__);
+ debugfs_remove_recursive(clocks_root);
+ return -ENOMEM;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+void gp106_init_clk_ops(struct gpu_ops *gops) {
+ gops->clk.init_clk_support = gp106_init_clk_support;
+}
+
+
diff --git a/drivers/gpu/nvgpu/gp106/clk_gp106.h b/drivers/gpu/nvgpu/gp106/clk_gp106.h
new file mode 100644
index 000000000..a50819aab
--- /dev/null
+++ b/drivers/gpu/nvgpu/gp106/clk_gp106.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef CLK_GP106_H
+#define CLK_GP106_H
+
+#include
+
+#define CLK_NAMEMAP_INDEX_GPC2CLK 0x00
+#define CLK_NAMEMAP_INDEX_XBAR2CLK 0x02
+#define CLK_NAMEMAP_INDEX_SYS2CLK 0x07 /* SYSPLL */
+#define CLK_NAMEMAP_INDEX_DRAMCLK 0x20 /* DRAMPLL */
+
+#define CLK_DEFAULT_CNTRL_SETTLE_RETRIES 10
+#define CLK_DEFAULT_CNTRL_SETTLE_USECS 5
+
+#define XTAL_CNTR_CLKS 2700 /* 100usec at 27KHz XTAL */
+#define XTAL_CNTR_DELAY 110 /* leave 10 extra usec */
+#define XTAL_SCALE_TO_KHZ 10
+
+
+
+struct namemap_cfg {
+ u32 namemap;
+ u32 is_enable; /* Namemap enabled */
+ u32 is_counter; /* Using cntr */
+ struct gk20a *g;
+ union {
+ struct {
+ u32 reg_ctrl_addr;
+ u32 reg_ctrl_idx;
+ u32 reg_cntr_addr;
+ } cntr;
+ struct {
+ /* Todo */
+ } pll;
+ };
+ char name[24];
+};
+
+void gp106_init_clk_ops(struct gpu_ops *gops);
+
+#endif /* CLK_GP106_H */
diff --git a/drivers/gpu/nvgpu/gp106/hal_gp106.c b/drivers/gpu/nvgpu/gp106/hal_gp106.c
index 822591ed5..6c8ac7bc0 100644
--- a/drivers/gpu/nvgpu/gp106/hal_gp106.c
+++ b/drivers/gpu/nvgpu/gp106/hal_gp106.c
@@ -37,7 +37,7 @@
#include "gm20b/gr_gm20b.h"
#include "gm20b/fifo_gm20b.h"
#include "gm20b/pmu_gm20b.h"
-#include "gm20b/clk_gm20b.h"
+#include "gp106/clk_gp106.h"
#include "gp106/mm_gp106.h"
#include "gp106/pmu_gp106.h"
@@ -156,6 +156,7 @@ int gp106_init_hal(struct gk20a *g)
gp106_init_pmu_ops(gops);
gk20a_init_debug_ops(gops);
gk20a_init_dbg_session_ops(gops);
+ gp106_init_clk_ops(gops);
gp10b_init_regops(gops);
gp10b_init_cde_ops(gops);
gk20a_init_tsg_ops(gops);
diff --git a/drivers/gpu/nvgpu/gp106/hw_trim_gp106.h b/drivers/gpu/nvgpu/gp106/hw_trim_gp106.h
new file mode 100644
index 000000000..42d3fd322
--- /dev/null
+++ b/drivers/gpu/nvgpu/gp106/hw_trim_gp106.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+/*
+ * Function naming determines intended use:
+ *
+ * _r(void) : Returns the offset for register .
+ *
+ * _o(void) : Returns the offset for element .
+ *
+ * _w(void) : Returns the word offset for word (4 byte) element .
+ *
+ * __s(void) : Returns size of field of register in bits.
+ *
+ * __f(u32 v) : Returns a value based on 'v' which has been shifted
+ * and masked to place it at field of register . This value
+ * can be |'d with others to produce a full register value for
+ * register .
+ *
+ * __m(void) : Returns a mask for field of register . This
+ * value can be ~'d and then &'d to clear the value of field for
+ * register .
+ *
+ * ___f(void) : Returns the constant value after being shifted
+ * to place it at field of register . This value can be |'d
+ * with others to produce a full register value for .
+ *
+ * __v(u32 r) : Returns the value of field from a full register
+ * value 'r' after being shifted to place its LSB at bit 0.
+ * This value is suitable for direct comparison with other unshifted
+ * values appropriate for use in field of register .
+ *
+ * ___v(void) : Returns the constant value for defined for
+ * field of register . This value is suitable for direct
+ * comparison with unshifted values appropriate for use in field
+ * of register .
+ */
+#ifndef _hw_trim_gp106_h_
+#define _hw_trim_gp106_h_
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_r(void)
+{
+ return 0x00132924;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_noofipclks_s(void)
+{
+ return 16;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_noofipclks_f(u32 v)
+{
+ return (v & 0xffff) << 0;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_noofipclks_m(void)
+{
+ return 0xffff << 0;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_noofipclks_v(u32 r)
+{
+ return (r >> 0) & 0xffff;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_s(void)
+{
+ return 1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_f(u32 v)
+{
+ return (v & 0x1) << 16;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_m(void)
+{
+ return 0x1 << 16;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_v(u32 r)
+{
+ return (r >> 16) & 0x1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_deasserted_f(void)
+{
+ return 0;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_write_en_asserted_f(void)
+{
+ return 0x10000;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_s(void)
+{
+ return 1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_f(u32 v)
+{
+ return (v & 0x1) << 20;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_m(void)
+{
+ return 0x1 << 20;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_v(u32 r)
+{
+ return (r >> 20) & 0x1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_deasserted_f(void)
+{
+ return 0;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_enable_asserted_f(void)
+{
+ return 0x100000;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_s(void)
+{
+ return 1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_f(u32 v)
+{
+ return (v & 0x1) << 24;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_m(void)
+{
+ return 0x1 << 24;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_v(u32 r)
+{
+ return (r >> 24) & 0x1;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_deasserted_f(void)
+{
+ return 0;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_reset_asserted_f(void)
+{
+ return 0x1000000;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cfg_source_gpc2clk_f(void)
+{
+ return 0x70000000;
+}
+static inline u32 trim_gpc_bcast_clk_cntr_ncgpcclk_cnt_r(void)
+{
+ return 0x00132928;
+}
+static inline u32 trim_fbpa_bcast_clk_cntr_ncltcclk_cfg_r(void)
+{
+ return 0x00132128;
+}
+static inline u32 trim_fbpa_bcast_clk_cntr_ncltcclk_cfg_source_dramdiv4_rec_clk1_f(void)
+{
+ return 0x20000000;
+}
+static inline u32 trim_fbpa_bcast_clk_cntr_ncltcclk_cnt_r(void)
+{
+ return 0x0013212c;
+}
+static inline u32 trim_sys_clk_cntr_ncltcpll_cfg_r(void)
+{
+ return 0x001373c0;
+}
+static inline u32 trim_sys_clk_cntr_ncltcpll_cfg_source_xbar2clk_f(void)
+{
+ return 0x20000000;
+}
+static inline u32 trim_sys_clk_cntr_ncltcpll_cnt_r(void)
+{
+ return 0x001373c4;
+}
+static inline u32 trim_sys_clk_cntr_ncsyspll_cfg_r(void)
+{
+ return 0x001373b0;
+}
+static inline u32 trim_sys_clk_cntr_ncsyspll_cfg_source_sys2clk_f(void)
+{
+ return 0x0;
+}
+static inline u32 trim_sys_clk_cntr_ncsyspll_cnt_r(void)
+{
+ return 0x001373b4;
+}
+
+#endif