mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'thermal-v5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux
Pull thermal updates from Daniel Lezcano: - Convert tsens configuration DT binding to yaml (Rajeshwari) - Add interrupt support on the rcar sensor (Niklas Söderlund) - Add a new Spreadtrum thermal driver (Baolin Wang) - Add thermal binding for the fsl scu board, a new API to retrieve the sensor id bound to the thermal zone and i.MX system controller sensor (Anson Huang)) - Remove warning log when a deferred probe is requested on Exynos (Marek Szyprowski) - Add the thermal monitoring unit support for imx8mm with its DT bindings (Anson Huang) - Rephrase the Kconfig text for clarity (Linus Walleij) - Use the gpio descriptor for the ti-soc-thermal (Linus Walleij) - Align msg structure to 4 bytes for i.MX SC, fix the Kconfig dependency, add the __may_be unused annotation for PM functions and the COMPILE_TEST option for imx8mm (Anson Huang) - Fix a dependency on regmap in Kconfig for qoriq (Yuantian Tang) - Add DT binding and support for the rcar gen3 r8a77961 and improve the error path on the rcar init function (Niklas Söderlund) - Cleanup and improvements for the tsens Qcom sensor (Amit Kucheria) - Improve code by removing lock and caching values in the rcar thermal sensor (Niklas Söderlund) - Cleanup in the qoriq drivers and add a call to imx_thermal_unregister_legacy_cooling in the removal function (Anson Huang) - Remove redundant 'maxItems' in tsens and sprd DT bindings (Rob Herring) - Change the thermal DT bindings by making the cooling-maps optional (Yuantian Tang) - Add Tiger Lake support (Sumeet Pawnikar) - Use scnprintf() for avoiding potential buffer overflow (Takashi Iwai) - Make pkg_temp_lock a raw_spinlock_t(Clark Williams) - Fix incorrect data types by changing them to signed on i.MX SC (Anson Huang) - Replace zero-length array with flexible-array member (Gustavo A. R. Silva) - Add support for i.MX8MP in the driver and in the DT bindings (Anson Huang) - Fix return value of the cpufreq_set_cur_state() function (Willy Wolff) - Remove abusing and scary WARN_ON in the cpufreq cooling device (Daniel Lezcano) - Fix build warning of incorrect argument type reported by sparse on imx8mm (Anson Huang) - Fix stub for the devfreq cooling device (Martin Blumenstingl) - Fix cpu idle cooling documentation (Sergey Vidishev) * tag 'thermal-v5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (52 commits) Documentation: cpu-idle-cooling: Fix diagram for 33% duty cycle thermal: devfreq_cooling: inline all stubs for CONFIG_DEVFREQ_THERMAL=n thermal: imx8mm: Fix build warning of incorrect argument type thermal/drivers/cpufreq_cooling: Remove abusing WARN_ON thermal/drivers/cpufreq_cooling: Fix return of cpufreq_set_cur_state thermal: imx8mm: Add i.MX8MP support dt-bindings: thermal: imx8mm-thermal: Add support for i.MX8MP thermal: qcom: tsens.h: Replace zero-length array with flexible-array member thermal: imx_sc_thermal: Fix incorrect data type thermal: int340x_thermal: Use scnprintf() for avoiding potential buffer overflow thermal: int340x: processor_thermal: Add Tiger Lake support thermal/x86_pkg_temp: Make pkg_temp_lock a raw_spinlock_t dt-bindings: thermal: make cooling-maps property optional dt-bindings: thermal: qcom-tsens: Remove redundant 'maxItems' dt-bindings: thermal: sprd: Remove redundant 'maxItems' thermal: imx: Calling imx_thermal_unregister_legacy_cooling() in .remove thermal: qoriq: Sort includes alphabetically thermal: qoriq: Use devm_add_action_or_reset() to handle all cleanups thermal: rcar_thermal: Remove lock in rcar_thermal_get_current_temp() thermal: rcar_thermal: Do not store ctemp in rcar_thermal_priv ...
This commit is contained in:
@@ -166,6 +166,17 @@ Required properties:
|
||||
followed by "fsl,imx-sc-key";
|
||||
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
|
||||
|
||||
Thermal bindings based on SCU Message Protocol
|
||||
------------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be :
|
||||
"fsl,imx8qxp-sc-thermal"
|
||||
followed by "fsl,imx-sc-thermal";
|
||||
|
||||
- #thermal-sensor-cells: See Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
for a description.
|
||||
|
||||
Example (imx8qxp):
|
||||
-------------
|
||||
aliases {
|
||||
@@ -238,6 +249,11 @@ firmware {
|
||||
compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt";
|
||||
timeout-sec = <60>;
|
||||
};
|
||||
|
||||
tsens: thermal-sensor {
|
||||
compatible = "fsl,imx8qxp-sc-thermal", "fsl,imx-sc-thermal";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
* Thermal Monitoring Unit (TMU) on Freescale i.MX8MM SoC
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "fsl,imx8mm-tmu" or "fsl,imx8mp-tmu".
|
||||
- reg : Address range of TMU registers.
|
||||
- clocks : TMU's clock source.
|
||||
- #thermal-sensor-cells : Should be 0 or 1. See ./thermal.txt for a description.
|
||||
|
||||
Example:
|
||||
tmu: tmu@30260000 {
|
||||
compatible = "fsl,imx8mm-tmu";
|
||||
reg = <0x30260000 0x10000>;
|
||||
clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
@@ -38,11 +38,11 @@ properties:
|
||||
- enum:
|
||||
- qcom,msm8996-tsens
|
||||
- qcom,msm8998-tsens
|
||||
- qcom,sc7180-tsens
|
||||
- qcom,sdm845-tsens
|
||||
- const: qcom,tsens-v2
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
items:
|
||||
- description: TM registers
|
||||
- description: SROT registers
|
||||
|
||||
@@ -11,6 +11,7 @@ Required properties:
|
||||
- "renesas,r8a774b1-thermal" (RZ/G2N)
|
||||
- "renesas,r8a7795-thermal" (R-Car H3)
|
||||
- "renesas,r8a7796-thermal" (R-Car M3-W)
|
||||
- "renesas,r8a77961-thermal" (R-Car M3-W+)
|
||||
- "renesas,r8a77965-thermal" (R-Car M3-N)
|
||||
- "renesas,r8a77980-thermal" (R-Car V3H)
|
||||
- reg : Address ranges of the thermal registers. Each sensor
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/thermal/sprd-thermal.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Spreadtrum thermal sensor controller bindings
|
||||
|
||||
maintainers:
|
||||
- Orson Zhai <orsonzhai@gmail.com>
|
||||
- Baolin Wang <baolin.wang7@gmail.com>
|
||||
- Chunyan Zhang <zhang.lyra@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sprd,ums512-thermal
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: enable
|
||||
|
||||
nvmem-cells:
|
||||
maxItems: 2
|
||||
description:
|
||||
Reference to nvmem nodes for the calibration data.
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: thm_sign_cal
|
||||
- const: thm_ratio_cal
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^([a-z]*-)?sensor(-section)?@[0-9]+$":
|
||||
type: object
|
||||
description:
|
||||
Represent one thermal sensor.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Specify the sensor id.
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cells:
|
||||
maxItems: 1
|
||||
description:
|
||||
Reference to an nvmem node for the calibration data.
|
||||
|
||||
nvmem-cell-names:
|
||||
const: sen_delta_cal
|
||||
|
||||
required:
|
||||
- reg
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- nvmem-cells
|
||||
- nvmem-cell-names
|
||||
- "#thermal-sensor-cells"
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
examples:
|
||||
- |
|
||||
ap_thm0: thermal@32200000 {
|
||||
compatible = "sprd,ums512-thermal";
|
||||
reg = <0 0x32200000 0 0x10000>;
|
||||
clock-names = "enable";
|
||||
clocks = <&aonapb_gate 32>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
nvmem-cells = <&thm0_sign>, <&thm0_ratio>;
|
||||
nvmem-cell-names = "thm_sign_cal", "thm_ratio_cal";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
prometheus-sensor@0 {
|
||||
reg = <0>;
|
||||
nvmem-cells = <&thm0_sen0>;
|
||||
nvmem-cell-names = "sen_delta_cal";
|
||||
};
|
||||
|
||||
ank-sensor@1 {
|
||||
reg = <1>;
|
||||
nvmem-cells = <&thm0_sen1>;
|
||||
nvmem-cell-names = "sen_delta_cal";
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -142,11 +142,11 @@ Required properties:
|
||||
- trips: A sub-node which is a container of only trip point nodes
|
||||
Type: sub-node required to describe the thermal zone.
|
||||
|
||||
Optional property:
|
||||
- cooling-maps: A sub-node which is a container of only cooling device
|
||||
Type: sub-node map nodes, used to describe the relation between trips
|
||||
and cooling devices.
|
||||
|
||||
Optional property:
|
||||
- coefficients: An array of integers (one signed cell) containing
|
||||
Type: array coefficients to compose a linear relation between
|
||||
Elem size: one cell the sensors listed in the thermal-sensors property.
|
||||
|
||||
@@ -105,8 +105,8 @@ and this variation will modulate the cooling effect.
|
||||
idle <-------------->
|
||||
running
|
||||
|
||||
<----------------------------->
|
||||
duty cycle 33%
|
||||
<--------------------->
|
||||
duty cycle 33%
|
||||
|
||||
|
||||
^
|
||||
|
||||
+36
-6
@@ -1,17 +1,18 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Generic thermal sysfs drivers configuration
|
||||
# Generic thermal drivers configuration
|
||||
#
|
||||
|
||||
menuconfig THERMAL
|
||||
bool "Generic Thermal sysfs driver"
|
||||
bool "Thermal drivers"
|
||||
help
|
||||
Generic Thermal Sysfs driver offers a generic mechanism for
|
||||
Thermal drivers offer a generic mechanism for
|
||||
thermal management. Usually it's made up of one or more thermal
|
||||
zone and cooling device.
|
||||
zones and cooling devices.
|
||||
Each thermal zone contains its own temperature, trip points,
|
||||
cooling devices.
|
||||
All platforms with ACPI thermal support can use this driver.
|
||||
and cooling devices.
|
||||
All platforms with ACPI or Open Firmware thermal support can use
|
||||
this driver.
|
||||
If you want this support, you should say Y here.
|
||||
|
||||
if THERMAL
|
||||
@@ -251,6 +252,27 @@ config IMX_THERMAL
|
||||
cpufreq is used as the cooling device to throttle CPUs when the
|
||||
passive trip is crossed.
|
||||
|
||||
config IMX_SC_THERMAL
|
||||
tristate "Temperature sensor driver for NXP i.MX SoCs with System Controller"
|
||||
depends on IMX_SCU
|
||||
depends on OF
|
||||
help
|
||||
Support for Temperature Monitor (TEMPMON) found on NXP i.MX SoCs with
|
||||
system controller inside, Linux kernel has to communicate with system
|
||||
controller via MU (message unit) IPC to get temperature from thermal
|
||||
sensor. It supports one critical trip point and one
|
||||
passive trip point for each thermal sensor.
|
||||
|
||||
config IMX8MM_THERMAL
|
||||
tristate "Temperature sensor driver for Freescale i.MX8MM SoC"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on OF
|
||||
help
|
||||
Support for Thermal Monitoring Unit (TMU) found on Freescale i.MX8MM SoC.
|
||||
It supports one critical trip point and one passive trip point. The
|
||||
cpufreq is used as the cooling device to throttle CPUs when the passive
|
||||
trip is crossed.
|
||||
|
||||
config MAX77620_THERMAL
|
||||
tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
|
||||
depends on MFD_MAX77620
|
||||
@@ -265,6 +287,7 @@ config QORIQ_THERMAL
|
||||
tristate "QorIQ Thermal Monitoring Unit"
|
||||
depends on THERMAL_OF
|
||||
depends on HAS_IOMEM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
|
||||
It supports one critical trip point and one passive trip point. The
|
||||
@@ -460,4 +483,11 @@ config UNIPHIER_THERMAL
|
||||
Enable this to plug in UniPhier on-chip PVT thermal driver into the
|
||||
thermal framework. The driver supports CPU thermal zone temperature
|
||||
reporting and a couple of trip points.
|
||||
|
||||
config SPRD_THERMAL
|
||||
tristate "Temperature sensor on Spreadtrum SoCs"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
help
|
||||
Support for the Spreadtrum thermal sensor driver in the Linux thermal
|
||||
framework.
|
||||
endif
|
||||
|
||||
@@ -43,6 +43,8 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
|
||||
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
|
||||
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
|
||||
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
|
||||
obj-$(CONFIG_IMX_SC_THERMAL) += imx_sc_thermal.o
|
||||
obj-$(CONFIG_IMX8MM_THERMAL) += imx8mm_thermal.o
|
||||
obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
|
||||
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
|
||||
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
|
||||
@@ -57,3 +59,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
|
||||
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
|
||||
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
|
||||
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
|
||||
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
|
||||
|
||||
@@ -273,7 +273,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
|
||||
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
|
||||
|
||||
/* Request state should be less than max_level */
|
||||
if (WARN_ON(state > cpufreq_cdev->max_level))
|
||||
if (state > cpufreq_cdev->max_level)
|
||||
return -EINVAL;
|
||||
|
||||
num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
|
||||
@@ -437,7 +437,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
int ret;
|
||||
|
||||
/* Request state should be less than max_level */
|
||||
if (WARN_ON(state > cpufreq_cdev->max_level))
|
||||
if (state > cpufreq_cdev->max_level)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if the old cooling action is same as new cooling action */
|
||||
@@ -456,6 +456,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
capacity = frequency * max_capacity;
|
||||
capacity /= cpufreq_cdev->policy->cpuinfo.max_freq;
|
||||
arch_set_thermal_pressure(cpus, max_capacity - capacity);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2020 NXP.
|
||||
*
|
||||
* Author: Anson Huang <Anson.Huang@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
#define TER 0x0 /* TMU enable */
|
||||
#define TPS 0x4
|
||||
#define TRITSR 0x20 /* TMU immediate temp */
|
||||
|
||||
#define TER_EN BIT(31)
|
||||
#define TRITSR_TEMP0_VAL_MASK 0xff
|
||||
#define TRITSR_TEMP1_VAL_MASK 0xff0000
|
||||
|
||||
#define PROBE_SEL_ALL GENMASK(31, 30)
|
||||
|
||||
#define probe_status_offset(x) (30 + x)
|
||||
#define SIGN_BIT BIT(7)
|
||||
#define TEMP_VAL_MASK GENMASK(6, 0)
|
||||
|
||||
#define VER1_TEMP_LOW_LIMIT 10000
|
||||
#define VER2_TEMP_LOW_LIMIT -40000
|
||||
#define VER2_TEMP_HIGH_LIMIT 125000
|
||||
|
||||
#define TMU_VER1 0x1
|
||||
#define TMU_VER2 0x2
|
||||
|
||||
struct thermal_soc_data {
|
||||
u32 num_sensors;
|
||||
u32 version;
|
||||
int (*get_temp)(void *, int *);
|
||||
};
|
||||
|
||||
struct tmu_sensor {
|
||||
struct imx8mm_tmu *priv;
|
||||
u32 hw_id;
|
||||
struct thermal_zone_device *tzd;
|
||||
};
|
||||
|
||||
struct imx8mm_tmu {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
const struct thermal_soc_data *socdata;
|
||||
struct tmu_sensor sensors[0];
|
||||
};
|
||||
|
||||
static int imx8mm_tmu_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct tmu_sensor *sensor = data;
|
||||
struct imx8mm_tmu *tmu = sensor->priv;
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
|
||||
*temp = val * 1000;
|
||||
if (*temp < VER1_TEMP_LOW_LIMIT)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mp_tmu_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct tmu_sensor *sensor = data;
|
||||
struct imx8mm_tmu *tmu = sensor->priv;
|
||||
unsigned long val;
|
||||
bool ready;
|
||||
|
||||
val = readl_relaxed(tmu->base + TRITSR);
|
||||
ready = test_bit(probe_status_offset(sensor->hw_id), &val);
|
||||
if (!ready)
|
||||
return -EAGAIN;
|
||||
|
||||
val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) :
|
||||
FIELD_GET(TRITSR_TEMP0_VAL_MASK, val);
|
||||
if (val & SIGN_BIT) /* negative */
|
||||
val = (~(val & TEMP_VAL_MASK) + 1);
|
||||
|
||||
*temp = val * 1000;
|
||||
if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmu_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct tmu_sensor *sensor = data;
|
||||
struct imx8mm_tmu *tmu = sensor->priv;
|
||||
|
||||
return tmu->socdata->get_temp(data, temp);
|
||||
}
|
||||
|
||||
static struct thermal_zone_of_device_ops tmu_tz_ops = {
|
||||
.get_temp = tmu_get_temp,
|
||||
};
|
||||
|
||||
static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(tmu->base + TER);
|
||||
val = enable ? (val | TER_EN) : (val & ~TER_EN);
|
||||
writel_relaxed(val, tmu->base + TER);
|
||||
}
|
||||
|
||||
static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(tmu->base + TPS);
|
||||
val |= PROBE_SEL_ALL;
|
||||
writel_relaxed(val, tmu->base + TPS);
|
||||
}
|
||||
|
||||
static int imx8mm_tmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct thermal_soc_data *data;
|
||||
struct imx8mm_tmu *tmu;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors,
|
||||
data->num_sensors), GFP_KERNEL);
|
||||
if (!tmu)
|
||||
return -ENOMEM;
|
||||
|
||||
tmu->socdata = data;
|
||||
|
||||
tmu->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(tmu->base))
|
||||
return PTR_ERR(tmu->base);
|
||||
|
||||
tmu->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tmu->clk)) {
|
||||
ret = PTR_ERR(tmu->clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get tmu clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(tmu->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable the monitor during initialization */
|
||||
imx8mm_tmu_enable(tmu, false);
|
||||
|
||||
for (i = 0; i < data->num_sensors; i++) {
|
||||
tmu->sensors[i].priv = tmu;
|
||||
tmu->sensors[i].tzd =
|
||||
devm_thermal_zone_of_sensor_register(&pdev->dev, i,
|
||||
&tmu->sensors[i],
|
||||
&tmu_tz_ops);
|
||||
if (IS_ERR(tmu->sensors[i].tzd)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register thermal zone sensor[%d]: %d\n",
|
||||
i, ret);
|
||||
return PTR_ERR(tmu->sensors[i].tzd);
|
||||
}
|
||||
tmu->sensors[i].hw_id = i;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tmu);
|
||||
|
||||
/* enable all the probes for V2 TMU */
|
||||
if (tmu->socdata->version == TMU_VER2)
|
||||
imx8mm_tmu_probe_sel_all(tmu);
|
||||
|
||||
/* enable the monitor */
|
||||
imx8mm_tmu_enable(tmu, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mm_tmu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
|
||||
|
||||
/* disable TMU */
|
||||
imx8mm_tmu_enable(tmu, false);
|
||||
|
||||
clk_disable_unprepare(tmu->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_soc_data imx8mm_tmu_data = {
|
||||
.num_sensors = 1,
|
||||
.version = TMU_VER1,
|
||||
.get_temp = imx8mm_tmu_get_temp,
|
||||
};
|
||||
|
||||
static struct thermal_soc_data imx8mp_tmu_data = {
|
||||
.num_sensors = 2,
|
||||
.version = TMU_VER2,
|
||||
.get_temp = imx8mp_tmu_get_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx8mm_tmu_table[] = {
|
||||
{ .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, },
|
||||
{ .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver imx8mm_tmu = {
|
||||
.driver = {
|
||||
.name = "i.mx8mm_thermal",
|
||||
.of_match_table = imx8mm_tmu_table,
|
||||
},
|
||||
.probe = imx8mm_tmu_probe,
|
||||
.remove = imx8mm_tmu_remove,
|
||||
};
|
||||
module_platform_driver(imx8mm_tmu);
|
||||
|
||||
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
|
||||
MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018-2020 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/firmware/imx/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
#define IMX_SC_MISC_FUNC_GET_TEMP 13
|
||||
|
||||
static struct imx_sc_ipc *thermal_ipc_handle;
|
||||
|
||||
struct imx_sc_sensor {
|
||||
struct thermal_zone_device *tzd;
|
||||
u32 resource_id;
|
||||
};
|
||||
|
||||
struct req_get_temp {
|
||||
u16 resource_id;
|
||||
u8 type;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct resp_get_temp {
|
||||
s16 celsius;
|
||||
s8 tenths;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct imx_sc_msg_misc_get_temp {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
union {
|
||||
struct req_get_temp req;
|
||||
struct resp_get_temp resp;
|
||||
} data;
|
||||
} __packed __aligned(4);
|
||||
|
||||
static int imx_sc_thermal_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct imx_sc_msg_misc_get_temp msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
struct imx_sc_sensor *sensor = data;
|
||||
int ret;
|
||||
|
||||
msg.data.req.resource_id = sensor->resource_id;
|
||||
msg.data.req.type = IMX_SC_C_TEMP;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_MISC;
|
||||
hdr->func = IMX_SC_MISC_FUNC_GET_TEMP;
|
||||
hdr->size = 2;
|
||||
|
||||
ret = imx_scu_call_rpc(thermal_ipc_handle, &msg, true);
|
||||
if (ret) {
|
||||
dev_err(&sensor->tzd->device, "read temp sensor %d failed, ret %d\n",
|
||||
sensor->resource_id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*temp = msg.data.resp.celsius * 1000 + msg.data.resp.tenths * 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops imx_sc_thermal_ops = {
|
||||
.get_temp = imx_sc_thermal_get_temp,
|
||||
};
|
||||
|
||||
static int imx_sc_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np, *child, *sensor_np;
|
||||
struct imx_sc_sensor *sensor;
|
||||
int ret;
|
||||
|
||||
ret = imx_scu_get_handle(&thermal_ipc_handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
np = of_find_node_by_name(NULL, "thermal-zones");
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
sensor_np = of_node_get(pdev->dev.of_node);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor) {
|
||||
of_node_put(sensor_np);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = thermal_zone_of_get_sensor_id(child,
|
||||
sensor_np,
|
||||
&sensor->resource_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get valid sensor resource id: %d\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
|
||||
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
|
||||
sensor->resource_id,
|
||||
sensor,
|
||||
&imx_sc_thermal_ops);
|
||||
if (IS_ERR(sensor->tzd)) {
|
||||
dev_err(&pdev->dev, "failed to register thermal zone\n");
|
||||
ret = PTR_ERR(sensor->tzd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
of_node_put(sensor_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_sc_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_sc_thermal_table[] = {
|
||||
{ .compatible = "fsl,imx-sc-thermal", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_sc_thermal_table);
|
||||
|
||||
static struct platform_driver imx_sc_thermal_driver = {
|
||||
.probe = imx_sc_thermal_probe,
|
||||
.remove = imx_sc_thermal_remove,
|
||||
.driver = {
|
||||
.name = "imx-sc-thermal",
|
||||
.of_match_table = imx_sc_thermal_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx_sc_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
|
||||
MODULE_DESCRIPTION("Thermal driver for NXP i.MX SoCs with system controller");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -3,24 +3,17 @@
|
||||
// Copyright 2013 Freescale Semiconductor, Inc.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
|
||||
#define REG_SET 0x4
|
||||
@@ -872,14 +865,12 @@ static int imx_thermal_remove(struct platform_device *pdev)
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
|
||||
thermal_zone_device_unregister(data->tz);
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
imx_thermal_unregister_legacy_cooling(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int imx_thermal_suspend(struct device *dev)
|
||||
static int __maybe_unused imx_thermal_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
@@ -900,7 +891,7 @@ static int imx_thermal_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_thermal_resume(struct device *dev)
|
||||
static int __maybe_unused imx_thermal_resume(struct device *dev)
|
||||
{
|
||||
struct imx_thermal_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *map = data->tempmon;
|
||||
@@ -918,7 +909,6 @@ static int imx_thermal_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
|
||||
imx_thermal_suspend, imx_thermal_resume);
|
||||
|
||||
@@ -65,7 +65,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))
|
||||
if (PAGE_SIZE - length > 0)
|
||||
length += snprintf(&buf[length],
|
||||
length += scnprintf(&buf[length],
|
||||
PAGE_SIZE - length,
|
||||
"%s\n",
|
||||
int3400_thermal_uuids[i]);
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
/* JasperLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503
|
||||
|
||||
/* TigerLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_TGL_THERMAL 0x9A03
|
||||
|
||||
#define DRV_NAME "proc_thermal"
|
||||
|
||||
struct power_config {
|
||||
@@ -728,6 +731,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL),
|
||||
.driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_TGL_THERMAL),
|
||||
.driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
|
||||
@@ -448,6 +448,50 @@ thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
return tzd;
|
||||
}
|
||||
|
||||
/**
|
||||
* thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone
|
||||
* @tz_np: a valid thermal zone device node.
|
||||
* @sensor_np: a sensor node of a valid sensor device.
|
||||
* @id: the sensor ID returned if success.
|
||||
*
|
||||
* This function will get sensor ID from a given thermal zone node and
|
||||
* the sensor node must match the temperature provider @sensor_np.
|
||||
*
|
||||
* Return: 0 on success, proper error code otherwise.
|
||||
*/
|
||||
|
||||
int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
|
||||
struct device_node *sensor_np,
|
||||
u32 *id)
|
||||
{
|
||||
struct of_phandle_args sensor_specs;
|
||||
int ret;
|
||||
|
||||
ret = of_parse_phandle_with_args(tz_np,
|
||||
"thermal-sensors",
|
||||
"#thermal-sensor-cells",
|
||||
0,
|
||||
&sensor_specs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sensor_specs.np != sensor_np) {
|
||||
of_node_put(sensor_specs.np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (sensor_specs.args_count > 1)
|
||||
pr_warn("%pOFn: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np, sensor_specs.args_count);
|
||||
|
||||
*id = sensor_specs.args_count ? sensor_specs.args[0] : 0;
|
||||
|
||||
of_node_put(sensor_specs.np);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id);
|
||||
|
||||
/**
|
||||
* thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
|
||||
* @dev: a valid struct device pointer of a sensor device. Must contain
|
||||
@@ -499,36 +543,22 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
|
||||
sensor_np = of_node_get(dev->of_node);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct of_phandle_args sensor_specs;
|
||||
int ret, id;
|
||||
|
||||
/* For now, thermal framework supports only 1 sensor per zone */
|
||||
ret = of_parse_phandle_with_args(child, "thermal-sensors",
|
||||
"#thermal-sensor-cells",
|
||||
0, &sensor_specs);
|
||||
ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
if (sensor_specs.args_count >= 1) {
|
||||
id = sensor_specs.args[0];
|
||||
WARN(sensor_specs.args_count > 1,
|
||||
"%pOFn: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np, sensor_specs.args_count);
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
if (sensor_specs.np == sensor_np && id == sensor_id) {
|
||||
if (id == sensor_id) {
|
||||
tzd = thermal_zone_of_add_sensor(child, sensor_np,
|
||||
data, ops);
|
||||
if (!IS_ERR(tzd))
|
||||
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
|
||||
|
||||
of_node_put(sensor_specs.np);
|
||||
of_node_put(child);
|
||||
goto exit;
|
||||
}
|
||||
of_node_put(sensor_specs.np);
|
||||
}
|
||||
exit:
|
||||
of_node_put(sensor_np);
|
||||
|
||||
@@ -245,7 +245,7 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
|
||||
return adc_code * slope + offset;
|
||||
}
|
||||
|
||||
static int get_temp_8960(struct tsens_sensor *s, int *temp)
|
||||
static int get_temp_8960(const struct tsens_sensor *s, int *temp)
|
||||
{
|
||||
int ret;
|
||||
u32 code, trdy;
|
||||
@@ -279,7 +279,7 @@ static const struct tsens_ops ops_8960 = {
|
||||
.resume = resume_8960,
|
||||
};
|
||||
|
||||
const struct tsens_plat_data data_8960 = {
|
||||
struct tsens_plat_data data_8960 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8960,
|
||||
};
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
* @low_thresh: lower threshold temperature value
|
||||
* @low_irq_mask: mask register for lower threshold irqs
|
||||
* @low_irq_clear: clear register for lower threshold irqs
|
||||
* @crit_viol: critical threshold violated
|
||||
* @crit_thresh: critical threshold temperature value
|
||||
* @crit_irq_mask: mask register for critical threshold irqs
|
||||
* @crit_irq_clear: clear register for critical threshold irqs
|
||||
*
|
||||
* Structure containing data about temperature threshold settings and
|
||||
* irq status if they were violated.
|
||||
@@ -36,6 +40,10 @@ struct tsens_irq_data {
|
||||
int low_thresh;
|
||||
u32 low_irq_mask;
|
||||
u32 low_irq_clear;
|
||||
u32 crit_viol;
|
||||
u32 crit_thresh;
|
||||
u32 crit_irq_mask;
|
||||
u32 crit_irq_clear;
|
||||
};
|
||||
|
||||
char *qfprom_read(struct device *dev, const char *cname)
|
||||
@@ -128,7 +136,7 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
|
||||
* Return: Temperature in milliCelsius on success, a negative errno will
|
||||
* be returned in error cases
|
||||
*/
|
||||
static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
|
||||
static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
u32 resolution;
|
||||
@@ -160,7 +168,7 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
|
||||
*
|
||||
* Return: ADC code or temperature in deciCelsius.
|
||||
*/
|
||||
static int tsens_mC_to_hw(struct tsens_sensor *s, int temp)
|
||||
static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
|
||||
@@ -189,6 +197,9 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
|
||||
case LOWER:
|
||||
index = LOW_INT_CLEAR_0 + hw_id;
|
||||
break;
|
||||
case CRITICAL:
|
||||
/* No critical interrupts before v2 */
|
||||
return;
|
||||
}
|
||||
regmap_field_write(priv->rf[index], enable ? 0 : 1);
|
||||
}
|
||||
@@ -214,6 +225,10 @@ static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
|
||||
index_mask = LOW_INT_MASK_0 + hw_id;
|
||||
index_clear = LOW_INT_CLEAR_0 + hw_id;
|
||||
break;
|
||||
case CRITICAL:
|
||||
index_mask = CRIT_INT_MASK_0 + hw_id;
|
||||
index_clear = CRIT_INT_CLEAR_0 + hw_id;
|
||||
break;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
@@ -268,14 +283,23 @@ static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
|
||||
ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (d->up_viol || d->low_viol)
|
||||
|
||||
if (priv->feat->crit_int) {
|
||||
ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
|
||||
&d->crit_viol);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (d->up_viol || d->low_viol || d->crit_viol)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
|
||||
struct tsens_sensor *s, struct tsens_irq_data *d)
|
||||
const struct tsens_sensor *s,
|
||||
struct tsens_irq_data *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -292,22 +316,37 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
|
||||
ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
|
||||
&d->crit_irq_clear);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
|
||||
&d->crit_irq_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
|
||||
} else {
|
||||
/* No mask register on older TSENS */
|
||||
d->up_irq_mask = 0;
|
||||
d->low_irq_mask = 0;
|
||||
d->crit_irq_clear = 0;
|
||||
d->crit_irq_mask = 0;
|
||||
d->crit_thresh = 0;
|
||||
}
|
||||
|
||||
d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
|
||||
d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
|
||||
|
||||
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u) | clr(%u|%u) | mask(%u|%u)\n",
|
||||
hw_id, __func__, (d->up_viol || d->low_viol) ? "(V)" : "",
|
||||
d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear,
|
||||
d->low_irq_mask, d->up_irq_mask);
|
||||
dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__,
|
||||
(d->up_viol || d->low_viol) ? "(violation)" : "",
|
||||
d->low_thresh, d->up_thresh);
|
||||
dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
|
||||
hw_id, __func__,
|
||||
(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
|
||||
d->low_viol, d->up_viol, d->crit_viol,
|
||||
d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
|
||||
d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
|
||||
dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
|
||||
(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
|
||||
d->low_thresh, d->up_thresh, d->crit_thresh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -321,6 +360,76 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsens_critical_irq_thread() - Threaded handler for critical interrupts
|
||||
* @irq: irq number
|
||||
* @data: tsens controller private data
|
||||
*
|
||||
* Check FSM watchdog bark status and clear if needed.
|
||||
* Check all sensors to find ones that violated their critical threshold limits.
|
||||
* Clear and then re-enable the interrupt.
|
||||
*
|
||||
* The level-triggered interrupt might deassert if the temperature returned to
|
||||
* within the threshold limits by the time the handler got scheduled. We
|
||||
* consider the irq to have been handled in that case.
|
||||
*
|
||||
* Return: IRQ_HANDLED
|
||||
*/
|
||||
irqreturn_t tsens_critical_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct tsens_priv *priv = data;
|
||||
struct tsens_irq_data d;
|
||||
int temp, ret, i;
|
||||
u32 wdog_status, wdog_count;
|
||||
|
||||
if (priv->feat->has_watchdog) {
|
||||
ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
|
||||
&wdog_status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (wdog_status) {
|
||||
/* Clear WDOG interrupt */
|
||||
regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
|
||||
regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
|
||||
ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
|
||||
&wdog_count);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (wdog_count)
|
||||
dev_dbg(priv->dev, "%s: watchdog count: %d\n",
|
||||
__func__, wdog_count);
|
||||
|
||||
/* Fall through to handle critical interrupts if any */
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
const struct tsens_sensor *s = &priv->sensor[i];
|
||||
u32 hw_id = s->hw_id;
|
||||
|
||||
if (IS_ERR(s->tzd))
|
||||
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;
|
||||
}
|
||||
|
||||
tsens_read_irq_state(priv, hw_id, s, &d);
|
||||
if (d.crit_viol &&
|
||||
!masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
|
||||
/* Mask critical interrupts, unused on Linux */
|
||||
tsens_set_interrupt(priv, hw_id, CRITICAL, false);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsens_irq_thread - Threaded interrupt handler for uplow interrupts
|
||||
* @irq: irq number
|
||||
@@ -346,10 +455,10 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
bool trigger = false;
|
||||
struct tsens_sensor *s = &priv->sensor[i];
|
||||
const struct tsens_sensor *s = &priv->sensor[i];
|
||||
u32 hw_id = s->hw_id;
|
||||
|
||||
if (IS_ERR(priv->sensor[i].tzd))
|
||||
if (IS_ERR(s->tzd))
|
||||
continue;
|
||||
if (!tsens_threshold_violated(priv, hw_id, &d))
|
||||
continue;
|
||||
@@ -368,7 +477,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
tsens_set_interrupt(priv, hw_id, UPPER, disable);
|
||||
if (d.up_thresh > temp) {
|
||||
dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
|
||||
priv->sensor[i].hw_id, __func__);
|
||||
hw_id, __func__);
|
||||
tsens_set_interrupt(priv, hw_id, UPPER, enable);
|
||||
} else {
|
||||
trigger = true;
|
||||
@@ -379,7 +488,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
tsens_set_interrupt(priv, hw_id, LOWER, disable);
|
||||
if (d.low_thresh < temp) {
|
||||
dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
|
||||
priv->sensor[i].hw_id, __func__);
|
||||
hw_id, __func__);
|
||||
tsens_set_interrupt(priv, hw_id, LOWER, enable);
|
||||
} else {
|
||||
trigger = true;
|
||||
@@ -392,7 +501,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
|
||||
if (trigger) {
|
||||
dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
|
||||
hw_id, __func__, temp);
|
||||
thermal_zone_device_update(priv->sensor[i].tzd,
|
||||
thermal_zone_device_update(s->tzd,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
} else {
|
||||
dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
|
||||
@@ -435,7 +544,7 @@ int tsens_set_trips(void *_sensor, int low, int high)
|
||||
spin_unlock_irqrestore(&priv->ul_lock, flags);
|
||||
|
||||
dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
|
||||
s->hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
|
||||
hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -457,7 +566,7 @@ void tsens_disable_irq(struct tsens_priv *priv)
|
||||
regmap_field_write(priv->rf[INT_EN], 0);
|
||||
}
|
||||
|
||||
int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
|
||||
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
int hw_id = s->hw_id;
|
||||
@@ -486,7 +595,7 @@ int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_temp_common(struct tsens_sensor *s, int *temp)
|
||||
int get_temp_common(const struct tsens_sensor *s, int *temp)
|
||||
{
|
||||
struct tsens_priv *priv = s->priv;
|
||||
int hw_id = s->hw_id;
|
||||
@@ -590,6 +699,7 @@ int __init init_common(struct tsens_priv *priv)
|
||||
{
|
||||
void __iomem *tm_base, *srot_base;
|
||||
struct device *dev = priv->dev;
|
||||
u32 ver_minor;
|
||||
struct resource *res;
|
||||
u32 enabled;
|
||||
int ret, i, j;
|
||||
@@ -602,7 +712,7 @@ int __init init_common(struct tsens_priv *priv)
|
||||
/* DT with separate SROT and TM address space */
|
||||
priv->tm_offset = 0;
|
||||
res = platform_get_resource(op, IORESOURCE_MEM, 1);
|
||||
srot_base = devm_ioremap_resource(&op->dev, res);
|
||||
srot_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(srot_base)) {
|
||||
ret = PTR_ERR(srot_base);
|
||||
goto err_put_device;
|
||||
@@ -620,7 +730,7 @@ int __init init_common(struct tsens_priv *priv)
|
||||
}
|
||||
|
||||
res = platform_get_resource(op, IORESOURCE_MEM, 0);
|
||||
tm_base = devm_ioremap_resource(&op->dev, res);
|
||||
tm_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(tm_base)) {
|
||||
ret = PTR_ERR(tm_base);
|
||||
goto err_put_device;
|
||||
@@ -639,6 +749,9 @@ int __init init_common(struct tsens_priv *priv)
|
||||
if (IS_ERR(priv->rf[i]))
|
||||
return PTR_ERR(priv->rf[i]);
|
||||
}
|
||||
ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
|
||||
if (ret)
|
||||
goto err_put_device;
|
||||
}
|
||||
|
||||
priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
|
||||
@@ -683,12 +796,47 @@ int __init init_common(struct tsens_priv *priv)
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->feat->crit_int) {
|
||||
/* Loop might need changes if enum regfield_ids is reordered */
|
||||
for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
|
||||
for (i = 0; i < priv->feat->max_sensors; i++) {
|
||||
int idx = j + i;
|
||||
|
||||
priv->rf[idx] =
|
||||
devm_regmap_field_alloc(dev,
|
||||
priv->tm_map,
|
||||
priv->fields[idx]);
|
||||
if (IS_ERR(priv->rf[idx])) {
|
||||
ret = PTR_ERR(priv->rf[idx]);
|
||||
goto err_put_device;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tsens_version(priv) > VER_1_X && ver_minor > 2) {
|
||||
/* Watchdog is present only on v2.3+ */
|
||||
priv->feat->has_watchdog = 1;
|
||||
for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
|
||||
priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
|
||||
priv->fields[i]);
|
||||
if (IS_ERR(priv->rf[i])) {
|
||||
ret = PTR_ERR(priv->rf[i]);
|
||||
goto err_put_device;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Watchdog is already enabled, unmask the bark.
|
||||
* Disable cycle completion monitoring
|
||||
*/
|
||||
regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
|
||||
regmap_field_write(priv->rf[CC_MON_MASK], 1);
|
||||
}
|
||||
|
||||
spin_lock_init(&priv->ul_lock);
|
||||
tsens_enable_irq(priv);
|
||||
tsens_debug_init(op);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_device:
|
||||
put_device(&op->dev);
|
||||
return ret;
|
||||
|
||||
@@ -327,7 +327,7 @@ static int calibrate_8974(struct tsens_priv *priv)
|
||||
|
||||
/* v0.1: 8916, 8974 */
|
||||
|
||||
static const struct tsens_features tsens_v0_1_feat = {
|
||||
static struct tsens_features tsens_v0_1_feat = {
|
||||
.ver_major = VER_0_1,
|
||||
.crit_int = 0,
|
||||
.adc = 1,
|
||||
@@ -377,7 +377,7 @@ static const struct tsens_ops ops_8916 = {
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
const struct tsens_plat_data data_8916 = {
|
||||
struct tsens_plat_data data_8916 = {
|
||||
.num_sensors = 5,
|
||||
.ops = &ops_8916,
|
||||
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
|
||||
@@ -392,7 +392,7 @@ static const struct tsens_ops ops_8974 = {
|
||||
.get_temp = get_temp_common,
|
||||
};
|
||||
|
||||
const struct tsens_plat_data data_8974 = {
|
||||
struct tsens_plat_data data_8974 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8974,
|
||||
.feat = &tsens_v0_1_feat,
|
||||
|
||||
@@ -299,7 +299,7 @@ static int calibrate_8976(struct tsens_priv *priv)
|
||||
|
||||
/* v1.x: msm8956,8976,qcs404,405 */
|
||||
|
||||
static const struct tsens_features tsens_v1_feat = {
|
||||
static struct tsens_features tsens_v1_feat = {
|
||||
.ver_major = VER_1_X,
|
||||
.crit_int = 0,
|
||||
.adc = 1,
|
||||
@@ -368,7 +368,7 @@ static const struct tsens_ops ops_generic_v1 = {
|
||||
.get_temp = get_temp_tsens_valid,
|
||||
};
|
||||
|
||||
const struct tsens_plat_data data_tsens_v1 = {
|
||||
struct tsens_plat_data data_tsens_v1 = {
|
||||
.ops = &ops_generic_v1,
|
||||
.feat = &tsens_v1_feat,
|
||||
.fields = tsens_v1_regfields,
|
||||
@@ -381,7 +381,7 @@ static const struct tsens_ops ops_8976 = {
|
||||
};
|
||||
|
||||
/* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */
|
||||
const struct tsens_plat_data data_8976 = {
|
||||
struct tsens_plat_data data_8976 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8976,
|
||||
.hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user