mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
Thermal control updates for 6.3-rc1
- Rework a large bunch of drivers to use the generic thermal trip structure and use the opportunity to do more cleanups by removing unused functions from the OF code (Daniel Lezcano). - Remove core header inclusion from drivers (Daniel Lezcano). - Fix some locking issues related to the generic thermal trip rework (Johan Hovold). - Fix a crash when requesting the critical temperature on tegra, which is related to the generic trip point work (Jon Hunter). - Clean up thermal device unregistration code (Viresh Kumar). - Fix and clean up thermal control core initialization error code paths (Daniel Lezcano). - Relocate the trip points handling code into a separate file (Daniel Lezcano). - Make the thermal core fail registration of thermal zones and cooling devices if the thermal class has not been registered (Rafael Wysocki). - Add trip point initialization helper functions for ACPI-defined trip points and modify two thermal drivers to use them (Rafael Wysocki, Daniel Lezcano). - Make the core thermal control code use sysfs_emit_at() instead of scnprintf() where applicable (ye xingchen). - Consolidate code accessing the Intel TCC (Thermal Control Circuitry) MSRs by introducing library functions for that and making the TCC-related code in thermal drivers use them (Zhang Rui). - Enhance the x86_pkg_temp_thermal driver to support dynamic tjmax changes (Zhang Rui). - Address an "unsigned expression compared with zero" warning in the intel_soc_dts_iosf thermal driver (Yang Li). - Update comments regarding two functions in the Intel Menlow thermal driver (Deming Wang). - Use sysfs_emit_at() instead of scnprintf() in the int340x thermal driver (ye xingchen). - Make the intel_pch thermal driver support the Wellsburg PCH (Tim Zimmermann). - Modify the intel_pch and processor_thermal_device_pci thermal drivers use generic trip point tables instead of thermal zone trip point callbacks (Daniel Lezcano). - Add production mode attribute sysfs attribute to the int340x thermal driver (Srinivas Pandruvada). - Rework dynamic trip point updates handling and locking in the int340x thermal driver (Rafael Wysocki). - Make the int340x thermal driver use a generic trip points table instead of thermal zone trip point callbacks (Rafael Wysocki, Daniel Lezcano). - Clean up and improve the int340x thermal driver (Rafael Wysocki). - Simplify and clean up the intel_pch thermal driver (Rafael Wysocki). - Fix the Intel powerclamp thermal driver and make it use the common idle injection framework (Srinivas Pandruvada). - Add two module parameters, cpumask and max_idle, to the Intel powerclamp thermal driver to allow it to affect only a specific subset of CPUs instead of all of them (Srinivas Pandruvada). - Make the Intel quark_dts thermal driver Use generic trip point objects instead of its own trip point representation (Daniel Lezcano). - Add toctree entry for thermal documents and fix two issues in the Intel powerclamp driver documentation (Bagas Sanjaya). - Use strscpy() to instead of strncpy() in the thermal core (Xu Panda). - Fix thermal_sampling_exit() (Vincent Guittot). - Add Mediatek Low Voltage Thermal Sensor (LVTS) driver (Balsam Chihi). - Add r8a779g0 RCar support to the rcar_gen3 thermal driver (Geert Uytterhoeven). - Fix useless call to set_trips() when resuming in the rcar_gen3 thermal control driver and add interrupt support detection at init time to it (Niklas Söderlund). - Fix memory corruption in the hi3660 thermal driver (Yongqin Liu). - Fix include path for libnl3 in pkg-config file for libthermal (Vibhav Pant). - Remove syscfg-based driver for st as the platform is not supported any more (Alain Volmat). -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmPuJuESHHJqd0Byand5 c29ja2kubmV0AAoJEILEb/54YlRxef0P/3h73rPjGEyuDlvXaazyXsJ2ItIoGeXF v9sDwK3IPeFTNwAu80RySXQViOG6G1e5Cl8Ee+LuuMZfPRlBnr3n35BazejDDK0N u3YAhPqtNOvWqr31T3A27dYtK+feFR2QL9SGFP0E4yxS1jpMOSO4Q24z7yaXdegT hD8YT1HbTW4Cra7A17qdXsG8LkIe0+GQXy7Ig/Dul1eqXTM4RSReGTmXic66hGpv lutqIQl8VdjmVBcQtTustpdycAD9zj07xd9BvOyM0lmF90zt6S0VOWFDsk+8u1jA FCiuRLBAM1xbguxGubahTVOM051J/MdfM5WqGgPtesNIXlDq4Je2WUGC07jGvSfV DMjNNb+nTkD3BK+BEe+rgv3KZBngj4p2sGHFW19v3EPdGftzohqDD5Oqn0GpsKR0 J4GaT04T66A6jlNdzY/nPfOIw5FYEAsMwx4hR0qtEWDMT4uYtXQYM5iml9TBDoDx Kqyx+N8KhaKnQ4PLZ0MwtusyZydKQC1S1YK6G2eo+bXeJEre07FjZkiNfURi5gv9 lrKS5nbAGBqUrNV4XnS18RmGAC+bxuQrNA5Gr0ouYaaLMT+jGzcdu1yCMeWJxwZI fFGAwE6sOU8EtmdGJrQdJt4eKCnpzOS7I1XuMDTBstl8Wv92x/YbH39vOl9wbJVs rmSkM+4t+sXb =tZwm -----END PGP SIGNATURE----- Merge tag 'thermal-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull thermal control updates from Rafael Wysocki: "The majority of changes here are related to the general switch-over to using arrays of generic trip point structures registered along with a thermal zone instead of trip point callbacks (this has been done mostly by Daniel Lezcano with some help from yours truly on the Intel drivers front). Apart from that and the related reorganization of code, there are some enhancements of the existing driver and a new Mediatek Low Voltage Thermal Sensor (LVTS) driver. The Intel powerclamp undergoes a major rework so it will use the generic idle_inject facility for CPU idle time injection going forward and it will take additional module parameters for specifying the subset of CPUs to be affected by it (work done by Srinivas Pandruvada). Also included are assorted fixes and a whole bunch of cleanups. Specifics: - Rework a large bunch of drivers to use the generic thermal trip structure and use the opportunity to do more cleanups by removing unused functions from the OF code (Daniel Lezcano) - Remove core header inclusion from drivers (Daniel Lezcano) - Fix some locking issues related to the generic thermal trip rework (Johan Hovold) - Fix a crash when requesting the critical temperature on tegra, which is related to the generic trip point work (Jon Hunter) - Clean up thermal device unregistration code (Viresh Kumar) - Fix and clean up thermal control core initialization error code paths (Daniel Lezcano) - Relocate the trip points handling code into a separate file (Daniel Lezcano) - Make the thermal core fail registration of thermal zones and cooling devices if the thermal class has not been registered (Rafael Wysocki) - Add trip point initialization helper functions for ACPI-defined trip points and modify two thermal drivers to use them (Rafael Wysocki, Daniel Lezcano) - Make the core thermal control code use sysfs_emit_at() instead of scnprintf() where applicable (ye xingchen) - Consolidate code accessing the Intel TCC (Thermal Control Circuitry) MSRs by introducing library functions for that and making the TCC-related code in thermal drivers use them (Zhang Rui) - Enhance the x86_pkg_temp_thermal driver to support dynamic tjmax changes (Zhang Rui) - Address an "unsigned expression compared with zero" warning in the intel_soc_dts_iosf thermal driver (Yang Li) - Update comments regarding two functions in the Intel Menlow thermal driver (Deming Wang) - Use sysfs_emit_at() instead of scnprintf() in the int340x thermal driver (ye xingchen) - Make the intel_pch thermal driver support the Wellsburg PCH (Tim Zimmermann) - Modify the intel_pch and processor_thermal_device_pci thermal drivers use generic trip point tables instead of thermal zone trip point callbacks (Daniel Lezcano) - Add production mode attribute sysfs attribute to the int340x thermal driver (Srinivas Pandruvada) - Rework dynamic trip point updates handling and locking in the int340x thermal driver (Rafael Wysocki) - Make the int340x thermal driver use a generic trip points table instead of thermal zone trip point callbacks (Rafael Wysocki, Daniel Lezcano) - Clean up and improve the int340x thermal driver (Rafael Wysocki) - Simplify and clean up the intel_pch thermal driver (Rafael Wysocki) - Fix the Intel powerclamp thermal driver and make it use the common idle injection framework (Srinivas Pandruvada) - Add two module parameters, cpumask and max_idle, to the Intel powerclamp thermal driver to allow it to affect only a specific subset of CPUs instead of all of them (Srinivas Pandruvada) - Make the Intel quark_dts thermal driver Use generic trip point objects instead of its own trip point representation (Daniel Lezcano) - Add toctree entry for thermal documents and fix two issues in the Intel powerclamp driver documentation (Bagas Sanjaya) - Use strscpy() to instead of strncpy() in the thermal core (Xu Panda) - Fix thermal_sampling_exit() (Vincent Guittot) - Add Mediatek Low Voltage Thermal Sensor (LVTS) driver (Balsam Chihi) - Add r8a779g0 RCar support to the rcar_gen3 thermal driver (Geert Uytterhoeven) - Fix useless call to set_trips() when resuming in the rcar_gen3 thermal control driver and add interrupt support detection at init time to it (Niklas Söderlund) - Fix memory corruption in the hi3660 thermal driver (Yongqin Liu) - Fix include path for libnl3 in pkg-config file for libthermal (Vibhav Pant) - Remove syscfg-based driver for st as the platform is not supported any more (Alain Volmat)" * tag 'thermal-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (135 commits) thermal/drivers/st: Remove syscfg based driver thermal: Remove core header inclusion from drivers tools/lib/thermal: Fix include path for libnl3 in pkg-config file. thermal/drivers/hisi: Drop second sensor hi3660 thermal/drivers/rcar_gen3_thermal: Fix device initialization thermal/drivers/rcar_gen3_thermal: Create device local ops struct thermal/drivers/rcar_gen3_thermal: Do not call set_trips() when resuming thermal/drivers/rcar_gen3: Add support for R-Car V4H dt-bindings: thermal: rcar-gen3-thermal: Add r8a779g0 support thermal/drivers/mediatek: Add the Low Voltage Thermal Sensor driver dt-bindings: thermal: mediatek: Add LVTS thermal controllers thermal/drivers/mediatek: Relocate driver to mediatek folder tools/lib/thermal: Fix thermal_sampling_exit() Documentation: powerclamp: Fix numbered lists formatting Documentation: powerclamp: Escape wildcard in cpumask description Documentation: admin-guide: Add toctree entry for thermal docs thermal: intel: powerclamp: Add two module parameters Documentation: admin-guide: Move intel_powerclamp documentation thermal: core: Use sysfs_emit_at() instead of scnprintf() thermal: intel: powerclamp: Fix duration module parameter ...
This commit is contained in:
commit
1b72607d73
@ -116,6 +116,7 @@ configure specific aspects of kernel behavior to your liking.
|
||||
svga
|
||||
syscall-user-dispatch
|
||||
sysrq
|
||||
thermal/index
|
||||
thunderbolt
|
||||
ufs
|
||||
unicode
|
||||
|
8
Documentation/admin-guide/thermal/index.rst
Normal file
8
Documentation/admin-guide/thermal/index.rst
Normal file
@ -0,0 +1,8 @@
|
||||
=================
|
||||
Thermal Subsystem
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
intel_powerclamp
|
@ -26,6 +26,8 @@ By:
|
||||
- Generic Thermal Layer (sysfs)
|
||||
- Kernel APIs (TBD)
|
||||
|
||||
(*) Module Parameters
|
||||
|
||||
INTRODUCTION
|
||||
============
|
||||
|
||||
@ -153,13 +155,15 @@ b) determine the amount of compensation needed at each target ratio
|
||||
Compensation to each target ratio consists of two parts:
|
||||
|
||||
a) steady state error compensation
|
||||
This is to offset the error occurring when the system can
|
||||
enter idle without extra wakeups (such as external interrupts).
|
||||
|
||||
This is to offset the error occurring when the system can
|
||||
enter idle without extra wakeups (such as external interrupts).
|
||||
|
||||
b) dynamic error compensation
|
||||
When an excessive amount of wakeups occurs during idle, an
|
||||
additional idle ratio can be added to quiet interrupts, by
|
||||
slowing down CPU activities.
|
||||
|
||||
When an excessive amount of wakeups occurs during idle, an
|
||||
additional idle ratio can be added to quiet interrupts, by
|
||||
slowing down CPU activities.
|
||||
|
||||
A debugfs file is provided for the user to examine compensation
|
||||
progress and results, such as on a Westmere system::
|
||||
@ -281,6 +285,7 @@ cur_state returns value -1 instead of 0 which is to avoid confusing
|
||||
100% busy state with the disabled state.
|
||||
|
||||
Example usage:
|
||||
|
||||
- To inject 25% idle time::
|
||||
|
||||
$ sudo sh -c "echo 25 > /sys/class/thermal/cooling_device80/cur_state
|
||||
@ -318,3 +323,23 @@ device, a PID based userspace thermal controller can manage to
|
||||
control CPU temperature effectively, when no other thermal influence
|
||||
is added. For example, a UltraBook user can compile the kernel under
|
||||
certain temperature (below most active trip points).
|
||||
|
||||
Module Parameters
|
||||
=================
|
||||
|
||||
``cpumask`` (RW)
|
||||
A bit mask of CPUs to inject idle. The format of the bitmask is same as
|
||||
used in other subsystems like in /proc/irq/\*/smp_affinity. The mask is
|
||||
comma separated 32 bit groups. Each CPU is one bit. For example for a 256
|
||||
CPU system the full mask is:
|
||||
ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff
|
||||
|
||||
The rightmost mask is for CPU 0-32.
|
||||
|
||||
``max_idle`` (RW)
|
||||
Maximum injected idle time to the total CPU time ratio in percent range
|
||||
from 1 to 100. Even if the cooling device max_state is always 100 (100%),
|
||||
this parameter allows to add a max idle percent limit. The default is 50,
|
||||
to match the current implementation of powerclamp driver. Also doesn't
|
||||
allow value more than 75, if the cpumask includes every CPU present in
|
||||
the system.
|
@ -0,0 +1,142 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/mediatek,lvts-thermal.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek SoC Low Voltage Thermal Sensor (LVTS)
|
||||
|
||||
maintainers:
|
||||
- Balsam CHIHI <bchihi@baylibre.com>
|
||||
|
||||
description: |
|
||||
LVTS is a thermal management architecture composed of three subsystems,
|
||||
a Sensing device - Thermal Sensing Micro Circuit Unit (TSMCU),
|
||||
a Converter - Low Voltage Thermal Sensor converter (LVTS), and
|
||||
a Digital controller (LVTS_CTRL).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8192-lvts-ap
|
||||
- mediatek,mt8192-lvts-mcu
|
||||
- mediatek,mt8195-lvts-ap
|
||||
- mediatek,mt8195-lvts-mcu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
description: LVTS reset for clearing temporary data on AP/MCU.
|
||||
|
||||
nvmem-cells:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Calibration eFuse data 1 for LVTS
|
||||
- description: Calibration eFuse data 2 for LVTS
|
||||
|
||||
nvmem-cell-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: lvts-calib-data-1
|
||||
- const: lvts-calib-data-2
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
|
||||
allOf:
|
||||
- $ref: thermal-sensor.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- mediatek,mt8192-lvts-ap
|
||||
- mediatek,mt8192-lvts-mcu
|
||||
then:
|
||||
properties:
|
||||
nvmem-cells:
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cell-names:
|
||||
maxItems: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- mediatek,mt8195-lvts-ap
|
||||
- mediatek,mt8195-lvts-mcu
|
||||
then:
|
||||
properties:
|
||||
nvmem-cells:
|
||||
minItems: 2
|
||||
|
||||
nvmem-cell-names:
|
||||
minItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- resets
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
- "#thermal-sensor-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/mt8195-clk.h>
|
||||
#include <dt-bindings/reset/mt8195-resets.h>
|
||||
#include <dt-bindings/thermal/mediatek,lvts-thermal.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
lvts_mcu: thermal-sensor@11278000 {
|
||||
compatible = "mediatek,mt8195-lvts-mcu";
|
||||
reg = <0 0x11278000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&infracfg_ao CLK_INFRA_AO_THERM>;
|
||||
resets = <&infracfg_ao MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST>;
|
||||
nvmem-cells = <&lvts_efuse_data1 &lvts_efuse_data2>;
|
||||
nvmem-cell-names = "lvts-calib-data-1", "lvts-calib-data-2";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
thermal_zones: thermal-zones {
|
||||
cpu0-thermal {
|
||||
polling-delay = <1000>;
|
||||
polling-delay-passive = <250>;
|
||||
thermal-sensors = <&lvts_mcu MT8195_MCU_LITTLE_CPU0>;
|
||||
|
||||
trips {
|
||||
cpu0_alert: trip-alert {
|
||||
temperature = <85000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
cpu0_crit: trip-crit {
|
||||
temperature = <100000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -13,6 +13,7 @@ properties:
|
||||
enum:
|
||||
- qcom,spmi-adc-tm5
|
||||
- qcom,spmi-adc-tm5-gen2
|
||||
- qcom,adc-tm7 # Incomplete / subject to change
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -37,6 +37,7 @@ properties:
|
||||
- description: v1 of TSENS
|
||||
items:
|
||||
- enum:
|
||||
- qcom,msm8956-tsens
|
||||
- qcom,msm8976-tsens
|
||||
- qcom,qcs404-tsens
|
||||
- const: qcom,tsens-v1
|
||||
@ -80,18 +81,120 @@ properties:
|
||||
maxItems: 2
|
||||
|
||||
nvmem-cells:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
Reference to an nvmem node for the calibration data
|
||||
oneOf:
|
||||
- minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
Reference to an nvmem node for the calibration data
|
||||
- minItems: 5
|
||||
maxItems: 35
|
||||
description: |
|
||||
Reference to nvmem cells for the calibration mode, two calibration
|
||||
bases and two cells per each sensor
|
||||
# special case for msm8974 / apq8084
|
||||
- maxItems: 51
|
||||
description: |
|
||||
Reference to nvmem cells for the calibration mode, two calibration
|
||||
bases and two cells per each sensor, main and backup copies, plus use_backup cell
|
||||
|
||||
nvmem-cell-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: calib
|
||||
- enum:
|
||||
- calib_backup
|
||||
- calib_sel
|
||||
oneOf:
|
||||
- minItems: 1
|
||||
items:
|
||||
- const: calib
|
||||
- enum:
|
||||
- calib_backup
|
||||
- calib_sel
|
||||
- minItems: 5
|
||||
items:
|
||||
- const: mode
|
||||
- const: base1
|
||||
- const: base2
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
- pattern: '^s[0-9]+_p1$'
|
||||
- pattern: '^s[0-9]+_p2$'
|
||||
# special case for msm8974 / apq8084
|
||||
- items:
|
||||
- const: mode
|
||||
- const: base1
|
||||
- const: base2
|
||||
- const: use_backup
|
||||
- const: mode_backup
|
||||
- const: base1_backup
|
||||
- const: base2_backup
|
||||
- const: s0_p1
|
||||
- const: s0_p2
|
||||
- const: s1_p1
|
||||
- const: s1_p2
|
||||
- const: s2_p1
|
||||
- const: s2_p2
|
||||
- const: s3_p1
|
||||
- const: s3_p2
|
||||
- const: s4_p1
|
||||
- const: s4_p2
|
||||
- const: s5_p1
|
||||
- const: s5_p2
|
||||
- const: s6_p1
|
||||
- const: s6_p2
|
||||
- const: s7_p1
|
||||
- const: s7_p2
|
||||
- const: s8_p1
|
||||
- const: s8_p2
|
||||
- const: s9_p1
|
||||
- const: s9_p2
|
||||
- const: s10_p1
|
||||
- const: s10_p2
|
||||
- const: s0_p1_backup
|
||||
- const: s0_p2_backup
|
||||
- const: s1_p1_backup
|
||||
- const: s1_p2_backup
|
||||
- const: s2_p1_backup
|
||||
- const: s2_p2_backup
|
||||
- const: s3_p1_backup
|
||||
- const: s3_p2_backup
|
||||
- const: s4_p1_backup
|
||||
- const: s4_p2_backup
|
||||
- const: s5_p1_backup
|
||||
- const: s5_p2_backup
|
||||
- const: s6_p1_backup
|
||||
- const: s6_p2_backup
|
||||
- const: s7_p1_backup
|
||||
- const: s7_p2_backup
|
||||
- const: s8_p1_backup
|
||||
- const: s8_p2_backup
|
||||
- const: s9_p1_backup
|
||||
- const: s9_p2_backup
|
||||
- const: s10_p1_backup
|
||||
- const: s10_p2_backup
|
||||
|
||||
"#qcom,sensors":
|
||||
description:
|
||||
@ -220,6 +323,36 @@ examples:
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
// Example 1 (new calbiration data: for pre v1 IP):
|
||||
thermal-sensor@900000 {
|
||||
compatible = "qcom,msm8916-tsens", "qcom,tsens-v0_1";
|
||||
reg = <0x4a9000 0x1000>, /* TM */
|
||||
<0x4a8000 0x1000>; /* SROT */
|
||||
|
||||
nvmem-cells = <&tsens_mode>,
|
||||
<&tsens_base1>, <&tsens_base2>,
|
||||
<&tsens_s0_p1>, <&tsens_s0_p2>,
|
||||
<&tsens_s1_p1>, <&tsens_s1_p2>,
|
||||
<&tsens_s2_p1>, <&tsens_s2_p2>,
|
||||
<&tsens_s4_p1>, <&tsens_s4_p2>,
|
||||
<&tsens_s5_p1>, <&tsens_s5_p2>;
|
||||
nvmem-cell-names = "mode",
|
||||
"base1", "base2",
|
||||
"s0_p1", "s0_p2",
|
||||
"s1_p1", "s1_p2",
|
||||
"s2_p1", "s2_p2",
|
||||
"s4_p1", "s4_p2",
|
||||
"s5_p1", "s5_p2";
|
||||
|
||||
interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "uplow";
|
||||
|
||||
#qcom,sensors = <5>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
// Example 1 (legacy: for pre v1 IP):
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
- renesas,r8a77980-thermal # R-Car V3H
|
||||
- renesas,r8a779a0-thermal # R-Car V3U
|
||||
- renesas,r8a779f0-thermal # R-Car S4-8
|
||||
- renesas,r8a779g0-thermal # R-Car V4H
|
||||
|
||||
reg: true
|
||||
|
||||
@ -80,6 +81,7 @@ else:
|
||||
- description: TSC1 registers
|
||||
- description: TSC2 registers
|
||||
- description: TSC3 registers
|
||||
- description: TSC4 registers
|
||||
if:
|
||||
not:
|
||||
properties:
|
||||
@ -87,6 +89,7 @@ else:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,r8a779f0-thermal
|
||||
- renesas,r8a779g0-thermal
|
||||
then:
|
||||
required:
|
||||
- interrupts
|
||||
|
@ -14,7 +14,6 @@ Thermal
|
||||
|
||||
exynos_thermal
|
||||
exynos_thermal_emulation
|
||||
intel_powerclamp
|
||||
nouveau_thermal
|
||||
x86_pkg_temperature_thermal
|
||||
intel_dptf
|
||||
|
@ -84,6 +84,9 @@ DPTF ACPI Drivers interface
|
||||
https:/github.com/intel/thermal_daemon for decoding
|
||||
thermal table.
|
||||
|
||||
``production_mode`` (RO)
|
||||
When different from zero, manufacturer locked thermal configuration
|
||||
from further changes.
|
||||
|
||||
ACPI Thermal Relationship table interface
|
||||
------------------------------------------
|
||||
|
@ -20529,6 +20529,7 @@ S: Supported
|
||||
Q: https://patchwork.kernel.org/project/linux-pm/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git thermal
|
||||
F: Documentation/ABI/testing/sysfs-class-thermal
|
||||
F: Documentation/admin-guide/thermal/
|
||||
F: Documentation/devicetree/bindings/thermal/
|
||||
F: Documentation/driver-api/thermal/
|
||||
F: drivers/thermal/
|
||||
|
@ -1079,8 +1079,6 @@ struct mbox_list {
|
||||
#if IS_ENABLED(CONFIG_THERMAL)
|
||||
struct ch_thermal {
|
||||
struct thermal_zone_device *tzdev;
|
||||
int trip_temp;
|
||||
int trip_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -29,36 +29,12 @@ static int cxgb4_thermal_get_temp(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxgb4_thermal_get_trip_type(struct thermal_zone_device *tzdev,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct adapter *adap = tzdev->devdata;
|
||||
|
||||
if (!adap->ch_thermal.trip_temp)
|
||||
return -EINVAL;
|
||||
|
||||
*type = adap->ch_thermal.trip_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxgb4_thermal_get_trip_temp(struct thermal_zone_device *tzdev,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct adapter *adap = tzdev->devdata;
|
||||
|
||||
if (!adap->ch_thermal.trip_temp)
|
||||
return -EINVAL;
|
||||
|
||||
*temp = adap->ch_thermal.trip_temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops cxgb4_thermal_ops = {
|
||||
.get_temp = cxgb4_thermal_get_temp,
|
||||
.get_trip_type = cxgb4_thermal_get_trip_type,
|
||||
.get_trip_temp = cxgb4_thermal_get_trip_temp,
|
||||
};
|
||||
|
||||
static struct thermal_trip trip = { .type = THERMAL_TRIP_CRITICAL } ;
|
||||
|
||||
int cxgb4_thermal_init(struct adapter *adap)
|
||||
{
|
||||
struct ch_thermal *ch_thermal = &adap->ch_thermal;
|
||||
@ -79,15 +55,14 @@ int cxgb4_thermal_init(struct adapter *adap)
|
||||
if (ret < 0) {
|
||||
num_trip = 0; /* could not get trip temperature */
|
||||
} else {
|
||||
ch_thermal->trip_temp = val * 1000;
|
||||
ch_thermal->trip_type = THERMAL_TRIP_CRITICAL;
|
||||
trip.temperature = val * 1000;
|
||||
}
|
||||
|
||||
snprintf(ch_tz_name, sizeof(ch_tz_name), "cxgb4_%s", adap->name);
|
||||
ch_thermal->tzdev = thermal_zone_device_register(ch_tz_name, num_trip,
|
||||
0, adap,
|
||||
&cxgb4_thermal_ops,
|
||||
NULL, 0, 0);
|
||||
ch_thermal->tzdev = thermal_zone_device_register_with_trips(ch_tz_name, &trip, num_trip,
|
||||
0, adap,
|
||||
&cxgb4_thermal_ops,
|
||||
NULL, 0, 0);
|
||||
if (IS_ERR(ch_thermal->tzdev)) {
|
||||
ret = PTR_ERR(ch_thermal->tzdev);
|
||||
dev_err(adap->pdev_dev, "Failed to register thermal zone\n");
|
||||
|
@ -36,33 +36,39 @@ enum mlxsw_thermal_trips {
|
||||
MLXSW_THERMAL_TEMP_TRIP_HOT,
|
||||
};
|
||||
|
||||
struct mlxsw_thermal_trip {
|
||||
int type;
|
||||
int temp;
|
||||
int hyst;
|
||||
struct mlxsw_cooling_states {
|
||||
int min_state;
|
||||
int max_state;
|
||||
};
|
||||
|
||||
static const struct mlxsw_thermal_trip default_thermal_trips[] = {
|
||||
static const struct thermal_trip default_thermal_trips[] = {
|
||||
{ /* In range - 0-40% PWM */
|
||||
.type = THERMAL_TRIP_ACTIVE,
|
||||
.temp = MLXSW_THERMAL_ASIC_TEMP_NORM,
|
||||
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
|
||||
.min_state = 0,
|
||||
.max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
|
||||
.temperature = MLXSW_THERMAL_ASIC_TEMP_NORM,
|
||||
.hysteresis = MLXSW_THERMAL_HYSTERESIS_TEMP,
|
||||
},
|
||||
{
|
||||
/* In range - 40-100% PWM */
|
||||
.type = THERMAL_TRIP_ACTIVE,
|
||||
.temp = MLXSW_THERMAL_ASIC_TEMP_HIGH,
|
||||
.hyst = MLXSW_THERMAL_HYSTERESIS_TEMP,
|
||||
.min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
|
||||
.max_state = MLXSW_THERMAL_MAX_STATE,
|
||||
.temperature = MLXSW_THERMAL_ASIC_TEMP_HIGH,
|
||||
.hysteresis = MLXSW_THERMAL_HYSTERESIS_TEMP,
|
||||
},
|
||||
{ /* Warning */
|
||||
.type = THERMAL_TRIP_HOT,
|
||||
.temp = MLXSW_THERMAL_ASIC_TEMP_HOT,
|
||||
.temperature = MLXSW_THERMAL_ASIC_TEMP_HOT,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mlxsw_cooling_states default_cooling_states[] = {
|
||||
{
|
||||
.min_state = 0,
|
||||
.max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
|
||||
},
|
||||
{
|
||||
.min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
|
||||
.max_state = MLXSW_THERMAL_MAX_STATE,
|
||||
},
|
||||
{
|
||||
.min_state = MLXSW_THERMAL_MAX_STATE,
|
||||
.max_state = MLXSW_THERMAL_MAX_STATE,
|
||||
},
|
||||
@ -78,7 +84,8 @@ struct mlxsw_thermal;
|
||||
struct mlxsw_thermal_module {
|
||||
struct mlxsw_thermal *parent;
|
||||
struct thermal_zone_device *tzdev;
|
||||
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
struct thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
struct mlxsw_cooling_states cooling_states[MLXSW_THERMAL_NUM_TRIPS];
|
||||
int module; /* Module or gearbox number */
|
||||
u8 slot_index;
|
||||
};
|
||||
@ -99,7 +106,8 @@ struct mlxsw_thermal {
|
||||
int polling_delay;
|
||||
struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
|
||||
u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
|
||||
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
struct thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
|
||||
struct mlxsw_cooling_states cooling_states[MLXSW_THERMAL_NUM_TRIPS];
|
||||
struct mlxsw_thermal_area line_cards[];
|
||||
};
|
||||
|
||||
@ -136,9 +144,9 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
|
||||
static void
|
||||
mlxsw_thermal_module_trips_reset(struct mlxsw_thermal_module *tz)
|
||||
{
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = 0;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = 0;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = 0;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = 0;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temperature = 0;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temperature = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -180,12 +188,12 @@ mlxsw_thermal_module_trips_update(struct device *dev, struct mlxsw_core *core,
|
||||
* by subtracting double hysteresis value.
|
||||
*/
|
||||
if (crit_temp >= MLXSW_THERMAL_MODULE_TEMP_SHIFT)
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp -
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = crit_temp -
|
||||
MLXSW_THERMAL_MODULE_TEMP_SHIFT;
|
||||
else
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = crit_temp;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = emerg_temp;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temperature = crit_temp;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temperature = crit_temp;
|
||||
tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temperature = emerg_temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -202,11 +210,11 @@ static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
const struct mlxsw_thermal_trip *trip = &thermal->trips[i];
|
||||
const struct mlxsw_cooling_states *state = &thermal->cooling_states[i];
|
||||
|
||||
err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
|
||||
trip->max_state,
|
||||
trip->min_state,
|
||||
state->max_state,
|
||||
state->min_state,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Failed to bind cooling device to trip %d\n", i);
|
||||
@ -260,61 +268,6 @@ static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_trip_type(struct thermal_zone_device *tzdev,
|
||||
int trip,
|
||||
enum thermal_trip_type *p_type)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*p_type = thermal->trips[trip].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_trip_temp(struct thermal_zone_device *tzdev,
|
||||
int trip, int *p_temp)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*p_temp = thermal->trips[trip].temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev,
|
||||
int trip, int temp)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
thermal->trips[trip].temp = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_get_trip_hyst(struct thermal_zone_device *tzdev,
|
||||
int trip, int *p_hyst)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
*p_hyst = thermal->trips[trip].hyst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxsw_thermal_set_trip_hyst(struct thermal_zone_device *tzdev,
|
||||
int trip, int hyst)
|
||||
{
|
||||
struct mlxsw_thermal *thermal = tzdev->devdata;
|
||||
|
||||
thermal->trips[trip].hyst = hyst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_params mlxsw_thermal_params = {
|
||||
.no_hwmon = true,
|
||||
};
|
||||
@ -323,11 +276,6 @@ static struct thermal_zone_device_ops mlxsw_thermal_ops = {
|
||||
.bind = mlxsw_thermal_bind,
|
||||
.unbind = mlxsw_thermal_unbind,
|
||||
.get_temp = mlxsw_thermal_get_temp,
|
||||
.get_trip_type = mlxsw_thermal_get_trip_type,
|
||||
.get_trip_temp = mlxsw_thermal_get_trip_temp,
|
||||
.set_trip_temp = mlxsw_thermal_set_trip_temp,
|
||||
.get_trip_hyst = mlxsw_thermal_get_trip_hyst,
|
||||
.set_trip_hyst = mlxsw_thermal_set_trip_hyst,
|
||||
};
|
||||
|
||||
static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev,
|
||||
@ -342,11 +290,11 @@ static int mlxsw_thermal_module_bind(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
|
||||
const struct mlxsw_thermal_trip *trip = &tz->trips[i];
|
||||
const struct mlxsw_cooling_states *state = &tz->cooling_states[i];
|
||||
|
||||
err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
|
||||
trip->max_state,
|
||||
trip->min_state,
|
||||
state->max_state,
|
||||
state->min_state,
|
||||
THERMAL_WEIGHT_DEFAULT);
|
||||
if (err < 0)
|
||||
goto err_thermal_zone_bind_cooling_device;
|
||||
@ -434,74 +382,10 @@ static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_thermal_module_trip_type_get(struct thermal_zone_device *tzdev, int trip,
|
||||
enum thermal_trip_type *p_type)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*p_type = tz->trips[trip].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_thermal_module_trip_temp_get(struct thermal_zone_device *tzdev,
|
||||
int trip, int *p_temp)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*p_temp = tz->trips[trip].temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_thermal_module_trip_temp_set(struct thermal_zone_device *tzdev,
|
||||
int trip, int temp)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
tz->trips[trip].temp = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_thermal_module_trip_hyst_get(struct thermal_zone_device *tzdev, int trip,
|
||||
int *p_hyst)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
*p_hyst = tz->trips[trip].hyst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_thermal_module_trip_hyst_set(struct thermal_zone_device *tzdev, int trip,
|
||||
int hyst)
|
||||
{
|
||||
struct mlxsw_thermal_module *tz = tzdev->devdata;
|
||||
|
||||
tz->trips[trip].hyst = hyst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.get_temp = mlxsw_thermal_module_temp_get,
|
||||
.get_trip_type = mlxsw_thermal_module_trip_type_get,
|
||||
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
|
||||
.set_trip_temp = mlxsw_thermal_module_trip_temp_set,
|
||||
.get_trip_hyst = mlxsw_thermal_module_trip_hyst_get,
|
||||
.set_trip_hyst = mlxsw_thermal_module_trip_hyst_set,
|
||||
};
|
||||
|
||||
static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev,
|
||||
@ -531,11 +415,6 @@ static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = {
|
||||
.bind = mlxsw_thermal_module_bind,
|
||||
.unbind = mlxsw_thermal_module_unbind,
|
||||
.get_temp = mlxsw_thermal_gearbox_temp_get,
|
||||
.get_trip_type = mlxsw_thermal_module_trip_type_get,
|
||||
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
|
||||
.set_trip_temp = mlxsw_thermal_module_trip_temp_set,
|
||||
.get_trip_hyst = mlxsw_thermal_module_trip_hyst_get,
|
||||
.set_trip_hyst = mlxsw_thermal_module_trip_hyst_set,
|
||||
};
|
||||
|
||||
static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev,
|
||||
@ -617,7 +496,8 @@ mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz)
|
||||
else
|
||||
snprintf(tz_name, sizeof(tz_name), "mlxsw-module%d",
|
||||
module_tz->module + 1);
|
||||
module_tz->tzdev = thermal_zone_device_register(tz_name,
|
||||
module_tz->tzdev = thermal_zone_device_register_with_trips(tz_name,
|
||||
module_tz->trips,
|
||||
MLXSW_THERMAL_NUM_TRIPS,
|
||||
MLXSW_THERMAL_TRIP_MASK,
|
||||
module_tz,
|
||||
@ -661,6 +541,8 @@ mlxsw_thermal_module_init(struct device *dev, struct mlxsw_core *core,
|
||||
module_tz->parent = thermal;
|
||||
memcpy(module_tz->trips, default_thermal_trips,
|
||||
sizeof(thermal->trips));
|
||||
memcpy(module_tz->cooling_states, default_cooling_states,
|
||||
sizeof(thermal->cooling_states));
|
||||
/* Initialize all trip point. */
|
||||
mlxsw_thermal_module_trips_reset(module_tz);
|
||||
/* Read module temperature and thresholds. */
|
||||
@ -756,7 +638,8 @@ mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
|
||||
else
|
||||
snprintf(tz_name, sizeof(tz_name), "mlxsw-gearbox%d",
|
||||
gearbox_tz->module + 1);
|
||||
gearbox_tz->tzdev = thermal_zone_device_register(tz_name,
|
||||
gearbox_tz->tzdev = thermal_zone_device_register_with_trips(tz_name,
|
||||
gearbox_tz->trips,
|
||||
MLXSW_THERMAL_NUM_TRIPS,
|
||||
MLXSW_THERMAL_TRIP_MASK,
|
||||
gearbox_tz,
|
||||
@ -813,6 +696,8 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
|
||||
gearbox_tz = &area->tz_gearbox_arr[i];
|
||||
memcpy(gearbox_tz->trips, default_thermal_trips,
|
||||
sizeof(thermal->trips));
|
||||
memcpy(gearbox_tz->cooling_states, default_cooling_states,
|
||||
sizeof(thermal->cooling_states));
|
||||
gearbox_tz->module = i;
|
||||
gearbox_tz->parent = thermal;
|
||||
gearbox_tz->slot_index = area->slot_index;
|
||||
@ -928,6 +813,7 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
|
||||
thermal->core = core;
|
||||
thermal->bus_info = bus_info;
|
||||
memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips));
|
||||
memcpy(thermal->cooling_states, default_cooling_states, sizeof(thermal->cooling_states));
|
||||
thermal->line_cards[0].slot_index = 0;
|
||||
|
||||
err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl);
|
||||
@ -981,7 +867,8 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
|
||||
MLXSW_THERMAL_SLOW_POLL_INT :
|
||||
MLXSW_THERMAL_POLL_INT;
|
||||
|
||||
thermal->tzdev = thermal_zone_device_register("mlxsw",
|
||||
thermal->tzdev = thermal_zone_device_register_with_trips("mlxsw",
|
||||
thermal->trips,
|
||||
MLXSW_THERMAL_NUM_TRIPS,
|
||||
MLXSW_THERMAL_TRIP_MASK,
|
||||
thermal,
|
||||
|
@ -501,7 +501,7 @@ struct iwl_mvm_tt_mgmt {
|
||||
* @tzone: thermal zone device data
|
||||
*/
|
||||
struct iwl_mvm_thermal_device {
|
||||
s16 temp_trips[IWL_MAX_DTS_TRIPS];
|
||||
struct thermal_trip trips[IWL_MAX_DTS_TRIPS];
|
||||
u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
|
||||
struct thermal_zone_device *tzone;
|
||||
};
|
||||
|
@ -573,11 +573,11 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
|
||||
* and uncompressed, the FW should get it compressed and sorted
|
||||
*/
|
||||
|
||||
/* compress temp_trips to cmd array, remove uninitialized values*/
|
||||
/* compress trips to cmd array, remove uninitialized values*/
|
||||
for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
|
||||
if (mvm->tz_device.temp_trips[i] != S16_MIN) {
|
||||
if (mvm->tz_device.trips[i].temperature != INT_MIN) {
|
||||
cmd.thresholds[idx++] =
|
||||
cpu_to_le16(mvm->tz_device.temp_trips[i]);
|
||||
cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
|
||||
}
|
||||
}
|
||||
cmd.num_temps = cpu_to_le32(idx);
|
||||
@ -593,8 +593,8 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
|
||||
*/
|
||||
for (i = 0; i < idx; i++) {
|
||||
for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
|
||||
if (le16_to_cpu(cmd.thresholds[i]) ==
|
||||
mvm->tz_device.temp_trips[j])
|
||||
if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
|
||||
mvm->tz_device.trips[j].temperature)
|
||||
mvm->tz_device.fw_trips_index[i] = j;
|
||||
}
|
||||
}
|
||||
@ -638,37 +638,12 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
|
||||
|
||||
if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*temp = mvm->tz_device.temp_trips[trip] * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
|
||||
int trip, int temp)
|
||||
{
|
||||
struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
|
||||
struct iwl_mvm_thermal_device *tzone;
|
||||
int i, ret;
|
||||
s16 temperature;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
@ -678,40 +653,17 @@ static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((temp / 1000) > S16_MAX) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
temperature = (s16)(temp / 1000);
|
||||
tzone = &mvm->tz_device;
|
||||
|
||||
if (!tzone) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* no updates*/
|
||||
if (tzone->temp_trips[trip] == temperature) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* already existing temperature */
|
||||
for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
|
||||
if (tzone->temp_trips[i] == temperature) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
tzone->temp_trips[trip] = temperature;
|
||||
|
||||
ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
@ -720,8 +672,6 @@ out:
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = iwl_mvm_tzone_get_temp,
|
||||
.get_trip_temp = iwl_mvm_tzone_get_trip_temp,
|
||||
.get_trip_type = iwl_mvm_tzone_get_trip_type,
|
||||
.set_trip_temp = iwl_mvm_tzone_set_trip_temp,
|
||||
};
|
||||
|
||||
@ -743,7 +693,8 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
|
||||
BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
|
||||
|
||||
sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
|
||||
mvm->tz_device.tzone = thermal_zone_device_register(name,
|
||||
mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
|
||||
mvm->tz_device.trips,
|
||||
IWL_MAX_DTS_TRIPS,
|
||||
IWL_WRITABLE_TRIPS_MSK,
|
||||
mvm, &tzone_ops,
|
||||
@ -766,8 +717,10 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
|
||||
/* 0 is a valid temperature,
|
||||
* so initialize the array with S16_MIN which invalid temperature
|
||||
*/
|
||||
for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
|
||||
mvm->tz_device.temp_trips[i] = S16_MIN;
|
||||
for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
|
||||
mvm->tz_device.trips[i].temperature = INT_MIN;
|
||||
mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
|
||||
|
@ -46,6 +46,8 @@
|
||||
* measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
|
||||
* assume 89°C is critical temperature.
|
||||
*/
|
||||
#define ACERHDF_DEFAULT_TEMP_FANON 60000
|
||||
#define ACERHDF_DEFAULT_TEMP_FANOFF 53000
|
||||
#define ACERHDF_TEMP_CRIT 89000
|
||||
#define ACERHDF_FAN_OFF 0
|
||||
#define ACERHDF_FAN_AUTO 1
|
||||
@ -70,8 +72,8 @@ static int kernelmode;
|
||||
#endif
|
||||
|
||||
static unsigned int interval = 10;
|
||||
static unsigned int fanon = 60000;
|
||||
static unsigned int fanoff = 53000;
|
||||
static unsigned int fanon = ACERHDF_DEFAULT_TEMP_FANON;
|
||||
static unsigned int fanoff = ACERHDF_DEFAULT_TEMP_FANOFF;
|
||||
static unsigned int verbose;
|
||||
static unsigned int list_supported;
|
||||
static unsigned int fanstate = ACERHDF_FAN_AUTO;
|
||||
@ -137,6 +139,15 @@ struct ctrl_settings {
|
||||
int mcmd_enable;
|
||||
};
|
||||
|
||||
static struct thermal_trip trips[] = {
|
||||
[0] = { .temperature = ACERHDF_DEFAULT_TEMP_FANON,
|
||||
.hysteresis = ACERHDF_DEFAULT_TEMP_FANON - ACERHDF_DEFAULT_TEMP_FANOFF,
|
||||
.type = THERMAL_TRIP_ACTIVE },
|
||||
|
||||
[1] = { .temperature = ACERHDF_TEMP_CRIT,
|
||||
.type = THERMAL_TRIP_CRITICAL }
|
||||
};
|
||||
|
||||
static struct ctrl_settings ctrl_cfg __read_mostly;
|
||||
|
||||
/* Register addresses and values for different BIOS versions */
|
||||
@ -326,6 +337,15 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
|
||||
fanon = ACERHDF_MAX_FANON;
|
||||
}
|
||||
|
||||
if (fanon < fanoff) {
|
||||
pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n",
|
||||
fanoff, fanon, fanon);
|
||||
fanoff = fanon;
|
||||
};
|
||||
|
||||
trips[0].temperature = fanon;
|
||||
trips[0].hysteresis = fanon - fanoff;
|
||||
|
||||
if (kernelmode && prev_interval != interval) {
|
||||
if (interval > ACERHDF_MAX_INTERVAL) {
|
||||
pr_err("interval too high, set to %d\n",
|
||||
@ -424,43 +444,6 @@ static int acerhdf_change_mode(struct thermal_zone_device *thermal,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
if (trip == 0)
|
||||
*type = THERMAL_TRIP_ACTIVE;
|
||||
else if (trip == 1)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
|
||||
int *temp)
|
||||
{
|
||||
if (trip != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*temp = fanon - fanoff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
|
||||
int *temp)
|
||||
{
|
||||
if (trip == 0)
|
||||
*temp = fanon;
|
||||
else if (trip == 1)
|
||||
*temp = ACERHDF_TEMP_CRIT;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
|
||||
int *temperature)
|
||||
{
|
||||
@ -474,13 +457,9 @@ static struct thermal_zone_device_ops acerhdf_dev_ops = {
|
||||
.unbind = acerhdf_unbind,
|
||||
.get_temp = acerhdf_get_ec_temp,
|
||||
.change_mode = acerhdf_change_mode,
|
||||
.get_trip_type = acerhdf_get_trip_type,
|
||||
.get_trip_hyst = acerhdf_get_trip_hyst,
|
||||
.get_trip_temp = acerhdf_get_trip_temp,
|
||||
.get_crit_temp = acerhdf_get_crit_temp,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* cooling device callback functions
|
||||
* get maximal fan cooling state
|
||||
@ -710,10 +689,10 @@ static int __init acerhdf_register_thermal(void)
|
||||
if (IS_ERR(cl_dev))
|
||||
return -EINVAL;
|
||||
|
||||
thz_dev = thermal_zone_device_register("acerhdf", 2, 0, NULL,
|
||||
&acerhdf_dev_ops,
|
||||
&acerhdf_zone_params, 0,
|
||||
(kernelmode) ? interval*1000 : 0);
|
||||
thz_dev = thermal_zone_device_register_with_trips("acerhdf", trips, ARRAY_SIZE(trips),
|
||||
0, NULL, &acerhdf_dev_ops,
|
||||
&acerhdf_zone_params, 0,
|
||||
(kernelmode) ? interval*1000 : 0);
|
||||
if (IS_ERR(thz_dev))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -63,13 +63,29 @@ struct idle_inject_thread {
|
||||
* @idle_duration_us: duration of CPU idle time to inject
|
||||
* @run_duration_us: duration of CPU run time to allow
|
||||
* @latency_us: max allowed latency
|
||||
* @update: Optional callback deciding whether or not to skip idle
|
||||
* injection in the given cycle.
|
||||
* @cpumask: mask of CPUs affected by idle injection
|
||||
*
|
||||
* This structure is used to define per instance idle inject device data. Each
|
||||
* instance has an idle duration, a run duration and mask of CPUs to inject
|
||||
* idle.
|
||||
*
|
||||
* Actual CPU idle time is injected by calling kernel scheduler interface
|
||||
* play_idle_precise(). There is one optional callback that can be registered
|
||||
* by calling idle_inject_register_full():
|
||||
*
|
||||
* update() - This callback is invoked just before waking up CPUs to inject
|
||||
* idle. If it returns false, CPUs are not woken up to inject idle in the given
|
||||
* cycle. It also allows the caller to readjust the idle and run duration by
|
||||
* calling idle_inject_set_duration() for the next cycle.
|
||||
*/
|
||||
struct idle_inject_device {
|
||||
struct hrtimer timer;
|
||||
unsigned int idle_duration_us;
|
||||
unsigned int run_duration_us;
|
||||
unsigned int latency_us;
|
||||
bool (*update)(void);
|
||||
unsigned long cpumask[];
|
||||
};
|
||||
|
||||
@ -111,11 +127,12 @@ static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer)
|
||||
struct idle_inject_device *ii_dev =
|
||||
container_of(timer, struct idle_inject_device, timer);
|
||||
|
||||
if (!ii_dev->update || (ii_dev->update && ii_dev->update()))
|
||||
idle_inject_wakeup(ii_dev);
|
||||
|
||||
duration_us = READ_ONCE(ii_dev->run_duration_us);
|
||||
duration_us += READ_ONCE(ii_dev->idle_duration_us);
|
||||
|
||||
idle_inject_wakeup(ii_dev);
|
||||
|
||||
hrtimer_forward_now(timer, ns_to_ktime(duration_us * NSEC_PER_USEC));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
@ -162,6 +179,7 @@ void idle_inject_set_duration(struct idle_inject_device *ii_dev,
|
||||
if (!run_duration_us)
|
||||
pr_debug("CPU is forced to 100 percent idle\n");
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_set_duration, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_get_duration - idle and run duration retrieval helper
|
||||
@ -176,6 +194,7 @@ void idle_inject_get_duration(struct idle_inject_device *ii_dev,
|
||||
*run_duration_us = READ_ONCE(ii_dev->run_duration_us);
|
||||
*idle_duration_us = READ_ONCE(ii_dev->idle_duration_us);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_get_duration, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_set_latency - set the maximum latency allowed
|
||||
@ -187,6 +206,7 @@ void idle_inject_set_latency(struct idle_inject_device *ii_dev,
|
||||
{
|
||||
WRITE_ONCE(ii_dev->latency_us, latency_us);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_set_latency, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_start - start idle injections
|
||||
@ -218,6 +238,7 @@ int idle_inject_start(struct idle_inject_device *ii_dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_start, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_stop - stops idle injections
|
||||
@ -264,6 +285,7 @@ void idle_inject_stop(struct idle_inject_device *ii_dev)
|
||||
|
||||
cpu_hotplug_enable();
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_stop, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_setup - prepare the current task for idle injection
|
||||
@ -292,17 +314,22 @@ static int idle_inject_should_run(unsigned int cpu)
|
||||
}
|
||||
|
||||
/**
|
||||
* idle_inject_register - initialize idle injection on a set of CPUs
|
||||
* idle_inject_register_full - initialize idle injection on a set of CPUs
|
||||
* @cpumask: CPUs to be affected by idle injection
|
||||
* @update: This callback is called just before waking up CPUs to inject
|
||||
* idle
|
||||
*
|
||||
* This function creates an idle injection control device structure for the
|
||||
* given set of CPUs and initializes the timer associated with it. It does not
|
||||
* start any injection cycles.
|
||||
* given set of CPUs and initializes the timer associated with it. This
|
||||
* function also allows to register update()callback.
|
||||
* It does not start any injection cycles.
|
||||
*
|
||||
* Return: NULL if memory allocation fails, idle injection control device
|
||||
* pointer on success.
|
||||
*/
|
||||
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
|
||||
|
||||
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
|
||||
bool (*update)(void))
|
||||
{
|
||||
struct idle_inject_device *ii_dev;
|
||||
int cpu, cpu_rb;
|
||||
@ -315,6 +342,7 @@ struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
|
||||
hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
ii_dev->timer.function = idle_inject_timer_fn;
|
||||
ii_dev->latency_us = UINT_MAX;
|
||||
ii_dev->update = update;
|
||||
|
||||
for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) {
|
||||
|
||||
@ -339,6 +367,24 @@ out_rollback:
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_register_full, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_register - initialize idle injection on a set of CPUs
|
||||
* @cpumask: CPUs to be affected by idle injection
|
||||
*
|
||||
* This function creates an idle injection control device structure for the
|
||||
* given set of CPUs and initializes the timer associated with it. It does not
|
||||
* start any injection cycles.
|
||||
*
|
||||
* Return: NULL if memory allocation fails, idle injection control device
|
||||
* pointer on success.
|
||||
*/
|
||||
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask)
|
||||
{
|
||||
return idle_inject_register_full(cpumask, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_register, IDLE_INJECT);
|
||||
|
||||
/**
|
||||
* idle_inject_unregister - unregister idle injection control device
|
||||
@ -359,6 +405,7 @@ void idle_inject_unregister(struct idle_inject_device *ii_dev)
|
||||
|
||||
kfree(ii_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(idle_inject_unregister, IDLE_INJECT);
|
||||
|
||||
static struct smp_hotplug_thread idle_inject_threads = {
|
||||
.store = &idle_inject_thread.tsk,
|
||||
|
@ -76,6 +76,10 @@ config THERMAL_OF
|
||||
Say 'Y' here if you need to build thermal infrastructure
|
||||
based on device tree.
|
||||
|
||||
config THERMAL_ACPI
|
||||
depends on ACPI
|
||||
bool
|
||||
|
||||
config THERMAL_WRITABLE_TRIPS
|
||||
bool "Enable writable trip points"
|
||||
help
|
||||
@ -412,16 +416,10 @@ config DA9062_THERMAL
|
||||
zone.
|
||||
Compatible with the DA9062 and DA9061 PMICs.
|
||||
|
||||
config MTK_THERMAL
|
||||
tristate "Temperature sensor driver for mediatek SoCs"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on NVMEM || NVMEM=n
|
||||
depends on RESET_CONTROLLER
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to have support for thermal management
|
||||
controller present in Mediatek SoCs
|
||||
menu "Mediatek thermal drivers"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
source "drivers/thermal/mediatek/Kconfig"
|
||||
endmenu
|
||||
|
||||
config AMLOGIC_THERMAL
|
||||
tristate "Amlogic Thermal Support"
|
||||
|
@ -4,8 +4,8 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_THERMAL) += thermal_sys.o
|
||||
thermal_sys-y += thermal_core.o thermal_sysfs.o \
|
||||
thermal_helpers.o
|
||||
thermal_sys-y += thermal_core.o thermal_sysfs.o
|
||||
thermal_sys-y += thermal_trip.o thermal_helpers.o
|
||||
|
||||
# netlink interface to manage the thermal framework
|
||||
thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
|
||||
@ -13,6 +13,7 @@ thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
|
||||
# interface to/from other layers providing sensors
|
||||
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
|
||||
thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
|
||||
thermal_sys-$(CONFIG_THERMAL_ACPI) += thermal_acpi.o
|
||||
|
||||
# governors
|
||||
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += gov_fair_share.o
|
||||
@ -55,7 +56,7 @@ obj-y += st/
|
||||
obj-y += qcom/
|
||||
obj-y += tegra/
|
||||
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
|
||||
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
|
||||
obj-y += mediatek/
|
||||
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
|
||||
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
|
||||
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define TSENSOR_CFG_REG1 0x4
|
||||
|
@ -19,8 +19,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/* Thermal Manager Control and Status Register */
|
||||
#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
|
||||
#define PMU_TM_DISABLE_OFFS 0
|
||||
@ -709,12 +707,10 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
|
||||
struct armada_thermal_priv *priv)
|
||||
{
|
||||
struct armada_thermal_data *data = priv->data;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
|
||||
/* First memory region points towards the status register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
@ -761,8 +757,7 @@ static void armada_set_sane_name(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
/* Save the name locally */
|
||||
strncpy(priv->zone_name, name, THERMAL_NAME_LENGTH - 1);
|
||||
priv->zone_name[THERMAL_NAME_LENGTH - 1] = '\0';
|
||||
strscpy(priv->zone_name, name, THERMAL_NAME_LENGTH);
|
||||
|
||||
/* Then check there are no '-' or hwmon core will complain */
|
||||
do {
|
||||
@ -785,30 +780,23 @@ static int armada_configure_overheat_int(struct armada_thermal_priv *priv,
|
||||
int sensor_id)
|
||||
{
|
||||
/* Retrieve the critical trip point to enable the overheat interrupt */
|
||||
const struct thermal_trip *trips = of_thermal_get_trip_points(tz);
|
||||
int temperature;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!trips)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < of_thermal_get_ntrips(tz); i++)
|
||||
if (trips[i].type == THERMAL_TRIP_CRITICAL)
|
||||
break;
|
||||
|
||||
if (i == of_thermal_get_ntrips(tz))
|
||||
return -EINVAL;
|
||||
ret = thermal_zone_get_crit_temp(tz, &temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = armada_select_channel(priv, sensor_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
armada_set_overheat_thresholds(priv,
|
||||
trips[i].temperature,
|
||||
trips[i].hysteresis);
|
||||
/*
|
||||
* A critical temperature does not have a hysteresis
|
||||
*/
|
||||
armada_set_overheat_thresholds(priv, temperature, 0);
|
||||
priv->overheat_sensor = tz;
|
||||
priv->interrupt_source = sensor_id;
|
||||
|
||||
armada_enable_overheat_interrupt(priv);
|
||||
|
||||
return 0;
|
||||
|
@ -166,7 +166,6 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *match;
|
||||
struct thermal_zone_device *tz;
|
||||
struct bcm2835_thermal_data *data;
|
||||
struct resource *res;
|
||||
int err = 0;
|
||||
u32 val;
|
||||
unsigned long rate;
|
||||
@ -180,8 +179,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
data->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(data->regs)) {
|
||||
err = PTR_ERR(data->regs);
|
||||
return err;
|
||||
@ -224,7 +222,8 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
*/
|
||||
val = readl(data->regs + BCM2835_TS_TSENSCTL);
|
||||
if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
|
||||
int trip_temp, offset, slope;
|
||||
struct thermal_trip trip;
|
||||
int offset, slope;
|
||||
|
||||
slope = thermal_zone_get_slope(tz);
|
||||
offset = thermal_zone_get_offset(tz);
|
||||
@ -232,7 +231,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
* For now we deal only with critical, otherwise
|
||||
* would need to iterate
|
||||
*/
|
||||
err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
|
||||
err = thermal_zone_get_trip(tz, 0, &trip);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Not able to read trip_temp: %d\n",
|
||||
@ -249,7 +248,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
|
||||
|
||||
/* trip_adc value from info */
|
||||
val |= bcm2835_thermal_temp2adc(trip_temp,
|
||||
val |= bcm2835_thermal_temp2adc(trip.temperature,
|
||||
offset,
|
||||
slope)
|
||||
<< BCM2835_TS_TSENSCTL_THOLD_SHIFT;
|
||||
|
@ -321,7 +321,6 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
||||
const struct thermal_zone_device_ops *of_ops;
|
||||
struct thermal_zone_device *thermal;
|
||||
struct brcmstb_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
int irq, ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -332,8 +331,7 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
|
||||
if (!priv->temp_params)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->tmon_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->tmon_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(priv->tmon_base))
|
||||
return PTR_ERR(priv->tmon_base);
|
||||
|
||||
|
@ -120,44 +120,6 @@ static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int da9062_thermal_get_trip_type(struct thermal_zone_device *z,
|
||||
int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct da9062_thermal *thermal = z->devdata;
|
||||
|
||||
switch (trip) {
|
||||
case 0:
|
||||
*type = THERMAL_TRIP_HOT;
|
||||
break;
|
||||
default:
|
||||
dev_err(thermal->dev,
|
||||
"Driver does not support more than 1 trip-wire\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9062_thermal_get_trip_temp(struct thermal_zone_device *z,
|
||||
int trip,
|
||||
int *temp)
|
||||
{
|
||||
struct da9062_thermal *thermal = z->devdata;
|
||||
|
||||
switch (trip) {
|
||||
case 0:
|
||||
*temp = DA9062_MILLI_CELSIUS(125);
|
||||
break;
|
||||
default:
|
||||
dev_err(thermal->dev,
|
||||
"Driver does not support more than 1 trip-wire\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9062_thermal_get_temp(struct thermal_zone_device *z,
|
||||
int *temp)
|
||||
{
|
||||
@ -172,8 +134,10 @@ static int da9062_thermal_get_temp(struct thermal_zone_device *z,
|
||||
|
||||
static struct thermal_zone_device_ops da9062_thermal_ops = {
|
||||
.get_temp = da9062_thermal_get_temp,
|
||||
.get_trip_type = da9062_thermal_get_trip_type,
|
||||
.get_trip_temp = da9062_thermal_get_trip_temp,
|
||||
};
|
||||
|
||||
static struct thermal_trip trips[] = {
|
||||
{ .temperature = DA9062_MILLI_CELSIUS(125), .type = THERMAL_TRIP_HOT },
|
||||
};
|
||||
|
||||
static const struct da9062_thermal_config da9062_config = {
|
||||
@ -228,10 +192,10 @@ static int da9062_thermal_probe(struct platform_device *pdev)
|
||||
INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
|
||||
mutex_init(&thermal->lock);
|
||||
|
||||
thermal->zone = thermal_zone_device_register(thermal->config->name,
|
||||
1, 0, thermal,
|
||||
&da9062_thermal_ops, NULL, pp_tmp,
|
||||
0);
|
||||
thermal->zone = thermal_zone_device_register_with_trips(thermal->config->name,
|
||||
trips, ARRAY_SIZE(trips), 0, thermal,
|
||||
&da9062_thermal_ops, NULL, pp_tmp,
|
||||
0);
|
||||
if (IS_ERR(thermal->zone)) {
|
||||
dev_err(&pdev->dev, "Cannot register thermal zone device\n");
|
||||
ret = PTR_ERR(thermal->zone);
|
||||
|
@ -122,20 +122,17 @@ static int dove_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_zone_device *thermal = NULL;
|
||||
struct dove_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->sensor = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->sensor = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(priv->sensor))
|
||||
return PTR_ERR(priv->sensor);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
priv->control = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->control = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
|
||||
if (IS_ERR(priv->control))
|
||||
return PTR_ERR(priv->control);
|
||||
|
||||
|
@ -13,26 +13,28 @@
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
static int thermal_zone_trip_update(struct thermal_zone_device *tz, int trip_id)
|
||||
{
|
||||
int trip_temp, trip_hyst;
|
||||
struct thermal_trip trip;
|
||||
struct thermal_instance *instance;
|
||||
int ret;
|
||||
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
if (ret) {
|
||||
pr_warn_once("Failed to retrieve trip point %d\n", trip_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!tz->ops->get_trip_hyst) {
|
||||
pr_warn_once("Undefined get_trip_hyst for thermal zone %s - "
|
||||
"running with default hysteresis zero\n", tz->type);
|
||||
trip_hyst = 0;
|
||||
} else
|
||||
tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
|
||||
if (!trip.hysteresis)
|
||||
dev_info_once(&tz->device,
|
||||
"Zero hysteresis value for thermal zone %s\n", tz->type);
|
||||
|
||||
dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
|
||||
trip, trip_temp, tz->temperature,
|
||||
trip_hyst);
|
||||
trip_id, trip.temperature, tz->temperature,
|
||||
trip.hysteresis);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (instance->trip != trip)
|
||||
if (instance->trip != trip_id)
|
||||
continue;
|
||||
|
||||
/* in case fan is in initial state, switch the fan off */
|
||||
@ -50,10 +52,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
* enable fan when temperature exceeds trip_temp and disable
|
||||
* the fan in case it falls below trip_temp minus hysteresis
|
||||
*/
|
||||
if (instance->target == 0 && tz->temperature >= trip_temp)
|
||||
if (instance->target == 0 && tz->temperature >= trip.temperature)
|
||||
instance->target = 1;
|
||||
else if (instance->target == 1 &&
|
||||
tz->temperature <= trip_temp - trip_hyst)
|
||||
tz->temperature <= trip.temperature - trip.hysteresis)
|
||||
instance->target = 0;
|
||||
|
||||
dev_dbg(&instance->cdev->device, "target=%d\n",
|
||||
@ -63,6 +65,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
instance->cdev->updated = false; /* cdev needs update */
|
||||
mutex_unlock(&instance->cdev->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,10 +99,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
static int bang_bang_control(struct thermal_zone_device *tz, int trip)
|
||||
{
|
||||
struct thermal_instance *instance;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
thermal_zone_trip_update(tz, trip);
|
||||
ret = thermal_zone_trip_update(tz, trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
||||
thermal_cdev_update(instance->cdev);
|
||||
|
@ -21,16 +21,12 @@
|
||||
*/
|
||||
static int get_trip_level(struct thermal_zone_device *tz)
|
||||
{
|
||||
int count = 0;
|
||||
int trip_temp;
|
||||
enum thermal_trip_type trip_type;
|
||||
|
||||
if (tz->num_trips == 0 || !tz->ops->get_trip_temp)
|
||||
return 0;
|
||||
struct thermal_trip trip;
|
||||
int count;
|
||||
|
||||
for (count = 0; count < tz->num_trips; count++) {
|
||||
tz->ops->get_trip_temp(tz, count, &trip_temp);
|
||||
if (tz->temperature < trip_temp)
|
||||
__thermal_zone_get_trip(tz, count, &trip);
|
||||
if (tz->temperature < trip.temperature)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -38,10 +34,8 @@ static int get_trip_level(struct thermal_zone_device *tz)
|
||||
* count > 0 only if temperature is greater than first trip
|
||||
* point, in which case, trip_point = count - 1
|
||||
*/
|
||||
if (count > 0) {
|
||||
tz->ops->get_trip_type(tz, count - 1, &trip_type);
|
||||
trace_thermal_zone_trip(tz, count - 1, trip_type);
|
||||
}
|
||||
if (count > 0)
|
||||
trace_thermal_zone_trip(tz, count - 1, trip.type);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -124,16 +124,15 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
|
||||
u32 sustainable_power, int trip_switch_on,
|
||||
int control_temp)
|
||||
{
|
||||
struct thermal_trip trip;
|
||||
u32 temperature_threshold = control_temp;
|
||||
int ret;
|
||||
int switch_on_temp;
|
||||
u32 temperature_threshold;
|
||||
s32 k_i;
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
|
||||
if (ret)
|
||||
switch_on_temp = 0;
|
||||
ret = __thermal_zone_get_trip(tz, trip_switch_on, &trip);
|
||||
if (!ret)
|
||||
temperature_threshold -= trip.temperature;
|
||||
|
||||
temperature_threshold = control_temp - switch_on_temp;
|
||||
/*
|
||||
* estimate_pid_constants() tries to find appropriate default
|
||||
* values for thermal zones that don't provide them. If a
|
||||
@ -519,10 +518,10 @@ static void get_governor_trips(struct thermal_zone_device *tz,
|
||||
last_passive = INVALID_TRIP;
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
enum thermal_trip_type type;
|
||||
struct thermal_trip trip;
|
||||
int ret;
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, i, &type);
|
||||
ret = __thermal_zone_get_trip(tz, i, &trip);
|
||||
if (ret) {
|
||||
dev_warn(&tz->device,
|
||||
"Failed to get trip point %d type: %d\n", i,
|
||||
@ -530,14 +529,14 @@ static void get_governor_trips(struct thermal_zone_device *tz,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == THERMAL_TRIP_PASSIVE) {
|
||||
if (trip.type == THERMAL_TRIP_PASSIVE) {
|
||||
if (!found_first_passive) {
|
||||
params->trip_switch_on = i;
|
||||
found_first_passive = true;
|
||||
} else {
|
||||
last_passive = i;
|
||||
}
|
||||
} else if (type == THERMAL_TRIP_ACTIVE) {
|
||||
} else if (trip.type == THERMAL_TRIP_ACTIVE) {
|
||||
last_active = i;
|
||||
} else {
|
||||
break;
|
||||
@ -632,7 +631,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
|
||||
{
|
||||
int ret;
|
||||
struct power_allocator_params *params;
|
||||
int control_temp;
|
||||
struct thermal_trip trip;
|
||||
|
||||
ret = check_power_actors(tz);
|
||||
if (ret)
|
||||
@ -658,13 +657,12 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
|
||||
get_governor_trips(tz, params);
|
||||
|
||||
if (tz->num_trips > 0) {
|
||||
ret = tz->ops->get_trip_temp(tz,
|
||||
params->trip_max_desired_temperature,
|
||||
&control_temp);
|
||||
ret = __thermal_zone_get_trip(tz, params->trip_max_desired_temperature,
|
||||
&trip);
|
||||
if (!ret)
|
||||
estimate_pid_constants(tz, tz->tzp->sustainable_power,
|
||||
params->trip_switch_on,
|
||||
control_temp);
|
||||
trip.temperature);
|
||||
}
|
||||
|
||||
reset_pid_controller(params);
|
||||
@ -694,11 +692,11 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
|
||||
tz->governor_data = NULL;
|
||||
}
|
||||
|
||||
static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
|
||||
static int power_allocator_throttle(struct thermal_zone_device *tz, int trip_id)
|
||||
{
|
||||
int ret;
|
||||
int switch_on_temp, control_temp;
|
||||
struct power_allocator_params *params = tz->governor_data;
|
||||
struct thermal_trip trip;
|
||||
int ret;
|
||||
bool update;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
@ -707,13 +705,12 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
|
||||
* 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_id != params->trip_max_desired_temperature)
|
||||
return 0;
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
|
||||
&switch_on_temp);
|
||||
if (!ret && (tz->temperature < switch_on_temp)) {
|
||||
update = (tz->last_temperature >= switch_on_temp);
|
||||
ret = __thermal_zone_get_trip(tz, params->trip_switch_on, &trip);
|
||||
if (!ret && (tz->temperature < trip.temperature)) {
|
||||
update = (tz->last_temperature >= trip.temperature);
|
||||
tz->passive = 0;
|
||||
reset_pid_controller(params);
|
||||
allow_maximum_power(tz, update);
|
||||
@ -722,16 +719,14 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
|
||||
|
||||
tz->passive = 1;
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
|
||||
&control_temp);
|
||||
ret = __thermal_zone_get_trip(tz, params->trip_max_desired_temperature, &trip);
|
||||
if (ret) {
|
||||
dev_warn(&tz->device,
|
||||
"Failed to get the maximum desired temperature: %d\n",
|
||||
dev_warn(&tz->device, "Failed to get the maximum desired temperature: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return allocate_power(tz, control_temp);
|
||||
return allocate_power(tz, trip.temperature);
|
||||
}
|
||||
|
||||
static struct thermal_governor thermal_gov_power_allocator = {
|
||||
|
@ -95,30 +95,28 @@ static void update_passive_instance(struct thermal_zone_device *tz,
|
||||
tz->passive += value;
|
||||
}
|
||||
|
||||
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip_id)
|
||||
{
|
||||
int trip_temp;
|
||||
enum thermal_trip_type trip_type;
|
||||
enum thermal_trend trend;
|
||||
struct thermal_instance *instance;
|
||||
struct thermal_trip trip;
|
||||
bool throttle = false;
|
||||
int old_target;
|
||||
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
tz->ops->get_trip_type(tz, trip, &trip_type);
|
||||
__thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
|
||||
trend = get_tz_trend(tz, trip);
|
||||
trend = get_tz_trend(tz, trip_id);
|
||||
|
||||
if (tz->temperature >= trip_temp) {
|
||||
if (tz->temperature >= trip.temperature) {
|
||||
throttle = true;
|
||||
trace_thermal_zone_trip(tz, trip, trip_type);
|
||||
trace_thermal_zone_trip(tz, trip_id, trip.type);
|
||||
}
|
||||
|
||||
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
|
||||
trip, trip_type, trip_temp, trend, throttle);
|
||||
trip_id, trip.type, trip.temperature, trend, throttle);
|
||||
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (instance->trip != trip)
|
||||
if (instance->trip != trip_id)
|
||||
continue;
|
||||
|
||||
old_target = instance->target;
|
||||
@ -132,11 +130,11 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
|
||||
/* Activate a passive thermal instance */
|
||||
if (old_target == THERMAL_NO_TARGET &&
|
||||
instance->target != THERMAL_NO_TARGET)
|
||||
update_passive_instance(tz, trip_type, 1);
|
||||
update_passive_instance(tz, trip.type, 1);
|
||||
/* Deactivate a passive thermal instance */
|
||||
else if (old_target != THERMAL_NO_TARGET &&
|
||||
instance->target == THERMAL_NO_TARGET)
|
||||
update_passive_instance(tz, trip_type, -1);
|
||||
update_passive_instance(tz, trip.type, -1);
|
||||
|
||||
instance->initialized = true;
|
||||
mutex_lock(&instance->cdev->lock);
|
||||
|
@ -16,8 +16,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define HI6220_TEMP0_LAG (0x0)
|
||||
#define HI6220_TEMP0_TH (0x4)
|
||||
@ -427,10 +426,6 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data)
|
||||
data->sensor[0].irq_name = "tsensor_a73";
|
||||
data->sensor[0].data = data;
|
||||
|
||||
data->sensor[1].id = HI3660_LITTLE_SENSOR;
|
||||
data->sensor[1].irq_name = "tsensor_a53";
|
||||
data->sensor[1].data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -482,7 +477,7 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
||||
struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
int ret, i;
|
||||
const struct thermal_trip *trip;
|
||||
struct thermal_trip trip;
|
||||
|
||||
sensor->tzd = devm_thermal_of_zone_register(&pdev->dev,
|
||||
sensor->id, sensor,
|
||||
@ -495,11 +490,12 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
trip = of_thermal_get_trip_points(sensor->tzd);
|
||||
for (i = 0; i < thermal_zone_get_num_trips(sensor->tzd); i++) {
|
||||
|
||||
for (i = 0; i < of_thermal_get_ntrips(sensor->tzd); i++) {
|
||||
if (trip[i].type == THERMAL_TRIP_PASSIVE) {
|
||||
sensor->thres_temp = trip[i].temperature;
|
||||
thermal_zone_get_trip(sensor->tzd, i, &trip);
|
||||
|
||||
if (trip.type == THERMAL_TRIP_PASSIVE) {
|
||||
sensor->thres_temp = trip.temperature;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define TER 0x0 /* TMU enable */
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define IMX_SC_MISC_FUNC_GET_TEMP 13
|
||||
@ -88,7 +87,7 @@ static int imx_sc_thermal_probe(struct platform_device *pdev)
|
||||
if (!resource_id)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; resource_id[i] > 0; i++) {
|
||||
for (i = 0; resource_id[i] >= 0; i++) {
|
||||
|
||||
sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor)
|
||||
@ -127,7 +126,11 @@ static int imx_sc_thermal_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_sc_sensors[] = { IMX_SC_R_SYSTEM, IMX_SC_R_PMIC_0, -1 };
|
||||
static const int imx_sc_sensors[] = {
|
||||
IMX_SC_R_SYSTEM, IMX_SC_R_PMIC_0,
|
||||
IMX_SC_R_AP_0, IMX_SC_R_AP_1,
|
||||
IMX_SC_R_GPU_0_PID0, IMX_SC_R_GPU_1_PID0,
|
||||
IMX_SC_R_DRC_0, -1 };
|
||||
|
||||
static const struct of_device_id imx_sc_thermal_table[] = {
|
||||
{ .compatible = "fsl,imx-sc-thermal", .data = imx_sc_sensors },
|
||||
|
@ -76,7 +76,6 @@
|
||||
enum imx_thermal_trip {
|
||||
IMX_TRIP_PASSIVE,
|
||||
IMX_TRIP_CRITICAL,
|
||||
IMX_TRIP_NUM,
|
||||
};
|
||||
|
||||
#define IMX_POLLING_DELAY 2000 /* millisecond */
|
||||
@ -115,6 +114,11 @@ struct thermal_soc_data {
|
||||
u32 low_alarm_shift;
|
||||
};
|
||||
|
||||
static struct thermal_trip trips[] = {
|
||||
[IMX_TRIP_PASSIVE] = { .type = THERMAL_TRIP_PASSIVE },
|
||||
[IMX_TRIP_CRITICAL] = { .type = THERMAL_TRIP_CRITICAL },
|
||||
};
|
||||
|
||||
static struct thermal_soc_data thermal_imx6q_data = {
|
||||
.version = TEMPMON_IMX6Q,
|
||||
|
||||
@ -201,8 +205,6 @@ struct imx_thermal_data {
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct regmap *tempmon;
|
||||
u32 c1, c2; /* See formula in imx_init_calib() */
|
||||
int temp_passive;
|
||||
int temp_critical;
|
||||
int temp_max;
|
||||
int alarm_temp;
|
||||
int last_temp;
|
||||
@ -279,12 +281,12 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
|
||||
/* Update alarm value to next higher trip point for TEMPMON_IMX6Q */
|
||||
if (data->socdata->version == TEMPMON_IMX6Q) {
|
||||
if (data->alarm_temp == data->temp_passive &&
|
||||
*temp >= data->temp_passive)
|
||||
imx_set_alarm_temp(data, data->temp_critical);
|
||||
if (data->alarm_temp == data->temp_critical &&
|
||||
*temp < data->temp_passive) {
|
||||
imx_set_alarm_temp(data, data->temp_passive);
|
||||
if (data->alarm_temp == trips[IMX_TRIP_PASSIVE].temperature &&
|
||||
*temp >= trips[IMX_TRIP_PASSIVE].temperature)
|
||||
imx_set_alarm_temp(data, trips[IMX_TRIP_CRITICAL].temperature);
|
||||
if (data->alarm_temp == trips[IMX_TRIP_CRITICAL].temperature &&
|
||||
*temp < trips[IMX_TRIP_PASSIVE].temperature) {
|
||||
imx_set_alarm_temp(data, trips[IMX_TRIP_PASSIVE].temperature);
|
||||
dev_dbg(&tz->device, "thermal alarm off: T < %d\n",
|
||||
data->alarm_temp / 1000);
|
||||
}
|
||||
@ -330,29 +332,10 @@ static int imx_change_mode(struct thermal_zone_device *tz,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
*type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
|
||||
THERMAL_TRIP_CRITICAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_get_crit_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
struct imx_thermal_data *data = tz->devdata;
|
||||
*temp = trips[IMX_TRIP_CRITICAL].temperature;
|
||||
|
||||
*temp = data->temp_critical;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
int *temp)
|
||||
{
|
||||
struct imx_thermal_data *data = tz->devdata;
|
||||
|
||||
*temp = (trip == IMX_TRIP_PASSIVE) ? data->temp_passive :
|
||||
data->temp_critical;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -371,10 +354,10 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
return -EPERM;
|
||||
|
||||
/* do not allow passive to be set higher than critical */
|
||||
if (temp < 0 || temp > data->temp_critical)
|
||||
if (temp < 0 || temp > trips[IMX_TRIP_CRITICAL].temperature)
|
||||
return -EINVAL;
|
||||
|
||||
data->temp_passive = temp;
|
||||
trips[IMX_TRIP_PASSIVE].temperature = temp;
|
||||
|
||||
imx_set_alarm_temp(data, temp);
|
||||
|
||||
@ -423,8 +406,6 @@ static struct thermal_zone_device_ops imx_tz_ops = {
|
||||
.unbind = imx_unbind,
|
||||
.get_temp = imx_get_temp,
|
||||
.change_mode = imx_change_mode,
|
||||
.get_trip_type = imx_get_trip_type,
|
||||
.get_trip_temp = imx_get_trip_temp,
|
||||
.get_crit_temp = imx_get_crit_temp,
|
||||
.set_trip_temp = imx_set_trip_temp,
|
||||
};
|
||||
@ -507,8 +488,8 @@ static void imx_init_temp_grade(struct platform_device *pdev, u32 ocotp_mem0)
|
||||
* Set the critical trip point at 5 °C under max
|
||||
* Set the passive trip point at 10 °C under max (changeable via sysfs)
|
||||
*/
|
||||
data->temp_critical = data->temp_max - (1000 * 5);
|
||||
data->temp_passive = data->temp_max - (1000 * 10);
|
||||
trips[IMX_TRIP_PASSIVE].temperature = data->temp_max - (1000 * 10);
|
||||
trips[IMX_TRIP_CRITICAL].temperature = data->temp_max - (1000 * 5);
|
||||
}
|
||||
|
||||
static int imx_init_from_tempmon_data(struct platform_device *pdev)
|
||||
@ -743,12 +724,13 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||
goto legacy_cleanup;
|
||||
}
|
||||
|
||||
data->tz = thermal_zone_device_register("imx_thermal_zone",
|
||||
IMX_TRIP_NUM,
|
||||
BIT(IMX_TRIP_PASSIVE), data,
|
||||
&imx_tz_ops, NULL,
|
||||
IMX_PASSIVE_DELAY,
|
||||
IMX_POLLING_DELAY);
|
||||
data->tz = thermal_zone_device_register_with_trips("imx_thermal_zone",
|
||||
trips,
|
||||
ARRAY_SIZE(trips),
|
||||
BIT(IMX_TRIP_PASSIVE), data,
|
||||
&imx_tz_ops, NULL,
|
||||
IMX_PASSIVE_DELAY,
|
||||
IMX_POLLING_DELAY);
|
||||
if (IS_ERR(data->tz)) {
|
||||
ret = PTR_ERR(data->tz);
|
||||
dev_err(&pdev->dev,
|
||||
@ -758,8 +740,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||
|
||||
dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC"
|
||||
" critical:%dC passive:%dC\n", data->temp_grade,
|
||||
data->temp_max / 1000, data->temp_critical / 1000,
|
||||
data->temp_passive / 1000);
|
||||
data->temp_max / 1000, trips[IMX_TRIP_CRITICAL].temperature / 1000,
|
||||
trips[IMX_TRIP_PASSIVE].temperature / 1000);
|
||||
|
||||
/* Enable measurements at ~ 10 Hz */
|
||||
regmap_write(map, data->socdata->measure_freq_ctrl + REG_CLR,
|
||||
@ -767,10 +749,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||
measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
|
||||
regmap_write(map, data->socdata->measure_freq_ctrl + REG_SET,
|
||||
measure_freq << data->socdata->measure_freq_shift);
|
||||
imx_set_alarm_temp(data, data->temp_passive);
|
||||
imx_set_alarm_temp(data, trips[IMX_TRIP_PASSIVE].temperature);
|
||||
|
||||
if (data->socdata->version == TEMPMON_IMX6SX)
|
||||
imx_set_panic_temp(data, data->temp_critical);
|
||||
imx_set_panic_temp(data, trips[IMX_TRIP_CRITICAL].temperature);
|
||||
|
||||
regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
|
||||
data->socdata->power_down_mask);
|
||||
|
@ -3,6 +3,9 @@ config INTEL_POWERCLAMP
|
||||
tristate "Intel PowerClamp idle injection driver"
|
||||
depends on X86
|
||||
depends on CPU_SUP_INTEL
|
||||
depends on CPU_IDLE
|
||||
select POWERCAP
|
||||
select IDLE_INJECT
|
||||
help
|
||||
Enable this to enable Intel PowerClamp idle injection driver. This
|
||||
enforce idle time which results in more package C-state residency. The
|
||||
@ -12,11 +15,16 @@ config X86_THERMAL_VECTOR
|
||||
def_bool y
|
||||
depends on X86 && CPU_SUP_INTEL && X86_LOCAL_APIC
|
||||
|
||||
config INTEL_TCC
|
||||
bool
|
||||
depends on X86
|
||||
|
||||
config X86_PKG_TEMP_THERMAL
|
||||
tristate "X86 package temperature thermal driver"
|
||||
depends on X86_THERMAL_VECTOR
|
||||
select THERMAL_GOV_USER_SPACE
|
||||
select THERMAL_WRITABLE_TRIPS
|
||||
select INTEL_TCC
|
||||
default m
|
||||
help
|
||||
Enable this to register CPU digital sensor for package temperature as
|
||||
@ -28,6 +36,7 @@ config INTEL_SOC_DTS_IOSF_CORE
|
||||
tristate
|
||||
depends on X86 && PCI
|
||||
select IOSF_MBI
|
||||
select INTEL_TCC
|
||||
help
|
||||
This is becoming a common feature for Intel SoCs to expose the additional
|
||||
digital temperature sensors (DTSs) using side band interface (IOSF). This
|
||||
@ -75,6 +84,7 @@ config INTEL_BXT_PMIC_THERMAL
|
||||
config INTEL_PCH_THERMAL
|
||||
tristate "Intel PCH Thermal Reporting Driver"
|
||||
depends on X86 && PCI
|
||||
select THERMAL_ACPI if ACPI
|
||||
help
|
||||
Enable this to support thermal reporting on certain intel PCHs.
|
||||
Thermal reporting device will provide temperature reading,
|
||||
@ -83,6 +93,7 @@ config INTEL_PCH_THERMAL
|
||||
config INTEL_TCC_COOLING
|
||||
tristate "Intel TCC offset cooling Driver"
|
||||
depends on X86
|
||||
select INTEL_TCC
|
||||
help
|
||||
Enable this to support system cooling by adjusting the effective TCC
|
||||
activation temperature via the TCC Offset register, which is widely
|
||||
|
@ -2,6 +2,7 @@
|
||||
#
|
||||
# Makefile for various Intel thermal drivers.
|
||||
|
||||
obj-$(CONFIG_INTEL_TCC) += intel_tcc.o
|
||||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
|
||||
obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o
|
||||
|
@ -9,7 +9,9 @@ config INT340X_THERMAL
|
||||
select THERMAL_GOV_USER_SPACE
|
||||
select ACPI_THERMAL_REL
|
||||
select ACPI_FAN
|
||||
select THERMAL_ACPI
|
||||
select INTEL_SOC_DTS_IOSF_CORE
|
||||
select INTEL_TCC
|
||||
select PROC_THERMAL_MMIO_RAPL if POWERCAP
|
||||
help
|
||||
Newer laptops and tablets that use ACPI may have thermal sensors and
|
||||
|
@ -60,6 +60,7 @@ struct int3400_thermal_priv {
|
||||
int odvp_count;
|
||||
int *odvp;
|
||||
u32 os_uuid_mask;
|
||||
int production_mode;
|
||||
struct odvp_attr *odvp_attrs;
|
||||
};
|
||||
|
||||
@ -130,10 +131,7 @@ static ssize_t available_uuids_show(struct device *dev,
|
||||
|
||||
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
|
||||
if (priv->uuid_bitmap & (1 << i))
|
||||
length += scnprintf(&buf[length],
|
||||
PAGE_SIZE - length,
|
||||
"%s\n",
|
||||
int3400_thermal_uuids[i]);
|
||||
length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
|
||||
}
|
||||
|
||||
return length;
|
||||
@ -151,10 +149,7 @@ static ssize_t current_uuid_show(struct device *dev,
|
||||
|
||||
for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
|
||||
if (priv->os_uuid_mask & BIT(i))
|
||||
length += scnprintf(&buf[length],
|
||||
PAGE_SIZE - length,
|
||||
"%s\n",
|
||||
int3400_thermal_uuids[i]);
|
||||
length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
|
||||
}
|
||||
|
||||
if (length)
|
||||
@ -315,6 +310,44 @@ end:
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", priv->production_mode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(production_mode);
|
||||
|
||||
static int production_mode_init(struct int3400_thermal_priv *priv)
|
||||
{
|
||||
unsigned long long mode;
|
||||
acpi_status status;
|
||||
int ret;
|
||||
|
||||
priv->production_mode = -1;
|
||||
|
||||
status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode);
|
||||
/* If the method is not present, this is not an error */
|
||||
if (ACPI_FAILURE(status))
|
||||
return 0;
|
||||
|
||||
ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->production_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void production_mode_exit(struct int3400_thermal_priv *priv)
|
||||
{
|
||||
if (priv->production_mode >= 0)
|
||||
sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
|
||||
}
|
||||
|
||||
static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -610,8 +643,15 @@ static int int3400_thermal_probe(struct platform_device *pdev)
|
||||
if (result)
|
||||
goto free_sysfs;
|
||||
|
||||
result = production_mode_init(priv);
|
||||
if (result)
|
||||
goto free_notify;
|
||||
|
||||
return 0;
|
||||
|
||||
free_notify:
|
||||
acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
|
||||
int3400_notify);
|
||||
free_sysfs:
|
||||
cleanup_odvp(priv);
|
||||
if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
|
||||
@ -638,6 +678,8 @@ static int int3400_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
production_mode_exit(priv);
|
||||
|
||||
acpi_remove_notify_handler(
|
||||
priv->adev->handle, ACPI_DEVICE_NOTIFY,
|
||||
int3400_notify);
|
||||
|
@ -69,7 +69,7 @@ static void int3403_notify(acpi_handle handle,
|
||||
THERMAL_TRIP_VIOLATED);
|
||||
break;
|
||||
case INT3403_PERF_TRIP_POINT_CHANGED:
|
||||
int340x_thermal_read_trips(obj->int340x_zone);
|
||||
int340x_thermal_update_trips(obj->int340x_zone);
|
||||
int340x_thermal_zone_device_update(obj->int340x_zone,
|
||||
THERMAL_TRIP_CHANGED);
|
||||
break;
|
||||
|
@ -18,9 +18,6 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_temp)
|
||||
return d->override_ops->get_temp(zone, temp);
|
||||
|
||||
status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
@ -32,125 +29,30 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
if (conv_temp < 0)
|
||||
return conv_temp;
|
||||
|
||||
*temp = (unsigned long)conv_temp * 10;
|
||||
} else
|
||||
*temp = conv_temp * 10;
|
||||
} else {
|
||||
/* _TMP returns the temperature in tenths of degrees Kelvin */
|
||||
*temp = deci_kelvin_to_millicelsius(tmp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
int i, ret = 0;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_temp)
|
||||
return d->override_ops->get_trip_temp(zone, trip, temp);
|
||||
|
||||
mutex_lock(&d->trip_mutex);
|
||||
|
||||
if (trip < d->aux_trip_nr)
|
||||
*temp = d->aux_trips[trip];
|
||||
else if (trip == d->crt_trip_id)
|
||||
*temp = d->crt_temp;
|
||||
else if (trip == d->psv_trip_id)
|
||||
*temp = d->psv_temp;
|
||||
else if (trip == d->hot_trip_id)
|
||||
*temp = d->hot_temp;
|
||||
else {
|
||||
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*temp = d->act_trips[i].temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&d->trip_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
|
||||
int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
int i, ret = 0;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_type)
|
||||
return d->override_ops->get_trip_type(zone, trip, type);
|
||||
|
||||
mutex_lock(&d->trip_mutex);
|
||||
|
||||
if (trip < d->aux_trip_nr)
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
else if (trip == d->crt_trip_id)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else if (trip == d->hot_trip_id)
|
||||
*type = THERMAL_TRIP_HOT;
|
||||
else if (trip == d->psv_trip_id)
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
else {
|
||||
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*type = THERMAL_TRIP_ACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&d->trip_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, int temp)
|
||||
int trip, int temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
char name[] = {'P', 'A', 'T', '0' + trip, '\0'};
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
|
||||
if (d->override_ops && d->override_ops->set_trip_temp)
|
||||
return d->override_ops->set_trip_temp(zone, trip, temp);
|
||||
if (trip > 9)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
status = acpi_execute_simple_method(d->adev->handle, name,
|
||||
millicelsius_to_deci_kelvin(temp));
|
||||
millicelsius_to_deci_kelvin(temp));
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
d->aux_trips[trip] = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
acpi_status status;
|
||||
unsigned long long hyst;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_hyst)
|
||||
return d->override_ops->get_trip_hyst(zone, trip, temp);
|
||||
|
||||
status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
*temp = 0;
|
||||
else
|
||||
*temp = hyst * 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -161,67 +63,49 @@ static void int340x_thermal_critical(struct thermal_zone_device *zone)
|
||||
|
||||
static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
|
||||
.get_temp = int340x_thermal_get_zone_temp,
|
||||
.get_trip_temp = int340x_thermal_get_trip_temp,
|
||||
.get_trip_type = int340x_thermal_get_trip_type,
|
||||
.set_trip_temp = int340x_thermal_set_trip_temp,
|
||||
.get_trip_hyst = int340x_thermal_get_trip_hyst,
|
||||
.critical = int340x_thermal_critical,
|
||||
};
|
||||
|
||||
static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
|
||||
int *temp)
|
||||
static int int340x_thermal_read_trips(struct acpi_device *zone_adev,
|
||||
struct thermal_trip *zone_trips,
|
||||
int trip_cnt)
|
||||
{
|
||||
unsigned long long r;
|
||||
acpi_status status;
|
||||
int i, ret;
|
||||
|
||||
status = acpi_evaluate_integer(handle, name, NULL, &r);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = deci_kelvin_to_millicelsius(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
|
||||
{
|
||||
int trip_cnt = int34x_zone->aux_trip_nr;
|
||||
int i;
|
||||
|
||||
mutex_lock(&int34x_zone->trip_mutex);
|
||||
|
||||
int34x_zone->crt_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
|
||||
&int34x_zone->crt_temp))
|
||||
int34x_zone->crt_trip_id = trip_cnt++;
|
||||
|
||||
int34x_zone->hot_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
|
||||
&int34x_zone->hot_temp))
|
||||
int34x_zone->hot_trip_id = trip_cnt++;
|
||||
|
||||
int34x_zone->psv_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
|
||||
&int34x_zone->psv_temp))
|
||||
int34x_zone->psv_trip_id = trip_cnt++;
|
||||
|
||||
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
|
||||
|
||||
if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
|
||||
name,
|
||||
&int34x_zone->act_trips[i].temp))
|
||||
break;
|
||||
|
||||
int34x_zone->act_trips[i].id = trip_cnt++;
|
||||
int34x_zone->act_trips[i].valid = true;
|
||||
ret = thermal_acpi_critical_trip_temp(zone_adev,
|
||||
&zone_trips[trip_cnt].temperature);
|
||||
if (!ret) {
|
||||
zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL;
|
||||
trip_cnt++;
|
||||
}
|
||||
|
||||
mutex_unlock(&int34x_zone->trip_mutex);
|
||||
ret = thermal_acpi_hot_trip_temp(zone_adev,
|
||||
&zone_trips[trip_cnt].temperature);
|
||||
if (!ret) {
|
||||
zone_trips[trip_cnt].type = THERMAL_TRIP_HOT;
|
||||
trip_cnt++;
|
||||
}
|
||||
|
||||
ret = thermal_acpi_passive_trip_temp(zone_adev,
|
||||
&zone_trips[trip_cnt].temperature);
|
||||
if (!ret) {
|
||||
zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE;
|
||||
trip_cnt++;
|
||||
}
|
||||
|
||||
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
ret = thermal_acpi_active_trip_temp(zone_adev, i,
|
||||
&zone_trips[trip_cnt].temperature);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE;
|
||||
trip_cnt++;
|
||||
}
|
||||
|
||||
return trip_cnt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
|
||||
|
||||
static struct thermal_zone_params int340x_thermal_params = {
|
||||
.governor_name = "user_space",
|
||||
@ -229,89 +113,147 @@ static struct thermal_zone_params int340x_thermal_params = {
|
||||
};
|
||||
|
||||
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
|
||||
struct thermal_zone_device_ops *override_ops)
|
||||
int (*get_temp) (struct thermal_zone_device *, int *))
|
||||
{
|
||||
struct int34x_thermal_zone *int34x_thermal_zone;
|
||||
acpi_status status;
|
||||
unsigned long long trip_cnt;
|
||||
struct int34x_thermal_zone *int34x_zone;
|
||||
struct thermal_trip *zone_trips;
|
||||
unsigned long long trip_cnt = 0;
|
||||
unsigned long long hyst;
|
||||
int trip_mask = 0;
|
||||
int ret;
|
||||
acpi_status status;
|
||||
int i, ret;
|
||||
|
||||
int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
|
||||
GFP_KERNEL);
|
||||
if (!int34x_thermal_zone)
|
||||
int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);
|
||||
if (!int34x_zone)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&int34x_thermal_zone->trip_mutex);
|
||||
int34x_zone->adev = adev;
|
||||
|
||||
int34x_thermal_zone->adev = adev;
|
||||
int34x_thermal_zone->override_ops = override_ops;
|
||||
int34x_zone->ops = kmemdup(&int340x_thermal_zone_ops,
|
||||
sizeof(int340x_thermal_zone_ops), GFP_KERNEL);
|
||||
if (!int34x_zone->ops) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ops_alloc;
|
||||
}
|
||||
|
||||
if (get_temp)
|
||||
int34x_zone->ops->get_temp = get_temp;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
else {
|
||||
int i;
|
||||
|
||||
int34x_thermal_zone->aux_trips =
|
||||
kcalloc(trip_cnt,
|
||||
sizeof(*int34x_thermal_zone->aux_trips),
|
||||
GFP_KERNEL);
|
||||
if (!int34x_thermal_zone->aux_trips) {
|
||||
ret = -ENOMEM;
|
||||
goto err_trip_alloc;
|
||||
}
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
int34x_zone->aux_trip_nr = trip_cnt;
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
int34x_thermal_zone->aux_trip_nr = trip_cnt;
|
||||
for (i = 0; i < trip_cnt; ++i)
|
||||
int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID;
|
||||
}
|
||||
|
||||
trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
|
||||
zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),
|
||||
GFP_KERNEL);
|
||||
if (!zone_trips) {
|
||||
ret = -ENOMEM;
|
||||
goto err_trips_alloc;
|
||||
}
|
||||
|
||||
int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
|
||||
adev->handle);
|
||||
for (i = 0; i < trip_cnt; i++) {
|
||||
zone_trips[i].type = THERMAL_TRIP_PASSIVE;
|
||||
zone_trips[i].temperature = THERMAL_TEMP_INVALID;
|
||||
}
|
||||
|
||||
int34x_thermal_zone->zone = thermal_zone_device_register(
|
||||
acpi_device_bid(adev),
|
||||
trip_cnt,
|
||||
trip_mask, int34x_thermal_zone,
|
||||
&int340x_thermal_zone_ops,
|
||||
&int340x_thermal_params,
|
||||
0, 0);
|
||||
if (IS_ERR(int34x_thermal_zone->zone)) {
|
||||
ret = PTR_ERR(int34x_thermal_zone->zone);
|
||||
trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt);
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_SUCCESS(status))
|
||||
hyst *= 100;
|
||||
else
|
||||
hyst = 0;
|
||||
|
||||
for (i = 0; i < trip_cnt; ++i)
|
||||
zone_trips[i].hysteresis = hyst;
|
||||
|
||||
int34x_zone->trips = zone_trips;
|
||||
|
||||
int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);
|
||||
|
||||
int34x_zone->zone = thermal_zone_device_register_with_trips(
|
||||
acpi_device_bid(adev),
|
||||
zone_trips, trip_cnt,
|
||||
trip_mask, int34x_zone,
|
||||
int34x_zone->ops,
|
||||
&int340x_thermal_params,
|
||||
0, 0);
|
||||
if (IS_ERR(int34x_zone->zone)) {
|
||||
ret = PTR_ERR(int34x_zone->zone);
|
||||
goto err_thermal_zone;
|
||||
}
|
||||
ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
|
||||
ret = thermal_zone_device_enable(int34x_zone->zone);
|
||||
if (ret)
|
||||
goto err_enable;
|
||||
|
||||
return int34x_thermal_zone;
|
||||
return int34x_zone;
|
||||
|
||||
err_enable:
|
||||
thermal_zone_device_unregister(int34x_thermal_zone->zone);
|
||||
thermal_zone_device_unregister(int34x_zone->zone);
|
||||
err_thermal_zone:
|
||||
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
|
||||
kfree(int34x_thermal_zone->aux_trips);
|
||||
err_trip_alloc:
|
||||
mutex_destroy(&int34x_thermal_zone->trip_mutex);
|
||||
kfree(int34x_thermal_zone);
|
||||
kfree(int34x_zone->trips);
|
||||
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
|
||||
err_trips_alloc:
|
||||
kfree(int34x_zone->ops);
|
||||
err_ops_alloc:
|
||||
kfree(int34x_zone);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
|
||||
|
||||
void int340x_thermal_zone_remove(struct int34x_thermal_zone
|
||||
*int34x_thermal_zone)
|
||||
void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone)
|
||||
{
|
||||
thermal_zone_device_unregister(int34x_thermal_zone->zone);
|
||||
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
|
||||
kfree(int34x_thermal_zone->aux_trips);
|
||||
mutex_destroy(&int34x_thermal_zone->trip_mutex);
|
||||
kfree(int34x_thermal_zone);
|
||||
thermal_zone_device_unregister(int34x_zone->zone);
|
||||
acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
|
||||
kfree(int34x_zone->trips);
|
||||
kfree(int34x_zone->ops);
|
||||
kfree(int34x_zone);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
|
||||
|
||||
void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)
|
||||
{
|
||||
struct acpi_device *zone_adev = int34x_zone->adev;
|
||||
struct thermal_trip *zone_trips = int34x_zone->trips;
|
||||
int trip_cnt = int34x_zone->zone->num_trips;
|
||||
int act_trip_nr = 0;
|
||||
int i;
|
||||
|
||||
mutex_lock(&int34x_zone->zone->lock);
|
||||
|
||||
for (i = int34x_zone->aux_trip_nr; i < trip_cnt; i++) {
|
||||
int temp, err;
|
||||
|
||||
switch (zone_trips[i].type) {
|
||||
case THERMAL_TRIP_CRITICAL:
|
||||
err = thermal_acpi_critical_trip_temp(zone_adev, &temp);
|
||||
break;
|
||||
case THERMAL_TRIP_HOT:
|
||||
err = thermal_acpi_hot_trip_temp(zone_adev, &temp);
|
||||
break;
|
||||
case THERMAL_TRIP_PASSIVE:
|
||||
err = thermal_acpi_passive_trip_temp(zone_adev, &temp);
|
||||
break;
|
||||
case THERMAL_TRIP_ACTIVE:
|
||||
err = thermal_acpi_active_trip_temp(zone_adev, act_trip_nr++,
|
||||
&temp);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
}
|
||||
if (err) {
|
||||
zone_trips[i].temperature = THERMAL_TEMP_INVALID;
|
||||
continue;
|
||||
}
|
||||
|
||||
zone_trips[i].temperature = temp;
|
||||
}
|
||||
|
||||
mutex_unlock(&int34x_zone->zone->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);
|
||||
|
||||
MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <acpi/acpi_lpat.h>
|
||||
|
||||
#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
|
||||
#define INT340X_THERMAL_MAX_TRIP_COUNT INT340X_THERMAL_MAX_ACT_TRIP_COUNT + 3
|
||||
|
||||
struct active_trip {
|
||||
int temp;
|
||||
@ -19,26 +20,18 @@ struct active_trip {
|
||||
|
||||
struct int34x_thermal_zone {
|
||||
struct acpi_device *adev;
|
||||
struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
|
||||
unsigned long *aux_trips;
|
||||
struct thermal_trip *trips;
|
||||
int aux_trip_nr;
|
||||
int psv_temp;
|
||||
int psv_trip_id;
|
||||
int crt_temp;
|
||||
int crt_trip_id;
|
||||
int hot_temp;
|
||||
int hot_trip_id;
|
||||
struct thermal_zone_device *zone;
|
||||
struct thermal_zone_device_ops *override_ops;
|
||||
struct thermal_zone_device_ops *ops;
|
||||
void *priv_data;
|
||||
struct acpi_lpat_conversion_table *lpat_table;
|
||||
struct mutex trip_mutex;
|
||||
};
|
||||
|
||||
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
|
||||
struct thermal_zone_device_ops *override_ops);
|
||||
int (*get_temp) (struct thermal_zone_device *, int *));
|
||||
void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
|
||||
int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
|
||||
void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone);
|
||||
|
||||
static inline void int340x_thermal_zone_set_priv_data(
|
||||
struct int34x_thermal_zone *tzone, void *priv_data)
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/intel_tcc.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
@ -68,54 +69,17 @@ static const struct attribute_group power_limit_attribute_group = {
|
||||
.name = "power_limits"
|
||||
};
|
||||
|
||||
static int tcc_get_offset(void)
|
||||
{
|
||||
u64 val;
|
||||
int err;
|
||||
|
||||
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return (val >> 24) & 0x3f;
|
||||
}
|
||||
|
||||
static ssize_t tcc_offset_degree_celsius_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int tcc;
|
||||
int offset;
|
||||
|
||||
tcc = tcc_get_offset();
|
||||
if (tcc < 0)
|
||||
return tcc;
|
||||
offset = intel_tcc_get_offset(-1);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
|
||||
return sprintf(buf, "%d\n", tcc);
|
||||
}
|
||||
|
||||
static int tcc_offset_update(unsigned int tcc)
|
||||
{
|
||||
u64 val;
|
||||
int err;
|
||||
|
||||
if (tcc > 63)
|
||||
return -EINVAL;
|
||||
|
||||
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val & BIT(31))
|
||||
return -EPERM;
|
||||
|
||||
val &= ~GENMASK_ULL(29, 24);
|
||||
val |= (tcc & 0x3f) << 24;
|
||||
|
||||
err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return sprintf(buf, "%d\n", offset);
|
||||
}
|
||||
|
||||
static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
|
||||
@ -136,7 +100,7 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
|
||||
if (kstrtouint(buf, 0, &tcc))
|
||||
return -EINVAL;
|
||||
|
||||
err = tcc_offset_update(tcc);
|
||||
err = intel_tcc_set_offset(-1, tcc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -145,72 +109,27 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR_RW(tcc_offset_degree_celsius);
|
||||
|
||||
static int stored_tjmax; /* since it is fixed, we can have local storage */
|
||||
|
||||
static int get_tjmax(void)
|
||||
{
|
||||
u32 eax, edx;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (eax >> 16) & 0xff;
|
||||
if (val)
|
||||
return val;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int read_temp_msr(int *temp)
|
||||
static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
int *temp)
|
||||
{
|
||||
int cpu;
|
||||
u32 eax, edx;
|
||||
int err;
|
||||
unsigned long curr_temp_off = 0;
|
||||
int curr_temp;
|
||||
|
||||
*temp = 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
|
||||
&edx);
|
||||
if (err)
|
||||
goto err_ret;
|
||||
else {
|
||||
if (eax & 0x80000000) {
|
||||
curr_temp_off = (eax >> 16) & 0x7f;
|
||||
if (!*temp || curr_temp_off < *temp)
|
||||
*temp = curr_temp_off;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
curr_temp = intel_tcc_get_temp(cpu, false);
|
||||
if (curr_temp < 0)
|
||||
return curr_temp;
|
||||
if (!*temp || curr_temp > *temp)
|
||||
*temp = curr_temp;
|
||||
}
|
||||
|
||||
*temp *= 1000;
|
||||
|
||||
return 0;
|
||||
err_ret:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
int *temp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = read_temp_msr(temp);
|
||||
if (!ret)
|
||||
*temp = (stored_tjmax - *temp) * 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops proc_thermal_local_ops = {
|
||||
.get_temp = proc_thermal_get_zone_temp,
|
||||
};
|
||||
|
||||
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
|
||||
{
|
||||
int i;
|
||||
@ -285,7 +204,7 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
|
||||
struct acpi_device *adev;
|
||||
acpi_status status;
|
||||
unsigned long long tmp;
|
||||
struct thermal_zone_device_ops *ops = NULL;
|
||||
int (*get_temp) (struct thermal_zone_device *, int *) = NULL;
|
||||
int ret;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
@ -302,12 +221,11 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
|
||||
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
/* there is no _TMP method, add local method */
|
||||
stored_tjmax = get_tjmax();
|
||||
if (stored_tjmax > 0)
|
||||
ops = &proc_thermal_local_ops;
|
||||
if (intel_tcc_get_tjmax(-1) > 0)
|
||||
get_temp = proc_thermal_get_zone_temp;
|
||||
}
|
||||
|
||||
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
|
||||
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, get_temp);
|
||||
if (IS_ERR(proc_priv->int340x_zone)) {
|
||||
return PTR_ERR(proc_priv->int340x_zone);
|
||||
} else
|
||||
@ -356,7 +274,7 @@ static int tcc_offset_save = -1;
|
||||
|
||||
int proc_thermal_suspend(struct device *dev)
|
||||
{
|
||||
tcc_offset_save = tcc_get_offset();
|
||||
tcc_offset_save = intel_tcc_get_offset(-1);
|
||||
if (tcc_offset_save < 0)
|
||||
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
|
||||
|
||||
@ -373,7 +291,7 @@ int proc_thermal_resume(struct device *dev)
|
||||
|
||||
/* Do not update if saving failed */
|
||||
if (tcc_offset_save >= 0)
|
||||
tcc_offset_update(tcc_offset_save);
|
||||
intel_tcc_set_offset(-1, tcc_offset_save);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -460,6 +378,7 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(proc_thermal_mmio_remove);
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TCC);
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -144,34 +144,6 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzd,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct proc_thermal_pci *pci_info = tzd->devdata;
|
||||
u32 _temp;
|
||||
|
||||
proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &_temp);
|
||||
if (!_temp) {
|
||||
*temp = THERMAL_TEMP_INVALID;
|
||||
} else {
|
||||
int tjmax;
|
||||
|
||||
proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
|
||||
_temp = tjmax - _temp;
|
||||
*temp = (unsigned long)_temp * 1000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *tzd, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
|
||||
{
|
||||
struct proc_thermal_pci *pci_info = tzd->devdata;
|
||||
@ -200,10 +172,26 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_trip_temp(struct proc_thermal_pci *pci_info)
|
||||
{
|
||||
int temp, tjmax;
|
||||
|
||||
proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &temp);
|
||||
if (!temp)
|
||||
return THERMAL_TEMP_INVALID;
|
||||
|
||||
proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
|
||||
temp = (tjmax - temp) * 1000;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
static struct thermal_trip psv_trip = {
|
||||
.type = THERMAL_TRIP_PASSIVE,
|
||||
};
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
};
|
||||
|
||||
@ -251,7 +239,10 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
|
||||
if (ret)
|
||||
goto err_ret_thermal;
|
||||
|
||||
pci_info->tzone = thermal_zone_device_register("TCPU_PCI", 1, 1, pci_info,
|
||||
psv_trip.temperature = get_trip_temp(pci_info);
|
||||
|
||||
pci_info->tzone = thermal_zone_device_register_with_trips("TCPU_PCI", &psv_trip,
|
||||
1, 1, pci_info,
|
||||
&tzone_ops,
|
||||
&tzone_params, 0, 0);
|
||||
if (IS_ERR(pci_info->tzone)) {
|
||||
|
@ -40,10 +40,11 @@
|
||||
|
||||
#include <asm/msr.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "intel_hfi.h"
|
||||
#include "thermal_interrupt.h"
|
||||
|
||||
#include "../thermal_netlink.h"
|
||||
|
||||
/* Hardware Feedback Interface MSR configuration bits */
|
||||
#define HW_FEEDBACK_PTR_VALID_BIT BIT(0)
|
||||
#define HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT BIT(0)
|
||||
|
@ -232,9 +232,9 @@ static DEFINE_MUTEX(intel_menlow_attr_lock);
|
||||
|
||||
/*
|
||||
* sensor_get_auxtrip - get the current auxtrip value from sensor
|
||||
* @name: Thermalzone name
|
||||
* @auxtype : AUX0/AUX1
|
||||
* @buf: syfs buffer
|
||||
* @handle: Object handle
|
||||
* @index : GET_AUX1/GET_AUX0
|
||||
* @value : The address will be fill by the value
|
||||
*/
|
||||
static int sensor_get_auxtrip(acpi_handle handle, int index,
|
||||
unsigned long long *value)
|
||||
@ -254,9 +254,9 @@ static int sensor_get_auxtrip(acpi_handle handle, int index,
|
||||
|
||||
/*
|
||||
* sensor_set_auxtrip - set the new auxtrip value to sensor
|
||||
* @name: Thermalzone name
|
||||
* @auxtype : AUX0/AUX1
|
||||
* @buf: syfs buffer
|
||||
* @handle: Object handle
|
||||
* @index : GET_AUX1/GET_AUX0
|
||||
* @value : The value will be set
|
||||
*/
|
||||
static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
|
||||
{
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */
|
||||
#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */
|
||||
#define PCH_THERMAL_DID_LWB 0xA1B1 /* Lewisburg PCH */
|
||||
#define PCH_THERMAL_DID_WBG 0x8D24 /* Wellsburg PCH */
|
||||
|
||||
/* Wildcat Point-LP PCH Thermal registers */
|
||||
#define WPT_TEMP 0x0000 /* Temperature */
|
||||
@ -65,6 +66,8 @@
|
||||
#define WPT_TEMP_OFFSET (PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE)
|
||||
#define GET_PCH_TEMP(x) (((x) / 2) + PCH_TEMP_OFFSET)
|
||||
|
||||
#define PCH_MAX_TRIPS 3 /* critical, hot, passive */
|
||||
|
||||
/* Amount of time for each cooling delay, 100ms by default for now */
|
||||
static unsigned int delay_timeout = 100;
|
||||
module_param(delay_timeout, int, 0644);
|
||||
@ -79,66 +82,114 @@ static char driver_name[] = "Intel PCH thermal driver";
|
||||
|
||||
struct pch_thermal_device {
|
||||
void __iomem *hw_base;
|
||||
const struct pch_dev_ops *ops;
|
||||
struct pci_dev *pdev;
|
||||
struct thermal_zone_device *tzd;
|
||||
int crt_trip_id;
|
||||
unsigned long crt_temp;
|
||||
int hot_trip_id;
|
||||
unsigned long hot_temp;
|
||||
int psv_trip_id;
|
||||
unsigned long psv_temp;
|
||||
struct thermal_trip trips[PCH_MAX_TRIPS];
|
||||
bool bios_enabled;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
/*
|
||||
* On some platforms, there is a companion ACPI device, which adds
|
||||
* passive trip temperature using _PSV method. There is no specific
|
||||
* passive temperature setting in MMIO interface of this PCI device.
|
||||
*/
|
||||
static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
|
||||
int *nr_trips)
|
||||
static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
ptd->psv_trip_id = -1;
|
||||
int temp;
|
||||
|
||||
adev = ACPI_COMPANION(&ptd->pdev->dev);
|
||||
if (adev) {
|
||||
unsigned long long r;
|
||||
acpi_status status;
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
|
||||
&r);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
unsigned long trip_temp;
|
||||
if (thermal_acpi_passive_trip_temp(adev, &temp) || temp <= 0)
|
||||
return 0;
|
||||
|
||||
trip_temp = deci_kelvin_to_millicelsius(r);
|
||||
if (trip_temp) {
|
||||
ptd->psv_temp = trip_temp;
|
||||
ptd->psv_trip_id = *nr_trips;
|
||||
++(*nr_trips);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptd->trips[trip].type = THERMAL_TRIP_PASSIVE;
|
||||
ptd->trips[trip].temperature = temp;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
|
||||
int *nr_trips)
|
||||
static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
|
||||
{
|
||||
ptd->psv_trip_id = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
|
||||
static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
u8 tsel;
|
||||
u16 trip_temp;
|
||||
struct pch_thermal_device *ptd = tzd->devdata;
|
||||
|
||||
*nr_trips = 0;
|
||||
*temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pch_critical(struct thermal_zone_device *tzd)
|
||||
{
|
||||
dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzd_ops = {
|
||||
.get_temp = pch_thermal_get_temp,
|
||||
.critical = pch_critical,
|
||||
};
|
||||
|
||||
enum pch_board_ids {
|
||||
PCH_BOARD_HSW = 0,
|
||||
PCH_BOARD_WPT,
|
||||
PCH_BOARD_SKL,
|
||||
PCH_BOARD_CNL,
|
||||
PCH_BOARD_CML,
|
||||
PCH_BOARD_LWB,
|
||||
PCH_BOARD_WBG,
|
||||
};
|
||||
|
||||
static const char *board_names[] = {
|
||||
[PCH_BOARD_HSW] = "pch_haswell",
|
||||
[PCH_BOARD_WPT] = "pch_wildcat_point",
|
||||
[PCH_BOARD_SKL] = "pch_skylake",
|
||||
[PCH_BOARD_CNL] = "pch_cannonlake",
|
||||
[PCH_BOARD_CML] = "pch_cometlake",
|
||||
[PCH_BOARD_LWB] = "pch_lewisburg",
|
||||
[PCH_BOARD_WBG] = "pch_wellsburg",
|
||||
};
|
||||
|
||||
static int intel_pch_thermal_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
enum pch_board_ids board_id = id->driver_data;
|
||||
struct pch_thermal_device *ptd;
|
||||
int nr_trips = 0;
|
||||
u16 trip_temp;
|
||||
u8 tsel;
|
||||
int err;
|
||||
|
||||
ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
|
||||
if (!ptd)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_set_drvdata(pdev, ptd);
|
||||
ptd->pdev = pdev;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to enable pci device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, driver_name);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to request pci region\n");
|
||||
goto error_disable;
|
||||
}
|
||||
|
||||
ptd->hw_base = pci_ioremap_bar(pdev, 0);
|
||||
if (!ptd->hw_base) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to map mem base\n");
|
||||
goto error_release;
|
||||
}
|
||||
|
||||
/* Check if BIOS has already enabled thermal sensor */
|
||||
if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
|
||||
@ -153,52 +204,79 @@ static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
|
||||
*/
|
||||
if (tsel & WPT_TSEL_PLDB) {
|
||||
dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
|
||||
return -ENODEV;
|
||||
err = -ENODEV;
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
|
||||
if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
|
||||
dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
|
||||
return -ENODEV;
|
||||
err = -ENODEV;
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
read_trips:
|
||||
ptd->crt_trip_id = -1;
|
||||
trip_temp = readw(ptd->hw_base + WPT_CTT);
|
||||
trip_temp &= 0x1FF;
|
||||
if (trip_temp) {
|
||||
ptd->crt_temp = GET_WPT_TEMP(trip_temp);
|
||||
ptd->crt_trip_id = 0;
|
||||
++(*nr_trips);
|
||||
ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
|
||||
ptd->trips[nr_trips++].type = THERMAL_TRIP_CRITICAL;
|
||||
}
|
||||
|
||||
ptd->hot_trip_id = -1;
|
||||
trip_temp = readw(ptd->hw_base + WPT_PHL);
|
||||
trip_temp &= 0x1FF;
|
||||
if (trip_temp) {
|
||||
ptd->hot_temp = GET_WPT_TEMP(trip_temp);
|
||||
ptd->hot_trip_id = *nr_trips;
|
||||
++(*nr_trips);
|
||||
ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
|
||||
ptd->trips[nr_trips++].type = THERMAL_TRIP_HOT;
|
||||
}
|
||||
|
||||
pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
|
||||
nr_trips += pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
|
||||
|
||||
ptd->tzd = thermal_zone_device_register_with_trips(board_names[board_id],
|
||||
ptd->trips, nr_trips,
|
||||
0, ptd, &tzd_ops,
|
||||
NULL, 0, 0);
|
||||
if (IS_ERR(ptd->tzd)) {
|
||||
dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
|
||||
board_names[board_id]);
|
||||
err = PTR_ERR(ptd->tzd);
|
||||
goto error_cleanup;
|
||||
}
|
||||
err = thermal_zone_device_enable(ptd->tzd);
|
||||
if (err)
|
||||
goto err_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
thermal_zone_device_unregister(ptd->tzd);
|
||||
error_cleanup:
|
||||
iounmap(ptd->hw_base);
|
||||
error_release:
|
||||
pci_release_regions(pdev);
|
||||
error_disable:
|
||||
pci_disable_device(pdev);
|
||||
dev_err(&pdev->dev, "pci device failed to probe\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
|
||||
static void intel_pch_thermal_remove(struct pci_dev *pdev)
|
||||
{
|
||||
*temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
|
||||
struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
|
||||
|
||||
return 0;
|
||||
thermal_zone_device_unregister(ptd->tzd);
|
||||
iounmap(ptd->hw_base);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/* Cool the PCH when it's overheat in .suspend_noirq phase */
|
||||
static int pch_wpt_suspend(struct pch_thermal_device *ptd)
|
||||
static int intel_pch_thermal_suspend_noirq(struct device *device)
|
||||
{
|
||||
u8 tsel;
|
||||
int pch_delay_cnt = 0;
|
||||
struct pch_thermal_device *ptd = dev_get_drvdata(device);
|
||||
u16 pch_thr_temp, pch_cur_temp;
|
||||
int pch_delay_cnt = 0;
|
||||
u8 tsel;
|
||||
|
||||
/* Shutdown the thermal sensor if it is not enabled by BIOS */
|
||||
if (!ptd->bios_enabled) {
|
||||
@ -261,8 +339,9 @@ static int pch_wpt_suspend(struct pch_thermal_device *ptd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_wpt_resume(struct pch_thermal_device *ptd)
|
||||
static int intel_pch_thermal_resume(struct device *device)
|
||||
{
|
||||
struct pch_thermal_device *ptd = dev_get_drvdata(device);
|
||||
u8 tsel;
|
||||
|
||||
if (ptd->bios_enabled)
|
||||
@ -275,226 +354,29 @@ static int pch_wpt_resume(struct pch_thermal_device *ptd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pch_dev_ops {
|
||||
int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
|
||||
int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
|
||||
int (*suspend)(struct pch_thermal_device *ptd);
|
||||
int (*resume)(struct pch_thermal_device *ptd);
|
||||
};
|
||||
|
||||
|
||||
/* dev ops for Wildcat Point */
|
||||
static const struct pch_dev_ops pch_dev_ops_wpt = {
|
||||
.hw_init = pch_wpt_init,
|
||||
.get_temp = pch_wpt_get_temp,
|
||||
.suspend = pch_wpt_suspend,
|
||||
.resume = pch_wpt_resume,
|
||||
};
|
||||
|
||||
static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
struct pch_thermal_device *ptd = tzd->devdata;
|
||||
|
||||
return ptd->ops->get_temp(ptd, temp);
|
||||
}
|
||||
|
||||
static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct pch_thermal_device *ptd = tzd->devdata;
|
||||
|
||||
if (ptd->crt_trip_id == trip)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else if (ptd->hot_trip_id == trip)
|
||||
*type = THERMAL_TRIP_HOT;
|
||||
else if (ptd->psv_trip_id == trip)
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
|
||||
{
|
||||
struct pch_thermal_device *ptd = tzd->devdata;
|
||||
|
||||
if (ptd->crt_trip_id == trip)
|
||||
*temp = ptd->crt_temp;
|
||||
else if (ptd->hot_trip_id == trip)
|
||||
*temp = ptd->hot_temp;
|
||||
else if (ptd->psv_trip_id == trip)
|
||||
*temp = ptd->psv_temp;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pch_critical(struct thermal_zone_device *tzd)
|
||||
{
|
||||
dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzd_ops = {
|
||||
.get_temp = pch_thermal_get_temp,
|
||||
.get_trip_type = pch_get_trip_type,
|
||||
.get_trip_temp = pch_get_trip_temp,
|
||||
.critical = pch_critical,
|
||||
};
|
||||
|
||||
enum board_ids {
|
||||
board_hsw,
|
||||
board_wpt,
|
||||
board_skl,
|
||||
board_cnl,
|
||||
board_cml,
|
||||
board_lwb,
|
||||
};
|
||||
|
||||
static const struct board_info {
|
||||
const char *name;
|
||||
const struct pch_dev_ops *ops;
|
||||
} board_info[] = {
|
||||
[board_hsw] = {
|
||||
.name = "pch_haswell",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_wpt] = {
|
||||
.name = "pch_wildcat_point",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_skl] = {
|
||||
.name = "pch_skylake",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_cnl] = {
|
||||
.name = "pch_cannonlake",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_cml] = {
|
||||
.name = "pch_cometlake",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
[board_lwb] = {
|
||||
.name = "pch_lewisburg",
|
||||
.ops = &pch_dev_ops_wpt,
|
||||
},
|
||||
};
|
||||
|
||||
static int intel_pch_thermal_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
enum board_ids board_id = id->driver_data;
|
||||
const struct board_info *bi = &board_info[board_id];
|
||||
struct pch_thermal_device *ptd;
|
||||
int err;
|
||||
int nr_trips;
|
||||
|
||||
ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
|
||||
if (!ptd)
|
||||
return -ENOMEM;
|
||||
|
||||
ptd->ops = bi->ops;
|
||||
|
||||
pci_set_drvdata(pdev, ptd);
|
||||
ptd->pdev = pdev;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to enable pci device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, driver_name);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to request pci region\n");
|
||||
goto error_disable;
|
||||
}
|
||||
|
||||
ptd->hw_base = pci_ioremap_bar(pdev, 0);
|
||||
if (!ptd->hw_base) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&pdev->dev, "failed to map mem base\n");
|
||||
goto error_release;
|
||||
}
|
||||
|
||||
err = ptd->ops->hw_init(ptd, &nr_trips);
|
||||
if (err)
|
||||
goto error_cleanup;
|
||||
|
||||
ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
|
||||
&tzd_ops, NULL, 0, 0);
|
||||
if (IS_ERR(ptd->tzd)) {
|
||||
dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
|
||||
bi->name);
|
||||
err = PTR_ERR(ptd->tzd);
|
||||
goto error_cleanup;
|
||||
}
|
||||
err = thermal_zone_device_enable(ptd->tzd);
|
||||
if (err)
|
||||
goto err_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
thermal_zone_device_unregister(ptd->tzd);
|
||||
error_cleanup:
|
||||
iounmap(ptd->hw_base);
|
||||
error_release:
|
||||
pci_release_regions(pdev);
|
||||
error_disable:
|
||||
pci_disable_device(pdev);
|
||||
dev_err(&pdev->dev, "pci device failed to probe\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static void intel_pch_thermal_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
|
||||
|
||||
thermal_zone_device_unregister(ptd->tzd);
|
||||
iounmap(ptd->hw_base);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static int intel_pch_thermal_suspend_noirq(struct device *device)
|
||||
{
|
||||
struct pch_thermal_device *ptd = dev_get_drvdata(device);
|
||||
|
||||
return ptd->ops->suspend(ptd);
|
||||
}
|
||||
|
||||
static int intel_pch_thermal_resume(struct device *device)
|
||||
{
|
||||
struct pch_thermal_device *ptd = dev_get_drvdata(device);
|
||||
|
||||
return ptd->ops->resume(ptd);
|
||||
}
|
||||
|
||||
static const struct pci_device_id intel_pch_thermal_id[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
|
||||
.driver_data = board_hsw, },
|
||||
.driver_data = PCH_BOARD_HSW, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
|
||||
.driver_data = board_hsw, },
|
||||
.driver_data = PCH_BOARD_HSW, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
|
||||
.driver_data = board_wpt, },
|
||||
.driver_data = PCH_BOARD_WPT, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
|
||||
.driver_data = board_skl, },
|
||||
.driver_data = PCH_BOARD_SKL, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
|
||||
.driver_data = board_skl, },
|
||||
.driver_data = PCH_BOARD_SKL, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
|
||||
.driver_data = board_cnl, },
|
||||
.driver_data = PCH_BOARD_CNL, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
|
||||
.driver_data = board_cnl, },
|
||||
.driver_data = PCH_BOARD_CNL, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_LP),
|
||||
.driver_data = board_cnl, },
|
||||
.driver_data = PCH_BOARD_CNL, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
|
||||
.driver_data = board_cml, },
|
||||
.driver_data = PCH_BOARD_CML, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB),
|
||||
.driver_data = board_lwb, },
|
||||
.driver_data = PCH_BOARD_LWB, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WBG),
|
||||
.driver_data = PCH_BOARD_WBG, },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* intel_powerclamp.c - package c-state idle injection
|
||||
*
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
* Copyright (c) 2012-2023, Intel Corporation.
|
||||
*
|
||||
* Authors:
|
||||
* Arjan van de Ven <arjan@linux.intel.com>
|
||||
@ -27,23 +27,17 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/rt.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
#include <linux/idle_inject.h>
|
||||
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/mwait.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/hardirq.h>
|
||||
|
||||
#define MAX_TARGET_RATIO (50U)
|
||||
#define MAX_TARGET_RATIO (100U)
|
||||
/* For each undisturbed clamping period (no extra wake ups during idle time),
|
||||
* we increment the confidence counter for the given target ratio.
|
||||
* CONFIDENCE_OK defines the level where runtime calibration results are
|
||||
@ -57,37 +51,30 @@
|
||||
|
||||
static unsigned int target_mwait;
|
||||
static struct dentry *debug_dir;
|
||||
static bool poll_pkg_cstate_enable;
|
||||
|
||||
/* user selected target */
|
||||
static unsigned int set_target_ratio;
|
||||
/* Idle ratio observed using package C-state counters */
|
||||
static unsigned int current_ratio;
|
||||
|
||||
/* Skip the idle injection till set to true */
|
||||
static bool should_skip;
|
||||
|
||||
static unsigned int control_cpu; /* The cpu assigned to collect stat and update
|
||||
* control parameters. default to BSP but BSP
|
||||
* can be offlined.
|
||||
*/
|
||||
static bool clamping;
|
||||
|
||||
struct powerclamp_worker_data {
|
||||
struct kthread_worker *worker;
|
||||
struct kthread_work balancing_work;
|
||||
struct kthread_delayed_work idle_injection_work;
|
||||
struct powerclamp_data {
|
||||
unsigned int cpu;
|
||||
unsigned int count;
|
||||
unsigned int guard;
|
||||
unsigned int window_size_now;
|
||||
unsigned int target_ratio;
|
||||
unsigned int duration_jiffies;
|
||||
bool clamping;
|
||||
};
|
||||
|
||||
static struct powerclamp_worker_data __percpu *worker_data;
|
||||
static struct thermal_cooling_device *cooling_dev;
|
||||
static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu
|
||||
* clamping kthread worker
|
||||
*/
|
||||
static struct powerclamp_data powerclamp_data;
|
||||
|
||||
static struct thermal_cooling_device *cooling_dev;
|
||||
|
||||
static DEFINE_MUTEX(powerclamp_lock);
|
||||
|
||||
/* This duration is in microseconds */
|
||||
static unsigned int duration;
|
||||
static unsigned int pkg_cstate_ratio_cur;
|
||||
static unsigned int window_size;
|
||||
@ -104,24 +91,170 @@ static int duration_set(const char *arg, const struct kernel_param *kp)
|
||||
pr_err("Out of recommended range %lu, between 6-25ms\n",
|
||||
new_duration);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
duration = clamp(new_duration, 6ul, 25ul);
|
||||
smp_mb();
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
duration = clamp(new_duration, 6ul, 25ul) * 1000;
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
exit:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int duration_get(char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
ret = sysfs_emit(buf, "%d\n", duration / 1000);
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops duration_ops = {
|
||||
.set = duration_set,
|
||||
.get = duration_get,
|
||||
};
|
||||
|
||||
module_param_cb(duration, &duration_ops, NULL, 0644);
|
||||
MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
|
||||
|
||||
#define DEFAULT_MAX_IDLE 50
|
||||
#define MAX_ALL_CPU_IDLE 75
|
||||
|
||||
static u8 max_idle = DEFAULT_MAX_IDLE;
|
||||
|
||||
static cpumask_var_t idle_injection_cpu_mask;
|
||||
|
||||
static int allocate_copy_idle_injection_mask(const struct cpumask *copy_mask)
|
||||
{
|
||||
if (cpumask_available(idle_injection_cpu_mask))
|
||||
goto copy_mask;
|
||||
|
||||
/* This mask is allocated only one time and freed during module exit */
|
||||
if (!alloc_cpumask_var(&idle_injection_cpu_mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
copy_mask:
|
||||
cpumask_copy(idle_injection_cpu_mask, copy_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return true if the cpumask and idle percent combination is invalid */
|
||||
static bool check_invalid(cpumask_var_t mask, u8 idle)
|
||||
{
|
||||
if (cpumask_equal(cpu_present_mask, mask) && idle > MAX_ALL_CPU_IDLE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cpumask_set(const char *arg, const struct kernel_param *kp)
|
||||
{
|
||||
cpumask_var_t new_mask;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
|
||||
/* Can't set mask when cooling device is in use */
|
||||
if (powerclamp_data.clamping) {
|
||||
ret = -EAGAIN;
|
||||
goto skip_cpumask_set;
|
||||
}
|
||||
|
||||
ret = alloc_cpumask_var(&new_mask, GFP_KERNEL);
|
||||
if (!ret)
|
||||
goto skip_cpumask_set;
|
||||
|
||||
ret = bitmap_parse(arg, strlen(arg), cpumask_bits(new_mask),
|
||||
nr_cpumask_bits);
|
||||
if (ret)
|
||||
goto free_cpumask_set;
|
||||
|
||||
if (cpumask_empty(new_mask) || check_invalid(new_mask, max_idle)) {
|
||||
ret = -EINVAL;
|
||||
goto free_cpumask_set;
|
||||
}
|
||||
|
||||
/*
|
||||
* When module parameters are passed from kernel command line
|
||||
* during insmod, the module parameter callback is called
|
||||
* before powerclamp_init(), so we can't assume that some
|
||||
* cpumask can be allocated and copied before here. Also
|
||||
* in this case this cpumask is used as the default mask.
|
||||
*/
|
||||
ret = allocate_copy_idle_injection_mask(new_mask);
|
||||
|
||||
free_cpumask_set:
|
||||
free_cpumask_var(new_mask);
|
||||
skip_cpumask_set:
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpumask_get(char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
if (!cpumask_available(idle_injection_cpu_mask))
|
||||
return -ENODEV;
|
||||
|
||||
return bitmap_print_to_pagebuf(false, buf, cpumask_bits(idle_injection_cpu_mask),
|
||||
nr_cpumask_bits);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops cpumask_ops = {
|
||||
.set = cpumask_set,
|
||||
.get = cpumask_get,
|
||||
};
|
||||
|
||||
module_param_cb(cpumask, &cpumask_ops, NULL, 0644);
|
||||
MODULE_PARM_DESC(cpumask, "Mask of CPUs to use for idle injection.");
|
||||
|
||||
static int max_idle_set(const char *arg, const struct kernel_param *kp)
|
||||
{
|
||||
u8 new_max_idle;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
|
||||
/* Can't set mask when cooling device is in use */
|
||||
if (powerclamp_data.clamping) {
|
||||
ret = -EAGAIN;
|
||||
goto skip_limit_set;
|
||||
}
|
||||
|
||||
ret = kstrtou8(arg, 10, &new_max_idle);
|
||||
if (ret)
|
||||
goto skip_limit_set;
|
||||
|
||||
if (new_max_idle > MAX_TARGET_RATIO) {
|
||||
ret = -EINVAL;
|
||||
goto skip_limit_set;
|
||||
}
|
||||
|
||||
if (check_invalid(idle_injection_cpu_mask, new_max_idle)) {
|
||||
ret = -EINVAL;
|
||||
goto skip_limit_set;
|
||||
}
|
||||
|
||||
max_idle = new_max_idle;
|
||||
|
||||
skip_limit_set:
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops max_idle_ops = {
|
||||
.set = max_idle_set,
|
||||
.get = param_get_int,
|
||||
};
|
||||
|
||||
|
||||
module_param_cb(duration, &duration_ops, &duration, 0644);
|
||||
MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
|
||||
module_param_cb(max_idle, &max_idle_ops, &max_idle, 0644);
|
||||
MODULE_PARM_DESC(max_idle, "maximum injected idle time to the total CPU time ratio in percent range:1-100");
|
||||
|
||||
struct powerclamp_calibration_data {
|
||||
unsigned long confidence; /* used for calibration, basically a counter
|
||||
@ -261,6 +394,9 @@ static unsigned int get_compensation(int ratio)
|
||||
{
|
||||
unsigned int comp = 0;
|
||||
|
||||
if (!poll_pkg_cstate_enable)
|
||||
return 0;
|
||||
|
||||
/* we only use compensation if all adjacent ones are good */
|
||||
if (ratio == 1 &&
|
||||
cal_data[ratio].confidence >= CONFIDENCE_OK &&
|
||||
@ -302,7 +438,7 @@ static void adjust_compensation(int target_ratio, unsigned int win)
|
||||
if (d->confidence >= CONFIDENCE_OK)
|
||||
return;
|
||||
|
||||
delta = set_target_ratio - current_ratio;
|
||||
delta = powerclamp_data.target_ratio - current_ratio;
|
||||
/* filter out bad data */
|
||||
if (delta >= 0 && delta <= (1+target_ratio/10)) {
|
||||
if (d->steady_comp)
|
||||
@ -341,82 +477,39 @@ static bool powerclamp_adjust_controls(unsigned int target_ratio,
|
||||
adjust_compensation(target_ratio, win);
|
||||
|
||||
/* if we are above target+guard, skip */
|
||||
return set_target_ratio + guard <= current_ratio;
|
||||
return powerclamp_data.target_ratio + guard <= current_ratio;
|
||||
}
|
||||
|
||||
static void clamp_balancing_func(struct kthread_work *work)
|
||||
/*
|
||||
* This function calculates runtime from the current target ratio.
|
||||
* This function gets called under powerclamp_lock.
|
||||
*/
|
||||
static unsigned int get_run_time(void)
|
||||
{
|
||||
struct powerclamp_worker_data *w_data;
|
||||
int sleeptime;
|
||||
unsigned long target_jiffies;
|
||||
unsigned int compensated_ratio;
|
||||
int interval; /* jiffies to sleep for each attempt */
|
||||
|
||||
w_data = container_of(work, struct powerclamp_worker_data,
|
||||
balancing_work);
|
||||
unsigned int runtime;
|
||||
|
||||
/*
|
||||
* make sure user selected ratio does not take effect until
|
||||
* the next round. adjust target_ratio if user has changed
|
||||
* target such that we can converge quickly.
|
||||
*/
|
||||
w_data->target_ratio = READ_ONCE(set_target_ratio);
|
||||
w_data->guard = 1 + w_data->target_ratio / 20;
|
||||
w_data->window_size_now = window_size;
|
||||
w_data->duration_jiffies = msecs_to_jiffies(duration);
|
||||
w_data->count++;
|
||||
powerclamp_data.guard = 1 + powerclamp_data.target_ratio / 20;
|
||||
powerclamp_data.window_size_now = window_size;
|
||||
|
||||
/*
|
||||
* systems may have different ability to enter package level
|
||||
* c-states, thus we need to compensate the injected idle ratio
|
||||
* to achieve the actual target reported by the HW.
|
||||
*/
|
||||
compensated_ratio = w_data->target_ratio +
|
||||
get_compensation(w_data->target_ratio);
|
||||
compensated_ratio = powerclamp_data.target_ratio +
|
||||
get_compensation(powerclamp_data.target_ratio);
|
||||
if (compensated_ratio <= 0)
|
||||
compensated_ratio = 1;
|
||||
interval = w_data->duration_jiffies * 100 / compensated_ratio;
|
||||
|
||||
/* align idle time */
|
||||
target_jiffies = roundup(jiffies, interval);
|
||||
sleeptime = target_jiffies - jiffies;
|
||||
if (sleeptime <= 0)
|
||||
sleeptime = 1;
|
||||
runtime = duration * 100 / compensated_ratio - duration;
|
||||
|
||||
if (clamping && w_data->clamping && cpu_online(w_data->cpu))
|
||||
kthread_queue_delayed_work(w_data->worker,
|
||||
&w_data->idle_injection_work,
|
||||
sleeptime);
|
||||
}
|
||||
|
||||
static void clamp_idle_injection_func(struct kthread_work *work)
|
||||
{
|
||||
struct powerclamp_worker_data *w_data;
|
||||
|
||||
w_data = container_of(work, struct powerclamp_worker_data,
|
||||
idle_injection_work.work);
|
||||
|
||||
/*
|
||||
* only elected controlling cpu can collect stats and update
|
||||
* control parameters.
|
||||
*/
|
||||
if (w_data->cpu == control_cpu &&
|
||||
!(w_data->count % w_data->window_size_now)) {
|
||||
should_skip =
|
||||
powerclamp_adjust_controls(w_data->target_ratio,
|
||||
w_data->guard,
|
||||
w_data->window_size_now);
|
||||
smp_mb();
|
||||
}
|
||||
|
||||
if (should_skip)
|
||||
goto balance;
|
||||
|
||||
play_idle(jiffies_to_usecs(w_data->duration_jiffies));
|
||||
|
||||
balance:
|
||||
if (clamping && w_data->clamping && cpu_online(w_data->cpu))
|
||||
kthread_queue_work(w_data->worker, &w_data->balancing_work);
|
||||
return runtime;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -452,128 +545,131 @@ static void poll_pkg_cstate(struct work_struct *dummy)
|
||||
msr_last = msr_now;
|
||||
tsc_last = tsc_now;
|
||||
|
||||
if (true == clamping)
|
||||
mutex_lock(&powerclamp_lock);
|
||||
if (powerclamp_data.clamping)
|
||||
schedule_delayed_work(&poll_pkg_cstate_work, HZ);
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
}
|
||||
|
||||
static void start_power_clamp_worker(unsigned long cpu)
|
||||
{
|
||||
struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
|
||||
struct kthread_worker *worker;
|
||||
static struct idle_inject_device *ii_dev;
|
||||
|
||||
worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inj/%ld", cpu);
|
||||
if (IS_ERR(worker))
|
||||
/*
|
||||
* This function is called from idle injection core on timer expiry
|
||||
* for the run duration. This allows powerclamp to readjust or skip
|
||||
* injecting idle for this cycle.
|
||||
*/
|
||||
static bool idle_inject_update(void)
|
||||
{
|
||||
bool update = false;
|
||||
|
||||
/* We can't sleep in this callback */
|
||||
if (!mutex_trylock(&powerclamp_lock))
|
||||
return true;
|
||||
|
||||
if (!(powerclamp_data.count % powerclamp_data.window_size_now)) {
|
||||
|
||||
should_skip = powerclamp_adjust_controls(powerclamp_data.target_ratio,
|
||||
powerclamp_data.guard,
|
||||
powerclamp_data.window_size_now);
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
unsigned int runtime = get_run_time();
|
||||
|
||||
idle_inject_set_duration(ii_dev, runtime, duration);
|
||||
}
|
||||
|
||||
powerclamp_data.count++;
|
||||
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
if (should_skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This function starts idle injection by calling idle_inject_start() */
|
||||
static void trigger_idle_injection(void)
|
||||
{
|
||||
unsigned int runtime = get_run_time();
|
||||
|
||||
idle_inject_set_duration(ii_dev, runtime, duration);
|
||||
idle_inject_start(ii_dev);
|
||||
powerclamp_data.clamping = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from start_power_clamp() to register
|
||||
* CPUS with powercap idle injection register and set default
|
||||
* idle duration and latency.
|
||||
*/
|
||||
static int powerclamp_idle_injection_register(void)
|
||||
{
|
||||
poll_pkg_cstate_enable = false;
|
||||
if (cpumask_equal(cpu_present_mask, idle_injection_cpu_mask)) {
|
||||
ii_dev = idle_inject_register_full(idle_injection_cpu_mask, idle_inject_update);
|
||||
if (topology_max_packages() == 1 && topology_max_die_per_package() == 1)
|
||||
poll_pkg_cstate_enable = true;
|
||||
} else {
|
||||
ii_dev = idle_inject_register(idle_injection_cpu_mask);
|
||||
}
|
||||
|
||||
if (!ii_dev) {
|
||||
pr_err("powerclamp: idle_inject_register failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
idle_inject_set_duration(ii_dev, TICK_USEC, duration);
|
||||
idle_inject_set_latency(ii_dev, UINT_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from end_power_clamp() to stop idle injection
|
||||
* and unregister CPUS from powercap idle injection core.
|
||||
*/
|
||||
static void remove_idle_injection(void)
|
||||
{
|
||||
if (!powerclamp_data.clamping)
|
||||
return;
|
||||
|
||||
w_data->worker = worker;
|
||||
w_data->count = 0;
|
||||
w_data->cpu = cpu;
|
||||
w_data->clamping = true;
|
||||
set_bit(cpu, cpu_clamping_mask);
|
||||
sched_set_fifo(worker->task);
|
||||
kthread_init_work(&w_data->balancing_work, clamp_balancing_func);
|
||||
kthread_init_delayed_work(&w_data->idle_injection_work,
|
||||
clamp_idle_injection_func);
|
||||
kthread_queue_work(w_data->worker, &w_data->balancing_work);
|
||||
}
|
||||
|
||||
static void stop_power_clamp_worker(unsigned long cpu)
|
||||
{
|
||||
struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
|
||||
|
||||
if (!w_data->worker)
|
||||
return;
|
||||
|
||||
w_data->clamping = false;
|
||||
/*
|
||||
* Make sure that all works that get queued after this point see
|
||||
* the clamping disabled. The counter part is not needed because
|
||||
* there is an implicit memory barrier when the queued work
|
||||
* is proceed.
|
||||
*/
|
||||
smp_wmb();
|
||||
kthread_cancel_work_sync(&w_data->balancing_work);
|
||||
kthread_cancel_delayed_work_sync(&w_data->idle_injection_work);
|
||||
/*
|
||||
* The balancing work still might be queued here because
|
||||
* the handling of the "clapming" variable, cancel, and queue
|
||||
* operations are not synchronized via a lock. But it is not
|
||||
* a big deal. The balancing work is fast and destroy kthread
|
||||
* will wait for it.
|
||||
*/
|
||||
clear_bit(w_data->cpu, cpu_clamping_mask);
|
||||
kthread_destroy_worker(w_data->worker);
|
||||
|
||||
w_data->worker = NULL;
|
||||
powerclamp_data.clamping = false;
|
||||
idle_inject_stop(ii_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when user change the cooling device
|
||||
* state from zero to some other value.
|
||||
*/
|
||||
static int start_power_clamp(void)
|
||||
{
|
||||
unsigned long cpu;
|
||||
int ret;
|
||||
|
||||
set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
|
||||
/* prevent cpu hotplug */
|
||||
cpus_read_lock();
|
||||
|
||||
/* prefer BSP */
|
||||
control_cpu = cpumask_first(cpu_online_mask);
|
||||
|
||||
clamping = true;
|
||||
schedule_delayed_work(&poll_pkg_cstate_work, 0);
|
||||
|
||||
/* start one kthread worker per online cpu */
|
||||
for_each_online_cpu(cpu) {
|
||||
start_power_clamp_worker(cpu);
|
||||
ret = powerclamp_idle_injection_register();
|
||||
if (!ret) {
|
||||
trigger_idle_injection();
|
||||
if (poll_pkg_cstate_enable)
|
||||
schedule_delayed_work(&poll_pkg_cstate_work, 0);
|
||||
}
|
||||
cpus_read_unlock();
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when user change the cooling device
|
||||
* state from non zero value zero.
|
||||
*/
|
||||
static void end_power_clamp(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Block requeuing in all the kthread workers. They will flush and
|
||||
* stop faster.
|
||||
*/
|
||||
clamping = false;
|
||||
for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) {
|
||||
pr_debug("clamping worker for cpu %d alive, destroy\n", i);
|
||||
stop_power_clamp_worker(i);
|
||||
if (powerclamp_data.clamping) {
|
||||
remove_idle_injection();
|
||||
idle_inject_unregister(ii_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int powerclamp_cpu_online(unsigned int cpu)
|
||||
{
|
||||
if (clamping == false)
|
||||
return 0;
|
||||
start_power_clamp_worker(cpu);
|
||||
/* prefer BSP as controlling CPU */
|
||||
if (cpu == 0) {
|
||||
control_cpu = 0;
|
||||
smp_mb();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int powerclamp_cpu_predown(unsigned int cpu)
|
||||
{
|
||||
if (clamping == false)
|
||||
return 0;
|
||||
|
||||
stop_power_clamp_worker(cpu);
|
||||
if (cpu != control_cpu)
|
||||
return 0;
|
||||
|
||||
control_cpu = cpumask_first(cpu_online_mask);
|
||||
if (control_cpu == cpu)
|
||||
control_cpu = cpumask_next(cpu, cpu_online_mask);
|
||||
smp_mb();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
@ -585,11 +681,9 @@ static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
|
||||
static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
if (true == clamping)
|
||||
*state = pkg_cstate_ratio_cur;
|
||||
else
|
||||
/* to save power, do not poll idle ratio while not clamping */
|
||||
*state = -1; /* indicates invalid state */
|
||||
mutex_lock(&powerclamp_lock);
|
||||
*state = powerclamp_data.target_ratio;
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -599,24 +693,32 @@ static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
|
||||
new_target_ratio = clamp(new_target_ratio, 0UL,
|
||||
(unsigned long) (MAX_TARGET_RATIO-1));
|
||||
if (set_target_ratio == 0 && new_target_ratio > 0) {
|
||||
(unsigned long) (max_idle - 1));
|
||||
if (!powerclamp_data.target_ratio && new_target_ratio > 0) {
|
||||
pr_info("Start idle injection to reduce power\n");
|
||||
set_target_ratio = new_target_ratio;
|
||||
powerclamp_data.target_ratio = new_target_ratio;
|
||||
ret = start_power_clamp();
|
||||
if (ret)
|
||||
powerclamp_data.target_ratio = 0;
|
||||
goto exit_set;
|
||||
} else if (set_target_ratio > 0 && new_target_ratio == 0) {
|
||||
} else if (powerclamp_data.target_ratio > 0 && new_target_ratio == 0) {
|
||||
pr_info("Stop forced idle injection\n");
|
||||
end_power_clamp();
|
||||
set_target_ratio = 0;
|
||||
powerclamp_data.target_ratio = 0;
|
||||
} else /* adjust currently running */ {
|
||||
set_target_ratio = new_target_ratio;
|
||||
/* make new set_target_ratio visible to other cpus */
|
||||
smp_mb();
|
||||
unsigned int runtime;
|
||||
|
||||
powerclamp_data.target_ratio = new_target_ratio;
|
||||
runtime = get_run_time();
|
||||
idle_inject_set_duration(ii_dev, runtime, duration);
|
||||
}
|
||||
|
||||
exit_set:
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -657,7 +759,6 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
seq_printf(m, "controlling cpu: %d\n", control_cpu);
|
||||
seq_printf(m, "pct confidence steady dynamic (compensation)\n");
|
||||
for (i = 0; i < MAX_TARGET_RATIO; i++) {
|
||||
seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
|
||||
@ -680,75 +781,57 @@ static inline void powerclamp_create_debug_files(void)
|
||||
&powerclamp_debug_fops);
|
||||
}
|
||||
|
||||
static enum cpuhp_state hp_state;
|
||||
|
||||
static int __init powerclamp_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
cpu_clamping_mask = bitmap_zalloc(num_possible_cpus(), GFP_KERNEL);
|
||||
if (!cpu_clamping_mask)
|
||||
return -ENOMEM;
|
||||
|
||||
/* probe cpu features and ids here */
|
||||
retval = powerclamp_probe();
|
||||
if (retval)
|
||||
goto exit_free;
|
||||
return retval;
|
||||
|
||||
mutex_lock(&powerclamp_lock);
|
||||
retval = allocate_copy_idle_injection_mask(cpu_present_mask);
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* set default limit, maybe adjusted during runtime based on feedback */
|
||||
window_size = 2;
|
||||
retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
|
||||
"thermal/intel_powerclamp:online",
|
||||
powerclamp_cpu_online,
|
||||
powerclamp_cpu_predown);
|
||||
if (retval < 0)
|
||||
goto exit_free;
|
||||
|
||||
hp_state = retval;
|
||||
|
||||
worker_data = alloc_percpu(struct powerclamp_worker_data);
|
||||
if (!worker_data) {
|
||||
retval = -ENOMEM;
|
||||
goto exit_unregister;
|
||||
}
|
||||
|
||||
cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
|
||||
&powerclamp_cooling_ops);
|
||||
if (IS_ERR(cooling_dev)) {
|
||||
retval = -ENODEV;
|
||||
goto exit_free_thread;
|
||||
}
|
||||
&powerclamp_cooling_ops);
|
||||
if (IS_ERR(cooling_dev))
|
||||
return -ENODEV;
|
||||
|
||||
if (!duration)
|
||||
duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES);
|
||||
duration = jiffies_to_usecs(DEFAULT_DURATION_JIFFIES);
|
||||
|
||||
powerclamp_create_debug_files();
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_thread:
|
||||
free_percpu(worker_data);
|
||||
exit_unregister:
|
||||
cpuhp_remove_state_nocalls(hp_state);
|
||||
exit_free:
|
||||
bitmap_free(cpu_clamping_mask);
|
||||
return retval;
|
||||
}
|
||||
module_init(powerclamp_init);
|
||||
|
||||
static void __exit powerclamp_exit(void)
|
||||
{
|
||||
mutex_lock(&powerclamp_lock);
|
||||
end_power_clamp();
|
||||
cpuhp_remove_state_nocalls(hp_state);
|
||||
free_percpu(worker_data);
|
||||
mutex_unlock(&powerclamp_lock);
|
||||
|
||||
thermal_cooling_device_unregister(cooling_dev);
|
||||
bitmap_free(cpu_clamping_mask);
|
||||
|
||||
cancel_delayed_work_sync(&poll_pkg_cstate_work);
|
||||
debugfs_remove_recursive(debug_dir);
|
||||
|
||||
if (cpumask_available(idle_injection_cpu_mask))
|
||||
free_cpumask_var(idle_injection_cpu_mask);
|
||||
}
|
||||
module_exit(powerclamp_exit);
|
||||
|
||||
MODULE_IMPORT_NS(IDLE_INJECT);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
|
||||
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
|
||||
|
@ -84,6 +84,7 @@
|
||||
#define QRK_DTS_MASK_TP_THRES 0xFF
|
||||
#define QRK_DTS_SHIFT_TP 8
|
||||
#define QRK_DTS_ID_TP_CRITICAL 0
|
||||
#define QRK_DTS_ID_TP_HOT 1
|
||||
#define QRK_DTS_SAFE_TP_THRES 105
|
||||
|
||||
/* Thermal Sensor Register Lock */
|
||||
@ -104,6 +105,7 @@ struct soc_sensor_entry {
|
||||
u32 store_ptps;
|
||||
u32 store_dts_enable;
|
||||
struct thermal_zone_device *tzone;
|
||||
struct thermal_trip trips[QRK_MAX_DTS_TRIPS];
|
||||
};
|
||||
|
||||
static struct soc_sensor_entry *soc_dts;
|
||||
@ -172,9 +174,9 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _get_trip_temp(int trip, int *temp)
|
||||
static int get_trip_temp(int trip)
|
||||
{
|
||||
int status;
|
||||
int status, temp;
|
||||
u32 out;
|
||||
|
||||
mutex_lock(&dts_update_mutex);
|
||||
@ -183,7 +185,7 @@ static int _get_trip_temp(int trip, int *temp)
|
||||
mutex_unlock(&dts_update_mutex);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
return THERMAL_TEMP_INVALID;
|
||||
|
||||
/*
|
||||
* Thermal Sensor Programmable Trip Point Register has 8-bit
|
||||
@ -191,21 +193,10 @@ static int _get_trip_temp(int trip, int *temp)
|
||||
* thresholds. The threshold value is always offset by its
|
||||
* temperature base (50 degree Celsius).
|
||||
*/
|
||||
*temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
|
||||
*temp -= QRK_DTS_TEMP_BASE;
|
||||
temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
|
||||
temp -= QRK_DTS_TEMP_BASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
|
||||
int trip, int *temp)
|
||||
{
|
||||
return _get_trip_temp(trip, temp);
|
||||
}
|
||||
|
||||
static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
static int update_trip_temp(struct soc_sensor_entry *aux_entry,
|
||||
@ -262,17 +253,6 @@ static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
return update_trip_temp(tzd->devdata, trip, temp);
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
if (trip)
|
||||
*type = THERMAL_TRIP_HOT;
|
||||
else
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzd,
|
||||
int *temp)
|
||||
{
|
||||
@ -315,10 +295,7 @@ static int sys_change_mode(struct thermal_zone_device *tzd,
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_crit_temp = sys_get_crit_temp,
|
||||
.change_mode = sys_change_mode,
|
||||
};
|
||||
|
||||
@ -385,10 +362,18 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
aux_entry->tzone = thermal_zone_device_register("quark_dts",
|
||||
QRK_MAX_DTS_TRIPS,
|
||||
wr_mask,
|
||||
aux_entry, &tzone_ops, NULL, 0, polling_delay);
|
||||
aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].temperature = get_trip_temp(QRK_DTS_ID_TP_CRITICAL);
|
||||
aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].type = THERMAL_TRIP_CRITICAL;
|
||||
|
||||
aux_entry->trips[QRK_DTS_ID_TP_HOT].temperature = get_trip_temp(QRK_DTS_ID_TP_HOT);
|
||||
aux_entry->trips[QRK_DTS_ID_TP_HOT].type = THERMAL_TRIP_HOT;
|
||||
|
||||
aux_entry->tzone = thermal_zone_device_register_with_trips("quark_dts",
|
||||
aux_entry->trips,
|
||||
QRK_MAX_DTS_TRIPS,
|
||||
wr_mask,
|
||||
aux_entry, &tzone_ops,
|
||||
NULL, 0, polling_delay);
|
||||
if (IS_ERR(aux_entry->tzone)) {
|
||||
err = PTR_ERR(aux_entry->tzone);
|
||||
goto err_ret;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/intel_tcc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -45,32 +46,6 @@
|
||||
/* DTS0 and DTS 1 */
|
||||
#define SOC_MAX_DTS_SENSORS 2
|
||||
|
||||
static int get_tj_max(u32 *tj_max)
|
||||
{
|
||||
u32 eax, edx;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
||||
if (err)
|
||||
goto err_ret;
|
||||
else {
|
||||
val = (eax >> 16) & 0xff;
|
||||
if (val)
|
||||
*tj_max = val * 1000;
|
||||
else {
|
||||
err = -EINVAL;
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_ret:
|
||||
*tj_max = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
|
||||
int *temp)
|
||||
{
|
||||
@ -405,7 +380,7 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
|
||||
{
|
||||
struct intel_soc_dts_sensors *sensors;
|
||||
bool notification;
|
||||
u32 tj_max;
|
||||
int tj_max;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -415,8 +390,9 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
|
||||
if (!trip_count || read_only_trip_count > trip_count)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (get_tj_max(&tj_max))
|
||||
return ERR_PTR(-EINVAL);
|
||||
tj_max = intel_tcc_get_tjmax(-1);
|
||||
if (tj_max < 0)
|
||||
return ERR_PTR(tj_max);
|
||||
|
||||
sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
|
||||
if (!sensors)
|
||||
@ -475,4 +451,5 @@ void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit);
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TCC);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
139
drivers/thermal/intel/intel_tcc.c
Normal file
139
drivers/thermal/intel/intel_tcc.c
Normal file
@ -0,0 +1,139 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* intel_tcc.c - Library for Intel TCC (thermal control circuitry) MSR access
|
||||
* Copyright (c) 2022, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/intel_tcc.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
/**
|
||||
* intel_tcc_get_tjmax() - returns the default TCC activation Temperature
|
||||
* @cpu: cpu that the MSR should be run on, nagative value means any cpu.
|
||||
*
|
||||
* Get the TjMax value, which is the default thermal throttling or TCC
|
||||
* activation temperature in degrees C.
|
||||
*
|
||||
* Return: Tjmax value in degrees C on success, negative error code otherwise.
|
||||
*/
|
||||
int intel_tcc_get_tjmax(int cpu)
|
||||
{
|
||||
u32 low, high;
|
||||
int val, err;
|
||||
|
||||
if (cpu < 0)
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
else
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (low >> 16) & 0xff;
|
||||
|
||||
return val ? val : -ENODATA;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, INTEL_TCC);
|
||||
|
||||
/**
|
||||
* intel_tcc_get_offset() - returns the TCC Offset value to Tjmax
|
||||
* @cpu: cpu that the MSR should be run on, nagative value means any cpu.
|
||||
*
|
||||
* Get the TCC offset value to Tjmax. The effective thermal throttling or TCC
|
||||
* activation temperature equals "Tjmax" - "TCC Offset", in degrees C.
|
||||
*
|
||||
* Return: Tcc offset value in degrees C on success, negative error code otherwise.
|
||||
*/
|
||||
int intel_tcc_get_offset(int cpu)
|
||||
{
|
||||
u32 low, high;
|
||||
int err;
|
||||
|
||||
if (cpu < 0)
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
else
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return (low >> 24) & 0x3f;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, INTEL_TCC);
|
||||
|
||||
/**
|
||||
* intel_tcc_set_offset() - set the TCC offset value to Tjmax
|
||||
* @cpu: cpu that the MSR should be run on, nagative value means any cpu.
|
||||
* @offset: TCC offset value in degree C
|
||||
*
|
||||
* Set the TCC Offset value to Tjmax. The effective thermal throttling or TCC
|
||||
* activation temperature equals "Tjmax" - "TCC Offset", in degree C.
|
||||
*
|
||||
* Return: On success returns 0, negative error code otherwise.
|
||||
*/
|
||||
|
||||
int intel_tcc_set_offset(int cpu, int offset)
|
||||
{
|
||||
u32 low, high;
|
||||
int err;
|
||||
|
||||
if (offset < 0 || offset > 0x3f)
|
||||
return -EINVAL;
|
||||
|
||||
if (cpu < 0)
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
else
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* MSR Locked */
|
||||
if (low & BIT(31))
|
||||
return -EPERM;
|
||||
|
||||
low &= ~(0x3f << 24);
|
||||
low |= offset << 24;
|
||||
|
||||
if (cpu < 0)
|
||||
return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, low, high);
|
||||
else
|
||||
return wrmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, low, high);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, INTEL_TCC);
|
||||
|
||||
/**
|
||||
* intel_tcc_get_temp() - returns the current temperature
|
||||
* @cpu: cpu that the MSR should be run on, nagative value means any cpu.
|
||||
* @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor.
|
||||
*
|
||||
* Get the current temperature returned by the CPU core/package level
|
||||
* thermal sensor, in degrees C.
|
||||
*
|
||||
* Return: Temperature in degrees C on success, negative error code otherwise.
|
||||
*/
|
||||
int intel_tcc_get_temp(int cpu, bool pkg)
|
||||
{
|
||||
u32 low, high;
|
||||
u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS;
|
||||
int tjmax, temp, err;
|
||||
|
||||
tjmax = intel_tcc_get_tjmax(cpu);
|
||||
if (tjmax < 0)
|
||||
return tjmax;
|
||||
|
||||
if (cpu < 0)
|
||||
err = rdmsr_safe(msr, &low, &high);
|
||||
else
|
||||
err = rdmsr_safe_on_cpu(cpu, msr, &low, &high);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Temperature is beyond the valid thermal sensor range */
|
||||
if (!(low & BIT(31)))
|
||||
return -ENODATA;
|
||||
|
||||
temp = tjmax - ((low >> 16) & 0x7f);
|
||||
|
||||
/* Do not allow negative CPU temperature */
|
||||
return temp >= 0 ? temp : -ENODATA;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(intel_tcc_get_temp, INTEL_TCC);
|
@ -7,12 +7,11 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/intel_tcc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#define TCC_SHIFT 24
|
||||
#define TCC_MASK (0x3fULL<<24)
|
||||
#define TCC_PROGRAMMABLE BIT(30)
|
||||
#define TCC_LOCKED BIT(31)
|
||||
|
||||
@ -21,47 +20,26 @@ static struct thermal_cooling_device *tcc_cdev;
|
||||
static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
*state)
|
||||
{
|
||||
*state = TCC_MASK >> TCC_SHIFT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcc_offset_update(int tcc)
|
||||
{
|
||||
u64 val;
|
||||
int err;
|
||||
|
||||
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val &= ~TCC_MASK;
|
||||
val |= tcc << TCC_SHIFT;
|
||||
|
||||
err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*state = 0x3f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
*state)
|
||||
{
|
||||
u64 val;
|
||||
int err;
|
||||
int offset = intel_tcc_get_offset(-1);
|
||||
|
||||
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
|
||||
if (err)
|
||||
return err;
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
|
||||
*state = (val & TCC_MASK) >> TCC_SHIFT;
|
||||
*state = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
state)
|
||||
{
|
||||
return tcc_offset_update(state);
|
||||
return intel_tcc_set_offset(-1, (int)state);
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops tcc_cooling_ops = {
|
||||
@ -140,6 +118,7 @@ static void __exit tcc_cooling_exit(void)
|
||||
|
||||
module_exit(tcc_cooling_exit)
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TCC);
|
||||
MODULE_DESCRIPTION("TCC offset cooling device Driver");
|
||||
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/intel_tcc.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/device.h>
|
||||
@ -48,11 +49,11 @@ MODULE_PARM_DESC(notify_delay_ms,
|
||||
struct zone_device {
|
||||
int cpu;
|
||||
bool work_scheduled;
|
||||
u32 tj_max;
|
||||
u32 msr_pkg_therm_low;
|
||||
u32 msr_pkg_therm_high;
|
||||
struct delayed_work work;
|
||||
struct thermal_zone_device *tzone;
|
||||
struct thermal_trip *trips;
|
||||
struct cpumask cpumask;
|
||||
};
|
||||
|
||||
@ -104,71 +105,17 @@ static struct zone_device *pkg_temp_thermal_get_dev(unsigned int cpu)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* tj-max is interesting because threshold is set relative to this
|
||||
* temperature.
|
||||
*/
|
||||
static int get_tj_max(int cpu, u32 *tj_max)
|
||||
{
|
||||
u32 eax, edx, val;
|
||||
int err;
|
||||
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (eax >> 16) & 0xff;
|
||||
*tj_max = val * 1000;
|
||||
|
||||
return val ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
struct zone_device *zonedev = tzd->devdata;
|
||||
u32 eax, edx;
|
||||
int val;
|
||||
|
||||
rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_STATUS,
|
||||
&eax, &edx);
|
||||
if (eax & 0x80000000) {
|
||||
*temp = zonedev->tj_max - ((eax >> 16) & 0x7f) * 1000;
|
||||
pr_debug("sys_get_curr_temp %d\n", *temp);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzd,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct zone_device *zonedev = tzd->devdata;
|
||||
unsigned long thres_reg_value;
|
||||
u32 mask, shift, eax, edx;
|
||||
int ret;
|
||||
|
||||
if (trip >= MAX_NUMBER_OF_TRIPS)
|
||||
return -EINVAL;
|
||||
|
||||
if (trip) {
|
||||
mask = THERM_MASK_THRESHOLD1;
|
||||
shift = THERM_SHIFT_THRESHOLD1;
|
||||
} else {
|
||||
mask = THERM_MASK_THRESHOLD0;
|
||||
shift = THERM_SHIFT_THRESHOLD0;
|
||||
}
|
||||
|
||||
ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
|
||||
&eax, &edx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
thres_reg_value = (eax & mask) >> shift;
|
||||
if (thres_reg_value)
|
||||
*temp = zonedev->tj_max - thres_reg_value * 1000;
|
||||
else
|
||||
*temp = THERMAL_TEMP_INVALID;
|
||||
pr_debug("sys_get_trip_temp %d\n", *temp);
|
||||
val = intel_tcc_get_temp(zonedev->cpu, true);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
*temp = val * 1000;
|
||||
pr_debug("sys_get_curr_temp %d\n", *temp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -177,9 +124,14 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
|
||||
{
|
||||
struct zone_device *zonedev = tzd->devdata;
|
||||
u32 l, h, mask, shift, intr;
|
||||
int ret;
|
||||
int tj_max, ret;
|
||||
|
||||
if (trip >= MAX_NUMBER_OF_TRIPS || temp >= zonedev->tj_max)
|
||||
tj_max = intel_tcc_get_tjmax(zonedev->cpu);
|
||||
if (tj_max < 0)
|
||||
return tj_max;
|
||||
tj_max *= 1000;
|
||||
|
||||
if (trip >= MAX_NUMBER_OF_TRIPS || temp >= tj_max)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
|
||||
@ -204,7 +156,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
|
||||
if (!temp) {
|
||||
l &= ~intr;
|
||||
} else {
|
||||
l |= (zonedev->tj_max - temp)/1000 << shift;
|
||||
l |= (tj_max - temp)/1000 << shift;
|
||||
l |= intr;
|
||||
}
|
||||
|
||||
@ -212,18 +164,9 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
|
||||
l, h);
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Thermal zone callback registry */
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
};
|
||||
|
||||
@ -323,12 +266,55 @@ static int pkg_thermal_notify(u64 msr_val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_trip *pkg_temp_thermal_trips_init(int cpu, int tj_max, int num_trips)
|
||||
{
|
||||
struct thermal_trip *trips;
|
||||
unsigned long thres_reg_value;
|
||||
u32 mask, shift, eax, edx;
|
||||
int ret, i;
|
||||
|
||||
trips = kzalloc(sizeof(*trips) * num_trips, GFP_KERNEL);
|
||||
if (!trips)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < num_trips; i++) {
|
||||
|
||||
if (i) {
|
||||
mask = THERM_MASK_THRESHOLD1;
|
||||
shift = THERM_SHIFT_THRESHOLD1;
|
||||
} else {
|
||||
mask = THERM_MASK_THRESHOLD0;
|
||||
shift = THERM_SHIFT_THRESHOLD0;
|
||||
}
|
||||
|
||||
ret = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
|
||||
&eax, &edx);
|
||||
if (ret < 0) {
|
||||
kfree(trips);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
thres_reg_value = (eax & mask) >> shift;
|
||||
|
||||
trips[i].temperature = thres_reg_value ?
|
||||
tj_max - thres_reg_value * 1000 : THERMAL_TEMP_INVALID;
|
||||
|
||||
trips[i].type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
pr_debug("%s: cpu=%d, trip=%d, temp=%d\n",
|
||||
__func__, cpu, i, trips[i].temperature);
|
||||
}
|
||||
|
||||
return trips;
|
||||
}
|
||||
|
||||
static int pkg_temp_thermal_device_add(unsigned int cpu)
|
||||
{
|
||||
int id = topology_logical_die_id(cpu);
|
||||
u32 tj_max, eax, ebx, ecx, edx;
|
||||
u32 eax, ebx, ecx, edx;
|
||||
struct zone_device *zonedev;
|
||||
int thres_count, err;
|
||||
int tj_max;
|
||||
|
||||
if (id >= max_id)
|
||||
return -ENOMEM;
|
||||
@ -340,32 +326,34 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
|
||||
|
||||
thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
|
||||
|
||||
err = get_tj_max(cpu, &tj_max);
|
||||
if (err)
|
||||
return err;
|
||||
tj_max = intel_tcc_get_tjmax(cpu);
|
||||
if (tj_max < 0)
|
||||
return tj_max;
|
||||
|
||||
zonedev = kzalloc(sizeof(*zonedev), GFP_KERNEL);
|
||||
if (!zonedev)
|
||||
return -ENOMEM;
|
||||
|
||||
zonedev->trips = pkg_temp_thermal_trips_init(cpu, tj_max, thres_count);
|
||||
if (IS_ERR(zonedev->trips)) {
|
||||
err = PTR_ERR(zonedev->trips);
|
||||
goto out_kfree_zonedev;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&zonedev->work, pkg_temp_thermal_threshold_work_fn);
|
||||
zonedev->cpu = cpu;
|
||||
zonedev->tj_max = tj_max;
|
||||
zonedev->tzone = thermal_zone_device_register("x86_pkg_temp",
|
||||
thres_count,
|
||||
zonedev->tzone = thermal_zone_device_register_with_trips("x86_pkg_temp",
|
||||
zonedev->trips, thres_count,
|
||||
(thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
|
||||
zonedev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
|
||||
if (IS_ERR(zonedev->tzone)) {
|
||||
err = PTR_ERR(zonedev->tzone);
|
||||
kfree(zonedev);
|
||||
return err;
|
||||
goto out_kfree_trips;
|
||||
}
|
||||
err = thermal_zone_device_enable(zonedev->tzone);
|
||||
if (err) {
|
||||
thermal_zone_device_unregister(zonedev->tzone);
|
||||
kfree(zonedev);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
goto out_unregister_tz;
|
||||
|
||||
/* Store MSR value for package thermal interrupt, to restore at exit */
|
||||
rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, zonedev->msr_pkg_therm_low,
|
||||
zonedev->msr_pkg_therm_high);
|
||||
@ -374,7 +362,16 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
|
||||
raw_spin_lock_irq(&pkg_temp_lock);
|
||||
zones[id] = zonedev;
|
||||
raw_spin_unlock_irq(&pkg_temp_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister_tz:
|
||||
thermal_zone_device_unregister(zonedev->tzone);
|
||||
out_kfree_trips:
|
||||
kfree(zonedev->trips);
|
||||
out_kfree_zonedev:
|
||||
kfree(zonedev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pkg_thermal_cpu_offline(unsigned int cpu)
|
||||
@ -458,8 +455,10 @@ static int pkg_thermal_cpu_offline(unsigned int cpu)
|
||||
raw_spin_unlock_irq(&pkg_temp_lock);
|
||||
|
||||
/* Final cleanup if this is the last cpu */
|
||||
if (lastcpu)
|
||||
if (lastcpu) {
|
||||
kfree(zonedev->trips);
|
||||
kfree(zonedev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -531,6 +530,7 @@ static void __exit pkg_temp_thermal_exit(void)
|
||||
}
|
||||
module_exit(pkg_temp_thermal_exit)
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TCC);
|
||||
MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -64,15 +64,13 @@ static int kirkwood_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_zone_device *thermal = NULL;
|
||||
struct kirkwood_thermal_priv *priv;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->sensor = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->sensor = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(priv->sensor))
|
||||
return PTR_ERR(priv->sensor);
|
||||
|
||||
|
37
drivers/thermal/mediatek/Kconfig
Normal file
37
drivers/thermal/mediatek/Kconfig
Normal file
@ -0,0 +1,37 @@
|
||||
config MTK_THERMAL
|
||||
tristate "MediaTek thermal drivers"
|
||||
depends on THERMAL_OF
|
||||
help
|
||||
This is the option for MediaTek thermal software solutions.
|
||||
Please enable corresponding options to get temperature
|
||||
information from thermal sensors or turn on throttle
|
||||
mechaisms for thermal mitigation.
|
||||
|
||||
if MTK_THERMAL
|
||||
|
||||
config MTK_SOC_THERMAL
|
||||
tristate "AUXADC temperature sensor driver for MediaTek SoCs"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enable this option if you want to get SoC temperature
|
||||
information for MediaTek platforms.
|
||||
This driver configures thermal controllers to collect
|
||||
temperature via AUXADC interface.
|
||||
|
||||
config MTK_LVTS_THERMAL
|
||||
tristate "LVTS Thermal Driver for MediaTek SoCs"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Enable this option if you want to get SoC temperature
|
||||
information for supported MediaTek platforms.
|
||||
This driver configures LVTS (Low Voltage Thermal Sensor)
|
||||
thermal controllers to collect temperatures via ASIF
|
||||
(Analog Serial Interface).
|
||||
|
||||
config MTK_LVTS_THERMAL_DEBUGFS
|
||||
bool "LVTS thermal debugfs"
|
||||
depends on MTK_LVTS_THERMAL && DEBUG_FS
|
||||
help
|
||||
Enable this option to debug the internals of the device driver.
|
||||
|
||||
endif
|
2
drivers/thermal/mediatek/Makefile
Normal file
2
drivers/thermal/mediatek/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_MTK_SOC_THERMAL) += auxadc_thermal.o
|
||||
obj-$(CONFIG_MTK_LVTS_THERMAL) += lvts_thermal.o
|
@ -23,7 +23,7 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "thermal_hwmon.h"
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
/* AUXADC Registers */
|
||||
#define AUXADC_CON1_SET_V 0x008
|
||||
@ -150,6 +150,20 @@
|
||||
#define CALIB_BUF1_VALID_V2(x) (((x) >> 4) & 0x1)
|
||||
#define CALIB_BUF1_O_SLOPE_SIGN_V2(x) (((x) >> 3) & 0x1)
|
||||
|
||||
/*
|
||||
* Layout of the fuses providing the calibration data
|
||||
* These macros can be used for MT7981 and MT7986.
|
||||
*/
|
||||
#define CALIB_BUF0_ADC_GE_V3(x) (((x) >> 0) & 0x3ff)
|
||||
#define CALIB_BUF0_DEGC_CALI_V3(x) (((x) >> 20) & 0x3f)
|
||||
#define CALIB_BUF0_O_SLOPE_V3(x) (((x) >> 26) & 0x3f)
|
||||
#define CALIB_BUF1_VTS_TS1_V3(x) (((x) >> 0) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TS2_V3(x) (((x) >> 21) & 0x1ff)
|
||||
#define CALIB_BUF1_VTS_TSABB_V3(x) (((x) >> 9) & 0x1ff)
|
||||
#define CALIB_BUF1_VALID_V3(x) (((x) >> 18) & 0x1)
|
||||
#define CALIB_BUF1_O_SLOPE_SIGN_V3(x) (((x) >> 19) & 0x1)
|
||||
#define CALIB_BUF1_ID_V3(x) (((x) >> 20) & 0x1)
|
||||
|
||||
enum {
|
||||
VTS1,
|
||||
VTS2,
|
||||
@ -163,6 +177,7 @@ enum {
|
||||
enum mtk_thermal_version {
|
||||
MTK_THERMAL_V1 = 1,
|
||||
MTK_THERMAL_V2,
|
||||
MTK_THERMAL_V3,
|
||||
};
|
||||
|
||||
/* MT2701 thermal sensors */
|
||||
@ -245,6 +260,27 @@ enum mtk_thermal_version {
|
||||
/* The calibration coefficient of sensor */
|
||||
#define MT8183_CALIBRATION 153
|
||||
|
||||
/* AUXADC channel 11 is used for the temperature sensors */
|
||||
#define MT7986_TEMP_AUXADC_CHANNEL 11
|
||||
|
||||
/* The total number of temperature sensors in the MT7986 */
|
||||
#define MT7986_NUM_SENSORS 1
|
||||
|
||||
/* The number of banks in the MT7986 */
|
||||
#define MT7986_NUM_ZONES 1
|
||||
|
||||
/* The number of sensing points per bank */
|
||||
#define MT7986_NUM_SENSORS_PER_ZONE 1
|
||||
|
||||
/* MT7986 thermal sensors */
|
||||
#define MT7986_TS1 0
|
||||
|
||||
/* The number of controller in the MT7986 */
|
||||
#define MT7986_NUM_CONTROLLER 1
|
||||
|
||||
/* The calibration coefficient of sensor */
|
||||
#define MT7986_CALIBRATION 165
|
||||
|
||||
struct mtk_thermal;
|
||||
|
||||
struct thermal_bank_cfg {
|
||||
@ -292,6 +328,8 @@ struct mtk_thermal {
|
||||
|
||||
const struct mtk_thermal_data *conf;
|
||||
struct mtk_thermal_bank banks[MAX_NUM_ZONES];
|
||||
|
||||
int (*raw_to_mcelsius)(struct mtk_thermal *mt, int sensno, s32 raw);
|
||||
};
|
||||
|
||||
/* MT8183 thermal sensor data */
|
||||
@ -386,6 +424,14 @@ static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, };
|
||||
static const int mt7622_vts_index[MT7622_NUM_SENSORS] = { VTS1 };
|
||||
static const int mt7622_tc_offset[MT7622_NUM_CONTROLLER] = { 0x0, };
|
||||
|
||||
/* MT7986 thermal sensor data */
|
||||
static const int mt7986_bank_data[MT7986_NUM_SENSORS] = { MT7986_TS1, };
|
||||
static const int mt7986_msr[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, };
|
||||
static const int mt7986_adcpnp[MT7986_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, };
|
||||
static const int mt7986_mux_values[MT7986_NUM_SENSORS] = { 0, };
|
||||
static const int mt7986_vts_index[MT7986_NUM_SENSORS] = { VTS1 };
|
||||
static const int mt7986_tc_offset[MT7986_NUM_CONTROLLER] = { 0x0, };
|
||||
|
||||
/*
|
||||
* The MT8173 thermal controller has four banks. Each bank can read up to
|
||||
* four temperature sensors simultaneously. The MT8173 has a total of 5
|
||||
@ -549,8 +595,32 @@ static const struct mtk_thermal_data mt8183_thermal_data = {
|
||||
.version = MTK_THERMAL_V1,
|
||||
};
|
||||
|
||||
/*
|
||||
* MT7986 uses AUXADC Channel 11 for raw data access.
|
||||
*/
|
||||
static const struct mtk_thermal_data mt7986_thermal_data = {
|
||||
.auxadc_channel = MT7986_TEMP_AUXADC_CHANNEL,
|
||||
.num_banks = MT7986_NUM_ZONES,
|
||||
.num_sensors = MT7986_NUM_SENSORS,
|
||||
.vts_index = mt7986_vts_index,
|
||||
.cali_val = MT7986_CALIBRATION,
|
||||
.num_controller = MT7986_NUM_CONTROLLER,
|
||||
.controller_offset = mt7986_tc_offset,
|
||||
.need_switch_bank = true,
|
||||
.bank_data = {
|
||||
{
|
||||
.num_sensors = 1,
|
||||
.sensors = mt7986_bank_data,
|
||||
},
|
||||
},
|
||||
.msr = mt7986_msr,
|
||||
.adcpnp = mt7986_adcpnp,
|
||||
.sensor_mux_values = mt7986_mux_values,
|
||||
.version = MTK_THERMAL_V3,
|
||||
};
|
||||
|
||||
/**
|
||||
* raw_to_mcelsius - convert a raw ADC value to mcelsius
|
||||
* raw_to_mcelsius_v1 - convert a raw ADC value to mcelsius
|
||||
* @mt: The thermal controller
|
||||
* @sensno: sensor number
|
||||
* @raw: raw ADC value
|
||||
@ -603,6 +673,22 @@ static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
return (format_2 - tmp) * 100;
|
||||
}
|
||||
|
||||
static int raw_to_mcelsius_v3(struct mtk_thermal *mt, int sensno, s32 raw)
|
||||
{
|
||||
s32 tmp;
|
||||
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
|
||||
raw &= 0xfff;
|
||||
tmp = 100000 * 15 / 16 * 10000;
|
||||
tmp /= 4096 - 512 + mt->adc_ge;
|
||||
tmp /= 1490;
|
||||
tmp *= raw - mt->vts[sensno] - 2900;
|
||||
|
||||
return mt->degc_cali * 500 - tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* mtk_thermal_get_bank - get bank
|
||||
* @bank: The bank
|
||||
@ -656,13 +742,9 @@ static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank)
|
||||
for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
|
||||
raw = readl(mt->thermal_base + conf->msr[i]);
|
||||
|
||||
if (mt->conf->version == MTK_THERMAL_V1) {
|
||||
temp = raw_to_mcelsius_v1(
|
||||
mt, conf->bank_data[bank->id].sensors[i], raw);
|
||||
} else {
|
||||
temp = raw_to_mcelsius_v2(
|
||||
mt, conf->bank_data[bank->id].sensors[i], raw);
|
||||
}
|
||||
temp = mt->raw_to_mcelsius(
|
||||
mt, conf->bank_data[bank->id].sensors[i], raw);
|
||||
|
||||
|
||||
/*
|
||||
* The first read of a sensor often contains very high bogus
|
||||
@ -887,6 +969,25 @@ static int mtk_thermal_extract_efuse_v2(struct mtk_thermal *mt, u32 *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_thermal_extract_efuse_v3(struct mtk_thermal *mt, u32 *buf)
|
||||
{
|
||||
if (!CALIB_BUF1_VALID_V3(buf[1]))
|
||||
return -EINVAL;
|
||||
|
||||
mt->adc_ge = CALIB_BUF0_ADC_GE_V3(buf[0]);
|
||||
mt->degc_cali = CALIB_BUF0_DEGC_CALI_V3(buf[0]);
|
||||
mt->o_slope = CALIB_BUF0_O_SLOPE_V3(buf[0]);
|
||||
mt->vts[VTS1] = CALIB_BUF1_VTS_TS1_V3(buf[1]);
|
||||
mt->vts[VTS2] = CALIB_BUF1_VTS_TS2_V3(buf[1]);
|
||||
mt->vts[VTSABB] = CALIB_BUF1_VTS_TSABB_V3(buf[1]);
|
||||
mt->o_slope_sign = CALIB_BUF1_O_SLOPE_SIGN_V3(buf[1]);
|
||||
|
||||
if (CALIB_BUF1_ID_V3(buf[1]) == 0)
|
||||
mt->o_slope = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_thermal_get_calibration_data(struct device *dev,
|
||||
struct mtk_thermal *mt)
|
||||
{
|
||||
@ -897,6 +998,7 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
|
||||
|
||||
/* Start with default values */
|
||||
mt->adc_ge = 512;
|
||||
mt->adc_oe = 512;
|
||||
for (i = 0; i < mt->conf->num_sensors; i++)
|
||||
mt->vts[i] = 260;
|
||||
mt->degc_cali = 40;
|
||||
@ -922,10 +1024,20 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mt->conf->version == MTK_THERMAL_V1)
|
||||
switch (mt->conf->version) {
|
||||
case MTK_THERMAL_V1:
|
||||
ret = mtk_thermal_extract_efuse_v1(mt, buf);
|
||||
else
|
||||
break;
|
||||
case MTK_THERMAL_V2:
|
||||
ret = mtk_thermal_extract_efuse_v2(mt, buf);
|
||||
break;
|
||||
case MTK_THERMAL_V3:
|
||||
ret = mtk_thermal_extract_efuse_v3(mt, buf);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_info(dev, "Device not calibrated, using default calibration values\n");
|
||||
@ -955,6 +1067,10 @@ static const struct of_device_id mtk_thermal_of_match[] = {
|
||||
.compatible = "mediatek,mt7622-thermal",
|
||||
.data = (void *)&mt7622_thermal_data,
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt7986-thermal",
|
||||
.data = (void *)&mt7986_thermal_data,
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt8183-thermal",
|
||||
.data = (void *)&mt8183_thermal_data,
|
||||
@ -990,7 +1106,6 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
int ret, i, ctrl_id;
|
||||
struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node;
|
||||
struct mtk_thermal *mt;
|
||||
struct resource *res;
|
||||
u64 auxadc_phys_base, apmixed_phys_base;
|
||||
struct thermal_zone_device *tzdev;
|
||||
void __iomem *apmixed_base, *auxadc_base;
|
||||
@ -1009,8 +1124,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mt->clk_auxadc))
|
||||
return PTR_ERR(mt->clk_auxadc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mt->thermal_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
mt->thermal_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(mt->thermal_base))
|
||||
return PTR_ERR(mt->thermal_base);
|
||||
|
||||
@ -1070,11 +1184,18 @@ static int mtk_thermal_probe(struct platform_device *pdev)
|
||||
goto err_disable_clk_auxadc;
|
||||
}
|
||||
|
||||
if (mt->conf->version == MTK_THERMAL_V2) {
|
||||
if (mt->conf->version != MTK_THERMAL_V1) {
|
||||
mtk_thermal_turn_on_buffer(apmixed_base);
|
||||
mtk_thermal_release_periodic_ts(mt, auxadc_base);
|
||||
}
|
||||
|
||||
if (mt->conf->version == MTK_THERMAL_V1)
|
||||
mt->raw_to_mcelsius = raw_to_mcelsius_v1;
|
||||
else if (mt->conf->version == MTK_THERMAL_V2)
|
||||
mt->raw_to_mcelsius = raw_to_mcelsius_v2;
|
||||
else
|
||||
mt->raw_to_mcelsius = raw_to_mcelsius_v3;
|
||||
|
||||
for (ctrl_id = 0; ctrl_id < mt->conf->num_controller ; ctrl_id++)
|
||||
for (i = 0; i < mt->conf->num_banks; i++)
|
||||
mtk_thermal_init_bank(mt, i, apmixed_phys_base,
|
1224
drivers/thermal/mediatek/lvts_thermal.c
Normal file
1224
drivers/thermal/mediatek/lvts_thermal.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <asm-generic/unaligned.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
#define QPNP_TM_REG_DIG_MAJOR 0x01
|
||||
@ -264,17 +263,17 @@ skip:
|
||||
return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
|
||||
}
|
||||
|
||||
static int qpnp_tm_set_trip_temp(struct thermal_zone_device *tz, int trip, int temp)
|
||||
static int qpnp_tm_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp)
|
||||
{
|
||||
struct qpnp_tm_chip *chip = tz->devdata;
|
||||
const struct thermal_trip *trip_points;
|
||||
struct thermal_trip trip;
|
||||
int ret;
|
||||
|
||||
trip_points = of_thermal_get_trip_points(chip->tz_dev);
|
||||
if (!trip_points)
|
||||
return -EINVAL;
|
||||
ret = __thermal_zone_get_trip(chip->tz_dev, trip_id, &trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (trip_points[trip].type != THERMAL_TRIP_CRITICAL)
|
||||
if (trip.type != THERMAL_TRIP_CRITICAL)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
@ -300,22 +299,17 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
|
||||
|
||||
static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
|
||||
{
|
||||
int ntrips;
|
||||
const struct thermal_trip *trips;
|
||||
int i;
|
||||
struct thermal_trip trip;
|
||||
int i, ret;
|
||||
|
||||
ntrips = of_thermal_get_ntrips(chip->tz_dev);
|
||||
if (ntrips <= 0)
|
||||
return THERMAL_TEMP_INVALID;
|
||||
for (i = 0; i < thermal_zone_get_num_trips(chip->tz_dev); i++) {
|
||||
|
||||
trips = of_thermal_get_trip_points(chip->tz_dev);
|
||||
if (!trips)
|
||||
return THERMAL_TEMP_INVALID;
|
||||
ret = thermal_zone_get_trip(chip->tz_dev, i, &trip);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < ntrips; i++) {
|
||||
if (of_thermal_is_trip_valid(chip->tz_dev, i) &&
|
||||
trips[i].type == THERMAL_TRIP_CRITICAL)
|
||||
return trips[i].temperature;
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL)
|
||||
return trip.temperature;
|
||||
}
|
||||
|
||||
return THERMAL_TEMP_INVALID;
|
||||
@ -353,7 +347,12 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
|
||||
if (stage)
|
||||
chip->temp = qpnp_tm_decode_temp(chip, stage);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
crit_temp = qpnp_tm_get_critical_trip_temp(chip);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "tsens.h"
|
||||
|
||||
@ -15,220 +17,117 @@
|
||||
#define TM_Sn_STATUS_OFF 0x0030
|
||||
#define TM_TRDY_OFF 0x005c
|
||||
|
||||
/* eeprom layout data for 8916 */
|
||||
#define MSM8916_BASE0_MASK 0x0000007f
|
||||
#define MSM8916_BASE1_MASK 0xfe000000
|
||||
#define MSM8916_BASE0_SHIFT 0
|
||||
#define MSM8916_BASE1_SHIFT 25
|
||||
|
||||
#define MSM8916_S0_P1_MASK 0x00000f80
|
||||
#define MSM8916_S1_P1_MASK 0x003e0000
|
||||
#define MSM8916_S2_P1_MASK 0xf8000000
|
||||
#define MSM8916_S3_P1_MASK 0x000003e0
|
||||
#define MSM8916_S4_P1_MASK 0x000f8000
|
||||
|
||||
#define MSM8916_S0_P2_MASK 0x0001f000
|
||||
#define MSM8916_S1_P2_MASK 0x07c00000
|
||||
#define MSM8916_S2_P2_MASK 0x0000001f
|
||||
#define MSM8916_S3_P2_MASK 0x00007c00
|
||||
#define MSM8916_S4_P2_MASK 0x01f00000
|
||||
|
||||
#define MSM8916_S0_P1_SHIFT 7
|
||||
#define MSM8916_S1_P1_SHIFT 17
|
||||
#define MSM8916_S2_P1_SHIFT 27
|
||||
#define MSM8916_S3_P1_SHIFT 5
|
||||
#define MSM8916_S4_P1_SHIFT 15
|
||||
|
||||
#define MSM8916_S0_P2_SHIFT 12
|
||||
#define MSM8916_S1_P2_SHIFT 22
|
||||
#define MSM8916_S2_P2_SHIFT 0
|
||||
#define MSM8916_S3_P2_SHIFT 10
|
||||
#define MSM8916_S4_P2_SHIFT 20
|
||||
|
||||
#define MSM8916_CAL_SEL_MASK 0xe0000000
|
||||
#define MSM8916_CAL_SEL_SHIFT 29
|
||||
|
||||
/* eeprom layout data for 8939 */
|
||||
#define MSM8939_BASE0_MASK 0x000000ff
|
||||
#define MSM8939_BASE1_MASK 0xff000000
|
||||
#define MSM8939_BASE0_SHIFT 0
|
||||
#define MSM8939_BASE1_SHIFT 24
|
||||
|
||||
#define MSM8939_S0_P1_MASK 0x000001f8
|
||||
#define MSM8939_S1_P1_MASK 0x001f8000
|
||||
#define MSM8939_S2_P1_MASK_0_4 0xf8000000
|
||||
#define MSM8939_S2_P1_MASK_5 0x00000001
|
||||
#define MSM8939_S3_P1_MASK 0x00001f80
|
||||
#define MSM8939_S4_P1_MASK 0x01f80000
|
||||
#define MSM8939_S5_P1_MASK 0x00003f00
|
||||
#define MSM8939_S6_P1_MASK 0x03f00000
|
||||
#define MSM8939_S7_P1_MASK 0x0000003f
|
||||
#define MSM8939_S8_P1_MASK 0x0003f000
|
||||
#define MSM8939_S9_P1_MASK 0x07e00000
|
||||
|
||||
#define MSM8939_S0_P2_MASK 0x00007e00
|
||||
#define MSM8939_S1_P2_MASK 0x07e00000
|
||||
#define MSM8939_S2_P2_MASK 0x0000007e
|
||||
#define MSM8939_S3_P2_MASK 0x0007e000
|
||||
#define MSM8939_S4_P2_MASK 0x7e000000
|
||||
#define MSM8939_S5_P2_MASK 0x000fc000
|
||||
#define MSM8939_S6_P2_MASK 0xfc000000
|
||||
#define MSM8939_S7_P2_MASK 0x00000fc0
|
||||
#define MSM8939_S8_P2_MASK 0x00fc0000
|
||||
#define MSM8939_S9_P2_MASK_0_4 0xf8000000
|
||||
#define MSM8939_S9_P2_MASK_5 0x00002000
|
||||
|
||||
#define MSM8939_S0_P1_SHIFT 3
|
||||
#define MSM8939_S1_P1_SHIFT 15
|
||||
#define MSM8939_S2_P1_SHIFT_0_4 27
|
||||
#define MSM8939_S2_P1_SHIFT_5 0
|
||||
#define MSM8939_S3_P1_SHIFT 7
|
||||
#define MSM8939_S4_P1_SHIFT 19
|
||||
#define MSM8939_S5_P1_SHIFT 8
|
||||
#define MSM8939_S6_P1_SHIFT 20
|
||||
#define MSM8939_S7_P1_SHIFT 0
|
||||
#define MSM8939_S8_P1_SHIFT 12
|
||||
#define MSM8939_S9_P1_SHIFT 21
|
||||
|
||||
#define MSM8939_S0_P2_SHIFT 9
|
||||
#define MSM8939_S1_P2_SHIFT 21
|
||||
#define MSM8939_S2_P2_SHIFT 1
|
||||
#define MSM8939_S3_P2_SHIFT 13
|
||||
#define MSM8939_S4_P2_SHIFT 25
|
||||
#define MSM8939_S5_P2_SHIFT 14
|
||||
#define MSM8939_S6_P2_SHIFT 26
|
||||
#define MSM8939_S7_P2_SHIFT 6
|
||||
#define MSM8939_S8_P2_SHIFT 18
|
||||
#define MSM8939_S9_P2_SHIFT_0_4 27
|
||||
#define MSM8939_S9_P2_SHIFT_5 13
|
||||
|
||||
#define MSM8939_CAL_SEL_MASK 0x7
|
||||
#define MSM8939_CAL_SEL_SHIFT 0
|
||||
|
||||
/* eeprom layout data for 8974 */
|
||||
#define BASE1_MASK 0xff
|
||||
#define S0_P1_MASK 0x3f00
|
||||
#define S1_P1_MASK 0xfc000
|
||||
#define S2_P1_MASK 0x3f00000
|
||||
#define S3_P1_MASK 0xfc000000
|
||||
#define S4_P1_MASK 0x3f
|
||||
#define S5_P1_MASK 0xfc0
|
||||
#define S6_P1_MASK 0x3f000
|
||||
#define S7_P1_MASK 0xfc0000
|
||||
#define S8_P1_MASK 0x3f000000
|
||||
#define S8_P1_MASK_BKP 0x3f
|
||||
#define S9_P1_MASK 0x3f
|
||||
#define S9_P1_MASK_BKP 0xfc0
|
||||
#define S10_P1_MASK 0xfc0
|
||||
#define S10_P1_MASK_BKP 0x3f000
|
||||
#define CAL_SEL_0_1 0xc0000000
|
||||
#define CAL_SEL_2 0x40000000
|
||||
#define CAL_SEL_SHIFT 30
|
||||
#define CAL_SEL_SHIFT_2 28
|
||||
|
||||
#define S0_P1_SHIFT 8
|
||||
#define S1_P1_SHIFT 14
|
||||
#define S2_P1_SHIFT 20
|
||||
#define S3_P1_SHIFT 26
|
||||
#define S5_P1_SHIFT 6
|
||||
#define S6_P1_SHIFT 12
|
||||
#define S7_P1_SHIFT 18
|
||||
#define S8_P1_SHIFT 24
|
||||
#define S9_P1_BKP_SHIFT 6
|
||||
#define S10_P1_SHIFT 6
|
||||
#define S10_P1_BKP_SHIFT 12
|
||||
|
||||
#define BASE2_SHIFT 12
|
||||
#define BASE2_BKP_SHIFT 18
|
||||
#define S0_P2_SHIFT 20
|
||||
#define S0_P2_BKP_SHIFT 26
|
||||
#define S1_P2_SHIFT 26
|
||||
#define S2_P2_BKP_SHIFT 6
|
||||
#define S3_P2_SHIFT 6
|
||||
#define S3_P2_BKP_SHIFT 12
|
||||
#define S4_P2_SHIFT 12
|
||||
#define S4_P2_BKP_SHIFT 18
|
||||
#define S5_P2_SHIFT 18
|
||||
#define S5_P2_BKP_SHIFT 24
|
||||
#define S6_P2_SHIFT 24
|
||||
#define S7_P2_BKP_SHIFT 6
|
||||
#define S8_P2_SHIFT 6
|
||||
#define S8_P2_BKP_SHIFT 12
|
||||
#define S9_P2_SHIFT 12
|
||||
#define S9_P2_BKP_SHIFT 18
|
||||
#define S10_P2_SHIFT 18
|
||||
#define S10_P2_BKP_SHIFT 24
|
||||
|
||||
#define BASE2_MASK 0xff000
|
||||
#define BASE2_BKP_MASK 0xfc0000
|
||||
#define S0_P2_MASK 0x3f00000
|
||||
#define S0_P2_BKP_MASK 0xfc000000
|
||||
#define S1_P2_MASK 0xfc000000
|
||||
#define S1_P2_BKP_MASK 0x3f
|
||||
#define S2_P2_MASK 0x3f
|
||||
#define S2_P2_BKP_MASK 0xfc0
|
||||
#define S3_P2_MASK 0xfc0
|
||||
#define S3_P2_BKP_MASK 0x3f000
|
||||
#define S4_P2_MASK 0x3f000
|
||||
#define S4_P2_BKP_MASK 0xfc0000
|
||||
#define S5_P2_MASK 0xfc0000
|
||||
#define S5_P2_BKP_MASK 0x3f000000
|
||||
#define S6_P2_MASK 0x3f000000
|
||||
#define S6_P2_BKP_MASK 0x3f
|
||||
#define S7_P2_MASK 0x3f
|
||||
#define S7_P2_BKP_MASK 0xfc0
|
||||
#define S8_P2_MASK 0xfc0
|
||||
#define S8_P2_BKP_MASK 0x3f000
|
||||
#define S9_P2_MASK 0x3f000
|
||||
#define S9_P2_BKP_MASK 0xfc0000
|
||||
#define S10_P2_MASK 0xfc0000
|
||||
#define S10_P2_BKP_MASK 0x3f000000
|
||||
|
||||
/* extra data for 8974 */
|
||||
#define BKP_SEL 0x3
|
||||
#define BKP_REDUN_SEL 0xe0000000
|
||||
#define BKP_REDUN_SHIFT 29
|
||||
|
||||
#define BIT_APPEND 0x3
|
||||
|
||||
/* eeprom layout data for mdm9607 */
|
||||
#define MDM9607_BASE0_MASK 0x000000ff
|
||||
#define MDM9607_BASE1_MASK 0x000ff000
|
||||
#define MDM9607_BASE0_SHIFT 0
|
||||
#define MDM9607_BASE1_SHIFT 12
|
||||
struct tsens_legacy_calibration_format tsens_8916_nvmem = {
|
||||
.base_len = 7,
|
||||
.base_shift = 3,
|
||||
.sp_len = 5,
|
||||
.mode = { 0, 29, 1 },
|
||||
.invalid = { 0, 31, 1 },
|
||||
.base = { { 0, 0 }, { 1, 25 } },
|
||||
.sp = {
|
||||
{ { 0, 7 }, { 0, 12 } },
|
||||
{ { 0, 17 }, { 0, 22 } },
|
||||
{ { 0, 27 }, { 1, 0 } },
|
||||
{ { 1, 5 }, { 1, 10 } },
|
||||
{ { 1, 15 }, { 1, 20 } },
|
||||
},
|
||||
};
|
||||
|
||||
#define MDM9607_S0_P1_MASK 0x00003f00
|
||||
#define MDM9607_S1_P1_MASK 0x03f00000
|
||||
#define MDM9607_S2_P1_MASK 0x0000003f
|
||||
#define MDM9607_S3_P1_MASK 0x0003f000
|
||||
#define MDM9607_S4_P1_MASK 0x0000003f
|
||||
struct tsens_legacy_calibration_format tsens_8939_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 12, 0 },
|
||||
.invalid = { 12, 2 },
|
||||
.base = { { 0, 0 }, { 1, 24 } },
|
||||
.sp = {
|
||||
{ { 12, 3 }, { 12, 9 } },
|
||||
{ { 12, 15 }, { 12, 21 } },
|
||||
{ { 12, 27 }, { 13, 1 } },
|
||||
{ { 13, 7 }, { 13, 13 } },
|
||||
{ { 13, 19 }, { 13, 25 } },
|
||||
{ { 0, 8 }, { 0, 14 } },
|
||||
{ { 0, 20 }, { 0, 26 } },
|
||||
{ { 1, 0 }, { 1, 6 } },
|
||||
{ { 1, 12 }, { 1, 18 } },
|
||||
},
|
||||
};
|
||||
|
||||
#define MDM9607_S0_P2_MASK 0x000fc000
|
||||
#define MDM9607_S1_P2_MASK 0xfc000000
|
||||
#define MDM9607_S2_P2_MASK 0x00000fc0
|
||||
#define MDM9607_S3_P2_MASK 0x00fc0000
|
||||
#define MDM9607_S4_P2_MASK 0x00000fc0
|
||||
struct tsens_legacy_calibration_format tsens_8974_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 1, 30 },
|
||||
.invalid = { 3, 30 },
|
||||
.base = { { 0, 0 }, { 2, 12 } },
|
||||
.sp = {
|
||||
{ { 0, 8 }, { 2, 20 } },
|
||||
{ { 0, 14 }, { 2, 26 } },
|
||||
{ { 0, 20 }, { 3, 0 } },
|
||||
{ { 0, 26 }, { 3, 6 } },
|
||||
{ { 1, 0 }, { 3, 12 } },
|
||||
{ { 1, 6 }, { 3, 18 } },
|
||||
{ { 1, 12 }, { 3, 24 } },
|
||||
{ { 1, 18 }, { 4, 0 } },
|
||||
{ { 1, 24 }, { 4, 6 } },
|
||||
{ { 2, 0 }, { 4, 12 } },
|
||||
{ { 2, 6 }, { 4, 18 } },
|
||||
},
|
||||
};
|
||||
|
||||
#define MDM9607_S0_P1_SHIFT 8
|
||||
#define MDM9607_S1_P1_SHIFT 20
|
||||
#define MDM9607_S2_P1_SHIFT 0
|
||||
#define MDM9607_S3_P1_SHIFT 12
|
||||
#define MDM9607_S4_P1_SHIFT 0
|
||||
struct tsens_legacy_calibration_format tsens_8974_backup_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 4, 30, 1 },
|
||||
.invalid = { 5, 30, 1 },
|
||||
.base = { { 0, 0 }, { 2, 18 } },
|
||||
.sp = {
|
||||
{ { 0, 8 }, { 2, 26 } },
|
||||
{ { 0, 14 }, { 3, 0 } },
|
||||
{ { 0, 20 }, { 3, 6 } },
|
||||
{ { 0, 26 }, { 3, 12 } },
|
||||
{ { 1, 0 }, { 3, 18 } },
|
||||
{ { 1, 6 }, { 3, 24, 1 } },
|
||||
{ { 1, 12 }, { 4, 0, 1 } },
|
||||
{ { 1, 18 }, { 4, 6, 1 } },
|
||||
{ { 2, 0 }, { 4, 12, 1 } },
|
||||
{ { 2, 6 }, { 4, 18, 1 } },
|
||||
{ { 2, 12 }, { 4, 24, 1 } },
|
||||
},
|
||||
};
|
||||
|
||||
#define MDM9607_S0_P2_SHIFT 14
|
||||
#define MDM9607_S1_P2_SHIFT 26
|
||||
#define MDM9607_S2_P2_SHIFT 6
|
||||
#define MDM9607_S3_P2_SHIFT 18
|
||||
#define MDM9607_S4_P2_SHIFT 6
|
||||
|
||||
#define MDM9607_CAL_SEL_MASK 0x00700000
|
||||
#define MDM9607_CAL_SEL_SHIFT 20
|
||||
struct tsens_legacy_calibration_format tsens_9607_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 2, 20 },
|
||||
.invalid = { 2, 22 },
|
||||
.base = { { 0, 0 }, { 2, 12 } },
|
||||
.sp = {
|
||||
{ { 0, 8 }, { 0, 14 } },
|
||||
{ { 0, 20 }, { 0, 26 } },
|
||||
{ { 1, 0 }, { 1, 6 } },
|
||||
{ { 1, 12 }, { 1, 18 } },
|
||||
{ { 2, 0 }, { 2, 6 } },
|
||||
},
|
||||
};
|
||||
|
||||
static int calibrate_8916(struct tsens_priv *priv)
|
||||
{
|
||||
int base0 = 0, base1 = 0, i;
|
||||
u32 p1[5], p2[5];
|
||||
int mode = 0;
|
||||
u32 *qfprom_cdata, *qfprom_csel;
|
||||
int mode, ret;
|
||||
|
||||
ret = tsens_calibrate_nvmem(priv, 3);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
@ -240,37 +139,9 @@ static int calibrate_8916(struct tsens_priv *priv)
|
||||
return PTR_ERR(qfprom_csel);
|
||||
}
|
||||
|
||||
mode = (qfprom_csel[0] & MSM8916_CAL_SEL_MASK) >> MSM8916_CAL_SEL_SHIFT;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base1 = (qfprom_cdata[1] & MSM8916_BASE1_MASK) >> MSM8916_BASE1_SHIFT;
|
||||
p2[0] = (qfprom_cdata[0] & MSM8916_S0_P2_MASK) >> MSM8916_S0_P2_SHIFT;
|
||||
p2[1] = (qfprom_cdata[0] & MSM8916_S1_P2_MASK) >> MSM8916_S1_P2_SHIFT;
|
||||
p2[2] = (qfprom_cdata[1] & MSM8916_S2_P2_MASK) >> MSM8916_S2_P2_SHIFT;
|
||||
p2[3] = (qfprom_cdata[1] & MSM8916_S3_P2_MASK) >> MSM8916_S3_P2_SHIFT;
|
||||
p2[4] = (qfprom_cdata[1] & MSM8916_S4_P2_MASK) >> MSM8916_S4_P2_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = ((base1 + p2[i]) << 3);
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base0 = (qfprom_cdata[0] & MSM8916_BASE0_MASK);
|
||||
p1[0] = (qfprom_cdata[0] & MSM8916_S0_P1_MASK) >> MSM8916_S0_P1_SHIFT;
|
||||
p1[1] = (qfprom_cdata[0] & MSM8916_S1_P1_MASK) >> MSM8916_S1_P1_SHIFT;
|
||||
p1[2] = (qfprom_cdata[0] & MSM8916_S2_P1_MASK) >> MSM8916_S2_P1_SHIFT;
|
||||
p1[3] = (qfprom_cdata[1] & MSM8916_S3_P1_MASK) >> MSM8916_S3_P1_SHIFT;
|
||||
p1[4] = (qfprom_cdata[1] & MSM8916_S4_P1_MASK) >> MSM8916_S4_P1_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = (((base0) + p1[i]) << 3);
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
break;
|
||||
}
|
||||
mode = tsens_read_calibration_legacy(priv, &tsens_8916_nvmem,
|
||||
p1, p2,
|
||||
qfprom_cdata, qfprom_csel);
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
@ -279,83 +150,68 @@ static int calibrate_8916(struct tsens_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_8939(struct tsens_priv *priv)
|
||||
static void fixup_8974_points(int mode, u32 *p1, u32 *p2)
|
||||
{
|
||||
int base0 = 0, base1 = 0, i;
|
||||
u32 p1[10], p2[10];
|
||||
int mode = 0;
|
||||
u32 *qfprom_cdata;
|
||||
u32 cdata[6];
|
||||
int i;
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
return PTR_ERR(qfprom_cdata);
|
||||
|
||||
/* Mapping between qfprom nvmem and calibration data */
|
||||
cdata[0] = qfprom_cdata[12];
|
||||
cdata[1] = qfprom_cdata[13];
|
||||
cdata[2] = qfprom_cdata[0];
|
||||
cdata[3] = qfprom_cdata[1];
|
||||
cdata[4] = qfprom_cdata[22];
|
||||
cdata[5] = qfprom_cdata[21];
|
||||
|
||||
mode = (cdata[0] & MSM8939_CAL_SEL_MASK) >> MSM8939_CAL_SEL_SHIFT;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base1 = (cdata[3] & MSM8939_BASE1_MASK) >> MSM8939_BASE1_SHIFT;
|
||||
p2[0] = (cdata[0] & MSM8939_S0_P2_MASK) >> MSM8939_S0_P2_SHIFT;
|
||||
p2[1] = (cdata[0] & MSM8939_S1_P2_MASK) >> MSM8939_S1_P2_SHIFT;
|
||||
p2[2] = (cdata[1] & MSM8939_S2_P2_MASK) >> MSM8939_S2_P2_SHIFT;
|
||||
p2[3] = (cdata[1] & MSM8939_S3_P2_MASK) >> MSM8939_S3_P2_SHIFT;
|
||||
p2[4] = (cdata[1] & MSM8939_S4_P2_MASK) >> MSM8939_S4_P2_SHIFT;
|
||||
p2[5] = (cdata[2] & MSM8939_S5_P2_MASK) >> MSM8939_S5_P2_SHIFT;
|
||||
p2[6] = (cdata[2] & MSM8939_S6_P2_MASK) >> MSM8939_S6_P2_SHIFT;
|
||||
p2[7] = (cdata[3] & MSM8939_S7_P2_MASK) >> MSM8939_S7_P2_SHIFT;
|
||||
p2[8] = (cdata[3] & MSM8939_S8_P2_MASK) >> MSM8939_S8_P2_SHIFT;
|
||||
p2[9] = (cdata[4] & MSM8939_S9_P2_MASK_0_4) >> MSM8939_S9_P2_SHIFT_0_4;
|
||||
p2[9] |= ((cdata[5] & MSM8939_S9_P2_MASK_5) >> MSM8939_S9_P2_SHIFT_5) << 5;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = (base1 + p2[i]) << 2;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base0 = (cdata[2] & MSM8939_BASE0_MASK) >> MSM8939_BASE0_SHIFT;
|
||||
p1[0] = (cdata[0] & MSM8939_S0_P1_MASK) >> MSM8939_S0_P1_SHIFT;
|
||||
p1[1] = (cdata[0] & MSM8939_S1_P1_MASK) >> MSM8939_S1_P1_SHIFT;
|
||||
p1[2] = (cdata[0] & MSM8939_S2_P1_MASK_0_4) >> MSM8939_S2_P1_SHIFT_0_4;
|
||||
p1[2] |= ((cdata[1] & MSM8939_S2_P1_MASK_5) >> MSM8939_S2_P1_SHIFT_5) << 5;
|
||||
p1[3] = (cdata[1] & MSM8939_S3_P1_MASK) >> MSM8939_S3_P1_SHIFT;
|
||||
p1[4] = (cdata[1] & MSM8939_S4_P1_MASK) >> MSM8939_S4_P1_SHIFT;
|
||||
p1[5] = (cdata[2] & MSM8939_S5_P1_MASK) >> MSM8939_S5_P1_SHIFT;
|
||||
p1[6] = (cdata[2] & MSM8939_S6_P1_MASK) >> MSM8939_S6_P1_SHIFT;
|
||||
p1[7] = (cdata[3] & MSM8939_S7_P1_MASK) >> MSM8939_S7_P1_SHIFT;
|
||||
p1[8] = (cdata[3] & MSM8939_S8_P1_MASK) >> MSM8939_S8_P1_SHIFT;
|
||||
p1[9] = (cdata[4] & MSM8939_S9_P1_MASK) >> MSM8939_S9_P1_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = ((base0) + p1[i]) << 2;
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
if (mode == NO_PT_CALIB) {
|
||||
p1[0] += 2;
|
||||
p1[1] += 9;
|
||||
p1[2] += 3;
|
||||
p1[3] += 9;
|
||||
p1[4] += 5;
|
||||
p1[5] += 9;
|
||||
p1[6] += 7;
|
||||
p1[7] += 10;
|
||||
p1[8] += 8;
|
||||
p1[9] += 9;
|
||||
p1[10] += 8;
|
||||
} else {
|
||||
for (i = 0; i < 11; i++) {
|
||||
/*
|
||||
* ONE_PT_CALIB requires using addition here instead of
|
||||
* using OR operation.
|
||||
*/
|
||||
p1[i] += BIT_APPEND;
|
||||
p2[i] += BIT_APPEND;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int calibrate_8974_nvmem(struct tsens_priv *priv)
|
||||
{
|
||||
u32 p1[11], p2[11];
|
||||
u32 backup;
|
||||
int ret, mode;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, "use_backup", &backup);
|
||||
if (ret == -ENOENT)
|
||||
dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mode = tsens_read_calibration(priv, 2, p1, p2, backup == BKP_SEL);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
fixup_8974_points(mode, p1, p2);
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_8974(struct tsens_priv *priv)
|
||||
{
|
||||
int base1 = 0, base2 = 0, i;
|
||||
u32 p1[11], p2[11];
|
||||
int mode = 0;
|
||||
u32 *calib, *bkp;
|
||||
u32 calib_redun_sel;
|
||||
int mode, ret;
|
||||
|
||||
ret = calibrate_8974_nvmem(priv);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
calib = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(calib))
|
||||
@ -367,116 +223,18 @@ static int calibrate_8974(struct tsens_priv *priv)
|
||||
return PTR_ERR(bkp);
|
||||
}
|
||||
|
||||
calib_redun_sel = bkp[1] & BKP_REDUN_SEL;
|
||||
calib_redun_sel >>= BKP_REDUN_SHIFT;
|
||||
calib_redun_sel = FIELD_GET(BKP_REDUN_SEL, bkp[1]);
|
||||
|
||||
if (calib_redun_sel == BKP_SEL) {
|
||||
mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
|
||||
mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
|
||||
if (calib_redun_sel == BKP_SEL)
|
||||
mode = tsens_read_calibration_legacy(priv, &tsens_8974_backup_nvmem,
|
||||
p1, p2,
|
||||
bkp, calib);
|
||||
else
|
||||
mode = tsens_read_calibration_legacy(priv, &tsens_8974_nvmem,
|
||||
p1, p2,
|
||||
calib, NULL);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
|
||||
p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
|
||||
p2[1] = (bkp[3] & S1_P2_BKP_MASK);
|
||||
p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
|
||||
p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
|
||||
p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
|
||||
p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
|
||||
p2[6] = (calib[5] & S6_P2_BKP_MASK);
|
||||
p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
|
||||
p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
|
||||
p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
|
||||
p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB:
|
||||
case ONE_PT_CALIB2:
|
||||
base1 = bkp[0] & BASE1_MASK;
|
||||
p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
p1[4] = (bkp[1] & S4_P1_MASK);
|
||||
p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
|
||||
p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
|
||||
p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
|
||||
p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
|
||||
p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
|
||||
p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
|
||||
mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
|
||||
p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
|
||||
p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
|
||||
p2[2] = (calib[3] & S2_P2_MASK);
|
||||
p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
|
||||
p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
|
||||
p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
|
||||
p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
|
||||
p2[7] = (calib[4] & S7_P2_MASK);
|
||||
p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
|
||||
p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
|
||||
p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB:
|
||||
case ONE_PT_CALIB2:
|
||||
base1 = calib[0] & BASE1_MASK;
|
||||
p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
p1[4] = (calib[1] & S4_P1_MASK);
|
||||
p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
|
||||
p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
|
||||
p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
|
||||
p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
|
||||
p1[9] = (calib[2] & S9_P1_MASK);
|
||||
p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case ONE_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] += (base1 << 2) | BIT_APPEND;
|
||||
break;
|
||||
case TWO_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p2[i] += base2;
|
||||
p2[i] <<= 2;
|
||||
p2[i] |= BIT_APPEND;
|
||||
}
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] += base1;
|
||||
p1[i] <<= 2;
|
||||
p1[i] |= BIT_APPEND;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = 780;
|
||||
p1[0] = 502;
|
||||
p1[1] = 509;
|
||||
p1[2] = 503;
|
||||
p1[3] = 509;
|
||||
p1[4] = 505;
|
||||
p1[5] = 509;
|
||||
p1[6] = 507;
|
||||
p1[7] = 510;
|
||||
p1[8] = 508;
|
||||
p1[9] = 509;
|
||||
p1[10] = 508;
|
||||
break;
|
||||
}
|
||||
fixup_8974_points(mode, p1, p2);
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(calib);
|
||||
@ -485,53 +243,19 @@ static int calibrate_8974(struct tsens_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_9607(struct tsens_priv *priv)
|
||||
{
|
||||
int base, i;
|
||||
u32 p1[5], p2[5];
|
||||
int mode = 0;
|
||||
u32 *qfprom_cdata;
|
||||
static int __init init_8939(struct tsens_priv *priv) {
|
||||
priv->sensor[0].slope = 2911;
|
||||
priv->sensor[1].slope = 2789;
|
||||
priv->sensor[2].slope = 2906;
|
||||
priv->sensor[3].slope = 2763;
|
||||
priv->sensor[4].slope = 2922;
|
||||
priv->sensor[5].slope = 2867;
|
||||
priv->sensor[6].slope = 2833;
|
||||
priv->sensor[7].slope = 2838;
|
||||
priv->sensor[8].slope = 2840;
|
||||
/* priv->sensor[9].slope = 2852; */
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
return PTR_ERR(qfprom_cdata);
|
||||
|
||||
mode = (qfprom_cdata[2] & MDM9607_CAL_SEL_MASK) >> MDM9607_CAL_SEL_SHIFT;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base = (qfprom_cdata[2] & MDM9607_BASE1_MASK) >> MDM9607_BASE1_SHIFT;
|
||||
p2[0] = (qfprom_cdata[0] & MDM9607_S0_P2_MASK) >> MDM9607_S0_P2_SHIFT;
|
||||
p2[1] = (qfprom_cdata[0] & MDM9607_S1_P2_MASK) >> MDM9607_S1_P2_SHIFT;
|
||||
p2[2] = (qfprom_cdata[1] & MDM9607_S2_P2_MASK) >> MDM9607_S2_P2_SHIFT;
|
||||
p2[3] = (qfprom_cdata[1] & MDM9607_S3_P2_MASK) >> MDM9607_S3_P2_SHIFT;
|
||||
p2[4] = (qfprom_cdata[2] & MDM9607_S4_P2_MASK) >> MDM9607_S4_P2_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = ((base + p2[i]) << 2);
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base = (qfprom_cdata[0] & MDM9607_BASE0_MASK);
|
||||
p1[0] = (qfprom_cdata[0] & MDM9607_S0_P1_MASK) >> MDM9607_S0_P1_SHIFT;
|
||||
p1[1] = (qfprom_cdata[0] & MDM9607_S1_P1_MASK) >> MDM9607_S1_P1_SHIFT;
|
||||
p1[2] = (qfprom_cdata[1] & MDM9607_S2_P1_MASK) >> MDM9607_S2_P1_SHIFT;
|
||||
p1[3] = (qfprom_cdata[1] & MDM9607_S3_P1_MASK) >> MDM9607_S3_P1_SHIFT;
|
||||
p1[4] = (qfprom_cdata[2] & MDM9607_S4_P1_MASK) >> MDM9607_S4_P1_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = ((base + p1[i]) << 2);
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
|
||||
return 0;
|
||||
return init_common(priv);
|
||||
}
|
||||
|
||||
/* v0.1: 8916, 8939, 8974, 9607 */
|
||||
@ -583,6 +307,12 @@ static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
|
||||
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_v0_1 = {
|
||||
.init = init_common,
|
||||
.calibrate = tsens_calibrate_common,
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8916 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_8916,
|
||||
@ -599,15 +329,15 @@ struct tsens_plat_data data_8916 = {
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8939 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_8939,
|
||||
.init = init_8939,
|
||||
.calibrate = tsens_calibrate_common,
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
struct tsens_plat_data data_8939 = {
|
||||
.num_sensors = 10,
|
||||
.num_sensors = 9,
|
||||
.ops = &ops_8939,
|
||||
.hw_ids = (unsigned int []){ 0, 1, 2, 3, 5, 6, 7, 8, 9, 10 },
|
||||
.hw_ids = (unsigned int []){ 0, 1, 2, 3, 5, 6, 7, 8, 9, /* 10 */ },
|
||||
|
||||
.feat = &tsens_v0_1_feat,
|
||||
.fields = tsens_v0_1_regfields,
|
||||
@ -626,16 +356,9 @@ struct tsens_plat_data data_8974 = {
|
||||
.fields = tsens_v0_1_regfields,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_9607 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_9607,
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
struct tsens_plat_data data_9607 = {
|
||||
.num_sensors = 5,
|
||||
.ops = &ops_9607,
|
||||
.hw_ids = (unsigned int []){ 0, 1, 2, 3, 4 },
|
||||
.ops = &ops_v0_1,
|
||||
.feat = &tsens_v0_1_feat,
|
||||
.fields = tsens_v0_1_regfields,
|
||||
};
|
||||
|
@ -21,210 +21,66 @@
|
||||
#define TM_HIGH_LOW_INT_STATUS_OFF 0x0088
|
||||
#define TM_HIGH_LOW_Sn_INT_THRESHOLD_OFF 0x0090
|
||||
|
||||
/* eeprom layout data for msm8956/76 (v1) */
|
||||
#define MSM8976_BASE0_MASK 0xff
|
||||
#define MSM8976_BASE1_MASK 0xff
|
||||
#define MSM8976_BASE1_SHIFT 8
|
||||
struct tsens_legacy_calibration_format tsens_qcs404_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 4, 0 },
|
||||
.invalid = { 4, 2 },
|
||||
.base = { { 4, 3 }, { 4, 11 } },
|
||||
.sp = {
|
||||
{ { 0, 0 }, { 0, 6 } },
|
||||
{ { 0, 12 }, { 0, 18 } },
|
||||
{ { 0, 24 }, { 0, 30 } },
|
||||
{ { 1, 4 }, { 1, 10 } },
|
||||
{ { 1, 16 }, { 1, 22 } },
|
||||
{ { 2, 0 }, { 2, 6 } },
|
||||
{ { 2, 12 }, { 2, 18 } },
|
||||
{ { 2, 24 }, { 2, 30 } },
|
||||
{ { 3, 4 }, { 3, 10 } },
|
||||
{ { 3, 16 }, { 3, 22 } },
|
||||
},
|
||||
};
|
||||
|
||||
#define MSM8976_S0_P1_MASK 0x3f00
|
||||
#define MSM8976_S1_P1_MASK 0x3f00000
|
||||
#define MSM8976_S2_P1_MASK 0x3f
|
||||
#define MSM8976_S3_P1_MASK 0x3f000
|
||||
#define MSM8976_S4_P1_MASK 0x3f00
|
||||
#define MSM8976_S5_P1_MASK 0x3f00000
|
||||
#define MSM8976_S6_P1_MASK 0x3f
|
||||
#define MSM8976_S7_P1_MASK 0x3f000
|
||||
#define MSM8976_S8_P1_MASK 0x1f8
|
||||
#define MSM8976_S9_P1_MASK 0x1f8000
|
||||
#define MSM8976_S10_P1_MASK 0xf8000000
|
||||
#define MSM8976_S10_P1_MASK_1 0x1
|
||||
|
||||
#define MSM8976_S0_P2_MASK 0xfc000
|
||||
#define MSM8976_S1_P2_MASK 0xfc000000
|
||||
#define MSM8976_S2_P2_MASK 0xfc0
|
||||
#define MSM8976_S3_P2_MASK 0xfc0000
|
||||
#define MSM8976_S4_P2_MASK 0xfc000
|
||||
#define MSM8976_S5_P2_MASK 0xfc000000
|
||||
#define MSM8976_S6_P2_MASK 0xfc0
|
||||
#define MSM8976_S7_P2_MASK 0xfc0000
|
||||
#define MSM8976_S8_P2_MASK 0x7e00
|
||||
#define MSM8976_S9_P2_MASK 0x7e00000
|
||||
#define MSM8976_S10_P2_MASK 0x7e
|
||||
|
||||
#define MSM8976_S0_P1_SHIFT 8
|
||||
#define MSM8976_S1_P1_SHIFT 20
|
||||
#define MSM8976_S2_P1_SHIFT 0
|
||||
#define MSM8976_S3_P1_SHIFT 12
|
||||
#define MSM8976_S4_P1_SHIFT 8
|
||||
#define MSM8976_S5_P1_SHIFT 20
|
||||
#define MSM8976_S6_P1_SHIFT 0
|
||||
#define MSM8976_S7_P1_SHIFT 12
|
||||
#define MSM8976_S8_P1_SHIFT 3
|
||||
#define MSM8976_S9_P1_SHIFT 15
|
||||
#define MSM8976_S10_P1_SHIFT 27
|
||||
#define MSM8976_S10_P1_SHIFT_1 0
|
||||
|
||||
#define MSM8976_S0_P2_SHIFT 14
|
||||
#define MSM8976_S1_P2_SHIFT 26
|
||||
#define MSM8976_S2_P2_SHIFT 6
|
||||
#define MSM8976_S3_P2_SHIFT 18
|
||||
#define MSM8976_S4_P2_SHIFT 14
|
||||
#define MSM8976_S5_P2_SHIFT 26
|
||||
#define MSM8976_S6_P2_SHIFT 6
|
||||
#define MSM8976_S7_P2_SHIFT 18
|
||||
#define MSM8976_S8_P2_SHIFT 9
|
||||
#define MSM8976_S9_P2_SHIFT 21
|
||||
#define MSM8976_S10_P2_SHIFT 1
|
||||
|
||||
#define MSM8976_CAL_SEL_MASK 0x3
|
||||
|
||||
#define MSM8976_CAL_DEGC_PT1 30
|
||||
#define MSM8976_CAL_DEGC_PT2 120
|
||||
#define MSM8976_SLOPE_FACTOR 1000
|
||||
#define MSM8976_SLOPE_DEFAULT 3200
|
||||
|
||||
/* eeprom layout data for qcs404/405 (v1) */
|
||||
#define BASE0_MASK 0x000007f8
|
||||
#define BASE1_MASK 0x0007f800
|
||||
#define BASE0_SHIFT 3
|
||||
#define BASE1_SHIFT 11
|
||||
|
||||
#define S0_P1_MASK 0x0000003f
|
||||
#define S1_P1_MASK 0x0003f000
|
||||
#define S2_P1_MASK 0x3f000000
|
||||
#define S3_P1_MASK 0x000003f0
|
||||
#define S4_P1_MASK 0x003f0000
|
||||
#define S5_P1_MASK 0x0000003f
|
||||
#define S6_P1_MASK 0x0003f000
|
||||
#define S7_P1_MASK 0x3f000000
|
||||
#define S8_P1_MASK 0x000003f0
|
||||
#define S9_P1_MASK 0x003f0000
|
||||
|
||||
#define S0_P2_MASK 0x00000fc0
|
||||
#define S1_P2_MASK 0x00fc0000
|
||||
#define S2_P2_MASK_1_0 0xc0000000
|
||||
#define S2_P2_MASK_5_2 0x0000000f
|
||||
#define S3_P2_MASK 0x0000fc00
|
||||
#define S4_P2_MASK 0x0fc00000
|
||||
#define S5_P2_MASK 0x00000fc0
|
||||
#define S6_P2_MASK 0x00fc0000
|
||||
#define S7_P2_MASK_1_0 0xc0000000
|
||||
#define S7_P2_MASK_5_2 0x0000000f
|
||||
#define S8_P2_MASK 0x0000fc00
|
||||
#define S9_P2_MASK 0x0fc00000
|
||||
|
||||
#define S0_P1_SHIFT 0
|
||||
#define S0_P2_SHIFT 6
|
||||
#define S1_P1_SHIFT 12
|
||||
#define S1_P2_SHIFT 18
|
||||
#define S2_P1_SHIFT 24
|
||||
#define S2_P2_SHIFT_1_0 30
|
||||
|
||||
#define S2_P2_SHIFT_5_2 0
|
||||
#define S3_P1_SHIFT 4
|
||||
#define S3_P2_SHIFT 10
|
||||
#define S4_P1_SHIFT 16
|
||||
#define S4_P2_SHIFT 22
|
||||
|
||||
#define S5_P1_SHIFT 0
|
||||
#define S5_P2_SHIFT 6
|
||||
#define S6_P1_SHIFT 12
|
||||
#define S6_P2_SHIFT 18
|
||||
#define S7_P1_SHIFT 24
|
||||
#define S7_P2_SHIFT_1_0 30
|
||||
|
||||
#define S7_P2_SHIFT_5_2 0
|
||||
#define S8_P1_SHIFT 4
|
||||
#define S8_P2_SHIFT 10
|
||||
#define S9_P1_SHIFT 16
|
||||
#define S9_P2_SHIFT 22
|
||||
|
||||
#define CAL_SEL_MASK 7
|
||||
#define CAL_SEL_SHIFT 0
|
||||
|
||||
static void compute_intercept_slope_8976(struct tsens_priv *priv,
|
||||
u32 *p1, u32 *p2, u32 mode)
|
||||
{
|
||||
int i;
|
||||
|
||||
priv->sensor[0].slope = 3313;
|
||||
priv->sensor[1].slope = 3275;
|
||||
priv->sensor[2].slope = 3320;
|
||||
priv->sensor[3].slope = 3246;
|
||||
priv->sensor[4].slope = 3279;
|
||||
priv->sensor[5].slope = 3257;
|
||||
priv->sensor[6].slope = 3234;
|
||||
priv->sensor[7].slope = 3269;
|
||||
priv->sensor[8].slope = 3255;
|
||||
priv->sensor[9].slope = 3239;
|
||||
priv->sensor[10].slope = 3286;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
priv->sensor[i].offset = (p1[i] * MSM8976_SLOPE_FACTOR) -
|
||||
(MSM8976_CAL_DEGC_PT1 *
|
||||
priv->sensor[i].slope);
|
||||
}
|
||||
}
|
||||
struct tsens_legacy_calibration_format tsens_8976_nvmem = {
|
||||
.base_len = 8,
|
||||
.base_shift = 2,
|
||||
.sp_len = 6,
|
||||
.mode = { 4, 0 },
|
||||
.invalid = { 4, 2 },
|
||||
.base = { { 0, 0 }, { 2, 8 } },
|
||||
.sp = {
|
||||
{ { 0, 8 }, { 0, 14 } },
|
||||
{ { 0, 20 }, { 0, 26 } },
|
||||
{ { 1, 0 }, { 1, 6 } },
|
||||
{ { 1, 12 }, { 1, 18 } },
|
||||
{ { 2, 8 }, { 2, 14 } },
|
||||
{ { 2, 20 }, { 2, 26 } },
|
||||
{ { 3, 0 }, { 3, 6 } },
|
||||
{ { 3, 12 }, { 3, 18 } },
|
||||
{ { 4, 2 }, { 4, 9 } },
|
||||
{ { 4, 14 }, { 4, 21 } },
|
||||
{ { 4, 26 }, { 5, 1 } },
|
||||
},
|
||||
};
|
||||
|
||||
static int calibrate_v1(struct tsens_priv *priv)
|
||||
{
|
||||
u32 base0 = 0, base1 = 0;
|
||||
u32 p1[10], p2[10];
|
||||
u32 mode = 0, lsb = 0, msb = 0;
|
||||
u32 *qfprom_cdata;
|
||||
int i;
|
||||
int mode, ret;
|
||||
|
||||
ret = tsens_calibrate_common(priv);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
return PTR_ERR(qfprom_cdata);
|
||||
|
||||
mode = (qfprom_cdata[4] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base1 = (qfprom_cdata[4] & BASE1_MASK) >> BASE1_SHIFT;
|
||||
p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
|
||||
p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
|
||||
/* This value is split over two registers, 2 bits and 4 bits */
|
||||
lsb = (qfprom_cdata[0] & S2_P2_MASK_1_0) >> S2_P2_SHIFT_1_0;
|
||||
msb = (qfprom_cdata[1] & S2_P2_MASK_5_2) >> S2_P2_SHIFT_5_2;
|
||||
p2[2] = msb << 2 | lsb;
|
||||
p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
|
||||
p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
|
||||
p2[5] = (qfprom_cdata[2] & S5_P2_MASK) >> S5_P2_SHIFT;
|
||||
p2[6] = (qfprom_cdata[2] & S6_P2_MASK) >> S6_P2_SHIFT;
|
||||
/* This value is split over two registers, 2 bits and 4 bits */
|
||||
lsb = (qfprom_cdata[2] & S7_P2_MASK_1_0) >> S7_P2_SHIFT_1_0;
|
||||
msb = (qfprom_cdata[3] & S7_P2_MASK_5_2) >> S7_P2_SHIFT_5_2;
|
||||
p2[7] = msb << 2 | lsb;
|
||||
p2[8] = (qfprom_cdata[3] & S8_P2_MASK) >> S8_P2_SHIFT;
|
||||
p2[9] = (qfprom_cdata[3] & S9_P2_MASK) >> S9_P2_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = ((base1 + p2[i]) << 2);
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base0 = (qfprom_cdata[4] & BASE0_MASK) >> BASE0_SHIFT;
|
||||
p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
|
||||
p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
|
||||
p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
|
||||
p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
|
||||
p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
|
||||
p1[5] = (qfprom_cdata[2] & S5_P1_MASK) >> S5_P1_SHIFT;
|
||||
p1[6] = (qfprom_cdata[2] & S6_P1_MASK) >> S6_P1_SHIFT;
|
||||
p1[7] = (qfprom_cdata[2] & S7_P1_MASK) >> S7_P1_SHIFT;
|
||||
p1[8] = (qfprom_cdata[3] & S8_P1_MASK) >> S8_P1_SHIFT;
|
||||
p1[9] = (qfprom_cdata[3] & S9_P1_MASK) >> S9_P1_SHIFT;
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = (((base0) + p1[i]) << 2);
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
break;
|
||||
}
|
||||
mode = tsens_read_calibration_legacy(priv, &tsens_qcs404_nvmem,
|
||||
p1, p2,
|
||||
qfprom_cdata, NULL);
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
@ -232,71 +88,6 @@ static int calibrate_v1(struct tsens_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calibrate_8976(struct tsens_priv *priv)
|
||||
{
|
||||
int base0 = 0, base1 = 0, i;
|
||||
u32 p1[11], p2[11];
|
||||
int mode = 0, tmp = 0;
|
||||
u32 *qfprom_cdata;
|
||||
|
||||
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
|
||||
if (IS_ERR(qfprom_cdata))
|
||||
return PTR_ERR(qfprom_cdata);
|
||||
|
||||
mode = (qfprom_cdata[4] & MSM8976_CAL_SEL_MASK);
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
switch (mode) {
|
||||
case TWO_PT_CALIB:
|
||||
base1 = (qfprom_cdata[2] & MSM8976_BASE1_MASK) >> MSM8976_BASE1_SHIFT;
|
||||
p2[0] = (qfprom_cdata[0] & MSM8976_S0_P2_MASK) >> MSM8976_S0_P2_SHIFT;
|
||||
p2[1] = (qfprom_cdata[0] & MSM8976_S1_P2_MASK) >> MSM8976_S1_P2_SHIFT;
|
||||
p2[2] = (qfprom_cdata[1] & MSM8976_S2_P2_MASK) >> MSM8976_S2_P2_SHIFT;
|
||||
p2[3] = (qfprom_cdata[1] & MSM8976_S3_P2_MASK) >> MSM8976_S3_P2_SHIFT;
|
||||
p2[4] = (qfprom_cdata[2] & MSM8976_S4_P2_MASK) >> MSM8976_S4_P2_SHIFT;
|
||||
p2[5] = (qfprom_cdata[2] & MSM8976_S5_P2_MASK) >> MSM8976_S5_P2_SHIFT;
|
||||
p2[6] = (qfprom_cdata[3] & MSM8976_S6_P2_MASK) >> MSM8976_S6_P2_SHIFT;
|
||||
p2[7] = (qfprom_cdata[3] & MSM8976_S7_P2_MASK) >> MSM8976_S7_P2_SHIFT;
|
||||
p2[8] = (qfprom_cdata[4] & MSM8976_S8_P2_MASK) >> MSM8976_S8_P2_SHIFT;
|
||||
p2[9] = (qfprom_cdata[4] & MSM8976_S9_P2_MASK) >> MSM8976_S9_P2_SHIFT;
|
||||
p2[10] = (qfprom_cdata[5] & MSM8976_S10_P2_MASK) >> MSM8976_S10_P2_SHIFT;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = ((base1 + p2[i]) << 2);
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
base0 = qfprom_cdata[0] & MSM8976_BASE0_MASK;
|
||||
p1[0] = (qfprom_cdata[0] & MSM8976_S0_P1_MASK) >> MSM8976_S0_P1_SHIFT;
|
||||
p1[1] = (qfprom_cdata[0] & MSM8976_S1_P1_MASK) >> MSM8976_S1_P1_SHIFT;
|
||||
p1[2] = (qfprom_cdata[1] & MSM8976_S2_P1_MASK) >> MSM8976_S2_P1_SHIFT;
|
||||
p1[3] = (qfprom_cdata[1] & MSM8976_S3_P1_MASK) >> MSM8976_S3_P1_SHIFT;
|
||||
p1[4] = (qfprom_cdata[2] & MSM8976_S4_P1_MASK) >> MSM8976_S4_P1_SHIFT;
|
||||
p1[5] = (qfprom_cdata[2] & MSM8976_S5_P1_MASK) >> MSM8976_S5_P1_SHIFT;
|
||||
p1[6] = (qfprom_cdata[3] & MSM8976_S6_P1_MASK) >> MSM8976_S6_P1_SHIFT;
|
||||
p1[7] = (qfprom_cdata[3] & MSM8976_S7_P1_MASK) >> MSM8976_S7_P1_SHIFT;
|
||||
p1[8] = (qfprom_cdata[4] & MSM8976_S8_P1_MASK) >> MSM8976_S8_P1_SHIFT;
|
||||
p1[9] = (qfprom_cdata[4] & MSM8976_S9_P1_MASK) >> MSM8976_S9_P1_SHIFT;
|
||||
p1[10] = (qfprom_cdata[4] & MSM8976_S10_P1_MASK) >> MSM8976_S10_P1_SHIFT;
|
||||
tmp = (qfprom_cdata[5] & MSM8976_S10_P1_MASK_1) << MSM8976_S10_P1_SHIFT_1;
|
||||
p1[10] |= tmp;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = (((base0) + p1[i]) << 2);
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
compute_intercept_slope_8976(priv, p1, p2, mode);
|
||||
kfree(qfprom_cdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* v1.x: msm8956,8976,qcs404,405 */
|
||||
|
||||
static struct tsens_features tsens_v1_feat = {
|
||||
@ -365,6 +156,22 @@ static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
|
||||
[TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
|
||||
};
|
||||
|
||||
static int __init init_8956(struct tsens_priv *priv) {
|
||||
priv->sensor[0].slope = 3313;
|
||||
priv->sensor[1].slope = 3275;
|
||||
priv->sensor[2].slope = 3320;
|
||||
priv->sensor[3].slope = 3246;
|
||||
priv->sensor[4].slope = 3279;
|
||||
priv->sensor[5].slope = 3257;
|
||||
priv->sensor[6].slope = 3234;
|
||||
priv->sensor[7].slope = 3269;
|
||||
priv->sensor[8].slope = 3255;
|
||||
priv->sensor[9].slope = 3239;
|
||||
priv->sensor[10].slope = 3286;
|
||||
|
||||
return init_common(priv);
|
||||
}
|
||||
|
||||
static const struct tsens_ops ops_generic_v1 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_v1,
|
||||
@ -377,17 +184,28 @@ struct tsens_plat_data data_tsens_v1 = {
|
||||
.fields = tsens_v1_regfields,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8976 = {
|
||||
.init = init_common,
|
||||
.calibrate = calibrate_8976,
|
||||
static const struct tsens_ops ops_8956 = {
|
||||
.init = init_8956,
|
||||
.calibrate = tsens_calibrate_common,
|
||||
.get_temp = get_temp_tsens_valid,
|
||||
};
|
||||
|
||||
/* Valid for both MSM8956 and MSM8976. */
|
||||
struct tsens_plat_data data_8976 = {
|
||||
struct tsens_plat_data data_8956 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8976,
|
||||
.hw_ids = (unsigned int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
.ops = &ops_8956,
|
||||
.feat = &tsens_v1_feat,
|
||||
.fields = tsens_v1_regfields,
|
||||
};
|
||||
|
||||
static const struct tsens_ops ops_8976 = {
|
||||
.init = init_common,
|
||||
.calibrate = tsens_calibrate_common,
|
||||
.get_temp = get_temp_tsens_valid,
|
||||
};
|
||||
|
||||
struct tsens_plat_data data_8976 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8976,
|
||||
.feat = &tsens_v1_feat,
|
||||
.fields = tsens_v1_regfields,
|
||||
};
|
||||
|
@ -70,6 +70,171 @@ char *qfprom_read(struct device *dev, const char *cname)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup)
|
||||
{
|
||||
u32 mode;
|
||||
u32 base1, base2;
|
||||
char name[] = "sXX_pY_backup"; /* s10_p1_backup */
|
||||
int i, ret;
|
||||
|
||||
if (priv->num_sensors > MAX_SENSORS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = snprintf(name, sizeof(name), "mode%s", backup ? "_backup" : "");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &mode);
|
||||
if (ret == -ENOENT)
|
||||
dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
ret = snprintf(name, sizeof(name), "base1%s", backup ? "_backup" : "");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snprintf(name, sizeof(name), "base2%s", backup ? "_backup" : "");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
ret = snprintf(name, sizeof(name), "s%d_p1%s", priv->sensor[i].hw_id,
|
||||
backup ? "_backup" : "");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p1[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snprintf(name, sizeof(name), "s%d_p2%s", priv->sensor[i].hw_id,
|
||||
backup ? "_backup" : "");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p2[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case ONE_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = p1[i] + (base1 << shift);
|
||||
break;
|
||||
case TWO_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = (p2[i] + base2) << shift;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = (p1[i] + base1) << shift;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(priv->dev, "calibrationless mode\n");
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift)
|
||||
{
|
||||
u32 p1[MAX_SENSORS], p2[MAX_SENSORS];
|
||||
int mode;
|
||||
|
||||
mode = tsens_read_calibration(priv, shift, p1, p2, false);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
compute_intercept_slope(priv, p1, p2, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsens_calibrate_common(struct tsens_priv *priv)
|
||||
{
|
||||
return tsens_calibrate_nvmem(priv, 2);
|
||||
}
|
||||
|
||||
static u32 tsens_read_cell(const struct tsens_single_value *cell, u8 len, u32 *data0, u32 *data1)
|
||||
{
|
||||
u32 val;
|
||||
u32 *data = cell->blob ? data1 : data0;
|
||||
|
||||
if (cell->shift + len <= 32) {
|
||||
val = data[cell->idx] >> cell->shift;
|
||||
} else {
|
||||
u8 part = 32 - cell->shift;
|
||||
|
||||
val = data[cell->idx] >> cell->shift;
|
||||
val |= data[cell->idx + 1] << part;
|
||||
}
|
||||
|
||||
return val & ((1 << len) - 1);
|
||||
}
|
||||
|
||||
int tsens_read_calibration_legacy(struct tsens_priv *priv,
|
||||
const struct tsens_legacy_calibration_format *format,
|
||||
u32 *p1, u32 *p2,
|
||||
u32 *cdata0, u32 *cdata1)
|
||||
{
|
||||
u32 mode, invalid;
|
||||
u32 base1, base2;
|
||||
int i;
|
||||
|
||||
mode = tsens_read_cell(&format->mode, 2, cdata0, cdata1);
|
||||
invalid = tsens_read_cell(&format->invalid, 1, cdata0, cdata1);
|
||||
if (invalid)
|
||||
mode = NO_PT_CALIB;
|
||||
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
|
||||
|
||||
base1 = tsens_read_cell(&format->base[0], format->base_len, cdata0, cdata1);
|
||||
base2 = tsens_read_cell(&format->base[1], format->base_len, cdata0, cdata1);
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = tsens_read_cell(&format->sp[i][0], format->sp_len, cdata0, cdata1);
|
||||
p2[i] = tsens_read_cell(&format->sp[i][1], format->sp_len, cdata0, cdata1);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case ONE_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = p1[i] + (base1 << format->base_shift);
|
||||
break;
|
||||
case TWO_PT_CALIB:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p2[i] = (p2[i] + base2) << format->base_shift;
|
||||
fallthrough;
|
||||
case ONE_PT_CALIB2:
|
||||
for (i = 0; i < priv->num_sensors; i++)
|
||||
p1[i] = (p1[i] + base1) << format->base_shift;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(priv->dev, "calibrationless mode\n");
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
p1[i] = 500;
|
||||
p2[i] = 780;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this function on devices where slope and offset calculations
|
||||
* depend on calibration data read from qfprom. On others the slope
|
||||
@ -459,12 +624,9 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct tsens_priv *priv = data;
|
||||
struct tsens_irq_data d;
|
||||
bool enable = true, disable = false;
|
||||
unsigned long flags;
|
||||
int temp, ret, i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
bool trigger = false;
|
||||
const struct tsens_sensor *s = &priv->sensor[i];
|
||||
u32 hw_id = s->hw_id;
|
||||
|
||||
@ -472,52 +634,8 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
continue;
|
||||
if (!tsens_threshold_violated(priv, hw_id, &d))
|
||||
continue;
|
||||
ret = get_temp_tsens_valid(s, &temp);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "[%u] %s: error reading sensor\n",
|
||||
hw_id, __func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv->ul_lock, flags);
|
||||
|
||||
tsens_read_irq_state(priv, hw_id, s, &d);
|
||||
|
||||
if (d.up_viol &&
|
||||
!masked_irq(hw_id, d.up_irq_mask, tsens_version(priv))) {
|
||||
tsens_set_interrupt(priv, hw_id, UPPER, disable);
|
||||
if (d.up_thresh > temp) {
|
||||
dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
|
||||
hw_id, __func__);
|
||||
tsens_set_interrupt(priv, hw_id, UPPER, enable);
|
||||
} else {
|
||||
trigger = true;
|
||||
/* Keep irq masked */
|
||||
}
|
||||
} else if (d.low_viol &&
|
||||
!masked_irq(hw_id, d.low_irq_mask, tsens_version(priv))) {
|
||||
tsens_set_interrupt(priv, hw_id, LOWER, disable);
|
||||
if (d.low_thresh < temp) {
|
||||
dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
|
||||
hw_id, __func__);
|
||||
tsens_set_interrupt(priv, hw_id, LOWER, enable);
|
||||
} else {
|
||||
trigger = true;
|
||||
/* Keep irq masked */
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&priv->ul_lock, flags);
|
||||
|
||||
if (trigger) {
|
||||
dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
|
||||
hw_id, __func__, temp);
|
||||
thermal_zone_device_update(s->tzd,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
} else {
|
||||
dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
|
||||
hw_id, __func__, temp);
|
||||
}
|
||||
thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
if (tsens_version(priv) < VER_0_1) {
|
||||
/* Constraint: There is only 1 interrupt control register for all
|
||||
@ -983,6 +1101,9 @@ static const struct of_device_id tsens_table[] = {
|
||||
}, {
|
||||
.compatible = "qcom,msm8939-tsens",
|
||||
.data = &data_8939,
|
||||
}, {
|
||||
.compatible = "qcom,msm8956-tsens",
|
||||
.data = &data_8956,
|
||||
}, {
|
||||
.compatible = "qcom,msm8960-tsens",
|
||||
.data = &data_8960,
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef __QCOM_TSENS_H__
|
||||
#define __QCOM_TSENS_H__
|
||||
|
||||
#define NO_PT_CALIB 0x0
|
||||
#define ONE_PT_CALIB 0x1
|
||||
#define ONE_PT_CALIB2 0x2
|
||||
#define TWO_PT_CALIB 0x3
|
||||
@ -17,6 +18,8 @@
|
||||
#define THRESHOLD_MAX_ADC_CODE 0x3ff
|
||||
#define THRESHOLD_MIN_ADC_CODE 0x0
|
||||
|
||||
#define MAX_SENSORS 16
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -581,7 +584,48 @@ struct tsens_priv {
|
||||
struct tsens_sensor sensor[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tsens_single_value - internal representation of a single field inside nvmem calibration data
|
||||
* @idx: index into the u32 data array
|
||||
* @shift: the shift of the first bit in the value
|
||||
* @blob: index of the data blob to use for this cell
|
||||
*/
|
||||
struct tsens_single_value {
|
||||
u8 idx;
|
||||
u8 shift;
|
||||
u8 blob;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tsens_legacy_calibration_format - description of calibration data used when parsing the legacy nvmem blob
|
||||
* @base_len: the length of the base fields inside calibration data
|
||||
* @base_shift: the shift to be applied to base data
|
||||
* @sp_len: the length of the sN_pM fields inside calibration data
|
||||
* @mode: descriptor of the calibration mode field
|
||||
* @invalid: descriptor of the calibration mode invalid field
|
||||
* @base: descriptors of the base0 and base1 fields
|
||||
* @sp: descriptors of the sN_pM fields
|
||||
*/
|
||||
struct tsens_legacy_calibration_format {
|
||||
unsigned int base_len;
|
||||
unsigned int base_shift;
|
||||
unsigned int sp_len;
|
||||
/* just two bits */
|
||||
struct tsens_single_value mode;
|
||||
/* on all platforms except 8974 invalid is the third bit of what downstream calls 'mode' */
|
||||
struct tsens_single_value invalid;
|
||||
struct tsens_single_value base[2];
|
||||
struct tsens_single_value sp[][2];
|
||||
};
|
||||
|
||||
char *qfprom_read(struct device *dev, const char *cname);
|
||||
int tsens_read_calibration_legacy(struct tsens_priv *priv,
|
||||
const struct tsens_legacy_calibration_format *format,
|
||||
u32 *p1, u32 *p2,
|
||||
u32 *cdata, u32 *csel);
|
||||
int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup);
|
||||
int tsens_calibrate_nvmem(struct tsens_priv *priv, int shift);
|
||||
int tsens_calibrate_common(struct tsens_priv *priv);
|
||||
void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
|
||||
int init_common(struct tsens_priv *priv);
|
||||
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
|
||||
@ -594,7 +638,7 @@ extern struct tsens_plat_data data_8960;
|
||||
extern struct tsens_plat_data data_8916, data_8939, data_8974, data_9607;
|
||||
|
||||
/* TSENS v1 targets */
|
||||
extern struct tsens_plat_data data_tsens_v1, data_8976;
|
||||
extern struct tsens_plat_data data_tsens_v1, data_8976, data_8956;
|
||||
|
||||
/* TSENS v2 targets */
|
||||
extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
#define SITES_MAX 16
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
/* Register offsets */
|
||||
@ -87,8 +86,10 @@ struct rcar_gen3_thermal_tsc {
|
||||
|
||||
struct rcar_gen3_thermal_priv {
|
||||
struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
|
||||
struct thermal_zone_device_ops ops;
|
||||
unsigned int num_tscs;
|
||||
void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc);
|
||||
void (*thermal_init)(struct rcar_gen3_thermal_priv *priv,
|
||||
struct rcar_gen3_thermal_tsc *tsc);
|
||||
int ptat[3];
|
||||
};
|
||||
|
||||
@ -225,7 +226,7 @@ static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {
|
||||
static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {
|
||||
.get_temp = rcar_gen3_thermal_get_temp,
|
||||
.set_trips = rcar_gen3_thermal_set_trips,
|
||||
};
|
||||
@ -239,7 +240,7 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
|
||||
for (i = 0; i < priv->num_tscs; i++) {
|
||||
status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
|
||||
rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
|
||||
if (status)
|
||||
if (status && priv->tscs[i]->zone)
|
||||
thermal_zone_device_update(priv->tscs[i]->zone,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
@ -310,7 +311,8 @@ static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
|
||||
static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_priv *priv,
|
||||
struct rcar_gen3_thermal_tsc *tsc)
|
||||
{
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR);
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0);
|
||||
@ -321,7 +323,7 @@ static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
|
||||
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
|
||||
if (tsc->zone->ops->set_trips)
|
||||
if (priv->ops.set_trips)
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
|
||||
IRQ_TEMPD1 | IRQ_TEMP2);
|
||||
|
||||
@ -337,7 +339,8 @@ static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
|
||||
static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,
|
||||
struct rcar_gen3_thermal_tsc *tsc)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
@ -349,7 +352,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
|
||||
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
|
||||
if (tsc->zone->ops->set_trips)
|
||||
if (priv->ops.set_trips)
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
|
||||
IRQ_TEMPD1 | IRQ_TEMP2);
|
||||
|
||||
@ -403,6 +406,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
|
||||
.compatible = "renesas,r8a779f0-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r8a779g0-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
|
||||
@ -466,6 +473,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->ops = rcar_gen3_tz_of_ops;
|
||||
priv->thermal_init = rcar_gen3_thermal_init;
|
||||
if (soc_device_match(r8a7795es1))
|
||||
priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1;
|
||||
@ -473,7 +481,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
if (rcar_gen3_thermal_request_irqs(priv, pdev))
|
||||
rcar_gen3_tz_of_ops.set_trips = NULL;
|
||||
priv->ops.set_trips = NULL;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
@ -508,8 +516,10 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < priv->num_tscs; i++) {
|
||||
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
|
||||
|
||||
zone = devm_thermal_of_zone_register(dev, i, tsc,
|
||||
&rcar_gen3_tz_of_ops);
|
||||
priv->thermal_init(priv, tsc);
|
||||
rcar_gen3_thermal_calc_coefs(priv, tsc, *ths_tj_1);
|
||||
|
||||
zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops);
|
||||
if (IS_ERR(zone)) {
|
||||
dev_err(dev, "Sensor %u: Can't register thermal zone\n", i);
|
||||
ret = PTR_ERR(zone);
|
||||
@ -517,9 +527,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
}
|
||||
tsc->zone = zone;
|
||||
|
||||
priv->thermal_init(tsc);
|
||||
rcar_gen3_thermal_calc_coefs(priv, tsc, *ths_tj_1);
|
||||
|
||||
tsc->zone->tzp->no_hwmon = false;
|
||||
ret = thermal_add_hwmon_sysfs(tsc->zone);
|
||||
if (ret)
|
||||
@ -529,7 +536,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
|
||||
ret = of_thermal_get_ntrips(tsc->zone);
|
||||
ret = thermal_zone_get_num_trips(tsc->zone);
|
||||
if (ret < 0)
|
||||
goto error_unregister;
|
||||
|
||||
@ -556,12 +563,8 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
|
||||
|
||||
for (i = 0; i < priv->num_tscs; i++) {
|
||||
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
|
||||
struct thermal_zone_device *zone = tsc->zone;
|
||||
|
||||
priv->thermal_init(tsc);
|
||||
if (zone->ops->set_trips)
|
||||
rcar_gen3_thermal_set_trips(zone, zone->prev_low_trip,
|
||||
zone->prev_high_trip);
|
||||
priv->thermal_init(priv, tsc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -278,52 +278,12 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp)
|
||||
return rcar_thermal_get_current_temp(priv, temp);
|
||||
}
|
||||
|
||||
static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
|
||||
struct device *dev = rcar_priv_to_dev(priv);
|
||||
|
||||
/* see rcar_thermal_get_temp() */
|
||||
switch (trip) {
|
||||
case 0: /* +90 <= temp */
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "rcar driver trip error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
|
||||
struct device *dev = rcar_priv_to_dev(priv);
|
||||
|
||||
/* see rcar_thermal_get_temp() */
|
||||
switch (trip) {
|
||||
case 0: /* +90 <= temp */
|
||||
*temp = MCELSIUS(90);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "rcar driver trip error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_device_ops rcar_thermal_zone_of_ops = {
|
||||
static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
|
||||
.get_temp = rcar_thermal_get_temp,
|
||||
};
|
||||
|
||||
static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
|
||||
.get_temp = rcar_thermal_get_temp,
|
||||
.get_trip_type = rcar_thermal_get_trip_type,
|
||||
.get_trip_temp = rcar_thermal_get_trip_temp,
|
||||
static struct thermal_trip trips[] = {
|
||||
{ .type = THERMAL_TRIP_CRITICAL, .temperature = 90000 }
|
||||
};
|
||||
|
||||
/*
|
||||
@ -529,11 +489,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
if (chip->use_of_thermal) {
|
||||
priv->zone = devm_thermal_of_zone_register(
|
||||
dev, i, priv,
|
||||
&rcar_thermal_zone_of_ops);
|
||||
&rcar_thermal_zone_ops);
|
||||
} else {
|
||||
priv->zone = thermal_zone_device_register(
|
||||
"rcar_thermal",
|
||||
1, 0, priv,
|
||||
priv->zone = thermal_zone_device_register_with_trips(
|
||||
"rcar_thermal", trips, ARRAY_SIZE(trips), 0, priv,
|
||||
&rcar_thermal_zone_ops, NULL, 0,
|
||||
idle);
|
||||
|
||||
|
@ -60,7 +60,7 @@ enum adc_sort_mode {
|
||||
|
||||
#include "thermal_hwmon.h"
|
||||
|
||||
/**
|
||||
/*
|
||||
* The max sensors is two in rockchip SoCs.
|
||||
* Two sensors: CPU and GPU sensor.
|
||||
*/
|
||||
@ -169,7 +169,7 @@ struct rockchip_thermal_data {
|
||||
enum tshut_polarity tshut_polarity;
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* TSADC Sensor Register description:
|
||||
*
|
||||
* TSADCV2_* are used for RK3288 SoCs, the other chips can reuse it.
|
||||
@ -1339,7 +1339,7 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset TSADC Controller, reset all tsadc registers.
|
||||
* rockchip_thermal_reset_controller - Reset TSADC Controller, reset all tsadc registers.
|
||||
* @reset: the reset controller of tsadc
|
||||
*/
|
||||
static void rockchip_thermal_reset_controller(struct reset_control *reset)
|
||||
@ -1354,7 +1354,6 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct rockchip_thermal_data *thermal;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int i;
|
||||
int error;
|
||||
@ -1378,8 +1377,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
|
||||
if (!thermal->chip)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
thermal->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
thermal->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(thermal->regs))
|
||||
return PTR_ERR(thermal->regs);
|
||||
|
||||
|
@ -20,11 +20,10 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <dt-bindings/thermal/thermal_exynos.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
|
||||
/* Exynos generic registers */
|
||||
#define EXYNOS_TMU_REG_TRIMINFO 0x0
|
||||
#define EXYNOS_TMU_REG_CONTROL 0x20
|
||||
@ -260,31 +259,23 @@ 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;
|
||||
const struct thermal_trip * const trips =
|
||||
of_thermal_get_trip_points(tzd);
|
||||
int num_trips = thermal_zone_get_num_trips(tzd);
|
||||
unsigned int status;
|
||||
int ret = 0, temp, hyst;
|
||||
int ret = 0, temp;
|
||||
|
||||
if (!trips) {
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot get trip points from device tree!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */
|
||||
ret = tzd->ops->get_crit_temp(tzd, &temp);
|
||||
if (ret) {
|
||||
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 (of_thermal_get_ntrips(tzd) > data->ntrip) {
|
||||
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",
|
||||
(of_thermal_get_ntrips(tzd) - data->ntrip));
|
||||
num_trips - data->ntrip);
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
@ -297,25 +288,22 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
int i, ntrips =
|
||||
min_t(int, of_thermal_get_ntrips(tzd), data->ntrip);
|
||||
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++) {
|
||||
/* Write temperature code for rising threshold */
|
||||
ret = tzd->ops->get_trip_temp(tzd, i, &temp);
|
||||
if (ret)
|
||||
goto err;
|
||||
temp /= MCELSIUS;
|
||||
data->tmu_set_trip_temp(data, i, temp);
|
||||
|
||||
/* Write temperature code for falling threshold */
|
||||
ret = tzd->ops->get_trip_hyst(tzd, i, &hyst);
|
||||
struct thermal_trip trip;
|
||||
|
||||
ret = thermal_zone_get_trip(tzd, i, &trip);
|
||||
if (ret)
|
||||
goto err;
|
||||
hyst /= MCELSIUS;
|
||||
data->tmu_set_trip_hyst(data, i, temp, hyst);
|
||||
|
||||
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);
|
||||
@ -360,21 +348,23 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
|
||||
}
|
||||
|
||||
static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data,
|
||||
int trip, u8 temp)
|
||||
int trip_id, u8 temp)
|
||||
{
|
||||
const struct thermal_trip * const trips =
|
||||
of_thermal_get_trip_points(data->tzd);
|
||||
struct thermal_trip trip;
|
||||
u8 ref, th_code;
|
||||
|
||||
ref = trips[0].temperature / MCELSIUS;
|
||||
if (thermal_zone_get_trip(data->tzd, 0, &trip))
|
||||
return;
|
||||
|
||||
if (trip == 0) {
|
||||
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 * 4);
|
||||
writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip_id * 4);
|
||||
}
|
||||
|
||||
/* failing thresholds are not supported on Exynos4210 */
|
||||
@ -562,13 +552,14 @@ 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;
|
||||
|
||||
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
|
||||
|
||||
if (on) {
|
||||
for (i = 0; i < data->ntrip; i++) {
|
||||
if (!of_thermal_is_trip_valid(tz, i))
|
||||
if (thermal_zone_get_trip(tz, i, &trip))
|
||||
continue;
|
||||
|
||||
interrupt_en |=
|
||||
@ -592,13 +583,14 @@ 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;
|
||||
|
||||
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
|
||||
|
||||
if (on) {
|
||||
for (i = 0; i < data->ntrip; i++) {
|
||||
if (!of_thermal_is_trip_valid(tz, i))
|
||||
if (thermal_zone_get_trip(tz, i, &trip))
|
||||
continue;
|
||||
|
||||
interrupt_en |=
|
||||
@ -623,13 +615,14 @@ 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;
|
||||
|
||||
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
|
||||
|
||||
if (on) {
|
||||
for (i = 0; i < data->ntrip; i++) {
|
||||
if (!of_thermal_is_trip_valid(tz, i))
|
||||
if (thermal_zone_get_trip(tz, i, &trip))
|
||||
continue;
|
||||
|
||||
interrupt_en |=
|
||||
|
@ -91,7 +91,6 @@ static int spear_thermal_probe(struct platform_device *pdev)
|
||||
struct thermal_zone_device *spear_thermal = NULL;
|
||||
struct spear_thermal_dev *stdev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
int ret = 0, val;
|
||||
|
||||
if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
|
||||
@ -104,8 +103,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Enable thermal sensor */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
stdev->thermal_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
stdev->thermal_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(stdev->thermal_base))
|
||||
return PTR_ERR(stdev->thermal_base);
|
||||
|
||||
|
@ -8,10 +8,6 @@ config ST_THERMAL
|
||||
help
|
||||
Support for thermal sensors on STMicroelectronics STi series of SoCs.
|
||||
|
||||
config ST_THERMAL_SYSCFG
|
||||
select ST_THERMAL
|
||||
tristate "STi series syscfg register access based thermal sensors"
|
||||
|
||||
config ST_THERMAL_MEMMAP
|
||||
select ST_THERMAL
|
||||
tristate "STi series memory mapped access based thermal sensors"
|
||||
|
@ -1,5 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_ST_THERMAL) := st_thermal.o
|
||||
obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o
|
||||
obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o
|
||||
obj-$(CONFIG_STM32_THERMAL) += stm_thermal.o
|
||||
|
@ -134,48 +134,12 @@ static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_thermal_get_trip_type(struct thermal_zone_device *th,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct st_thermal_sensor *sensor = th->devdata;
|
||||
struct device *dev = sensor->dev;
|
||||
|
||||
switch (trip) {
|
||||
case 0:
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid trip point\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_thermal_get_trip_temp(struct thermal_zone_device *th,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct st_thermal_sensor *sensor = th->devdata;
|
||||
struct device *dev = sensor->dev;
|
||||
|
||||
switch (trip) {
|
||||
case 0:
|
||||
*temp = mcelsius(sensor->cdata->crit_temp);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid trip point\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops st_tz_ops = {
|
||||
.get_temp = st_thermal_get_temp,
|
||||
.get_trip_type = st_thermal_get_trip_type,
|
||||
.get_trip_temp = st_thermal_get_trip_temp,
|
||||
};
|
||||
|
||||
static struct thermal_trip trip;
|
||||
|
||||
int st_thermal_register(struct platform_device *pdev,
|
||||
const struct of_device_id *st_thermal_of_match)
|
||||
{
|
||||
@ -238,9 +202,12 @@ int st_thermal_register(struct platform_device *pdev,
|
||||
|
||||
polling_delay = sensor->ops->register_enable_irq ? 0 : 1000;
|
||||
|
||||
trip.temperature = sensor->cdata->crit_temp;
|
||||
trip.type = THERMAL_TRIP_CRITICAL;
|
||||
|
||||
sensor->thermal_dev =
|
||||
thermal_zone_device_register(dev_name(dev), 1, 0, sensor,
|
||||
&st_tz_ops, NULL, 0, polling_delay);
|
||||
thermal_zone_device_register_with_trips(dev_name(dev), &trip, 1, 0, sensor,
|
||||
&st_tz_ops, NULL, 0, polling_delay);
|
||||
if (IS_ERR(sensor->thermal_dev)) {
|
||||
dev_err(dev, "failed to register thermal zone device\n");
|
||||
ret = PTR_ERR(sensor->thermal_dev);
|
||||
|
@ -1,174 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* ST Thermal Sensor Driver for syscfg based sensors.
|
||||
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
|
||||
*
|
||||
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "st_thermal.h"
|
||||
|
||||
/* STiH415 */
|
||||
#define STIH415_SYSCFG_FRONT(num) ((num - 100) * 4)
|
||||
#define STIH415_SAS_THSENS_CONF STIH415_SYSCFG_FRONT(178)
|
||||
#define STIH415_SAS_THSENS_STATUS STIH415_SYSCFG_FRONT(198)
|
||||
#define STIH415_SYSCFG_MPE(num) ((num - 600) * 4)
|
||||
#define STIH415_MPE_THSENS_CONF STIH415_SYSCFG_MPE(607)
|
||||
#define STIH415_MPE_THSENS_STATUS STIH415_SYSCFG_MPE(667)
|
||||
|
||||
/* STiH416 */
|
||||
#define STIH416_SYSCFG_FRONT(num) ((num - 1000) * 4)
|
||||
#define STIH416_SAS_THSENS_CONF STIH416_SYSCFG_FRONT(1552)
|
||||
#define STIH416_SAS_THSENS_STATUS1 STIH416_SYSCFG_FRONT(1554)
|
||||
#define STIH416_SAS_THSENS_STATUS2 STIH416_SYSCFG_FRONT(1594)
|
||||
|
||||
/* STiD127 */
|
||||
#define STID127_SYSCFG_CPU(num) ((num - 700) * 4)
|
||||
#define STID127_THSENS_CONF STID127_SYSCFG_CPU(743)
|
||||
#define STID127_THSENS_STATUS STID127_SYSCFG_CPU(767)
|
||||
|
||||
static const struct reg_field st_415sas_regfields[MAX_REGFIELDS] = {
|
||||
[TEMP_PWR] = REG_FIELD(STIH415_SAS_THSENS_CONF, 9, 9),
|
||||
[DCORRECT] = REG_FIELD(STIH415_SAS_THSENS_CONF, 4, 8),
|
||||
[OVERFLOW] = REG_FIELD(STIH415_SAS_THSENS_STATUS, 8, 8),
|
||||
[DATA] = REG_FIELD(STIH415_SAS_THSENS_STATUS, 10, 16),
|
||||
};
|
||||
|
||||
static const struct reg_field st_415mpe_regfields[MAX_REGFIELDS] = {
|
||||
[TEMP_PWR] = REG_FIELD(STIH415_MPE_THSENS_CONF, 8, 8),
|
||||
[DCORRECT] = REG_FIELD(STIH415_MPE_THSENS_CONF, 3, 7),
|
||||
[OVERFLOW] = REG_FIELD(STIH415_MPE_THSENS_STATUS, 9, 9),
|
||||
[DATA] = REG_FIELD(STIH415_MPE_THSENS_STATUS, 11, 18),
|
||||
};
|
||||
|
||||
static const struct reg_field st_416sas_regfields[MAX_REGFIELDS] = {
|
||||
[TEMP_PWR] = REG_FIELD(STIH416_SAS_THSENS_CONF, 9, 9),
|
||||
[DCORRECT] = REG_FIELD(STIH416_SAS_THSENS_CONF, 4, 8),
|
||||
[OVERFLOW] = REG_FIELD(STIH416_SAS_THSENS_STATUS1, 8, 8),
|
||||
[DATA] = REG_FIELD(STIH416_SAS_THSENS_STATUS2, 10, 16),
|
||||
};
|
||||
|
||||
static const struct reg_field st_127_regfields[MAX_REGFIELDS] = {
|
||||
[TEMP_PWR] = REG_FIELD(STID127_THSENS_CONF, 7, 7),
|
||||
[DCORRECT] = REG_FIELD(STID127_THSENS_CONF, 2, 6),
|
||||
[OVERFLOW] = REG_FIELD(STID127_THSENS_STATUS, 9, 9),
|
||||
[DATA] = REG_FIELD(STID127_THSENS_STATUS, 11, 18),
|
||||
};
|
||||
|
||||
/* Private OPs for System Configuration Register based thermal sensors */
|
||||
static int st_syscfg_power_ctrl(struct st_thermal_sensor *sensor,
|
||||
enum st_thermal_power_state power_state)
|
||||
{
|
||||
return regmap_field_write(sensor->pwr, power_state);
|
||||
}
|
||||
|
||||
static int st_syscfg_alloc_regfields(struct st_thermal_sensor *sensor)
|
||||
{
|
||||
struct device *dev = sensor->dev;
|
||||
|
||||
sensor->pwr = devm_regmap_field_alloc(dev, sensor->regmap,
|
||||
sensor->cdata->reg_fields[TEMP_PWR]);
|
||||
|
||||
if (IS_ERR(sensor->pwr)) {
|
||||
dev_err(dev, "failed to alloc syscfg regfields\n");
|
||||
return PTR_ERR(sensor->pwr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_syscfg_regmap_init(struct st_thermal_sensor *sensor)
|
||||
{
|
||||
sensor->regmap =
|
||||
syscon_regmap_lookup_by_compatible(sensor->cdata->sys_compat);
|
||||
if (IS_ERR(sensor->regmap)) {
|
||||
dev_err(sensor->dev, "failed to find syscfg regmap\n");
|
||||
return PTR_ERR(sensor->regmap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct st_thermal_sensor_ops st_syscfg_sensor_ops = {
|
||||
.power_ctrl = st_syscfg_power_ctrl,
|
||||
.alloc_regfields = st_syscfg_alloc_regfields,
|
||||
.regmap_init = st_syscfg_regmap_init,
|
||||
};
|
||||
|
||||
/* Compatible device data for stih415 sas thermal sensor */
|
||||
static const struct st_thermal_compat_data st_415sas_cdata = {
|
||||
.sys_compat = "st,stih415-front-syscfg",
|
||||
.reg_fields = st_415sas_regfields,
|
||||
.ops = &st_syscfg_sensor_ops,
|
||||
.calibration_val = 16,
|
||||
.temp_adjust_val = 20,
|
||||
.crit_temp = 120,
|
||||
};
|
||||
|
||||
/* Compatible device data for stih415 mpe thermal sensor */
|
||||
static const struct st_thermal_compat_data st_415mpe_cdata = {
|
||||
.sys_compat = "st,stih415-system-syscfg",
|
||||
.reg_fields = st_415mpe_regfields,
|
||||
.ops = &st_syscfg_sensor_ops,
|
||||
.calibration_val = 16,
|
||||
.temp_adjust_val = -103,
|
||||
.crit_temp = 120,
|
||||
};
|
||||
|
||||
/* Compatible device data for stih416 sas thermal sensor */
|
||||
static const struct st_thermal_compat_data st_416sas_cdata = {
|
||||
.sys_compat = "st,stih416-front-syscfg",
|
||||
.reg_fields = st_416sas_regfields,
|
||||
.ops = &st_syscfg_sensor_ops,
|
||||
.calibration_val = 16,
|
||||
.temp_adjust_val = 20,
|
||||
.crit_temp = 120,
|
||||
};
|
||||
|
||||
/* Compatible device data for stid127 thermal sensor */
|
||||
static const struct st_thermal_compat_data st_127_cdata = {
|
||||
.sys_compat = "st,stid127-cpu-syscfg",
|
||||
.reg_fields = st_127_regfields,
|
||||
.ops = &st_syscfg_sensor_ops,
|
||||
.calibration_val = 8,
|
||||
.temp_adjust_val = -103,
|
||||
.crit_temp = 120,
|
||||
};
|
||||
|
||||
static const struct of_device_id st_syscfg_thermal_of_match[] = {
|
||||
{ .compatible = "st,stih415-sas-thermal", .data = &st_415sas_cdata },
|
||||
{ .compatible = "st,stih415-mpe-thermal", .data = &st_415mpe_cdata },
|
||||
{ .compatible = "st,stih416-sas-thermal", .data = &st_416sas_cdata },
|
||||
{ .compatible = "st,stid127-thermal", .data = &st_127_cdata },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_syscfg_thermal_of_match);
|
||||
|
||||
static int st_syscfg_probe(struct platform_device *pdev)
|
||||
{
|
||||
return st_thermal_register(pdev, st_syscfg_thermal_of_match);
|
||||
}
|
||||
|
||||
static int st_syscfg_remove(struct platform_device *pdev)
|
||||
{
|
||||
return st_thermal_unregister(pdev);
|
||||
}
|
||||
|
||||
static struct platform_driver st_syscfg_thermal_driver = {
|
||||
.driver = {
|
||||
.name = "st_syscfg_thermal",
|
||||
.pm = &st_thermal_pm_ops,
|
||||
.of_match_table = st_syscfg_thermal_of_match,
|
||||
},
|
||||
.probe = st_syscfg_probe,
|
||||
.remove = st_syscfg_remove,
|
||||
};
|
||||
module_platform_driver(st_syscfg_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -19,7 +19,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
/* DTS register offsets */
|
||||
|
@ -210,7 +210,7 @@ static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
|
||||
|
||||
regmap_update_bits(tmdev->regmap,
|
||||
SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)),
|
||||
0xfff << offset,
|
||||
TEMP_CALIB_MASK << offset,
|
||||
caldata[i] << offset);
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
|
||||
offset = (i % 2) * 16;
|
||||
regmap_update_bits(tmdev->regmap,
|
||||
SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4),
|
||||
0xfff << offset,
|
||||
TEMP_CALIB_MASK << offset,
|
||||
cdata << offset);
|
||||
}
|
||||
|
||||
|
@ -582,23 +582,23 @@ static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id)
|
||||
return temp;
|
||||
}
|
||||
|
||||
static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip, int temp)
|
||||
static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp)
|
||||
{
|
||||
struct tegra_thermctl_zone *zone = tz->devdata;
|
||||
struct tegra_soctherm *ts = zone->ts;
|
||||
struct thermal_trip trip;
|
||||
const struct tegra_tsensor_group *sg = zone->sg;
|
||||
struct device *dev = zone->dev;
|
||||
enum thermal_trip_type type;
|
||||
int ret;
|
||||
|
||||
if (!tz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, trip, &type);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (type == THERMAL_TRIP_CRITICAL) {
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL) {
|
||||
/*
|
||||
* If thermtrips property is set in DT,
|
||||
* doesn't need to program critical type trip to HW,
|
||||
@ -609,7 +609,7 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip
|
||||
else
|
||||
return 0;
|
||||
|
||||
} else if (type == THERMAL_TRIP_HOT) {
|
||||
} else if (trip.type == THERMAL_TRIP_HOT) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < THROTTLE_SIZE; i++) {
|
||||
@ -620,7 +620,7 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip
|
||||
continue;
|
||||
|
||||
cdev = ts->throt_cfgs[i].cdev;
|
||||
if (get_thermal_instance(tz, cdev, trip))
|
||||
if (get_thermal_instance(tz, cdev, trip_id))
|
||||
stc = find_throttle_cfg_by_name(ts, cdev->type);
|
||||
else
|
||||
continue;
|
||||
@ -687,25 +687,20 @@ static const struct thermal_zone_device_ops tegra_of_thermal_ops = {
|
||||
.set_trips = tegra_thermctl_set_trips,
|
||||
};
|
||||
|
||||
static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp)
|
||||
static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp)
|
||||
{
|
||||
int ntrips, i, ret;
|
||||
enum thermal_trip_type type;
|
||||
int i, ret;
|
||||
struct thermal_trip trip;
|
||||
|
||||
ntrips = of_thermal_get_ntrips(tz);
|
||||
if (ntrips <= 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < thermal_zone_get_num_trips(tz); i++) {
|
||||
|
||||
for (i = 0; i < ntrips; i++) {
|
||||
ret = tz->ops->get_trip_type(tz, i, &type);
|
||||
ret = thermal_zone_get_trip(tz, i, &trip);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
if (type == THERMAL_TRIP_HOT) {
|
||||
ret = tz->ops->get_trip_temp(tz, i, temp);
|
||||
if (!ret)
|
||||
*trip = i;
|
||||
|
||||
return ret;
|
||||
if (trip.type == THERMAL_TRIP_HOT) {
|
||||
*trip_id = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,7 +742,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
|
||||
/* Get thermtrips. If missing, try to get critical trips. */
|
||||
temperature = tsensor_group_thermtrip_get(ts, sg->id);
|
||||
if (min_low_temp == temperature)
|
||||
if (tz->ops->get_crit_temp(tz, &temperature))
|
||||
if (thermal_zone_get_crit_temp(tz, &temperature))
|
||||
temperature = max_high_temp;
|
||||
|
||||
ret = thermtrip_program(dev, sg, temperature);
|
||||
|
@ -28,7 +28,6 @@
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
#define TSENSOR_SENSOR0_CONFIG0 0x0
|
||||
@ -316,18 +315,17 @@ static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd,
|
||||
*hot_trip = 85000;
|
||||
*crit_trip = 90000;
|
||||
|
||||
for (i = 0; i < tzd->num_trips; i++) {
|
||||
enum thermal_trip_type type;
|
||||
int trip_temp;
|
||||
for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) {
|
||||
|
||||
tzd->ops->get_trip_temp(tzd, i, &trip_temp);
|
||||
tzd->ops->get_trip_type(tzd, i, &type);
|
||||
struct thermal_trip trip;
|
||||
|
||||
if (type == THERMAL_TRIP_HOT)
|
||||
*hot_trip = trip_temp;
|
||||
thermal_zone_get_trip(tzd, i, &trip);
|
||||
|
||||
if (type == THERMAL_TRIP_CRITICAL)
|
||||
*crit_trip = trip_temp;
|
||||
if (trip.type == THERMAL_TRIP_HOT)
|
||||
*hot_trip = trip.temperature;
|
||||
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL)
|
||||
*crit_trip = trip.temperature;
|
||||
}
|
||||
|
||||
/* clamp hardware trips to the calibration limits */
|
||||
|
117
drivers/thermal/thermal_acpi.c
Normal file
117
drivers/thermal/thermal_acpi.c
Normal file
@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2023 Linaro Limited
|
||||
* Copyright 2023 Intel Corporation
|
||||
*
|
||||
* Library routines for populating a generic thermal trip point structure
|
||||
* with data obtained by evaluating a specific object in the ACPI Namespace.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*
|
||||
* Minimum temperature for full military grade is 218°K (-55°C) and
|
||||
* max temperature is 448°K (175°C). We can consider those values as
|
||||
* the boundaries for the [trips] temperature returned by the
|
||||
* firmware. Any values out of these boundaries may be considered
|
||||
* bogus and we can assume the firmware has no data to provide.
|
||||
*/
|
||||
#define TEMP_MIN_DECIK 2180
|
||||
#define TEMP_MAX_DECIK 4480
|
||||
|
||||
static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name,
|
||||
int *ret_temp)
|
||||
{
|
||||
unsigned long long temp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, obj_name, NULL, &temp);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_debug(adev->handle, "%s evaluation failed\n", obj_name);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) {
|
||||
*ret_temp = deci_kelvin_to_millicelsius(temp);
|
||||
} else {
|
||||
acpi_handle_debug(adev->handle, "%s result %llu out of range\n",
|
||||
obj_name, temp);
|
||||
*ret_temp = THERMAL_TEMP_INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_acpi_active_trip_temp - Retrieve active trip point temperature
|
||||
* @adev: Target thermal zone ACPI device object.
|
||||
* @id: Active cooling level (0 - 9).
|
||||
* @ret_temp: Address to store the retrieved temperature value on success.
|
||||
*
|
||||
* Evaluate the _ACx object for the thermal zone represented by @adev to obtain
|
||||
* the temperature of the active cooling trip point corresponding to the active
|
||||
* cooling level given by @id.
|
||||
*
|
||||
* Return 0 on success or a negative error value on failure.
|
||||
*/
|
||||
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
|
||||
{
|
||||
char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'};
|
||||
|
||||
if (id < 0 || id > 9)
|
||||
return -EINVAL;
|
||||
|
||||
return thermal_acpi_trip_temp(adev, obj_name, ret_temp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
|
||||
|
||||
/**
|
||||
* thermal_acpi_passive_trip_temp - Retrieve passive trip point temperature
|
||||
* @adev: Target thermal zone ACPI device object.
|
||||
* @ret_temp: Address to store the retrieved temperature value on success.
|
||||
*
|
||||
* Evaluate the _PSV object for the thermal zone represented by @adev to obtain
|
||||
* the temperature of the passive cooling trip point.
|
||||
*
|
||||
* Return 0 on success or -ENODATA on failure.
|
||||
*/
|
||||
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
|
||||
{
|
||||
return thermal_acpi_trip_temp(adev, "_PSV", ret_temp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
|
||||
|
||||
/**
|
||||
* thermal_acpi_hot_trip_temp - Retrieve hot trip point temperature
|
||||
* @adev: Target thermal zone ACPI device object.
|
||||
* @ret_temp: Address to store the retrieved temperature value on success.
|
||||
*
|
||||
* Evaluate the _HOT object for the thermal zone represented by @adev to obtain
|
||||
* the temperature of the trip point at which the system is expected to be put
|
||||
* into the S4 sleep state.
|
||||
*
|
||||
* Return 0 on success or -ENODATA on failure.
|
||||
*/
|
||||
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
|
||||
{
|
||||
return thermal_acpi_trip_temp(adev, "_HOT", ret_temp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
|
||||
|
||||
/**
|
||||
* thermal_acpi_critical_trip_temp - Retrieve critical trip point temperature
|
||||
* @adev: Target thermal zone ACPI device object.
|
||||
* @ret_temp: Address to store the retrieved temperature value on success.
|
||||
*
|
||||
* Evaluate the _CRT object for the thermal zone represented by @adev to obtain
|
||||
* the temperature of the critical cooling trip point.
|
||||
*
|
||||
* Return 0 on success or -ENODATA on failure.
|
||||
*/
|
||||
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
|
||||
{
|
||||
return thermal_acpi_trip_temp(adev, "_CRT", ret_temp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp);
|
@ -229,10 +229,9 @@ int thermal_build_list_of_policies(char *buf)
|
||||
mutex_lock(&thermal_governor_lock);
|
||||
|
||||
list_for_each_entry(pos, &thermal_governor_list, governor_list) {
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, "%s ",
|
||||
pos->name);
|
||||
count += sysfs_emit_at(buf, count, "%s ", pos->name);
|
||||
}
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
|
||||
count += sysfs_emit_at(buf, count, "\n");
|
||||
|
||||
mutex_unlock(&thermal_governor_lock);
|
||||
|
||||
@ -344,35 +343,31 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
tz->ops->critical(tz);
|
||||
}
|
||||
|
||||
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
|
||||
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id)
|
||||
{
|
||||
enum thermal_trip_type type;
|
||||
int trip_temp, hyst = 0;
|
||||
struct thermal_trip trip;
|
||||
|
||||
/* Ignore disabled trip points */
|
||||
if (test_bit(trip, &tz->trips_disabled))
|
||||
if (test_bit(trip_id, &tz->trips_disabled))
|
||||
return;
|
||||
|
||||
tz->ops->get_trip_temp(tz, trip, &trip_temp);
|
||||
tz->ops->get_trip_type(tz, trip, &type);
|
||||
if (tz->ops->get_trip_hyst)
|
||||
tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||||
__thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
|
||||
if (tz->last_temperature != THERMAL_TEMP_INVALID) {
|
||||
if (tz->last_temperature < trip_temp &&
|
||||
tz->temperature >= trip_temp)
|
||||
thermal_notify_tz_trip_up(tz->id, trip,
|
||||
if (tz->last_temperature < trip.temperature &&
|
||||
tz->temperature >= trip.temperature)
|
||||
thermal_notify_tz_trip_up(tz->id, trip_id,
|
||||
tz->temperature);
|
||||
if (tz->last_temperature >= trip_temp &&
|
||||
tz->temperature < (trip_temp - hyst))
|
||||
thermal_notify_tz_trip_down(tz->id, trip,
|
||||
if (tz->last_temperature >= trip.temperature &&
|
||||
tz->temperature < (trip.temperature - trip.hysteresis))
|
||||
thermal_notify_tz_trip_down(tz->id, trip_id,
|
||||
tz->temperature);
|
||||
}
|
||||
|
||||
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
|
||||
handle_critical_trips(tz, trip, trip_temp, type);
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL || trip.type == THERMAL_TRIP_HOT)
|
||||
handle_critical_trips(tz, trip_id, trip.temperature, trip.type);
|
||||
else
|
||||
handle_non_critical_trips(tz, trip);
|
||||
handle_non_critical_trips(tz, trip_id);
|
||||
}
|
||||
|
||||
static void update_temperature(struct thermal_zone_device *tz)
|
||||
@ -774,14 +769,14 @@ static void thermal_release(struct device *dev)
|
||||
} else if (!strncmp(dev_name(dev), "cooling_device",
|
||||
sizeof("cooling_device") - 1)) {
|
||||
cdev = to_cooling_device(dev);
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
kfree(cdev->type);
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
kfree(cdev);
|
||||
}
|
||||
}
|
||||
|
||||
static struct class thermal_class = {
|
||||
.name = "thermal",
|
||||
.dev_release = thermal_release,
|
||||
};
|
||||
static struct class *thermal_class;
|
||||
|
||||
static inline
|
||||
void print_bind_err_msg(struct thermal_zone_device *tz,
|
||||
@ -884,6 +879,9 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
!ops->set_cur_state)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!thermal_class)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -905,27 +903,25 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
cdev->np = np;
|
||||
cdev->ops = ops;
|
||||
cdev->updated = false;
|
||||
cdev->device.class = &thermal_class;
|
||||
cdev->device.class = thermal_class;
|
||||
cdev->devdata = devdata;
|
||||
|
||||
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
|
||||
if (ret) {
|
||||
kfree(cdev->type);
|
||||
goto out_ida_remove;
|
||||
}
|
||||
if (ret)
|
||||
goto out_cdev_type;
|
||||
|
||||
thermal_cooling_device_setup_sysfs(cdev);
|
||||
|
||||
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||
if (ret) {
|
||||
kfree(cdev->type);
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
goto out_ida_remove;
|
||||
}
|
||||
if (ret)
|
||||
goto out_cooling_dev;
|
||||
|
||||
ret = device_register(&cdev->device);
|
||||
if (ret)
|
||||
goto out_kfree_type;
|
||||
if (ret) {
|
||||
/* thermal_release() handles rest of the cleanup */
|
||||
put_device(&cdev->device);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Add 'this' new cdev to the global cdev list */
|
||||
mutex_lock(&thermal_list_lock);
|
||||
@ -944,13 +940,10 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||
|
||||
return cdev;
|
||||
|
||||
out_kfree_type:
|
||||
out_cooling_dev:
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
out_cdev_type:
|
||||
kfree(cdev->type);
|
||||
put_device(&cdev->device);
|
||||
|
||||
/* thermal_release() takes care of the rest */
|
||||
cdev = NULL;
|
||||
out_ida_remove:
|
||||
ida_free(&thermal_cdev_ida, id);
|
||||
out_kfree_cdev:
|
||||
@ -1111,11 +1104,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
||||
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
ida_free(&thermal_cdev_ida, cdev->id);
|
||||
device_del(&cdev->device);
|
||||
thermal_cooling_device_destroy_sysfs(cdev);
|
||||
kfree(cdev->type);
|
||||
put_device(&cdev->device);
|
||||
device_unregister(&cdev->device);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
|
||||
|
||||
@ -1166,6 +1155,32 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
|
||||
*delay_jiffies = round_jiffies(*delay_jiffies);
|
||||
}
|
||||
|
||||
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
if (tz->ops->get_crit_temp)
|
||||
return tz->ops->get_crit_temp(tz, temp);
|
||||
|
||||
if (!tz->trips)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = tz->trips[i].temperature;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp);
|
||||
|
||||
/**
|
||||
* thermal_zone_device_register_with_trips() - register a new thermal zone device
|
||||
* @type: the thermal zone device type
|
||||
@ -1198,8 +1213,6 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
int polling_delay)
|
||||
{
|
||||
struct thermal_zone_device *tz;
|
||||
enum thermal_trip_type trip_type;
|
||||
int trip_temp;
|
||||
int id;
|
||||
int result;
|
||||
int count;
|
||||
@ -1239,9 +1252,12 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
|
||||
if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp) && !trips)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!thermal_class)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
|
||||
if (!tz)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -1263,7 +1279,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
|
||||
tz->ops = ops;
|
||||
tz->tzp = tzp;
|
||||
tz->device.class = &thermal_class;
|
||||
tz->device.class = thermal_class;
|
||||
tz->devdata = devdata;
|
||||
tz->trips = trips;
|
||||
tz->num_trips = num_trips;
|
||||
@ -1290,9 +1306,10 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
|
||||
goto release_device;
|
||||
|
||||
for (count = 0; count < num_trips; count++) {
|
||||
if (tz->ops->get_trip_type(tz, count, &trip_type) ||
|
||||
tz->ops->get_trip_temp(tz, count, &trip_temp) ||
|
||||
!trip_temp)
|
||||
struct thermal_trip trip;
|
||||
|
||||
result = thermal_zone_get_trip(tz, count, &trip);
|
||||
if (result)
|
||||
set_bit(count, &tz->trips_disabled);
|
||||
}
|
||||
|
||||
@ -1505,11 +1522,23 @@ static int __init thermal_init(void)
|
||||
|
||||
result = thermal_register_governors();
|
||||
if (result)
|
||||
goto error;
|
||||
goto unregister_netlink;
|
||||
|
||||
result = class_register(&thermal_class);
|
||||
if (result)
|
||||
thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
|
||||
if (!thermal_class) {
|
||||
result = -ENOMEM;
|
||||
goto unregister_governors;
|
||||
}
|
||||
|
||||
thermal_class->name = "thermal";
|
||||
thermal_class->dev_release = thermal_release;
|
||||
|
||||
result = class_register(thermal_class);
|
||||
if (result) {
|
||||
kfree(thermal_class);
|
||||
thermal_class = NULL;
|
||||
goto unregister_governors;
|
||||
}
|
||||
|
||||
result = register_pm_notifier(&thermal_pm_nb);
|
||||
if (result)
|
||||
@ -1520,9 +1549,9 @@ static int __init thermal_init(void)
|
||||
|
||||
unregister_governors:
|
||||
thermal_unregister_governors();
|
||||
unregister_netlink:
|
||||
thermal_netlink_exit();
|
||||
error:
|
||||
ida_destroy(&thermal_tz_ida);
|
||||
ida_destroy(&thermal_cdev_ida);
|
||||
mutex_destroy(&thermal_list_lock);
|
||||
mutex_destroy(&thermal_governor_lock);
|
||||
return result;
|
||||
|
@ -52,6 +52,10 @@ int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
|
||||
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
|
||||
void *thermal_governor);
|
||||
|
||||
int __for_each_thermal_trip(struct thermal_zone_device *,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *);
|
||||
|
||||
struct thermal_zone_device *thermal_zone_get_by_id(int id);
|
||||
|
||||
struct thermal_attr {
|
||||
@ -114,6 +118,8 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
|
||||
/* Helpers */
|
||||
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_get_temp(struct thermal_zone_device *tz, int *temp);
|
||||
|
||||
/* sysfs I/F */
|
||||
@ -137,28 +143,6 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
||||
#endif /* CONFIG_THERMAL_STATISTICS */
|
||||
|
||||
/* device tree support */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
int of_thermal_get_ntrips(struct thermal_zone_device *);
|
||||
bool of_thermal_is_trip_valid(struct thermal_zone_device *, int);
|
||||
const struct thermal_trip *
|
||||
of_thermal_get_trip_points(struct thermal_zone_device *);
|
||||
#else
|
||||
static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz,
|
||||
int trip)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline const struct thermal_trip *
|
||||
of_thermal_get_trip_points(struct thermal_zone_device *tz)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz);
|
||||
|
||||
#endif /* __THERMAL_CORE_H__ */
|
||||
|
@ -83,7 +83,7 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
int ret = -EINVAL;
|
||||
int count;
|
||||
int crit_temp = INT_MAX;
|
||||
enum thermal_trip_type type;
|
||||
struct thermal_trip trip;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
@ -91,10 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
|
||||
for (count = 0; count < tz->num_trips; count++) {
|
||||
ret = tz->ops->get_trip_type(tz, count, &type);
|
||||
if (!ret && type == THERMAL_TRIP_CRITICAL) {
|
||||
ret = tz->ops->get_trip_temp(tz, count,
|
||||
&crit_temp);
|
||||
ret = __thermal_zone_get_trip(tz, count, &trip);
|
||||
if (!ret && trip.type == THERMAL_TRIP_CRITICAL) {
|
||||
crit_temp = trip.temperature;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -147,67 +146,6 @@ unlock:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
|
||||
|
||||
/**
|
||||
* __thermal_zone_set_trips - Computes the next trip points for the driver
|
||||
* @tz: a pointer to a thermal zone device structure
|
||||
*
|
||||
* The function computes the next temperature boundaries by browsing
|
||||
* the trip points. The result is the closer low and high trip points
|
||||
* to the current temperature. These values are passed to the backend
|
||||
* driver to let it set its own notification mechanism (usually an
|
||||
* interrupt).
|
||||
*
|
||||
* This function must be called with tz->lock held. Both tz and tz->ops
|
||||
* must be valid pointers.
|
||||
*
|
||||
* It does not return a value
|
||||
*/
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
int low = -INT_MAX;
|
||||
int high = INT_MAX;
|
||||
int trip_temp, hysteresis;
|
||||
int i, ret;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
|
||||
return;
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
int trip_low;
|
||||
|
||||
tz->ops->get_trip_temp(tz, i, &trip_temp);
|
||||
tz->ops->get_trip_hyst(tz, i, &hysteresis);
|
||||
|
||||
trip_low = trip_temp - hysteresis;
|
||||
|
||||
if (trip_low < tz->temperature && trip_low > low)
|
||||
low = trip_low;
|
||||
|
||||
if (trip_temp > tz->temperature && trip_temp < high)
|
||||
high = trip_temp;
|
||||
}
|
||||
|
||||
/* No need to change trip points */
|
||||
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
|
||||
return;
|
||||
|
||||
tz->prev_low_trip = low;
|
||||
tz->prev_high_trip = high;
|
||||
|
||||
dev_dbg(&tz->device,
|
||||
"new temperature boundaries: %d < x < %d\n", low, high);
|
||||
|
||||
/*
|
||||
* Set a temperature window. When this window is left the driver
|
||||
* must inform the thermal core via thermal_zone_device_update.
|
||||
*/
|
||||
ret = tz->ops->set_trips(tz, low, high);
|
||||
if (ret)
|
||||
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
|
||||
}
|
||||
|
||||
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
int target)
|
||||
{
|
||||
|
@ -39,7 +39,6 @@ static const struct thermal_zone_device_ops thermal_mmio_ops = {
|
||||
|
||||
static int thermal_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *resource;
|
||||
struct thermal_mmio *sensor;
|
||||
int (*sensor_init_func)(struct platform_device *pdev,
|
||||
struct thermal_mmio *sensor);
|
||||
@ -51,8 +50,7 @@ static int thermal_mmio_probe(struct platform_device *pdev)
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource);
|
||||
sensor->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(sensor->mmio_base))
|
||||
return PTR_ERR(sensor->mmio_base);
|
||||
|
||||
|
@ -452,7 +452,8 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
struct sk_buff *msg = p->msg;
|
||||
struct thermal_zone_device *tz;
|
||||
struct nlattr *start_trip;
|
||||
int i, id;
|
||||
struct thermal_trip trip;
|
||||
int ret, i, id;
|
||||
|
||||
if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
|
||||
return -EINVAL;
|
||||
@ -471,18 +472,14 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
|
||||
enum thermal_trip_type type;
|
||||
int temp, hyst = 0;
|
||||
|
||||
tz->ops->get_trip_type(tz, i, &type);
|
||||
tz->ops->get_trip_temp(tz, i, &temp);
|
||||
if (tz->ops->get_trip_hyst)
|
||||
tz->ops->get_trip_hyst(tz, i, &hyst);
|
||||
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, type) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
|
||||
nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
|
||||
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;
|
||||
}
|
||||
|
||||
@ -702,3 +699,8 @@ int __init thermal_netlink_init(void)
|
||||
{
|
||||
return genl_register_family(&thermal_gnl_family);
|
||||
}
|
||||
|
||||
void __init thermal_netlink_exit(void)
|
||||
{
|
||||
genl_unregister_family(&thermal_gnl_family);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ struct thermal_genl_cpu_caps {
|
||||
/* Netlink notification function */
|
||||
#ifdef CONFIG_THERMAL_NETLINK
|
||||
int __init thermal_netlink_init(void);
|
||||
void __init thermal_netlink_exit(void);
|
||||
int thermal_notify_tz_create(int tz_id, const char *name);
|
||||
int thermal_notify_tz_delete(int tz_id);
|
||||
int thermal_notify_tz_enable(int tz_id);
|
||||
@ -115,4 +116,6 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __init thermal_netlink_exit(void) {}
|
||||
|
||||
#endif /* CONFIG_THERMAL_NETLINK */
|
||||
|
@ -19,117 +19,6 @@
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/**
|
||||
* of_thermal_get_ntrips - function to export number of available trip
|
||||
* points.
|
||||
* @tz: pointer to a thermal zone
|
||||
*
|
||||
* This function is a globally visible wrapper to get number of trip points
|
||||
* stored in the local struct __thermal_zone
|
||||
*
|
||||
* Return: number of available trip points, -ENODEV when data not available
|
||||
*/
|
||||
int of_thermal_get_ntrips(struct thermal_zone_device *tz)
|
||||
{
|
||||
return tz->num_trips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
|
||||
|
||||
/**
|
||||
* of_thermal_is_trip_valid - function to check if trip point is valid
|
||||
*
|
||||
* @tz: pointer to a thermal zone
|
||||
* @trip: trip point to evaluate
|
||||
*
|
||||
* This function is responsible for checking if passed trip point is valid
|
||||
*
|
||||
* Return: true if trip point is valid, false otherwise
|
||||
*/
|
||||
bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
|
||||
{
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
|
||||
|
||||
/**
|
||||
* of_thermal_get_trip_points - function to get access to a globally exported
|
||||
* trip points
|
||||
*
|
||||
* @tz: pointer to a thermal zone
|
||||
*
|
||||
* This function provides a pointer to trip points table
|
||||
*
|
||||
* Return: pointer to trip points table, NULL otherwise
|
||||
*/
|
||||
const struct thermal_trip *
|
||||
of_thermal_get_trip_points(struct thermal_zone_device *tz)
|
||||
{
|
||||
return tz->trips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
|
||||
|
||||
static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*type = tz->trips[trip].type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
|
||||
int *temp)
|
||||
{
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*temp = tz->trips[trip].temperature;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
int *hyst)
|
||||
{
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
*hyst = tz->trips[trip].hysteresis;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
|
||||
int hyst)
|
||||
{
|
||||
if (trip >= tz->num_trips || trip < 0)
|
||||
return -EDOM;
|
||||
|
||||
/* thermal framework should take care of data->mask & (1 << trip) */
|
||||
tz->trips[trip].hysteresis = hyst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
|
||||
int *temp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++)
|
||||
if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) {
|
||||
*temp = tz->trips[i].temperature;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*** functions parsing device tree nodes ***/
|
||||
|
||||
static int of_find_trip_id(struct device_node *np, struct device_node *trip)
|
||||
@ -628,11 +517,6 @@ struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor,
|
||||
goto out_kfree_trips;
|
||||
}
|
||||
|
||||
of_ops->get_trip_type = of_ops->get_trip_type ? : of_thermal_get_trip_type;
|
||||
of_ops->get_trip_temp = of_ops->get_trip_temp ? : of_thermal_get_trip_temp;
|
||||
of_ops->get_trip_hyst = of_ops->get_trip_hyst ? : of_thermal_get_trip_hyst;
|
||||
of_ops->set_trip_hyst = of_ops->set_trip_hyst ? : of_thermal_set_trip_hyst;
|
||||
of_ops->get_crit_temp = of_ops->get_crit_temp ? : of_thermal_get_crit_temp;
|
||||
of_ops->bind = thermal_of_bind;
|
||||
of_ops->unbind = thermal_of_unbind;
|
||||
|
||||
|
@ -83,27 +83,25 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
enum thermal_trip_type type;
|
||||
int trip, result;
|
||||
struct thermal_trip trip;
|
||||
int trip_id, result;
|
||||
|
||||
if (!tz->ops->get_trip_type)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1)
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
result = tz->ops->get_trip_type(tz, trip, &type);
|
||||
result = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
else
|
||||
result = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
switch (type) {
|
||||
switch (trip.type) {
|
||||
case THERMAL_TRIP_CRITICAL:
|
||||
return sprintf(buf, "critical\n");
|
||||
case THERMAL_TRIP_HOT:
|
||||
@ -122,17 +120,10 @@ 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);
|
||||
int trip, ret;
|
||||
int temperature, hyst = 0;
|
||||
enum thermal_trip_type type;
|
||||
struct thermal_trip trip;
|
||||
int trip_id, ret;
|
||||
|
||||
if (!tz->ops->set_trip_temp && !tz->trips)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
@ -142,36 +133,19 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (tz->ops->set_trip_temp) {
|
||||
ret = tz->ops->set_trip_temp(tz, trip, temperature);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (tz->trips)
|
||||
tz->trips[trip].temperature = temperature;
|
||||
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = tz->ops->get_trip_type(tz, trip, &type);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst);
|
||||
|
||||
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
|
||||
ret = kstrtoint(buf, 10, &trip.temperature);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = thermal_zone_set_trip(tz, trip_id, &trip);
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -179,19 +153,16 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip, ret;
|
||||
int temperature;
|
||||
struct thermal_trip trip;
|
||||
int trip_id, ret;
|
||||
|
||||
if (!tz->ops->get_trip_temp)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
@ -200,7 +171,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temperature);
|
||||
return sprintf(buf, "%d\n", trip.temperature);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -208,16 +179,13 @@ 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);
|
||||
int trip, ret;
|
||||
int temperature;
|
||||
struct thermal_trip trip;
|
||||
int trip_id, ret;
|
||||
|
||||
if (!tz->ops->set_trip_hyst)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtoint(buf, 10, &temperature))
|
||||
if (kstrtoint(buf, 10, &trip.hysteresis))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
@ -227,16 +195,11 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not doing any check on the 'temperature' value
|
||||
* here. The driver implementing 'set_trip_hyst' has to
|
||||
* take care of this.
|
||||
*/
|
||||
ret = tz->ops->set_trip_hyst(tz, trip, temperature);
|
||||
|
||||
if (!ret)
|
||||
__thermal_zone_set_trips(tz);
|
||||
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = thermal_zone_set_trip(tz, trip_id, &trip);
|
||||
unlock:
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
@ -248,25 +211,22 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||
int trip, ret;
|
||||
int temperature;
|
||||
struct thermal_trip trip;
|
||||
int trip_id, ret;
|
||||
|
||||
if (!tz->ops->get_trip_hyst)
|
||||
return -EPERM;
|
||||
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
|
||||
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
|
||||
if (device_is_registered(dev))
|
||||
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, &trip);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret ? ret : sprintf(buf, "%d\n", temperature);
|
||||
return ret ? ret : sprintf(buf, "%d\n", trip.hysteresis);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -491,23 +451,20 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
tz->trip_hyst_attrs = kcalloc(tz->num_trips,
|
||||
sizeof(*tz->trip_hyst_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!tz->trip_hyst_attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tz->trip_hyst_attrs = kcalloc(tz->num_trips,
|
||||
sizeof(*tz->trip_hyst_attrs),
|
||||
GFP_KERNEL);
|
||||
if (!tz->trip_hyst_attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL);
|
||||
if (!attrs) {
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
if (tz->ops->get_trip_hyst)
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -540,9 +497,6 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
|
||||
}
|
||||
attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr;
|
||||
|
||||
/* create Optional trip hyst attribute */
|
||||
if (!tz->ops->get_trip_hyst)
|
||||
continue;
|
||||
snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH,
|
||||
"trip_point_%d_hyst", indx);
|
||||
|
||||
@ -579,8 +533,7 @@ static void destroy_trip_attrs(struct thermal_zone_device *tz)
|
||||
|
||||
kfree(tz->trip_type_attrs);
|
||||
kfree(tz->trip_temp_attrs);
|
||||
if (tz->ops->get_trip_hyst)
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
kfree(tz->trip_hyst_attrs);
|
||||
kfree(tz->trips_attribute_group.attrs);
|
||||
}
|
||||
|
||||
|
182
drivers/thermal/thermal_trip.c
Normal file
182
drivers/thermal/thermal_trip.c
Normal file
@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2008 Intel Corp
|
||||
* Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
|
||||
* Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
|
||||
* Copyright 2022 Linaro Limited
|
||||
*
|
||||
* Thermal trips handling
|
||||
*/
|
||||
#include "thermal_core.h"
|
||||
|
||||
int __for_each_thermal_trip(struct thermal_zone_device *tz,
|
||||
int (*cb)(struct thermal_trip *, void *),
|
||||
void *data)
|
||||
{
|
||||
int i, ret;
|
||||
struct thermal_trip trip;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
|
||||
ret = __thermal_zone_get_trip(tz, i, &trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cb(&trip, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
return tz->num_trips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
|
||||
|
||||
/**
|
||||
* __thermal_zone_set_trips - Computes the next trip points for the driver
|
||||
* @tz: a pointer to a thermal zone device structure
|
||||
*
|
||||
* The function computes the next temperature boundaries by browsing
|
||||
* the trip points. The result is the closer low and high trip points
|
||||
* to the current temperature. These values are passed to the backend
|
||||
* driver to let it set its own notification mechanism (usually an
|
||||
* interrupt).
|
||||
*
|
||||
* This function must be called with tz->lock held. Both tz and tz->ops
|
||||
* must be valid pointers.
|
||||
*
|
||||
* It does not return a value
|
||||
*/
|
||||
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct thermal_trip trip;
|
||||
int low = -INT_MAX, high = INT_MAX;
|
||||
int i, ret;
|
||||
|
||||
lockdep_assert_held(&tz->lock);
|
||||
|
||||
if (!tz->ops->set_trips)
|
||||
return;
|
||||
|
||||
for (i = 0; i < tz->num_trips; i++) {
|
||||
int trip_low;
|
||||
|
||||
ret = __thermal_zone_get_trip(tz, i , &trip);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
trip_low = trip.temperature - trip.hysteresis;
|
||||
|
||||
if (trip_low < tz->temperature && trip_low > low)
|
||||
low = trip_low;
|
||||
|
||||
if (trip.temperature > tz->temperature &&
|
||||
trip.temperature < high)
|
||||
high = trip.temperature;
|
||||
}
|
||||
|
||||
/* No need to change trip points */
|
||||
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
|
||||
return;
|
||||
|
||||
tz->prev_low_trip = low;
|
||||
tz->prev_high_trip = high;
|
||||
|
||||
dev_dbg(&tz->device,
|
||||
"new temperature boundaries: %d < x < %d\n", low, high);
|
||||
|
||||
/*
|
||||
* Set a temperature window. When this window is left the driver
|
||||
* must inform the thermal core via thermal_zone_device_update.
|
||||
*/
|
||||
ret = tz->ops->set_trips(tz, low, high);
|
||||
if (ret)
|
||||
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
|
||||
}
|
||||
|
||||
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
|
||||
struct thermal_trip *trip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
|
||||
return -EINVAL;
|
||||
|
||||
if (tz->trips) {
|
||||
*trip = tz->trips[trip_id];
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tz->ops->get_trip_hyst) {
|
||||
ret = tz->ops->get_trip_hyst(tz, trip_id, &trip->hysteresis);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
trip->hysteresis = 0;
|
||||
}
|
||||
|
||||
ret = tz->ops->get_trip_temp(tz, trip_id, &trip->temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tz->ops->get_trip_type(tz, trip_id, &trip->type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
|
||||
|
||||
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
|
||||
struct thermal_trip *trip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tz->lock);
|
||||
ret = __thermal_zone_get_trip(tz, trip_id, trip);
|
||||
mutex_unlock(&tz->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
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;
|
||||
}
|
@ -38,21 +38,6 @@
|
||||
/* Update rates */
|
||||
#define FAST_TEMP_MONITORING_RATE 250
|
||||
|
||||
/* helper macros */
|
||||
/**
|
||||
* ti_thermal_get_trip_value - returns trip temperature based on index
|
||||
* @i: trip index
|
||||
*/
|
||||
#define ti_thermal_get_trip_value(i) \
|
||||
(OMAP_TRIP_HOT + ((i) * OMAP_TRIP_STEP))
|
||||
|
||||
/**
|
||||
* ti_thermal_is_valid_trip - check for trip index
|
||||
* @i: trip index
|
||||
*/
|
||||
#define ti_thermal_is_valid_trip(trip) \
|
||||
((trip) >= 0 && (trip) < OMAP_TRIP_NUMBER)
|
||||
|
||||
#ifdef CONFIG_TI_THERMAL
|
||||
int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain);
|
||||
int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
/*
|
||||
* uniphier_thermal.c - Socionext UniPhier thermal driver
|
||||
* Copyright 2014 Panasonic Corporation
|
||||
* Copyright 2016-2017 Socionext Inc.
|
||||
@ -17,8 +17,6 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*
|
||||
* block registers
|
||||
* addresses are the offset from .block_base
|
||||
@ -248,8 +246,7 @@ static int uniphier_tm_probe(struct platform_device *pdev)
|
||||
struct regmap *regmap;
|
||||
struct device_node *parent;
|
||||
struct uniphier_tm_dev *tdev;
|
||||
const struct thermal_trip *trips;
|
||||
int i, ret, irq, ntrips, crit_temp = INT_MAX;
|
||||
int i, ret, irq, crit_temp = INT_MAX;
|
||||
|
||||
tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL);
|
||||
if (!tdev)
|
||||
@ -296,20 +293,18 @@ static int uniphier_tm_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(tdev->tz_dev);
|
||||
}
|
||||
|
||||
/* get trip points */
|
||||
trips = of_thermal_get_trip_points(tdev->tz_dev);
|
||||
ntrips = of_thermal_get_ntrips(tdev->tz_dev);
|
||||
if (ntrips > ALERT_CH_NUM) {
|
||||
dev_err(dev, "thermal zone has too many trips\n");
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* set alert temperatures */
|
||||
for (i = 0; i < ntrips; i++) {
|
||||
if (trips[i].type == THERMAL_TRIP_CRITICAL &&
|
||||
trips[i].temperature < crit_temp)
|
||||
crit_temp = trips[i].temperature;
|
||||
uniphier_tm_set_alert(tdev, i, trips[i].temperature);
|
||||
for (i = 0; i < thermal_zone_get_num_trips(tdev->tz_dev); i++) {
|
||||
struct thermal_trip trip;
|
||||
|
||||
ret = thermal_zone_get_trip(tdev->tz_dev, i, &trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (trip.type == THERMAL_TRIP_CRITICAL &&
|
||||
trip.temperature < crit_temp)
|
||||
crit_temp = trip.temperature;
|
||||
uniphier_tm_set_alert(tdev, i, trip.temperature);
|
||||
tdev->alert_en[i] = true;
|
||||
}
|
||||
if (crit_temp > CRITICAL_TEMP_LIMIT) {
|
||||
|
19
include/dt-bindings/thermal/mediatek,lvts-thermal.h
Normal file
19
include/dt-bindings/thermal/mediatek,lvts-thermal.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
/*
|
||||
* Copyright (c) 2023 MediaTek Inc.
|
||||
* Author: Balsam CHIHI <bchihi@baylibre.com>
|
||||
*/
|
||||
|
||||
#ifndef __MEDIATEK_LVTS_DT_H
|
||||
#define __MEDIATEK_LVTS_DT_H
|
||||
|
||||
#define MT8195_MCU_BIG_CPU0 0
|
||||
#define MT8195_MCU_BIG_CPU1 1
|
||||
#define MT8195_MCU_BIG_CPU2 2
|
||||
#define MT8195_MCU_BIG_CPU3 3
|
||||
#define MT8195_MCU_LITTLE_CPU0 4
|
||||
#define MT8195_MCU_LITTLE_CPU1 5
|
||||
#define MT8195_MCU_LITTLE_CPU2 6
|
||||
#define MT8195_MCU_LITTLE_CPU3 7
|
||||
|
||||
#endif /* __MEDIATEK_LVTS_DT_H */
|
@ -13,6 +13,9 @@ struct idle_inject_device;
|
||||
|
||||
struct idle_inject_device *idle_inject_register(struct cpumask *cpumask);
|
||||
|
||||
struct idle_inject_device *idle_inject_register_full(struct cpumask *cpumask,
|
||||
bool (*update)(void));
|
||||
|
||||
void idle_inject_unregister(struct idle_inject_device *ii_dev);
|
||||
|
||||
int idle_inject_start(struct idle_inject_device *ii_dev);
|
||||
|
18
include/linux/intel_tcc.h
Normal file
18
include/linux/intel_tcc.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* header for Intel TCC (thermal control circuitry) library
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_TCC_H__
|
||||
#define __INTEL_TCC_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
int intel_tcc_get_tjmax(int cpu);
|
||||
int intel_tcc_get_offset(int cpu);
|
||||
int intel_tcc_set_offset(int cpu, int offset);
|
||||
int intel_tcc_get_temp(int cpu, bool pkg);
|
||||
|
||||
#endif /* __INTEL_TCC_H__ */
|
@ -334,6 +334,25 @@ static inline void devm_thermal_of_zone_unregister(struct device *dev,
|
||||
}
|
||||
#endif
|
||||
|
||||
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 thermal_zone_get_num_trips(struct thermal_zone_device *tz);
|
||||
|
||||
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp);
|
||||
|
||||
#ifdef CONFIG_THERMAL_ACPI
|
||||
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp);
|
||||
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp);
|
||||
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp);
|
||||
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_THERMAL
|
||||
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
|
||||
void *, struct thermal_zone_device_ops *,
|
||||
|
@ -9,4 +9,4 @@ Description: thermal library
|
||||
Requires: libnl-3.0 libnl-genl-3.0
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lnl-genl-3 -lnl-3
|
||||
Cflags: -I${includedir} -I{include}/libnl3
|
||||
Cflags: -I${includedir} -I${include}/libnl3
|
||||
|
@ -54,7 +54,7 @@ int thermal_sampling_fd(struct thermal_handler *th)
|
||||
thermal_error_t thermal_sampling_exit(struct thermal_handler *th)
|
||||
{
|
||||
if (nl_unsubscribe_thermal(th->sk_sampling, th->cb_sampling,
|
||||
THERMAL_GENL_EVENT_GROUP_NAME))
|
||||
THERMAL_GENL_SAMPLING_GROUP_NAME))
|
||||
return THERMAL_ERROR;
|
||||
|
||||
nl_thermal_disconnect(th->sk_sampling, th->cb_sampling);
|
||||
|
Loading…
Reference in New Issue
Block a user