Commit 34183ddd authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 8645f09b 11700fcb
...@@ -166,6 +166,17 @@ Required properties: ...@@ -166,6 +166,17 @@ Required properties:
followed by "fsl,imx-sc-key"; followed by "fsl,imx-sc-key";
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml - 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): Example (imx8qxp):
------------- -------------
aliases { aliases {
...@@ -238,6 +249,11 @@ firmware { ...@@ -238,6 +249,11 @@ firmware {
compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt"; compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt";
timeout-sec = <60>; timeout-sec = <60>;
}; };
tsens: thermal-sensor {
compatible = "fsl,imx8qxp-sc-thermal", "fsl,imx-sc-thermal";
#thermal-sensor-cells = <1>;
};
}; };
}; };
......
* 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: ...@@ -38,11 +38,11 @@ properties:
- enum: - enum:
- qcom,msm8996-tsens - qcom,msm8996-tsens
- qcom,msm8998-tsens - qcom,msm8998-tsens
- qcom,sc7180-tsens
- qcom,sdm845-tsens - qcom,sdm845-tsens
- const: qcom,tsens-v2 - const: qcom,tsens-v2
reg: reg:
maxItems: 2
items: items:
- description: TM registers - description: TM registers
- description: SROT registers - description: SROT registers
......
...@@ -11,6 +11,7 @@ Required properties: ...@@ -11,6 +11,7 @@ Required properties:
- "renesas,r8a774b1-thermal" (RZ/G2N) - "renesas,r8a774b1-thermal" (RZ/G2N)
- "renesas,r8a7795-thermal" (R-Car H3) - "renesas,r8a7795-thermal" (R-Car H3)
- "renesas,r8a7796-thermal" (R-Car M3-W) - "renesas,r8a7796-thermal" (R-Car M3-W)
- "renesas,r8a77961-thermal" (R-Car M3-W+)
- "renesas,r8a77965-thermal" (R-Car M3-N) - "renesas,r8a77965-thermal" (R-Car M3-N)
- "renesas,r8a77980-thermal" (R-Car V3H) - "renesas,r8a77980-thermal" (R-Car V3H)
- reg : Address ranges of the thermal registers. Each sensor - reg : Address ranges of the thermal registers. Each sensor
......
# 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: ...@@ -142,11 +142,11 @@ Required properties:
- trips: A sub-node which is a container of only trip point nodes - trips: A sub-node which is a container of only trip point nodes
Type: sub-node required to describe the thermal zone. Type: sub-node required to describe the thermal zone.
Optional property:
- cooling-maps: A sub-node which is a container of only cooling device - 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 Type: sub-node map nodes, used to describe the relation between trips
and cooling devices. and cooling devices.
Optional property:
- coefficients: An array of integers (one signed cell) containing - coefficients: An array of integers (one signed cell) containing
Type: array coefficients to compose a linear relation between Type: array coefficients to compose a linear relation between
Elem size: one cell the sensors listed in the thermal-sensors property. Elem size: one cell the sensors listed in the thermal-sensors property.
......
...@@ -105,7 +105,7 @@ and this variation will modulate the cooling effect. ...@@ -105,7 +105,7 @@ and this variation will modulate the cooling effect.
idle <--------------> idle <-------------->
running running
<-----------------------------> <--------------------->
duty cycle 33% duty cycle 33%
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# #
# Generic thermal sysfs drivers configuration # Generic thermal drivers configuration
# #
menuconfig THERMAL menuconfig THERMAL
bool "Generic Thermal sysfs driver" bool "Thermal drivers"
help 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 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, Each thermal zone contains its own temperature, trip points,
cooling devices. and cooling devices.
All platforms with ACPI thermal support can use this driver. All platforms with ACPI or Open Firmware thermal support can use
this driver.
If you want this support, you should say Y here. If you want this support, you should say Y here.
if THERMAL if THERMAL
...@@ -251,6 +252,27 @@ config IMX_THERMAL ...@@ -251,6 +252,27 @@ config IMX_THERMAL
cpufreq is used as the cooling device to throttle CPUs when the cpufreq is used as the cooling device to throttle CPUs when the
passive trip is crossed. 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 config MAX77620_THERMAL
tristate "Temperature sensor driver for Maxim MAX77620 PMIC" tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
depends on MFD_MAX77620 depends on MFD_MAX77620
...@@ -265,6 +287,7 @@ config QORIQ_THERMAL ...@@ -265,6 +287,7 @@ config QORIQ_THERMAL
tristate "QorIQ Thermal Monitoring Unit" tristate "QorIQ Thermal Monitoring Unit"
depends on THERMAL_OF depends on THERMAL_OF
depends on HAS_IOMEM depends on HAS_IOMEM
select REGMAP_MMIO
help help
Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms. Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
It supports one critical trip point and one passive trip point. The It supports one critical trip point and one passive trip point. The
...@@ -460,4 +483,11 @@ config UNIPHIER_THERMAL ...@@ -460,4 +483,11 @@ config UNIPHIER_THERMAL
Enable this to plug in UniPhier on-chip PVT thermal driver into the Enable this to plug in UniPhier on-chip PVT thermal driver into the
thermal framework. The driver supports CPU thermal zone temperature thermal framework. The driver supports CPU thermal zone temperature
reporting and a couple of trip points. 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 endif
...@@ -43,6 +43,8 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o ...@@ -43,6 +43,8 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
obj-$(CONFIG_IMX_THERMAL) += imx_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_MAX77620_THERMAL) += max77620_thermal.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
...@@ -57,3 +59,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o ...@@ -57,3 +59,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_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, ...@@ -273,7 +273,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */ /* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level)) if (state > cpufreq_cdev->max_level)
return -EINVAL; return -EINVAL;
num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
...@@ -437,7 +437,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, ...@@ -437,7 +437,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
int ret; int ret;
/* Request state should be less than max_level */ /* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_cdev->max_level)) if (state > cpufreq_cdev->max_level)
return -EINVAL; return -EINVAL;
/* Check if the old cooling action is same as new cooling action */ /* 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, ...@@ -456,6 +456,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
capacity = frequency * max_capacity; capacity = frequency * max_capacity;
capacity /= cpufreq_cdev->policy->cpuinfo.max_freq; capacity /= cpufreq_cdev->policy->cpuinfo.max_freq;
arch_set_thermal_pressure(cpus, max_capacity - capacity); arch_set_thermal_pressure(cpus, max_capacity - capacity);
ret = 0;
} }
return ret; return ret;
......
// 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");
// 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 @@ ...@@ -3,24 +3,17 @@
// Copyright 2013 Freescale Semiconductor, Inc. // Copyright 2013 Freescale Semiconductor, Inc.
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/types.h>
#include <linux/nvmem-consumer.h> #include <linux/nvmem-consumer.h>
#define REG_SET 0x4 #define REG_SET 0x4
...@@ -872,14 +865,12 @@ static int imx_thermal_remove(struct platform_device *pdev) ...@@ -872,14 +865,12 @@ static int imx_thermal_remove(struct platform_device *pdev)
clk_disable_unprepare(data->thermal_clk); clk_disable_unprepare(data->thermal_clk);
thermal_zone_device_unregister(data->tz); thermal_zone_device_unregister(data->tz);
cpufreq_cooling_unregister(data->cdev); imx_thermal_unregister_legacy_cooling(data);
cpufreq_cpu_put(data->policy);
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP static int __maybe_unused imx_thermal_suspend(struct device *dev)
static int imx_thermal_suspend(struct device *dev)
{ {
struct imx_thermal_data *data = dev_get_drvdata(dev); struct imx_thermal_data *data = dev_get_drvdata(dev);
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
...@@ -900,7 +891,7 @@ static int imx_thermal_suspend(struct device *dev) ...@@ -900,7 +891,7 @@ static int imx_thermal_suspend(struct device *dev)
return 0; 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 imx_thermal_data *data = dev_get_drvdata(dev);
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
...@@ -918,7 +909,6 @@ static int imx_thermal_resume(struct device *dev) ...@@ -918,7 +909,6 @@ static int imx_thermal_resume(struct device *dev)
return 0; return 0;
} }
#endif
static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
imx_thermal_suspend, imx_thermal_resume); imx_thermal_suspend, imx_thermal_resume);
......
...@@ -65,7 +65,7 @@ static ssize_t available_uuids_show(struct device *dev, ...@@ -65,7 +65,7 @@ static ssize_t available_uuids_show(struct device *dev,
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i)) if (priv->uuid_bitmap & (1 << i))
if (PAGE_SIZE - length > 0) if (PAGE_SIZE - length > 0)
length += snprintf(&buf[length], length += scnprintf(&buf[length],
PAGE_SIZE - length, PAGE_SIZE - length,
"%s\n", "%s\n",
int3400_thermal_uuids[i]); int3400_thermal_uuids[i]);
......
...@@ -45,6 +45,9 @@ ...@@ -45,6 +45,9 @@
/* JasperLake thermal reporting device */ /* JasperLake thermal reporting device */
#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503 #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" #define DRV_NAME "proc_thermal"
struct power_config { struct power_config {
...@@ -728,6 +731,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { ...@@ -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), { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL),
.driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, .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_JSL_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_TGL_THERMAL),
.driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
{ 0, }, { 0, },
}; };
......
...@@ -448,6 +448,50 @@ thermal_zone_of_add_sensor(struct device_node *zone, ...@@ -448,6 +448,50 @@ thermal_zone_of_add_sensor(struct device_node *zone,
return tzd; 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 * 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 * @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, ...@@ -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); sensor_np = of_node_get(dev->of_node);
for_each_available_child_of_node(np, child) { for_each_available_child_of_node(np, child) {
struct of_phandle_args sensor_specs;
int ret, id; int ret, id;
/* For now, thermal framework supports only 1 sensor per zone */ /* For now, thermal framework supports only 1 sensor per zone */
ret = of_parse_phandle_with_args(child, "thermal-sensors", ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id);
"#thermal-sensor-cells",
0, &sensor_specs);
if (ret) if (ret)
continue; continue;
if (sensor_specs.args_count >= 1) { if (id == sensor_id) {
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) {
tzd = thermal_zone_of_add_sensor(child, sensor_np, tzd = thermal_zone_of_add_sensor(child, sensor_np,
data, ops); data, ops);
if (!IS_ERR(tzd)) if (!IS_ERR(tzd))
tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED); tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
of_node_put(sensor_specs.np);
of_node_put(child); of_node_put(child);
goto exit; goto exit;
} }
of_node_put(sensor_specs.np);
} }
exit: exit:
of_node_put(sensor_np); of_node_put(sensor_np);
......
...@@ -245,7 +245,7 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) ...@@ -245,7 +245,7 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
return adc_code * slope + offset; 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; int ret;
u32 code, trdy; u32 code, trdy;
...@@ -279,7 +279,7 @@ static const struct tsens_ops ops_8960 = { ...@@ -279,7 +279,7 @@ static const struct tsens_ops ops_8960 = {
.resume = resume_8960, .resume = resume_8960,
}; };
const struct tsens_plat_data data_8960 = { struct tsens_plat_data data_8960 = {
.num_sensors = 11, .num_sensors = 11,
.ops = &ops_8960, .ops = &ops_8960,
}; };
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
* @low_thresh: lower threshold temperature value * @low_thresh: lower threshold temperature value
* @low_irq_mask: mask register for lower threshold irqs * @low_irq_mask: mask register for lower threshold irqs
* @low_irq_clear: clear 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 * Structure containing data about temperature threshold settings and
* irq status if they were violated. * irq status if they were violated.
...@@ -36,6 +40,10 @@ struct tsens_irq_data { ...@@ -36,6 +40,10 @@ struct tsens_irq_data {
int low_thresh; int low_thresh;
u32 low_irq_mask; u32 low_irq_mask;
u32 low_irq_clear; 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) 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) ...@@ -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 * Return: Temperature in milliCelsius on success, a negative errno will
* be returned in error cases * 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; struct tsens_priv *priv = s->priv;
u32 resolution; u32 resolution;
...@@ -160,7 +168,7 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field) ...@@ -160,7 +168,7 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
* *
* Return: ADC code or temperature in deciCelsius. * 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; struct tsens_priv *priv = s->priv;
...@@ -189,6 +197,9 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, ...@@ -189,6 +197,9 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
case LOWER: case LOWER:
index = LOW_INT_CLEAR_0 + hw_id; index = LOW_INT_CLEAR_0 + hw_id;
break; break;
case CRITICAL:
/* No critical interrupts before v2 */
return;
} }
regmap_field_write(priv->rf[index], enable ? 0 : 1); 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, ...@@ -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_mask = LOW_INT_MASK_0 + hw_id;
index_clear = LOW_INT_CLEAR_0 + hw_id; index_clear = LOW_INT_CLEAR_0 + hw_id;
break; break;
case CRITICAL:
index_mask = CRIT_INT_MASK_0 + hw_id;
index_clear = CRIT_INT_CLEAR_0 + hw_id;
break;
} }
if (enable) { if (enable) {
...@@ -268,14 +283,23 @@ static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, ...@@ -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); ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
if (ret) if (ret)
return 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 1;
return 0; return 0;
} }
static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, 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; int ret;
...@@ -292,22 +316,37 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, ...@@ -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); ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
if (ret) if (ret)
return 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 { } else {
/* No mask register on older TSENS */ /* No mask register on older TSENS */
d->up_irq_mask = 0; d->up_irq_mask = 0;
d->low_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->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); 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", 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) ? "(V)" : "", hw_id, __func__,
d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear, (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
d->low_irq_mask, d->up_irq_mask); d->low_viol, d->up_viol, d->crit_viol,
dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__, d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
(d->up_viol || d->low_viol) ? "(violation)" : "", d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
d->low_thresh, d->up_thresh); 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; return 0;
} }
...@@ -321,6 +360,76 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) ...@@ -321,6 +360,76 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
return 0; 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 * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
* @irq: irq number * @irq: irq number
...@@ -346,10 +455,10 @@ irqreturn_t tsens_irq_thread(int irq, void *data) ...@@ -346,10 +455,10 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
for (i = 0; i < priv->num_sensors; i++) { for (i = 0; i < priv->num_sensors; i++) {
bool trigger = false; bool trigger = false;
struct tsens_sensor *s = &priv->sensor[i]; const struct tsens_sensor *s = &priv->sensor[i];
u32 hw_id = s->hw_id; u32 hw_id = s->hw_id;
if (IS_ERR(priv->sensor[i].tzd)) if (IS_ERR(s->tzd))
continue; continue;
if (!tsens_threshold_violated(priv, hw_id, &d)) if (!tsens_threshold_violated(priv, hw_id, &d))
continue; continue;
...@@ -368,7 +477,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) ...@@ -368,7 +477,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
tsens_set_interrupt(priv, hw_id, UPPER, disable); tsens_set_interrupt(priv, hw_id, UPPER, disable);
if (d.up_thresh > temp) { if (d.up_thresh > temp) {
dev_dbg(priv->dev, "[%u] %s: re-arm upper\n", 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); tsens_set_interrupt(priv, hw_id, UPPER, enable);
} else { } else {
trigger = true; trigger = true;
...@@ -379,7 +488,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) ...@@ -379,7 +488,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
tsens_set_interrupt(priv, hw_id, LOWER, disable); tsens_set_interrupt(priv, hw_id, LOWER, disable);
if (d.low_thresh < temp) { if (d.low_thresh < temp) {
dev_dbg(priv->dev, "[%u] %s: re-arm low\n", 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); tsens_set_interrupt(priv, hw_id, LOWER, enable);
} else { } else {
trigger = true; trigger = true;
...@@ -392,7 +501,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data) ...@@ -392,7 +501,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
if (trigger) { if (trigger) {
dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n", dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
hw_id, __func__, temp); hw_id, __func__, temp);
thermal_zone_device_update(priv->sensor[i].tzd, thermal_zone_device_update(s->tzd,
THERMAL_EVENT_UNSPECIFIED); THERMAL_EVENT_UNSPECIFIED);
} else { } else {
dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
...@@ -435,7 +544,7 @@ int tsens_set_trips(void *_sensor, int low, int high) ...@@ -435,7 +544,7 @@ int tsens_set_trips(void *_sensor, int low, int high)
spin_unlock_irqrestore(&priv->ul_lock, flags); spin_unlock_irqrestore(&priv->ul_lock, flags);
dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", 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; return 0;
} }
...@@ -457,7 +566,7 @@ void tsens_disable_irq(struct tsens_priv *priv) ...@@ -457,7 +566,7 @@ void tsens_disable_irq(struct tsens_priv *priv)
regmap_field_write(priv->rf[INT_EN], 0); 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; struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id; int hw_id = s->hw_id;
...@@ -486,7 +595,7 @@ int get_temp_tsens_valid(struct tsens_sensor *s, int *temp) ...@@ -486,7 +595,7 @@ int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
return 0; 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; struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id; int hw_id = s->hw_id;
...@@ -590,6 +699,7 @@ int __init init_common(struct tsens_priv *priv) ...@@ -590,6 +699,7 @@ int __init init_common(struct tsens_priv *priv)
{ {
void __iomem *tm_base, *srot_base; void __iomem *tm_base, *srot_base;
struct device *dev = priv->dev; struct device *dev = priv->dev;
u32 ver_minor;
struct resource *res; struct resource *res;
u32 enabled; u32 enabled;
int ret, i, j; int ret, i, j;
...@@ -602,7 +712,7 @@ int __init init_common(struct tsens_priv *priv) ...@@ -602,7 +712,7 @@ int __init init_common(struct tsens_priv *priv)
/* DT with separate SROT and TM address space */ /* DT with separate SROT and TM address space */
priv->tm_offset = 0; priv->tm_offset = 0;
res = platform_get_resource(op, IORESOURCE_MEM, 1); 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)) { if (IS_ERR(srot_base)) {
ret = PTR_ERR(srot_base); ret = PTR_ERR(srot_base);
goto err_put_device; goto err_put_device;
...@@ -620,7 +730,7 @@ int __init init_common(struct tsens_priv *priv) ...@@ -620,7 +730,7 @@ int __init init_common(struct tsens_priv *priv)
} }
res = platform_get_resource(op, IORESOURCE_MEM, 0); 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)) { if (IS_ERR(tm_base)) {
ret = PTR_ERR(tm_base); ret = PTR_ERR(tm_base);
goto err_put_device; goto err_put_device;
...@@ -639,6 +749,9 @@ int __init init_common(struct tsens_priv *priv) ...@@ -639,6 +749,9 @@ int __init init_common(struct tsens_priv *priv)
if (IS_ERR(priv->rf[i])) if (IS_ERR(priv->rf[i]))
return PTR_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, priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
...@@ -683,12 +796,47 @@ int __init init_common(struct tsens_priv *priv) ...@@ -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); spin_lock_init(&priv->ul_lock);
tsens_enable_irq(priv); tsens_enable_irq(priv);
tsens_debug_init(op); tsens_debug_init(op);
return 0;
err_put_device: err_put_device:
put_device(&op->dev); put_device(&op->dev);
return ret; return ret;
......
...@@ -327,7 +327,7 @@ static int calibrate_8974(struct tsens_priv *priv) ...@@ -327,7 +327,7 @@ static int calibrate_8974(struct tsens_priv *priv)
/* v0.1: 8916, 8974 */ /* 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, .ver_major = VER_0_1,
.crit_int = 0, .crit_int = 0,
.adc = 1, .adc = 1,
...@@ -377,7 +377,7 @@ static const struct tsens_ops ops_8916 = { ...@@ -377,7 +377,7 @@ static const struct tsens_ops ops_8916 = {
.get_temp = get_temp_common, .get_temp = get_temp_common,
}; };
const struct tsens_plat_data data_8916 = { struct tsens_plat_data data_8916 = {
.num_sensors = 5, .num_sensors = 5,
.ops = &ops_8916, .ops = &ops_8916,
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, .hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
...@@ -392,7 +392,7 @@ static const struct tsens_ops ops_8974 = { ...@@ -392,7 +392,7 @@ static const struct tsens_ops ops_8974 = {
.get_temp = get_temp_common, .get_temp = get_temp_common,
}; };
const struct tsens_plat_data data_8974 = { struct tsens_plat_data data_8974 = {
.num_sensors = 11, .num_sensors = 11,
.ops = &ops_8974, .ops = &ops_8974,
.feat = &tsens_v0_1_feat, .feat = &tsens_v0_1_feat,
......
...@@ -299,7 +299,7 @@ static int calibrate_8976(struct tsens_priv *priv) ...@@ -299,7 +299,7 @@ static int calibrate_8976(struct tsens_priv *priv)
/* v1.x: msm8956,8976,qcs404,405 */ /* 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, .ver_major = VER_1_X,
.crit_int = 0, .crit_int = 0,
.adc = 1, .adc = 1,
...@@ -368,7 +368,7 @@ static const struct tsens_ops ops_generic_v1 = { ...@@ -368,7 +368,7 @@ static const struct tsens_ops ops_generic_v1 = {
.get_temp = get_temp_tsens_valid, .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, .ops = &ops_generic_v1,
.feat = &tsens_v1_feat, .feat = &tsens_v1_feat,
.fields = tsens_v1_regfields, .fields = tsens_v1_regfields,
...@@ -381,7 +381,7 @@ static const struct tsens_ops ops_8976 = { ...@@ -381,7 +381,7 @@ static const struct tsens_ops ops_8976 = {
}; };
/* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */ /* 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, .num_sensors = 11,
.ops = &ops_8976, .ops = &ops_8976,
.hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10}, .hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10},
......
...@@ -24,10 +24,11 @@ ...@@ -24,10 +24,11 @@
#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060 #define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
#define TM_Sn_STATUS_OFF 0x00a0 #define TM_Sn_STATUS_OFF 0x00a0
#define TM_TRDY_OFF 0x00e4 #define TM_TRDY_OFF 0x00e4
#define TM_WDOG_LOG_OFF 0x013c
/* v2.x: 8996, 8998, sdm845 */ /* 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, .ver_major = VER_2_X,
.crit_int = 1, .crit_int = 1,
.adc = 0, .adc = 0,
...@@ -53,6 +54,7 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { ...@@ -53,6 +54,7 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
/* TEMPERATURE THRESHOLDS */ /* TEMPERATURE THRESHOLDS */
REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 0, 11), 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(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] */ /* INTERRUPTS [CLEAR/STATUS/MASK] */
REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS, TM_UPPER_LOWER_INT_STATUS_OFF), 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] = { ...@@ -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_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_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_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 */ /* Sn_STATUS */
REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11), 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 = { ...@@ -81,14 +95,14 @@ static const struct tsens_ops ops_generic_v2 = {
.get_temp = get_temp_tsens_valid, .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, .ops = &ops_generic_v2,
.feat = &tsens_v2_feat, .feat = &tsens_v2_feat,
.fields = tsens_v2_regfields, .fields = tsens_v2_regfields,
}; };
/* Kept around for backward compatibility with old msm8996.dtsi */ /* 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, .num_sensors = 13,
.ops = &ops_generic_v2, .ops = &ops_generic_v2,
.feat = &tsens_v2_feat, .feat = &tsens_v2_feat,
......
...@@ -85,11 +85,42 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = { ...@@ -85,11 +85,42 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = {
.set_trips = tsens_set_trips, .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) static int tsens_register(struct tsens_priv *priv)
{ {
int i, ret, irq; int i, ret;
struct thermal_zone_device *tzd; struct thermal_zone_device *tzd;
struct platform_device *pdev;
for (i = 0; i < priv->num_sensors; i++) { for (i = 0; i < priv->num_sensors; i++) {
priv->sensor[i].priv = priv; priv->sensor[i].priv = priv;
...@@ -103,32 +134,14 @@ static int tsens_register(struct tsens_priv *priv) ...@@ -103,32 +134,14 @@ static int tsens_register(struct tsens_priv *priv)
priv->ops->enable(priv, i); priv->ops->enable(priv, i);
} }
pdev = of_find_device_by_node(priv->dev->of_node); ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
if (!pdev) if (ret < 0)
return -ENODEV; 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;
}
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); if (priv->feat->crit_int)
ret = tsens_register_irq(priv, "critical",
tsens_critical_irq_thread);
err_put_device:
put_device(&pdev->dev);
return ret; return ret;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
struct tsens_priv; struct tsens_priv;
/* IP version numbers in ascending order */
enum tsens_ver { enum tsens_ver {
VER_0_1 = 0, VER_0_1 = 0,
VER_1_X, VER_1_X,
...@@ -32,6 +33,7 @@ enum tsens_ver { ...@@ -32,6 +33,7 @@ enum tsens_ver {
enum tsens_irq_type { enum tsens_irq_type {
LOWER, LOWER,
UPPER, UPPER,
CRITICAL,
}; };
/** /**
...@@ -67,7 +69,7 @@ struct tsens_ops { ...@@ -67,7 +69,7 @@ struct tsens_ops {
/* mandatory callbacks */ /* mandatory callbacks */
int (*init)(struct tsens_priv *priv); int (*init)(struct tsens_priv *priv);
int (*calibrate)(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 */ /* optional callbacks */
int (*enable)(struct tsens_priv *priv, int i); int (*enable)(struct tsens_priv *priv, int i);
void (*disable)(struct tsens_priv *priv); void (*disable)(struct tsens_priv *priv);
...@@ -374,6 +376,82 @@ enum regfield_ids { ...@@ -374,6 +376,82 @@ enum regfield_ids {
CRITICAL_STATUS_13, CRITICAL_STATUS_13,
CRITICAL_STATUS_14, CRITICAL_STATUS_14,
CRITICAL_STATUS_15, 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_0, /* MIN threshold violated */
MIN_STATUS_1, MIN_STATUS_1,
MIN_STATUS_2, MIN_STATUS_2,
...@@ -418,6 +496,7 @@ enum regfield_ids { ...@@ -418,6 +496,7 @@ enum regfield_ids {
* @adc: do the sensors only output adc code (instead of temperature)? * @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, * @srot_split: does the IP neatly splits the register space into SROT and TM,
* with SROT only being available to secure boot firmware? * 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 * @max_sensors: maximum sensors supported by this version of the IP
*/ */
struct tsens_features { struct tsens_features {
...@@ -425,6 +504,7 @@ struct tsens_features { ...@@ -425,6 +504,7 @@ struct tsens_features {
unsigned int crit_int:1; unsigned int crit_int:1;
unsigned int adc:1; unsigned int adc:1;
unsigned int srot_split:1; unsigned int srot_split:1;
unsigned int has_watchdog:1;
unsigned int max_sensors; unsigned int max_sensors;
}; };
...@@ -440,12 +520,14 @@ struct tsens_plat_data { ...@@ -440,12 +520,14 @@ struct tsens_plat_data {
const u32 num_sensors; const u32 num_sensors;
const struct tsens_ops *ops; const struct tsens_ops *ops;
unsigned int *hw_ids; unsigned int *hw_ids;
const struct tsens_features *feat; struct tsens_features *feat;
const struct reg_field *fields; const struct reg_field *fields;
}; };
/** /**
* struct tsens_context - Registers to be saved/restored across a context loss * struct tsens_context - Registers to be saved/restored across a context loss
* @threshold: Threshold register value
* @control: Control register value
*/ */
struct tsens_context { struct tsens_context {
int threshold; int threshold;
...@@ -460,6 +542,8 @@ struct tsens_context { ...@@ -460,6 +542,8 @@ struct tsens_context {
* @srot_map: pointer to SROT register address space * @srot_map: pointer to SROT register address space
* @tm_offset: deal with old device trees that don't address TM and SROT * @tm_offset: deal with old device trees that don't address TM and SROT
* address space separately * 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 * @rf: array of regmap_fields used to store value of the field
* @ctx: registers to be saved and restored during suspend/resume * @ctx: registers to be saved and restored during suspend/resume
* @feat: features of the IP * @feat: features of the IP
...@@ -481,36 +565,37 @@ struct tsens_priv { ...@@ -481,36 +565,37 @@ struct tsens_priv {
struct regmap_field *rf[MAX_REGFIELDS]; struct regmap_field *rf[MAX_REGFIELDS];
struct tsens_context ctx; struct tsens_context ctx;
const struct tsens_features *feat; struct tsens_features *feat;
const struct reg_field *fields; const struct reg_field *fields;
const struct tsens_ops *ops; const struct tsens_ops *ops;
struct dentry *debug_root; struct dentry *debug_root;
struct dentry *debug; struct dentry *debug;
struct tsens_sensor sensor[0]; struct tsens_sensor sensor[];
}; };
char *qfprom_read(struct device *dev, const char *cname); char *qfprom_read(struct device *dev, const char *cname);
void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode); void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
int init_common(struct tsens_priv *priv); int init_common(struct tsens_priv *priv);
int get_temp_tsens_valid(struct tsens_sensor *s, int *temp); int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
int get_temp_common(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); int tsens_enable_irq(struct tsens_priv *priv);
void tsens_disable_irq(struct tsens_priv *priv); void tsens_disable_irq(struct tsens_priv *priv);
int tsens_set_trips(void *_sensor, int low, int high); int tsens_set_trips(void *_sensor, int low, int high);
irqreturn_t tsens_irq_thread(int irq, void *data); irqreturn_t tsens_irq_thread(int irq, void *data);
irqreturn_t tsens_critical_irq_thread(int irq, void *data);
/* TSENS target */ /* TSENS target */
extern const struct tsens_plat_data data_8960; extern struct tsens_plat_data data_8960;
/* TSENS v0.1 targets */ /* 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 */ /* 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 */ /* 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__ */ #endif /* __QCOM_TSENS_H__ */
...@@ -3,12 +3,11 @@ ...@@ -3,12 +3,11 @@
// Copyright 2016 Freescale Semiconductor, Inc. // Copyright 2016 Freescale Semiconductor, Inc.
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/thermal.h> #include <linux/thermal.h>
...@@ -228,6 +227,14 @@ static const struct regmap_access_table qoriq_rd_table = { ...@@ -228,6 +227,14 @@ static const struct regmap_access_table qoriq_rd_table = {
.n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges), .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) static int qoriq_tmu_probe(struct platform_device *pdev)
{ {
int ret; int ret;
...@@ -278,6 +285,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev) ...@@ -278,6 +285,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
return ret; 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 */ /* version register offset at: 0xbf8 on both v1 and v2 */
ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver); ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);
if (ret) { if (ret) {
...@@ -290,34 +301,16 @@ static int qoriq_tmu_probe(struct platform_device *pdev) ...@@ -290,34 +301,16 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */
if (ret < 0) if (ret < 0)
goto err; return ret;
ret = qoriq_tmu_register_tmu_zone(dev, data); ret = qoriq_tmu_register_tmu_zone(dev, data);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to register sensors\n"); dev_err(dev, "Failed to register sensors\n");
ret = -ENODEV; return ret;
goto err;
} }
platform_set_drvdata(pdev, data); 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; return 0;
} }
...@@ -365,7 +358,6 @@ static struct platform_driver qoriq_tmu = { ...@@ -365,7 +358,6 @@ static struct platform_driver qoriq_tmu = {
.of_match_table = qoriq_tmu_match, .of_match_table = qoriq_tmu_match,
}, },
.probe = qoriq_tmu_probe, .probe = qoriq_tmu_probe,
.remove = qoriq_tmu_remove,
}; };
module_platform_driver(qoriq_tmu); module_platform_driver(qoriq_tmu);
......
...@@ -81,8 +81,6 @@ struct rcar_gen3_thermal_tsc { ...@@ -81,8 +81,6 @@ struct rcar_gen3_thermal_tsc {
void __iomem *base; void __iomem *base;
struct thermal_zone_device *zone; struct thermal_zone_device *zone;
struct equation_coefs coef; struct equation_coefs coef;
int low;
int high;
int tj_t; int tj_t;
int id; /* thermal channel id */ int id; /* thermal channel id */
}; };
...@@ -204,12 +202,14 @@ static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, ...@@ -204,12 +202,14 @@ static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
return INT_FIXPT(val); 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;
rcar_gen3_thermal_get_temp(tsc, &temperature);
low = clamp_val(low, -40000, 120000); low = temperature - MCELSIUS(1);
high = clamp_val(high, -40000, 120000); high = temperature + MCELSIUS(1);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); 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) ...@@ -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_write(tsc, REG_GEN3_IRQTEMP2,
rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
tsc->low = low;
tsc->high = high;
return 0; return 0;
} }
static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
.get_temp = rcar_gen3_thermal_get_temp, .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) static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on)
...@@ -246,10 +242,12 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) ...@@ -246,10 +242,12 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
for (i = 0; i < priv->num_tscs; i++) { for (i = 0; i < priv->num_tscs; i++) {
status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); 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_zone_device_update(priv->tscs[i]->zone,
THERMAL_EVENT_UNSPECIFIED); THERMAL_EVENT_UNSPECIFIED);
} }
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -324,6 +322,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { ...@@ -324,6 +322,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
.compatible = "renesas,r8a7796-thermal", .compatible = "renesas,r8a7796-thermal",
.data = &rcar_gen3_ths_tj_1_m3_w, .data = &rcar_gen3_ths_tj_1_m3_w,
}, },
{
.compatible = "renesas,r8a77961-thermal",
.data = &rcar_gen3_ths_tj_1_m3_w,
},
{ {
.compatible = "renesas,r8a77965-thermal", .compatible = "renesas,r8a77965-thermal",
.data = &rcar_gen3_ths_tj_1, .data = &rcar_gen3_ths_tj_1,
...@@ -446,14 +448,15 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) ...@@ -446,14 +448,15 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
goto error_unregister; goto error_unregister;
ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone); ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);
if (ret) { if (ret)
goto error_unregister; goto error_unregister;
}
ret = of_thermal_get_ntrips(tsc->zone); ret = of_thermal_get_ntrips(tsc->zone);
if (ret < 0) if (ret < 0)
goto error_unregister; goto error_unregister;
rcar_gen3_thermal_update_range(tsc);
dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret); 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) ...@@ -492,7 +495,7 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
priv->thermal_init(tsc); 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); rcar_thermal_irq_set(priv, true);
......
...@@ -95,7 +95,6 @@ struct rcar_thermal_priv { ...@@ -95,7 +95,6 @@ struct rcar_thermal_priv {
struct mutex lock; struct mutex lock;
struct list_head list; struct list_head list;
int id; int id;
u32 ctemp;
}; };
#define rcar_thermal_for_each_priv(pos, common) \ #define rcar_thermal_for_each_priv(pos, common) \
...@@ -201,7 +200,6 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) ...@@ -201,7 +200,6 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
struct device *dev = rcar_priv_to_dev(priv); struct device *dev = rcar_priv_to_dev(priv);
int i; int i;
u32 ctemp, old, new; u32 ctemp, old, new;
int ret = -EINVAL;
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
...@@ -247,37 +245,29 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) ...@@ -247,37 +245,29 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
((ctemp - 1) << 0))); ((ctemp - 1) << 0)));
} }
dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp);
priv->ctemp = ctemp;
ret = 0;
err_out_unlock: err_out_unlock:
mutex_unlock(&priv->lock); mutex_unlock(&priv->lock);
return ret;
return ctemp ? ctemp : -EINVAL;
} }
static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
int *temp) int *temp)
{ {
int tmp; int ctemp;
int ret;
ret = rcar_thermal_update_temp(priv); ctemp = rcar_thermal_update_temp(priv);
if (ret < 0) if (ctemp < 0)
return ret; return ctemp;
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);
/* Guaranteed operating range is -45C to 125C. */ /* 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; return 0;
} }
...@@ -387,28 +377,17 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) ...@@ -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) static void rcar_thermal_work(struct work_struct *work)
{ {
struct rcar_thermal_priv *priv; struct rcar_thermal_priv *priv;
int cctemp, nctemp;
int ret; int ret;
priv = container_of(work, struct rcar_thermal_priv, work.work); 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); ret = rcar_thermal_update_temp(priv);
if (ret < 0) if (ret < 0)
return; return;
rcar_thermal_irq_enable(priv); rcar_thermal_irq_enable(priv);
ret = rcar_thermal_get_current_temp(priv, &nctemp); thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED);
if (ret < 0)
return;
if (nctemp != cctemp)
thermal_zone_device_update(priv->zone,
THERMAL_EVENT_UNSPECIFIED);
} }
static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) 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) ...@@ -521,8 +500,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, res = platform_get_resource(pdev, IORESOURCE_MEM,
mres++); mres++);
common->base = devm_ioremap_resource(dev, res); common->base = devm_ioremap_resource(dev, res);
if (IS_ERR(common->base)) if (IS_ERR(common->base)) {
return PTR_ERR(common->base); ret = PTR_ERR(common->base);
goto error_unregister;
}
idle = 0; /* polling delay is not needed */ idle = 0; /* polling delay is not needed */
} }
......
...@@ -1094,7 +1094,9 @@ static int exynos_tmu_probe(struct platform_device *pdev) ...@@ -1094,7 +1094,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
&exynos_sensor_ops); &exynos_sensor_ops);
if (IS_ERR(data->tzd)) { if (IS_ERR(data->tzd)) {
ret = PTR_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; goto err_sclk;
} }
......
// 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) ...@@ -478,7 +478,8 @@ static int stm_thermal_resume(struct device *dev)
} }
#endif /* CONFIG_PM_SLEEP */ #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 = { static const struct thermal_zone_of_device_ops stm_tz_ops = {
.get_temp = stm_thermal_get_temp, .get_temp = stm_thermal_get_temp,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/io.h> #include <linux/io.h>
#include "ti-bandgap.h" #include "ti-bandgap.h"
...@@ -743,27 +742,13 @@ int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend) ...@@ -743,27 +742,13 @@ int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend)
static int ti_bandgap_tshut_init(struct ti_bandgap *bgp, static int ti_bandgap_tshut_init(struct ti_bandgap *bgp,
struct platform_device *pdev) struct platform_device *pdev)
{ {
int gpio_nr = bgp->tshut_gpio;
int status; int status;
/* Request for gpio_86 line */ status = request_irq(gpiod_to_irq(bgp->tshut_gpiod),
status = gpio_request(gpio_nr, "tshut"); ti_bandgap_tshut_irq_handler,
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,
IRQF_TRIGGER_RISING, "tshut", NULL); IRQF_TRIGGER_RISING, "tshut", NULL);
if (status) { if (status)
gpio_free(gpio_nr);
dev_err(bgp->dev, "request irq failed for TSHUT"); dev_err(bgp->dev, "request irq failed for TSHUT");
}
return 0; return 0;
} }
...@@ -860,11 +845,10 @@ static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev) ...@@ -860,11 +845,10 @@ static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev)
} while (res); } while (res);
if (TI_BANDGAP_HAS(bgp, TSHUT)) { if (TI_BANDGAP_HAS(bgp, TSHUT)) {
bgp->tshut_gpio = of_get_gpio(node, 0); bgp->tshut_gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
if (!gpio_is_valid(bgp->tshut_gpio)) { if (IS_ERR(bgp->tshut_gpiod)) {
dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n", dev_err(&pdev->dev, "invalid gpio for tshut\n");
bgp->tshut_gpio); return ERR_CAST(bgp->tshut_gpiod);
return ERR_PTR(-EINVAL);
} }
} }
...@@ -1046,10 +1030,8 @@ int ti_bandgap_probe(struct platform_device *pdev) ...@@ -1046,10 +1030,8 @@ int ti_bandgap_probe(struct platform_device *pdev)
put_fclock: put_fclock:
clk_put(bgp->fclock); clk_put(bgp->fclock);
free_irqs: free_irqs:
if (TI_BANDGAP_HAS(bgp, TSHUT)) { if (TI_BANDGAP_HAS(bgp, TSHUT))
free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
gpio_free(bgp->tshut_gpio);
}
return ret; return ret;
} }
...@@ -1079,10 +1061,8 @@ int ti_bandgap_remove(struct platform_device *pdev) ...@@ -1079,10 +1061,8 @@ int ti_bandgap_remove(struct platform_device *pdev)
if (TI_BANDGAP_HAS(bgp, TALERT)) if (TI_BANDGAP_HAS(bgp, TALERT))
free_irq(bgp->irq, bgp); free_irq(bgp->irq, bgp);
if (TI_BANDGAP_HAS(bgp, TSHUT)) { if (TI_BANDGAP_HAS(bgp, TSHUT))
free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
gpio_free(bgp->tshut_gpio);
}
return 0; return 0;
} }
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/err.h> #include <linux/err.h>
struct gpio_desc;
/** /**
* DOC: bandgap driver data structure * DOC: bandgap driver data structure
* ================================== * ==================================
...@@ -199,7 +201,7 @@ struct ti_bandgap { ...@@ -199,7 +201,7 @@ struct ti_bandgap {
struct clk *div_clk; struct clk *div_clk;
spinlock_t lock; /* shields this struct */ spinlock_t lock; /* shields this struct */
int irq; int irq;
int tshut_gpio; struct gpio_desc *tshut_gpiod;
u32 clk_rate; u32 clk_rate;
}; };
......
...@@ -75,7 +75,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *dfc); ...@@ -75,7 +75,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *dfc);
#else /* !CONFIG_DEVFREQ_THERMAL */ #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, of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power) struct devfreq_cooling_power *dfc_power)
{ {
......
...@@ -364,6 +364,9 @@ struct thermal_trip { ...@@ -364,6 +364,9 @@ struct thermal_trip {
/* Function declarations */ /* Function declarations */
#ifdef CONFIG_THERMAL_OF #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 * struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data, thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
const struct thermal_zone_of_device_ops *ops); const struct thermal_zone_of_device_ops *ops);
...@@ -375,6 +378,13 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register( ...@@ -375,6 +378,13 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
void devm_thermal_zone_of_sensor_unregister(struct device *dev, void devm_thermal_zone_of_sensor_unregister(struct device *dev,
struct thermal_zone_device *tz); struct thermal_zone_device *tz);
#else #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 * static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data, thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
const struct thermal_zone_of_device_ops *ops) const struct thermal_zone_of_device_ops *ops)
......
...@@ -46,7 +46,7 @@ static void start_daemon_mode(void); ...@@ -46,7 +46,7 @@ static void start_daemon_mode(void);
pthread_t event_tid; pthread_t event_tid;
pthread_mutex_t input_lock; pthread_mutex_t input_lock;
void usage() void usage(void)
{ {
printf("Usage: tmon [OPTION...]\n"); printf("Usage: tmon [OPTION...]\n");
printf(" -c, --control cooling device in control\n"); printf(" -c, --control cooling device in control\n");
...@@ -62,7 +62,7 @@ void usage() ...@@ -62,7 +62,7 @@ void usage()
exit(0); exit(0);
} }
void version() void version(void)
{ {
printf("TMON version %s\n", VERSION); printf("TMON version %s\n", VERSION);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
...@@ -70,7 +70,6 @@ void version() ...@@ -70,7 +70,6 @@ void version()
static void tmon_cleanup(void) static void tmon_cleanup(void)
{ {
syslog(LOG_INFO, "TMON exit cleanup\n"); syslog(LOG_INFO, "TMON exit cleanup\n");
fflush(stdout); fflush(stdout);
refresh(); refresh();
...@@ -96,7 +95,6 @@ static void tmon_cleanup(void) ...@@ -96,7 +95,6 @@ static void tmon_cleanup(void)
exit(1); exit(1);
} }
static void tmon_sig_handler(int sig) static void tmon_sig_handler(int sig)
{ {
syslog(LOG_INFO, "TMON caught signal %d\n", sig); syslog(LOG_INFO, "TMON caught signal %d\n", sig);
...@@ -120,7 +118,6 @@ static void tmon_sig_handler(int sig) ...@@ -120,7 +118,6 @@ static void tmon_sig_handler(int sig)
tmon_exit = true; tmon_exit = true;
} }
static void start_syslog(void) static void start_syslog(void)
{ {
if (debug_on) if (debug_on)
...@@ -167,7 +164,6 @@ static void prepare_logging(void) ...@@ -167,7 +164,6 @@ static void prepare_logging(void)
return; return;
} }
fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n"); fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n");
for (i = 0; i < ptdata.nr_tz_sensor; i++) { for (i = 0; i < ptdata.nr_tz_sensor; i++) {
char binding_str[33]; /* size of long + 1 */ char binding_str[33]; /* size of long + 1 */
...@@ -175,7 +171,7 @@ static void prepare_logging(void) ...@@ -175,7 +171,7 @@ static void prepare_logging(void)
memset(binding_str, 0, sizeof(binding_str)); memset(binding_str, 0, sizeof(binding_str));
for (j = 0; j < 32; j++) 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'; '1' : '0';
fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n", fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n",
...@@ -187,7 +183,6 @@ static void prepare_logging(void) ...@@ -187,7 +183,6 @@ static void prepare_logging(void)
trip_type_name[ptdata.tzi[i].tp[j].type], trip_type_name[ptdata.tzi[i].tp[j].type],
ptdata.tzi[i].tp[j].temp); ptdata.tzi[i].tp[j].temp);
} }
} }
for (i = 0; i < ptdata.nr_cooling_dev; i++) for (i = 0; i < ptdata.nr_cooling_dev; i++)
...@@ -219,7 +214,6 @@ static struct option opts[] = { ...@@ -219,7 +214,6 @@ static struct option opts[] = {
{ 0, 0, NULL, 0 } { 0, 0, NULL, 0 }
}; };
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int err = 0; int err = 0;
...@@ -283,7 +277,7 @@ int main(int argc, char **argv) ...@@ -283,7 +277,7 @@ int main(int argc, char **argv)
if (signal(SIGINT, tmon_sig_handler) == SIG_ERR) if (signal(SIGINT, tmon_sig_handler) == SIG_ERR)
syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR) 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()) { if (probe_thermal_sysfs()) {
pthread_mutex_destroy(&input_lock); pthread_mutex_destroy(&input_lock);
...@@ -328,8 +322,7 @@ int main(int argc, char **argv) ...@@ -328,8 +322,7 @@ int main(int argc, char **argv)
show_cooling_device(); show_cooling_device();
} }
time_elapsed += ticktime; time_elapsed += ticktime;
controller_handler(trec[0].temp[target_tz_index] / 1000, controller_handler(trec[0].temp[target_tz_index] / 1000, &yk);
&yk);
trec[0].pid_out_pct = yk; trec[0].pid_out_pct = yk;
if (!dialogue_on) if (!dialogue_on)
show_control_w(); show_control_w();
...@@ -340,14 +333,15 @@ int main(int argc, char **argv) ...@@ -340,14 +333,15 @@ int main(int argc, char **argv)
return 0; return 0;
} }
static void start_daemon_mode() static void start_daemon_mode(void)
{ {
daemon_mode = 1; daemon_mode = 1;
/* fork */ /* fork */
pid_t sid, pid = fork(); pid_t sid, pid = fork();
if (pid < 0) {
if (pid < 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else if (pid > 0) else if (pid > 0)
/* kill parent */ /* kill parent */
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
...@@ -366,11 +360,9 @@ static void start_daemon_mode() ...@@ -366,11 +360,9 @@ static void start_daemon_mode()
if ((chdir("/")) < 0) if ((chdir("/")) < 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
sleep(10); sleep(10);
close(STDIN_FILENO); close(STDIN_FILENO);
close(STDOUT_FILENO); close(STDOUT_FILENO);
close(STDERR_FILENO); close(STDERR_FILENO);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment