|
@@ -5,22 +5,9 @@
|
|
|
* Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
|
|
|
* Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
|
|
|
*
|
|
|
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
- *
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
- *
|
|
|
- * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
|
|
|
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
|
- *
|
|
|
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
*/
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
@@ -64,6 +51,13 @@ static atomic_t in_suspend;
|
|
|
|
|
|
static struct thermal_governor *def_governor;
|
|
|
|
|
|
+/*
|
|
|
+ * Governor section: set of functions to handle thermal governors
|
|
|
+ *
|
|
|
+ * Functions to help in the life cycle of thermal governors within
|
|
|
+ * the thermal core and by the thermal governor code.
|
|
|
+ */
|
|
|
+
|
|
|
static struct thermal_governor *__find_governor(const char *name)
|
|
|
{
|
|
|
struct thermal_governor *pos;
|
|
@@ -142,11 +136,16 @@ int thermal_register_governor(struct thermal_governor *governor)
|
|
|
mutex_lock(&thermal_governor_lock);
|
|
|
|
|
|
err = -EBUSY;
|
|
|
- if (__find_governor(governor->name) == NULL) {
|
|
|
+ if (!__find_governor(governor->name)) {
|
|
|
+ bool match_default;
|
|
|
+
|
|
|
err = 0;
|
|
|
list_add(&governor->governor_list, &thermal_governor_list);
|
|
|
- if (!def_governor && !strncmp(governor->name,
|
|
|
- DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
|
|
|
+ match_default = !strncmp(governor->name,
|
|
|
+ DEFAULT_THERMAL_GOVERNOR,
|
|
|
+ THERMAL_NAME_LENGTH);
|
|
|
+
|
|
|
+ if (!def_governor && match_default)
|
|
|
def_governor = governor;
|
|
|
}
|
|
|
|
|
@@ -188,14 +187,14 @@ void thermal_unregister_governor(struct thermal_governor *governor)
|
|
|
|
|
|
mutex_lock(&thermal_governor_lock);
|
|
|
|
|
|
- if (__find_governor(governor->name) == NULL)
|
|
|
+ if (!__find_governor(governor->name))
|
|
|
goto exit;
|
|
|
|
|
|
mutex_lock(&thermal_list_lock);
|
|
|
|
|
|
list_for_each_entry(pos, &thermal_tz_list, node) {
|
|
|
if (!strncasecmp(pos->governor->name, governor->name,
|
|
|
- THERMAL_NAME_LENGTH))
|
|
|
+ THERMAL_NAME_LENGTH))
|
|
|
thermal_set_governor(pos, NULL);
|
|
|
}
|
|
|
|
|
@@ -203,195 +202,92 @@ void thermal_unregister_governor(struct thermal_governor *governor)
|
|
|
list_del(&governor->governor_list);
|
|
|
exit:
|
|
|
mutex_unlock(&thermal_governor_lock);
|
|
|
- return;
|
|
|
-}
|
|
|
-
|
|
|
-static int get_idr(struct idr *idr, struct mutex *lock, int *id)
|
|
|
-{
|
|
|
- int ret;
|
|
|
-
|
|
|
- if (lock)
|
|
|
- mutex_lock(lock);
|
|
|
- ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
|
|
|
- if (lock)
|
|
|
- mutex_unlock(lock);
|
|
|
- if (unlikely(ret < 0))
|
|
|
- return ret;
|
|
|
- *id = ret;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void release_idr(struct idr *idr, struct mutex *lock, int id)
|
|
|
-{
|
|
|
- if (lock)
|
|
|
- mutex_lock(lock);
|
|
|
- idr_remove(idr, id);
|
|
|
- if (lock)
|
|
|
- mutex_unlock(lock);
|
|
|
-}
|
|
|
-
|
|
|
-int get_tz_trend(struct thermal_zone_device *tz, int trip)
|
|
|
-{
|
|
|
- enum thermal_trend trend;
|
|
|
-
|
|
|
- if (tz->emul_temperature || !tz->ops->get_trend ||
|
|
|
- tz->ops->get_trend(tz, trip, &trend)) {
|
|
|
- if (tz->temperature > tz->last_temperature)
|
|
|
- trend = THERMAL_TREND_RAISING;
|
|
|
- else if (tz->temperature < tz->last_temperature)
|
|
|
- trend = THERMAL_TREND_DROPPING;
|
|
|
- else
|
|
|
- trend = THERMAL_TREND_STABLE;
|
|
|
- }
|
|
|
-
|
|
|
- return trend;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(get_tz_trend);
|
|
|
|
|
|
-struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz,
|
|
|
- struct thermal_cooling_device *cdev, int trip)
|
|
|
+int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
|
|
|
+ char *policy)
|
|
|
{
|
|
|
- struct thermal_instance *pos = NULL;
|
|
|
- struct thermal_instance *target_instance = NULL;
|
|
|
+ struct thermal_governor *gov;
|
|
|
+ int ret = -EINVAL;
|
|
|
|
|
|
+ mutex_lock(&thermal_governor_lock);
|
|
|
mutex_lock(&tz->lock);
|
|
|
- mutex_lock(&cdev->lock);
|
|
|
|
|
|
- list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
|
|
|
- if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
|
|
- target_instance = pos;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ gov = __find_governor(strim(policy));
|
|
|
+ if (!gov)
|
|
|
+ goto exit;
|
|
|
|
|
|
- mutex_unlock(&cdev->lock);
|
|
|
- mutex_unlock(&tz->lock);
|
|
|
+ ret = thermal_set_governor(tz, gov);
|
|
|
|
|
|
- return target_instance;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(get_thermal_instance);
|
|
|
+exit:
|
|
|
+ mutex_unlock(&tz->lock);
|
|
|
+ mutex_unlock(&thermal_governor_lock);
|
|
|
|
|
|
-static void print_bind_err_msg(struct thermal_zone_device *tz,
|
|
|
- struct thermal_cooling_device *cdev, int ret)
|
|
|
-{
|
|
|
- dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n",
|
|
|
- tz->type, cdev->type, ret);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-static void __bind(struct thermal_zone_device *tz, int mask,
|
|
|
- struct thermal_cooling_device *cdev,
|
|
|
- unsigned long *limits,
|
|
|
- unsigned int weight)
|
|
|
+int thermal_build_list_of_policies(char *buf)
|
|
|
{
|
|
|
- int i, ret;
|
|
|
+ struct thermal_governor *pos;
|
|
|
+ ssize_t count = 0;
|
|
|
+ ssize_t size = PAGE_SIZE;
|
|
|
|
|
|
- for (i = 0; i < tz->trips; i++) {
|
|
|
- if (mask & (1 << i)) {
|
|
|
- unsigned long upper, lower;
|
|
|
+ mutex_lock(&thermal_governor_lock);
|
|
|
|
|
|
- upper = THERMAL_NO_LIMIT;
|
|
|
- lower = THERMAL_NO_LIMIT;
|
|
|
- if (limits) {
|
|
|
- lower = limits[i * 2];
|
|
|
- upper = limits[i * 2 + 1];
|
|
|
- }
|
|
|
- ret = thermal_zone_bind_cooling_device(tz, i, cdev,
|
|
|
- upper, lower,
|
|
|
- weight);
|
|
|
- if (ret)
|
|
|
- print_bind_err_msg(tz, cdev, ret);
|
|
|
- }
|
|
|
+ list_for_each_entry(pos, &thermal_governor_list, governor_list) {
|
|
|
+ size = PAGE_SIZE - count;
|
|
|
+ count += scnprintf(buf + count, size, "%s ", pos->name);
|
|
|
}
|
|
|
-}
|
|
|
+ count += scnprintf(buf + count, size, "\n");
|
|
|
|
|
|
-static void __unbind(struct thermal_zone_device *tz, int mask,
|
|
|
- struct thermal_cooling_device *cdev)
|
|
|
-{
|
|
|
- int i;
|
|
|
+ mutex_unlock(&thermal_governor_lock);
|
|
|
|
|
|
- for (i = 0; i < tz->trips; i++)
|
|
|
- if (mask & (1 << i))
|
|
|
- thermal_zone_unbind_cooling_device(tz, i, cdev);
|
|
|
+ return count;
|
|
|
}
|
|
|
|
|
|
-static void bind_cdev(struct thermal_cooling_device *cdev)
|
|
|
+static int __init thermal_register_governors(void)
|
|
|
{
|
|
|
- int i, ret;
|
|
|
- const struct thermal_zone_params *tzp;
|
|
|
- struct thermal_zone_device *pos = NULL;
|
|
|
-
|
|
|
- mutex_lock(&thermal_list_lock);
|
|
|
+ int result;
|
|
|
|
|
|
- list_for_each_entry(pos, &thermal_tz_list, node) {
|
|
|
- if (!pos->tzp && !pos->ops->bind)
|
|
|
- continue;
|
|
|
+ result = thermal_gov_step_wise_register();
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
|
|
|
- if (pos->ops->bind) {
|
|
|
- ret = pos->ops->bind(pos, cdev);
|
|
|
- if (ret)
|
|
|
- print_bind_err_msg(pos, cdev, ret);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ result = thermal_gov_fair_share_register();
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
|
|
|
- tzp = pos->tzp;
|
|
|
- if (!tzp || !tzp->tbp)
|
|
|
- continue;
|
|
|
+ result = thermal_gov_bang_bang_register();
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
|
|
|
- for (i = 0; i < tzp->num_tbps; i++) {
|
|
|
- if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
|
|
|
- continue;
|
|
|
- if (tzp->tbp[i].match(pos, cdev))
|
|
|
- continue;
|
|
|
- tzp->tbp[i].cdev = cdev;
|
|
|
- __bind(pos, tzp->tbp[i].trip_mask, cdev,
|
|
|
- tzp->tbp[i].binding_limits,
|
|
|
- tzp->tbp[i].weight);
|
|
|
- }
|
|
|
- }
|
|
|
+ result = thermal_gov_user_space_register();
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
|
|
|
- mutex_unlock(&thermal_list_lock);
|
|
|
+ return thermal_gov_power_allocator_register();
|
|
|
}
|
|
|
|
|
|
-static void bind_tz(struct thermal_zone_device *tz)
|
|
|
+static void thermal_unregister_governors(void)
|
|
|
{
|
|
|
- int i, ret;
|
|
|
- struct thermal_cooling_device *pos = NULL;
|
|
|
- const struct thermal_zone_params *tzp = tz->tzp;
|
|
|
-
|
|
|
- if (!tzp && !tz->ops->bind)
|
|
|
- return;
|
|
|
-
|
|
|
- mutex_lock(&thermal_list_lock);
|
|
|
-
|
|
|
- /* If there is ops->bind, try to use ops->bind */
|
|
|
- if (tz->ops->bind) {
|
|
|
- list_for_each_entry(pos, &thermal_cdev_list, node) {
|
|
|
- ret = tz->ops->bind(tz, pos);
|
|
|
- if (ret)
|
|
|
- print_bind_err_msg(tz, pos, ret);
|
|
|
- }
|
|
|
- goto exit;
|
|
|
- }
|
|
|
-
|
|
|
- if (!tzp || !tzp->tbp)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- list_for_each_entry(pos, &thermal_cdev_list, node) {
|
|
|
- for (i = 0; i < tzp->num_tbps; i++) {
|
|
|
- if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
|
|
|
- continue;
|
|
|
- if (tzp->tbp[i].match(tz, pos))
|
|
|
- continue;
|
|
|
- tzp->tbp[i].cdev = pos;
|
|
|
- __bind(tz, tzp->tbp[i].trip_mask, pos,
|
|
|
- tzp->tbp[i].binding_limits,
|
|
|
- tzp->tbp[i].weight);
|
|
|
- }
|
|
|
- }
|
|
|
-exit:
|
|
|
- mutex_unlock(&thermal_list_lock);
|
|
|
+ thermal_gov_step_wise_unregister();
|
|
|
+ thermal_gov_fair_share_unregister();
|
|
|
+ thermal_gov_bang_bang_unregister();
|
|
|
+ thermal_gov_user_space_unregister();
|
|
|
+ thermal_gov_power_allocator_unregister();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Zone update section: main control loop applied to each zone while monitoring
|
|
|
+ *
|
|
|
+ * in polling mode. The monitoring is done using a workqueue.
|
|
|
+ * Same update may be done on a zone by calling thermal_zone_device_update().
|
|
|
+ *
|
|
|
+ * An update means:
|
|
|
+ * - Non-critical trips will invoke the governor responsible for that zone;
|
|
|
+ * - Hot trips will produce a notification to userspace;
|
|
|
+ * - Critical trip point will cause a system shutdown.
|
|
|
+ */
|
|
|
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
|
|
|
int delay)
|
|
|
{
|
|
@@ -420,14 +316,15 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
|
|
|
}
|
|
|
|
|
|
static void handle_non_critical_trips(struct thermal_zone_device *tz,
|
|
|
- int trip, enum thermal_trip_type trip_type)
|
|
|
+ int trip,
|
|
|
+ enum thermal_trip_type trip_type)
|
|
|
{
|
|
|
tz->governor ? tz->governor->throttle(tz, trip) :
|
|
|
def_governor->throttle(tz, trip);
|
|
|
}
|
|
|
|
|
|
static void handle_critical_trips(struct thermal_zone_device *tz,
|
|
|
- int trip, enum thermal_trip_type trip_type)
|
|
|
+ int trip, enum thermal_trip_type trip_type)
|
|
|
{
|
|
|
int trip_temp;
|
|
|
|
|
@@ -471,105 +368,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
|
|
|
monitor_thermal_zone(tz);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * thermal_zone_get_temp() - returns the temperature of a thermal zone
|
|
|
- * @tz: a valid pointer to a struct thermal_zone_device
|
|
|
- * @temp: a valid pointer to where to store the resulting temperature.
|
|
|
- *
|
|
|
- * When a valid thermal zone reference is passed, it will fetch its
|
|
|
- * temperature and fill @temp.
|
|
|
- *
|
|
|
- * Return: On success returns 0, an error code otherwise
|
|
|
- */
|
|
|
-int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
|
|
-{
|
|
|
- int ret = -EINVAL;
|
|
|
- int count;
|
|
|
- int crit_temp = INT_MAX;
|
|
|
- enum thermal_trip_type type;
|
|
|
-
|
|
|
- if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- mutex_lock(&tz->lock);
|
|
|
-
|
|
|
- ret = tz->ops->get_temp(tz, temp);
|
|
|
-
|
|
|
- if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
|
|
|
- for (count = 0; count < tz->trips; count++) {
|
|
|
- ret = tz->ops->get_trip_type(tz, count, &type);
|
|
|
- if (!ret && type == THERMAL_TRIP_CRITICAL) {
|
|
|
- ret = tz->ops->get_trip_temp(tz, count,
|
|
|
- &crit_temp);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Only allow emulating a temperature when the real temperature
|
|
|
- * is below the critical temperature so that the emulation code
|
|
|
- * cannot hide critical conditions.
|
|
|
- */
|
|
|
- if (!ret && *temp < crit_temp)
|
|
|
- *temp = tz->emul_temperature;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_unlock(&tz->lock);
|
|
|
-exit:
|
|
|
- return ret;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
|
|
|
-
|
|
|
-void thermal_zone_set_trips(struct thermal_zone_device *tz)
|
|
|
-{
|
|
|
- int low = -INT_MAX;
|
|
|
- int high = INT_MAX;
|
|
|
- int trip_temp, hysteresis;
|
|
|
- int i, ret;
|
|
|
-
|
|
|
- mutex_lock(&tz->lock);
|
|
|
-
|
|
|
- if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- for (i = 0; i < tz->trips; i++) {
|
|
|
- int trip_low;
|
|
|
-
|
|
|
- tz->ops->get_trip_temp(tz, i, &trip_temp);
|
|
|
- tz->ops->get_trip_hyst(tz, i, &hysteresis);
|
|
|
-
|
|
|
- trip_low = trip_temp - hysteresis;
|
|
|
-
|
|
|
- if (trip_low < tz->temperature && trip_low > low)
|
|
|
- low = trip_low;
|
|
|
-
|
|
|
- if (trip_temp > tz->temperature && trip_temp < high)
|
|
|
- high = trip_temp;
|
|
|
- }
|
|
|
-
|
|
|
- /* No need to change trip points */
|
|
|
- if (tz->prev_low_trip == low && tz->prev_high_trip == high)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- tz->prev_low_trip = low;
|
|
|
- tz->prev_high_trip = high;
|
|
|
-
|
|
|
- dev_dbg(&tz->device,
|
|
|
- "new temperature boundaries: %d < x < %d\n", low, high);
|
|
|
-
|
|
|
- /*
|
|
|
- * Set a temperature window. When this window is left the driver
|
|
|
- * must inform the thermal core via thermal_zone_device_update.
|
|
|
- */
|
|
|
- ret = tz->ops->set_trips(tz, low, high);
|
|
|
- if (ret)
|
|
|
- dev_err(&tz->device, "Failed to set trips: %d\n", ret);
|
|
|
-
|
|
|
-exit:
|
|
|
- mutex_unlock(&tz->lock);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
|
|
|
-
|
|
|
static void update_temperature(struct thermal_zone_device *tz)
|
|
|
{
|
|
|
int temp, ret;
|
|
@@ -629,6 +427,24 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
|
|
|
|
|
|
+/**
|
|
|
+ * thermal_notify_framework - Sensor drivers use this API to notify framework
|
|
|
+ * @tz: thermal zone device
|
|
|
+ * @trip: indicates which trip point has been crossed
|
|
|
+ *
|
|
|
+ * This function handles the trip events from sensor drivers. It starts
|
|
|
+ * throttling the cooling devices according to the policy configured.
|
|
|
+ * For CRITICAL and HOT trip points, this notifies the respective drivers,
|
|
|
+ * and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
|
|
|
+ * The throttling policy is based on the configured platform data; if no
|
|
|
+ * platform data is provided, this uses the step_wise throttling policy.
|
|
|
+ */
|
|
|
+void thermal_notify_framework(struct thermal_zone_device *tz, int trip)
|
|
|
+{
|
|
|
+ handle_thermal_trip(tz, trip);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(thermal_notify_framework);
|
|
|
+
|
|
|
static void thermal_zone_device_check(struct work_struct *work)
|
|
|
{
|
|
|
struct thermal_zone_device *tz = container_of(work, struct
|
|
@@ -637,445 +453,12 @@ static void thermal_zone_device_check(struct work_struct *work)
|
|
|
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
|
|
}
|
|
|
|
|
|
-/* sys I/F for thermal zone */
|
|
|
-
|
|
|
-#define to_thermal_zone(_dev) \
|
|
|
- container_of(_dev, struct thermal_zone_device, device)
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
-
|
|
|
- return sprintf(buf, "%s\n", tz->type);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-temp_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int temperature, ret;
|
|
|
-
|
|
|
- ret = thermal_zone_get_temp(tz, &temperature);
|
|
|
-
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", temperature);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- enum thermal_device_mode mode;
|
|
|
- int result;
|
|
|
-
|
|
|
- if (!tz->ops->get_mode)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- result = tz->ops->get_mode(tz, &mode);
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
|
|
|
- : "disabled");
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-mode_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int result;
|
|
|
-
|
|
|
- if (!tz->ops->set_mode)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
|
|
|
- result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
|
|
|
- else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
|
|
|
- result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
|
|
|
- else
|
|
|
- result = -EINVAL;
|
|
|
-
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- enum thermal_trip_type type;
|
|
|
- int trip, result;
|
|
|
-
|
|
|
- if (!tz->ops->get_trip_type)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- result = tz->ops->get_trip_type(tz, trip, &type);
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- switch (type) {
|
|
|
- case THERMAL_TRIP_CRITICAL:
|
|
|
- return sprintf(buf, "critical\n");
|
|
|
- case THERMAL_TRIP_HOT:
|
|
|
- return sprintf(buf, "hot\n");
|
|
|
- case THERMAL_TRIP_PASSIVE:
|
|
|
- return sprintf(buf, "passive\n");
|
|
|
- case THERMAL_TRIP_ACTIVE:
|
|
|
- return sprintf(buf, "active\n");
|
|
|
- default:
|
|
|
- return sprintf(buf, "unknown\n");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int trip, ret;
|
|
|
- int temperature;
|
|
|
-
|
|
|
- if (!tz->ops->set_trip_temp)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (kstrtoint(buf, 10, &temperature))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- ret = tz->ops->set_trip_temp(tz, trip, temperature);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int trip, ret;
|
|
|
- int temperature;
|
|
|
-
|
|
|
- if (!tz->ops->get_trip_temp)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- ret = tz->ops->get_trip_temp(tz, trip, &temperature);
|
|
|
-
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", temperature);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int trip, ret;
|
|
|
- int temperature;
|
|
|
-
|
|
|
- if (!tz->ops->set_trip_hyst)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (kstrtoint(buf, 10, &temperature))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- /*
|
|
|
- * We are not doing any check on the 'temperature' value
|
|
|
- * here. The driver implementing 'set_trip_hyst' has to
|
|
|
- * take care of this.
|
|
|
- */
|
|
|
- ret = tz->ops->set_trip_hyst(tz, trip, temperature);
|
|
|
-
|
|
|
- if (!ret)
|
|
|
- thermal_zone_set_trips(tz);
|
|
|
-
|
|
|
- return ret ? ret : count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int trip, ret;
|
|
|
- int temperature;
|
|
|
-
|
|
|
- if (!tz->ops->get_trip_hyst)
|
|
|
- return -EPERM;
|
|
|
-
|
|
|
- if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
|
|
|
-
|
|
|
- return ret ? ret : sprintf(buf, "%d\n", temperature);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-passive_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- struct thermal_cooling_device *cdev = NULL;
|
|
|
- int state;
|
|
|
-
|
|
|
- if (!sscanf(buf, "%d\n", &state))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- /* sanity check: values below 1000 millicelcius don't make sense
|
|
|
- * and can cause the system to go into a thermal heart attack
|
|
|
- */
|
|
|
- if (state && state < 1000)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (state && !tz->forced_passive) {
|
|
|
- mutex_lock(&thermal_list_lock);
|
|
|
- list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
|
|
- if (!strncmp("Processor", cdev->type,
|
|
|
- sizeof("Processor")))
|
|
|
- thermal_zone_bind_cooling_device(tz,
|
|
|
- THERMAL_TRIPS_NONE, cdev,
|
|
|
- THERMAL_NO_LIMIT,
|
|
|
- THERMAL_NO_LIMIT,
|
|
|
- THERMAL_WEIGHT_DEFAULT);
|
|
|
- }
|
|
|
- mutex_unlock(&thermal_list_lock);
|
|
|
- if (!tz->passive_delay)
|
|
|
- tz->passive_delay = 1000;
|
|
|
- } else if (!state && tz->forced_passive) {
|
|
|
- mutex_lock(&thermal_list_lock);
|
|
|
- list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
|
|
- if (!strncmp("Processor", cdev->type,
|
|
|
- sizeof("Processor")))
|
|
|
- thermal_zone_unbind_cooling_device(tz,
|
|
|
- THERMAL_TRIPS_NONE,
|
|
|
- cdev);
|
|
|
- }
|
|
|
- mutex_unlock(&thermal_list_lock);
|
|
|
- tz->passive_delay = 0;
|
|
|
- }
|
|
|
-
|
|
|
- tz->forced_passive = state;
|
|
|
-
|
|
|
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-passive_show(struct device *dev, struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", tz->forced_passive);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-policy_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- int ret = -EINVAL;
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- struct thermal_governor *gov;
|
|
|
- char name[THERMAL_NAME_LENGTH];
|
|
|
-
|
|
|
- snprintf(name, sizeof(name), "%s", buf);
|
|
|
-
|
|
|
- mutex_lock(&thermal_governor_lock);
|
|
|
- mutex_lock(&tz->lock);
|
|
|
-
|
|
|
- gov = __find_governor(strim(name));
|
|
|
- if (!gov)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- ret = thermal_set_governor(tz, gov);
|
|
|
- if (!ret)
|
|
|
- ret = count;
|
|
|
-
|
|
|
-exit:
|
|
|
- mutex_unlock(&tz->lock);
|
|
|
- mutex_unlock(&thermal_governor_lock);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
-
|
|
|
- return sprintf(buf, "%s\n", tz->governor->name);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-available_policies_show(struct device *dev, struct device_attribute *devattr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_governor *pos;
|
|
|
- ssize_t count = 0;
|
|
|
- ssize_t size = PAGE_SIZE;
|
|
|
-
|
|
|
- mutex_lock(&thermal_governor_lock);
|
|
|
-
|
|
|
- list_for_each_entry(pos, &thermal_governor_list, governor_list) {
|
|
|
- size = PAGE_SIZE - count;
|
|
|
- count += scnprintf(buf + count, size, "%s ", pos->name);
|
|
|
- }
|
|
|
- count += scnprintf(buf + count, size, "\n");
|
|
|
-
|
|
|
- mutex_unlock(&thermal_governor_lock);
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-emul_temp_store(struct device *dev, struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- int ret = 0;
|
|
|
- int temperature;
|
|
|
-
|
|
|
- if (kstrtoint(buf, 10, &temperature))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (!tz->ops->set_emul_temp) {
|
|
|
- mutex_lock(&tz->lock);
|
|
|
- tz->emul_temperature = temperature;
|
|
|
- mutex_unlock(&tz->lock);
|
|
|
- } else {
|
|
|
- ret = tz->ops->set_emul_temp(tz, temperature);
|
|
|
- }
|
|
|
-
|
|
|
- if (!ret)
|
|
|
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
|
|
-
|
|
|
- return ret ? ret : count;
|
|
|
-}
|
|
|
-static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-sustainable_power_show(struct device *dev, struct device_attribute *devattr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
-
|
|
|
- if (tz->tzp)
|
|
|
- return sprintf(buf, "%u\n", tz->tzp->sustainable_power);
|
|
|
- else
|
|
|
- return -EIO;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-sustainable_power_store(struct device *dev, struct device_attribute *devattr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev);
|
|
|
- u32 sustainable_power;
|
|
|
-
|
|
|
- if (!tz->tzp)
|
|
|
- return -EIO;
|
|
|
-
|
|
|
- if (kstrtou32(buf, 10, &sustainable_power))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- tz->tzp->sustainable_power = sustainable_power;
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show,
|
|
|
- sustainable_power_store);
|
|
|
-
|
|
|
-#define create_s32_tzp_attr(name) \
|
|
|
- static ssize_t \
|
|
|
- name##_show(struct device *dev, struct device_attribute *devattr, \
|
|
|
- char *buf) \
|
|
|
- { \
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev); \
|
|
|
- \
|
|
|
- if (tz->tzp) \
|
|
|
- return sprintf(buf, "%d\n", tz->tzp->name); \
|
|
|
- else \
|
|
|
- return -EIO; \
|
|
|
- } \
|
|
|
- \
|
|
|
- static ssize_t \
|
|
|
- name##_store(struct device *dev, struct device_attribute *devattr, \
|
|
|
- const char *buf, size_t count) \
|
|
|
- { \
|
|
|
- struct thermal_zone_device *tz = to_thermal_zone(dev); \
|
|
|
- s32 value; \
|
|
|
- \
|
|
|
- if (!tz->tzp) \
|
|
|
- return -EIO; \
|
|
|
- \
|
|
|
- if (kstrtos32(buf, 10, &value)) \
|
|
|
- return -EINVAL; \
|
|
|
- \
|
|
|
- tz->tzp->name = value; \
|
|
|
- \
|
|
|
- return count; \
|
|
|
- } \
|
|
|
- static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store)
|
|
|
-
|
|
|
-create_s32_tzp_attr(k_po);
|
|
|
-create_s32_tzp_attr(k_pu);
|
|
|
-create_s32_tzp_attr(k_i);
|
|
|
-create_s32_tzp_attr(k_d);
|
|
|
-create_s32_tzp_attr(integral_cutoff);
|
|
|
-create_s32_tzp_attr(slope);
|
|
|
-create_s32_tzp_attr(offset);
|
|
|
-#undef create_s32_tzp_attr
|
|
|
-
|
|
|
-static struct device_attribute *dev_tzp_attrs[] = {
|
|
|
- &dev_attr_sustainable_power,
|
|
|
- &dev_attr_k_po,
|
|
|
- &dev_attr_k_pu,
|
|
|
- &dev_attr_k_i,
|
|
|
- &dev_attr_k_d,
|
|
|
- &dev_attr_integral_cutoff,
|
|
|
- &dev_attr_slope,
|
|
|
- &dev_attr_offset,
|
|
|
-};
|
|
|
-
|
|
|
-static int create_tzp_attrs(struct device *dev)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) {
|
|
|
- int ret;
|
|
|
- struct device_attribute *dev_attr = dev_tzp_attrs[i];
|
|
|
-
|
|
|
- ret = device_create_file(dev, dev_attr);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
+/*
|
|
|
+ * Power actor section: interface to power actors to estimate power
|
|
|
+ *
|
|
|
+ * Set of functions used to interact to cooling devices that know
|
|
|
+ * how to estimate their devices power consumption.
|
|
|
+ */
|
|
|
|
|
|
/**
|
|
|
* power_actor_get_max_power() - get the maximum power that a cdev can consume
|
|
@@ -1127,12 +510,13 @@ int power_actor_get_min_power(struct thermal_cooling_device *cdev,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * power_actor_set_power() - limit the maximum power that a cooling device can consume
|
|
|
+ * power_actor_set_power() - limit the maximum power a cooling device consumes
|
|
|
* @cdev: pointer to &thermal_cooling_device
|
|
|
* @instance: thermal instance to update
|
|
|
* @power: the power in milliwatts
|
|
|
*
|
|
|
- * Set the cooling device to consume at most @power milliwatts.
|
|
|
+ * Set the cooling device to consume at most @power milliwatts. The limit is
|
|
|
+ * expected to be a cap at the maximum power consumption.
|
|
|
*
|
|
|
* Return: 0 on success, -EINVAL if the cooling device does not
|
|
|
* implement the power actor API or -E* for other failures.
|
|
@@ -1159,143 +543,75 @@ int power_actor_set_power(struct thermal_cooling_device *cdev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static DEVICE_ATTR(type, 0444, type_show, NULL);
|
|
|
-static DEVICE_ATTR(temp, 0444, temp_show, NULL);
|
|
|
-static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
|
|
|
-static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
|
|
|
-static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store);
|
|
|
-static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL);
|
|
|
-
|
|
|
-/* sys I/F for cooling device */
|
|
|
-#define to_cooling_device(_dev) \
|
|
|
- container_of(_dev, struct thermal_cooling_device, device)
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_type_show(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
+void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
|
|
|
+ const char *cdev_type, size_t size)
|
|
|
{
|
|
|
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
|
|
+ struct thermal_cooling_device *cdev = NULL;
|
|
|
|
|
|
- return sprintf(buf, "%s\n", cdev->type);
|
|
|
+ mutex_lock(&thermal_list_lock);
|
|
|
+ list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
|
|
+ /* skip non matching cdevs */
|
|
|
+ if (strncmp(cdev_type, cdev->type, size))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* re binding the exception matching the type pattern */
|
|
|
+ thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev,
|
|
|
+ THERMAL_NO_LIMIT,
|
|
|
+ THERMAL_NO_LIMIT,
|
|
|
+ THERMAL_WEIGHT_DEFAULT);
|
|
|
+ }
|
|
|
+ mutex_unlock(&thermal_list_lock);
|
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_max_state_show(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
+void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz,
|
|
|
+ const char *cdev_type, size_t size)
|
|
|
{
|
|
|
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
|
|
- unsigned long state;
|
|
|
- int ret;
|
|
|
+ struct thermal_cooling_device *cdev = NULL;
|
|
|
|
|
|
- ret = cdev->ops->get_max_state(cdev, &state);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- return sprintf(buf, "%ld\n", state);
|
|
|
+ mutex_lock(&thermal_list_lock);
|
|
|
+ list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
|
|
+ /* skip non matching cdevs */
|
|
|
+ if (strncmp(cdev_type, cdev->type, size))
|
|
|
+ continue;
|
|
|
+ /* unbinding the exception matching the type pattern */
|
|
|
+ thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE,
|
|
|
+ cdev);
|
|
|
+ }
|
|
|
+ mutex_unlock(&thermal_list_lock);
|
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_cur_state_show(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
+/*
|
|
|
+ * Device management section: cooling devices, zones devices, and binding
|
|
|
+ *
|
|
|
+ * Set of functions provided by the thermal core for:
|
|
|
+ * - cooling devices lifecycle: registration, unregistration,
|
|
|
+ * binding, and unbinding.
|
|
|
+ * - thermal zone devices lifecycle: registration, unregistration,
|
|
|
+ * binding, and unbinding.
|
|
|
+ */
|
|
|
+static int get_idr(struct idr *idr, struct mutex *lock, int *id)
|
|
|
{
|
|
|
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
|
|
- unsigned long state;
|
|
|
int ret;
|
|
|
|
|
|
- ret = cdev->ops->get_cur_state(cdev, &state);
|
|
|
- if (ret)
|
|
|
+ if (lock)
|
|
|
+ mutex_lock(lock);
|
|
|
+ ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
|
|
|
+ if (lock)
|
|
|
+ mutex_unlock(lock);
|
|
|
+ if (unlikely(ret < 0))
|
|
|
return ret;
|
|
|
- return sprintf(buf, "%ld\n", state);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_cur_state_store(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
|
|
- unsigned long state;
|
|
|
- int result;
|
|
|
-
|
|
|
- if (!sscanf(buf, "%ld\n", &state))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if ((long)state < 0)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- result = cdev->ops->set_cur_state(cdev, state);
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static struct device_attribute dev_attr_cdev_type =
|
|
|
-__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
|
|
|
-static DEVICE_ATTR(max_state, 0444,
|
|
|
- thermal_cooling_device_max_state_show, NULL);
|
|
|
-static DEVICE_ATTR(cur_state, 0644,
|
|
|
- thermal_cooling_device_cur_state_show,
|
|
|
- thermal_cooling_device_cur_state_store);
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_trip_point_show(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_instance *instance;
|
|
|
-
|
|
|
- instance =
|
|
|
- container_of(attr, struct thermal_instance, attr);
|
|
|
-
|
|
|
- if (instance->trip == THERMAL_TRIPS_NONE)
|
|
|
- return sprintf(buf, "-1\n");
|
|
|
- else
|
|
|
- return sprintf(buf, "%d\n", instance->trip);
|
|
|
-}
|
|
|
-
|
|
|
-static struct attribute *cooling_device_attrs[] = {
|
|
|
- &dev_attr_cdev_type.attr,
|
|
|
- &dev_attr_max_state.attr,
|
|
|
- &dev_attr_cur_state.attr,
|
|
|
- NULL,
|
|
|
-};
|
|
|
-
|
|
|
-static const struct attribute_group cooling_device_attr_group = {
|
|
|
- .attrs = cooling_device_attrs,
|
|
|
-};
|
|
|
-
|
|
|
-static const struct attribute_group *cooling_device_attr_groups[] = {
|
|
|
- &cooling_device_attr_group,
|
|
|
- NULL,
|
|
|
-};
|
|
|
-
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_weight_show(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- struct thermal_instance *instance;
|
|
|
-
|
|
|
- instance = container_of(attr, struct thermal_instance, weight_attr);
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", instance->weight);
|
|
|
+ *id = ret;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
-thermal_cooling_device_weight_store(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct thermal_instance *instance;
|
|
|
- int ret, weight;
|
|
|
-
|
|
|
- ret = kstrtoint(buf, 0, &weight);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- instance = container_of(attr, struct thermal_instance, weight_attr);
|
|
|
- instance->weight = weight;
|
|
|
-
|
|
|
- return count;
|
|
|
+static void release_idr(struct idr *idr, struct mutex *lock, int id)
|
|
|
+{
|
|
|
+ if (lock)
|
|
|
+ mutex_lock(lock);
|
|
|
+ idr_remove(idr, id);
|
|
|
+ if (lock)
|
|
|
+ mutex_unlock(lock);
|
|
|
}
|
|
|
-/* Device management */
|
|
|
|
|
|
/**
|
|
|
* thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone
|
|
@@ -1358,8 +674,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
|
|
if (lower > upper || upper > max_state)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- dev =
|
|
|
- kzalloc(sizeof(struct thermal_instance), GFP_KERNEL);
|
|
|
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
|
if (!dev)
|
|
|
return -ENOMEM;
|
|
|
dev->tz = tz;
|
|
@@ -1402,10 +717,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
|
|
mutex_lock(&tz->lock);
|
|
|
mutex_lock(&cdev->lock);
|
|
|
list_for_each_entry(pos, &tz->thermal_instances, tz_node)
|
|
|
- if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
|
|
- result = -EEXIST;
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
|
|
|
+ result = -EEXIST;
|
|
|
+ break;
|
|
|
+ }
|
|
|
if (!result) {
|
|
|
list_add_tail(&dev->tz_node, &tz->thermal_instances);
|
|
|
list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
|
|
@@ -1485,8 +800,8 @@ static void thermal_release(struct device *dev)
|
|
|
sizeof("thermal_zone") - 1)) {
|
|
|
tz = to_thermal_zone(dev);
|
|
|
kfree(tz);
|
|
|
- } else if(!strncmp(dev_name(dev), "cooling_device",
|
|
|
- sizeof("cooling_device") - 1)){
|
|
|
+ } else if (!strncmp(dev_name(dev), "cooling_device",
|
|
|
+ sizeof("cooling_device") - 1)) {
|
|
|
cdev = to_cooling_device(dev);
|
|
|
kfree(cdev);
|
|
|
}
|
|
@@ -1497,6 +812,78 @@ static struct class thermal_class = {
|
|
|
.dev_release = thermal_release,
|
|
|
};
|
|
|
|
|
|
+static inline
|
|
|
+void print_bind_err_msg(struct thermal_zone_device *tz,
|
|
|
+ struct thermal_cooling_device *cdev, int ret)
|
|
|
+{
|
|
|
+ dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n",
|
|
|
+ tz->type, cdev->type, ret);
|
|
|
+}
|
|
|
+
|
|
|
+static void __bind(struct thermal_zone_device *tz, int mask,
|
|
|
+ struct thermal_cooling_device *cdev,
|
|
|
+ unsigned long *limits,
|
|
|
+ unsigned int weight)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ for (i = 0; i < tz->trips; i++) {
|
|
|
+ if (mask & (1 << i)) {
|
|
|
+ unsigned long upper, lower;
|
|
|
+
|
|
|
+ upper = THERMAL_NO_LIMIT;
|
|
|
+ lower = THERMAL_NO_LIMIT;
|
|
|
+ if (limits) {
|
|
|
+ lower = limits[i * 2];
|
|
|
+ upper = limits[i * 2 + 1];
|
|
|
+ }
|
|
|
+ ret = thermal_zone_bind_cooling_device(tz, i, cdev,
|
|
|
+ upper, lower,
|
|
|
+ weight);
|
|
|
+ if (ret)
|
|
|
+ print_bind_err_msg(tz, cdev, ret);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void bind_cdev(struct thermal_cooling_device *cdev)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+ const struct thermal_zone_params *tzp;
|
|
|
+ struct thermal_zone_device *pos = NULL;
|
|
|
+
|
|
|
+ mutex_lock(&thermal_list_lock);
|
|
|
+
|
|
|
+ list_for_each_entry(pos, &thermal_tz_list, node) {
|
|
|
+ if (!pos->tzp && !pos->ops->bind)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (pos->ops->bind) {
|
|
|
+ ret = pos->ops->bind(pos, cdev);
|
|
|
+ if (ret)
|
|
|
+ print_bind_err_msg(pos, cdev, ret);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ tzp = pos->tzp;
|
|
|
+ if (!tzp || !tzp->tbp)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ for (i = 0; i < tzp->num_tbps; i++) {
|
|
|
+ if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
|
|
|
+ continue;
|
|
|
+ if (tzp->tbp[i].match(pos, cdev))
|
|
|
+ continue;
|
|
|
+ tzp->tbp[i].cdev = cdev;
|
|
|
+ __bind(pos, tzp->tbp[i].trip_mask, cdev,
|
|
|
+ tzp->tbp[i].binding_limits,
|
|
|
+ tzp->tbp[i].weight);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&thermal_list_lock);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __thermal_cooling_device_register() - register a new thermal cooling device
|
|
|
* @np: a pointer to a device tree node.
|
|
@@ -1529,7 +916,7 @@ __thermal_cooling_device_register(struct device_node *np,
|
|
|
!ops->set_cur_state)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
|
|
|
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
|
|
if (!cdev)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
@@ -1546,7 +933,7 @@ __thermal_cooling_device_register(struct device_node *np,
|
|
|
cdev->ops = ops;
|
|
|
cdev->updated = false;
|
|
|
cdev->device.class = &thermal_class;
|
|
|
- cdev->device.groups = cooling_device_attr_groups;
|
|
|
+ thermal_cooling_device_setup_sysfs(cdev);
|
|
|
cdev->devdata = devdata;
|
|
|
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
|
|
result = device_register(&cdev->device);
|
|
@@ -1619,12 +1006,22 @@ thermal_of_cooling_device_register(struct device_node *np,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
|
|
|
|
|
|
+static void __unbind(struct thermal_zone_device *tz, int mask,
|
|
|
+ struct thermal_cooling_device *cdev)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < tz->trips; i++)
|
|
|
+ if (mask & (1 << i))
|
|
|
+ thermal_zone_unbind_cooling_device(tz, i, cdev);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * thermal_cooling_device_unregister - removes the registered thermal cooling device
|
|
|
+ * thermal_cooling_device_unregister - removes a thermal cooling device
|
|
|
* @cdev: the thermal cooling device to remove.
|
|
|
*
|
|
|
- * thermal_cooling_device_unregister() must be called when the device is no
|
|
|
- * longer needed.
|
|
|
+ * thermal_cooling_device_unregister() must be called when a registered
|
|
|
+ * thermal cooling device is no longer needed.
|
|
|
*/
|
|
|
void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
|
|
{
|
|
@@ -1638,8 +1035,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
|
|
|
|
|
mutex_lock(&thermal_list_lock);
|
|
|
list_for_each_entry(pos, &thermal_cdev_list, node)
|
|
|
- if (pos == cdev)
|
|
|
- break;
|
|
|
+ if (pos == cdev)
|
|
|
+ break;
|
|
|
if (pos != cdev) {
|
|
|
/* thermal cooling device not found */
|
|
|
mutex_unlock(&thermal_list_lock);
|
|
@@ -1668,171 +1065,49 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
|
|
|
|
|
mutex_unlock(&thermal_list_lock);
|
|
|
|
|
|
- if (cdev->type[0])
|
|
|
- device_remove_file(&cdev->device, &dev_attr_cdev_type);
|
|
|
- device_remove_file(&cdev->device, &dev_attr_max_state);
|
|
|
- device_remove_file(&cdev->device, &dev_attr_cur_state);
|
|
|
-
|
|
|
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
|
|
|
device_unregister(&cdev->device);
|
|
|
- return;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
|
|
|
|
|
|
-void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
|
|
+static void bind_tz(struct thermal_zone_device *tz)
|
|
|
{
|
|
|
- struct thermal_instance *instance;
|
|
|
- unsigned long target = 0;
|
|
|
+ int i, ret;
|
|
|
+ struct thermal_cooling_device *pos = NULL;
|
|
|
+ const struct thermal_zone_params *tzp = tz->tzp;
|
|
|
|
|
|
- mutex_lock(&cdev->lock);
|
|
|
- /* cooling device is updated*/
|
|
|
- if (cdev->updated) {
|
|
|
- mutex_unlock(&cdev->lock);
|
|
|
+ if (!tzp && !tz->ops->bind)
|
|
|
return;
|
|
|
- }
|
|
|
-
|
|
|
- /* Make sure cdev enters the deepest cooling state */
|
|
|
- list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
|
|
|
- dev_dbg(&cdev->device, "zone%d->target=%lu\n",
|
|
|
- instance->tz->id, instance->target);
|
|
|
- if (instance->target == THERMAL_NO_TARGET)
|
|
|
- continue;
|
|
|
- if (instance->target > target)
|
|
|
- target = instance->target;
|
|
|
- }
|
|
|
- cdev->ops->set_cur_state(cdev, target);
|
|
|
- cdev->updated = true;
|
|
|
- mutex_unlock(&cdev->lock);
|
|
|
- trace_cdev_update(cdev, target);
|
|
|
- dev_dbg(&cdev->device, "set to state %lu\n", target);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(thermal_cdev_update);
|
|
|
-
|
|
|
-/**
|
|
|
- * thermal_notify_framework - Sensor drivers use this API to notify framework
|
|
|
- * @tz: thermal zone device
|
|
|
- * @trip: indicates which trip point has been crossed
|
|
|
- *
|
|
|
- * This function handles the trip events from sensor drivers. It starts
|
|
|
- * throttling the cooling devices according to the policy configured.
|
|
|
- * For CRITICAL and HOT trip points, this notifies the respective drivers,
|
|
|
- * and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
|
|
|
- * The throttling policy is based on the configured platform data; if no
|
|
|
- * platform data is provided, this uses the step_wise throttling policy.
|
|
|
- */
|
|
|
-void thermal_notify_framework(struct thermal_zone_device *tz, int trip)
|
|
|
-{
|
|
|
- handle_thermal_trip(tz, trip);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(thermal_notify_framework);
|
|
|
-
|
|
|
-/**
|
|
|
- * create_trip_attrs() - create attributes for trip points
|
|
|
- * @tz: the thermal zone device
|
|
|
- * @mask: Writeable trip point bitmap.
|
|
|
- *
|
|
|
- * helper function to instantiate sysfs entries for every trip
|
|
|
- * point and its properties of a struct thermal_zone_device.
|
|
|
- *
|
|
|
- * Return: 0 on success, the proper error value otherwise.
|
|
|
- */
|
|
|
-static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
|
|
|
-{
|
|
|
- int indx;
|
|
|
- int size = sizeof(struct thermal_attr) * tz->trips;
|
|
|
|
|
|
- tz->trip_type_attrs = kzalloc(size, GFP_KERNEL);
|
|
|
- if (!tz->trip_type_attrs)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL);
|
|
|
- if (!tz->trip_temp_attrs) {
|
|
|
- kfree(tz->trip_type_attrs);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ mutex_lock(&thermal_list_lock);
|
|
|
|
|
|
- if (tz->ops->get_trip_hyst) {
|
|
|
- tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL);
|
|
|
- if (!tz->trip_hyst_attrs) {
|
|
|
- kfree(tz->trip_type_attrs);
|
|
|
- kfree(tz->trip_temp_attrs);
|
|
|
- return -ENOMEM;
|
|
|
+ /* If there is ops->bind, try to use ops->bind */
|
|
|
+ if (tz->ops->bind) {
|
|
|
+ list_for_each_entry(pos, &thermal_cdev_list, node) {
|
|
|
+ ret = tz->ops->bind(tz, pos);
|
|
|
+ if (ret)
|
|
|
+ print_bind_err_msg(tz, pos, ret);
|
|
|
}
|
|
|
+ goto exit;
|
|
|
}
|
|
|
|
|
|
+ if (!tzp || !tzp->tbp)
|
|
|
+ goto exit;
|
|
|
|
|
|
- for (indx = 0; indx < tz->trips; indx++) {
|
|
|
- /* create trip type attribute */
|
|
|
- snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
|
|
|
- "trip_point_%d_type", indx);
|
|
|
-
|
|
|
- sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr);
|
|
|
- tz->trip_type_attrs[indx].attr.attr.name =
|
|
|
- tz->trip_type_attrs[indx].name;
|
|
|
- tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO;
|
|
|
- tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
|
|
|
-
|
|
|
- device_create_file(&tz->device,
|
|
|
- &tz->trip_type_attrs[indx].attr);
|
|
|
-
|
|
|
- /* create trip temp attribute */
|
|
|
- snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
|
|
|
- "trip_point_%d_temp", indx);
|
|
|
-
|
|
|
- sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr);
|
|
|
- tz->trip_temp_attrs[indx].attr.attr.name =
|
|
|
- tz->trip_temp_attrs[indx].name;
|
|
|
- tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
|
|
|
- tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
|
|
|
- if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) &&
|
|
|
- mask & (1 << indx)) {
|
|
|
- tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
|
|
|
- tz->trip_temp_attrs[indx].attr.store =
|
|
|
- trip_point_temp_store;
|
|
|
- }
|
|
|
-
|
|
|
- device_create_file(&tz->device,
|
|
|
- &tz->trip_temp_attrs[indx].attr);
|
|
|
-
|
|
|
- /* create Optional trip hyst attribute */
|
|
|
- if (!tz->ops->get_trip_hyst)
|
|
|
- continue;
|
|
|
- snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH,
|
|
|
- "trip_point_%d_hyst", indx);
|
|
|
-
|
|
|
- sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr);
|
|
|
- tz->trip_hyst_attrs[indx].attr.attr.name =
|
|
|
- tz->trip_hyst_attrs[indx].name;
|
|
|
- tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
|
|
|
- tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show;
|
|
|
- if (tz->ops->set_trip_hyst) {
|
|
|
- tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR;
|
|
|
- tz->trip_hyst_attrs[indx].attr.store =
|
|
|
- trip_point_hyst_store;
|
|
|
+ list_for_each_entry(pos, &thermal_cdev_list, node) {
|
|
|
+ for (i = 0; i < tzp->num_tbps; i++) {
|
|
|
+ if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
|
|
|
+ continue;
|
|
|
+ if (tzp->tbp[i].match(tz, pos))
|
|
|
+ continue;
|
|
|
+ tzp->tbp[i].cdev = pos;
|
|
|
+ __bind(tz, tzp->tbp[i].trip_mask, pos,
|
|
|
+ tzp->tbp[i].binding_limits,
|
|
|
+ tzp->tbp[i].weight);
|
|
|
}
|
|
|
-
|
|
|
- device_create_file(&tz->device,
|
|
|
- &tz->trip_hyst_attrs[indx].attr);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void remove_trip_attrs(struct thermal_zone_device *tz)
|
|
|
-{
|
|
|
- int indx;
|
|
|
-
|
|
|
- for (indx = 0; indx < tz->trips; indx++) {
|
|
|
- device_remove_file(&tz->device,
|
|
|
- &tz->trip_type_attrs[indx].attr);
|
|
|
- device_remove_file(&tz->device,
|
|
|
- &tz->trip_temp_attrs[indx].attr);
|
|
|
- if (tz->ops->get_trip_hyst)
|
|
|
- device_remove_file(&tz->device,
|
|
|
- &tz->trip_hyst_attrs[indx].attr);
|
|
|
}
|
|
|
- kfree(tz->trip_type_attrs);
|
|
|
- kfree(tz->trip_temp_attrs);
|
|
|
- kfree(tz->trip_hyst_attrs);
|
|
|
+exit:
|
|
|
+ mutex_unlock(&thermal_list_lock);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1859,20 +1134,22 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
|
|
|
* in case of error, an ERR_PTR. Caller must check return value with
|
|
|
* IS_ERR*() helpers.
|
|
|
*/
|
|
|
-struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
- int trips, int mask, void *devdata,
|
|
|
- struct thermal_zone_device_ops *ops,
|
|
|
- struct thermal_zone_params *tzp,
|
|
|
- int passive_delay, int polling_delay)
|
|
|
+struct thermal_zone_device *
|
|
|
+thermal_zone_device_register(const char *type, int trips, int mask,
|
|
|
+ void *devdata, struct thermal_zone_device_ops *ops,
|
|
|
+ struct thermal_zone_params *tzp, int passive_delay,
|
|
|
+ int polling_delay)
|
|
|
{
|
|
|
struct thermal_zone_device *tz;
|
|
|
enum thermal_trip_type trip_type;
|
|
|
int trip_temp;
|
|
|
int result;
|
|
|
int count;
|
|
|
- int passive = 0;
|
|
|
struct thermal_governor *governor;
|
|
|
|
|
|
+ if (!type || strlen(type) == 0)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
@@ -1885,7 +1162,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
- tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
|
|
|
+ tz = kzalloc(sizeof(*tz), GFP_KERNEL);
|
|
|
if (!tz)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
@@ -1898,7 +1175,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
return ERR_PTR(result);
|
|
|
}
|
|
|
|
|
|
- strlcpy(tz->type, type ? : "", sizeof(tz->type));
|
|
|
+ strlcpy(tz->type, type, sizeof(tz->type));
|
|
|
tz->ops = ops;
|
|
|
tz->tzp = tzp;
|
|
|
tz->device.class = &thermal_class;
|
|
@@ -1906,6 +1183,13 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
tz->trips = trips;
|
|
|
tz->passive_delay = passive_delay;
|
|
|
tz->polling_delay = polling_delay;
|
|
|
+
|
|
|
+ /* sys I/F */
|
|
|
+ /* Add nodes that are always present via .groups */
|
|
|
+ result = thermal_zone_create_device_groups(tz, mask);
|
|
|
+ if (result)
|
|
|
+ goto unregister;
|
|
|
+
|
|
|
/* A new thermal zone needs to be updated anyway. */
|
|
|
atomic_set(&tz->need_update, 1);
|
|
|
|
|
@@ -1917,32 +1201,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
return ERR_PTR(result);
|
|
|
}
|
|
|
|
|
|
- /* sys I/F */
|
|
|
- if (type) {
|
|
|
- result = device_create_file(&tz->device, &dev_attr_type);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
- }
|
|
|
-
|
|
|
- result = device_create_file(&tz->device, &dev_attr_temp);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
-
|
|
|
- if (ops->get_mode) {
|
|
|
- result = device_create_file(&tz->device, &dev_attr_mode);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
- }
|
|
|
-
|
|
|
- result = create_trip_attrs(tz, mask);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
-
|
|
|
for (count = 0; count < trips; count++) {
|
|
|
if (tz->ops->get_trip_type(tz, count, &trip_type))
|
|
|
set_bit(count, &tz->trips_disabled);
|
|
|
- if (trip_type == THERMAL_TRIP_PASSIVE)
|
|
|
- passive = 1;
|
|
|
if (tz->ops->get_trip_temp(tz, count, &trip_temp))
|
|
|
set_bit(count, &tz->trips_disabled);
|
|
|
/* Check for bogus trip points */
|
|
@@ -1950,33 +1211,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
set_bit(count, &tz->trips_disabled);
|
|
|
}
|
|
|
|
|
|
- if (!passive) {
|
|
|
- result = device_create_file(&tz->device, &dev_attr_passive);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
- }
|
|
|
-
|
|
|
- if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) {
|
|
|
- result = device_create_file(&tz->device, &dev_attr_emul_temp);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
- }
|
|
|
-
|
|
|
- /* Create policy attribute */
|
|
|
- result = device_create_file(&tz->device, &dev_attr_policy);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
-
|
|
|
- /* Add thermal zone params */
|
|
|
- result = create_tzp_attrs(&tz->device);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
-
|
|
|
- /* Create available_policies attribute */
|
|
|
- result = device_create_file(&tz->device, &dev_attr_available_policies);
|
|
|
- if (result)
|
|
|
- goto unregister;
|
|
|
-
|
|
|
/* Update 'this' zone's governor information */
|
|
|
mutex_lock(&thermal_governor_lock);
|
|
|
|
|
@@ -2006,7 +1240,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|
|
/* Bind cooling devices for this zone */
|
|
|
bind_tz(tz);
|
|
|
|
|
|
- INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
|
|
|
+ INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
|
|
|
|
|
|
thermal_zone_device_reset(tz);
|
|
|
/* Update the new thermal zone and mark it as already updated. */
|
|
@@ -2040,8 +1274,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
|
|
|
|
|
mutex_lock(&thermal_list_lock);
|
|
|
list_for_each_entry(pos, &thermal_tz_list, node)
|
|
|
- if (pos == tz)
|
|
|
- break;
|
|
|
+ if (pos == tz)
|
|
|
+ break;
|
|
|
if (pos != tz) {
|
|
|
/* thermal zone device not found */
|
|
|
mutex_unlock(&thermal_list_lock);
|
|
@@ -2071,14 +1305,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
|
|
|
|
|
thermal_zone_device_set_polling(tz, 0);
|
|
|
|
|
|
- if (tz->type[0])
|
|
|
- device_remove_file(&tz->device, &dev_attr_type);
|
|
|
- device_remove_file(&tz->device, &dev_attr_temp);
|
|
|
- if (tz->ops->get_mode)
|
|
|
- device_remove_file(&tz->device, &dev_attr_mode);
|
|
|
- device_remove_file(&tz->device, &dev_attr_policy);
|
|
|
- device_remove_file(&tz->device, &dev_attr_available_policies);
|
|
|
- remove_trip_attrs(tz);
|
|
|
+ kfree(tz->trip_type_attrs);
|
|
|
+ kfree(tz->trip_temp_attrs);
|
|
|
+ kfree(tz->trip_hyst_attrs);
|
|
|
+ kfree(tz->trips_attribute_group.attrs);
|
|
|
thermal_set_governor(tz, NULL);
|
|
|
|
|
|
thermal_remove_hwmon_sysfs(tz);
|
|
@@ -2086,7 +1316,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
|
|
idr_destroy(&tz->idr);
|
|
|
mutex_destroy(&tz->lock);
|
|
|
device_unregister(&tz->device);
|
|
|
- return;
|
|
|
+ kfree(tz->device.groups);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
|
|
|
|
|
@@ -2128,36 +1358,6 @@ exit:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
|
|
|
|
|
|
-/**
|
|
|
- * thermal_zone_get_slope - return the slope attribute of the thermal zone
|
|
|
- * @tz: thermal zone device with the slope attribute
|
|
|
- *
|
|
|
- * Return: If the thermal zone device has a slope attribute, return it, else
|
|
|
- * return 1.
|
|
|
- */
|
|
|
-int thermal_zone_get_slope(struct thermal_zone_device *tz)
|
|
|
-{
|
|
|
- if (tz && tz->tzp)
|
|
|
- return tz->tzp->slope;
|
|
|
- return 1;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
|
|
|
-
|
|
|
-/**
|
|
|
- * thermal_zone_get_offset - return the offset attribute of the thermal zone
|
|
|
- * @tz: thermal zone device with the offset attribute
|
|
|
- *
|
|
|
- * Return: If the thermal zone device has a offset attribute, return it, else
|
|
|
- * return 0.
|
|
|
- */
|
|
|
-int thermal_zone_get_offset(struct thermal_zone_device *tz)
|
|
|
-{
|
|
|
- if (tz && tz->tzp)
|
|
|
- return tz->tzp->offset;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
|
|
|
-
|
|
|
#ifdef CONFIG_NET
|
|
|
static const struct genl_multicast_group thermal_event_mcgrps[] = {
|
|
|
{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
|
|
@@ -2173,7 +1373,7 @@ static struct genl_family thermal_event_genl_family __ro_after_init = {
|
|
|
};
|
|
|
|
|
|
int thermal_generate_netlink_event(struct thermal_zone_device *tz,
|
|
|
- enum events event)
|
|
|
+ enum events event)
|
|
|
{
|
|
|
struct sk_buff *skb;
|
|
|
struct nlattr *attr;
|
|
@@ -2249,40 +1449,8 @@ static inline int genetlink_init(void) { return 0; }
|
|
|
static inline void genetlink_exit(void) {}
|
|
|
#endif /* !CONFIG_NET */
|
|
|
|
|
|
-static int __init thermal_register_governors(void)
|
|
|
-{
|
|
|
- int result;
|
|
|
-
|
|
|
- result = thermal_gov_step_wise_register();
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- result = thermal_gov_fair_share_register();
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- result = thermal_gov_bang_bang_register();
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- result = thermal_gov_user_space_register();
|
|
|
- if (result)
|
|
|
- return result;
|
|
|
-
|
|
|
- return thermal_gov_power_allocator_register();
|
|
|
-}
|
|
|
-
|
|
|
-static void thermal_unregister_governors(void)
|
|
|
-{
|
|
|
- thermal_gov_step_wise_unregister();
|
|
|
- thermal_gov_fair_share_unregister();
|
|
|
- thermal_gov_bang_bang_unregister();
|
|
|
- thermal_gov_user_space_unregister();
|
|
|
- thermal_gov_power_allocator_unregister();
|
|
|
-}
|
|
|
-
|
|
|
static int thermal_pm_notify(struct notifier_block *nb,
|
|
|
- unsigned long mode, void *_unused)
|
|
|
+ unsigned long mode, void *_unused)
|
|
|
{
|
|
|
struct thermal_zone_device *tz;
|
|
|
|