Commit 583f2bcf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'thermal-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux

Pull thermal updates from Daniel Lezcano:

 - Remove duplicate error message for the amlogic driver (Tang Bin)

 - Fix spellos in comments for the tegra and sun8i (Bhaskar Chowdhury)

 - Add the missing fifth node on the rcar_gen3 sensor (Niklas Söderlund)

 - Remove duplicate include in ti-bandgap (Zhang Yunkai)

 - Assign error code in the error path in the function
   thermal_of_populate_bind_params() (Jia-Ju Bai)

 - Fix spelling mistake in a comment 'disabed' -> 'disabled' (Colin Ian
   King)

 - Use the device name instead of auto-numbering for a better
   identification of the cooling device (Daniel Lezcano)

 - Improve a bit the division accuracy in the power allocator governor
   (Jeson Gao)

 - Enable the missing third sensor on msm8976 (Konrad Dybcio)

 - Add QCom tsens driver co-maintainer (Thara Gopinath)

 - Fix memory leak and use after free errors in the core code (Daniel
   Lezcano)

 - Add the MDM9607 compatible bindings (Konrad Dybcio)

 - Fix trivial spello in the copyright name for Hisilicon (Hao Fang)

 - Fix negative index array access when converting the frequency to
   power in the energy model (Brian-sy Yang)

 - Add support for Gen2 new PMIC support for Qcom SPMI (David Collins)

 - Update maintainer file for CPU cooling device section (Lukasz Luba)

 - Fix missing put_device on error in the Qcom tsens driver (Guangqing
   Zhu)

 - Add compatible DT binding for sm8350 (Robert Foss)

 - Add support for the MDM9607's tsens driver (Konrad Dybcio)

 - Remove duplicate error messages in thermal_mmio and the bcm2835
   driver (Ruiqi Gong)

 - Add the Thermal Temperature Cooling driver (Zhang Rui)

 - Remove duplicate error messages in the Hisilicon sensor driver (Ye
   Bin)

 - Use the devm_platform_ioremap_resource_byname() function instead of a
   couple of corresponding calls (dingsenjie)

 - Sort the headers alphabetically in the ti-bandgap driver (Zhen Lei)

 - Add missing property in the DT thermal sensor binding (Rafał Miłecki)

 - Remove dead code in the ti-bandgap sensor driver (Lin Ruizhe)

 - Convert the BRCM DT bindings to the yaml schema (Rafał Miłecki)

 - Replace the thermal_notify_framework() call by a call to the
   thermal_zone_device_update() function. Remove the function as well as
   the corresponding documentation (Thara Gopinath)

 - Add support for the ipq8064-tsens sensor along with a set of cleanups
   and code preparation (Ansuel Smith)

 - Add a lockless __thermal_cdev_update() function to improve the
   locking scheme in the core code and governors (Lukasz Luba)

 - Fix multiple cooling device notification changes (Lukasz Luba)

 - Remove unneeded variable initialization (Colin Ian King)

* tag 'thermal-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux: (55 commits)
  thermal/drivers/mtk_thermal: Remove redundant initializations of several variables
  thermal/core/power allocator: Use the lockless __thermal_cdev_update() function
  thermal/core/fair share: Use the lockless __thermal_cdev_update() function
  thermal/core/fair share: Lock the thermal zone while looping over instances
  thermal/core/power_allocator: Update once cooling devices when temp is low
  thermal/core/power_allocator: Maintain the device statistics from going stale
  thermal/core: Create a helper __thermal_cdev_update() without a lock
  dt-bindings: thermal: tsens: Document ipq8064 bindings
  thermal/drivers/tsens: Add support for ipq8064-tsens
  thermal/drivers/tsens: Drop unused define for msm8960
  thermal/drivers/tsens: Replace custom 8960 apis with generic apis
  thermal/drivers/tsens: Fix bug in sensor enable for msm8960
  thermal/drivers/tsens: Use init_common for msm8960
  thermal/drivers/tsens: Add VER_0 tsens version
  thermal/drivers/tsens: Convert msm8960 to reg_field
  thermal/drivers/tsens: Don't hardcode sensor slope
  Documentation: driver-api: thermal: Remove thermal_notify_framework from documentation
  thermal/core: Remove thermal_notify_framework
  iwlwifi: mvm: tt: Replace thermal_notify_framework
  dt-bindings: thermal: brcm,ns-thermal: Convert to the json-schema
  ...
parents 5d6a1b84 c310e546
* Broadcom Northstar Thermal
This binding describes thermal sensor that is part of Northstar's DMU (Device
Management Unit).
Required properties:
- compatible : Must be "brcm,ns-thermal"
- reg : iomem address range of PVTMON registers
- #thermal-sensor-cells : Should be <0>
Example:
thermal: thermal@1800c2c0 {
compatible = "brcm,ns-thermal";
reg = <0x1800c2c0 0x10>;
#thermal-sensor-cells = <0>;
};
thermal-zones {
cpu_thermal: cpu-thermal {
polling-delay-passive = <0>;
polling-delay = <1000>;
coefficients = <(-556) 418000>;
thermal-sensors = <&thermal>;
trips {
cpu-crit {
temperature = <125000>;
hysteresis = <0>;
type = "critical";
};
};
cooling-maps {
};
};
};
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/thermal/brcm,ns-thermal.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Northstar Thermal
maintainers:
- Rafał Miłecki <rafal@milecki.pl>
description:
Thermal sensor that is part of Northstar's DMU (Device Management Unit).
allOf:
- $ref: thermal-sensor.yaml#
properties:
compatible:
const: brcm,ns-thermal
reg:
description: PVTMON registers range
maxItems: 1
"#thermal-sensor-cells":
const: 0
unevaluatedProperties: false
required:
- reg
examples:
- |
thermal: thermal@1800c2c0 {
compatible = "brcm,ns-thermal";
reg = <0x1800c2c0 0x10>;
#thermal-sensor-cells = <0>;
};
thermal-zones {
cpu-thermal {
polling-delay-passive = <0>;
polling-delay = <1000>;
coefficients = <(-556) 418000>;
thermal-sensors = <&thermal>;
trips {
cpu-crit {
temperature = <125000>;
hysteresis = <0>;
type = "critical";
};
};
cooling-maps {
};
};
};
...@@ -19,9 +19,15 @@ description: | ...@@ -19,9 +19,15 @@ description: |
properties: properties:
compatible: compatible:
oneOf: oneOf:
- description: msm9860 TSENS based
items:
- enum:
- qcom,ipq8064-tsens
- description: v0.1 of TSENS - description: v0.1 of TSENS
items: items:
- enum: - enum:
- qcom,mdm9607-tsens
- qcom,msm8916-tsens - qcom,msm8916-tsens
- qcom,msm8939-tsens - qcom,msm8939-tsens
- qcom,msm8974-tsens - qcom,msm8974-tsens
...@@ -43,6 +49,7 @@ properties: ...@@ -43,6 +49,7 @@ properties:
- qcom,sdm845-tsens - qcom,sdm845-tsens
- qcom,sm8150-tsens - qcom,sm8150-tsens
- qcom,sm8250-tsens - qcom,sm8250-tsens
- qcom,sm8350-tsens
- const: qcom,tsens-v2 - const: qcom,tsens-v2
reg: reg:
...@@ -73,7 +80,9 @@ properties: ...@@ -73,7 +80,9 @@ properties:
maxItems: 2 maxItems: 2
items: items:
- const: calib - const: calib
- const: calib_sel - enum:
- calib_backup
- calib_sel
"#qcom,sensors": "#qcom,sensors":
description: description:
...@@ -88,12 +97,21 @@ properties: ...@@ -88,12 +97,21 @@ properties:
Number of cells required to uniquely identify the thermal sensors. Since Number of cells required to uniquely identify the thermal sensors. Since
we have multiple sensors this is set to 1 we have multiple sensors this is set to 1
required:
- compatible
- interrupts
- interrupt-names
- "#thermal-sensor-cells"
- "#qcom,sensors"
allOf: allOf:
- if: - if:
properties: properties:
compatible: compatible:
contains: contains:
enum: enum:
- qcom,ipq8064-tsens
- qcom,mdm9607-tsens
- qcom,msm8916-tsens - qcom,msm8916-tsens
- qcom,msm8974-tsens - qcom,msm8974-tsens
- qcom,msm8976-tsens - qcom,msm8976-tsens
...@@ -114,17 +132,42 @@ allOf: ...@@ -114,17 +132,42 @@ allOf:
interrupt-names: interrupt-names:
minItems: 2 minItems: 2
required: - if:
- compatible properties:
- reg compatible:
- "#qcom,sensors" contains:
- interrupts enum:
- interrupt-names - qcom,tsens-v0_1
- "#thermal-sensor-cells" - qcom,tsens-v1
- qcom,tsens-v2
then:
required:
- reg
additionalProperties: false additionalProperties: false
examples: examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
// Example msm9860 based SoC (ipq8064):
gcc: clock-controller {
/* ... */
tsens: thermal-sensor {
compatible = "qcom,ipq8064-tsens";
nvmem-cells = <&tsens_calib>, <&tsens_calib_backup>;
nvmem-cell-names = "calib", "calib_backup";
interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "uplow";
#qcom,sensors = <11>;
#thermal-sensor-cells = <1>;
};
};
- | - |
#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/interrupt-controller/arm-gic.h>
// Example 1 (legacy: for pre v1 IP): // Example 1 (legacy: for pre v1 IP):
......
...@@ -28,14 +28,7 @@ properties: ...@@ -28,14 +28,7 @@ properties:
- renesas,r8a77980-thermal # R-Car V3H - renesas,r8a77980-thermal # R-Car V3H
- renesas,r8a779a0-thermal # R-Car V3U - renesas,r8a779a0-thermal # R-Car V3U
reg: reg: true
minItems: 2
maxItems: 4
items:
- description: TSC1 registers
- description: TSC2 registers
- description: TSC3 registers
- description: TSC4 registers
interrupts: interrupts:
items: items:
...@@ -71,8 +64,25 @@ if: ...@@ -71,8 +64,25 @@ if:
enum: enum:
- renesas,r8a779a0-thermal - renesas,r8a779a0-thermal
then: then:
properties:
reg:
minItems: 2
maxItems: 3
items:
- description: TSC1 registers
- description: TSC2 registers
- description: TSC3 registers
required: required:
- interrupts - interrupts
else:
properties:
reg:
items:
- description: TSC0 registers
- description: TSC1 registers
- description: TSC2 registers
- description: TSC3 registers
- description: TSC4 registers
additionalProperties: false additionalProperties: false
...@@ -111,3 +121,20 @@ examples: ...@@ -111,3 +121,20 @@ examples:
}; };
}; };
}; };
- |
#include <dt-bindings/clock/r8a779a0-cpg-mssr.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/r8a779a0-sysc.h>
tsc_r8a779a0: thermal@e6190000 {
compatible = "renesas,r8a779a0-thermal";
reg = <0xe6190000 0x200>,
<0xe6198000 0x200>,
<0xe61a0000 0x200>,
<0xe61a8000 0x200>,
<0xe61b0000 0x200>;
clocks = <&cpg CPG_MOD 919>;
power-domains = <&sysc R8A779A0_PD_ALWAYS_ON>;
resets = <&cpg 919>;
#thermal-sensor-cells = <1>;
};
...@@ -36,6 +36,9 @@ properties: ...@@ -36,6 +36,9 @@ properties:
containing several internal sensors. containing several internal sensors.
enum: [0, 1] enum: [0, 1]
required:
- "#thermal-sensor-cells"
additionalProperties: true additionalProperties: true
examples: examples:
......
...@@ -730,17 +730,7 @@ This function returns the thermal_instance corresponding to a given ...@@ -730,17 +730,7 @@ This function returns the thermal_instance corresponding to a given
{thermal_zone, cooling_device, trip_point} combination. Returns NULL {thermal_zone, cooling_device, trip_point} combination. Returns NULL
if such an instance does not exist. if such an instance does not exist.
4.3. thermal_notify_framework 4.3. thermal_cdev_update
-----------------------------
This function handles the trip events from sensor drivers. It starts
throttling the cooling devices according to the policy configured.
For CRITICAL and HOT trip points, this notifies the respective drivers,
and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
The throttling policy is based on the configured platform data; if no
platform data is provided, this uses the step_wise throttling policy.
4.4. thermal_cdev_update
------------------------ ------------------------
This function serves as an arbitrator to set the state of a cooling This function serves as an arbitrator to set the state of a cooling
......
...@@ -15197,6 +15197,7 @@ F: include/linux/if_rmnet.h ...@@ -15197,6 +15197,7 @@ F: include/linux/if_rmnet.h
QUALCOMM TSENS THERMAL DRIVER QUALCOMM TSENS THERMAL DRIVER
M: Amit Kucheria <amitk@kernel.org> M: Amit Kucheria <amitk@kernel.org>
M: Thara Gopinath <thara.gopinath@linaro.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
L: linux-arm-msm@vger.kernel.org L: linux-arm-msm@vger.kernel.org
S: Maintained S: Maintained
...@@ -18101,7 +18102,7 @@ THERMAL/CPU_COOLING ...@@ -18101,7 +18102,7 @@ THERMAL/CPU_COOLING
M: Amit Daniel Kachhap <amit.kachhap@gmail.com> M: Amit Daniel Kachhap <amit.kachhap@gmail.com>
M: Daniel Lezcano <daniel.lezcano@linaro.org> M: Daniel Lezcano <daniel.lezcano@linaro.org>
M: Viresh Kumar <viresh.kumar@linaro.org> M: Viresh Kumar <viresh.kumar@linaro.org>
M: Javi Merino <javi.merino@kernel.org> R: Lukasz Luba <lukasz.luba@arm.com>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
S: Supported S: Supported
F: Documentation/driver-api/thermal/cpu-cooling-api.rst F: Documentation/driver-api/thermal/cpu-cooling-api.rst
......
...@@ -132,7 +132,7 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal, ...@@ -132,7 +132,7 @@ static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
/* Allow mlxsw thermal zone binding to an external cooling device */ /* Allow mlxsw thermal zone binding to an external cooling device */
for (i = 0; i < ARRAY_SIZE(mlxsw_thermal_external_allowed_cdev); i++) { for (i = 0; i < ARRAY_SIZE(mlxsw_thermal_external_allowed_cdev); i++) {
if (strnstr(cdev->type, mlxsw_thermal_external_allowed_cdev[i], if (strnstr(cdev->type, mlxsw_thermal_external_allowed_cdev[i],
sizeof(cdev->type))) strlen(cdev->type)))
return 0; return 0;
} }
......
...@@ -146,8 +146,8 @@ void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ...@@ -146,8 +146,8 @@ void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
if (mvm->tz_device.tzone) { if (mvm->tz_device.tzone) {
struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device; struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
thermal_notify_framework(tz_dev->tzone, thermal_zone_device_update(tz_dev->tzone,
tz_dev->fw_trips_index[ths_crossed]); THERMAL_TRIP_VIOLATED);
} }
#endif /* CONFIG_THERMAL */ #endif /* CONFIG_THERMAL */
} }
......
...@@ -254,10 +254,8 @@ static int amlogic_thermal_probe(struct platform_device *pdev) ...@@ -254,10 +254,8 @@ static int amlogic_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pdata); platform_set_drvdata(pdev, pdata);
base = devm_platform_ioremap_resource(pdev, 0); base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) { if (IS_ERR(base))
dev_err(dev, "failed to get io address\n");
return PTR_ERR(base); return PTR_ERR(base);
}
pdata->regmap = devm_regmap_init_mmio(dev, base, pdata->regmap = devm_regmap_init_mmio(dev, base,
pdata->data->regmap_config); pdata->data->regmap_config);
......
...@@ -184,7 +184,6 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) ...@@ -184,7 +184,6 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
data->regs = devm_ioremap_resource(&pdev->dev, res); data->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->regs)) { if (IS_ERR(data->regs)) {
err = PTR_ERR(data->regs); err = PTR_ERR(data->regs);
dev_err(&pdev->dev, "Could not get registers: %d\n", err);
return err; return err;
} }
......
...@@ -13,10 +13,10 @@ ...@@ -13,10 +13,10 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/device.h>
#include <linux/energy_model.h> #include <linux/energy_model.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/idr.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -50,8 +50,6 @@ struct time_in_idle { ...@@ -50,8 +50,6 @@ struct time_in_idle {
/** /**
* struct cpufreq_cooling_device - data for cooling device with cpufreq * struct cpufreq_cooling_device - data for cooling device with cpufreq
* @id: unique integer value corresponding to each cpufreq_cooling_device
* registered.
* @last_load: load measured by the latest call to cpufreq_get_requested_power() * @last_load: load measured by the latest call to cpufreq_get_requested_power()
* @cpufreq_state: integer value representing the current state of cpufreq * @cpufreq_state: integer value representing the current state of cpufreq
* cooling devices. * cooling devices.
...@@ -61,7 +59,6 @@ struct time_in_idle { ...@@ -61,7 +59,6 @@ struct time_in_idle {
* @cdev: thermal_cooling_device pointer to keep track of the * @cdev: thermal_cooling_device pointer to keep track of the
* registered cooling device. * registered cooling device.
* @policy: cpufreq policy. * @policy: cpufreq policy.
* @node: list_head to link all cpufreq_cooling_device together.
* @idle_time: idle time stats * @idle_time: idle time stats
* @qos_req: PM QoS contraint to apply * @qos_req: PM QoS contraint to apply
* *
...@@ -69,23 +66,17 @@ struct time_in_idle { ...@@ -69,23 +66,17 @@ struct time_in_idle {
* cpufreq_cooling_device. * cpufreq_cooling_device.
*/ */
struct cpufreq_cooling_device { struct cpufreq_cooling_device {
int id;
u32 last_load; u32 last_load;
unsigned int cpufreq_state; unsigned int cpufreq_state;
unsigned int max_level; unsigned int max_level;
struct em_perf_domain *em; struct em_perf_domain *em;
struct cpufreq_policy *policy; struct cpufreq_policy *policy;
struct list_head node;
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
struct time_in_idle *idle_time; struct time_in_idle *idle_time;
#endif #endif
struct freq_qos_request qos_req; struct freq_qos_request qos_req;
}; };
static DEFINE_IDA(cpufreq_ida);
static DEFINE_MUTEX(cooling_list_lock);
static LIST_HEAD(cpufreq_cdev_list);
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR #ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
/** /**
* get_level: Find the level for a particular frequency * get_level: Find the level for a particular frequency
...@@ -125,7 +116,7 @@ static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, ...@@ -125,7 +116,7 @@ static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev,
{ {
int i; int i;
for (i = cpufreq_cdev->max_level; i >= 0; i--) { for (i = cpufreq_cdev->max_level; i > 0; i--) {
if (power >= cpufreq_cdev->em->table[i].power) if (power >= cpufreq_cdev->em->table[i].power)
break; break;
} }
...@@ -528,11 +519,11 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -528,11 +519,11 @@ __cpufreq_cooling_register(struct device_node *np,
{ {
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct cpufreq_cooling_device *cpufreq_cdev; struct cpufreq_cooling_device *cpufreq_cdev;
char dev_name[THERMAL_NAME_LENGTH];
unsigned int i; unsigned int i;
struct device *dev; struct device *dev;
int ret; int ret;
struct thermal_cooling_device_ops *cooling_ops; struct thermal_cooling_device_ops *cooling_ops;
char *name;
dev = get_cpu_device(policy->cpu); dev = get_cpu_device(policy->cpu);
if (unlikely(!dev)) { if (unlikely(!dev)) {
...@@ -567,16 +558,6 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -567,16 +558,6 @@ __cpufreq_cooling_register(struct device_node *np,
/* max_level is an index, not a counter */ /* max_level is an index, not a counter */
cpufreq_cdev->max_level = i - 1; cpufreq_cdev->max_level = i - 1;
ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL);
if (ret < 0) {
cdev = ERR_PTR(ret);
goto free_idle_time;
}
cpufreq_cdev->id = ret;
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
cpufreq_cdev->id);
cooling_ops = &cpufreq_cooling_ops; cooling_ops = &cpufreq_cooling_ops;
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR #ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
...@@ -591,7 +572,7 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -591,7 +572,7 @@ __cpufreq_cooling_register(struct device_node *np,
pr_err("%s: unsorted frequency tables are not supported\n", pr_err("%s: unsorted frequency tables are not supported\n",
__func__); __func__);
cdev = ERR_PTR(-EINVAL); cdev = ERR_PTR(-EINVAL);
goto remove_ida; goto free_idle_time;
} }
ret = freq_qos_add_request(&policy->constraints, ret = freq_qos_add_request(&policy->constraints,
...@@ -601,24 +582,25 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -601,24 +582,25 @@ __cpufreq_cooling_register(struct device_node *np,
pr_err("%s: Failed to add freq constraint (%d)\n", __func__, pr_err("%s: Failed to add freq constraint (%d)\n", __func__,
ret); ret);
cdev = ERR_PTR(ret); cdev = ERR_PTR(ret);
goto remove_ida; goto free_idle_time;
} }
cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cdev = ERR_PTR(-ENOMEM);
name = kasprintf(GFP_KERNEL, "cpufreq-%s", dev_name(dev));
if (!name)
goto remove_qos_req;
cdev = thermal_of_cooling_device_register(np, name, cpufreq_cdev,
cooling_ops); cooling_ops);
kfree(name);
if (IS_ERR(cdev)) if (IS_ERR(cdev))
goto remove_qos_req; goto remove_qos_req;
mutex_lock(&cooling_list_lock);
list_add(&cpufreq_cdev->node, &cpufreq_cdev_list);
mutex_unlock(&cooling_list_lock);
return cdev; return cdev;
remove_qos_req: remove_qos_req:
freq_qos_remove_request(&cpufreq_cdev->qos_req); freq_qos_remove_request(&cpufreq_cdev->qos_req);
remove_ida:
ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
free_idle_time: free_idle_time:
free_idle_time(cpufreq_cdev); free_idle_time(cpufreq_cdev);
free_cdev: free_cdev:
...@@ -706,13 +688,8 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) ...@@ -706,13 +688,8 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
cpufreq_cdev = cdev->devdata; cpufreq_cdev = cdev->devdata;
mutex_lock(&cooling_list_lock);
list_del(&cpufreq_cdev->node);
mutex_unlock(&cooling_list_lock);
thermal_cooling_device_unregister(cdev); thermal_cooling_device_unregister(cdev);
freq_qos_remove_request(&cpufreq_cdev->qos_req); freq_qos_remove_request(&cpufreq_cdev->qos_req);
ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id);
free_idle_time(cpufreq_cdev); free_idle_time(cpufreq_cdev);
kfree(cpufreq_cdev); kfree(cpufreq_cdev);
} }
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/idle_inject.h> #include <linux/idle_inject.h>
#include <linux/idr.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/thermal.h> #include <linux/thermal.h>
...@@ -26,8 +26,6 @@ struct cpuidle_cooling_device { ...@@ -26,8 +26,6 @@ struct cpuidle_cooling_device {
unsigned long state; unsigned long state;
}; };
static DEFINE_IDA(cpuidle_ida);
/** /**
* cpuidle_cooling_runtime - Running time computation * cpuidle_cooling_runtime - Running time computation
* @idle_duration_us: CPU idle time to inject in microseconds * @idle_duration_us: CPU idle time to inject in microseconds
...@@ -174,10 +172,11 @@ static int __cpuidle_cooling_register(struct device_node *np, ...@@ -174,10 +172,11 @@ static int __cpuidle_cooling_register(struct device_node *np,
struct idle_inject_device *ii_dev; struct idle_inject_device *ii_dev;
struct cpuidle_cooling_device *idle_cdev; struct cpuidle_cooling_device *idle_cdev;
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct device *dev;
unsigned int idle_duration_us = TICK_USEC; unsigned int idle_duration_us = TICK_USEC;
unsigned int latency_us = UINT_MAX; unsigned int latency_us = UINT_MAX;
char dev_name[THERMAL_NAME_LENGTH]; char *name;
int id, ret; int ret;
idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL); idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL);
if (!idle_cdev) { if (!idle_cdev) {
...@@ -185,16 +184,10 @@ static int __cpuidle_cooling_register(struct device_node *np, ...@@ -185,16 +184,10 @@ static int __cpuidle_cooling_register(struct device_node *np,
goto out; goto out;
} }
id = ida_simple_get(&cpuidle_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
ret = id;
goto out_kfree;
}
ii_dev = idle_inject_register(drv->cpumask); ii_dev = idle_inject_register(drv->cpumask);
if (!ii_dev) { if (!ii_dev) {
ret = -EINVAL; ret = -EINVAL;
goto out_id; goto out_kfree;
} }
of_property_read_u32(np, "duration-us", &idle_duration_us); of_property_read_u32(np, "duration-us", &idle_duration_us);
...@@ -205,24 +198,32 @@ static int __cpuidle_cooling_register(struct device_node *np, ...@@ -205,24 +198,32 @@ static int __cpuidle_cooling_register(struct device_node *np,
idle_cdev->ii_dev = ii_dev; idle_cdev->ii_dev = ii_dev;
snprintf(dev_name, sizeof(dev_name), "thermal-idle-%d", id); dev = get_cpu_device(cpumask_first(drv->cpumask));
cdev = thermal_of_cooling_device_register(np, dev_name, idle_cdev, name = kasprintf(GFP_KERNEL, "idle-%s", dev_name(dev));
if (!name) {
ret = -ENOMEM;
goto out_unregister;
}
cdev = thermal_of_cooling_device_register(np, name, idle_cdev,
&cpuidle_cooling_ops); &cpuidle_cooling_ops);
if (IS_ERR(cdev)) { if (IS_ERR(cdev)) {
ret = PTR_ERR(cdev); ret = PTR_ERR(cdev);
goto out_unregister; goto out_kfree_name;
} }
pr_debug("%s: Idle injection set with idle duration=%u, latency=%u\n", pr_debug("%s: Idle injection set with idle duration=%u, latency=%u\n",
dev_name, idle_duration_us, latency_us); name, idle_duration_us, latency_us);
kfree(name);
return 0; return 0;
out_kfree_name:
kfree(name);
out_unregister: out_unregister:
idle_inject_unregister(ii_dev); idle_inject_unregister(ii_dev);
out_id:
ida_simple_remove(&cpuidle_ida, id);
out_kfree: out_kfree:
kfree(idle_cdev); kfree(idle_cdev);
out: out:
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/devfreq_cooling.h> #include <linux/devfreq_cooling.h>
#include <linux/energy_model.h> #include <linux/energy_model.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/idr.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
...@@ -25,11 +24,8 @@ ...@@ -25,11 +24,8 @@
#define HZ_PER_KHZ 1000 #define HZ_PER_KHZ 1000
#define SCALE_ERROR_MITIGATION 100 #define SCALE_ERROR_MITIGATION 100
static DEFINE_IDA(devfreq_ida);
/** /**
* struct devfreq_cooling_device - Devfreq cooling device * struct devfreq_cooling_device - Devfreq cooling device
* @id: unique integer value corresponding to each
* devfreq_cooling_device registered. * devfreq_cooling_device registered.
* @cdev: Pointer to associated thermal cooling device. * @cdev: Pointer to associated thermal cooling device.
* @devfreq: Pointer to associated devfreq device. * @devfreq: Pointer to associated devfreq device.
...@@ -51,7 +47,6 @@ static DEFINE_IDA(devfreq_ida); ...@@ -51,7 +47,6 @@ static DEFINE_IDA(devfreq_ida);
* @em_pd: Energy Model for the associated Devfreq device * @em_pd: Energy Model for the associated Devfreq device
*/ */
struct devfreq_cooling_device { struct devfreq_cooling_device {
int id;
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct devfreq *devfreq; struct devfreq *devfreq;
unsigned long cooling_state; unsigned long cooling_state;
...@@ -363,7 +358,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, ...@@ -363,7 +358,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct device *dev = df->dev.parent; struct device *dev = df->dev.parent;
struct devfreq_cooling_device *dfc; struct devfreq_cooling_device *dfc;
char dev_name[THERMAL_NAME_LENGTH]; char *name;
int err, num_opps; int err, num_opps;
dfc = kzalloc(sizeof(*dfc), GFP_KERNEL); dfc = kzalloc(sizeof(*dfc), GFP_KERNEL);
...@@ -407,30 +402,27 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, ...@@ -407,30 +402,27 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
if (err < 0) if (err < 0)
goto free_table; goto free_table;
err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL); err = -ENOMEM;
if (err < 0) name = kasprintf(GFP_KERNEL, "devfreq-%s", dev_name(dev));
if (!name)
goto remove_qos_req; goto remove_qos_req;
dfc->id = err; cdev = thermal_of_cooling_device_register(np, name, dfc,
snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id);
cdev = thermal_of_cooling_device_register(np, dev_name, dfc,
&devfreq_cooling_ops); &devfreq_cooling_ops);
kfree(name);
if (IS_ERR(cdev)) { if (IS_ERR(cdev)) {
err = PTR_ERR(cdev); err = PTR_ERR(cdev);
dev_err(dev, dev_err(dev,
"Failed to register devfreq cooling device (%d)\n", "Failed to register devfreq cooling device (%d)\n",
err); err);
goto release_ida; goto remove_qos_req;
} }
dfc->cdev = cdev; dfc->cdev = cdev;
return cdev; return cdev;
release_ida:
ida_simple_remove(&devfreq_ida, dfc->id);
remove_qos_req: remove_qos_req:
dev_pm_qos_remove_request(&dfc->req_max_freq); dev_pm_qos_remove_request(&dfc->req_max_freq);
free_table: free_table:
...@@ -527,7 +519,6 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) ...@@ -527,7 +519,6 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
dev = dfc->devfreq->dev.parent; dev = dfc->devfreq->dev.parent;
thermal_cooling_device_unregister(dfc->cdev); thermal_cooling_device_unregister(dfc->cdev);
ida_simple_remove(&devfreq_ida, dfc->id);
dev_pm_qos_remove_request(&dfc->req_max_freq); dev_pm_qos_remove_request(&dfc->req_max_freq);
em_dev_unregister_perf_domain(dev); em_dev_unregister_perf_domain(dev);
......
...@@ -82,6 +82,8 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip) ...@@ -82,6 +82,8 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
int total_instance = 0; int total_instance = 0;
int cur_trip_level = get_trip_level(tz); int cur_trip_level = get_trip_level(tz);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) { list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip) if (instance->trip != trip)
continue; continue;
...@@ -105,11 +107,12 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip) ...@@ -105,11 +107,12 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
instance->target = get_target_state(tz, cdev, percentage, instance->target = get_target_state(tz, cdev, percentage,
cur_trip_level); cur_trip_level);
mutex_lock(&instance->cdev->lock); mutex_lock(&cdev->lock);
instance->cdev->updated = false; __thermal_cdev_update(cdev);
mutex_unlock(&instance->cdev->lock); mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
} }
mutex_unlock(&tz->lock);
return 0; return 0;
} }
......
...@@ -301,9 +301,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev, ...@@ -301,9 +301,8 @@ power_actor_set_power(struct thermal_cooling_device *cdev,
instance->target = clamp_val(state, instance->lower, instance->upper); instance->target = clamp_val(state, instance->lower, instance->upper);
mutex_lock(&cdev->lock); mutex_lock(&cdev->lock);
cdev->updated = false; __thermal_cdev_update(cdev);
mutex_unlock(&cdev->lock); mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
return 0; return 0;
} }
...@@ -374,9 +373,11 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors, ...@@ -374,9 +373,11 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
*/ */
extra_power = min(extra_power, capped_extra_power); extra_power = min(extra_power, capped_extra_power);
if (capped_extra_power > 0) if (capped_extra_power > 0)
for (i = 0; i < num_actors; i++) for (i = 0; i < num_actors; i++) {
granted_power[i] += (extra_actor_power[i] * u64 extra_range = (u64)extra_actor_power[i] * extra_power;
extra_power) / capped_extra_power; granted_power[i] += DIV_ROUND_CLOSEST_ULL(extra_range,
capped_extra_power);
}
} }
static int allocate_power(struct thermal_zone_device *tz, static int allocate_power(struct thermal_zone_device *tz,
...@@ -569,22 +570,33 @@ static void reset_pid_controller(struct power_allocator_params *params) ...@@ -569,22 +570,33 @@ static void reset_pid_controller(struct power_allocator_params *params)
params->prev_err = 0; params->prev_err = 0;
} }
static void allow_maximum_power(struct thermal_zone_device *tz) static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
{ {
struct thermal_instance *instance; struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data; struct power_allocator_params *params = tz->governor_data;
u32 req_power;
mutex_lock(&tz->lock); mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) { list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
if ((instance->trip != params->trip_max_desired_temperature) || if ((instance->trip != params->trip_max_desired_temperature) ||
(!cdev_is_power_actor(instance->cdev))) (!cdev_is_power_actor(instance->cdev)))
continue; continue;
instance->target = 0; instance->target = 0;
mutex_lock(&instance->cdev->lock); mutex_lock(&instance->cdev->lock);
instance->cdev->updated = false; /*
* Call for updating the cooling devices local stats and avoid
* periods of dozen of seconds when those have not been
* maintained.
*/
cdev->ops->get_requested_power(cdev, &req_power);
if (update)
__thermal_cdev_update(instance->cdev);
mutex_unlock(&instance->cdev->lock); mutex_unlock(&instance->cdev->lock);
thermal_cdev_update(instance->cdev);
} }
mutex_unlock(&tz->lock); mutex_unlock(&tz->lock);
} }
...@@ -698,6 +710,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) ...@@ -698,6 +710,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
int ret; int ret;
int switch_on_temp, control_temp; int switch_on_temp, control_temp;
struct power_allocator_params *params = tz->governor_data; struct power_allocator_params *params = tz->governor_data;
bool update;
/* /*
* We get called for every trip point but we only need to do * We get called for every trip point but we only need to do
...@@ -709,9 +722,10 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) ...@@ -709,9 +722,10 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
&switch_on_temp); &switch_on_temp);
if (!ret && (tz->temperature < switch_on_temp)) { if (!ret && (tz->temperature < switch_on_temp)) {
update = (tz->last_temperature >= switch_on_temp);
tz->passive = 0; tz->passive = 0;
reset_pid_controller(params); reset_pid_controller(params);
allow_maximum_power(tz); allow_maximum_power(tz, update);
return 0; return 0;
} }
......
/* /*
* Hisilicon thermal sensor driver * HiSilicon thermal sensor driver
* *
* Copyright (c) 2014-2015 Hisilicon Limited. * Copyright (c) 2014-2015 HiSilicon Limited.
* Copyright (c) 2014-2015 Linaro Limited. * Copyright (c) 2014-2015 Linaro Limited.
* *
* Xinwei Kong <kong.kongxinwei@hisilicon.com> * Xinwei Kong <kong.kongxinwei@hisilicon.com>
...@@ -572,10 +572,8 @@ static int hisi_thermal_probe(struct platform_device *pdev) ...@@ -572,10 +572,8 @@ static int hisi_thermal_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->regs = devm_ioremap_resource(dev, res); data->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(data->regs)) { if (IS_ERR(data->regs))
dev_err(dev, "failed to get io address\n");
return PTR_ERR(data->regs); return PTR_ERR(data->regs);
}
ret = data->ops->probe(data); ret = data->ops->probe(data);
if (ret) if (ret)
...@@ -672,5 +670,5 @@ module_platform_driver(hisi_thermal_driver); ...@@ -672,5 +670,5 @@ module_platform_driver(hisi_thermal_driver);
MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>"); MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
MODULE_DESCRIPTION("Hisilicon thermal driver"); MODULE_DESCRIPTION("HiSilicon thermal driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -79,3 +79,14 @@ config INTEL_PCH_THERMAL ...@@ -79,3 +79,14 @@ config INTEL_PCH_THERMAL
Enable this to support thermal reporting on certain intel PCHs. Enable this to support thermal reporting on certain intel PCHs.
Thermal reporting device will provide temperature reading, Thermal reporting device will provide temperature reading,
programmable trip points and other information. programmable trip points and other information.
config INTEL_TCC_COOLING
tristate "Intel TCC offset cooling Driver"
depends on X86
help
Enable this to support system cooling by adjusting the effective TCC
activation temperature via the TCC Offset register, which is widely
supported on modern Intel platforms.
Note that, on different platforms, the behavior might be different
on how fast the setting takes effect, and how much the CPU frequency
is reduced.
...@@ -10,4 +10,5 @@ obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o ...@@ -10,4 +10,5 @@ obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
obj-$(CONFIG_INTEL_TCC_COOLING) += intel_tcc_cooling.o
obj-$(CONFIG_X86_THERMAL_VECTOR) += therm_throt.o obj-$(CONFIG_X86_THERMAL_VECTOR) += therm_throt.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* cooling device driver that activates the processor throttling by
* programming the TCC Offset register.
* Copyright (c) 2021, Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <asm/cpu_device_id.h>
#define TCC_SHIFT 24
#define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30)
static struct thermal_cooling_device *tcc_cdev;
static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
*state = TCC_MASK >> TCC_SHIFT;
return 0;
}
static int tcc_offset_update(int tcc)
{
u64 val;
int err;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
val &= ~TCC_MASK;
val |= tcc << TCC_SHIFT;
err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
if (err)
return err;
return 0;
}
static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
u64 val;
int err;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
*state = (val & TCC_MASK) >> TCC_SHIFT;
return 0;
}
static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
state)
{
return tcc_offset_update(state);
}
static const struct thermal_cooling_device_ops tcc_cooling_ops = {
.get_max_state = tcc_get_max_state,
.get_cur_state = tcc_get_cur_state,
.set_cur_state = tcc_set_cur_state,
};
static const struct x86_cpu_id tcc_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL),
{}
};
MODULE_DEVICE_TABLE(x86cpu, tcc_ids);
static int __init tcc_cooling_init(void)
{
int ret;
u64 val;
const struct x86_cpu_id *id;
int err;
id = x86_match_cpu(tcc_ids);
if (!id)
return -ENODEV;
err = rdmsrl_safe(MSR_PLATFORM_INFO, &val);
if (err)
return err;
if (!(val & TCC_PROGRAMMABLE))
return -ENODEV;
pr_info("Programmable TCC Offset detected\n");
tcc_cdev =
thermal_cooling_device_register("TCC Offset", NULL,
&tcc_cooling_ops);
if (IS_ERR(tcc_cdev)) {
ret = PTR_ERR(tcc_cdev);
return ret;
}
return 0;
}
module_init(tcc_cooling_init)
static void __exit tcc_cooling_exit(void)
{
thermal_cooling_device_unregister(tcc_cdev);
}
module_exit(tcc_cooling_exit)
MODULE_DESCRIPTION("TCC offset cooling device Driver");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2");
...@@ -573,12 +573,12 @@ static int raw_to_mcelsius_v1(struct mtk_thermal *mt, int sensno, s32 raw) ...@@ -573,12 +573,12 @@ static int raw_to_mcelsius_v1(struct mtk_thermal *mt, int sensno, s32 raw)
static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw) static int raw_to_mcelsius_v2(struct mtk_thermal *mt, int sensno, s32 raw)
{ {
s32 format_1 = 0; s32 format_1;
s32 format_2 = 0; s32 format_2;
s32 g_oe = 1; s32 g_oe;
s32 g_gain = 1; s32 g_gain;
s32 g_x_roomt = 0; s32 g_x_roomt;
s32 tmp = 0; s32 tmp;
if (raw == 0) if (raw == 0)
return 0; return 0;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "../thermal_core.h" #include "../thermal_core.h"
#define QPNP_TM_REG_DIG_MAJOR 0x01
#define QPNP_TM_REG_TYPE 0x04 #define QPNP_TM_REG_TYPE 0x04
#define QPNP_TM_REG_SUBTYPE 0x05 #define QPNP_TM_REG_SUBTYPE 0x05
#define QPNP_TM_REG_STATUS 0x08 #define QPNP_TM_REG_STATUS 0x08
...@@ -38,26 +39,30 @@ ...@@ -38,26 +39,30 @@
#define ALARM_CTRL_FORCE_ENABLE BIT(7) #define ALARM_CTRL_FORCE_ENABLE BIT(7)
/* #define THRESH_COUNT 4
* Trip point values based on threshold control #define STAGE_COUNT 3
* 0 = {105 C, 125 C, 145 C}
* 1 = {110 C, 130 C, 150 C} /* Over-temperature trip point values in mC */
* 2 = {115 C, 135 C, 155 C} static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = {
* 3 = {120 C, 140 C, 160 C} { 105000, 125000, 145000 },
*/ { 110000, 130000, 150000 },
#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */ { 115000, 135000, 155000 },
#define TEMP_STAGE_HYSTERESIS 2000 { 120000, 140000, 160000 },
};
static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = {
{ 90000, 110000, 140000 },
{ 95000, 115000, 145000 },
{ 100000, 120000, 150000 },
{ 105000, 125000, 155000 },
};
#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */ #define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
#define THRESH_MIN 0 #define THRESH_MIN 0
#define THRESH_MAX 3 #define THRESH_MAX 3
/* Stage 2 Threshold Min: 125 C */ #define TEMP_STAGE_HYSTERESIS 2000
#define STAGE2_THRESHOLD_MIN 125000
/* Stage 2 Threshold Max: 140 C */
#define STAGE2_THRESHOLD_MAX 140000
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
#define DEFAULT_TEMP 37000 #define DEFAULT_TEMP 37000
...@@ -77,6 +82,7 @@ struct qpnp_tm_chip { ...@@ -77,6 +82,7 @@ struct qpnp_tm_chip {
bool initialized; bool initialized;
struct iio_channel *adc; struct iio_channel *adc;
const long (*temp_map)[THRESH_COUNT][STAGE_COUNT];
}; };
/* This array maps from GEN2 alarm state to GEN1 alarm stage */ /* This array maps from GEN2 alarm state to GEN1 alarm stage */
...@@ -100,6 +106,23 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data) ...@@ -100,6 +106,23 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
return regmap_write(chip->map, chip->base + addr, data); return regmap_write(chip->map, chip->base + addr, data);
} }
/**
* qpnp_tm_decode_temp() - return temperature in mC corresponding to the
* specified over-temperature stage
* @chip: Pointer to the qpnp_tm chip
* @stage: Over-temperature stage
*
* Return: temperature in mC
*/
static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage)
{
if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0 ||
stage > STAGE_COUNT)
return 0;
return (*chip->temp_map)[chip->thresh][stage - 1];
}
/** /**
* qpnp_tm_get_temp_stage() - return over-temperature stage * qpnp_tm_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip * @chip: Pointer to the qpnp_tm chip
...@@ -149,14 +172,12 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) ...@@ -149,14 +172,12 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
if (stage_new > stage_old) { if (stage_new > stage_old) {
/* increasing stage, use lower bound */ /* increasing stage, use lower bound */
chip->temp = (stage_new - 1) * TEMP_STAGE_STEP + chip->temp = qpnp_tm_decode_temp(chip, stage_new)
chip->thresh * TEMP_THRESH_STEP + + TEMP_STAGE_HYSTERESIS;
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
} else if (stage_new < stage_old) { } else if (stage_new < stage_old) {
/* decreasing stage, use upper bound */ /* decreasing stage, use upper bound */
chip->temp = stage_new * TEMP_STAGE_STEP + chip->temp = qpnp_tm_decode_temp(chip, stage_new + 1)
chip->thresh * TEMP_THRESH_STEP - - TEMP_STAGE_HYSTERESIS;
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
} }
chip->stage = stage; chip->stage = stage;
...@@ -199,26 +220,28 @@ static int qpnp_tm_get_temp(void *data, int *temp) ...@@ -199,26 +220,28 @@ static int qpnp_tm_get_temp(void *data, int *temp)
static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
int temp) int temp)
{ {
u8 reg; long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1];
long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1];
bool disable_s2_shutdown = false; bool disable_s2_shutdown = false;
u8 reg;
WARN_ON(!mutex_is_locked(&chip->lock)); WARN_ON(!mutex_is_locked(&chip->lock));
/* /*
* Default: S2 and S3 shutdown enabled, thresholds at * Default: S2 and S3 shutdown enabled, thresholds at
* 105C/125C/145C, monitoring at 25Hz * lowest threshold set, monitoring at 25Hz
*/ */
reg = SHUTDOWN_CTRL1_RATE_25HZ; reg = SHUTDOWN_CTRL1_RATE_25HZ;
if (temp == THERMAL_TEMP_INVALID || if (temp == THERMAL_TEMP_INVALID ||
temp < STAGE2_THRESHOLD_MIN) { temp < stage2_threshold_min) {
chip->thresh = THRESH_MIN; chip->thresh = THRESH_MIN;
goto skip; goto skip;
} }
if (temp <= STAGE2_THRESHOLD_MAX) { if (temp <= stage2_threshold_max) {
chip->thresh = THRESH_MAX - chip->thresh = THRESH_MAX -
((STAGE2_THRESHOLD_MAX - temp) / ((stage2_threshold_max - temp) /
TEMP_THRESH_STEP); TEMP_THRESH_STEP);
disable_s2_shutdown = true; disable_s2_shutdown = true;
} else { } else {
...@@ -326,9 +349,7 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) ...@@ -326,9 +349,7 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
? chip->stage : alarm_state_map[chip->stage]; ? chip->stage : alarm_state_map[chip->stage];
if (stage) if (stage)
chip->temp = chip->thresh * TEMP_THRESH_STEP + chip->temp = qpnp_tm_decode_temp(chip, stage);
(stage - 1) * TEMP_STAGE_STEP +
TEMP_THRESH_MIN;
crit_temp = qpnp_tm_get_critical_trip_temp(chip); crit_temp = qpnp_tm_get_critical_trip_temp(chip);
ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp); ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
...@@ -350,7 +371,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) ...@@ -350,7 +371,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
{ {
struct qpnp_tm_chip *chip; struct qpnp_tm_chip *chip;
struct device_node *node; struct device_node *node;
u8 type, subtype; u8 type, subtype, dig_major;
u32 res; u32 res;
int ret, irq; int ret, irq;
...@@ -400,6 +421,12 @@ static int qpnp_tm_probe(struct platform_device *pdev) ...@@ -400,6 +421,12 @@ static int qpnp_tm_probe(struct platform_device *pdev)
return ret; return ret;
} }
ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MAJOR, &dig_major);
if (ret < 0) {
dev_err(&pdev->dev, "could not read dig_major\n");
return ret;
}
if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1 if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
&& subtype != QPNP_TM_SUBTYPE_GEN2)) { && subtype != QPNP_TM_SUBTYPE_GEN2)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
...@@ -408,6 +435,10 @@ static int qpnp_tm_probe(struct platform_device *pdev) ...@@ -408,6 +435,10 @@ static int qpnp_tm_probe(struct platform_device *pdev)
} }
chip->subtype = subtype; chip->subtype = subtype;
if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1)
chip->temp_map = &temp_map_gen2_v1;
else
chip->temp_map = &temp_map_gen1;
/* /*
* Register the sensor before initializing the hardware to be able to * Register the sensor before initializing the hardware to be able to
......
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#include <linux/thermal.h> #include <linux/thermal.h>
#include "tsens.h" #include "tsens.h"
#define CAL_MDEGC 30000
#define CONFIG_ADDR 0x3640 #define CONFIG_ADDR 0x3640
#define CONFIG_ADDR_8660 0x3620 #define CONFIG_ADDR_8660 0x3620
/* CONFIG_ADDR bitmasks */ /* CONFIG_ADDR bitmasks */
...@@ -21,40 +19,38 @@ ...@@ -21,40 +19,38 @@
#define CONFIG_SHIFT_8660 28 #define CONFIG_SHIFT_8660 28
#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) #define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660)
#define STATUS_CNTL_ADDR_8064 0x3660
#define CNTL_ADDR 0x3620 #define CNTL_ADDR 0x3620
/* CNTL_ADDR bitmasks */ /* CNTL_ADDR bitmasks */
#define EN BIT(0) #define EN BIT(0)
#define SW_RST BIT(1) #define SW_RST BIT(1)
#define SENSOR0_EN BIT(3)
#define MEASURE_PERIOD BIT(18)
#define SLP_CLK_ENA BIT(26) #define SLP_CLK_ENA BIT(26)
#define SLP_CLK_ENA_8660 BIT(24) #define SLP_CLK_ENA_8660 BIT(24)
#define MEASURE_PERIOD 1
#define SENSOR0_SHIFT 3 #define SENSOR0_SHIFT 3
/* INT_STATUS_ADDR bitmasks */
#define MIN_STATUS_MASK BIT(0)
#define LOWER_STATUS_CLR BIT(1)
#define UPPER_STATUS_CLR BIT(2)
#define MAX_STATUS_MASK BIT(3)
#define THRESHOLD_ADDR 0x3624 #define THRESHOLD_ADDR 0x3624
/* THRESHOLD_ADDR bitmasks */
#define THRESHOLD_MAX_LIMIT_SHIFT 24
#define THRESHOLD_MIN_LIMIT_SHIFT 16
#define THRESHOLD_UPPER_LIMIT_SHIFT 8
#define THRESHOLD_LOWER_LIMIT_SHIFT 0
/* Initial temperature threshold values */
#define LOWER_LIMIT_TH 0x50
#define UPPER_LIMIT_TH 0xdf
#define MIN_LIMIT_TH 0x0
#define MAX_LIMIT_TH 0xff
#define S0_STATUS_ADDR 0x3628
#define INT_STATUS_ADDR 0x363c #define INT_STATUS_ADDR 0x363c
#define TRDY_MASK BIT(7)
#define TIMEOUT_US 100 #define S0_STATUS_OFF 0x3628
#define S1_STATUS_OFF 0x362c
#define S2_STATUS_OFF 0x3630
#define S3_STATUS_OFF 0x3634
#define S4_STATUS_OFF 0x3638
#define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */
#define S6_STATUS_OFF 0x3668
#define S7_STATUS_OFF 0x366c
#define S8_STATUS_OFF 0x3670
#define S9_STATUS_OFF 0x3674
#define S10_STATUS_OFF 0x3678
/* Original slope - 350 to compensate mC to C inaccuracy */
static u32 tsens_msm8960_slope[] = {
826, 826, 804, 826,
761, 782, 782, 849,
782, 849, 782
};
static int suspend_8960(struct tsens_priv *priv) static int suspend_8960(struct tsens_priv *priv)
{ {
...@@ -115,17 +111,34 @@ static int resume_8960(struct tsens_priv *priv) ...@@ -115,17 +111,34 @@ static int resume_8960(struct tsens_priv *priv)
static int enable_8960(struct tsens_priv *priv, int id) static int enable_8960(struct tsens_priv *priv, int id)
{ {
int ret; int ret;
u32 reg, mask; u32 reg, mask = BIT(id);
ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg); ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg);
if (ret) if (ret)
return ret; return ret;
mask = BIT(id + SENSOR0_SHIFT); /* HARDWARE BUG:
* On platforms with more than 6 sensors, all remaining sensors
* must be enabled together, otherwise undefined results are expected.
* (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver,
* all the sensors are enabled in one step hence this bug is not
* triggered.
*/
if (id > 5)
mask = GENMASK(10, 6);
mask <<= SENSOR0_SHIFT;
/* Sensors already enabled. Skip. */
if ((reg & mask) == mask)
return 0;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
if (ret) if (ret)
return ret; return ret;
reg |= MEASURE_PERIOD;
if (priv->num_sensors > 1) if (priv->num_sensors > 1)
reg |= mask | SLP_CLK_ENA | EN; reg |= mask | SLP_CLK_ENA | EN;
else else
...@@ -162,63 +175,11 @@ static void disable_8960(struct tsens_priv *priv) ...@@ -162,63 +175,11 @@ static void disable_8960(struct tsens_priv *priv)
regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
} }
static int init_8960(struct tsens_priv *priv)
{
int ret, i;
u32 reg_cntl;
priv->tm_map = dev_get_regmap(priv->dev, NULL);
if (!priv->tm_map)
return -ENODEV;
/*
* The status registers for each sensor are discontiguous
* because some SoCs have 5 sensors while others have more
* but the control registers stay in the same place, i.e
* directly after the first 5 status registers.
*/
for (i = 0; i < priv->num_sensors; i++) {
if (i >= 5)
priv->sensor[i].status = S0_STATUS_ADDR + 40;
priv->sensor[i].status += i * 4;
}
reg_cntl = SW_RST;
ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
if (ret)
return ret;
if (priv->num_sensors > 1) {
reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
reg_cntl &= ~SW_RST;
ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR,
CONFIG_MASK, CONFIG);
} else {
reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
reg_cntl &= ~CONFIG_MASK_8660;
reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
}
reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
reg_cntl |= EN;
ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
return 0;
}
static int calibrate_8960(struct tsens_priv *priv) static int calibrate_8960(struct tsens_priv *priv)
{ {
int i; int i;
char *data; char *data;
u32 p1[11];
ssize_t num_read = priv->num_sensors;
struct tsens_sensor *s = priv->sensor;
data = qfprom_read(priv->dev, "calib"); data = qfprom_read(priv->dev, "calib");
if (IS_ERR(data)) if (IS_ERR(data))
...@@ -226,60 +187,96 @@ static int calibrate_8960(struct tsens_priv *priv) ...@@ -226,60 +187,96 @@ static int calibrate_8960(struct tsens_priv *priv)
if (IS_ERR(data)) if (IS_ERR(data))
return PTR_ERR(data); return PTR_ERR(data);
for (i = 0; i < num_read; i++, s++) for (i = 0; i < priv->num_sensors; i++) {
s->offset = data[i]; p1[i] = data[i];
priv->sensor[i].slope = tsens_msm8960_slope[i];
}
compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB);
kfree(data); kfree(data);
return 0; return 0;
} }
/* Temperature on y axis and ADC-code on x-axis */ static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) /* ----- SROT ------ */
{ /* No VERSION information */
int slope, offset;
/* CNTL */
slope = thermal_zone_get_slope(s->tzd); [TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0),
offset = CAL_MDEGC - slope * s->offset; [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1),
/* 8960 has 5 sensors, 8660 has 11, we only handle 5 */
return adc_code * slope + offset; [SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7),
}
/* ----- TM ------ */
static int get_temp_8960(const struct tsens_sensor *s, int *temp) /* INTERRUPT ENABLE */
{ /* NO INTERRUPT ENABLE */
int ret;
u32 code, trdy; /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */
struct tsens_priv *priv = s->priv; [LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7),
unsigned long timeout; [UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15),
/* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield
timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp
do { * MIN_THRESH_0 -> CRIT_THRESH_1
ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); * MAX_THRESH_0 -> CRIT_THRESH_0
if (ret) */
return ret; [CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23),
if (!(trdy & TRDY_MASK)) [CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31),
continue;
ret = regmap_read(priv->tm_map, s->status, &code); /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */
if (ret) /* 1 == clear, 0 == normal operation */
return ret; [LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9),
*temp = code_to_mdegC(code, s); [UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10),
return 0;
} while (time_before(jiffies, timeout)); /* NO CRITICAL INTERRUPT SUPPORT on 8960 */
return -ETIMEDOUT; /* Sn_STATUS */
} [LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7),
[LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7),
[LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7),
[LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7),
[LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7),
[LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7),
[LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7),
[LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7),
[LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7),
[LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7),
[LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7),
/* No VALID field on 8960 */
/* TSENS_INT_STATUS bits: 1 == threshold violated */
[MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0),
[LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1),
[UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2),
/* No CRITICAL field on 8960 */
[MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3),
/* TRDY: 1=ready, 0=in progress */
[TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7),
};
static const struct tsens_ops ops_8960 = { static const struct tsens_ops ops_8960 = {
.init = init_8960, .init = init_common,
.calibrate = calibrate_8960, .calibrate = calibrate_8960,
.get_temp = get_temp_8960, .get_temp = get_temp_common,
.enable = enable_8960, .enable = enable_8960,
.disable = disable_8960, .disable = disable_8960,
.suspend = suspend_8960, .suspend = suspend_8960,
.resume = resume_8960, .resume = resume_8960,
}; };
static struct tsens_features tsens_8960_feat = {
.ver_major = VER_0,
.crit_int = 0,
.adc = 1,
.srot_split = 0,
.max_sensors = 11,
};
struct tsens_plat_data data_8960 = { struct tsens_plat_data data_8960 = {
.num_sensors = 11, .num_sensors = 11,
.ops = &ops_8960, .ops = &ops_8960,
.feat = &tsens_8960_feat,
.fields = tsens_8960_regfields,
}; };
...@@ -190,6 +190,39 @@ ...@@ -190,6 +190,39 @@
#define BIT_APPEND 0x3 #define BIT_APPEND 0x3
/* eeprom layout data for mdm9607 */
#define MDM9607_BASE0_MASK 0x000000ff
#define MDM9607_BASE1_MASK 0x000ff000
#define MDM9607_BASE0_SHIFT 0
#define MDM9607_BASE1_SHIFT 12
#define MDM9607_S0_P1_MASK 0x00003f00
#define MDM9607_S1_P1_MASK 0x03f00000
#define MDM9607_S2_P1_MASK 0x0000003f
#define MDM9607_S3_P1_MASK 0x0003f000
#define MDM9607_S4_P1_MASK 0x0000003f
#define MDM9607_S0_P2_MASK 0x000fc000
#define MDM9607_S1_P2_MASK 0xfc000000
#define MDM9607_S2_P2_MASK 0x00000fc0
#define MDM9607_S3_P2_MASK 0x00fc0000
#define MDM9607_S4_P2_MASK 0x00000fc0
#define MDM9607_S0_P1_SHIFT 8
#define MDM9607_S1_P1_SHIFT 20
#define MDM9607_S2_P1_SHIFT 0
#define MDM9607_S3_P1_SHIFT 12
#define MDM9607_S4_P1_SHIFT 0
#define MDM9607_S0_P2_SHIFT 14
#define MDM9607_S1_P2_SHIFT 26
#define MDM9607_S2_P2_SHIFT 6
#define MDM9607_S3_P2_SHIFT 18
#define MDM9607_S4_P2_SHIFT 6
#define MDM9607_CAL_SEL_MASK 0x00700000
#define MDM9607_CAL_SEL_SHIFT 20
static int calibrate_8916(struct tsens_priv *priv) static int calibrate_8916(struct tsens_priv *priv)
{ {
int base0 = 0, base1 = 0, i; int base0 = 0, base1 = 0, i;
...@@ -452,7 +485,56 @@ static int calibrate_8974(struct tsens_priv *priv) ...@@ -452,7 +485,56 @@ static int calibrate_8974(struct tsens_priv *priv)
return 0; return 0;
} }
/* v0.1: 8916, 8939, 8974 */ static int calibrate_9607(struct tsens_priv *priv)
{
int base, i;
u32 p1[5], p2[5];
int mode = 0;
u32 *qfprom_cdata;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
if (IS_ERR(qfprom_cdata))
return PTR_ERR(qfprom_cdata);
mode = (qfprom_cdata[2] & MDM9607_CAL_SEL_MASK) >> MDM9607_CAL_SEL_SHIFT;
dev_dbg(priv->dev, "calibration mode is %d\n", mode);
switch (mode) {
case TWO_PT_CALIB:
base = (qfprom_cdata[2] & MDM9607_BASE1_MASK) >> MDM9607_BASE1_SHIFT;
p2[0] = (qfprom_cdata[0] & MDM9607_S0_P2_MASK) >> MDM9607_S0_P2_SHIFT;
p2[1] = (qfprom_cdata[0] & MDM9607_S1_P2_MASK) >> MDM9607_S1_P2_SHIFT;
p2[2] = (qfprom_cdata[1] & MDM9607_S2_P2_MASK) >> MDM9607_S2_P2_SHIFT;
p2[3] = (qfprom_cdata[1] & MDM9607_S3_P2_MASK) >> MDM9607_S3_P2_SHIFT;
p2[4] = (qfprom_cdata[2] & MDM9607_S4_P2_MASK) >> MDM9607_S4_P2_SHIFT;
for (i = 0; i < priv->num_sensors; i++)
p2[i] = ((base + p2[i]) << 2);
fallthrough;
case ONE_PT_CALIB2:
base = (qfprom_cdata[0] & MDM9607_BASE0_MASK);
p1[0] = (qfprom_cdata[0] & MDM9607_S0_P1_MASK) >> MDM9607_S0_P1_SHIFT;
p1[1] = (qfprom_cdata[0] & MDM9607_S1_P1_MASK) >> MDM9607_S1_P1_SHIFT;
p1[2] = (qfprom_cdata[1] & MDM9607_S2_P1_MASK) >> MDM9607_S2_P1_SHIFT;
p1[3] = (qfprom_cdata[1] & MDM9607_S3_P1_MASK) >> MDM9607_S3_P1_SHIFT;
p1[4] = (qfprom_cdata[2] & MDM9607_S4_P1_MASK) >> MDM9607_S4_P1_SHIFT;
for (i = 0; i < priv->num_sensors; i++)
p1[i] = ((base + p1[i]) << 2);
break;
default:
for (i = 0; i < priv->num_sensors; i++) {
p1[i] = 500;
p2[i] = 780;
}
break;
}
compute_intercept_slope(priv, p1, p2, mode);
kfree(qfprom_cdata);
return 0;
}
/* v0.1: 8916, 8939, 8974, 9607 */
static struct tsens_features tsens_v0_1_feat = { static struct tsens_features tsens_v0_1_feat = {
.ver_major = VER_0_1, .ver_major = VER_0_1,
...@@ -540,3 +622,17 @@ struct tsens_plat_data data_8974 = { ...@@ -540,3 +622,17 @@ struct tsens_plat_data data_8974 = {
.feat = &tsens_v0_1_feat, .feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields, .fields = tsens_v0_1_regfields,
}; };
static const struct tsens_ops ops_9607 = {
.init = init_common,
.calibrate = calibrate_9607,
.get_temp = get_temp_common,
};
struct tsens_plat_data data_9607 = {
.num_sensors = 5,
.ops = &ops_9607,
.hw_ids = (unsigned int []){ 0, 1, 2, 3, 4 },
.feat = &tsens_v0_1_feat,
.fields = tsens_v0_1_regfields,
};
...@@ -380,11 +380,11 @@ static const struct tsens_ops ops_8976 = { ...@@ -380,11 +380,11 @@ static const struct tsens_ops ops_8976 = {
.get_temp = get_temp_tsens_valid, .get_temp = get_temp_tsens_valid,
}; };
/* Valid for both MSM8956 and MSM8976. Sensor ID 3 is unused. */ /* Valid for both MSM8956 and MSM8976. */
struct tsens_plat_data data_8976 = { struct tsens_plat_data data_8976 = {
.num_sensors = 11, .num_sensors = 11,
.ops = &ops_8976, .ops = &ops_8976,
.hw_ids = (unsigned int[]){0, 1, 2, 4, 5, 6, 7, 8, 9, 10}, .hw_ids = (unsigned int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
.feat = &tsens_v1_feat, .feat = &tsens_v1_feat,
.fields = tsens_v1_regfields, .fields = tsens_v1_regfields,
}; };
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -85,7 +86,8 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, ...@@ -85,7 +86,8 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
"%s: sensor%d - data_point1:%#x data_point2:%#x\n", "%s: sensor%d - data_point1:%#x data_point2:%#x\n",
__func__, i, p1[i], p2[i]); __func__, i, p1[i], p2[i]);
priv->sensor[i].slope = SLOPE_DEFAULT; if (!priv->sensor[i].slope)
priv->sensor[i].slope = SLOPE_DEFAULT;
if (mode == TWO_PT_CALIB) { if (mode == TWO_PT_CALIB) {
/* /*
* slope (m) = adc_code2 - adc_code1 (y2 - y1)/ * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
...@@ -515,6 +517,15 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) ...@@ -515,6 +517,15 @@ static irqreturn_t tsens_irq_thread(int irq, void *data)
dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", dev_dbg(priv->dev, "[%u] %s: no violation: %d\n",
hw_id, __func__, temp); hw_id, __func__, temp);
} }
if (tsens_version(priv) < VER_0_1) {
/* Constraint: There is only 1 interrupt control register for all
* 11 temperature sensor. So monitoring more than 1 sensor based
* on interrupts will yield inconsistent result. To overcome this
* issue we will monitor only sensor 0 which is the master sensor.
*/
break;
}
} }
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -530,6 +541,13 @@ static int tsens_set_trips(void *_sensor, int low, int high) ...@@ -530,6 +541,13 @@ static int tsens_set_trips(void *_sensor, int low, int high)
int high_val, low_val, cl_high, cl_low; int high_val, low_val, cl_high, cl_low;
u32 hw_id = s->hw_id; u32 hw_id = s->hw_id;
if (tsens_version(priv) < VER_0_1) {
/* Pre v0.1 IP had a single register for each type of interrupt
* and thresholds
*/
hw_id = 0;
}
dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
hw_id, __func__, low, high); hw_id, __func__, low, high);
...@@ -584,18 +602,21 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp) ...@@ -584,18 +602,21 @@ int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
u32 valid; u32 valid;
int ret; int ret;
ret = regmap_field_read(priv->rf[valid_idx], &valid); /* VER_0 doesn't have VALID bit */
if (ret) if (tsens_version(priv) >= VER_0_1) {
return ret;
while (!valid) {
/* Valid bit is 0 for 6 AHB clock cycles.
* At 19.2MHz, 1 AHB clock is ~60ns.
* We should enter this loop very, very rarely.
*/
ndelay(400);
ret = regmap_field_read(priv->rf[valid_idx], &valid); ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret) if (ret)
return ret; return ret;
while (!valid) {
/* Valid bit is 0 for 6 AHB clock cycles.
* At 19.2MHz, 1 AHB clock is ~60ns.
* We should enter this loop very, very rarely.
*/
ndelay(400);
ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
}
} }
/* Valid bit is set, OK to read the temperature */ /* Valid bit is set, OK to read the temperature */
...@@ -608,15 +629,29 @@ int get_temp_common(const struct tsens_sensor *s, int *temp) ...@@ -608,15 +629,29 @@ int get_temp_common(const struct tsens_sensor *s, int *temp)
{ {
struct tsens_priv *priv = s->priv; struct tsens_priv *priv = s->priv;
int hw_id = s->hw_id; int hw_id = s->hw_id;
int last_temp = 0, ret; int last_temp = 0, ret, trdy;
unsigned long timeout;
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp); timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
if (ret) do {
return ret; if (tsens_version(priv) == VER_0) {
ret = regmap_field_read(priv->rf[TRDY], &trdy);
if (ret)
return ret;
if (!trdy)
continue;
}
*temp = code_to_degc(last_temp, s) * 1000; ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
if (ret)
return ret;
return 0; *temp = code_to_degc(last_temp, s) * 1000;
return 0;
} while (time_before(jiffies, timeout));
return -ETIMEDOUT;
} }
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -738,25 +773,42 @@ int __init init_common(struct tsens_priv *priv) ...@@ -738,25 +773,42 @@ int __init init_common(struct tsens_priv *priv)
priv->tm_offset = 0x1000; priv->tm_offset = 0x1000;
} }
res = platform_get_resource(op, IORESOURCE_MEM, 0); if (tsens_version(priv) >= VER_0_1) {
tm_base = devm_ioremap_resource(dev, res); res = platform_get_resource(op, IORESOURCE_MEM, 0);
if (IS_ERR(tm_base)) { tm_base = devm_ioremap_resource(dev, res);
ret = PTR_ERR(tm_base); if (IS_ERR(tm_base)) {
goto err_put_device; ret = PTR_ERR(tm_base);
goto err_put_device;
}
priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
} else { /* VER_0 share the same gcc regs using a syscon */
struct device *parent = priv->dev->parent;
if (parent)
priv->tm_map = syscon_node_to_regmap(parent->of_node);
} }
priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); if (IS_ERR_OR_NULL(priv->tm_map)) {
if (IS_ERR(priv->tm_map)) { if (!priv->tm_map)
ret = PTR_ERR(priv->tm_map); ret = -ENODEV;
else
ret = PTR_ERR(priv->tm_map);
goto err_put_device; goto err_put_device;
} }
/* VER_0 have only tm_map */
if (!priv->srot_map)
priv->srot_map = priv->tm_map;
if (tsens_version(priv) > VER_0_1) { if (tsens_version(priv) > VER_0_1) {
for (i = VER_MAJOR; i <= VER_STEP; i++) { for (i = VER_MAJOR; i <= VER_STEP; i++) {
priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map, priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
priv->fields[i]); priv->fields[i]);
if (IS_ERR(priv->rf[i])) if (IS_ERR(priv->rf[i])) {
return PTR_ERR(priv->rf[i]); ret = PTR_ERR(priv->rf[i]);
goto err_put_device;
}
} }
ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor); ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
if (ret) if (ret)
...@@ -769,6 +821,10 @@ int __init init_common(struct tsens_priv *priv) ...@@ -769,6 +821,10 @@ int __init init_common(struct tsens_priv *priv)
ret = PTR_ERR(priv->rf[TSENS_EN]); ret = PTR_ERR(priv->rf[TSENS_EN]);
goto err_put_device; goto err_put_device;
} }
/* in VER_0 TSENS need to be explicitly enabled */
if (tsens_version(priv) == VER_0)
regmap_field_write(priv->rf[TSENS_EN], 1);
ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
if (ret) if (ret)
goto err_put_device; goto err_put_device;
...@@ -791,6 +847,19 @@ int __init init_common(struct tsens_priv *priv) ...@@ -791,6 +847,19 @@ int __init init_common(struct tsens_priv *priv)
goto err_put_device; goto err_put_device;
} }
priv->rf[TSENS_SW_RST] =
devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]);
if (IS_ERR(priv->rf[TSENS_SW_RST])) {
ret = PTR_ERR(priv->rf[TSENS_SW_RST]);
goto err_put_device;
}
priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]);
if (IS_ERR(priv->rf[TRDY])) {
ret = PTR_ERR(priv->rf[TRDY]);
goto err_put_device;
}
/* This loop might need changes if enum regfield_ids is reordered */ /* This loop might need changes if enum regfield_ids is reordered */
for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
for (i = 0; i < priv->feat->max_sensors; i++) { for (i = 0; i < priv->feat->max_sensors; i++) {
...@@ -806,7 +875,7 @@ int __init init_common(struct tsens_priv *priv) ...@@ -806,7 +875,7 @@ int __init init_common(struct tsens_priv *priv)
} }
} }
if (priv->feat->crit_int) { if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) {
/* Loop might need changes if enum regfield_ids is reordered */ /* Loop might need changes if enum regfield_ids is reordered */
for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) { for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
for (i = 0; i < priv->feat->max_sensors; i++) { for (i = 0; i < priv->feat->max_sensors; i++) {
...@@ -844,7 +913,11 @@ int __init init_common(struct tsens_priv *priv) ...@@ -844,7 +913,11 @@ int __init init_common(struct tsens_priv *priv)
} }
spin_lock_init(&priv->ul_lock); spin_lock_init(&priv->ul_lock);
tsens_enable_irq(priv);
/* VER_0 interrupt doesn't need to be enabled */
if (tsens_version(priv) >= VER_0_1)
tsens_enable_irq(priv);
tsens_debug_init(op); tsens_debug_init(op);
err_put_device: err_put_device:
...@@ -895,6 +968,12 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); ...@@ -895,6 +968,12 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
static const struct of_device_id tsens_table[] = { static const struct of_device_id tsens_table[] = {
{ {
.compatible = "qcom,ipq8064-tsens",
.data = &data_8960,
}, {
.compatible = "qcom,mdm9607-tsens",
.data = &data_9607,
}, {
.compatible = "qcom,msm8916-tsens", .compatible = "qcom,msm8916-tsens",
.data = &data_8916, .data = &data_8916,
}, { }, {
...@@ -943,10 +1022,19 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, ...@@ -943,10 +1022,19 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
if (irq == -ENXIO) if (irq == -ENXIO)
ret = 0; ret = 0;
} else { } else {
ret = devm_request_threaded_irq(&pdev->dev, irq, /* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
NULL, thread_fn, if (tsens_version(priv) == VER_0)
IRQF_ONESHOT, ret = devm_request_threaded_irq(&pdev->dev, irq,
dev_name(&pdev->dev), priv); thread_fn, NULL,
IRQF_TRIGGER_RISING,
dev_name(&pdev->dev),
priv);
else
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
thread_fn, IRQF_ONESHOT,
dev_name(&pdev->dev),
priv);
if (ret) if (ret)
dev_err(&pdev->dev, "%s: failed to get irq\n", dev_err(&pdev->dev, "%s: failed to get irq\n",
__func__); __func__);
...@@ -975,6 +1063,19 @@ static int tsens_register(struct tsens_priv *priv) ...@@ -975,6 +1063,19 @@ static int tsens_register(struct tsens_priv *priv)
priv->ops->enable(priv, i); priv->ops->enable(priv, i);
} }
/* VER_0 require to set MIN and MAX THRESH
* These 2 regs are set using the:
* - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C
* - CRIT_THRESH_1 for MIN THRESH hardcoded to 0°C
*/
if (tsens_version(priv) < VER_0_1) {
regmap_field_write(priv->rf[CRIT_THRESH_0],
tsens_mC_to_hw(priv->sensor, 120000));
regmap_field_write(priv->rf[CRIT_THRESH_1],
tsens_mC_to_hw(priv->sensor, 0));
}
ret = tsens_register_irq(priv, "uplow", tsens_irq_thread); ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define CAL_DEGC_PT2 120 #define CAL_DEGC_PT2 120
#define SLOPE_FACTOR 1000 #define SLOPE_FACTOR 1000
#define SLOPE_DEFAULT 3200 #define SLOPE_DEFAULT 3200
#define TIMEOUT_US 100
#define THRESHOLD_MAX_ADC_CODE 0x3ff #define THRESHOLD_MAX_ADC_CODE 0x3ff
#define THRESHOLD_MIN_ADC_CODE 0x0 #define THRESHOLD_MIN_ADC_CODE 0x0
...@@ -25,7 +26,8 @@ struct tsens_priv; ...@@ -25,7 +26,8 @@ struct tsens_priv;
/* IP version numbers in ascending order */ /* IP version numbers in ascending order */
enum tsens_ver { enum tsens_ver {
VER_0_1 = 0, VER_0 = 0,
VER_0_1,
VER_1_X, VER_1_X,
VER_2_X, VER_2_X,
}; };
...@@ -585,7 +587,7 @@ int get_temp_common(const struct tsens_sensor *s, int *temp); ...@@ -585,7 +587,7 @@ int get_temp_common(const struct tsens_sensor *s, int *temp);
extern struct tsens_plat_data data_8960; extern struct tsens_plat_data data_8960;
/* TSENS v0.1 targets */ /* TSENS v0.1 targets */
extern struct tsens_plat_data data_8916, data_8939, data_8974; extern struct tsens_plat_data data_8916, data_8939, data_8974, data_9607;
/* TSENS v1 targets */ /* TSENS v1 targets */
extern struct tsens_plat_data data_tsens_v1, data_8976; extern struct tsens_plat_data data_tsens_v1, data_8976;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
#define MCELSIUS(temp) ((temp) * 1000) #define MCELSIUS(temp) ((temp) * 1000)
#define GEN3_FUSE_MASK 0xFFF #define GEN3_FUSE_MASK 0xFFF
#define TSC_MAX_NUM 4 #define TSC_MAX_NUM 5
/* default THCODE values if FUSEs are missing */ /* default THCODE values if FUSEs are missing */
static const int thcodes[TSC_MAX_NUM][3] = { static const int thcodes[TSC_MAX_NUM][3] = {
...@@ -68,6 +68,7 @@ static const int thcodes[TSC_MAX_NUM][3] = { ...@@ -68,6 +68,7 @@ static const int thcodes[TSC_MAX_NUM][3] = {
{ 3393, 2795, 2216 }, { 3393, 2795, 2216 },
{ 3389, 2805, 2237 }, { 3389, 2805, 2237 },
{ 3415, 2694, 2195 }, { 3415, 2694, 2195 },
{ 3356, 2724, 2244 },
}; };
/* Structure for thermal temperature calculation */ /* Structure for thermal temperature calculation */
......
...@@ -300,7 +300,7 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev) ...@@ -300,7 +300,7 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev)
* or 0x8xx, so they won't be away from the default value * or 0x8xx, so they won't be away from the default value
* for a lot. * for a lot.
* *
* So here we do not return error if the calibartion data is * So here we do not return error if the calibration data is
* not available, except the probe needs deferring. * not available, except the probe needs deferring.
*/ */
goto out; goto out;
...@@ -418,7 +418,7 @@ static int sun8i_h3_thermal_init(struct ths_device *tmdev) ...@@ -418,7 +418,7 @@ static int sun8i_h3_thermal_init(struct ths_device *tmdev)
} }
/* /*
* Without this undocummented value, the returned temperatures would * Without this undocumented value, the returned temperatures would
* be higher than real ones by about 20C. * be higher than real ones by about 20C.
*/ */
#define SUN50I_H6_CTRL0_UNK 0x0000002f #define SUN50I_H6_CTRL0_UNK 0x0000002f
......
...@@ -2118,7 +2118,6 @@ static int tegra_soctherm_probe(struct platform_device *pdev) ...@@ -2118,7 +2118,6 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
struct tegra_soctherm *tegra; struct tegra_soctherm *tegra;
struct thermal_zone_device *z; struct thermal_zone_device *z;
struct tsensor_shared_calib shared_calib; struct tsensor_shared_calib shared_calib;
struct resource *res;
struct tegra_soctherm_soc *soc; struct tegra_soctherm_soc *soc;
unsigned int i; unsigned int i;
int err; int err;
...@@ -2140,26 +2139,20 @@ static int tegra_soctherm_probe(struct platform_device *pdev) ...@@ -2140,26 +2139,20 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
tegra->soc = soc; tegra->soc = soc;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, tegra->regs = devm_platform_ioremap_resource_byname(pdev, "soctherm-reg");
"soctherm-reg");
tegra->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->regs)) { if (IS_ERR(tegra->regs)) {
dev_err(&pdev->dev, "can't get soctherm registers"); dev_err(&pdev->dev, "can't get soctherm registers");
return PTR_ERR(tegra->regs); return PTR_ERR(tegra->regs);
} }
if (!tegra->soc->use_ccroc) { if (!tegra->soc->use_ccroc) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, tegra->clk_regs = devm_platform_ioremap_resource_byname(pdev, "car-reg");
"car-reg");
tegra->clk_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->clk_regs)) { if (IS_ERR(tegra->clk_regs)) {
dev_err(&pdev->dev, "can't get car clk registers"); dev_err(&pdev->dev, "can't get car clk registers");
return PTR_ERR(tegra->clk_regs); return PTR_ERR(tegra->clk_regs);
} }
} else { } else {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, tegra->ccroc_regs = devm_platform_ioremap_resource_byname(pdev, "ccroc-reg");
"ccroc-reg");
tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->ccroc_regs)) { if (IS_ERR(tegra->ccroc_regs)) {
dev_err(&pdev->dev, "can't get ccroc registers"); dev_err(&pdev->dev, "can't get ccroc registers");
return PTR_ERR(tegra->ccroc_regs); return PTR_ERR(tegra->ccroc_regs);
...@@ -2195,7 +2188,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) ...@@ -2195,7 +2188,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
if (err) if (err)
return err; return err;
/* calculate tsensor calibaration data */ /* calculate tsensor calibration data */
for (i = 0; i < soc->num_tsensors; ++i) { for (i = 0; i < soc->num_tsensors; ++i) {
err = tegra_calc_tsensor_calib(&soc->tsensors[i], err = tegra_calc_tsensor_calib(&soc->tsensors[i],
&shared_calib, &shared_calib,
......
...@@ -561,24 +561,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, ...@@ -561,24 +561,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
} }
EXPORT_SYMBOL_GPL(thermal_zone_device_update); EXPORT_SYMBOL_GPL(thermal_zone_device_update);
/**
* thermal_notify_framework - Sensor drivers use this API to notify framework
* @tz: thermal zone device
* @trip: indicates which trip point has been crossed
*
* This function handles the trip events from sensor drivers. It starts
* throttling the cooling devices according to the policy configured.
* For CRITICAL and HOT trip points, this notifies the respective drivers,
* and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
* The throttling policy is based on the configured platform data; if no
* platform data is provided, this uses the step_wise throttling policy.
*/
void thermal_notify_framework(struct thermal_zone_device *tz, int trip)
{
handle_thermal_trip(tz, trip);
}
EXPORT_SYMBOL_GPL(thermal_notify_framework);
static void thermal_zone_device_check(struct work_struct *work) static void thermal_zone_device_check(struct work_struct *work)
{ {
struct thermal_zone_device *tz = container_of(work, struct struct thermal_zone_device *tz = container_of(work, struct
...@@ -960,10 +942,7 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -960,10 +942,7 @@ __thermal_cooling_device_register(struct device_node *np,
{ {
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct thermal_zone_device *pos = NULL; struct thermal_zone_device *pos = NULL;
int result; int ret;
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
if (!ops || !ops->get_max_state || !ops->get_cur_state || if (!ops || !ops->get_max_state || !ops->get_cur_state ||
!ops->set_cur_state) !ops->set_cur_state)
...@@ -973,14 +952,17 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -973,14 +952,17 @@ __thermal_cooling_device_register(struct device_node *np,
if (!cdev) if (!cdev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
result = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL); ret = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL);
if (result < 0) { if (ret < 0)
kfree(cdev); goto out_kfree_cdev;
return ERR_PTR(result); cdev->id = ret;
cdev->type = kstrdup(type ? type : "", GFP_KERNEL);
if (!cdev->type) {
ret = -ENOMEM;
goto out_ida_remove;
} }
cdev->id = result;
strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
mutex_init(&cdev->lock); mutex_init(&cdev->lock);
INIT_LIST_HEAD(&cdev->thermal_instances); INIT_LIST_HEAD(&cdev->thermal_instances);
cdev->np = np; cdev->np = np;
...@@ -990,12 +972,9 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -990,12 +972,9 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->devdata = devdata; cdev->devdata = devdata;
thermal_cooling_device_setup_sysfs(cdev); thermal_cooling_device_setup_sysfs(cdev);
dev_set_name(&cdev->device, "cooling_device%d", cdev->id); dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
result = device_register(&cdev->device); ret = device_register(&cdev->device);
if (result) { if (ret)
ida_simple_remove(&thermal_cdev_ida, cdev->id); goto out_kfree_type;
put_device(&cdev->device);
return ERR_PTR(result);
}
/* Add 'this' new cdev to the global cdev list */ /* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock); mutex_lock(&thermal_list_lock);
...@@ -1013,6 +992,15 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -1013,6 +992,15 @@ __thermal_cooling_device_register(struct device_node *np,
mutex_unlock(&thermal_list_lock); mutex_unlock(&thermal_list_lock);
return cdev; return cdev;
out_kfree_type:
kfree(cdev->type);
put_device(&cdev->device);
out_ida_remove:
ida_simple_remove(&thermal_cdev_ida, cdev->id);
out_kfree_cdev:
kfree(cdev);
return ERR_PTR(ret);
} }
/** /**
...@@ -1171,6 +1159,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) ...@@ -1171,6 +1159,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
ida_simple_remove(&thermal_cdev_ida, cdev->id); ida_simple_remove(&thermal_cdev_ida, cdev->id);
device_del(&cdev->device); device_del(&cdev->device);
thermal_cooling_device_destroy_sysfs(cdev); thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
put_device(&cdev->device); put_device(&cdev->device);
} }
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
......
...@@ -66,6 +66,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) ...@@ -66,6 +66,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
} }
void thermal_cdev_update(struct thermal_cooling_device *); void thermal_cdev_update(struct thermal_cooling_device *);
void __thermal_cdev_update(struct thermal_cooling_device *cdev);
/** /**
* struct thermal_trip - representation of a point in temperature domain * struct thermal_trip - representation of a point in temperature domain
......
...@@ -192,18 +192,11 @@ static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, ...@@ -192,18 +192,11 @@ static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
thermal_cooling_device_stats_update(cdev, target); thermal_cooling_device_stats_update(cdev, target);
} }
void thermal_cdev_update(struct thermal_cooling_device *cdev) void __thermal_cdev_update(struct thermal_cooling_device *cdev)
{ {
struct thermal_instance *instance; struct thermal_instance *instance;
unsigned long target = 0; unsigned long target = 0;
mutex_lock(&cdev->lock);
/* cooling device is updated*/
if (cdev->updated) {
mutex_unlock(&cdev->lock);
return;
}
/* Make sure cdev enters the deepest cooling state */ /* Make sure cdev enters the deepest cooling state */
list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
dev_dbg(&cdev->device, "zone%d->target=%lu\n", dev_dbg(&cdev->device, "zone%d->target=%lu\n",
...@@ -216,11 +209,25 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) ...@@ -216,11 +209,25 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
thermal_cdev_set_cur_state(cdev, target); thermal_cdev_set_cur_state(cdev, target);
cdev->updated = true;
mutex_unlock(&cdev->lock);
trace_cdev_update(cdev, target); trace_cdev_update(cdev, target);
dev_dbg(&cdev->device, "set to state %lu\n", target); dev_dbg(&cdev->device, "set to state %lu\n", target);
} }
/**
* thermal_cdev_update - update cooling device state if needed
* @cdev: pointer to struct thermal_cooling_device
*
* Update the cooling device state if there is a need.
*/
void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
mutex_lock(&cdev->lock);
if (!cdev->updated) {
__thermal_cdev_update(cdev);
cdev->updated = true;
}
mutex_unlock(&cdev->lock);
}
EXPORT_SYMBOL(thermal_cdev_update); EXPORT_SYMBOL(thermal_cdev_update);
/** /**
......
...@@ -54,11 +54,8 @@ static int thermal_mmio_probe(struct platform_device *pdev) ...@@ -54,11 +54,8 @@ static int thermal_mmio_probe(struct platform_device *pdev)
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource); sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource);
if (IS_ERR(sensor->mmio_base)) { if (IS_ERR(sensor->mmio_base))
dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n",
PTR_ERR(sensor->mmio_base));
return PTR_ERR(sensor->mmio_base); return PTR_ERR(sensor->mmio_base);
}
sensor_init_func = device_get_match_data(&pdev->dev); sensor_init_func = device_get_match_data(&pdev->dev);
if (sensor_init_func) { if (sensor_init_func) {
......
...@@ -704,14 +704,17 @@ static int thermal_of_populate_bind_params(struct device_node *np, ...@@ -704,14 +704,17 @@ static int thermal_of_populate_bind_params(struct device_node *np,
count = of_count_phandle_with_args(np, "cooling-device", count = of_count_phandle_with_args(np, "cooling-device",
"#cooling-cells"); "#cooling-cells");
if (!count) { if (count <= 0) {
pr_err("Add a cooling_device property with at least one device\n"); pr_err("Add a cooling_device property with at least one device\n");
ret = -ENOENT;
goto end; goto end;
} }
__tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL); __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
if (!__tcbp) if (!__tcbp) {
ret = -ENOMEM;
goto end; goto end;
}
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
ret = of_parse_phandle_with_args(np, "cooling-device", ret = of_parse_phandle_with_args(np, "cooling-device",
......
...@@ -9,30 +9,29 @@ ...@@ -9,30 +9,29 @@
* Eduardo Valentin <eduardo.valentin@ti.com> * Eduardo Valentin <eduardo.valentin@ti.com>
*/ */
#include <linux/module.h> #include <linux/clk.h>
#include <linux/cpu_pm.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/sys_soc.h>
#include <linux/reboot.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/cpu_pm.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/sys_soc.h>
#include <linux/types.h>
#include "ti-bandgap.h" #include "ti-bandgap.h"
...@@ -1143,14 +1142,10 @@ static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp) ...@@ -1143,14 +1142,10 @@ static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp)
for (i = 0; i < bgp->conf->sensor_count; i++) { for (i = 0; i < bgp->conf->sensor_count; i++) {
struct temp_sensor_registers *tsr; struct temp_sensor_registers *tsr;
struct temp_sensor_regval *rval; struct temp_sensor_regval *rval;
u32 val = 0;
rval = &bgp->regval[i]; rval = &bgp->regval[i];
tsr = bgp->conf->sensors[i].registers; tsr = bgp->conf->sensors[i].registers;
if (TI_BANDGAP_HAS(bgp, COUNTER))
val = ti_bandgap_readl(bgp, tsr->bgap_counter);
if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG))
ti_bandgap_writel(bgp, rval->tshut_threshold, ti_bandgap_writel(bgp, rval->tshut_threshold,
tsr->tshut_threshold); tsr->tshut_threshold);
......
...@@ -91,7 +91,7 @@ struct thermal_cooling_device_ops { ...@@ -91,7 +91,7 @@ struct thermal_cooling_device_ops {
struct thermal_cooling_device { struct thermal_cooling_device {
int id; int id;
char type[THERMAL_NAME_LENGTH]; char *type;
struct device device; struct device device;
struct device_node *np; struct device_node *np;
void *devdata; void *devdata;
...@@ -390,7 +390,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); ...@@ -390,7 +390,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
int thermal_zone_get_slope(struct thermal_zone_device *tz); int thermal_zone_get_slope(struct thermal_zone_device *tz);
int thermal_zone_get_offset(struct thermal_zone_device *tz); int thermal_zone_get_offset(struct thermal_zone_device *tz);
void thermal_notify_framework(struct thermal_zone_device *, int);
int thermal_zone_device_enable(struct thermal_zone_device *tz); int thermal_zone_device_enable(struct thermal_zone_device *tz);
int thermal_zone_device_disable(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); void thermal_zone_device_critical(struct thermal_zone_device *tz);
...@@ -436,10 +435,6 @@ static inline int thermal_zone_get_offset( ...@@ -436,10 +435,6 @@ static inline int thermal_zone_get_offset(
struct thermal_zone_device *tz) struct thermal_zone_device *tz)
{ return -ENODEV; } { return -ENODEV; }
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
int trip)
{ }
static inline int thermal_zone_device_enable(struct thermal_zone_device *tz) static inline int thermal_zone_device_enable(struct thermal_zone_device *tz)
{ return -ENODEV; } { return -ENODEV; }
......
...@@ -60,7 +60,7 @@ enum thermal_genl_event { ...@@ -60,7 +60,7 @@ enum thermal_genl_event {
THERMAL_GENL_EVENT_UNSPEC, THERMAL_GENL_EVENT_UNSPEC,
THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone creation */ THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone creation */
THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone deletion */ THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone deletion */
THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone disabed */ THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone disabled */
THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone enabled */ THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone enabled */
THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point crossed the way up */ THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point crossed the way up */
THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point crossed the way down */ THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point crossed the way down */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment