Merge tag 'thermal-v5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Apr 2020 03:00:16 +0000 (20:00 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Apr 2020 03:00:16 +0000 (20:00 -0700)
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
  ...

34 files changed:
Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt
Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
Documentation/devicetree/bindings/thermal/sprd-thermal.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal.txt
Documentation/driver-api/thermal/cpu-idle-cooling.rst
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/cpufreq_cooling.c
drivers/thermal/imx8mm_thermal.c [new file with mode: 0644]
drivers/thermal/imx_sc_thermal.c [new file with mode: 0644]
drivers/thermal/imx_thermal.c
drivers/thermal/intel/int340x_thermal/int3400_thermal.c
drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
drivers/thermal/of-thermal.c
drivers/thermal/qcom/tsens-8960.c
drivers/thermal/qcom/tsens-common.c
drivers/thermal/qcom/tsens-v0_1.c
drivers/thermal/qcom/tsens-v1.c
drivers/thermal/qcom/tsens-v2.c
drivers/thermal/qcom/tsens.c
drivers/thermal/qcom/tsens.h
drivers/thermal/qoriq_thermal.c
drivers/thermal/rcar_gen3_thermal.c
drivers/thermal/rcar_thermal.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/sprd_thermal.c [new file with mode: 0644]
drivers/thermal/st/stm_thermal.c
drivers/thermal/ti-soc-thermal/ti-bandgap.c
drivers/thermal/ti-soc-thermal/ti-bandgap.h
include/linux/devfreq_cooling.h
include/linux/thermal.h
tools/thermal/tmon/tmon.c

index 8cac6fa..623fedf 100644 (file)
@@ -166,6 +166,17 @@ Required properties:
               followed by "fsl,imx-sc-key";
 - linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
 
+Thermal bindings based on SCU Message Protocol
+------------------------------------------------------------
+
+Required properties:
+- compatible:                  Should be :
+                                 "fsl,imx8qxp-sc-thermal"
+                               followed by "fsl,imx-sc-thermal";
+
+- #thermal-sensor-cells:       See Documentation/devicetree/bindings/thermal/thermal.txt
+                               for a description.
+
 Example (imx8qxp):
 -------------
 aliases {
@@ -238,6 +249,11 @@ firmware {
                        compatible = "fsl,imx8qxp-sc-wdt", "fsl,imx-sc-wdt";
                        timeout-sec = <60>;
                };
+
+               tsens: thermal-sensor {
+                       compatible = "fsl,imx8qxp-sc-thermal", "fsl,imx-sc-thermal";
+                       #thermal-sensor-cells = <1>;
+               };
        };
 };
 
diff --git a/Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt b/Documentation/devicetree/bindings/thermal/imx8mm-thermal.txt
new file mode 100644 (file)
index 0000000..3629d3c
--- /dev/null
@@ -0,0 +1,15 @@
+* Thermal Monitoring Unit (TMU) on Freescale i.MX8MM SoC
+
+Required properties:
+- compatible : Must be "fsl,imx8mm-tmu" or "fsl,imx8mp-tmu".
+- reg : Address range of TMU registers.
+- clocks : TMU's clock source.
+- #thermal-sensor-cells : Should be 0 or 1. See ./thermal.txt for a description.
+
+Example:
+tmu: tmu@30260000 {
+       compatible = "fsl,imx8mm-tmu";
+       reg = <0x30260000 0x10000>;
+       clocks = <&clk IMX8MM_CLK_TMU_ROOT>;
+       #thermal-sensor-cells = <0>;
+};
index a57b76a..2ddd39d 100644 (file)
@@ -38,11 +38,11 @@ properties:
           - enum:
               - qcom,msm8996-tsens
               - qcom,msm8998-tsens
+              - qcom,sc7180-tsens
               - qcom,sdm845-tsens
           - const: qcom,tsens-v2
 
   reg:
-    maxItems: 2
     items:
       - description: TM registers
       - description: SROT registers
index 12c740b..2993fa7 100644 (file)
@@ -11,6 +11,7 @@ Required properties:
                            - "renesas,r8a774b1-thermal" (RZ/G2N)
                            - "renesas,r8a7795-thermal" (R-Car H3)
                            - "renesas,r8a7796-thermal" (R-Car M3-W)
+                           - "renesas,r8a77961-thermal" (R-Car M3-W+)
                            - "renesas,r8a77965-thermal" (R-Car M3-N)
                            - "renesas,r8a77980-thermal" (R-Car V3H)
 - reg                  : Address ranges of the thermal registers. Each sensor
diff --git a/Documentation/devicetree/bindings/thermal/sprd-thermal.yaml b/Documentation/devicetree/bindings/thermal/sprd-thermal.yaml
new file mode 100644 (file)
index 0000000..058c4cc
--- /dev/null
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/sprd-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Spreadtrum thermal sensor controller bindings
+
+maintainers:
+  - Orson Zhai <orsonzhai@gmail.com>
+  - Baolin Wang <baolin.wang7@gmail.com>
+  - Chunyan Zhang <zhang.lyra@gmail.com>
+
+properties:
+  compatible:
+    const: sprd,ums512-thermal
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: enable
+
+  nvmem-cells:
+    maxItems: 2
+    description:
+      Reference to nvmem nodes for the calibration data.
+
+  nvmem-cell-names:
+    items:
+      - const: thm_sign_cal
+      - const: thm_ratio_cal
+
+  "#thermal-sensor-cells":
+    const: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^([a-z]*-)?sensor(-section)?@[0-9]+$":
+    type: object
+    description:
+      Represent one thermal sensor.
+
+    properties:
+      reg:
+        description: Specify the sensor id.
+        maxItems: 1
+
+      nvmem-cells:
+        maxItems: 1
+        description:
+          Reference to an nvmem node for the calibration data.
+
+      nvmem-cell-names:
+        const: sen_delta_cal
+
+    required:
+      - reg
+      - nvmem-cells
+      - nvmem-cell-names
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - nvmem-cells
+  - nvmem-cell-names
+  - "#thermal-sensor-cells"
+  - "#address-cells"
+  - "#size-cells"
+
+examples:
+  - |
+        ap_thm0: thermal@32200000 {
+                compatible = "sprd,ums512-thermal";
+                reg = <0 0x32200000 0 0x10000>;
+                clock-names = "enable";
+                clocks = <&aonapb_gate 32>;
+                #thermal-sensor-cells = <1>;
+                nvmem-cells = <&thm0_sign>, <&thm0_ratio>;
+                nvmem-cell-names = "thm_sign_cal", "thm_ratio_cal";
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                prometheus-sensor@0 {
+                        reg = <0>;
+                        nvmem-cells = <&thm0_sen0>;
+                        nvmem-cell-names = "sen_delta_cal";
+                };
+
+                ank-sensor@1 {
+                        reg = <1>;
+                        nvmem-cells = <&thm0_sen1>;
+                        nvmem-cell-names = "sen_delta_cal";
+                };
+        };
+...
index ca14ba9..f78bec1 100644 (file)
@@ -142,11 +142,11 @@ Required properties:
 - trips:               A sub-node which is a container of only trip point nodes
   Type: sub-node       required to describe the thermal zone.
 
+Optional property:
 - cooling-maps:                A sub-node which is a container of only cooling device
   Type: sub-node       map nodes, used to describe the relation between trips
                        and cooling devices.
 
-Optional property:
 - coefficients:                An array of integers (one signed cell) containing
   Type: array          coefficients to compose a linear relation between
   Elem size: one cell  the sensors listed in the thermal-sensors property.
index 9f0016e..a1c3ede 100644 (file)
@@ -105,8 +105,8 @@ and this variation will modulate the cooling effect.
        idle  <-------------->
                 running
 
-      <----------------------------->
-              duty cycle 33%
+      <--------------------->
+          duty cycle 33%
 
 
      ^
index 5a05db5..91af271 100644 (file)
@@ -1,17 +1,18 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
-# Generic thermal sysfs drivers configuration
+# Generic thermal drivers configuration
 #
 
 menuconfig THERMAL
-       bool "Generic Thermal sysfs driver"
+       bool "Thermal drivers"
        help
-         Generic Thermal Sysfs driver offers a generic mechanism for
+         Thermal drivers offer a generic mechanism for
          thermal management. Usually it's made up of one or more thermal
-         zone and cooling device.
+         zones and cooling devices.
          Each thermal zone contains its own temperature, trip points,
-         cooling devices.
-         All platforms with ACPI thermal support can use this driver.
+         and cooling devices.
+         All platforms with ACPI or Open Firmware thermal support can use
+         this driver.
          If you want this support, you should say Y here.
 
 if THERMAL
@@ -251,6 +252,27 @@ config IMX_THERMAL
          cpufreq is used as the cooling device to throttle CPUs when the
          passive trip is crossed.
 
+config IMX_SC_THERMAL
+       tristate "Temperature sensor driver for NXP i.MX SoCs with System Controller"
+       depends on IMX_SCU
+       depends on OF
+       help
+         Support for Temperature Monitor (TEMPMON) found on NXP i.MX SoCs with
+         system controller inside, Linux kernel has to communicate with system
+         controller via MU (message unit) IPC to get temperature from thermal
+         sensor. It supports one critical trip point and one
+         passive trip point for each thermal sensor.
+
+config IMX8MM_THERMAL
+       tristate "Temperature sensor driver for Freescale i.MX8MM SoC"
+       depends on ARCH_MXC || COMPILE_TEST
+       depends on OF
+       help
+         Support for Thermal Monitoring Unit (TMU) found on Freescale i.MX8MM SoC.
+         It supports one critical trip point and one passive trip point. The
+         cpufreq is used as the cooling device to throttle CPUs when the passive
+         trip is crossed.
+
 config MAX77620_THERMAL
        tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
        depends on MFD_MAX77620
@@ -265,6 +287,7 @@ config QORIQ_THERMAL
        tristate "QorIQ Thermal Monitoring Unit"
        depends on THERMAL_OF
        depends on HAS_IOMEM
+       select REGMAP_MMIO
        help
          Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
          It supports one critical trip point and one passive trip point. The
@@ -460,4 +483,11 @@ config UNIPHIER_THERMAL
          Enable this to plug in UniPhier on-chip PVT thermal driver into the
          thermal framework. The driver supports CPU thermal zone temperature
          reporting and a couple of trip points.
+
+config SPRD_THERMAL
+       tristate "Temperature sensor on Spreadtrum SoCs"
+       depends on ARCH_SPRD || COMPILE_TEST
+       help
+         Support for the Spreadtrum thermal sensor driver in the Linux thermal
+         framework.
 endif
index 9fb88e2..8c8ed7b 100644 (file)
@@ -43,6 +43,8 @@ obj-$(CONFIG_DB8500_THERMAL)  += db8500_thermal.o
 obj-$(CONFIG_ARMADA_THERMAL)   += armada_thermal.o
 obj-$(CONFIG_TANGO_THERMAL)    += tango_thermal.o
 obj-$(CONFIG_IMX_THERMAL)      += imx_thermal.o
+obj-$(CONFIG_IMX_SC_THERMAL)   += imx_sc_thermal.o
+obj-$(CONFIG_IMX8MM_THERMAL)   += imx8mm_thermal.o
 obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
 obj-$(CONFIG_QORIQ_THERMAL)    += qoriq_thermal.o
 obj-$(CONFIG_DA9062_THERMAL)   += da9062-thermal.o
@@ -57,3 +59,4 @@ obj-$(CONFIG_GENERIC_ADC_THERMAL)     += thermal-generic-adc.o
 obj-$(CONFIG_ZX2967_THERMAL)   += zx2967_thermal.o
 obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
 obj-$(CONFIG_AMLOGIC_THERMAL)     += amlogic_thermal.o
+obj-$(CONFIG_SPRD_THERMAL)     += sprd_thermal.o
index 4ae8c85..e297e13 100644 (file)
@@ -273,7 +273,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev,
        struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
 
        /* Request state should be less than max_level */
-       if (WARN_ON(state > cpufreq_cdev->max_level))
+       if (state > cpufreq_cdev->max_level)
                return -EINVAL;
 
        num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
@@ -437,7 +437,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
        int ret;
 
        /* Request state should be less than max_level */
-       if (WARN_ON(state > cpufreq_cdev->max_level))
+       if (state > cpufreq_cdev->max_level)
                return -EINVAL;
 
        /* Check if the old cooling action is same as new cooling action */
@@ -456,6 +456,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
                capacity = frequency * max_capacity;
                capacity /= cpufreq_cdev->policy->cpuinfo.max_freq;
                arch_set_thermal_pressure(cpus, max_capacity - capacity);
+               ret = 0;
        }
 
        return ret;
diff --git a/drivers/thermal/imx8mm_thermal.c b/drivers/thermal/imx8mm_thermal.c
new file mode 100644 (file)
index 0000000..0d60f8d
--- /dev/null
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020 NXP.
+ *
+ * Author: Anson Huang <Anson.Huang@nxp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define TER                    0x0     /* TMU enable */
+#define TPS                    0x4
+#define TRITSR                 0x20    /* TMU immediate temp */
+
+#define TER_EN                 BIT(31)
+#define TRITSR_TEMP0_VAL_MASK  0xff
+#define TRITSR_TEMP1_VAL_MASK  0xff0000
+
+#define PROBE_SEL_ALL          GENMASK(31, 30)
+
+#define probe_status_offset(x) (30 + x)
+#define SIGN_BIT               BIT(7)
+#define TEMP_VAL_MASK          GENMASK(6, 0)
+
+#define VER1_TEMP_LOW_LIMIT    10000
+#define VER2_TEMP_LOW_LIMIT    -40000
+#define VER2_TEMP_HIGH_LIMIT   125000
+
+#define TMU_VER1               0x1
+#define TMU_VER2               0x2
+
+struct thermal_soc_data {
+       u32 num_sensors;
+       u32 version;
+       int (*get_temp)(void *, int *);
+};
+
+struct tmu_sensor {
+       struct imx8mm_tmu *priv;
+       u32 hw_id;
+       struct thermal_zone_device *tzd;
+};
+
+struct imx8mm_tmu {
+       void __iomem *base;
+       struct clk *clk;
+       const struct thermal_soc_data *socdata;
+       struct tmu_sensor sensors[0];
+};
+
+static int imx8mm_tmu_get_temp(void *data, int *temp)
+{
+       struct tmu_sensor *sensor = data;
+       struct imx8mm_tmu *tmu = sensor->priv;
+       u32 val;
+
+       val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
+       *temp = val * 1000;
+       if (*temp < VER1_TEMP_LOW_LIMIT)
+               return -EAGAIN;
+
+       return 0;
+}
+
+static int imx8mp_tmu_get_temp(void *data, int *temp)
+{
+       struct tmu_sensor *sensor = data;
+       struct imx8mm_tmu *tmu = sensor->priv;
+       unsigned long val;
+       bool ready;
+
+       val = readl_relaxed(tmu->base + TRITSR);
+       ready = test_bit(probe_status_offset(sensor->hw_id), &val);
+       if (!ready)
+               return -EAGAIN;
+
+       val = sensor->hw_id ? FIELD_GET(TRITSR_TEMP1_VAL_MASK, val) :
+             FIELD_GET(TRITSR_TEMP0_VAL_MASK, val);
+       if (val & SIGN_BIT) /* negative */
+               val = (~(val & TEMP_VAL_MASK) + 1);
+
+       *temp = val * 1000;
+       if (*temp < VER2_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
+               return -EAGAIN;
+
+       return 0;
+}
+
+static int tmu_get_temp(void *data, int *temp)
+{
+       struct tmu_sensor *sensor = data;
+       struct imx8mm_tmu *tmu = sensor->priv;
+
+       return tmu->socdata->get_temp(data, temp);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+       .get_temp = tmu_get_temp,
+};
+
+static void imx8mm_tmu_enable(struct imx8mm_tmu *tmu, bool enable)
+{
+       u32 val;
+
+       val = readl_relaxed(tmu->base + TER);
+       val = enable ? (val | TER_EN) : (val & ~TER_EN);
+       writel_relaxed(val, tmu->base + TER);
+}
+
+static void imx8mm_tmu_probe_sel_all(struct imx8mm_tmu *tmu)
+{
+       u32 val;
+
+       val = readl_relaxed(tmu->base + TPS);
+       val |= PROBE_SEL_ALL;
+       writel_relaxed(val, tmu->base + TPS);
+}
+
+static int imx8mm_tmu_probe(struct platform_device *pdev)
+{
+       const struct thermal_soc_data *data;
+       struct imx8mm_tmu *tmu;
+       int ret;
+       int i;
+
+       data = of_device_get_match_data(&pdev->dev);
+
+       tmu = devm_kzalloc(&pdev->dev, struct_size(tmu, sensors,
+                          data->num_sensors), GFP_KERNEL);
+       if (!tmu)
+               return -ENOMEM;
+
+       tmu->socdata = data;
+
+       tmu->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(tmu->base))
+               return PTR_ERR(tmu->base);
+
+       tmu->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(tmu->clk)) {
+               ret = PTR_ERR(tmu->clk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "failed to get tmu clock: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(tmu->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable tmu clock: %d\n", ret);
+               return ret;
+       }
+
+       /* disable the monitor during initialization */
+       imx8mm_tmu_enable(tmu, false);
+
+       for (i = 0; i < data->num_sensors; i++) {
+               tmu->sensors[i].priv = tmu;
+               tmu->sensors[i].tzd =
+                       devm_thermal_zone_of_sensor_register(&pdev->dev, i,
+                                                            &tmu->sensors[i],
+                                                            &tmu_tz_ops);
+               if (IS_ERR(tmu->sensors[i].tzd)) {
+                       dev_err(&pdev->dev,
+                               "failed to register thermal zone sensor[%d]: %d\n",
+                               i, ret);
+                       return PTR_ERR(tmu->sensors[i].tzd);
+               }
+               tmu->sensors[i].hw_id = i;
+       }
+
+       platform_set_drvdata(pdev, tmu);
+
+       /* enable all the probes for V2 TMU */
+       if (tmu->socdata->version == TMU_VER2)
+               imx8mm_tmu_probe_sel_all(tmu);
+
+       /* enable the monitor */
+       imx8mm_tmu_enable(tmu, true);
+
+       return 0;
+}
+
+static int imx8mm_tmu_remove(struct platform_device *pdev)
+{
+       struct imx8mm_tmu *tmu = platform_get_drvdata(pdev);
+
+       /* disable TMU */
+       imx8mm_tmu_enable(tmu, false);
+
+       clk_disable_unprepare(tmu->clk);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct thermal_soc_data imx8mm_tmu_data = {
+       .num_sensors = 1,
+       .version = TMU_VER1,
+       .get_temp = imx8mm_tmu_get_temp,
+};
+
+static struct thermal_soc_data imx8mp_tmu_data = {
+       .num_sensors = 2,
+       .version = TMU_VER2,
+       .get_temp = imx8mp_tmu_get_temp,
+};
+
+static const struct of_device_id imx8mm_tmu_table[] = {
+       { .compatible = "fsl,imx8mm-tmu", .data = &imx8mm_tmu_data, },
+       { .compatible = "fsl,imx8mp-tmu", .data = &imx8mp_tmu_data, },
+       { },
+};
+
+static struct platform_driver imx8mm_tmu = {
+       .driver = {
+               .name   = "i.mx8mm_thermal",
+               .of_match_table = imx8mm_tmu_table,
+       },
+       .probe = imx8mm_tmu_probe,
+       .remove = imx8mm_tmu_remove,
+};
+module_platform_driver(imx8mm_tmu);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("i.MX8MM Thermal Monitor Unit driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/imx_sc_thermal.c b/drivers/thermal/imx_sc_thermal.c
new file mode 100644 (file)
index 0000000..a8723b1
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018-2020 NXP.
+ */
+
+#include <linux/err.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/firmware/imx/types.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define IMX_SC_MISC_FUNC_GET_TEMP      13
+
+static struct imx_sc_ipc *thermal_ipc_handle;
+
+struct imx_sc_sensor {
+       struct thermal_zone_device *tzd;
+       u32 resource_id;
+};
+
+struct req_get_temp {
+       u16 resource_id;
+       u8 type;
+} __packed __aligned(4);
+
+struct resp_get_temp {
+       s16 celsius;
+       s8 tenths;
+} __packed __aligned(4);
+
+struct imx_sc_msg_misc_get_temp {
+       struct imx_sc_rpc_msg hdr;
+       union {
+               struct req_get_temp req;
+               struct resp_get_temp resp;
+       } data;
+} __packed __aligned(4);
+
+static int imx_sc_thermal_get_temp(void *data, int *temp)
+{
+       struct imx_sc_msg_misc_get_temp msg;
+       struct imx_sc_rpc_msg *hdr = &msg.hdr;
+       struct imx_sc_sensor *sensor = data;
+       int ret;
+
+       msg.data.req.resource_id = sensor->resource_id;
+       msg.data.req.type = IMX_SC_C_TEMP;
+
+       hdr->ver = IMX_SC_RPC_VERSION;
+       hdr->svc = IMX_SC_RPC_SVC_MISC;
+       hdr->func = IMX_SC_MISC_FUNC_GET_TEMP;
+       hdr->size = 2;
+
+       ret = imx_scu_call_rpc(thermal_ipc_handle, &msg, true);
+       if (ret) {
+               dev_err(&sensor->tzd->device, "read temp sensor %d failed, ret %d\n",
+                       sensor->resource_id, ret);
+               return ret;
+       }
+
+       *temp = msg.data.resp.celsius * 1000 + msg.data.resp.tenths * 100;
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops imx_sc_thermal_ops = {
+       .get_temp = imx_sc_thermal_get_temp,
+};
+
+static int imx_sc_thermal_probe(struct platform_device *pdev)
+{
+       struct device_node *np, *child, *sensor_np;
+       struct imx_sc_sensor *sensor;
+       int ret;
+
+       ret = imx_scu_get_handle(&thermal_ipc_handle);
+       if (ret)
+               return ret;
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np)
+               return -ENODEV;
+
+       sensor_np = of_node_get(pdev->dev.of_node);
+
+       for_each_available_child_of_node(np, child) {
+               sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
+               if (!sensor) {
+                       of_node_put(sensor_np);
+                       return -ENOMEM;
+               }
+
+               ret = thermal_zone_of_get_sensor_id(child,
+                                                   sensor_np,
+                                                   &sensor->resource_id);
+               if (ret < 0) {
+                       dev_err(&pdev->dev,
+                               "failed to get valid sensor resource id: %d\n",
+                               ret);
+                       break;
+               }
+
+               sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
+                                                                  sensor->resource_id,
+                                                                  sensor,
+                                                                  &imx_sc_thermal_ops);
+               if (IS_ERR(sensor->tzd)) {
+                       dev_err(&pdev->dev, "failed to register thermal zone\n");
+                       ret = PTR_ERR(sensor->tzd);
+                       break;
+               }
+       }
+
+       of_node_put(sensor_np);
+
+       return ret;
+}
+
+static int imx_sc_thermal_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static const struct of_device_id imx_sc_thermal_table[] = {
+       { .compatible = "fsl,imx-sc-thermal", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, imx_sc_thermal_table);
+
+static struct platform_driver imx_sc_thermal_driver = {
+               .probe = imx_sc_thermal_probe,
+               .remove = imx_sc_thermal_remove,
+               .driver = {
+                       .name = "imx-sc-thermal",
+                       .of_match_table = imx_sc_thermal_table,
+               },
+};
+module_platform_driver(imx_sc_thermal_driver);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("Thermal driver for NXP i.MX SoCs with system controller");
+MODULE_LICENSE("GPL v2");
index bb6754a..e761c9b 100644 (file)
@@ -3,24 +3,17 @@
 // Copyright 2013 Freescale Semiconductor, Inc.
 
 #include <linux/clk.h>
-#include <linux/cpu.h>
 #include <linux/cpufreq.h>
 #include <linux/cpu_cooling.h>
 #include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
-#include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <linux/slab.h>
 #include <linux/thermal.h>
-#include <linux/types.h>
 #include <linux/nvmem-consumer.h>
 
 #define REG_SET                0x4
@@ -872,14 +865,12 @@ static int imx_thermal_remove(struct platform_device *pdev)
                clk_disable_unprepare(data->thermal_clk);
 
        thermal_zone_device_unregister(data->tz);
-       cpufreq_cooling_unregister(data->cdev);
-       cpufreq_cpu_put(data->policy);
+       imx_thermal_unregister_legacy_cooling(data);
 
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int imx_thermal_suspend(struct device *dev)
+static int __maybe_unused imx_thermal_suspend(struct device *dev)
 {
        struct imx_thermal_data *data = dev_get_drvdata(dev);
        struct regmap *map = data->tempmon;
@@ -900,7 +891,7 @@ static int imx_thermal_suspend(struct device *dev)
        return 0;
 }
 
-static int imx_thermal_resume(struct device *dev)
+static int __maybe_unused imx_thermal_resume(struct device *dev)
 {
        struct imx_thermal_data *data = dev_get_drvdata(dev);
        struct regmap *map = data->tempmon;
@@ -918,7 +909,6 @@ static int imx_thermal_resume(struct device *dev)
 
        return 0;
 }
-#endif
 
 static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
                         imx_thermal_suspend, imx_thermal_resume);
index 6cad15e..ceef89c 100644 (file)
@@ -65,7 +65,7 @@ static ssize_t available_uuids_show(struct device *dev,
        for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
                if (priv->uuid_bitmap & (1 << i))
                        if (PAGE_SIZE - length > 0)
-                               length += snprintf(&buf[length],
+                               length += scnprintf(&buf[length],
                                                   PAGE_SIZE - length,
                                                   "%s\n",
                                                   int3400_thermal_uuids[i]);
index b1fd345..297db1d 100644 (file)
@@ -45,6 +45,9 @@
 /* JasperLake thermal reporting device */
 #define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4503
 
+/* TigerLake thermal reporting device */
+#define PCI_DEVICE_ID_PROC_TGL_THERMAL 0x9A03
+
 #define DRV_NAME "proc_thermal"
 
 struct power_config {
@@ -728,6 +731,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL),
                .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)},
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_TGL_THERMAL),
+               .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
        { 0, },
 };
 
index ef0baa9..874a47d 100644 (file)
@@ -449,6 +449,50 @@ thermal_zone_of_add_sensor(struct device_node *zone,
 }
 
 /**
+ * thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone
+ * @tz_np: a valid thermal zone device node.
+ * @sensor_np: a sensor node of a valid sensor device.
+ * @id: the sensor ID returned if success.
+ *
+ * This function will get sensor ID from a given thermal zone node and
+ * the sensor node must match the temperature provider @sensor_np.
+ *
+ * Return: 0 on success, proper error code otherwise.
+ */
+
+int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
+                                 struct device_node *sensor_np,
+                                 u32 *id)
+{
+       struct of_phandle_args sensor_specs;
+       int ret;
+
+       ret = of_parse_phandle_with_args(tz_np,
+                                        "thermal-sensors",
+                                        "#thermal-sensor-cells",
+                                        0,
+                                        &sensor_specs);
+       if (ret)
+               return ret;
+
+       if (sensor_specs.np != sensor_np) {
+               of_node_put(sensor_specs.np);
+               return -ENODEV;
+       }
+
+       if (sensor_specs.args_count > 1)
+               pr_warn("%pOFn: too many cells in sensor specifier %d\n",
+                    sensor_specs.np, sensor_specs.args_count);
+
+       *id = sensor_specs.args_count ? sensor_specs.args[0] : 0;
+
+       of_node_put(sensor_specs.np);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id);
+
+/**
  * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
  * @dev: a valid struct device pointer of a sensor device. Must contain
  *       a valid .of_node, for the sensor node.
@@ -499,36 +543,22 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
        sensor_np = of_node_get(dev->of_node);
 
        for_each_available_child_of_node(np, child) {
-               struct of_phandle_args sensor_specs;
                int ret, id;
 
                /* For now, thermal framework supports only 1 sensor per zone */
-               ret = of_parse_phandle_with_args(child, "thermal-sensors",
-                                                "#thermal-sensor-cells",
-                                                0, &sensor_specs);
+               ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id);
                if (ret)
                        continue;
 
-               if (sensor_specs.args_count >= 1) {
-                       id = sensor_specs.args[0];
-                       WARN(sensor_specs.args_count > 1,
-                            "%pOFn: too many cells in sensor specifier %d\n",
-                            sensor_specs.np, sensor_specs.args_count);
-               } else {
-                       id = 0;
-               }
-
-               if (sensor_specs.np == sensor_np && id == sensor_id) {
+               if (id == sensor_id) {
                        tzd = thermal_zone_of_add_sensor(child, sensor_np,
                                                         data, ops);
                        if (!IS_ERR(tzd))
                                tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
 
-                       of_node_put(sensor_specs.np);
                        of_node_put(child);
                        goto exit;
                }
-               of_node_put(sensor_specs.np);
        }
 exit:
        of_node_put(sensor_np);
index fb77acb..2a28a5a 100644 (file)
@@ -245,7 +245,7 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
        return adc_code * slope + offset;
 }
 
-static int get_temp_8960(struct tsens_sensor *s, int *temp)
+static int get_temp_8960(const struct tsens_sensor *s, int *temp)
 {
        int ret;
        u32 code, trdy;
@@ -279,7 +279,7 @@ static const struct tsens_ops ops_8960 = {
        .resume         = resume_8960,
 };
 
-const struct tsens_plat_data data_8960 = {
+struct tsens_plat_data data_8960 = {
        .num_sensors    = 11,
        .ops            = &ops_8960,
 };
index c8d57ee..1725453 100644 (file)
  * @low_thresh:     lower threshold temperature value
  * @low_irq_mask:   mask register for lower threshold irqs
  * @low_irq_clear:  clear register for lower threshold irqs
+ * @crit_viol:      critical threshold violated
+ * @crit_thresh:    critical threshold temperature value
+ * @crit_irq_mask:  mask register for critical threshold irqs
+ * @crit_irq_clear: clear register for critical threshold irqs
  *
  * Structure containing data about temperature threshold settings and
  * irq status if they were violated.
@@ -36,6 +40,10 @@ struct tsens_irq_data {
        int low_thresh;
        u32 low_irq_mask;
        u32 low_irq_clear;
+       u32 crit_viol;
+       u32 crit_thresh;
+       u32 crit_irq_mask;
+       u32 crit_irq_clear;
 };
 
 char *qfprom_read(struct device *dev, const char *cname)
@@ -128,7 +136,7 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
  * Return: Temperature in milliCelsius on success, a negative errno will
  * be returned in error cases
  */
-static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
+static int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
 {
        struct tsens_priv *priv = s->priv;
        u32 resolution;
@@ -160,7 +168,7 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field)
  *
  * Return: ADC code or temperature in deciCelsius.
  */
-static int tsens_mC_to_hw(struct tsens_sensor *s, int temp)
+static int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
 {
        struct tsens_priv *priv = s->priv;
 
@@ -189,6 +197,9 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
        case LOWER:
                index = LOW_INT_CLEAR_0 + hw_id;
                break;
+       case CRITICAL:
+               /* No critical interrupts before v2 */
+               return;
        }
        regmap_field_write(priv->rf[index], enable ? 0 : 1);
 }
@@ -214,6 +225,10 @@ static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
                index_mask  = LOW_INT_MASK_0 + hw_id;
                index_clear = LOW_INT_CLEAR_0 + hw_id;
                break;
+       case CRITICAL:
+               index_mask  = CRIT_INT_MASK_0 + hw_id;
+               index_clear = CRIT_INT_CLEAR_0 + hw_id;
+               break;
        }
 
        if (enable) {
@@ -268,14 +283,23 @@ static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
        ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
        if (ret)
                return ret;
-       if (d->up_viol || d->low_viol)
+
+       if (priv->feat->crit_int) {
+               ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
+                                       &d->crit_viol);
+               if (ret)
+                       return ret;
+       }
+
+       if (d->up_viol || d->low_viol || d->crit_viol)
                return 1;
 
        return 0;
 }
 
 static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
-                               struct tsens_sensor *s, struct tsens_irq_data *d)
+                               const struct tsens_sensor *s,
+                               struct tsens_irq_data *d)
 {
        int ret;
 
@@ -292,22 +316,37 @@ static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
                ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
                if (ret)
                        return ret;
+               ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
+                                       &d->crit_irq_clear);
+               if (ret)
+                       return ret;
+               ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
+                                       &d->crit_irq_mask);
+               if (ret)
+                       return ret;
+
+               d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
        } else {
                /* No mask register on older TSENS */
                d->up_irq_mask = 0;
                d->low_irq_mask = 0;
+               d->crit_irq_clear = 0;
+               d->crit_irq_mask = 0;
+               d->crit_thresh = 0;
        }
 
        d->up_thresh  = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
        d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
 
-       dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u) | clr(%u|%u) | mask(%u|%u)\n",
-               hw_id, __func__, (d->up_viol || d->low_viol) ? "(V)" : "",
-               d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear,
-               d->low_irq_mask, d->up_irq_mask);
-       dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__,
-               (d->up_viol || d->low_viol) ? "(violation)" : "",
-               d->low_thresh, d->up_thresh);
+       dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
+               hw_id, __func__,
+               (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
+               d->low_viol, d->up_viol, d->crit_viol,
+               d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
+               d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
+       dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
+               (d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
+               d->low_thresh, d->up_thresh, d->crit_thresh);
 
        return 0;
 }
@@ -322,6 +361,76 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
 }
 
 /**
+ * tsens_critical_irq_thread() - Threaded handler for critical interrupts
+ * @irq: irq number
+ * @data: tsens controller private data
+ *
+ * Check FSM watchdog bark status and clear if needed.
+ * Check all sensors to find ones that violated their critical threshold limits.
+ * Clear and then re-enable the interrupt.
+ *
+ * The level-triggered interrupt might deassert if the temperature returned to
+ * within the threshold limits by the time the handler got scheduled. We
+ * consider the irq to have been handled in that case.
+ *
+ * Return: IRQ_HANDLED
+ */
+irqreturn_t tsens_critical_irq_thread(int irq, void *data)
+{
+       struct tsens_priv *priv = data;
+       struct tsens_irq_data d;
+       int temp, ret, i;
+       u32 wdog_status, wdog_count;
+
+       if (priv->feat->has_watchdog) {
+               ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
+                                       &wdog_status);
+               if (ret)
+                       return ret;
+
+               if (wdog_status) {
+                       /* Clear WDOG interrupt */
+                       regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
+                       regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
+                       ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
+                                               &wdog_count);
+                       if (ret)
+                               return ret;
+                       if (wdog_count)
+                               dev_dbg(priv->dev, "%s: watchdog count: %d\n",
+                                       __func__, wdog_count);
+
+                       /* Fall through to handle critical interrupts if any */
+               }
+       }
+
+       for (i = 0; i < priv->num_sensors; i++) {
+               const struct tsens_sensor *s = &priv->sensor[i];
+               u32 hw_id = s->hw_id;
+
+               if (IS_ERR(s->tzd))
+                       continue;
+               if (!tsens_threshold_violated(priv, hw_id, &d))
+                       continue;
+               ret = get_temp_tsens_valid(s, &temp);
+               if (ret) {
+                       dev_err(priv->dev, "[%u] %s: error reading sensor\n",
+                               hw_id, __func__);
+                       continue;
+               }
+
+               tsens_read_irq_state(priv, hw_id, s, &d);
+               if (d.crit_viol &&
+                   !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
+                       /* Mask critical interrupts, unused on Linux */
+                       tsens_set_interrupt(priv, hw_id, CRITICAL, false);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
  * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
  * @irq: irq number
  * @data: tsens controller private data
@@ -346,10 +455,10 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
 
        for (i = 0; i < priv->num_sensors; i++) {
                bool trigger = false;
-               struct tsens_sensor *s = &priv->sensor[i];
+               const struct tsens_sensor *s = &priv->sensor[i];
                u32 hw_id = s->hw_id;
 
-               if (IS_ERR(priv->sensor[i].tzd))
+               if (IS_ERR(s->tzd))
                        continue;
                if (!tsens_threshold_violated(priv, hw_id, &d))
                        continue;
@@ -368,7 +477,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
                        tsens_set_interrupt(priv, hw_id, UPPER, disable);
                        if (d.up_thresh > temp) {
                                dev_dbg(priv->dev, "[%u] %s: re-arm upper\n",
-                                       priv->sensor[i].hw_id, __func__);
+                                       hw_id, __func__);
                                tsens_set_interrupt(priv, hw_id, UPPER, enable);
                        } else {
                                trigger = true;
@@ -379,7 +488,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
                        tsens_set_interrupt(priv, hw_id, LOWER, disable);
                        if (d.low_thresh < temp) {
                                dev_dbg(priv->dev, "[%u] %s: re-arm low\n",
-                                       priv->sensor[i].hw_id, __func__);
+                                       hw_id, __func__);
                                tsens_set_interrupt(priv, hw_id, LOWER, enable);
                        } else {
                                trigger = true;
@@ -392,7 +501,7 @@ irqreturn_t tsens_irq_thread(int irq, void *data)
                if (trigger) {
                        dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n",
                                hw_id, __func__, temp);
-                       thermal_zone_device_update(priv->sensor[i].tzd,
+                       thermal_zone_device_update(s->tzd,
                                                   THERMAL_EVENT_UNSPECIFIED);
                } else {
                        dev_dbg(priv->dev, "[%u] %s: no violation:  %d\n",
@@ -435,7 +544,7 @@ int tsens_set_trips(void *_sensor, int low, int high)
        spin_unlock_irqrestore(&priv->ul_lock, flags);
 
        dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
-               s->hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
+               hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
 
        return 0;
 }
@@ -457,7 +566,7 @@ void tsens_disable_irq(struct tsens_priv *priv)
        regmap_field_write(priv->rf[INT_EN], 0);
 }
 
-int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
+int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
 {
        struct tsens_priv *priv = s->priv;
        int hw_id = s->hw_id;
@@ -486,7 +595,7 @@ int get_temp_tsens_valid(struct tsens_sensor *s, int *temp)
        return 0;
 }
 
-int get_temp_common(struct tsens_sensor *s, int *temp)
+int get_temp_common(const struct tsens_sensor *s, int *temp)
 {
        struct tsens_priv *priv = s->priv;
        int hw_id = s->hw_id;
@@ -590,6 +699,7 @@ int __init init_common(struct tsens_priv *priv)
 {
        void __iomem *tm_base, *srot_base;
        struct device *dev = priv->dev;
+       u32 ver_minor;
        struct resource *res;
        u32 enabled;
        int ret, i, j;
@@ -602,7 +712,7 @@ int __init init_common(struct tsens_priv *priv)
                /* DT with separate SROT and TM address space */
                priv->tm_offset = 0;
                res = platform_get_resource(op, IORESOURCE_MEM, 1);
-               srot_base = devm_ioremap_resource(&op->dev, res);
+               srot_base = devm_ioremap_resource(dev, res);
                if (IS_ERR(srot_base)) {
                        ret = PTR_ERR(srot_base);
                        goto err_put_device;
@@ -620,7 +730,7 @@ int __init init_common(struct tsens_priv *priv)
        }
 
        res = platform_get_resource(op, IORESOURCE_MEM, 0);
-       tm_base = devm_ioremap_resource(&op->dev, res);
+       tm_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(tm_base)) {
                ret = PTR_ERR(tm_base);
                goto err_put_device;
@@ -639,6 +749,9 @@ int __init init_common(struct tsens_priv *priv)
                        if (IS_ERR(priv->rf[i]))
                                return PTR_ERR(priv->rf[i]);
                }
+               ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
+               if (ret)
+                       goto err_put_device;
        }
 
        priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
@@ -683,12 +796,47 @@ int __init init_common(struct tsens_priv *priv)
                }
        }
 
+       if (priv->feat->crit_int) {
+               /* Loop might need changes if enum regfield_ids is reordered */
+               for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
+                       for (i = 0; i < priv->feat->max_sensors; i++) {
+                               int idx = j + i;
+
+                               priv->rf[idx] =
+                                       devm_regmap_field_alloc(dev,
+                                                               priv->tm_map,
+                                                               priv->fields[idx]);
+                               if (IS_ERR(priv->rf[idx])) {
+                                       ret = PTR_ERR(priv->rf[idx]);
+                                       goto err_put_device;
+                               }
+                       }
+               }
+       }
+
+       if (tsens_version(priv) > VER_1_X &&  ver_minor > 2) {
+               /* Watchdog is present only on v2.3+ */
+               priv->feat->has_watchdog = 1;
+               for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
+                       priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
+                                                             priv->fields[i]);
+                       if (IS_ERR(priv->rf[i])) {
+                               ret = PTR_ERR(priv->rf[i]);
+                               goto err_put_device;
+                       }
+               }
+               /*
+                * Watchdog is already enabled, unmask the bark.
+                * Disable cycle completion monitoring
+                */
+               regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
+               regmap_field_write(priv->rf[CC_MON_MASK], 1);
+       }
+
        spin_lock_init(&priv->ul_lock);
        tsens_enable_irq(priv);
        tsens_debug_init(op);
 
-       return 0;
-
 err_put_device:
        put_device(&op->dev);
        return ret;
index 4b8dd6d..959a937 100644 (file)
@@ -327,7 +327,7 @@ static int calibrate_8974(struct tsens_priv *priv)
 
 /* v0.1: 8916, 8974 */
 
-static const struct tsens_features tsens_v0_1_feat = {
+static struct tsens_features tsens_v0_1_feat = {
        .ver_major      = VER_0_1,
        .crit_int       = 0,
        .adc            = 1,
@@ -377,7 +377,7 @@ static const struct tsens_ops ops_8916 = {
        .get_temp       = get_temp_common,
 };
 
-const struct tsens_plat_data data_8916 = {
+struct tsens_plat_data data_8916 = {
        .num_sensors    = 5,
        .ops            = &ops_8916,
        .hw_ids         = (unsigned int []){0, 1, 2, 4, 5 },
@@ -392,7 +392,7 @@ static const struct tsens_ops ops_8974 = {
        .get_temp       = get_temp_common,
 };
 
-const struct tsens_plat_data data_8974 = {
+struct tsens_plat_data data_8974 = {
        .num_sensors    = 11,
        .ops            = &ops_8974,
        .feat           = &tsens_v0_1_feat,
index bd2ddb6..b682a4d 100644 (file)
@@ -299,7 +299,7 @@ static int calibrate_8976(struct tsens_priv *priv)
 
 /* v1.x: msm8956,8976,qcs404,405 */
 
-static const struct tsens_features tsens_v1_feat = {
+static struct tsens_features tsens_v1_feat = {
        .ver_major      = VER_1_X,
        .crit_int       = 0,
        .adc            = 1,
@@ -368,7 +368,7 @@ static const struct tsens_ops ops_generic_v1 = {
        .get_temp       = get_temp_tsens_valid,
 };
 
-const struct tsens_plat_data data_tsens_v1 = {
+struct tsens_plat_data data_tsens_v1 = {
        .ops            = &ops_generic_v1,
        .feat           = &tsens_v1_feat,
        .fields = tsens_v1_regfields,
@@ -381,7 +381,7 @@ static const struct tsens_ops ops_8976 = {
 };
 
 /* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */
-const struct tsens_plat_data data_8976 = {
+struct tsens_plat_data data_8976 = {
        .num_sensors    = 11,
        .ops            = &ops_8976,
        .hw_ids         = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10},
index a4d15e1..b293ed3 100644 (file)
 #define TM_Sn_CRITICAL_THRESHOLD_OFF   0x0060
 #define TM_Sn_STATUS_OFF               0x00a0
 #define TM_TRDY_OFF                    0x00e4
+#define TM_WDOG_LOG_OFF                0x013c
 
 /* v2.x: 8996, 8998, sdm845 */
 
-static const struct tsens_features tsens_v2_feat = {
+static struct tsens_features tsens_v2_feat = {
        .ver_major      = VER_2_X,
        .crit_int       = 1,
        .adc            = 0,
@@ -51,8 +52,9 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
        [INT_EN]  = REG_FIELD(TM_INT_EN_OFF, 0, 2),
 
        /* TEMPERATURE THRESHOLDS */
-       REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH, TM_Sn_UPPER_LOWER_THRESHOLD_OFF,  0,  11),
-       REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH,  TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12,  23),
+       REG_FIELD_FOR_EACH_SENSOR16(LOW_THRESH,  TM_Sn_UPPER_LOWER_THRESHOLD_OFF,  0,  11),
+       REG_FIELD_FOR_EACH_SENSOR16(UP_THRESH,   TM_Sn_UPPER_LOWER_THRESHOLD_OFF, 12,  23),
+       REG_FIELD_FOR_EACH_SENSOR16(CRIT_THRESH, TM_Sn_CRITICAL_THRESHOLD_OFF,     0,  11),
 
        /* INTERRUPTS [CLEAR/STATUS/MASK] */
        REG_FIELD_SPLIT_BITS_0_15(LOW_INT_STATUS,  TM_UPPER_LOWER_INT_STATUS_OFF),
@@ -61,6 +63,18 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
        REG_FIELD_SPLIT_BITS_16_31(UP_INT_STATUS,  TM_UPPER_LOWER_INT_STATUS_OFF),
        REG_FIELD_SPLIT_BITS_16_31(UP_INT_CLEAR,   TM_UPPER_LOWER_INT_CLEAR_OFF),
        REG_FIELD_SPLIT_BITS_16_31(UP_INT_MASK,    TM_UPPER_LOWER_INT_MASK_OFF),
+       REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_STATUS, TM_CRITICAL_INT_STATUS_OFF),
+       REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_CLEAR,  TM_CRITICAL_INT_CLEAR_OFF),
+       REG_FIELD_SPLIT_BITS_0_15(CRIT_INT_MASK,   TM_CRITICAL_INT_MASK_OFF),
+
+       /* WATCHDOG on v2.3 or later */
+       [WDOG_BARK_STATUS] = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 31, 31),
+       [WDOG_BARK_CLEAR]  = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF,  31, 31),
+       [WDOG_BARK_MASK]   = REG_FIELD(TM_CRITICAL_INT_MASK_OFF,   31, 31),
+       [CC_MON_STATUS]    = REG_FIELD(TM_CRITICAL_INT_STATUS_OFF, 30, 30),
+       [CC_MON_CLEAR]     = REG_FIELD(TM_CRITICAL_INT_CLEAR_OFF,  30, 30),
+       [CC_MON_MASK]      = REG_FIELD(TM_CRITICAL_INT_MASK_OFF,   30, 30),
+       [WDOG_BARK_COUNT]  = REG_FIELD(TM_WDOG_LOG_OFF,             0,  7),
 
        /* Sn_STATUS */
        REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP,       TM_Sn_STATUS_OFF,  0,  11),
@@ -81,14 +95,14 @@ static const struct tsens_ops ops_generic_v2 = {
        .get_temp       = get_temp_tsens_valid,
 };
 
-const struct tsens_plat_data data_tsens_v2 = {
+struct tsens_plat_data data_tsens_v2 = {
        .ops            = &ops_generic_v2,
        .feat           = &tsens_v2_feat,
        .fields = tsens_v2_regfields,
 };
 
 /* Kept around for backward compatibility with old msm8996.dtsi */
-const struct tsens_plat_data data_8996 = {
+struct tsens_plat_data data_8996 = {
        .num_sensors    = 13,
        .ops            = &ops_generic_v2,
        .feat           = &tsens_v2_feat,
index 0e7cf52..2f77d23 100644 (file)
@@ -85,11 +85,42 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = {
        .set_trips = tsens_set_trips,
 };
 
+static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
+                             irq_handler_t thread_fn)
+{
+       struct platform_device *pdev;
+       int ret, irq;
+
+       pdev = of_find_device_by_node(priv->dev->of_node);
+       if (!pdev)
+               return -ENODEV;
+
+       irq = platform_get_irq_byname(pdev, irqname);
+       if (irq < 0) {
+               ret = irq;
+               /* For old DTs with no IRQ defined */
+               if (irq == -ENXIO)
+                       ret = 0;
+       } else {
+               ret = devm_request_threaded_irq(&pdev->dev, irq,
+                                               NULL, thread_fn,
+                                               IRQF_ONESHOT,
+                                               dev_name(&pdev->dev), priv);
+               if (ret)
+                       dev_err(&pdev->dev, "%s: failed to get irq\n",
+                               __func__);
+               else
+                       enable_irq_wake(irq);
+       }
+
+       put_device(&pdev->dev);
+       return ret;
+}
+
 static int tsens_register(struct tsens_priv *priv)
 {
-       int i, ret, irq;
+       int i, ret;
        struct thermal_zone_device *tzd;
-       struct platform_device *pdev;
 
        for (i = 0;  i < priv->num_sensors; i++) {
                priv->sensor[i].priv = priv;
@@ -103,32 +134,14 @@ static int tsens_register(struct tsens_priv *priv)
                        priv->ops->enable(priv, i);
        }
 
-       pdev = of_find_device_by_node(priv->dev->of_node);
-       if (!pdev)
-               return -ENODEV;
-
-       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;
-       }
+       ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
+       if (ret < 0)
+               return ret;
 
-       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;
 }
 
index e24a865..502acf0 100644 (file)
@@ -23,6 +23,7 @@
 
 struct tsens_priv;
 
+/* IP version numbers in ascending order */
 enum tsens_ver {
        VER_0_1 = 0,
        VER_1_X,
@@ -32,6 +33,7 @@ enum tsens_ver {
 enum tsens_irq_type {
        LOWER,
        UPPER,
+       CRITICAL,
 };
 
 /**
@@ -67,7 +69,7 @@ struct tsens_ops {
        /* mandatory callbacks */
        int (*init)(struct tsens_priv *priv);
        int (*calibrate)(struct tsens_priv *priv);
-       int (*get_temp)(struct tsens_sensor *s, int *temp);
+       int (*get_temp)(const struct tsens_sensor *s, int *temp);
        /* optional callbacks */
        int (*enable)(struct tsens_priv *priv, int i);
        void (*disable)(struct tsens_priv *priv);
@@ -374,6 +376,82 @@ enum regfield_ids {
        CRITICAL_STATUS_13,
        CRITICAL_STATUS_14,
        CRITICAL_STATUS_15,
+       CRIT_INT_STATUS_0,      /* CRITICAL interrupt status */
+       CRIT_INT_STATUS_1,
+       CRIT_INT_STATUS_2,
+       CRIT_INT_STATUS_3,
+       CRIT_INT_STATUS_4,
+       CRIT_INT_STATUS_5,
+       CRIT_INT_STATUS_6,
+       CRIT_INT_STATUS_7,
+       CRIT_INT_STATUS_8,
+       CRIT_INT_STATUS_9,
+       CRIT_INT_STATUS_10,
+       CRIT_INT_STATUS_11,
+       CRIT_INT_STATUS_12,
+       CRIT_INT_STATUS_13,
+       CRIT_INT_STATUS_14,
+       CRIT_INT_STATUS_15,
+       CRIT_INT_CLEAR_0,       /* CRITICAL interrupt clear */
+       CRIT_INT_CLEAR_1,
+       CRIT_INT_CLEAR_2,
+       CRIT_INT_CLEAR_3,
+       CRIT_INT_CLEAR_4,
+       CRIT_INT_CLEAR_5,
+       CRIT_INT_CLEAR_6,
+       CRIT_INT_CLEAR_7,
+       CRIT_INT_CLEAR_8,
+       CRIT_INT_CLEAR_9,
+       CRIT_INT_CLEAR_10,
+       CRIT_INT_CLEAR_11,
+       CRIT_INT_CLEAR_12,
+       CRIT_INT_CLEAR_13,
+       CRIT_INT_CLEAR_14,
+       CRIT_INT_CLEAR_15,
+       CRIT_INT_MASK_0,        /* CRITICAL interrupt mask */
+       CRIT_INT_MASK_1,
+       CRIT_INT_MASK_2,
+       CRIT_INT_MASK_3,
+       CRIT_INT_MASK_4,
+       CRIT_INT_MASK_5,
+       CRIT_INT_MASK_6,
+       CRIT_INT_MASK_7,
+       CRIT_INT_MASK_8,
+       CRIT_INT_MASK_9,
+       CRIT_INT_MASK_10,
+       CRIT_INT_MASK_11,
+       CRIT_INT_MASK_12,
+       CRIT_INT_MASK_13,
+       CRIT_INT_MASK_14,
+       CRIT_INT_MASK_15,
+       CRIT_THRESH_0,          /* CRITICAL threshold values */
+       CRIT_THRESH_1,
+       CRIT_THRESH_2,
+       CRIT_THRESH_3,
+       CRIT_THRESH_4,
+       CRIT_THRESH_5,
+       CRIT_THRESH_6,
+       CRIT_THRESH_7,
+       CRIT_THRESH_8,
+       CRIT_THRESH_9,
+       CRIT_THRESH_10,
+       CRIT_THRESH_11,
+       CRIT_THRESH_12,
+       CRIT_THRESH_13,
+       CRIT_THRESH_14,
+       CRIT_THRESH_15,
+
+       /* WATCHDOG */
+       WDOG_BARK_STATUS,
+       WDOG_BARK_CLEAR,
+       WDOG_BARK_MASK,
+       WDOG_BARK_COUNT,
+
+       /* CYCLE COMPLETION MONITOR */
+       CC_MON_STATUS,
+       CC_MON_CLEAR,
+       CC_MON_MASK,
+
        MIN_STATUS_0,           /* MIN threshold violated */
        MIN_STATUS_1,
        MIN_STATUS_2,
@@ -418,6 +496,7 @@ enum regfield_ids {
  * @adc:      do the sensors only output adc code (instead of temperature)?
  * @srot_split: does the IP neatly splits the register space into SROT and TM,
  *              with SROT only being available to secure boot firmware?
+ * @has_watchdog: does this IP support watchdog functionality?
  * @max_sensors: maximum sensors supported by this version of the IP
  */
 struct tsens_features {
@@ -425,6 +504,7 @@ struct tsens_features {
        unsigned int crit_int:1;
        unsigned int adc:1;
        unsigned int srot_split:1;
+       unsigned int has_watchdog:1;
        unsigned int max_sensors;
 };
 
@@ -440,12 +520,14 @@ struct tsens_plat_data {
        const u32               num_sensors;
        const struct tsens_ops  *ops;
        unsigned int            *hw_ids;
-       const struct tsens_features     *feat;
+       struct tsens_features   *feat;
        const struct reg_field          *fields;
 };
 
 /**
  * struct tsens_context - Registers to be saved/restored across a context loss
+ * @threshold: Threshold register value
+ * @control: Control register value
  */
 struct tsens_context {
        int     threshold;
@@ -460,6 +542,8 @@ struct tsens_context {
  * @srot_map: pointer to SROT register address space
  * @tm_offset: deal with old device trees that don't address TM and SROT
  *             address space separately
+ * @ul_lock: lock while processing upper/lower threshold interrupts
+ * @crit_lock: lock while processing critical threshold interrupts
  * @rf: array of regmap_fields used to store value of the field
  * @ctx: registers to be saved and restored during suspend/resume
  * @feat: features of the IP
@@ -481,36 +565,37 @@ struct tsens_priv {
 
        struct regmap_field             *rf[MAX_REGFIELDS];
        struct tsens_context            ctx;
-       const struct tsens_features     *feat;
+       struct tsens_features           *feat;
        const struct reg_field          *fields;
        const struct tsens_ops          *ops;
 
        struct dentry                   *debug_root;
        struct dentry                   *debug;
 
-       struct tsens_sensor             sensor[0];
+       struct tsens_sensor             sensor[];
 };
 
 char *qfprom_read(struct device *dev, const char *cname);
 void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
 int init_common(struct tsens_priv *priv);
-int get_temp_tsens_valid(struct tsens_sensor *s, int *temp);
-int get_temp_common(struct tsens_sensor *s, int *temp);
+int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
+int get_temp_common(const struct tsens_sensor *s, int *temp);
 int tsens_enable_irq(struct tsens_priv *priv);
 void tsens_disable_irq(struct tsens_priv *priv);
 int tsens_set_trips(void *_sensor, int low, int high);
 irqreturn_t tsens_irq_thread(int irq, void *data);
+irqreturn_t tsens_critical_irq_thread(int irq, void *data);
 
 /* TSENS target */
-extern const struct tsens_plat_data data_8960;
+extern struct tsens_plat_data data_8960;
 
 /* TSENS v0.1 targets */
-extern const struct tsens_plat_data data_8916, data_8974;
+extern struct tsens_plat_data data_8916, data_8974;
 
 /* TSENS v1 targets */
-extern const struct tsens_plat_data data_tsens_v1, data_8976;
+extern struct tsens_plat_data data_tsens_v1, data_8976;
 
 /* TSENS v2 targets */
-extern const struct tsens_plat_data data_8996, data_tsens_v2;
+extern struct tsens_plat_data data_8996, data_tsens_v2;
 
 #endif /* __QCOM_TSENS_H__ */
index 874bc46..028a6bb 100644 (file)
@@ -3,12 +3,11 @@
 // Copyright 2016 Freescale Semiconductor, Inc.
 
 #include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_address.h>
+#include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/sizes.h>
 #include <linux/thermal.h>
@@ -228,6 +227,14 @@ static const struct regmap_access_table qoriq_rd_table = {
        .n_yes_ranges   = ARRAY_SIZE(qoriq_yes_ranges),
 };
 
+static void qoriq_tmu_action(void *p)
+{
+       struct qoriq_tmu_data *data = p;
+
+       regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
+       clk_disable_unprepare(data->clk);
+}
+
 static int qoriq_tmu_probe(struct platform_device *pdev)
 {
        int ret;
@@ -278,6 +285,10 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
                return ret;
        }
 
+       ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data);
+       if (ret)
+               return ret;
+
        /* version register offset at: 0xbf8 on both v1 and v2 */
        ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);
        if (ret) {
@@ -290,35 +301,17 @@ static int qoriq_tmu_probe(struct platform_device *pdev)
 
        ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */
        if (ret < 0)
-               goto err;
+               return ret;
 
        ret = qoriq_tmu_register_tmu_zone(dev, data);
        if (ret < 0) {
                dev_err(dev, "Failed to register sensors\n");
-               ret = -ENODEV;
-               goto err;
+               return ret;
        }
 
        platform_set_drvdata(pdev, data);
 
        return 0;
-
-err:
-       clk_disable_unprepare(data->clk);
-
-       return ret;
-}
-
-static int qoriq_tmu_remove(struct platform_device *pdev)
-{
-       struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
-
-       /* Disable monitoring */
-       regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);
-
-       clk_disable_unprepare(data->clk);
-
-       return 0;
 }
 
 static int __maybe_unused qoriq_tmu_suspend(struct device *dev)
@@ -365,7 +358,6 @@ static struct platform_driver qoriq_tmu = {
                .of_match_table = qoriq_tmu_match,
        },
        .probe  = qoriq_tmu_probe,
-       .remove = qoriq_tmu_remove,
 };
 module_platform_driver(qoriq_tmu);
 
index 72877bd..58fe7c1 100644 (file)
@@ -81,8 +81,6 @@ struct rcar_gen3_thermal_tsc {
        void __iomem *base;
        struct thermal_zone_device *zone;
        struct equation_coefs coef;
-       int low;
-       int high;
        int tj_t;
        int id; /* thermal channel id */
 };
@@ -204,12 +202,14 @@ static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
        return INT_FIXPT(val);
 }
 
-static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
+static int rcar_gen3_thermal_update_range(struct rcar_gen3_thermal_tsc *tsc)
 {
-       struct rcar_gen3_thermal_tsc *tsc = devdata;
+       int temperature, low, high;
+
+       rcar_gen3_thermal_get_temp(tsc, &temperature);
 
-       low = clamp_val(low, -40000, 120000);
-       high = clamp_val(high, -40000, 120000);
+       low = temperature - MCELSIUS(1);
+       high = temperature + MCELSIUS(1);
 
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
                                rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
@@ -217,15 +217,11 @@ static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
                                rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
 
-       tsc->low = low;
-       tsc->high = high;
-
        return 0;
 }
 
 static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
        .get_temp       = rcar_gen3_thermal_get_temp,
-       .set_trips      = rcar_gen3_thermal_set_trips,
 };
 
 static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on)
@@ -246,9 +242,11 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
        for (i = 0; i < priv->num_tscs; i++) {
                status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
                rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
-               if (status)
+               if (status) {
+                       rcar_gen3_thermal_update_range(priv->tscs[i]);
                        thermal_zone_device_update(priv->tscs[i]->zone,
                                                   THERMAL_EVENT_UNSPECIFIED);
+               }
        }
 
        return IRQ_HANDLED;
@@ -325,6 +323,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
                .data = &rcar_gen3_ths_tj_1_m3_w,
        },
        {
+               .compatible = "renesas,r8a77961-thermal",
+               .data = &rcar_gen3_ths_tj_1_m3_w,
+       },
+       {
                .compatible = "renesas,r8a77965-thermal",
                .data = &rcar_gen3_ths_tj_1,
        },
@@ -446,14 +448,15 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
                        goto error_unregister;
 
                ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);
-               if (ret) {
+               if (ret)
                        goto error_unregister;
-               }
 
                ret = of_thermal_get_ntrips(tsc->zone);
                if (ret < 0)
                        goto error_unregister;
 
+               rcar_gen3_thermal_update_range(tsc);
+
                dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
        }
 
@@ -492,7 +495,7 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
                struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
 
                priv->thermal_init(tsc);
-               rcar_gen3_thermal_set_trips(tsc, tsc->low, tsc->high);
+               rcar_gen3_thermal_update_range(tsc);
        }
 
        rcar_thermal_irq_set(priv, true);
index 8f1aafa..e0c1f24 100644 (file)
@@ -95,7 +95,6 @@ struct rcar_thermal_priv {
        struct mutex lock;
        struct list_head list;
        int id;
-       u32 ctemp;
 };
 
 #define rcar_thermal_for_each_priv(pos, common)        \
@@ -201,7 +200,6 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
        struct device *dev = rcar_priv_to_dev(priv);
        int i;
        u32 ctemp, old, new;
-       int ret = -EINVAL;
 
        mutex_lock(&priv->lock);
 
@@ -247,37 +245,29 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
                                                   ((ctemp - 1) << 0)));
        }
 
-       dev_dbg(dev, "thermal%d  %d -> %d\n", priv->id, priv->ctemp, ctemp);
-
-       priv->ctemp = ctemp;
-       ret = 0;
 err_out_unlock:
        mutex_unlock(&priv->lock);
-       return ret;
+
+       return ctemp ? ctemp : -EINVAL;
 }
 
 static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
                                         int *temp)
 {
-       int tmp;
-       int ret;
-
-       ret = rcar_thermal_update_temp(priv);
-       if (ret < 0)
-               return ret;
+       int 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);
+       ctemp = rcar_thermal_update_temp(priv);
+       if (ctemp < 0)
+               return ctemp;
 
        /* Guaranteed operating range is -45C to 125C. */
 
-       *temp = tmp;
+       if (priv->chip->ctemp_bands == 1)
+               *temp = MCELSIUS((ctemp * 5) - 65);
+       else if (ctemp < 24)
+               *temp = MCELSIUS(((ctemp * 55) - 720) / 10);
+       else
+               *temp = MCELSIUS((ctemp * 5) - 60);
 
        return 0;
 }
@@ -387,28 +377,17 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
 static void rcar_thermal_work(struct work_struct *work)
 {
        struct rcar_thermal_priv *priv;
-       int cctemp, nctemp;
        int ret;
 
        priv = container_of(work, struct rcar_thermal_priv, work.work);
 
-       ret = rcar_thermal_get_current_temp(priv, &cctemp);
-       if (ret < 0)
-               return;
-
        ret = rcar_thermal_update_temp(priv);
        if (ret < 0)
                return;
 
        rcar_thermal_irq_enable(priv);
 
-       ret = rcar_thermal_get_current_temp(priv, &nctemp);
-       if (ret < 0)
-               return;
-
-       if (nctemp != cctemp)
-               thermal_zone_device_update(priv->zone,
-                                          THERMAL_EVENT_UNSPECIFIED);
+       thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED);
 }
 
 static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
@@ -521,8 +500,10 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                        res = platform_get_resource(pdev, IORESOURCE_MEM,
                                                    mres++);
                        common->base = devm_ioremap_resource(dev, res);
-                       if (IS_ERR(common->base))
-                               return PTR_ERR(common->base);
+                       if (IS_ERR(common->base)) {
+                               ret = PTR_ERR(common->base);
+                               goto error_unregister;
+                       }
 
                        idle = 0; /* polling delay is not needed */
                }
index fd4a178..e9a90bc 100644 (file)
@@ -1094,7 +1094,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
                                                    &exynos_sensor_ops);
        if (IS_ERR(data->tzd)) {
                ret = PTR_ERR(data->tzd);
-               dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to register sensor: %d\n",
+                               ret);
                goto err_sclk;
        }
 
diff --git a/drivers/thermal/sprd_thermal.c b/drivers/thermal/sprd_thermal.c
new file mode 100644 (file)
index 0000000..a340374
--- /dev/null
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 Spreadtrum Communications Inc.
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define SPRD_THM_CTL                   0x0
+#define SPRD_THM_INT_EN                        0x4
+#define SPRD_THM_INT_STS               0x8
+#define SPRD_THM_INT_RAW_STS           0xc
+#define SPRD_THM_DET_PERIOD            0x10
+#define SPRD_THM_INT_CLR               0x14
+#define SPRD_THM_INT_CLR_ST            0x18
+#define SPRD_THM_MON_PERIOD            0x4c
+#define SPRD_THM_MON_CTL               0x50
+#define SPRD_THM_INTERNAL_STS1         0x54
+#define SPRD_THM_RAW_READ_MSK          0x3ff
+
+#define SPRD_THM_OFFSET(id)            ((id) * 0x4)
+#define SPRD_THM_TEMP(id)              (SPRD_THM_OFFSET(id) + 0x5c)
+#define SPRD_THM_THRES(id)             (SPRD_THM_OFFSET(id) + 0x2c)
+
+#define SPRD_THM_SEN(id)               BIT((id) + 2)
+#define SPRD_THM_SEN_OVERHEAT_EN(id)   BIT((id) + 8)
+#define SPRD_THM_SEN_OVERHEAT_ALARM_EN(id)     BIT((id) + 0)
+
+/* bits definitions for register THM_CTL */
+#define SPRD_THM_SET_RDY_ST            BIT(13)
+#define SPRD_THM_SET_RDY               BIT(12)
+#define SPRD_THM_MON_EN                        BIT(1)
+#define SPRD_THM_EN                    BIT(0)
+
+/* bits definitions for register THM_INT_CTL */
+#define SPRD_THM_BIT_INT_EN            BIT(26)
+#define SPRD_THM_OVERHEAT_EN           BIT(25)
+#define SPRD_THM_OTP_TRIP_SHIFT                10
+
+/* bits definitions for register SPRD_THM_INTERNAL_STS1 */
+#define SPRD_THM_TEMPER_RDY            BIT(0)
+
+#define SPRD_THM_DET_PERIOD_DATA       0x800
+#define SPRD_THM_DET_PERIOD_MASK       GENMASK(19, 0)
+#define SPRD_THM_MON_MODE              0x7
+#define SPRD_THM_MON_MODE_MASK         GENMASK(3, 0)
+#define SPRD_THM_MON_PERIOD_DATA       0x10
+#define SPRD_THM_MON_PERIOD_MASK       GENMASK(15, 0)
+#define SPRD_THM_THRES_MASK            GENMASK(19, 0)
+#define SPRD_THM_INT_CLR_MASK          GENMASK(24, 0)
+
+/* thermal sensor calibration parameters */
+#define SPRD_THM_TEMP_LOW              -40000
+#define SPRD_THM_TEMP_HIGH             120000
+#define SPRD_THM_OTP_TEMP              120000
+#define SPRD_THM_HOT_TEMP              75000
+#define SPRD_THM_RAW_DATA_LOW          0
+#define SPRD_THM_RAW_DATA_HIGH         1000
+#define SPRD_THM_SEN_NUM               8
+#define SPRD_THM_DT_OFFSET             24
+#define SPRD_THM_RATION_OFFSET         17
+#define SPRD_THM_RATION_SIGN           16
+
+#define SPRD_THM_RDYST_POLLING_TIME    10
+#define SPRD_THM_RDYST_TIMEOUT         700
+#define SPRD_THM_TEMP_READY_POLL_TIME  10000
+#define SPRD_THM_TEMP_READY_TIMEOUT    600000
+#define SPRD_THM_MAX_SENSOR            8
+
+struct sprd_thermal_sensor {
+       struct thermal_zone_device *tzd;
+       struct sprd_thermal_data *data;
+       struct device *dev;
+       int cal_slope;
+       int cal_offset;
+       int id;
+};
+
+struct sprd_thermal_data {
+       const struct sprd_thm_variant_data *var_data;
+       struct sprd_thermal_sensor *sensor[SPRD_THM_MAX_SENSOR];
+       struct clk *clk;
+       void __iomem *base;
+       u32 ratio_off;
+       int ratio_sign;
+       int nr_sensors;
+};
+
+/*
+ * The conversion between ADC and temperature is based on linear relationship,
+ * and use idea_k to specify the slope and ideal_b to specify the offset.
+ *
+ * Since different Spreadtrum SoCs have different ideal_k and ideal_b,
+ * we should save ideal_k and ideal_b in the device data structure.
+ */
+struct sprd_thm_variant_data {
+       u32 ideal_k;
+       u32 ideal_b;
+};
+
+static const struct sprd_thm_variant_data ums512_data = {
+       .ideal_k = 262,
+       .ideal_b = 66400,
+};
+
+static inline void sprd_thm_update_bits(void __iomem *reg, u32 mask, u32 val)
+{
+       u32 tmp, orig;
+
+       orig = readl(reg);
+       tmp = orig & ~mask;
+       tmp |= val & mask;
+       writel(tmp, reg);
+}
+
+static int sprd_thm_cal_read(struct device_node *np, const char *cell_id,
+                            u32 *val)
+{
+       struct nvmem_cell *cell;
+       void *buf;
+       size_t len;
+
+       cell = of_nvmem_cell_get(np, cell_id);
+       if (IS_ERR(cell))
+               return PTR_ERR(cell);
+
+       buf = nvmem_cell_read(cell, &len);
+       nvmem_cell_put(cell);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
+
+       if (len > sizeof(u32)) {
+               kfree(buf);
+               return -EINVAL;
+       }
+
+       memcpy(val, buf, len);
+
+       kfree(buf);
+       return 0;
+}
+
+static int sprd_thm_sensor_calibration(struct device_node *np,
+                                      struct sprd_thermal_data *thm,
+                                      struct sprd_thermal_sensor *sen)
+{
+       int ret;
+       /*
+        * According to thermal datasheet, the default calibration offset is 64,
+        * and the default ratio is 1000.
+        */
+       int dt_offset = 64, ratio = 1000;
+
+       ret = sprd_thm_cal_read(np, "sen_delta_cal", &dt_offset);
+       if (ret)
+               return ret;
+
+       ratio += thm->ratio_sign * thm->ratio_off;
+
+       /*
+        * According to the ideal slope K and ideal offset B, combined with
+        * calibration value of thermal from efuse, then calibrate the real
+        * slope k and offset b:
+        * k_cal = (k * ratio) / 1000.
+        * b_cal = b + (dt_offset - 64) * 500.
+        */
+       sen->cal_slope = (thm->var_data->ideal_k * ratio) / 1000;
+       sen->cal_offset = thm->var_data->ideal_b + (dt_offset - 128) * 250;
+
+       return 0;
+}
+
+static int sprd_thm_rawdata_to_temp(struct sprd_thermal_sensor *sen,
+                                   u32 rawdata)
+{
+       clamp(rawdata, (u32)SPRD_THM_RAW_DATA_LOW, (u32)SPRD_THM_RAW_DATA_HIGH);
+
+       /*
+        * According to the thermal datasheet, the formula of converting
+        * adc value to the temperature value should be:
+        * T_final = k_cal * x - b_cal.
+        */
+       return sen->cal_slope * rawdata - sen->cal_offset;
+}
+
+static int sprd_thm_temp_to_rawdata(int temp, struct sprd_thermal_sensor *sen)
+{
+       u32 val;
+
+       clamp(temp, (int)SPRD_THM_TEMP_LOW, (int)SPRD_THM_TEMP_HIGH);
+
+       /*
+        * According to the thermal datasheet, the formula of converting
+        * adc value to the temperature value should be:
+        * T_final = k_cal * x - b_cal.
+        */
+       val = (temp + sen->cal_offset) / sen->cal_slope;
+
+       return clamp(val, val, (u32)(SPRD_THM_RAW_DATA_HIGH - 1));
+}
+
+static int sprd_thm_read_temp(void *devdata, int *temp)
+{
+       struct sprd_thermal_sensor *sen = devdata;
+       u32 data;
+
+       data = readl(sen->data->base + SPRD_THM_TEMP(sen->id)) &
+               SPRD_THM_RAW_READ_MSK;
+
+       *temp = sprd_thm_rawdata_to_temp(sen, data);
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops sprd_thm_ops = {
+       .get_temp = sprd_thm_read_temp,
+};
+
+static int sprd_thm_poll_ready_status(struct sprd_thermal_data *thm)
+{
+       u32 val;
+       int ret;
+
+       /*
+        * Wait for thermal ready status before configuring thermal parameters.
+        */
+       ret = readl_poll_timeout(thm->base + SPRD_THM_CTL, val,
+                                !(val & SPRD_THM_SET_RDY_ST),
+                                SPRD_THM_RDYST_POLLING_TIME,
+                                SPRD_THM_RDYST_TIMEOUT);
+       if (ret)
+               return ret;
+
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_MON_EN,
+                            SPRD_THM_MON_EN);
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SET_RDY,
+                            SPRD_THM_SET_RDY);
+       return 0;
+}
+
+static int sprd_thm_wait_temp_ready(struct sprd_thermal_data *thm)
+{
+       u32 val;
+
+       /* Wait for first temperature data ready before reading temperature */
+       return readl_poll_timeout(thm->base + SPRD_THM_INTERNAL_STS1, val,
+                                 !(val & SPRD_THM_TEMPER_RDY),
+                                 SPRD_THM_TEMP_READY_POLL_TIME,
+                                 SPRD_THM_TEMP_READY_TIMEOUT);
+}
+
+static int sprd_thm_set_ready(struct sprd_thermal_data *thm)
+{
+       int ret;
+
+       ret = sprd_thm_poll_ready_status(thm);
+       if (ret)
+               return ret;
+
+       /*
+        * Clear interrupt status, enable thermal interrupt and enable thermal.
+        *
+        * The SPRD thermal controller integrates a hardware interrupt signal,
+        * which means if the temperature is overheat, it will generate an
+        * interrupt and notify the event to PMIC automatically to shutdown the
+        * system. So here we should enable the interrupt bits, though we have
+        * not registered an irq handler.
+        */
+       writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR);
+       sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN,
+                            SPRD_THM_BIT_INT_EN, SPRD_THM_BIT_INT_EN);
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
+                            SPRD_THM_EN, SPRD_THM_EN);
+       return 0;
+}
+
+static void sprd_thm_sensor_init(struct sprd_thermal_data *thm,
+                                struct sprd_thermal_sensor *sen)
+{
+       u32 otp_rawdata, hot_rawdata;
+
+       otp_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_OTP_TEMP, sen);
+       hot_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_HOT_TEMP, sen);
+
+       /* Enable the sensor' overheat temperature protection interrupt */
+       sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN,
+                            SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id),
+                            SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id));
+
+       /* Set the sensor' overheat and hot threshold temperature */
+       sprd_thm_update_bits(thm->base + SPRD_THM_THRES(sen->id),
+                            SPRD_THM_THRES_MASK,
+                            (otp_rawdata << SPRD_THM_OTP_TRIP_SHIFT) |
+                            hot_rawdata);
+
+       /* Enable the corresponding sensor */
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SEN(sen->id),
+                            SPRD_THM_SEN(sen->id));
+}
+
+static void sprd_thm_para_config(struct sprd_thermal_data *thm)
+{
+       /* Set the period of two valid temperature detection action */
+       sprd_thm_update_bits(thm->base + SPRD_THM_DET_PERIOD,
+                            SPRD_THM_DET_PERIOD_MASK, SPRD_THM_DET_PERIOD);
+
+       /* Set the sensors' monitor mode */
+       sprd_thm_update_bits(thm->base + SPRD_THM_MON_CTL,
+                            SPRD_THM_MON_MODE_MASK, SPRD_THM_MON_MODE);
+
+       /* Set the sensors' monitor period */
+       sprd_thm_update_bits(thm->base + SPRD_THM_MON_PERIOD,
+                            SPRD_THM_MON_PERIOD_MASK, SPRD_THM_MON_PERIOD);
+}
+
+static void sprd_thm_toggle_sensor(struct sprd_thermal_sensor *sen, bool on)
+{
+       struct thermal_zone_device *tzd = sen->tzd;
+
+       tzd->ops->set_mode(tzd,
+               on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
+}
+
+static int sprd_thm_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *sen_child;
+       struct sprd_thermal_data *thm;
+       struct sprd_thermal_sensor *sen;
+       const struct sprd_thm_variant_data *pdata;
+       int ret, i;
+       u32 val;
+
+       pdata = of_device_get_match_data(&pdev->dev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "No matching driver data found\n");
+               return -EINVAL;
+       }
+
+       thm = devm_kzalloc(&pdev->dev, sizeof(*thm), GFP_KERNEL);
+       if (!thm)
+               return -ENOMEM;
+
+       thm->var_data = pdata;
+       thm->base = devm_platform_ioremap_resource(pdev, 0);
+       if (!thm->base)
+               return -ENOMEM;
+
+       thm->nr_sensors = of_get_child_count(np);
+       if (thm->nr_sensors == 0 || thm->nr_sensors > SPRD_THM_MAX_SENSOR) {
+               dev_err(&pdev->dev, "incorrect sensor count\n");
+               return -EINVAL;
+       }
+
+       thm->clk = devm_clk_get(&pdev->dev, "enable");
+       if (IS_ERR(thm->clk)) {
+               dev_err(&pdev->dev, "failed to get enable clock\n");
+               return PTR_ERR(thm->clk);
+       }
+
+       ret = clk_prepare_enable(thm->clk);
+       if (ret)
+               return ret;
+
+       sprd_thm_para_config(thm);
+
+       ret = sprd_thm_cal_read(np, "thm_sign_cal", &val);
+       if (ret)
+               goto disable_clk;
+
+       if (val > 0)
+               thm->ratio_sign = -1;
+       else
+               thm->ratio_sign = 1;
+
+       ret = sprd_thm_cal_read(np, "thm_ratio_cal", &thm->ratio_off);
+       if (ret)
+               goto disable_clk;
+
+       for_each_child_of_node(np, sen_child) {
+               sen = devm_kzalloc(&pdev->dev, sizeof(*sen), GFP_KERNEL);
+               if (!sen) {
+                       ret = -ENOMEM;
+                       goto disable_clk;
+               }
+
+               sen->data = thm;
+               sen->dev = &pdev->dev;
+
+               ret = of_property_read_u32(sen_child, "reg", &sen->id);
+               if (ret) {
+                       dev_err(&pdev->dev, "get sensor reg failed");
+                       goto disable_clk;
+               }
+
+               ret = sprd_thm_sensor_calibration(sen_child, thm, sen);
+               if (ret) {
+                       dev_err(&pdev->dev, "efuse cal analysis failed");
+                       goto disable_clk;
+               }
+
+               sprd_thm_sensor_init(thm, sen);
+
+               sen->tzd = devm_thermal_zone_of_sensor_register(sen->dev,
+                                                               sen->id,
+                                                               sen,
+                                                               &sprd_thm_ops);
+               if (IS_ERR(sen->tzd)) {
+                       dev_err(&pdev->dev, "register thermal zone failed %d\n",
+                               sen->id);
+                       ret = PTR_ERR(sen->tzd);
+                       goto disable_clk;
+               }
+
+               thm->sensor[sen->id] = sen;
+       }
+
+       ret = sprd_thm_set_ready(thm);
+       if (ret)
+               goto disable_clk;
+
+       ret = sprd_thm_wait_temp_ready(thm);
+       if (ret)
+               goto disable_clk;
+
+       for (i = 0; i < thm->nr_sensors; i++)
+               sprd_thm_toggle_sensor(thm->sensor[i], true);
+
+       platform_set_drvdata(pdev, thm);
+       return 0;
+
+disable_clk:
+       clk_disable_unprepare(thm->clk);
+       return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void sprd_thm_hw_suspend(struct sprd_thermal_data *thm)
+{
+       int i;
+
+       for (i = 0; i < thm->nr_sensors; i++) {
+               sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
+                                    SPRD_THM_SEN(thm->sensor[i]->id), 0);
+       }
+
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
+                            SPRD_THM_EN, 0x0);
+}
+
+static int sprd_thm_suspend(struct device *dev)
+{
+       struct sprd_thermal_data *thm = dev_get_drvdata(dev);
+       int i;
+
+       for (i = 0; i < thm->nr_sensors; i++)
+               sprd_thm_toggle_sensor(thm->sensor[i], false);
+
+       sprd_thm_hw_suspend(thm);
+       clk_disable_unprepare(thm->clk);
+
+       return 0;
+}
+
+static int sprd_thm_hw_resume(struct sprd_thermal_data *thm)
+{
+       int ret, i;
+
+       for (i = 0; i < thm->nr_sensors; i++) {
+               sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
+                                    SPRD_THM_SEN(thm->sensor[i]->id),
+                                    SPRD_THM_SEN(thm->sensor[i]->id));
+       }
+
+       ret = sprd_thm_poll_ready_status(thm);
+       if (ret)
+               return ret;
+
+       writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR);
+       sprd_thm_update_bits(thm->base + SPRD_THM_CTL,
+                            SPRD_THM_EN, SPRD_THM_EN);
+       return sprd_thm_wait_temp_ready(thm);
+}
+
+static int sprd_thm_resume(struct device *dev)
+{
+       struct sprd_thermal_data *thm = dev_get_drvdata(dev);
+       int ret, i;
+
+       ret = clk_prepare_enable(thm->clk);
+       if (ret)
+               return ret;
+
+       ret = sprd_thm_hw_resume(thm);
+       if (ret)
+               goto disable_clk;
+
+       for (i = 0; i < thm->nr_sensors; i++)
+               sprd_thm_toggle_sensor(thm->sensor[i], true);
+
+       return 0;
+
+disable_clk:
+       clk_disable_unprepare(thm->clk);
+       return ret;
+}
+#endif
+
+static int sprd_thm_remove(struct platform_device *pdev)
+{
+       struct sprd_thermal_data *thm = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < thm->nr_sensors; i++) {
+               sprd_thm_toggle_sensor(thm->sensor[i], false);
+               devm_thermal_zone_of_sensor_unregister(&pdev->dev,
+                                                      thm->sensor[i]->tzd);
+       }
+
+       clk_disable_unprepare(thm->clk);
+       return 0;
+}
+
+static const struct of_device_id sprd_thermal_of_match[] = {
+       { .compatible = "sprd,ums512-thermal", .data = &ums512_data },
+       { },
+};
+
+static const struct dev_pm_ops sprd_thermal_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(sprd_thm_suspend, sprd_thm_resume)
+};
+
+static struct platform_driver sprd_thermal_driver = {
+       .probe = sprd_thm_probe,
+       .remove = sprd_thm_remove,
+       .driver = {
+               .name = "sprd-thermal",
+               .pm = &sprd_thermal_pm_ops,
+               .of_match_table = sprd_thermal_of_match,
+       },
+};
+
+module_platform_driver(sprd_thermal_driver);
+
+MODULE_AUTHOR("Freeman Liu <freeman.liu@unisoc.com>");
+MODULE_DESCRIPTION("Spreadtrum thermal driver");
+MODULE_LICENSE("GPL v2");
index ad9e3bf..9314e3d 100644 (file)
@@ -478,7 +478,8 @@ static int stm_thermal_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume);
+static SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops,
+                        stm_thermal_suspend, stm_thermal_resume);
 
 static const struct thermal_zone_of_device_ops stm_tz_ops = {
        .get_temp       = stm_thermal_get_temp,
index 2fa78f7..263b042 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/clk.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/types.h>
@@ -24,7 +24,6 @@
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
 #include <linux/of_irq.h>
-#include <linux/of_gpio.h>
 #include <linux/io.h>
 
 #include "ti-bandgap.h"
@@ -743,27 +742,13 @@ exit:
 static int ti_bandgap_tshut_init(struct ti_bandgap *bgp,
                                 struct platform_device *pdev)
 {
-       int gpio_nr = bgp->tshut_gpio;
        int status;
 
-       /* Request for gpio_86 line */
-       status = gpio_request(gpio_nr, "tshut");
-       if (status < 0) {
-               dev_err(bgp->dev, "Could not request for TSHUT GPIO:%i\n", 86);
-               return status;
-       }
-       status = gpio_direction_input(gpio_nr);
-       if (status) {
-               dev_err(bgp->dev, "Cannot set input TSHUT GPIO %d\n", gpio_nr);
-               return status;
-       }
-
-       status = request_irq(gpio_to_irq(gpio_nr), ti_bandgap_tshut_irq_handler,
+       status = request_irq(gpiod_to_irq(bgp->tshut_gpiod),
+                            ti_bandgap_tshut_irq_handler,
                             IRQF_TRIGGER_RISING, "tshut", NULL);
-       if (status) {
-               gpio_free(gpio_nr);
+       if (status)
                dev_err(bgp->dev, "request irq failed for TSHUT");
-       }
 
        return 0;
 }
@@ -860,11 +845,10 @@ static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev)
        } while (res);
 
        if (TI_BANDGAP_HAS(bgp, TSHUT)) {
-               bgp->tshut_gpio = of_get_gpio(node, 0);
-               if (!gpio_is_valid(bgp->tshut_gpio)) {
-                       dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
-                               bgp->tshut_gpio);
-                       return ERR_PTR(-EINVAL);
+               bgp->tshut_gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
+               if (IS_ERR(bgp->tshut_gpiod)) {
+                       dev_err(&pdev->dev, "invalid gpio for tshut\n");
+                       return ERR_CAST(bgp->tshut_gpiod);
                }
        }
 
@@ -1046,10 +1030,8 @@ put_clks:
 put_fclock:
        clk_put(bgp->fclock);
 free_irqs:
-       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
-               free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
-               gpio_free(bgp->tshut_gpio);
-       }
+       if (TI_BANDGAP_HAS(bgp, TSHUT))
+               free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
 
        return ret;
 }
@@ -1079,10 +1061,8 @@ int ti_bandgap_remove(struct platform_device *pdev)
        if (TI_BANDGAP_HAS(bgp, TALERT))
                free_irq(bgp->irq, bgp);
 
-       if (TI_BANDGAP_HAS(bgp, TSHUT)) {
-               free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
-               gpio_free(bgp->tshut_gpio);
-       }
+       if (TI_BANDGAP_HAS(bgp, TSHUT))
+               free_irq(gpiod_to_irq(bgp->tshut_gpiod), NULL);
 
        return 0;
 }
index bb9b0f7..fce4657 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/types.h>
 #include <linux/err.h>
 
+struct gpio_desc;
+
 /**
  * DOC: bandgap driver data structure
  * ==================================
@@ -199,7 +201,7 @@ struct ti_bandgap {
        struct clk                      *div_clk;
        spinlock_t                      lock; /* shields this struct */
        int                             irq;
-       int                             tshut_gpio;
+       struct gpio_desc                *tshut_gpiod;
        u32                             clk_rate;
 };
 
index 4635f95..79a6e37 100644 (file)
@@ -75,7 +75,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *dfc);
 
 #else /* !CONFIG_DEVFREQ_THERMAL */
 
-struct thermal_cooling_device *
+static inline struct thermal_cooling_device *
 of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
                                  struct devfreq_cooling_power *dfc_power)
 {
index 126913c..c91b1e3 100644 (file)
@@ -364,6 +364,9 @@ struct thermal_trip {
 
 /* Function declarations */
 #ifdef CONFIG_THERMAL_OF
+int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
+                                 struct device_node *sensor_np,
+                                 u32 *id);
 struct thermal_zone_device *
 thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
                                const struct thermal_zone_of_device_ops *ops);
@@ -375,6 +378,13 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
 void devm_thermal_zone_of_sensor_unregister(struct device *dev,
                                            struct thermal_zone_device *tz);
 #else
+
+static inline int thermal_zone_of_get_sensor_id(struct device_node *tz_np,
+                                        struct device_node *sensor_np,
+                                        u32 *id)
+{
+       return -ENOENT;
+}
 static inline struct thermal_zone_device *
 thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
                                const struct thermal_zone_of_device_ops *ops)
index 83ec6e4..7eb3216 100644 (file)
@@ -46,7 +46,7 @@ static void   start_daemon_mode(void);
 
 pthread_t event_tid;
 pthread_mutex_t input_lock;
-void usage()
+void usage(void)
 {
        printf("Usage: tmon [OPTION...]\n");
        printf("  -c, --control         cooling device in control\n");
@@ -62,7 +62,7 @@ void usage()
        exit(0);
 }
 
-void version()
+void version(void)
 {
        printf("TMON version %s\n", VERSION);
        exit(EXIT_SUCCESS);
@@ -70,7 +70,6 @@ void version()
 
 static void tmon_cleanup(void)
 {
-
        syslog(LOG_INFO, "TMON exit cleanup\n");
        fflush(stdout);
        refresh();
@@ -96,7 +95,6 @@ static void tmon_cleanup(void)
        exit(1);
 }
 
-
 static void tmon_sig_handler(int sig)
 {
        syslog(LOG_INFO, "TMON caught signal %d\n", sig);
@@ -120,7 +118,6 @@ static void tmon_sig_handler(int sig)
        tmon_exit = true;
 }
 
-
 static void start_syslog(void)
 {
        if (debug_on)
@@ -167,7 +164,6 @@ static void prepare_logging(void)
                return;
        }
 
-
        fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG -------------\n");
        for (i = 0; i < ptdata.nr_tz_sensor; i++) {
                char binding_str[33]; /* size of long + 1 */
@@ -175,7 +171,7 @@ static void prepare_logging(void)
 
                memset(binding_str, 0, sizeof(binding_str));
                for (j = 0; j < 32; j++)
-                       binding_str[j] = (ptdata.tzi[i].cdev_binding & 1<<j) ?
+                       binding_str[j] = (ptdata.tzi[i].cdev_binding & (1 << j)) ?
                                '1' : '0';
 
                fprintf(tmon_log, "#thermal zone %s%02d cdevs binding: %32s\n",
@@ -187,7 +183,6 @@ static void prepare_logging(void)
                                trip_type_name[ptdata.tzi[i].tp[j].type],
                                ptdata.tzi[i].tp[j].temp);
                }
-
        }
 
        for (i = 0; i < ptdata.nr_cooling_dev; i++)
@@ -219,7 +214,6 @@ static struct option opts[] = {
        { 0, 0, NULL, 0 }
 };
 
-
 int main(int argc, char **argv)
 {
        int err = 0;
@@ -283,7 +277,7 @@ int main(int argc, char **argv)
        if (signal(SIGINT, tmon_sig_handler) == SIG_ERR)
                syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
        if (signal(SIGTERM, tmon_sig_handler) == SIG_ERR)
-               syslog(LOG_DEBUG, "Cannot handle SIGINT\n");
+               syslog(LOG_DEBUG, "Cannot handle SIGTERM\n");
 
        if (probe_thermal_sysfs()) {
                pthread_mutex_destroy(&input_lock);
@@ -328,8 +322,7 @@ int main(int argc, char **argv)
                        show_cooling_device();
                }
                time_elapsed += ticktime;
-               controller_handler(trec[0].temp[target_tz_index] / 1000,
-                               &yk);
+               controller_handler(trec[0].temp[target_tz_index] / 1000, &yk);
                trec[0].pid_out_pct = yk;
                if (!dialogue_on)
                        show_control_w();
@@ -340,14 +333,15 @@ int main(int argc, char **argv)
        return 0;
 }
 
-static void start_daemon_mode()
+static void start_daemon_mode(void)
 {
        daemon_mode = 1;
        /* fork */
        pid_t   sid, pid = fork();
-       if (pid < 0) {
+
+       if (pid < 0)
                exit(EXIT_FAILURE);
-       else if (pid > 0)
+       else if (pid > 0)
                /* kill parent */
                exit(EXIT_SUCCESS);
 
@@ -366,11 +360,9 @@ static void start_daemon_mode()
        if ((chdir("/")) < 0)
                exit(EXIT_FAILURE);
 
-
        sleep(10);
 
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
-
 }