forked from rrcarlosr/Jetpack
642 lines
16 KiB
C
642 lines
16 KiB
C
|
/*
|
||
|
* drivers/thermal/continuous_thermal_gov.c
|
||
|
*
|
||
|
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms and conditions of the GNU General Public License,
|
||
|
* version 2, as published by the Free Software Foundation.
|
||
|
|
||
|
* This program is distributed in the hope 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.
|
||
|
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include <linux/kobject.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/thermal.h>
|
||
|
#include <linux/pwm.h>
|
||
|
#include <trace/events/thermal.h>
|
||
|
#include <linux/platform_data/pwm_fan.h>
|
||
|
|
||
|
#include "thermal_core.h"
|
||
|
|
||
|
#define DRV_NAME "continuous_therm_gov"
|
||
|
|
||
|
#define IIR_POWER (7)
|
||
|
#define UPPER_WIDTH (5000)
|
||
|
#define LOWER_WIDTH (15000)
|
||
|
|
||
|
#define QPOINT (10000)
|
||
|
#define IIR_GAIN_QP (1000)
|
||
|
#define IIR_MIN (IIR_GAIN_QP / 100)
|
||
|
#define IIR_MAX (IIR_GAIN_QP * 1)
|
||
|
|
||
|
#define FAN_TURN_ON_NUM (1)
|
||
|
|
||
|
#define ABS(x, y) ((x > y)?(x - y):(y - x))
|
||
|
|
||
|
int (*fetch_trip_pwm)(struct thermal_cooling_device *cdev, int trip);
|
||
|
|
||
|
/*function defined to fetch active pwm table to caculate slope*/
|
||
|
/*Notice that the register action must be done to get the pwm table*/
|
||
|
void register_fetch_pwm_func(int (*func)(struct thermal_cooling_device *cdev, int trip))
|
||
|
{
|
||
|
fetch_trip_pwm = func;
|
||
|
}
|
||
|
EXPORT_SYMBOL(register_fetch_pwm_func);
|
||
|
|
||
|
struct continuous_thermal_gov_params {
|
||
|
int iir_power;
|
||
|
int iir_min;
|
||
|
int iir_max;
|
||
|
int iir_gain_qp;
|
||
|
int iir_upper_width;
|
||
|
int iir_lower_width;
|
||
|
long long iir_gain;
|
||
|
};
|
||
|
|
||
|
static struct continuous_thermal_gov_params pm_default = {
|
||
|
.iir_power = IIR_POWER,
|
||
|
.iir_min = IIR_MIN,
|
||
|
.iir_max = IIR_MAX,
|
||
|
.iir_gain_qp = IIR_GAIN_QP,
|
||
|
.iir_upper_width = UPPER_WIDTH,
|
||
|
.iir_lower_width = LOWER_WIDTH,
|
||
|
.iir_gain = 0,
|
||
|
};
|
||
|
|
||
|
struct continuous_thermal_gov_attribute {
|
||
|
struct attribute attr;
|
||
|
ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf);
|
||
|
ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
|
||
|
const char *buf, size_t count);
|
||
|
};
|
||
|
|
||
|
struct continuous_thermal_governor {
|
||
|
struct kobject kobj;
|
||
|
struct continuous_thermal_gov_params pm;
|
||
|
|
||
|
int trip;
|
||
|
int prelta;
|
||
|
int newlta;
|
||
|
int target_pwm;
|
||
|
bool is_fan_on;
|
||
|
|
||
|
/* params used to debug */
|
||
|
int rawtemp;
|
||
|
int cur_width;
|
||
|
long delttemp;
|
||
|
long long iir_gain;
|
||
|
};
|
||
|
|
||
|
#define tz_to_gov(t) \
|
||
|
(t->governor_data)
|
||
|
|
||
|
#define gov_to_tz(g) \
|
||
|
container_of((void *)g, struct thermal_zone_device, governor_data)
|
||
|
|
||
|
#define kobj_to_gov(k) \
|
||
|
container_of(k, struct continuous_thermal_governor, kobj)
|
||
|
|
||
|
#define attr_to_gov_attr(a) \
|
||
|
container_of(a, struct continuous_thermal_gov_attribute, attr)
|
||
|
|
||
|
#define kobjp_to_kobj(kp) \
|
||
|
container_of(kp, struct kobject, parent)
|
||
|
|
||
|
|
||
|
static long long multi_gain(int iirconst, int delta, int width, int iir_power)
|
||
|
{
|
||
|
int index;
|
||
|
long long multi = iirconst * delta / width;
|
||
|
|
||
|
for (index = 0; index < iir_power - 1; index++)
|
||
|
multi = multi * delta / width;
|
||
|
|
||
|
return multi;
|
||
|
}
|
||
|
|
||
|
static ssize_t iir_power_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
return sprintf(buf, "%d\n", gov->pm.iir_power);
|
||
|
}
|
||
|
|
||
|
static ssize_t iir_power_store(struct kobject *kobj, struct attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
int val;
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
if (!sscanf(buf, "%d\n", &val) || (val < 0))
|
||
|
return -EINVAL;
|
||
|
|
||
|
gov->pm.iir_power = val;
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute iir_power_attr =
|
||
|
__ATTR(iir_power, 0644, iir_power_show, iir_power_store);
|
||
|
|
||
|
static ssize_t iir_min_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
return sprintf(buf, "%d\n", gov->pm.iir_min);
|
||
|
}
|
||
|
|
||
|
static ssize_t iir_min_store(struct kobject *kobj, struct attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
int val;
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
if (!sscanf(buf, "%d\n", &val) || (val < 0))
|
||
|
return -EINVAL;
|
||
|
|
||
|
gov->pm.iir_min = val;
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute iir_min_attr =
|
||
|
__ATTR(iir_min, 0644, iir_min_show, iir_min_store);
|
||
|
|
||
|
static ssize_t iir_width_show(struct kobject *kobj,
|
||
|
struct attribute *attr, char *buf)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
return sprintf(buf, "%d %d\n", gov->pm.iir_upper_width,
|
||
|
gov->pm.iir_lower_width);
|
||
|
}
|
||
|
|
||
|
static ssize_t iir_width_store(struct kobject *kobj, struct attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
int upper, lower;
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
if (!sscanf(buf, "%d %d\n", &upper, &lower) || (upper <= 0) || (lower <= 0))
|
||
|
return -EINVAL;
|
||
|
|
||
|
gov->pm.iir_upper_width = upper;
|
||
|
gov->pm.iir_lower_width = lower;
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute iir_width_attr =
|
||
|
__ATTR(iir_width, 0644, iir_width_show, iir_width_store);
|
||
|
|
||
|
static ssize_t gov_param_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
|
||
|
if (!gov)
|
||
|
return -ENODEV;
|
||
|
|
||
|
ret += sprintf(buf + ret, "prevLTA raw newSTA raw-prevLTA gain width pwm\n");
|
||
|
ret += sprintf(buf + ret, "%7d %7d %7d %11d %4lld %5d %5d\n",
|
||
|
gov->prelta, gov->rawtemp, gov->newlta,
|
||
|
gov->rawtemp - gov->prelta, gov->iir_gain,
|
||
|
gov->cur_width, gov->target_pwm);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute gov_param_attr =
|
||
|
__ATTR(gov_param, 0444, gov_param_show, NULL);
|
||
|
|
||
|
static ssize_t trip_temps_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
int ret = 0, trip, trip_temp;
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
struct thermal_zone_device *tz = gov_to_tz(gov);
|
||
|
|
||
|
if (!gov || !tz || !tz->ops || !tz->ops->get_trip_temp)
|
||
|
return -ENODEV;
|
||
|
|
||
|
for (trip = 0; trip < gov->trip; trip++) {
|
||
|
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||
|
ret += sprintf(buf + ret, "[%d]:%d ",
|
||
|
trip, trip_temp);
|
||
|
}
|
||
|
ret += sprintf(buf + ret, "\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute trip_temps_attr =
|
||
|
__ATTR(trip_temps, 0444, trip_temps_show, NULL);
|
||
|
|
||
|
static ssize_t trip_hyst_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
int ret = 0, trip, hyst;
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
struct thermal_zone_device *tz = gov_to_tz(gov);
|
||
|
|
||
|
if (!gov || !tz || !tz->ops || !tz->ops->get_trip_hyst)
|
||
|
return -ENODEV;
|
||
|
|
||
|
for (trip = 0; trip < gov->trip; trip++) {
|
||
|
tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||
|
ret += sprintf(buf + ret, "[%d]:%d ", trip, hyst);
|
||
|
}
|
||
|
ret += sprintf(buf + ret, "\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute trip_hyst_attr =
|
||
|
__ATTR(trip_hyst, 0444, trip_hyst_show, NULL);
|
||
|
|
||
|
static ssize_t pwm_table_show(struct kobject *kobj, struct attribute *attr,
|
||
|
char *buf)
|
||
|
{
|
||
|
int ret = 0, trip;
|
||
|
struct thermal_instance *instance;
|
||
|
struct continuous_thermal_governor *gov = kobj_to_gov(kobj);
|
||
|
struct thermal_zone_device *tz = gov_to_tz(gov);
|
||
|
|
||
|
if (!gov || !tz || !fetch_trip_pwm)
|
||
|
return -ENODEV;
|
||
|
|
||
|
for (trip = 0; trip < gov->trip; trip++) {
|
||
|
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||
|
if (instance->trip != trip)
|
||
|
continue;
|
||
|
|
||
|
ret += sprintf(buf + ret, "[%d]:%d ",
|
||
|
trip, fetch_trip_pwm(instance->cdev, trip));
|
||
|
}
|
||
|
}
|
||
|
ret += sprintf(buf + ret, "\n");
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static struct continuous_thermal_gov_attribute pwm_table_attr =
|
||
|
__ATTR(pwm_table, 0444, pwm_table_show, NULL);
|
||
|
|
||
|
static struct attribute *continuous_thermal_gov_default_attrs[] = {
|
||
|
&iir_power_attr.attr,
|
||
|
&iir_min_attr.attr,
|
||
|
&iir_width_attr.attr,
|
||
|
&gov_param_attr.attr,
|
||
|
&trip_temps_attr.attr,
|
||
|
&trip_hyst_attr.attr,
|
||
|
&pwm_table_attr.attr,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static ssize_t continuous_thermal_gov_show(struct kobject *kobj,
|
||
|
struct attribute *attr, char *buf)
|
||
|
{
|
||
|
struct continuous_thermal_gov_attribute *gov_attr = attr_to_gov_attr(attr);
|
||
|
|
||
|
if (!gov_attr->show)
|
||
|
return -EIO;
|
||
|
|
||
|
return gov_attr->show(kobj, attr, buf);
|
||
|
}
|
||
|
|
||
|
static ssize_t continuous_thermal_gov_store(struct kobject *kobj,
|
||
|
struct attribute *attr, const char *buf,
|
||
|
size_t len)
|
||
|
{
|
||
|
struct continuous_thermal_gov_attribute *gov_attr = attr_to_gov_attr(attr);
|
||
|
|
||
|
if (!gov_attr->store)
|
||
|
return -EIO;
|
||
|
|
||
|
return gov_attr->store(kobj, attr, buf, len);
|
||
|
}
|
||
|
|
||
|
static const struct sysfs_ops continuous_thermal_gov_sysfs_ops = {
|
||
|
.show = continuous_thermal_gov_show,
|
||
|
.store = continuous_thermal_gov_store,
|
||
|
};
|
||
|
|
||
|
static struct kobj_type continuous_thermal_gov_ktype = {
|
||
|
.default_attrs = continuous_thermal_gov_default_attrs,
|
||
|
.sysfs_ops = &continuous_thermal_gov_sysfs_ops,
|
||
|
};
|
||
|
|
||
|
static int continuous_thermal_gov_bind(struct thermal_zone_device *tz)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov;
|
||
|
struct continuous_thermal_gov_params *params;
|
||
|
int ret;
|
||
|
|
||
|
gov = kzalloc(sizeof(struct continuous_thermal_governor), GFP_KERNEL);
|
||
|
if (!gov) {
|
||
|
dev_err(&tz->device, "%s: Can't alloc governor data\n",
|
||
|
DRV_NAME);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
ret = kobject_init_and_add(&gov->kobj, &continuous_thermal_gov_ktype,
|
||
|
&tz->device.kobj, DRV_NAME);
|
||
|
if (ret) {
|
||
|
dev_err(&tz->device, "%s: Can't init kobject\n", DRV_NAME);
|
||
|
kobject_put(&gov->kobj);
|
||
|
kfree(gov);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
params = (struct continuous_thermal_gov_params *)tz->tzp->governor_params;
|
||
|
gov->pm = params ? *params : pm_default;
|
||
|
if (params)
|
||
|
pr_info("%s: DTB IIR params used\n", DRV_NAME);
|
||
|
else
|
||
|
pr_warn("%s: Default IIR params used\n", DRV_NAME);
|
||
|
|
||
|
if (!gov->pm.iir_upper_width)
|
||
|
gov->pm.iir_upper_width = UPPER_WIDTH;
|
||
|
if (!gov->pm.iir_lower_width)
|
||
|
gov->pm.iir_upper_width = LOWER_WIDTH;
|
||
|
|
||
|
gov->trip = tz->trips;
|
||
|
|
||
|
if (!tz->ops || !tz->ops->get_trip_temp || !tz->ops->get_trip_hyst)
|
||
|
dev_err(&tz->device, "%s: Can't get trip temp and hyst data\n", DRV_NAME);
|
||
|
|
||
|
tz_to_gov(tz) = gov;
|
||
|
pr_info("GOV gov_bind tz:%s\n", tz->type);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void continuous_thermal_gov_unbind(struct thermal_zone_device *tz)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = tz_to_gov(tz);
|
||
|
|
||
|
if (!gov)
|
||
|
return;
|
||
|
|
||
|
kobject_put(&gov->kobj);
|
||
|
kfree(gov);
|
||
|
}
|
||
|
|
||
|
static long continuous_thermal_gov_get_lta(struct thermal_zone_device *tz,
|
||
|
struct continuous_thermal_governor *gov)
|
||
|
{
|
||
|
int rawtemp, width;
|
||
|
long delttemp = 0, lta = 0;
|
||
|
long long iir_gain = 0;
|
||
|
|
||
|
tz->ops->get_temp(tz, &rawtemp);
|
||
|
gov->rawtemp = rawtemp;
|
||
|
gov->prelta = gov->newlta;
|
||
|
|
||
|
delttemp = ABS(gov->prelta, rawtemp);
|
||
|
gov->delttemp = delttemp;
|
||
|
|
||
|
if (gov->prelta - rawtemp < 0)
|
||
|
width = gov->pm.iir_upper_width;
|
||
|
else
|
||
|
width = gov->pm.iir_lower_width;
|
||
|
|
||
|
iir_gain = gov->pm.iir_min + multi_gain((gov->pm.iir_max - gov->pm.iir_min),
|
||
|
delttemp, width, gov->pm.iir_power);
|
||
|
if (iir_gain > gov->pm.iir_max) {
|
||
|
pr_debug("iir_gain:%lld iir_min:%d iir_max:%d prelta:%d rawtemp:%d newlta:%d delta:%ld width:%d\n",
|
||
|
iir_gain, gov->pm.iir_min, gov->pm.iir_max, gov->prelta, rawtemp, gov->newlta, delttemp, width);
|
||
|
iir_gain = gov->pm.iir_max;
|
||
|
}
|
||
|
|
||
|
lta = gov->prelta + iir_gain * (rawtemp - gov->prelta) / gov->pm.iir_gain_qp;
|
||
|
|
||
|
//check error
|
||
|
if ((gov->delttemp >= 100) && (gov->prelta == lta)) {
|
||
|
pr_err("GOV delttemp:%ld is big enough, rawtemp:%d gain:%lld; newlta:%ld == prelta:%d is error, should check this\n",
|
||
|
gov->delttemp, gov->rawtemp, gov->iir_gain, lta, gov->prelta);
|
||
|
}
|
||
|
|
||
|
gov->newlta = lta;
|
||
|
gov->cur_width = width;
|
||
|
gov->iir_gain = iir_gain;
|
||
|
|
||
|
return lta;
|
||
|
}
|
||
|
|
||
|
static unsigned long
|
||
|
continuous_thermal_gov_calculate_pwm(struct thermal_zone_device *tz,
|
||
|
struct thermal_cooling_device *cdev,
|
||
|
struct continuous_thermal_governor *gov, long lta, int index)
|
||
|
{
|
||
|
long long m, b;
|
||
|
unsigned long target_pwm = 0;
|
||
|
int trip_temp = 0, trip_temp_low = 0, pwm = 0, pwm_low = 0;
|
||
|
|
||
|
tz->ops->get_trip_temp(tz, index, &trip_temp);
|
||
|
tz->ops->get_trip_temp(tz, index - 1, &trip_temp_low);
|
||
|
|
||
|
pwm = fetch_trip_pwm(cdev, index);
|
||
|
pwm_low = fetch_trip_pwm(cdev, index - 1);
|
||
|
|
||
|
m = (pwm - pwm_low) * QPOINT / (trip_temp - trip_temp_low);
|
||
|
b = pwm * QPOINT - m * trip_temp;
|
||
|
|
||
|
pr_debug("GOV m:%lld b:%lld iir_gain:%lld, delttemp:%ld\n"
|
||
|
"fan pwm[%d]:%d [%d]:%d \n"
|
||
|
"trip temp[%d]:%d [%d]:%d\n"
|
||
|
"prev lta:%d cur lta: %ld\n",
|
||
|
m, b, gov->iir_gain, gov->delttemp,
|
||
|
index, pwm, index - 1, pwm_low,
|
||
|
index, trip_temp, index - 1, pwm_low,
|
||
|
gov->prelta, lta);
|
||
|
|
||
|
target_pwm = (int)(m * lta + b) / QPOINT;
|
||
|
|
||
|
return target_pwm;
|
||
|
}
|
||
|
|
||
|
static unsigned long
|
||
|
continuous_thermal_gov_get_target(struct thermal_zone_device *tz,
|
||
|
struct thermal_cooling_device *cdev, int trip)
|
||
|
{
|
||
|
struct continuous_thermal_governor *gov = tz_to_gov(tz);
|
||
|
int turn_on_temp, trip_temp, hyst = 0, index;
|
||
|
long lta = 0;
|
||
|
|
||
|
if (!gov || !trip)
|
||
|
return -1;
|
||
|
|
||
|
if (trip < tz->trips - 1)
|
||
|
return -1;
|
||
|
|
||
|
lta = continuous_thermal_gov_get_lta(tz, gov);
|
||
|
|
||
|
/*
|
||
|
* turn_on_temp is active_trip_temps[1],
|
||
|
* the condition to turn on the fan
|
||
|
*/
|
||
|
tz->ops->get_trip_temp(tz, FAN_TURN_ON_NUM, &turn_on_temp);
|
||
|
if (!gov->is_fan_on) {
|
||
|
if (lta > turn_on_temp)
|
||
|
gov->is_fan_on = true;
|
||
|
} else {
|
||
|
tz->ops->get_trip_hyst(tz, FAN_TURN_ON_NUM, &hyst);
|
||
|
|
||
|
if (lta < (turn_on_temp - hyst))
|
||
|
gov->is_fan_on = false;
|
||
|
}
|
||
|
|
||
|
//check if doing step based fan control, or smooth ramp
|
||
|
if (gov->is_fan_on) {
|
||
|
//fan table lookup
|
||
|
for (index = FAN_TURN_ON_NUM; index < tz->trips; index++) {
|
||
|
tz->ops->get_trip_temp(tz, index, &trip_temp);
|
||
|
tz->ops->get_trip_hyst(tz, index, &hyst);
|
||
|
//index==1,hyst > 0; index > 1,hyst == 0.
|
||
|
if (lta < (trip_temp - hyst))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (index >= tz->trips)
|
||
|
index = tz->trips - 1;
|
||
|
else if (index < 0) {
|
||
|
index = 0;
|
||
|
}
|
||
|
|
||
|
//Notice that the backgroud is that active_pwm[1] equal active_pwm[2].
|
||
|
gov->target_pwm = continuous_thermal_gov_calculate_pwm(tz, cdev, gov, lta, index);
|
||
|
|
||
|
} else {
|
||
|
gov->target_pwm = 0;
|
||
|
}
|
||
|
|
||
|
pr_debug("%7d %7d %7d %11d %4lld %5d %5d\n",
|
||
|
gov->prelta, gov->rawtemp, gov->newlta,
|
||
|
gov->rawtemp - gov->prelta, gov->iir_gain,
|
||
|
gov->cur_width, gov->target_pwm);
|
||
|
|
||
|
return gov->target_pwm;
|
||
|
}
|
||
|
|
||
|
static int continuous_thermal_gov_throttle(struct thermal_zone_device *tz, int trip)
|
||
|
{
|
||
|
struct thermal_instance *instance;
|
||
|
unsigned long target;
|
||
|
|
||
|
if (!fetch_trip_pwm) {
|
||
|
dev_err(&tz->device, "%s: Can't get cdev pwm value\n", DRV_NAME);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!tz->ops->get_trip_hyst) {
|
||
|
dev_err(&tz->device, "%s: Can't get trip hyst data\n", DRV_NAME);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!tz->ops->get_trip_temp) {
|
||
|
dev_err(&tz->device, "%s: Can't get trip temp\n", DRV_NAME);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&tz->lock);
|
||
|
|
||
|
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||
|
if (instance->trip != trip)
|
||
|
continue;
|
||
|
|
||
|
target = continuous_thermal_gov_get_target(tz, instance->cdev, trip);
|
||
|
|
||
|
if ((instance->target == target) || (target == -1))
|
||
|
continue;
|
||
|
|
||
|
instance->target = target;
|
||
|
instance->cdev->updated = false;
|
||
|
}
|
||
|
|
||
|
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
||
|
thermal_cdev_update(instance->cdev);
|
||
|
|
||
|
mutex_unlock(&tz->lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int continuous_thermal_gov_of_parse(struct thermal_zone_params *tzp,
|
||
|
struct device_node *np)
|
||
|
{
|
||
|
u32 val;
|
||
|
int of_err = 0;
|
||
|
struct continuous_thermal_gov_params *gpm;
|
||
|
|
||
|
gpm = kzalloc(sizeof(struct continuous_thermal_gov_params), GFP_KERNEL);
|
||
|
if (!gpm)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
*gpm = pm_default;
|
||
|
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_power", &val)))
|
||
|
gpm->iir_power = val;
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_min", &val)))
|
||
|
gpm->iir_min = val;
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_max", &val)))
|
||
|
gpm->iir_max = val;
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_gain_qp", &val)))
|
||
|
gpm->iir_gain_qp = val;
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_upper_width", &val)))
|
||
|
gpm->iir_upper_width = val;
|
||
|
if (!(of_err |= of_property_read_u32(np, "iir_lower_width", &val)))
|
||
|
gpm->iir_lower_width = val;
|
||
|
|
||
|
if (of_err)
|
||
|
pr_warn("%s: Can't init iir parameters\n", DRV_NAME);
|
||
|
|
||
|
pr_debug("GOV of parse\n");
|
||
|
tzp->governor_params = gpm;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct thermal_governor continuous_thermal_gov = {
|
||
|
.name = DRV_NAME,
|
||
|
.bind_to_tz = continuous_thermal_gov_bind,
|
||
|
.unbind_from_tz = continuous_thermal_gov_unbind,
|
||
|
.throttle = continuous_thermal_gov_throttle,
|
||
|
.of_parse = continuous_thermal_gov_of_parse,
|
||
|
};
|
||
|
|
||
|
int thermal_gov_continuous_register(void)
|
||
|
{
|
||
|
return thermal_register_governor(&continuous_thermal_gov);
|
||
|
}
|
||
|
|
||
|
void thermal_gov_continuous_unregister(void)
|
||
|
{
|
||
|
thermal_unregister_governor(&continuous_thermal_gov);
|
||
|
}
|