Commit b109bc72 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull thermal updates from Daniel Lezcano:

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

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

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

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

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

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

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

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

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

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

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

 - Add PCI ids for Lewisburg PCH (Andres Freund)

 - Add hwmon support on amlogic (Martin Blumenstingl)

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

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

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

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

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

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

 - Add r8a779a0 support (Niklas Söderlund)

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

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

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

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

 - Fix workload name on PCH (Srinivas Pandruvada)

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

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

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

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

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

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