diff options
-rw-r--r-- | arch/arm/mach-tegra/dvfs.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dvfs.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.c | 44 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_dvfs.c | 46 | ||||
-rw-r--r-- | arch/arm/mach-tegra/timer.c | 91 | ||||
-rw-r--r-- | arch/arm/mach-tegra/timer.h | 3 |
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_ */ |