summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnshul Jain <anshulj@nvidia.com>2012-07-23 14:59:32 -0700
committerSimone Willett <swillett@nvidia.com>2012-07-30 20:46:28 -0700
commit4582bcef392ae029336673f5685a18fc67ac041a (patch)
tree3f0da45b86faadf3f3f2efde51871ed5b0efd235
parent4012b752d64fd88efc3797e42ad8fedb29045e06 (diff)
ARM: tegra: dvfs: Adjust VDD_CPU to offset aging
Add silicon aging for VDD_CPU, this recovers some of millivolts based on the age of the chip. BUG 1006420 Change-Id: Idddb5861ab039e7ece262dec3697a69c3534ccf2 Signed-off-by: Anshul Jain <anshulj@nvidia.com> Reviewed-on: http://git-master/r/116911 Reviewed-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> GVS: Gerrit_Virtual_Submit
-rw-r--r--arch/arm/mach-tegra/dvfs.c5
-rw-r--r--arch/arm/mach-tegra/dvfs.h5
-rw-r--r--arch/arm/mach-tegra/fuse.c44
-rw-r--r--arch/arm/mach-tegra/fuse.h1
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c46
-rw-r--r--arch/arm/mach-tegra/timer.c91
-rw-r--r--arch/arm/mach-tegra/timer.h3
7 files changed, 191 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
index cb33e3db862f..43f42cdf527d 100644
--- a/arch/arm/mach-tegra/dvfs.c
+++ b/arch/arm/mach-tegra/dvfs.c
@@ -38,6 +38,7 @@
#include "board.h"
#include "clock.h"
#include "dvfs.h"
+#include "timer.h"
#define DVFS_RAIL_STATS_BIN 25
#define DVFS_RAIL_STATS_SCALE 2
@@ -681,9 +682,13 @@ int __init tegra_dvfs_late_init(void)
{
bool connected = true;
struct dvfs_rail *rail;
+ int cur_linear_age = tegra_get_linear_age();
mutex_lock(&dvfs_lock);
+ if (cur_linear_age >= 0)
+ tegra_dvfs_age_cpu(cur_linear_age);
+
list_for_each_entry(rail, &dvfs_rail_list, node)
if (dvfs_rail_connect_to_regulator(rail))
connected = false;
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index 91901b361724..7cacd954b914 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -22,7 +22,7 @@
#define _TEGRA_DVFS_H_
#define MAX_DVFS_FREQS 40
-#define DVFS_RAIL_STATS_TOP_BIN 40
+#define DVFS_RAIL_STATS_TOP_BIN 42
struct clk;
struct dvfs_rail;
@@ -165,11 +165,14 @@ static inline int tegra_cpu_dvfs_alter(int edp_thermal_index,
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail);
int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail);
+void tegra_dvfs_age_cpu(int cur_linear_age);
#else
static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
{ return 0; }
static inline int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
{ return 0; }
+static inline void tegra_dvfs_age_cpu(int cur_linear_age)
+{ return; }
#endif
#endif
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index 6df9da994fb9..bb7c242ef47e 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -62,6 +62,21 @@
#endif
+#define TEGRA_AGE_0_6 0x2cc /*Spare bit 34*/
+#define TEGRA_AGE_1_6 0x308 /*Spare bit 49*/
+#define TEGRA_AGE_0_5 0x2c8 /*Spare bit 33*/
+#define TEGRA_AGE_1_5 0x304 /*Spare bit 48*/
+#define TEGRA_AGE_0_4 0x2c4 /*Spare bit 32*/
+#define TEGRA_AGE_1_4 0x300 /*Spare bit 47*/
+#define TEGRA_AGE_0_3 0x2c0 /*Spare bit 31*/
+#define TEGRA_AGE_1_3 0x2fc /*Spare bit 46*/
+#define TEGRA_AGE_0_2 0x2bc /*Spare bit 30*/
+#define TEGRA_AGE_1_2 0x2f8 /*Spare bit 45*/
+#define TEGRA_AGE_0_1 0x2b8 /*Spare bit 29*/
+#define TEGRA_AGE_1_1 0x2f4 /*Spare bit 44*/
+#define TEGRA_AGE_0_0 0x2b4 /*Spare bit 28*/
+#define TEGRA_AGE_1_0 0x2f0 /*Spare bit 43*/
+
struct tegra_id {
enum tegra_chipid chipid;
unsigned int major, minor, netlist, patch;
@@ -172,6 +187,35 @@ int tegra_fuse_get_tsensor_spare_bits(u32 *spare_bits)
EXPORT_SYMBOL(tegra_fuse_get_tsensor_spare_bits);
#endif
+#define TEGRA_READ_AGE_BIT(n, bit, age) {\
+ bit = tegra_fuse_readl(TEGRA_AGE_0_##n);\
+ bit |= tegra_fuse_readl(TEGRA_AGE_1_##n);\
+ bit = bit << n;\
+ age |= bit;\
+}
+
+int tegra_get_age(void)
+{
+ int linear_age, age_bit;
+ linear_age = age_bit = 0;
+
+ TEGRA_READ_AGE_BIT(6, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(5, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(4, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(3, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(2, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(1, age_bit, linear_age);
+ TEGRA_READ_AGE_BIT(0, age_bit, linear_age);
+
+ /*Default Aug, 2012*/
+ if (linear_age <= 0)
+ linear_age = 8;
+
+ pr_info("TEGRA: Linear age: %d\n", linear_age);
+
+ return linear_age;
+}
+
unsigned long long tegra_chip_uid(void)
{
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
index 37f591af5695..0b04938988c7 100644
--- a/arch/arm/mach-tegra/fuse.h
+++ b/arch/arm/mach-tegra/fuse.h
@@ -36,6 +36,7 @@ int tegra_cpu_process_id(void);
int tegra_core_process_id(void);
int tegra_soc_speedo_id(void);
void tegra_init_speedo_data(void);
+int tegra_get_age(void);
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
int tegra_package_id(void);
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index 38f6ed0b317c..feb69a4621ed 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <linux/kobject.h>
#include <linux/err.h>
+#include <linux/time.h>
#include "clock.h"
#include "dvfs.h"
@@ -28,12 +29,16 @@
#include "board.h"
#include "tegra3_emc.h"
+#define CPU_MILLIVOLTS {\
+ 750, 762, 775, 787, 800, 825, 837, 850, 862, 875, 887, 900, 912, 916, 925, 937, 950, 962, 975, 987, 1000, 1007, 1012, 1025, 1037, 1050, 1062, 1075, 1087, 1100, 1112, 1125, 1137, 1150, 1162, 1175, 1187, 1200, 1212, 1237};
+
static bool tegra_dvfs_cpu_disabled;
static bool tegra_dvfs_core_disabled;
static struct dvfs *cpu_dvfs;
-static const int cpu_millivolts[MAX_DVFS_FREQS] = {
- 750, 762, 775, 787, 800, 825, 837, 850, 862, 875, 887, 900, 912, 916, 925, 937, 950, 962, 975, 987, 1000, 1007, 1012, 1025, 1037, 1050, 1062, 1075, 1087, 1100, 1112, 1125, 1137, 1150, 1162, 1175, 1187, 1200, 1212, 1237};
+static int cpu_millivolts[MAX_DVFS_FREQS] = CPU_MILLIVOLTS;
+
+static const int cpu_millivolts_aged[MAX_DVFS_FREQS] = CPU_MILLIVOLTS;
static const unsigned int cpu_cold_offs_mhz[MAX_DVFS_FREQS] = {
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50};
@@ -54,7 +59,7 @@ static int cpu_below_core = VDD_CPU_BELOW_VDD_CORE;
static struct dvfs_rail tegra3_dvfs_rail_vdd_cpu = {
.reg_id = "vdd_cpu",
.max_millivolts = 1250,
- .min_millivolts = 750,
+ .min_millivolts = 725,
.step = VDD_SAFE_STEP,
.jmp_to_zero = true,
};
@@ -632,6 +637,41 @@ static int __init get_core_nominal_mv_index(int speedo_id)
return (i - 1);
}
+static void tegra_adjust_cpu_mvs(int mvs)
+{
+ int i;
+
+ BUG_ON(ARRAY_SIZE(cpu_millivolts) != ARRAY_SIZE(cpu_millivolts_aged));
+
+ for (i = 0; i < ARRAY_SIZE(cpu_millivolts); i++)
+ cpu_millivolts[i] = cpu_millivolts_aged[i] - mvs;
+}
+
+/**
+ * Adjust VDD_CPU to offset aging.
+ * 25mV for 1st year
+ * 12mV for 2nd and 3rd year
+ * 0mV for 4th year onwards
+ */
+void tegra_dvfs_age_cpu(int cur_linear_age)
+{
+ int chip_linear_age;
+ int chip_life;
+ chip_linear_age = tegra_get_age();
+ chip_life = cur_linear_age - chip_linear_age;
+
+ /*For T37 and AP37*/
+ if (tegra_cpu_speedo_id() == 12 || tegra_cpu_speedo_id() == 13) {
+ if (chip_linear_age <= 0) {
+ return;
+ } else if (chip_life <= 12) {
+ tegra_adjust_cpu_mvs(25);
+ } else if (chip_life <= 36) {
+ tegra_adjust_cpu_mvs(13);
+ }
+ }
+}
+
void __init tegra_soc_init_dvfs(void)
{
int cpu_speedo_id = tegra_cpu_speedo_id();
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 83d0e17b50c1..5771bfc9bdde 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/syscore_ops.h>
+#include <linux/rtc.h>
#include <asm/mach/time.h>
#include <asm/localtimer.h>
@@ -238,6 +239,96 @@ void tegra_twd_resume(struct tegra_twd_context *context)
}
#endif
+#ifdef CONFIG_RTC_CLASS
+/**
+ * has_readtime - check rtc device has readtime ability
+ * @dev: current device
+ * @name_ptr: name to be returned
+ *
+ * This helper function checks to see if the rtc device can be
+ * used for reading time
+ */
+static int has_readtime(struct device *dev, void *name_ptr)
+{
+ struct rtc_device *candidate = to_rtc_device(dev);
+
+ if (!candidate->ops->read_time)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * tegra_get_linear_age - helper function to return linear age
+ * from Jan 2012.
+ *
+ * @return
+ * 1 - Jan 2012,
+ * 2 - Feb 2012,
+ * .....
+ * 13 - Jan 2013
+ */
+int tegra_get_linear_age(void)
+{
+ struct rtc_time tm;
+ int year, month, linear_age;
+ struct rtc_device *rtc_dev = NULL;
+ const char *name = NULL;
+ int ret;
+ struct device *dev = NULL;
+
+ linear_age = -1;
+ year = month = 0;
+ dev = class_find_device(rtc_class, NULL, &name, has_readtime);
+
+ if (!dev) {
+ pr_err("DVFS: No device with readtime capability\n");
+ goto done;
+ }
+
+ name = dev_name(dev);
+
+ pr_info("DVFS: Got RTC device name:%s\n", name);
+
+ if (name)
+ rtc_dev = rtc_class_open((char *)name);
+
+ if (!rtc_dev) {
+ pr_err("DVFS: No RTC device\n");
+ goto error_dev;
+ }
+
+ ret = rtc_read_time(rtc_dev, &tm);
+
+ if (ret < 0) {
+ pr_err("DVFS: Can't read RTC time\n");
+ goto error_rtc;
+ }
+
+ year = tm.tm_year;
+ /*Normalize it to 2012*/
+ year -= 112;
+ month = tm.tm_mon + 1;
+
+ if (year >= 0)
+ linear_age = year * 12 + month;
+
+error_rtc:
+ rtc_class_close(rtc_dev);
+error_dev:
+ put_device(dev);
+done:
+ return linear_age;
+
+}
+
+#else
+int tegra_get_linear_age()
+{
+ return -1;
+}
+#endif
+
static void __init tegra_init_timer(void)
{
struct clk *clk;
diff --git a/arch/arm/mach-tegra/timer.h b/arch/arm/mach-tegra/timer.h
index 4a91792f5d99..47628330dceb 100644
--- a/arch/arm/mach-tegra/timer.h
+++ b/arch/arm/mach-tegra/timer.h
@@ -45,11 +45,14 @@ struct tegra_twd_context {
int tegra_twd_get_state(struct tegra_twd_context *context);
void tegra_twd_suspend(struct tegra_twd_context *context);
void tegra_twd_resume(struct tegra_twd_context *context);
+int tegra_get_linear_age(void);
#else
static inline int tegra_twd_get_state(struct tegra_twd_context *context)
{ return -ENODEV; }
static inline void tegra_twd_suspend(struct tegra_twd_context *context) {}
static inline void tegra_twd_resume(struct tegra_twd_context *context) {}
+static inline int tegra_get_linear_age()
+{ return -1; }
#endif
#endif /* _MACH_TEGRA_TIMER_H_ */