ARM: tegra: power: Update core EDP with temperature
Alex Frid [Sun, 25 Nov 2012 03:13:04 +0000 (19:13 -0800)]
Added core EDP thermal layer as active cooling device, and updated
core EDP limits when temperature threshold are tripped.

Bug 1165638

Change-Id: I37cd8ab0a94909d198f21ba02e9308ca4d23bcb6
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/168929
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/board-common.c
arch/arm/mach-tegra/board-common.h
arch/arm/mach-tegra/board-dalmore-sensors.c
arch/arm/mach-tegra/board-pluto-sensors.c
arch/arm/mach-tegra/board-roth-sensors.c
arch/arm/mach-tegra/edp_core.c
arch/arm/mach-tegra/include/mach/edp.h
arch/arm/mach-tegra/include/mach/thermal.h

index 6da3af3..2cda070 100644 (file)
@@ -132,3 +132,33 @@ int uart_console_debug_init(int default_debug_port)
        }
        return debug_port_id;
 }
+
+void nct1008_add_cdev_trips(struct nct1008_platform_data *nct1008_data,
+                           struct tegra_cooling_device *cdev_data)
+{
+       int i, trip;
+       struct nct_trip_temp *trip_state;
+
+       if (!nct1008_data || !cdev_data)
+               return;
+
+       if (nct1008_data->num_trips + cdev_data->trip_temperatures_num >
+           NCT_MAX_TRIPS) {
+               WARN(1, "%s: cooling device %s has too many trips\n",
+                    __func__, cdev_data->cdev_type);
+               return;
+       }
+
+       for (i = 0; i < cdev_data->trip_temperatures_num; i++) {
+               trip = nct1008_data->num_trips;
+               trip_state = &nct1008_data->trips[trip];
+
+               trip_state->cdev_type = cdev_data->cdev_type;
+               trip_state->trip_temp = cdev_data->trip_temperatures[i] * 1000;
+               trip_state->trip_type = THERMAL_TRIP_ACTIVE;
+               trip_state->state = i + 1;
+               trip_state->hysteresis = 1000;
+
+               nct1008_data->num_trips++;
+       }
+}
index 05cd4e8..b7a7c9b 100644 (file)
 #ifndef __MACH_TEGRA_BOARD_COMMON_H
 #define __MACH_TEGRA_BOARD_COMMON_H
 
+#include <linux/nct1008.h>
+#include <mach/thermal.h>
+
 extern struct platform_device *uart_console_debug_device;
 int  uart_console_debug_init(int defaul_debug_port);
 int tegra_vibrator_init(void);
-
+void nct1008_add_cdev_trips(struct nct1008_platform_data *nct1008_data,
+                           struct tegra_cooling_device *cdev_data);
 #endif
index 0eb5920..7e37cd4 100644 (file)
@@ -49,6 +49,7 @@
 
 #include "gpio-names.h"
 #include "board.h"
+#include "board-common.h"
 #include "board-dalmore.h"
 #include "cpu-tegra.h"
 #include "devices.h"
@@ -570,12 +571,12 @@ static int dalmore_nct1008_init(void)
        }
 
        if (nct1008_port >= 0) {
+               struct nct1008_platform_data *data = &dalmore_nct1008_pdata;
 #ifdef CONFIG_TEGRA_EDP_LIMITS
                const struct tegra_edp_limits *cpu_edp_limits;
                int cpu_edp_limits_size;
                int i;
                int trip;
-               struct nct1008_platform_data *data = &dalmore_nct1008_pdata;
                struct nct_trip_temp *trip_state;
 
                /* edp capping */
@@ -601,6 +602,7 @@ static int dalmore_nct1008_init(void)
                                BUG();
                }
 #endif
+               nct1008_add_cdev_trips(data, tegra_core_edp_get_cdev());
 
                dalmore_i2c4_nct1008_board_info[0].irq = gpio_to_irq(nct1008_port);
                pr_info("%s: dalmore nct1008 irq %d", __func__, dalmore_i2c4_nct1008_board_info[0].irq);
index 20704a7..e566d55 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "gpio-names.h"
 #include "board.h"
+#include "board-common.h"
 #include "board-pluto.h"
 #include "cpu-tegra.h"
 #include "devices.h"
@@ -760,12 +761,12 @@ static int pluto_nct1008_init(void)
        }
 
        if (nct1008_port >= 0) {
+               struct nct1008_platform_data *data = &pluto_nct1008_pdata;
 #ifdef CONFIG_TEGRA_EDP_LIMITS
                const struct tegra_edp_limits *cpu_edp_limits;
                int cpu_edp_limits_size;
                int i;
                int trip;
-               struct nct1008_platform_data *data = &pluto_nct1008_pdata;
                struct nct_trip_temp *trip_state;
 
                /* edp capping */
@@ -791,6 +792,7 @@ static int pluto_nct1008_init(void)
                                BUG();
                }
 #endif
+               nct1008_add_cdev_trips(data, tegra_core_edp_get_cdev());
 
                pluto_i2c4_nct1008_board_info[0].irq =
                                gpio_to_irq(nct1008_port);
index f066a22..e05b8f9 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "gpio-names.h"
 #include "board.h"
+#include "board-common.h"
 #include "board-roth.h"
 #include "cpu-tegra.h"
 #include "devices.h"
@@ -210,13 +211,13 @@ static int roth_nct1008_init(void)
 {
        int nct1008_port = TEGRA_GPIO_PX6;
        int ret = 0;
+       struct nct1008_platform_data *data = &roth_nct1008_pdata;
 
 #ifdef CONFIG_TEGRA_EDP_LIMITS
                const struct tegra_edp_limits *cpu_edp_limits;
                int cpu_edp_limits_size;
                int i;
                int trip;
-               struct nct1008_platform_data *data = &roth_nct1008_pdata;
                struct nct_trip_temp *trip_state;
 
                /* edp capping */
@@ -243,6 +244,8 @@ static int roth_nct1008_init(void)
                }
 #endif
 
+       nct1008_add_cdev_trips(data, tegra_core_edp_get_cdev());
+
        roth_i2c4_nct1008_board_info[0].irq = gpio_to_irq(nct1008_port);
        pr_info("%s: roth nct1008 irq %d", __func__, \
                                roth_i2c4_nct1008_board_info[0].irq);
index 378916d..1909dd6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/suspend.h>
 
 #include <mach/edp.h>
+#include <mach/thermal.h>
 
 #include "clock.h"
 #include "fuse.h"
@@ -321,6 +322,78 @@ const struct attribute *core_edp_attributes[] = {
 
 static struct kobject *core_edp_kobj;
 
+/* core edp temperature update */
+static int core_edp_get_cdev_max_state(struct thermal_cooling_device *cdev,
+                                      unsigned long *max_state)
+{
+       *max_state = limits ? limits->temperature_ranges - 1 : 0;
+       return 0;
+}
+
+static int core_edp_get_cdev_cur_state(struct thermal_cooling_device *cdev,
+                                      unsigned long *cur_state)
+{
+       *cur_state = core_edp_thermal_idx;
+       return 0;
+}
+
+static int core_edp_set_cdev_state(struct thermal_cooling_device *cdev,
+                                  unsigned long cur_state)
+{
+       int ret = 0;
+       unsigned long *old_cap_rates;
+       unsigned long *new_cap_rates;
+
+       if (!limits) {
+               core_edp_thermal_idx = cur_state;
+               return 0;
+       }
+
+       mutex_lock(&core_edp_lock);
+
+       if (core_edp_thermal_idx != cur_state) {
+               old_cap_rates = get_current_cap_rates();
+               new_cap_rates = get_cap_rates(
+                       core_edp_scpu_state, core_edp_profile,
+                       core_edp_modules_state, cur_state);
+               ret = update_cap_rates(new_cap_rates, old_cap_rates);
+               /*
+                * Unlike updating other state variables, temperature change
+                * must be always "accepted" (it's already happened) - just
+                * re-try one more time in case of error
+                */
+               if (ret)
+                       update_cap_rates(new_cap_rates, old_cap_rates);
+
+               core_edp_thermal_idx = cur_state;
+       }
+       mutex_unlock(&core_edp_lock);
+
+       return 0;
+}
+
+static struct thermal_cooling_device_ops core_edp_cooling_ops = {
+       .get_max_state = core_edp_get_cdev_max_state,
+       .get_cur_state = core_edp_get_cdev_cur_state,
+       .set_cur_state = core_edp_set_cdev_state,
+};
+
+static struct tegra_cooling_device core_edp_cdev;
+
+struct tegra_cooling_device *tegra_core_edp_get_cdev(void)
+{
+       if (!limits)
+               return NULL;
+
+       if (!core_edp_cdev.cdev_type) {
+               core_edp_cdev.cdev_type = "core_edp";
+               core_edp_cdev.trip_temperatures = limits->temperatures;
+               core_edp_cdev.trip_temperatures_num =
+                       limits->temperature_ranges-1;
+       }
+       return &core_edp_cdev;
+}
+
 /*
  * Since EMC rate on suspend exit is set to boot configuration with no regards
  * to EDP constraints, force profile_favor_emc on suspend entry, and restore
@@ -369,6 +442,11 @@ static int __init tegra_core_edp_late_init(void)
        if (!limits)
                return 0;
 
+       /* continue on error - initialized at max temperature, anyway */
+       if (IS_ERR_OR_NULL(thermal_cooling_device_register(
+               core_edp_cdev.cdev_type, NULL, &core_edp_cooling_ops)))
+               pr_err("%s: failed to register edp cooling device\n", __func__);
+
        /* exit on error - prevent changing profile_favor_emc  */
        if (register_pm_notifier(&core_edp_pm_notifier)) {
                pr_err("%s: failed to register edp pm notifier\n", __func__);
index bd79eab..f8dab77 100644 (file)
@@ -116,6 +116,7 @@ void tegra_edp_throttle_cpu_now(u8 factor);
 void tegra_init_core_edp_limits(unsigned int regulator_mA);
 int tegra_core_edp_debugfs_init(struct dentry *edp_dir);
 int tegra_core_edp_cpu_state_update(bool scpu_state);
+struct tegra_cooling_device *tegra_core_edp_get_cdev(void);
 #else
 static inline void tegra_init_core_edp_limits(unsigned int regulator_mA)
 {}
@@ -123,6 +124,8 @@ static inline int tegra_core_edp_debugfs_init(struct dentry *edp_dir)
 { return 0; }
 static inline int tegra_core_edp_cpu_state_update(bool scpu_state)
 { return 0; }
+static inline struct tegra_cooling_device *tegra_core_edp_get_cdev(void)
+{ return NULL; }
 #endif
 
 #ifdef CONFIG_ARCH_TEGRA_11x_SOC
index abcec01..b3b85f5 100644 (file)
@@ -67,6 +67,9 @@ struct tegra_thermal_device {
 
 struct tegra_cooling_device {
        enum cooling_device_id id;
+       char *cdev_type;
+       int *trip_temperatures;
+       int trip_temperatures_num;
 };
 
 #define MAX_THROT_TABLE_SIZE   (64)