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:
Linus Torvalds 2020-12-15 16:21:37 -08:00
commit b109bc7229
31 changed files with 1290 additions and 811 deletions

View File

@ -14,18 +14,19 @@ Required properties:
- "mediatek,mt2712-thermal" : For MT2712 family of SoCs
- "mediatek,mt7622-thermal" : For MT7622 SoC
- "mediatek,mt8183-thermal" : For MT8183 family of SoCs
- "mediatek,mt8516-thermal", "mediatek,mt2701-thermal : For MT8516 family of SoCs
- reg: Address range of the thermal controller
- interrupts: IRQ for the thermal controller
- clocks, clock-names: Clocks needed for the thermal controller. required
clocks are:
"therm": Main clock needed for register access
"auxadc": The AUXADC clock
- resets: Reference to the reset controller controlling the thermal controller.
- mediatek,auxadc: A phandle to the AUXADC which the thermal controller uses
- mediatek,apmixedsys: A phandle to the APMIXEDSYS controller.
- #thermal-sensor-cells : Should be 0. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
Optional properties:
- resets: Reference to the reset controller controlling the thermal controller.
- nvmem-cells: A phandle to the calibration data provided by a nvmem device. If
unspecified default values shall be used.
- nvmem-cell-names: Should be "calibration-data"

View File

@ -26,13 +26,16 @@ properties:
- renesas,r8a77961-thermal # R-Car M3-W+
- renesas,r8a77965-thermal # R-Car M3-N
- renesas,r8a77980-thermal # R-Car V3H
- renesas,r8a779a0-thermal # R-Car V3U
reg:
minItems: 2
maxItems: 3
maxItems: 4
items:
- description: TSC1 registers
- description: TSC2 registers
- description: TSC3 registers
- description: TSC4 registers
interrupts:
items:
@ -55,12 +58,22 @@ properties:
required:
- compatible
- reg
- interrupts
- clocks
- power-domains
- resets
- "#thermal-sensor-cells"
if:
not:
properties:
compatible:
contains:
enum:
- renesas,r8a779a0-thermal
then:
required:
- interrupts
additionalProperties: false
examples:

View File

@ -62,25 +62,35 @@ properties:
"#thermal-sensor-cells":
const: 0
if:
properties:
compatible:
contains:
enum:
- renesas,thermal-r8a73a4 # R-Mobile APE6
- renesas,thermal-r8a7779 # R-Car H1
then:
required:
- compatible
- reg
else:
required:
- compatible
- reg
- interrupts
- clocks
- power-domains
- resets
required:
- compatible
- reg
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- renesas,thermal-r8a73a4 # R-Mobile APE6
- renesas,thermal-r8a7779 # R-Car H1
then:
required:
- resets
- '#thermal-sensor-cells'
- if:
not:
properties:
compatible:
contains:
const: renesas,thermal-r8a7779 # R-Car H1
then:
required:
- interrupts
- clocks
- power-domains
additionalProperties: false

View File

@ -654,8 +654,7 @@ stats/time_in_state_ms:
The amount of time spent by the cooling device in various cooling
states. The output will have "<state> <time>" pair in each line, which
will mean this cooling device spent <time> msec of time at <state>.
Output will have one line for each of the supported states. usertime
units here is 10mS (similar to other time exported in /proc).
Output will have one line for each of the supported states.
RO, Required

View File

@ -138,7 +138,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
}
pfdevfreq->devfreq = devfreq;
cooling = of_devfreq_cooling_register(dev->of_node, devfreq);
cooling = devfreq_cooling_em_register(devfreq, NULL);
if (IS_ERR(cooling))
DRM_DEV_INFO(dev, "Failed to register cooling device\n");
else

View File

@ -84,8 +84,6 @@ static struct platform_device *acerhdf_dev;
module_param(kernelmode, uint, 0);
MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
module_param(interval, uint, 0600);
MODULE_PARM_DESC(interval, "Polling interval of temperature check");
module_param(fanon, uint, 0600);
MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
module_param(fanoff, uint, 0600);
@ -336,7 +334,10 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
}
if (verbose)
pr_notice("interval changed to: %d\n", interval);
thermal->polling_delay = interval*1000;
if (thermal)
thermal->polling_delay = interval*1000;
prev_interval = interval;
}
}
@ -351,8 +352,6 @@ static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t)
{
int temp, err = 0;
acerhdf_check_param(thermal);
err = acerhdf_get_temp(&temp);
if (err)
return err;
@ -824,3 +823,24 @@ MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
module_init(acerhdf_init);
module_exit(acerhdf_exit);
static int interval_set_uint(const char *val, const struct kernel_param *kp)
{
int ret;
ret = param_set_uint(val, kp);
if (ret)
return ret;
acerhdf_check_param(thz_dev);
return 0;
}
static const struct kernel_param_ops interval_ops = {
.set = interval_set_uint,
.get = param_get_uint,
};
module_param_cb(interval, &interval_ops, &interval, 0600);
MODULE_PARM_DESC(interval, "Polling interval of temperature check");

View File

@ -29,6 +29,7 @@
#include <linux/thermal.h>
#include "thermal_core.h"
#include "thermal_hwmon.h"
#define TSENSOR_CFG_REG1 0x4
#define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
@ -287,6 +288,9 @@ static int amlogic_thermal_probe(struct platform_device *pdev)
return ret;
}
if (devm_thermal_add_hwmon_sysfs(pdata->tzd))
dev_warn(&pdev->dev, "Failed to add hwmon sysfs attributes\n");
ret = amlogic_thermal_initialize(pdata);
if (ret)
return ret;

View File

@ -102,11 +102,7 @@ static int bcm2711_thermal_probe(struct platform_device *pdev)
priv->thermal = thermal;
thermal->tzp->no_hwmon = false;
ret = thermal_add_hwmon_sysfs(thermal);
if (ret)
return ret;
return 0;
return thermal_add_hwmon_sysfs(thermal);
}
static struct platform_driver bcm2711_thermal_driver = {

View File

@ -438,13 +438,11 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
if (cpufreq_cdev->cpufreq_state == state)
return 0;
cpufreq_cdev->cpufreq_state = state;
frequency = get_state_freq(cpufreq_cdev, state);
ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
if (ret > 0) {
cpufreq_cdev->cpufreq_state = state;
cpus = cpufreq_cdev->policy->cpus;
max_capacity = arch_scale_cpu_capacity(cpumask_first(cpus));
capacity = frequency * max_capacity;

View File

@ -12,6 +12,7 @@
#include <linux/devfreq.h>
#include <linux/devfreq_cooling.h>
#include <linux/energy_model.h>
#include <linux/export.h>
#include <linux/idr.h>
#include <linux/slab.h>
@ -33,36 +34,34 @@ static DEFINE_IDA(devfreq_ida);
* @cdev: Pointer to associated thermal cooling device.
* @devfreq: Pointer to associated devfreq device.
* @cooling_state: Current cooling state.
* @power_table: Pointer to table with maximum power draw for each
* cooling state. State is the index into the table, and
* the power is in mW.
* @freq_table: Pointer to a table with the frequencies sorted in descending
* order. You can index the table by cooling device state
* @freq_table_size: Size of the @freq_table and @power_table
* @power_ops: Pointer to devfreq_cooling_power, used to generate the
* @power_table.
* @max_state: It is the last index, that is, one less than the number of the
* OPPs
* @power_ops: Pointer to devfreq_cooling_power, a more precised model.
* @res_util: Resource utilization scaling factor for the power.
* It is multiplied by 100 to minimize the error. It is used
* for estimation of the power budget instead of using
* 'utilization' (which is 'busy_time / 'total_time').
* The 'res_util' range is from 100 to (power_table[state] * 100)
* for the corresponding 'state'.
* 'utilization' (which is 'busy_time' / 'total_time').
* The 'res_util' range is from 100 to power * 100 for the
* corresponding 'state'.
* @capped_state: index to cooling state with in dynamic power budget
* @req_max_freq: PM QoS request for limiting the maximum frequency
* of the devfreq device.
* @em_pd: Energy Model for the associated Devfreq device
*/
struct devfreq_cooling_device {
int id;
struct thermal_cooling_device *cdev;
struct devfreq *devfreq;
unsigned long cooling_state;
u32 *power_table;
u32 *freq_table;
size_t freq_table_size;
size_t max_state;
struct devfreq_cooling_power *power_ops;
u32 res_util;
int capped_state;
struct dev_pm_qos_request req_max_freq;
struct em_perf_domain *em_pd;
};
static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
@ -70,7 +69,7 @@ static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev,
{
struct devfreq_cooling_device *dfc = cdev->devdata;
*state = dfc->freq_table_size - 1;
*state = dfc->max_state;
return 0;
}
@ -92,16 +91,22 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
struct devfreq *df = dfc->devfreq;
struct device *dev = df->dev.parent;
unsigned long freq;
int perf_idx;
if (state == dfc->cooling_state)
return 0;
dev_dbg(dev, "Setting cooling state %lu\n", state);
if (state >= dfc->freq_table_size)
if (state > dfc->max_state)
return -EINVAL;
freq = dfc->freq_table[state];
if (dfc->em_pd) {
perf_idx = dfc->max_state - state;
freq = dfc->em_pd->table[perf_idx].frequency * 1000;
} else {
freq = dfc->freq_table[state];
}
dev_pm_qos_update_request(&dfc->req_max_freq,
DIV_ROUND_UP(freq, HZ_PER_KHZ));
@ -112,24 +117,23 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
}
/**
* freq_get_state() - get the cooling state corresponding to a frequency
* @dfc: Pointer to devfreq cooling device
* @freq: frequency in Hz
* get_perf_idx() - get the performance index corresponding to a frequency
* @em_pd: Pointer to device's Energy Model
* @freq: frequency in kHz
*
* Return: the cooling state associated with the @freq, or
* THERMAL_CSTATE_INVALID if it wasn't found.
* Return: the performance index associated with the @freq, or
* -EINVAL if it wasn't found.
*/
static unsigned long
freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq)
static int get_perf_idx(struct em_perf_domain *em_pd, unsigned long freq)
{
int i;
for (i = 0; i < dfc->freq_table_size; i++) {
if (dfc->freq_table[i] == freq)
for (i = 0; i < em_pd->nr_perf_states; i++) {
if (em_pd->table[i].frequency == freq)
return i;
}
return THERMAL_CSTATE_INVALID;
return -EINVAL;
}
static unsigned long get_voltage(struct devfreq *df, unsigned long freq)
@ -160,94 +164,38 @@ static unsigned long get_voltage(struct devfreq *df, unsigned long freq)
return voltage;
}
/**
* get_static_power() - calculate the static power
* @dfc: Pointer to devfreq cooling device
* @freq: Frequency in Hz
*
* Calculate the static power in milliwatts using the supplied
* get_static_power(). The current voltage is calculated using the
* OPP library. If no get_static_power() was supplied, assume the
* static power is negligible.
*/
static unsigned long
get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq)
static void _normalize_load(struct devfreq_dev_status *status)
{
struct devfreq *df = dfc->devfreq;
unsigned long voltage;
if (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);

View File

@ -63,6 +63,8 @@ static inline s64 div_frac(s64 x, s64 y)
* @trip_max_desired_temperature: last passive trip point of the thermal
* zone. The temperature we are
* controlling for.
* @sustainable_power: Sustainable power (heat) that this thermal zone can
* dissipate
*/
struct power_allocator_params {
bool allocated_tzp;
@ -70,6 +72,7 @@ struct power_allocator_params {
s32 prev_err;
int trip_switch_on;
int trip_max_desired_temperature;
u32 sustainable_power;
};
/**
@ -96,7 +99,10 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
if (instance->trip != params->trip_max_desired_temperature)
continue;
if (power_actor_get_min_power(cdev, &min_power))
if (!cdev_is_power_actor(cdev))
continue;
if (cdev->ops->state2power(cdev, instance->upper, &min_power))
continue;
sustainable_power += min_power;
@ -111,26 +117,18 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
* @sustainable_power: sustainable power for the thermal zone
* @trip_switch_on: trip point number for the switch on temperature
* @control_temp: target temperature for the power allocator governor
* @force: whether to force the update of the constants
*
* This function is used to update the estimation of the PID
* controller constants in struct thermal_zone_parameters.
* Sustainable power is provided in case it was estimated. The
* estimated sustainable_power should not be stored in the
* thermal_zone_parameters so it has to be passed explicitly to this
* function.
*
* If @force is not set, the values in the thermal zone's parameters
* are preserved if they are not zero. If @force is set, the values
* in thermal zone's parameters are overwritten.
*/
static void estimate_pid_constants(struct thermal_zone_device *tz,
u32 sustainable_power, int trip_switch_on,
int control_temp, bool force)
int control_temp)
{
int ret;
int switch_on_temp;
u32 temperature_threshold;
s32 k_i;
ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
if (ret)
@ -148,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);

View File

@ -166,10 +166,11 @@ static int imx8mm_tmu_probe(struct platform_device *pdev)
&tmu->sensors[i],
&tmu_tz_ops);
if (IS_ERR(tmu->sensors[i].tzd)) {
ret = PTR_ERR(tmu->sensors[i].tzd);
dev_err(&pdev->dev,
"failed to register thermal zone sensor[%d]: %d\n",
i, ret);
return PTR_ERR(tmu->sensors[i].tzd);
goto disable_clk;
}
tmu->sensors[i].hw_id = i;
}
@ -184,6 +185,10 @@ static int imx8mm_tmu_probe(struct platform_device *pdev)
imx8mm_tmu_enable(tmu, true);
return 0;
disable_clk:
clk_disable_unprepare(tmu->clk);
return ret;
}
static int imx8mm_tmu_remove(struct platform_device *pdev)

View File

@ -10,6 +10,7 @@ config INT340X_THERMAL
select ACPI_THERMAL_REL
select ACPI_FAN
select INTEL_SOC_DTS_IOSF_CORE
select PROC_THERMAL_MMIO_RAPL if X86_64 && POWERCAP
help
Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core
@ -41,9 +42,6 @@ config INT3406_THERMAL
power consumed by display device.
config PROC_THERMAL_MMIO_RAPL
bool
depends on 64BIT
depends on POWERCAP
tristate
select INTEL_RAPL_CORE
default y
endif

View File

@ -4,5 +4,8 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

View File

@ -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);

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -7,14 +7,16 @@
* Tushar Dave <tushar.n.dave@intel.com>
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/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);

View File

@ -1052,7 +1052,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
return -EINVAL;
}
ret = device_reset(&pdev->dev);
ret = device_reset_optional(&pdev->dev);
if (ret)
return ret;

View File

@ -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 = {

View File

@ -323,24 +323,6 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone,
return 0;
}
static int rcar_thermal_notify(struct thermal_zone_device *zone,
int trip, enum thermal_trip_type type)
{
struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
struct device *dev = rcar_priv_to_dev(priv);
switch (type) {
case THERMAL_TRIP_CRITICAL:
/* FIXME */
dev_warn(dev, "Thermal reached to critical temperature\n");
break;
default:
break;
}
return 0;
}
static const struct thermal_zone_of_device_ops rcar_thermal_zone_of_ops = {
.get_temp = rcar_thermal_of_get_temp,
};
@ -349,7 +331,6 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
.get_temp = rcar_thermal_get_temp,
.get_trip_type = rcar_thermal_get_trip_type,
.get_trip_temp = rcar_thermal_get_trip_temp,
.notify = rcar_thermal_notify,
};
/*
@ -409,16 +390,15 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
{
struct rcar_thermal_common *common = data;
struct rcar_thermal_priv *priv;
unsigned long flags;
u32 status, mask;
spin_lock_irqsave(&common->lock, flags);
spin_lock(&common->lock);
mask = rcar_thermal_common_read(common, INTMSK);
status = rcar_thermal_common_read(common, STR);
rcar_thermal_common_write(common, STR, 0x000F0F0F & mask);
spin_unlock_irqrestore(&common->lock, flags);
spin_unlock(&common->lock);
status = status & ~mask;

View File

@ -8,6 +8,7 @@
* Based on the work of Josef Gajdusek <atx@atx.name>
*/
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@ -74,7 +75,7 @@ struct ths_thermal_chip {
int (*calibrate)(struct ths_device *tmdev,
u16 *caldata, int callen);
int (*init)(struct ths_device *tmdev);
int (*irq_ack)(struct ths_device *tmdev);
unsigned long (*irq_ack)(struct ths_device *tmdev);
int (*calc_temp)(struct ths_device *tmdev,
int id, int reg);
};
@ -146,9 +147,10 @@ static const struct regmap_config config = {
.max_register = 0xfc,
};
static int sun8i_h3_irq_ack(struct ths_device *tmdev)
static unsigned long sun8i_h3_irq_ack(struct ths_device *tmdev)
{
int i, state, ret = 0;
unsigned long irq_bitmap = 0;
int i, state;
regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
@ -156,16 +158,17 @@ static int sun8i_h3_irq_ack(struct ths_device *tmdev)
if (state & SUN8I_THS_DATA_IRQ_STS(i)) {
regmap_write(tmdev->regmap, SUN8I_THS_IS,
SUN8I_THS_DATA_IRQ_STS(i));
ret |= BIT(i);
bitmap_set(&irq_bitmap, i, 1);
}
}
return ret;
return irq_bitmap;
}
static int sun50i_h6_irq_ack(struct ths_device *tmdev)
static unsigned long sun50i_h6_irq_ack(struct ths_device *tmdev)
{
int i, state, ret = 0;
unsigned long irq_bitmap = 0;
int i, state;
regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
@ -173,24 +176,22 @@ static int sun50i_h6_irq_ack(struct ths_device *tmdev)
if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
SUN50I_H6_THS_DATA_IRQ_STS(i));
ret |= BIT(i);
bitmap_set(&irq_bitmap, i, 1);
}
}
return ret;
return irq_bitmap;
}
static irqreturn_t sun8i_irq_thread(int irq, void *data)
{
struct ths_device *tmdev = data;
int i, state;
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;

View File

@ -380,6 +380,25 @@ static void thermal_emergency_poweroff(void)
msecs_to_jiffies(poweroff_delay_ms));
}
void thermal_zone_device_critical(struct thermal_zone_device *tz)
{
dev_emerg(&tz->device, "%s: critical temperature reached, "
"shutting down\n", tz->type);
mutex_lock(&poweroff_lock);
if (!power_off_triggered) {
/*
* Queue a backup emergency shutdown in the event of
* orderly_poweroff failure
*/
thermal_emergency_poweroff();
orderly_poweroff(true);
power_off_triggered = true;
}
mutex_unlock(&poweroff_lock);
}
EXPORT_SYMBOL(thermal_zone_device_critical);
static void handle_critical_trips(struct thermal_zone_device *tz,
int trip, enum thermal_trip_type trip_type)
{
@ -396,22 +415,10 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
if (tz->ops->notify)
tz->ops->notify(tz, trip, trip_type);
if (trip_type == THERMAL_TRIP_CRITICAL) {
dev_emerg(&tz->device,
"critical temperature reached (%d C), shutting down\n",
tz->temperature / 1000);
mutex_lock(&poweroff_lock);
if (!power_off_triggered) {
/*
* Queue a backup emergency shutdown in the event of
* orderly_poweroff failure
*/
thermal_emergency_poweroff();
orderly_poweroff(true);
power_off_triggered = true;
}
mutex_unlock(&poweroff_lock);
}
if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot)
tz->ops->hot(tz);
else if (trip_type == THERMAL_TRIP_CRITICAL)
tz->ops->critical(tz);
}
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
@ -553,7 +560,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
if (atomic_read(&in_suspend))
return;
if (!tz->ops->get_temp)
if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without "
"'get_temp' ops set\n", __func__))
return;
update_temperature(tz);
@ -593,94 +601,6 @@ static void thermal_zone_device_check(struct work_struct *work)
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
/*
* Power actor section: interface to power actors to estimate power
*
* Set of functions used to interact to cooling devices that know
* how to estimate their devices power consumption.
*/
/**
* power_actor_get_max_power() - get the maximum power that a cdev can consume
* @cdev: pointer to &thermal_cooling_device
* @max_power: pointer in which to store the maximum power
*
* Calculate the maximum power consumption in milliwats that the
* cooling device can currently consume and store it in @max_power.
*
* Return: 0 on success, -EINVAL if @cdev doesn't support the
* power_actor API or -E* on other error.
*/
int power_actor_get_max_power(struct thermal_cooling_device *cdev,
u32 *max_power)
{
if (!cdev_is_power_actor(cdev))
return -EINVAL;
return cdev->ops->state2power(cdev, 0, max_power);
}
/**
* power_actor_get_min_power() - get the mainimum power that a cdev can consume
* @cdev: pointer to &thermal_cooling_device
* @min_power: pointer in which to store the minimum power
*
* Calculate the minimum power consumption in milliwatts that the
* cooling device can currently consume and store it in @min_power.
*
* Return: 0 on success, -EINVAL if @cdev doesn't support the
* power_actor API or -E* on other error.
*/
int power_actor_get_min_power(struct thermal_cooling_device *cdev,
u32 *min_power)
{
unsigned long max_state;
int ret;
if (!cdev_is_power_actor(cdev))
return -EINVAL;
ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
return cdev->ops->state2power(cdev, max_state, min_power);
}
/**
* power_actor_set_power() - limit the maximum power a cooling device consumes
* @cdev: pointer to &thermal_cooling_device
* @instance: thermal instance to update
* @power: the power in milliwatts
*
* Set the cooling device to consume at most @power milliwatts. The limit is
* expected to be a cap at the maximum power consumption.
*
* Return: 0 on success, -EINVAL if the cooling device does not
* implement the power actor API or -E* for other failures.
*/
int power_actor_set_power(struct thermal_cooling_device *cdev,
struct thermal_instance *instance, u32 power)
{
unsigned long state;
int ret;
if (!cdev_is_power_actor(cdev))
return -EINVAL;
ret = cdev->ops->power2state(cdev, power, &state);
if (ret)
return ret;
instance->target = state;
mutex_lock(&cdev->lock);
cdev->updated = false;
mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
return 0;
}
void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
const char *cdev_type, size_t size)
{
@ -1423,6 +1343,10 @@ thermal_zone_device_register(const char *type, int trips, int mask,
tz->id = id;
strlcpy(tz->type, type, sizeof(tz->type));
if (!ops->critical)
ops->critical = thermal_zone_device_critical;
tz->ops = ops;
tz->tzp = tzp;
tz->device.class = &thermal_class;
@ -1446,12 +1370,9 @@ thermal_zone_device_register(const char *type, int trips, int mask,
goto release_device;
for (count = 0; count < trips; count++) {
if (tz->ops->get_trip_type(tz, count, &trip_type))
set_bit(count, &tz->trips_disabled);
if (tz->ops->get_trip_temp(tz, count, &trip_temp))
set_bit(count, &tz->trips_disabled);
/* Check for bogus trip points */
if (trip_temp == 0)
if (tz->ops->get_trip_type(tz, count, &trip_type) ||
tz->ops->get_trip_temp(tz, count, &trip_temp) ||
!trip_temp)
set_bit(count, &tz->trips_disabled);
}

View File

@ -65,12 +65,6 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
cdev->ops->power2state;
}
int power_actor_get_max_power(struct thermal_cooling_device *cdev,
u32 *max_power);
int power_actor_get_min_power(struct thermal_cooling_device *cdev,
u32 *min_power);
int power_actor_set_power(struct thermal_cooling_device *cdev,
struct thermal_instance *ti, u32 power);
/**
* struct thermal_trip - representation of a point in temperature domain
* @np: pointer to struct device_node that this trip point was created from

View File

@ -206,8 +206,7 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
if (new_hwmon_device)
hwmon_device_unregister(hwmon->device);
free_mem:
if (new_hwmon_device)
kfree(hwmon);
kfree(hwmon);
return result;
}

View File

@ -425,7 +425,7 @@ static struct attribute *thermal_zone_dev_attrs[] = {
NULL,
};
static struct attribute_group thermal_zone_attribute_group = {
static const struct attribute_group thermal_zone_attribute_group = {
.attrs = thermal_zone_dev_attrs,
};
@ -434,7 +434,7 @@ static struct attribute *thermal_zone_mode_attrs[] = {
NULL,
};
static struct attribute_group thermal_zone_mode_attribute_group = {
static const struct attribute_group thermal_zone_mode_attribute_group = {
.attrs = thermal_zone_mode_attrs,
};
@ -468,7 +468,7 @@ static umode_t thermal_zone_passive_is_visible(struct kobject *kobj,
return 0;
}
static struct attribute_group thermal_zone_passive_attribute_group = {
static const struct attribute_group thermal_zone_passive_attribute_group = {
.attrs = thermal_zone_passive_attrs,
.is_visible = thermal_zone_passive_is_visible,
};

View File

@ -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)
{

View File

@ -79,6 +79,8 @@ struct thermal_zone_device_ops {
enum thermal_trend *);
int (*notify) (struct thermal_zone_device *, int,
enum thermal_trip_type);
void (*hot)(struct thermal_zone_device *);
void (*critical)(struct thermal_zone_device *);
};
struct thermal_cooling_device_ops {
@ -399,6 +401,7 @@ void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
int thermal_zone_device_enable(struct thermal_zone_device *tz);
int thermal_zone_device_disable(struct thermal_zone_device *tz);
void thermal_zone_device_critical(struct thermal_zone_device *tz);
#else
static inline struct thermal_zone_device *thermal_zone_device_register(
const char *type, int trips, int mask, void *devdata,

View File

@ -153,31 +153,30 @@ TRACE_EVENT(thermal_power_cpu_limit,
TRACE_EVENT(thermal_power_devfreq_get_power,
TP_PROTO(struct thermal_cooling_device *cdev,
struct devfreq_dev_status *status, unsigned long freq,
u32 dynamic_power, u32 static_power, u32 power),
u32 power),
TP_ARGS(cdev, status, freq, dynamic_power, static_power, power),
TP_ARGS(cdev, status, freq, power),
TP_STRUCT__entry(
__string(type, cdev->type )
__field(unsigned long, freq )
__field(u32, load )
__field(u32, dynamic_power )
__field(u32, static_power )
__field(u32, busy_time)
__field(u32, total_time)
__field(u32, power)
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->freq = freq;
__entry->load = (100 * status->busy_time) / status->total_time;
__entry->dynamic_power = dynamic_power;
__entry->static_power = static_power;
__entry->busy_time = status->busy_time;
__entry->total_time = status->total_time;
__entry->power = power;
),
TP_printk("type=%s freq=%lu load=%u dynamic_power=%u static_power=%u power=%u",
TP_printk("type=%s freq=%lu load=%u power=%u",
__get_str(type), __entry->freq,
__entry->load, __entry->dynamic_power, __entry->static_power,
__entry->total_time == 0 ? 0 :
(100 * __entry->busy_time) / __entry->total_time,
__entry->power)
);