Commit ced255c0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux

Pull thermal management fixes from Zhang Rui:

 - Power allocator governor changes to allow binding on thermal zones
   with missing power estimates information.  From Javi Merino.

 - Add compile test flags on thermal drivers that allow it without
   producing compilation errors.  From Eduardo Valentin.

 - Fixes around memory allocation on cpu_cooling.  From Javi Merino.

 - Fix on db8500 cpufreq code to allow autoload.  From Luis de
   Bethencourt.

 - Maintainer entries for cpu cooling device

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux:
  thermal: power_allocator: exit early if there are no cooling devices
  thermal: power_allocator: don't require tzp to be present for the thermal zone
  thermal: power_allocator: relax the requirement of two passive trip points
  thermal: power_allocator: relax the requirement of a sustainable_power in tzp
  thermal: Add a function to get the minimum power
  thermal: cpu_cooling: free power table on error or when unregistering
  thermal: cpu_cooling: don't call kcalloc() under rcu_read_lock
  thermal: db8500_cpufreq_cooling: Fix module autoload for OF platform driver
  thermal: cpu_cooling: Add MAINTAINERS entry
  thermal: ti-soc: Kconfig fix to avoid menu showing wrongly
  thermal: ti-soc: allow compile test
  thermal: qcom_spmi: allow compile test
  thermal: exynos: allow compile test
  thermal: armada: allow compile test
  thermal: dove: allow compile test
  thermal: kirkwood: allow compile test
  thermal: rockchip: allow compile test
  thermal: spear: allow compile test
  thermal: hisi: allow compile test
  thermal: Fix thermal_zone_of_sensor_register to match documentation
parents 4401555a 97584d18
...@@ -4,7 +4,7 @@ Power allocator governor tunables ...@@ -4,7 +4,7 @@ Power allocator governor tunables
Trip points Trip points
----------- -----------
The governor requires the following two passive trip points: The governor works optimally with the following two passive trip points:
1. "switch on" trip point: temperature above which the governor 1. "switch on" trip point: temperature above which the governor
control loop starts operating. This is the first passive trip control loop starts operating. This is the first passive trip
......
...@@ -10338,6 +10338,16 @@ F: include/uapi/linux/thermal.h ...@@ -10338,6 +10338,16 @@ F: include/uapi/linux/thermal.h
F: include/linux/cpu_cooling.h F: include/linux/cpu_cooling.h
F: Documentation/devicetree/bindings/thermal/ F: Documentation/devicetree/bindings/thermal/
THERMAL/CPU_COOLING
M: Amit Daniel Kachhap <amit.kachhap@gmail.com>
M: Viresh Kumar <viresh.kumar@linaro.org>
M: Javi Merino <javi.merino@arm.com>
L: linux-pm@vger.kernel.org
S: Supported
F: Documentation/thermal/cpu-cooling-api.txt
F: drivers/thermal/cpu_cooling.c
F: include/linux/cpu_cooling.h
THINGM BLINK(1) USB RGB LED DRIVER THINGM BLINK(1) USB RGB LED DRIVER
M: Vivien Didelot <vivien.didelot@savoirfairelinux.com> M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
S: Maintained S: Maintained
......
...@@ -163,7 +163,7 @@ config THERMAL_EMULATION ...@@ -163,7 +163,7 @@ config THERMAL_EMULATION
config HISI_THERMAL config HISI_THERMAL
tristate "Hisilicon thermal driver" tristate "Hisilicon thermal driver"
depends on ARCH_HISI && CPU_THERMAL && OF depends on (ARCH_HISI && CPU_THERMAL && OF) || COMPILE_TEST
help help
Enable this to plug hisilicon's thermal sensor driver into the Linux Enable this to plug hisilicon's thermal sensor driver into the Linux
thermal framework. cpufreq is used as the cooling device to throttle thermal framework. cpufreq is used as the cooling device to throttle
...@@ -182,7 +182,7 @@ config IMX_THERMAL ...@@ -182,7 +182,7 @@ config IMX_THERMAL
config SPEAR_THERMAL config SPEAR_THERMAL
bool "SPEAr thermal sensor driver" bool "SPEAr thermal sensor driver"
depends on PLAT_SPEAR depends on PLAT_SPEAR || COMPILE_TEST
depends on OF depends on OF
help help
Enable this to plug the SPEAr thermal sensor driver into the Linux Enable this to plug the SPEAr thermal sensor driver into the Linux
...@@ -190,7 +190,7 @@ config SPEAR_THERMAL ...@@ -190,7 +190,7 @@ config SPEAR_THERMAL
config ROCKCHIP_THERMAL config ROCKCHIP_THERMAL
tristate "Rockchip thermal driver" tristate "Rockchip thermal driver"
depends on ARCH_ROCKCHIP depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on RESET_CONTROLLER depends on RESET_CONTROLLER
help help
Rockchip thermal driver provides support for Temperature sensor Rockchip thermal driver provides support for Temperature sensor
...@@ -208,7 +208,7 @@ config RCAR_THERMAL ...@@ -208,7 +208,7 @@ config RCAR_THERMAL
config KIRKWOOD_THERMAL config KIRKWOOD_THERMAL
tristate "Temperature sensor on Marvell Kirkwood SoCs" tristate "Temperature sensor on Marvell Kirkwood SoCs"
depends on MACH_KIRKWOOD depends on MACH_KIRKWOOD || COMPILE_TEST
depends on OF depends on OF
help help
Support for the Kirkwood thermal sensor driver into the Linux thermal Support for the Kirkwood thermal sensor driver into the Linux thermal
...@@ -216,7 +216,7 @@ config KIRKWOOD_THERMAL ...@@ -216,7 +216,7 @@ config KIRKWOOD_THERMAL
config DOVE_THERMAL config DOVE_THERMAL
tristate "Temperature sensor on Marvell Dove SoCs" tristate "Temperature sensor on Marvell Dove SoCs"
depends on ARCH_DOVE || MACH_DOVE depends on ARCH_DOVE || MACH_DOVE || COMPILE_TEST
depends on OF depends on OF
help help
Support for the Dove thermal sensor driver in the Linux thermal Support for the Dove thermal sensor driver in the Linux thermal
...@@ -234,7 +234,7 @@ config DB8500_THERMAL ...@@ -234,7 +234,7 @@ config DB8500_THERMAL
config ARMADA_THERMAL config ARMADA_THERMAL
tristate "Armada 370/XP thermal management" tristate "Armada 370/XP thermal management"
depends on ARCH_MVEBU depends on ARCH_MVEBU || COMPILE_TEST
depends on OF depends on OF
help help
Enable this option if you want to have support for thermal management Enable this option if you want to have support for thermal management
...@@ -349,11 +349,12 @@ config INTEL_PCH_THERMAL ...@@ -349,11 +349,12 @@ config INTEL_PCH_THERMAL
programmable trip points and other information. programmable trip points and other information.
menu "Texas Instruments thermal drivers" menu "Texas Instruments thermal drivers"
depends on ARCH_HAS_BANDGAP || COMPILE_TEST
source "drivers/thermal/ti-soc-thermal/Kconfig" source "drivers/thermal/ti-soc-thermal/Kconfig"
endmenu endmenu
menu "Samsung thermal drivers" menu "Samsung thermal drivers"
depends on ARCH_EXYNOS depends on ARCH_EXYNOS || COMPILE_TEST
source "drivers/thermal/samsung/Kconfig" source "drivers/thermal/samsung/Kconfig"
endmenu endmenu
...@@ -364,7 +365,7 @@ endmenu ...@@ -364,7 +365,7 @@ endmenu
config QCOM_SPMI_TEMP_ALARM config QCOM_SPMI_TEMP_ALARM
tristate "Qualcomm SPMI PMIC Temperature Alarm" tristate "Qualcomm SPMI PMIC Temperature Alarm"
depends on OF && SPMI && IIO depends on OF && (SPMI || COMPILE_TEST) && IIO
select REGMAP_SPMI select REGMAP_SPMI
help help
This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP) This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
......
...@@ -262,7 +262,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, ...@@ -262,7 +262,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
* efficiently. Power is stored in mW, frequency in KHz. The * efficiently. Power is stored in mW, frequency in KHz. The
* resulting table is in ascending order. * resulting table is in ascending order.
* *
* Return: 0 on success, -E* on error. * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs,
* -ENOMEM if we run out of memory or -EAGAIN if an OPP was
* added/enabled while the function was executing.
*/ */
static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
u32 capacitance) u32 capacitance)
...@@ -273,8 +275,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, ...@@ -273,8 +275,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
int num_opps = 0, cpu, i, ret = 0; int num_opps = 0, cpu, i, ret = 0;
unsigned long freq; unsigned long freq;
rcu_read_lock();
for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
dev = get_cpu_device(cpu); dev = get_cpu_device(cpu);
if (!dev) { if (!dev) {
...@@ -284,24 +284,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, ...@@ -284,24 +284,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
} }
num_opps = dev_pm_opp_get_opp_count(dev); num_opps = dev_pm_opp_get_opp_count(dev);
if (num_opps > 0) { if (num_opps > 0)
break; break;
} else if (num_opps < 0) { else if (num_opps < 0)
ret = num_opps; return num_opps;
goto unlock;
}
} }
if (num_opps == 0) { if (num_opps == 0)
ret = -EINVAL; return -EINVAL;
goto unlock;
}
power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
if (!power_table) { if (!power_table)
ret = -ENOMEM; return -ENOMEM;
goto unlock;
} rcu_read_lock();
for (freq = 0, i = 0; for (freq = 0, i = 0;
opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
...@@ -309,6 +305,12 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, ...@@ -309,6 +305,12 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
u32 freq_mhz, voltage_mv; u32 freq_mhz, voltage_mv;
u64 power; u64 power;
if (i >= num_opps) {
rcu_read_unlock();
ret = -EAGAIN;
goto free_power_table;
}
freq_mhz = freq / 1000000; freq_mhz = freq / 1000000;
voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
...@@ -326,17 +328,22 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, ...@@ -326,17 +328,22 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
power_table[i].power = power; power_table[i].power = power;
} }
if (i == 0) { rcu_read_unlock();
if (i != num_opps) {
ret = PTR_ERR(opp); ret = PTR_ERR(opp);
goto unlock; goto free_power_table;
} }
cpufreq_device->cpu_dev = dev; cpufreq_device->cpu_dev = dev;
cpufreq_device->dyn_power_table = power_table; cpufreq_device->dyn_power_table = power_table;
cpufreq_device->dyn_power_table_entries = i; cpufreq_device->dyn_power_table_entries = i;
unlock: return 0;
rcu_read_unlock();
free_power_table:
kfree(power_table);
return ret; return ret;
} }
...@@ -847,7 +854,7 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -847,7 +854,7 @@ __cpufreq_cooling_register(struct device_node *np,
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) { if (ret) {
cool_dev = ERR_PTR(ret); cool_dev = ERR_PTR(ret);
goto free_table; goto free_power_table;
} }
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
...@@ -889,6 +896,8 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -889,6 +896,8 @@ __cpufreq_cooling_register(struct device_node *np,
remove_idr: remove_idr:
release_idr(&cpufreq_idr, cpufreq_dev->id); release_idr(&cpufreq_idr, cpufreq_dev->id);
free_power_table:
kfree(cpufreq_dev->dyn_power_table);
free_table: free_table:
kfree(cpufreq_dev->freq_table); kfree(cpufreq_dev->freq_table);
free_time_in_idle_timestamp: free_time_in_idle_timestamp:
...@@ -1039,6 +1048,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) ...@@ -1039,6 +1048,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
thermal_cooling_device_unregister(cpufreq_dev->cool_dev); thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
release_idr(&cpufreq_idr, cpufreq_dev->id); release_idr(&cpufreq_idr, cpufreq_dev->id);
kfree(cpufreq_dev->dyn_power_table);
kfree(cpufreq_dev->time_in_idle_timestamp); kfree(cpufreq_dev->time_in_idle_timestamp);
kfree(cpufreq_dev->time_in_idle); kfree(cpufreq_dev->time_in_idle);
kfree(cpufreq_dev->freq_table); kfree(cpufreq_dev->freq_table);
......
...@@ -72,6 +72,7 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = { ...@@ -72,6 +72,7 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = {
{ .compatible = "stericsson,db8500-cpufreq-cooling" }, { .compatible = "stericsson,db8500-cpufreq-cooling" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, db8500_cpufreq_cooling_match);
#endif #endif
static struct platform_driver db8500_cpufreq_cooling_driver = { static struct platform_driver db8500_cpufreq_cooling_driver = {
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include "thermal_core.h" #include "thermal_core.h"
#define INVALID_TRIP -1
#define FRAC_BITS 10 #define FRAC_BITS 10
#define int_to_frac(x) ((x) << FRAC_BITS) #define int_to_frac(x) ((x) << FRAC_BITS)
#define frac_to_int(x) ((x) >> FRAC_BITS) #define frac_to_int(x) ((x) >> FRAC_BITS)
...@@ -56,22 +58,109 @@ static inline s64 div_frac(s64 x, s64 y) ...@@ -56,22 +58,109 @@ static inline s64 div_frac(s64 x, s64 y)
/** /**
* struct power_allocator_params - parameters for the power allocator governor * struct power_allocator_params - parameters for the power allocator governor
* @allocated_tzp: whether we have allocated tzp for this thermal zone and
* it needs to be freed on unbind
* @err_integral: accumulated error in the PID controller. * @err_integral: accumulated error in the PID controller.
* @prev_err: error in the previous iteration of the PID controller. * @prev_err: error in the previous iteration of the PID controller.
* Used to calculate the derivative term. * Used to calculate the derivative term.
* @trip_switch_on: first passive trip point of the thermal zone. The * @trip_switch_on: first passive trip point of the thermal zone. The
* governor switches on when this trip point is crossed. * governor switches on when this trip point is crossed.
* If the thermal zone only has one passive trip point,
* @trip_switch_on should be INVALID_TRIP.
* @trip_max_desired_temperature: last passive trip point of the thermal * @trip_max_desired_temperature: last passive trip point of the thermal
* zone. The temperature we are * zone. The temperature we are
* controlling for. * controlling for.
*/ */
struct power_allocator_params { struct power_allocator_params {
bool allocated_tzp;
s64 err_integral; s64 err_integral;
s32 prev_err; s32 prev_err;
int trip_switch_on; int trip_switch_on;
int trip_max_desired_temperature; int trip_max_desired_temperature;
}; };
/**
* estimate_sustainable_power() - Estimate the sustainable power of a thermal zone
* @tz: thermal zone we are operating in
*
* For thermal zones that don't provide a sustainable_power in their
* thermal_zone_params, estimate one. Calculate it using the minimum
* power of all the cooling devices as that gives a valid value that
* can give some degree of functionality. For optimal performance of
* this governor, provide a sustainable_power in the thermal zone's
* thermal_zone_params.
*/
static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
{
u32 sustainable_power = 0;
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
u32 min_power;
if (instance->trip != params->trip_max_desired_temperature)
continue;
if (power_actor_get_min_power(cdev, tz, &min_power))
continue;
sustainable_power += min_power;
}
return sustainable_power;
}
/**
* estimate_pid_constants() - Estimate the constants for the PID controller
* @tz: thermal zone for which to estimate the constants
* @sustainable_power: sustainable power for the thermal zone
* @trip_switch_on: trip point number for the switch on temperature
* @control_temp: target temperature for the power allocator governor
* @force: whether to force the update of the constants
*
* This function is used to update the estimation of the PID
* controller constants in struct thermal_zone_parameters.
* Sustainable power is provided in case it was estimated. The
* estimated sustainable_power should not be stored in the
* thermal_zone_parameters so it has to be passed explicitly to this
* function.
*
* If @force is not set, the values in the thermal zone's parameters
* are preserved if they are not zero. If @force is set, the values
* in thermal zone's parameters are overwritten.
*/
static void estimate_pid_constants(struct thermal_zone_device *tz,
u32 sustainable_power, int trip_switch_on,
int control_temp, bool force)
{
int ret;
int switch_on_temp;
u32 temperature_threshold;
ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
if (ret)
switch_on_temp = 0;
temperature_threshold = control_temp - switch_on_temp;
if (!tz->tzp->k_po || force)
tz->tzp->k_po = int_to_frac(sustainable_power) /
temperature_threshold;
if (!tz->tzp->k_pu || force)
tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
temperature_threshold;
if (!tz->tzp->k_i || force)
tz->tzp->k_i = int_to_frac(10) / 1000;
/*
* The default for k_d and integral_cutoff is 0, so we can
* leave them as they are.
*/
}
/** /**
* pid_controller() - PID controller * pid_controller() - PID controller
* @tz: thermal zone we are operating in * @tz: thermal zone we are operating in
...@@ -98,10 +187,20 @@ static u32 pid_controller(struct thermal_zone_device *tz, ...@@ -98,10 +187,20 @@ static u32 pid_controller(struct thermal_zone_device *tz,
{ {
s64 p, i, d, power_range; s64 p, i, d, power_range;
s32 err, max_power_frac; s32 err, max_power_frac;
u32 sustainable_power;
struct power_allocator_params *params = tz->governor_data; struct power_allocator_params *params = tz->governor_data;
max_power_frac = int_to_frac(max_allocatable_power); max_power_frac = int_to_frac(max_allocatable_power);
if (tz->tzp->sustainable_power) {
sustainable_power = tz->tzp->sustainable_power;
} else {
sustainable_power = estimate_sustainable_power(tz);
estimate_pid_constants(tz, sustainable_power,
params->trip_switch_on, control_temp,
true);
}
err = control_temp - current_temp; err = control_temp - current_temp;
err = int_to_frac(err); err = int_to_frac(err);
...@@ -139,7 +238,7 @@ static u32 pid_controller(struct thermal_zone_device *tz, ...@@ -139,7 +238,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
power_range = p + i + d; power_range = p + i + d;
/* feed-forward the known sustainable dissipatable power */ /* feed-forward the known sustainable dissipatable power */
power_range = tz->tzp->sustainable_power + frac_to_int(power_range); power_range = sustainable_power + frac_to_int(power_range);
power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
...@@ -247,6 +346,11 @@ static int allocate_power(struct thermal_zone_device *tz, ...@@ -247,6 +346,11 @@ static int allocate_power(struct thermal_zone_device *tz,
} }
} }
if (!num_actors) {
ret = -ENODEV;
goto unlock;
}
/* /*
* We need to allocate five arrays of the same size: * We need to allocate five arrays of the same size:
* req_power, max_power, granted_power, extra_actor_power and * req_power, max_power, granted_power, extra_actor_power and
...@@ -340,43 +444,66 @@ static int allocate_power(struct thermal_zone_device *tz, ...@@ -340,43 +444,66 @@ static int allocate_power(struct thermal_zone_device *tz,
return ret; return ret;
} }
static int get_governor_trips(struct thermal_zone_device *tz, /**
struct power_allocator_params *params) * get_governor_trips() - get the number of the two trip points that are key for this governor
* @tz: thermal zone to operate on
* @params: pointer to private data for this governor
*
* The power allocator governor works optimally with two trips points:
* a "switch on" trip point and a "maximum desired temperature". These
* are defined as the first and last passive trip points.
*
* If there is only one trip point, then that's considered to be the
* "maximum desired temperature" trip point and the governor is always
* on. If there are no passive or active trip points, then the
* governor won't do anything. In fact, its throttle function
* won't be called at all.
*/
static void get_governor_trips(struct thermal_zone_device *tz,
struct power_allocator_params *params)
{ {
int i, ret, last_passive; int i, last_active, last_passive;
bool found_first_passive; bool found_first_passive;
found_first_passive = false; found_first_passive = false;
last_passive = -1; last_active = INVALID_TRIP;
ret = -EINVAL; last_passive = INVALID_TRIP;
for (i = 0; i < tz->trips; i++) { for (i = 0; i < tz->trips; i++) {
enum thermal_trip_type type; enum thermal_trip_type type;
int ret;
ret = tz->ops->get_trip_type(tz, i, &type); ret = tz->ops->get_trip_type(tz, i, &type);
if (ret) if (ret) {
return ret; dev_warn(&tz->device,
"Failed to get trip point %d type: %d\n", i,
ret);
continue;
}
if (!found_first_passive) { if (type == THERMAL_TRIP_PASSIVE) {
if (type == THERMAL_TRIP_PASSIVE) { if (!found_first_passive) {
params->trip_switch_on = i; params->trip_switch_on = i;
found_first_passive = true; found_first_passive = true;
} else {
last_passive = i;
} }
} else if (type == THERMAL_TRIP_PASSIVE) { } else if (type == THERMAL_TRIP_ACTIVE) {
last_passive = i; last_active = i;
} else { } else {
break; break;
} }
} }
if (last_passive != -1) { if (last_passive != INVALID_TRIP) {
params->trip_max_desired_temperature = last_passive; params->trip_max_desired_temperature = last_passive;
ret = 0; } else if (found_first_passive) {
params->trip_max_desired_temperature = params->trip_switch_on;
params->trip_switch_on = INVALID_TRIP;
} else { } else {
ret = -EINVAL; params->trip_switch_on = INVALID_TRIP;
params->trip_max_desired_temperature = last_active;
} }
return ret;
} }
static void reset_pid_controller(struct power_allocator_params *params) static void reset_pid_controller(struct power_allocator_params *params)
...@@ -405,60 +532,45 @@ static void allow_maximum_power(struct thermal_zone_device *tz) ...@@ -405,60 +532,45 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
* power_allocator_bind() - bind the power_allocator governor to a thermal zone * power_allocator_bind() - bind the power_allocator governor to a thermal zone
* @tz: thermal zone to bind it to * @tz: thermal zone to bind it to
* *
* Check that the thermal zone is valid for this governor, that is, it * Initialize the PID controller parameters and bind it to the thermal
* has two thermal trips. If so, initialize the PID controller * zone.
* parameters and bind it to the thermal zone.
* *
* Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM * Return: 0 on success, or -ENOMEM if we ran out of memory.
* if we ran out of memory.
*/ */
static int power_allocator_bind(struct thermal_zone_device *tz) static int power_allocator_bind(struct thermal_zone_device *tz)
{ {
int ret; int ret;
struct power_allocator_params *params; struct power_allocator_params *params;
int switch_on_temp, control_temp; int control_temp;
u32 temperature_threshold;
if (!tz->tzp || !tz->tzp->sustainable_power) {
dev_err(&tz->device,
"power_allocator: missing sustainable_power\n");
return -EINVAL;
}
params = kzalloc(sizeof(*params), GFP_KERNEL); params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params) if (!params)
return -ENOMEM; return -ENOMEM;
ret = get_governor_trips(tz, params); if (!tz->tzp) {
if (ret) { tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL);
dev_err(&tz->device, if (!tz->tzp) {
"thermal zone %s has wrong trip setup for power allocator\n", ret = -ENOMEM;
tz->type); goto free_params;
goto free; }
}
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, params->allocated_tzp = true;
&switch_on_temp); }
if (ret)
goto free;
ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, if (!tz->tzp->sustainable_power)
&control_temp); dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n");
if (ret)
goto free;
temperature_threshold = control_temp - switch_on_temp; get_governor_trips(tz, params);
tz->tzp->k_po = tz->tzp->k_po ?: if (tz->trips > 0) {
int_to_frac(tz->tzp->sustainable_power) / temperature_threshold; ret = tz->ops->get_trip_temp(tz,
tz->tzp->k_pu = tz->tzp->k_pu ?: params->trip_max_desired_temperature,
int_to_frac(2 * tz->tzp->sustainable_power) / &control_temp);
temperature_threshold; if (!ret)
tz->tzp->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000; estimate_pid_constants(tz, tz->tzp->sustainable_power,
/* params->trip_switch_on,
* The default for k_d and integral_cutoff is 0, so we can control_temp, false);
* leave them as they are. }
*/
reset_pid_controller(params); reset_pid_controller(params);
...@@ -466,14 +578,23 @@ static int power_allocator_bind(struct thermal_zone_device *tz) ...@@ -466,14 +578,23 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
return 0; return 0;
free: free_params:
kfree(params); kfree(params);
return ret; return ret;
} }
static void power_allocator_unbind(struct thermal_zone_device *tz) static void power_allocator_unbind(struct thermal_zone_device *tz)
{ {
struct power_allocator_params *params = tz->governor_data;
dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
if (params->allocated_tzp) {
kfree(tz->tzp);
tz->tzp = NULL;
}
kfree(tz->governor_data); kfree(tz->governor_data);
tz->governor_data = NULL; tz->governor_data = NULL;
} }
...@@ -499,13 +620,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) ...@@ -499,13 +620,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
&switch_on_temp); &switch_on_temp);
if (ret) { if (!ret && (current_temp < switch_on_temp)) {
dev_warn(&tz->device,
"Failed to get switch on temperature: %d\n", ret);
return ret;
}
if (current_temp < switch_on_temp) {
tz->passive = 0; tz->passive = 0;
reset_pid_controller(params); reset_pid_controller(params);
allow_maximum_power(tz); allow_maximum_power(tz);
......
...@@ -1012,6 +1012,34 @@ int power_actor_get_max_power(struct thermal_cooling_device *cdev, ...@@ -1012,6 +1012,34 @@ int power_actor_get_max_power(struct thermal_cooling_device *cdev,
return cdev->ops->state2power(cdev, tz, 0, max_power); return cdev->ops->state2power(cdev, tz, 0, max_power);
} }
/**
* power_actor_get_min_power() - get the mainimum power that a cdev can consume
* @cdev: pointer to &thermal_cooling_device
* @tz: a valid thermal zone device pointer
* @min_power: pointer in which to store the minimum power
*
* Calculate the minimum power consumption in milliwatts that the
* cooling device can currently consume and store it in @min_power.
*
* Return: 0 on success, -EINVAL if @cdev doesn't support the
* power_actor API or -E* on other error.
*/
int power_actor_get_min_power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz, u32 *min_power)
{
unsigned long max_state;
int ret;
if (!cdev_is_power_actor(cdev))
return -EINVAL;
ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
return cdev->ops->state2power(cdev, tz, max_state, min_power);
}
/** /**
* power_actor_set_power() - limit the maximum power that a cooling device can consume * power_actor_set_power() - limit the maximum power that a cooling device can consume
* @cdev: pointer to &thermal_cooling_device * @cdev: pointer to &thermal_cooling_device
......
config TI_SOC_THERMAL config TI_SOC_THERMAL
tristate "Texas Instruments SoCs temperature sensor driver" tristate "Texas Instruments SoCs temperature sensor driver"
depends on THERMAL
depends on ARCH_HAS_BANDGAP
help help
If you say yes here you get support for the Texas Instruments If you say yes here you get support for the Texas Instruments
OMAP4460+ on die bandgap temperature sensor support. The register OMAP4460+ on die bandgap temperature sensor support. The register
...@@ -24,7 +22,7 @@ config TI_THERMAL ...@@ -24,7 +22,7 @@ config TI_THERMAL
config OMAP4_THERMAL config OMAP4_THERMAL
bool "Texas Instruments OMAP4 thermal support" bool "Texas Instruments OMAP4 thermal support"
depends on TI_SOC_THERMAL depends on TI_SOC_THERMAL
depends on ARCH_OMAP4 depends on ARCH_OMAP4 || COMPILE_TEST
help help
If you say yes here you get thermal support for the Texas Instruments If you say yes here you get thermal support for the Texas Instruments
OMAP4 SoC family. The current chip supported are: OMAP4 SoC family. The current chip supported are:
...@@ -38,7 +36,7 @@ config OMAP4_THERMAL ...@@ -38,7 +36,7 @@ config OMAP4_THERMAL
config OMAP5_THERMAL config OMAP5_THERMAL
bool "Texas Instruments OMAP5 thermal support" bool "Texas Instruments OMAP5 thermal support"
depends on TI_SOC_THERMAL depends on TI_SOC_THERMAL
depends on SOC_OMAP5 depends on SOC_OMAP5 || COMPILE_TEST
help help
If you say yes here you get thermal support for the Texas Instruments If you say yes here you get thermal support for the Texas Instruments
OMAP5 SoC family. The current chip supported are: OMAP5 SoC family. The current chip supported are:
...@@ -50,7 +48,7 @@ config OMAP5_THERMAL ...@@ -50,7 +48,7 @@ config OMAP5_THERMAL
config DRA752_THERMAL config DRA752_THERMAL
bool "Texas Instruments DRA752 thermal support" bool "Texas Instruments DRA752 thermal support"
depends on TI_SOC_THERMAL depends on TI_SOC_THERMAL
depends on SOC_DRA7XX depends on SOC_DRA7XX || COMPILE_TEST
help help
If you say yes here you get thermal support for the Texas Instruments If you say yes here you get thermal support for the Texas Instruments
DRA752 SoC family. The current chip supported are: DRA752 SoC family. The current chip supported are:
......
...@@ -360,7 +360,7 @@ static inline struct thermal_zone_device * ...@@ -360,7 +360,7 @@ static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data, thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
const struct thermal_zone_of_device_ops *ops) const struct thermal_zone_of_device_ops *ops)
{ {
return NULL; return ERR_PTR(-ENODEV);
} }
static inline static inline
...@@ -380,6 +380,8 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) ...@@ -380,6 +380,8 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
int power_actor_get_max_power(struct thermal_cooling_device *, int power_actor_get_max_power(struct thermal_cooling_device *,
struct thermal_zone_device *tz, u32 *max_power); struct thermal_zone_device *tz, u32 *max_power);
int power_actor_get_min_power(struct thermal_cooling_device *,
struct thermal_zone_device *tz, u32 *min_power);
int power_actor_set_power(struct thermal_cooling_device *, int power_actor_set_power(struct thermal_cooling_device *,
struct thermal_instance *, u32); struct thermal_instance *, u32);
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
...@@ -415,6 +417,10 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) ...@@ -415,6 +417,10 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
static inline int power_actor_get_max_power(struct thermal_cooling_device *cdev, static inline int power_actor_get_max_power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz, u32 *max_power) struct thermal_zone_device *tz, u32 *max_power)
{ return 0; } { return 0; }
static inline int power_actor_get_min_power(struct thermal_cooling_device *cdev,
struct thermal_zone_device *tz,
u32 *min_power)
{ return -ENODEV; }
static inline int power_actor_set_power(struct thermal_cooling_device *cdev, static inline int power_actor_set_power(struct thermal_cooling_device *cdev,
struct thermal_instance *tz, u32 power) struct thermal_instance *tz, u32 power)
{ return 0; } { return 0; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment