diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-16 03:21:37 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-12-16 03:21:37 +0300 |
commit | b109bc72295363fb746bc42bdd777f7a8abb177b (patch) | |
tree | d4ce43634e549ebb3ce37dbe87e46490f840267f /drivers/thermal | |
parent | ee249d30fadec7677364063648f5547e243bf93f (diff) | |
parent | 4401117bf7fc11dc738c0963fa0c94814abc8dcd (diff) | |
download | linux-b109bc72295363fb746bc42bdd777f7a8abb177b.tar.xz |
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
...
Diffstat (limited to 'drivers/thermal')
22 files changed, 1183 insertions, 742 deletions
diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index ccb1fe18e993..dffe3ba8c7c4 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -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; diff --git a/drivers/thermal/broadcom/bcm2711_thermal.c b/drivers/thermal/broadcom/bcm2711_thermal.c index 67c2a737bc9d..1ec57d9ecf53 100644 --- a/drivers/thermal/broadcom/bcm2711_thermal.c +++ b/drivers/thermal/broadcom/bcm2711_thermal.c @@ -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 = { diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_cooling.c index cc2959f22f01..612f063c1cfc 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -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; diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index dfab49a67252..fed3121ff2a1 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -12,6 +12,7 @@ #include <linux/devfreq.h> #include <linux/devfreq_cooling.h> +#include <linux/energy_model.h> #include <linux/export.h> #include <linux/idr.h> #include <linux/slab.h> @@ -33,36 +34,34 @@ static DEFINE_IDA(devfreq_ida); * @cdev: Pointer to associated thermal cooling device. * @devfreq: Pointer to associated devfreq device. * @cooling_state: Current cooling state. - * @power_table: Pointer to table with maximum power draw for each - * cooling state. State is the index into the table, and - * the power is in mW. * @freq_table: Pointer to a table with the frequencies sorted in descending * order. You can index the table by cooling device state - * @freq_table_size: Size of the @freq_table and @power_table - * @power_ops: Pointer to devfreq_cooling_power, used to generate the - * @power_table. + * @max_state: It is the last index, that is, one less than the number of the + * OPPs + * @power_ops: Pointer to devfreq_cooling_power, a more precised model. * @res_util: Resource utilization scaling factor for the power. * It is multiplied by 100 to minimize the error. It is used * for estimation of the power budget instead of using - * 'utilization' (which is 'busy_time / 'total_time'). - * The 'res_util' range is from 100 to (power_table[state] * 100) - * for the corresponding 'state'. + * 'utilization' (which is 'busy_time' / 'total_time'). + * The 'res_util' range is from 100 to power * 100 for the + * corresponding 'state'. * @capped_state: index to cooling state with in dynamic power budget * @req_max_freq: PM QoS request for limiting the maximum frequency * of the devfreq device. + * @em_pd: Energy Model for the associated Devfreq device */ struct devfreq_cooling_device { int id; struct thermal_cooling_device *cdev; struct devfreq *devfreq; unsigned long cooling_state; - u32 *power_table; u32 *freq_table; - size_t freq_table_size; + size_t max_state; struct devfreq_cooling_power *power_ops; u32 res_util; int capped_state; struct dev_pm_qos_request req_max_freq; + struct em_perf_domain *em_pd; }; static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev, @@ -70,7 +69,7 @@ static int devfreq_cooling_get_max_state(struct thermal_cooling_device *cdev, { struct devfreq_cooling_device *dfc = cdev->devdata; - *state = dfc->freq_table_size - 1; + *state = dfc->max_state; return 0; } @@ -92,16 +91,22 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev, struct devfreq *df = dfc->devfreq; struct device *dev = df->dev.parent; unsigned long freq; + int perf_idx; if (state == dfc->cooling_state) return 0; dev_dbg(dev, "Setting cooling state %lu\n", state); - if (state >= dfc->freq_table_size) + if (state > dfc->max_state) return -EINVAL; - freq = dfc->freq_table[state]; + if (dfc->em_pd) { + perf_idx = dfc->max_state - state; + freq = dfc->em_pd->table[perf_idx].frequency * 1000; + } else { + freq = dfc->freq_table[state]; + } dev_pm_qos_update_request(&dfc->req_max_freq, DIV_ROUND_UP(freq, HZ_PER_KHZ)); @@ -112,24 +117,23 @@ static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev, } /** - * freq_get_state() - get the cooling state corresponding to a frequency - * @dfc: Pointer to devfreq cooling device - * @freq: frequency in Hz + * get_perf_idx() - get the performance index corresponding to a frequency + * @em_pd: Pointer to device's Energy Model + * @freq: frequency in kHz * - * Return: the cooling state associated with the @freq, or - * THERMAL_CSTATE_INVALID if it wasn't found. + * Return: the performance index associated with the @freq, or + * -EINVAL if it wasn't found. */ -static unsigned long -freq_get_state(struct devfreq_cooling_device *dfc, unsigned long freq) +static int get_perf_idx(struct em_perf_domain *em_pd, unsigned long freq) { int i; - for (i = 0; i < dfc->freq_table_size; i++) { - if (dfc->freq_table[i] == freq) + for (i = 0; i < em_pd->nr_perf_states; i++) { + if (em_pd->table[i].frequency == freq) return i; } - return THERMAL_CSTATE_INVALID; + return -EINVAL; } static unsigned long get_voltage(struct devfreq *df, unsigned long freq) @@ -160,94 +164,38 @@ static unsigned long get_voltage(struct devfreq *df, unsigned long freq) return voltage; } -/** - * get_static_power() - calculate the static power - * @dfc: Pointer to devfreq cooling device - * @freq: Frequency in Hz - * - * Calculate the static power in milliwatts using the supplied - * get_static_power(). The current voltage is calculated using the - * OPP library. If no get_static_power() was supplied, assume the - * static power is negligible. - */ -static unsigned long -get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) +static void _normalize_load(struct devfreq_dev_status *status) { - struct devfreq *df = dfc->devfreq; - unsigned long voltage; - - if (!dfc->power_ops->get_static_power) - return 0; - - voltage = get_voltage(df, freq); - - if (voltage == 0) - return 0; - - return dfc->power_ops->get_static_power(df, voltage); -} - -/** - * get_dynamic_power - calculate the dynamic power - * @dfc: Pointer to devfreq cooling device - * @freq: Frequency in Hz - * @voltage: Voltage in millivolts - * - * Calculate the dynamic power in milliwatts consumed by the device at - * frequency @freq and voltage @voltage. If the get_dynamic_power() - * was supplied as part of the devfreq_cooling_power struct, then that - * function is used. Otherwise, a simple power model (Pdyn = Coeff * - * Voltage^2 * Frequency) is used. - */ -static unsigned long -get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq, - unsigned long voltage) -{ - u64 power; - u32 freq_mhz; - struct devfreq_cooling_power *dfc_power = dfc->power_ops; - - if (dfc_power->get_dynamic_power) - return dfc_power->get_dynamic_power(dfc->devfreq, freq, - voltage); - - freq_mhz = freq / 1000000; - power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage; - do_div(power, 1000000000); - - return power; -} + if (status->total_time > 0xfffff) { + status->total_time >>= 10; + status->busy_time >>= 10; + } + status->busy_time <<= 10; + status->busy_time /= status->total_time ? : 1; -static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc, - unsigned long freq, - unsigned long voltage) -{ - return get_static_power(dfc, freq) + get_dynamic_power(dfc, freq, - voltage); + status->busy_time = status->busy_time ? : 1; + status->total_time = 1024; } - static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, u32 *power) { struct devfreq_cooling_device *dfc = cdev->devdata; struct devfreq *df = dfc->devfreq; - struct devfreq_dev_status *status = &df->last_status; + struct devfreq_dev_status status; unsigned long state; - unsigned long freq = status->current_frequency; + unsigned long freq; unsigned long voltage; - u32 dyn_power = 0; - u32 static_power = 0; - int res; - - state = freq_get_state(dfc, freq); - if (state == THERMAL_CSTATE_INVALID) { - res = -EAGAIN; - goto fail; - } + int res, perf_idx; + + mutex_lock(&df->lock); + status = df->last_status; + mutex_unlock(&df->lock); - if (dfc->power_ops->get_real_power) { + freq = status.current_frequency; + + if (dfc->power_ops && dfc->power_ops->get_real_power) { voltage = get_voltage(df, freq); if (voltage == 0) { res = -EINVAL; @@ -257,7 +205,7 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd res = dfc->power_ops->get_real_power(df, power, freq, voltage); if (!res) { state = dfc->capped_state; - dfc->res_util = dfc->power_table[state]; + dfc->res_util = dfc->em_pd->table[state].power; dfc->res_util *= SCALE_ERROR_MITIGATION; if (*power > 1) @@ -266,19 +214,22 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd goto fail; } } else { - dyn_power = dfc->power_table[state]; + /* Energy Model frequencies are in kHz */ + perf_idx = get_perf_idx(dfc->em_pd, freq / 1000); + if (perf_idx < 0) { + res = -EAGAIN; + goto fail; + } - /* Scale dynamic power for utilization */ - dyn_power *= status->busy_time; - dyn_power /= status->total_time; - /* Get static power */ - static_power = get_static_power(dfc, freq); + _normalize_load(&status); - *power = dyn_power + static_power; + /* Scale power for utilization */ + *power = dfc->em_pd->table[perf_idx].power; + *power *= status.busy_time; + *power >>= 10; } - trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power, - static_power, *power); + trace_thermal_power_devfreq_get_power(cdev, &status, freq, *power); return 0; fail: @@ -288,20 +239,17 @@ fail: } static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, - unsigned long state, - u32 *power) + unsigned long state, u32 *power) { struct devfreq_cooling_device *dfc = cdev->devdata; - unsigned long freq; - u32 static_power; + int perf_idx; - if (state >= dfc->freq_table_size) + if (state > dfc->max_state) return -EINVAL; - freq = dfc->freq_table[state]; - static_power = get_static_power(dfc, freq); + perf_idx = dfc->max_state - state; + *power = dfc->em_pd->table[perf_idx].power; - *power = dfc->power_table[state] + static_power; return 0; } @@ -310,39 +258,39 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev, { struct devfreq_cooling_device *dfc = cdev->devdata; struct devfreq *df = dfc->devfreq; - struct devfreq_dev_status *status = &df->last_status; - unsigned long freq = status->current_frequency; - unsigned long busy_time; - s32 dyn_power; - u32 static_power; + struct devfreq_dev_status status; + unsigned long freq; s32 est_power; int i; - if (dfc->power_ops->get_real_power) { + mutex_lock(&df->lock); + status = df->last_status; + mutex_unlock(&df->lock); + + freq = status.current_frequency; + + if (dfc->power_ops && dfc->power_ops->get_real_power) { /* Scale for resource utilization */ est_power = power * dfc->res_util; est_power /= SCALE_ERROR_MITIGATION; } else { - static_power = get_static_power(dfc, freq); - - dyn_power = power - static_power; - dyn_power = dyn_power > 0 ? dyn_power : 0; - /* Scale dynamic power for utilization */ - busy_time = status->busy_time ?: 1; - est_power = (dyn_power * status->total_time) / busy_time; + _normalize_load(&status); + est_power = power << 10; + est_power /= status.busy_time; } /* * Find the first cooling state that is within the power - * budget for dynamic power. + * budget. The EM power table is sorted ascending. */ - for (i = 0; i < dfc->freq_table_size - 1; i++) - if (est_power >= dfc->power_table[i]) + for (i = dfc->max_state; i > 0; i--) + if (est_power >= dfc->em_pd->table[i].power) break; - *state = i; - dfc->capped_state = i; + *state = dfc->max_state - i; + dfc->capped_state = *state; + trace_thermal_power_devfreq_limit(cdev, freq, *state, power); return 0; } @@ -354,91 +302,43 @@ static struct thermal_cooling_device_ops devfreq_cooling_ops = { }; /** - * devfreq_cooling_gen_tables() - Generate power and freq tables. - * @dfc: Pointer to devfreq cooling device. - * - * Generate power and frequency tables: the power table hold the - * device's maximum power usage at each cooling state (OPP). The - * static and dynamic power using the appropriate voltage and - * frequency for the state, is acquired from the struct - * devfreq_cooling_power, and summed to make the maximum power draw. + * devfreq_cooling_gen_tables() - Generate frequency table. + * @dfc: Pointer to devfreq cooling device. + * @num_opps: Number of OPPs * - * The frequency table holds the frequencies in descending order. - * That way its indexed by cooling device state. - * - * The tables are malloced, and pointers put in dfc. They must be - * freed when unregistering the devfreq cooling device. + * Generate frequency table which holds the frequencies in descending + * order. That way its indexed by cooling device state. This is for + * compatibility with drivers which do not register Energy Model. * * Return: 0 on success, negative error code on failure. */ -static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) +static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc, + int num_opps) { struct devfreq *df = dfc->devfreq; struct device *dev = df->dev.parent; - int ret, num_opps; unsigned long freq; - u32 *power_table = NULL; - u32 *freq_table; int i; - num_opps = dev_pm_opp_get_opp_count(dev); - - if (dfc->power_ops) { - power_table = kcalloc(num_opps, sizeof(*power_table), - GFP_KERNEL); - if (!power_table) - return -ENOMEM; - } - - freq_table = kcalloc(num_opps, sizeof(*freq_table), + dfc->freq_table = kcalloc(num_opps, sizeof(*dfc->freq_table), GFP_KERNEL); - if (!freq_table) { - ret = -ENOMEM; - goto free_power_table; - } + if (!dfc->freq_table) + return -ENOMEM; for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { - unsigned long power, voltage; struct dev_pm_opp *opp; opp = dev_pm_opp_find_freq_floor(dev, &freq); if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto free_tables; + kfree(dfc->freq_table); + return PTR_ERR(opp); } - voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */ dev_pm_opp_put(opp); - - if (dfc->power_ops) { - if (dfc->power_ops->get_real_power) - power = get_total_power(dfc, freq, voltage); - else - power = get_dynamic_power(dfc, freq, voltage); - - dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n", - freq / 1000000, voltage, power, power); - - power_table[i] = power; - } - - freq_table[i] = freq; + dfc->freq_table[i] = freq; } - if (dfc->power_ops) - dfc->power_table = power_table; - - dfc->freq_table = freq_table; - dfc->freq_table_size = num_opps; - return 0; - -free_tables: - kfree(freq_table); -free_power_table: - kfree(power_table); - - return ret; } /** @@ -461,9 +361,10 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, struct devfreq_cooling_power *dfc_power) { struct thermal_cooling_device *cdev; + struct device *dev = df->dev.parent; struct devfreq_cooling_device *dfc; char dev_name[THERMAL_NAME_LENGTH]; - int err; + int err, num_opps; dfc = kzalloc(sizeof(*dfc), GFP_KERNEL); if (!dfc) @@ -471,28 +372,45 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, dfc->devfreq = df; - if (dfc_power) { - dfc->power_ops = dfc_power; - + dfc->em_pd = em_pd_get(dev); + if (dfc->em_pd) { devfreq_cooling_ops.get_requested_power = devfreq_cooling_get_requested_power; devfreq_cooling_ops.state2power = devfreq_cooling_state2power; devfreq_cooling_ops.power2state = devfreq_cooling_power2state; + + dfc->power_ops = dfc_power; + + num_opps = em_pd_nr_perf_states(dfc->em_pd); + } else { + /* Backward compatibility for drivers which do not use IPA */ + dev_dbg(dev, "missing EM for cooling device\n"); + + num_opps = dev_pm_opp_get_opp_count(dev); + + err = devfreq_cooling_gen_tables(dfc, num_opps); + if (err) + goto free_dfc; } - err = devfreq_cooling_gen_tables(dfc); - if (err) + if (num_opps <= 0) { + err = -EINVAL; goto free_dfc; + } + + /* max_state is an index, not a counter */ + dfc->max_state = num_opps - 1; - err = dev_pm_qos_add_request(df->dev.parent, &dfc->req_max_freq, + err = dev_pm_qos_add_request(dev, &dfc->req_max_freq, DEV_PM_QOS_MAX_FREQUENCY, PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); if (err < 0) - goto free_tables; + goto free_table; err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL); if (err < 0) goto remove_qos_req; + dfc->id = err; snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id); @@ -501,7 +419,7 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, &devfreq_cooling_ops); if (IS_ERR(cdev)) { err = PTR_ERR(cdev); - dev_err(df->dev.parent, + dev_err(dev, "Failed to register devfreq cooling device (%d)\n", err); goto release_ida; @@ -513,12 +431,9 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, release_ida: ida_simple_remove(&devfreq_ida, dfc->id); - remove_qos_req: dev_pm_qos_remove_request(&dfc->req_max_freq); - -free_tables: - kfree(dfc->power_table); +free_table: kfree(dfc->freq_table); free_dfc: kfree(dfc); @@ -551,24 +466,73 @@ struct thermal_cooling_device *devfreq_cooling_register(struct devfreq *df) EXPORT_SYMBOL_GPL(devfreq_cooling_register); /** + * devfreq_cooling_em_register_power() - Register devfreq cooling device with + * power information and automatically register Energy Model (EM) + * @df: Pointer to devfreq device. + * @dfc_power: Pointer to devfreq_cooling_power. + * + * Register a devfreq cooling device and automatically register EM. The + * available OPPs must be registered for the device. + * + * If @dfc_power is provided, the cooling device is registered with the + * power extensions. It is using the simple Energy Model which requires + * "dynamic-power-coefficient" a devicetree property. To not break drivers + * which miss that DT property, the function won't bail out when the EM + * registration failed. The cooling device will be registered if everything + * else is OK. + */ +struct thermal_cooling_device * +devfreq_cooling_em_register(struct devfreq *df, + struct devfreq_cooling_power *dfc_power) +{ + struct thermal_cooling_device *cdev; + struct device *dev; + int ret; + + if (IS_ERR_OR_NULL(df)) + return ERR_PTR(-EINVAL); + + dev = df->dev.parent; + + ret = dev_pm_opp_of_register_em(dev, NULL); + if (ret) + dev_dbg(dev, "Unable to register EM for devfreq cooling device (%d)\n", + ret); + + cdev = of_devfreq_cooling_register_power(dev->of_node, df, dfc_power); + + if (IS_ERR_OR_NULL(cdev)) + em_dev_unregister_perf_domain(dev); + + return cdev; +} +EXPORT_SYMBOL_GPL(devfreq_cooling_em_register); + +/** * devfreq_cooling_unregister() - Unregister devfreq cooling device. * @cdev: Pointer to devfreq cooling device to unregister. + * + * Unregisters devfreq cooling device and related Energy Model if it was + * present. */ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) { struct devfreq_cooling_device *dfc; + struct device *dev; - if (!cdev) + if (IS_ERR_OR_NULL(cdev)) return; dfc = cdev->devdata; + dev = dfc->devfreq->dev.parent; thermal_cooling_device_unregister(dfc->cdev); ida_simple_remove(&devfreq_ida, dfc->id); dev_pm_qos_remove_request(&dfc->req_max_freq); - kfree(dfc->power_table); - kfree(dfc->freq_table); + em_dev_unregister_perf_domain(dev); + + kfree(dfc->freq_table); kfree(dfc); } EXPORT_SYMBOL_GPL(devfreq_cooling_unregister); diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index ab0be26f0816..7a4170a0b51f 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -63,6 +63,8 @@ static inline s64 div_frac(s64 x, s64 y) * @trip_max_desired_temperature: last passive trip point of the thermal * zone. The temperature we are * controlling for. + * @sustainable_power: Sustainable power (heat) that this thermal zone can + * dissipate */ struct power_allocator_params { bool allocated_tzp; @@ -70,6 +72,7 @@ struct power_allocator_params { s32 prev_err; int trip_switch_on; int trip_max_desired_temperature; + u32 sustainable_power; }; /** @@ -96,7 +99,10 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz) if (instance->trip != params->trip_max_desired_temperature) continue; - if (power_actor_get_min_power(cdev, &min_power)) + if (!cdev_is_power_actor(cdev)) + continue; + + if (cdev->ops->state2power(cdev, instance->upper, &min_power)) continue; sustainable_power += min_power; @@ -111,26 +117,18 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz) * @sustainable_power: sustainable power for the thermal zone * @trip_switch_on: trip point number for the switch on temperature * @control_temp: target temperature for the power allocator governor - * @force: whether to force the update of the constants * * This function is used to update the estimation of the PID * controller constants in struct thermal_zone_parameters. - * Sustainable power is provided in case it was estimated. The - * estimated sustainable_power should not be stored in the - * thermal_zone_parameters so it has to be passed explicitly to this - * function. - * - * If @force is not set, the values in the thermal zone's parameters - * are preserved if they are not zero. If @force is set, the values - * in thermal zone's parameters are overwritten. */ static void estimate_pid_constants(struct thermal_zone_device *tz, u32 sustainable_power, int trip_switch_on, - int control_temp, bool force) + int control_temp) { int ret; int switch_on_temp; u32 temperature_threshold; + s32 k_i; ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp); if (ret) @@ -148,16 +146,15 @@ static void estimate_pid_constants(struct thermal_zone_device *tz, if (!temperature_threshold) return; - if (!tz->tzp->k_po || force) - tz->tzp->k_po = int_to_frac(sustainable_power) / - temperature_threshold; + tz->tzp->k_po = int_to_frac(sustainable_power) / + temperature_threshold; + + tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / + temperature_threshold; - if (!tz->tzp->k_pu || force) - tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / - temperature_threshold; + k_i = tz->tzp->k_pu / 10; + tz->tzp->k_i = k_i > 0 ? k_i : 1; - if (!tz->tzp->k_i || force) - tz->tzp->k_i = int_to_frac(10) / 1000; /* * The default for k_d and integral_cutoff is 0, so we can * leave them as they are. @@ -165,6 +162,41 @@ static void estimate_pid_constants(struct thermal_zone_device *tz, } /** + * get_sustainable_power() - Get the right sustainable power + * @tz: thermal zone for which to estimate the constants + * @params: parameters for the power allocator governor + * @control_temp: target temperature for the power allocator governor + * + * This function is used for getting the proper sustainable power value based + * on variables which might be updated by the user sysfs interface. If that + * happen the new value is going to be estimated and updated. It is also used + * after thermal zone binding, where the initial values where set to 0. + */ +static u32 get_sustainable_power(struct thermal_zone_device *tz, + struct power_allocator_params *params, + int control_temp) +{ + u32 sustainable_power; + + if (!tz->tzp->sustainable_power) + sustainable_power = estimate_sustainable_power(tz); + else + sustainable_power = tz->tzp->sustainable_power; + + /* Check if it's init value 0 or there was update via sysfs */ + if (sustainable_power != params->sustainable_power) { + estimate_pid_constants(tz, sustainable_power, + params->trip_switch_on, control_temp); + + /* Do the estimation only once and make available in sysfs */ + tz->tzp->sustainable_power = sustainable_power; + params->sustainable_power = sustainable_power; + } + + return sustainable_power; +} + +/** * pid_controller() - PID controller * @tz: thermal zone we are operating in * @control_temp: the target temperature in millicelsius @@ -193,14 +225,7 @@ static u32 pid_controller(struct thermal_zone_device *tz, max_power_frac = int_to_frac(max_allocatable_power); - if (tz->tzp->sustainable_power) { - sustainable_power = tz->tzp->sustainable_power; - } else { - sustainable_power = estimate_sustainable_power(tz); - estimate_pid_constants(tz, sustainable_power, - params->trip_switch_on, control_temp, - true); - } + sustainable_power = get_sustainable_power(tz, params, control_temp); err = control_temp - tz->temperature; err = int_to_frac(err); @@ -252,6 +277,38 @@ static u32 pid_controller(struct thermal_zone_device *tz, } /** + * power_actor_set_power() - limit the maximum power a cooling device consumes + * @cdev: pointer to &thermal_cooling_device + * @instance: thermal instance to update + * @power: the power in milliwatts + * + * Set the cooling device to consume at most @power milliwatts. The limit is + * expected to be a cap at the maximum power consumption. + * + * Return: 0 on success, -EINVAL if the cooling device does not + * implement the power actor API or -E* for other failures. + */ +static int +power_actor_set_power(struct thermal_cooling_device *cdev, + struct thermal_instance *instance, u32 power) +{ + unsigned long state; + int ret; + + ret = cdev->ops->power2state(cdev, power, &state); + if (ret) + return ret; + + instance->target = clamp_val(state, instance->lower, instance->upper); + mutex_lock(&cdev->lock); + cdev->updated = false; + mutex_unlock(&cdev->lock); + thermal_cdev_update(cdev); + + return 0; +} + +/** * divvy_up_power() - divvy the allocated power between the actors * @req_power: each actor's requested power * @max_power: each actor's maximum available power @@ -398,7 +455,8 @@ static int allocate_power(struct thermal_zone_device *tz, weighted_req_power[i] = frac_to_int(weight * req_power[i]); - if (power_actor_get_max_power(cdev, &max_power[i])) + if (cdev->ops->state2power(cdev, instance->lower, + &max_power[i])) continue; total_req_power += req_power[i]; @@ -572,7 +630,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz) if (!ret) estimate_pid_constants(tz, tz->tzp->sustainable_power, params->trip_switch_on, - control_temp, false); + control_temp); } reset_pid_controller(params); diff --git a/drivers/thermal/imx8mm_thermal.c b/drivers/thermal/imx8mm_thermal.c index a1e4f9bb4cb0..7442e013738f 100644 --- a/drivers/thermal/imx8mm_thermal.c +++ b/drivers/thermal/imx8mm_thermal.c @@ -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) diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig index 797907542e43..45c31f3d6054 100644 --- a/drivers/thermal/intel/int340x_thermal/Kconfig +++ b/drivers/thermal/intel/int340x_thermal/Kconfig @@ -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 diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile index 287eb0a1476d..38a2731e503c 100644 --- a/drivers/thermal/intel/int340x_thermal/Makefile +++ b/drivers/thermal/intel/int340x_thermal/Makefile @@ -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 diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 81e8b15ef405..9e6f2a895a23 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -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,152 +405,87 @@ static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid) return IRQ_HANDLED; } -#ifdef CONFIG_PROC_THERMAL_MMIO_RAPL - #define MCHBAR 0 -/* RAPL Support via MMIO interface */ -static struct rapl_if_priv rapl_mmio_priv; - -static int rapl_mmio_cpu_online(unsigned int cpu) +static int proc_thermal_set_mmio_base(struct pci_dev *pdev, + struct proc_thermal_device *proc_priv) { - struct rapl_package *rp; - - /* mmio rapl supports package 0 only for now */ - if (topology_physical_package_id(cpu)) - return 0; + int ret; - rp = rapl_find_package_domain(cpu, &rapl_mmio_priv); - if (!rp) { - rp = rapl_add_package(cpu, &rapl_mmio_priv); - if (IS_ERR(rp)) - return PTR_ERR(rp); + ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); + return -ENOMEM; } - cpumask_set_cpu(cpu, &rp->cpumask); - return 0; -} - -static int rapl_mmio_cpu_down_prep(unsigned int cpu) -{ - struct rapl_package *rp; - int lead_cpu; - - rp = rapl_find_package_domain(cpu, &rapl_mmio_priv); - if (!rp) - return 0; - - cpumask_clear_cpu(cpu, &rp->cpumask); - lead_cpu = cpumask_first(&rp->cpumask); - if (lead_cpu >= nr_cpu_ids) - rapl_remove_package(rp); - else if (rp->lead_cpu == cpu) - rp->lead_cpu = lead_cpu; - return 0; -} - -static int rapl_mmio_read_raw(int cpu, struct reg_action *ra) -{ - if (!ra->reg) - return -EINVAL; - ra->value = readq((void __iomem *)ra->reg); - ra->value &= ra->mask; - return 0; -} - -static int rapl_mmio_write_raw(int cpu, struct reg_action *ra) -{ - u64 val; - - if (!ra->reg) - return -EINVAL; + proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR]; - val = readq((void __iomem *)ra->reg); - val &= ~ra->mask; - val |= ra->value; - writeq(val, (void __iomem *)ra->reg); return 0; } -static int proc_thermal_rapl_add(struct pci_dev *pdev, +static int proc_thermal_mmio_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv, - struct rapl_mmio_regs *rapl_regs) + kernel_ulong_t feature_mask) { - enum rapl_domain_reg_id reg; - enum rapl_domain_type domain; int ret; - if (!rapl_regs) - return 0; + proc_priv->mmio_feature_mask = feature_mask; - ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME); - if (ret) { - dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); - return -ENOMEM; + if (feature_mask) { + ret = proc_thermal_set_mmio_base(pdev, proc_priv); + if (ret) + return ret; } - proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR]; - - for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) { - for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++) - if (rapl_regs->regs[domain][reg]) - rapl_mmio_priv.regs[domain][reg] = - (u64)proc_priv->mmio_base + - rapl_regs->regs[domain][reg]; - rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain]; + if (feature_mask & PROC_THERMAL_FEATURE_RAPL) { + ret = proc_thermal_rapl_add(pdev, proc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to add RAPL MMIO interface\n"); + return ret; + } } - rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit; - - rapl_mmio_priv.read_raw = rapl_mmio_read_raw; - rapl_mmio_priv.write_raw = rapl_mmio_write_raw; - rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL); - if (IS_ERR(rapl_mmio_priv.control_type)) { - pr_debug("failed to register powercap control_type.\n"); - return PTR_ERR(rapl_mmio_priv.control_type); + if (feature_mask & PROC_THERMAL_FEATURE_FIVR || + feature_mask & PROC_THERMAL_FEATURE_DVFS) { + ret = proc_thermal_rfim_add(pdev, proc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to add RFIM interface\n"); + goto err_rem_rapl; + } } - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online", - rapl_mmio_cpu_online, rapl_mmio_cpu_down_prep); - if (ret < 0) { - powercap_unregister_control_type(rapl_mmio_priv.control_type); - rapl_mmio_priv.control_type = NULL; - return ret; + if (feature_mask & PROC_THERMAL_FEATURE_MBOX) { + ret = proc_thermal_mbox_add(pdev, proc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to add MBOX interface\n"); + goto err_rem_rfim; + } } - rapl_mmio_priv.pcap_rapl_online = ret; return 0; -} -static void proc_thermal_rapl_remove(void) -{ - if (IS_ERR_OR_NULL(rapl_mmio_priv.control_type)) - return; +err_rem_rfim: + proc_thermal_rfim_remove(pdev); +err_rem_rapl: + proc_thermal_rapl_remove(); - cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online); - powercap_unregister_control_type(rapl_mmio_priv.control_type); + return ret; } -static const struct rapl_mmio_regs rapl_mmio_hsw = { - .reg_unit = 0x5938, - .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930}, - .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0}, - .limits[RAPL_DOMAIN_PACKAGE] = 2, - .limits[RAPL_DOMAIN_DRAM] = 2, -}; +static void proc_thermal_mmio_remove(struct pci_dev *pdev) +{ + struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev); -#else + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL) + proc_thermal_rapl_remove(); -static int proc_thermal_rapl_add(struct pci_dev *pdev, - struct proc_thermal_device *proc_priv, - struct rapl_mmio_regs *rapl_regs) -{ - return 0; -} -static void proc_thermal_rapl_remove(void) {} -static const struct rapl_mmio_regs rapl_mmio_hsw; + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR || + proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) + proc_thermal_rfim_remove(pdev); -#endif /* CONFIG_MMIO_RAPL */ + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_MBOX) + proc_thermal_mbox_remove(pdev); +} static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -629,18 +508,10 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, if (ret) return ret; - ret = proc_thermal_rapl_add(pdev, proc_priv, - (struct rapl_mmio_regs *)id->driver_data); - if (ret) { - dev_err(&pdev->dev, "failed to add RAPL MMIO interface\n"); - proc_thermal_remove(proc_priv); - return ret; - } - pci_set_drvdata(pdev, proc_priv); proc_thermal_emum_mode = PROC_THERMAL_PCI; - if (pdev->device == PCI_DEVICE_ID_PROC_BSW_THERMAL) { + if (pdev->device == PCI_DEVICE_ID_INTEL_BSW_THERMAL) { /* * Enumerate additional DTS sensors available via IOSF. * But we are not treating as a failure condition, if @@ -676,10 +547,18 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, return ret; ret = sysfs_create_group(&pdev->dev.kobj, &power_limit_attribute_group); - if (ret) + if (ret) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_tcc_offset_degree_celsius.attr); + return ret; + } - return ret; + ret = proc_thermal_mmio_add(pdev, proc_priv, id->driver_data); + if (ret) { + proc_thermal_remove(proc_priv); + return ret; + } + + return 0; } static void proc_thermal_pci_remove(struct pci_dev *pdev) @@ -693,7 +572,8 @@ static void proc_thermal_pci_remove(struct pci_dev *pdev) pci_disable_msi(pdev); } } - proc_thermal_rapl_remove(); + + proc_thermal_mmio_remove(pdev); proc_thermal_remove(proc_priv); } @@ -716,24 +596,22 @@ static int proc_thermal_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(proc_thermal_pm, NULL, proc_thermal_resume); static const struct pci_device_id proc_thermal_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL), - .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT0_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_ICL_THERMAL), - .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_JSL_THERMAL)}, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_TGL_THERMAL), - .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, }, - { 0, }, + { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) }, + { PCI_DEVICE_DATA(INTEL, BDW_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, BSW_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, BXT0_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, BXT1_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, BXTX_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, BXTP_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, CNL_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, CFL_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, GLK_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, HSB_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, ICL_THERMAL, PROC_THERMAL_FEATURE_RAPL) }, + { PCI_DEVICE_DATA(INTEL, JSL_THERMAL, 0) }, + { PCI_DEVICE_DATA(INTEL, SKL_THERMAL, PROC_THERMAL_FEATURE_RAPL) }, + { PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_MBOX) }, + { }, }; MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h new file mode 100644 index 000000000000..b9ed64561aaf --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * processor_thermal_device.h + * Copyright (c) 2020, Intel Corporation. + */ + +#ifndef __PROCESSOR_THERMAL_DEVICE_H__ +#define __PROCESSOR_THERMAL_DEVICE_H__ + +#include <linux/intel_rapl.h> + +#define PCI_DEVICE_ID_INTEL_ADL_THERMAL 0x461d +#define PCI_DEVICE_ID_INTEL_BDW_THERMAL 0x1603 +#define PCI_DEVICE_ID_INTEL_BSW_THERMAL 0x22DC + +#define PCI_DEVICE_ID_INTEL_BXT0_THERMAL 0x0A8C +#define PCI_DEVICE_ID_INTEL_BXT1_THERMAL 0x1A8C +#define PCI_DEVICE_ID_INTEL_BXTX_THERMAL 0x4A8C +#define PCI_DEVICE_ID_INTEL_BXTP_THERMAL 0x5A8C + +#define PCI_DEVICE_ID_INTEL_CNL_THERMAL 0x5a03 +#define PCI_DEVICE_ID_INTEL_CFL_THERMAL 0x3E83 +#define PCI_DEVICE_ID_INTEL_GLK_THERMAL 0x318C +#define PCI_DEVICE_ID_INTEL_HSB_THERMAL 0x0A03 +#define PCI_DEVICE_ID_INTEL_ICL_THERMAL 0x8a03 +#define PCI_DEVICE_ID_INTEL_JSL_THERMAL 0x4E03 +#define PCI_DEVICE_ID_INTEL_SKL_THERMAL 0x1903 +#define PCI_DEVICE_ID_INTEL_TGL_THERMAL 0x9A03 + +struct power_config { + u32 index; + u32 min_uw; + u32 max_uw; + u32 tmin_us; + u32 tmax_us; + u32 step_uw; +}; + +struct proc_thermal_device { + struct device *dev; + struct acpi_device *adev; + struct power_config power_limits[2]; + struct int34x_thermal_zone *int340x_zone; + struct intel_soc_dts_sensors *soc_dts; + u32 mmio_feature_mask; + void __iomem *mmio_base; +}; + +struct rapl_mmio_regs { + u64 reg_unit; + u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; + int limits[RAPL_DOMAIN_MAX]; +}; + +#define PROC_THERMAL_FEATURE_NONE 0x00 +#define PROC_THERMAL_FEATURE_RAPL 0x01 +#define PROC_THERMAL_FEATURE_FIVR 0x02 +#define PROC_THERMAL_FEATURE_DVFS 0x04 +#define PROC_THERMAL_FEATURE_MBOX 0x08 + +#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL) +int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); +void proc_thermal_rapl_remove(void); +#else +static int __maybe_unused proc_thermal_rapl_add(struct pci_dev *pdev, + struct proc_thermal_device *proc_priv) +{ + return 0; +} + +static void __maybe_unused proc_thermal_rapl_remove(void) +{ +} +#endif + +int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); +void proc_thermal_rfim_remove(struct pci_dev *pdev); + +int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); +void proc_thermal_mbox_remove(struct pci_dev *pdev); + +#endif diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c new file mode 100644 index 000000000000..990f51f22884 --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * processor thermal device mailbox driver for Workload type hints + * Copyright (c) 2020, Intel Corporation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include "processor_thermal_device.h" + +#define MBOX_CMD_WORKLOAD_TYPE_READ 0x0E +#define MBOX_CMD_WORKLOAD_TYPE_WRITE 0x0F + +#define MBOX_OFFSET_DATA 0x5810 +#define MBOX_OFFSET_INTERFACE 0x5818 + +#define MBOX_BUSY_BIT 31 +#define MBOX_RETRY_COUNT 100 + +#define MBOX_DATA_BIT_VALID 31 +#define MBOX_DATA_BIT_AC_DC 30 + +static DEFINE_MUTEX(mbox_lock); + +static int send_mbox_cmd(struct pci_dev *pdev, u8 cmd_id, u32 cmd_data, u8 *cmd_resp) +{ + struct proc_thermal_device *proc_priv; + u32 retries, data; + int ret; + + mutex_lock(&mbox_lock); + proc_priv = pci_get_drvdata(pdev); + + /* Poll for rb bit == 0 */ + retries = MBOX_RETRY_COUNT; + do { + data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE)); + if (data & BIT_ULL(MBOX_BUSY_BIT)) { + ret = -EBUSY; + continue; + } + ret = 0; + break; + } while (--retries); + + if (ret) + goto unlock_mbox; + + if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_WRITE) + writel(cmd_data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_DATA))); + + /* Write command register */ + data = BIT_ULL(MBOX_BUSY_BIT) | cmd_id; + writel(data, (void __iomem *) ((proc_priv->mmio_base + MBOX_OFFSET_INTERFACE))); + + /* Poll for rb bit == 0 */ + retries = MBOX_RETRY_COUNT; + do { + data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE)); + if (data & BIT_ULL(MBOX_BUSY_BIT)) { + ret = -EBUSY; + continue; + } + + if (data) { + ret = -ENXIO; + goto unlock_mbox; + } + + if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_READ) { + data = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_DATA)); + *cmd_resp = data & 0xff; + } + + ret = 0; + break; + } while (--retries); + +unlock_mbox: + mutex_unlock(&mbox_lock); + return ret; +} + +/* List of workload types */ +static const char * const workload_types[] = { + "none", + "idle", + "semi_active", + "bursty", + "sustained", + "battery_life", + NULL +}; + + +static ssize_t workload_available_types_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = 0; + int ret = 0; + + while (workload_types[i] != NULL) + ret += sprintf(&buf[ret], "%s ", workload_types[i++]); + + ret += sprintf(&buf[ret], "\n"); + + return ret; +} + +static DEVICE_ATTR_RO(workload_available_types); + +static ssize_t workload_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + char str_preference[15]; + u32 data = 0; + ssize_t ret; + + ret = sscanf(buf, "%14s", str_preference); + if (ret != 1) + return -EINVAL; + + ret = match_string(workload_types, -1, str_preference); + if (ret < 0) + return ret; + + ret &= 0xff; + + if (ret) + data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC); + + data |= ret; + + ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data, NULL); + if (ret) + return false; + + return count; +} + +static ssize_t workload_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + u8 cmd_resp; + int ret; + + ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp); + if (ret) + return false; + + cmd_resp &= 0xff; + + if (cmd_resp > ARRAY_SIZE(workload_types) - 1) + return -EINVAL; + + return sprintf(buf, "%s\n", workload_types[cmd_resp]); +} + +static DEVICE_ATTR_RW(workload_type); + +static struct attribute *workload_req_attrs[] = { + &dev_attr_workload_available_types.attr, + &dev_attr_workload_type.attr, + NULL +}; + +static const struct attribute_group workload_req_attribute_group = { + .attrs = workload_req_attrs, + .name = "workload_request" +}; + + + +static bool workload_req_created; + +int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) +{ + u8 cmd_resp; + int ret; + + /* Check if there is a mailbox support, if fails return success */ + ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp); + if (ret) + return 0; + + ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group); + if (ret) + return ret; + + workload_req_created = true; + + return 0; +} +EXPORT_SYMBOL_GPL(proc_thermal_mbox_add); + +void proc_thermal_mbox_remove(struct pci_dev *pdev) +{ + if (workload_req_created) + sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group); + + workload_req_created = false; + +} +EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c new file mode 100644 index 000000000000..a205221ec8df --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * processor thermal device RFIM control + * Copyright (c) 2020, Intel Corporation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include "processor_thermal_device.h" + +static struct rapl_if_priv rapl_mmio_priv; + +static const struct rapl_mmio_regs rapl_mmio_default = { + .reg_unit = 0x5938, + .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930}, + .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0}, + .limits[RAPL_DOMAIN_PACKAGE] = 2, + .limits[RAPL_DOMAIN_DRAM] = 2, +}; + +static int rapl_mmio_cpu_online(unsigned int cpu) +{ + struct rapl_package *rp; + + /* mmio rapl supports package 0 only for now */ + if (topology_physical_package_id(cpu)) + return 0; + + rp = rapl_find_package_domain(cpu, &rapl_mmio_priv); + if (!rp) { + rp = rapl_add_package(cpu, &rapl_mmio_priv); + if (IS_ERR(rp)) + return PTR_ERR(rp); + } + cpumask_set_cpu(cpu, &rp->cpumask); + return 0; +} + +static int rapl_mmio_cpu_down_prep(unsigned int cpu) +{ + struct rapl_package *rp; + int lead_cpu; + + rp = rapl_find_package_domain(cpu, &rapl_mmio_priv); + if (!rp) + return 0; + + cpumask_clear_cpu(cpu, &rp->cpumask); + lead_cpu = cpumask_first(&rp->cpumask); + if (lead_cpu >= nr_cpu_ids) + rapl_remove_package(rp); + else if (rp->lead_cpu == cpu) + rp->lead_cpu = lead_cpu; + return 0; +} + +static int rapl_mmio_read_raw(int cpu, struct reg_action *ra) +{ + if (!ra->reg) + return -EINVAL; + + ra->value = readq((void __iomem *)ra->reg); + ra->value &= ra->mask; + return 0; +} + +static int rapl_mmio_write_raw(int cpu, struct reg_action *ra) +{ + u64 val; + + if (!ra->reg) + return -EINVAL; + + val = readq((void __iomem *)ra->reg); + val &= ~ra->mask; + val |= ra->value; + writeq(val, (void __iomem *)ra->reg); + return 0; +} + +int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) +{ + const struct rapl_mmio_regs *rapl_regs = &rapl_mmio_default; + enum rapl_domain_reg_id reg; + enum rapl_domain_type domain; + int ret; + + if (!rapl_regs) + return 0; + + for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) { + for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++) + if (rapl_regs->regs[domain][reg]) + rapl_mmio_priv.regs[domain][reg] = + (u64)proc_priv->mmio_base + + rapl_regs->regs[domain][reg]; + rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain]; + } + rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit; + + rapl_mmio_priv.read_raw = rapl_mmio_read_raw; + rapl_mmio_priv.write_raw = rapl_mmio_write_raw; + + rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL); + if (IS_ERR(rapl_mmio_priv.control_type)) { + pr_debug("failed to register powercap control_type.\n"); + return PTR_ERR(rapl_mmio_priv.control_type); + } + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online", + rapl_mmio_cpu_online, rapl_mmio_cpu_down_prep); + if (ret < 0) { + powercap_unregister_control_type(rapl_mmio_priv.control_type); + rapl_mmio_priv.control_type = NULL; + return ret; + } + rapl_mmio_priv.pcap_rapl_online = ret; + + return 0; +} +EXPORT_SYMBOL_GPL(proc_thermal_rapl_add); + +void proc_thermal_rapl_remove(void) +{ + if (IS_ERR_OR_NULL(rapl_mmio_priv.control_type)) + return; + + cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online); + powercap_unregister_control_type(rapl_mmio_priv.control_type); +} +EXPORT_SYMBOL_GPL(proc_thermal_rapl_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c new file mode 100644 index 000000000000..aef993a813e2 --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -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"); diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index 3b813ebb6ca1..41723c6c6c0c 100644 --- a/drivers/thermal/intel/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c @@ -7,14 +7,16 @@ * Tushar Dave <tushar.n.dave@intel.com> */ +#include <linux/acpi.h> +#include <linux/delay.h> #include <linux/module.h> -#include <linux/types.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/acpi.h> +#include <linux/pm.h> +#include <linux/suspend.h> #include <linux/thermal.h> +#include <linux/types.h> #include <linux/units.h> -#include <linux/pm.h> /* Intel PCH thermal Device IDs */ #define PCH_THERMAL_DID_HSW_1 0x9C24 /* Haswell PCH */ @@ -26,6 +28,7 @@ #define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */ #define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */ #define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */ +#define PCH_THERMAL_DID_LWB 0xA1B1 /* Lewisburg PCH */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -35,6 +38,7 @@ #define WPT_TSREL 0x0A /* Thermal Sensor Report Enable and Lock */ #define WPT_TSMIC 0x0C /* Thermal Sensor SMI Control */ #define WPT_CTT 0x0010 /* Catastrophic Trip Point */ +#define WPT_TSPM 0x001C /* Thermal Sensor Power Management */ #define WPT_TAHV 0x0014 /* Thermal Alert High Value */ #define WPT_TALV 0x0018 /* Thermal Alert Low Value */ #define WPT_TL 0x00000040 /* Throttle Value */ @@ -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 @@ read_trips: trip_temp = readw(ptd->hw_base + WPT_CTT); trip_temp &= 0x1FF; if (trip_temp) { - /* Resolution of 1/2 degree C and an offset of -50C */ - ptd->crt_temp = trip_temp * 1000 / 2 - 50000; + ptd->crt_temp = GET_WPT_TEMP(trip_temp); ptd->crt_trip_id = 0; ++(*nr_trips); } @@ -157,8 +176,7 @@ read_trips: trip_temp = readw(ptd->hw_base + WPT_PHL); trip_temp &= 0x1FF; if (trip_temp) { - /* Resolution of 1/2 degree C and an offset of -50C */ - ptd->hot_temp = trip_temp * 1000 / 2 - 50000; + ptd->hot_temp = GET_WPT_TEMP(trip_temp); ptd->hot_trip_id = *nr_trips; ++(*nr_trips); } @@ -170,12 +188,7 @@ read_trips: static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp) { - u16 wpt_temp; - - wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP); - - /* Resolution of 1/2 degree C and an offset of -50C */ - *temp = (wpt_temp * 1000 / 2 - 50000); + *temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP)); return 0; } @@ -183,13 +196,62 @@ static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp) static int pch_wpt_suspend(struct pch_thermal_device *ptd) { u8 tsel; + u8 pch_delay_cnt = 1; + u16 pch_thr_temp, pch_cur_temp; - if (ptd->bios_enabled) + /* Shutdown the thermal sensor if it is not enabled by BIOS */ + if (!ptd->bios_enabled) { + tsel = readb(ptd->hw_base + WPT_TSEL); + writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL); return 0; + } - tsel = readb(ptd->hw_base + WPT_TSEL); + /* Do not check temperature if it is not a S0ix capable platform */ +#ifdef CONFIG_ACPI + if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) + return 0; +#else + return 0; +#endif + + /* Do not check temperature if it is not s2idle */ + if (pm_suspend_via_firmware()) + return 0; + + /* Get the PCH temperature threshold value */ + pch_thr_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TSPM)); + + /* Get the PCH current temperature value */ + pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP)); + + /* + * If current PCH temperature is higher than configured PCH threshold + * value, run some delay loop with sleep to let the current temperature + * go down below the threshold value which helps to allow system enter + * lower power S0ix suspend state. Even after delay loop if PCH current + * temperature stays above threshold, notify the warning message + * which helps to indentify the reason why S0ix entry was rejected. + */ + while (pch_delay_cnt <= delay_cnt) { + if (pch_cur_temp <= pch_thr_temp) + break; + + dev_warn(&ptd->pdev->dev, + "CPU-PCH current temp [%dC] higher than the threshold temp [%dC], sleep %d times for %d ms duration\n", + pch_cur_temp, pch_thr_temp, pch_delay_cnt, delay_timeout); + msleep(delay_timeout); + /* Read the PCH current temperature for next cycle. */ + pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP)); + pch_delay_cnt++; + } - writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL); + if (pch_cur_temp > pch_thr_temp) + dev_warn(&ptd->pdev->dev, + "CPU-PCH is hot [%dC] even after delay, continue to suspend. S0ix might fail\n", + pch_cur_temp); + else + dev_info(&ptd->pdev->dev, + "CPU-PCH is cool [%dC], continue to suspend\n", pch_cur_temp); return 0; } @@ -276,6 +338,7 @@ enum board_ids { board_skl, board_cnl, board_cml, + board_lwb, }; static const struct board_info { @@ -301,7 +364,11 @@ static const struct board_info { [board_cml] = { .name = "pch_cometlake", .ops = &pch_dev_ops_wpt, - } + }, + [board_lwb] = { + .name = "pch_lewisburg", + .ops = &pch_dev_ops_wpt, + }, }; static int intel_pch_thermal_probe(struct pci_dev *pdev, @@ -415,6 +482,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = { .driver_data = board_cnl, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H), .driver_data = board_cml, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB), + .driver_data = board_lwb, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 0bd7aa564bc2..149c6d7fd5a0 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -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; diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 0dd47dca3e77..75c69fe6e955 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -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 @@ error_unregister: return ret; } -static int __maybe_unused rcar_gen3_thermal_suspend(struct device *dev) -{ - struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); - - rcar_thermal_irq_set(priv, false); - - return 0; -} - static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) { struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); @@ -499,15 +403,12 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; priv->thermal_init(tsc); - rcar_gen3_thermal_update_range(tsc); } - rcar_thermal_irq_set(priv, true); - return 0; } -static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, rcar_gen3_thermal_suspend, +static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL, rcar_gen3_thermal_resume); static struct platform_driver rcar_gen3_thermal_driver = { diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 5c2a13bf249c..b49f04daaf47 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -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; diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index f8b13071a6f4..8c80bd06dd9f 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -8,6 +8,7 @@ * Based on the work of Josef Gajdusek <atx@atx.name> */ +#include <linux/bitmap.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/interrupt.h> @@ -74,7 +75,7 @@ struct ths_thermal_chip { int (*calibrate)(struct ths_device *tmdev, u16 *caldata, int callen); int (*init)(struct ths_device *tmdev); - int (*irq_ack)(struct ths_device *tmdev); + unsigned long (*irq_ack)(struct ths_device *tmdev); int (*calc_temp)(struct ths_device *tmdev, int id, int reg); }; @@ -146,9 +147,10 @@ static const struct regmap_config config = { .max_register = 0xfc, }; -static int sun8i_h3_irq_ack(struct ths_device *tmdev) +static unsigned long sun8i_h3_irq_ack(struct ths_device *tmdev) { - int i, state, ret = 0; + unsigned long irq_bitmap = 0; + int i, state; regmap_read(tmdev->regmap, SUN8I_THS_IS, &state); @@ -156,16 +158,17 @@ static int sun8i_h3_irq_ack(struct ths_device *tmdev) if (state & SUN8I_THS_DATA_IRQ_STS(i)) { regmap_write(tmdev->regmap, SUN8I_THS_IS, SUN8I_THS_DATA_IRQ_STS(i)); - ret |= BIT(i); + bitmap_set(&irq_bitmap, i, 1); } } - return ret; + return irq_bitmap; } -static int sun50i_h6_irq_ack(struct ths_device *tmdev) +static unsigned long sun50i_h6_irq_ack(struct ths_device *tmdev) { - int i, state, ret = 0; + unsigned long irq_bitmap = 0; + int i, state; regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state); @@ -173,24 +176,22 @@ static int sun50i_h6_irq_ack(struct ths_device *tmdev) if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) { regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS, SUN50I_H6_THS_DATA_IRQ_STS(i)); - ret |= BIT(i); + bitmap_set(&irq_bitmap, i, 1); } } - return ret; + return irq_bitmap; } static irqreturn_t sun8i_irq_thread(int irq, void *data) { struct ths_device *tmdev = data; - int i, state; - - state = tmdev->chip->irq_ack(tmdev); + unsigned long irq_bitmap = tmdev->chip->irq_ack(tmdev); + int i; - for (i = 0; i < tmdev->chip->sensor_num; i++) { - if (state & BIT(i)) - thermal_zone_device_update(tmdev->sensor[i].tzd, - THERMAL_EVENT_UNSPECIFIED); + for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) { + thermal_zone_device_update(tmdev->sensor[i].tzd, + THERMAL_EVENT_UNSPECIFIED); } return IRQ_HANDLED; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c6d74bc1c90b..4a291d205d5c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -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); } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 681209db42a8..8df600fa7b79 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -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 diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 8b92e00ff236..ad03262cca56 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -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; } diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index a6f371fc9af2..0866e949339b 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -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, }; |