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