Commit 7f73ba68 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents bd012f3a 17e8b764
......@@ -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
......
......@@ -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>;
};
# 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>;
};
* 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";
};
......@@ -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>;
......
......@@ -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>;
......
......@@ -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
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -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 = {
......
This diff is collapsed.
......@@ -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);
......
......@@ -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);
}
......
......@@ -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");
......
This diff is collapsed.
......@@ -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);
......
This diff is collapsed.
......@@ -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 */
......
......@@ -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);
......
......@@ -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);
......
......@@ -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 @@ static int thermal_genl_cmd_tz_get_id(struct param *p)
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;
}
......
......@@ -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);
......
......@@ -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];
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
if (ret)
goto unlock;
if (temp != trip->temperature) {
if (tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip_id, temp);
if (ret)
goto unlock;
}
ret = kstrtoint(buf, 10, &trip.temperature);
if (ret)
goto unlock;
thermal_zone_set_trip_temp(tz, trip, temp);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
}
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];
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
if (ret)
goto unlock;
if (hyst != trip->hysteresis) {
if (tz->ops->set_trip_hyst) {
ret = tz->ops->set_trip_hyst(tz, trip_id, hyst);
if (ret)
goto unlock;
}
ret = kstrtoint(buf, 10, &trip.hysteresis);
if (ret)
goto unlock;
trip->hysteresis = hyst;
thermal_zone_trip_updated(tz, trip);
}
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;
}
......@@ -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),
......
......@@ -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);
......@@ -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.
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment