forked from Minki/linux
- Add support to enable/disable the thermal zones resulting on core code and
drivers cleanup (Andrzej Pietrasiewicz) - Add generic netlink support for userspace notifications: events, temperature and discovery commands (Daniel Lezcano) - Fix redundant initialization for a ret variable (Colin Ian King) - Remove the clock cooling code as it is used nowhere (Amit Kucheria) - Add the rcar_gen3_thermal's r8a774e1 support (Marian-Cristian Rotariu) - Replace all references to thermal.txt in the documentation to the corresponding yaml files (Amit Kucheria) - Add maintainer entry for the IPA (Lukasz Luba) - Add support for MSM8939 for the tsens (Shawn Guo) - Update power allocator and devfreq cooling to SPDX licensing (Lukasz Luba) - Add Cannon Lake Low Power PCH support (Sumeet Pawnikar) - Add tsensor support for V2 mediatek thermal system (Henry Yen) - Fix thermal zone lookup by ID for the core code (Thierry Reding) -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEGn3N4YVz0WNVyHskqDIjiipP6E8FAl8q7tsACgkQqDIjiipP 6E+5Rwf7BFEn5YXPvng8cmnAlgvEBc9DdT6mGSo0NpFm9MdUxXlaqvw3WWSGyqWQ +z0Ka7lmn5XyiMsVN11++Snp+79X17HzZf9SXO3glyIpAn+5prTDRhzzj0/jPrtS sEeI++DrILsKKMGVljzftLmwNJN9DkUDNcnmWmZdCDbYVEKtP9Pjf2wBjAnXj7sX JA3CkHRMwYLEQbfaKz37M11cYM+LqbDOlb6U11YWgAGGJ7d7zNYRf2/YSYPM4AN6 iE6j0E+3jIlXesULsap1AzeJaBq+wFxj1FL2TUZ8KscvRrm3AucqzNAT2M/Bc5Az XLKKzc6Gp9JfqB5KXhX2EDu7VRnDBg== =cSMN -----END PGP SIGNATURE----- Merge tag 'thermal-v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux Pull thermal updates from Daniel Lezcano: - Add support to enable/disable the thermal zones resulting on core code and drivers cleanup (Andrzej Pietrasiewicz) - Add generic netlink support for userspace notifications: events, temperature and discovery commands (Daniel Lezcano) - Fix redundant initialization for a ret variable (Colin Ian King) - Remove the clock cooling code as it is used nowhere (Amit Kucheria) - Add the rcar_gen3_thermal's r8a774e1 support (Marian-Cristian Rotariu) - Replace all references to thermal.txt in the documentation to the corresponding yaml files (Amit Kucheria) - Add maintainer entry for the IPA (Lukasz Luba) - Add support for MSM8939 for the tsens (Shawn Guo) - Update power allocator and devfreq cooling to SPDX licensing (Lukasz Luba) - Add Cannon Lake Low Power PCH support (Sumeet Pawnikar) - Add tsensor support for V2 mediatek thermal system (Henry Yen) - Fix thermal zone lookup by ID for the core code (Thierry Reding) * tag 'thermal-v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (40 commits) thermal: intel: intel_pch_thermal: Add Cannon Lake Low Power PCH support thermal: mediatek: Add tsensor support for V2 thermal system thermal: mediatek: Prepare to add support for other platforms thermal: Update power allocator and devfreq cooling to SPDX licensing MAINTAINERS: update entry to thermal governors file name prefixing thermal: core: Add thermal zone enable/disable notification thermal: qcom: tsens-v0_1: Add support for MSM8939 dt-bindings: tsens: qcom: Document MSM8939 compatible thermal: core: Fix thermal zone lookup by ID thermal: int340x: processor_thermal: fix: update Jasper Lake PCI id thermal: imx8mm: Support module autoloading thermal: ti-soc-thermal: Fix reversed condition in ti_thermal_expose_sensor() MAINTAINERS: Add maintenance information for IPA thermal: rcar_gen3_thermal: Do not shadow thcode variable dt-bindings: thermal: Get rid of thermal.txt and replace references thermal: core: Move initialization after core initcall thermal: netlink: Improve the initcall ordering net: genetlink: Move initialization to core_initcall thermal: rcar_gen3_thermal: Add r8a774e1 support thermal/drivers/clock_cooling: Remove clock_cooling code ...
This commit is contained in:
commit
96e3f3c16b
@ -102,7 +102,7 @@ Required sub-node properties:
|
||||
[0] http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/index.html
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/power/power-domain.yaml
|
||||
[3] Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
[3] Documentation/devicetree/bindings/thermal/thermal*.yaml
|
||||
[4] Documentation/devicetree/bindings/sram/sram.yaml
|
||||
[5] Documentation/devicetree/bindings/reset/reset.txt
|
||||
|
||||
|
@ -108,7 +108,7 @@ Required properties:
|
||||
|
||||
[0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
[2] Documentation/devicetree/bindings/thermal/thermal*.yaml
|
||||
[3] Documentation/devicetree/bindings/sram/sram.yaml
|
||||
[4] Documentation/devicetree/bindings/power/power-domain.yaml
|
||||
|
||||
|
@ -176,7 +176,7 @@ Required properties:
|
||||
"fsl,imx8qxp-sc-thermal"
|
||||
followed by "fsl,imx-sc-thermal";
|
||||
|
||||
- #thermal-sensor-cells: See Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
- #thermal-sensor-cells: See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml
|
||||
for a description.
|
||||
|
||||
Example (imx8qxp):
|
||||
|
@ -111,7 +111,7 @@ Thermal:
|
||||
--------
|
||||
|
||||
For common binding part and usage, refer to
|
||||
Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
Documentation/devicetree/bindings/thermal/thermal*.yaml
|
||||
|
||||
The thermal IP can probe the temperature all around the processor. It
|
||||
may feature several channels, each of them wired to one sensor.
|
||||
|
@ -203,7 +203,7 @@ It is possible to setup an overheat interrupt by giving at least one
|
||||
critical point to any subnode of the thermal-zone node.
|
||||
|
||||
For common binding part and usage, refer to
|
||||
Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
Documentation/devicetree/bindings/thermal/thermal*.yaml
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of:
|
||||
|
@ -18,7 +18,8 @@ Optional properties:
|
||||
in unit of nanoseconds.
|
||||
- voltage-tolerance: Specify the CPU voltage tolerance in percentage.
|
||||
- #cooling-cells:
|
||||
Please refer to Documentation/devicetree/bindings/thermal/thermal.txt.
|
||||
Please refer to
|
||||
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml.
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -21,8 +21,8 @@ Optional properties:
|
||||
flow is handled by hardware, hence no software "voltage tracking" is
|
||||
needed.
|
||||
- #cooling-cells:
|
||||
Please refer to Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
for detail.
|
||||
For details, please refer to
|
||||
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
|
||||
|
||||
Example 1 (MT7623 SoC):
|
||||
|
||||
|
@ -5,7 +5,7 @@ Required properties:
|
||||
- clocks: Must contain an entry for the CPU clock.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- operating-points-v2: See ../bindings/opp/opp.txt for details.
|
||||
- #cooling-cells: Should be 2. See ../thermal/thermal.txt for details.
|
||||
- #cooling-cells: Should be 2. See ../thermal/thermal-cooling-devices.yaml for details.
|
||||
|
||||
For each opp entry in 'operating-points-v2' table:
|
||||
- opp-supported-hw: Two bitfields indicating:
|
||||
|
@ -12,7 +12,8 @@ Optional properties:
|
||||
- alarm-gpios: This pin going active indicates something is wrong with
|
||||
the fan, and a udev event will be fired.
|
||||
- #cooling-cells: If used as a cooling device, must be <2>
|
||||
Also see: Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
Also see:
|
||||
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
|
||||
min and max states are derived from the speed-map of the fan.
|
||||
|
||||
Note: At least one the "gpios" or "alarm-gpios" properties must be set.
|
||||
|
@ -34,8 +34,8 @@ Optional properties:
|
||||
LM90 "-ALERT" pin output.
|
||||
See interrupt-controller/interrupts.txt for the format.
|
||||
|
||||
- #thermal-sensor-cells: should be set to 1. See thermal/thermal.txt for
|
||||
details. See <include/dt-bindings/thermal/lm90.h> for the
|
||||
- #thermal-sensor-cells: should be set to 1. See thermal/thermal-sensor.yaml
|
||||
for details. See <include/dt-bindings/thermal/lm90.h> for the
|
||||
definition of the local, remote and 2nd remote sensor index
|
||||
constants.
|
||||
|
||||
|
@ -50,7 +50,7 @@ properties:
|
||||
nvmem-cell-names:
|
||||
const: calibration
|
||||
|
||||
# See ./thermal.txt for details
|
||||
# See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for details
|
||||
"#thermal-sensor-cells":
|
||||
enum:
|
||||
- 0
|
||||
|
@ -6,7 +6,7 @@ transaction.
|
||||
Required properties:
|
||||
- compatible: "amazon,al-thermal".
|
||||
- reg: The physical base address and length of the sensor's registers.
|
||||
- #thermal-sensor-cells: Must be 1. See ./thermal.txt for a description.
|
||||
- #thermal-sensor-cells: Must be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
|
||||
|
||||
Example:
|
||||
thermal: thermal {
|
||||
|
@ -23,7 +23,7 @@ properties:
|
||||
compatible:
|
||||
const: brcm,bcm2711-thermal
|
||||
|
||||
# See ./thermal.txt for details
|
||||
# See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for details
|
||||
"#thermal-sensor-cells":
|
||||
const: 0
|
||||
|
||||
|
@ -7,7 +7,7 @@ compatible: should be one of: "brcm,bcm2835-thermal",
|
||||
"brcm,bcm2836-thermal" or "brcm,bcm2837-thermal"
|
||||
reg: Address range of the thermal registers.
|
||||
clocks: Phandle of the clock used by the thermal sensor.
|
||||
#thermal-sensor-cells: should be 0 (see thermal.txt)
|
||||
#thermal-sensor-cells: should be 0 (see Documentation/devicetree/bindings/thermal/thermal-sensor.yaml)
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
by /SOCTHERM/tsensor.
|
||||
- clock-names: Input clock name, should be 'thermal_clk'.
|
||||
- clocks: phandles for clock specified in "clock-names" property.
|
||||
- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description.
|
||||
- #thermal-sensor-cells: Should be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
|
||||
|
||||
Example :
|
||||
|
||||
|
@ -8,12 +8,12 @@ below threshold level.
|
||||
|
||||
Required properties:
|
||||
-------------------
|
||||
#thermal-sensor-cells: Please refer <devicetree/bindings/thermal/thermal.txt>
|
||||
for more details.
|
||||
#thermal-sensor-cells: For more details, please refer to
|
||||
<devicetree/bindings/thermal/thermal-sensor.yaml>
|
||||
The value must be 0.
|
||||
|
||||
For more details, please refer generic thermal DT binding document
|
||||
<devicetree/bindings/thermal/thermal.txt>.
|
||||
<devicetree/bindings/thermal/thermal*.yaml>.
|
||||
|
||||
Please refer <devicetree/bindings/mfd/max77620.txt> for mfd DT binding
|
||||
document for the MAX77620.
|
||||
|
@ -23,7 +23,7 @@ Required properties:
|
||||
- resets: Reference to the reset controller controlling the thermal controller.
|
||||
- mediatek,auxadc: A phandle to the AUXADC which the thermal controller uses
|
||||
- mediatek,apmixedsys: A phandle to the APMIXEDSYS controller.
|
||||
- #thermal-sensor-cells : Should be 0. See ./thermal.txt for a description.
|
||||
- #thermal-sensor-cells : Should be 0. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
|
||||
|
||||
Optional properties:
|
||||
- nvmem-cells: A phandle to the calibration data provided by a nvmem device. If
|
||||
|
@ -28,9 +28,10 @@ Required properties :
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names : Must include the following entries:
|
||||
- soctherm
|
||||
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description
|
||||
of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
|
||||
list of valid values when referring to thermal sensors.
|
||||
- #thermal-sensor-cells : Should be 1. For a description of this property, see
|
||||
Documentation/devicetree/bindings/thermal/thermal-sensor.yaml.
|
||||
See <dt-bindings/thermal/tegra124-soctherm.h> for a list of valid values
|
||||
when referring to thermal sensors.
|
||||
- throttle-cfgs: A sub-node which is a container of configuration for each
|
||||
hardware throttle events. These events can be set as cooling devices.
|
||||
* throttle events: Sub-nodes must be named as "light" or "heavy".
|
||||
@ -62,7 +63,8 @@ Required properties :
|
||||
TEGRA_SOCTHERM_THROT_LEVEL_MED (75%),
|
||||
TEGRA_SOCTHERM_THROT_LEVEL_HIGH (85%).
|
||||
- #cooling-cells: Should be 1. This cooling device only support on/off state.
|
||||
See ./thermal.txt for a description of this property.
|
||||
For a description of this property see:
|
||||
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
|
||||
|
||||
Optional properties: The following properties are T210 specific and
|
||||
valid only for OCx throttle events.
|
||||
|
@ -8,7 +8,7 @@ exposed by BPMP.
|
||||
The BPMP thermal node must be located directly inside the main BPMP node. See
|
||||
../firmware/nvidia,tegra186-bpmp.txt for details of the BPMP binding.
|
||||
|
||||
This node represents a thermal sensor. See thermal.txt for details of the
|
||||
This node represents a thermal sensor. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for details of the
|
||||
core thermal binding.
|
||||
|
||||
Required properties:
|
||||
|
@ -8,7 +8,7 @@ Required properties:
|
||||
- compatible: Should contain "qcom,spmi-temp-alarm".
|
||||
- reg: Specifies the SPMI address.
|
||||
- interrupts: PMIC temperature alarm interrupt.
|
||||
- #thermal-sensor-cells: Should be 0. See thermal.txt for a description.
|
||||
- #thermal-sensor-cells: Should be 0. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
|
||||
|
||||
Optional properties:
|
||||
- io-channels: Should contain IIO channel specifier for the ADC channel,
|
||||
|
@ -23,6 +23,7 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,msm8916-tsens
|
||||
- qcom,msm8939-tsens
|
||||
- qcom,msm8974-tsens
|
||||
- const: qcom,tsens-v0_1
|
||||
|
||||
|
@ -24,7 +24,7 @@ Required properties:
|
||||
- pinctrl-1 : The "default" pinctrl state, it will be set after reset the
|
||||
TSADC controller.
|
||||
- pinctrl-2 : The "sleep" pinctrl state, it will be in for suspend.
|
||||
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
|
||||
- #thermal-sensor-cells : Should be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
|
||||
|
||||
Optional properties:
|
||||
- rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value.
|
||||
|
@ -4,7 +4,7 @@ The SMP8758 SoC includes 3 instances of this temperature sensor
|
||||
(in the CPU, video decoder, and PCIe controller).
|
||||
|
||||
Required properties:
|
||||
- #thermal-sensor-cells: Should be 0 (see thermal.txt)
|
||||
- #thermal-sensor-cells: Should be 0 (see Documentation/devicetree/bindings/thermal/thermal-sensor.yaml)
|
||||
- compatible: "sigma,smp8758-thermal"
|
||||
- reg: Address range of the thermal registers
|
||||
|
||||
|
@ -8,7 +8,7 @@ temperature using voltage-temperature lookup table.
|
||||
Required properties:
|
||||
===================
|
||||
- compatible: Must be "generic-adc-thermal".
|
||||
- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description
|
||||
- #thermal-sensor-cells: Should be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description
|
||||
of this property.
|
||||
Optional properties:
|
||||
===================
|
||||
|
@ -1,586 +0,0 @@
|
||||
* 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
|
||||
should be defined in a particular device's binding.
|
||||
For more examples of cooling devices, refer to the example sections below.
|
||||
|
||||
Required properties:
|
||||
- #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 list of phandles of cooling devices with their specifiers,
|
||||
Type: phandle + referring to which cooling devices are 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.
|
||||
|
||||
Optional property:
|
||||
- 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.
|
||||
|
||||
- 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.
|
||||
|
||||
- sustainable-power: An estimate of the sustainable power (in mW) that the
|
||||
Type: unsigned thermal zone can dissipate at the desired
|
||||
Size: one cell control temperature. For reference, the
|
||||
sustainable power of a 4'' phone is typically
|
||||
2000mW, while on a 10'' tablet is around
|
||||
4500mW.
|
||||
|
||||
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-cells = <2>; /* min followed by max */
|
||||
};
|
||||
...
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
...
|
||||
/*
|
||||
* A simple fan controller which supports 10 speeds of operation
|
||||
* (represented as 0-9).
|
||||
*/
|
||||
fan0: fan@48 {
|
||||
...
|
||||
#cooling-cells = <2>; /* min followed by max */
|
||||
};
|
||||
};
|
||||
|
||||
ocp {
|
||||
...
|
||||
/*
|
||||
* A simple IC with a single bandgap temperature sensor.
|
||||
*/
|
||||
bandgap0: bandgap@0000ed00 {
|
||||
...
|
||||
#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-alert0 {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "active";
|
||||
};
|
||||
cpu_alert1: cpu-alert1 {
|
||||
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_LIMIT 4>;
|
||||
};
|
||||
map1 {
|
||||
trip = <&cpu_alert1>;
|
||||
cooling-device = <&fan0 5 THERMAL_NO_LIMIT>, <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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@0000ed00 {
|
||||
...
|
||||
#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: dsp-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@49 {
|
||||
...
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
ocp {
|
||||
...
|
||||
/*
|
||||
* A simple IC with a single bandgap temperature sensor.
|
||||
*/
|
||||
bandgap0: bandgap@0000ed00 {
|
||||
...
|
||||
#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@50 {
|
||||
...
|
||||
#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_dummy 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>;
|
||||
|
||||
sustainable-power = <2500>;
|
||||
|
||||
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.
|
@ -17086,6 +17086,14 @@ F: drivers/thermal/cpufreq_cooling.c
|
||||
F: drivers/thermal/cpuidle_cooling.c
|
||||
F: include/linux/cpu_cooling.h
|
||||
|
||||
THERMAL/POWER_ALLOCATOR
|
||||
M: Lukasz Luba <lukasz.luba@arm.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/driver-api/thermal/power_allocator.rst
|
||||
F: drivers/thermal/gov_power_allocator.c
|
||||
F: include/trace/events/thermal_power_allocator.h
|
||||
|
||||
THINKPAD ACPI EXTRAS DRIVER
|
||||
M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
|
||||
L: ibm-acpi-devel@lists.sourceforge.net
|
||||
|
@ -172,7 +172,6 @@ struct acpi_thermal {
|
||||
struct acpi_thermal_trips trips;
|
||||
struct acpi_handle_list devices;
|
||||
struct thermal_zone_device *thermal_zone;
|
||||
int tz_enabled;
|
||||
int kelvin_offset; /* in millidegrees */
|
||||
struct work_struct thermal_check_work;
|
||||
};
|
||||
@ -500,9 +499,6 @@ static void acpi_thermal_check(void *data)
|
||||
{
|
||||
struct acpi_thermal *tz = data;
|
||||
|
||||
if (!tz->tz_enabled)
|
||||
return;
|
||||
|
||||
thermal_zone_device_update(tz->thermal_zone,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
@ -526,50 +522,6 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_get_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
*mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
|
||||
THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_set_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct acpi_thermal *tz = thermal->devdata;
|
||||
int enable;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* enable/disable thermal management from ACPI thermal driver
|
||||
*/
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
enable = 1;
|
||||
else if (mode == THERMAL_DEVICE_DISABLED) {
|
||||
enable = 0;
|
||||
pr_warn("thermal zone will be disabled\n");
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if (enable != tz->tz_enabled) {
|
||||
tz->tz_enabled = enable;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"%s kernel ACPI thermal control\n",
|
||||
tz->tz_enabled ? "Enable" : "Disable"));
|
||||
acpi_thermal_check(tz);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
@ -856,8 +808,6 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
|
||||
.bind = acpi_thermal_bind_cooling_device,
|
||||
.unbind = acpi_thermal_unbind_cooling_device,
|
||||
.get_temp = thermal_get_temp,
|
||||
.get_mode = thermal_get_mode,
|
||||
.set_mode = thermal_set_mode,
|
||||
.get_trip_type = thermal_get_trip_type,
|
||||
.get_trip_temp = thermal_get_trip_temp,
|
||||
.get_crit_temp = thermal_get_crit_temp,
|
||||
@ -901,23 +851,39 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
|
||||
result = sysfs_create_link(&tz->device->dev.kobj,
|
||||
&tz->thermal_zone->device.kobj, "thermal_zone");
|
||||
if (result)
|
||||
return result;
|
||||
goto unregister_tzd;
|
||||
|
||||
result = sysfs_create_link(&tz->thermal_zone->device.kobj,
|
||||
&tz->device->dev.kobj, "device");
|
||||
if (result)
|
||||
return result;
|
||||
goto remove_tz_link;
|
||||
|
||||
status = acpi_bus_attach_private_data(tz->device->handle,
|
||||
tz->thermal_zone);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
if (ACPI_FAILURE(status)) {
|
||||
result = -ENODEV;
|
||||
goto remove_dev_link;
|
||||
}
|
||||
|
||||
tz->tz_enabled = 1;
|
||||
result = thermal_zone_device_enable(tz->thermal_zone);
|
||||
if (result)
|
||||
goto acpi_bus_detach;
|
||||
|
||||
dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
|
||||
tz->thermal_zone->id);
|
||||
|
||||
return 0;
|
||||
|
||||
acpi_bus_detach:
|
||||
acpi_bus_detach_private_data(tz->device->handle);
|
||||
remove_dev_link:
|
||||
sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
|
||||
remove_tz_link:
|
||||
sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
|
||||
unregister_tzd:
|
||||
thermal_zone_device_unregister(tz->thermal_zone);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
|
||||
|
@ -92,6 +92,14 @@ int cxgb4_thermal_init(struct adapter *adap)
|
||||
ch_thermal->tzdev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = thermal_zone_device_enable(ch_thermal->tzdev);
|
||||
if (ret) {
|
||||
dev_err(adap->pdev_dev, "Failed to enable thermal zone\n");
|
||||
thermal_zone_device_unregister(adap->ch_thermal.tzdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,6 @@ struct mlxsw_thermal_module {
|
||||
struct mlxsw_thermal *parent;
|
||||
struct thermal_zone_device *tzdev;
|
||||
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
enum thermal_device_mode mode;
|
||||
int module; /* Module or gearbox number */
|
||||
};
|
||||
|
||||
@ -110,7 +109,6 @@ struct mlxsw_thermal {
|
||||
struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
|
||||
u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
|
||||
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
enum thermal_device_mode mode;
|
||||
struct mlxsw_thermal_module *tz_module_arr;
|
||||
u8 tz_module_num;
|
||||
struct mlxsw_thermal_module *tz_gearbox_arr;
|
||||
@ -277,36 +275,6 @@ static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_mode(struct thermal_zone_device *tzdev,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
*mode = thermal->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
mutex_lock(&tzdev->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
tzdev->polling_delay = thermal->polling_delay;
|
||||
else
|
||||
tzdev->polling_delay = 0;
|
||||
|
||||
mutex_unlock(&tzdev->lock);
|
||||
|
||||
thermal->mode = mode;
|
||||
thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev,
|
||||
int *p_temp)
|
||||
{
|
||||
@ -406,8 +374,6 @@ static int mlxsw_thermal_trend_get(struct thermal_zone_device *tzdev,
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_ops = {
|
||||
.bind = mlxsw_thermal_bind,
|
||||
.unbind = mlxsw_thermal_unbind,
|
||||
.get_mode = mlxsw_thermal_get_mode,
|
||||
.set_mode = mlxsw_thermal_set_mode,
|
||||
.get_temp = mlxsw_thermal_get_temp,
|
||||
.get_trip_type = mlxsw_thermal_get_trip_type,
|
||||
.get_trip_temp = mlxsw_thermal_get_trip_temp,
|
||||
@ -465,37 +431,6 @@ static int mlxsw_thermal_module_unbind(struct thermal_zone_device *tzdev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_module_mode_get(struct thermal_zone_device *tzdev,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
*mode = tz->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_module_mode_set(struct thermal_zone_device *tzdev,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
struct mlxsw_thermal *thermal = tz->parent;
|
||||
|
||||
mutex_lock(&tzdev->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
tzdev->polling_delay = thermal->polling_delay;
|
||||
else
|
||||
tzdev->polling_delay = 0;
|
||||
|
||||
mutex_unlock(&tzdev->lock);
|
||||
|
||||
tz->mode = mode;
|
||||
thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
|
||||
int *p_temp)
|
||||
{
|
||||
@ -611,8 +546,6 @@ static int mlxsw_thermal_module_trend_get(struct thermal_zone_device *tzdev,
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.get_mode = mlxsw_thermal_module_mode_get,
|
||||
.set_mode = mlxsw_thermal_module_mode_set,
|
||||
.get_temp = mlxsw_thermal_module_temp_get,
|
||||
.get_trip_type = mlxsw_thermal_module_trip_type_get,
|
||||
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
|
||||
@ -650,8 +583,6 @@ static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev,
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.get_mode = mlxsw_thermal_module_mode_get,
|
||||
.set_mode = mlxsw_thermal_module_mode_set,
|
||||
.get_temp = mlxsw_thermal_gearbox_temp_get,
|
||||
.get_trip_type = mlxsw_thermal_module_trip_type_get,
|
||||
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
|
||||
@ -780,8 +711,11 @@ mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz)
|
||||
return err;
|
||||
}
|
||||
|
||||
module_tz->mode = THERMAL_DEVICE_ENABLED;
|
||||
return 0;
|
||||
err = thermal_zone_device_enable(module_tz->tzdev);
|
||||
if (err)
|
||||
thermal_zone_device_unregister(module_tz->tzdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlxsw_thermal_module_tz_fini(struct thermal_zone_device *tzdev)
|
||||
@ -884,6 +818,7 @@ static int
|
||||
mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
|
||||
{
|
||||
char tz_name[MLXSW_THERMAL_ZONE_MAX_NAME];
|
||||
int ret;
|
||||
|
||||
snprintf(tz_name, sizeof(tz_name), "mlxsw-gearbox%d",
|
||||
gearbox_tz->module + 1);
|
||||
@ -896,8 +831,11 @@ mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
|
||||
if (IS_ERR(gearbox_tz->tzdev))
|
||||
return PTR_ERR(gearbox_tz->tzdev);
|
||||
|
||||
gearbox_tz->mode = THERMAL_DEVICE_ENABLED;
|
||||
return 0;
|
||||
ret = thermal_zone_device_enable(gearbox_tz->tzdev);
|
||||
if (ret)
|
||||
thermal_zone_device_unregister(gearbox_tz->tzdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1065,10 +1003,15 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
|
||||
if (err)
|
||||
goto err_unreg_modules_tzdev;
|
||||
|
||||
thermal->mode = THERMAL_DEVICE_ENABLED;
|
||||
err = thermal_zone_device_enable(thermal->tzdev);
|
||||
if (err)
|
||||
goto err_unreg_gearboxes;
|
||||
|
||||
*p_thermal = thermal;
|
||||
return 0;
|
||||
|
||||
err_unreg_gearboxes:
|
||||
mlxsw_thermal_gearboxes_fini(thermal);
|
||||
err_unreg_modules_tzdev:
|
||||
mlxsw_thermal_modules_fini(thermal);
|
||||
err_unreg_tzdev:
|
||||
|
@ -733,7 +733,7 @@ static struct thermal_zone_device_ops tzone_ops = {
|
||||
|
||||
static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
|
||||
{
|
||||
int i;
|
||||
int i, ret;
|
||||
char name[16];
|
||||
static atomic_t counter = ATOMIC_INIT(0);
|
||||
|
||||
@ -759,6 +759,13 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = thermal_zone_device_enable(mvm->tz_device.tzone);
|
||||
if (ret) {
|
||||
IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
|
||||
thermal_zone_device_unregister(mvm->tz_device.tzone);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 0 is a valid temperature,
|
||||
* so initialize the array with S16_MIN which invalid temperature
|
||||
*/
|
||||
|
@ -397,39 +397,24 @@ static inline void acerhdf_revert_to_bios_mode(void)
|
||||
{
|
||||
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
|
||||
kernelmode = 0;
|
||||
if (thz_dev)
|
||||
thz_dev->polling_delay = 0;
|
||||
|
||||
pr_notice("kernel mode fan control OFF\n");
|
||||
}
|
||||
static inline void acerhdf_enable_kernelmode(void)
|
||||
{
|
||||
kernelmode = 1;
|
||||
|
||||
thz_dev->polling_delay = interval*1000;
|
||||
thermal_zone_device_update(thz_dev, THERMAL_EVENT_UNSPECIFIED);
|
||||
pr_notice("kernel mode fan control ON\n");
|
||||
}
|
||||
|
||||
static int acerhdf_get_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
if (verbose)
|
||||
pr_notice("kernel mode fan control %d\n", kernelmode);
|
||||
|
||||
*mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
|
||||
: THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* set operation mode;
|
||||
* enabled: the thermal layer of the kernel takes care about
|
||||
* the temperature and the fan.
|
||||
* disabled: the BIOS takes control of the fan.
|
||||
*/
|
||||
static int acerhdf_set_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
static int acerhdf_change_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
|
||||
acerhdf_revert_to_bios_mode();
|
||||
@ -488,8 +473,7 @@ static struct thermal_zone_device_ops acerhdf_dev_ops = {
|
||||
.bind = acerhdf_bind,
|
||||
.unbind = acerhdf_unbind,
|
||||
.get_temp = acerhdf_get_ec_temp,
|
||||
.get_mode = acerhdf_get_mode,
|
||||
.set_mode = acerhdf_set_mode,
|
||||
.change_mode = acerhdf_change_mode,
|
||||
.get_trip_type = acerhdf_get_trip_type,
|
||||
.get_trip_hyst = acerhdf_get_trip_hyst,
|
||||
.get_trip_temp = acerhdf_get_trip_temp,
|
||||
@ -733,6 +717,8 @@ static void acerhdf_unregister_platform(void)
|
||||
|
||||
static int __init acerhdf_register_thermal(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
|
||||
&acerhdf_cooling_ops);
|
||||
|
||||
@ -746,6 +732,13 @@ static int __init acerhdf_register_thermal(void)
|
||||
if (IS_ERR(thz_dev))
|
||||
return -EINVAL;
|
||||
|
||||
if (kernelmode)
|
||||
ret = thermal_zone_device_enable(thz_dev);
|
||||
else
|
||||
ret = thermal_zone_device_disable(thz_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (strcmp(thz_dev->governor->name,
|
||||
acerhdf_zone_params.governor_name)) {
|
||||
pr_err("Didn't get thermal governor %s, perhaps not compiled into thermal subsystem.\n",
|
||||
|
@ -493,6 +493,12 @@ static int mid_thermal_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(pinfo->tzd[i]);
|
||||
goto err;
|
||||
}
|
||||
ret = thermal_zone_device_enable(pinfo->tzd[i]);
|
||||
if (ret) {
|
||||
kfree(td_info);
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
pinfo->pdev = pdev;
|
||||
|
@ -939,7 +939,7 @@ static struct thermal_zone_device_ops psy_tzd_ops = {
|
||||
|
||||
static int psy_register_thermal(struct power_supply *psy)
|
||||
{
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
if (psy->desc->no_thermal)
|
||||
return 0;
|
||||
@ -949,7 +949,12 @@ static int psy_register_thermal(struct power_supply *psy)
|
||||
if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) {
|
||||
psy->tzd = thermal_zone_device_register(psy->desc->name,
|
||||
0, 0, psy, &psy_tzd_ops, NULL, 0, 0);
|
||||
return PTR_ERR_OR_ZERO(psy->tzd);
|
||||
if (IS_ERR(psy->tzd))
|
||||
return PTR_ERR(psy->tzd);
|
||||
ret = thermal_zone_device_enable(psy->tzd);
|
||||
if (ret)
|
||||
thermal_zone_device_unregister(psy->tzd);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -17,6 +17,16 @@ menuconfig THERMAL
|
||||
|
||||
if THERMAL
|
||||
|
||||
config THERMAL_NETLINK
|
||||
bool "Thermal netlink management"
|
||||
depends on NET
|
||||
default y
|
||||
help
|
||||
The thermal framework has a netlink interface to do thermal
|
||||
zones discovery, temperature readings and events such as
|
||||
trip point crossed, cooling device update or governor
|
||||
change. It is recommended to enable the feature.
|
||||
|
||||
config THERMAL_STATISTICS
|
||||
bool "Thermal state transition statistics"
|
||||
help
|
||||
@ -180,16 +190,6 @@ config CPU_IDLE_THERMAL
|
||||
idle cycle.
|
||||
endif
|
||||
|
||||
config CLOCK_THERMAL
|
||||
bool "Generic clock cooling support"
|
||||
depends on COMMON_CLK
|
||||
depends on PM_OPP
|
||||
help
|
||||
This entry implements the generic clock cooling mechanism through
|
||||
frequency clipping. Typically used to cool off co-processors. The
|
||||
device that is configured to use this cooling mechanism will be
|
||||
controlled to reduce clock frequency whenever temperature is high.
|
||||
|
||||
config DEVFREQ_THERMAL
|
||||
bool "Generic device cooling support"
|
||||
depends on PM_DEVFREQ
|
||||
|
@ -7,6 +7,9 @@ obj-$(CONFIG_THERMAL) += thermal_sys.o
|
||||
thermal_sys-y += thermal_core.o thermal_sysfs.o \
|
||||
thermal_helpers.o
|
||||
|
||||
# netlink interface to manage the thermal framework
|
||||
thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
|
||||
|
||||
# interface to/from other layers providing sensors
|
||||
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
|
||||
thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
|
||||
@ -22,9 +25,6 @@ thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += gov_power_allocator.o
|
||||
thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o
|
||||
thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o
|
||||
|
||||
# clock cooling
|
||||
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
|
||||
|
||||
# devfreq cooling
|
||||
thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
|
||||
|
||||
|
@ -874,6 +874,12 @@ static int armada_thermal_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(tz);
|
||||
}
|
||||
|
||||
ret = thermal_zone_device_enable(tz);
|
||||
if (ret) {
|
||||
thermal_zone_device_unregister(tz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->type = LEGACY;
|
||||
drvdata->data.tz = tz;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
@ -1,445 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* drivers/thermal/clock_cooling.c
|
||||
*
|
||||
* Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Inc.
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* Highly based on cpufreq_cooling.c.
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clock_cooling.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/**
|
||||
* struct clock_cooling_device - data for cooling device with clock
|
||||
* @id: unique integer value corresponding to each clock_cooling_device
|
||||
* registered.
|
||||
* @dev: struct device pointer to the device being used to cool off using
|
||||
* clock frequencies.
|
||||
* @cdev: thermal_cooling_device pointer to keep track of the
|
||||
* registered cooling device.
|
||||
* @clk_rate_change_nb: reference to notifier block used to receive clock
|
||||
* rate changes.
|
||||
* @freq_table: frequency table used to keep track of available frequencies.
|
||||
* @clock_state: integer value representing the current state of clock
|
||||
* cooling devices.
|
||||
* @clock_val: integer value representing the absolute value of the clipped
|
||||
* frequency.
|
||||
* @clk: struct clk reference used to enforce clock limits.
|
||||
* @lock: mutex lock to protect this struct.
|
||||
*
|
||||
* This structure is required for keeping information of each
|
||||
* clock_cooling_device registered. In order to prevent corruption of this a
|
||||
* mutex @lock is used.
|
||||
*/
|
||||
struct clock_cooling_device {
|
||||
int id;
|
||||
struct device *dev;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct notifier_block clk_rate_change_nb;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
unsigned long clock_state;
|
||||
unsigned long clock_val;
|
||||
struct clk *clk;
|
||||
struct mutex lock; /* lock to protect the content of this struct */
|
||||
};
|
||||
#define to_clock_cooling_device(x) \
|
||||
container_of(x, struct clock_cooling_device, clk_rate_change_nb)
|
||||
static DEFINE_IDA(clock_ida);
|
||||
|
||||
/* Below code defines functions to be used for clock as cooling device */
|
||||
|
||||
enum clock_cooling_property {
|
||||
GET_LEVEL,
|
||||
GET_FREQ,
|
||||
GET_MAXL,
|
||||
};
|
||||
|
||||
/**
|
||||
* clock_cooling_get_property - fetch a property of interest for a give cpu.
|
||||
* @ccdev: clock cooling device reference
|
||||
* @input: query parameter
|
||||
* @output: query return
|
||||
* @property: type of query (frequency, level, max level)
|
||||
*
|
||||
* This is the common function to
|
||||
* 1. get maximum clock cooling states
|
||||
* 2. translate frequency to cooling state
|
||||
* 3. translate cooling state to frequency
|
||||
* Note that the code may be not in good shape
|
||||
* but it is written in this way in order to:
|
||||
* a) reduce duplicate code as most of the code can be shared.
|
||||
* b) make sure the logic is consistent when translating between
|
||||
* cooling states and frequencies.
|
||||
*
|
||||
* Return: 0 on success, -EINVAL when invalid parameters are passed.
|
||||
*/
|
||||
static int clock_cooling_get_property(struct clock_cooling_device *ccdev,
|
||||
unsigned long input,
|
||||
unsigned long *output,
|
||||
enum clock_cooling_property property)
|
||||
{
|
||||
int i;
|
||||
unsigned long max_level = 0, level = 0;
|
||||
unsigned int freq = CPUFREQ_ENTRY_INVALID;
|
||||
int descend = -1;
|
||||
struct cpufreq_frequency_table *pos, *table = ccdev->freq_table;
|
||||
|
||||
if (!output)
|
||||
return -EINVAL;
|
||||
|
||||
if (!table)
|
||||
return -EINVAL;
|
||||
|
||||
cpufreq_for_each_valid_entry(pos, table) {
|
||||
/* ignore duplicate entry */
|
||||
if (freq == pos->frequency)
|
||||
continue;
|
||||
|
||||
/* get the frequency order */
|
||||
if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
|
||||
descend = freq > pos->frequency;
|
||||
|
||||
freq = pos->frequency;
|
||||
max_level++;
|
||||
}
|
||||
|
||||
/* No valid cpu frequency entry */
|
||||
if (max_level == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* max_level is an index, not a counter */
|
||||
max_level--;
|
||||
|
||||
/* get max level */
|
||||
if (property == GET_MAXL) {
|
||||
*output = max_level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (property == GET_FREQ)
|
||||
level = descend ? input : (max_level - input);
|
||||
|
||||
i = 0;
|
||||
cpufreq_for_each_valid_entry(pos, table) {
|
||||
/* ignore duplicate entry */
|
||||
if (freq == pos->frequency)
|
||||
continue;
|
||||
|
||||
/* now we have a valid frequency entry */
|
||||
freq = pos->frequency;
|
||||
|
||||
if (property == GET_LEVEL && (unsigned int)input == freq) {
|
||||
/* get level by frequency */
|
||||
*output = descend ? i : (max_level - i);
|
||||
return 0;
|
||||
}
|
||||
if (property == GET_FREQ && level == i) {
|
||||
/* get frequency by level */
|
||||
*output = freq;
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_get_level - return the cooling level of given clock cooling.
|
||||
* @cdev: reference of a thermal cooling device of used as clock cooling device
|
||||
* @freq: the frequency of interest
|
||||
*
|
||||
* This function will match the cooling level corresponding to the
|
||||
* requested @freq and return it.
|
||||
*
|
||||
* Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
|
||||
* otherwise.
|
||||
*/
|
||||
unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
|
||||
unsigned long freq)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
unsigned long val;
|
||||
|
||||
if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val,
|
||||
GET_LEVEL))
|
||||
return THERMAL_CSTATE_INVALID;
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_get_level);
|
||||
|
||||
/**
|
||||
* clock_cooling_get_frequency - get the absolute value of frequency from level.
|
||||
* @ccdev: clock cooling device reference
|
||||
* @level: cooling level
|
||||
*
|
||||
* This function matches cooling level with frequency. Based on a cooling level
|
||||
* of frequency, equals cooling state of cpu cooling device, it will return
|
||||
* the corresponding frequency.
|
||||
* e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
|
||||
*
|
||||
* Return: 0 on error, the corresponding frequency otherwise.
|
||||
*/
|
||||
static unsigned long
|
||||
clock_cooling_get_frequency(struct clock_cooling_device *ccdev,
|
||||
unsigned long level)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long freq;
|
||||
|
||||
ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_apply - function to apply frequency clipping.
|
||||
* @ccdev: clock_cooling_device pointer containing frequency clipping data.
|
||||
* @cooling_state: value of the cooling state.
|
||||
*
|
||||
* Function used to make sure the clock layer is aware of current thermal
|
||||
* limits. The limits are applied by updating the clock rate in case it is
|
||||
* higher than the corresponding frequency based on the requested cooling_state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise (-EINVAL in case wrong
|
||||
* cooling state).
|
||||
*/
|
||||
static int clock_cooling_apply(struct clock_cooling_device *ccdev,
|
||||
unsigned long cooling_state)
|
||||
{
|
||||
unsigned long clip_freq, cur_freq;
|
||||
int ret = 0;
|
||||
|
||||
/* Here we write the clipping */
|
||||
/* Check if the old cooling action is same as new cooling action */
|
||||
if (ccdev->clock_state == cooling_state)
|
||||
return 0;
|
||||
|
||||
clip_freq = clock_cooling_get_frequency(ccdev, cooling_state);
|
||||
if (!clip_freq)
|
||||
return -EINVAL;
|
||||
|
||||
cur_freq = clk_get_rate(ccdev->clk);
|
||||
|
||||
mutex_lock(&ccdev->lock);
|
||||
ccdev->clock_state = cooling_state;
|
||||
ccdev->clock_val = clip_freq;
|
||||
/* enforce clock level */
|
||||
if (cur_freq > clip_freq)
|
||||
ret = clk_set_rate(ccdev->clk, clip_freq);
|
||||
mutex_unlock(&ccdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_clock_notifier - notifier callback on clock rate changes.
|
||||
* @nb: struct notifier_block * with callback info.
|
||||
* @event: value showing clock event for which this function invoked.
|
||||
* @data: callback-specific data
|
||||
*
|
||||
* Callback to hijack the notification on clock transition.
|
||||
* Every time there is a clock change, we intercept all pre change events
|
||||
* and block the transition in case the new rate infringes thermal limits.
|
||||
*
|
||||
* Return: NOTIFY_DONE (success) or NOTIFY_BAD (new_rate > thermal limit).
|
||||
*/
|
||||
static int clock_cooling_clock_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct clock_cooling_device *ccdev = to_clock_cooling_device(nb);
|
||||
|
||||
switch (event) {
|
||||
case PRE_RATE_CHANGE:
|
||||
/*
|
||||
* checks on current state
|
||||
* TODO: current method is not best we can find as it
|
||||
* allows possibly voltage transitions, in case DVFS
|
||||
* layer is also hijacking clock pre notifications.
|
||||
*/
|
||||
if (ndata->new_rate > ccdev->clock_val)
|
||||
return NOTIFY_BAD;
|
||||
/* fall through */
|
||||
case POST_RATE_CHANGE:
|
||||
case ABORT_RATE_CHANGE:
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* clock cooling device thermal callback functions are defined below */
|
||||
|
||||
/**
|
||||
* clock_cooling_get_max_state - callback function to get the max cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the max cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the clock
|
||||
* max cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
unsigned long count = 0;
|
||||
int ret;
|
||||
|
||||
ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL);
|
||||
if (!ret)
|
||||
*state = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_get_cur_state - function to get the current cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the clock
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 (success)
|
||||
*/
|
||||
static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
|
||||
*state = ccdev->clock_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_set_cur_state - function to set the current cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: set this variable to the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to change the clock cooling
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct clock_cooling_device *clock_device = cdev->devdata;
|
||||
|
||||
return clock_cooling_apply(clock_device, state);
|
||||
}
|
||||
|
||||
/* Bind clock callbacks to thermal cooling device ops */
|
||||
static struct thermal_cooling_device_ops const clock_cooling_ops = {
|
||||
.get_max_state = clock_cooling_get_max_state,
|
||||
.get_cur_state = clock_cooling_get_cur_state,
|
||||
.set_cur_state = clock_cooling_set_cur_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* clock_cooling_register - function to create clock cooling device.
|
||||
* @dev: struct device pointer to the device used as clock cooling device.
|
||||
* @clock_name: string containing the clock used as cooling mechanism.
|
||||
*
|
||||
* This interface function registers the clock cooling device with the name
|
||||
* "thermal-clock-%x". The cooling device is based on clock frequencies.
|
||||
* The struct device is assumed to be capable of DVFS transitions.
|
||||
* The OPP layer is used to fetch and fill the available frequencies for
|
||||
* the referred device. The ordered frequency table is used to control
|
||||
* the clock cooling device cooling states and to limit clock transitions
|
||||
* based on the cooling state requested by the thermal framework.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
clock_cooling_register(struct device *dev, const char *clock_name)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct clock_cooling_device *ccdev = NULL;
|
||||
char dev_name[THERMAL_NAME_LENGTH];
|
||||
int ret = 0;
|
||||
|
||||
ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL);
|
||||
if (!ccdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&ccdev->lock);
|
||||
ccdev->dev = dev;
|
||||
ccdev->clk = devm_clk_get(dev, clock_name);
|
||||
if (IS_ERR(ccdev->clk))
|
||||
return ERR_CAST(ccdev->clk);
|
||||
|
||||
ret = ida_simple_get(&clock_ida, 0, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
ccdev->id = ret;
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id);
|
||||
|
||||
cdev = thermal_cooling_device_register(dev_name, ccdev,
|
||||
&clock_cooling_ops);
|
||||
if (IS_ERR(cdev)) {
|
||||
ida_simple_remove(&clock_ida, ccdev->id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
ccdev->cdev = cdev;
|
||||
ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier;
|
||||
|
||||
/* Assuming someone has already filled the opp table for this device */
|
||||
ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table);
|
||||
if (ret) {
|
||||
ida_simple_remove(&clock_ida, ccdev->id);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
ccdev->clock_state = 0;
|
||||
ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0);
|
||||
|
||||
clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb);
|
||||
|
||||
return cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_register);
|
||||
|
||||
/**
|
||||
* clock_cooling_unregister - function to remove clock cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
*
|
||||
* This interface function unregisters the "thermal-clock-%x" cooling device.
|
||||
*/
|
||||
void clock_cooling_unregister(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct clock_cooling_device *ccdev;
|
||||
|
||||
if (!cdev)
|
||||
return;
|
||||
|
||||
ccdev = cdev->devdata;
|
||||
|
||||
clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb);
|
||||
dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table);
|
||||
|
||||
thermal_cooling_device_unregister(ccdev->cdev);
|
||||
ida_simple_remove(&clock_ida, ccdev->id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_unregister);
|
@ -49,7 +49,6 @@ struct da9062_thermal {
|
||||
struct da9062 *hw;
|
||||
struct delayed_work work;
|
||||
struct thermal_zone_device *zone;
|
||||
enum thermal_device_mode mode;
|
||||
struct mutex lock; /* protection for da9062_thermal temperature */
|
||||
int temperature;
|
||||
int irq;
|
||||
@ -121,14 +120,6 @@ static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int da9062_thermal_get_mode(struct thermal_zone_device *z,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct da9062_thermal *thermal = z->devdata;
|
||||
*mode = thermal->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9062_thermal_get_trip_type(struct thermal_zone_device *z,
|
||||
int trip,
|
||||
enum thermal_trip_type *type)
|
||||
@ -181,7 +172,6 @@ static int da9062_thermal_get_temp(struct thermal_zone_device *z,
|
||||
|
||||
static struct thermal_zone_device_ops da9062_thermal_ops = {
|
||||
.get_temp = da9062_thermal_get_temp,
|
||||
.get_mode = da9062_thermal_get_mode,
|
||||
.get_trip_type = da9062_thermal_get_trip_type,
|
||||
.get_trip_temp = da9062_thermal_get_trip_temp,
|
||||
};
|
||||
@ -233,7 +223,6 @@ static int da9062_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
thermal->config = match->data;
|
||||
thermal->hw = chip;
|
||||
thermal->mode = THERMAL_DEVICE_ENABLED;
|
||||
thermal->dev = &pdev->dev;
|
||||
|
||||
INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
|
||||
@ -248,6 +237,11 @@ static int da9062_thermal_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(thermal->zone);
|
||||
goto err;
|
||||
}
|
||||
ret = thermal_zone_device_enable(thermal->zone);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot enable thermal zone device\n");
|
||||
goto err_zone;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev,
|
||||
"TJUNC temperature polling period set at %d ms\n",
|
||||
|
@ -1,18 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* devfreq_cooling: Thermal cooling device implementation for devices using
|
||||
* devfreq
|
||||
*
|
||||
* Copyright (C) 2014-2015 ARM Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* TODO:
|
||||
* - If OPPs are added or removed after devfreq cooling has
|
||||
* registered, the devfreq cooling won't react to it.
|
||||
|
@ -153,6 +153,12 @@ static int dove_thermal_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(thermal);
|
||||
}
|
||||
|
||||
ret = thermal_zone_device_enable(thermal);
|
||||
if (ret) {
|
||||
thermal_zone_device_unregister(thermal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, thermal);
|
||||
|
||||
return 0;
|
||||
|
@ -1,16 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* A power allocator to manage temperature
|
||||
*
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "Power allocator: " fmt
|
||||
|
@ -549,8 +549,10 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
|
||||
{
|
||||
struct thermal_zone_device *tzd = sensor->tzd;
|
||||
|
||||
tzd->ops->set_mode(tzd,
|
||||
on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
|
||||
if (on)
|
||||
thermal_zone_device_enable(tzd);
|
||||
else
|
||||
thermal_zone_device_disable(tzd);
|
||||
}
|
||||
|
||||
static int hisi_thermal_probe(struct platform_device *pdev)
|
||||
|
@ -220,6 +220,7 @@ static const struct of_device_id imx8mm_tmu_table[] = {
|
||||
{ .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mm_tmu_table);
|
||||
|
||||
static struct platform_driver imx8mm_tmu = {
|
||||
.driver = {
|
||||
|
@ -197,7 +197,6 @@ struct imx_thermal_data {
|
||||
struct cpufreq_policy *policy;
|
||||
struct thermal_zone_device *tz;
|
||||
struct thermal_cooling_device *cdev;
|
||||
enum thermal_device_mode mode;
|
||||
struct regmap *tempmon;
|
||||
u32 c1, c2; /* See formula in imx_init_calib() */
|
||||
int temp_passive;
|
||||
@ -253,10 +252,11 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
const struct thermal_soc_data *soc_data = data->socdata;
|
||||
struct regmap *map = data->tempmon;
|
||||
unsigned int n_meas;
|
||||
bool wait;
|
||||
bool wait, run_measurement;
|
||||
u32 val;
|
||||
|
||||
if (data->mode == THERMAL_DEVICE_ENABLED) {
|
||||
run_measurement = !data->irq_enabled;
|
||||
if (!run_measurement) {
|
||||
/* Check if a measurement is currently in progress */
|
||||
regmap_read(map, soc_data->temp_data, &val);
|
||||
wait = !(val & soc_data->temp_valid_mask);
|
||||
@ -283,7 +283,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
|
||||
regmap_read(map, soc_data->temp_data, &val);
|
||||
|
||||
if (data->mode != THERMAL_DEVICE_ENABLED) {
|
||||
if (run_measurement) {
|
||||
regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
|
||||
soc_data->measure_temp_mask);
|
||||
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
|
||||
@ -331,27 +331,14 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_get_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct imx_thermal_data *data = tz->devdata;
|
||||
|
||||
*mode = data->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
static int imx_change_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct imx_thermal_data *data = tz->devdata;
|
||||
struct regmap *map = data->tempmon;
|
||||
const struct thermal_soc_data *soc_data = data->socdata;
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED) {
|
||||
tz->polling_delay = IMX_POLLING_DELAY;
|
||||
tz->passive_delay = IMX_PASSIVE_DELAY;
|
||||
|
||||
regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
|
||||
soc_data->power_down_mask);
|
||||
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
|
||||
@ -367,18 +354,12 @@ static int imx_set_mode(struct thermal_zone_device *tz,
|
||||
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
|
||||
soc_data->power_down_mask);
|
||||
|
||||
tz->polling_delay = 0;
|
||||
tz->passive_delay = 0;
|
||||
|
||||
if (data->irq_enabled) {
|
||||
disable_irq(data->irq);
|
||||
data->irq_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
data->mode = mode;
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -467,8 +448,7 @@ static struct thermal_zone_device_ops imx_tz_ops = {
|
||||
.bind = imx_bind,
|
||||
.unbind = imx_unbind,
|
||||
.get_temp = imx_get_temp,
|
||||
.get_mode = imx_get_mode,
|
||||
.set_mode = imx_set_mode,
|
||||
.change_mode = imx_change_mode,
|
||||
.get_trip_type = imx_get_trip_type,
|
||||
.get_trip_temp = imx_get_trip_temp,
|
||||
.get_crit_temp = imx_get_crit_temp,
|
||||
@ -832,7 +812,9 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||
data->socdata->measure_temp_mask);
|
||||
|
||||
data->irq_enabled = true;
|
||||
data->mode = THERMAL_DEVICE_ENABLED;
|
||||
ret = thermal_zone_device_enable(data->tz);
|
||||
if (ret)
|
||||
goto thermal_zone_unregister;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
|
||||
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
|
||||
@ -874,19 +856,18 @@ static int imx_thermal_remove(struct platform_device *pdev)
|
||||
static int __maybe_unused imx_thermal_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* down. This is done in change_mode() operation called from
|
||||
* thermal_zone_device_disable()
|
||||
*/
|
||||
regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
|
||||
data->socdata->measure_temp_mask);
|
||||
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
|
||||
data->socdata->power_down_mask);
|
||||
data->mode = THERMAL_DEVICE_DISABLED;
|
||||
ret = thermal_zone_device_disable(data->tz);
|
||||
if (ret)
|
||||
return ret;
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
|
||||
return 0;
|
||||
@ -895,18 +876,15 @@ static int __maybe_unused imx_thermal_suspend(struct device *dev)
|
||||
static int __maybe_unused imx_thermal_resume(struct device *dev)
|
||||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(data->thermal_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Enabled thermal sensor after resume */
|
||||
regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
|
||||
data->socdata->power_down_mask);
|
||||
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
|
||||
data->socdata->measure_temp_mask);
|
||||
data->mode = THERMAL_DEVICE_ENABLED;
|
||||
ret = thermal_zone_device_enable(data->tz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ struct int3400_thermal_priv {
|
||||
struct acpi_device *adev;
|
||||
struct platform_device *pdev;
|
||||
struct thermal_zone_device *thermal;
|
||||
int mode;
|
||||
int art_count;
|
||||
struct art *arts;
|
||||
int trt_count;
|
||||
@ -383,42 +382,20 @@ static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode *mode)
|
||||
static int int3400_thermal_change_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = thermal->devdata;
|
||||
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
*mode = priv->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = thermal->devdata;
|
||||
bool enable;
|
||||
int result = 0;
|
||||
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
enable = true;
|
||||
else if (mode == THERMAL_DEVICE_DISABLED)
|
||||
enable = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (enable != priv->mode) {
|
||||
priv->mode = enable;
|
||||
if (mode != thermal->mode)
|
||||
result = int3400_thermal_run_osc(priv->adev->handle,
|
||||
priv->current_uuid_index,
|
||||
enable);
|
||||
}
|
||||
priv->current_uuid_index,
|
||||
mode == THERMAL_DEVICE_ENABLED);
|
||||
|
||||
|
||||
evaluate_odvp(priv);
|
||||
|
||||
@ -427,8 +404,7 @@ static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
|
||||
|
||||
static struct thermal_zone_device_ops int3400_thermal_ops = {
|
||||
.get_temp = int3400_thermal_get_temp,
|
||||
.get_mode = int3400_thermal_get_mode,
|
||||
.set_mode = int3400_thermal_set_mode,
|
||||
.change_mode = int3400_thermal_change_mode,
|
||||
};
|
||||
|
||||
static struct thermal_zone_params int3400_thermal_params = {
|
||||
|
@ -259,9 +259,14 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
|
||||
ret = PTR_ERR(int34x_thermal_zone->zone);
|
||||
goto err_thermal_zone;
|
||||
}
|
||||
ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
|
||||
if (ret)
|
||||
goto err_enable;
|
||||
|
||||
return int34x_thermal_zone;
|
||||
|
||||
err_enable:
|
||||
thermal_zone_device_unregister(int34x_thermal_zone->zone);
|
||||
err_thermal_zone:
|
||||
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
|
||||
kfree(int34x_thermal_zone->aux_trips);
|
||||
|
@ -43,7 +43,7 @@
|
||||
#define PCI_DEVICE_ID_PROC_ICL_THERMAL 0x8a03
|
||||
|
||||
/* JasperLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503
|
||||
#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4E03
|
||||
|
||||
/* TigerLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_TGL_THERMAL 0x9A03
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */
|
||||
#define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */
|
||||
#define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */
|
||||
#define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */
|
||||
#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */
|
||||
|
||||
/* Wildcat Point-LP PCH Thermal registers */
|
||||
@ -352,9 +353,14 @@ static int intel_pch_thermal_probe(struct pci_dev *pdev,
|
||||
err = PTR_ERR(ptd->tzd);
|
||||
goto error_cleanup;
|
||||
}
|
||||
err = thermal_zone_device_enable(ptd->tzd);
|
||||
if (err)
|
||||
goto err_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
thermal_zone_device_unregister(ptd->tzd);
|
||||
error_cleanup:
|
||||
iounmap(ptd->hw_base);
|
||||
error_release:
|
||||
@ -405,6 +411,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = {
|
||||
.driver_data = board_cnl, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
|
||||
.driver_data = board_cnl, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_LP),
|
||||
.driver_data = board_cnl, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
|
||||
.driver_data = board_cml, },
|
||||
{ 0, },
|
||||
|
@ -103,7 +103,6 @@ struct soc_sensor_entry {
|
||||
bool locked;
|
||||
u32 store_ptps;
|
||||
u32 store_dts_enable;
|
||||
enum thermal_device_mode mode;
|
||||
struct thermal_zone_device *tzone;
|
||||
};
|
||||
|
||||
@ -127,10 +126,8 @@ static int soc_dts_enable(struct thermal_zone_device *tzd)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (out & QRK_DTS_ENABLE_BIT) {
|
||||
aux_entry->mode = THERMAL_DEVICE_ENABLED;
|
||||
if (out & QRK_DTS_ENABLE_BIT)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!aux_entry->locked) {
|
||||
out |= QRK_DTS_ENABLE_BIT;
|
||||
@ -138,10 +135,7 @@ static int soc_dts_enable(struct thermal_zone_device *tzd)
|
||||
QRK_DTS_REG_OFFSET_ENABLE, out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
aux_entry->mode = THERMAL_DEVICE_ENABLED;
|
||||
} else {
|
||||
aux_entry->mode = THERMAL_DEVICE_DISABLED;
|
||||
pr_info("DTS is locked. Cannot enable DTS\n");
|
||||
ret = -EPERM;
|
||||
}
|
||||
@ -160,10 +154,8 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(out & QRK_DTS_ENABLE_BIT)) {
|
||||
aux_entry->mode = THERMAL_DEVICE_DISABLED;
|
||||
if (!(out & QRK_DTS_ENABLE_BIT))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!aux_entry->locked) {
|
||||
out &= ~QRK_DTS_ENABLE_BIT;
|
||||
@ -172,10 +164,7 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
aux_entry->mode = THERMAL_DEVICE_DISABLED;
|
||||
} else {
|
||||
aux_entry->mode = THERMAL_DEVICE_ENABLED;
|
||||
pr_info("DTS is locked. Cannot disable DTS\n");
|
||||
ret = -EPERM;
|
||||
}
|
||||
@ -309,16 +298,8 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_mode(struct thermal_zone_device *tzd,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct soc_sensor_entry *aux_entry = tzd->devdata;
|
||||
*mode = aux_entry->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_set_mode(struct thermal_zone_device *tzd,
|
||||
enum thermal_device_mode mode)
|
||||
static int sys_change_mode(struct thermal_zone_device *tzd,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -338,8 +319,7 @@ static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_crit_temp = sys_get_crit_temp,
|
||||
.get_mode = sys_get_mode,
|
||||
.set_mode = sys_set_mode,
|
||||
.change_mode = sys_change_mode,
|
||||
};
|
||||
|
||||
static void free_soc_dts(struct soc_sensor_entry *aux_entry)
|
||||
@ -414,9 +394,7 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
mutex_lock(&dts_update_mutex);
|
||||
err = soc_dts_enable(aux_entry->tzone);
|
||||
mutex_unlock(&dts_update_mutex);
|
||||
err = thermal_zone_device_enable(aux_entry->tzone);
|
||||
if (err)
|
||||
goto err_aux_status;
|
||||
|
||||
|
@ -329,6 +329,9 @@ static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
|
||||
ret = PTR_ERR(dts->tzone);
|
||||
goto err_ret;
|
||||
}
|
||||
ret = thermal_zone_device_enable(dts->tzone);
|
||||
if (ret)
|
||||
goto err_enable;
|
||||
|
||||
ret = soc_dts_enable(id);
|
||||
if (ret)
|
||||
|
@ -363,6 +363,12 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
|
||||
kfree(zonedev);
|
||||
return err;
|
||||
}
|
||||
err = thermal_zone_device_enable(zonedev->tzone);
|
||||
if (err) {
|
||||
thermal_zone_device_unregister(zonedev->tzone);
|
||||
kfree(zonedev);
|
||||
return err;
|
||||
}
|
||||
/* Store MSR value for package thermal interrupt, to restore at exit */
|
||||
rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, zonedev->msr_pkg_therm_low,
|
||||
zonedev->msr_pkg_therm_high);
|
||||
|
@ -65,6 +65,7 @@ static int kirkwood_thermal_probe(struct platform_device *pdev)
|
||||
struct thermal_zone_device *thermal = NULL;
|
||||
struct kirkwood_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@ -82,6 +83,12 @@ static int kirkwood_thermal_probe(struct platform_device *pdev)
|
||||
"Failed to register thermal zone device\n");
|
||||
return PTR_ERR(thermal);
|
||||
}
|
||||
ret = thermal_zone_device_enable(thermal);
|
||||
if (ret) {
|
||||
thermal_zone_device_unregister(thermal);
|
||||
dev_err(&pdev->dev, "Failed to enable thermal zone device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, thermal);
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#define TEMP_MONIDET0 0x014
|
||||
#define TEMP_MONIDET1 0x018
|
||||
#define TEMP_MSRCTL0 0x038
|
||||
#define TEMP_MSRCTL1 0x03c
|
||||
#define TEMP_AHBPOLL 0x040
|
||||
#define TEMP_AHBTO 0x044
|
||||
#define TEMP_ADCPNP0 0x048
|
||||
@ -120,18 +121,32 @@
|
||||
* MT2701 has 3 sensors and needs 3 VTS calibration data.
|
||||
* MT2712 has 4 sensors and needs 4 VTS calibration data.
|
||||
*/
|
||||
#define CALIB_BUF0_VALID BIT(0)
|
||||
#define CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff)
|
||||
#define CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff)
|
||||
#define CALIB_BUF0_VTS_TS2(x) (((x) >> 8) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TS3(x) (((x) >> 0) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TS4(x) (((x) >> 23) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TS5(x) (((x) >> 5) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TSABB(x) (((x) >> 14) & 0x1ff)
|
||||
#define CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE_SIGN(x) (((x) >> 7) & 0x1)
|
||||
#define CALIB_BUF1_ID(x) (((x) >> 9) & 0x1)
|
||||
#define CALIB_BUF0_VALID_V1 BIT(0)
|
||||
#define CALIB_BUF1_ADC_GE_V1(x) (((x) >> 22) & 0x3ff)
|
||||
#define CALIB_BUF0_VTS_TS1_V1(x) (((x) >> 17) & 0x1ff)
|
||||
#define CALIB_BUF0_VTS_TS2_V1(x) (((x) >> 8) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TS3_V1(x) (((x) >> 0) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TS4_V1(x) (((x) >> 23) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TS5_V1(x) (((x) >> 5) & 0x1ff)
|
||||
#define CALIB_BUF2_VTS_TSABB_V1(x) (((x) >> 14) & 0x1ff)
|
||||
#define CALIB_BUF0_DEGC_CALI_V1(x) (((x) >> 1) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE_V1(x) (((x) >> 26) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE_SIGN_V1(x) (((x) >> 7) & 0x1)
|
||||
#define CALIB_BUF1_ID_V1(x) (((x) >> 9) & 0x1)
|
||||
|
||||
/*
|
||||
* Layout of the fuses providing the calibration data
|
||||
* These macros could be used for MT7622.
|
||||
*/
|
||||
#define CALIB_BUF0_ADC_OE_V2(x) (((x) >> 22) & 0x3ff)
|
||||
#define CALIB_BUF0_ADC_GE_V2(x) (((x) >> 12) & 0x3ff)
|
||||
#define CALIB_BUF0_DEGC_CALI_V2(x) (((x) >> 6) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE_V2(x) (((x) >> 0) & 0x3f)
|
||||
#define CALIB_BUF1_VTS_TS1_V2(x) (((x) >> 23) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TS2_V2(x) (((x) >> 14) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TSABB_V2(x) (((x) >> 5) & 0x1ff)
|
||||
#define CALIB_BUF1_VALID_V2(x) (((x) >> 4) & 0x1)
|
||||
#define CALIB_BUF1_O_SLOPE_SIGN_V2(x) (((x) >> 3) & 0x1)
|
||||
|
||||
enum {
|
||||
VTS1,
|
||||
@ -143,6 +158,11 @@ enum {
|
||||
MAX_NUM_VTS,
|
||||
};
|
||||
|
||||
enum mtk_thermal_version {
|
||||
MTK_THERMAL_V1 = 1,
|
||||
MTK_THERMAL_V2,
|
||||
};
|
||||
|
||||
/* MT2701 thermal sensors */
|
||||
#define MT2701_TS1 0
|
||||
#define MT2701_TS2 1
|
||||
@ -248,6 +268,7 @@ struct mtk_thermal_data {
|
||||
const int *controller_offset;
|
||||
bool need_switch_bank;
|
||||
struct thermal_bank_cfg bank_data[MAX_NUM_ZONES];
|
||||
enum mtk_thermal_version version;
|
||||
};
|
||||
|
||||
struct mtk_thermal {
|
||||
@ -261,8 +282,10 @@ struct mtk_thermal {
|
||||
|
||||
/* Calibration values */
|
||||
s32 adc_ge;
|
||||
s32 adc_oe;
|
||||
s32 degc_cali;
|
||||
s32 o_slope;
|
||||
s32 o_slope_sign;
|
||||
s32 vts[MAX_NUM_VTS];
|
||||
|
||||
const struct mtk_thermal_data *conf;
|
||||
@ -401,6 +424,7 @@ static const struct mtk_thermal_data mt8173_thermal_data = {
|
||||
.msr = mt8173_msr,
|
||||
.adcpnp = mt8173_adcpnp,
|
||||
.sensor_mux_values = mt8173_mux_values,
|
||||
.version = MTK_THERMAL_V1,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -431,6 +455,7 @@ static const struct mtk_thermal_data mt2701_thermal_data = {
|
||||
.msr = mt2701_msr,
|
||||
.adcpnp = mt2701_adcpnp,
|
||||
.sensor_mux_values = mt2701_mux_values,
|
||||
.version = MTK_THERMAL_V1,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -461,6 +486,7 @@ static const struct mtk_thermal_data mt2712_thermal_data = {
|
||||
.msr = mt2712_msr,
|
||||
.adcpnp = mt2712_adcpnp,
|
||||
.sensor_mux_values = mt2712_mux_values,
|
||||
.version = MTK_THERMAL_V1,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -485,6 +511,7 @@ static const struct mtk_thermal_data mt7622_thermal_data = {
|
||||
.msr = mt7622_msr,
|
||||
.adcpnp = mt7622_adcpnp,
|
||||
.sensor_mux_values = mt7622_mux_values,
|
||||
.version = MTK_THERMAL_V2,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -517,6 +544,7 @@ static const struct mtk_thermal_data mt8183_thermal_data = {
|
||||
.msr = mt8183_msr,
|
||||
.adcpnp = mt8183_adcpnp,
|
||||
.sensor_mux_values = mt8183_mux_values,
|
||||
.version = MTK_THERMAL_V1,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -528,7 +556,7 @@ static const struct mtk_thermal_data mt8183_thermal_data = {
|
||||
* This converts the raw ADC value to mcelsius using the SoC specific
|
||||
* calibration constants
|
||||
*/
|
||||
static int raw_to_mcelsius(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
static int raw_to_mcelsius_v1(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
{
|
||||
s32 tmp;
|
||||
|
||||
@ -543,6 +571,36 @@ static int raw_to_mcelsius(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
return mt->degc_cali * 500 - tmp;
|
||||
}
|
||||
|
||||
static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
{
|
||||
s32 format_1 = 0;
|
||||
s32 format_2 = 0;
|
||||
s32 g_oe = 1;
|
||||
s32 g_gain = 1;
|
||||
s32 g_x_roomt = 0;
|
||||
s32 tmp = 0;
|
||||
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
|
||||
raw &= 0xfff;
|
||||
g_gain = 10000 + (((mt->adc_ge - 512) * 10000) >> 12);
|
||||
g_oe = mt->adc_oe - 512;
|
||||
format_1 = mt->vts[VTS2] + 3105 - g_oe;
|
||||
format_2 = (mt->degc_cali * 10) >> 1;
|
||||
g_x_roomt = (((format_1 * 10000) >> 12) * 10000) / g_gain;
|
||||
|
||||
tmp = (((((raw - g_oe) * 10000) >> 12) * 10000) / g_gain) - g_x_roomt;
|
||||
tmp = tmp * 10 * 100 / 11;
|
||||
|
||||
if (mt->o_slope_sign == 0)
|
||||
tmp = tmp / (165 - mt->o_slope);
|
||||
else
|
||||
tmp = tmp / (165 + mt->o_slope);
|
||||
|
||||
return (format_2 - tmp) * 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtk_thermal_get_bank - get bank
|
||||
* @bank: The bank
|
||||
@ -596,9 +654,13 @@ static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank)
|
||||
for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
|
||||
raw = readl(mt->thermal_base + conf->msr[i]);
|
||||
|
||||
temp = raw_to_mcelsius(mt,
|
||||
conf->bank_data[bank->id].sensors[i],
|
||||
raw);
|
||||
if (mt->conf->version == MTK_THERMAL_V1) {
|
||||
temp = raw_to_mcelsius_v1(
|
||||
mt, conf->bank_data[bank->id].sensors[i], raw);
|
||||
} else {
|
||||
temp = raw_to_mcelsius_v2(
|
||||
mt, conf->bank_data[bank->id].sensors[i], raw);
|
||||
}
|
||||
|
||||
/*
|
||||
* The first read of a sensor often contains very high bogus
|
||||
@ -700,9 +762,11 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
|
||||
writel(auxadc_phys_base + AUXADC_CON1_CLR_V,
|
||||
controller_base + TEMP_ADCMUXADDR);
|
||||
|
||||
/* AHB address for pnp sensor mux selection */
|
||||
writel(apmixed_phys_base + APMIXED_SYS_TS_CON1,
|
||||
controller_base + TEMP_PNPMUXADDR);
|
||||
if (mt->conf->version == MTK_THERMAL_V1) {
|
||||
/* AHB address for pnp sensor mux selection */
|
||||
writel(apmixed_phys_base + APMIXED_SYS_TS_CON1,
|
||||
controller_base + TEMP_PNPMUXADDR);
|
||||
}
|
||||
|
||||
/* AHB value for auxadc enable */
|
||||
writel(BIT(conf->auxadc_channel), controller_base + TEMP_ADCEN);
|
||||
@ -759,6 +823,68 @@ static u64 of_get_phys_base(struct device_node *np)
|
||||
return of_translate_address(np, regaddr_p);
|
||||
}
|
||||
|
||||
static int mtk_thermal_extract_efuse_v1(struct mtk_thermal *mt, u32 *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(buf[0] & CALIB_BUF0_VALID_V1))
|
||||
return -EINVAL;
|
||||
|
||||
mt->adc_ge = CALIB_BUF1_ADC_GE_V1(buf[1]);
|
||||
|
||||
for (i = 0; i < mt->conf->num_sensors; i++) {
|
||||
switch (mt->conf->vts_index[i]) {
|
||||
case VTS1:
|
||||
mt->vts[VTS1] = CALIB_BUF0_VTS_TS1_V1(buf[0]);
|
||||
break;
|
||||
case VTS2:
|
||||
mt->vts[VTS2] = CALIB_BUF0_VTS_TS2_V1(buf[0]);
|
||||
break;
|
||||
case VTS3:
|
||||
mt->vts[VTS3] = CALIB_BUF1_VTS_TS3_V1(buf[1]);
|
||||
break;
|
||||
case VTS4:
|
||||
mt->vts[VTS4] = CALIB_BUF2_VTS_TS4_V1(buf[2]);
|
||||
break;
|
||||
case VTS5:
|
||||
mt->vts[VTS5] = CALIB_BUF2_VTS_TS5_V1(buf[2]);
|
||||
break;
|
||||
case VTSABB:
|
||||
mt->vts[VTSABB] =
|
||||
CALIB_BUF2_VTS_TSABB_V1(buf[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mt->degc_cali = CALIB_BUF0_DEGC_CALI_V1(buf[0]);
|
||||
if (CALIB_BUF1_ID_V1(buf[1]) &
|
||||
CALIB_BUF0_O_SLOPE_SIGN_V1(buf[0]))
|
||||
mt->o_slope = -CALIB_BUF0_O_SLOPE_V1(buf[0]);
|
||||
else
|
||||
mt->o_slope = CALIB_BUF0_O_SLOPE_V1(buf[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_thermal_extract_efuse_v2(struct mtk_thermal *mt, u32 *buf)
|
||||
{
|
||||
if (!CALIB_BUF1_VALID_V2(buf[1]))
|
||||
return -EINVAL;
|
||||
|
||||
mt->adc_oe = CALIB_BUF0_ADC_OE_V2(buf[0]);
|
||||
mt->adc_ge = CALIB_BUF0_ADC_GE_V2(buf[0]);
|
||||
mt->degc_cali = CALIB_BUF0_DEGC_CALI_V2(buf[0]);
|
||||
mt->o_slope = CALIB_BUF0_O_SLOPE_V2(buf[0]);
|
||||
mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V2(buf[1]);
|
||||
mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V2(buf[1]);
|
||||
mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V2(buf[1]);
|
||||
mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V2(buf[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_thermal_get_calibration_data(struct device *dev,
|
||||
struct mtk_thermal *mt)
|
||||
{
|
||||
@ -794,42 +920,14 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf[0] & CALIB_BUF0_VALID) {
|
||||
mt->adc_ge = CALIB_BUF1_ADC_GE(buf[1]);
|
||||
if (mt->conf->version == MTK_THERMAL_V1)
|
||||
ret = mtk_thermal_extract_efuse_v1(mt, buf);
|
||||
else
|
||||
ret = mtk_thermal_extract_efuse_v2(mt, buf);
|
||||
|
||||
for (i = 0; i < mt->conf->num_sensors; i++) {
|
||||
switch (mt->conf->vts_index[i]) {
|
||||
case VTS1:
|
||||
mt->vts[VTS1] = CALIB_BUF0_VTS_TS1(buf[0]);
|
||||
break;
|
||||
case VTS2:
|
||||
mt->vts[VTS2] = CALIB_BUF0_VTS_TS2(buf[0]);
|
||||
break;
|
||||
case VTS3:
|
||||
mt->vts[VTS3] = CALIB_BUF1_VTS_TS3(buf[1]);
|
||||
break;
|
||||
case VTS4:
|
||||
mt->vts[VTS4] = CALIB_BUF2_VTS_TS4(buf[2]);
|
||||
break;
|
||||
case VTS5:
|
||||
mt->vts[VTS5] = CALIB_BUF2_VTS_TS5(buf[2]);
|
||||
break;
|
||||
case VTSABB:
|
||||
mt->vts[VTSABB] = CALIB_BUF2_VTS_TSABB(buf[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mt->degc_cali = CALIB_BUF0_DEGC_CALI(buf[0]);
|
||||
if (CALIB_BUF1_ID(buf[1]) &
|
||||
CALIB_BUF0_O_SLOPE_SIGN(buf[0]))
|
||||
mt->o_slope = -CALIB_BUF0_O_SLOPE(buf[0]);
|
||||
else
|
||||
mt->o_slope = CALIB_BUF0_O_SLOPE(buf[0]);
|
||||
} else {
|
||||
if (ret) {
|
||||
dev_info(dev, "Device not calibrated, using default calibration values\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -863,6 +961,28 @@ static const struct of_device_id mtk_thermal_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_thermal_of_match);
|
||||
|
||||
static void mtk_thermal_turn_on_buffer(void __iomem *apmixed_base)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
tmp = readl(apmixed_base + APMIXED_SYS_TS_CON1);
|
||||
tmp &= ~(0x37);
|
||||
tmp |= 0x1;
|
||||
writel(tmp, apmixed_base + APMIXED_SYS_TS_CON1);
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
static void mtk_thermal_release_periodic_ts(struct mtk_thermal *mt,
|
||||
void __iomem *auxadc_base)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
writel(0x800, auxadc_base + AUXADC_CON1_SET_V);
|
||||
writel(0x1, mt->thermal_base + TEMP_MONCTL0);
|
||||
tmp = readl(mt->thermal_base + TEMP_MSRCTL1);
|
||||
writel((tmp & (~0x10e)), mt->thermal_base + TEMP_MSRCTL1);
|
||||
}
|
||||
|
||||
static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, i, ctrl_id;
|
||||
@ -871,6 +991,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
u64 auxadc_phys_base, apmixed_phys_base;
|
||||
struct thermal_zone_device *tzdev;
|
||||
void __iomem *apmixed_base, *auxadc_base;
|
||||
|
||||
mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL);
|
||||
if (!mt)
|
||||
@ -905,6 +1026,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
auxadc_base = of_iomap(auxadc, 0);
|
||||
auxadc_phys_base = of_get_phys_base(auxadc);
|
||||
|
||||
of_node_put(auxadc);
|
||||
@ -920,6 +1042,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
apmixed_base = of_iomap(apmixedsys, 0);
|
||||
apmixed_phys_base = of_get_phys_base(apmixedsys);
|
||||
|
||||
of_node_put(apmixedsys);
|
||||
@ -945,6 +1068,11 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
goto err_disable_clk_auxadc;
|
||||
}
|
||||
|
||||
if (mt->conf->version == MTK_THERMAL_V2) {
|
||||
mtk_thermal_turn_on_buffer(apmixed_base);
|
||||
mtk_thermal_release_periodic_ts(mt, auxadc_base);
|
||||
}
|
||||
|
||||
for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++)
|
||||
for (i = 0; i < mt->conf->num_banks; i++)
|
||||
mtk_thermal_init_bank(mt, i, apmixed_phys_base,
|
||||
|
@ -48,6 +48,63 @@
|
||||
#define MSM8916_CAL_SEL_MASK 0xe0000000
|
||||
#define MSM8916_CAL_SEL_SHIFT 29
|
||||
|
||||
/* eeprom layout data for 8939 */
|
||||
#define MSM8939_BASE0_MASK 0x000000ff
|
||||
#define MSM8939_BASE1_MASK 0xff000000
|
||||
#define MSM8939_BASE0_SHIFT 0
|
||||
#define MSM8939_BASE1_SHIFT 24
|
||||
|
||||
#define MSM8939_S0_P1_MASK 0x000001f8
|
||||
#define MSM8939_S1_P1_MASK 0x001f8000
|
||||
#define MSM8939_S2_P1_MASK_0_4 0xf8000000
|
||||
#define MSM8939_S2_P1_MASK_5 0x00000001
|
||||
#define MSM8939_S3_P1_MASK 0x00001f80
|
||||
#define MSM8939_S4_P1_MASK 0x01f80000
|
||||
#define MSM8939_S5_P1_MASK 0x00003f00
|
||||
#define MSM8939_S6_P1_MASK 0x03f00000
|
||||
#define MSM8939_S7_P1_MASK 0x0000003f
|
||||
#define MSM8939_S8_P1_MASK 0x0003f000
|
||||
#define MSM8939_S9_P1_MASK 0x07e00000
|
||||
|
||||
#define MSM8939_S0_P2_MASK 0x00007e00
|
||||
#define MSM8939_S1_P2_MASK 0x07e00000
|
||||
#define MSM8939_S2_P2_MASK 0x0000007e
|
||||
#define MSM8939_S3_P2_MASK 0x0007e000
|
||||
#define MSM8939_S4_P2_MASK 0x7e000000
|
||||
#define MSM8939_S5_P2_MASK 0x000fc000
|
||||
#define MSM8939_S6_P2_MASK 0xfc000000
|
||||
#define MSM8939_S7_P2_MASK 0x00000fc0
|
||||
#define MSM8939_S8_P2_MASK 0x00fc0000
|
||||
#define MSM8939_S9_P2_MASK_0_4 0xf8000000
|
||||
#define MSM8939_S9_P2_MASK_5 0x00002000
|
||||
|
||||
#define MSM8939_S0_P1_SHIFT 3
|
||||
#define MSM8939_S1_P1_SHIFT 15
|
||||
#define MSM8939_S2_P1_SHIFT_0_4 27
|
||||
#define MSM8939_S2_P1_SHIFT_5 0
|
||||
#define MSM8939_S3_P1_SHIFT 7
|
||||
#define MSM8939_S4_P1_SHIFT 19
|
||||
#define MSM8939_S5_P1_SHIFT 8
|
||||
#define MSM8939_S6_P1_SHIFT 20
|
||||
#define MSM8939_S7_P1_SHIFT 0
|
||||
#define MSM8939_S8_P1_SHIFT 12
|
||||
#define MSM8939_S9_P1_SHIFT 21
|
||||
|
||||
#define MSM8939_S0_P2_SHIFT 9
|
||||
#define MSM8939_S1_P2_SHIFT 21
|
||||
#define MSM8939_S2_P2_SHIFT 1
|
||||
#define MSM8939_S3_P2_SHIFT 13
|
||||
#define MSM8939_S4_P2_SHIFT 25
|
||||
#define MSM8939_S5_P2_SHIFT 14
|
||||
#define MSM8939_S6_P2_SHIFT 26
|
||||
#define MSM8939_S7_P2_SHIFT 6
|
||||
#define MSM8939_S8_P2_SHIFT 18
|
||||
#define MSM8939_S9_P2_SHIFT_0_4 27
|
||||
#define MSM8939_S9_P2_SHIFT_5 13
|
||||
|
||||
#define MSM8939_CAL_SEL_MASK 0x7
|
||||
#define MSM8939_CAL_SEL_SHIFT 0
|
||||
|
||||
/* eeprom layout data for 8974 */
|
||||
#define BASE1_MASK 0xff
|
||||
#define S0_P1_MASK 0x3f00
|
||||
@ -189,6 +246,76 @@ static int calibrate_8916(struct tsens_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_8939(struct tsens_priv *priv)
|
||||
{
|
||||
int base0 = 0, base1 = 0, i;
|
||||
u32 p1[10], p2[10];
|
||||
int mode = 0;
|
||||
u32 *qfprom_cdata;
|
||||
u32 cdata[6];
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
return PTR_ERR(qfprom_cdata);
|
||||
|
||||
/* Mapping between qfprom nvmem and calibration data */
|
||||
cdata[0] = qfprom_cdata[12];
|
||||
cdata[1] = qfprom_cdata[13];
|
||||
cdata[2] = qfprom_cdata[0];
|
||||
cdata[3] = qfprom_cdata[1];
|
||||
cdata[4] = qfprom_cdata[22];
|
||||
cdata[5] = qfprom_cdata[21];
|
||||
|
||||
mode = (cdata[0] & MSM8939_CAL_SEL_MASK) >> MSM8939_CAL_SEL_SHIFT;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base1 = (cdata[3] & MSM8939_BASE1_MASK) >> MSM8939_BASE1_SHIFT;
|
||||
p2[0] = (cdata[0] & MSM8939_S0_P2_MASK) >> MSM8939_S0_P2_SHIFT;
|
||||
p2[1] = (cdata[0] & MSM8939_S1_P2_MASK) >> MSM8939_S1_P2_SHIFT;
|
||||
p2[2] = (cdata[1] & MSM8939_S2_P2_MASK) >> MSM8939_S2_P2_SHIFT;
|
||||
p2[3] = (cdata[1] & MSM8939_S3_P2_MASK) >> MSM8939_S3_P2_SHIFT;
|
||||
p2[4] = (cdata[1] & MSM8939_S4_P2_MASK) >> MSM8939_S4_P2_SHIFT;
|
||||
p2[5] = (cdata[2] & MSM8939_S5_P2_MASK) >> MSM8939_S5_P2_SHIFT;
|
||||
p2[6] = (cdata[2] & MSM8939_S6_P2_MASK) >> MSM8939_S6_P2_SHIFT;
|
||||
p2[7] = (cdata[3] & MSM8939_S7_P2_MASK) >> MSM8939_S7_P2_SHIFT;
|
||||
p2[8] = (cdata[3] & MSM8939_S8_P2_MASK) >> MSM8939_S8_P2_SHIFT;
|
||||
p2[9] = (cdata[4] & MSM8939_S9_P2_MASK_0_4) >> MSM8939_S9_P2_SHIFT_0_4;
|
||||
p2[9] |= ((cdata[5] & MSM8939_S9_P2_MASK_5) >> MSM8939_S9_P2_SHIFT_5) << 5;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = (base1 + p2[i]) << 2;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base0 = (cdata[2] & MSM8939_BASE0_MASK) >> MSM8939_BASE0_SHIFT;
|
||||
p1[0] = (cdata[0] & MSM8939_S0_P1_MASK) >> MSM8939_S0_P1_SHIFT;
|
||||
p1[1] = (cdata[0] & MSM8939_S1_P1_MASK) >> MSM8939_S1_P1_SHIFT;
|
||||
p1[2] = (cdata[0] & MSM8939_S2_P1_MASK_0_4) >> MSM8939_S2_P1_SHIFT_0_4;
|
||||
p1[2] |= ((cdata[1] & MSM8939_S2_P1_MASK_5) >> MSM8939_S2_P1_SHIFT_5) << 5;
|
||||
p1[3] = (cdata[1] & MSM8939_S3_P1_MASK) >> MSM8939_S3_P1_SHIFT;
|
||||
p1[4] = (cdata[1] & MSM8939_S4_P1_MASK) >> MSM8939_S4_P1_SHIFT;
|
||||
p1[5] = (cdata[2] & MSM8939_S5_P1_MASK) >> MSM8939_S5_P1_SHIFT;
|
||||
p1[6] = (cdata[2] & MSM8939_S6_P1_MASK) >> MSM8939_S6_P1_SHIFT;
|
||||
p1[7] = (cdata[3] & MSM8939_S7_P1_MASK) >> MSM8939_S7_P1_SHIFT;
|
||||
p1[8] = (cdata[3] & MSM8939_S8_P1_MASK) >> MSM8939_S8_P1_SHIFT;
|
||||
p1[9] = (cdata[4] & MSM8939_S9_P1_MASK) >> MSM8939_S9_P1_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = ((base0) + p1[i]) << 2;
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_8974(struct tsens_priv *priv)
|
||||
{
|
||||
int base1 = 0, base2 = 0, i;
|
||||
@ -325,7 +452,7 @@ static int calibrate_8974(struct tsens_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* v0.1: 8916, 8974 */
|
||||
/* v0.1: 8916, 8939, 8974 */
|
||||
|
||||
static struct tsens_features tsens_v0_1_feat = {
|
||||
.ver_major = VER_0_1,
|
||||
@ -386,6 +513,21 @@ struct tsens_plat_data data_8916 = {
|
||||
.fields = tsens_v0_1_regfields,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8939 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_8939,
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
struct tsens_plat_data data_8939 = {
|
||||
.num_sensors = 10,
|
||||
.ops = &ops_8939,
|
||||
.hw_ids = (unsigned int []){ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10 },
|
||||
|
||||
.feat = &tsens_v0_1_feat,
|
||||
.fields = tsens_v0_1_regfields,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8974 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_8974,
|
||||
|
@ -897,6 +897,9 @@ static const struct of_device_id tsens_table[] = {
|
||||
{
|
||||
.compatible = "qcom,msm8916-tsens",
|
||||
.data = &data_8916,
|
||||
}, {
|
||||
.compatible = "qcom,msm8939-tsens",
|
||||
.data = &data_8939,
|
||||
}, {
|
||||
.compatible = "qcom,msm8974-tsens",
|
||||
.data = &data_8974,
|
||||
|
@ -585,7 +585,7 @@ int get_temp_common(const struct tsens_sensor *s, int *temp);
|
||||
extern struct tsens_plat_data data_8960;
|
||||
|
||||
/* TSENS v0.1 targets */
|
||||
extern struct tsens_plat_data data_8916, data_8974;
|
||||
extern struct tsens_plat_data data_8916, data_8939, data_8974;
|
||||
|
||||
/* TSENS v1 targets */
|
||||
extern struct tsens_plat_data data_tsens_v1, data_8976;
|
||||
|
@ -63,7 +63,7 @@
|
||||
#define TSC_MAX_NUM 3
|
||||
|
||||
/* default THCODE values if FUSEs are missing */
|
||||
static const int thcode[TSC_MAX_NUM][3] = {
|
||||
static const int thcodes[TSC_MAX_NUM][3] = {
|
||||
{ 3397, 2800, 2221 },
|
||||
{ 3393, 2795, 2216 },
|
||||
{ 3389, 2805, 2237 },
|
||||
@ -172,7 +172,7 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
|
||||
/* Read register and convert to mili Celsius */
|
||||
reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
|
||||
|
||||
if (reg <= thcode[tsc->id][1])
|
||||
if (reg <= thcodes[tsc->id][1])
|
||||
val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1,
|
||||
tsc->coef.a1);
|
||||
else
|
||||
@ -314,6 +314,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
|
||||
.compatible = "renesas,r8a774b1-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r8a774e1-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r8a7795-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
@ -430,7 +434,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
priv->tscs[i] = tsc;
|
||||
|
||||
priv->thermal_init(tsc);
|
||||
rcar_gen3_thermal_calc_coefs(tsc, ptat, thcode[i],
|
||||
rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i],
|
||||
*rcar_gen3_ths_tj_1);
|
||||
|
||||
zone = devm_thermal_zone_of_sensor_register(dev, i, tsc,
|
||||
|
@ -550,12 +550,19 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
priv->zone = devm_thermal_zone_of_sensor_register(
|
||||
dev, i, priv,
|
||||
&rcar_thermal_zone_of_ops);
|
||||
else
|
||||
else {
|
||||
priv->zone = thermal_zone_device_register(
|
||||
"rcar_thermal",
|
||||
1, 0, priv,
|
||||
&rcar_thermal_zone_ops, NULL, 0,
|
||||
idle);
|
||||
|
||||
ret = thermal_zone_device_enable(priv->zone);
|
||||
if (ret) {
|
||||
thermal_zone_device_unregister(priv->zone);
|
||||
priv->zone = ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
if (IS_ERR(priv->zone)) {
|
||||
dev_err(dev, "can't register thermal zone\n");
|
||||
ret = PTR_ERR(priv->zone);
|
||||
|
@ -1068,8 +1068,10 @@ rockchip_thermal_toggle_sensor(struct rockchip_thermal_sensor *sensor, bool on)
|
||||
{
|
||||
struct thermal_zone_device *tzd = sensor->tzd;
|
||||
|
||||
tzd->ops->set_mode(tzd,
|
||||
on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
|
||||
if (on)
|
||||
thermal_zone_device_enable(tzd);
|
||||
else
|
||||
thermal_zone_device_disable(tzd);
|
||||
}
|
||||
|
||||
static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
|
||||
|
@ -131,6 +131,11 @@ static int spear_thermal_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(spear_thermal);
|
||||
goto disable_clk;
|
||||
}
|
||||
ret = thermal_zone_device_enable(spear_thermal);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot enable thermal zone\n");
|
||||
goto unregister_tzd;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, spear_thermal);
|
||||
|
||||
@ -139,6 +144,8 @@ static int spear_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_tzd:
|
||||
thermal_zone_device_unregister(spear_thermal);
|
||||
disable_clk:
|
||||
clk_disable(stdev->clk);
|
||||
|
||||
|
@ -322,8 +322,10 @@ static void sprd_thm_toggle_sensor(struct sprd_thermal_sensor *sen, bool on)
|
||||
{
|
||||
struct thermal_zone_device *tzd = sen->tzd;
|
||||
|
||||
tzd->ops->set_mode(tzd,
|
||||
on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
|
||||
if (on)
|
||||
thermal_zone_device_enable(tzd);
|
||||
else
|
||||
thermal_zone_device_disable(tzd);
|
||||
}
|
||||
|
||||
static int sprd_thm_probe(struct platform_device *pdev)
|
||||
|
@ -246,11 +246,16 @@ int st_thermal_register(struct platform_device *pdev,
|
||||
ret = PTR_ERR(sensor->thermal_dev);
|
||||
goto sensor_off;
|
||||
}
|
||||
ret = thermal_zone_device_enable(sensor->thermal_dev);
|
||||
if (ret)
|
||||
goto tzd_unregister;
|
||||
|
||||
platform_set_drvdata(pdev, sensor);
|
||||
|
||||
return 0;
|
||||
|
||||
tzd_unregister:
|
||||
thermal_zone_device_unregister(sensor->thermal_dev);
|
||||
sensor_off:
|
||||
st_thermal_sensor_off(sensor);
|
||||
|
||||
|
@ -215,6 +215,8 @@ exit:
|
||||
mutex_unlock(&tz->lock);
|
||||
mutex_unlock(&thermal_governor_lock);
|
||||
|
||||
thermal_notify_tz_gov_change(tz->id, policy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -301,13 +303,22 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
|
||||
cancel_delayed_work(&tz->poll_queue);
|
||||
}
|
||||
|
||||
static inline bool should_stop_polling(struct thermal_zone_device *tz)
|
||||
{
|
||||
return !thermal_zone_device_is_enabled(tz);
|
||||
}
|
||||
|
||||
static void monitor_thermal_zone(struct thermal_zone_device *tz)
|
||||
{
|
||||
bool stop;
|
||||
|
||||
stop = should_stop_polling(tz);
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (tz->passive)
|
||||
if (!stop && tz->passive)
|
||||
thermal_zone_device_set_polling(tz, tz->passive_delay);
|
||||
else if (tz->polling_delay)
|
||||
else if (!stop && tz->polling_delay)
|
||||
thermal_zone_device_set_polling(tz, tz->polling_delay);
|
||||
else
|
||||
thermal_zone_device_set_polling(tz, 0);
|
||||
@ -406,12 +417,25 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
|
||||
{
|
||||
enum thermal_trip_type type;
|
||||
int trip_temp, hyst = 0;
|
||||
|
||||
/* Ignore disabled trip points */
|
||||
if (test_bit(trip, &tz->trips_disabled))
|
||||
return;
|
||||
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
tz->ops->get_trip_type(tz, trip, &type);
|
||||
if (tz->ops->get_trip_hyst)
|
||||
tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||||
|
||||
if (tz->last_temperature != THERMAL_TEMP_INVALID) {
|
||||
if (tz->last_temperature < trip_temp &&
|
||||
tz->temperature >= trip_temp)
|
||||
thermal_notify_tz_trip_up(tz->id, trip);
|
||||
if (tz->last_temperature >= trip_temp &&
|
||||
tz->temperature < (trip_temp - hyst))
|
||||
thermal_notify_tz_trip_down(tz->id, trip);
|
||||
}
|
||||
|
||||
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
|
||||
handle_critical_trips(tz, trip, type);
|
||||
@ -443,6 +467,8 @@ static void update_temperature(struct thermal_zone_device *tz)
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
trace_thermal_temperature(tz);
|
||||
|
||||
thermal_genl_sampling_temp(tz->id, temp);
|
||||
}
|
||||
|
||||
static void thermal_zone_device_init(struct thermal_zone_device *tz)
|
||||
@ -459,11 +485,71 @@ static void thermal_zone_device_reset(struct thermal_zone_device *tz)
|
||||
thermal_zone_device_init(tz);
|
||||
}
|
||||
|
||||
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
/* do nothing if mode isn't changing */
|
||||
if (mode == tz->mode) {
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tz->ops->change_mode)
|
||||
ret = tz->ops->change_mode(tz, mode);
|
||||
|
||||
if (!ret)
|
||||
tz->mode = mode;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
thermal_notify_tz_enable(tz->id);
|
||||
else
|
||||
thermal_notify_tz_disable(tz->id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int thermal_zone_device_enable(struct thermal_zone_device *tz)
|
||||
{
|
||||
return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_device_enable);
|
||||
|
||||
int thermal_zone_device_disable(struct thermal_zone_device *tz)
|
||||
{
|
||||
return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_device_disable);
|
||||
|
||||
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
|
||||
{
|
||||
enum thermal_device_mode mode;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
mode = tz->mode;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return mode == THERMAL_DEVICE_ENABLED;
|
||||
}
|
||||
|
||||
void thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (should_stop_polling(tz))
|
||||
return;
|
||||
|
||||
if (atomic_read(&in_suspend))
|
||||
return;
|
||||
|
||||
@ -617,6 +703,73 @@ void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
}
|
||||
|
||||
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct thermal_governor *gov;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&thermal_governor_lock);
|
||||
list_for_each_entry(gov, &thermal_governor_list, governor_list) {
|
||||
ret = cb(gov, data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&thermal_governor_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
|
||||
void *), void *data)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
||||
ret = cb(cdev, data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct thermal_zone_device *tz;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(tz, &thermal_tz_list, node) {
|
||||
ret = cb(tz, data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct thermal_zone_device *thermal_zone_get_by_id(int id)
|
||||
{
|
||||
struct thermal_zone_device *tz, *match = NULL;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(tz, &thermal_tz_list, node) {
|
||||
if (tz->id == id) {
|
||||
match = tz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz,
|
||||
const char *cdev_type, size_t size)
|
||||
{
|
||||
@ -1340,6 +1493,8 @@ thermal_zone_device_register(const char *type, int trips, int mask,
|
||||
if (atomic_cmpxchg(&tz->need_update, 1, 0))
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
thermal_notify_tz_create(tz->id, tz->type);
|
||||
|
||||
return tz;
|
||||
|
||||
unregister:
|
||||
@ -1411,6 +1566,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
||||
ida_destroy(&tz->ida);
|
||||
mutex_destroy(&tz->lock);
|
||||
device_unregister(&tz->device);
|
||||
|
||||
thermal_notify_tz_delete(tz->id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
|
||||
|
||||
@ -1456,7 +1613,6 @@ static int thermal_pm_notify(struct notifier_block *nb,
|
||||
unsigned long mode, void *_unused)
|
||||
{
|
||||
struct thermal_zone_device *tz;
|
||||
enum thermal_device_mode tz_mode;
|
||||
|
||||
switch (mode) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
@ -1469,11 +1625,7 @@ static int thermal_pm_notify(struct notifier_block *nb,
|
||||
case PM_POST_SUSPEND:
|
||||
atomic_set(&in_suspend, 0);
|
||||
list_for_each_entry(tz, &thermal_tz_list, node) {
|
||||
tz_mode = THERMAL_DEVICE_ENABLED;
|
||||
if (tz->ops->get_mode)
|
||||
tz->ops->get_mode(tz, &tz_mode);
|
||||
|
||||
if (tz_mode == THERMAL_DEVICE_DISABLED)
|
||||
if (!thermal_zone_device_is_enabled(tz))
|
||||
continue;
|
||||
|
||||
thermal_zone_device_init(tz);
|
||||
@ -1495,6 +1647,10 @@ static int __init thermal_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = thermal_netlink_init();
|
||||
if (result)
|
||||
goto error;
|
||||
|
||||
mutex_init(&poweroff_lock);
|
||||
result = thermal_register_governors();
|
||||
if (result)
|
||||
@ -1527,4 +1683,4 @@ error:
|
||||
mutex_destroy(&poweroff_lock);
|
||||
return result;
|
||||
}
|
||||
core_initcall(thermal_init);
|
||||
postcore_initcall(thermal_init);
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_netlink.h"
|
||||
|
||||
/* Default Thermal Governor */
|
||||
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
|
||||
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
|
||||
@ -41,6 +43,17 @@ extern struct thermal_governor *__governor_thermal_table_end[];
|
||||
__governor < __governor_thermal_table_end; \
|
||||
__governor++)
|
||||
|
||||
int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *),
|
||||
void *);
|
||||
|
||||
int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
|
||||
void *), void *);
|
||||
|
||||
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||
void *thermal_governor);
|
||||
|
||||
struct thermal_zone_device *thermal_zone_get_by_id(int id);
|
||||
|
||||
struct thermal_attr {
|
||||
struct device_attribute attr;
|
||||
char name[THERMAL_NAME_LENGTH];
|
||||
@ -166,4 +179,6 @@ of_thermal_get_trip_points(struct thermal_zone_device *tz)
|
||||
}
|
||||
#endif
|
||||
|
||||
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz);
|
||||
|
||||
#endif /* __THERMAL_CORE_H__ */
|
||||
|
@ -175,6 +175,16 @@ exit:
|
||||
mutex_unlock(&tz->lock);
|
||||
}
|
||||
|
||||
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
int target)
|
||||
{
|
||||
if (cdev->ops->set_cur_state(cdev, target))
|
||||
return;
|
||||
|
||||
thermal_notify_cdev_state_update(cdev->id, target);
|
||||
thermal_cooling_device_stats_update(cdev, target);
|
||||
}
|
||||
|
||||
void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
@ -197,8 +207,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
target = instance->target;
|
||||
}
|
||||
|
||||
if (!cdev->ops->set_cur_state(cdev, target))
|
||||
thermal_cooling_device_stats_update(cdev, target);
|
||||
thermal_cdev_set_cur_state(cdev, target);
|
||||
|
||||
cdev->updated = true;
|
||||
mutex_unlock(&cdev->lock);
|
||||
|
647
drivers/thermal/thermal_netlink.c
Normal file
647
drivers/thermal/thermal_netlink.c
Normal file
@ -0,0 +1,647 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2020 Linaro Limited
|
||||
*
|
||||
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
*
|
||||
* Generic netlink for thermal management framework
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
static const struct genl_multicast_group thermal_genl_mcgrps[] = {
|
||||
{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
|
||||
{ .name = THERMAL_GENL_EVENT_GROUP_NAME, },
|
||||
};
|
||||
|
||||
static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
|
||||
/* Thermal zone */
|
||||
[THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING,
|
||||
.len = THERMAL_NAME_LENGTH },
|
||||
/* Governor(s) */
|
||||
[THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING,
|
||||
.len = THERMAL_NAME_LENGTH },
|
||||
/* Cooling devices */
|
||||
[THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED },
|
||||
[THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
|
||||
[THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING,
|
||||
.len = THERMAL_NAME_LENGTH },
|
||||
};
|
||||
|
||||
struct param {
|
||||
struct nlattr **attrs;
|
||||
struct sk_buff *msg;
|
||||
const char *name;
|
||||
int tz_id;
|
||||
int cdev_id;
|
||||
int trip_id;
|
||||
int trip_temp;
|
||||
int trip_type;
|
||||
int trip_hyst;
|
||||
int temp;
|
||||
int cdev_state;
|
||||
int cdev_max_state;
|
||||
};
|
||||
|
||||
typedef int (*cb_t)(struct param *);
|
||||
|
||||
static struct genl_family thermal_gnl_family;
|
||||
|
||||
/************************** Sampling encoding *******************************/
|
||||
|
||||
int thermal_genl_sampling_temp(int id, int temp)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *hdr;
|
||||
|
||||
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
|
||||
THERMAL_GENL_SAMPLING_TEMP);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
|
||||
goto out_cancel;
|
||||
|
||||
if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
|
||||
goto out_cancel;
|
||||
|
||||
genlmsg_end(skb, hdr);
|
||||
|
||||
genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
out_cancel:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
nlmsg_free(skb);
|
||||
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/**************************** Event encoding *********************************/
|
||||
|
||||
static int thermal_genl_event_tz_create(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_tz(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_tz_trip_up(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_tz_trip_add(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_tz_trip_delete(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_cdev_add(struct param *p)
|
||||
{
|
||||
if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
|
||||
p->name) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
|
||||
p->cdev_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
|
||||
p->cdev_max_state))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_cdev_delete(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_cdev_state_update(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
|
||||
p->cdev_id) ||
|
||||
nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
|
||||
p->cdev_state))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_event_gov_change(struct param *p)
|
||||
{
|
||||
if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
|
||||
nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_genl_event_tz_delete(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz")));
|
||||
|
||||
int thermal_genl_event_tz_enable(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz")));
|
||||
|
||||
int thermal_genl_event_tz_disable(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz")));
|
||||
|
||||
int thermal_genl_event_tz_trip_down(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz_trip_up")));
|
||||
|
||||
int thermal_genl_event_tz_trip_change(struct param *p)
|
||||
__attribute__((alias("thermal_genl_event_tz_trip_add")));
|
||||
|
||||
static cb_t event_cb[] = {
|
||||
[THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create,
|
||||
[THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete,
|
||||
[THERMAL_GENL_EVENT_TZ_ENABLE] = thermal_genl_event_tz_enable,
|
||||
[THERMAL_GENL_EVENT_TZ_DISABLE] = thermal_genl_event_tz_disable,
|
||||
[THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up,
|
||||
[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down,
|
||||
[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change,
|
||||
[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add,
|
||||
[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete,
|
||||
[THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add,
|
||||
[THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete,
|
||||
[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update,
|
||||
[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change,
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic netlink event encoding
|
||||
*/
|
||||
static int thermal_genl_send_event(enum thermal_genl_event event,
|
||||
struct param *p)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
int ret = -EMSGSIZE;
|
||||
void *hdr;
|
||||
|
||||
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
p->msg = msg;
|
||||
|
||||
hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
ret = event_cb[event](p);
|
||||
if (ret)
|
||||
goto out_cancel_msg;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_msg:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int thermal_notify_tz_create(int tz_id, const char *name)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .name = name };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_delete(int tz_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_enable(int tz_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_disable(int tz_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_trip_down(int tz_id, int trip_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .trip_id = trip_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_trip_up(int tz_id, int trip_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .trip_id = trip_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
|
||||
int trip_temp, int trip_hyst)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .trip_id = trip_id,
|
||||
.trip_type = trip_type, .trip_temp = trip_temp,
|
||||
.trip_hyst = trip_hyst };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .trip_id = trip_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
|
||||
int trip_temp, int trip_hyst)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .trip_id = trip_id,
|
||||
.trip_type = trip_type, .trip_temp = trip_temp,
|
||||
.trip_hyst = trip_hyst };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
|
||||
{
|
||||
struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
|
||||
{
|
||||
struct param p = { .cdev_id = cdev_id, .name = name,
|
||||
.cdev_max_state = cdev_max_state };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_cdev_delete(int cdev_id)
|
||||
{
|
||||
struct param p = { .cdev_id = cdev_id };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
|
||||
}
|
||||
|
||||
int thermal_notify_tz_gov_change(int tz_id, const char *name)
|
||||
{
|
||||
struct param p = { .tz_id = tz_id, .name = name };
|
||||
|
||||
return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
|
||||
}
|
||||
|
||||
/*************************** Command encoding ********************************/
|
||||
|
||||
static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
|
||||
void *data)
|
||||
{
|
||||
struct sk_buff *msg = data;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
|
||||
nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_tz_get_id(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct nlattr *start_tz;
|
||||
int ret;
|
||||
|
||||
start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
|
||||
if (!start_tz)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
|
||||
if (ret)
|
||||
goto out_cancel_nest;
|
||||
|
||||
nla_nest_end(msg, start_tz);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_nest:
|
||||
nla_nest_cancel(msg, start_tz);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct thermal_zone_device *tz;
|
||||
struct nlattr *start_trip;
|
||||
int i, id;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
|
||||
tz = thermal_zone_get_by_id(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
|
||||
if (!start_trip)
|
||||
return -EMSGSIZE;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for (i = 0; i < tz->trips; i++) {
|
||||
|
||||
enum thermal_trip_type type;
|
||||
int temp, hyst;
|
||||
|
||||
tz->ops->get_trip_type(tz, i, &type);
|
||||
tz->ops->get_trip_temp(tz, i, &temp);
|
||||
tz->ops->get_trip_hyst(tz, i, &hyst);
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
|
||||
goto out_cancel_nest;
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
nla_nest_end(msg, start_trip);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_nest:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_tz_get_temp(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct thermal_zone_device *tz;
|
||||
int temp, ret, id;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
|
||||
tz = thermal_zone_get_by_id(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = thermal_zone_get_temp(tz, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_tz_get_gov(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct thermal_zone_device *tz;
|
||||
int id, ret = 0;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
|
||||
|
||||
tz = thermal_zone_get_by_id(id);
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
|
||||
nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
|
||||
tz->governor->name))
|
||||
ret = -EMSGSIZE;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
|
||||
void *data)
|
||||
{
|
||||
struct sk_buff *msg = data;
|
||||
|
||||
if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_cdev_get(struct param *p)
|
||||
{
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct nlattr *start_cdev;
|
||||
int ret;
|
||||
|
||||
start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
|
||||
if (!start_cdev)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
|
||||
if (ret)
|
||||
goto out_cancel_nest;
|
||||
|
||||
nla_nest_end(msg, start_cdev);
|
||||
|
||||
return 0;
|
||||
out_cancel_nest:
|
||||
nla_nest_cancel(msg, start_cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cb_t cmd_cb[] = {
|
||||
[THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id,
|
||||
[THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip,
|
||||
[THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp,
|
||||
[THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov,
|
||||
[THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
|
||||
};
|
||||
|
||||
static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct param p = { .msg = skb };
|
||||
const struct genl_dumpit_info *info = genl_dumpit_info(cb);
|
||||
int cmd = info->ops->cmd;
|
||||
int ret;
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = cmd_cb[cmd](&p);
|
||||
if (ret)
|
||||
goto out_cancel_msg;
|
||||
|
||||
genlmsg_end(skb, hdr);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_msg:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int thermal_genl_cmd_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct param p = { .attrs = info->attrs };
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
int cmd = info->genlhdr->cmd;
|
||||
int ret = -EMSGSIZE;
|
||||
|
||||
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
p.msg = msg;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
ret = cmd_cb[cmd](&p);
|
||||
if (ret)
|
||||
goto out_cancel_msg;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
out_cancel_msg:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct genl_ops thermal_genl_ops[] = {
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.dumpit = thermal_genl_cmd_dumpit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = thermal_genl_cmd_doit,
|
||||
},
|
||||
{
|
||||
.cmd = THERMAL_GENL_CMD_CDEV_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.dumpit = thermal_genl_cmd_dumpit,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family thermal_gnl_family __ro_after_init = {
|
||||
.hdrsize = 0,
|
||||
.name = THERMAL_GENL_FAMILY_NAME,
|
||||
.version = THERMAL_GENL_VERSION,
|
||||
.maxattr = THERMAL_GENL_ATTR_MAX,
|
||||
.policy = thermal_genl_policy,
|
||||
.ops = thermal_genl_ops,
|
||||
.n_ops = ARRAY_SIZE(thermal_genl_ops),
|
||||
.mcgrps = thermal_genl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
|
||||
};
|
||||
|
||||
int __init thermal_netlink_init(void)
|
||||
{
|
||||
return genl_register_family(&thermal_gnl_family);
|
||||
}
|
104
drivers/thermal/thermal_netlink.h
Normal file
104
drivers/thermal/thermal_netlink.h
Normal file
@ -0,0 +1,104 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) Linaro Ltd 2020
|
||||
* Author: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
*/
|
||||
|
||||
/* Netlink notification function */
|
||||
#ifdef CONFIG_THERMAL_NETLINK
|
||||
int __init thermal_netlink_init(void);
|
||||
int thermal_notify_tz_create(int tz_id, const char *name);
|
||||
int thermal_notify_tz_delete(int tz_id);
|
||||
int thermal_notify_tz_enable(int tz_id);
|
||||
int thermal_notify_tz_disable(int tz_id);
|
||||
int thermal_notify_tz_trip_down(int tz_id, int id);
|
||||
int thermal_notify_tz_trip_up(int tz_id, int id);
|
||||
int thermal_notify_tz_trip_delete(int tz_id, int id);
|
||||
int thermal_notify_tz_trip_add(int tz_id, int id, int type,
|
||||
int temp, int hyst);
|
||||
int thermal_notify_tz_trip_change(int tz_id, int id, int type,
|
||||
int temp, int hyst);
|
||||
int thermal_notify_cdev_state_update(int cdev_id, int state);
|
||||
int thermal_notify_cdev_add(int cdev_id, const char *name, int max_state);
|
||||
int thermal_notify_cdev_delete(int cdev_id);
|
||||
int thermal_notify_tz_gov_change(int tz_id, const char *name);
|
||||
int thermal_genl_sampling_temp(int id, int temp);
|
||||
#else
|
||||
static inline int thermal_netlink_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_create(int tz_id, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_delete(int tz_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_enable(int tz_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_disable(int tz_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_trip_down(int tz_id, int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_trip_up(int tz_id, int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_trip_delete(int tz_id, int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_trip_add(int tz_id, int id, int type,
|
||||
int temp, int hyst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_trip_change(int tz_id, int id, int type,
|
||||
int temp, int hyst)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_cdev_state_update(int cdev_id, int state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_cdev_add(int cdev_id, const char *name,
|
||||
int max_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_cdev_delete(int cdev_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_notify_tz_gov_change(int tz_id, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int thermal_genl_sampling_temp(int id, int temp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_THERMAL_NETLINK */
|
@ -51,7 +51,6 @@ struct __thermal_bind_params {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @slope: slope of the temperature adjustment curve
|
||||
@ -65,7 +64,6 @@ struct __thermal_bind_params {
|
||||
*/
|
||||
|
||||
struct __thermal_zone {
|
||||
enum thermal_device_mode mode;
|
||||
int passive_delay;
|
||||
int polling_delay;
|
||||
int slope;
|
||||
@ -269,39 +267,6 @@ static int of_thermal_unbind(struct thermal_zone_device *thermal,
|
||||
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;
|
||||
tz->passive_delay = data->passive_delay;
|
||||
} else {
|
||||
tz->polling_delay = 0;
|
||||
tz->passive_delay = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
data->mode = mode;
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
@ -393,9 +358,6 @@ static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
|
||||
}
|
||||
|
||||
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,
|
||||
@ -554,7 +516,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
|
||||
tzd = thermal_zone_of_add_sensor(child, sensor_np,
|
||||
data, ops);
|
||||
if (!IS_ERR(tzd))
|
||||
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
|
||||
thermal_zone_device_enable(tzd);
|
||||
|
||||
of_node_put(child);
|
||||
goto exit;
|
||||
@ -979,7 +941,6 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
|
||||
|
||||
finish:
|
||||
of_node_put(child);
|
||||
tz->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return tz;
|
||||
|
||||
|
@ -49,18 +49,9 @@ static ssize_t
|
||||
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
enum thermal_device_mode mode;
|
||||
int result;
|
||||
int enabled = thermal_zone_device_is_enabled(tz);
|
||||
|
||||
if (!tz->ops->get_mode)
|
||||
return -EPERM;
|
||||
|
||||
result = tz->ops->get_mode(tz, &mode);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
|
||||
: "disabled");
|
||||
return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -70,13 +61,10 @@ mode_store(struct device *dev, struct device_attribute *attr,
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int result;
|
||||
|
||||
if (!tz->ops->set_mode)
|
||||
return -EPERM;
|
||||
|
||||
if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
|
||||
result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
|
||||
result = thermal_zone_device_enable(tz);
|
||||
else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
|
||||
result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
|
||||
result = thermal_zone_device_disable(tz);
|
||||
else
|
||||
result = -EINVAL;
|
||||
|
||||
@ -124,7 +112,8 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip, ret;
|
||||
int temperature;
|
||||
int temperature, hyst = 0;
|
||||
enum thermal_trip_type type;
|
||||
|
||||
if (!tz->ops->set_trip_temp)
|
||||
return -EPERM;
|
||||
@ -139,6 +128,18 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, trip, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst);
|
||||
|
||||
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return count;
|
||||
@ -428,30 +429,13 @@ static struct attribute_group thermal_zone_attribute_group = {
|
||||
.attrs = thermal_zone_dev_attrs,
|
||||
};
|
||||
|
||||
/* We expose mode only if .get_mode is present */
|
||||
static struct attribute *thermal_zone_mode_attrs[] = {
|
||||
&dev_attr_mode.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t thermal_zone_mode_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int attrno)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct thermal_zone_device *tz;
|
||||
|
||||
tz = container_of(dev, struct thermal_zone_device, device);
|
||||
|
||||
if (tz->ops->get_mode)
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct attribute_group thermal_zone_mode_attribute_group = {
|
||||
.attrs = thermal_zone_mode_attrs,
|
||||
.is_visible = thermal_zone_mode_is_visible,
|
||||
};
|
||||
|
||||
/* We expose passive only if passive trips are present */
|
||||
|
@ -169,7 +169,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
|
||||
|
||||
data = ti_bandgap_get_sensor_data(bgp, id);
|
||||
|
||||
if (!IS_ERR_OR_NULL(data))
|
||||
if (IS_ERR_OR_NULL(data))
|
||||
data = ti_thermal_build_data(bgp, id);
|
||||
|
||||
if (!data)
|
||||
|
@ -1,57 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* linux/include/linux/clock_cooling.h
|
||||
*
|
||||
* Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Inc.
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* Highly based on cpufreq_cooling.c.
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
|
||||
*/
|
||||
|
||||
#ifndef __CPU_COOLING_H__
|
||||
#define __CPU_COOLING_H__
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/cpumask.h>
|
||||
|
||||
#ifdef CONFIG_CLOCK_THERMAL
|
||||
/**
|
||||
* clock_cooling_register - function to create clock cooling device.
|
||||
* @dev: struct device pointer to the device used as clock cooling device.
|
||||
* @clock_name: string containing the clock used as cooling mechanism.
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
clock_cooling_register(struct device *dev, const char *clock_name);
|
||||
|
||||
/**
|
||||
* clock_cooling_unregister - function to remove clock cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
*/
|
||||
void clock_cooling_unregister(struct thermal_cooling_device *cdev);
|
||||
|
||||
unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
|
||||
unsigned long freq);
|
||||
#else /* !CONFIG_CLOCK_THERMAL */
|
||||
static inline struct thermal_cooling_device *
|
||||
clock_cooling_register(struct device *dev, const char *clock_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline
|
||||
void clock_cooling_unregister(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
}
|
||||
static inline
|
||||
unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
|
||||
unsigned long freq)
|
||||
{
|
||||
return THERMAL_CSTATE_INVALID;
|
||||
}
|
||||
#endif /* CONFIG_CLOCK_THERMAL */
|
||||
|
||||
#endif /* __CPU_COOLING_H__ */
|
@ -1,17 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* devfreq_cooling: Thermal cooling device implementation for devices using
|
||||
* devfreq
|
||||
*
|
||||
* Copyright (C) 2014-2015 ARM Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DEVFREQ_COOLING_H__
|
||||
|
@ -37,18 +37,6 @@ struct thermal_cooling_device;
|
||||
struct thermal_instance;
|
||||
struct thermal_attr;
|
||||
|
||||
enum thermal_device_mode {
|
||||
THERMAL_DEVICE_DISABLED = 0,
|
||||
THERMAL_DEVICE_ENABLED,
|
||||
};
|
||||
|
||||
enum thermal_trip_type {
|
||||
THERMAL_TRIP_ACTIVE = 0,
|
||||
THERMAL_TRIP_PASSIVE,
|
||||
THERMAL_TRIP_HOT,
|
||||
THERMAL_TRIP_CRITICAL,
|
||||
};
|
||||
|
||||
enum thermal_trend {
|
||||
THERMAL_TREND_STABLE, /* temperature is stable */
|
||||
THERMAL_TREND_RAISING, /* temperature is raising */
|
||||
@ -76,9 +64,7 @@ struct thermal_zone_device_ops {
|
||||
struct thermal_cooling_device *);
|
||||
int (*get_temp) (struct thermal_zone_device *, int *);
|
||||
int (*set_trips) (struct thermal_zone_device *, int, int);
|
||||
int (*get_mode) (struct thermal_zone_device *,
|
||||
enum thermal_device_mode *);
|
||||
int (*set_mode) (struct thermal_zone_device *,
|
||||
int (*change_mode) (struct thermal_zone_device *,
|
||||
enum thermal_device_mode);
|
||||
int (*get_trip_type) (struct thermal_zone_device *, int,
|
||||
enum thermal_trip_type *);
|
||||
@ -128,6 +114,7 @@ struct thermal_cooling_device {
|
||||
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
|
||||
* @trip_type_attrs: attributes for trip points for sysfs: trip type
|
||||
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
|
||||
* @mode: current mode of this thermal zone
|
||||
* @devdata: private pointer for device private data
|
||||
* @trips: number of trip points the thermal zone supports
|
||||
* @trips_disabled; bitmap for disabled trips
|
||||
@ -170,6 +157,7 @@ struct thermal_zone_device {
|
||||
struct thermal_attr *trip_temp_attrs;
|
||||
struct thermal_attr *trip_type_attrs;
|
||||
struct thermal_attr *trip_hyst_attrs;
|
||||
enum thermal_device_mode mode;
|
||||
void *devdata;
|
||||
int trips;
|
||||
unsigned long trips_disabled; /* bitmap for disabled trips */
|
||||
@ -303,11 +291,6 @@ struct thermal_zone_params {
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct thermal_genl_event {
|
||||
u32 orig;
|
||||
enum events event;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thermal_zone_of_device_ops - scallbacks for handling DT based zones
|
||||
*
|
||||
@ -416,6 +399,8 @@ int thermal_zone_get_offset(struct thermal_zone_device *tz);
|
||||
|
||||
void thermal_cdev_update(struct thermal_cooling_device *);
|
||||
void thermal_notify_framework(struct thermal_zone_device *, int);
|
||||
int thermal_zone_device_enable(struct thermal_zone_device *tz);
|
||||
int thermal_zone_device_disable(struct thermal_zone_device *tz);
|
||||
#else
|
||||
static inline struct thermal_zone_device *thermal_zone_device_register(
|
||||
const char *type, int trips, int mask, void *devdata,
|
||||
@ -463,6 +448,12 @@ static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
|
||||
int trip)
|
||||
{ }
|
||||
|
||||
static inline int thermal_zone_device_enable(struct thermal_zone_device *tz)
|
||||
{ return -ENODEV; }
|
||||
|
||||
static inline int thermal_zone_device_disable(struct thermal_zone_device *tz)
|
||||
{ return -ENODEV; }
|
||||
#endif /* CONFIG_THERMAL */
|
||||
|
||||
#endif /* __THERMAL_H__ */
|
||||
|
@ -4,31 +4,86 @@
|
||||
|
||||
#define THERMAL_NAME_LENGTH 20
|
||||
|
||||
/* Adding event notification support elements */
|
||||
#define THERMAL_GENL_FAMILY_NAME "thermal_event"
|
||||
#define THERMAL_GENL_VERSION 0x01
|
||||
#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_grp"
|
||||
|
||||
/* Events supported by Thermal Netlink */
|
||||
enum events {
|
||||
THERMAL_AUX0,
|
||||
THERMAL_AUX1,
|
||||
THERMAL_CRITICAL,
|
||||
THERMAL_DEV_FAULT,
|
||||
enum thermal_device_mode {
|
||||
THERMAL_DEVICE_DISABLED = 0,
|
||||
THERMAL_DEVICE_ENABLED,
|
||||
};
|
||||
|
||||
/* attributes of thermal_genl_family */
|
||||
enum {
|
||||
enum thermal_trip_type {
|
||||
THERMAL_TRIP_ACTIVE = 0,
|
||||
THERMAL_TRIP_PASSIVE,
|
||||
THERMAL_TRIP_HOT,
|
||||
THERMAL_TRIP_CRITICAL,
|
||||
};
|
||||
|
||||
/* Adding event notification support elements */
|
||||
#define THERMAL_GENL_FAMILY_NAME "thermal"
|
||||
#define THERMAL_GENL_VERSION 0x01
|
||||
#define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling"
|
||||
#define THERMAL_GENL_EVENT_GROUP_NAME "event"
|
||||
|
||||
/* Attributes of thermal_genl_family */
|
||||
enum thermal_genl_attr {
|
||||
THERMAL_GENL_ATTR_UNSPEC,
|
||||
THERMAL_GENL_ATTR_EVENT,
|
||||
THERMAL_GENL_ATTR_TZ,
|
||||
THERMAL_GENL_ATTR_TZ_ID,
|
||||
THERMAL_GENL_ATTR_TZ_TEMP,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_ID,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_TEMP,
|
||||
THERMAL_GENL_ATTR_TZ_TRIP_HYST,
|
||||
THERMAL_GENL_ATTR_TZ_MODE,
|
||||
THERMAL_GENL_ATTR_TZ_NAME,
|
||||
THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT,
|
||||
THERMAL_GENL_ATTR_TZ_GOV,
|
||||
THERMAL_GENL_ATTR_TZ_GOV_NAME,
|
||||
THERMAL_GENL_ATTR_CDEV,
|
||||
THERMAL_GENL_ATTR_CDEV_ID,
|
||||
THERMAL_GENL_ATTR_CDEV_CUR_STATE,
|
||||
THERMAL_GENL_ATTR_CDEV_MAX_STATE,
|
||||
THERMAL_GENL_ATTR_CDEV_NAME,
|
||||
THERMAL_GENL_ATTR_GOV_NAME,
|
||||
|
||||
__THERMAL_GENL_ATTR_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
|
||||
|
||||
/* commands supported by the thermal_genl_family */
|
||||
enum {
|
||||
enum thermal_genl_sampling {
|
||||
THERMAL_GENL_SAMPLING_TEMP,
|
||||
__THERMAL_GENL_SAMPLING_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_SAMPLING_MAX (__THERMAL_GENL_SAMPLING_MAX - 1)
|
||||
|
||||
/* Events of thermal_genl_family */
|
||||
enum thermal_genl_event {
|
||||
THERMAL_GENL_EVENT_UNSPEC,
|
||||
THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone creation */
|
||||
THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone deletion */
|
||||
THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone disabed */
|
||||
THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone enabled */
|
||||
THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point crossed the way up */
|
||||
THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point crossed the way down */
|
||||
THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, /* Trip point changed */
|
||||
THERMAL_GENL_EVENT_TZ_TRIP_ADD, /* Trip point added */
|
||||
THERMAL_GENL_EVENT_TZ_TRIP_DELETE, /* Trip point deleted */
|
||||
THERMAL_GENL_EVENT_CDEV_ADD, /* Cdev bound to the thermal zone */
|
||||
THERMAL_GENL_EVENT_CDEV_DELETE, /* Cdev unbound */
|
||||
THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */
|
||||
THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */
|
||||
__THERMAL_GENL_EVENT_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
|
||||
|
||||
/* Commands supported by the thermal_genl_family */
|
||||
enum thermal_genl_cmd {
|
||||
THERMAL_GENL_CMD_UNSPEC,
|
||||
THERMAL_GENL_CMD_EVENT,
|
||||
THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */
|
||||
THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */
|
||||
THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */
|
||||
THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */
|
||||
THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */
|
||||
THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */
|
||||
__THERMAL_GENL_CMD_MAX,
|
||||
};
|
||||
#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)
|
||||
|
@ -1192,7 +1192,7 @@ problem:
|
||||
panic("GENL: Cannot register controller: %d\n", err);
|
||||
}
|
||||
|
||||
subsys_initcall(genl_init);
|
||||
core_initcall(genl_init);
|
||||
|
||||
static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
|
||||
gfp_t flags)
|
||||
|
Loading…
Reference in New Issue
Block a user