Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 26 Oct 2018 19:04:29 +0000 (12:04 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 26 Oct 2018 19:04:29 +0000 (12:04 -0700)
Pull thermal SoC updates from Eduardo Valentin:
 "Several new things coming up. Specifics:

   - Rework of tsens and hisi thermal drivers

   - OF-thermal now allows sharing multiple cooling devices on maps

   - Added support for r8a7744 and R8A77970 on rcar thermal driver

   - Added support for r8a774a1 on rcar_gen3 thermal driver

   - New thermal driver stm32

   - Fixes on multiple thermal drivers: of-thermal, imx, qoriq, armada,
     qcom-spmi, rcar, da9062/61"

* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (41 commits)
  thermal: da9062/61: Prevent hardware access during system suspend
  thermal: rcar_thermal: Prevent doing work after unbind
  thermal: rcar_thermal: Prevent hardware access during system suspend
  thermal: rcar_gen3_thermal: add R8A77980 support
  dt-bindings: thermal: rcar-gen3-thermal: document R8A77980 bindings
  thermal: add stm32 thermal driver
  dt-bindings: stm32-thermal: add binding documentation
  thermal: rcar_thermal: add R8A77970 support
  dt-bindings: thermal: rcar-thermal: document R8A77970 bindings
  thermal: rcar_thermal: fix duplicate IRQ request
  dt-bindings: thermal: rcar: Add device tree support for r8a7744
  thermal/drivers/hisi: Add the dual clusters sensors for hi3660
  thermal/drivers/hisi: Add more sensors channel
  thermal/drivers/hisi: Remove pointless irq field
  thermal/drivers/hisi: Use platform_get_irq_byname
  thermal/drivers/hisi: Replace macro name with relevant sensor location
  thermal/drivers/hisi: Add multiple sensors support
  thermal/drivers/hisi: Prepare to support multiple sensors
  thermal/drivers/hisi: Factor out the probe functions
  thermal/drivers/hisi: Set the thermal zone private data to the sensor pointer
  ...

27 files changed:
Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
Documentation/devicetree/bindings/thermal/qoriq-thermal.txt
Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt
Documentation/devicetree/bindings/thermal/rcar-thermal.txt
Documentation/devicetree/bindings/thermal/stm32-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal.txt
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/armada_thermal.c
drivers/thermal/da9062-thermal.c
drivers/thermal/hisi_thermal.c
drivers/thermal/imx_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/qcom-spmi-temp-alarm.c
drivers/thermal/qcom/tsens-8916.c
drivers/thermal/qcom/tsens-8960.c
drivers/thermal/qcom/tsens-8974.c
drivers/thermal/qcom/tsens-common.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/st/Kconfig
drivers/thermal/st/Makefile
drivers/thermal/st/stm_thermal.c [new file with mode: 0644]

index 290ec06fa33ab12314b7ad8e2b2e50f870af9b87..0273a92a2a849bb6bcc8b97f4291124b0eec2044 100644 (file)
@@ -6,8 +6,7 @@ interrupt signal and status register to identify high PMIC die temperature.
 
 Required properties:
 - compatible:      Should contain "qcom,spmi-temp-alarm".
-- reg:             Specifies the SPMI address and length of the controller's
-                   registers.
+- reg:             Specifies the SPMI address.
 - interrupts:      PMIC temperature alarm interrupt.
 - #thermal-sensor-cells: Should be 0. See thermal.txt for a description.
 
@@ -20,7 +19,7 @@ Example:
 
        pm8941_temp: thermal-alarm@2400 {
                compatible = "qcom,spmi-temp-alarm";
-               reg = <0x2400 0x100>;
+               reg = <0x2400>;
                interrupts = <0 0x24 0 IRQ_TYPE_EDGE_RISING>;
                #thermal-sensor-cells = <0>;
 
@@ -36,19 +35,14 @@ Example:
                        thermal-sensors = <&pm8941_temp>;
 
                        trips {
-                               passive {
-                                       temperature = <1050000>;
+                               stage1 {
+                                       temperature = <105000>;
                                        hysteresis = <2000>;
                                        type = "passive";
                                };
-                               alert {
+                               stage2 {
                                        temperature = <125000>;
                                        hysteresis = <2000>;
-                                       type = "hot";
-                               };
-                               crit {
-                                       temperature = <145000>;
-                                       hysteresis = <2000>;
                                        type = "critical";
                                };
                        };
index 20ca4ef9d77609a0bc0432256c70694e0ac1cbac..04cbb90a5d3ee51788cb63c5e668b39eea274795 100644 (file)
@@ -1,9 +1,9 @@
 * Thermal Monitoring Unit (TMU) on Freescale QorIQ SoCs
 
 Required properties:
-- compatible : Must include "fsl,qoriq-tmu". The version of the device is
-       determined by the TMU IP Block Revision Register (IPBRR0) at
-       offset 0x0BF8.
+- compatible : Must include "fsl,qoriq-tmu" or "fsl,imx8mq-tmu". The
+       version of the device is determined by the TMU IP Block Revision
+       Register (IPBRR0) at offset 0x0BF8.
        Table of correspondences between IPBRR0 values and example  chips:
                Value           Device
                ----------      -----
index cfa154bb0fa7c7ec5977de4c38c92a7ebd596e7e..ad9a435afef446f68327e931a0431935e29a3129 100644 (file)
@@ -7,9 +7,11 @@ inside the LSI.
 Required properties:
 - compatible           : "renesas,<soctype>-thermal",
                          Examples with soctypes are:
+                           - "renesas,r8a774a1-thermal" (RZ/G2M)
                            - "renesas,r8a7795-thermal" (R-Car H3)
                            - "renesas,r8a7796-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
                          needs one address range. Sorting must be done in
                          increasing order according to datasheet, i.e.
@@ -19,7 +21,8 @@ Required properties:
 
 Optional properties:
 
-- interrupts           : interrupts routed to the TSC (3 for H3, M3-W and M3-N)
+- interrupts           : interrupts routed to the TSC (3 for H3, M3-W, M3-N,
+                         and V3H)
 - power-domain         : Must contain a reference to the power domain. This
                          property is mandatory if the thermal sensor instance
                          is part of a controllable power domain.
index 67c563f1b4c42131157e8428300c5a12d39bbc2f..73e1613d2cb0026749d1dc6c9d9d44eebf40dfa2 100644 (file)
@@ -4,15 +4,17 @@ Required properties:
 - compatible           : "renesas,thermal-<soctype>",
                           "renesas,rcar-gen2-thermal" (with thermal-zone) or
                           "renesas,rcar-thermal" (without thermal-zone) as
-                           fallback except R-Car D3.
+                           fallback except R-Car V3M/D3.
                          Examples with soctypes are:
                            - "renesas,thermal-r8a73a4" (R-Mobile APE6)
                            - "renesas,thermal-r8a7743" (RZ/G1M)
+                           - "renesas,thermal-r8a7744" (RZ/G1N)
                            - "renesas,thermal-r8a7779" (R-Car H1)
                            - "renesas,thermal-r8a7790" (R-Car H2)
                            - "renesas,thermal-r8a7791" (R-Car M2-W)
                            - "renesas,thermal-r8a7792" (R-Car V2H)
                            - "renesas,thermal-r8a7793" (R-Car M2-N)
+                           - "renesas,thermal-r8a77970" (R-Car V3M)
                            - "renesas,thermal-r8a77995" (R-Car D3)
 - reg                  : Address range of the thermal registers.
                          The 1st reg will be recognized as common register
@@ -21,7 +23,7 @@ Required properties:
 Option properties:
 
 - interrupts           : If present should contain 3 interrupts for
-                          R-Car D3 or 1 interrupt otherwise.
+                          R-Car V3M/D3 or 1 interrupt otherwise.
 
 Example (non interrupt support):
 
diff --git a/Documentation/devicetree/bindings/thermal/stm32-thermal.txt b/Documentation/devicetree/bindings/thermal/stm32-thermal.txt
new file mode 100644 (file)
index 0000000..8c0d5a4
--- /dev/null
@@ -0,0 +1,61 @@
+Binding for Thermal Sensor for STMicroelectronics STM32 series of SoCs.
+
+On STM32 SoCs, the Digital Temperature Sensor (DTS) is in charge of managing an
+analog block which delivers a frequency depending on the internal SoC's
+temperature. By using a reference frequency, DTS is able to provide a sample
+number which can be translated into a temperature by the user.
+
+DTS provides interrupt notification mechanism by threshold. This mechanism
+offers two temperature trip points: passive and critical. The first is intended
+for passive cooling notification while the second is used for over-temperature
+reset.
+
+Required parameters:
+-------------------
+
+compatible:    Should be "st,stm32-thermal"
+reg:           This should be the physical base address and length of the
+               sensor's registers.
+clocks:        Phandle of the clock used by the thermal sensor.
+                 See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+clock-names:   Should be "pclk" for register access clock and reference clock.
+                 See: Documentation/devicetree/bindings/resource-names.txt
+#thermal-sensor-cells: Should be 0. See ./thermal.txt for a description.
+interrupts:    Standard way to define interrupt number.
+
+Example:
+
+       thermal-zones {
+               cpu_thermal: cpu-thermal {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors = <&thermal>;
+
+                       trips {
+                               cpu_alert1: cpu-alert1 {
+                                       temperature = <85000>;
+                                       hysteresis = <0>;
+                                       type = "passive";
+                               };
+
+                               cpu-crit: cpu-crit {
+                                       temperature = <120000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                       };
+               };
+       };
+
+       thermal: thermal@50028000 {
+               compatible = "st,stm32-thermal";
+               reg = <0x50028000 0x100>;
+               clocks = <&rcc TMPSENS>;
+               clock-names = "pclk";
+               #thermal-sensor-cells = <0>;
+               interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+       };
index eb7ee91556a5cf25e983f23625b58345706ba8eb..ca14ba959e0d0cc6bdf1a3a1b12510af738262ba 100644 (file)
@@ -152,7 +152,7 @@ Optional property:
   Elem size: one cell  the sensors listed in the thermal-sensors property.
   Elem type: signed    Coefficients defaults to 1, in case this property
                        is not specified. A simple linear polynomial is used:
-                       Z = c0 * x0 + c1 + x1 + ... + c(n-1) * x(n-1) + cn.
+                       Z = c0 * x0 + c1 * x1 + ... + c(n-1) * x(n-1) + cn.
 
                        The coefficients are ordered and they match with sensors
                        by means of sensor ID. Additional coefficients are
index 0e69edc77d184d436f2220caf90895438e36fc70..5422523c03f8638746b8a2b3a7fece4809a28f9c 100644 (file)
@@ -432,7 +432,7 @@ source "drivers/thermal/samsung/Kconfig"
 endmenu
 
 menu "STMicroelectronics thermal drivers"
-depends on ARCH_STI && OF
+depends on (ARCH_STI || ARCH_STM32) && OF
 source "drivers/thermal/st/Kconfig"
 endmenu
 
index 610344eb3e03378c69d56f60178ff8222f5d0435..82bb50dc6423026741d5c22312e52d9b9e4c3b14 100644 (file)
@@ -53,7 +53,7 @@ obj-$(CONFIG_TI_SOC_THERMAL)  += ti-soc-thermal/
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
 obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
 obj-$(CONFIG_INTEL_PCH_THERMAL)        += intel_pch_thermal.o
-obj-$(CONFIG_ST_THERMAL)       += st/
+obj-y                          += st/
 obj-$(CONFIG_QCOM_TSENS)       += qcom/
 obj-y                          += tegra/
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
index 2c2f6d93034ec9764f3596d18cd3891d0e3565b6..92f67d40f2e96a876083a71b6287e2bf159243c6 100644 (file)
@@ -526,8 +526,8 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
 
        /* First memory region points towards the status register */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (IS_ERR(res))
-               return PTR_ERR(res);
+       if (!res)
+               return -EIO;
 
        /*
         * Edit the resource start address and length to map over all the
index dd8dd947b7f0737c8a1228c1a7ce89ea915e44d1..01b0cb99445778513b67506a5cd7654909f2988c 100644 (file)
@@ -106,7 +106,7 @@ static void da9062_thermal_poll_on(struct work_struct *work)
                                           THERMAL_EVENT_UNSPECIFIED);
 
                delay = msecs_to_jiffies(thermal->zone->passive_delay);
-               schedule_delayed_work(&thermal->work, delay);
+               queue_delayed_work(system_freezable_wq, &thermal->work, delay);
                return;
        }
 
@@ -125,7 +125,7 @@ static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
        struct da9062_thermal *thermal = data;
 
        disable_irq_nosync(thermal->irq);
-       schedule_delayed_work(&thermal->work, 0);
+       queue_delayed_work(system_freezable_wq, &thermal->work, 0);
 
        return IRQ_HANDLED;
 }
index 761d0559c268b6068770a44ac91e490637a038fc..c4111a98f1a79570c9eb98e7d0fcf9dbcbf2ec67 100644 (file)
 #define HI3660_TEMP_STEP               (205)
 #define HI3660_TEMP_LAG                        (4000)
 
-#define HI6220_DEFAULT_SENSOR          2
-#define HI3660_DEFAULT_SENSOR          1
+#define HI6220_CLUSTER0_SENSOR         2
+#define HI6220_CLUSTER1_SENSOR         1
+
+#define HI3660_LITTLE_SENSOR           0
+#define HI3660_BIG_SENSOR              1
+#define HI3660_G3D_SENSOR              2
+#define HI3660_MODEM_SENSOR            3
+
+struct hisi_thermal_data;
 
 struct hisi_thermal_sensor {
+       struct hisi_thermal_data *data;
        struct thermal_zone_device *tzd;
+       const char *irq_name;
        uint32_t id;
        uint32_t thres_temp;
 };
 
+struct hisi_thermal_ops {
+       int (*get_temp)(struct hisi_thermal_sensor *sensor);
+       int (*enable_sensor)(struct hisi_thermal_sensor *sensor);
+       int (*disable_sensor)(struct hisi_thermal_sensor *sensor);
+       int (*irq_handler)(struct hisi_thermal_sensor *sensor);
+       int (*probe)(struct hisi_thermal_data *data);
+};
+
 struct hisi_thermal_data {
-       int (*get_temp)(struct hisi_thermal_data *data);
-       int (*enable_sensor)(struct hisi_thermal_data *data);
-       int (*disable_sensor)(struct hisi_thermal_data *data);
-       int (*irq_handler)(struct hisi_thermal_data *data);
+       const struct hisi_thermal_ops *ops;
+       struct hisi_thermal_sensor *sensor;
        struct platform_device *pdev;
        struct clk *clk;
-       struct hisi_thermal_sensor sensor;
        void __iomem *regs;
-       int irq;
+       int nr_sensors;
 };
 
 /*
@@ -266,30 +280,40 @@ static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value)
               (value << 4), addr + HI6220_TEMP0_CFG);
 }
 
-static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data)
+static int hi6220_thermal_irq_handler(struct hisi_thermal_sensor *sensor)
 {
+       struct hisi_thermal_data *data = sensor->data;
+
        hi6220_thermal_alarm_clear(data->regs, 1);
        return 0;
 }
 
-static int hi3660_thermal_irq_handler(struct hisi_thermal_data *data)
+static int hi3660_thermal_irq_handler(struct hisi_thermal_sensor *sensor)
 {
-       hi3660_thermal_alarm_clear(data->regs, data->sensor.id, 1);
+       struct hisi_thermal_data *data = sensor->data;
+
+       hi3660_thermal_alarm_clear(data->regs, sensor->id, 1);
        return 0;
 }
 
-static int hi6220_thermal_get_temp(struct hisi_thermal_data *data)
+static int hi6220_thermal_get_temp(struct hisi_thermal_sensor *sensor)
 {
+       struct hisi_thermal_data *data = sensor->data;
+
        return hi6220_thermal_get_temperature(data->regs);
 }
 
-static int hi3660_thermal_get_temp(struct hisi_thermal_data *data)
+static int hi3660_thermal_get_temp(struct hisi_thermal_sensor *sensor)
 {
-       return hi3660_thermal_get_temperature(data->regs, data->sensor.id);
+       struct hisi_thermal_data *data = sensor->data;
+
+       return hi3660_thermal_get_temperature(data->regs, sensor->id);
 }
 
-static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data)
+static int hi6220_thermal_disable_sensor(struct hisi_thermal_sensor *sensor)
 {
+       struct hisi_thermal_data *data = sensor->data;
+
        /* disable sensor module */
        hi6220_thermal_enable(data->regs, 0);
        hi6220_thermal_alarm_enable(data->regs, 0);
@@ -300,16 +324,18 @@ static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data)
        return 0;
 }
 
-static int hi3660_thermal_disable_sensor(struct hisi_thermal_data *data)
+static int hi3660_thermal_disable_sensor(struct hisi_thermal_sensor *sensor)
 {
+       struct hisi_thermal_data *data = sensor->data;
+
        /* disable sensor module */
-       hi3660_thermal_alarm_enable(data->regs, data->sensor.id, 0);
+       hi3660_thermal_alarm_enable(data->regs, sensor->id, 0);
        return 0;
 }
 
-static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data)
+static int hi6220_thermal_enable_sensor(struct hisi_thermal_sensor *sensor)
 {
-       struct hisi_thermal_sensor *sensor = &data->sensor;
+       struct hisi_thermal_data *data = sensor->data;
        int ret;
 
        /* enable clock for tsensor */
@@ -345,10 +371,10 @@ static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data)
        return 0;
 }
 
-static int hi3660_thermal_enable_sensor(struct hisi_thermal_data *data)
+static int hi3660_thermal_enable_sensor(struct hisi_thermal_sensor *sensor)
 {
        unsigned int value;
-       struct hisi_thermal_sensor *sensor = &data->sensor;
+       struct hisi_thermal_data *data = sensor->data;
 
        /* disable interrupt */
        hi3660_thermal_alarm_enable(data->regs, sensor->id, 0);
@@ -371,21 +397,8 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data)
 {
        struct platform_device *pdev = data->pdev;
        struct device *dev = &pdev->dev;
-       struct resource *res;
        int ret;
 
-       data->get_temp = hi6220_thermal_get_temp;
-       data->enable_sensor = hi6220_thermal_enable_sensor;
-       data->disable_sensor = hi6220_thermal_disable_sensor;
-       data->irq_handler = hi6220_thermal_irq_handler;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(data->regs)) {
-               dev_err(dev, "failed to get io address\n");
-               return PTR_ERR(data->regs);
-       }
-
        data->clk = devm_clk_get(dev, "thermal_clk");
        if (IS_ERR(data->clk)) {
                ret = PTR_ERR(data->clk);
@@ -394,11 +407,14 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data)
                return ret;
        }
 
-       data->irq = platform_get_irq(pdev, 0);
-       if (data->irq < 0)
-               return data->irq;
+       data->sensor = devm_kzalloc(dev, sizeof(*data->sensor), GFP_KERNEL);
+       if (!data->sensor)
+               return -ENOMEM;
 
-       data->sensor.id = HI6220_DEFAULT_SENSOR;
+       data->sensor[0].id = HI6220_CLUSTER0_SENSOR;
+       data->sensor[0].irq_name = "tsensor_intr";
+       data->sensor[0].data = data;
+       data->nr_sensors = 1;
 
        return 0;
 }
@@ -407,38 +423,34 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data)
 {
        struct platform_device *pdev = data->pdev;
        struct device *dev = &pdev->dev;
-       struct resource *res;
 
-       data->get_temp = hi3660_thermal_get_temp;
-       data->enable_sensor = hi3660_thermal_enable_sensor;
-       data->disable_sensor = hi3660_thermal_disable_sensor;
-       data->irq_handler = hi3660_thermal_irq_handler;
+       data->nr_sensors = 2;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(data->regs)) {
-               dev_err(dev, "failed to get io address\n");
-               return PTR_ERR(data->regs);
-       }
+       data->sensor = devm_kzalloc(dev, sizeof(*data->sensor) *
+                                   data->nr_sensors, GFP_KERNEL);
+       if (!data->sensor)
+               return -ENOMEM;
 
-       data->irq = platform_get_irq(pdev, 0);
-       if (data->irq < 0)
-               return data->irq;
+       data->sensor[0].id = HI3660_BIG_SENSOR;
+       data->sensor[0].irq_name = "tsensor_a73";
+       data->sensor[0].data = data;
 
-       data->sensor.id = HI3660_DEFAULT_SENSOR;
+       data->sensor[1].id = HI3660_LITTLE_SENSOR;
+       data->sensor[1].irq_name = "tsensor_a53";
+       data->sensor[1].data = data;
 
        return 0;
 }
 
 static int hisi_thermal_get_temp(void *__data, int *temp)
 {
-       struct hisi_thermal_data *data = __data;
-       struct hisi_thermal_sensor *sensor = &data->sensor;
+       struct hisi_thermal_sensor *sensor = __data;
+       struct hisi_thermal_data *data = sensor->data;
 
-       *temp = data->get_temp(data);
+       *temp = data->ops->get_temp(sensor);
 
-       dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n",
-               sensor->id, *temp, sensor->thres_temp);
+       dev_dbg(&data->pdev->dev, "tzd=%p, id=%d, temp=%d, thres=%d\n",
+               sensor->tzd, sensor->id, *temp, sensor->thres_temp);
 
        return 0;
 }
@@ -449,38 +461,39 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
 
 static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
 {
-       struct hisi_thermal_data *data = dev;
-       struct hisi_thermal_sensor *sensor = &data->sensor;
+       struct hisi_thermal_sensor *sensor = dev;
+       struct hisi_thermal_data *data = sensor->data;
        int temp = 0;
 
-       data->irq_handler(data);
+       data->ops->irq_handler(sensor);
 
-       hisi_thermal_get_temp(data, &temp);
+       hisi_thermal_get_temp(sensor, &temp);
 
        if (temp >= sensor->thres_temp) {
-               dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
-                        temp, sensor->thres_temp);
+               dev_crit(&data->pdev->dev,
+                        "sensor <%d> THERMAL ALARM: %d > %d\n",
+                        sensor->id, temp, sensor->thres_temp);
 
-               thermal_zone_device_update(data->sensor.tzd,
+               thermal_zone_device_update(sensor->tzd,
                                           THERMAL_EVENT_UNSPECIFIED);
 
        } else {
-               dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n",
-                        temp, sensor->thres_temp);
+               dev_crit(&data->pdev->dev,
+                        "sensor <%d> THERMAL ALARM stopped: %d < %d\n",
+                        sensor->id, temp, sensor->thres_temp);
        }
 
        return IRQ_HANDLED;
 }
 
 static int hisi_thermal_register_sensor(struct platform_device *pdev,
-                                       struct hisi_thermal_data *data,
                                        struct hisi_thermal_sensor *sensor)
 {
        int ret, i;
        const struct thermal_trip *trip;
 
        sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
-                                                          sensor->id, data,
+                                                          sensor->id, sensor,
                                                           &hisi_of_thermal_ops);
        if (IS_ERR(sensor->tzd)) {
                ret = PTR_ERR(sensor->tzd);
@@ -502,14 +515,30 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
        return 0;
 }
 
+static const struct hisi_thermal_ops hi6220_ops = {
+       .get_temp       = hi6220_thermal_get_temp,
+       .enable_sensor  = hi6220_thermal_enable_sensor,
+       .disable_sensor = hi6220_thermal_disable_sensor,
+       .irq_handler    = hi6220_thermal_irq_handler,
+       .probe          = hi6220_thermal_probe,
+};
+
+static const struct hisi_thermal_ops hi3660_ops = {
+       .get_temp       = hi3660_thermal_get_temp,
+       .enable_sensor  = hi3660_thermal_enable_sensor,
+       .disable_sensor = hi3660_thermal_disable_sensor,
+       .irq_handler    = hi3660_thermal_irq_handler,
+       .probe          = hi3660_thermal_probe,
+};
+
 static const struct of_device_id of_hisi_thermal_match[] = {
        {
                .compatible = "hisilicon,tsensor",
-               .data = hi6220_thermal_probe
+               .data = &hi6220_ops,
        },
        {
                .compatible = "hisilicon,hi3660-tsensor",
-               .data = hi3660_thermal_probe
+               .data = &hi3660_ops,
        },
        { /* end */ }
 };
@@ -527,9 +556,9 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
 static int hisi_thermal_probe(struct platform_device *pdev)
 {
        struct hisi_thermal_data *data;
-       int (*platform_probe)(struct hisi_thermal_data *);
        struct device *dev = &pdev->dev;
-       int ret;
+       struct resource *res;
+       int i, ret;
 
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -537,41 +566,50 @@ static int hisi_thermal_probe(struct platform_device *pdev)
 
        data->pdev = pdev;
        platform_set_drvdata(pdev, data);
+       data->ops = of_device_get_match_data(dev);
 
-       platform_probe = of_device_get_match_data(dev);
-       if (!platform_probe) {
-               dev_err(dev, "failed to get probe func\n");
-               return -EINVAL;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(data->regs)) {
+               dev_err(dev, "failed to get io address\n");
+               return PTR_ERR(data->regs);
        }
 
-       ret = platform_probe(data);
+       ret = data->ops->probe(data);
        if (ret)
                return ret;
 
-       ret = hisi_thermal_register_sensor(pdev, data,
-                                          &data->sensor);
-       if (ret) {
-               dev_err(dev, "failed to register thermal sensor: %d\n", ret);
-               return ret;
-       }
+       for (i = 0; i < data->nr_sensors; i++) {
+               struct hisi_thermal_sensor *sensor = &data->sensor[i];
 
-       ret = data->enable_sensor(data);
-       if (ret) {
-               dev_err(dev, "Failed to setup the sensor: %d\n", ret);
-               return ret;
-       }
+               ret = hisi_thermal_register_sensor(pdev, sensor);
+               if (ret) {
+                       dev_err(dev, "failed to register thermal sensor: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = platform_get_irq_byname(pdev, sensor->irq_name);
+               if (ret < 0)
+                       return ret;
 
-       if (data->irq) {
-               ret = devm_request_threaded_irq(dev, data->irq, NULL,
-                               hisi_thermal_alarm_irq_thread,
-                               IRQF_ONESHOT, "hisi_thermal", data);
+               ret = devm_request_threaded_irq(dev, ret, NULL,
+                                               hisi_thermal_alarm_irq_thread,
+                                               IRQF_ONESHOT, sensor->irq_name,
+                                               sensor);
                if (ret < 0) {
-                       dev_err(dev, "failed to request alarm irq: %d\n", ret);
+                       dev_err(dev, "Failed to request alarm irq: %d\n", ret);
                        return ret;
                }
-       }
 
-       hisi_thermal_toggle_sensor(&data->sensor, true);
+               ret = data->ops->enable_sensor(sensor);
+               if (ret) {
+                       dev_err(dev, "Failed to setup the sensor: %d\n", ret);
+                       return ret;
+               }
+
+               hisi_thermal_toggle_sensor(sensor, true);
+       }
 
        return 0;
 }
@@ -579,11 +617,14 @@ static int hisi_thermal_probe(struct platform_device *pdev)
 static int hisi_thermal_remove(struct platform_device *pdev)
 {
        struct hisi_thermal_data *data = platform_get_drvdata(pdev);
-       struct hisi_thermal_sensor *sensor = &data->sensor;
+       int i;
 
-       hisi_thermal_toggle_sensor(sensor, false);
+       for (i = 0; i < data->nr_sensors; i++) {
+               struct hisi_thermal_sensor *sensor = &data->sensor[i];
 
-       data->disable_sensor(data);
+               hisi_thermal_toggle_sensor(sensor, false);
+               data->ops->disable_sensor(sensor);
+       }
 
        return 0;
 }
@@ -592,8 +633,10 @@ static int hisi_thermal_remove(struct platform_device *pdev)
 static int hisi_thermal_suspend(struct device *dev)
 {
        struct hisi_thermal_data *data = dev_get_drvdata(dev);
+       int i;
 
-       data->disable_sensor(data);
+       for (i = 0; i < data->nr_sensors; i++)
+               data->ops->disable_sensor(&data->sensor[i]);
 
        return 0;
 }
@@ -601,8 +644,12 @@ static int hisi_thermal_suspend(struct device *dev)
 static int hisi_thermal_resume(struct device *dev)
 {
        struct hisi_thermal_data *data = dev_get_drvdata(dev);
+       int i, ret = 0;
+
+       for (i = 0; i < data->nr_sensors; i++)
+               ret |= data->ops->enable_sensor(&data->sensor[i]);
 
-       return data->enable_sensor(data);
+       return ret;
 }
 #endif
 
index aa452acb60b6c93f2ca0d8ace93aa7c4cc883d32..15661549eb67a79d2974e0d1b0166422acd3ee5e 100644 (file)
@@ -725,7 +725,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
        } else {
                ret = imx_init_from_tempmon_data(pdev);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to init from from fsl,tempmon-data\n");
+                       dev_err(&pdev->dev, "failed to init from fsl,tempmon-data\n");
                        return ret;
                }
        }
@@ -762,9 +762,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
                if (ret != -EPROBE_DEFER)
                        dev_err(&pdev->dev,
                                "failed to get thermal clk: %d\n", ret);
-               cpufreq_cooling_unregister(data->cdev);
-               cpufreq_cpu_put(data->policy);
-               return ret;
+               goto cpufreq_put;
        }
 
        /*
@@ -777,9 +775,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
        ret = clk_prepare_enable(data->thermal_clk);
        if (ret) {
                dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
-               cpufreq_cooling_unregister(data->cdev);
-               cpufreq_cpu_put(data->policy);
-               return ret;
+               goto cpufreq_put;
        }
 
        data->tz = thermal_zone_device_register("imx_thermal_zone",
@@ -792,10 +788,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
                ret = PTR_ERR(data->tz);
                dev_err(&pdev->dev,
                        "failed to register thermal zone device %d\n", ret);
-               clk_disable_unprepare(data->thermal_clk);
-               cpufreq_cooling_unregister(data->cdev);
-               cpufreq_cpu_put(data->policy);
-               return ret;
+               goto clk_disable;
        }
 
        dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC"
@@ -827,14 +820,20 @@ static int imx_thermal_probe(struct platform_device *pdev)
                        0, "imx_thermal", data);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
-               clk_disable_unprepare(data->thermal_clk);
-               thermal_zone_device_unregister(data->tz);
-               cpufreq_cooling_unregister(data->cdev);
-               cpufreq_cpu_put(data->policy);
-               return ret;
+               goto thermal_zone_unregister;
        }
 
        return 0;
+
+thermal_zone_unregister:
+       thermal_zone_device_unregister(data->tz);
+clk_disable:
+       clk_disable_unprepare(data->thermal_clk);
+cpufreq_put:
+       cpufreq_cooling_unregister(data->cdev);
+       cpufreq_cpu_put(data->policy);
+
+       return ret;
 }
 
 static int imx_thermal_remove(struct platform_device *pdev)
index 4f28165592056d5ec90309c5bff737f5ba66ea0b..4bfdb4a1e47d4fa1f7527c7cf16fd82872e8118a 100644 (file)
 /***   Private data structures to represent thermal device tree data ***/
 
 /**
- * struct __thermal_bind_param - a match between trip and cooling device
+ * struct __thermal_cooling_bind_param - a cooling device for a trip point
  * @cooling_device: a pointer to identify the referred cooling device
- * @trip_id: the trip point index
- * @usage: the percentage (from 0 to 100) of cooling contribution
  * @min: minimum cooling state used at this trip point
  * @max: maximum cooling state used at this trip point
  */
 
-struct __thermal_bind_params {
+struct __thermal_cooling_bind_param {
        struct device_node *cooling_device;
-       unsigned int trip_id;
-       unsigned int usage;
        unsigned long min;
        unsigned long max;
 };
 
+/**
+ * struct __thermal_bind_param - a match between trip and cooling device
+ * @tcbp: a pointer to an array of cooling devices
+ * @count: number of elements in array
+ * @trip_id: the trip point index
+ * @usage: the percentage (from 0 to 100) of cooling contribution
+ */
+
+struct __thermal_bind_params {
+       struct __thermal_cooling_bind_param *tcbp;
+       unsigned int count;
+       unsigned int trip_id;
+       unsigned int usage;
+};
+
 /**
  * struct __thermal_zone - internal representation of a thermal zone
  * @mode: current thermal zone device mode (enabled/disabled)
@@ -192,25 +203,31 @@ static int of_thermal_bind(struct thermal_zone_device *thermal,
                           struct thermal_cooling_device *cdev)
 {
        struct __thermal_zone *data = thermal->devdata;
-       int i;
+       struct __thermal_bind_params *tbp;
+       struct __thermal_cooling_bind_param *tcbp;
+       int i, j;
 
        if (!data || IS_ERR(data))
                return -ENODEV;
 
        /* find where to bind */
        for (i = 0; i < data->num_tbps; i++) {
-               struct __thermal_bind_params *tbp = data->tbps + i;
+               tbp = data->tbps + i;
 
-               if (tbp->cooling_device == cdev->np) {
-                       int ret;
+               for (j = 0; j < tbp->count; j++) {
+                       tcbp = tbp->tcbp + j;
 
-                       ret = thermal_zone_bind_cooling_device(thermal,
+                       if (tcbp->cooling_device == cdev->np) {
+                               int ret;
+
+                               ret = thermal_zone_bind_cooling_device(thermal,
                                                tbp->trip_id, cdev,
-                                               tbp->max,
-                                               tbp->min,
+                                               tcbp->max,
+                                               tcbp->min,
                                                tbp->usage);
-                       if (ret)
-                               return ret;
+                               if (ret)
+                                       return ret;
+                       }
                }
        }
 
@@ -221,22 +238,28 @@ static int of_thermal_unbind(struct thermal_zone_device *thermal,
                             struct thermal_cooling_device *cdev)
 {
        struct __thermal_zone *data = thermal->devdata;
-       int i;
+       struct __thermal_bind_params *tbp;
+       struct __thermal_cooling_bind_param *tcbp;
+       int i, j;
 
        if (!data || IS_ERR(data))
                return -ENODEV;
 
        /* find where to unbind */
        for (i = 0; i < data->num_tbps; i++) {
-               struct __thermal_bind_params *tbp = data->tbps + i;
+               tbp = data->tbps + i;
+
+               for (j = 0; j < tbp->count; j++) {
+                       tcbp = tbp->tcbp + j;
 
-               if (tbp->cooling_device == cdev->np) {
-                       int ret;
+                       if (tcbp->cooling_device == cdev->np) {
+                               int ret;
 
-                       ret = thermal_zone_unbind_cooling_device(thermal,
-                                               tbp->trip_id, cdev);
-                       if (ret)
-                               return ret;
+                               ret = thermal_zone_unbind_cooling_device(thermal,
+                                                       tbp->trip_id, cdev);
+                               if (ret)
+                                       return ret;
+                       }
                }
        }
 
@@ -486,8 +509,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
                if (sensor_specs.args_count >= 1) {
                        id = sensor_specs.args[0];
                        WARN(sensor_specs.args_count > 1,
-                            "%s: too many cells in sensor specifier %d\n",
-                            sensor_specs.np->name, sensor_specs.args_count);
+                            "%pOFn: too many cells in sensor specifier %d\n",
+                            sensor_specs.np, sensor_specs.args_count);
                } else {
                        id = 0;
                }
@@ -655,8 +678,9 @@ static int thermal_of_populate_bind_params(struct device_node *np,
                                           int ntrips)
 {
        struct of_phandle_args cooling_spec;
+       struct __thermal_cooling_bind_param *__tcbp;
        struct device_node *trip;
-       int ret, i;
+       int ret, i, count;
        u32 prop;
 
        /* Default weight. Usage is optional */
@@ -683,20 +707,44 @@ static int thermal_of_populate_bind_params(struct device_node *np,
                goto end;
        }
 
-       ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
-                                        0, &cooling_spec);
-       if (ret < 0) {
-               pr_err("missing cooling_device property\n");
+       count = of_count_phandle_with_args(np, "cooling-device",
+                                          "#cooling-cells");
+       if (!count) {
+               pr_err("Add a cooling_device property with at least one device\n");
                goto end;
        }
-       __tbp->cooling_device = cooling_spec.np;
-       if (cooling_spec.args_count >= 2) { /* at least min and max */
-               __tbp->min = cooling_spec.args[0];
-               __tbp->max = cooling_spec.args[1];
-       } else {
-               pr_err("wrong reference to cooling device, missing limits\n");
+
+       __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
+       if (!__tcbp)
+               goto end;
+
+       for (i = 0; i < count; i++) {
+               ret = of_parse_phandle_with_args(np, "cooling-device",
+                               "#cooling-cells", i, &cooling_spec);
+               if (ret < 0) {
+                       pr_err("Invalid cooling-device entry\n");
+                       goto free_tcbp;
+               }
+
+               __tcbp[i].cooling_device = cooling_spec.np;
+
+               if (cooling_spec.args_count >= 2) { /* at least min and max */
+                       __tcbp[i].min = cooling_spec.args[0];
+                       __tcbp[i].max = cooling_spec.args[1];
+               } else {
+                       pr_err("wrong reference to cooling device, missing limits\n");
+               }
        }
 
+       __tbp->tcbp = __tcbp;
+       __tbp->count = count;
+
+       goto end;
+
+free_tcbp:
+       for (i = i - 1; i >= 0; i--)
+               of_node_put(__tcbp[i].cooling_device);
+       kfree(__tcbp);
 end:
        of_node_put(trip);
 
@@ -903,8 +951,16 @@ finish:
        return tz;
 
 free_tbps:
-       for (i = i - 1; i >= 0; i--)
-               of_node_put(tz->tbps[i].cooling_device);
+       for (i = i - 1; i >= 0; i--) {
+               struct __thermal_bind_params *tbp = tz->tbps + i;
+               int j;
+
+               for (j = 0; j < tbp->count; j++)
+                       of_node_put(tbp->tcbp[j].cooling_device);
+
+               kfree(tbp->tcbp);
+       }
+
        kfree(tz->tbps);
 free_trips:
        for (i = 0; i < tz->ntrips; i++)
@@ -920,10 +976,18 @@ free_tz:
 
 static inline void of_thermal_free_zone(struct __thermal_zone *tz)
 {
-       int i;
+       struct __thermal_bind_params *tbp;
+       int i, j;
+
+       for (i = 0; i < tz->num_tbps; i++) {
+               tbp = tz->tbps + i;
+
+               for (j = 0; j < tbp->count; j++)
+                       of_node_put(tbp->tcbp[j].cooling_device);
+
+               kfree(tbp->tcbp);
+       }
 
-       for (i = 0; i < tz->num_tbps; i++)
-               of_node_put(tz->tbps[i].cooling_device);
        kfree(tz->tbps);
        for (i = 0; i < tz->ntrips; i++)
                of_node_put(tz->trips[i].np);
@@ -963,8 +1027,8 @@ int __init of_parse_thermal_zones(void)
 
                tz = thermal_of_build_thermal_zone(child);
                if (IS_ERR(tz)) {
-                       pr_err("failed to build thermal zone %s: %ld\n",
-                              child->name,
+                       pr_err("failed to build thermal zone %pOFn: %ld\n",
+                              child,
                               PTR_ERR(tz));
                        continue;
                }
@@ -998,7 +1062,7 @@ int __init of_parse_thermal_zones(void)
                                                    tz->passive_delay,
                                                    tz->polling_delay);
                if (IS_ERR(zone)) {
-                       pr_err("Failed to build %s zone %ld\n", child->name,
+                       pr_err("Failed to build %pOFn zone %ld\n", child,
                               PTR_ERR(zone));
                        kfree(tzp);
                        kfree(ops);
index ad4f3a8d656056971a05e1bbb3c3ee7fa20d5a11..b2d5d5bf4a9bd1638eb53b8f5d420e719f0ad140 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/regmap.h>
 #include <linux/thermal.h>
 
+#include "thermal_core.h"
+
 #define QPNP_TM_REG_TYPE               0x04
 #define QPNP_TM_REG_SUBTYPE            0x05
 #define QPNP_TM_REG_STATUS             0x08
 #define STATUS_GEN2_STATE_MASK         GENMASK(6, 4)
 #define STATUS_GEN2_STATE_SHIFT                4
 
-#define SHUTDOWN_CTRL1_OVERRIDE_MASK   GENMASK(7, 6)
+#define SHUTDOWN_CTRL1_OVERRIDE_S2     BIT(6)
 #define SHUTDOWN_CTRL1_THRESHOLD_MASK  GENMASK(1, 0)
 
+#define SHUTDOWN_CTRL1_RATE_25HZ       BIT(3)
+
 #define ALARM_CTRL_FORCE_ENABLE                BIT(7)
 
 /*
 #define TEMP_THRESH_STEP               5000    /* Threshold step: 5 C */
 
 #define THRESH_MIN                     0
+#define THRESH_MAX                     3
+
+/* Stage 2 Threshold Min: 125 C */
+#define STAGE2_THRESHOLD_MIN           125000
+/* Stage 2 Threshold Max: 140 C */
+#define STAGE2_THRESHOLD_MAX           140000
 
 /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
 #define DEFAULT_TEMP                   37000
 
 struct qpnp_tm_chip {
        struct regmap                   *map;
+       struct device                   *dev;
        struct thermal_zone_device      *tz_dev;
        unsigned int                    subtype;
        long                            temp;
@@ -69,6 +80,10 @@ struct qpnp_tm_chip {
        unsigned int                    stage;
        unsigned int                    prev_stage;
        unsigned int                    base;
+       /* protects .thresh, .stage and chip registers */
+       struct mutex                    lock;
+       bool                            initialized;
+
        struct iio_channel              *adc;
 };
 
@@ -125,6 +140,8 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
        unsigned int stage, stage_new, stage_old;
        int ret;
 
+       WARN_ON(!mutex_is_locked(&chip->lock));
+
        ret = qpnp_tm_get_temp_stage(chip);
        if (ret < 0)
                return ret;
@@ -163,8 +180,15 @@ static int qpnp_tm_get_temp(void *data, int *temp)
        if (!temp)
                return -EINVAL;
 
+       if (!chip->initialized) {
+               *temp = DEFAULT_TEMP;
+               return 0;
+       }
+
        if (!chip->adc) {
+               mutex_lock(&chip->lock);
                ret = qpnp_tm_update_temp_no_adc(chip);
+               mutex_unlock(&chip->lock);
                if (ret < 0)
                        return ret;
        } else {
@@ -180,8 +204,72 @@ static int qpnp_tm_get_temp(void *data, int *temp)
        return 0;
 }
 
+static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
+                                            int temp)
+{
+       u8 reg;
+       bool disable_s2_shutdown = false;
+
+       WARN_ON(!mutex_is_locked(&chip->lock));
+
+       /*
+        * Default: S2 and S3 shutdown enabled, thresholds at
+        * 105C/125C/145C, monitoring at 25Hz
+        */
+       reg = SHUTDOWN_CTRL1_RATE_25HZ;
+
+       if (temp == THERMAL_TEMP_INVALID ||
+           temp < STAGE2_THRESHOLD_MIN) {
+               chip->thresh = THRESH_MIN;
+               goto skip;
+       }
+
+       if (temp <= STAGE2_THRESHOLD_MAX) {
+               chip->thresh = THRESH_MAX -
+                       ((STAGE2_THRESHOLD_MAX - temp) /
+                        TEMP_THRESH_STEP);
+               disable_s2_shutdown = true;
+       } else {
+               chip->thresh = THRESH_MAX;
+
+               if (chip->adc)
+                       disable_s2_shutdown = true;
+               else
+                       dev_warn(chip->dev,
+                                "No ADC is configured and critical temperature is above the maximum stage 2 threshold of 140 C! Configuring stage 2 shutdown at 140 C.\n");
+       }
+
+skip:
+       reg |= chip->thresh;
+       if (disable_s2_shutdown)
+               reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
+
+       return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
+}
+
+static int qpnp_tm_set_trip_temp(void *data, int trip, int temp)
+{
+       struct qpnp_tm_chip *chip = data;
+       const struct thermal_trip *trip_points;
+       int ret;
+
+       trip_points = of_thermal_get_trip_points(chip->tz_dev);
+       if (!trip_points)
+               return -EINVAL;
+
+       if (trip_points[trip].type != THERMAL_TRIP_CRITICAL)
+               return 0;
+
+       mutex_lock(&chip->lock);
+       ret = qpnp_tm_update_critical_trip_temp(chip, temp);
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
 static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
        .get_temp = qpnp_tm_get_temp,
+       .set_trip_temp = qpnp_tm_set_trip_temp,
 };
 
 static irqreturn_t qpnp_tm_isr(int irq, void *data)
@@ -193,6 +281,29 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
+{
+       int ntrips;
+       const struct thermal_trip *trips;
+       int i;
+
+       ntrips = of_thermal_get_ntrips(chip->tz_dev);
+       if (ntrips <= 0)
+               return THERMAL_TEMP_INVALID;
+
+       trips = of_thermal_get_trip_points(chip->tz_dev);
+       if (!trips)
+               return THERMAL_TEMP_INVALID;
+
+       for (i = 0; i < ntrips; i++) {
+               if (of_thermal_is_trip_valid(chip->tz_dev, i) &&
+                   trips[i].type == THERMAL_TRIP_CRITICAL)
+                       return trips[i].temperature;
+       }
+
+       return THERMAL_TEMP_INVALID;
+}
+
 /*
  * This function initializes the internal temp value based on only the
  * current thermal stage and threshold. Setup threshold control and
@@ -203,17 +314,20 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
        unsigned int stage;
        int ret;
        u8 reg = 0;
+       int crit_temp;
+
+       mutex_lock(&chip->lock);
 
        ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
        if (ret < 0)
-               return ret;
+               goto out;
 
        chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
        chip->temp = DEFAULT_TEMP;
 
        ret = qpnp_tm_get_temp_stage(chip);
        if (ret < 0)
-               return ret;
+               goto out;
        chip->stage = ret;
 
        stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
@@ -224,21 +338,19 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
                             (stage - 1) * TEMP_STAGE_STEP +
                             TEMP_THRESH_MIN;
 
-       /*
-        * Set threshold and disable software override of stage 2 and 3
-        * shutdowns.
-        */
-       chip->thresh = THRESH_MIN;
-       reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK);
-       reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
-       ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
+       crit_temp = qpnp_tm_get_critical_trip_temp(chip);
+       ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
        if (ret < 0)
-               return ret;
+               goto out;
 
        /* Enable the thermal alarm PMIC module in always-on mode. */
        reg = ALARM_CTRL_FORCE_ENABLE;
        ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
 
+       chip->initialized = true;
+
+out:
+       mutex_unlock(&chip->lock);
        return ret;
 }
 
@@ -257,6 +369,9 @@ static int qpnp_tm_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        dev_set_drvdata(&pdev->dev, chip);
+       chip->dev = &pdev->dev;
+
+       mutex_init(&chip->lock);
 
        chip->map = dev_get_regmap(pdev->dev.parent, NULL);
        if (!chip->map)
@@ -302,6 +417,18 @@ static int qpnp_tm_probe(struct platform_device *pdev)
 
        chip->subtype = subtype;
 
+       /*
+        * Register the sensor before initializing the hardware to be able to
+        * read the trip points. get_temp() returns the default temperature
+        * before the hardware initialization is completed.
+        */
+       chip->tz_dev = devm_thermal_zone_of_sensor_register(
+               &pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
+       if (IS_ERR(chip->tz_dev)) {
+               dev_err(&pdev->dev, "failed to register sensor\n");
+               return PTR_ERR(chip->tz_dev);
+       }
+
        ret = qpnp_tm_init(chip);
        if (ret < 0) {
                dev_err(&pdev->dev, "init failed\n");
@@ -313,12 +440,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
-       chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
-                                                       &qpnp_tm_sensor_ops);
-       if (IS_ERR(chip->tz_dev)) {
-               dev_err(&pdev->dev, "failed to register sensor\n");
-               return PTR_ERR(chip->tz_dev);
-       }
+       thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
 
        return 0;
 }
index fdf561b8b81d6b711d95e0fa2d0bba92395ea378..c6dd620ac029fc788bffd4adc8622551df6977e5 100644 (file)
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/platform_device.h>
@@ -109,5 +100,6 @@ static const struct tsens_ops ops_8916 = {
 const struct tsens_data data_8916 = {
        .num_sensors    = 5,
        .ops            = &ops_8916,
+       .reg_offsets    = { [SROT_CTRL_OFFSET] = 0x0 },
        .hw_ids         = (unsigned int []){0, 1, 2, 4, 5 },
 };
index 0451277d3a8f0fbb8246d550e887d9a5678668a3..0f0adb302a7bce8149dd973f1830c13c264a39f2 100644 (file)
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/platform_device.h>
@@ -69,7 +60,7 @@ static int suspend_8960(struct tsens_device *tmdev)
 {
        int ret;
        unsigned int mask;
-       struct regmap *map = tmdev->map;
+       struct regmap *map = tmdev->tm_map;
 
        ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
        if (ret)
@@ -94,7 +85,7 @@ static int suspend_8960(struct tsens_device *tmdev)
 static int resume_8960(struct tsens_device *tmdev)
 {
        int ret;
-       struct regmap *map = tmdev->map;
+       struct regmap *map = tmdev->tm_map;
 
        ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
        if (ret)
@@ -126,12 +117,12 @@ static int enable_8960(struct tsens_device *tmdev, int id)
        int ret;
        u32 reg, mask;
 
-       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg);
+       ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg);
        if (ret)
                return ret;
 
        mask = BIT(id + SENSOR0_SHIFT);
-       ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
+       ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST);
        if (ret)
                return ret;
 
@@ -140,7 +131,7 @@ static int enable_8960(struct tsens_device *tmdev, int id)
        else
                reg |= mask | SLP_CLK_ENA_8660 | EN;
 
-       ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
+       ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg);
        if (ret)
                return ret;
 
@@ -157,7 +148,7 @@ static void disable_8960(struct tsens_device *tmdev)
        mask <<= SENSOR0_SHIFT;
        mask |= EN;
 
-       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
+       ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg_cntl);
        if (ret)
                return;
 
@@ -168,7 +159,7 @@ static void disable_8960(struct tsens_device *tmdev)
        else
                reg_cntl &= ~SLP_CLK_ENA_8660;
 
-       regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
 }
 
 static int init_8960(struct tsens_device *tmdev)
@@ -176,8 +167,8 @@ static int init_8960(struct tsens_device *tmdev)
        int ret, i;
        u32 reg_cntl;
 
-       tmdev->map = dev_get_regmap(tmdev->dev, NULL);
-       if (!tmdev->map)
+       tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL);
+       if (!tmdev->tm_map)
                return -ENODEV;
 
        /*
@@ -193,14 +184,14 @@ static int init_8960(struct tsens_device *tmdev)
        }
 
        reg_cntl = SW_RST;
-       ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
+       ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
        if (ret)
                return ret;
 
        if (tmdev->num_sensors > 1) {
                reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
                reg_cntl &= ~SW_RST;
-               ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
+               ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR,
                                         CONFIG_MASK, CONFIG);
        } else {
                reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
@@ -209,12 +200,12 @@ static int init_8960(struct tsens_device *tmdev)
        }
 
        reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
-       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
        if (ret)
                return ret;
 
        reg_cntl |= EN;
-       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
        if (ret)
                return ret;
 
@@ -261,12 +252,12 @@ static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
 
        timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
        do {
-               ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
+               ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy);
                if (ret)
                        return ret;
                if (!(trdy & TRDY_MASK))
                        continue;
-               ret = regmap_read(tmdev->map, s->status, &code);
+               ret = regmap_read(tmdev->tm_map, s->status, &code);
                if (ret)
                        return ret;
                *temp = code_to_mdegC(code, s);
index 9baf77e8cbe369c006a012067f050f585264866c..3d3fda3d731b30dd48f5ffaaee23fe87d185b0df 100644 (file)
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/platform_device.h>
@@ -241,4 +232,5 @@ static const struct tsens_ops ops_8974 = {
 const struct tsens_data data_8974 = {
        .num_sensors    = 11,
        .ops            = &ops_8974,
+       .reg_offsets    = { [SROT_CTRL_OFFSET] = 0x0 },
 };
index 6207d8d923513a377e0ebcbf19279b1c8e249876..3be4be2e046598a494e48c41392c89913ff3ce70 100644 (file)
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/err.h>
 #include <linux/regmap.h>
 #include "tsens.h"
 
-#define S0_ST_ADDR             0x1030
+/* SROT */
+#define TSENS_EN               BIT(0)
+
+/* TM */
+#define STATUS_OFFSET          0x30
 #define SN_ADDR_OFFSET         0x4
 #define SN_ST_TEMP_MASK                0x3ff
 #define CAL_DEGC_PT1           30
@@ -107,8 +102,8 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
        unsigned int status_reg;
        int last_temp = 0, ret;
 
-       status_reg = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
-       ret = regmap_read(tmdev->map, status_reg, &code);
+       status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET;
+       ret = regmap_read(tmdev->tm_map, status_reg, &code);
        if (ret)
                return ret;
        last_temp = code & SN_ST_TEMP_MASK;
@@ -126,29 +121,52 @@ static const struct regmap_config tsens_config = {
 
 int __init init_common(struct tsens_device *tmdev)
 {
-       void __iomem *base;
+       void __iomem *tm_base, *srot_base;
        struct resource *res;
+       u32 code;
+       int ret;
        struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node);
+       u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET];
 
        if (!op)
                return -EINVAL;
 
-       /* The driver only uses the TM register address space for now */
        if (op->num_resources > 1) {
+               /* DT with separate SROT and TM address space */
                tmdev->tm_offset = 0;
+               res = platform_get_resource(op, IORESOURCE_MEM, 1);
+               srot_base = devm_ioremap_resource(&op->dev, res);
+               if (IS_ERR(srot_base))
+                       return PTR_ERR(srot_base);
+
+               tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev,
+                                                       srot_base, &tsens_config);
+               if (IS_ERR(tmdev->srot_map))
+                       return PTR_ERR(tmdev->srot_map);
+
        } else {
                /* old DTs where SROT and TM were in a contiguous 2K block */
                tmdev->tm_offset = 0x1000;
        }
 
        res = platform_get_resource(op, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&op->dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
-
-       tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
-       if (IS_ERR(tmdev->map))
-               return PTR_ERR(tmdev->map);
+       tm_base = devm_ioremap_resource(&op->dev, res);
+       if (IS_ERR(tm_base))
+               return PTR_ERR(tm_base);
+
+       tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config);
+       if (IS_ERR(tmdev->tm_map))
+               return PTR_ERR(tmdev->tm_map);
+
+       if (tmdev->srot_map) {
+               ret = regmap_read(tmdev->srot_map, ctrl_offset, &code);
+               if (ret)
+                       return ret;
+               if (!(code & TSENS_EN)) {
+                       dev_err(tmdev->dev, "tsens device is not enabled\n");
+                       return -ENODEV;
+               }
+       }
 
        return 0;
 }
index 44da02f594ac04edfe6f8096f8dcc0756a11ce54..381a212872bfb4151b3606bea23d821403997741 100644 (file)
@@ -21,7 +21,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
        int ret;
 
        status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4;
-       ret = regmap_read(tmdev->map, status_reg, &code);
+       ret = regmap_read(tmdev->tm_map, status_reg, &code);
        if (ret)
                return ret;
        last_temp = code & LAST_TEMP_MASK;
@@ -29,7 +29,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
                goto done;
 
        /* Try a second time */
-       ret = regmap_read(tmdev->map, status_reg, &code);
+       ret = regmap_read(tmdev->tm_map, status_reg, &code);
        if (ret)
                return ret;
        if (code & STATUS_VALID_BIT) {
@@ -40,7 +40,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
        }
 
        /* Try a third/last time */
-       ret = regmap_read(tmdev->map, status_reg, &code);
+       ret = regmap_read(tmdev->tm_map, status_reg, &code);
        if (ret)
                return ret;
        if (code & STATUS_VALID_BIT) {
@@ -68,10 +68,12 @@ static const struct tsens_ops ops_generic_v2 = {
 
 const struct tsens_data data_tsens_v2 = {
        .ops            = &ops_generic_v2,
+       .reg_offsets    = { [SROT_CTRL_OFFSET] = 0x4 },
 };
 
 /* Kept around for backward compatibility with old msm8996.dtsi */
 const struct tsens_data data_8996 = {
        .num_sensors    = 13,
        .ops            = &ops_generic_v2,
+       .reg_offsets    = { [SROT_CTRL_OFFSET] = 0x4 },
 };
index a2c9bfae3d867e8aa5e7ef2daa146212e673ff72..f1ec9bbe4717e6f0f5c3aea4fb19b5963a189d19 100644 (file)
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/err.h>
@@ -89,11 +80,6 @@ static int tsens_register(struct tsens_device *tmdev)
 {
        int i;
        struct thermal_zone_device *tzd;
-       u32 *hw_id, n = tmdev->num_sensors;
-
-       hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
-       if (!hw_id)
-               return -ENOMEM;
 
        for (i = 0;  i < tmdev->num_sensors; i++) {
                tmdev->sensor[i].tmdev = tmdev;
@@ -158,6 +144,9 @@ static int tsens_probe(struct platform_device *pdev)
                else
                        tmdev->sensor[i].hw_id = i;
        }
+       for (i = 0; i < REG_ARRAY_SIZE; i++) {
+               tmdev->reg_offsets[i] = data->reg_offsets[i];
+       }
 
        if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
                return -EINVAL;
index 14331eb45a86b80b3b0f581621586f1312a98110..7b7feee5dc4633b72893ddf36ec69c8405f7c318 100644 (file)
@@ -1,15 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
+
 #ifndef __QCOM_TSENS_H__
 #define __QCOM_TSENS_H__
 
@@ -55,15 +48,23 @@ struct tsens_ops {
        int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
 };
 
+enum reg_list {
+       SROT_CTRL_OFFSET,
+
+       REG_ARRAY_SIZE,
+};
+
 /**
  * struct tsens_data - tsens instance specific data
  * @num_sensors: Max number of sensors supported by platform
  * @ops: operations the tsens instance supports
  * @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ * @reg_offsets: Register offsets for commonly used registers
  */
 struct tsens_data {
        const u32               num_sensors;
        const struct tsens_ops  *ops;
+       const u16               reg_offsets[REG_ARRAY_SIZE];
        unsigned int            *hw_ids;
 };
 
@@ -76,8 +77,10 @@ struct tsens_context {
 struct tsens_device {
        struct device                   *dev;
        u32                             num_sensors;
-       struct regmap                   *map;
+       struct regmap                   *tm_map;
+       struct regmap                   *srot_map;
        u32                             tm_offset;
+       u16                             reg_offsets[REG_ARRAY_SIZE];
        struct tsens_context            ctx;
        const struct tsens_ops          *ops;
        struct tsens_sensor             sensor[0];
index 450ed66edf582b4c6c77e3a376a17173cfdf57b9..18c711b1951413832434c86d654589550d5cc6ed 100644 (file)
@@ -119,8 +119,8 @@ static int qoriq_tmu_get_sensor_id(void)
        if (sensor_specs.args_count >= 1) {
                id = sensor_specs.args[0];
                WARN(sensor_specs.args_count > 1,
-                               "%s: too many cells in sensor specifier %d\n",
-                               sensor_specs.np->name, sensor_specs.args_count);
+                               "%pOFn: too many cells in sensor specifier %d\n",
+                               sensor_specs.np, sensor_specs.args_count);
        } else {
                id = 0;
        }
@@ -294,6 +294,7 @@ static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
 
 static const struct of_device_id qoriq_tmu_match[] = {
        { .compatible = "fsl,qoriq-tmu", },
+       { .compatible = "fsl,imx8mq-tmu", },
        {},
 };
 MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
index 7aed5337bdd35b3b3214372e7d5f9a01cf26789c..75786cc8e2f9195d2a3f8120f26eb787918f881f 100644 (file)
@@ -318,9 +318,11 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
 }
 
 static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
+       { .compatible = "renesas,r8a774a1-thermal", },
        { .compatible = "renesas,r8a7795-thermal", },
        { .compatible = "renesas,r8a7796-thermal", },
        { .compatible = "renesas,r8a77965-thermal", },
+       { .compatible = "renesas,r8a77980-thermal", },
        {},
 };
 MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
index 78f932822d381c9dbc013aee618be56e533baaab..8014a207d8d9b1a97d45378cdf00d6b4730c1788 100644 (file)
@@ -112,6 +112,10 @@ static const struct of_device_id rcar_thermal_dt_ids[] = {
                .compatible = "renesas,rcar-gen2-thermal",
                 .data = &rcar_gen2_thermal,
        },
+       {
+               .compatible = "renesas,thermal-r8a77970",
+               .data = &rcar_gen3_thermal,
+       },
        {
                .compatible = "renesas,thermal-r8a77995",
                .data = &rcar_gen3_thermal,
@@ -434,8 +438,8 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
        rcar_thermal_for_each_priv(priv, common) {
                if (rcar_thermal_had_changed(priv, status)) {
                        rcar_thermal_irq_disable(priv);
-                       schedule_delayed_work(&priv->work,
-                                             msecs_to_jiffies(300));
+                       queue_delayed_work(system_freezable_wq, &priv->work,
+                                          msecs_to_jiffies(300));
                }
        }
 
@@ -453,6 +457,7 @@ static int rcar_thermal_remove(struct platform_device *pdev)
 
        rcar_thermal_for_each_priv(priv, common) {
                rcar_thermal_irq_disable(priv);
+               cancel_delayed_work_sync(&priv->work);
                if (priv->chip->use_of_thermal)
                        thermal_remove_hwmon_sysfs(priv->zone);
                else
@@ -492,7 +497,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
        pm_runtime_get_sync(dev);
 
        for (i = 0; i < chip->nirqs; i++) {
-               irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+               irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
                if (!irq)
                        continue;
                if (!common->base) {
index 490fdbe22eea4f907c42979b41121dd3b157e321..b80f9a9e4f8f603dd822d90a9154dea96fafcb6d 100644 (file)
@@ -1,3 +1,7 @@
+#
+# STMicroelectronics thermal drivers configuration
+#
+
 config ST_THERMAL
        tristate "Thermal sensors on STMicroelectronics STi series of SoCs"
        help
@@ -10,3 +14,13 @@ config ST_THERMAL_SYSCFG
 config ST_THERMAL_MEMMAP
        select ST_THERMAL
        tristate "STi series memory mapped access based thermal sensors"
+
+config STM32_THERMAL
+       tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs"
+       depends on MACH_STM32MP157
+       default y
+       help
+       Support for thermal framework on STMicroelectronics STM32 series of
+       SoCs. This thermal driver allows to access to general thermal framework
+       functionalities and to acces to SoC sensor functionalities. This
+       configuration is fully dependent of MACH_STM32MP157.
index b38878977bd853370b68ad823bb07ce967cd496b..b2b9e9b962966a57b83e1baf8faaa6cf7f5e8483 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_ST_THERMAL)               := st_thermal.o
 obj-$(CONFIG_ST_THERMAL_SYSCFG)                += st_thermal_syscfg.o
 obj-$(CONFIG_ST_THERMAL_MEMMAP)                += st_thermal_memmap.o
+obj-$(CONFIG_STM32_THERMAL)            := stm_thermal.o
\ No newline at end of file
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c
new file mode 100644 (file)
index 0000000..47623da
--- /dev/null
@@ -0,0 +1,760 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: David Hernandez Sanchez <david.hernandezsanchez@st.com> for
+ * STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#include "../thermal_core.h"
+#include "../thermal_hwmon.h"
+
+/* DTS register offsets */
+#define DTS_CFGR1_OFFSET       0x0
+#define DTS_T0VALR1_OFFSET     0x8
+#define DTS_RAMPVALR_OFFSET    0X10
+#define DTS_ITR1_OFFSET                0x14
+#define DTS_DR_OFFSET          0x1C
+#define DTS_SR_OFFSET          0x20
+#define DTS_ITENR_OFFSET       0x24
+#define DTS_CIFR_OFFSET                0x28
+
+/* DTS_CFGR1 register mask definitions */
+#define HSREF_CLK_DIV_MASK     GENMASK(30, 24)
+#define TS1_SMP_TIME_MASK      GENMASK(19, 16)
+#define TS1_INTRIG_SEL_MASK    GENMASK(11, 8)
+
+/* DTS_T0VALR1 register mask definitions */
+#define TS1_T0_MASK            GENMASK(17, 16)
+#define TS1_FMT0_MASK          GENMASK(15, 0)
+
+/* DTS_RAMPVALR register mask definitions */
+#define TS1_RAMP_COEFF_MASK    GENMASK(15, 0)
+
+/* DTS_ITR1 register mask definitions */
+#define TS1_HITTHD_MASK                GENMASK(31, 16)
+#define TS1_LITTHD_MASK                GENMASK(15, 0)
+
+/* DTS_DR register mask definitions */
+#define TS1_MFREQ_MASK         GENMASK(15, 0)
+
+/* Less significant bit position definitions */
+#define TS1_T0_POS             16
+#define TS1_SMP_TIME_POS       16
+#define TS1_HITTHD_POS         16
+#define HSREF_CLK_DIV_POS      24
+
+/* DTS_CFGR1 bit definitions */
+#define TS1_EN                 BIT(0)
+#define TS1_START              BIT(4)
+#define REFCLK_SEL             BIT(20)
+#define REFCLK_LSE             REFCLK_SEL
+#define Q_MEAS_OPT             BIT(21)
+#define CALIBRATION_CONTROL    Q_MEAS_OPT
+
+/* DTS_SR bit definitions */
+#define TS_RDY                 BIT(15)
+/* Bit definitions below are common for DTS_SR, DTS_ITENR and DTS_CIFR */
+#define HIGH_THRESHOLD         BIT(2)
+#define LOW_THRESHOLD          BIT(1)
+
+/* Constants */
+#define ADJUST                 100
+#define ONE_MHZ                        1000000
+#define POLL_TIMEOUT           5000
+#define STARTUP_TIME           40
+#define TS1_T0_VAL0            30
+#define TS1_T0_VAL1            130
+#define NO_HW_TRIG             0
+
+/* The Thermal Framework expects millidegrees */
+#define mcelsius(temp)         ((temp) * 1000)
+
+/* The Sensor expects oC degrees */
+#define celsius(temp)          ((temp) / 1000)
+
+struct stm_thermal_sensor {
+       struct device *dev;
+       struct thermal_zone_device *th_dev;
+       enum thermal_device_mode mode;
+       struct clk *clk;
+       int high_temp;
+       int low_temp;
+       int temp_critical;
+       int temp_passive;
+       unsigned int low_temp_enabled;
+       int num_trips;
+       int irq;
+       unsigned int irq_enabled;
+       void __iomem *base;
+       int t0, fmt0, ramp_coeff;
+};
+
+static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata)
+{
+       struct stm_thermal_sensor *sensor = sdata;
+
+       disable_irq_nosync(irq);
+       sensor->irq_enabled = false;
+
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata)
+{
+       u32 value;
+       struct stm_thermal_sensor *sensor = sdata;
+
+       /* read IT reason in SR and clear flags */
+       value = readl_relaxed(sensor->base + DTS_SR_OFFSET);
+
+       if ((value & LOW_THRESHOLD) == LOW_THRESHOLD)
+               writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
+
+       if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD)
+               writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
+
+       thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
+
+       return IRQ_HANDLED;
+}
+
+static int stm_sensor_power_on(struct stm_thermal_sensor *sensor)
+{
+       int ret;
+       u32 value;
+
+       /* Enable sensor */
+       value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
+       value |= TS1_EN;
+       writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
+
+       /*
+        * The DTS block can be enabled by setting TSx_EN bit in
+        * DTS_CFGRx register. It requires a startup time of
+        * 40μs. Use 5 ms as arbitrary timeout.
+        */
+       ret = readl_poll_timeout(sensor->base + DTS_SR_OFFSET,
+                                value, (value & TS_RDY),
+                                STARTUP_TIME, POLL_TIMEOUT);
+       if (ret)
+               return ret;
+
+       /* Start continuous measuring */
+       value = readl_relaxed(sensor->base +
+                             DTS_CFGR1_OFFSET);
+       value |= TS1_START;
+       writel_relaxed(value, sensor->base +
+                      DTS_CFGR1_OFFSET);
+
+       return 0;
+}
+
+static int stm_sensor_power_off(struct stm_thermal_sensor *sensor)
+{
+       u32 value;
+
+       /* Stop measuring */
+       value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
+       value &= ~TS1_START;
+       writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
+
+       /* Ensure stop is taken into account */
+       usleep_range(STARTUP_TIME, POLL_TIMEOUT);
+
+       /* Disable sensor */
+       value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
+       value &= ~TS1_EN;
+       writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
+
+       /* Ensure disable is taken into account */
+       return readl_poll_timeout(sensor->base + DTS_SR_OFFSET, value,
+                                 !(value & TS_RDY),
+                                 STARTUP_TIME, POLL_TIMEOUT);
+}
+
+static int stm_thermal_calibration(struct stm_thermal_sensor *sensor)
+{
+       u32 value, clk_freq;
+       u32 prescaler;
+
+       /* Figure out prescaler value for PCLK during calibration */
+       clk_freq = clk_get_rate(sensor->clk);
+       if (!clk_freq)
+               return -EINVAL;
+
+       prescaler = 0;
+       clk_freq /= ONE_MHZ;
+       if (clk_freq) {
+               while (prescaler <= clk_freq)
+                       prescaler++;
+       }
+
+       value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
+
+       /* Clear prescaler */
+       value &= ~HSREF_CLK_DIV_MASK;
+
+       /* Set prescaler. pclk_freq/prescaler < 1MHz */
+       value |= (prescaler << HSREF_CLK_DIV_POS);
+
+       /* Select PCLK as reference clock */
+       value &= ~REFCLK_SEL;
+
+       /* Set maximal sampling time for better precision */
+       value |= TS1_SMP_TIME_MASK;
+
+       /* Measure with calibration */
+       value &= ~CALIBRATION_CONTROL;
+
+       /* select trigger */
+       value &= ~TS1_INTRIG_SEL_MASK;
+       value |= NO_HW_TRIG;
+
+       writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
+
+       return 0;
+}
+
+/* Fill in DTS structure with factory sensor values */
+static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
+{
+       /* Retrieve engineering calibration temperature */
+       sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) &
+                                       TS1_T0_MASK;
+       if (!sensor->t0)
+               sensor->t0 = TS1_T0_VAL0;
+       else
+               sensor->t0 = TS1_T0_VAL1;
+
+       /* Retrieve fmt0 and put it on Hz */
+       sensor->fmt0 = ADJUST * readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET)
+                                             & TS1_FMT0_MASK;
+
+       /* Retrieve ramp coefficient */
+       sensor->ramp_coeff = readl_relaxed(sensor->base + DTS_RAMPVALR_OFFSET) &
+                                          TS1_RAMP_COEFF_MASK;
+
+       if (!sensor->fmt0 || !sensor->ramp_coeff) {
+               dev_err(sensor->dev, "%s: wrong setting\n", __func__);
+               return -EINVAL;
+       }
+
+       dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
+               __func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff);
+
+       return 0;
+}
+
+static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor,
+                                          int temp, u32 *th)
+{
+       int freqM;
+       u32 sampling_time;
+
+       /* Retrieve the number of periods to sample */
+       sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
+                       TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
+
+       /* Figure out the CLK_PTAT frequency for a given temperature */
+       freqM = ((temp - sensor->t0) * sensor->ramp_coeff)
+                + sensor->fmt0;
+
+       dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz",
+               __func__, freqM);
+
+       /* Figure out the threshold sample number */
+       *th = clk_get_rate(sensor->clk);
+       if (!*th)
+               return -EINVAL;
+
+       *th = *th / freqM;
+
+       *th *= sampling_time;
+
+       return 0;
+}
+
+static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor)
+{
+       u32 value, th;
+       int ret;
+
+       value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
+
+       /* Erase threshold content */
+       value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
+
+       /* Retrieve the sample threshold number th for a given temperature */
+       ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th);
+       if (ret)
+               return ret;
+
+       value |= th & TS1_LITTHD_MASK;
+
+       if (sensor->low_temp_enabled) {
+               /* Retrieve the sample threshold */
+               ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp,
+                                                     &th);
+               if (ret)
+                       return ret;
+
+               value |= (TS1_HITTHD_MASK  & (th << TS1_HITTHD_POS));
+       }
+
+       /* Write value on the Low interrupt threshold */
+       writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET);
+
+       return 0;
+}
+
+/* Disable temperature interrupt */
+static int stm_disable_irq(struct stm_thermal_sensor *sensor)
+{
+       u32 value;
+
+       /* Disable IT generation for low and high thresholds */
+       value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
+       writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD),
+                      sensor->base + DTS_ITENR_OFFSET);
+
+       dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__);
+
+       return 0;
+}
+
+/* Enable temperature interrupt */
+static int stm_enable_irq(struct stm_thermal_sensor *sensor)
+{
+       u32 value;
+
+       /*
+        * Code below enables High temperature threshold using a low threshold
+        * sampling value
+        */
+
+       /* Make sure LOW_THRESHOLD IT is clear before enabling */
+       writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
+
+       /* Enable IT generation for low threshold */
+       value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
+       value |= LOW_THRESHOLD;
+
+       /* Enable the low temperature threshold if needed */
+       if (sensor->low_temp_enabled) {
+               /* Make sure HIGH_THRESHOLD IT is clear before enabling */
+               writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
+
+               /* Enable IT generation for high threshold */
+               value |= HIGH_THRESHOLD;
+       }
+
+       /* Enable thresholds */
+       writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
+
+       dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__);
+
+       return 0;
+}
+
+static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
+{
+       int ret;
+
+       sensor->mode = THERMAL_DEVICE_DISABLED;
+
+       ret = stm_sensor_power_off(sensor);
+       if (ret)
+               return ret;
+
+       ret = stm_disable_irq(sensor);
+       if (ret)
+               return ret;
+
+       ret = stm_thermal_set_threshold(sensor);
+       if (ret)
+               return ret;
+
+       ret = stm_enable_irq(sensor);
+       if (ret)
+               return ret;
+
+       ret = stm_sensor_power_on(sensor);
+       if (ret)
+               return ret;
+
+       sensor->mode = THERMAL_DEVICE_ENABLED;
+
+       return 0;
+}
+
+/* Callback to get temperature from HW */
+static int stm_thermal_get_temp(void *data, int *temp)
+{
+       struct stm_thermal_sensor *sensor = data;
+       u32 sampling_time;
+       int freqM, ret;
+
+       if (sensor->mode != THERMAL_DEVICE_ENABLED)
+               return -EAGAIN;
+
+       /* Retrieve the number of samples */
+       ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM,
+                                (freqM & TS1_MFREQ_MASK), STARTUP_TIME,
+                                POLL_TIMEOUT);
+
+       if (ret)
+               return ret;
+
+       if (!freqM)
+               return -ENODATA;
+
+       /* Retrieve the number of periods sampled */
+       sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
+                       TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
+
+       /* Figure out the number of samples per period */
+       freqM /= sampling_time;
+
+       /* Figure out the CLK_PTAT frequency */
+       freqM = clk_get_rate(sensor->clk) / freqM;
+       if (!freqM)
+               return -EINVAL;
+
+       dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM);
+
+       /* Figure out the temperature in mili celsius */
+       *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) /
+                        sensor->ramp_coeff));
+
+       dev_dbg(sensor->dev, "%s: temperature = %d millicelsius",
+               __func__, *temp);
+
+       /* Update thresholds */
+       if (sensor->num_trips > 1) {
+               /* Update alarm threshold value to next higher trip point */
+               if (sensor->high_temp == sensor->temp_passive &&
+                   celsius(*temp) >= sensor->temp_passive) {
+                       sensor->high_temp = sensor->temp_critical;
+                       sensor->low_temp = sensor->temp_passive;
+                       sensor->low_temp_enabled = true;
+                       ret = stm_thermal_update_threshold(sensor);
+                       if (ret)
+                               return ret;
+               }
+
+               if (sensor->high_temp == sensor->temp_critical &&
+                   celsius(*temp) < sensor->temp_passive) {
+                       sensor->high_temp = sensor->temp_passive;
+                       sensor->low_temp_enabled = false;
+                       ret = stm_thermal_update_threshold(sensor);
+                       if (ret)
+                               return ret;
+               }
+
+               /*
+                * Re-enable alarm IRQ if temperature below critical
+                * temperature
+                */
+               if (!sensor->irq_enabled &&
+                   (celsius(*temp) < sensor->temp_critical)) {
+                       sensor->irq_enabled = true;
+                       enable_irq(sensor->irq);
+               }
+       }
+
+       return 0;
+}
+
+/* Registers DTS irq to be visible by GIC */
+static int stm_register_irq(struct stm_thermal_sensor *sensor)
+{
+       struct device *dev = sensor->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       int ret;
+
+       sensor->irq = platform_get_irq(pdev, 0);
+       if (sensor->irq < 0) {
+               dev_err(dev, "%s: Unable to find IRQ\n", __func__);
+               return sensor->irq;
+       }
+
+       ret = devm_request_threaded_irq(dev, sensor->irq,
+                                       stm_thermal_alarm_irq,
+                                       stm_thermal_alarm_irq_thread,
+                                       IRQF_ONESHOT,
+                                       dev->driver->name, sensor);
+       if (ret) {
+               dev_err(dev, "%s: Failed to register IRQ %d\n", __func__,
+                       sensor->irq);
+               return ret;
+       }
+
+       sensor->irq_enabled = true;
+
+       dev_dbg(dev, "%s: thermal IRQ registered", __func__);
+
+       return 0;
+}
+
+static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
+{
+       int ret;
+
+       ret = stm_sensor_power_off(sensor);
+       if (ret)
+               return ret;
+
+       clk_disable_unprepare(sensor->clk);
+
+       return 0;
+}
+
+static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
+{
+       int ret;
+       struct device *dev = sensor->dev;
+
+       ret = clk_prepare_enable(sensor->clk);
+       if (ret)
+               return ret;
+
+       ret = stm_thermal_calibration(sensor);
+       if (ret)
+               goto thermal_unprepare;
+
+       /* Set threshold(s) for IRQ */
+       ret = stm_thermal_set_threshold(sensor);
+       if (ret)
+               goto thermal_unprepare;
+
+       ret = stm_enable_irq(sensor);
+       if (ret)
+               goto thermal_unprepare;
+
+       ret = stm_sensor_power_on(sensor);
+       if (ret) {
+               dev_err(dev, "%s: failed to power on sensor\n", __func__);
+               goto irq_disable;
+       }
+
+       return 0;
+
+irq_disable:
+       stm_disable_irq(sensor);
+
+thermal_unprepare:
+       clk_disable_unprepare(sensor->clk);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stm_thermal_suspend(struct device *dev)
+{
+       int ret;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
+
+       ret = stm_thermal_sensor_off(sensor);
+       if (ret)
+               return ret;
+
+       sensor->mode = THERMAL_DEVICE_DISABLED;
+
+       return 0;
+}
+
+static int stm_thermal_resume(struct device *dev)
+{
+       int ret;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
+
+       ret = stm_thermal_prepare(sensor);
+       if (ret)
+               return ret;
+
+       sensor->mode = THERMAL_DEVICE_ENABLED;
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+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,
+};
+
+static const struct of_device_id stm_thermal_of_match[] = {
+               { .compatible = "st,stm32-thermal"},
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, stm_thermal_of_match);
+
+static int stm_thermal_probe(struct platform_device *pdev)
+{
+       struct stm_thermal_sensor *sensor;
+       struct resource *res;
+       const struct thermal_trip *trip;
+       void __iomem *base;
+       int ret, i;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "%s: device tree node not found\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
+       if (!sensor)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, sensor);
+
+       sensor->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       /* Populate sensor */
+       sensor->base = base;
+
+       ret = stm_thermal_read_factory_settings(sensor);
+       if (ret)
+               return ret;
+
+       sensor->clk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(sensor->clk)) {
+               dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n",
+                       __func__);
+               return PTR_ERR(sensor->clk);
+       }
+
+       /* Register IRQ into GIC */
+       ret = stm_register_irq(sensor);
+       if (ret)
+               return ret;
+
+       sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
+                                                             sensor,
+                                                             &stm_tz_ops);
+
+       if (IS_ERR(sensor->th_dev)) {
+               dev_err(&pdev->dev, "%s: thermal zone sensor registering KO\n",
+                       __func__);
+               ret = PTR_ERR(sensor->th_dev);
+               return ret;
+       }
+
+       if (!sensor->th_dev->ops->get_crit_temp) {
+               /* Critical point must be provided */
+               ret = -EINVAL;
+               goto err_tz;
+       }
+
+       ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev,
+                       &sensor->temp_critical);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Not able to read critical_temp: %d\n", ret);
+               goto err_tz;
+       }
+
+       sensor->temp_critical = celsius(sensor->temp_critical);
+
+       /* Set thresholds for IRQ */
+       sensor->high_temp = sensor->temp_critical;
+
+       trip = of_thermal_get_trip_points(sensor->th_dev);
+       sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev);
+
+       /* Find out passive temperature if it exists */
+       for (i = (sensor->num_trips - 1); i >= 0;  i--) {
+               if (trip[i].type == THERMAL_TRIP_PASSIVE) {
+                       sensor->temp_passive = celsius(trip[i].temperature);
+                       /* Update high temperature threshold */
+                       sensor->high_temp = sensor->temp_passive;
+                       }
+       }
+
+       /*
+        * Ensure low_temp_enabled flag is disabled.
+        * By disabling low_temp_enabled, low threshold IT will not be
+        * configured neither enabled because it is not needed as high
+        * threshold is set on the lowest temperature trip point after
+        * probe.
+        */
+       sensor->low_temp_enabled = false;
+
+       /* Configure and enable HW sensor */
+       ret = stm_thermal_prepare(sensor);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Not able to enable sensor: %d\n", ret);
+               goto err_tz;
+       }
+
+       /*
+        * Thermal_zone doesn't enable hwmon as default,
+        * enable it here
+        */
+       sensor->th_dev->tzp->no_hwmon = false;
+       ret = thermal_add_hwmon_sysfs(sensor->th_dev);
+       if (ret)
+               goto err_tz;
+
+       sensor->mode = THERMAL_DEVICE_ENABLED;
+
+       dev_info(&pdev->dev, "%s: Driver initialized successfully\n",
+                __func__);
+
+       return 0;
+
+err_tz:
+       thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev);
+       return ret;
+}
+
+static int stm_thermal_remove(struct platform_device *pdev)
+{
+       struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
+
+       stm_thermal_sensor_off(sensor);
+       thermal_remove_hwmon_sysfs(sensor->th_dev);
+       thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev);
+
+       return 0;
+}
+
+static struct platform_driver stm_thermal_driver = {
+       .driver = {
+               .name   = "stm_thermal",
+               .pm     = &stm_thermal_pm_ops,
+               .of_match_table = stm_thermal_of_match,
+       },
+       .probe          = stm_thermal_probe,
+       .remove         = stm_thermal_remove,
+};
+module_platform_driver(stm_thermal_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 Thermal Sensor Driver");
+MODULE_AUTHOR("David Hernandez Sanchez <david.hernandezsanchez@st.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm_thermal");