Merge tag 'thermal-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/therma...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Dec 2020 00:21:37 +0000 (16:21 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Dec 2020 00:21:37 +0000 (16:21 -0800)
Pull thermal updates from Daniel Lezcano:

 - Add upper and lower limits clamps for the cooling device state in the
   power allocator governor (Michael Kao)

 - Add upper and lower limits support for the power allocator governor
   (Lukasz Luba)

 - Optimize conditions testing for the trip points (Bernard Zhao)

 - Replace spin_lock_irqsave by spin_lock in hard IRQ on the rcar driver
   (Tian Tao)

 - Add MT8516 dt-bindings and device reset optional support (Fabien
   Parent)

 - Add a quiescent period to cool down the PCH when entering S0iX
   (Sumeet Pawnikar)

 - Use bitmap API instead of re-inventing the wheel on sun8i (Yangtao
   Li)

 - Remove useless NULL check in the hwmon driver (Bernard Zhao)

 - Update the current state in the cpufreq cooling device only if the
   frequency change is effective (Zhuguangqing)

 - Improve the schema validation for the rcar DT bindings (Geert
   Uytterhoeven)

 - Fix the user time unit in the documentation (Viresh Kumar)

 - Add PCI ids for Lewisburg PCH (Andres Freund)

 - Add hwmon support on amlogic (Martin Blumenstingl)

 - Fix build failure for PCH entering on in S0iX (Randy Dunlap)

 - Improve the k_* coefficient for the power allocator governor (Lukasz
   Luba)

 - Fix missing const on a sysfs attribute (Rikard Falkeborn)

 - Remove broken interrupt support on rcar to be replaced by a new one
   (Niklas Söderlund)

 - Improve the error code handling at init time on imx8mm (Fabio
   Estevam)

 - Compute interval validity once instead at each temperature reading
   iteration on acerhdf (Daniel Lezcano)

 - Add r8a779a0 support (Niklas Söderlund)

 - Add PCI ids for AlderLake PCH and mmio refactoring (Srinivas
   Pandruvada)

 - Add RFIM and mailbox support on int340x (Srinivas Pandruvada)

 - Use macro for temperature calculation on PCH (Sumeet Pawnikar)

 - Simplify return conditions at probe time on Broadcom (Zheng Yongjun)

 - Fix workload name on PCH (Srinivas Pandruvada)

 - Migrate the devfreq cooling device code to the energy model API
   (Lukasz Luba)

 - Emit a warning if the thermal_zone_device_update is called without
   the .get_temp() ops (Daniel Lezcano)

 - Add critical and hot ops for the thermal zone (Daniel Lezcano)

 - Remove notification usage when critical is reached on rcar (Daniel
   Lezcano)

 - Fix devfreq build when ENERGY_MODEL is not set (Lukasz Luba)

* tag 'thermal-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (45 commits)
  thermal/drivers/devfreq_cooling: Fix the build when !ENERGY_MODEL
  thermal/drivers/rcar: Remove notification usage
  thermal/core: Add critical and hot ops
  thermal/core: Emit a warning if the thermal zone is updated without ops
  drm/panfrost: Register devfreq cooling and attempt to add Energy Model
  thermal: devfreq_cooling: remove old power model and use EM
  thermal: devfreq_cooling: add new registration functions with Energy Model
  thermal: devfreq_cooling: use a copy of device status
  thermal: devfreq_cooling: change tracing function and arguments
  thermal: int340x: processor_thermal: Correct workload type name
  thermal: broadcom: simplify the return expression of bcm2711_thermal_probe()
  thermal: intel: pch: use macro for temperature calculation
  thermal: int340x: processor_thermal: Add mailbox driver
  thermal: int340x: processor_thermal: Add RFIM driver
  thermal: int340x: processor_thermal: Add AlderLake PCI device id
  thermal: int340x: processor_thermal: Refactor MMIO interface
  thermal: rcar_gen3_thermal: Add r8a779a0 support
  dt-bindings: thermal: rcar-gen3-thermal: Add r8a779a0 support
  platform/x86/drivers/acerhdf: Check the interval value when it is set
  platform/x86/drivers/acerhdf: Use module_param_cb to set/get polling interval
  ...

31 files changed:
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.yaml
Documentation/devicetree/bindings/thermal/rcar-thermal.yaml
Documentation/driver-api/thermal/sysfs-api.rst
drivers/gpu/drm/panfrost/panfrost_devfreq.c
drivers/platform/x86/acerhdf.c
drivers/thermal/amlogic_thermal.c
drivers/thermal/broadcom/bcm2711_thermal.c
drivers/thermal/cpufreq_cooling.c
drivers/thermal/devfreq_cooling.c
drivers/thermal/gov_power_allocator.c
drivers/thermal/imx8mm_thermal.c
drivers/thermal/intel/int340x_thermal/Kconfig
drivers/thermal/intel/int340x_thermal/Makefile
drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
drivers/thermal/intel/int340x_thermal/processor_thermal_device.h [new file with mode: 0644]
drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c [new file with mode: 0644]
drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c [new file with mode: 0644]
drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c [new file with mode: 0644]
drivers/thermal/intel/intel_pch_thermal.c
drivers/thermal/mtk_thermal.c
drivers/thermal/rcar_gen3_thermal.c
drivers/thermal/rcar_thermal.c
drivers/thermal/sun8i_thermal.c
drivers/thermal/thermal_core.c
drivers/thermal/thermal_core.h
drivers/thermal/thermal_hwmon.c
drivers/thermal/thermal_sysfs.c
include/linux/devfreq_cooling.h
include/linux/thermal.h
include/trace/events/thermal.h

index 1e249c4..5c7e7bd 100644 (file)
@@ -14,18 +14,19 @@ Required properties:
   - "mediatek,mt2712-thermal" : For MT2712 family of SoCs
   - "mediatek,mt7622-thermal" : For MT7622 SoC
   - "mediatek,mt8183-thermal" : For MT8183 family of SoCs
+  - "mediatek,mt8516-thermal", "mediatek,mt2701-thermal : For MT8516 family of SoCs
 - reg: Address range of the thermal controller
 - interrupts: IRQ for the thermal controller
 - clocks, clock-names: Clocks needed for the thermal controller. required
                        clocks are:
                       "therm":  Main clock needed for register access
                       "auxadc": The AUXADC clock
-- resets: Reference to the reset controller controlling the thermal controller.
 - mediatek,auxadc: A phandle to the AUXADC which the thermal controller uses
 - mediatek,apmixedsys: A phandle to the APMIXEDSYS controller.
 - #thermal-sensor-cells : Should be 0. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
 
 Optional properties:
+- resets: Reference to the reset controller controlling the thermal controller.
 - nvmem-cells: A phandle to the calibration data provided by a nvmem device. If
                unspecified default values shall be used.
 - nvmem-cell-names: Should be "calibration-data"
index f386f2a..b33a76e 100644 (file)
@@ -26,13 +26,16 @@ properties:
       - renesas,r8a77961-thermal # R-Car M3-W+
       - renesas,r8a77965-thermal # R-Car M3-N
       - renesas,r8a77980-thermal # R-Car V3H
+      - renesas,r8a779a0-thermal # R-Car V3U
+
   reg:
     minItems: 2
-    maxItems: 3
+    maxItems: 4
     items:
       - description: TSC1 registers
       - description: TSC2 registers
       - description: TSC3 registers
+      - description: TSC4 registers
 
   interrupts:
     items:
@@ -55,12 +58,22 @@ properties:
 required:
   - compatible
   - reg
-  - interrupts
   - clocks
   - power-domains
   - resets
   - "#thermal-sensor-cells"
 
+if:
+  not:
+    properties:
+      compatible:
+        contains:
+          enum:
+            - renesas,r8a779a0-thermal
+then:
+  required:
+    - interrupts
+
 additionalProperties: false
 
 examples:
index 7e9557a..927de79 100644 (file)
@@ -62,25 +62,35 @@ properties:
   "#thermal-sensor-cells":
     const: 0
 
-if:
-  properties:
-    compatible:
-      contains:
-        enum:
-          - renesas,thermal-r8a73a4 # R-Mobile APE6
-          - renesas,thermal-r8a7779 # R-Car H1
-then:
-  required:
-    - compatible
-    - reg
-else:
-  required:
-    - compatible
-    - reg
-    - interrupts
-    - clocks
-    - power-domains
-    - resets
+required:
+  - compatible
+  - reg
+
+allOf:
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - renesas,thermal-r8a73a4 # R-Mobile APE6
+                - renesas,thermal-r8a7779 # R-Car H1
+    then:
+      required:
+        - resets
+        - '#thermal-sensor-cells'
+
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              const: renesas,thermal-r8a7779 # R-Car H1
+    then:
+      required:
+        - interrupts
+        - clocks
+        - power-domains
 
 additionalProperties: false
 
index b40b1f8..e7520cb 100644 (file)
@@ -654,8 +654,7 @@ stats/time_in_state_ms:
        The amount of time spent by the cooling device in various cooling
        states. The output will have "<state> <time>" pair in each line, which
        will mean this cooling device spent <time> msec of time at <state>.
-       Output will have one line for each of the supported states.  usertime
-       units here is 10mS (similar to other time exported in /proc).
+       Output will have one line for each of the supported states.
        RO, Required
 
 
index 913eaa6..7428b97 100644 (file)
@@ -138,7 +138,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
        }
        pfdevfreq->devfreq = devfreq;
 
-       cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
+       cooling = devfreq_cooling_em_register(devfreq, NULL);
        if (IS_ERR(cooling))
                DRM_DEV_INFO(dev, "Failed to register cooling device\n");
        else
index 44b6bfb..b6aa6e5 100644 (file)
@@ -84,8 +84,6 @@ static struct platform_device *acerhdf_dev;
 
 module_param(kernelmode, uint, 0);
 MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
-module_param(interval, uint, 0600);
-MODULE_PARM_DESC(interval, "Polling interval of temperature check");
 module_param(fanon, uint, 0600);
 MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
 module_param(fanoff, uint, 0600);
@@ -336,7 +334,10 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
                }
                if (verbose)
                        pr_notice("interval changed to: %d\n", interval);
-               thermal->polling_delay = interval*1000;
+
+               if (thermal)
+                       thermal->polling_delay = interval*1000;
+
                prev_interval = interval;
        }
 }
@@ -351,8 +352,6 @@ static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t)
 {
        int temp, err = 0;
 
-       acerhdf_check_param(thermal);
-
        err = acerhdf_get_temp(&temp);
        if (err)
                return err;
@@ -824,3 +823,24 @@ MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
 
 module_init(acerhdf_init);
 module_exit(acerhdf_exit);
+
+static int interval_set_uint(const char *val, const struct kernel_param *kp)
+{
+       int ret;
+
+       ret = param_set_uint(val, kp);
+       if (ret)
+               return ret;
+
+       acerhdf_check_param(thz_dev);
+
+       return 0;
+}
+
+static const struct kernel_param_ops interval_ops = {
+       .set = interval_set_uint,
+       .get = param_get_uint,
+};
+
+module_param_cb(interval, &interval_ops, &interval, 0600);
+MODULE_PARM_DESC(interval, "Polling interval of temperature check");
index ccb1fe1..dffe3ba 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/thermal.h>
 
 #include "thermal_core.h"
+#include "thermal_hwmon.h"
 
 #define TSENSOR_CFG_REG1                       0x4
        #define TSENSOR_CFG_REG1_RSET_VBG       BIT(12)
@@ -287,6 +288,9 @@ static int amlogic_thermal_probe(struct platform_device *pdev)
                return ret;
        }
 
+       if (devm_thermal_add_hwmon_sysfs(pdata->tzd))
+               dev_warn(&pdev->dev, "Failed to add hwmon sysfs attributes\n");
+
        ret = amlogic_thermal_initialize(pdata);
        if (ret)
                return ret;
index 67c2a73..1ec57d9 100644 (file)
@@ -102,11 +102,7 @@ static int bcm2711_thermal_probe(struct platform_device *pdev)
        priv->thermal = thermal;
 
        thermal->tzp->no_hwmon = false;
-       ret = thermal_add_hwmon_sysfs(thermal);
-       if (ret)
-               return ret;
-
-       return 0;
+       return thermal_add_hwmon_sysfs(thermal);
 }
 
 static struct platform_driver bcm2711_thermal_driver = {
index cc2959f..612f063 100644 (file)
@@ -438,13 +438,11 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
        if (cpufreq_cdev->cpufreq_state == state)
                return 0;
 
-       cpufreq_cdev->cpufreq_state = state;
-
        frequency = get_state_freq(cpufreq_cdev, state);
 
        ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
-
        if (ret > 0) {
+               cpufreq_cdev->cpufreq_state = state;
                cpus = cpufreq_cdev->policy->cpus;
                max_capacity = arch_scale_cpu_capacity(cpumask_first(cpus));
                capacity = frequency * max_capacity;
index dfab49a..fed3121 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/devfreq.h>
 #include <linux/devfreq_cooling.h>
+#include <linux/energy_model.h>
 #include <linux/export.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
@@ -33,36 +34,34 @@ static DEFINE_IDA(devfreq_ida);
  * @cdev:      Pointer to associated thermal cooling device.
  * @devfreq:   Pointer to associated devfreq device.
  * @cooling_state:     Current cooling state.
- * @power_table:       Pointer to table with maximum power draw for each
- *                     cooling state. State is the index into the table, and
- *                     the power is in mW.
  * @freq_table:        Pointer to a table with the frequencies sorted in descending
  *             order.  You can index the table by cooling device state
- * @freq_table_size:   Size of the @freq_table and @power_table
- * @power_ops: Pointer to devfreq_cooling_power, used to generate the
- *             @power_table.
+ * @max_state: It is the last index, that is, one less than the number of the
+ *             OPPs
+ * @power_ops: Pointer to devfreq_cooling_power, a more precised model.
  * @res_util:  Resource utilization scaling factor for the power.
  *             It is multiplied by 100 to minimize the error. It is used
  *             for estimation of the power budget instead of using
- *             'utilization' (which is 'busy_time / 'total_time').
- *             The 'res_util' range is from 100 to (power_table[state] * 100)
- *             for the corresponding 'state'.
+ *             'utilization' (which is 'busy_time' / 'total_time').
+ *             The 'res_util' range is from 100 to power * 100 for the
+ *             corresponding 'state'.
  * @capped_state:      index to cooling state with in dynamic power budget
  * @req_max_freq:      PM QoS request for limiting the maximum frequency
  *                     of the devfreq device.
+ * @em_pd:             Energy Model for the associated Devfreq device
  */
 struct devfreq_cooling_device {
        int id;
        struct thermal_cooling_device *cdev;
        struct devfreq *devfreq;
        unsigned long cooling_state;
-       u32 *power_table;
        u32 *freq_table;
-       size_t freq_table_size;
+       size_t max_state;
        struct devfreq_cooling_power *power_ops;
        u32 res_util;
        int capped_state;
        struct dev_pm_qos_request req_max_freq;
+       struct em_perf_domain *em_pd;
 };
 
 static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
@@ -70,7 +69,7 @@ static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
 {
        struct devfreq_cooling_device *dfc = cdev->devdata;
 
-       *state = dfc->freq_table_size - 1;
+       *state = dfc->max_state;
 
        return 0;
 }
@@ -92,16 +91,22 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
        struct devfreq *df = dfc->devfreq;
        struct device *dev = df->dev.parent;
        unsigned long freq;
+       int perf_idx;
 
        if (state == dfc->cooling_state)
                return 0;
 
        dev_dbg(dev, "Setting cooling state %lu\n", state);
 
-       if (state >= dfc->freq_table_size)
+       if (state > dfc->max_state)
                return -EINVAL;
 
-       freq = dfc->freq_table[state];
+       if (dfc->em_pd) {
+               perf_idx = dfc->max_state - state;
+               freq = dfc->em_pd->table[perf_idx].frequency * 1000;
+       } else {
+               freq = dfc->freq_table[state];
+       }
 
        dev_pm_qos_update_request(&dfc->req_max_freq,
                                  DIV_ROUND_UP(freq, HZ_PER_KHZ));
@@ -112,24 +117,23 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
 }
 
 /**
- * freq_get_state() - get the cooling state corresponding to a frequency
- * @dfc:       Pointer to devfreq cooling device
- * @freq:      frequency in Hz
+ * get_perf_idx() - get the performance index corresponding to a frequency
+ * @em_pd:     Pointer to device's Energy Model
+ * @freq:      frequency in kHz
  *
- * Return: the cooling state associated with the @freq, or
- * THERMAL_CSTATE_INVALID if it wasn't found.
+ * Return: the performance index associated with the @freq, or
+ * -EINVAL if it wasn't found.
  */
-static unsigned long
-freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq)
+static int get_perf_idx(struct em_perf_domain *em_pd, unsigned long freq)
 {
        int i;
 
-       for (i = 0; i < dfc->freq_table_size; i++) {
-               if (dfc->freq_table[i] == freq)
+       for (i = 0; i < em_pd->nr_perf_states; i++) {
+               if (em_pd->table[i].frequency == freq)
                        return i;
        }
 
-       return THERMAL_CSTATE_INVALID;
+       return -EINVAL;
 }
 
 static unsigned long get_voltage(struct devfreq *df, unsigned long freq)
@@ -160,94 +164,38 @@ static unsigned long get_voltage(struct devfreq *df, unsigned long freq)
        return voltage;
 }
 
-/**
- * get_static_power() - calculate the static power
- * @dfc:       Pointer to devfreq cooling device
- * @freq:      Frequency in Hz
- *
- * Calculate the static power in milliwatts using the supplied
- * get_static_power().  The current voltage is calculated using the
- * OPP library.  If no get_static_power() was supplied, assume the
- * static power is negligible.
- */
-static unsigned long
-get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq)
+static void _normalize_load(struct devfreq_dev_status *status)
 {
-       struct devfreq *df = dfc->devfreq;
-       unsigned long voltage;
-
-       if (!dfc->power_ops->get_static_power)
-               return 0;
-
-       voltage = get_voltage(df, freq);
-
-       if (voltage == 0)
-               return 0;
-
-       return dfc->power_ops->get_static_power(df, voltage);
-}
-
-/**
- * get_dynamic_power - calculate the dynamic power
- * @dfc:       Pointer to devfreq cooling device
- * @freq:      Frequency in Hz
- * @voltage:   Voltage in millivolts
- *
- * Calculate the dynamic power in milliwatts consumed by the device at
- * frequency @freq and voltage @voltage.  If the get_dynamic_power()
- * was supplied as part of the devfreq_cooling_power struct, then that
- * function is used.  Otherwise, a simple power model (Pdyn = Coeff *
- * Voltage^2 * Frequency) is used.
- */
-static unsigned long
-get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq,
-                 unsigned long voltage)
-{
-       u64 power;
-       u32 freq_mhz;
-       struct devfreq_cooling_power *dfc_power = dfc->power_ops;
-
-       if (dfc_power->get_dynamic_power)
-               return dfc_power->get_dynamic_power(dfc->devfreq, freq,
-                                                   voltage);
-
-       freq_mhz = freq / 1000000;
-       power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage;
-       do_div(power, 1000000000);
-
-       return power;
-}
+       if (status->total_time > 0xfffff) {
+               status->total_time >>= 10;
+               status->busy_time >>= 10;
+       }
 
+       status->busy_time <<= 10;
+       status->busy_time /= status->total_time ? : 1;
 
-static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc,
-                                           unsigned long freq,
-                                           unsigned long voltage)
-{
-       return get_static_power(dfc, freq) + get_dynamic_power(dfc, freq,
-                                                              voltage);
+       status->busy_time = status->busy_time ? : 1;
+       status->total_time = 1024;
 }
 
-
 static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev,
                                               u32 *power)
 {
        struct devfreq_cooling_device *dfc = cdev->devdata;
        struct devfreq *df = dfc->devfreq;
-       struct devfreq_dev_status *status = &df->last_status;
+       struct devfreq_dev_status status;
        unsigned long state;
-       unsigned long freq = status->current_frequency;
+       unsigned long freq;
        unsigned long voltage;
-       u32 dyn_power = 0;
-       u32 static_power = 0;
-       int res;
-
-       state = freq_get_state(dfc, freq);
-       if (state == THERMAL_CSTATE_INVALID) {
-               res = -EAGAIN;
-               goto fail;
-       }
+       int res, perf_idx;
+
+       mutex_lock(&df->lock);
+       status = df->last_status;
+       mutex_unlock(&df->lock);
 
-       if (dfc->power_ops->get_real_power) {
+       freq = status.current_frequency;
+
+       if (dfc->power_ops && dfc->power_ops->get_real_power) {
                voltage = get_voltage(df, freq);
                if (voltage == 0) {
                        res = -EINVAL;
@@ -257,7 +205,7 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
                res = dfc->power_ops->get_real_power(df, power, freq, voltage);
                if (!res) {
                        state = dfc->capped_state;
-                       dfc->res_util = dfc->power_table[state];
+                       dfc->res_util = dfc->em_pd->table[state].power;
                        dfc->res_util *= SCALE_ERROR_MITIGATION;
 
                        if (*power > 1)
@@ -266,19 +214,22 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
                        goto fail;
                }
        } else {
-               dyn_power = dfc->power_table[state];
+               /* Energy Model frequencies are in kHz */
+               perf_idx = get_perf_idx(dfc->em_pd, freq / 1000);
+               if (perf_idx < 0) {
+                       res = -EAGAIN;
+                       goto fail;
+               }
 
-               /* Scale dynamic power for utilization */
-               dyn_power *= status->busy_time;
-               dyn_power /= status->total_time;
-               /* Get static power */
-               static_power = get_static_power(dfc, freq);
+               _normalize_load(&status);
 
-               *power = dyn_power + static_power;
+               /* Scale power for utilization */
+               *power = dfc->em_pd->table[perf_idx].power;
+               *power *= status.busy_time;
+               *power >>= 10;
        }
 
-       trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power,
-                                             static_power, *power);
+       trace_thermal_power_devfreq_get_power(cdev, &status, freq, *power);
 
        return 0;
 fail:
@@ -288,20 +239,17 @@ fail:
 }
 
 static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
-                                      unsigned long state,
-                                      u32 *power)
+                                      unsigned long state, u32 *power)
 {
        struct devfreq_cooling_device *dfc = cdev->devdata;
-       unsigned long freq;
-       u32 static_power;
+       int perf_idx;
 
-       if (state >= dfc->freq_table_size)
+       if (state > dfc->max_state)
                return -EINVAL;
 
-       freq = dfc->freq_table[state];
-       static_power = get_static_power(dfc, freq);
+       perf_idx = dfc->max_state - state;
+       *power = dfc->em_pd->table[perf_idx].power;
 
-       *power = dfc->power_table[state] + static_power;
        return 0;
 }
 
@@ -310,39 +258,39 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
 {
        struct devfreq_cooling_device *dfc = cdev->devdata;
        struct devfreq *df = dfc->devfreq;
-       struct devfreq_dev_status *status = &df->last_status;
-       unsigned long freq = status->current_frequency;
-       unsigned long busy_time;
-       s32 dyn_power;
-       u32 static_power;
+       struct devfreq_dev_status status;
+       unsigned long freq;
        s32 est_power;
        int i;
 
-       if (dfc->power_ops->get_real_power) {
+       mutex_lock(&df->lock);
+       status = df->last_status;
+       mutex_unlock(&df->lock);
+
+       freq = status.current_frequency;
+
+       if (dfc->power_ops && dfc->power_ops->get_real_power) {
                /* Scale for resource utilization */
                est_power = power * dfc->res_util;
                est_power /= SCALE_ERROR_MITIGATION;
        } else {
-               static_power = get_static_power(dfc, freq);
-
-               dyn_power = power - static_power;
-               dyn_power = dyn_power > 0 ? dyn_power : 0;
-
                /* Scale dynamic power for utilization */
-               busy_time = status->busy_time ?: 1;
-               est_power = (dyn_power * status->total_time) / busy_time;
+               _normalize_load(&status);
+               est_power = power << 10;
+               est_power /= status.busy_time;
        }
 
        /*
         * Find the first cooling state that is within the power
-        * budget for dynamic power.
+        * budget. The EM power table is sorted ascending.
         */
-       for (i = 0; i < dfc->freq_table_size - 1; i++)
-               if (est_power >= dfc->power_table[i])
+       for (i = dfc->max_state; i > 0; i--)
+               if (est_power >= dfc->em_pd->table[i].power)
                        break;
 
-       *state = i;
-       dfc->capped_state = i;
+       *state = dfc->max_state - i;
+       dfc->capped_state = *state;
+
        trace_thermal_power_devfreq_limit(cdev, freq, *state, power);
        return 0;
 }
@@ -354,91 +302,43 @@ static struct thermal_cooling_device_ops devfreq_cooling_ops = {
 };
 
 /**
- * devfreq_cooling_gen_tables() - Generate power and freq tables.
- * @dfc: Pointer to devfreq cooling device.
- *
- * Generate power and frequency tables: the power table hold the
- * device's maximum power usage at each cooling state (OPP).  The
- * static and dynamic power using the appropriate voltage and
- * frequency for the state, is acquired from the struct
- * devfreq_cooling_power, and summed to make the maximum power draw.
+ * devfreq_cooling_gen_tables() - Generate frequency table.
+ * @dfc:       Pointer to devfreq cooling device.
+ * @num_opps:  Number of OPPs
  *
- * The frequency table holds the frequencies in descending order.
- * That way its indexed by cooling device state.
- *
- * The tables are malloced, and pointers put in dfc.  They must be
- * freed when unregistering the devfreq cooling device.
+ * Generate frequency table which holds the frequencies in descending
+ * order. That way its indexed by cooling device state. This is for
+ * compatibility with drivers which do not register Energy Model.
  *
  * Return: 0 on success, negative error code on failure.
  */
-static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
+static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc,
+                                     int num_opps)
 {
        struct devfreq *df = dfc->devfreq;
        struct device *dev = df->dev.parent;
-       int ret, num_opps;
        unsigned long freq;
-       u32 *power_table = NULL;
-       u32 *freq_table;
        int i;
 
-       num_opps = dev_pm_opp_get_opp_count(dev);
-
-       if (dfc->power_ops) {
-               power_table = kcalloc(num_opps, sizeof(*power_table),
-                                     GFP_KERNEL);
-               if (!power_table)
-                       return -ENOMEM;
-       }
-
-       freq_table = kcalloc(num_opps, sizeof(*freq_table),
+       dfc->freq_table = kcalloc(num_opps, sizeof(*dfc->freq_table),
                             GFP_KERNEL);
-       if (!freq_table) {
-               ret = -ENOMEM;
-               goto free_power_table;
-       }
+       if (!dfc->freq_table)
+               return -ENOMEM;
 
        for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) {
-               unsigned long power, voltage;
                struct dev_pm_opp *opp;
 
                opp = dev_pm_opp_find_freq_floor(dev, &freq);
                if (IS_ERR(opp)) {
-                       ret = PTR_ERR(opp);
-                       goto free_tables;
+                       kfree(dfc->freq_table);
+                       return PTR_ERR(opp);
                }
 
-               voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */
                dev_pm_opp_put(opp);
-
-               if (dfc->power_ops) {
-                       if (dfc->power_ops->get_real_power)
-                               power = get_total_power(dfc, freq, voltage);
-                       else
-                               power = get_dynamic_power(dfc, freq, voltage);
-
-                       dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n",
-                               freq / 1000000, voltage, power, power);
-
-                       power_table[i] = power;
-               }
-
-               freq_table[i] = freq;
+               dfc->freq_table[i] = freq;
        }
 
-       if (dfc->power_ops)
-               dfc->power_table = power_table;
-
-       dfc->freq_table = freq_table;
-       dfc->freq_table_size = num_opps;
-
        return 0;
-
-free_tables:
-       kfree(freq_table);
-free_power_table:
-       kfree(power_table);
-
-       return ret;
 }
 
 /**
@@ -461,9 +361,10 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
                                  struct devfreq_cooling_power *dfc_power)
 {
        struct thermal_cooling_device *cdev;
+       struct device *dev = df->dev.parent;
        struct devfreq_cooling_device *dfc;
        char dev_name[THERMAL_NAME_LENGTH];
-       int err;
+       int err, num_opps;
 
        dfc = kzalloc(sizeof(*dfc), GFP_KERNEL);
        if (!dfc)
@@ -471,28 +372,45 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
 
        dfc->devfreq = df;
 
-       if (dfc_power) {
-               dfc->power_ops = dfc_power;
-
+       dfc->em_pd = em_pd_get(dev);
+       if (dfc->em_pd) {
                devfreq_cooling_ops.get_requested_power =
                        devfreq_cooling_get_requested_power;
                devfreq_cooling_ops.state2power = devfreq_cooling_state2power;
                devfreq_cooling_ops.power2state = devfreq_cooling_power2state;
+
+               dfc->power_ops = dfc_power;
+
+               num_opps = em_pd_nr_perf_states(dfc->em_pd);
+       } else {
+               /* Backward compatibility for drivers which do not use IPA */
+               dev_dbg(dev, "missing EM for cooling device\n");
+
+               num_opps = dev_pm_opp_get_opp_count(dev);
+
+               err = devfreq_cooling_gen_tables(dfc, num_opps);
+               if (err)
+                       goto free_dfc;
        }
 
-       err = devfreq_cooling_gen_tables(dfc);
-       if (err)
+       if (num_opps <= 0) {
+               err = -EINVAL;
                goto free_dfc;
+       }
+
+       /* max_state is an index, not a counter */
+       dfc->max_state = num_opps - 1;
 
-       err = dev_pm_qos_add_request(df->dev.parent, &dfc->req_max_freq,
+       err = dev_pm_qos_add_request(dev, &dfc->req_max_freq,
                                     DEV_PM_QOS_MAX_FREQUENCY,
                                     PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
        if (err < 0)
-               goto free_tables;
+               goto free_table;
 
        err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL);
        if (err < 0)
                goto remove_qos_req;
+
        dfc->id = err;
 
        snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id);
@@ -501,7 +419,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
                                                  &devfreq_cooling_ops);
        if (IS_ERR(cdev)) {
                err = PTR_ERR(cdev);
-               dev_err(df->dev.parent,
+               dev_err(dev,
                        "Failed to register devfreq cooling device (%d)\n",
                        err);
                goto release_ida;
@@ -513,12 +431,9 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
 
 release_ida:
        ida_simple_remove(&devfreq_ida, dfc->id);
-
 remove_qos_req:
        dev_pm_qos_remove_request(&dfc->req_max_freq);
-
-free_tables:
-       kfree(dfc->power_table);
+free_table:
        kfree(dfc->freq_table);
 free_dfc:
        kfree(dfc);
@@ -551,24 +466,73 @@ struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df)
 EXPORT_SYMBOL_GPL(devfreq_cooling_register);
 
 /**
+ * devfreq_cooling_em_register_power() - Register devfreq cooling device with
+ *             power information and automatically register Energy Model (EM)
+ * @df:                Pointer to devfreq device.
+ * @dfc_power: Pointer to devfreq_cooling_power.
+ *
+ * Register a devfreq cooling device and automatically register EM. The
+ * available OPPs must be registered for the device.
+ *
+ * If @dfc_power is provided, the cooling device is registered with the
+ * power extensions. It is using the simple Energy Model which requires
+ * "dynamic-power-coefficient" a devicetree property. To not break drivers
+ * which miss that DT property, the function won't bail out when the EM
+ * registration failed. The cooling device will be registered if everything
+ * else is OK.
+ */
+struct thermal_cooling_device *
+devfreq_cooling_em_register(struct devfreq *df,
+                           struct devfreq_cooling_power *dfc_power)
+{
+       struct thermal_cooling_device *cdev;
+       struct device *dev;
+       int ret;
+
+       if (IS_ERR_OR_NULL(df))
+               return ERR_PTR(-EINVAL);
+
+       dev = df->dev.parent;
+
+       ret = dev_pm_opp_of_register_em(dev, NULL);
+       if (ret)
+               dev_dbg(dev, "Unable to register EM for devfreq cooling device (%d)\n",
+                       ret);
+
+       cdev = of_devfreq_cooling_register_power(dev->of_node, df, dfc_power);
+
+       if (IS_ERR_OR_NULL(cdev))
+               em_dev_unregister_perf_domain(dev);
+
+       return cdev;
+}
+EXPORT_SYMBOL_GPL(devfreq_cooling_em_register);
+
+/**
  * devfreq_cooling_unregister() - Unregister devfreq cooling device.
  * @cdev: Pointer to devfreq cooling device to unregister.
+ *
+ * Unregisters devfreq cooling device and related Energy Model if it was
+ * present.
  */
 void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {
        struct devfreq_cooling_device *dfc;
+       struct device *dev;
 
-       if (!cdev)
+       if (IS_ERR_OR_NULL(cdev))
                return;
 
        dfc = cdev->devdata;
+       dev = dfc->devfreq->dev.parent;
 
        thermal_cooling_device_unregister(dfc->cdev);
        ida_simple_remove(&devfreq_ida, dfc->id);
        dev_pm_qos_remove_request(&dfc->req_max_freq);
-       kfree(dfc->power_table);
-       kfree(dfc->freq_table);
 
+       em_dev_unregister_perf_domain(dev);
+
+       kfree(dfc->freq_table);
        kfree(dfc);
 }
 EXPORT_SYMBOL_GPL(devfreq_cooling_unregister);
index ab0be26..7a4170a 100644 (file)
@@ -63,6 +63,8 @@ static inline s64 div_frac(s64 x, s64 y)
  * @trip_max_desired_temperature:      last passive trip point of the thermal
  *                                     zone.  The temperature we are
  *                                     controlling for.
+ * @sustainable_power: Sustainable power (heat) that this thermal zone can
+ *                     dissipate
  */
 struct power_allocator_params {
        bool allocated_tzp;
@@ -70,6 +72,7 @@ struct power_allocator_params {
        s32 prev_err;
        int trip_switch_on;
        int trip_max_desired_temperature;
+       u32 sustainable_power;
 };
 
 /**
@@ -96,7 +99,10 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
                if (instance->trip != params->trip_max_desired_temperature)
                        continue;
 
-               if (power_actor_get_min_power(cdev, &min_power))
+               if (!cdev_is_power_actor(cdev))
+                       continue;
+
+               if (cdev->ops->state2power(cdev, instance->upper, &min_power))
                        continue;
 
                sustainable_power += min_power;
@@ -111,26 +117,18 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
  * @sustainable_power: sustainable power for the thermal zone
  * @trip_switch_on:    trip point number for the switch on temperature
  * @control_temp:      target temperature for the power allocator governor
- * @force:     whether to force the update of the constants
  *
  * This function is used to update the estimation of the PID
  * controller constants in struct thermal_zone_parameters.
- * Sustainable power is provided in case it was estimated.  The
- * estimated sustainable_power should not be stored in the
- * thermal_zone_parameters so it has to be passed explicitly to this
- * function.
- *
- * If @force is not set, the values in the thermal zone's parameters
- * are preserved if they are not zero.  If @force is set, the values
- * in thermal zone's parameters are overwritten.
  */
 static void estimate_pid_constants(struct thermal_zone_device *tz,
                                   u32 sustainable_power, int trip_switch_on,
-                                  int control_temp, bool force)
+                                  int control_temp)
 {
        int ret;
        int switch_on_temp;
        u32 temperature_threshold;
+       s32 k_i;
 
        ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
        if (ret)
@@ -148,16 +146,15 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
        if (!temperature_threshold)
                return;
 
-       if (!tz->tzp->k_po || force)
-               tz->tzp->k_po = int_to_frac(sustainable_power) /
-                       temperature_threshold;
+       tz->tzp->k_po = int_to_frac(sustainable_power) /
+               temperature_threshold;
+
+       tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
+               temperature_threshold;
 
-       if (!tz->tzp->k_pu || force)
-               tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
-                       temperature_threshold;
+       k_i = tz->tzp->k_pu / 10;
+       tz->tzp->k_i = k_i > 0 ? k_i : 1;
 
-       if (!tz->tzp->k_i || force)
-               tz->tzp->k_i = int_to_frac(10) / 1000;
        /*
         * The default for k_d and integral_cutoff is 0, so we can
         * leave them as they are.
@@ -165,6 +162,41 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
 }
 
 /**
+ * get_sustainable_power() - Get the right sustainable power
+ * @tz:                thermal zone for which to estimate the constants
+ * @params:    parameters for the power allocator governor
+ * @control_temp:      target temperature for the power allocator governor
+ *
+ * This function is used for getting the proper sustainable power value based
+ * on variables which might be updated by the user sysfs interface. If that
+ * happen the new value is going to be estimated and updated. It is also used
+ * after thermal zone binding, where the initial values where set to 0.
+ */
+static u32 get_sustainable_power(struct thermal_zone_device *tz,
+                                struct power_allocator_params *params,
+                                int control_temp)
+{
+       u32 sustainable_power;
+
+       if (!tz->tzp->sustainable_power)
+               sustainable_power = estimate_sustainable_power(tz);
+       else
+               sustainable_power = tz->tzp->sustainable_power;
+
+       /* Check if it's init value 0 or there was update via sysfs */
+       if (sustainable_power != params->sustainable_power) {
+               estimate_pid_constants(tz, sustainable_power,
+                                      params->trip_switch_on, control_temp);
+
+               /* Do the estimation only once and make available in sysfs */
+               tz->tzp->sustainable_power = sustainable_power;
+               params->sustainable_power = sustainable_power;
+       }
+
+       return sustainable_power;
+}
+
+/**
  * pid_controller() - PID controller
  * @tz:        thermal zone we are operating in
  * @control_temp:      the target temperature in millicelsius
@@ -193,14 +225,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
 
        max_power_frac = int_to_frac(max_allocatable_power);
 
-       if (tz->tzp->sustainable_power) {
-               sustainable_power = tz->tzp->sustainable_power;
-       } else {
-               sustainable_power = estimate_sustainable_power(tz);
-               estimate_pid_constants(tz, sustainable_power,
-                                      params->trip_switch_on, control_temp,
-                                      true);
-       }
+       sustainable_power = get_sustainable_power(tz, params, control_temp);
 
        err = control_temp - tz->temperature;
        err = int_to_frac(err);
@@ -252,6 +277,38 @@ static u32 pid_controller(struct thermal_zone_device *tz,
 }
 
 /**
+ * power_actor_set_power() - limit the maximum power a cooling device consumes
+ * @cdev:      pointer to &thermal_cooling_device
+ * @instance:  thermal instance to update
+ * @power:     the power in milliwatts
+ *
+ * Set the cooling device to consume at most @power milliwatts. The limit is
+ * expected to be a cap at the maximum power consumption.
+ *
+ * Return: 0 on success, -EINVAL if the cooling device does not
+ * implement the power actor API or -E* for other failures.
+ */
+static int
+power_actor_set_power(struct thermal_cooling_device *cdev,
+                     struct thermal_instance *instance, u32 power)
+{
+       unsigned long state;
+       int ret;
+
+       ret = cdev->ops->power2state(cdev, power, &state);
+       if (ret)
+               return ret;
+
+       instance->target = clamp_val(state, instance->lower, instance->upper);
+       mutex_lock(&cdev->lock);
+       cdev->updated = false;
+       mutex_unlock(&cdev->lock);
+       thermal_cdev_update(cdev);
+
+       return 0;
+}
+
+/**
  * divvy_up_power() - divvy the allocated power between the actors
  * @req_power: each actor's requested power
  * @max_power: each actor's maximum available power
@@ -398,7 +455,8 @@ static int allocate_power(struct thermal_zone_device *tz,
 
                weighted_req_power[i] = frac_to_int(weight * req_power[i]);
 
-               if (power_actor_get_max_power(cdev, &max_power[i]))
+               if (cdev->ops->state2power(cdev, instance->lower,
+                                          &max_power[i]))
                        continue;
 
                total_req_power += req_power[i];
@@ -572,7 +630,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
                if (!ret)
                        estimate_pid_constants(tz, tz->tzp->sustainable_power,
                                               params->trip_switch_on,
-                                              control_temp, false);
+                                              control_temp);
        }
 
        reset_pid_controller(params);
index a1e4f9b..7442e01 100644 (file)
@@ -166,10 +166,11 @@ static int imx8mm_tmu_probe(struct platform_device *pdev)
                                                             &tmu->sensors[i],
                                                             &tmu_tz_ops);
                if (IS_ERR(tmu->sensors[i].tzd)) {
+                       ret = PTR_ERR(tmu->sensors[i].tzd);
                        dev_err(&pdev->dev,
                                "failed to register thermal zone sensor[%d]: %d\n",
                                i, ret);
-                       return PTR_ERR(tmu->sensors[i].tzd);
+                       goto disable_clk;
                }
                tmu->sensors[i].hw_id = i;
        }
@@ -184,6 +185,10 @@ static int imx8mm_tmu_probe(struct platform_device *pdev)
        imx8mm_tmu_enable(tmu, true);
 
        return 0;
+
+disable_clk:
+       clk_disable_unprepare(tmu->clk);
+       return ret;
 }
 
 static int imx8mm_tmu_remove(struct platform_device *pdev)
index 7979075..45c31f3 100644 (file)
@@ -10,6 +10,7 @@ config INT340X_THERMAL
        select ACPI_THERMAL_REL
        select ACPI_FAN
        select INTEL_SOC_DTS_IOSF_CORE
+       select PROC_THERMAL_MMIO_RAPL if X86_64 && POWERCAP
        help
          Newer laptops and tablets that use ACPI may have thermal sensors and
          other devices with thermal control capabilities outside the core
@@ -41,9 +42,6 @@ config INT3406_THERMAL
          power consumed by display device.
 
 config PROC_THERMAL_MMIO_RAPL
-       bool
-       depends on 64BIT
-       depends on POWERCAP
+       tristate
        select INTEL_RAPL_CORE
-       default y
 endif
index 287eb0a..38a2731 100644 (file)
@@ -4,5 +4,8 @@ obj-$(CONFIG_INT340X_THERMAL)   += int340x_thermal_zone.o
 obj-$(CONFIG_INT340X_THERMAL)  += int3402_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)  += int3403_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)  += processor_thermal_device.o
+obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
+obj-$(CONFIG_INT340X_THERMAL)  += processor_thermal_rfim.o
+obj-$(CONFIG_INT340X_THERMAL)  += processor_thermal_mbox.o
 obj-$(CONFIG_INT3406_THERMAL)  += int3406_thermal.o
 obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
index 81e8b15..9e6f2a8 100644 (file)
 #include <linux/acpi.h>
 #include <linux/thermal.h>
 #include <linux/cpuhotplug.h>
-#include <linux/intel_rapl.h>
 #include "int340x_thermal_zone.h"
+#include "processor_thermal_device.h"
 #include "../intel_soc_dts_iosf.h"
 
-/* Broadwell-U/HSB thermal reporting device */
-#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
-#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
-
-/* Skylake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903
-
-/* CannonLake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_CNL_THERMAL 0x5a03
-#define PCI_DEVICE_ID_PROC_CFL_THERMAL 0x3E83
-
-/* Braswell thermal reporting device */
-#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
-
-/* Broxton thermal reporting device */
-#define PCI_DEVICE_ID_PROC_BXT0_THERMAL  0x0A8C
-#define PCI_DEVICE_ID_PROC_BXT1_THERMAL  0x1A8C
-#define PCI_DEVICE_ID_PROC_BXTX_THERMAL  0x4A8C
-#define PCI_DEVICE_ID_PROC_BXTP_THERMAL  0x5A8C
-
-/* GeminiLake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_GLK_THERMAL 0x318C
-
-/* IceLake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_ICL_THERMAL 0x8a03
-
-/* JasperLake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_JSL_THERMAL 0x4E03
-
-/* TigerLake thermal reporting device */
-#define PCI_DEVICE_ID_PROC_TGL_THERMAL 0x9A03
-
 #define DRV_NAME "proc_thermal"
 
-struct power_config {
-       u32     index;
-       u32     min_uw;
-       u32     max_uw;
-       u32     tmin_us;
-       u32     tmax_us;
-       u32     step_uw;
-};
-
-struct proc_thermal_device {
-       struct device *dev;
-       struct acpi_device *adev;
-       struct power_config power_limits[2];
-       struct int34x_thermal_zone *int340x_zone;
-       struct intel_soc_dts_sensors *soc_dts;
-       void __iomem *mmio_base;
-};
-
 enum proc_thermal_emum_mode_type {
        PROC_THERMAL_NONE,
        PROC_THERMAL_PCI,
        PROC_THERMAL_PLATFORM_DEV
 };
 
-struct rapl_mmio_regs {
-       u64 reg_unit;
-       u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
-       int limits[RAPL_DOMAIN_MAX];
-};
-
 /*
  * We can have only one type of enumeration, PCI or Platform,
  * not both. So we don't need instance specific data.
@@ -461,152 +405,87 @@ static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid)
        return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_PROC_THERMAL_MMIO_RAPL
-
 #define MCHBAR 0
 
-/* RAPL Support via MMIO interface */
-static struct rapl_if_priv rapl_mmio_priv;
-
-static int rapl_mmio_cpu_online(unsigned int cpu)
+static int proc_thermal_set_mmio_base(struct pci_dev *pdev,
+                                     struct proc_thermal_device *proc_priv)
 {
-       struct rapl_package *rp;
-
-       /* mmio rapl supports package 0 only for now */
-       if (topology_physical_package_id(cpu))
-               return 0;
+       int ret;
 
-       rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
-       if (!rp) {
-               rp = rapl_add_package(cpu, &rapl_mmio_priv);
-               if (IS_ERR(rp))
-                       return PTR_ERR(rp);
+       ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME);
+       if (ret) {
+               dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+               return -ENOMEM;
        }
-       cpumask_set_cpu(cpu, &rp->cpumask);
-       return 0;
-}
-
-static int rapl_mmio_cpu_down_prep(unsigned int cpu)
-{
-       struct rapl_package *rp;
-       int lead_cpu;
-
-       rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
-       if (!rp)
-               return 0;
-
-       cpumask_clear_cpu(cpu, &rp->cpumask);
-       lead_cpu = cpumask_first(&rp->cpumask);
-       if (lead_cpu >= nr_cpu_ids)
-               rapl_remove_package(rp);
-       else if (rp->lead_cpu == cpu)
-               rp->lead_cpu = lead_cpu;
-       return 0;
-}
-
-static int rapl_mmio_read_raw(int cpu, struct reg_action *ra)
-{
-       if (!ra->reg)
-               return -EINVAL;
 
-       ra->value = readq((void __iomem *)ra->reg);
-       ra->value &= ra->mask;
-       return 0;
-}
-
-static int rapl_mmio_write_raw(int cpu, struct reg_action *ra)
-{
-       u64 val;
-
-       if (!ra->reg)
-               return -EINVAL;
+       proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR];
 
-       val = readq((void __iomem *)ra->reg);
-       val &= ~ra->mask;
-       val |= ra->value;
-       writeq(val, (void __iomem *)ra->reg);
        return 0;
 }
 
-static int proc_thermal_rapl_add(struct pci_dev *pdev,
+static int proc_thermal_mmio_add(struct pci_dev *pdev,
                                 struct proc_thermal_device *proc_priv,
-                                struct rapl_mmio_regs *rapl_regs)
+                                kernel_ulong_t feature_mask)
 {
-       enum rapl_domain_reg_id reg;
-       enum rapl_domain_type domain;
        int ret;
 
-       if (!rapl_regs)
-               return 0;
+       proc_priv->mmio_feature_mask = feature_mask;
 
-       ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME);
-       if (ret) {
-               dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
-               return -ENOMEM;
+       if (feature_mask) {
+               ret = proc_thermal_set_mmio_base(pdev, proc_priv);
+               if (ret)
+                       return ret;
        }
 
-       proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR];
-
-       for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) {
-               for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++)
-                       if (rapl_regs->regs[domain][reg])
-                               rapl_mmio_priv.regs[domain][reg] =
-                                               (u64)proc_priv->mmio_base +
-                                               rapl_regs->regs[domain][reg];
-               rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain];
+       if (feature_mask & PROC_THERMAL_FEATURE_RAPL) {
+               ret = proc_thermal_rapl_add(pdev, proc_priv);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to add RAPL MMIO interface\n");
+                       return ret;
+               }
        }
-       rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit;
-
-       rapl_mmio_priv.read_raw = rapl_mmio_read_raw;
-       rapl_mmio_priv.write_raw = rapl_mmio_write_raw;
 
-       rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL);
-       if (IS_ERR(rapl_mmio_priv.control_type)) {
-               pr_debug("failed to register powercap control_type.\n");
-               return PTR_ERR(rapl_mmio_priv.control_type);
+       if (feature_mask & PROC_THERMAL_FEATURE_FIVR ||
+           feature_mask & PROC_THERMAL_FEATURE_DVFS) {
+               ret = proc_thermal_rfim_add(pdev, proc_priv);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to add RFIM interface\n");
+                       goto err_rem_rapl;
+               }
        }
 
-       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
-                               rapl_mmio_cpu_online, rapl_mmio_cpu_down_prep);
-       if (ret < 0) {
-               powercap_unregister_control_type(rapl_mmio_priv.control_type);
-               rapl_mmio_priv.control_type = NULL;
-               return ret;
+       if (feature_mask & PROC_THERMAL_FEATURE_MBOX) {
+               ret = proc_thermal_mbox_add(pdev, proc_priv);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to add MBOX interface\n");
+                       goto err_rem_rfim;
+               }
        }
-       rapl_mmio_priv.pcap_rapl_online = ret;
 
        return 0;
-}
 
-static void proc_thermal_rapl_remove(void)
-{
-       if (IS_ERR_OR_NULL(rapl_mmio_priv.control_type))
-               return;
+err_rem_rfim:
+       proc_thermal_rfim_remove(pdev);
+err_rem_rapl:
+       proc_thermal_rapl_remove();
 
-       cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online);
-       powercap_unregister_control_type(rapl_mmio_priv.control_type);
+       return ret;
 }
 
-static const struct rapl_mmio_regs rapl_mmio_hsw = {
-       .reg_unit = 0x5938,
-       .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930},
-       .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0},
-       .limits[RAPL_DOMAIN_PACKAGE] = 2,
-       .limits[RAPL_DOMAIN_DRAM] = 2,
-};
+static void proc_thermal_mmio_remove(struct pci_dev *pdev)
+{
+       struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
 
-#else
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL)
+               proc_thermal_rapl_remove();
 
-static int proc_thermal_rapl_add(struct pci_dev *pdev,
-                                struct proc_thermal_device *proc_priv,
-                                struct rapl_mmio_regs *rapl_regs)
-{
-       return 0;
-}
-static void proc_thermal_rapl_remove(void) {}
-static const struct rapl_mmio_regs rapl_mmio_hsw;
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR ||
+           proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
+               proc_thermal_rfim_remove(pdev);
 
-#endif /* CONFIG_MMIO_RAPL */
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_MBOX)
+               proc_thermal_mbox_remove(pdev);
+}
 
 static int  proc_thermal_pci_probe(struct pci_dev *pdev,
                                   const struct pci_device_id *id)
@@ -629,18 +508,10 @@ static int  proc_thermal_pci_probe(struct pci_dev *pdev,
        if (ret)
                return ret;
 
-       ret = proc_thermal_rapl_add(pdev, proc_priv,
-                               (struct rapl_mmio_regs *)id->driver_data);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to add RAPL MMIO interface\n");
-               proc_thermal_remove(proc_priv);
-               return ret;
-       }
-
        pci_set_drvdata(pdev, proc_priv);
        proc_thermal_emum_mode = PROC_THERMAL_PCI;
 
-       if (pdev->device == PCI_DEVICE_ID_PROC_BSW_THERMAL) {
+       if (pdev->device == PCI_DEVICE_ID_INTEL_BSW_THERMAL) {
                /*
                 * Enumerate additional DTS sensors available via IOSF.
                 * But we are not treating as a failure condition, if
@@ -676,10 +547,18 @@ static int  proc_thermal_pci_probe(struct pci_dev *pdev,
                return ret;
 
        ret = sysfs_create_group(&pdev->dev.kobj, &power_limit_attribute_group);
-       if (ret)
+       if (ret) {
                sysfs_remove_file(&pdev->dev.kobj, &dev_attr_tcc_offset_degree_celsius.attr);
+               return ret;
+       }
 
-       return ret;
+       ret = proc_thermal_mmio_add(pdev, proc_priv, id->driver_data);
+       if (ret) {
+               proc_thermal_remove(proc_priv);
+               return ret;
+       }
+
+       return 0;
 }
 
 static void  proc_thermal_pci_remove(struct pci_dev *pdev)
@@ -693,7 +572,8 @@ static void  proc_thermal_pci_remove(struct pci_dev *pdev)
                        pci_disable_msi(pdev);
                }
        }
-       proc_thermal_rapl_remove();
+
+       proc_thermal_mmio_remove(pdev);
        proc_thermal_remove(proc_priv);
 }
 
@@ -716,24 +596,22 @@ static int proc_thermal_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(proc_thermal_pm, NULL, proc_thermal_resume);
 
 static const struct pci_device_id proc_thermal_pci_ids[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL),
-               .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT0_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL),
-               .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)},
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_TGL_THERMAL),
-               .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
-       { 0, },
+       { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) },
+       { PCI_DEVICE_DATA(INTEL, BDW_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, BSW_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, BXT0_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, BXT1_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, BXTX_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, BXTP_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, CNL_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, CFL_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, GLK_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, HSB_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, ICL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
+       { PCI_DEVICE_DATA(INTEL, JSL_THERMAL, 0) },
+       { PCI_DEVICE_DATA(INTEL, SKL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
+       { PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_MBOX) },
+       { },
 };
 
 MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h
new file mode 100644 (file)
index 0000000..b9ed645
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * processor_thermal_device.h
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#ifndef __PROCESSOR_THERMAL_DEVICE_H__
+#define __PROCESSOR_THERMAL_DEVICE_H__
+
+#include <linux/intel_rapl.h>
+
+#define PCI_DEVICE_ID_INTEL_ADL_THERMAL        0x461d
+#define PCI_DEVICE_ID_INTEL_BDW_THERMAL        0x1603
+#define PCI_DEVICE_ID_INTEL_BSW_THERMAL        0x22DC
+
+#define PCI_DEVICE_ID_INTEL_BXT0_THERMAL       0x0A8C
+#define PCI_DEVICE_ID_INTEL_BXT1_THERMAL       0x1A8C
+#define PCI_DEVICE_ID_INTEL_BXTX_THERMAL       0x4A8C
+#define PCI_DEVICE_ID_INTEL_BXTP_THERMAL       0x5A8C
+
+#define PCI_DEVICE_ID_INTEL_CNL_THERMAL        0x5a03
+#define PCI_DEVICE_ID_INTEL_CFL_THERMAL        0x3E83
+#define PCI_DEVICE_ID_INTEL_GLK_THERMAL        0x318C
+#define PCI_DEVICE_ID_INTEL_HSB_THERMAL        0x0A03
+#define PCI_DEVICE_ID_INTEL_ICL_THERMAL        0x8a03
+#define PCI_DEVICE_ID_INTEL_JSL_THERMAL        0x4E03
+#define PCI_DEVICE_ID_INTEL_SKL_THERMAL        0x1903
+#define PCI_DEVICE_ID_INTEL_TGL_THERMAL        0x9A03
+
+struct power_config {
+       u32     index;
+       u32     min_uw;
+       u32     max_uw;
+       u32     tmin_us;
+       u32     tmax_us;
+       u32     step_uw;
+};
+
+struct proc_thermal_device {
+       struct device *dev;
+       struct acpi_device *adev;
+       struct power_config power_limits[2];
+       struct int34x_thermal_zone *int340x_zone;
+       struct intel_soc_dts_sensors *soc_dts;
+       u32 mmio_feature_mask;
+       void __iomem *mmio_base;
+};
+
+struct rapl_mmio_regs {
+       u64 reg_unit;
+       u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
+       int limits[RAPL_DOMAIN_MAX];
+};
+
+#define PROC_THERMAL_FEATURE_NONE      0x00
+#define PROC_THERMAL_FEATURE_RAPL      0x01
+#define PROC_THERMAL_FEATURE_FIVR      0x02
+#define PROC_THERMAL_FEATURE_DVFS      0x04
+#define PROC_THERMAL_FEATURE_MBOX      0x08
+
+#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
+int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_rapl_remove(void);
+#else
+static int __maybe_unused proc_thermal_rapl_add(struct pci_dev *pdev,
+                                               struct proc_thermal_device *proc_priv)
+{
+       return 0;
+}
+
+static void __maybe_unused proc_thermal_rapl_remove(void)
+{
+}
+#endif
+
+int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_rfim_remove(struct pci_dev *pdev);
+
+int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
+void proc_thermal_mbox_remove(struct pci_dev *pdev);
+
+#endif
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c
new file mode 100644 (file)
index 0000000..990f51f
--- /dev/null
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device mailbox driver for Workload type hints
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+#define MBOX_CMD_WORKLOAD_TYPE_READ    0x0E
+#define MBOX_CMD_WORKLOAD_TYPE_WRITE   0x0F
+
+#define MBOX_OFFSET_DATA               0x5810
+#define MBOX_OFFSET_INTERFACE          0x5818
+
+#define MBOX_BUSY_BIT                  31
+#define MBOX_RETRY_COUNT               100
+
+#define MBOX_DATA_BIT_VALID            31
+#define MBOX_DATA_BIT_AC_DC            30
+
+static DEFINE_MUTEX(mbox_lock);
+
+static int send_mbox_cmd(struct pci_dev *pdev, u8 cmd_id, u32 cmd_data, u8 *cmd_resp)
+{
+       struct proc_thermal_device *proc_priv;
+       u32 retries, data;
+       int ret;
+
+       mutex_lock(&mbox_lock);
+       proc_priv = pci_get_drvdata(pdev);
+
+       /* Poll for rb bit == 0 */
+       retries = MBOX_RETRY_COUNT;
+       do {
+               data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
+               if (data & BIT_ULL(MBOX_BUSY_BIT)) {
+                       ret = -EBUSY;
+                       continue;
+               }
+               ret = 0;
+               break;
+       } while (--retries);
+
+       if (ret)
+               goto unlock_mbox;
+
+       if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_WRITE)
+               writel(cmd_data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_DATA)));
+
+       /* Write command register */
+       data = BIT_ULL(MBOX_BUSY_BIT) | cmd_id;
+       writel(data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_INTERFACE)));
+
+       /* Poll for rb bit == 0 */
+       retries = MBOX_RETRY_COUNT;
+       do {
+               data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
+               if (data & BIT_ULL(MBOX_BUSY_BIT)) {
+                       ret = -EBUSY;
+                       continue;
+               }
+
+               if (data) {
+                       ret = -ENXIO;
+                       goto unlock_mbox;
+               }
+
+               if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_READ) {
+                       data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_DATA));
+                       *cmd_resp = data & 0xff;
+               }
+
+               ret = 0;
+               break;
+       } while (--retries);
+
+unlock_mbox:
+       mutex_unlock(&mbox_lock);
+       return ret;
+}
+
+/* List of workload types */
+static const char * const workload_types[] = {
+       "none",
+       "idle",
+       "semi_active",
+       "bursty",
+       "sustained",
+       "battery_life",
+       NULL
+};
+
+
+static ssize_t workload_available_types_show(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       int i = 0;
+       int ret = 0;
+
+       while (workload_types[i] != NULL)
+               ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
+
+       ret += sprintf(&buf[ret], "\n");
+
+       return ret;
+}
+
+static DEVICE_ATTR_RO(workload_available_types);
+
+static ssize_t workload_type_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       char str_preference[15];
+       u32 data = 0;
+       ssize_t ret;
+
+       ret = sscanf(buf, "%14s", str_preference);
+       if (ret != 1)
+               return -EINVAL;
+
+       ret = match_string(workload_types, -1, str_preference);
+       if (ret < 0)
+               return ret;
+
+       ret &= 0xff;
+
+       if (ret)
+               data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
+
+       data |= ret;
+
+       ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data, NULL);
+       if (ret)
+               return false;
+
+       return count;
+}
+
+static ssize_t workload_type_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       u8 cmd_resp;
+       int ret;
+
+       ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
+       if (ret)
+               return false;
+
+       cmd_resp &= 0xff;
+
+       if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
+               return -EINVAL;
+
+       return sprintf(buf, "%s\n", workload_types[cmd_resp]);
+}
+
+static DEVICE_ATTR_RW(workload_type);
+
+static struct attribute *workload_req_attrs[] = {
+       &dev_attr_workload_available_types.attr,
+       &dev_attr_workload_type.attr,
+       NULL
+};
+
+static const struct attribute_group workload_req_attribute_group = {
+       .attrs = workload_req_attrs,
+       .name = "workload_request"
+};
+
+
+
+static bool workload_req_created;
+
+int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+       u8 cmd_resp;
+       int ret;
+
+       /* Check if there is a mailbox support, if fails return success */
+       ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
+       if (ret)
+               return 0;
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
+       if (ret)
+               return ret;
+
+       workload_req_created = true;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
+
+void proc_thermal_mbox_remove(struct pci_dev *pdev)
+{
+       if (workload_req_created)
+               sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
+
+       workload_req_created = false;
+
+}
+EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c
new file mode 100644 (file)
index 0000000..a205221
--- /dev/null
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device RFIM control
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+static struct rapl_if_priv rapl_mmio_priv;
+
+static const struct rapl_mmio_regs rapl_mmio_default = {
+       .reg_unit = 0x5938,
+       .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930},
+       .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0},
+       .limits[RAPL_DOMAIN_PACKAGE] = 2,
+       .limits[RAPL_DOMAIN_DRAM] = 2,
+};
+
+static int rapl_mmio_cpu_online(unsigned int cpu)
+{
+       struct rapl_package *rp;
+
+       /* mmio rapl supports package 0 only for now */
+       if (topology_physical_package_id(cpu))
+               return 0;
+
+       rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
+       if (!rp) {
+               rp = rapl_add_package(cpu, &rapl_mmio_priv);
+               if (IS_ERR(rp))
+                       return PTR_ERR(rp);
+       }
+       cpumask_set_cpu(cpu, &rp->cpumask);
+       return 0;
+}
+
+static int rapl_mmio_cpu_down_prep(unsigned int cpu)
+{
+       struct rapl_package *rp;
+       int lead_cpu;
+
+       rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
+       if (!rp)
+               return 0;
+
+       cpumask_clear_cpu(cpu, &rp->cpumask);
+       lead_cpu = cpumask_first(&rp->cpumask);
+       if (lead_cpu >= nr_cpu_ids)
+               rapl_remove_package(rp);
+       else if (rp->lead_cpu == cpu)
+               rp->lead_cpu = lead_cpu;
+       return 0;
+}
+
+static int rapl_mmio_read_raw(int cpu, struct reg_action *ra)
+{
+       if (!ra->reg)
+               return -EINVAL;
+
+       ra->value = readq((void __iomem *)ra->reg);
+       ra->value &= ra->mask;
+       return 0;
+}
+
+static int rapl_mmio_write_raw(int cpu, struct reg_action *ra)
+{
+       u64 val;
+
+       if (!ra->reg)
+               return -EINVAL;
+
+       val = readq((void __iomem *)ra->reg);
+       val &= ~ra->mask;
+       val |= ra->value;
+       writeq(val, (void __iomem *)ra->reg);
+       return 0;
+}
+
+int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+       const struct rapl_mmio_regs *rapl_regs = &rapl_mmio_default;
+       enum rapl_domain_reg_id reg;
+       enum rapl_domain_type domain;
+       int ret;
+
+       if (!rapl_regs)
+               return 0;
+
+       for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) {
+               for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++)
+                       if (rapl_regs->regs[domain][reg])
+                               rapl_mmio_priv.regs[domain][reg] =
+                                               (u64)proc_priv->mmio_base +
+                                               rapl_regs->regs[domain][reg];
+               rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain];
+       }
+       rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit;
+
+       rapl_mmio_priv.read_raw = rapl_mmio_read_raw;
+       rapl_mmio_priv.write_raw = rapl_mmio_write_raw;
+
+       rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL);
+       if (IS_ERR(rapl_mmio_priv.control_type)) {
+               pr_debug("failed to register powercap control_type.\n");
+               return PTR_ERR(rapl_mmio_priv.control_type);
+       }
+
+       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
+                               rapl_mmio_cpu_online, rapl_mmio_cpu_down_prep);
+       if (ret < 0) {
+               powercap_unregister_control_type(rapl_mmio_priv.control_type);
+               rapl_mmio_priv.control_type = NULL;
+               return ret;
+       }
+       rapl_mmio_priv.pcap_rapl_online = ret;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_rapl_add);
+
+void proc_thermal_rapl_remove(void)
+{
+       if (IS_ERR_OR_NULL(rapl_mmio_priv.control_type))
+               return;
+
+       cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online);
+       powercap_unregister_control_type(rapl_mmio_priv.control_type);
+}
+EXPORT_SYMBOL_GPL(proc_thermal_rapl_remove);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
new file mode 100644 (file)
index 0000000..aef993a
--- /dev/null
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * processor thermal device RFIM control
+ * Copyright (c) 2020, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "processor_thermal_device.h"
+
+struct mmio_reg {
+       int read_only;
+       u32 offset;
+       int bits;
+       u16 mask;
+       u16 shift;
+};
+
+/* These will represent sysfs attribute names */
+static const char * const fivr_strings[] = {
+       "vco_ref_code_lo",
+       "vco_ref_code_hi",
+       "spread_spectrum_pct",
+       "spread_spectrum_clk_enable",
+       "rfi_vco_ref_code",
+       "fivr_fffc_rev",
+       NULL
+};
+
+static const struct mmio_reg tgl_fivr_mmio_regs[] = {
+       { 0, 0x5A18, 3, 0x7, 12}, /* vco_ref_code_lo */
+       { 0, 0x5A18, 8, 0xFF, 16}, /* vco_ref_code_hi */
+       { 0, 0x5A08, 8, 0xFF, 0}, /* spread_spectrum_pct */
+       { 0, 0x5A08, 1, 0x1, 8}, /* spread_spectrum_clk_enable */
+       { 1, 0x5A10, 12, 0xFFF, 0}, /* rfi_vco_ref_code */
+       { 1, 0x5A14, 2, 0x3, 1}, /* fivr_fffc_rev */
+};
+
+/* These will represent sysfs attribute names */
+static const char * const dvfs_strings[] = {
+       "rfi_restriction_run_busy",
+       "rfi_restriction_err_code",
+       "rfi_restriction_data_rate",
+       "rfi_restriction_data_rate_base",
+       "ddr_data_rate_point_0",
+       "ddr_data_rate_point_1",
+       "ddr_data_rate_point_2",
+       "ddr_data_rate_point_3",
+       "rfi_disable",
+       NULL
+};
+
+static const struct mmio_reg adl_dvfs_mmio_regs[] = {
+       { 0, 0x5A38, 1, 0x1, 31}, /* rfi_restriction_run_busy */
+       { 0, 0x5A38, 7, 0x7F, 24}, /* rfi_restriction_err_code */
+       { 0, 0x5A38, 8, 0xFF, 16}, /* rfi_restriction_data_rate */
+       { 0, 0x5A38, 16, 0xFFFF, 0}, /* rfi_restriction_data_rate_base */
+       { 0, 0x5A30, 10, 0x3FF, 0}, /* ddr_data_rate_point_0 */
+       { 0, 0x5A30, 10, 0x3FF, 10}, /* ddr_data_rate_point_1 */
+       { 0, 0x5A30, 10, 0x3FF, 20}, /* ddr_data_rate_point_2 */
+       { 0, 0x5A30, 10, 0x3FF, 30}, /* ddr_data_rate_point_3 */
+       { 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */
+};
+
+#define RFIM_SHOW(suffix, table)\
+static ssize_t suffix##_show(struct device *dev,\
+                             struct device_attribute *attr,\
+                             char *buf)\
+{\
+       struct proc_thermal_device *proc_priv;\
+       struct pci_dev *pdev = to_pci_dev(dev);\
+       const struct mmio_reg *mmio_regs;\
+       const char **match_strs;\
+       u32 reg_val;\
+       int ret;\
+\
+       proc_priv = pci_get_drvdata(pdev);\
+       if (table) {\
+               match_strs = (const char **)dvfs_strings;\
+               mmio_regs = adl_dvfs_mmio_regs;\
+       } else { \
+               match_strs = (const char **)fivr_strings;\
+               mmio_regs = tgl_fivr_mmio_regs;\
+       } \
+       \
+       ret = match_string(match_strs, -1, attr->attr.name);\
+       if (ret < 0)\
+               return ret;\
+       reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
+       ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\
+       return sprintf(buf, "%u\n", ret);\
+}
+
+#define RFIM_STORE(suffix, table)\
+static ssize_t suffix##_store(struct device *dev,\
+                              struct device_attribute *attr,\
+                              const char *buf, size_t count)\
+{\
+       struct proc_thermal_device *proc_priv;\
+       struct pci_dev *pdev = to_pci_dev(dev);\
+       unsigned int input;\
+       const char **match_strs;\
+       const struct mmio_reg *mmio_regs;\
+       int ret, err;\
+       u32 reg_val;\
+       u32 mask;\
+\
+       proc_priv = pci_get_drvdata(pdev);\
+       if (table) {\
+               match_strs = (const char **)dvfs_strings;\
+               mmio_regs = adl_dvfs_mmio_regs;\
+       } else { \
+               match_strs = (const char **)fivr_strings;\
+               mmio_regs = tgl_fivr_mmio_regs;\
+       } \
+       \
+       ret = match_string(match_strs, -1, attr->attr.name);\
+       if (ret < 0)\
+               return ret;\
+       if (mmio_regs[ret].read_only)\
+               return -EPERM;\
+       err = kstrtouint(buf, 10, &input);\
+       if (err)\
+               return err;\
+       mask = GENMASK(mmio_regs[ret].shift + mmio_regs[ret].bits - 1, mmio_regs[ret].shift);\
+       reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
+       reg_val &= ~mask;\
+       reg_val |= (input << mmio_regs[ret].shift);\
+       writel(reg_val, (void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
+       return count;\
+}
+
+RFIM_SHOW(vco_ref_code_lo, 0)
+RFIM_SHOW(vco_ref_code_hi, 0)
+RFIM_SHOW(spread_spectrum_pct, 0)
+RFIM_SHOW(spread_spectrum_clk_enable, 0)
+RFIM_SHOW(rfi_vco_ref_code, 0)
+RFIM_SHOW(fivr_fffc_rev, 0)
+
+RFIM_STORE(vco_ref_code_lo, 0)
+RFIM_STORE(vco_ref_code_hi, 0)
+RFIM_STORE(spread_spectrum_pct, 0)
+RFIM_STORE(spread_spectrum_clk_enable, 0)
+RFIM_STORE(rfi_vco_ref_code, 0)
+RFIM_STORE(fivr_fffc_rev, 0)
+
+static DEVICE_ATTR_RW(vco_ref_code_lo);
+static DEVICE_ATTR_RW(vco_ref_code_hi);
+static DEVICE_ATTR_RW(spread_spectrum_pct);
+static DEVICE_ATTR_RW(spread_spectrum_clk_enable);
+static DEVICE_ATTR_RW(rfi_vco_ref_code);
+static DEVICE_ATTR_RW(fivr_fffc_rev);
+
+static struct attribute *fivr_attrs[] = {
+       &dev_attr_vco_ref_code_lo.attr,
+       &dev_attr_vco_ref_code_hi.attr,
+       &dev_attr_spread_spectrum_pct.attr,
+       &dev_attr_spread_spectrum_clk_enable.attr,
+       &dev_attr_rfi_vco_ref_code.attr,
+       &dev_attr_fivr_fffc_rev.attr,
+       NULL
+};
+
+static const struct attribute_group fivr_attribute_group = {
+       .attrs = fivr_attrs,
+       .name = "fivr"
+};
+
+RFIM_SHOW(rfi_restriction_run_busy, 1)
+RFIM_SHOW(rfi_restriction_err_code, 1)
+RFIM_SHOW(rfi_restriction_data_rate, 1)
+RFIM_SHOW(ddr_data_rate_point_0, 1)
+RFIM_SHOW(ddr_data_rate_point_1, 1)
+RFIM_SHOW(ddr_data_rate_point_2, 1)
+RFIM_SHOW(ddr_data_rate_point_3, 1)
+RFIM_SHOW(rfi_disable, 1)
+
+RFIM_STORE(rfi_restriction_run_busy, 1)
+RFIM_STORE(rfi_restriction_err_code, 1)
+RFIM_STORE(rfi_restriction_data_rate, 1)
+RFIM_STORE(rfi_disable, 1)
+
+static DEVICE_ATTR_RW(rfi_restriction_run_busy);
+static DEVICE_ATTR_RW(rfi_restriction_err_code);
+static DEVICE_ATTR_RW(rfi_restriction_data_rate);
+static DEVICE_ATTR_RO(ddr_data_rate_point_0);
+static DEVICE_ATTR_RO(ddr_data_rate_point_1);
+static DEVICE_ATTR_RO(ddr_data_rate_point_2);
+static DEVICE_ATTR_RO(ddr_data_rate_point_3);
+static DEVICE_ATTR_RW(rfi_disable);
+
+static struct attribute *dvfs_attrs[] = {
+       &dev_attr_rfi_restriction_run_busy.attr,
+       &dev_attr_rfi_restriction_err_code.attr,
+       &dev_attr_rfi_restriction_data_rate.attr,
+       &dev_attr_ddr_data_rate_point_0.attr,
+       &dev_attr_ddr_data_rate_point_1.attr,
+       &dev_attr_ddr_data_rate_point_2.attr,
+       &dev_attr_ddr_data_rate_point_3.attr,
+       &dev_attr_rfi_disable.attr,
+       NULL
+};
+
+static const struct attribute_group dvfs_attribute_group = {
+       .attrs = dvfs_attrs,
+       .name = "dvfs"
+};
+
+int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
+{
+       int ret;
+
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
+               ret = sysfs_create_group(&pdev->dev.kobj, &fivr_attribute_group);
+               if (ret)
+                       return ret;
+       }
+
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
+               ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group);
+               if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
+                       sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(proc_thermal_rfim_add);
+
+void proc_thermal_rfim_remove(struct pci_dev *pdev)
+{
+       struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
+
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
+               sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
+
+       if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
+               sysfs_remove_group(&pdev->dev.kobj, &dvfs_attribute_group);
+}
+EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove);
+
+MODULE_LICENSE("GPL v2");
index 3b813eb..41723c6 100644 (file)
@@ -7,14 +7,16 @@
  *     Tushar Dave <tushar.n.dave@intel.com>
  */
 
+#include <linux/acpi.h>
+#include <linux/delay.h>
 #include <linux/module.h>
-#include <linux/types.h>
 #include <linux/init.h>
 #include <linux/pci.h>
-#include <linux/acpi.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
 #include <linux/thermal.h>
+#include <linux/types.h>
 #include <linux/units.h>
-#include <linux/pm.h>
 
 /* Intel PCH thermal Device IDs */
 #define PCH_THERMAL_DID_HSW_1  0x9C24 /* Haswell PCH */
@@ -26,6 +28,7 @@
 #define PCH_THERMAL_DID_CNL_H  0xA379 /* CNL-H PCH */
 #define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */
 #define PCH_THERMAL_DID_CML_H  0X06F9 /* CML-H PCH */
+#define PCH_THERMAL_DID_LWB    0xA1B1 /* Lewisburg PCH */
 
 /* Wildcat Point-LP  PCH Thermal registers */
 #define WPT_TEMP       0x0000  /* Temperature */
@@ -35,6 +38,7 @@
 #define WPT_TSREL      0x0A    /* Thermal Sensor Report Enable and Lock */
 #define WPT_TSMIC      0x0C    /* Thermal Sensor SMI Control */
 #define WPT_CTT        0x0010  /* Catastrophic Trip Point */
+#define WPT_TSPM       0x001C  /* Thermal Sensor Power Management */
 #define WPT_TAHV       0x0014  /* Thermal Alert High Value */
 #define WPT_TALV       0x0018  /* Thermal Alert Low Value */
 #define WPT_TL         0x00000040      /* Throttle Value */
 #define WPT_TL_T1L     0x1ff00000      /* T1 Level */
 #define WPT_TL_TTEN    0x20000000      /* TT Enable */
 
+/* Resolution of 1/2 degree C and an offset of -50C */
+#define PCH_TEMP_OFFSET        (-50)
+#define GET_WPT_TEMP(x)        ((x) * MILLIDEGREE_PER_DEGREE / 2 + WPT_TEMP_OFFSET)
+#define WPT_TEMP_OFFSET        (PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE)
+#define GET_PCH_TEMP(x)        (((x) / 2) + PCH_TEMP_OFFSET)
+
+/* Amount of time for each cooling delay, 100ms by default for now */
+static unsigned int delay_timeout = 100;
+module_param(delay_timeout, int, 0644);
+MODULE_PARM_DESC(delay_timeout, "amount of time delay for each iteration.");
+
+/* Number of iterations for cooling delay, 10 counts by default for now */
+static unsigned int delay_cnt = 10;
+module_param(delay_cnt, int, 0644);
+MODULE_PARM_DESC(delay_cnt, "total number of iterations for time delay.");
+
 static char driver_name[] = "Intel PCH thermal driver";
 
 struct pch_thermal_device {
@@ -147,8 +167,7 @@ read_trips:
        trip_temp = readw(ptd->hw_base + WPT_CTT);
        trip_temp &= 0x1FF;
        if (trip_temp) {
-               /* Resolution of 1/2 degree C and an offset of -50C */
-               ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
+               ptd->crt_temp = GET_WPT_TEMP(trip_temp);
                ptd->crt_trip_id = 0;
                ++(*nr_trips);
        }
@@ -157,8 +176,7 @@ read_trips:
        trip_temp = readw(ptd->hw_base + WPT_PHL);
        trip_temp &= 0x1FF;
        if (trip_temp) {
-               /* Resolution of 1/2 degree C and an offset of -50C */
-               ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
+               ptd->hot_temp = GET_WPT_TEMP(trip_temp);
                ptd->hot_trip_id = *nr_trips;
                ++(*nr_trips);
        }
@@ -170,12 +188,7 @@ read_trips:
 
 static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
 {
-       u16 wpt_temp;
-
-       wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP);
-
-       /* Resolution of 1/2 degree C and an offset of -50C */
-       *temp = (wpt_temp * 1000 / 2 - 50000);
+       *temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
 
        return 0;
 }
@@ -183,13 +196,62 @@ static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
 static int pch_wpt_suspend(struct pch_thermal_device *ptd)
 {
        u8 tsel;
+       u8 pch_delay_cnt = 1;
+       u16 pch_thr_temp, pch_cur_temp;
 
-       if (ptd->bios_enabled)
+       /* Shutdown the thermal sensor if it is not enabled by BIOS */
+       if (!ptd->bios_enabled) {
+               tsel = readb(ptd->hw_base + WPT_TSEL);
+               writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
                return 0;
+       }
 
-       tsel = readb(ptd->hw_base + WPT_TSEL);
+       /* Do not check temperature if it is not a S0ix capable platform */
+#ifdef CONFIG_ACPI
+       if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
+               return 0;
+#else
+       return 0;
+#endif
+
+       /* Do not check temperature if it is not s2idle */
+       if (pm_suspend_via_firmware())
+               return 0;
+
+       /* Get the PCH temperature threshold value */
+       pch_thr_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TSPM));
+
+       /* Get the PCH current temperature value */
+       pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
+
+       /*
+        * If current PCH temperature is higher than configured PCH threshold
+        * value, run some delay loop with sleep to let the current temperature
+        * go down below the threshold value which helps to allow system enter
+        * lower power S0ix suspend state. Even after delay loop if PCH current
+        * temperature stays above threshold, notify the warning message
+        * which helps to indentify the reason why S0ix entry was rejected.
+        */
+       while (pch_delay_cnt <= delay_cnt) {
+               if (pch_cur_temp <= pch_thr_temp)
+                       break;
+
+               dev_warn(&ptd->pdev->dev,
+                       "CPU-PCH current temp [%dC] higher than the threshold temp [%dC], sleep %d times for %d ms duration\n",
+                       pch_cur_temp, pch_thr_temp, pch_delay_cnt, delay_timeout);
+               msleep(delay_timeout);
+               /* Read the PCH current temperature for next cycle. */
+               pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
+               pch_delay_cnt++;
+       }
 
-       writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
+       if (pch_cur_temp > pch_thr_temp)
+               dev_warn(&ptd->pdev->dev,
+                       "CPU-PCH is hot [%dC] even after delay, continue to suspend. S0ix might fail\n",
+                       pch_cur_temp);
+       else
+               dev_info(&ptd->pdev->dev,
+                       "CPU-PCH is cool [%dC], continue to suspend\n", pch_cur_temp);
 
        return 0;
 }
@@ -276,6 +338,7 @@ enum board_ids {
        board_skl,
        board_cnl,
        board_cml,
+       board_lwb,
 };
 
 static const struct board_info {
@@ -301,7 +364,11 @@ static const struct board_info {
        [board_cml] = {
                .name = "pch_cometlake",
                .ops = &pch_dev_ops_wpt,
-       }
+       },
+       [board_lwb] = {
+               .name = "pch_lewisburg",
+               .ops = &pch_dev_ops_wpt,
+       },
 };
 
 static int intel_pch_thermal_probe(struct pci_dev *pdev,
@@ -415,6 +482,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = {
                .driver_data = board_cnl, },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
                .driver_data = board_cml, },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB),
+               .driver_data = board_lwb, },
        { 0, },
 };
 MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
index 0bd7aa5..149c6d7 100644 (file)
@@ -1052,7 +1052,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       ret = device_reset(&pdev->dev);
+       ret = device_reset_optional(&pdev->dev);
        if (ret)
                return ret;
 
index 0dd47dc..75c69fe 100644 (file)
 #define MCELSIUS(temp) ((temp) * 1000)
 #define GEN3_FUSE_MASK 0xFFF
 
-#define TSC_MAX_NUM    3
+#define TSC_MAX_NUM    4
 
 /* default THCODE values if FUSEs are missing */
 static const int thcodes[TSC_MAX_NUM][3] = {
        { 3397, 2800, 2221 },
        { 3393, 2795, 2216 },
        { 3389, 2805, 2237 },
+       { 3415, 2694, 2195 },
 };
 
 /* Structure for thermal temperature calculation */
@@ -188,70 +189,10 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
        return 0;
 }
 
-static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
-                                             int mcelsius)
-{
-       int celsius, val;
-
-       celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
-       if (celsius <= INT_FIXPT(tsc->tj_t))
-               val = celsius * tsc->coef.a1 + tsc->coef.b1;
-       else
-               val = celsius * tsc->coef.a2 + tsc->coef.b2;
-
-       return INT_FIXPT(val);
-}
-
-static int rcar_gen3_thermal_update_range(struct rcar_gen3_thermal_tsc *tsc)
-{
-       int temperature, low, high;
-
-       rcar_gen3_thermal_get_temp(tsc, &temperature);
-
-       low = temperature - MCELSIUS(1);
-       high = temperature + MCELSIUS(1);
-
-       rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
-                               rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
-
-       rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
-                               rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
-
-       return 0;
-}
-
 static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
        .get_temp       = rcar_gen3_thermal_get_temp,
 };
 
-static void rcar_thermal_irq_set(struct rcar_gen3_thermal_priv *priv, bool on)
-{
-       unsigned int i;
-       u32 val = on ? IRQ_TEMPD1 | IRQ_TEMP2 : 0;
-
-       for (i = 0; i < priv->num_tscs; i++)
-               rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, val);
-}
-
-static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
-{
-       struct rcar_gen3_thermal_priv *priv = data;
-       u32 status;
-       int i;
-
-       for (i = 0; i < priv->num_tscs; i++) {
-               status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
-               rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
-               if (status) {
-                       rcar_gen3_thermal_update_range(priv->tscs[i]);
-                       thermal_zone_device_update(priv->tscs[i]->zone,
-                                                  THERMAL_EVENT_UNSPECIFIED);
-               }
-       }
-
-       return IRQ_HANDLED;
-}
-
 static const struct soc_device_attribute r8a7795es1[] = {
        { .soc_id = "r8a7795", .revision = "ES1.*" },
        { /* sentinel */ }
@@ -268,7 +209,6 @@ static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
 
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
-       rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2);
 
        rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
                                CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN);
@@ -294,7 +234,6 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
 
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
        rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
-       rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2);
 
        reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
        reg_val |= THCTR_THSST;
@@ -338,6 +277,10 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
                .compatible = "renesas,r8a77980-thermal",
                .data = &rcar_gen3_ths_tj_1,
        },
+       {
+               .compatible = "renesas,r8a779a0-thermal",
+               .data = &rcar_gen3_ths_tj_1,
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
@@ -345,9 +288,6 @@ MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
 static int rcar_gen3_thermal_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
-
-       rcar_thermal_irq_set(priv, false);
 
        pm_runtime_put(dev);
        pm_runtime_disable(dev);
@@ -369,8 +309,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
        const int *rcar_gen3_ths_tj_1 = of_device_get_match_data(dev);
        struct resource *res;
        struct thermal_zone_device *zone;
-       int ret, irq, i;
-       char *irqname;
+       int ret, i;
 
        /* default values if FUSEs are missing */
        /* TODO: Read values from hardware on supported platforms */
@@ -386,28 +325,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, priv);
 
-       /*
-        * Request 2 (of the 3 possible) IRQs, the driver only needs to
-        * to trigger on the low and high trip points of the current
-        * temp window at this point.
-        */
-       for (i = 0; i < 2; i++) {
-               irq = platform_get_irq(pdev, i);
-               if (irq < 0)
-                       return irq;
-
-               irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",
-                                        dev_name(dev), i);
-               if (!irqname)
-                       return -ENOMEM;
-
-               ret = devm_request_threaded_irq(dev, irq, NULL,
-                                               rcar_gen3_thermal_irq,
-                                               IRQF_ONESHOT, irqname, priv);
-               if (ret)
-                       return ret;
-       }
-
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
@@ -459,8 +376,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto error_unregister;
 
-               rcar_gen3_thermal_update_range(tsc);
-
                dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
        }
 
@@ -471,8 +386,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
                goto error_unregister;
        }
 
-       rcar_thermal_irq_set(priv, true);
-
        return 0;
 
 error_unregister:
@@ -481,15 +394,6 @@ error_unregister:
        return ret;
 }
 
-static int __maybe_unused rcar_gen3_thermal_suspend(struct device *dev)
-{
-       struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
-
-       rcar_thermal_irq_set(priv, false);
-
-       return 0;
-}
-
 static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
 {
        struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
@@ -499,15 +403,12 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
                struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
 
                priv->thermal_init(tsc);
-               rcar_gen3_thermal_update_range(tsc);
        }
 
-       rcar_thermal_irq_set(priv, true);
-
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, rcar_gen3_thermal_suspend,
+static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
                         rcar_gen3_thermal_resume);
 
 static struct platform_driver rcar_gen3_thermal_driver = {
index 5c2a13b..b49f04d 100644 (file)
@@ -323,24 +323,6 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone,
        return 0;
 }
 
-static int rcar_thermal_notify(struct thermal_zone_device *zone,
-                              int trip, enum thermal_trip_type type)
-{
-       struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
-       struct device *dev = rcar_priv_to_dev(priv);
-
-       switch (type) {
-       case THERMAL_TRIP_CRITICAL:
-               /* FIXME */
-               dev_warn(dev, "Thermal reached to critical temperature\n");
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
 static const struct thermal_zone_of_device_ops rcar_thermal_zone_of_ops = {
        .get_temp       = rcar_thermal_of_get_temp,
 };
@@ -349,7 +331,6 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
        .get_temp       = rcar_thermal_get_temp,
        .get_trip_type  = rcar_thermal_get_trip_type,
        .get_trip_temp  = rcar_thermal_get_trip_temp,
-       .notify         = rcar_thermal_notify,
 };
 
 /*
@@ -409,16 +390,15 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
 {
        struct rcar_thermal_common *common = data;
        struct rcar_thermal_priv *priv;
-       unsigned long flags;
        u32 status, mask;
 
-       spin_lock_irqsave(&common->lock, flags);
+       spin_lock(&common->lock);
 
        mask    = rcar_thermal_common_read(common, INTMSK);
        status  = rcar_thermal_common_read(common, STR);
        rcar_thermal_common_write(common, STR, 0x000F0F0F & mask);
 
-       spin_unlock_irqrestore(&common->lock, flags);
+       spin_unlock(&common->lock);
 
        status = status & ~mask;
 
index f8b1307..8c80bd0 100644 (file)
@@ -8,6 +8,7 @@
  * Based on the work of Josef Gajdusek <atx@atx.name>
  */
 
+#include <linux/bitmap.h>
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
@@ -74,7 +75,7 @@ struct ths_thermal_chip {
        int             (*calibrate)(struct ths_device *tmdev,
                                     u16 *caldata, int callen);
        int             (*init)(struct ths_device *tmdev);
-       int             (*irq_ack)(struct ths_device *tmdev);
+       unsigned long   (*irq_ack)(struct ths_device *tmdev);
        int             (*calc_temp)(struct ths_device *tmdev,
                                     int id, int reg);
 };
@@ -146,9 +147,10 @@ static const struct regmap_config config = {
        .max_register = 0xfc,
 };
 
-static int sun8i_h3_irq_ack(struct ths_device *tmdev)
+static unsigned long sun8i_h3_irq_ack(struct ths_device *tmdev)
 {
-       int i, state, ret = 0;
+       unsigned long irq_bitmap = 0;
+       int i, state;
 
        regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
 
@@ -156,16 +158,17 @@ static int sun8i_h3_irq_ack(struct ths_device *tmdev)
                if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
                        regmap_write(tmdev->regmap, SUN8I_THS_IS,
                                     SUN8I_THS_DATA_IRQ_STS(i));
-                       ret |= BIT(i);
+                       bitmap_set(&irq_bitmap, i, 1);
                }
        }
 
-       return ret;
+       return irq_bitmap;
 }
 
-static int sun50i_h6_irq_ack(struct ths_device *tmdev)
+static unsigned long sun50i_h6_irq_ack(struct ths_device *tmdev)
 {
-       int i, state, ret = 0;
+       unsigned long irq_bitmap = 0;
+       int i, state;
 
        regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
 
@@ -173,24 +176,22 @@ static int sun50i_h6_irq_ack(struct ths_device *tmdev)
                if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
                        regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
                                     SUN50I_H6_THS_DATA_IRQ_STS(i));
-                       ret |= BIT(i);
+                       bitmap_set(&irq_bitmap, i, 1);
                }
        }
 
-       return ret;
+       return irq_bitmap;
 }
 
 static irqreturn_t sun8i_irq_thread(int irq, void *data)
 {
        struct ths_device *tmdev = data;
-       int i, state;
-
-       state = tmdev->chip->irq_ack(tmdev);
+       unsigned long irq_bitmap = tmdev->chip->irq_ack(tmdev);
+       int i;
 
-       for (i = 0; i < tmdev->chip->sensor_num; i++) {
-               if (state & BIT(i))
-                       thermal_zone_device_update(tmdev->sensor[i].tzd,
-                                                  THERMAL_EVENT_UNSPECIFIED);
+       for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) {
+               thermal_zone_device_update(tmdev->sensor[i].tzd,
+                                          THERMAL_EVENT_UNSPECIFIED);
        }
 
        return IRQ_HANDLED;
index c6d74bc..4a291d2 100644 (file)
@@ -380,6 +380,25 @@ static void thermal_emergency_poweroff(void)
                              msecs_to_jiffies(poweroff_delay_ms));
 }
 
+void thermal_zone_device_critical(struct thermal_zone_device *tz)
+{
+       dev_emerg(&tz->device, "%s: critical temperature reached, "
+                 "shutting down\n", tz->type);
+
+       mutex_lock(&poweroff_lock);
+       if (!power_off_triggered) {
+               /*
+                * Queue a backup emergency shutdown in the event of
+                * orderly_poweroff failure
+                */
+               thermal_emergency_poweroff();
+               orderly_poweroff(true);
+               power_off_triggered = true;
+       }
+       mutex_unlock(&poweroff_lock);
+}
+EXPORT_SYMBOL(thermal_zone_device_critical);
+
 static void handle_critical_trips(struct thermal_zone_device *tz,
                                  int trip, enum thermal_trip_type trip_type)
 {
@@ -396,22 +415,10 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
        if (tz->ops->notify)
                tz->ops->notify(tz, trip, trip_type);
 
-       if (trip_type == THERMAL_TRIP_CRITICAL) {
-               dev_emerg(&tz->device,
-                         "critical temperature reached (%d C), shutting down\n",
-                         tz->temperature / 1000);
-               mutex_lock(&poweroff_lock);
-               if (!power_off_triggered) {
-                       /*
-                        * Queue a backup emergency shutdown in the event of
-                        * orderly_poweroff failure
-                        */
-                       thermal_emergency_poweroff();
-                       orderly_poweroff(true);
-                       power_off_triggered = true;
-               }
-               mutex_unlock(&poweroff_lock);
-       }
+       if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot)
+               tz->ops->hot(tz);
+       else if (trip_type == THERMAL_TRIP_CRITICAL)
+               tz->ops->critical(tz);
 }
 
 static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
@@ -553,7 +560,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
        if (atomic_read(&in_suspend))
                return;
 
-       if (!tz->ops->get_temp)
+       if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without "
+                     "'get_temp' ops set\n", __func__))
                return;
 
        update_temperature(tz);
@@ -593,94 +601,6 @@ static void thermal_zone_device_check(struct work_struct *work)
        thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 }
 
-/*
- * Power actor section: interface to power actors to estimate power
- *
- * Set of functions used to interact to cooling devices that know
- * how to estimate their devices power consumption.
- */
-
-/**
- * power_actor_get_max_power() - get the maximum power that a cdev can consume
- * @cdev:      pointer to &thermal_cooling_device
- * @max_power: pointer in which to store the maximum power
- *
- * Calculate the maximum power consumption in milliwats that the
- * cooling device can currently consume and store it in @max_power.
- *
- * Return: 0 on success, -EINVAL if @cdev doesn't support the
- * power_actor API or -E* on other error.
- */
-int power_actor_get_max_power(struct thermal_cooling_device *cdev,
-                             u32 *max_power)
-{
-       if (!cdev_is_power_actor(cdev))
-               return -EINVAL;
-
-       return cdev->ops->state2power(cdev, 0, max_power);
-}
-
-/**
- * power_actor_get_min_power() - get the mainimum power that a cdev can consume
- * @cdev:      pointer to &thermal_cooling_device
- * @min_power: pointer in which to store the minimum power
- *
- * Calculate the minimum power consumption in milliwatts that the
- * cooling device can currently consume and store it in @min_power.
- *
- * Return: 0 on success, -EINVAL if @cdev doesn't support the
- * power_actor API or -E* on other error.
- */
-int power_actor_get_min_power(struct thermal_cooling_device *cdev,
-                             u32 *min_power)
-{
-       unsigned long max_state;
-       int ret;
-
-       if (!cdev_is_power_actor(cdev))
-               return -EINVAL;
-
-       ret = cdev->ops->get_max_state(cdev, &max_state);
-       if (ret)
-               return ret;
-
-       return cdev->ops->state2power(cdev, max_state, min_power);
-}
-
-/**
- * power_actor_set_power() - limit the maximum power a cooling device consumes
- * @cdev:      pointer to &thermal_cooling_device
- * @instance:  thermal instance to update
- * @power:     the power in milliwatts
- *
- * Set the cooling device to consume at most @power milliwatts. The limit is
- * expected to be a cap at the maximum power consumption.
- *
- * Return: 0 on success, -EINVAL if the cooling device does not
- * implement the power actor API or -E* for other failures.
- */
-int power_actor_set_power(struct thermal_cooling_device *cdev,
-                         struct thermal_instance *instance, u32 power)
-{
-       unsigned long state;
-       int ret;
-
-       if (!cdev_is_power_actor(cdev))
-               return -EINVAL;
-
-       ret = cdev->ops->power2state(cdev, power, &state);
-       if (ret)
-               return ret;
-
-       instance->target = state;
-       mutex_lock(&cdev->lock);
-       cdev->updated = false;
-       mutex_unlock(&cdev->lock);
-       thermal_cdev_update(cdev);
-
-       return 0;
-}
-
 void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
                                          const char *cdev_type, size_t size)
 {
@@ -1423,6 +1343,10 @@ thermal_zone_device_register(const char *type, int trips, int mask,
 
        tz->id = id;
        strlcpy(tz->type, type, sizeof(tz->type));
+
+       if (!ops->critical)
+               ops->critical = thermal_zone_device_critical;
+
        tz->ops = ops;
        tz->tzp = tzp;
        tz->device.class = &thermal_class;
@@ -1446,12 +1370,9 @@ thermal_zone_device_register(const char *type, int trips, int mask,
                goto release_device;
 
        for (count = 0; count < trips; count++) {
-               if (tz->ops->get_trip_type(tz, count, &trip_type))
-                       set_bit(count, &tz->trips_disabled);
-               if (tz->ops->get_trip_temp(tz, count, &trip_temp))
-                       set_bit(count, &tz->trips_disabled);
-               /* Check for bogus trip points */
-               if (trip_temp == 0)
+               if (tz->ops->get_trip_type(tz, count, &trip_type) ||
+                   tz->ops->get_trip_temp(tz, count, &trip_temp) ||
+                   !trip_temp)
                        set_bit(count, &tz->trips_disabled);
        }
 
index 681209d..8df600f 100644 (file)
@@ -65,12 +65,6 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
                cdev->ops->power2state;
 }
 
-int power_actor_get_max_power(struct thermal_cooling_device *cdev,
-                             u32 *max_power);
-int power_actor_get_min_power(struct thermal_cooling_device *cdev,
-                             u32 *min_power);
-int power_actor_set_power(struct thermal_cooling_device *cdev,
-                         struct thermal_instance *ti, u32 power);
 /**
  * struct thermal_trip - representation of a point in temperature domain
  * @np: pointer to struct device_node that this trip point was created from
index 8b92e00..ad03262 100644 (file)
@@ -206,8 +206,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
        if (new_hwmon_device)
                hwmon_device_unregister(hwmon->device);
  free_mem:
-       if (new_hwmon_device)
-               kfree(hwmon);
+       kfree(hwmon);
 
        return result;
 }
index a6f371f..0866e94 100644 (file)
@@ -425,7 +425,7 @@ static struct attribute *thermal_zone_dev_attrs[] = {
        NULL,
 };
 
-static struct attribute_group thermal_zone_attribute_group = {
+static const struct attribute_group thermal_zone_attribute_group = {
        .attrs = thermal_zone_dev_attrs,
 };
 
@@ -434,7 +434,7 @@ static struct attribute *thermal_zone_mode_attrs[] = {
        NULL,
 };
 
-static struct attribute_group thermal_zone_mode_attribute_group = {
+static const struct attribute_group thermal_zone_mode_attribute_group = {
        .attrs = thermal_zone_mode_attrs,
 };
 
@@ -468,7 +468,7 @@ static umode_t thermal_zone_passive_is_visible(struct kobject *kobj,
        return 0;
 }
 
-static struct attribute_group thermal_zone_passive_attribute_group = {
+static const struct attribute_group thermal_zone_passive_attribute_group = {
        .attrs = thermal_zone_passive_attrs,
        .is_visible = thermal_zone_passive_is_visible,
 };
index 9df2dfc..14baa73 100644 (file)
 
 /**
  * struct devfreq_cooling_power - Devfreq cooling power ops
- * @get_static_power:  Take voltage, in mV, and return the static power
- *                     in mW.  If NULL, the static power is assumed
- *                     to be 0.
- * @get_dynamic_power: Take voltage, in mV, and frequency, in HZ, and
- *                     return the dynamic power draw in mW.  If NULL,
- *                     a simple power model is used.
- * @dyn_power_coeff:   Coefficient for the simple dynamic power model in
- *                     mW/(MHz mV mV).
- *                     If get_dynamic_power() is NULL, then the
- *                     dynamic power is calculated as
- *                     @dyn_power_coeff * frequency * voltage^2
  * @get_real_power:    When this is set, the framework uses it to ask the
  *                     device driver for the actual power.
  *                     Some devices have more sophisticated methods
  *                     max total (static + dynamic) power value for each OPP.
  */
 struct devfreq_cooling_power {
-       unsigned long (*get_static_power)(struct devfreq *devfreq,
-                                         unsigned long voltage);
-       unsigned long (*get_dynamic_power)(struct devfreq *devfreq,
-                                          unsigned long freq,
-                                          unsigned long voltage);
        int (*get_real_power)(struct devfreq *df, u32 *power,
                              unsigned long freq, unsigned long voltage);
-       unsigned long dyn_power_coeff;
 };
 
 #ifdef CONFIG_DEVFREQ_THERMAL
@@ -65,6 +48,9 @@ struct thermal_cooling_device *
 of_devfreq_cooling_register(struct device_node *np, struct devfreq *df);
 struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df);
 void devfreq_cooling_unregister(struct thermal_cooling_device *dfc);
+struct thermal_cooling_device *
+devfreq_cooling_em_register(struct devfreq *df,
+                           struct devfreq_cooling_power *dfc_power);
 
 #else /* !CONFIG_DEVFREQ_THERMAL */
 
@@ -87,6 +73,13 @@ devfreq_cooling_register(struct devfreq *df)
        return ERR_PTR(-EINVAL);
 }
 
+static inline struct thermal_cooling_device *
+devfreq_cooling_em_register(struct devfreq *df,
+                           struct devfreq_cooling_power *dfc_power)
+{
+       return ERR_PTR(-EINVAL);
+}
+
 static inline void
 devfreq_cooling_unregister(struct thermal_cooling_device *dfc)
 {
index d07ea27..31b8440 100644 (file)
@@ -79,6 +79,8 @@ struct thermal_zone_device_ops {
                          enum thermal_trend *);
        int (*notify) (struct thermal_zone_device *, int,
                       enum thermal_trip_type);
+       void (*hot)(struct thermal_zone_device *);
+       void (*critical)(struct thermal_zone_device *);
 };
 
 struct thermal_cooling_device_ops {
@@ -399,6 +401,7 @@ void thermal_cdev_update(struct thermal_cooling_device *);
 void thermal_notify_framework(struct thermal_zone_device *, int);
 int thermal_zone_device_enable(struct thermal_zone_device *tz);
 int thermal_zone_device_disable(struct thermal_zone_device *tz);
+void thermal_zone_device_critical(struct thermal_zone_device *tz);
 #else
 static inline struct thermal_zone_device *thermal_zone_device_register(
        const char *type, int trips, int mask, void *devdata,
index 135e542..8a5f048 100644 (file)
@@ -153,31 +153,30 @@ TRACE_EVENT(thermal_power_cpu_limit,
 TRACE_EVENT(thermal_power_devfreq_get_power,
        TP_PROTO(struct thermal_cooling_device *cdev,
                 struct devfreq_dev_status *status, unsigned long freq,
-               u32 dynamic_power, u32 static_power, u32 power),
+               u32 power),
 
-       TP_ARGS(cdev, status,  freq, dynamic_power, static_power, power),
+       TP_ARGS(cdev, status,  freq, power),
 
        TP_STRUCT__entry(
                __string(type,         cdev->type    )
                __field(unsigned long, freq          )
-               __field(u32,           load          )
-               __field(u32,           dynamic_power )
-               __field(u32,           static_power  )
+               __field(u32,           busy_time)
+               __field(u32,           total_time)
                __field(u32,           power)
        ),
 
        TP_fast_assign(
                __assign_str(type, cdev->type);
                __entry->freq = freq;
-               __entry->load = (100 * status->busy_time) / status->total_time;
-               __entry->dynamic_power = dynamic_power;
-               __entry->static_power = static_power;
+               __entry->busy_time = status->busy_time;
+               __entry->total_time = status->total_time;
                __entry->power = power;
        ),
 
-       TP_printk("type=%s freq=%lu load=%u dynamic_power=%u static_power=%u power=%u",
+       TP_printk("type=%s freq=%lu load=%u power=%u",
                __get_str(type), __entry->freq,
-               __entry->load, __entry->dynamic_power, __entry->static_power,
+               __entry->total_time == 0 ? 0 :
+                       (100 * __entry->busy_time) / __entry->total_time,
                __entry->power)
 );