Thermal control updates for 6.8-rc1

- Add dynamic thresholds for trip point crossing detection to prevent
    trip point crossing notifications from being sent at incorrect times
    or not at all in some cases (Rafael J. Wysocki).
 
  - Fix synchronization issues related to the resume of thermal zones
    during a system-wide resume and allow thermal zones to be resumed
    concurrently (Rafael J. Wysocki).
 
  - Modify the thermal zone unregistration to wait for the given zone to
    go away completely before returning to the caller and rework the
    sysfs interface for trip points on top of that (Rafael J. Wysocki).
 
  - Fix a possible NULL pointer dereference in thermal zone registration
    error path (Rafael J. Wysocki).
 
  - Clean up the IPA thermal governor and modify it (with the help of a
    new governor callback) to avoid allocating and freeing memory every
    time its throttling callback is invoked (Lukasz Luba).
 
  - Make the IPA thermal governor handle thermal instance weight changes
    via sysfs correctly (Lukasz Luba).
 
  - Update the thermal netlink code to avoid sending messages if there
    are no recipients (Stanislaw Gruszka).
 
  - Convert Mediatek Thermal to the json-schema (Rafał Miłecki).
 
  - Fix thermal DT bindings issue on Loongson (Binbin Zhou).
 
  - Fix returning NULL instead of -ENODEV during thermal probe on
    Loogsoon (Binbin Zhou).
 
  - Add thermal DT binding for tsens on the SM8650 platform (Neil
    Armstrong).
 
  - Add reboot on the critical trip point crossing option feature (Fabio
    Estevam).
 
  - Use DEFINE_SIMPLE_DEV_PM_OPS do define PM functions for thermal
    suspend/resume on AmLogic (Uwe Kleine-König)
 
  - Add D1/T113s THS controller support to the Sun8i thermal control
    driver (Maxim Kiselev)
 
  - Fix example in the thermal DT binding for QCom SPMI (Johan Hovold).
 
  - Fix compilation warning in the tmon utility (Florian Eckert).
 
  - Add support for interrupt-based thermal configuration on Exynos along
    with a set of related cleanups (Mateusz Majewski).
 
  - Make the Intel HFI thermal driver enable an HFI instance (eg. processor
    package) from its first online CPU and disable it when the last CPU in
    it goes offline (Ricardo Neri).
 
  - Fix a kernel-doc warning and a spello in the cpuidle_cooling thermal
    driver (Randy Dunlap).
 
  - Move the .get_temp() thermal zone callback presence check to the
    thermal zone registration code (Daniel Lezcano).
 
  - Use the for_each_trip() macro for trip points table walks in a few
    places in the thermal core (Rafael J. Wysocki).
 
  - Make all trip point updates (via sysfs as well as from the platform
    firmware) trigger trip change notifications (Rafael J. Wysocki).
 
  - Drop redundant code from the thermal core and make one function in
    it take a const pointer argument (Rafael J. Wysocki).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmWb8iUSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxKLkP/iDsuDwmhZAjbAu2iftk/8ad8Trm2VoK
 +9eZ5Eqa8lKEcJLb0RxueTnFT4ppvT/hY99HOG4FM+mCnWeH/Z32N697DhqiUg4v
 GZUpeOPzxYgsfOOTeuL5XgfrVMgBjJrJunTXmzgAd8lIhTmRbAMVmFVJ18CJO11O
 RHgqvYznYFi5cywA9/NkG2xkhFB0VDoiTuIiuMMV+pMjqF0d5ooBMkhmjvPQ5Rp9
 FjNJ7hqiTamAsDPdULAFqhIGGhKZWWFbh4+S+JPCwBW8nqvxyJpemsm20vrwctJR
 bSXWQkgkDpWEeg9yrEAOO/Uk9yGd3jiLfkvPBKbK0x/YxGZ4hOYHcbF3cOUvmPYP
 5K3ZJ61DNrzB/5S3LY54VYrWmTVRdK6Lk3HYNvfAUYFJZMZ5oMYZLCUmo4SswUdy
 UUEIY27H7L18eLhP9zCcKo4njdaVG+vXQn/rJIFOpG0k9OElzPs1X8Dp/m9pKQDR
 rDUsMXqB34NUVrIEhjAgqvwF5xHooW8gykpuJgxwBetA9w8Pls2A/mzLsDY3wgdQ
 htiANGpKTDqBQSn+HrjzYckv9/R+1tDyTJmEDNZwllA1DJfrOlpCRD2VHRpgTZEA
 Ldnq0bhyq6RQnousqxhgpYkIAoGaebs9XasRH0YtBG5gIumeWfqeVzmTcM5xdsNB
 yf6RdQy8QunS
 =QVyh
 -----END PGP SIGNATURE-----

Merge tag 'thermal-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull thermal control updates from Rafael Wysocki:
 "These add support for the D1/T113s THS controller to the sun8i driver
  and a DT-based mechanism for platforms to indicate a preference to
  reboot (instead of shutting down) on crossing a critical trip point,
  fix issues, make other improvements (in the IPA governor, the Intel
  HFI driver, the exynos driver and the thermal netlink interface among
  other places) and clean up code.

  One long-standing issue addressed here is that trip point crossing
  notifications sent to user space might be unreliable due to the
  incorrect handling of trip point hysteresis in the thermal core:
  multiple notifications might be sent for the same event or there might
  be events without any notification at all.

  Specifics:

   - Add dynamic thresholds for trip point crossing detection to prevent
     trip point crossing notifications from being sent at incorrect
     times or not at all in some cases (Rafael J. Wysocki)

   - Fix synchronization issues related to the resume of thermal zones
     during a system-wide resume and allow thermal zones to be resumed
     concurrently (Rafael J. Wysocki)

   - Modify the thermal zone unregistration to wait for the given zone
     to go away completely before returning to the caller and rework the
     sysfs interface for trip points on top of that (Rafael J. Wysocki)

   - Fix a possible NULL pointer dereference in thermal zone
     registration error path (Rafael J. Wysocki)

   - Clean up the IPA thermal governor and modify it (with the help of a
     new governor callback) to avoid allocating and freeing memory every
     time its throttling callback is invoked (Lukasz Luba)

   - Make the IPA thermal governor handle thermal instance weight
     changes via sysfs correctly (Lukasz Luba)

   - Update the thermal netlink code to avoid sending messages if there
     are no recipients (Stanislaw Gruszka)

   - Convert Mediatek Thermal to the json-schema (Rafał Miłecki)

   - Fix thermal DT bindings issue on Loongson (Binbin Zhou)

   - Fix returning NULL instead of -ENODEV during thermal probe on
     Loogsoon (Binbin Zhou)

   - Add thermal DT binding for tsens on the SM8650 platform (Neil
     Armstrong)

   - Add reboot on the critical trip point crossing option feature
     (Fabio Estevam)

   - Use DEFINE_SIMPLE_DEV_PM_OPS do define PM functions for thermal
     suspend/resume on AmLogic (Uwe Kleine-König)

   - Add D1/T113s THS controller support to the Sun8i thermal control
     driver (Maxim Kiselev)

   - Fix example in the thermal DT binding for QCom SPMI (Johan Hovold)

   - Fix compilation warning in the tmon utility (Florian Eckert)

   - Add support for interrupt-based thermal configuration on Exynos
     along with a set of related cleanups (Mateusz Majewski)

   - Make the Intel HFI thermal driver enable an HFI instance (eg.
     processor package) from its first online CPU and disable it when
     the last CPU in it goes offline (Ricardo Neri)

   - Fix a kernel-doc warning and a spello in the cpuidle_cooling
     thermal driver (Randy Dunlap)

   - Move the .get_temp() thermal zone callback presence check to the
     thermal zone registration code (Daniel Lezcano)

   - Use the for_each_trip() macro for trip points table walks in a few
     places in the thermal core (Rafael J. Wysocki)

   - Make all trip point updates (via sysfs as well as from the platform
     firmware) trigger trip change notifications (Rafael J. Wysocki)

   - Drop redundant code from the thermal core and make one function in
     it take a const pointer argument (Rafael J. Wysocki)"

* tag 'thermal-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (64 commits)
  thermal: trip: Constify thermal zone argument of thermal_zone_trip_id()
  thermal: intel: hfi: Disable an HFI instance when all its CPUs go offline
  thermal: intel: hfi: Enable an HFI instance from its first online CPU
  thermal: intel: hfi: Refactor enabling code into helper functions
  thermal/drivers/exynos: Use set_trips ops
  thermal/drivers/exynos: Use BIT wherever possible
  thermal/drivers/exynos: Split initialization of TMU and the thermal zone
  thermal/drivers/exynos: Stop using the threshold mechanism on Exynos 4210
  thermal/drivers/exynos: Simplify regulator (de)initialization
  thermal/drivers/exynos: Handle devm_regulator_get_optional return value correctly
  thermal/drivers/exynos: Wwitch from workqueue-driven interrupt handling to threaded interrupts
  thermal/drivers/exynos: Drop id field
  thermal/drivers/exynos: Remove an unnecessary field description
  tools/thermal/tmon: Fix compilation warning for wrong format
  dt-bindings: thermal: qcom-spmi-adc-tm5/hc: Clean up examples
  dt-bindings: thermal: qcom-spmi-adc-tm5/hc: Fix example node names
  thermal/drivers/sun8i: Add D1/T113s THS controller support
  dt-bindings: thermal: sun8i: Add binding for D1/T113s THS controller
  thermal: amlogic: Use DEFINE_SIMPLE_DEV_PM_OPS for PM functions
  thermal: amlogic: Make amlogic_thermal_disable() return void
  ...
This commit is contained in:
Linus Torvalds 2024-01-09 16:20:17 -08:00
commit 7f73ba68cf
30 changed files with 1086 additions and 776 deletions

View File

@ -16,6 +16,7 @@ properties:
- allwinner,sun8i-a83t-ths
- allwinner,sun8i-h3-ths
- allwinner,sun8i-r40-ths
- allwinner,sun20i-d1-ths
- allwinner,sun50i-a64-ths
- allwinner,sun50i-a100-ths
- allwinner,sun50i-h5-ths
@ -61,6 +62,7 @@ allOf:
compatible:
contains:
enum:
- allwinner,sun20i-d1-ths
- allwinner,sun50i-a100-ths
- allwinner,sun50i-h6-ths
@ -84,7 +86,9 @@ allOf:
properties:
compatible:
contains:
const: allwinner,sun8i-h3-ths
enum:
- allwinner,sun8i-h3-ths
- allwinner,sun20i-d1-ths
then:
properties:
@ -103,6 +107,7 @@ allOf:
enum:
- allwinner,sun8i-h3-ths
- allwinner,sun8i-r40-ths
- allwinner,sun20i-d1-ths
- allwinner,sun50i-a64-ths
- allwinner,sun50i-a100-ths
- allwinner,sun50i-h5-ths

View File

@ -10,6 +10,9 @@ maintainers:
- zhanghongchen <zhanghongchen@loongson.cn>
- Yinbo Zhu <zhuyinbo@loongson.cn>
allOf:
- $ref: /schemas/thermal/thermal-sensor.yaml#
properties:
compatible:
oneOf:
@ -26,12 +29,16 @@ properties:
interrupts:
maxItems: 1
'#thermal-sensor-cells':
const: 1
required:
- compatible
- reg
- interrupts
- '#thermal-sensor-cells'
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@ -41,4 +48,5 @@ examples:
reg = <0x1fe01500 0x30>;
interrupt-parent = <&liointc0>;
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
#thermal-sensor-cells = <1>;
};

View File

@ -0,0 +1,99 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/mediatek,thermal.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek thermal controller for on-SoC temperatures
maintainers:
- Sascha Hauer <s.hauer@pengutronix.de>
description:
This device does not have its own ADC, instead it directly controls the AUXADC
via AHB bus accesses. For this reason it needs phandles to the AUXADC. Also it
controls a mux in the apmixedsys register space via AHB bus accesses, so a
phandle to the APMIXEDSYS is also needed.
allOf:
- $ref: thermal-sensor.yaml#
properties:
compatible:
enum:
- mediatek,mt2701-thermal
- mediatek,mt2712-thermal
- mediatek,mt7622-thermal
- mediatek,mt7981-thermal
- mediatek,mt7986-thermal
- mediatek,mt8173-thermal
- mediatek,mt8183-thermal
- mediatek,mt8365-thermal
- mediatek,mt8516-thermal
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: Main clock needed for register access
- description: The AUXADC clock
clock-names:
items:
- const: therm
- const: auxadc
mediatek,auxadc:
$ref: /schemas/types.yaml#/definitions/phandle
description: A phandle to the AUXADC which the thermal controller uses
mediatek,apmixedsys:
$ref: /schemas/types.yaml#/definitions/phandle
description: A phandle to the APMIXEDSYS controller
resets:
description: Reset controller controlling the thermal controller
nvmem-cells:
items:
- description:
NVMEM cell with EEPROMA phandle to the calibration data provided by an
NVMEM device. If unspecified default values shall be used.
nvmem-cell-names:
items:
- const: calibration-data
required:
- reg
- interrupts
- clocks
- clock-names
- mediatek,auxadc
- mediatek,apmixedsys
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/reset/mt8173-resets.h>
thermal@1100b000 {
compatible = "mediatek,mt8173-thermal";
reg = <0x1100b000 0x1000>;
interrupts = <0 70 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_THERM>, <&pericfg CLK_PERI_AUXADC>;
clock-names = "therm", "auxadc";
resets = <&pericfg MT8173_PERI_THERM_SW_RST>;
mediatek,auxadc = <&auxadc>;
mediatek,apmixedsys = <&apmixedsys>;
nvmem-cells = <&thermal_calibration_data>;
nvmem-cell-names = "calibration-data";
#thermal-sensor-cells = <1>;
};

View File

@ -1,52 +0,0 @@
* Mediatek Thermal
This describes the device tree binding for the Mediatek thermal controller
which measures the on-SoC temperatures. This device does not have its own ADC,
instead it directly controls the AUXADC via AHB bus accesses. For this reason
this device needs phandles to the AUXADC. Also it controls a mux in the
apmixedsys register space via AHB bus accesses, so a phandle to the APMIXEDSYS
is also needed.
Required properties:
- compatible:
- "mediatek,mt8173-thermal" : For MT8173 family of SoCs
- "mediatek,mt2701-thermal" : For MT2701 family of SoCs
- "mediatek,mt2712-thermal" : For MT2712 family of SoCs
- "mediatek,mt7622-thermal" : For MT7622 SoC
- "mediatek,mt7981-thermal", "mediatek,mt7986-thermal" : For MT7981 SoC
- "mediatek,mt7986-thermal" : For MT7986 SoC
- "mediatek,mt8183-thermal" : For MT8183 family of SoCs
- "mediatek,mt8365-thermal" : For MT8365 family of SoCs
- "mediatek,mt8516-thermal", "mediatek,mt2701-thermal : For MT8516 family of SoCs
- reg: Address range of the thermal controller
- interrupts: IRQ for the thermal controller
- clocks, clock-names: Clocks needed for the thermal controller. required
clocks are:
"therm": Main clock needed for register access
"auxadc": The AUXADC clock
- 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 Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
Optional properties:
- resets: Reference to the reset controller controlling the thermal controller.
- nvmem-cells: A phandle to the calibration data provided by a nvmem device. If
unspecified default values shall be used.
- nvmem-cell-names: Should be "calibration-data"
Example:
thermal: thermal@1100b000 {
#thermal-sensor-cells = <1>;
compatible = "mediatek,mt8173-thermal";
reg = <0 0x1100b000 0 0x1000>;
interrupts = <0 70 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_THERM>, <&pericfg CLK_PERI_AUXADC>;
clock-names = "therm", "auxadc";
resets = <&pericfg MT8173_PERI_THERM_SW_RST>;
reset-names = "therm";
mediatek,auxadc = <&auxadc>;
mediatek,apmixedsys = <&apmixedsys>;
nvmem-cells = <&thermal_calibration_data>;
nvmem-cell-names = "calibration-data";
};

View File

@ -114,12 +114,14 @@ examples:
- |
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include <dt-bindings/interrupt-controller/irq.h>
spmi_bus {
pmic {
#address-cells = <1>;
#size-cells = <0>;
pm8998_adc: adc@3100 {
reg = <0x3100>;
compatible = "qcom,spmi-adc-rev2";
reg = <0x3100>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
@ -130,7 +132,7 @@ examples:
};
};
pm8998_adc_tm: adc-tm@3400 {
adc-tm@3400 {
compatible = "qcom,spmi-adc-tm-hc";
reg = <0x3400>;
interrupts = <0x2 0x34 0x0 IRQ_TYPE_EDGE_RISING>;

View File

@ -167,12 +167,14 @@ examples:
- |
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include <dt-bindings/interrupt-controller/irq.h>
spmi_bus {
pmic {
#address-cells = <1>;
#size-cells = <0>;
pm8150b_adc: adc@3100 {
reg = <0x3100>;
compatible = "qcom,spmi-adc5";
reg = <0x3100>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
@ -186,7 +188,7 @@ examples:
};
};
pm8150b_adc_tm: adc-tm@3500 {
adc-tm@3500 {
compatible = "qcom,spmi-adc-tm5";
reg = <0x3500>;
interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>;
@ -207,12 +209,14 @@ examples:
#include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
#include <dt-bindings/iio/qcom,spmi-adc7-pm8350.h>
#include <dt-bindings/interrupt-controller/irq.h>
spmi_bus {
pmic {
#address-cells = <1>;
#size-cells = <0>;
pmk8350_vadc: adc@3100 {
reg = <0x3100>;
compatible = "qcom,spmi-adc7";
reg = <0x3100>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
@ -233,7 +237,7 @@ examples:
};
};
pmk8350_adc_tm: adc-tm@3400 {
adc-tm@3400 {
compatible = "qcom,spmi-adc-tm5-gen2";
reg = <0x3400>;
interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;

View File

@ -66,6 +66,7 @@ properties:
- qcom,sm8350-tsens
- qcom,sm8450-tsens
- qcom,sm8550-tsens
- qcom,sm8650-tsens
- const: qcom,tsens-v2
- description: v2 of TSENS with combined interrupt

View File

@ -75,6 +75,22 @@ patternProperties:
framework and assumes that the thermal sensors in this zone
support interrupts.
critical-action:
$ref: /schemas/types.yaml#/definitions/string
description: |
The action the OS should perform after the critical temperature is reached.
By default the system will shutdown as a safe action to prevent damage
to the hardware, if the property is not set.
The shutdown action should be always the default and preferred one.
Choose 'reboot' with care, as the hardware may be in thermal stress,
thus leading to infinite reboots that may cause damage to the hardware.
Make sure the firmware/bootloader will act as the last resort and take
over the thermal control.
enum:
- shutdown
- reboot
thermal-sensors:
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 1

View File

@ -292,6 +292,7 @@ static int acpi_thermal_adjust_trip(struct thermal_trip *trip, void *data)
struct acpi_thermal_trip *acpi_trip = trip->priv;
struct adjust_trip_data *atd = data;
struct acpi_thermal *tz = atd->tz;
int temp;
if (!acpi_trip || !acpi_thermal_trip_valid(acpi_trip))
return 0;
@ -302,9 +303,11 @@ static int acpi_thermal_adjust_trip(struct thermal_trip *trip, void *data)
acpi_thermal_update_trip_devices(tz, trip);
if (acpi_thermal_trip_valid(acpi_trip))
trip->temperature = acpi_thermal_temp(tz, acpi_trip->temp_dk);
temp = acpi_thermal_temp(tz, acpi_trip->temp_dk);
else
trip->temperature = THERMAL_TEMP_INVALID;
temp = THERMAL_TEMP_INVALID;
thermal_zone_set_trip_temp(tz->thermal_zone, trip, temp);
return 0;
}

View File

@ -167,13 +167,11 @@ static int amlogic_thermal_enable(struct amlogic_thermal *data)
return 0;
}
static int amlogic_thermal_disable(struct amlogic_thermal *data)
static void amlogic_thermal_disable(struct amlogic_thermal *data)
{
regmap_update_bits(data->regmap, TSENSOR_CFG_REG1,
TSENSOR_CFG_REG1_ENABLE, 0);
clk_disable_unprepare(data->clk);
return 0;
}
static int amlogic_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
@ -298,27 +296,30 @@ static void amlogic_thermal_remove(struct platform_device *pdev)
amlogic_thermal_disable(data);
}
static int __maybe_unused amlogic_thermal_suspend(struct device *dev)
static int amlogic_thermal_suspend(struct device *dev)
{
struct amlogic_thermal *data = dev_get_drvdata(dev);
return amlogic_thermal_disable(data);
amlogic_thermal_disable(data);
return 0;
}
static int __maybe_unused amlogic_thermal_resume(struct device *dev)
static int amlogic_thermal_resume(struct device *dev)
{
struct amlogic_thermal *data = dev_get_drvdata(dev);
return amlogic_thermal_enable(data);
}
static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops,
amlogic_thermal_suspend, amlogic_thermal_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops,
amlogic_thermal_suspend,
amlogic_thermal_resume);
static struct platform_driver amlogic_thermal_driver = {
.driver = {
.name = "amlogic_thermal",
.pm = &amlogic_thermal_pm_ops,
.pm = pm_ptr(&amlogic_thermal_pm_ops),
.of_match_table = of_amlogic_thermal_match,
},
.probe = amlogic_thermal_probe,

View File

@ -66,7 +66,7 @@ static unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us,
* @state : a pointer to the state variable to be filled
*
* The function always returns 100 as the injection ratio. It is
* percentile based for consistency accross different platforms.
* percentile based for consistency across different platforms.
*
* Return: The function can not fail, it is always zero
*/
@ -146,7 +146,7 @@ static int cpuidle_cooling_set_cur_state(struct thermal_cooling_device *cdev,
return 0;
}
/**
/*
* cpuidle_cooling_ops - thermal cooling device ops
*/
static struct thermal_cooling_device_ops cpuidle_cooling_ops = {

View File

@ -46,6 +46,22 @@ static inline s64 div_frac(s64 x, s64 y)
return div_s64(x << FRAC_BITS, y);
}
/**
* struct power_actor - internal power information for power actor
* @req_power: requested power value (not weighted)
* @max_power: max allocatable power for this actor
* @granted_power: granted power for this actor
* @extra_actor_power: extra power that this actor can receive
* @weighted_req_power: weighted requested power as input to IPA
*/
struct power_actor {
u32 req_power;
u32 max_power;
u32 granted_power;
u32 extra_actor_power;
u32 weighted_req_power;
};
/**
* struct power_allocator_params - parameters for the power allocator governor
* @allocated_tzp: whether we have allocated tzp for this thermal zone and
@ -59,9 +75,12 @@ static inline s64 div_frac(s64 x, s64 y)
* governor switches on when this trip point is crossed.
* If the thermal zone only has one passive trip point,
* @trip_switch_on should be NULL.
* @trip_max_desired_temperature: last passive trip point of the thermal
* zone. The temperature we are
* controlling for.
* @trip_max: last passive trip point of the thermal zone. The
* temperature we are controlling for.
* @total_weight: Sum of all thermal instances weights
* @num_actors: number of cooling devices supporting IPA callbacks
* @buffer_size: internal buffer size, to avoid runtime re-calculation
* @power: buffer for all power actors internal power information
*/
struct power_allocator_params {
bool allocated_tzp;
@ -69,9 +88,20 @@ struct power_allocator_params {
s32 prev_err;
u32 sustainable_power;
const struct thermal_trip *trip_switch_on;
const struct thermal_trip *trip_max_desired_temperature;
const struct thermal_trip *trip_max;
int total_weight;
unsigned int num_actors;
unsigned int buffer_size;
struct power_actor *power;
};
static bool power_actor_is_valid(struct power_allocator_params *params,
struct thermal_instance *instance)
{
return (instance->trip == params->trip_max &&
cdev_is_power_actor(instance->cdev));
}
/**
* estimate_sustainable_power() - Estimate the sustainable power of a thermal zone
* @tz: thermal zone we are operating in
@ -85,20 +115,17 @@ struct power_allocator_params {
*/
static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
{
u32 sustainable_power = 0;
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
struct thermal_cooling_device *cdev;
struct thermal_instance *instance;
u32 sustainable_power = 0;
u32 min_power;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
u32 min_power;
if (instance->trip != params->trip_max_desired_temperature)
continue;
if (!cdev_is_power_actor(cdev))
if (!power_actor_is_valid(params, instance))
continue;
cdev = instance->cdev;
if (cdev->ops->state2power(cdev, instance->upper, &min_power))
continue;
@ -212,10 +239,10 @@ static u32 pid_controller(struct thermal_zone_device *tz,
int control_temp,
u32 max_allocatable_power)
{
struct power_allocator_params *params = tz->governor_data;
s64 p, i, d, power_range;
s32 err, max_power_frac;
u32 sustainable_power;
struct power_allocator_params *params = tz->governor_data;
max_power_frac = int_to_frac(max_allocatable_power);
@ -303,15 +330,10 @@ power_actor_set_power(struct thermal_cooling_device *cdev,
/**
* divvy_up_power() - divvy the allocated power between the actors
* @req_power: each actor's requested power
* @max_power: each actor's maximum available power
* @num_actors: size of the @req_power, @max_power and @granted_power's array
* @total_req_power: sum of @req_power
* @power: buffer for all power actors internal power information
* @num_actors: number of power actors in this thermal zone
* @total_req_power: sum of all weighted requested power for all actors
* @power_range: total allocated power
* @granted_power: output array: each actor's granted power
* @extra_actor_power: an appropriately sized array to be used in the
* function as temporary storage of the extra power given
* to the actors
*
* This function divides the total allocated power (@power_range)
* fairly between the actors. It first tries to give each actor a
@ -324,15 +346,12 @@ power_actor_set_power(struct thermal_cooling_device *cdev,
* If any actor received more than their maximum power, then that
* surplus is re-divvied among the actors based on how far they are
* from their respective maximums.
*
* Granted power for each actor is written to @granted_power, which
* should've been allocated by the calling function.
*/
static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
u32 total_req_power, u32 power_range,
u32 *granted_power, u32 *extra_actor_power)
static void divvy_up_power(struct power_actor *power, int num_actors,
u32 total_req_power, u32 power_range)
{
u32 extra_power, capped_extra_power;
u32 capped_extra_power = 0;
u32 extra_power = 0;
int i;
/*
@ -341,24 +360,23 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
if (!total_req_power)
total_req_power = 1;
capped_extra_power = 0;
extra_power = 0;
for (i = 0; i < num_actors; i++) {
u64 req_range = (u64)req_power[i] * power_range;
struct power_actor *pa = &power[i];
u64 req_range = (u64)pa->req_power * power_range;
granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range,
total_req_power);
pa->granted_power = DIV_ROUND_CLOSEST_ULL(req_range,
total_req_power);
if (granted_power[i] > max_power[i]) {
extra_power += granted_power[i] - max_power[i];
granted_power[i] = max_power[i];
if (pa->granted_power > pa->max_power) {
extra_power += pa->granted_power - pa->max_power;
pa->granted_power = pa->max_power;
}
extra_actor_power[i] = max_power[i] - granted_power[i];
capped_extra_power += extra_actor_power[i];
pa->extra_actor_power = pa->max_power - pa->granted_power;
capped_extra_power += pa->extra_actor_power;
}
if (!extra_power)
if (!extra_power || !capped_extra_power)
return;
/*
@ -366,127 +384,95 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
* how far they are from the max
*/
extra_power = min(extra_power, capped_extra_power);
if (capped_extra_power > 0)
for (i = 0; i < num_actors; i++) {
u64 extra_range = (u64)extra_actor_power[i] * extra_power;
granted_power[i] += DIV_ROUND_CLOSEST_ULL(extra_range,
capped_extra_power);
}
for (i = 0; i < num_actors; i++) {
struct power_actor *pa = &power[i];
u64 extra_range = pa->extra_actor_power;
extra_range *= extra_power;
pa->granted_power += DIV_ROUND_CLOSEST_ULL(extra_range,
capped_extra_power);
}
}
static int allocate_power(struct thermal_zone_device *tz,
int control_temp)
static int allocate_power(struct thermal_zone_device *tz, int control_temp)
{
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
const struct thermal_trip *trip_max_desired_temperature =
params->trip_max_desired_temperature;
u32 *req_power, *max_power, *granted_power, *extra_actor_power;
u32 *weighted_req_power;
u32 total_req_power, max_allocatable_power, total_weighted_req_power;
u32 total_granted_power, power_range;
int i, num_actors, total_weight, ret = 0;
num_actors = 0;
total_weight = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if ((instance->trip == trip_max_desired_temperature) &&
cdev_is_power_actor(instance->cdev)) {
num_actors++;
total_weight += instance->weight;
}
}
unsigned int num_actors = params->num_actors;
struct power_actor *power = params->power;
struct thermal_cooling_device *cdev;
struct thermal_instance *instance;
u32 total_weighted_req_power = 0;
u32 max_allocatable_power = 0;
u32 total_granted_power = 0;
u32 total_req_power = 0;
u32 power_range, weight;
int i = 0, ret;
if (!num_actors)
return -ENODEV;
/*
* We need to allocate five arrays of the same size:
* req_power, max_power, granted_power, extra_actor_power and
* weighted_req_power. They are going to be needed until this
* function returns. Allocate them all in one go to simplify
* the allocation and deallocation logic.
*/
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
if (!req_power)
return -ENOMEM;
max_power = &req_power[num_actors];
granted_power = &req_power[2 * num_actors];
extra_actor_power = &req_power[3 * num_actors];
weighted_req_power = &req_power[4 * num_actors];
i = 0;
total_weighted_req_power = 0;
total_req_power = 0;
max_allocatable_power = 0;
/* Clean all buffers for new power estimations */
memset(power, 0, params->buffer_size);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
int weight;
struct thermal_cooling_device *cdev = instance->cdev;
struct power_actor *pa = &power[i];
if (instance->trip != trip_max_desired_temperature)
if (!power_actor_is_valid(params, instance))
continue;
if (!cdev_is_power_actor(cdev))
cdev = instance->cdev;
ret = cdev->ops->get_requested_power(cdev, &pa->req_power);
if (ret)
continue;
if (cdev->ops->get_requested_power(cdev, &req_power[i]))
continue;
if (!total_weight)
if (!params->total_weight)
weight = 1 << FRAC_BITS;
else
weight = instance->weight;
weighted_req_power[i] = frac_to_int(weight * req_power[i]);
pa->weighted_req_power = frac_to_int(weight * pa->req_power);
if (cdev->ops->state2power(cdev, instance->lower,
&max_power[i]))
ret = cdev->ops->state2power(cdev, instance->lower,
&pa->max_power);
if (ret)
continue;
total_req_power += req_power[i];
max_allocatable_power += max_power[i];
total_weighted_req_power += weighted_req_power[i];
total_req_power += pa->req_power;
max_allocatable_power += pa->max_power;
total_weighted_req_power += pa->weighted_req_power;
i++;
}
power_range = pid_controller(tz, control_temp, max_allocatable_power);
divvy_up_power(weighted_req_power, max_power, num_actors,
total_weighted_req_power, power_range, granted_power,
extra_actor_power);
divvy_up_power(power, num_actors, total_weighted_req_power,
power_range);
total_granted_power = 0;
i = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip_max_desired_temperature)
continue;
struct power_actor *pa = &power[i];
if (!cdev_is_power_actor(instance->cdev))
if (!power_actor_is_valid(params, instance))
continue;
power_actor_set_power(instance->cdev, instance,
granted_power[i]);
total_granted_power += granted_power[i];
pa->granted_power);
total_granted_power += pa->granted_power;
trace_thermal_power_actor(tz, i, pa->req_power,
pa->granted_power);
i++;
}
trace_thermal_power_allocator(tz, req_power, total_req_power,
granted_power, total_granted_power,
trace_thermal_power_allocator(tz, total_req_power, total_granted_power,
num_actors, power_range,
max_allocatable_power, tz->temperature,
control_temp - tz->temperature);
kfree(req_power);
return ret;
return 0;
}
/**
@ -531,13 +517,13 @@ static void get_governor_trips(struct thermal_zone_device *tz,
if (last_passive) {
params->trip_switch_on = first_passive;
params->trip_max_desired_temperature = last_passive;
params->trip_max = last_passive;
} else if (first_passive) {
params->trip_switch_on = NULL;
params->trip_max_desired_temperature = first_passive;
params->trip_max = first_passive;
} else {
params->trip_switch_on = NULL;
params->trip_max_desired_temperature = last_active;
params->trip_max = last_active;
}
}
@ -549,19 +535,19 @@ static void reset_pid_controller(struct power_allocator_params *params)
static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
{
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
struct thermal_cooling_device *cdev;
struct thermal_instance *instance;
u32 req_power;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
if (instance->trip != params->trip_max_desired_temperature ||
(!cdev_is_power_actor(instance->cdev)))
if (!power_actor_is_valid(params, instance))
continue;
cdev = instance->cdev;
instance->target = 0;
mutex_lock(&instance->cdev->lock);
mutex_lock(&cdev->lock);
/*
* Call for updating the cooling devices local stats and avoid
* periods of dozen of seconds when those have not been
@ -570,9 +556,9 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
cdev->ops->get_requested_power(cdev, &req_power);
if (update)
__thermal_cdev_update(instance->cdev);
__thermal_cdev_update(cdev);
mutex_unlock(&instance->cdev->lock);
mutex_unlock(&cdev->lock);
}
}
@ -580,30 +566,99 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
* check_power_actors() - Check all cooling devices and warn when they are
* not power actors
* @tz: thermal zone to operate on
* @params: power allocator private data
*
* Check all cooling devices in the @tz and warn every time they are missing
* power actor API. The warning should help to investigate the issue, which
* could be e.g. lack of Energy Model for a given device.
*
* Return: 0 on success, -EINVAL if any cooling device does not implement
* the power actor API.
* If all of the cooling devices currently attached to @tz implement the power
* actor API, return the number of them (which may be 0, because some cooling
* devices may be attached later). Otherwise, return -EINVAL.
*/
static int check_power_actors(struct thermal_zone_device *tz)
static int check_power_actors(struct thermal_zone_device *tz,
struct power_allocator_params *params)
{
struct thermal_instance *instance;
int ret = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != params->trip_max)
continue;
if (!cdev_is_power_actor(instance->cdev)) {
dev_warn(&tz->device, "power_allocator: %s is not a power actor\n",
instance->cdev->type);
ret = -EINVAL;
return -EINVAL;
}
ret++;
}
return ret;
}
static int allocate_actors_buffer(struct power_allocator_params *params,
int num_actors)
{
int ret;
kfree(params->power);
/* There might be no cooling devices yet. */
if (!num_actors) {
ret = -EINVAL;
goto clean_state;
}
params->power = kcalloc(num_actors, sizeof(struct power_actor),
GFP_KERNEL);
if (!params->power) {
ret = -ENOMEM;
goto clean_state;
}
params->num_actors = num_actors;
params->buffer_size = num_actors * sizeof(struct power_actor);
return 0;
clean_state:
params->num_actors = 0;
params->buffer_size = 0;
params->power = NULL;
return ret;
}
static void power_allocator_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason)
{
struct power_allocator_params *params = tz->governor_data;
struct thermal_instance *instance;
int num_actors = 0;
switch (reason) {
case THERMAL_TZ_BIND_CDEV:
case THERMAL_TZ_UNBIND_CDEV:
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
if (power_actor_is_valid(params, instance))
num_actors++;
if (num_actors == params->num_actors)
return;
allocate_actors_buffer(params, num_actors);
break;
case THERMAL_INSTANCE_WEIGHT_CHANGED:
params->total_weight = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
if (power_actor_is_valid(params, instance))
params->total_weight += instance->weight;
break;
default:
break;
}
}
/**
* power_allocator_bind() - bind the power_allocator governor to a thermal zone
* @tz: thermal zone to bind it to
@ -616,17 +671,34 @@ static int check_power_actors(struct thermal_zone_device *tz)
*/
static int power_allocator_bind(struct thermal_zone_device *tz)
{
int ret;
struct power_allocator_params *params;
ret = check_power_actors(tz);
if (ret)
return ret;
int ret;
params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
get_governor_trips(tz, params);
if (!params->trip_max) {
dev_warn(&tz->device, "power_allocator: missing trip_max\n");
kfree(params);
return -EINVAL;
}
ret = check_power_actors(tz, params);
if (ret < 0) {
dev_warn(&tz->device, "power_allocator: binding failed\n");
kfree(params);
return ret;
}
ret = allocate_actors_buffer(params, ret);
if (ret) {
dev_warn(&tz->device, "power_allocator: allocation failed\n");
kfree(params);
return ret;
}
if (!tz->tzp) {
tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL);
if (!tz->tzp) {
@ -640,14 +712,9 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
if (!tz->tzp->sustainable_power)
dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n");
get_governor_trips(tz, params);
if (params->trip_max_desired_temperature) {
int temp = params->trip_max_desired_temperature->temperature;
estimate_pid_constants(tz, tz->tzp->sustainable_power,
params->trip_switch_on, temp);
}
estimate_pid_constants(tz, tz->tzp->sustainable_power,
params->trip_switch_on,
params->trip_max->temperature);
reset_pid_controller(params);
@ -656,6 +723,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
return 0;
free_params:
kfree(params->power);
kfree(params);
return ret;
@ -672,6 +740,7 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
tz->tzp = NULL;
}
kfree(params->power);
kfree(tz->governor_data);
tz->governor_data = NULL;
}
@ -688,7 +757,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz,
* We get called for every trip point but we only need to do
* our calculations once
*/
if (trip != params->trip_max_desired_temperature)
if (trip != params->trip_max)
return 0;
trip = params->trip_switch_on;
@ -702,7 +771,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz,
tz->passive = 1;
return allocate_power(tz, params->trip_max_desired_temperature->temperature);
return allocate_power(tz, params->trip_max->temperature);
}
static struct thermal_governor thermal_gov_power_allocator = {
@ -710,5 +779,6 @@ static struct thermal_governor thermal_gov_power_allocator = {
.bind_to_tz = power_allocator_bind,
.unbind_from_tz = power_allocator_unbind,
.throttle = power_allocator_throttle,
.update_tz = power_allocator_update_tz,
};
THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator);

View File

@ -225,7 +225,8 @@ EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
static int int340x_update_one_trip(struct thermal_trip *trip, void *arg)
{
struct acpi_device *zone_adev = arg;
struct int34x_thermal_zone *int34x_zone = arg;
struct acpi_device *zone_adev = int34x_zone->adev;
int temp, err;
switch (trip->type) {
@ -249,14 +250,15 @@ static int int340x_update_one_trip(struct thermal_trip *trip, void *arg)
if (err)
temp = THERMAL_TEMP_INVALID;
trip->temperature = temp;
thermal_zone_set_trip_temp(int34x_zone->zone, trip, temp);
return 0;
}
void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)
{
thermal_zone_for_each_trip(int34x_zone->zone, int340x_update_one_trip,
int34x_zone->adev);
int34x_zone);
}
EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);

View File

@ -24,6 +24,7 @@
#include <linux/bitops.h>
#include <linux/cpufeature.h>
#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/kernel.h>
@ -347,6 +348,52 @@ static void init_hfi_instance(struct hfi_instance *hfi_instance)
hfi_instance->data = hfi_instance->hdr + hfi_features.hdr_size;
}
/* Caller must hold hfi_instance_lock. */
static void hfi_enable(void)
{
u64 msr_val;
rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
msr_val |= HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT;
wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
}
static void hfi_set_hw_table(struct hfi_instance *hfi_instance)
{
phys_addr_t hw_table_pa;
u64 msr_val;
hw_table_pa = virt_to_phys(hfi_instance->hw_table);
msr_val = hw_table_pa | HW_FEEDBACK_PTR_VALID_BIT;
wrmsrl(MSR_IA32_HW_FEEDBACK_PTR, msr_val);
}
/* Caller must hold hfi_instance_lock. */
static void hfi_disable(void)
{
u64 msr_val;
int i;
rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
msr_val &= ~HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT;
wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
/*
* Wait for hardware to acknowledge the disabling of HFI. Some
* processors may not do it. Wait for ~2ms. This is a reasonable
* time for hardware to complete any pending actions on the HFI
* memory.
*/
for (i = 0; i < 2000; i++) {
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
if (msr_val & PACKAGE_THERM_STATUS_HFI_UPDATED)
break;
udelay(1);
cpu_relax();
}
}
/**
* intel_hfi_online() - Enable HFI on @cpu
* @cpu: CPU in which the HFI will be enabled
@ -364,8 +411,6 @@ void intel_hfi_online(unsigned int cpu)
{
struct hfi_instance *hfi_instance;
struct hfi_cpu_info *info;
phys_addr_t hw_table_pa;
u64 msr_val;
u16 die_id;
/* Nothing to do if hfi_instances are missing. */
@ -392,25 +437,26 @@ void intel_hfi_online(unsigned int cpu)
/*
* Now check if the HFI instance of the package/die of @cpu has been
* initialized (by checking its header). In such case, all we have to
* do is to add @cpu to this instance's cpumask.
* do is to add @cpu to this instance's cpumask and enable the instance
* if needed.
*/
mutex_lock(&hfi_instance_lock);
if (hfi_instance->hdr) {
cpumask_set_cpu(cpu, hfi_instance->cpus);
goto unlock;
}
if (hfi_instance->hdr)
goto enable;
/*
* Hardware is programmed with the physical address of the first page
* frame of the table. Hence, the allocated memory must be page-aligned.
*
* Some processors do not forget the initial address of the HFI table
* even after having been reprogrammed. Keep using the same pages. Do
* not free them.
*/
hfi_instance->hw_table = alloc_pages_exact(hfi_features.nr_table_pages,
GFP_KERNEL | __GFP_ZERO);
if (!hfi_instance->hw_table)
goto unlock;
hw_table_pa = virt_to_phys(hfi_instance->hw_table);
/*
* Allocate memory to keep a local copy of the table that
* hardware generates.
@ -420,31 +466,20 @@ void intel_hfi_online(unsigned int cpu)
if (!hfi_instance->local_table)
goto free_hw_table;
/*
* Program the address of the feedback table of this die/package. On
* some processors, hardware remembers the old address of the HFI table
* even after having been reprogrammed and re-enabled. Thus, do not free
* the pages allocated for the table or reprogram the hardware with a
* new base address. Namely, program the hardware only once.
*/
msr_val = hw_table_pa | HW_FEEDBACK_PTR_VALID_BIT;
wrmsrl(MSR_IA32_HW_FEEDBACK_PTR, msr_val);
init_hfi_instance(hfi_instance);
INIT_DELAYED_WORK(&hfi_instance->update_work, hfi_update_work_fn);
raw_spin_lock_init(&hfi_instance->table_lock);
raw_spin_lock_init(&hfi_instance->event_lock);
enable:
cpumask_set_cpu(cpu, hfi_instance->cpus);
/*
* Enable the hardware feedback interface and never disable it. See
* comment on programming the address of the table.
*/
rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
msr_val |= HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT;
wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val);
/* Enable this HFI instance if this is its first online CPU. */
if (cpumask_weight(hfi_instance->cpus) == 1) {
hfi_set_hw_table(hfi_instance);
hfi_enable();
}
unlock:
mutex_unlock(&hfi_instance_lock);
@ -484,6 +519,10 @@ void intel_hfi_offline(unsigned int cpu)
mutex_lock(&hfi_instance_lock);
cpumask_clear_cpu(cpu, hfi_instance->cpus);
if (!cpumask_weight(hfi_instance->cpus))
hfi_disable();
mutex_unlock(&hfi_instance_lock);
}

View File

@ -127,7 +127,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev)
if (!IS_ERR(tzd))
break;
if (PTR_ERR(tzd) != ENODEV)
if (PTR_ERR(tzd) != -ENODEV)
continue;
return dev_err_probe(dev, PTR_ERR(tzd), "failed to register");

View File

@ -138,12 +138,10 @@ enum soc_type {
/**
* struct exynos_tmu_data : A structure to hold the private data of the TMU
* driver
* @id: identifier of the one instance of the TMU controller.
* @base: base address of the single instance of the TMU controller.
* @base_second: base address of the common registers of the TMU controller.
* @irq: irq number of the TMU controller.
* @soc: id of the SOC type.
* @irq_work: pointer to the irq work structure.
* @lock: lock to implement synchronization.
* @clk: pointer to the clock structure.
* @clk_sec: pointer to the clock structure for accessing the base_second.
@ -159,13 +157,13 @@ enum soc_type {
* @reference_voltage: reference voltage of amplifier
* in the positive-TC generator block
* 0 < reference_voltage <= 31
* @regulator: pointer to the TMU regulator structure.
* @reg_conf: pointer to structure to register with core thermal.
* @tzd: pointer to thermal_zone_device structure
* @ntrip: number of supported trip points.
* @enabled: current status of TMU device
* @tmu_set_trip_temp: SoC specific method to set trip (rising threshold)
* @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold)
* @tmu_set_low_temp: SoC specific method to set trip (falling threshold)
* @tmu_set_high_temp: SoC specific method to set trip (rising threshold)
* @tmu_set_crit_temp: SoC specific method to set critical temperature
* @tmu_disable_low: SoC specific method to disable an interrupt (falling threshold)
* @tmu_disable_high: SoC specific method to disable an interrupt (rising threshold)
* @tmu_initialize: SoC specific TMU initialization method
* @tmu_control: SoC specific TMU control method
* @tmu_read: SoC specific TMU temperature read method
@ -173,12 +171,10 @@ enum soc_type {
* @tmu_clear_irqs: SoC specific TMU interrupts clearing method
*/
struct exynos_tmu_data {
int id;
void __iomem *base;
void __iomem *base_second;
int irq;
enum soc_type soc;
struct work_struct irq_work;
struct mutex lock;
struct clk *clk, *clk_sec, *sclk;
u32 cal_type;
@ -188,15 +184,14 @@ struct exynos_tmu_data {
u16 temp_error1, temp_error2;
u8 gain;
u8 reference_voltage;
struct regulator *regulator;
struct thermal_zone_device *tzd;
unsigned int ntrip;
bool enabled;
void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
u8 temp);
void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip,
u8 temp, u8 hyst);
void (*tmu_set_low_temp)(struct exynos_tmu_data *data, u8 temp);
void (*tmu_set_high_temp)(struct exynos_tmu_data *data, u8 temp);
void (*tmu_set_crit_temp)(struct exynos_tmu_data *data, u8 temp);
void (*tmu_disable_low)(struct exynos_tmu_data *data);
void (*tmu_disable_high)(struct exynos_tmu_data *data);
void (*tmu_initialize)(struct platform_device *pdev);
void (*tmu_control)(struct platform_device *pdev, bool on);
int (*tmu_read)(struct exynos_tmu_data *data);
@ -258,25 +253,8 @@ static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
static int exynos_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tzd = data->tzd;
int num_trips = thermal_zone_get_num_trips(tzd);
unsigned int status;
int ret = 0, temp;
ret = thermal_zone_get_crit_temp(tzd, &temp);
if (ret && data->soc != SOC_ARCH_EXYNOS5433) { /* FIXME */
dev_err(&pdev->dev,
"No CRITICAL trip point defined in device tree!\n");
goto out;
}
if (num_trips > data->ntrip) {
dev_info(&pdev->dev,
"More trip points than supported by this TMU.\n");
dev_info(&pdev->dev,
"%d trip points should be configured in polling mode.\n",
num_trips - data->ntrip);
}
int ret = 0;
mutex_lock(&data->lock);
clk_enable(data->clk);
@ -287,36 +265,46 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
if (!status) {
ret = -EBUSY;
} else {
int i, ntrips =
min_t(int, num_trips, data->ntrip);
data->tmu_initialize(pdev);
/* Write temperature code for rising and falling threshold */
for (i = 0; i < ntrips; i++) {
struct thermal_trip trip;
ret = thermal_zone_get_trip(tzd, i, &trip);
if (ret)
goto err;
data->tmu_set_trip_temp(data, i, trip.temperature / MCELSIUS);
data->tmu_set_trip_hyst(data, i, trip.temperature / MCELSIUS,
trip.hysteresis / MCELSIUS);
}
data->tmu_clear_irqs(data);
}
err:
clk_disable(data->clk);
mutex_unlock(&data->lock);
if (!IS_ERR(data->clk_sec))
clk_disable(data->clk_sec);
out:
clk_disable(data->clk);
mutex_unlock(&data->lock);
return ret;
}
static int exynos_thermal_zone_configure(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tzd = data->tzd;
int ret, temp;
ret = thermal_zone_get_crit_temp(tzd, &temp);
if (ret) {
/* FIXME: Remove this special case */
if (data->soc == SOC_ARCH_EXYNOS5433)
return 0;
dev_err(&pdev->dev,
"No CRITICAL trip point defined in device tree!\n");
return ret;
}
mutex_lock(&data->lock);
clk_enable(data->clk);
data->tmu_set_crit_temp(data, temp / MCELSIUS);
clk_disable(data->clk);
mutex_unlock(&data->lock);
return 0;
}
static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
{
if (data->soc == SOC_ARCH_EXYNOS4412 ||
@ -347,30 +335,74 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
mutex_unlock(&data->lock);
}
static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip_id, u8 temp)
static void exynos_tmu_update_bit(struct exynos_tmu_data *data, int reg_off,
int bit_off, bool enable)
{
struct thermal_trip trip;
u8 ref, th_code;
u32 interrupt_en;
if (thermal_zone_get_trip(data->tzd, 0, &trip))
return;
ref = trip.temperature / MCELSIUS;
if (trip_id == 0) {
th_code = temp_to_code(data, ref);
writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
}
temp -= ref;
writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip_id * 4);
interrupt_en = readl(data->base + reg_off);
if (enable)
interrupt_en |= BIT(bit_off);
else
interrupt_en &= ~BIT(bit_off);
writel(interrupt_en, data->base + reg_off);
}
/* failing thresholds are not supported on Exynos4210 */
static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
static void exynos_tmu_update_temp(struct exynos_tmu_data *data, int reg_off,
int bit_off, u8 temp)
{
u16 tmu_temp_mask;
u32 th;
tmu_temp_mask =
(data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK
: EXYNOS_TMU_TEMP_MASK;
th = readl(data->base + reg_off);
th &= ~(tmu_temp_mask << bit_off);
th |= temp_to_code(data, temp) << bit_off;
writel(th, data->base + reg_off);
}
static void exynos4210_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
{
/*
* Failing thresholds are not supported on Exynos 4210.
* We use polling instead.
*/
}
static void exynos4210_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp)
{
temp = temp_to_code(data, temp);
writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 4);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true);
}
static void exynos4210_tmu_disable_low(struct exynos_tmu_data *data)
{
/* Again, this is handled by polling. */
}
static void exynos4210_tmu_disable_high(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, false);
}
static void exynos4210_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{
/*
* Hardware critical temperature handling is not supported on Exynos 4210.
* We still set the critical temperature threshold, but this is only to
* make sure it is handled as soon as possible. It is just a normal interrupt.
*/
temp = temp_to_code(data, temp);
writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 12);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_RISE0_SHIFT + 12, true);
}
static void exynos4210_tmu_initialize(struct platform_device *pdev)
@ -378,35 +410,35 @@ static void exynos4210_tmu_initialize(struct platform_device *pdev)
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
writeb(0, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
}
static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip, u8 temp)
static void exynos4412_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
{
u32 th, con;
th = readl(data->base + EXYNOS_THD_TEMP_RISE);
th &= ~(0xff << 8 * trip);
th |= temp_to_code(data, temp) << 8 * trip;
writel(th, data->base + EXYNOS_THD_TEMP_RISE);
if (trip == 3) {
con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_FALL, 0, temp);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT, true);
}
static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
static void exynos4412_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp)
{
u32 th;
exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_RISE, 8, temp);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true);
}
th = readl(data->base + EXYNOS_THD_TEMP_FALL);
th &= ~(0xff << 8 * trip);
if (hyst)
th |= temp_to_code(data, temp - hyst) << 8 * trip;
writel(th, data->base + EXYNOS_THD_TEMP_FALL);
static void exynos4412_tmu_disable_low(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT, false);
}
static void exynos4412_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{
exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_RISE, 24, temp);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL,
EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true);
}
static void exynos4412_tmu_initialize(struct platform_device *pdev)
@ -436,44 +468,39 @@ static void exynos4412_tmu_initialize(struct platform_device *pdev)
sanitize_temp_error(data, trim_info);
}
static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip, u8 temp)
static void exynos5433_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
{
unsigned int reg_off, j;
u32 th;
if (trip > 3) {
reg_off = EXYNOS5433_THD_TEMP_RISE7_4;
j = trip - 4;
} else {
reg_off = EXYNOS5433_THD_TEMP_RISE3_0;
j = trip;
}
th = readl(data->base + reg_off);
th &= ~(0xff << j * 8);
th |= (temp_to_code(data, temp) << j * 8);
writel(th, data->base + reg_off);
exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_FALL3_0, 0, temp);
exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT, true);
}
static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
static void exynos5433_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp)
{
unsigned int reg_off, j;
u32 th;
exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_RISE3_0, 8, temp);
exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true);
}
if (trip > 3) {
reg_off = EXYNOS5433_THD_TEMP_FALL7_4;
j = trip - 4;
} else {
reg_off = EXYNOS5433_THD_TEMP_FALL3_0;
j = trip;
}
static void exynos5433_tmu_disable_low(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT, false);
}
th = readl(data->base + reg_off);
th &= ~(0xff << j * 8);
th |= (temp_to_code(data, temp - hyst) << j * 8);
writel(th, data->base + reg_off);
static void exynos5433_tmu_disable_high(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false);
}
static void exynos5433_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{
exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_RISE7_4, 24, temp);
exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL,
EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true);
exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true);
}
static void exynos5433_tmu_initialize(struct platform_device *pdev)
@ -509,34 +536,41 @@ static void exynos5433_tmu_initialize(struct platform_device *pdev)
cal_type ? 2 : 1);
}
static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip, u8 temp)
static void exynos7_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
{
unsigned int reg_off, bit_off;
u32 th;
reg_off = ((7 - trip) / 2) * 4;
bit_off = ((8 - trip) % 2);
th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
th |= temp_to_code(data, temp) << (16 * bit_off);
writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_FALL7_6 + 12, 0, temp);
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, true);
}
static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
static void exynos7_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp)
{
unsigned int reg_off, bit_off;
u32 th;
exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_RISE7_6 + 12, 16, temp);
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true);
}
reg_off = ((7 - trip) / 2) * 4;
bit_off = ((8 - trip) % 2);
static void exynos7_tmu_disable_low(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, false);
}
th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
th |= temp_to_code(data, temp - hyst) << (16 * bit_off);
writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
static void exynos7_tmu_disable_high(struct exynos_tmu_data *data)
{
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false);
}
static void exynos7_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
{
/*
* Like Exynos 4210, Exynos 7 does not seem to support critical temperature
* handling in hardware. Again, we still set a separate interrupt for it.
*/
exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_RISE7_6 + 0, 16, temp);
exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true);
}
static void exynos7_tmu_initialize(struct platform_device *pdev)
@ -551,95 +585,51 @@ static void exynos7_tmu_initialize(struct platform_device *pdev)
static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd;
struct thermal_trip trip;
unsigned int con, interrupt_en = 0, i;
unsigned int con;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) {
for (i = 0; i < data->ntrip; i++) {
if (thermal_zone_get_trip(tz, i, &trip))
continue;
if (on)
con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
else
con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en |=
(1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4));
}
if (data->soc != SOC_ARCH_EXYNOS4210)
interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
} else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
}
writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd;
struct thermal_trip trip;
unsigned int con, interrupt_en = 0, pd_det_en, i;
unsigned int con, pd_det_en;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) {
for (i = 0; i < data->ntrip; i++) {
if (thermal_zone_get_trip(tz, i, &trip))
continue;
interrupt_en |=
(1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
}
interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
} else
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
if (on)
con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
else
con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT);
pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0;
writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN);
writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
static void exynos7_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd;
struct thermal_trip trip;
unsigned int con, interrupt_en = 0, i;
unsigned int con;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) {
for (i = 0; i < data->ntrip; i++) {
if (thermal_zone_get_trip(tz, i, &trip))
continue;
interrupt_en |=
(1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
}
interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
con |= (1 << EXYNOS7_PD_DET_EN_SHIFT);
con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
con |= BIT(EXYNOS7_PD_DET_EN_SHIFT);
} else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT);
con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT);
con &= ~BIT(EXYNOS7_PD_DET_EN_SHIFT);
}
writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
@ -766,10 +756,9 @@ static int exynos7_tmu_read(struct exynos_tmu_data *data)
EXYNOS7_TMU_TEMP_MASK;
}
static void exynos_tmu_work(struct work_struct *work)
static irqreturn_t exynos_tmu_threaded_irq(int irq, void *id)
{
struct exynos_tmu_data *data = container_of(work,
struct exynos_tmu_data, irq_work);
struct exynos_tmu_data *data = id;
thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED);
@ -781,7 +770,8 @@ static void exynos_tmu_work(struct work_struct *work)
clk_disable(data->clk);
mutex_unlock(&data->lock);
enable_irq(data->irq);
return IRQ_HANDLED;
}
static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
@ -815,16 +805,6 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
writel(val_irq, data->base + tmu_intclear);
}
static irqreturn_t exynos_tmu_irq(int irq, void *id)
{
struct exynos_tmu_data *data = id;
disable_irq_nosync(irq);
schedule_work(&data->irq_work);
return IRQ_HANDLED;
}
static const struct of_device_id exynos_tmu_match[] = {
{
.compatible = "samsung,exynos3250-tmu",
@ -866,10 +846,6 @@ static int exynos_map_dt_data(struct platform_device *pdev)
if (!data || !pdev->dev.of_node)
return -ENODEV;
data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
if (data->id < 0)
data->id = 0;
data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (data->irq <= 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
@ -891,13 +867,15 @@ static int exynos_map_dt_data(struct platform_device *pdev)
switch (data->soc) {
case SOC_ARCH_EXYNOS4210:
data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst;
data->tmu_set_low_temp = exynos4210_tmu_set_low_temp;
data->tmu_set_high_temp = exynos4210_tmu_set_high_temp;
data->tmu_disable_low = exynos4210_tmu_disable_low;
data->tmu_disable_high = exynos4210_tmu_disable_high;
data->tmu_set_crit_temp = exynos4210_tmu_set_crit_temp;
data->tmu_initialize = exynos4210_tmu_initialize;
data->tmu_control = exynos4210_tmu_control;
data->tmu_read = exynos4210_tmu_read;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 4;
data->gain = 15;
data->reference_voltage = 7;
data->efuse_value = 55;
@ -910,14 +888,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
case SOC_ARCH_EXYNOS5260:
case SOC_ARCH_EXYNOS5420:
case SOC_ARCH_EXYNOS5420_TRIMINFO:
data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst;
data->tmu_set_low_temp = exynos4412_tmu_set_low_temp;
data->tmu_set_high_temp = exynos4412_tmu_set_high_temp;
data->tmu_disable_low = exynos4412_tmu_disable_low;
data->tmu_disable_high = exynos4210_tmu_disable_high;
data->tmu_set_crit_temp = exynos4412_tmu_set_crit_temp;
data->tmu_initialize = exynos4412_tmu_initialize;
data->tmu_control = exynos4210_tmu_control;
data->tmu_read = exynos4412_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 4;
data->gain = 8;
data->reference_voltage = 16;
data->efuse_value = 55;
@ -929,14 +909,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
data->max_efuse_value = 100;
break;
case SOC_ARCH_EXYNOS5433:
data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst;
data->tmu_set_low_temp = exynos5433_tmu_set_low_temp;
data->tmu_set_high_temp = exynos5433_tmu_set_high_temp;
data->tmu_disable_low = exynos5433_tmu_disable_low;
data->tmu_disable_high = exynos5433_tmu_disable_high;
data->tmu_set_crit_temp = exynos5433_tmu_set_crit_temp;
data->tmu_initialize = exynos5433_tmu_initialize;
data->tmu_control = exynos5433_tmu_control;
data->tmu_read = exynos4412_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 8;
data->gain = 8;
if (res.start == EXYNOS5433_G3D_BASE)
data->reference_voltage = 23;
@ -947,14 +929,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
data->max_efuse_value = 150;
break;
case SOC_ARCH_EXYNOS7:
data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst;
data->tmu_set_low_temp = exynos7_tmu_set_low_temp;
data->tmu_set_high_temp = exynos7_tmu_set_high_temp;
data->tmu_disable_low = exynos7_tmu_disable_low;
data->tmu_disable_high = exynos7_tmu_disable_high;
data->tmu_set_crit_temp = exynos7_tmu_set_crit_temp;
data->tmu_initialize = exynos7_tmu_initialize;
data->tmu_control = exynos7_tmu_control;
data->tmu_read = exynos7_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 8;
data->gain = 9;
data->reference_voltage = 17;
data->efuse_value = 75;
@ -990,9 +974,32 @@ static int exynos_map_dt_data(struct platform_device *pdev)
return 0;
}
static int exynos_set_trips(struct thermal_zone_device *tz, int low, int high)
{
struct exynos_tmu_data *data = thermal_zone_device_priv(tz);
mutex_lock(&data->lock);
clk_enable(data->clk);
if (low > INT_MIN)
data->tmu_set_low_temp(data, low / MCELSIUS);
else
data->tmu_disable_low(data);
if (high < INT_MAX)
data->tmu_set_high_temp(data, high / MCELSIUS);
else
data->tmu_disable_high(data);
clk_disable(data->clk);
mutex_unlock(&data->lock);
return 0;
}
static const struct thermal_zone_device_ops exynos_sensor_ops = {
.get_temp = exynos_get_temp,
.set_emul_temp = exynos_tmu_set_emulation,
.set_trips = exynos_set_trips,
};
static int exynos_tmu_probe(struct platform_device *pdev)
@ -1013,44 +1020,40 @@ static int exynos_tmu_probe(struct platform_device *pdev)
* TODO: Add regulator as an SOC feature, so that regulator enable
* is a compulsory call.
*/
data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
if (!IS_ERR(data->regulator)) {
ret = regulator_enable(data->regulator);
if (ret) {
dev_err(&pdev->dev, "failed to enable vtmu\n");
return ret;
}
} else {
if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
ret = devm_regulator_get_enable_optional(&pdev->dev, "vtmu");
switch (ret) {
case 0:
case -ENODEV:
break;
case -EPROBE_DEFER:
return -EPROBE_DEFER;
default:
dev_err(&pdev->dev, "Failed to get enabled regulator: %d\n",
ret);
return ret;
}
ret = exynos_map_dt_data(pdev);
if (ret)
goto err_sensor;
INIT_WORK(&data->irq_work, exynos_tmu_work);
return ret;
data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev, "Failed to get clock\n");
ret = PTR_ERR(data->clk);
goto err_sensor;
return PTR_ERR(data->clk);
}
data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
if (IS_ERR(data->clk_sec)) {
if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
dev_err(&pdev->dev, "Failed to get triminfo clock\n");
ret = PTR_ERR(data->clk_sec);
goto err_sensor;
return PTR_ERR(data->clk_sec);
}
} else {
ret = clk_prepare(data->clk_sec);
if (ret) {
dev_err(&pdev->dev, "Failed to get clock\n");
goto err_sensor;
return ret;
}
}
@ -1080,10 +1083,12 @@ static int exynos_tmu_probe(struct platform_device *pdev)
break;
}
/*
* data->tzd must be registered before calling exynos_tmu_initialize(),
* requesting irq and calling exynos_tmu_control().
*/
ret = exynos_tmu_initialize(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize TMU\n");
goto err_sclk;
}
data->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, data,
&exynos_sensor_ops);
if (IS_ERR(data->tzd)) {
@ -1094,14 +1099,17 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_sclk;
}
ret = exynos_tmu_initialize(pdev);
ret = exynos_thermal_zone_configure(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize TMU\n");
dev_err(&pdev->dev, "Failed to configure the thermal zone\n");
goto err_sclk;
}
ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL,
exynos_tmu_threaded_irq,
IRQF_TRIGGER_RISING
| IRQF_SHARED | IRQF_ONESHOT,
dev_name(&pdev->dev), data);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
goto err_sclk;
@ -1117,10 +1125,6 @@ err_clk:
err_clk_sec:
if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec);
err_sensor:
if (!IS_ERR(data->regulator))
regulator_disable(data->regulator);
return ret;
}
@ -1134,9 +1138,6 @@ static void exynos_tmu_remove(struct platform_device *pdev)
clk_unprepare(data->clk);
if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec);
if (!IS_ERR(data->regulator))
regulator_disable(data->regulator);
}
#ifdef CONFIG_PM_SLEEP

View File

@ -606,6 +606,18 @@ static const struct ths_thermal_chip sun50i_h6_ths = {
.calc_temp = sun8i_ths_calc_temp,
};
static const struct ths_thermal_chip sun20i_d1_ths = {
.sensor_num = 1,
.has_bus_clk_reset = true,
.offset = 188552,
.scale = 673,
.temp_data_base = SUN50I_H6_THS_TEMP_DATA,
.calibrate = sun50i_h6_ths_calibrate,
.init = sun50i_h6_thermal_init,
.irq_ack = sun50i_h6_irq_ack,
.calc_temp = sun8i_ths_calc_temp,
};
static const struct of_device_id of_ths_match[] = {
{ .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
{ .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
@ -614,6 +626,7 @@ static const struct of_device_id of_ths_match[] = {
{ .compatible = "allwinner,sun50i-a100-ths", .data = &sun50i_a100_ths },
{ .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
{ .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
{ .compatible = "allwinner,sun20i-d1-ths", .data = &sun20i_d1_ths },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, of_ths_match);

View File

@ -37,8 +37,6 @@ static LIST_HEAD(thermal_governor_list);
static DEFINE_MUTEX(thermal_list_lock);
static DEFINE_MUTEX(thermal_governor_lock);
static atomic_t in_suspend;
static struct thermal_governor *def_governor;
/*
@ -203,9 +201,6 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
mutex_lock(&thermal_governor_lock);
mutex_lock(&tz->lock);
if (!device_is_registered(&tz->device))
goto exit;
gov = __find_governor(strim(policy));
if (!gov)
goto exit;
@ -314,21 +309,43 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz,
def_governor->throttle(tz, trip);
}
void thermal_zone_device_critical(struct thermal_zone_device *tz)
void thermal_governor_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason)
{
if (!tz->governor || !tz->governor->update_tz)
return;
tz->governor->update_tz(tz, reason);
}
static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdown)
{
/*
* poweroff_delay_ms must be a carefully profiled positive value.
* Its a must for forced_emergency_poweroff_work to be scheduled.
*/
int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS;
const char *msg = "Temperature too high";
dev_emerg(&tz->device, "%s: critical temperature reached, "
"shutting down\n", tz->type);
dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type);
hw_protection_shutdown("Temperature too high", poweroff_delay_ms);
if (shutdown)
hw_protection_shutdown(msg, poweroff_delay_ms);
else
hw_protection_reboot(msg, poweroff_delay_ms);
}
void thermal_zone_device_critical(struct thermal_zone_device *tz)
{
thermal_zone_device_halt(tz, true);
}
EXPORT_SYMBOL(thermal_zone_device_critical);
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz)
{
thermal_zone_device_halt(tz, false);
}
static void handle_critical_trips(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
{
@ -345,22 +362,51 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
}
static void handle_thermal_trip(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
struct thermal_trip *trip)
{
if (trip->temperature == THERMAL_TEMP_INVALID)
return;
if (tz->last_temperature != THERMAL_TEMP_INVALID) {
if (tz->last_temperature < trip->temperature &&
tz->temperature >= trip->temperature)
if (tz->last_temperature == THERMAL_TEMP_INVALID) {
/* Initialization. */
trip->threshold = trip->temperature;
if (tz->temperature >= trip->threshold)
trip->threshold -= trip->hysteresis;
} else if (tz->last_temperature < trip->threshold) {
/*
* The trip threshold is equal to the trip temperature, unless
* the latter has changed in the meantime. In either case,
* the trip is crossed if the current zone temperature is at
* least equal to its temperature, but otherwise ensure that
* the threshold and the trip temperature will be equal.
*/
if (tz->temperature >= trip->temperature) {
thermal_notify_tz_trip_up(tz->id,
thermal_zone_trip_id(tz, trip),
tz->temperature);
if (tz->last_temperature >= trip->temperature &&
tz->temperature < trip->temperature - trip->hysteresis)
trip->threshold = trip->temperature - trip->hysteresis;
} else {
trip->threshold = trip->temperature;
}
} else {
/*
* The previous zone temperature was above or equal to the trip
* threshold, which would be equal to the "low temperature" of
* the trip (its temperature minus its hysteresis), unless the
* trip temperature or hysteresis had changed. In either case,
* the trip is crossed if the current zone temperature is below
* the low temperature of the trip, but otherwise ensure that
* the trip threshold will be equal to the low temperature of
* the trip.
*/
if (tz->temperature < trip->temperature - trip->hysteresis) {
thermal_notify_tz_trip_down(tz->id,
thermal_zone_trip_id(tz, trip),
tz->temperature);
trip->threshold = trip->temperature;
} else {
trip->threshold = trip->temperature - trip->hysteresis;
}
}
if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT)
@ -390,9 +436,20 @@ static void update_temperature(struct thermal_zone_device *tz)
thermal_genl_sampling_temp(tz->id, temp);
}
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
thermal_zone_device,
poll_queue.work);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
static void thermal_zone_device_init(struct thermal_zone_device *tz)
{
struct thermal_instance *pos;
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
tz->temperature = THERMAL_TEMP_INVALID;
tz->prev_low_trip = -INT_MAX;
tz->prev_high_trip = INT_MAX;
@ -403,14 +460,9 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
const struct thermal_trip *trip;
struct thermal_trip *trip;
if (atomic_read(&in_suspend))
return;
if (WARN_ONCE(!tz->ops->get_temp,
"'%s' must not be called without 'get_temp' ops set\n",
__func__))
if (tz->suspended)
return;
if (!thermal_zone_device_is_enabled(tz))
@ -442,12 +494,6 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
return ret;
}
if (!device_is_registered(&tz->device)) {
mutex_unlock(&tz->lock);
return -ENODEV;
}
if (tz->ops->change_mode)
ret = tz->ops->change_mode(tz, mode);
@ -485,24 +531,21 @@ int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
return tz->mode == THERMAL_DEVICE_ENABLED;
}
static bool thermal_zone_is_present(struct thermal_zone_device *tz)
{
return !list_empty(&tz->node);
}
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
mutex_lock(&tz->lock);
if (device_is_registered(&tz->device))
if (thermal_zone_is_present(tz))
__thermal_zone_device_update(tz, event);
mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
thermal_zone_device,
poll_queue.work);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
void *data)
{
@ -694,6 +737,8 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
list_add_tail(&dev->tz_node, &tz->thermal_instances);
list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
atomic_set(&tz->need_update, 1);
thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV);
}
mutex_unlock(&cdev->lock);
mutex_unlock(&tz->lock);
@ -752,6 +797,9 @@ int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
list_del(&pos->tz_node);
list_del(&pos->cdev_node);
thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV);
mutex_unlock(&cdev->lock);
mutex_unlock(&tz->lock);
goto unbind;
@ -793,7 +841,7 @@ static void thermal_release(struct device *dev)
tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
mutex_destroy(&tz->lock);
kfree(tz);
complete(&tz->removal);
} else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) {
cdev = to_cooling_device(dev);
@ -1260,7 +1308,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
return ERR_PTR(-EINVAL);
}
if (!ops) {
if (!ops || !ops->get_temp) {
pr_err("Thermal zone device ops not defined\n");
return ERR_PTR(-EINVAL);
}
@ -1284,8 +1332,10 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
}
INIT_LIST_HEAD(&tz->thermal_instances);
INIT_LIST_HEAD(&tz->node);
ida_init(&tz->ida);
mutex_init(&tz->lock);
init_completion(&tz->removal);
id = ida_alloc(&thermal_tz_ida, GFP_KERNEL);
if (id < 0) {
result = id;
@ -1348,14 +1398,14 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
}
mutex_lock(&thermal_list_lock);
mutex_lock(&tz->lock);
list_add_tail(&tz->node, &thermal_tz_list);
mutex_unlock(&tz->lock);
mutex_unlock(&thermal_list_lock);
/* Bind cooling devices for this zone */
bind_tz(tz);
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
thermal_zone_device_init(tz);
/* Update the new thermal zone and mark it as already updated. */
if (atomic_cmpxchg(&tz->need_update, 1, 0))
@ -1369,7 +1419,6 @@ unregister:
device_del(&tz->device);
release_device:
put_device(&tz->device);
tz = NULL;
remove_id:
ida_free(&thermal_tz_ida, id);
free_tzp:
@ -1439,7 +1488,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
mutex_unlock(&thermal_list_lock);
return;
}
mutex_lock(&tz->lock);
list_del(&tz->node);
mutex_unlock(&tz->lock);
/* Unbind all cdevs associated with 'this' thermal zone */
list_for_each_entry(cdev, &thermal_cdev_list, node)
@ -1456,15 +1508,16 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
ida_free(&thermal_tz_ida, tz->id);
ida_destroy(&tz->ida);
mutex_lock(&tz->lock);
device_del(&tz->device);
mutex_unlock(&tz->lock);
kfree(tz->tzp);
put_device(&tz->device);
thermal_notify_tz_delete(tz_id);
wait_for_completion(&tz->removal);
kfree(tz);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
@ -1506,6 +1559,22 @@ exit:
}
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
static void thermal_zone_device_resume(struct work_struct *work)
{
struct thermal_zone_device *tz;
tz = container_of(work, struct thermal_zone_device, poll_queue.work);
mutex_lock(&tz->lock);
tz->suspended = false;
thermal_zone_device_init(tz);
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
mutex_unlock(&tz->lock);
}
static int thermal_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
@ -1515,17 +1584,43 @@ static int thermal_pm_notify(struct notifier_block *nb,
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
atomic_set(&in_suspend, 1);
mutex_lock(&thermal_list_lock);
list_for_each_entry(tz, &thermal_tz_list, node) {
mutex_lock(&tz->lock);
tz->suspended = true;
mutex_unlock(&tz->lock);
}
mutex_unlock(&thermal_list_lock);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
atomic_set(&in_suspend, 0);
mutex_lock(&thermal_list_lock);
list_for_each_entry(tz, &thermal_tz_list, node) {
thermal_zone_device_init(tz);
thermal_zone_device_update(tz,
THERMAL_EVENT_UNSPECIFIED);
mutex_lock(&tz->lock);
cancel_delayed_work(&tz->poll_queue);
/*
* Replace the work function with the resume one, which
* will restore the original work function and schedule
* the polling work if needed.
*/
INIT_DELAYED_WORK(&tz->poll_queue,
thermal_zone_device_resume);
/* Queue up the work without a delay. */
mod_delayed_work(system_freezable_power_efficient_wq,
&tz->poll_queue, 0);
mutex_unlock(&tz->lock);
}
mutex_unlock(&thermal_list_lock);
break;
default:
break;

View File

@ -114,16 +114,19 @@ int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf);
void __thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event);
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz);
void thermal_governor_update_tz(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
/* Helpers */
#define for_each_trip(__tz, __trip) \
for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++)
void __thermal_zone_set_trips(struct thermal_zone_device *tz);
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip);
int thermal_zone_trip_id(struct thermal_zone_device *tz,
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
/* sysfs I/F */

View File

@ -82,20 +82,18 @@ EXPORT_SYMBOL(get_thermal_instance);
*/
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
int ret = -EINVAL;
int count;
const struct thermal_trip *trip;
int crit_temp = INT_MAX;
struct thermal_trip trip;
int ret = -EINVAL;
lockdep_assert_held(&tz->lock);
ret = tz->ops->get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
for (count = 0; count < tz->num_trips; count++) {
ret = __thermal_zone_get_trip(tz, count, &trip);
if (!ret && trip.type == THERMAL_TRIP_CRITICAL) {
crit_temp = trip.temperature;
for_each_trip(tz, trip) {
if (trip->type == THERMAL_TRIP_CRITICAL) {
crit_temp = trip->temperature;
break;
}
}
@ -139,10 +137,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
goto unlock;
}
if (device_is_registered(&tz->device))
ret = __thermal_zone_get_temp(tz, temp);
else
ret = -ENODEV;
ret = __thermal_zone_get_temp(tz, temp);
unlock:
mutex_unlock(&tz->lock);

View File

@ -80,10 +80,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
mutex_lock(&tz->lock);
if (device_is_registered(&tz->device))
ret = tz->ops->get_crit_temp(tz, &temperature);
else
ret = -ENODEV;
ret = tz->ops->get_crit_temp(tz, &temperature);
mutex_unlock(&tz->lock);

View File

@ -13,9 +13,14 @@
#include "thermal_core.h"
enum thermal_genl_multicast_groups {
THERMAL_GENL_SAMPLING_GROUP = 0,
THERMAL_GENL_EVENT_GROUP = 1,
};
static const struct genl_multicast_group thermal_genl_mcgrps[] = {
{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
{ .name = THERMAL_GENL_EVENT_GROUP_NAME, },
[THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
[THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
};
static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
@ -71,6 +76,11 @@ typedef int (*cb_t)(struct param *);
static struct genl_family thermal_gnl_family;
static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group)
{
return genl_has_listeners(&thermal_gnl_family, &init_net, group);
}
/************************** Sampling encoding *******************************/
int thermal_genl_sampling_temp(int id, int temp)
@ -78,6 +88,9 @@ int thermal_genl_sampling_temp(int id, int temp)
struct sk_buff *skb;
void *hdr;
if (!thermal_group_has_listeners(THERMAL_GENL_SAMPLING_GROUP))
return 0;
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
@ -95,7 +108,7 @@ int thermal_genl_sampling_temp(int id, int temp)
genlmsg_end(skb, hdr);
genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
genlmsg_multicast(&thermal_gnl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
return 0;
out_cancel:
@ -275,6 +288,9 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
int ret = -EMSGSIZE;
void *hdr;
if (!thermal_group_has_listeners(THERMAL_GENL_EVENT_GROUP))
return 0;
msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
@ -290,7 +306,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event,
genlmsg_end(msg, hdr);
genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
genlmsg_multicast(&thermal_gnl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
return 0;
@ -450,10 +466,10 @@ out_cancel_nest:
static int thermal_genl_cmd_tz_get_trip(struct param *p)
{
struct sk_buff *msg = p->msg;
const struct thermal_trip *trip;
struct thermal_zone_device *tz;
struct nlattr *start_trip;
struct thermal_trip trip;
int ret, i, id;
int id;
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
return -EINVAL;
@ -470,16 +486,12 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
mutex_lock(&tz->lock);
for (i = 0; i < tz->num_trips; i++) {
ret = __thermal_zone_get_trip(tz, i, &trip);
if (ret)
goto out_cancel_nest;
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip.type) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip.temperature) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip.hysteresis))
for_each_trip(tz, trip) {
if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID,
thermal_zone_trip_id(tz, trip)) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) ||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis))
goto out_cancel_nest;
}

View File

@ -475,6 +475,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
struct thermal_zone_params tzp = {};
struct thermal_zone_device_ops *of_ops;
struct device_node *np;
const char *action;
int delay, pdelay;
int ntrips, mask;
int ret;
@ -511,6 +512,11 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
mask = GENMASK_ULL((ntrips) - 1, 0);
ret = of_property_read_string(np, "critical-action", &action);
if (!ret)
if (!of_ops->critical && !strcasecmp(action, "reboot"))
of_ops->critical = thermal_zone_device_critical_reboot;
tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips,
mask, data, of_ops, &tzp,
pdelay, delay);

View File

@ -83,25 +83,12 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_trip trip;
int trip_id, result;
int trip_id;
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
return -EINVAL;
mutex_lock(&tz->lock);
if (device_is_registered(dev))
result = __thermal_zone_get_trip(tz, trip_id, &trip);
else
result = -ENODEV;
mutex_unlock(&tz->lock);
if (result)
return result;
switch (trip.type) {
switch (tz->trips[trip_id].type) {
case THERMAL_TRIP_CRITICAL:
return sprintf(buf, "critical\n");
case THERMAL_TRIP_HOT:
@ -120,28 +107,33 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_trip trip;
struct thermal_trip *trip;
int trip_id, ret;
int temp;
ret = kstrtoint(buf, 10, &temp);
if (ret)
return -EINVAL;
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
return -EINVAL;
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
trip = &tz->trips[trip_id];
if (temp != trip->temperature) {
if (tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip_id, temp);
if (ret)
goto unlock;
}
thermal_zone_set_trip_temp(tz, trip, temp);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
}
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
if (ret)
goto unlock;
ret = kstrtoint(buf, 10, &trip.temperature);
if (ret)
goto unlock;
ret = thermal_zone_set_trip(tz, trip_id, &trip);
unlock:
mutex_unlock(&tz->lock);
@ -153,25 +145,12 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_trip trip;
int trip_id, ret;
int trip_id;
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
return -EINVAL;
mutex_lock(&tz->lock);
if (device_is_registered(dev))
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
else
ret = -ENODEV;
mutex_unlock(&tz->lock);
if (ret)
return ret;
return sprintf(buf, "%d\n", trip.temperature);
return sprintf(buf, "%d\n", tz->trips[trip_id].temperature);
}
static ssize_t
@ -179,28 +158,33 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_trip trip;
struct thermal_trip *trip;
int trip_id, ret;
int hyst;
ret = kstrtoint(buf, 10, &hyst);
if (ret || hyst < 0)
return -EINVAL;
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL;
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
trip = &tz->trips[trip_id];
if (hyst != trip->hysteresis) {
if (tz->ops->set_trip_hyst) {
ret = tz->ops->set_trip_hyst(tz, trip_id, hyst);
if (ret)
goto unlock;
}
trip->hysteresis = hyst;
thermal_zone_trip_updated(tz, trip);
}
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
if (ret)
goto unlock;
ret = kstrtoint(buf, 10, &trip.hysteresis);
if (ret)
goto unlock;
ret = thermal_zone_set_trip(tz, trip_id, &trip);
unlock:
mutex_unlock(&tz->lock);
@ -212,22 +196,12 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_trip trip;
int trip_id, ret;
int trip_id;
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
return -EINVAL;
mutex_lock(&tz->lock);
if (device_is_registered(dev))
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
else
ret = -ENODEV;
mutex_unlock(&tz->lock);
return ret ? ret : sprintf(buf, "%d\n", trip.hysteresis);
return sprintf(buf, "%d\n", tz->trips[trip_id].hysteresis);
}
static ssize_t
@ -276,11 +250,6 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&tz->lock);
if (!device_is_registered(dev)) {
ret = -ENODEV;
goto unlock;
}
if (!tz->ops->set_emul_temp)
tz->emul_temperature = temperature;
else
@ -289,7 +258,6 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
if (!ret)
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
unlock:
mutex_unlock(&tz->lock);
return ret ? ret : count;
@ -968,7 +936,16 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr,
return ret;
instance = container_of(attr, struct thermal_instance, weight_attr);
/* Don't race with governors using the 'weight' value */
mutex_lock(&instance->tz->lock);
instance->weight = weight;
thermal_governor_update_tz(instance->tz,
THERMAL_INSTANCE_WEIGHT_CHANGED);
mutex_unlock(&instance->tz->lock);
return count;
}

View File

@ -8,19 +8,14 @@
#include <linux/tracepoint.h>
TRACE_EVENT(thermal_power_allocator,
TP_PROTO(struct thermal_zone_device *tz, u32 *req_power,
u32 total_req_power, u32 *granted_power,
u32 total_granted_power, size_t num_actors,
u32 power_range, u32 max_allocatable_power,
int current_temp, s32 delta_temp),
TP_ARGS(tz, req_power, total_req_power, granted_power,
total_granted_power, num_actors, power_range,
max_allocatable_power, current_temp, delta_temp),
TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power,
u32 total_granted_power, int num_actors, u32 power_range,
u32 max_allocatable_power, int current_temp, s32 delta_temp),
TP_ARGS(tz, total_req_power, total_granted_power, num_actors,
power_range, max_allocatable_power, current_temp, delta_temp),
TP_STRUCT__entry(
__field(int, tz_id )
__dynamic_array(u32, req_power, num_actors )
__field(u32, total_req_power )
__dynamic_array(u32, granted_power, num_actors)
__field(u32, total_granted_power )
__field(size_t, num_actors )
__field(u32, power_range )
@ -30,11 +25,7 @@ TRACE_EVENT(thermal_power_allocator,
),
TP_fast_assign(
__entry->tz_id = tz->id;
memcpy(__get_dynamic_array(req_power), req_power,
num_actors * sizeof(*req_power));
__entry->total_req_power = total_req_power;
memcpy(__get_dynamic_array(granted_power), granted_power,
num_actors * sizeof(*granted_power));
__entry->total_granted_power = total_granted_power;
__entry->num_actors = num_actors;
__entry->power_range = power_range;
@ -43,18 +34,35 @@ TRACE_EVENT(thermal_power_allocator,
__entry->delta_temp = delta_temp;
),
TP_printk("thermal_zone_id=%d req_power={%s} total_req_power=%u granted_power={%s} total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%d delta_temperature=%d",
__entry->tz_id,
__print_array(__get_dynamic_array(req_power),
__entry->num_actors, 4),
__entry->total_req_power,
__print_array(__get_dynamic_array(granted_power),
__entry->num_actors, 4),
TP_printk("thermal_zone_id=%d total_req_power=%u total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%d delta_temperature=%d",
__entry->tz_id, __entry->total_req_power,
__entry->total_granted_power, __entry->power_range,
__entry->max_allocatable_power, __entry->current_temp,
__entry->delta_temp)
);
TRACE_EVENT(thermal_power_actor,
TP_PROTO(struct thermal_zone_device *tz, int actor_id, u32 req_power,
u32 granted_power),
TP_ARGS(tz, actor_id, req_power, granted_power),
TP_STRUCT__entry(
__field(int, tz_id)
__field(int, actor_id)
__field(u32, req_power)
__field(u32, granted_power)
),
TP_fast_assign(
__entry->tz_id = tz->id;
__entry->actor_id = actor_id;
__entry->req_power = req_power;
__entry->granted_power = granted_power;
),
TP_printk("thermal_zone_id=%d actor_id=%d req_power=%u granted_power=%u",
__entry->tz_id, __entry->actor_id, __entry->req_power,
__entry->granted_power)
);
TRACE_EVENT(thermal_power_allocator_pid,
TP_PROTO(struct thermal_zone_device *tz, s32 err, s32 err_integral,
s64 p, s64 i, s64 d, s32 output),

View File

@ -63,25 +63,21 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
*/
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{
struct thermal_trip trip;
const struct thermal_trip *trip;
int low = -INT_MAX, high = INT_MAX;
bool same_trip = false;
int i, ret;
int ret;
lockdep_assert_held(&tz->lock);
if (!tz->ops->set_trips)
return;
for (i = 0; i < tz->num_trips; i++) {
for_each_trip(tz, trip) {
bool low_set = false;
int trip_low;
ret = __thermal_zone_get_trip(tz, i , &trip);
if (ret)
return;
trip_low = trip.temperature - trip.hysteresis;
trip_low = trip->temperature - trip->hysteresis;
if (trip_low < tz->temperature && trip_low > low) {
low = trip_low;
@ -89,9 +85,9 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz)
same_trip = false;
}
if (trip.temperature > tz->temperature &&
trip.temperature < high) {
high = trip.temperature;
if (trip->temperature > tz->temperature &&
trip->temperature < high) {
high = trip->temperature;
same_trip = low_set;
}
}
@ -147,46 +143,7 @@ int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
}
EXPORT_SYMBOL_GPL(thermal_zone_get_trip);
int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id,
const struct thermal_trip *trip)
{
struct thermal_trip t;
int ret;
if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips)
return -EINVAL;
ret = __thermal_zone_get_trip(tz, trip_id, &t);
if (ret)
return ret;
if (t.type != trip->type)
return -EINVAL;
if (t.temperature != trip->temperature && tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature);
if (ret)
return ret;
}
if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) {
ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis);
if (ret)
return ret;
}
if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis))
tz->trips[trip_id] = *trip;
thermal_notify_tz_trip_change(tz->id, trip_id, trip->type,
trip->temperature, trip->hysteresis);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
return 0;
}
int thermal_zone_trip_id(struct thermal_zone_device *tz,
int thermal_zone_trip_id(const struct thermal_zone_device *tz,
const struct thermal_trip *trip)
{
/*
@ -195,3 +152,24 @@ int thermal_zone_trip_id(struct thermal_zone_device *tz,
*/
return trip - tz->trips;
}
void thermal_zone_trip_updated(struct thermal_zone_device *tz,
const struct thermal_trip *trip)
{
thermal_notify_tz_trip_change(tz->id, thermal_zone_trip_id(tz, trip),
trip->type, trip->temperature,
trip->hysteresis);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
}
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
struct thermal_trip *trip, int temp)
{
if (trip->temperature == temp)
return;
trip->temperature = temp;
thermal_notify_tz_trip_change(tz->id, thermal_zone_trip_id(tz, trip),
trip->type, trip->temperature,
trip->hysteresis);
}
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);

View File

@ -177,7 +177,17 @@ void ctrl_alt_del(void);
extern void orderly_poweroff(bool force);
extern void orderly_reboot(void);
void hw_protection_shutdown(const char *reason, int ms_until_forced);
void __hw_protection_shutdown(const char *reason, int ms_until_forced, bool shutdown);
static inline void hw_protection_reboot(const char *reason, int ms_until_forced)
{
__hw_protection_shutdown(reason, ms_until_forced, false);
}
static inline void hw_protection_shutdown(const char *reason, int ms_until_forced)
{
__hw_protection_shutdown(reason, ms_until_forced, true);
}
/*
* Emergency restart, callable from an interrupt handler.

View File

@ -51,18 +51,23 @@ enum thermal_notify_event {
THERMAL_DEVICE_POWER_CAPABILITY_CHANGED, /* power capability changed */
THERMAL_TABLE_CHANGED, /* Thermal table(s) changed */
THERMAL_EVENT_KEEP_ALIVE, /* Request for user space handler to respond */
THERMAL_TZ_BIND_CDEV, /* Cooling dev is bind to the thermal zone */
THERMAL_TZ_UNBIND_CDEV, /* Cooling dev is unbind from the thermal zone */
THERMAL_INSTANCE_WEIGHT_CHANGED, /* Thermal instance weight changed */
};
/**
* struct thermal_trip - representation of a point in temperature domain
* @temperature: temperature value in miliCelsius
* @hysteresis: relative hysteresis in miliCelsius
* @threshold: trip crossing notification threshold miliCelsius
* @type: trip point type
* @priv: pointer to driver data associated with this trip
*/
struct thermal_trip {
int temperature;
int hysteresis;
int threshold;
enum thermal_trip_type type;
void *priv;
};
@ -115,6 +120,7 @@ struct thermal_cooling_device {
* @id: unique id number for each thermal zone
* @type: the thermal zone device type
* @device: &struct device for this thermal zone
* @removal: removal completion
* @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
@ -149,11 +155,13 @@ struct thermal_cooling_device {
* @node: node in thermal_tz_list (in thermal_core.c)
* @poll_queue: delayed work for polling
* @notify_event: Last notification event
* @suspended: thermal zone suspend indicator
*/
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct completion removal;
struct attribute_group trips_attribute_group;
struct thermal_attr *trip_temp_attrs;
struct thermal_attr *trip_type_attrs;
@ -181,6 +189,7 @@ struct thermal_zone_device {
struct list_head node;
struct delayed_work poll_queue;
enum thermal_notify_event notify_event;
bool suspended;
};
/**
@ -193,6 +202,8 @@ struct thermal_zone_device {
* thermal zone.
* @throttle: callback called for every trip point even if temperature is
* below the trip point temperature
* @update_tz: callback called when thermal zone internals have changed, e.g.
* thermal cooling instance was added/removed
* @governor_list: node in thermal_governor_list (in thermal_core.c)
*/
struct thermal_governor {
@ -201,6 +212,8 @@ struct thermal_governor {
void (*unbind_from_tz)(struct thermal_zone_device *tz);
int (*throttle)(struct thermal_zone_device *tz,
const struct thermal_trip *trip);
void (*update_tz)(struct thermal_zone_device *tz,
enum thermal_notify_event reason);
struct list_head governor_list;
};
@ -280,10 +293,6 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip);
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip);
int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id,
const struct thermal_trip *trip);
int for_each_thermal_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *),
void *data);
@ -291,6 +300,8 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *),
void *data);
int thermal_zone_get_num_trips(struct thermal_zone_device *tz);
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
struct thermal_trip *trip, int temp);
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp);

View File

@ -970,21 +970,24 @@ static void hw_failure_emergency_poweroff(int poweroff_delay_ms)
}
/**
* hw_protection_shutdown - Trigger an emergency system poweroff
* __hw_protection_shutdown - Trigger an emergency system shutdown or reboot
*
* @reason: Reason of emergency shutdown to be printed.
* @ms_until_forced: Time to wait for orderly shutdown before tiggering a
* forced shudown. Negative value disables the forced
* shutdown.
* @reason: Reason of emergency shutdown or reboot to be printed.
* @ms_until_forced: Time to wait for orderly shutdown or reboot before
* triggering it. Negative value disables the forced
* shutdown or reboot.
* @shutdown: If true, indicates that a shutdown will happen
* after the critical tempeature is reached.
* If false, indicates that a reboot will happen
* after the critical tempeature is reached.
*
* Initiate an emergency system shutdown in order to protect hardware from
* further damage. Usage examples include a thermal protection or a voltage or
* current regulator failures.
* NOTE: The request is ignored if protection shutdown is already pending even
* if the previous request has given a large timeout for forced shutdown.
* Can be called from any context.
* Initiate an emergency system shutdown or reboot in order to protect
* hardware from further damage. Usage examples include a thermal protection.
* NOTE: The request is ignored if protection shutdown or reboot is already
* pending even if the previous request has given a large timeout for forced
* shutdown/reboot.
*/
void hw_protection_shutdown(const char *reason, int ms_until_forced)
void __hw_protection_shutdown(const char *reason, int ms_until_forced, bool shutdown)
{
static atomic_t allow_proceed = ATOMIC_INIT(1);
@ -999,9 +1002,12 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced)
* orderly_poweroff failure
*/
hw_failure_emergency_poweroff(ms_until_forced);
orderly_poweroff(true);
if (shutdown)
orderly_poweroff(true);
else
orderly_reboot();
}
EXPORT_SYMBOL_GPL(hw_protection_shutdown);
EXPORT_SYMBOL_GPL(__hw_protection_shutdown);
static int __init reboot_setup(char *str)
{

View File

@ -213,7 +213,7 @@ void show_cooling_device(void)
* cooling device instances. skip unused idr.
*/
mvwprintw(cooling_device_window, j + 2, 1,
"%02d %12.12s%6d %6d",
"%02d %12.12s%6lu %6lu",
ptdata.cdi[j].instance,
ptdata.cdi[j].type,
ptdata.cdi[j].cur_state,