2018-05-07 17:52:29 +00:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
2012-09-18 05:34:53 +00:00
|
|
|
/*
|
|
|
|
* thermal_core.h
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Intel Corp
|
|
|
|
* Author: Durgadoss R <durgadoss.r@intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __THERMAL_CORE_H__
|
|
|
|
#define __THERMAL_CORE_H__
|
|
|
|
|
2024-10-10 22:05:25 +00:00
|
|
|
#include <linux/cleanup.h>
|
2012-09-18 05:34:53 +00:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/thermal.h>
|
|
|
|
|
2020-07-07 09:01:57 +00:00
|
|
|
#include "thermal_netlink.h"
|
thermal: core: Add user thresholds support
The user thresholds mechanism is a way to have the userspace to tell
the thermal framework to send a notification when a temperature limit
is crossed. There is no id, no hysteresis, just the temperature and
the direction of the limit crossing. That means we can be notified
when a threshold is crossed the way up only, or the way down only or
both ways. That allows to create hysteresis values if it is needed.
A threshold can be added, deleted or flushed. The latter means all
thresholds belonging to a thermal zone will be deleted.
When a threshold is added:
- if the same threshold (temperature and direction) exists, an error
is returned
- if a threshold is specified with the same temperature but a
different direction, the specified direction is added
- if there is no threshold with the same temperature then it is
created
When a threshold is deleted:
- if the same threshold (temperature and direction) exists, it is
deleted
- if a threshold is specified with the same temperature but a
different direction, the specified direction is removed
- if there is no threshold with the same temperature, then an error
is returned
When the threshold are flushed:
- All thresholds related to a thermal zone are deleted
When a threshold is crossed:
- the userspace does not need to know which threshold(s) have been
crossed, it will be notified with the current temperature and the
previous temperature
- if multiple thresholds have been crossed between two updates only
one notification will be send to the userspace, it is pointless to
send a notification per thresholds crossed as the userspace can
handle that easily when it has the temperature delta information
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://patch.msgid.link/20240923100005.2532430-2-daniel.lezcano@linaro.org
[ rjw: Subject edit, use BIT(0) and BIT(1) in symbol definitions ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-09-23 09:59:57 +00:00
|
|
|
#include "thermal_thresholds.h"
|
2024-01-09 09:41:11 +00:00
|
|
|
#include "thermal_debugfs.h"
|
2020-07-07 09:01:57 +00:00
|
|
|
|
2024-07-29 16:25:03 +00:00
|
|
|
struct thermal_attr {
|
|
|
|
struct device_attribute attr;
|
|
|
|
char name[THERMAL_NAME_LENGTH];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct thermal_trip_attrs {
|
|
|
|
struct thermal_attr type;
|
|
|
|
struct thermal_attr temp;
|
|
|
|
struct thermal_attr hyst;
|
|
|
|
};
|
|
|
|
|
2024-04-02 18:57:57 +00:00
|
|
|
struct thermal_trip_desc {
|
|
|
|
struct thermal_trip trip;
|
2024-07-29 16:25:03 +00:00
|
|
|
struct thermal_trip_attrs trip_attrs;
|
2024-10-16 11:23:27 +00:00
|
|
|
struct list_head list_node;
|
2024-10-04 19:39:19 +00:00
|
|
|
struct list_head thermal_instances;
|
2024-04-02 18:57:57 +00:00
|
|
|
int threshold;
|
|
|
|
};
|
|
|
|
|
2024-04-04 19:27:07 +00:00
|
|
|
/**
|
|
|
|
* struct thermal_governor - structure that holds thermal governor information
|
|
|
|
* @name: name of the governor
|
|
|
|
* @bind_to_tz: callback called when binding to a thermal zone. If it
|
|
|
|
* returns 0, the governor is bound to the thermal zone,
|
|
|
|
* otherwise it fails.
|
|
|
|
* @unbind_from_tz: callback called when a governor is unbound from a
|
|
|
|
* thermal zone.
|
2024-04-10 16:10:53 +00:00
|
|
|
* @trip_crossed: called for trip points that have just been crossed
|
2024-04-10 16:08:12 +00:00
|
|
|
* @manage: called on thermal zone temperature updates
|
2024-04-04 19:27:07 +00:00
|
|
|
* @update_tz: callback called when thermal zone internals have changed, e.g.
|
|
|
|
* thermal cooling instance was added/removed
|
|
|
|
* @governor_list: node in thermal_governor_list (in thermal_core.c)
|
|
|
|
*/
|
|
|
|
struct thermal_governor {
|
|
|
|
const char *name;
|
|
|
|
int (*bind_to_tz)(struct thermal_zone_device *tz);
|
|
|
|
void (*unbind_from_tz)(struct thermal_zone_device *tz);
|
2024-04-10 16:10:53 +00:00
|
|
|
void (*trip_crossed)(struct thermal_zone_device *tz,
|
|
|
|
const struct thermal_trip *trip,
|
|
|
|
bool crossed_up);
|
2024-04-10 16:08:12 +00:00
|
|
|
void (*manage)(struct thermal_zone_device *tz);
|
2024-04-04 19:27:07 +00:00
|
|
|
void (*update_tz)(struct thermal_zone_device *tz,
|
|
|
|
enum thermal_notify_event reason);
|
|
|
|
struct list_head governor_list;
|
|
|
|
};
|
|
|
|
|
2024-10-04 19:11:53 +00:00
|
|
|
#define TZ_STATE_FLAG_SUSPENDED BIT(0)
|
|
|
|
#define TZ_STATE_FLAG_RESUMING BIT(1)
|
thermal: core: Mark thermal zones as initializing to start with
After thermal_zone_device_register_with_trips() has called
device_register() and it has registered the new thermal zone device
with the driver core, user space may access its sysfs attributes and,
among other things, it may enable the thermal zone before it is ready.
To address this, introduce a new thermal zone state flag for
initialization and set it before calling device_register() in
thermal_zone_device_register_with_trips(). This causes
__thermal_zone_device_update() to return early until the new flag
is cleared.
To clear it when the thermal zone is ready, introduce a new
function called thermal_zone_init_complete() that will also invoke
__thermal_zone_device_update() after clearing that flag (both under the
thernal zone lock) and make thermal_zone_device_register_with_trips()
call the new function instead of checking need_update and calling
thermal_zone_device_update() when it is set.
After this change, if user space enables the thermal zone prematurely,
__thermal_zone_device_update() will return early for it until
thermal_zone_init_complete() is called. In turn, if the thermal zone
is not enabled by user space before thermal_zone_init_complete() is
called, the __thermal_zone_device_update() call in it will return early
because the thermal zone has not been enabled yet, but that function
will be invoked again by thermal_zone_device_set_mode() when the thermal
zone is enabled and it will not return early this time.
The checking of need_update is not necessary any more because the
__thermal_zone_device_update() calls potentially triggered by cooling
device binding take place before calling thermal_zone_init_complete(),
so they all will return early, which means that
thermal_zone_init_complete() must call __thermal_zone_device_update()
in case the thermal zone is enabled prematurely by user space.
Fixes: 203d3d4aa482 ("the generic thermal sysfs driver")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/9360231.CDJkKcVGEf@rjwysocki.net
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
2024-10-04 19:15:04 +00:00
|
|
|
#define TZ_STATE_FLAG_INIT BIT(2)
|
2024-10-04 19:26:12 +00:00
|
|
|
#define TZ_STATE_FLAG_EXIT BIT(3)
|
2024-10-04 19:11:53 +00:00
|
|
|
|
|
|
|
#define TZ_STATE_READY 0
|
|
|
|
|
2024-04-02 18:57:57 +00:00
|
|
|
/**
|
|
|
|
* struct thermal_zone_device - structure for a thermal zone
|
|
|
|
* @id: unique id number for each thermal zone
|
|
|
|
* @type: the thermal zone device type
|
|
|
|
* @device: &struct device for this thermal zone
|
|
|
|
* @removal: removal completion
|
2024-06-14 15:22:25 +00:00
|
|
|
* @resume: resume completion
|
thermal: core: Use trip lists for trip crossing detection
Modify the thermal core to use three lists of trip points:
trips_high, containing trips with thresholds strictly above the current
thermal zone temperature,
trips_reached, containing trips with thresholds at or below the current
zone temperature,
trips_invalid, containing trips with temperature equal to
THERMAL_ZONE_INVALID,
where the first two lists are always sorted by the current trip
threshold.
For each trip in trips_high, there is no mitigation under way and
the trip threshold is equal to its temperature. In turn, for each
trip in trips_reached, there is mitigation under way and the trip
threshold is equal to its low temperature. The trips in trips_invalid,
of course, need not be taken into consideration.
The idea is to make __thermal_zone_device_update() walk trips_high and
trips_reached instead of walking the entire table of trip points in a
thermal zone. Usually, it will only need to walk a few entries in one
of the lists and check one entry in the other list, depending on the
direction of the zone temperature changes, because crossing many trips
by the zone temperature in one go between two consecutive temperature
checks should be unlikely (if it occurs often, the thermal zone
temperature should probably be checked more often either or there
are too many trips).
This also helps to eliminate one temporary trip list used for trip
crossing notification (only one temporary list is needed for this
purpose instead of two) and the remaining temporary list may be sorted
by the current trip threshold value, like the trips_reached list, so
the additional notify_temp field in struct thermal_trip_desc is not
necessary any more.
Moreover, since the trips_reached and trips_high lists are sorted,
the "low" and "high" values needed by thermal_zone_set_trips() can be
determined in a straightforward way by looking at one end of each list.
Of course, additional work is needed in some places in order to
maintain the ordering of the lists, but it is limited to situations
that should be rare, like updating a trip point temperature or
hysteresis, thermal zone initialization, or system resume.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Link: https://patch.msgid.link/2003443.usQuhbGJ8B@rjwysocki.net
[ rjw: Added a comment to thermal_zone_handle_trips() ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-10-16 11:35:11 +00:00
|
|
|
* @trips_high: trips above the current zone temperature
|
|
|
|
* @trips_reached: trips below or at the current zone temperature
|
|
|
|
* @trips_invalid: trips with invalid temperature
|
2024-04-02 18:57:57 +00:00
|
|
|
* @mode: current mode of this thermal zone
|
|
|
|
* @devdata: private pointer for device private data
|
|
|
|
* @num_trips: number of trip points the thermal zone supports
|
|
|
|
* @passive_delay_jiffies: number of jiffies to wait between polls when
|
|
|
|
* performing passive cooling.
|
|
|
|
* @polling_delay_jiffies: number of jiffies to wait between polls when
|
|
|
|
* checking whether trip points have been crossed (0 for
|
|
|
|
* interrupt driven systems)
|
2024-07-18 19:01:14 +00:00
|
|
|
* @recheck_delay_jiffies: delay after a failed attempt to determine the zone
|
|
|
|
* temperature before trying again
|
2024-04-02 18:57:57 +00:00
|
|
|
* @temperature: current temperature. This is only for core code,
|
|
|
|
* drivers should use thermal_zone_get_temp() to get the
|
|
|
|
* current temperature
|
|
|
|
* @last_temperature: previous temperature read
|
|
|
|
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
|
|
|
|
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
|
|
|
|
* @prev_low_trip: the low current temperature if you've crossed a passive
|
|
|
|
trip point.
|
|
|
|
* @prev_high_trip: the above current temperature if you've crossed a
|
|
|
|
passive trip point.
|
|
|
|
* @ops: operations this &thermal_zone_device supports
|
|
|
|
* @tzp: thermal zone parameters
|
|
|
|
* @governor: pointer to the governor for this thermal zone
|
|
|
|
* @governor_data: private pointer for governor data
|
|
|
|
* @ida: &struct ida to generate unique id for this zone's cooling
|
|
|
|
* devices
|
|
|
|
* @lock: lock to protect thermal_instances list
|
|
|
|
* @node: node in thermal_tz_list (in thermal_core.c)
|
|
|
|
* @poll_queue: delayed work for polling
|
|
|
|
* @notify_event: Last notification event
|
2024-10-04 19:11:53 +00:00
|
|
|
* @state: current state of the thermal zone
|
2024-04-02 18:57:57 +00:00
|
|
|
* @trips: array of struct thermal_trip objects
|
|
|
|
*/
|
|
|
|
struct thermal_zone_device {
|
|
|
|
int id;
|
|
|
|
char type[THERMAL_NAME_LENGTH];
|
|
|
|
struct device device;
|
|
|
|
struct completion removal;
|
2024-06-14 15:22:25 +00:00
|
|
|
struct completion resume;
|
2024-04-02 18:57:57 +00:00
|
|
|
struct attribute_group trips_attribute_group;
|
thermal: core: Use trip lists for trip crossing detection
Modify the thermal core to use three lists of trip points:
trips_high, containing trips with thresholds strictly above the current
thermal zone temperature,
trips_reached, containing trips with thresholds at or below the current
zone temperature,
trips_invalid, containing trips with temperature equal to
THERMAL_ZONE_INVALID,
where the first two lists are always sorted by the current trip
threshold.
For each trip in trips_high, there is no mitigation under way and
the trip threshold is equal to its temperature. In turn, for each
trip in trips_reached, there is mitigation under way and the trip
threshold is equal to its low temperature. The trips in trips_invalid,
of course, need not be taken into consideration.
The idea is to make __thermal_zone_device_update() walk trips_high and
trips_reached instead of walking the entire table of trip points in a
thermal zone. Usually, it will only need to walk a few entries in one
of the lists and check one entry in the other list, depending on the
direction of the zone temperature changes, because crossing many trips
by the zone temperature in one go between two consecutive temperature
checks should be unlikely (if it occurs often, the thermal zone
temperature should probably be checked more often either or there
are too many trips).
This also helps to eliminate one temporary trip list used for trip
crossing notification (only one temporary list is needed for this
purpose instead of two) and the remaining temporary list may be sorted
by the current trip threshold value, like the trips_reached list, so
the additional notify_temp field in struct thermal_trip_desc is not
necessary any more.
Moreover, since the trips_reached and trips_high lists are sorted,
the "low" and "high" values needed by thermal_zone_set_trips() can be
determined in a straightforward way by looking at one end of each list.
Of course, additional work is needed in some places in order to
maintain the ordering of the lists, but it is limited to situations
that should be rare, like updating a trip point temperature or
hysteresis, thermal zone initialization, or system resume.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Link: https://patch.msgid.link/2003443.usQuhbGJ8B@rjwysocki.net
[ rjw: Added a comment to thermal_zone_handle_trips() ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-10-16 11:35:11 +00:00
|
|
|
struct list_head trips_high;
|
|
|
|
struct list_head trips_reached;
|
|
|
|
struct list_head trips_invalid;
|
2024-04-02 18:57:57 +00:00
|
|
|
enum thermal_device_mode mode;
|
|
|
|
void *devdata;
|
|
|
|
int num_trips;
|
|
|
|
unsigned long passive_delay_jiffies;
|
|
|
|
unsigned long polling_delay_jiffies;
|
2024-07-18 19:01:14 +00:00
|
|
|
unsigned long recheck_delay_jiffies;
|
2024-04-02 18:57:57 +00:00
|
|
|
int temperature;
|
|
|
|
int last_temperature;
|
|
|
|
int emul_temperature;
|
|
|
|
int passive;
|
|
|
|
int prev_low_trip;
|
|
|
|
int prev_high_trip;
|
|
|
|
struct thermal_zone_device_ops ops;
|
|
|
|
struct thermal_zone_params *tzp;
|
|
|
|
struct thermal_governor *governor;
|
|
|
|
void *governor_data;
|
|
|
|
struct ida ida;
|
|
|
|
struct mutex lock;
|
|
|
|
struct list_head node;
|
|
|
|
struct delayed_work poll_queue;
|
|
|
|
enum thermal_notify_event notify_event;
|
2024-10-04 19:11:53 +00:00
|
|
|
u8 state;
|
2024-04-02 18:57:57 +00:00
|
|
|
#ifdef CONFIG_THERMAL_DEBUGFS
|
|
|
|
struct thermal_debugfs *debugfs;
|
|
|
|
#endif
|
thermal: core: Add user thresholds support
The user thresholds mechanism is a way to have the userspace to tell
the thermal framework to send a notification when a temperature limit
is crossed. There is no id, no hysteresis, just the temperature and
the direction of the limit crossing. That means we can be notified
when a threshold is crossed the way up only, or the way down only or
both ways. That allows to create hysteresis values if it is needed.
A threshold can be added, deleted or flushed. The latter means all
thresholds belonging to a thermal zone will be deleted.
When a threshold is added:
- if the same threshold (temperature and direction) exists, an error
is returned
- if a threshold is specified with the same temperature but a
different direction, the specified direction is added
- if there is no threshold with the same temperature then it is
created
When a threshold is deleted:
- if the same threshold (temperature and direction) exists, it is
deleted
- if a threshold is specified with the same temperature but a
different direction, the specified direction is removed
- if there is no threshold with the same temperature, then an error
is returned
When the threshold are flushed:
- All thresholds related to a thermal zone are deleted
When a threshold is crossed:
- the userspace does not need to know which threshold(s) have been
crossed, it will be notified with the current temperature and the
previous temperature
- if multiple thresholds have been crossed between two updates only
one notification will be send to the userspace, it is pointless to
send a notification per thresholds crossed as the userspace can
handle that easily when it has the temperature delta information
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://patch.msgid.link/20240923100005.2532430-2-daniel.lezcano@linaro.org
[ rjw: Subject edit, use BIT(0) and BIT(1) in symbol definitions ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-09-23 09:59:57 +00:00
|
|
|
struct list_head user_thresholds;
|
2024-04-02 18:57:57 +00:00
|
|
|
struct thermal_trip_desc trips[] __counted_by(num_trips);
|
|
|
|
};
|
|
|
|
|
2024-10-10 22:05:25 +00:00
|
|
|
DEFINE_GUARD(thermal_zone, struct thermal_zone_device *, mutex_lock(&_T->lock),
|
|
|
|
mutex_unlock(&_T->lock))
|
|
|
|
|
2024-10-10 22:07:11 +00:00
|
|
|
DEFINE_GUARD(thermal_zone_reverse, struct thermal_zone_device *,
|
|
|
|
mutex_unlock(&_T->lock), mutex_lock(&_T->lock))
|
|
|
|
|
thermal: core: Allow thermal zones to tell the core to ignore them
The iwlwifi wireless driver registers a thermal zone that is only needed
when the network interface handled by it is up and it wants that thermal
zone to be effectively ignored by the core otherwise.
Before commit a8a261774466 ("thermal: core: Call monitor_thermal_zone()
if zone temperature is invalid") that could be achieved by returning
an error code from the thermal zone's .get_temp() callback because the
core did not really handle errors returned by it almost at all.
However, commit a8a261774466 made the core attempt to recover from the
situation in which the temperature of a thermal zone cannot be
determined due to errors returned by its .get_temp() and is always
invalid from the core's perspective.
That was done because there are thermal zones in which .get_temp()
returns errors to start with due to some difficulties related to the
initialization ordering, but then it will start to produce valid
temperature values at one point.
Unfortunately, the simple approach taken by commit a8a261774466,
which is to poll the thermal zone periodically until its .get_temp()
callback starts to return valid temperature values, is at odds with
the special thermal zone in iwlwifi in which .get_temp() may always
return an error because its network interface may always be down. If
that happens, every attempt to invoke the thermal zone's .get_temp()
callback resulting in an error causes the thermal core to print a
dev_warn() message to the kernel log which is super-noisy.
To address this problem, make the core handle the case in which
.get_temp() returns 0, but the temperature value returned by it
is not actually valid, in a special way. Namely, make the core
completely ignore the invalid temperature value coming from
.get_temp() in that case, which requires folding in
update_temperature() into its caller and a few related changes.
On the iwlwifi side, modify iwl_mvm_tzone_get_temp() to return 0
and put THERMAL_TEMP_INVALID into the temperature return memory
location instead of returning an error when the firmware is not
running or it is not of the right type.
Also, to clearly separate the handling of invalid temperature
values from the thermal zone initialization, introduce a special
THERMAL_TEMP_INIT value specifically for the latter purpose.
Fixes: a8a261774466 ("thermal: core: Call monitor_thermal_zone() if zone temperature is invalid")
Closes: https://lore.kernel.org/linux-pm/20240715044527.GA1544@sol.localdomain/
Reported-by: Eric Biggers <ebiggers@kernel.org>
Reported-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=201761
Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name>
Tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>
Cc: 6.10+ <stable@vger.kernel.org> # 6.10+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/4950004.31r3eYUQgx@rjwysocki.net
[ rjw: Rebased on top of the current mainline ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-07-17 19:45:02 +00:00
|
|
|
/* Initial thermal zone temperature. */
|
|
|
|
#define THERMAL_TEMP_INIT INT_MIN
|
|
|
|
|
2024-06-28 12:10:03 +00:00
|
|
|
/*
|
2024-07-18 19:01:14 +00:00
|
|
|
* Default and maximum delay after a failed thermal zone temperature check
|
|
|
|
* before attempting to check it again (in jiffies).
|
2024-06-28 12:10:03 +00:00
|
|
|
*/
|
2024-07-18 19:01:14 +00:00
|
|
|
#define THERMAL_RECHECK_DELAY msecs_to_jiffies(250)
|
|
|
|
#define THERMAL_MAX_RECHECK_DELAY (120 * HZ)
|
2024-06-28 12:10:03 +00:00
|
|
|
|
2020-04-02 14:27:39 +00:00
|
|
|
/* Default Thermal Governor */
|
|
|
|
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
|
|
|
|
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
|
|
|
|
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)
|
|
|
|
#define DEFAULT_THERMAL_GOVERNOR "fair_share"
|
|
|
|
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
|
|
|
|
#define DEFAULT_THERMAL_GOVERNOR "user_space"
|
|
|
|
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
|
|
|
|
#define DEFAULT_THERMAL_GOVERNOR "power_allocator"
|
2023-06-09 12:44:08 +00:00
|
|
|
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_BANG_BANG)
|
|
|
|
#define DEFAULT_THERMAL_GOVERNOR "bang_bang"
|
2020-04-02 14:27:39 +00:00
|
|
|
#endif
|
|
|
|
|
2012-09-18 05:34:53 +00:00
|
|
|
/* Initial state of a cooling device during binding */
|
|
|
|
#define THERMAL_NO_TARGET -1UL
|
|
|
|
|
2019-06-12 20:13:24 +00:00
|
|
|
/* Init section thermal table */
|
|
|
|
extern struct thermal_governor *__governor_thermal_table[];
|
|
|
|
extern struct thermal_governor *__governor_thermal_table_end[];
|
|
|
|
|
|
|
|
#define THERMAL_TABLE_ENTRY(table, name) \
|
|
|
|
static typeof(name) *__thermal_table_entry_##name \
|
2020-10-22 02:36:07 +00:00
|
|
|
__used __section("__" #table "_thermal_table") = &name
|
2019-06-12 20:13:24 +00:00
|
|
|
|
|
|
|
#define THERMAL_GOVERNOR_DECLARE(name) THERMAL_TABLE_ENTRY(governor, name)
|
|
|
|
|
|
|
|
#define for_each_governor_table(__governor) \
|
|
|
|
for (__governor = __governor_thermal_table; \
|
|
|
|
__governor < __governor_thermal_table_end; \
|
|
|
|
__governor++)
|
|
|
|
|
2020-07-06 10:55:35 +00:00
|
|
|
int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *),
|
|
|
|
void *);
|
|
|
|
|
|
|
|
int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
|
|
|
|
void *), void *);
|
|
|
|
|
|
|
|
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
|
|
|
void *thermal_governor);
|
|
|
|
|
2020-07-06 10:55:36 +00:00
|
|
|
struct thermal_zone_device *thermal_zone_get_by_id(int id);
|
|
|
|
|
2024-10-03 12:25:58 +00:00
|
|
|
DEFINE_CLASS(thermal_zone_get_by_id, struct thermal_zone_device *,
|
|
|
|
if (_T) put_device(&_T->device), thermal_zone_get_by_id(id), int id)
|
|
|
|
|
2020-04-02 14:27:41 +00:00
|
|
|
static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
|
|
|
|
{
|
|
|
|
return cdev->ops->get_requested_power && cdev->ops->state2power &&
|
|
|
|
cdev->ops->power2state;
|
|
|
|
}
|
|
|
|
|
2021-01-18 17:38:24 +00:00
|
|
|
void thermal_cdev_update(struct thermal_cooling_device *);
|
2024-10-10 22:16:54 +00:00
|
|
|
void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev);
|
2021-04-22 11:43:06 +00:00
|
|
|
void __thermal_cdev_update(struct thermal_cooling_device *cdev);
|
2021-01-18 17:38:24 +00:00
|
|
|
|
2023-10-12 18:34:50 +00:00
|
|
|
int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip);
|
2020-04-02 14:27:43 +00:00
|
|
|
|
2012-09-18 05:34:53 +00:00
|
|
|
/*
|
|
|
|
* This structure is used to describe the behavior of
|
|
|
|
* a certain cooling device on a certain trip point
|
|
|
|
* in a certain thermal zone
|
|
|
|
*/
|
|
|
|
struct thermal_instance {
|
|
|
|
int id;
|
|
|
|
char name[THERMAL_NAME_LENGTH];
|
|
|
|
struct thermal_cooling_device *cdev;
|
2023-09-21 17:52:44 +00:00
|
|
|
const struct thermal_trip *trip;
|
2015-10-30 08:31:47 +00:00
|
|
|
bool initialized;
|
2012-09-18 05:34:53 +00:00
|
|
|
unsigned long upper; /* Highest cooling state for this trip point */
|
|
|
|
unsigned long lower; /* Lowest cooling state for this trip point */
|
|
|
|
unsigned long target; /* expected cooling state */
|
|
|
|
char attr_name[THERMAL_NAME_LENGTH];
|
|
|
|
struct device_attribute attr;
|
2015-02-18 16:04:24 +00:00
|
|
|
char weight_attr_name[THERMAL_NAME_LENGTH];
|
|
|
|
struct device_attribute weight_attr;
|
2024-10-04 19:39:19 +00:00
|
|
|
struct list_head trip_node; /* node in trip->thermal_instances */
|
2012-09-18 05:34:53 +00:00
|
|
|
struct list_head cdev_node; /* node in cdev->thermal_instances */
|
2015-02-18 16:04:21 +00:00
|
|
|
unsigned int weight; /* The weight of the cooling device */
|
2023-03-17 17:01:26 +00:00
|
|
|
bool upper_no_limit;
|
2012-09-18 05:34:53 +00:00
|
|
|
};
|
|
|
|
|
2016-11-08 05:08:57 +00:00
|
|
|
#define to_thermal_zone(_dev) \
|
|
|
|
container_of(_dev, struct thermal_zone_device, device)
|
|
|
|
|
2016-11-08 05:09:01 +00:00
|
|
|
#define to_cooling_device(_dev) \
|
|
|
|
container_of(_dev, struct thermal_cooling_device, device)
|
|
|
|
|
2013-03-26 08:38:29 +00:00
|
|
|
int thermal_register_governor(struct thermal_governor *);
|
|
|
|
void thermal_unregister_governor(struct thermal_governor *);
|
2016-11-08 05:08:55 +00:00
|
|
|
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
|
2016-11-08 05:08:56 +00:00
|
|
|
int thermal_build_list_of_policies(char *buf);
|
2022-11-10 15:24:58 +00:00
|
|
|
void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
|
|
|
enum thermal_notify_event event);
|
2023-11-29 12:43:29 +00:00
|
|
|
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz);
|
2023-12-20 23:17:45 +00:00
|
|
|
void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
|
|
|
enum thermal_notify_event reason);
|
2013-03-26 08:38:29 +00:00
|
|
|
|
2020-03-31 16:54:48 +00:00
|
|
|
/* Helpers */
|
2024-04-02 18:56:43 +00:00
|
|
|
#define for_each_trip_desc(__tz, __td) \
|
|
|
|
for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++)
|
|
|
|
|
|
|
|
#define trip_to_trip_desc(__trip) \
|
|
|
|
container_of(__trip, struct thermal_trip_desc, trip)
|
2023-10-12 18:26:46 +00:00
|
|
|
|
2024-05-28 15:00:51 +00:00
|
|
|
const char *thermal_trip_type_name(enum thermal_trip_type trip_type);
|
|
|
|
|
2024-08-16 08:12:32 +00:00
|
|
|
void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high);
|
2023-12-15 19:53:52 +00:00
|
|
|
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
|
2023-09-21 17:52:44 +00:00
|
|
|
const struct thermal_trip *trip);
|
2022-08-05 15:38:34 +00:00
|
|
|
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
|
2024-07-29 16:27:25 +00:00
|
|
|
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
|
|
|
|
struct thermal_trip *trip, int hyst);
|
2020-03-31 16:54:48 +00:00
|
|
|
|
2016-11-08 05:09:00 +00:00
|
|
|
/* sysfs I/F */
|
2024-02-22 17:30:49 +00:00
|
|
|
int thermal_zone_create_device_groups(struct thermal_zone_device *tz);
|
2017-08-08 14:39:52 +00:00
|
|
|
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
|
2016-11-08 05:09:02 +00:00
|
|
|
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
|
2018-04-02 10:56:25 +00:00
|
|
|
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
|
2023-03-17 17:01:26 +00:00
|
|
|
void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev);
|
2016-11-08 05:09:02 +00:00
|
|
|
/* used only at binding time */
|
2018-04-03 09:49:03 +00:00
|
|
|
ssize_t trip_point_show(struct device *, struct device_attribute *, char *);
|
|
|
|
ssize_t weight_show(struct device *, struct device_attribute *, char *);
|
|
|
|
ssize_t weight_store(struct device *, struct device_attribute *, const char *,
|
|
|
|
size_t);
|
2016-11-08 05:09:00 +00:00
|
|
|
|
2018-04-02 10:56:25 +00:00
|
|
|
#ifdef CONFIG_THERMAL_STATISTICS
|
|
|
|
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
|
|
|
unsigned long new_state);
|
|
|
|
#else
|
|
|
|
static inline void
|
|
|
|
thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
|
|
|
unsigned long new_state) {}
|
|
|
|
#endif /* CONFIG_THERMAL_STATISTICS */
|
|
|
|
|
2012-09-18 05:34:53 +00:00
|
|
|
#endif /* __THERMAL_CORE_H__ */
|