mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:02:20 +00:00
- 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) -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEGn3N4YVz0WNVyHskqDIjiipP6E8FAl6MXkEACgkQqDIjiipP 6E+BIggAh52YBU8D8GthsvCPTgka95+wAIaZtx4Y7UnhvshHhM2w+m97TQOXK373 95mwO9mwQuAoksSmLo7pBQYJ7HItQ26Yoq9akpOL6EiT+BEWiqoHJgl+afVVtoKa n67d3Pa6coup1+PQNIA8kpTIOmUQTP8THtwpZ3HlChWsg22NTd0QUGpGJ1TtBD/q KqMdQjxahFJ4RTYsnECWBkw3EMkczMNMgdrXEvm4rMkcaJzv9g2ZPqerOShK/RzP 8sejWczt6jaRwu4hLpd/lLikTSHZoXMFJ8ylAiQXFELCPAQIrMS/ae+dTGJ4qP7x hkjsewPbeA1Z+al/IBVHiCtOzKkICQ== =XDYl -----END PGP SIGNATURE----- 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:
commit
34183ddd13
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
15
Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
Normal file
15
Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
Normal file
@ -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
|
||||
|
107
Documentation/devicetree/bindings/thermal/sprd-thermal.yaml
Normal file
107
Documentation/devicetree/bindings/thermal/sprd-thermal.yaml
Normal file
@ -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%
|
||||
|
||||
|
||||
^
|
||||
|
@ -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;
|
||||
|
236
drivers/thermal/imx8mm_thermal.c
Normal file
236
drivers/thermal/imx8mm_thermal.c
Normal file
@ -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");
|
148
drivers/thermal/imx_sc_thermal.c
Normal file
148
drivers/thermal/imx_sc_thermal.c
Normal file
@ -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},
|
||||
|
@ -24,10 +24,11 @@
|
||||
#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
|
||||
#define TM_Sn_STATUS_OFF 0x00a0
|
||||
#define TM_TRDY_OFF 0x00e4
|
||||
#define TM_WDOG_LOG_OFF 0x013c
|
||||
|
||||
/* v2.x: 8996, 8998, sdm845 */
|
||||
|
||||
static const struct tsens_features tsens_v2_feat = {
|
||||
static struct tsens_features tsens_v2_feat = {
|
||||
.ver_major = VER_2_X,
|
||||
.crit_int = 1,
|
||||
.adc = 0,
|
||||
@ -51,8 +52,9 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
|
||||
[INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
|
||||
|
||||
/* TEMPERATURE THRESHOLDS */
|
||||
REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11),
|
||||
REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23),
|
||||
REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11),
|
||||
REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12, 23),
|
||||
REG_FIELD_FOR_EACH_SENSOR16(CRIT_THRESH, TM_Sn_CRITICAL_THRESHOLD_OFF, 0, 11),
|
||||
|
||||
/* INTERRUPTS [CLEAR/STATUS/MASK] */
|
||||
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
|
||||
@ -61,6 +63,18 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
|
||||
REG_FIELD_SPLIT_BITS_16_31(UP_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF),
|
||||
REG_FIELD_SPLIT_BITS_16_31(UP_INT_CLEAR, TM_UPPER_LOWER_INT_CLEAR_OFF),
|
||||
REG_FIELD_SPLIT_BITS_16_31(UP_INT_MASK, TM_UPPER_LOWER_INT_MASK_OFF),
|
||||
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_STATUS, TM_CRITICAL_INT_STATUS_OFF),
|
||||
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_CLEAR, TM_CRITICAL_INT_CLEAR_OFF),
|
||||
REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_MASK, TM_CRITICAL_INT_MASK_OFF),
|
||||
|
||||
/* WATCHDOG on v2.3 or later */
|
||||
[WDOG_BARK_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 31, 31),
|
||||
[WDOG_BARK_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 31, 31),
|
||||
[WDOG_BARK_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 31, 31),
|
||||
[CC_MON_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 30, 30),
|
||||
[CC_MON_CLEAR] = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF, 30, 30),
|
||||
[CC_MON_MASK] = REG_FIELD(TM_CRITICAL_INT_MASK_OFF, 30, 30),
|
||||
[WDOG_BARK_COUNT] = REG_FIELD(TM_WDOG_LOG_OFF, 0, 7),
|
||||
|
||||
/* Sn_STATUS */
|
||||
REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11),
|
||||
@ -81,14 +95,14 @@ static const struct tsens_ops ops_generic_v2 = {
|
||||
.get_temp = get_temp_tsens_valid,
|
||||
};
|
||||
|
||||
const struct tsens_plat_data data_tsens_v2 = {
|
||||
struct tsens_plat_data data_tsens_v2 = {
|
||||
.ops = &ops_generic_v2,
|
||||
.feat = &tsens_v2_feat,
|
||||
.fields = tsens_v2_regfields,
|
||||
};
|
||||
|
||||
/* Kept around for backward compatibility with old msm8996.dtsi */
|
||||
const struct tsens_plat_data data_8996 = {
|
||||
struct tsens_plat_data data_8996 = {
|
||||
.num_sensors = 13,
|
||||
.ops = &ops_generic_v2,
|
||||
.feat = &tsens_v2_feat,
|
||||
|
@ -85,11 +85,42 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = {
|
||||
.set_trips = tsens_set_trips,
|
||||
};
|
||||
|
||||
static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
|
||||
irq_handler_t thread_fn)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret, irq;
|
||||
|
||||
pdev = of_find_device_by_node(priv->dev->of_node);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, irqname);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
/* For old DTs with no IRQ defined */
|
||||
if (irq == -ENXIO)
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq,
|
||||
NULL, thread_fn,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), priv);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "%s: failed to get irq\n",
|
||||
__func__);
|
||||
else
|
||||
enable_irq_wake(irq);
|
||||
}
|
||||
|
||||
put_device(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsens_register(struct tsens_priv *priv)
|
||||
{
|
||||
int i, ret, irq;
|
||||
int i, ret;
|
||||
struct thermal_zone_device *tzd;
|
||||
struct platform_device *pdev;
|
||||
|
||||
for (i = 0; i < priv->num_sensors; i++) {
|
||||
priv->sensor[i].priv = priv;
|
||||
@ -103,32 +134,14 @@ static int tsens_register(struct tsens_priv *priv)
|
||||
priv->ops->enable(priv, i);
|
||||
}
|
||||
|
||||
pdev = of_find_device_by_node(priv->dev->of_node);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "uplow");
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
/* For old DTs with no IRQ defined */
|
||||
if (irq == -ENXIO)
|
||||
ret = 0;
|
||||
goto err_put_device;
|
||||
}
|
||||
if (priv->feat->crit_int)
|
||||
ret = tsens_register_irq(priv, "critical",
|
||||
tsens_critical_irq_thread);
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq,
|
||||
NULL, tsens_irq_thread,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: failed to get irq\n", __func__);
|
||||
goto err_put_device;
|
||||
}
|
||||
|
||||
enable_irq_wake(irq);
|
||||
|
||||
err_put_device:
|
||||
put_device(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
struct tsens_priv;
|
||||
|
||||
/* IP version numbers in ascending order */
|
||||
enum tsens_ver {
|
||||
VER_0_1 = 0,
|
||||
VER_1_X,
|
||||
@ -32,6 +33,7 @@ enum tsens_ver {
|
||||
enum tsens_irq_type {
|
||||
LOWER,
|
||||
UPPER,
|
||||
CRITICAL,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -67,7 +69,7 @@ struct tsens_ops {
|
||||
/* mandatory callbacks */
|
||||
int (*init)(struct tsens_priv *priv);
|
||||
int (*calibrate)(struct tsens_priv *priv);
|
||||
int (*get_temp)(struct tsens_sensor *s, int *temp);
|
||||
int (*get_temp)(const struct tsens_sensor *s, int *temp);
|
||||
/* optional callbacks */
|
||||
int (*enable)(struct tsens_priv *priv, int i);
|
||||
void (*disable)(struct tsens_priv *priv);
|
||||
@ -374,6 +376,82 @@ enum regfield_ids {
|
||||
CRITICAL_STATUS_13,
|
||||
CRITICAL_STATUS_14,
|
||||
CRITICAL_STATUS_15,
|
||||
CRIT_INT_STATUS_0, /* CRITICAL interrupt status */
|
||||
CRIT_INT_STATUS_1,
|
||||
CRIT_INT_STATUS_2,
|
||||
CRIT_INT_STATUS_3,
|
||||
CRIT_INT_STATUS_4,
|
||||
CRIT_INT_STATUS_5,
|
||||
CRIT_INT_STATUS_6,
|
||||
CRIT_INT_STATUS_7,
|
||||
CRIT_INT_STATUS_8,
|
||||
CRIT_INT_STATUS_9,
|
||||
CRIT_INT_STATUS_10,
|
||||
CRIT_INT_STATUS_11,
|
||||
CRIT_INT_STATUS_12,
|
||||
CRIT_INT_STATUS_13,
|
||||
CRIT_INT_STATUS_14,
|
||||
CRIT_INT_STATUS_15,
|
||||
CRIT_INT_CLEAR_0, /* CRITICAL interrupt clear */
|
||||
CRIT_INT_CLEAR_1,
|
||||
CRIT_INT_CLEAR_2,
|
||||
CRIT_INT_CLEAR_3,
|
||||
CRIT_INT_CLEAR_4,
|
||||
CRIT_INT_CLEAR_5,
|
||||
CRIT_INT_CLEAR_6,
|
||||
CRIT_INT_CLEAR_7,
|
||||
CRIT_INT_CLEAR_8,
|
||||
CRIT_INT_CLEAR_9,
|
||||
CRIT_INT_CLEAR_10,
|
||||
CRIT_INT_CLEAR_11,
|
||||
CRIT_INT_CLEAR_12,
|
||||
CRIT_INT_CLEAR_13,
|
||||
CRIT_INT_CLEAR_14,
|
||||
CRIT_INT_CLEAR_15,
|
||||
CRIT_INT_MASK_0, /* CRITICAL interrupt mask */
|
||||
CRIT_INT_MASK_1,
|
||||
CRIT_INT_MASK_2,
|
||||
CRIT_INT_MASK_3,
|
||||
CRIT_INT_MASK_4,
|
||||
CRIT_INT_MASK_5,
|
||||
CRIT_INT_MASK_6,
|
||||
CRIT_INT_MASK_7,
|
||||
CRIT_INT_MASK_8,
|
||||
CRIT_INT_MASK_9,
|
||||
CRIT_INT_MASK_10,
|
||||
CRIT_INT_MASK_11,
|
||||
CRIT_INT_MASK_12,
|
||||
CRIT_INT_MASK_13,
|
||||
CRIT_INT_MASK_14,
|
||||
CRIT_INT_MASK_15,
|
||||
CRIT_THRESH_0, /* CRITICAL threshold values */
|
||||
CRIT_THRESH_1,
|
||||
CRIT_THRESH_2,
|
||||
CRIT_THRESH_3,
|
||||
CRIT_THRESH_4,
|
||||
CRIT_THRESH_5,
|
||||
CRIT_THRESH_6,
|
||||
CRIT_THRESH_7,
|
||||
CRIT_THRESH_8,
|
||||
CRIT_THRESH_9,
|
||||
CRIT_THRESH_10,
|
||||
CRIT_THRESH_11,
|
||||
CRIT_THRESH_12,
|
||||
CRIT_THRESH_13,
|
||||
CRIT_THRESH_14,
|
||||
CRIT_THRESH_15,
|
||||
|
||||
/* WATCHDOG */
|
||||
WDOG_BARK_STATUS,
|
||||
WDOG_BARK_CLEAR,
|
||||
WDOG_BARK_MASK,
|
||||
WDOG_BARK_COUNT,
|
||||
|
||||
/* CYCLE COMPLETION MONITOR */
|
||||
CC_MON_STATUS,
|
||||
CC_MON_CLEAR,
|
||||
CC_MON_MASK,
|
||||
|
||||
MIN_STATUS_0, /* MIN threshold violated */
|
||||
MIN_STATUS_1,
|
||||
MIN_STATUS_2,
|
||||
@ -418,6 +496,7 @@ enum regfield_ids {
|
||||
* @adc: do the sensors only output adc code (instead of temperature)?
|
||||
* @srot_split: does the IP neatly splits the register space into SROT and TM,
|
||||
* with SROT only being available to secure boot firmware?
|
||||
* @has_watchdog: does this IP support watchdog functionality?
|
||||
* @max_sensors: maximum sensors supported by this version of the IP
|
||||
*/
|
||||
struct tsens_features {
|
||||
@ -425,6 +504,7 @@ struct tsens_features {
|
||||
unsigned int crit_int:1;
|
||||
unsigned int adc:1;
|
||||
unsigned int srot_split:1;
|
||||
unsigned int has_watchdog:1;
|
||||
unsigned int max_sensors;
|
||||
};
|
||||
|
||||
@ -440,12 +520,14 @@ struct tsens_plat_data {
|
||||
const u32 num_sensors;
|
||||
const struct tsens_ops *ops;
|
||||
unsigned int *hw_ids;
|
||||
const struct tsens_features *feat;
|
||||
struct tsens_features *feat;
|
||||
const struct reg_field *fields;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tsens_context - Registers to be saved/restored across a context loss
|
||||
* @threshold: Threshold register value
|
||||
* @control: Control register value
|
||||
*/
|
||||
struct tsens_context {
|
||||
int threshold;
|
||||
@ -460,6 +542,8 @@ struct tsens_context {
|
||||
* @srot_map: pointer to SROT register address space
|
||||
* @tm_offset: deal with old device trees that don't address TM and SROT
|
||||
* address space separately
|
||||
* @ul_lock: lock while processing upper/lower threshold interrupts
|
||||
* @crit_lock: lock while processing critical threshold interrupts
|
||||
* @rf: array of regmap_fields used to store value of the field
|
||||
* @ctx: registers to be saved and restored during suspend/resume
|
||||
* @feat: features of the IP
|
||||
@ -481,36 +565,37 @@ struct tsens_priv {
|
||||
|
||||
struct regmap_field *rf[MAX_REGFIELDS];
|
||||
struct tsens_context ctx;
|
||||
const struct tsens_features *feat;
|
||||
struct tsens_features *feat;
|
||||
const struct reg_field *fields;
|
||||
const struct tsens_ops *ops;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug;
|
||||
|
||||
struct tsens_sensor sensor[0];
|
||||
struct tsens_sensor sensor[];
|
||||
};
|
||||
|
||||
char *qfprom_read(struct device *dev, const char *cname);
|
||||
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(struct tsens_sensor *s, int *temp);
|
||||
int get_temp_common(struct tsens_sensor *s, int *temp);
|
||||
int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
|
||||
int get_temp_common(const struct tsens_sensor *s, int *temp);
|
||||
int tsens_enable_irq(struct tsens_priv *priv);
|
||||
void tsens_disable_irq(struct tsens_priv *priv);
|
||||
int tsens_set_trips(void *_sensor, int low, int high);
|
||||
irqreturn_t tsens_irq_thread(int irq, void *data);
|
||||
irqreturn_t tsens_critical_irq_thread(int irq, void *data);
|
||||
|
||||
/* TSENS target */
|
||||
extern const struct tsens_plat_data data_8960;
|
||||
extern struct tsens_plat_data data_8960;
|
||||
|
||||
/* TSENS v0.1 targets */
|
||||
extern const struct tsens_plat_data data_8916, data_8974;
|
||||
extern struct tsens_plat_data data_8916, data_8974;
|
||||
|
||||
/* TSENS v1 targets */
|
||||
extern const struct tsens_plat_data data_tsens_v1, data_8976;
|
||||
extern struct tsens_plat_data data_tsens_v1, data_8976;
|
||||
|
||||
/* TSENS v2 targets */
|
||||
extern const struct tsens_plat_data data_8996, data_tsens_v2;
|
||||
extern struct tsens_plat_data data_8996, data_tsens_v2;
|
||||
|
||||
#endif /* __QCOM_TSENS_H__ */
|
||||
|
@ -3,12 +3,11 @@
|
||||
// Copyright 2016 Freescale Semiconductor, Inc.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/thermal.h>
|
||||
@ -228,6 +227,14 @@ static const struct regmap_access_table qoriq_rd_table = {
|
||||
.n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges),
|
||||
};
|
||||
|
||||
static void qoriq_tmu_action(void *p)
|
||||
{
|
||||
struct qoriq_tmu_data *data = p;
|
||||
|
||||
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
|
||||
clk_disable_unprepare(data->clk);
|
||||
}
|
||||
|
||||
static int qoriq_tmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@ -278,6 +285,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* version register offset at: 0xbf8 on both v1 and v2 */
|
||||
ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);
|
||||
if (ret) {
|
||||
@ -290,34 +301,16 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
|
||||
|
||||
ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
ret = qoriq_tmu_register_tmu_zone(dev, data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to register sensors\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qoriq_tmu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
/* Disable monitoring */
|
||||
regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -365,7 +358,6 @@ static struct platform_driver qoriq_tmu = {
|
||||
.of_match_table = qoriq_tmu_match,
|
||||
},
|
||||
.probe = qoriq_tmu_probe,
|
||||
.remove = qoriq_tmu_remove,
|
||||
};
|
||||
module_platform_driver(qoriq_tmu);
|
||||
|
||||
|
@ -81,8 +81,6 @@ struct rcar_gen3_thermal_tsc {
|
||||
void __iomem *base;
|
||||
struct thermal_zone_device *zone;
|
||||
struct equation_coefs coef;
|
||||
int low;
|
||||
int high;
|
||||
int tj_t;
|
||||
int id; /* thermal channel id */
|
||||
};
|
||||
@ -204,12 +202,14 @@ static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
|
||||
return INT_FIXPT(val);
|
||||
}
|
||||
|
||||
static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
|
||||
static int rcar_gen3_thermal_update_range(struct rcar_gen3_thermal_tsc *tsc)
|
||||
{
|
||||
struct rcar_gen3_thermal_tsc *tsc = devdata;
|
||||
int temperature, low, high;
|
||||
|
||||
low = clamp_val(low, -40000, 120000);
|
||||
high = clamp_val(high, -40000, 120000);
|
||||
rcar_gen3_thermal_get_temp(tsc, &temperature);
|
||||
|
||||
low = temperature - MCELSIUS(1);
|
||||
high = temperature + MCELSIUS(1);
|
||||
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
|
||||
rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
|
||||
@ -217,15 +217,11 @@ static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
|
||||
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
|
||||
rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
|
||||
|
||||
tsc->low = low;
|
||||
tsc->high = high;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
|
||||
.get_temp = rcar_gen3_thermal_get_temp,
|
||||
.set_trips = rcar_gen3_thermal_set_trips,
|
||||
};
|
||||
|
||||
static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on)
|
||||
@ -246,9 +242,11 @@ 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) {
|
||||
rcar_gen3_thermal_update_range(priv->tscs[i]);
|
||||
thermal_zone_device_update(priv->tscs[i]->zone,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -324,6 +322,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
|
||||
.compatible = "renesas,r8a7796-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1_m3_w,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r8a77961-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1_m3_w,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r8a77965-thermal",
|
||||
.data = &rcar_gen3_ths_tj_1,
|
||||
@ -446,14 +448,15 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
|
||||
goto error_unregister;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
}
|
||||
|
||||
ret = of_thermal_get_ntrips(tsc->zone);
|
||||
if (ret < 0)
|
||||
goto error_unregister;
|
||||
|
||||
rcar_gen3_thermal_update_range(tsc);
|
||||
|
||||
dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
|
||||
}
|
||||
|
||||
@ -492,7 +495,7 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
|
||||
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
|
||||
|
||||
priv->thermal_init(tsc);
|
||||
rcar_gen3_thermal_set_trips(tsc, tsc->low, tsc->high);
|
||||
rcar_gen3_thermal_update_range(tsc);
|
||||
}
|
||||
|
||||
rcar_thermal_irq_set(priv, true);
|
||||
|
@ -95,7 +95,6 @@ struct rcar_thermal_priv {
|
||||
struct mutex lock;
|
||||
struct list_head list;
|
||||
int id;
|
||||
u32 ctemp;
|
||||
};
|
||||
|
||||
#define rcar_thermal_for_each_priv(pos, common) \
|
||||
@ -201,7 +200,6 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
|
||||
struct device *dev = rcar_priv_to_dev(priv);
|
||||
int i;
|
||||
u32 ctemp, old, new;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
@ -247,37 +245,29 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
|
||||
((ctemp - 1) << 0)));
|
||||
}
|
||||
|
||||
dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp);
|
||||
|
||||
priv->ctemp = ctemp;
|
||||
ret = 0;
|
||||
err_out_unlock:
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
|
||||
return ctemp ? ctemp : -EINVAL;
|
||||
}
|
||||
|
||||
static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
|
||||
int *temp)
|
||||
{
|
||||
int tmp;
|
||||
int ret;
|
||||
int ctemp;
|
||||
|
||||
ret = rcar_thermal_update_temp(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if (priv->chip->ctemp_bands == 1)
|
||||
tmp = MCELSIUS((priv->ctemp * 5) - 65);
|
||||
else if (priv->ctemp < 24)
|
||||
tmp = MCELSIUS(((priv->ctemp * 55) - 720) / 10);
|
||||
else
|
||||
tmp = MCELSIUS((priv->ctemp * 5) - 60);
|
||||
mutex_unlock(&priv->lock);
|
||||
ctemp = rcar_thermal_update_temp(priv);
|
||||
if (ctemp < 0)
|
||||
return ctemp;
|
||||
|
||||
/* Guaranteed operating range is -45C to 125C. */
|
||||
|
||||
*temp = tmp;
|
||||
if (priv->chip->ctemp_bands == 1)
|
||||
*temp = MCELSIUS((ctemp * 5) - 65);
|
||||
else if (ctemp < 24)
|
||||
*temp = MCELSIUS(((ctemp * 55) - 720) / 10);
|
||||
else
|
||||
*temp = MCELSIUS((ctemp * 5) - 60);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -387,28 +377,17 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
|
||||
static void rcar_thermal_work(struct work_struct *work)
|
||||
{
|
||||
struct rcar_thermal_priv *priv;
|
||||
int cctemp, nctemp;
|
||||
int ret;
|
||||
|
||||
priv = container_of(work, struct rcar_thermal_priv, work.work);
|
||||
|
||||
ret = rcar_thermal_get_current_temp(priv, &cctemp);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = rcar_thermal_update_temp(priv);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
rcar_thermal_irq_enable(priv);
|
||||
|
||||
ret = rcar_thermal_get_current_temp(priv, &nctemp);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (nctemp != cctemp)
|
||||
thermal_zone_device_update(priv->zone,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
|
||||
@ -521,8 +500,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
mres++);
|
||||
common->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(common->base))
|
||||
return PTR_ERR(common->base);
|
||||
if (IS_ERR(common->base)) {
|
||||
ret = PTR_ERR(common->base);
|
||||
goto error_unregister;
|
||||
}
|
||||
|
||||
idle = 0; /* polling delay is not needed */
|
||||
}
|
||||
|
@ -1094,7 +1094,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
|
||||
&exynos_sensor_ops);
|
||||
if (IS_ERR(data->tzd)) {
|
||||
ret = PTR_ERR(data->tzd);
|
||||
dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to register sensor: %d\n",
|
||||
ret);
|
||||
goto err_sclk;
|
||||
}
|
||||
|
||||
|
552
drivers/thermal/sprd_thermal.c
Normal file
552
drivers/thermal/sprd_thermal.c
Normal file
@ -0,0 +1,552 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020 Spreadtrum Communications Inc.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define SPRD_THM_CTL 0x0
|
||||
#define SPRD_THM_INT_EN 0x4
|
||||
#define SPRD_THM_INT_STS 0x8
|
||||
#define SPRD_THM_INT_RAW_STS 0xc
|
||||
#define SPRD_THM_DET_PERIOD 0x10
|
||||
#define SPRD_THM_INT_CLR 0x14
|
||||
#define SPRD_THM_INT_CLR_ST 0x18
|
||||
#define SPRD_THM_MON_PERIOD 0x4c
|
||||
#define SPRD_THM_MON_CTL 0x50
|
||||
#define SPRD_THM_INTERNAL_STS1 0x54
|
||||
#define SPRD_THM_RAW_READ_MSK 0x3ff
|
||||
|
||||
#define SPRD_THM_OFFSET(id) ((id) * 0x4)
|
||||
#define SPRD_THM_TEMP(id) (SPRD_THM_OFFSET(id) + 0x5c)
|
||||
#define SPRD_THM_THRES(id) (SPRD_THM_OFFSET(id) + 0x2c)
|
||||
|
||||
#define SPRD_THM_SEN(id) BIT((id) + 2)
|
||||
#define SPRD_THM_SEN_OVERHEAT_EN(id) BIT((id) + 8)
|
||||
#define SPRD_THM_SEN_OVERHEAT_ALARM_EN(id) BIT((id) + 0)
|
||||
|
||||
/* bits definitions for register THM_CTL */
|
||||
#define SPRD_THM_SET_RDY_ST BIT(13)
|
||||
#define SPRD_THM_SET_RDY BIT(12)
|
||||
#define SPRD_THM_MON_EN BIT(1)
|
||||
#define SPRD_THM_EN BIT(0)
|
||||
|
||||
/* bits definitions for register THM_INT_CTL */
|
||||
#define SPRD_THM_BIT_INT_EN BIT(26)
|
||||
#define SPRD_THM_OVERHEAT_EN BIT(25)
|
||||
#define SPRD_THM_OTP_TRIP_SHIFT 10
|
||||
|
||||
/* bits definitions for register SPRD_THM_INTERNAL_STS1 */
|
||||
#define SPRD_THM_TEMPER_RDY BIT(0)
|
||||
|
||||
#define SPRD_THM_DET_PERIOD_DATA 0x800
|
||||
#define SPRD_THM_DET_PERIOD_MASK GENMASK(19, 0)
|
||||
#define SPRD_THM_MON_MODE 0x7
|
||||
#define SPRD_THM_MON_MODE_MASK GENMASK(3, 0)
|
||||
#define SPRD_THM_MON_PERIOD_DATA 0x10
|
||||
#define SPRD_THM_MON_PERIOD_MASK GENMASK(15, 0)
|
||||
#define SPRD_THM_THRES_MASK GENMASK(19, 0)
|
||||
#define SPRD_THM_INT_CLR_MASK GENMASK(24, 0)
|
||||
|
||||
/* thermal sensor calibration parameters */
|
||||
#define SPRD_THM_TEMP_LOW -40000
|
||||
#define SPRD_THM_TEMP_HIGH 120000
|
||||
#define SPRD_THM_OTP_TEMP 120000
|
||||
#define SPRD_THM_HOT_TEMP 75000
|
||||
#define SPRD_THM_RAW_DATA_LOW 0
|
||||
#define SPRD_THM_RAW_DATA_HIGH 1000
|
||||
#define SPRD_THM_SEN_NUM 8
|
||||
#define SPRD_THM_DT_OFFSET 24
|
||||
#define SPRD_THM_RATION_OFFSET 17
|
||||
#define SPRD_THM_RATION_SIGN 16
|
||||
|
||||
#define SPRD_THM_RDYST_POLLING_TIME 10
|
||||
#define SPRD_THM_RDYST_TIMEOUT 700
|
||||
#define SPRD_THM_TEMP_READY_POLL_TIME 10000
|
||||
#define SPRD_THM_TEMP_READY_TIMEOUT 600000
|
||||
#define SPRD_THM_MAX_SENSOR 8
|
||||
|
||||
struct sprd_thermal_sensor {
|
||||
struct thermal_zone_device *tzd;
|
||||
struct sprd_thermal_data *data;
|
||||
struct device *dev;
|
||||
int cal_slope;
|
||||
int cal_offset;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct sprd_thermal_data {
|
||||
const struct sprd_thm_variant_data *var_data;
|
||||
struct sprd_thermal_sensor *sensor[SPRD_THM_MAX_SENSOR];
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
u32 ratio_off;
|
||||
int ratio_sign;
|
||||
int nr_sensors;
|
||||
};
|
||||
|
||||
/*
|
||||
* The conversion between ADC and temperature is based on linear relationship,
|
||||
* and use idea_k to specify the slope and ideal_b to specify the offset.
|
||||
*
|
||||
* Since different Spreadtrum SoCs have different ideal_k and ideal_b,
|
||||
* we should save ideal_k and ideal_b in the device data structure.
|
||||
*/
|
||||
struct sprd_thm_variant_data {
|
||||
u32 ideal_k;
|
||||
u32 ideal_b;
|
||||
};
|
||||
|
||||
static const struct sprd_thm_variant_data ums512_data = {
|
||||
.ideal_k = 262,
|
||||
.ideal_b = 66400,
|
||||
};
|
||||
|
||||
static inline void sprd_thm_update_bits(void __iomem *reg, u32 mask, u32 val)
|
||||
{
|
||||
u32 tmp, orig;
|
||||
|
||||
orig = readl(reg);
|
||||
tmp = orig & ~mask;
|
||||
tmp |= val & mask;
|
||||
writel(tmp, reg);
|
||||
}
|
||||
|
||||
static int sprd_thm_cal_read(struct device_node *np, const char *cell_id,
|
||||
u32 *val)
|
||||
{
|
||||
struct nvmem_cell *cell;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
cell = of_nvmem_cell_get(np, cell_id);
|
||||
if (IS_ERR(cell))
|
||||
return PTR_ERR(cell);
|
||||
|
||||
buf = nvmem_cell_read(cell, &len);
|
||||
nvmem_cell_put(cell);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
if (len > sizeof(u32)) {
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(val, buf, len);
|
||||
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_thm_sensor_calibration(struct device_node *np,
|
||||
struct sprd_thermal_data *thm,
|
||||
struct sprd_thermal_sensor *sen)
|
||||
{
|
||||
int ret;
|
||||
/*
|
||||
* According to thermal datasheet, the default calibration offset is 64,
|
||||
* and the default ratio is 1000.
|
||||
*/
|
||||
int dt_offset = 64, ratio = 1000;
|
||||
|
||||
ret = sprd_thm_cal_read(np, "sen_delta_cal", &dt_offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ratio += thm->ratio_sign * thm->ratio_off;
|
||||
|
||||
/*
|
||||
* According to the ideal slope K and ideal offset B, combined with
|
||||
* calibration value of thermal from efuse, then calibrate the real
|
||||
* slope k and offset b:
|
||||
* k_cal = (k * ratio) / 1000.
|
||||
* b_cal = b + (dt_offset - 64) * 500.
|
||||
*/
|
||||
sen->cal_slope = (thm->var_data->ideal_k * ratio) / 1000;
|
||||
sen->cal_offset = thm->var_data->ideal_b + (dt_offset - 128) * 250;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_thm_rawdata_to_temp(struct sprd_thermal_sensor *sen,
|
||||
u32 rawdata)
|
||||
{
|
||||
clamp(rawdata, (u32)SPRD_THM_RAW_DATA_LOW, (u32)SPRD_THM_RAW_DATA_HIGH);
|
||||
|
||||
/*
|
||||
* According to the thermal datasheet, the formula of converting
|
||||
* adc value to the temperature value should be:
|
||||
* T_final = k_cal * x - b_cal.
|
||||
*/
|
||||
return sen->cal_slope * rawdata - sen->cal_offset;
|
||||
}
|
||||
|
||||
static int sprd_thm_temp_to_rawdata(int temp, struct sprd_thermal_sensor *sen)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
clamp(temp, (int)SPRD_THM_TEMP_LOW, (int)SPRD_THM_TEMP_HIGH);
|
||||
|
||||
/*
|
||||
* According to the thermal datasheet, the formula of converting
|
||||
* adc value to the temperature value should be:
|
||||
* T_final = k_cal * x - b_cal.
|
||||
*/
|
||||
val = (temp + sen->cal_offset) / sen->cal_slope;
|
||||
|
||||
return clamp(val, val, (u32)(SPRD_THM_RAW_DATA_HIGH - 1));
|
||||
}
|
||||
|
||||
static int sprd_thm_read_temp(void *devdata, int *temp)
|
||||
{
|
||||
struct sprd_thermal_sensor *sen = devdata;
|
||||
u32 data;
|
||||
|
||||
data = readl(sen->data->base + SPRD_THM_TEMP(sen->id)) &
|
||||
SPRD_THM_RAW_READ_MSK;
|
||||
|
||||
*temp = sprd_thm_rawdata_to_temp(sen, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops sprd_thm_ops = {
|
||||
.get_temp = sprd_thm_read_temp,
|
||||
};
|
||||
|
||||
static int sprd_thm_poll_ready_status(struct sprd_thermal_data *thm)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Wait for thermal ready status before configuring thermal parameters.
|
||||
*/
|
||||
ret = readl_poll_timeout(thm->base + SPRD_THM_CTL, val,
|
||||
!(val & SPRD_THM_SET_RDY_ST),
|
||||
SPRD_THM_RDYST_POLLING_TIME,
|
||||
SPRD_THM_RDYST_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_MON_EN,
|
||||
SPRD_THM_MON_EN);
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SET_RDY,
|
||||
SPRD_THM_SET_RDY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_thm_wait_temp_ready(struct sprd_thermal_data *thm)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Wait for first temperature data ready before reading temperature */
|
||||
return readl_poll_timeout(thm->base + SPRD_THM_INTERNAL_STS1, val,
|
||||
!(val & SPRD_THM_TEMPER_RDY),
|
||||
SPRD_THM_TEMP_READY_POLL_TIME,
|
||||
SPRD_THM_TEMP_READY_TIMEOUT);
|
||||
}
|
||||
|
||||
static int sprd_thm_set_ready(struct sprd_thermal_data *thm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sprd_thm_poll_ready_status(thm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Clear interrupt status, enable thermal interrupt and enable thermal.
|
||||
*
|
||||
* The SPRD thermal controller integrates a hardware interrupt signal,
|
||||
* which means if the temperature is overheat, it will generate an
|
||||
* interrupt and notify the event to PMIC automatically to shutdown the
|
||||
* system. So here we should enable the interrupt bits, though we have
|
||||
* not registered an irq handler.
|
||||
*/
|
||||
writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR);
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN,
|
||||
SPRD_THM_BIT_INT_EN, SPRD_THM_BIT_INT_EN);
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
|
||||
SPRD_THM_EN, SPRD_THM_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_thm_sensor_init(struct sprd_thermal_data *thm,
|
||||
struct sprd_thermal_sensor *sen)
|
||||
{
|
||||
u32 otp_rawdata, hot_rawdata;
|
||||
|
||||
otp_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_OTP_TEMP, sen);
|
||||
hot_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_HOT_TEMP, sen);
|
||||
|
||||
/* Enable the sensor' overheat temperature protection interrupt */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN,
|
||||
SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id),
|
||||
SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id));
|
||||
|
||||
/* Set the sensor' overheat and hot threshold temperature */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_THRES(sen->id),
|
||||
SPRD_THM_THRES_MASK,
|
||||
(otp_rawdata << SPRD_THM_OTP_TRIP_SHIFT) |
|
||||
hot_rawdata);
|
||||
|
||||
/* Enable the corresponding sensor */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SEN(sen->id),
|
||||
SPRD_THM_SEN(sen->id));
|
||||
}
|
||||
|
||||
static void sprd_thm_para_config(struct sprd_thermal_data *thm)
|
||||
{
|
||||
/* Set the period of two valid temperature detection action */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_DET_PERIOD,
|
||||
SPRD_THM_DET_PERIOD_MASK, SPRD_THM_DET_PERIOD);
|
||||
|
||||
/* Set the sensors' monitor mode */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_MON_CTL,
|
||||
SPRD_THM_MON_MODE_MASK, SPRD_THM_MON_MODE);
|
||||
|
||||
/* Set the sensors' monitor period */
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_MON_PERIOD,
|
||||
SPRD_THM_MON_PERIOD_MASK, SPRD_THM_MON_PERIOD);
|
||||
}
|
||||
|
||||
static void sprd_thm_toggle_sensor(struct sprd_thermal_sensor *sen, bool on)
|
||||
{
|
||||
struct thermal_zone_device *tzd = sen->tzd;
|
||||
|
||||
tzd->ops->set_mode(tzd,
|
||||
on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
|
||||
}
|
||||
|
||||
static int sprd_thm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *sen_child;
|
||||
struct sprd_thermal_data *thm;
|
||||
struct sprd_thermal_sensor *sen;
|
||||
const struct sprd_thm_variant_data *pdata;
|
||||
int ret, i;
|
||||
u32 val;
|
||||
|
||||
pdata = of_device_get_match_data(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No matching driver data found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
thm = devm_kzalloc(&pdev->dev, sizeof(*thm), GFP_KERNEL);
|
||||
if (!thm)
|
||||
return -ENOMEM;
|
||||
|
||||
thm->var_data = pdata;
|
||||
thm->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (!thm->base)
|
||||
return -ENOMEM;
|
||||
|
||||
thm->nr_sensors = of_get_child_count(np);
|
||||
if (thm->nr_sensors == 0 || thm->nr_sensors > SPRD_THM_MAX_SENSOR) {
|
||||
dev_err(&pdev->dev, "incorrect sensor count\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
thm->clk = devm_clk_get(&pdev->dev, "enable");
|
||||
if (IS_ERR(thm->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get enable clock\n");
|
||||
return PTR_ERR(thm->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(thm->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sprd_thm_para_config(thm);
|
||||
|
||||
ret = sprd_thm_cal_read(np, "thm_sign_cal", &val);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
if (val > 0)
|
||||
thm->ratio_sign = -1;
|
||||
else
|
||||
thm->ratio_sign = 1;
|
||||
|
||||
ret = sprd_thm_cal_read(np, "thm_ratio_cal", &thm->ratio_off);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
for_each_child_of_node(np, sen_child) {
|
||||
sen = devm_kzalloc(&pdev->dev, sizeof(*sen), GFP_KERNEL);
|
||||
if (!sen) {
|
||||
ret = -ENOMEM;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
sen->data = thm;
|
||||
sen->dev = &pdev->dev;
|
||||
|
||||
ret = of_property_read_u32(sen_child, "reg", &sen->id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "get sensor reg failed");
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
ret = sprd_thm_sensor_calibration(sen_child, thm, sen);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "efuse cal analysis failed");
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
sprd_thm_sensor_init(thm, sen);
|
||||
|
||||
sen->tzd = devm_thermal_zone_of_sensor_register(sen->dev,
|
||||
sen->id,
|
||||
sen,
|
||||
&sprd_thm_ops);
|
||||
if (IS_ERR(sen->tzd)) {
|
||||
dev_err(&pdev->dev, "register thermal zone failed %d\n",
|
||||
sen->id);
|
||||
ret = PTR_ERR(sen->tzd);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
thm->sensor[sen->id] = sen;
|
||||
}
|
||||
|
||||
ret = sprd_thm_set_ready(thm);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
ret = sprd_thm_wait_temp_ready(thm);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++)
|
||||
sprd_thm_toggle_sensor(thm->sensor[i], true);
|
||||
|
||||
platform_set_drvdata(pdev, thm);
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(thm->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void sprd_thm_hw_suspend(struct sprd_thermal_data *thm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++) {
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
|
||||
SPRD_THM_SEN(thm->sensor[i]->id), 0);
|
||||
}
|
||||
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
|
||||
SPRD_THM_EN, 0x0);
|
||||
}
|
||||
|
||||
static int sprd_thm_suspend(struct device *dev)
|
||||
{
|
||||
struct sprd_thermal_data *thm = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++)
|
||||
sprd_thm_toggle_sensor(thm->sensor[i], false);
|
||||
|
||||
sprd_thm_hw_suspend(thm);
|
||||
clk_disable_unprepare(thm->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_thm_hw_resume(struct sprd_thermal_data *thm)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++) {
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
|
||||
SPRD_THM_SEN(thm->sensor[i]->id),
|
||||
SPRD_THM_SEN(thm->sensor[i]->id));
|
||||
}
|
||||
|
||||
ret = sprd_thm_poll_ready_status(thm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR);
|
||||
sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
|
||||
SPRD_THM_EN, SPRD_THM_EN);
|
||||
return sprd_thm_wait_temp_ready(thm);
|
||||
}
|
||||
|
||||
static int sprd_thm_resume(struct device *dev)
|
||||
{
|
||||
struct sprd_thermal_data *thm = dev_get_drvdata(dev);
|
||||
int ret, i;
|
||||
|
||||
ret = clk_prepare_enable(thm->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprd_thm_hw_resume(thm);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++)
|
||||
sprd_thm_toggle_sensor(thm->sensor[i], true);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
clk_disable_unprepare(thm->clk);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sprd_thm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sprd_thermal_data *thm = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < thm->nr_sensors; i++) {
|
||||
sprd_thm_toggle_sensor(thm->sensor[i], false);
|
||||
devm_thermal_zone_of_sensor_unregister(&pdev->dev,
|
||||
thm->sensor[i]->tzd);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(thm->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_thermal_of_match[] = {
|
||||
{ .compatible = "sprd,ums512-thermal", .data = &ums512_data },
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops sprd_thermal_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sprd_thm_suspend, sprd_thm_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver sprd_thermal_driver = {
|
||||
.probe = sprd_thm_probe,
|
||||
.remove = sprd_thm_remove,
|
||||
.driver = {
|
||||
.name = "sprd-thermal",
|
||||
.pm = &sprd_thermal_pm_ops,
|
||||
.of_match_table = sprd_thermal_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sprd_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("Freeman Liu <freeman.liu@unisoc.com>");
|
||||
MODULE_DESCRIPTION("Spreadtrum thermal driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -478,7 +478,8 @@ static int stm_thermal_resume(struct device *dev)
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume);
|
||||
static SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops,
|
||||
stm_thermal_suspend, stm_thermal_resume);
|
||||
|
||||
static const struct thermal_zone_of_device_ops stm_tz_ops = {
|
||||
.get_temp = stm_thermal_get_temp,
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
@ -24,7 +24,6 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "ti-bandgap.h"
|
||||
@ -743,27 +742,13 @@ exit:
|
||||
static int ti_bandgap_tshut_init(struct ti_bandgap *bgp,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int gpio_nr = bgp->tshut_gpio;
|
||||
int status;
|
||||
|
||||
/* Request for gpio_86 line */
|
||||
status = gpio_request(gpio_nr, "tshut");
|
||||
if (status < 0) {
|
||||
dev_err(bgp->dev, "Could not request for TSHUT GPIO:%i\n", 86);
|
||||
return status;
|
||||
}
|
||||
status = gpio_direction_input(gpio_nr);
|
||||
if (status) {
|
||||
dev_err(bgp->dev, "Cannot set input TSHUT GPIO %d\n", gpio_nr);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = request_irq(gpio_to_irq(gpio_nr), ti_bandgap_tshut_irq_handler,
|
||||
status = request_irq(gpiod_to_irq(bgp->tshut_gpiod),
|
||||
ti_bandgap_tshut_irq_handler,
|
||||
IRQF_TRIGGER_RISING, "tshut", NULL);
|
||||
if (status) {
|
||||
gpio_free(gpio_nr);
|
||||
if (status)
|
||||
dev_err(bgp->dev, "request irq failed for TSHUT");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -860,11 +845,10 @@ static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev)
|
||||
} while (res);
|
||||
|
||||
if (TI_BANDGAP_HAS(bgp, TSHUT)) {
|
||||
bgp->tshut_gpio = of_get_gpio(node, 0);
|
||||
if (!gpio_is_valid(bgp->tshut_gpio)) {
|
||||
dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
|
||||
bgp->tshut_gpio);
|
||||
return ERR_PTR(-EINVAL);
|
||||
bgp->tshut_gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
|
||||
if (IS_ERR(bgp->tshut_gpiod)) {
|
||||
dev_err(&pdev->dev, "invalid gpio for tshut\n");
|
||||
return ERR_CAST(bgp->tshut_gpiod);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,10 +1030,8 @@ put_clks:
|
||||
put_fclock:
|
||||
clk_put(bgp->fclock);
|
||||
free_irqs:
|
||||
if (TI_BANDGAP_HAS(bgp, TSHUT)) {
|
||||
free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
|
||||
gpio_free(bgp->tshut_gpio);
|
||||
}
|
||||
if (TI_BANDGAP_HAS(bgp, TSHUT))
|
||||
free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1079,10 +1061,8 @@ int ti_bandgap_remove(struct platform_device *pdev)
|
||||
if (TI_BANDGAP_HAS(bgp, TALERT))
|
||||
free_irq(bgp->irq, bgp);
|
||||
|
||||
if (TI_BANDGAP_HAS(bgp, TSHUT)) {
|
||||
free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
|
||||
gpio_free(bgp->tshut_gpio);
|
||||
}
|
||||
if (TI_BANDGAP_HAS(bgp, TSHUT))
|
||||
free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
/**
|
||||
* DOC: bandgap driver data structure
|
||||
* ==================================
|
||||
@ -199,7 +201,7 @@ struct ti_bandgap {
|
||||
struct clk *div_clk;
|
||||
spinlock_t lock; /* shields this struct */
|
||||
int irq;
|
||||
int tshut_gpio;
|
||||
struct gpio_desc *tshut_gpiod;
|
||||
u32 clk_rate;
|
||||
};
|
||||
|
||||
|
@ -75,7 +75,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *dfc);
|
||||
|
||||
#else /* !CONFIG_DEVFREQ_THERMAL */
|
||||
|
||||
struct thermal_cooling_device *
|
||||
static inline struct thermal_cooling_device *
|
||||
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
|
||||
struct devfreq_cooling_power *dfc_power)
|
||||
{
|
||||
|
@ -364,6 +364,9 @@ struct thermal_trip {
|
||||
|
||||
/* Function declarations */
|
||||
#ifdef CONFIG_THERMAL_OF
|
||||
int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
|
||||
struct device_node *sensor_np,
|
||||
u32 *id);
|
||||
struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
|
||||
const struct thermal_zone_of_device_ops *ops);
|
||||
@ -375,6 +378,13 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
|
||||
void devm_thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
struct thermal_zone_device *tz);
|
||||
#else
|
||||
|
||||
static inline int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
|
||||
struct device_node *sensor_np,
|
||||
u32 *id)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
static inline struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
|
||||
const struct thermal_zone_of_device_ops *ops)
|
||||
|
@ -46,7 +46,7 @@ static void start_daemon_mode(void);
|
||||
|
||||
pthread_t event_tid;
|
||||
pthread_mutex_t input_lock;
|
||||
void usage()
|
||||
void usage(void)
|
||||
{
|
||||
printf("Usage: tmon [OPTION...]\n");
|
||||
printf(" -c, --control cooling device in control\n");
|
||||
@ -62,7 +62,7 @@ void usage()
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void version()
|
||||
void version(void)
|
||||
{
|
||||
printf("TMON version %s\n", VERSION);
|
||||
exit(EXIT_SUCCESS);
|
||||
@ -70,7 +70,6 @@ void version()
|
||||
|
||||
static void tmon_cleanup(void)
|
||||
{
|
||||
|
||||
syslog(LOG_INFO, "TMON exit cleanup\n");
|
||||
fflush(stdout);
|
||||
refresh();
|
||||
@ -96,7 +95,6 @@ static void tmon_cleanup(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
static void tmon_sig_handler(int sig)
|
||||
{
|
||||
syslog(LOG_INFO, "TMON caught signal %d\n", sig);
|
||||
@ -120,7 +118,6 @@ static void tmon_sig_handler(int sig)
|
||||
tmon_exit = true;
|
||||
}
|
||||
|
||||
|
||||
static void start_syslog(void)
|
||||
{
|
||||
if (debug_on)
|
||||
@ -167,7 +164,6 @@ static void prepare_logging(void)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n");
|
||||
for (i = 0; i < ptdata.nr_tz_sensor; i++) {
|
||||
char binding_str[33]; /* size of long + 1 */
|
||||
@ -175,7 +171,7 @@ static void prepare_logging(void)
|
||||
|
||||
memset(binding_str, 0, sizeof(binding_str));
|
||||
for (j = 0; j < 32; j++)
|
||||
binding_str[j] = (ptdata.tzi[i].cdev_binding & 1<<j) ?
|
||||
binding_str[j] = (ptdata.tzi[i].cdev_binding & (1 << j)) ?
|
||||
'1' : '0';
|
||||
|
||||
fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n",
|
||||
@ -187,7 +183,6 @@ static void prepare_logging(void)
|
||||
trip_type_name[ptdata.tzi[i].tp[j].type],
|
||||
ptdata.tzi[i].tp[j].temp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < ptdata.nr_cooling_dev; i++)
|
||||
@ -219,7 +214,6 @@ static struct option opts[] = {
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int err = 0;
|
||||
@ -283,7 +277,7 @@ int main(int argc, char **argv)
|
||||
if (signal(SIGINT, tmon_sig_handler) == SIG_ERR)
|
||||
syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
|
||||
if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR)
|
||||
syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
|
||||
syslog(LOG_DEBUG, "Cannot handle SIGTERM\n");
|
||||
|
||||
if (probe_thermal_sysfs()) {
|
||||
pthread_mutex_destroy(&input_lock);
|
||||
@ -328,8 +322,7 @@ int main(int argc, char **argv)
|
||||
show_cooling_device();
|
||||
}
|
||||
time_elapsed += ticktime;
|
||||
controller_handler(trec[0].temp[target_tz_index] / 1000,
|
||||
&yk);
|
||||
controller_handler(trec[0].temp[target_tz_index] / 1000, &yk);
|
||||
trec[0].pid_out_pct = yk;
|
||||
if (!dialogue_on)
|
||||
show_control_w();
|
||||
@ -340,14 +333,15 @@ int main(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void start_daemon_mode()
|
||||
static void start_daemon_mode(void)
|
||||
{
|
||||
daemon_mode = 1;
|
||||
/* fork */
|
||||
pid_t sid, pid = fork();
|
||||
if (pid < 0) {
|
||||
|
||||
if (pid < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (pid > 0)
|
||||
else if (pid > 0)
|
||||
/* kill parent */
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
@ -366,11 +360,9 @@ static void start_daemon_mode()
|
||||
if ((chdir("/")) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
|
||||
sleep(10);
|
||||
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user