Merge branches 'misc', 'soc', 'soc-eduardo' and 'int3404-thermal' of .git into next
This commit is contained in:
commit
201531c277
@ -15,6 +15,10 @@ Optional properties:
|
||||
- clock-latency: Specify the possible maximum transition latency for clock,
|
||||
in unit of nanoseconds.
|
||||
- voltage-tolerance: Specify the CPU voltage tolerance in percentage.
|
||||
- #cooling-cells:
|
||||
- cooling-min-level:
|
||||
- cooling-max-level:
|
||||
Please refer to Documentation/devicetree/bindings/thermal/thermal.txt.
|
||||
|
||||
Examples:
|
||||
|
||||
@ -33,6 +37,9 @@ cpus {
|
||||
198000 850000
|
||||
>;
|
||||
clock-latency = <61036>; /* two CLK32 periods */
|
||||
#cooling-cells = <2>;
|
||||
cooling-min-level = <0>;
|
||||
cooling-max-level = <2>;
|
||||
};
|
||||
|
||||
cpu@1 {
|
||||
|
595
Documentation/devicetree/bindings/thermal/thermal.txt
Normal file
595
Documentation/devicetree/bindings/thermal/thermal.txt
Normal file
@ -0,0 +1,595 @@
|
||||
* Thermal Framework Device Tree descriptor
|
||||
|
||||
This file describes a generic binding to provide a way of
|
||||
defining hardware thermal structure using device tree.
|
||||
A thermal structure includes thermal zones and their components,
|
||||
such as trip points, polling intervals, sensors and cooling devices
|
||||
binding descriptors.
|
||||
|
||||
The target of device tree thermal descriptors is to describe only
|
||||
the hardware thermal aspects. The thermal device tree bindings are
|
||||
not about how the system must control or which algorithm or policy
|
||||
must be taken in place.
|
||||
|
||||
There are five types of nodes involved to describe thermal bindings:
|
||||
- thermal sensors: devices which may be used to take temperature
|
||||
measurements.
|
||||
- cooling devices: devices which may be used to dissipate heat.
|
||||
- trip points: describe key temperatures at which cooling is recommended. The
|
||||
set of points should be chosen based on hardware limits.
|
||||
- cooling maps: used to describe links between trip points and cooling devices;
|
||||
- thermal zones: used to describe thermal data within the hardware;
|
||||
|
||||
The following is a description of each of these node types.
|
||||
|
||||
* Thermal sensor devices
|
||||
|
||||
Thermal sensor devices are nodes providing temperature sensing capabilities on
|
||||
thermal zones. Typical devices are I2C ADC converters and bandgaps. These are
|
||||
nodes providing temperature data to thermal zones. Thermal sensor devices may
|
||||
control one or more internal sensors.
|
||||
|
||||
Required property:
|
||||
- #thermal-sensor-cells: Used to provide sensor device specific information
|
||||
Type: unsigned while referring to it. Typically 0 on thermal sensor
|
||||
Size: one cell nodes with only one sensor, and at least 1 on nodes
|
||||
with several internal sensors, in order
|
||||
to identify uniquely the sensor instances within
|
||||
the IC. See thermal zone binding for more details
|
||||
on how consumers refer to sensor devices.
|
||||
|
||||
* Cooling device nodes
|
||||
|
||||
Cooling devices are nodes providing control on power dissipation. There
|
||||
are essentially two ways to provide control on power dissipation. First
|
||||
is by means of regulating device performance, which is known as passive
|
||||
cooling. A typical passive cooling is a CPU that has dynamic voltage and
|
||||
frequency scaling (DVFS), and uses lower frequencies as cooling states.
|
||||
Second is by means of activating devices in order to remove
|
||||
the dissipated heat, which is known as active cooling, e.g. regulating
|
||||
fan speeds. In both cases, cooling devices shall have a way to determine
|
||||
the state of cooling in which the device is.
|
||||
|
||||
Any cooling device has a range of cooling states (i.e. different levels
|
||||
of heat dissipation). For example a fan's cooling states correspond to
|
||||
the different fan speeds possible. Cooling states are referred to by
|
||||
single unsigned integers, where larger numbers mean greater heat
|
||||
dissipation. The precise set of cooling states associated with a device
|
||||
(as referred to be the cooling-min-state and cooling-max-state
|
||||
properties) should be defined in a particular device's binding.
|
||||
For more examples of cooling devices, refer to the example sections below.
|
||||
|
||||
Required properties:
|
||||
- cooling-min-state: An integer indicating the smallest
|
||||
Type: unsigned cooling state accepted. Typically 0.
|
||||
Size: one cell
|
||||
|
||||
- cooling-max-state: An integer indicating the largest
|
||||
Type: unsigned cooling state accepted.
|
||||
Size: one cell
|
||||
|
||||
- #cooling-cells: Used to provide cooling device specific information
|
||||
Type: unsigned while referring to it. Must be at least 2, in order
|
||||
Size: one cell to specify minimum and maximum cooling state used
|
||||
in the reference. The first cell is the minimum
|
||||
cooling state requested and the second cell is
|
||||
the maximum cooling state requested in the reference.
|
||||
See Cooling device maps section below for more details
|
||||
on how consumers refer to cooling devices.
|
||||
|
||||
* Trip points
|
||||
|
||||
The trip node is a node to describe a point in the temperature domain
|
||||
in which the system takes an action. This node describes just the point,
|
||||
not the action.
|
||||
|
||||
Required properties:
|
||||
- temperature: An integer indicating the trip temperature level,
|
||||
Type: signed in millicelsius.
|
||||
Size: one cell
|
||||
|
||||
- hysteresis: A low hysteresis value on temperature property (above).
|
||||
Type: unsigned This is a relative value, in millicelsius.
|
||||
Size: one cell
|
||||
|
||||
- type: a string containing the trip type. Expected values are:
|
||||
"active": A trip point to enable active cooling
|
||||
"passive": A trip point to enable passive cooling
|
||||
"hot": A trip point to notify emergency
|
||||
"critical": Hardware not reliable.
|
||||
Type: string
|
||||
|
||||
* Cooling device maps
|
||||
|
||||
The cooling device maps node is a node to describe how cooling devices
|
||||
get assigned to trip points of the zone. The cooling devices are expected
|
||||
to be loaded in the target system.
|
||||
|
||||
Required properties:
|
||||
- cooling-device: A phandle of a cooling device with its specifier,
|
||||
Type: phandle + referring to which cooling device is used in this
|
||||
cooling specifier binding. In the cooling specifier, the first cell
|
||||
is the minimum cooling state and the second cell
|
||||
is the maximum cooling state used in this map.
|
||||
- trip: A phandle of a trip point node within the same thermal
|
||||
Type: phandle of zone.
|
||||
trip point node
|
||||
|
||||
Optional property:
|
||||
- contribution: The cooling contribution to the thermal zone of the
|
||||
Type: unsigned referred cooling device at the referred trip point.
|
||||
Size: one cell The contribution is a ratio of the sum
|
||||
of all cooling contributions within a thermal zone.
|
||||
|
||||
Note: Using the THERMAL_NO_LIMIT (-1UL) constant in the cooling-device phandle
|
||||
limit specifier means:
|
||||
(i) - minimum state allowed for minimum cooling state used in the reference.
|
||||
(ii) - maximum state allowed for maximum cooling state used in the reference.
|
||||
Refer to include/dt-bindings/thermal/thermal.h for definition of this constant.
|
||||
|
||||
* Thermal zone nodes
|
||||
|
||||
The thermal zone node is the node containing all the required info
|
||||
for describing a thermal zone, including its cooling device bindings. The
|
||||
thermal zone node must contain, apart from its own properties, one sub-node
|
||||
containing trip nodes and one sub-node containing all the zone cooling maps.
|
||||
|
||||
Required properties:
|
||||
- polling-delay: The maximum number of milliseconds to wait between polls
|
||||
Type: unsigned when checking this thermal zone.
|
||||
Size: one cell
|
||||
|
||||
- polling-delay-passive: The maximum number of milliseconds to wait
|
||||
Type: unsigned between polls when performing passive cooling.
|
||||
Size: one cell
|
||||
|
||||
- thermal-sensors: A list of thermal sensor phandles and sensor specifier
|
||||
Type: list of used while monitoring the thermal zone.
|
||||
phandles + sensor
|
||||
specifier
|
||||
|
||||
- trips: A sub-node which is a container of only trip point nodes
|
||||
Type: sub-node required to describe the thermal zone.
|
||||
|
||||
- cooling-maps: A sub-node which is a container of only cooling device
|
||||
Type: sub-node map nodes, used to describe the relation between trips
|
||||
and cooling devices.
|
||||
|
||||
Optional property:
|
||||
- coefficients: An array of integers (one signed cell) containing
|
||||
Type: array coefficients to compose a linear relation between
|
||||
Elem size: one cell the sensors listed in the thermal-sensors property.
|
||||
Elem type: signed Coefficients defaults to 1, in case this property
|
||||
is not specified. A simple linear polynomial is used:
|
||||
Z = c0 * x0 + c1 + x1 + ... + c(n-1) * x(n-1) + cn.
|
||||
|
||||
The coefficients are ordered and they match with sensors
|
||||
by means of sensor ID. Additional coefficients are
|
||||
interpreted as constant offset.
|
||||
|
||||
Note: The delay properties are bound to the maximum dT/dt (temperature
|
||||
derivative over time) in two situations for a thermal zone:
|
||||
(i) - when passive cooling is activated (polling-delay-passive); and
|
||||
(ii) - when the zone just needs to be monitored (polling-delay) or
|
||||
when active cooling is activated.
|
||||
|
||||
The maximum dT/dt is highly bound to hardware power consumption and dissipation
|
||||
capability. The delays should be chosen to account for said max dT/dt,
|
||||
such that a device does not cross several trip boundaries unexpectedly
|
||||
between polls. Choosing the right polling delays shall avoid having the
|
||||
device in temperature ranges that may damage the silicon structures and
|
||||
reduce silicon lifetime.
|
||||
|
||||
* The thermal-zones node
|
||||
|
||||
The "thermal-zones" node is a container for all thermal zone nodes. It shall
|
||||
contain only sub-nodes describing thermal zones as in the section
|
||||
"Thermal zone nodes". The "thermal-zones" node appears under "/".
|
||||
|
||||
* Examples
|
||||
|
||||
Below are several examples on how to use thermal data descriptors
|
||||
using device tree bindings:
|
||||
|
||||
(a) - CPU thermal zone
|
||||
|
||||
The CPU thermal zone example below describes how to setup one thermal zone
|
||||
using one single sensor as temperature source and many cooling devices and
|
||||
power dissipation control sources.
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
cpus {
|
||||
/*
|
||||
* Here is an example of describing a cooling device for a DVFS
|
||||
* capable CPU. The CPU node describes its four OPPs.
|
||||
* The cooling states possible are 0..3, and they are
|
||||
* used as OPP indexes. The minimum cooling state is 0, which means
|
||||
* all four OPPs can be available to the system. The maximum
|
||||
* cooling state is 3, which means only the lowest OPPs (198MHz@0.85V)
|
||||
* can be available in the system.
|
||||
*/
|
||||
cpu0: cpu@0 {
|
||||
...
|
||||
operating-points = <
|
||||
/* kHz uV */
|
||||
970000 1200000
|
||||
792000 1100000
|
||||
396000 950000
|
||||
198000 850000
|
||||
>;
|
||||
cooling-min-state = <0>;
|
||||
cooling-max-state = <3>;
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
...
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
...
|
||||
/*
|
||||
* A simple fan controller which supports 10 speeds of operation
|
||||
* (represented as 0-9).
|
||||
*/
|
||||
fan0: fan@0x48 {
|
||||
...
|
||||
cooling-min-state = <0>;
|
||||
cooling-max-state = <9>;
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
};
|
||||
|
||||
ocp {
|
||||
...
|
||||
/*
|
||||
* A simple IC with a single bandgap temperature sensor.
|
||||
*/
|
||||
bandgap0: bandgap@0x0000ED00 {
|
||||
...
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu-thermal: cpu-thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
thermal-sensors = <&bandgap0>;
|
||||
|
||||
trips {
|
||||
cpu-alert0: cpu-alert {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "active";
|
||||
};
|
||||
cpu-alert1: cpu-alert {
|
||||
temperature = <100000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu-crit: cpu-crit {
|
||||
temperature = <125000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&cpu-alert0>;
|
||||
cooling-device = <&fan0 THERMAL_NO_LIMITS 4>;
|
||||
};
|
||||
map1 {
|
||||
trip = <&cpu-alert1>;
|
||||
cooling-device = <&fan0 5 THERMAL_NO_LIMITS>;
|
||||
};
|
||||
map2 {
|
||||
trip = <&cpu-alert1>;
|
||||
cooling-device =
|
||||
<&cpu0 THERMAL_NO_LIMITS THERMAL_NO_LIMITS>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
In the example above, the ADC sensor (bandgap0) at address 0x0000ED00 is
|
||||
used to monitor the zone 'cpu-thermal' using its sole sensor. A fan
|
||||
device (fan0) is controlled via I2C bus 1, at address 0x48, and has ten
|
||||
different cooling states 0-9. It is used to remove the heat out of
|
||||
the thermal zone 'cpu-thermal' using its cooling states
|
||||
from its minimum to 4, when it reaches trip point 'cpu-alert0'
|
||||
at 90C, as an example of active cooling. The same cooling device is used at
|
||||
'cpu-alert1', but from 5 to its maximum state. The cpu@0 device is also
|
||||
linked to the same thermal zone, 'cpu-thermal', as a passive cooling device,
|
||||
using all its cooling states at trip point 'cpu-alert1',
|
||||
which is a trip point at 100C. On the thermal zone 'cpu-thermal', at the
|
||||
temperature of 125C, represented by the trip point 'cpu-crit', the silicon
|
||||
is not reliable anymore.
|
||||
|
||||
(b) - IC with several internal sensors
|
||||
|
||||
The example below describes how to deploy several thermal zones based off a
|
||||
single sensor IC, assuming it has several internal sensors. This is a common
|
||||
case on SoC designs with several internal IPs that may need different thermal
|
||||
requirements, and thus may have their own sensor to monitor or detect internal
|
||||
hotspots in their silicon.
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
ocp {
|
||||
...
|
||||
/*
|
||||
* A simple IC with several bandgap temperature sensors.
|
||||
*/
|
||||
bandgap0: bandgap@0x0000ED00 {
|
||||
...
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu-thermal: cpu-thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap0 0>;
|
||||
|
||||
trips {
|
||||
/* each zone within the SoC may have its own trips */
|
||||
cpu-alert: cpu-alert {
|
||||
temperature = <100000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu-crit: cpu-crit {
|
||||
temperature = <125000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* each zone within the SoC may have its own cooling */
|
||||
...
|
||||
};
|
||||
};
|
||||
|
||||
gpu-thermal: gpu-thermal {
|
||||
polling-delay-passive = <120>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap0 1>;
|
||||
|
||||
trips {
|
||||
/* each zone within the SoC may have its own trips */
|
||||
gpu-alert: gpu-alert {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
gpu-crit: gpu-crit {
|
||||
temperature = <105000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* each zone within the SoC may have its own cooling */
|
||||
...
|
||||
};
|
||||
};
|
||||
|
||||
dsp-thermal: dsp-thermal {
|
||||
polling-delay-passive = <50>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap0 2>;
|
||||
|
||||
trips {
|
||||
/* each zone within the SoC may have its own trips */
|
||||
dsp-alert: gpu-alert {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
dsp-crit: gpu-crit {
|
||||
temperature = <135000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* each zone within the SoC may have its own cooling */
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
In the example above, there is one bandgap IC which has the capability to
|
||||
monitor three sensors. The hardware has been designed so that sensors are
|
||||
placed on different places in the DIE to monitor different temperature
|
||||
hotspots: one for CPU thermal zone, one for GPU thermal zone and the
|
||||
other to monitor a DSP thermal zone.
|
||||
|
||||
Thus, there is a need to assign each sensor provided by the bandgap IC
|
||||
to different thermal zones. This is achieved by means of using the
|
||||
#thermal-sensor-cells property and using the first cell of the sensor
|
||||
specifier as sensor ID. In the example, then, <bandgap 0> is used to
|
||||
monitor CPU thermal zone, <bandgap 1> is used to monitor GPU thermal
|
||||
zone and <bandgap 2> is used to monitor DSP thermal zone. Each zone
|
||||
may be uncorrelated, having its own dT/dt requirements, trips
|
||||
and cooling maps.
|
||||
|
||||
|
||||
(c) - Several sensors within one single thermal zone
|
||||
|
||||
The example below illustrates how to use more than one sensor within
|
||||
one thermal zone.
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
&i2c1 {
|
||||
...
|
||||
/*
|
||||
* A simple IC with a single temperature sensor.
|
||||
*/
|
||||
adc: sensor@0x49 {
|
||||
...
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
ocp {
|
||||
...
|
||||
/*
|
||||
* A simple IC with a single bandgap temperature sensor.
|
||||
*/
|
||||
bandgap0: bandgap@0x0000ED00 {
|
||||
...
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu-thermal: cpu-thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
thermal-sensors = <&bandgap0>, /* cpu */
|
||||
<&adc>; /* pcb north */
|
||||
|
||||
/* hotspot = 100 * bandgap - 120 * adc + 484 */
|
||||
coefficients = <100 -120 484>;
|
||||
|
||||
trips {
|
||||
...
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
In some cases, there is a need to use more than one sensor to extrapolate
|
||||
a thermal hotspot in the silicon. The above example illustrates this situation.
|
||||
For instance, it may be the case that a sensor external to CPU IP may be placed
|
||||
close to CPU hotspot and together with internal CPU sensor, it is used
|
||||
to determine the hotspot. Assuming this is the case for the above example,
|
||||
the hypothetical extrapolation rule would be:
|
||||
hotspot = 100 * bandgap - 120 * adc + 484
|
||||
|
||||
In other context, the same idea can be used to add fixed offset. For instance,
|
||||
consider the hotspot extrapolation rule below:
|
||||
hotspot = 1 * adc + 6000
|
||||
|
||||
In the above equation, the hotspot is always 6C higher than what is read
|
||||
from the ADC sensor. The binding would be then:
|
||||
thermal-sensors = <&adc>;
|
||||
|
||||
/* hotspot = 1 * adc + 6000 */
|
||||
coefficients = <1 6000>;
|
||||
|
||||
(d) - Board thermal
|
||||
|
||||
The board thermal example below illustrates how to setup one thermal zone
|
||||
with many sensors and many cooling devices.
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
&i2c1 {
|
||||
...
|
||||
/*
|
||||
* An IC with several temperature sensor.
|
||||
*/
|
||||
adc-dummy: sensor@0x50 {
|
||||
...
|
||||
#thermal-sensor-cells = <1>; /* sensor internal ID */
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
batt-thermal {
|
||||
polling-delay-passive = <500>; /* milliseconds */
|
||||
polling-delay = <2500>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&adc-dummy 4>;
|
||||
|
||||
trips {
|
||||
...
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
...
|
||||
};
|
||||
};
|
||||
|
||||
board-thermal: board-thermal {
|
||||
polling-delay-passive = <1000>; /* milliseconds */
|
||||
polling-delay = <2500>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&adc-dummy 0>, /* pcb top edge */
|
||||
<&adc-dummy 1>, /* lcd */
|
||||
<&adc-dymmy 2>; /* back cover */
|
||||
/*
|
||||
* An array of coefficients describing the sensor
|
||||
* linear relation. E.g.:
|
||||
* z = c1*x1 + c2*x2 + c3*x3
|
||||
*/
|
||||
coefficients = <1200 -345 890>;
|
||||
|
||||
trips {
|
||||
/* Trips are based on resulting linear equation */
|
||||
cpu-trip: cpu-trip {
|
||||
temperature = <60000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
gpu-trip: gpu-trip {
|
||||
temperature = <55000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
}
|
||||
lcd-trip: lcp-trip {
|
||||
temperature = <53000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
crit-trip: crit-trip {
|
||||
temperature = <68000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&cpu-trip>;
|
||||
cooling-device = <&cpu0 0 2>;
|
||||
contribution = <55>;
|
||||
};
|
||||
map1 {
|
||||
trip = <&gpu-trip>;
|
||||
cooling-device = <&gpu0 0 2>;
|
||||
contribution = <20>;
|
||||
};
|
||||
map2 {
|
||||
trip = <&lcd-trip>;
|
||||
cooling-device = <&lcd0 5 10>;
|
||||
contribution = <15>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
The above example is a mix of previous examples, a sensor IP with several internal
|
||||
sensors used to monitor different zones, one of them is composed by several sensors and
|
||||
with different cooling devices.
|
@ -8501,6 +8501,7 @@ S: Supported
|
||||
F: drivers/thermal/
|
||||
F: include/linux/thermal.h
|
||||
F: include/linux/cpu_cooling.h
|
||||
F: Documentation/devicetree/bindings/thermal/
|
||||
|
||||
THINGM BLINK(1) USB RGB LED DRIVER
|
||||
M: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||
|
41
arch/arm/boot/dts/omap4-cpu-thermal.dtsi
Normal file
41
arch/arm/boot/dts/omap4-cpu-thermal.dtsi
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Device Tree Source for OMAP4/5 SoC CPU thermal
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
cpu_thermal: cpu_thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap 0>;
|
||||
|
||||
trips {
|
||||
cpu_alert0: cpu_alert {
|
||||
temperature = <100000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit: cpu_crit {
|
||||
temperature = <125000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&cpu_alert0>;
|
||||
cooling-device =
|
||||
<&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||||
};
|
||||
};
|
||||
};
|
@ -12,7 +12,7 @@
|
||||
|
||||
/ {
|
||||
cpus {
|
||||
cpu@0 {
|
||||
cpu0: cpu@0 {
|
||||
/* OMAP443x variants OPP50-OPPNT */
|
||||
operating-points = <
|
||||
/* kHz uV */
|
||||
@ -22,12 +22,25 @@
|
||||
1008000 1375000
|
||||
>;
|
||||
clock-latency = <300000>; /* From legacy driver */
|
||||
|
||||
/* cooling options */
|
||||
cooling-min-level = <0>;
|
||||
cooling-max-level = <3>;
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
};
|
||||
|
||||
bandgap {
|
||||
reg = <0x4a002260 0x4
|
||||
0x4a00232C 0x4>;
|
||||
compatible = "ti,omap4430-bandgap";
|
||||
thermal-zones {
|
||||
#include "omap4-cpu-thermal.dtsi"
|
||||
};
|
||||
|
||||
ocp {
|
||||
bandgap: bandgap {
|
||||
reg = <0x4a002260 0x4
|
||||
0x4a00232C 0x4>;
|
||||
compatible = "ti,omap4430-bandgap";
|
||||
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -12,7 +12,7 @@
|
||||
/ {
|
||||
cpus {
|
||||
/* OMAP446x 'standard device' variants OPP50 to OPPTurbo */
|
||||
cpu@0 {
|
||||
cpu0: cpu@0 {
|
||||
operating-points = <
|
||||
/* kHz uV */
|
||||
350000 1025000
|
||||
@ -20,6 +20,11 @@
|
||||
920000 1313000
|
||||
>;
|
||||
clock-latency = <300000>; /* From legacy driver */
|
||||
|
||||
/* cooling options */
|
||||
cooling-min-level = <0>;
|
||||
cooling-max-level = <2>;
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
};
|
||||
|
||||
@ -30,12 +35,20 @@
|
||||
ti,hwmods = "debugss";
|
||||
};
|
||||
|
||||
bandgap {
|
||||
reg = <0x4a002260 0x4
|
||||
0x4a00232C 0x4
|
||||
0x4a002378 0x18>;
|
||||
compatible = "ti,omap4460-bandgap";
|
||||
interrupts = <0 126 IRQ_TYPE_LEVEL_HIGH>; /* talert */
|
||||
gpios = <&gpio3 22 0>; /* tshut */
|
||||
thermal-zones {
|
||||
#include "omap4-cpu-thermal.dtsi"
|
||||
};
|
||||
|
||||
ocp {
|
||||
bandgap: bandgap {
|
||||
reg = <0x4a002260 0x4
|
||||
0x4a00232C 0x4
|
||||
0x4a002378 0x18>;
|
||||
compatible = "ti,omap4460-bandgap";
|
||||
interrupts = <0 126 IRQ_TYPE_LEVEL_HIGH>; /* talert */
|
||||
gpios = <&gpio3 22 0>; /* tshut */
|
||||
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
28
arch/arm/boot/dts/omap5-core-thermal.dtsi
Normal file
28
arch/arm/boot/dts/omap5-core-thermal.dtsi
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Device Tree Source for OMAP543x SoC CORE thermal
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
core_thermal: core_thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap 2>;
|
||||
|
||||
trips {
|
||||
core_crit: core_crit {
|
||||
temperature = <125000>; /* milliCelsius */
|
||||
hysteresis = <2000>; /* milliCelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
28
arch/arm/boot/dts/omap5-gpu-thermal.dtsi
Normal file
28
arch/arm/boot/dts/omap5-gpu-thermal.dtsi
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Device Tree Source for OMAP543x SoC GPU thermal
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
|
||||
gpu_thermal: gpu_thermal {
|
||||
polling-delay-passive = <250>; /* milliseconds */
|
||||
polling-delay = <1000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&bandgap 1>;
|
||||
|
||||
trips {
|
||||
gpu_crit: gpu_crit {
|
||||
temperature = <125000>; /* milliCelsius */
|
||||
hysteresis = <2000>; /* milliCelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
@ -49,6 +49,10 @@
|
||||
1000000 1060000
|
||||
1500000 1250000
|
||||
>;
|
||||
/* cooling options */
|
||||
cooling-min-level = <0>;
|
||||
cooling-max-level = <2>;
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
cpu@1 {
|
||||
device_type = "cpu";
|
||||
@ -57,6 +61,12 @@
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
#include "omap4-cpu-thermal.dtsi"
|
||||
#include "omap5-gpu-thermal.dtsi"
|
||||
#include "omap5-core-thermal.dtsi"
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
/* PPI secure/nonsecure IRQ */
|
||||
@ -729,13 +739,15 @@
|
||||
};
|
||||
};
|
||||
|
||||
bandgap@4a0021e0 {
|
||||
bandgap: bandgap@4a0021e0 {
|
||||
reg = <0x4a0021e0 0xc
|
||||
0x4a00232c 0xc
|
||||
0x4a002380 0x2c
|
||||
0x4a0023C0 0x3c>;
|
||||
interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
|
||||
compatible = "ti,omap5430-bandgap";
|
||||
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -181,7 +181,7 @@ config CPU_FREQ_GOV_CONSERVATIVE
|
||||
|
||||
config GENERIC_CPUFREQ_CPU0
|
||||
tristate "Generic CPU0 cpufreq driver"
|
||||
depends on HAVE_CLK && REGULATOR && PM_OPP && OF
|
||||
depends on HAVE_CLK && REGULATOR && PM_OPP && OF && THERMAL && CPU_THERMAL
|
||||
help
|
||||
This adds a generic cpufreq driver for CPU0 frequency management.
|
||||
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
||||
|
@ -13,7 +13,9 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -21,6 +23,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
static unsigned int transition_latency;
|
||||
static unsigned int voltage_tolerance; /* in percentage */
|
||||
@ -29,6 +32,7 @@ static struct device *cpu_dev;
|
||||
static struct clk *cpu_clk;
|
||||
static struct regulator *cpu_reg;
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
static struct thermal_cooling_device *cdev;
|
||||
|
||||
static unsigned int cpu0_get_speed(unsigned int cpu)
|
||||
{
|
||||
@ -201,6 +205,17 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
|
||||
goto out_free_table;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, just loading the cooling device;
|
||||
* thermal DT code takes care of matching them.
|
||||
*/
|
||||
if (of_find_property(np, "#cooling-cells", NULL)) {
|
||||
cdev = of_cpufreq_cooling_register(np, cpu_present_mask);
|
||||
if (IS_ERR(cdev))
|
||||
pr_err("running cpufreq without cooling device: %ld\n",
|
||||
PTR_ERR(cdev));
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
|
||||
@ -213,6 +228,7 @@ out_put_node:
|
||||
|
||||
static int cpu0_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpufreq_cooling_unregister(cdev);
|
||||
cpufreq_unregister_driver(&cpu0_cpufreq_driver);
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/thermal.h>
|
||||
#include "lm75.h"
|
||||
|
||||
|
||||
@ -71,6 +73,7 @@ static const u8 LM75_REG_TEMP[3] = {
|
||||
/* Each client has this additional data */
|
||||
struct lm75_data {
|
||||
struct device *hwmon_dev;
|
||||
struct thermal_zone_device *tz;
|
||||
struct mutex update_lock;
|
||||
u8 orig_conf;
|
||||
u8 resolution; /* In bits, between 9 and 12 */
|
||||
@ -91,22 +94,36 @@ static struct lm75_data *lm75_update_device(struct device *dev);
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
|
||||
{
|
||||
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
|
||||
}
|
||||
|
||||
/* sysfs attributes for hwmon */
|
||||
|
||||
static int lm75_read_temp(void *dev, long *temp)
|
||||
{
|
||||
struct lm75_data *data = lm75_update_device(dev);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
*temp = lm75_reg_to_mc(data->temp[0], data->resolution);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct lm75_data *data = lm75_update_device(dev);
|
||||
long temp;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
temp = ((data->temp[attr->index] >> (16 - data->resolution)) * 1000)
|
||||
>> (data->resolution - 8);
|
||||
|
||||
return sprintf(buf, "%ld\n", temp);
|
||||
return sprintf(buf, "%ld\n", lm75_reg_to_mc(data->temp[attr->index],
|
||||
data->resolution));
|
||||
}
|
||||
|
||||
static ssize_t set_temp(struct device *dev, struct device_attribute *da,
|
||||
@ -273,6 +290,13 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
data->tz = thermal_zone_of_sensor_register(&client->dev,
|
||||
0,
|
||||
&client->dev,
|
||||
lm75_read_temp, NULL);
|
||||
if (IS_ERR(data->tz))
|
||||
data->tz = NULL;
|
||||
|
||||
dev_info(&client->dev, "%s: sensor '%s'\n",
|
||||
dev_name(data->hwmon_dev), client->name);
|
||||
|
||||
@ -287,6 +311,7 @@ static int lm75_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm75_data *data = i2c_get_clientdata(client);
|
||||
|
||||
thermal_zone_of_sensor_unregister(&client->dev, data->tz);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm75_group);
|
||||
lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define DRIVER_NAME "tmp102"
|
||||
|
||||
@ -50,6 +52,7 @@
|
||||
|
||||
struct tmp102 {
|
||||
struct device *hwmon_dev;
|
||||
struct thermal_zone_device *tz;
|
||||
struct mutex lock;
|
||||
u16 config_orig;
|
||||
unsigned long last_update;
|
||||
@ -93,6 +96,15 @@ static struct tmp102 *tmp102_update_device(struct i2c_client *client)
|
||||
return tmp102;
|
||||
}
|
||||
|
||||
static int tmp102_read_temp(void *dev, long *temp)
|
||||
{
|
||||
struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
|
||||
|
||||
*temp = tmp102->temp[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tmp102_show_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -204,6 +216,12 @@ static int tmp102_probe(struct i2c_client *client,
|
||||
goto fail_remove_sysfs;
|
||||
}
|
||||
|
||||
tmp102->tz = thermal_zone_of_sensor_register(&client->dev, 0,
|
||||
&client->dev,
|
||||
tmp102_read_temp, NULL);
|
||||
if (IS_ERR(tmp102->tz))
|
||||
tmp102->tz = NULL;
|
||||
|
||||
dev_info(&client->dev, "initialized\n");
|
||||
|
||||
return 0;
|
||||
@ -220,6 +238,7 @@ static int tmp102_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tmp102 *tmp102 = i2c_get_clientdata(client);
|
||||
|
||||
thermal_zone_of_sensor_unregister(&client->dev, tmp102->tz);
|
||||
hwmon_device_unregister(tmp102->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
|
||||
|
||||
|
@ -29,6 +29,19 @@ config THERMAL_HWMON
|
||||
Say 'Y' here if you want all thermal sensors to
|
||||
have hwmon sysfs interface too.
|
||||
|
||||
config THERMAL_OF
|
||||
bool
|
||||
prompt "APIs to parse thermal data out of device tree"
|
||||
depends on OF
|
||||
default y
|
||||
help
|
||||
This options provides helpers to add the support to
|
||||
read and parse thermal data definitions out of the
|
||||
device tree blob.
|
||||
|
||||
Say 'Y' here if you need to build thermal infrastructure
|
||||
based on device tree.
|
||||
|
||||
choice
|
||||
prompt "Default Thermal governor"
|
||||
default THERMAL_DEFAULT_GOV_STEP_WISE
|
||||
@ -79,6 +92,7 @@ config THERMAL_GOV_USER_SPACE
|
||||
config CPU_THERMAL
|
||||
bool "generic cpu cooling support"
|
||||
depends on CPU_FREQ
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
This implements the generic cpu cooling mechanism through frequency
|
||||
reduction. An ACPI version of this already exists
|
||||
@ -121,7 +135,7 @@ config SPEAR_THERMAL
|
||||
|
||||
config RCAR_THERMAL
|
||||
tristate "Renesas R-Car thermal driver"
|
||||
depends on ARCH_SHMOBILE
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Enable this to plug the R-Car thermal sensor driver into the Linux
|
||||
thermal framework.
|
||||
@ -192,6 +206,13 @@ config X86_PKG_TEMP_THERMAL
|
||||
two trip points which can be set by user to get notifications via thermal
|
||||
notification methods.
|
||||
|
||||
config ACPI_INT3403_THERMAL
|
||||
tristate "ACPI INT3403 thermal driver"
|
||||
depends on X86 && ACPI
|
||||
help
|
||||
This driver uses ACPI INT3403 device objects. If present, it will
|
||||
register each INT3403 thermal sensor as a thermal zone.
|
||||
|
||||
menu "Texas Instruments thermal drivers"
|
||||
source "drivers/thermal/ti-soc-thermal/Kconfig"
|
||||
endmenu
|
||||
|
@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o
|
||||
|
||||
# interface to/from other layers providing sensors
|
||||
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
|
||||
thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o
|
||||
|
||||
# governors
|
||||
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
|
||||
@ -29,3 +30,4 @@ obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
|
||||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
|
||||
|
@ -424,18 +424,21 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
|
||||
};
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* __cpufreq_cooling_register - helper function to create cpufreq cooling device
|
||||
* @np: a valid struct device_node to the cooling device device tree node
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices.
|
||||
* cooling devices. It also gives the opportunity to link the cooling device
|
||||
* with a device tree node, in order to bind it via the thermal DT code.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
static struct thermal_cooling_device *
|
||||
__cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
struct thermal_cooling_device *cool_dev;
|
||||
struct cpufreq_cooling_device *cpufreq_dev = NULL;
|
||||
@ -474,8 +477,8 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
|
||||
cpufreq_dev->id);
|
||||
|
||||
cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
|
||||
&cpufreq_cooling_ops);
|
||||
cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
|
||||
&cpufreq_cooling_ops);
|
||||
if (IS_ERR(cool_dev)) {
|
||||
release_idr(&cpufreq_idr, cpufreq_dev->id);
|
||||
kfree(cpufreq_dev);
|
||||
@ -495,8 +498,49 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
|
||||
return cool_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
{
|
||||
return __cpufreq_cooling_register(NULL, clip_cpus);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
|
||||
|
||||
/**
|
||||
* of_cpufreq_cooling_register - function to create cpufreq cooling device.
|
||||
* @np: a valid struct device_node to the cooling device device tree node
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
|
||||
*
|
||||
* This interface function registers the cpufreq cooling device with the name
|
||||
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
|
||||
* cooling devices. Using this API, the cpufreq cooling device will be
|
||||
* linked to the device tree node provided.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
if (!np)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return __cpufreq_cooling_register(np, clip_cpus);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_unregister - function to remove cpufreq cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
|
@ -490,27 +490,30 @@ static int imx_thermal_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
u32 val;
|
||||
|
||||
regmap_read(map, TEMPSENSE0, &val);
|
||||
if ((val & TEMPSENSE0_POWER_DOWN) == 0) {
|
||||
/*
|
||||
* If a measurement is taking place, wait for a long enough
|
||||
* time for it to finish, and then check again. If it still
|
||||
* does not finish, something must go wrong.
|
||||
*/
|
||||
udelay(50);
|
||||
regmap_read(map, TEMPSENSE0, &val);
|
||||
if ((val & TEMPSENSE0_POWER_DOWN) == 0)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
/*
|
||||
* Need to disable thermal sensor, otherwise, when thermal core
|
||||
* try to get temperature before thermal sensor resume, a wrong
|
||||
* temperature will be read as the thermal sensor is powered
|
||||
* down.
|
||||
*/
|
||||
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
|
||||
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
|
||||
data->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_thermal_resume(struct device *dev)
|
||||
{
|
||||
/* Nothing to do for now */
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
|
||||
/* Enabled thermal sensor after resume */
|
||||
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
|
||||
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
|
||||
data->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -522,6 +525,7 @@ static const struct of_device_id of_imx_thermal_match[] = {
|
||||
{ .compatible = "fsl,imx6q-tempmon", },
|
||||
{ /* end */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
|
||||
|
||||
static struct platform_driver imx_thermal = {
|
||||
.driver = {
|
||||
|
237
drivers/thermal/int3403_thermal.c
Normal file
237
drivers/thermal/int3403_thermal.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* ACPI INT3403 thermal driver
|
||||
* Copyright (c) 2013, Intel Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define INT3403_TYPE_SENSOR 0x03
|
||||
#define INT3403_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3403_THERMAL_EVENT 0x90
|
||||
|
||||
#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
|
||||
#define KELVIN_OFFSET 2732
|
||||
#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
|
||||
|
||||
#define ACPI_INT3403_CLASS "int3403"
|
||||
#define ACPI_INT3403_FILE_STATE "state"
|
||||
|
||||
struct int3403_sensor {
|
||||
struct thermal_zone_device *tzone;
|
||||
unsigned long *thresholds;
|
||||
};
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzone,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long hyst;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
/*
|
||||
* get_trip_temp is a mandatory callback but
|
||||
* PATx method doesn't return any value, so return
|
||||
* cached value, which was last set from user space.
|
||||
*/
|
||||
*temp = obj->thresholds[trip];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
/* Mandatory callback, may not mean much here */
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
int ret = 0;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
if (acpi_has_method(device->handle, name)) {
|
||||
status = acpi_execute_simple_method(device->handle, name,
|
||||
MILLI_CELSIUS_TO_DECI_KELVIN(temp,
|
||||
KELVIN_OFFSET));
|
||||
if (ACPI_FAILURE(status))
|
||||
ret = -EIO;
|
||||
else
|
||||
obj->thresholds[trip] = temp;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_trip_hyst = sys_get_trip_hyst,
|
||||
};
|
||||
|
||||
static void acpi_thermal_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3403_PERF_CHANGED_EVENT:
|
||||
break;
|
||||
case INT3403_THERMAL_EVENT:
|
||||
thermal_zone_device_update(obj->tzone);
|
||||
break;
|
||||
default:
|
||||
dev_err(&device->dev, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int acpi_int3403_add(struct acpi_device *device)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned long long ptyp;
|
||||
acpi_status status;
|
||||
struct int3403_sensor *obj;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0;
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EINVAL;
|
||||
|
||||
if (ptyp != INT3403_TYPE_SENSOR)
|
||||
return -EINVAL;
|
||||
|
||||
obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
device->driver_data = obj;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PATC", NULL,
|
||||
&trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
|
||||
if (trip_cnt) {
|
||||
/* We have to cache, thresholds can't be readback */
|
||||
obj->thresholds = devm_kzalloc(&device->dev,
|
||||
sizeof(*obj->thresholds) * trip_cnt,
|
||||
GFP_KERNEL);
|
||||
if (!obj->thresholds)
|
||||
return -ENOMEM;
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
}
|
||||
obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
|
||||
trip_cnt, trip_mask, device, &tzone_ops,
|
||||
NULL, 0, 0);
|
||||
if (IS_ERR(obj->tzone)) {
|
||||
result = PTR_ERR(obj->tzone);
|
||||
return result;
|
||||
}
|
||||
|
||||
strcpy(acpi_device_name(device), "INT3403");
|
||||
strcpy(acpi_device_class(device), ACPI_INT3403_CLASS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_int3403_remove(struct acpi_device *device)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACPI_MODULE_NAME("int3403");
|
||||
static const struct acpi_device_id int3403_device_ids[] = {
|
||||
{"INT3403", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
|
||||
|
||||
static struct acpi_driver acpi_int3403_driver = {
|
||||
.name = "INT3403",
|
||||
.class = ACPI_INT3403_CLASS,
|
||||
.ids = int3403_device_ids,
|
||||
.ops = {
|
||||
.add = acpi_int3403_add,
|
||||
.remove = acpi_int3403_remove,
|
||||
.notify = acpi_thermal_notify,
|
||||
},
|
||||
};
|
||||
|
||||
module_acpi_driver(acpi_int3403_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
|
849
drivers/thermal/of-thermal.c
Normal file
849
drivers/thermal/of-thermal.c
Normal file
@ -0,0 +1,849 @@
|
||||
/*
|
||||
* of-thermal.c - Generic Thermal Management device tree support.
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.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.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*** Private data structures to represent thermal device tree data ***/
|
||||
|
||||
/**
|
||||
* struct __thermal_trip - representation of a point in temperature domain
|
||||
* @np: pointer to struct device_node that this trip point was created from
|
||||
* @temperature: temperature value in miliCelsius
|
||||
* @hysteresis: relative hysteresis in miliCelsius
|
||||
* @type: trip point type
|
||||
*/
|
||||
|
||||
struct __thermal_trip {
|
||||
struct device_node *np;
|
||||
unsigned long int temperature;
|
||||
unsigned long int hysteresis;
|
||||
enum thermal_trip_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_bind_param - a match between trip and cooling device
|
||||
* @cooling_device: a pointer to identify the referred cooling device
|
||||
* @trip_id: the trip point index
|
||||
* @usage: the percentage (from 0 to 100) of cooling contribution
|
||||
* @min: minimum cooling state used at this trip point
|
||||
* @max: maximum cooling state used at this trip point
|
||||
*/
|
||||
|
||||
struct __thermal_bind_params {
|
||||
struct device_node *cooling_device;
|
||||
unsigned int trip_id;
|
||||
unsigned int usage;
|
||||
unsigned long min;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_zone - internal representation of a thermal zone
|
||||
* @mode: current thermal zone device mode (enabled/disabled)
|
||||
* @passive_delay: polling interval while passive cooling is activated
|
||||
* @polling_delay: zone polling interval
|
||||
* @ntrips: number of trip points
|
||||
* @trips: an array of trip points (0..ntrips - 1)
|
||||
* @num_tbps: number of thermal bind params
|
||||
* @tbps: an array of thermal bind params (0..num_tbps - 1)
|
||||
* @sensor_data: sensor private data used while reading temperature and trend
|
||||
* @get_temp: sensor callback to read temperature
|
||||
* @get_trend: sensor callback to read temperature trend
|
||||
*/
|
||||
|
||||
struct __thermal_zone {
|
||||
enum thermal_device_mode mode;
|
||||
int passive_delay;
|
||||
int polling_delay;
|
||||
|
||||
/* trip data */
|
||||
int ntrips;
|
||||
struct __thermal_trip *trips;
|
||||
|
||||
/* cooling binding data */
|
||||
int num_tbps;
|
||||
struct __thermal_bind_params *tbps;
|
||||
|
||||
/* sensor interface */
|
||||
void *sensor_data;
|
||||
int (*get_temp)(void *, long *);
|
||||
int (*get_trend)(void *, long *);
|
||||
};
|
||||
|
||||
/*** DT thermal zone device callbacks ***/
|
||||
|
||||
static int of_thermal_get_temp(struct thermal_zone_device *tz,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data->get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
return data->get_temp(data->sensor_data, temp);
|
||||
}
|
||||
|
||||
static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trend *trend)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
long dev_trend;
|
||||
int r;
|
||||
|
||||
if (!data->get_trend)
|
||||
return -EINVAL;
|
||||
|
||||
r = data->get_trend(data->sensor_data, &dev_trend);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* TODO: These intervals might have some thresholds, but in core code */
|
||||
if (dev_trend > 0)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (dev_trend < 0)
|
||||
*trend = THERMAL_TREND_DROPPING;
|
||||
else
|
||||
*trend = THERMAL_TREND_STABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_bind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to bind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_bind_cooling_device(thermal,
|
||||
tbp->trip_id, cdev,
|
||||
tbp->min,
|
||||
tbp->max);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_unbind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to unbind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_unbind_cooling_device(thermal,
|
||||
tbp->trip_id, cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
*mode = data->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
tz->polling_delay = data->polling_delay;
|
||||
else
|
||||
tz->polling_delay = 0;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
data->mode = mode;
|
||||
thermal_zone_device_update(tz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*type = data->trips[trip].type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*temp = data->trips[trip].temperature;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
/* thermal framework should take care of data->mask & (1 << trip) */
|
||||
data->trips[trip].temperature = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long *hyst)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*hyst = data->trips[trip].hysteresis;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
unsigned long hyst)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (trip >= data->ntrips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
/* thermal framework should take care of data->mask & (1 << trip) */
|
||||
data->trips[trip].hysteresis = hyst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->ntrips; i++)
|
||||
if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = data->trips[i].temperature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops of_thermal_ops = {
|
||||
.get_mode = of_thermal_get_mode,
|
||||
.set_mode = of_thermal_set_mode,
|
||||
|
||||
.get_trip_type = of_thermal_get_trip_type,
|
||||
.get_trip_temp = of_thermal_get_trip_temp,
|
||||
.set_trip_temp = of_thermal_set_trip_temp,
|
||||
.get_trip_hyst = of_thermal_get_trip_hyst,
|
||||
.set_trip_hyst = of_thermal_set_trip_hyst,
|
||||
.get_crit_temp = of_thermal_get_crit_temp,
|
||||
|
||||
.bind = of_thermal_bind,
|
||||
.unbind = of_thermal_unbind,
|
||||
};
|
||||
|
||||
/*** sensor API ***/
|
||||
|
||||
static struct thermal_zone_device *
|
||||
thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
struct device_node *sensor, void *data,
|
||||
int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
{
|
||||
struct thermal_zone_device *tzd;
|
||||
struct __thermal_zone *tz;
|
||||
|
||||
tzd = thermal_zone_get_zone_by_name(zone->name);
|
||||
if (IS_ERR(tzd))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
tz = tzd->devdata;
|
||||
|
||||
mutex_lock(&tzd->lock);
|
||||
tz->get_temp = get_temp;
|
||||
tz->get_trend = get_trend;
|
||||
tz->sensor_data = data;
|
||||
|
||||
tzd->ops->get_temp = of_thermal_get_temp;
|
||||
tzd->ops->get_trend = of_thermal_get_trend;
|
||||
mutex_unlock(&tzd->lock);
|
||||
|
||||
return tzd;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
|
||||
* @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
* a valid .of_node, for the sensor node.
|
||||
* @sensor_id: a sensor identifier, in case the sensor IP has more
|
||||
* than one sensors
|
||||
* @data: a private pointer (owned by the caller) that will be passed
|
||||
* back, when a temperature reading is needed.
|
||||
* @get_temp: a pointer to a function that reads the sensor temperature.
|
||||
* @get_trend: a pointer to a function that reads the sensor temperature trend.
|
||||
*
|
||||
* This function will search the list of thermal zones described in device
|
||||
* tree and look for the zone that refer to the sensor device pointed by
|
||||
* @dev->of_node as temperature providers. For the zone pointing to the
|
||||
* sensor node, the sensor will be added to the DT thermal zone device.
|
||||
*
|
||||
* The thermal zone temperature is provided by the @get_temp function
|
||||
* pointer. When called, it will have the private pointer @data back.
|
||||
*
|
||||
* The thermal zone temperature trend is provided by the @get_trend function
|
||||
* pointer. When called, it will have the private pointer @data back.
|
||||
*
|
||||
* TODO:
|
||||
* 01 - This function must enqueue the new sensor instead of using
|
||||
* it as the only source of temperature values.
|
||||
*
|
||||
* 02 - There must be a way to match the sensor with all thermal zones
|
||||
* that refer to it.
|
||||
*
|
||||
* Return: On success returns a valid struct thermal_zone_device,
|
||||
* otherwise, it returns a corresponding ERR_PTR(). Caller must
|
||||
* check the return value with help of IS_ERR() helper.
|
||||
*/
|
||||
struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
|
||||
void *data, int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
{
|
||||
struct device_node *np, *child, *sensor_np;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (!dev || !dev->of_node)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
sensor_np = dev->of_node;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct of_phandle_args sensor_specs;
|
||||
int ret, id;
|
||||
|
||||
/* For now, thermal framework supports only 1 sensor per zone */
|
||||
ret = of_parse_phandle_with_args(child, "thermal-sensors",
|
||||
"#thermal-sensor-cells",
|
||||
0, &sensor_specs);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
if (sensor_specs.args_count >= 1) {
|
||||
id = sensor_specs.args[0];
|
||||
WARN(sensor_specs.args_count > 1,
|
||||
"%s: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np->name, sensor_specs.args_count);
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
if (sensor_specs.np == sensor_np && id == sensor_id) {
|
||||
of_node_put(np);
|
||||
return thermal_zone_of_add_sensor(child, sensor_np,
|
||||
data,
|
||||
get_temp,
|
||||
get_trend);
|
||||
}
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
|
||||
|
||||
/**
|
||||
* thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
|
||||
* @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
* a valid .of_node, for the sensor node.
|
||||
* @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
|
||||
*
|
||||
* This function removes the sensor callbacks and private data from the
|
||||
* thermal zone device registered with thermal_zone_of_sensor_register()
|
||||
* API. It will also silent the zone by remove the .get_temp() and .get_trend()
|
||||
* thermal zone device callbacks.
|
||||
*
|
||||
* TODO: When the support to several sensors per zone is added, this
|
||||
* function must search the sensor list based on @dev parameter.
|
||||
*
|
||||
*/
|
||||
void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tzd)
|
||||
{
|
||||
struct __thermal_zone *tz;
|
||||
|
||||
if (!dev || !tzd || !tzd->devdata)
|
||||
return;
|
||||
|
||||
tz = tzd->devdata;
|
||||
|
||||
/* no __thermal_zone, nothing to be done */
|
||||
if (!tz)
|
||||
return;
|
||||
|
||||
mutex_lock(&tzd->lock);
|
||||
tzd->ops->get_temp = NULL;
|
||||
tzd->ops->get_trend = NULL;
|
||||
|
||||
tz->get_temp = NULL;
|
||||
tz->get_trend = NULL;
|
||||
tz->sensor_data = NULL;
|
||||
mutex_unlock(&tzd->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
|
||||
|
||||
/*** functions parsing device tree nodes ***/
|
||||
|
||||
/**
|
||||
* thermal_of_populate_bind_params - parse and fill cooling map data
|
||||
* @np: DT node containing a cooling-map node
|
||||
* @__tbp: data structure to be filled with cooling map info
|
||||
* @trips: array of thermal zone trip points
|
||||
* @ntrips: number of trip points inside trips.
|
||||
*
|
||||
* This function parses a cooling-map type of node represented by
|
||||
* @np parameter and fills the read data into @__tbp data structure.
|
||||
* It needs the already parsed array of trip points of the thermal zone
|
||||
* in consideration.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*/
|
||||
static int thermal_of_populate_bind_params(struct device_node *np,
|
||||
struct __thermal_bind_params *__tbp,
|
||||
struct __thermal_trip *trips,
|
||||
int ntrips)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
struct device_node *trip;
|
||||
int ret, i;
|
||||
u32 prop;
|
||||
|
||||
/* Default weight. Usage is optional */
|
||||
__tbp->usage = 0;
|
||||
ret = of_property_read_u32(np, "contribution", &prop);
|
||||
if (ret == 0)
|
||||
__tbp->usage = prop;
|
||||
|
||||
trip = of_parse_phandle(np, "trip", 0);
|
||||
if (!trip) {
|
||||
pr_err("missing trip property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* match using device_node */
|
||||
for (i = 0; i < ntrips; i++)
|
||||
if (trip == trips[i].np) {
|
||||
__tbp->trip_id = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ntrips) {
|
||||
ret = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
|
||||
0, &cooling_spec);
|
||||
if (ret < 0) {
|
||||
pr_err("missing cooling_device property\n");
|
||||
goto end;
|
||||
}
|
||||
__tbp->cooling_device = cooling_spec.np;
|
||||
if (cooling_spec.args_count >= 2) { /* at least min and max */
|
||||
__tbp->min = cooling_spec.args[0];
|
||||
__tbp->max = cooling_spec.args[1];
|
||||
} else {
|
||||
pr_err("wrong reference to cooling device, missing limits\n");
|
||||
}
|
||||
|
||||
end:
|
||||
of_node_put(trip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* It maps 'enum thermal_trip_type' found in include/linux/thermal.h
|
||||
* into the device tree binding of 'trip', property type.
|
||||
*/
|
||||
static const char * const trip_types[] = {
|
||||
[THERMAL_TRIP_ACTIVE] = "active",
|
||||
[THERMAL_TRIP_PASSIVE] = "passive",
|
||||
[THERMAL_TRIP_HOT] = "hot",
|
||||
[THERMAL_TRIP_CRITICAL] = "critical",
|
||||
};
|
||||
|
||||
/**
|
||||
* thermal_of_get_trip_type - Get phy mode for given device_node
|
||||
* @np: Pointer to the given device_node
|
||||
* @type: Pointer to resulting trip type
|
||||
*
|
||||
* The function gets trip type string from property 'type',
|
||||
* and store its index in trip_types table in @type,
|
||||
*
|
||||
* Return: 0 on success, or errno in error case.
|
||||
*/
|
||||
static int thermal_of_get_trip_type(struct device_node *np,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
const char *t;
|
||||
int err, i;
|
||||
|
||||
err = of_property_read_string(np, "type", &t);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trip_types); i++)
|
||||
if (!strcasecmp(t, trip_types[i])) {
|
||||
*type = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_of_populate_trip - parse and fill one trip point data
|
||||
* @np: DT node containing a trip point node
|
||||
* @trip: trip point data structure to be filled up
|
||||
*
|
||||
* This function parses a trip point type of node represented by
|
||||
* @np parameter and fills the read data into @trip data structure.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*/
|
||||
static int thermal_of_populate_trip(struct device_node *np,
|
||||
struct __thermal_trip *trip)
|
||||
{
|
||||
int prop;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "temperature", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing temperature property\n");
|
||||
return ret;
|
||||
}
|
||||
trip->temperature = prop;
|
||||
|
||||
ret = of_property_read_u32(np, "hysteresis", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing hysteresis property\n");
|
||||
return ret;
|
||||
}
|
||||
trip->hysteresis = prop;
|
||||
|
||||
ret = thermal_of_get_trip_type(np, &trip->type);
|
||||
if (ret < 0) {
|
||||
pr_err("wrong trip type property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Required for cooling map matching */
|
||||
trip->np = np;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_of_build_thermal_zone - parse and fill one thermal zone data
|
||||
* @np: DT node containing a thermal zone node
|
||||
*
|
||||
* This function parses a thermal zone type of node represented by
|
||||
* @np parameter and fills the read data into a __thermal_zone data structure
|
||||
* and return this pointer.
|
||||
*
|
||||
* TODO: Missing properties to parse: thermal-sensor-names and coefficients
|
||||
*
|
||||
* Return: On success returns a valid struct __thermal_zone,
|
||||
* otherwise, it returns a corresponding ERR_PTR(). Caller must
|
||||
* check the return value with help of IS_ERR() helper.
|
||||
*/
|
||||
static struct __thermal_zone *
|
||||
thermal_of_build_thermal_zone(struct device_node *np)
|
||||
{
|
||||
struct device_node *child = NULL, *gchild;
|
||||
struct __thermal_zone *tz;
|
||||
int ret, i;
|
||||
u32 prop;
|
||||
|
||||
if (!np) {
|
||||
pr_err("no thermal zone np\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
|
||||
if (!tz)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = of_property_read_u32(np, "polling-delay-passive", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing polling-delay-passive property\n");
|
||||
goto free_tz;
|
||||
}
|
||||
tz->passive_delay = prop;
|
||||
|
||||
ret = of_property_read_u32(np, "polling-delay", &prop);
|
||||
if (ret < 0) {
|
||||
pr_err("missing polling-delay property\n");
|
||||
goto free_tz;
|
||||
}
|
||||
tz->polling_delay = prop;
|
||||
|
||||
/* trips */
|
||||
child = of_get_child_by_name(np, "trips");
|
||||
|
||||
/* No trips provided */
|
||||
if (!child)
|
||||
goto finish;
|
||||
|
||||
tz->ntrips = of_get_child_count(child);
|
||||
if (tz->ntrips == 0) /* must have at least one child */
|
||||
goto finish;
|
||||
|
||||
tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);
|
||||
if (!tz->trips) {
|
||||
ret = -ENOMEM;
|
||||
goto free_tz;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(child, gchild) {
|
||||
ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
|
||||
if (ret)
|
||||
goto free_trips;
|
||||
}
|
||||
|
||||
of_node_put(child);
|
||||
|
||||
/* cooling-maps */
|
||||
child = of_get_child_by_name(np, "cooling-maps");
|
||||
|
||||
/* cooling-maps not provided */
|
||||
if (!child)
|
||||
goto finish;
|
||||
|
||||
tz->num_tbps = of_get_child_count(child);
|
||||
if (tz->num_tbps == 0)
|
||||
goto finish;
|
||||
|
||||
tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);
|
||||
if (!tz->tbps) {
|
||||
ret = -ENOMEM;
|
||||
goto free_trips;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(child, gchild)
|
||||
ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
|
||||
tz->trips, tz->ntrips);
|
||||
if (ret)
|
||||
goto free_tbps;
|
||||
|
||||
finish:
|
||||
of_node_put(child);
|
||||
tz->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return tz;
|
||||
|
||||
free_tbps:
|
||||
kfree(tz->tbps);
|
||||
free_trips:
|
||||
kfree(tz->trips);
|
||||
free_tz:
|
||||
kfree(tz);
|
||||
of_node_put(child);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static inline void of_thermal_free_zone(struct __thermal_zone *tz)
|
||||
{
|
||||
kfree(tz->tbps);
|
||||
kfree(tz->trips);
|
||||
kfree(tz);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_parse_thermal_zones - parse device tree thermal data
|
||||
*
|
||||
* Initialization function that can be called by machine initialization
|
||||
* code to parse thermal data and populate the thermal framework
|
||||
* with hardware thermal zones info. This function only parses thermal zones.
|
||||
* Cooling devices and sensor devices nodes are supposed to be parsed
|
||||
* by their respective drivers.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*
|
||||
*/
|
||||
int __init of_parse_thermal_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
struct __thermal_zone *tz;
|
||||
struct thermal_zone_device_ops *ops;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_debug("unable to find thermal zones\n");
|
||||
return 0; /* Run successfully on systems without thermal DT */
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
struct thermal_zone_params *tzp;
|
||||
|
||||
tz = thermal_of_build_thermal_zone(child);
|
||||
if (IS_ERR(tz)) {
|
||||
pr_err("failed to build thermal zone %s: %ld\n",
|
||||
child->name,
|
||||
PTR_ERR(tz));
|
||||
continue;
|
||||
}
|
||||
|
||||
ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
|
||||
if (!ops)
|
||||
goto exit_free;
|
||||
|
||||
tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
|
||||
if (!tzp) {
|
||||
kfree(ops);
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
/* No hwmon because there might be hwmon drivers registering */
|
||||
tzp->no_hwmon = true;
|
||||
|
||||
zone = thermal_zone_device_register(child->name, tz->ntrips,
|
||||
0, tz,
|
||||
ops, tzp,
|
||||
tz->passive_delay,
|
||||
tz->polling_delay);
|
||||
if (IS_ERR(zone)) {
|
||||
pr_err("Failed to build %s zone %ld\n", child->name,
|
||||
PTR_ERR(zone));
|
||||
kfree(tzp);
|
||||
kfree(ops);
|
||||
of_thermal_free_zone(tz);
|
||||
/* attempting to build remaining zones still */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free:
|
||||
of_thermal_free_zone(tz);
|
||||
|
||||
/* no memory available, so free what we have built */
|
||||
of_thermal_destroy_zones();
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_thermal_destroy_zones - remove all zones parsed and allocated resources
|
||||
*
|
||||
* Finds all zones parsed and added to the thermal framework and remove them
|
||||
* from the system, together with their resources.
|
||||
*
|
||||
*/
|
||||
void of_thermal_destroy_zones(void)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np) {
|
||||
pr_err("unable to find thermal zones\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct thermal_zone_device *zone;
|
||||
|
||||
zone = thermal_zone_get_zone_by_name(child->name);
|
||||
if (IS_ERR(zone))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_unregister(zone);
|
||||
kfree(zone->tzp);
|
||||
kfree(zone->ops);
|
||||
of_thermal_free_zone(zone->devdata);
|
||||
}
|
||||
}
|
@ -280,7 +280,7 @@ static int exynos_get_trend(struct thermal_zone_device *thermal,
|
||||
return 0;
|
||||
}
|
||||
/* Operation callback functions for thermal zone */
|
||||
static struct thermal_zone_device_ops const exynos_dev_ops = {
|
||||
static struct thermal_zone_device_ops exynos_dev_ops = {
|
||||
.bind = exynos_bind,
|
||||
.unbind = exynos_unbind,
|
||||
.get_temp = exynos_get_temp,
|
||||
|
@ -205,6 +205,7 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
|
||||
skip_calib_data:
|
||||
if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
|
||||
dev_err(&pdev->dev, "Invalid max trigger level\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
@ -403,7 +404,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
|
||||
enum thermal_trip_type type;
|
||||
#endif
|
||||
|
||||
if (!tz || IS_ERR(tz))
|
||||
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
|
||||
goto exit;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
@ -459,6 +460,9 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (!tz->ops->get_temp)
|
||||
return;
|
||||
|
||||
update_temperature(tz);
|
||||
|
||||
for (count = 0; count < tz->trips; count++)
|
||||
@ -1055,7 +1059,8 @@ static struct class thermal_class = {
|
||||
};
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* __thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
@ -1063,13 +1068,16 @@ static struct class thermal_class = {
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
* It also gives the opportunity to link the cooling device to a device tree
|
||||
* node, so that it can be bound to a thermal zone created out of device tree.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_cooling_device_register(char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
static struct thermal_cooling_device *
|
||||
__thermal_cooling_device_register(struct device_node *np,
|
||||
char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
int result;
|
||||
@ -1094,6 +1102,7 @@ thermal_cooling_device_register(char *type, void *devdata,
|
||||
strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
|
||||
mutex_init(&cdev->lock);
|
||||
INIT_LIST_HEAD(&cdev->thermal_instances);
|
||||
cdev->np = np;
|
||||
cdev->ops = ops;
|
||||
cdev->updated = true;
|
||||
cdev->device.class = &thermal_class;
|
||||
@ -1136,8 +1145,52 @@ unregister:
|
||||
device_unregister(&cdev->device);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_register() - register a new thermal cooling device
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_cooling_device_register(char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __thermal_cooling_device_register(NULL, type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_register);
|
||||
|
||||
/**
|
||||
* thermal_of_cooling_device_register() - register an OF thermal cooling device
|
||||
* @np: a pointer to a device tree node.
|
||||
* @type: the thermal cooling device type.
|
||||
* @devdata: device private data.
|
||||
* @ops: standard thermal cooling devices callbacks.
|
||||
*
|
||||
* This function will register a cooling device with device tree node reference.
|
||||
* This interface function adds a new thermal cooling device (fan/processor/...)
|
||||
* to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
|
||||
* to all the thermal zone devices registered at the same time.
|
||||
*
|
||||
* Return: a pointer to the created struct thermal_cooling_device or an
|
||||
* ERR_PTR. Caller must check return value with IS_ERR*() helpers.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
thermal_of_cooling_device_register(struct device_node *np,
|
||||
char *type, void *devdata,
|
||||
const struct thermal_cooling_device_ops *ops)
|
||||
{
|
||||
return __thermal_cooling_device_register(np, type, devdata, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
|
||||
|
||||
/**
|
||||
* thermal_cooling_device_unregister - removes the registered thermal cooling device
|
||||
* @cdev: the thermal cooling device to remove.
|
||||
@ -1376,7 +1429,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
|
||||
*/
|
||||
struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
||||
int trips, int mask, void *devdata,
|
||||
const struct thermal_zone_device_ops *ops,
|
||||
struct thermal_zone_device_ops *ops,
|
||||
const struct thermal_zone_params *tzp,
|
||||
int passive_delay, int polling_delay)
|
||||
{
|
||||
@ -1392,7 +1445,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
||||
if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!ops || !ops->get_temp)
|
||||
if (!ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
|
||||
@ -1496,6 +1549,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
||||
|
||||
INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
|
||||
|
||||
if (!tz->ops->get_temp)
|
||||
thermal_zone_device_set_polling(tz, 0);
|
||||
|
||||
thermal_zone_device_update(tz);
|
||||
|
||||
if (!result)
|
||||
@ -1746,8 +1802,14 @@ static int __init thermal_init(void)
|
||||
if (result)
|
||||
goto unregister_class;
|
||||
|
||||
result = of_parse_thermal_zones();
|
||||
if (result)
|
||||
goto exit_netlink;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_netlink:
|
||||
genetlink_exit();
|
||||
unregister_governors:
|
||||
thermal_unregister_governors();
|
||||
unregister_class:
|
||||
@ -1763,6 +1825,7 @@ error:
|
||||
|
||||
static void __exit thermal_exit(void)
|
||||
{
|
||||
of_thermal_destroy_zones();
|
||||
genetlink_exit();
|
||||
class_unregister(&thermal_class);
|
||||
thermal_unregister_governors();
|
||||
|
@ -77,4 +77,13 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
|
||||
static inline void thermal_gov_user_space_unregister(void) {}
|
||||
#endif /* CONFIG_THERMAL_GOV_USER_SPACE */
|
||||
|
||||
/* device tree support */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
int of_parse_thermal_zones(void);
|
||||
void of_thermal_destroy_zones(void);
|
||||
#else
|
||||
static inline int of_parse_thermal_zones(void) { return 0; }
|
||||
static inline void of_thermal_destroy_zones(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* __THERMAL_CORE_H__ */
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "ti-thermal.h"
|
||||
#include "ti-bandgap.h"
|
||||
@ -44,6 +45,7 @@ struct ti_thermal_data {
|
||||
enum thermal_device_mode mode;
|
||||
struct work_struct thermal_wq;
|
||||
int sensor_id;
|
||||
bool our_zone;
|
||||
};
|
||||
|
||||
static void ti_thermal_work(struct work_struct *work)
|
||||
@ -75,11 +77,10 @@ static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
|
||||
|
||||
/* thermal zone ops */
|
||||
/* Get temperature callback function for thermal zone*/
|
||||
static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
static inline int __ti_thermal_get_temp(void *devdata, long *temp)
|
||||
{
|
||||
struct thermal_zone_device *pcb_tz = NULL;
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
struct ti_thermal_data *data = devdata;
|
||||
struct ti_bandgap *bgp;
|
||||
const struct ti_temp_sensor *s;
|
||||
int ret, tmp, slope, constant;
|
||||
@ -118,6 +119,14 @@ static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
|
||||
return __ti_thermal_get_temp(data, temp);
|
||||
}
|
||||
|
||||
/* Bind callback functions for thermal zone */
|
||||
static int ti_thermal_bind(struct thermal_zone_device *thermal,
|
||||
struct thermal_cooling_device *cdev)
|
||||
@ -230,11 +239,9 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the temperature trend callback functions for thermal zone */
|
||||
static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trend *trend)
|
||||
static int __ti_thermal_get_trend(void *p, long *trend)
|
||||
{
|
||||
struct ti_thermal_data *data = thermal->devdata;
|
||||
struct ti_thermal_data *data = p;
|
||||
struct ti_bandgap *bgp;
|
||||
int id, tr, ret = 0;
|
||||
|
||||
@ -245,6 +252,22 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*trend = tr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the temperature trend callback functions for thermal zone */
|
||||
static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trend *trend)
|
||||
{
|
||||
int ret;
|
||||
long tr;
|
||||
|
||||
ret = __ti_thermal_get_trend(thermal->devdata, &tr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tr > 0)
|
||||
*trend = THERMAL_TREND_RAISING;
|
||||
else if (tr < 0)
|
||||
@ -308,16 +331,23 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
/* Create thermal zone */
|
||||
data->ti_thermal = thermal_zone_device_register(domain,
|
||||
/* in case this is specified by DT */
|
||||
data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id,
|
||||
data, __ti_thermal_get_temp,
|
||||
__ti_thermal_get_trend);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
/* Create thermal zone */
|
||||
data->ti_thermal = thermal_zone_device_register(domain,
|
||||
OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
|
||||
NULL, FAST_TEMP_MONITORING_RATE,
|
||||
FAST_TEMP_MONITORING_RATE);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
dev_err(bgp->dev, "thermal zone device is NULL\n");
|
||||
return PTR_ERR(data->ti_thermal);
|
||||
if (IS_ERR(data->ti_thermal)) {
|
||||
dev_err(bgp->dev, "thermal zone device is NULL\n");
|
||||
return PTR_ERR(data->ti_thermal);
|
||||
}
|
||||
data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
|
||||
data->our_zone = true;
|
||||
}
|
||||
data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE;
|
||||
ti_bandgap_set_sensor_data(bgp, id, data);
|
||||
ti_bandgap_write_update_interval(bgp, data->sensor_id,
|
||||
data->ti_thermal->polling_delay);
|
||||
@ -331,7 +361,13 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
|
||||
thermal_zone_device_unregister(data->ti_thermal);
|
||||
if (data && data->ti_thermal) {
|
||||
if (data->our_zone)
|
||||
thermal_zone_device_unregister(data->ti_thermal);
|
||||
else
|
||||
thermal_zone_of_sensor_unregister(bgp->dev,
|
||||
data->ti_thermal);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -350,6 +386,15 @@ int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
|
||||
int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
|
||||
{
|
||||
struct ti_thermal_data *data;
|
||||
struct device_node *np = bgp->dev->of_node;
|
||||
|
||||
/*
|
||||
* We are assuming here that if one deploys the zone
|
||||
* using DT, then it must be aware that the cooling device
|
||||
* loading has to happen via cpufreq driver.
|
||||
*/
|
||||
if (of_find_property(np, "#thermal-sensor-cells", NULL))
|
||||
return 0;
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
if (!data || IS_ERR(data))
|
||||
@ -380,7 +425,9 @@ int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
|
||||
struct ti_thermal_data *data;
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
cpufreq_cooling_unregister(data->cool_dev);
|
||||
|
||||
if (data && data->cool_dev)
|
||||
cpufreq_cooling_unregister(data->cool_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
u32 l, h;
|
||||
|
17
include/dt-bindings/thermal/thermal.h
Normal file
17
include/dt-bindings/thermal/thermal.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This header provides constants for most thermal bindings.
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
* Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* GPLv2 only
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_THERMAL_THERMAL_H
|
||||
#define _DT_BINDINGS_THERMAL_THERMAL_H
|
||||
|
||||
/* On cooling devices upper and lower limits */
|
||||
#define THERMAL_NO_LIMIT (-1UL)
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,7 @@
|
||||
#ifndef __CPU_COOLING_H__
|
||||
#define __CPU_COOLING_H__
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/cpumask.h>
|
||||
|
||||
@ -35,6 +36,24 @@
|
||||
struct thermal_cooling_device *
|
||||
cpufreq_cooling_register(const struct cpumask *clip_cpus);
|
||||
|
||||
/**
|
||||
* of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
|
||||
* @np: a valid struct device_node to the cooling device device tree node.
|
||||
* @clip_cpus: cpumask of cpus where the frequency constraints will happen
|
||||
*/
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus);
|
||||
#else
|
||||
static inline struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cpufreq_cooling_unregister - function to remove cpufreq cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
@ -48,6 +67,12 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline struct thermal_cooling_device *
|
||||
of_cpufreq_cooling_register(struct device_node *np,
|
||||
const struct cpumask *clip_cpus)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline
|
||||
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
|
@ -25,6 +25,7 @@
|
||||
#ifndef __THERMAL_H__
|
||||
#define __THERMAL_H__
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
@ -143,6 +144,7 @@ struct thermal_cooling_device {
|
||||
int id;
|
||||
char type[THERMAL_NAME_LENGTH];
|
||||
struct device device;
|
||||
struct device_node *np;
|
||||
void *devdata;
|
||||
const struct thermal_cooling_device_ops *ops;
|
||||
bool updated; /* true if the cooling device does not need update */
|
||||
@ -172,7 +174,7 @@ struct thermal_zone_device {
|
||||
int emul_temperature;
|
||||
int passive;
|
||||
unsigned int forced_passive;
|
||||
const struct thermal_zone_device_ops *ops;
|
||||
struct thermal_zone_device_ops *ops;
|
||||
const struct thermal_zone_params *tzp;
|
||||
struct thermal_governor *governor;
|
||||
struct list_head thermal_instances;
|
||||
@ -242,8 +244,31 @@ struct thermal_genl_event {
|
||||
};
|
||||
|
||||
/* Function declarations */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int id,
|
||||
void *data, int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *));
|
||||
void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int id,
|
||||
void *data, int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tz)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
|
||||
void *, const struct thermal_zone_device_ops *,
|
||||
void *, struct thermal_zone_device_ops *,
|
||||
const struct thermal_zone_params *, int, int);
|
||||
void thermal_zone_device_unregister(struct thermal_zone_device *);
|
||||
|
||||
@ -256,6 +281,9 @@ void thermal_zone_device_update(struct thermal_zone_device *);
|
||||
|
||||
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
|
||||
const struct thermal_cooling_device_ops *);
|
||||
struct thermal_cooling_device *
|
||||
thermal_of_cooling_device_register(struct device_node *np, char *, void *,
|
||||
const struct thermal_cooling_device_ops *);
|
||||
void thermal_cooling_device_unregister(struct thermal_cooling_device *);
|
||||
struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
|
||||
int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
|
||||
|
Loading…
Reference in New Issue
Block a user