summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSai Gurrappadi <sgurrappadi@nvidia.com>2014-01-14 13:48:59 -0800
committerHarshada Kale <hkale@nvidia.com>2014-06-26 09:30:30 -0700
commit57a716112badb1025fa1cf3160977977b6c0bdb2 (patch)
tree88ae0cb768af67207ae4ec104691eb69b0be247b
parente09a926e51cce1e82939c37139bd7aeb9ef5f708 (diff)
ARM: tegra: Interface to read ROSC BIN ISM freq
Allows for reading of the Ring oscillator frequency on the JTAG chain of ROSCs. Performs the read via the APB2JTAG interface. Bug 1511506 Change-Id: I6797f26c760e178f3d8dc4067b7d50bfce5086f0 Signed-off-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Reviewed-on: http://git-master/r/425050 Reviewed-by: Thomas Cherry <tcherry@nvidia.com>
-rw-r--r--drivers/platform/tegra/tegra_ism.c208
-rw-r--r--drivers/platform/tegra/tegra_ism.h25
2 files changed, 233 insertions, 0 deletions
diff --git a/drivers/platform/tegra/tegra_ism.c b/drivers/platform/tegra/tegra_ism.c
new file mode 100644
index 000000000000..72316cea503a
--- /dev/null
+++ b/drivers/platform/tegra/tegra_ism.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/platform/tegra/tegra_ism.c
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include "../../../arch/arm/mach-tegra/clock.h"
+#include "tegra_ism.h"
+#include "tegra_apb2jtag.h"
+
+#define NV_ISM_ROSC_BIN_IDDQ_LSB 8
+#define NV_ISM_ROSC_BIN_IDDQ_MSB 8
+#define NV_ISM_ROSC_BIN_MODE_LSB 9
+#define NV_ISM_ROSC_BIN_MODE_MSB 10
+#define NV_ISM_ROSC_BIN_OUT_DIV_LSB 5
+#define NV_ISM_ROSC_BIN_OUT_DIV_MSB 7
+#define NV_ISM_ROSC_BIN_LOCAL_DURATION_LSB 11
+#define NV_ISM_ROSC_BIN_LOCAL_DRUATION_MSB 26
+#define NV_ISM_ROSC_BIN_SRC_SEL_LSB 0
+#define NV_ISM_ROSC_BIN_SRC_SEL_MSB 4
+#define NV_ISM_ROSC_BIN_COUNT_LSB 27
+#define NV_ISM_ROSC_BIN_COUNT_MSB 42
+
+
+static u32 delay_us = 1000;
+
+static u32 get_bits(u32 value, u32 lsb, u32 msb)
+{
+ u32 mask = 0xFFFFFFFF;
+ u32 num_bits = msb - lsb + 1;
+ if (num_bits + lsb > 32)
+ return 0;
+ if (num_bits < 32)
+ mask = (1 << num_bits) - 1;
+
+ return (value >> lsb) & mask;
+}
+
+static u32 get_buf_bits(u32 *buf, u32 lsb, u32 msb)
+{
+ u32 lower_addr = lsb / 32;
+ u32 upper_addr = msb / 32;
+ u32 ret = 0;
+
+ if (msb < lsb)
+ return 0;
+ if (msb - lsb + 1 > 32)
+ return 0;
+ lsb = lsb % 32;
+ msb = msb % 32;
+ if (lower_addr == upper_addr) {
+ ret = get_bits(buf[lower_addr], lsb, msb);
+ } else {
+ ret = get_bits(buf[lower_addr], lsb, 31);
+ ret = ret | (get_bits(buf[upper_addr], 0, msb) << (32 - lsb));
+ }
+ return ret;
+}
+
+static inline u32 set_bits(u32 cur_val, u32 new_val, u32 lsb, u32 msb)
+{
+ u32 mask = 0xFFFFFFFF;
+ u32 num_bits = msb - lsb + 1;
+ if (num_bits + lsb > 32)
+ return 0;
+ if (num_bits < 32)
+ mask = (1 << num_bits) - 1;
+ return (cur_val & ~(mask << lsb)) | ((new_val & mask) << lsb);
+}
+
+void set_buf_bits(u32 *buf, u32 val, u32 lsb, u32 msb)
+{
+ u32 lower_addr = lsb / 32;
+ u32 upper_addr = msb / 32;
+ u32 num_bits;
+
+ if (msb < lsb)
+ return;
+ if (msb - lsb + 1 > 32)
+ return;
+ num_bits = msb - lsb + 1;
+ lsb = lsb % 32;
+ msb = msb % 32;
+ if (lower_addr == upper_addr) {
+ buf[lower_addr] = set_bits(buf[lower_addr], val, lsb, msb);
+ } else {
+ buf[lower_addr] = set_bits(buf[lower_addr], val, lsb, 31);
+ buf[upper_addr] = set_bits(buf[upper_addr], val >> (32 - lsb),
+ 0, msb);
+ }
+}
+
+static u32 set_disable_ism(u32 mode, u32 offset,
+ u8 chiplet, u16 len, u8 instr_id, u32 disable)
+{
+ u32 buf[10];
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ apb2jtag_get();
+ apb2jtag_read_locked(instr_id, len, chiplet, buf);
+ set_buf_bits(buf, mode, NV_ISM_ROSC_BIN_MODE_LSB + offset,
+ NV_ISM_ROSC_BIN_MODE_MSB + offset);
+ set_buf_bits(buf, disable, NV_ISM_ROSC_BIN_IDDQ_LSB + offset,
+ NV_ISM_ROSC_BIN_IDDQ_MSB + offset);
+ ret = apb2jtag_write_locked(instr_id, len, chiplet, buf);
+ apb2jtag_put();
+
+ if (ret < 0)
+ pr_err("%s: APB2JTAG write failed\n", __func__);
+
+ udelay(delay_us);
+
+ return ret;
+}
+
+u32 read_ism(u32 mode, u32 duration, u32 div, u32 sel, u32 offset,
+ u8 chiplet, u16 len, u8 instr_id)
+{
+ int ret;
+ u32 count;
+ u32 buf[10];
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Toggle IDDQ to reset ISM */
+ set_disable_ism(mode, offset, chiplet, len, instr_id, 1);
+
+ apb2jtag_get();
+
+ apb2jtag_read_locked(instr_id, len, chiplet, buf);
+
+ set_buf_bits(buf, mode, NV_ISM_ROSC_BIN_MODE_LSB + offset,
+ NV_ISM_ROSC_BIN_MODE_MSB + offset);
+ set_buf_bits(buf, sel, NV_ISM_ROSC_BIN_SRC_SEL_LSB + offset,
+ NV_ISM_ROSC_BIN_SRC_SEL_MSB + offset);
+ set_buf_bits(buf, div, NV_ISM_ROSC_BIN_OUT_DIV_LSB + offset,
+ NV_ISM_ROSC_BIN_OUT_DIV_MSB + offset);
+ set_buf_bits(buf, duration, NV_ISM_ROSC_BIN_LOCAL_DURATION_LSB + offset,
+ NV_ISM_ROSC_BIN_LOCAL_DRUATION_MSB + offset);
+ set_buf_bits(buf, 0, NV_ISM_ROSC_BIN_IDDQ_LSB + offset,
+ NV_ISM_ROSC_BIN_IDDQ_MSB + offset);
+
+ ret = apb2jtag_write_locked(instr_id, len, chiplet, buf);
+ if (ret < 0) {
+ pr_err("read_ism: APB2JTAG write failed.\n");
+ apb2jtag_put();
+ set_disable_ism(mode, offset, chiplet, len, instr_id, 1);
+ return 0;
+ }
+
+ /* Delay ensures that the apb2jtag interface has time to respond */
+ udelay(delay_us);
+
+ ret = apb2jtag_read_locked(instr_id, len, chiplet, buf);
+ apb2jtag_put();
+
+ /* Disable ISM */
+ set_disable_ism(mode, offset, chiplet, len, instr_id, 1);
+
+ if (ret < 0) {
+ pr_err("read_ism: APB2JTAG read failed.\n");
+ return 0;
+ }
+ count = get_buf_bits(buf, NV_ISM_ROSC_BIN_COUNT_LSB + offset,
+ NV_ISM_ROSC_BIN_COUNT_MSB + offset);
+ return (count * (tegra_clk_measure_input_freq() / 1000000) *
+ (1 << div)) / duration;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int __init debugfs_init(void)
+{
+ struct dentry *dfs_file, *dfs_dir;
+
+ dfs_dir = debugfs_create_dir("tegra_ism", NULL);
+ if (!dfs_dir)
+ return -ENOMEM;
+
+ dfs_file = debugfs_create_u32("delay_us", 0644, dfs_dir, &delay_us);
+ if (!dfs_file)
+ goto err;
+
+ return 0;
+err:
+ debugfs_remove_recursive(dfs_dir);
+ return -ENOMEM;
+}
+late_initcall_sync(debugfs_init);
+#endif
diff --git a/drivers/platform/tegra/tegra_ism.h b/drivers/platform/tegra/tegra_ism.h
new file mode 100644
index 000000000000..0b7c8990ebac
--- /dev/null
+++ b/drivers/platform/tegra/tegra_ism.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/platform/tegra/tegra_ism.h
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef _MACH_TEGRA_ISM_H_
+#define _MACH_TEGRA_ISM_H_
+
+/* Returns the RO frequency in MHz given the configuration */
+u32 read_ism(u32 mode, u32 duration, u32 div, u32 sel, u32 offset,
+ u8 chiplet, u16 len, u8 instr_id);
+
+void set_buf_bits(u32 *buf, u32 val, u32 lsb, u32 msb);
+#endif /* _MACH_TEGRA_ISM_H_ */