diff options
Diffstat (limited to 'drivers/thermal')
59 files changed, 2144 insertions, 1177 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 204ed89a3ec9..61e7ae524b1f 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -40,6 +40,15 @@ config THERMAL_DEBUGFS Say Y to allow the thermal subsystem to collect diagnostic information that can be accessed via debugfs. +config THERMAL_CORE_TESTING + tristate "Thermal core testing facility" + depends on DEBUG_FS + help + Say Y to add a debugfs-based thermal core testing facility. + It allows test thermal zones to be created and populated + with trip points in order to exercise the thermal core + functionality in a controlled way. + config THERMAL_EMERGENCY_POWEROFF_DELAY_MS int "Emergency poweroff delay in milli-seconds" default 0 @@ -343,32 +352,6 @@ config ROCKCHIP_THERMAL trip point. Cpufreq is used as the cooling device and will throttle CPUs when the Temperature crosses the passive trip point. -config RCAR_THERMAL - tristate "Renesas R-Car thermal driver" - depends on ARCH_RENESAS || COMPILE_TEST - depends on HAS_IOMEM - help - Enable this to plug the R-Car thermal sensor driver into the Linux - thermal framework. - -config RCAR_GEN3_THERMAL - tristate "Renesas R-Car Gen3 and RZ/G2 thermal driver" - depends on ARCH_RENESAS || COMPILE_TEST - depends on HAS_IOMEM - depends on OF - help - Enable this to plug the R-Car Gen3 or RZ/G2 thermal sensor driver into - the Linux thermal framework. - -config RZG2L_THERMAL - tristate "Renesas RZ/G2L thermal driver" - depends on ARCH_RENESAS || COMPILE_TEST - depends on HAS_IOMEM - depends on OF - help - Enable this to plug the RZ/G2L thermal sensor driver into the Linux - thermal framework. - config KIRKWOOD_THERMAL tristate "Temperature sensor on Marvell Kirkwood SoCs" depends on MACH_KIRKWOOD || COMPILE_TEST @@ -455,10 +438,12 @@ source "drivers/thermal/samsung/Kconfig" endmenu menu "STMicroelectronics thermal drivers" -depends on (ARCH_STI || ARCH_STM32) && OF +depends on (ARCH_STI || ARCH_STM32) && THERMAL_OF source "drivers/thermal/st/Kconfig" endmenu +source "drivers/thermal/renesas/Kconfig" + source "drivers/thermal/tegra/Kconfig" config GENERIC_ADC_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 5cdf7d68687f..41c4d56beb40 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -38,9 +38,7 @@ obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o -obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o -obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o -obj-$(CONFIG_RZG2L_THERMAL) += rzg2l_thermal.o +obj-y += renesas/ obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-y += samsung/ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o @@ -65,3 +63,4 @@ obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o +obj-$(CONFIG_THERMAL_CORE_TESTING) += testing/ diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index 5c1cebe07580..7d61493082b5 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -163,6 +163,7 @@ MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table); static int bcm2835_thermal_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; const struct of_device_id *match; struct thermal_zone_device *tz; struct bcm2835_thermal_data *data; @@ -170,12 +171,11 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) u32 val; unsigned long rate; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - match = of_match_device(bcm2835_thermal_of_match_table, - &pdev->dev); + match = of_match_device(bcm2835_thermal_of_match_table, dev); if (!match) return -EINVAL; @@ -185,34 +185,20 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) return err; } - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - err = PTR_ERR(data->clk); - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get clk: %d\n", err); - return err; - } - - err = clk_prepare_enable(data->clk); - if (err) - return err; + data->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "Could not get clk\n"); rate = clk_get_rate(data->clk); if ((rate < 1920000) || (rate > 5000000)) - dev_warn(&pdev->dev, + dev_warn(dev, "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n", data->clk, rate); /* register of thermal sensor and get info from DT */ - tz = devm_thermal_of_zone_register(&pdev->dev, 0, data, - &bcm2835_thermal_ops); - if (IS_ERR(tz)) { - err = PTR_ERR(tz); - dev_err(&pdev->dev, - "Failed to register the thermal device: %d\n", - err); - goto err_clk; - } + tz = devm_thermal_of_zone_register(dev, 0, data, &bcm2835_thermal_ops); + if (IS_ERR(tz)) + return dev_err_probe(dev, PTR_ERR(tz), "Failed to register the thermal device\n"); /* * right now the FW does set up the HW-block, so we are not @@ -222,8 +208,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) */ val = readl(data->regs + BCM2835_TS_TSENSCTL); if (!(val & BCM2835_TS_TSENSCTL_RSTB)) { - struct thermal_trip trip; - int offset, slope; + int offset, slope, crit_temp; slope = thermal_zone_get_slope(tz); offset = thermal_zone_get_offset(tz); @@ -231,12 +216,10 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) * For now we deal only with critical, otherwise * would need to iterate */ - err = thermal_zone_get_trip(tz, 0, &trip); + err = thermal_zone_get_crit_temp(tz, &crit_temp); if (err < 0) { - dev_err(&pdev->dev, - "Not able to read trip_temp: %d\n", - err); - goto err_tz; + dev_err(dev, "Not able to read trip_temp: %d\n", err); + return err; } /* set bandgap reference voltage and enable voltage regulator */ @@ -248,7 +231,7 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT); /* trip_adc value from info */ - val |= bcm2835_thermal_temp2adc(trip.temperature, + val |= bcm2835_thermal_temp2adc(crit_temp, offset, slope) << BCM2835_TS_TSENSCTL_THOLD_SHIFT; @@ -269,17 +252,11 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) */ err = thermal_add_hwmon_sysfs(tz); if (err) - goto err_tz; + return err; bcm2835_thermal_debugfs(pdev); return 0; -err_tz: - devm_thermal_of_zone_unregister(&pdev->dev, tz); -err_clk: - clk_disable_unprepare(data->clk); - - return err; } static void bcm2835_thermal_remove(struct platform_device *pdev) @@ -287,7 +264,6 @@ static void bcm2835_thermal_remove(struct platform_device *pdev) struct bcm2835_thermal_data *data = platform_get_drvdata(pdev); debugfs_remove_recursive(data->debugfsdir); - clk_disable_unprepare(data->clk); } static struct platform_driver bcm2835_thermal_driver = { diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 9674e5ffcfa2..270982740fde 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -338,11 +338,9 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) thermal = devm_thermal_of_zone_register(&pdev->dev, 0, priv, of_ops); - if (IS_ERR(thermal)) { - ret = PTR_ERR(thermal); - dev_err(&pdev->dev, "could not register sensor: %d\n", ret); - return ret; - } + if (IS_ERR(thermal)) + return dev_err_probe(&pdev->dev, PTR_ERR(thermal), + "could not register sensor\n"); priv->thermal = thermal; @@ -352,10 +350,9 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) brcmstb_tmon_irq_thread, IRQF_ONESHOT, DRV_NAME, priv); - if (ret < 0) { - dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "could not request IRQ\n"); } dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index acb52c9ee10f..863e7a4272e6 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -13,6 +13,28 @@ #include "thermal_core.h" +static void bang_bang_set_instance_target(struct thermal_instance *instance, + unsigned int target) +{ + if (instance->target != 0 && instance->target != 1 && + instance->target != THERMAL_NO_TARGET) + pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", + instance->target, instance->name); + + /* + * Enable the fan when the trip is crossed on the way up and disable it + * when the trip is crossed on the way down. + */ + instance->target = target; + instance->initialized = true; + + dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); + + mutex_lock(&instance->cdev->lock); + __thermal_cdev_update(instance->cdev); + mutex_unlock(&instance->cdev->lock); +} + /** * bang_bang_control - controls devices associated with the given zone * @tz: thermal_zone_device @@ -54,41 +76,58 @@ static void bang_bang_control(struct thermal_zone_device *tz, tz->temperature, trip->hysteresis); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; + if (instance->trip == trip) + bang_bang_set_instance_target(instance, crossed_up); + } +} + +static void bang_bang_manage(struct thermal_zone_device *tz) +{ + const struct thermal_trip_desc *td; + struct thermal_instance *instance; - if (instance->target == THERMAL_NO_TARGET) - instance->target = 0; + /* If the code below has run already, nothing needs to be done. */ + if (tz->governor_data) + return; - if (instance->target != 0 && instance->target != 1) { - pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", - instance->target, instance->name); + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + bool turn_on; - instance->target = 1; - } + if (trip->temperature == THERMAL_TEMP_INVALID || + trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + continue; /* - * Enable the fan when the trip is crossed on the way up and - * disable it when the trip is crossed on the way down. + * Adjust the target states for uninitialized thermal instances + * to the thermal zone temperature and the trip point threshold. */ - if (instance->target == 0 && crossed_up) - instance->target = 1; - else if (instance->target == 1 && !crossed_up) - instance->target = 0; - - dev_dbg(&instance->cdev->device, "target=%ld\n", instance->target); - - mutex_lock(&instance->cdev->lock); - instance->cdev->updated = false; /* cdev needs update */ - mutex_unlock(&instance->cdev->lock); + turn_on = tz->temperature >= td->threshold; + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (!instance->initialized && instance->trip == trip) + bang_bang_set_instance_target(instance, turn_on); + } } - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - thermal_cdev_update(instance->cdev); + tz->governor_data = (void *)true; +} + +static void bang_bang_update_tz(struct thermal_zone_device *tz, + enum thermal_notify_event reason) +{ + /* + * Let bang_bang_manage() know that it needs to walk trips after binding + * a new cdev and after system resume. + */ + if (reason == THERMAL_TZ_BIND_CDEV || reason == THERMAL_TZ_RESUME) + tz->governor_data = NULL; } static struct thermal_governor thermal_gov_bang_bang = { .name = "bang_bang", .trip_crossed = bang_bang_control, + .manage = bang_bang_manage, + .update_tz = bang_bang_update_tz, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang); diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index 45f04a25255a..1b2345a697c5 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -759,6 +759,9 @@ static void power_allocator_manage(struct thermal_zone_device *tz) return; } + if (!params->trip_max) + return; + allocate_power(tz, params->trip_max->temperature); params->update_cdevs = true; } diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 65974fe8be0d..fd5527188cf9 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -55,7 +55,11 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (cur_state <= instance->lower) return THERMAL_NO_TARGET; - return clamp(cur_state - 1, instance->lower, instance->upper); + /* + * If 'throttle' is false, no mitigation is necessary, so + * request the lower state for this instance. + */ + return instance->lower; } return instance->target; @@ -93,23 +97,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, if (instance->initialized && old_target == instance->target) continue; - if (trip->type == THERMAL_TRIP_PASSIVE) { - /* - * If the target state for this thermal instance - * changes from THERMAL_NO_TARGET to something else, - * ensure that the zone temperature will be updated - * (assuming enabled passive cooling) until it becomes - * THERMAL_NO_TARGET again, or the cooling device may - * not be reset to its initial state. - */ - if (old_target == THERMAL_NO_TARGET && - instance->target != THERMAL_NO_TARGET) - tz->passive++; - else if (old_target != THERMAL_NO_TARGET && - instance->target == THERMAL_NO_TARGET) - tz->passive--; - } - instance->initialized = true; mutex_lock(&instance->cdev->lock); diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index dd751ae63608..f1fe0f8ab04f 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -388,15 +388,10 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data) { struct platform_device *pdev = data->pdev; struct device *dev = &pdev->dev; - int ret; data->clk = devm_clk_get(dev, "thermal_clk"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get thermal clk: %d\n", ret); - return ret; - } + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "failed to get thermal clk\n"); data->sensor = devm_kzalloc(dev, sizeof(*data->sensor), GFP_KERNEL); if (!data->sensor) @@ -470,11 +465,22 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) return IRQ_HANDLED; } +static int hisi_trip_walk_cb(struct thermal_trip *trip, void *arg) +{ + struct hisi_thermal_sensor *sensor = arg; + + if (trip->type != THERMAL_TRIP_PASSIVE) + return 0; + + sensor->thres_temp = trip->temperature; + /* Return nonzero to terminate the search. */ + return 1; +} + static int hisi_thermal_register_sensor(struct platform_device *pdev, struct hisi_thermal_sensor *sensor) { - int ret, i; - struct thermal_trip trip; + int ret; sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, sensor->id, sensor, @@ -487,15 +493,7 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, return ret; } - for (i = 0; i < thermal_zone_get_num_trips(sensor->tzd); i++) { - - thermal_zone_get_trip(sensor->tzd, i, &trip); - - if (trip.type == THERMAL_TRIP_PASSIVE) { - sensor->thres_temp = trip.temperature; - break; - } - } + thermal_zone_for_each_trip(sensor->tzd, hisi_trip_walk_cb, sensor); return 0; } diff --git a/drivers/thermal/imx_sc_thermal.c b/drivers/thermal/imx_sc_thermal.c index 7224f8d21db9..88558ce58880 100644 --- a/drivers/thermal/imx_sc_thermal.c +++ b/drivers/thermal/imx_sc_thermal.c @@ -111,8 +111,7 @@ static int imx_sc_thermal_probe(struct platform_device *pdev) if (ret == -ENODEV) continue; - dev_err(&pdev->dev, "failed to register thermal zone\n"); - return ret; + return dev_err_probe(&pdev->dev, ret, "failed to register thermal zone\n"); } devm_thermal_add_hwmon_sysfs(&pdev->dev, sensor->tzd); diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 83eaae5ca3b8..b8e85a405351 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -331,25 +331,16 @@ static int imx_change_mode(struct thermal_zone_device *tz, return 0; } -static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip_id, - int temp) +static int imx_set_trip_temp(struct thermal_zone_device *tz, + const struct thermal_trip *trip, int temp) { struct imx_thermal_data *data = thermal_zone_device_priv(tz); - struct thermal_trip trip; int ret; ret = pm_runtime_resume_and_get(data->dev); if (ret < 0) return ret; - ret = __thermal_zone_get_trip(tz, trip_id, &trip); - if (ret) - return ret; - - /* do not allow changing critical threshold */ - if (trip.type == THERMAL_TRIP_CRITICAL) - return -EPERM; - /* do not allow passive to be set higher than critical */ if (temp < 0 || temp > trips[IMX_TRIP_CRITICAL].temperature) return -EINVAL; @@ -362,24 +353,16 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip_id, return 0; } -static int imx_bind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) -{ - return thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev, - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); -} - -static int imx_unbind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) +static bool imx_should_bind(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { - return thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev); + return trip->type == THERMAL_TRIP_PASSIVE; } static struct thermal_zone_device_ops imx_tz_ops = { - .bind = imx_bind, - .unbind = imx_unbind, + .should_bind = imx_should_bind, .get_temp = imx_get_temp, .change_mode = imx_change_mode, .set_trip_temp = imx_set_trip_temp, @@ -601,28 +584,29 @@ static inline void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data static int imx_thermal_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct imx_thermal_data *data; struct regmap *map; int measure_freq; int ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->dev = &pdev->dev; + data->dev = dev; - map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon"); + map = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,tempmon"); if (IS_ERR(map)) { ret = PTR_ERR(map); - dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret); + dev_err(dev, "failed to get tempmon regmap: %d\n", ret); return ret; } data->tempmon = map; - data->socdata = of_device_get_match_data(&pdev->dev); + data->socdata = of_device_get_match_data(dev); if (!data->socdata) { - dev_err(&pdev->dev, "no device match found\n"); + dev_err(dev, "no device match found\n"); return -ENODEV; } @@ -645,15 +629,15 @@ static int imx_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - if (of_property_present(pdev->dev.of_node, "nvmem-cells")) { + if (of_property_present(dev->of_node, "nvmem-cells")) { ret = imx_init_from_nvmem_cells(pdev); if (ret) - return dev_err_probe(&pdev->dev, ret, + return dev_err_probe(dev, ret, "failed to init from nvmem\n"); } else { ret = imx_init_from_tempmon_data(pdev); if (ret) { - dev_err(&pdev->dev, "failed to init from fsl,tempmon-data\n"); + dev_err(dev, "failed to init from fsl,tempmon-data\n"); return ret; } } @@ -673,15 +657,12 @@ static int imx_thermal_probe(struct platform_device *pdev) ret = imx_thermal_register_legacy_cooling(data); if (ret) - return dev_err_probe(&pdev->dev, ret, + return dev_err_probe(dev, ret, "failed to register cpufreq cooling device\n"); - data->thermal_clk = devm_clk_get(&pdev->dev, NULL); + data->thermal_clk = devm_clk_get(dev, NULL); if (IS_ERR(data->thermal_clk)) { - ret = PTR_ERR(data->thermal_clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get thermal clk: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(data->thermal_clk), "failed to get thermal clk\n"); goto legacy_cleanup; } @@ -694,7 +675,7 @@ static int imx_thermal_probe(struct platform_device *pdev) */ ret = clk_prepare_enable(data->thermal_clk); if (ret) { - dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); + dev_err(dev, "failed to enable thermal clk: %d\n", ret); goto legacy_cleanup; } @@ -707,12 +688,12 @@ static int imx_thermal_probe(struct platform_device *pdev) IMX_POLLING_DELAY); if (IS_ERR(data->tz)) { ret = PTR_ERR(data->tz); - dev_err(&pdev->dev, - "failed to register thermal zone device %d\n", ret); + dev_err(dev, "failed to register thermal zone device %d\n", + ret); goto clk_disable; } - dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC" + dev_info(dev, "%s CPU temperature grade - max:%dC" " critical:%dC passive:%dC\n", data->temp_grade, data->temp_max / 1000, trips[IMX_TRIP_CRITICAL].temperature / 1000, trips[IMX_TRIP_PASSIVE].temperature / 1000); @@ -736,7 +717,7 @@ static int imx_thermal_probe(struct platform_device *pdev) usleep_range(20, 50); /* the core was configured and enabled just before */ - pm_runtime_set_active(&pdev->dev); + pm_runtime_set_active(dev); pm_runtime_enable(data->dev); ret = pm_runtime_resume_and_get(data->dev); @@ -748,11 +729,11 @@ static int imx_thermal_probe(struct platform_device *pdev) if (ret) goto thermal_zone_unregister; - ret = devm_request_threaded_irq(&pdev->dev, data->irq, + ret = devm_request_threaded_irq(dev, data->irq, imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread, 0, "imx_thermal", data); if (ret < 0) { - dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + dev_err(dev, "failed to request alarm irq: %d\n", ret); goto thermal_zone_unregister; } @@ -784,7 +765,7 @@ static void imx_thermal_remove(struct platform_device *pdev) imx_thermal_unregister_legacy_cooling(data); } -static int __maybe_unused imx_thermal_suspend(struct device *dev) +static int imx_thermal_suspend(struct device *dev) { struct imx_thermal_data *data = dev_get_drvdata(dev); int ret; @@ -803,7 +784,7 @@ static int __maybe_unused imx_thermal_suspend(struct device *dev) return pm_runtime_force_suspend(data->dev); } -static int __maybe_unused imx_thermal_resume(struct device *dev) +static int imx_thermal_resume(struct device *dev) { struct imx_thermal_data *data = dev_get_drvdata(dev); int ret; @@ -815,7 +796,7 @@ static int __maybe_unused imx_thermal_resume(struct device *dev) return thermal_zone_device_enable(data->tz); } -static int __maybe_unused imx_thermal_runtime_suspend(struct device *dev) +static int imx_thermal_runtime_suspend(struct device *dev) { struct imx_thermal_data *data = dev_get_drvdata(dev); const struct thermal_soc_data *socdata = data->socdata; @@ -837,7 +818,7 @@ static int __maybe_unused imx_thermal_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused imx_thermal_runtime_resume(struct device *dev) +static int imx_thermal_runtime_resume(struct device *dev) { struct imx_thermal_data *data = dev_get_drvdata(dev); const struct thermal_soc_data *socdata = data->socdata; @@ -868,15 +849,15 @@ static int __maybe_unused imx_thermal_runtime_resume(struct device *dev) } static const struct dev_pm_ops imx_thermal_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx_thermal_suspend, imx_thermal_resume) - SET_RUNTIME_PM_OPS(imx_thermal_runtime_suspend, - imx_thermal_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(imx_thermal_suspend, imx_thermal_resume) + RUNTIME_PM_OPS(imx_thermal_runtime_suspend, + imx_thermal_runtime_resume, NULL) }; static struct platform_driver imx_thermal = { .driver = { .name = "imx_thermal", - .pm = &imx_thermal_pm_ops, + .pm = pm_ptr(&imx_thermal_pm_ops), .of_match_table = of_imx_thermal_match, }, .probe = imx_thermal_probe, diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index 4b4a4d63e61f..cb149bcdd7d5 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -564,7 +564,6 @@ static const struct file_operations acpi_thermal_rel_fops = { .open = acpi_thermal_rel_open, .release = acpi_thermal_rel_release, .unlocked_ioctl = acpi_thermal_rel_ioctl, - .llseek = no_llseek, }; static struct miscdevice acpi_thermal_rel_misc_device = { diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index fa96972266e4..b0c0f0ffdcb0 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -571,7 +571,7 @@ static int int3400_thermal_probe(struct platform_device *pdev) if (!adev) return -ENODEV; - priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c index 86901f9f54d8..c094a422ded3 100644 --- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c @@ -25,17 +25,6 @@ struct int3403_sensor { struct int34x_thermal_zone *int340x_zone; }; -struct int3403_performance_state { - u64 performance; - u64 power; - u64 latency; - u64 linear; - u64 control; - u64 raw_performace; - char *raw_unit; - int reserved; -}; - struct int3403_cdev { struct thermal_cooling_device *cdev; unsigned long max_state; diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c index 400fde7cb3b1..31ed338eb83c 100644 --- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c @@ -39,13 +39,14 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone, } static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, - int trip, int temp) + const struct thermal_trip *trip, int temp) { struct int34x_thermal_zone *d = thermal_zone_device_priv(zone); - char name[] = {'P', 'A', 'T', '0' + trip, '\0'}; + unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); + char name[] = {'P', 'A', 'T', '0' + trip_index, '\0'}; acpi_status status; - if (trip > 9) + if (trip_index > 9) return -EINVAL; status = acpi_execute_simple_method(d->adev->handle, name, @@ -62,16 +63,6 @@ static void int340x_thermal_critical(struct thermal_zone_device *zone) thermal_zone_device_type(zone)); } -static inline void *int_to_trip_priv(int i) -{ - return (void *)(long)i; -} - -static inline int trip_priv_to_int(const struct thermal_trip *trip) -{ - return (long)trip->priv; -} - static int int340x_thermal_read_trips(struct acpi_device *zone_adev, struct thermal_trip *zone_trips, int trip_cnt) @@ -106,7 +97,7 @@ static int int340x_thermal_read_trips(struct acpi_device *zone_adev, break; zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE; - zone_trips[trip_cnt].priv = int_to_trip_priv(i); + zone_trips[trip_cnt].priv = THERMAL_INT_TO_TRIP_PRIV(i); trip_cnt++; } @@ -154,6 +145,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, zone_trips[i].type = THERMAL_TRIP_PASSIVE; zone_trips[i].temperature = THERMAL_TEMP_INVALID; zone_trips[i].flags |= THERMAL_TRIP_FLAG_RW_TEMP; + zone_trips[i].priv = THERMAL_INT_TO_TRIP_PRIV(i); } trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt); @@ -224,7 +216,7 @@ static int int340x_update_one_trip(struct thermal_trip *trip, void *arg) break; case THERMAL_TRIP_ACTIVE: err = thermal_acpi_active_trip_temp(zone_adev, - trip_priv_to_int(trip), + THERMAL_TRIP_PRIV_TO_INT(trip->priv), &temp); break; default: diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index d75fae7b7ed2..7c46dd6bee73 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -440,7 +440,8 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device * proc_thermal_rapl_remove(); if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR || - proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) + proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS || + proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) proc_thermal_rfim_remove(pdev); if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR) diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h index 674f3c85dfbc..d5eca6db2c00 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -65,6 +65,7 @@ struct rapl_mmio_regs { #define PROC_THERMAL_FEATURE_DLVR 0x10 #define PROC_THERMAL_FEATURE_WT_HINT 0x20 #define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40 +#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80 #if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL) int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c index 4a1bfebb1b8e..006614921870 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -63,6 +63,18 @@ static struct proc_thermal_mmio_info proc_thermal_mmio_info[] = { { PROC_THERMAL_MMIO_INT_STATUS_1, 0x7200, 8, 0x01 }, }; +/* List of supported MSI IDs (sources) */ +enum proc_thermal_msi_ids { + PKG_THERMAL, + DDR_THERMAL, + THERM_POWER_FLOOR, + WORKLOAD_CHANGE, + MSI_THERMAL_MAX +}; + +/* Stores IRQ associated with a MSI ID */ +static int proc_thermal_msi_map[MSI_THERMAL_MAX]; + #define B0D4_THERMAL_NOTIFY_DELAY 1000 static int notify_delay_ms = B0D4_THERMAL_NOTIFY_DELAY; @@ -146,22 +158,41 @@ static irqreturn_t proc_thermal_irq_thread_handler(int irq, void *devid) return IRQ_HANDLED; } +static int proc_thermal_match_msi_irq(int irq) +{ + int i; + + if (!use_msi) + goto msi_fail; + + for (i = 0; i < MSI_THERMAL_MAX; i++) { + if (proc_thermal_msi_map[i] == irq) + return i; + } + +msi_fail: + return -EOPNOTSUPP; +} + static irqreturn_t proc_thermal_irq_handler(int irq, void *devid) { struct proc_thermal_pci *pci_info = devid; struct proc_thermal_device *proc_priv; - int ret = IRQ_NONE; + int ret = IRQ_NONE, msi_id; u32 status; proc_priv = pci_info->proc_priv; + msi_id = proc_thermal_match_msi_irq(irq); + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_HINT) { - if (proc_thermal_check_wt_intr(pci_info->proc_priv)) + if (msi_id == WORKLOAD_CHANGE || proc_thermal_check_wt_intr(pci_info->proc_priv)) ret = IRQ_WAKE_THREAD; } if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR) { - if (proc_thermal_check_power_floor_intr(pci_info->proc_priv)) + if (msi_id == THERM_POWER_FLOOR || + proc_thermal_check_power_floor_intr(pci_info->proc_priv)) ret = IRQ_WAKE_THREAD; } @@ -171,7 +202,7 @@ static irqreturn_t proc_thermal_irq_handler(int irq, void *devid) * interrupt before scheduling work function for thermal threshold. */ proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status); - if (status) { + if (msi_id == PKG_THERMAL || status) { /* Disable enable interrupt flag */ proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); pkg_thermal_schedule_work(&pci_info->work); @@ -194,7 +225,8 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) return 0; } -static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) +static int sys_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, int temp) { struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd); int tjmax, _temp; @@ -244,6 +276,57 @@ static struct thermal_zone_params tzone_params = { .no_hwmon = true, }; +static bool msi_irq; + +static void proc_thermal_free_msi(struct pci_dev *pdev, struct proc_thermal_pci *pci_info) +{ + int i; + + for (i = 0; i < MSI_THERMAL_MAX; i++) { + if (proc_thermal_msi_map[i]) + devm_free_irq(&pdev->dev, proc_thermal_msi_map[i], pci_info); + } + + pci_free_irq_vectors(pdev); +} + +static int proc_thermal_setup_msi(struct pci_dev *pdev, struct proc_thermal_pci *pci_info) +{ + int ret, i, irq, count; + + count = pci_alloc_irq_vectors(pdev, 1, MSI_THERMAL_MAX, PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (count < 0) { + dev_err(&pdev->dev, "Failed to allocate vectors!\n"); + return count; + } + + dev_info(&pdev->dev, "msi enabled:%d msix enabled:%d\n", pdev->msi_enabled, + pdev->msix_enabled); + + for (i = 0; i < count; i++) { + irq = pci_irq_vector(pdev, i); + + ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler, + proc_thermal_irq_thread_handler, + 0, KBUILD_MODNAME, pci_info); + if (ret) { + dev_err(&pdev->dev, "Request IRQ %d failed\n", irq); + goto err_free_msi_vectors; + } + + proc_thermal_msi_map[i] = irq; + } + + msi_irq = true; + + return 0; + +err_free_msi_vectors: + proc_thermal_free_msi(pdev, pci_info); + + return ret; +} + static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct proc_thermal_device *proc_priv; @@ -253,7 +336,6 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_ .flags = THERMAL_TRIP_FLAG_RW_TEMP, }; int irq_flag = 0, irq, ret; - bool msi_irq = false; proc_priv = devm_kzalloc(&pdev->dev, sizeof(*proc_priv), GFP_KERNEL); if (!proc_priv) @@ -299,27 +381,24 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_ goto err_del_legacy; } - if (use_msi && (pdev->msi_enabled || pdev->msix_enabled)) { - /* request and enable interrupt */ - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to allocate vectors!\n"); - goto err_ret_tzone; - } + if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_MSI_SUPPORT) + use_msi = true; - irq = pci_irq_vector(pdev, 0); - msi_irq = true; + if (use_msi) { + ret = proc_thermal_setup_msi(pdev, pci_info); + if (ret) + goto err_ret_tzone; } else { irq_flag = IRQF_SHARED; irq = pdev->irq; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, - proc_thermal_irq_handler, proc_thermal_irq_thread_handler, - irq_flag, KBUILD_MODNAME, pci_info); - if (ret) { - dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq); - goto err_free_vectors; + ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler, + proc_thermal_irq_thread_handler, irq_flag, + KBUILD_MODNAME, pci_info); + if (ret) { + dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq); + goto err_ret_tzone; + } } ret = thermal_zone_device_enable(pci_info->tzone); @@ -330,7 +409,7 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_ err_free_vectors: if (msi_irq) - pci_free_irq_vectors(pdev); + proc_thermal_free_msi(pdev, pci_info); err_ret_tzone: thermal_zone_device_unregister(pci_info->tzone); err_del_legacy: @@ -352,8 +431,8 @@ static void proc_thermal_pci_remove(struct pci_dev *pdev) proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); - devm_free_irq(&pdev->dev, pdev->irq, pci_info); - pci_free_irq_vectors(pdev); + if (msi_irq) + proc_thermal_free_msi(pdev, pci_info); thermal_zone_device_unregister(pci_info->tzone); proc_thermal_mmio_remove(pdev, pci_info->proc_priv); @@ -408,7 +487,9 @@ static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, proc_thermal_pci_suspend, static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) }, - { PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_RAPL) }, + { PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT | + PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | + PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) }, { PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) }, diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c index e56db75a94fb..0e2dc1426282 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -19,6 +19,12 @@ struct mmio_reg { u16 shift; }; +struct mapping_table { + const char *attr_name; + const u32 value; + const char *mapped_str; +}; + /* These will represent sysfs attribute names */ static const char * const fivr_strings[] = { "vco_ref_code_lo", @@ -62,6 +68,78 @@ static const struct mmio_reg dlvr_mmio_regs[] = { { 1, 0x15A10, 1, 0x1, 16}, /* dlvr_pll_busy */ }; +static const struct mmio_reg lnl_dlvr_mmio_regs[] = { + { 0, 0x5A08, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */ + { 0, 0x5A08, 1, 0x1, 5}, /* dlvr_control_mode */ + { 0, 0x5A08, 1, 0x1, 6}, /* dlvr_control_lock */ + { 0, 0x5A08, 1, 0x1, 7}, /* dlvr_rfim_enable */ + { 0, 0x5A08, 2, 0x3, 8}, /* dlvr_freq_select */ + { 1, 0x5A10, 2, 0x3, 30}, /* dlvr_hardware_rev */ + { 1, 0x5A10, 2, 0x3, 0}, /* dlvr_freq_mhz */ + { 1, 0x5A10, 1, 0x1, 23}, /* dlvr_pll_busy */ +}; + +static const struct mapping_table lnl_dlvr_mapping[] = { + {"dlvr_freq_select", 0, "2227.2"}, + {"dlvr_freq_select", 1, "2140"}, + {"dlvr_freq_mhz", 0, "2227.2"}, + {"dlvr_freq_mhz", 1, "2140"}, + {NULL, 0, NULL}, +}; + +static int match_mapping_table(const struct mapping_table *table, const char *attr_name, + bool match_int_value, const u32 value, const char *value_str, + char **result_str, u32 *result_int) +{ + bool attr_matched = false; + int i = 0; + + if (!table) + return -EOPNOTSUPP; + + while (table[i].attr_name) { + if (strncmp(table[i].attr_name, attr_name, strlen(attr_name))) + goto match_next; + + attr_matched = true; + + if (match_int_value) { + if (table[i].value != value) + goto match_next; + + *result_str = (char *)table[i].mapped_str; + return 0; + } + + if (strncmp(table[i].mapped_str, value_str, strlen(table[i].mapped_str))) + goto match_next; + + *result_int = table[i].value; + + return 0; +match_next: + i++; + } + + /* If attribute name is matched, then the user space value is invalid */ + if (attr_matched) + return -EINVAL; + + return -EOPNOTSUPP; +} + +static int get_mapped_string(const struct mapping_table *table, const char *attr_name, + u32 value, char **result) +{ + return match_mapping_table(table, attr_name, true, value, NULL, result, NULL); +} + +static int get_mapped_value(const struct mapping_table *table, const char *attr_name, + const char *value, unsigned int *result) +{ + return match_mapping_table(table, attr_name, false, 0, value, NULL, result); +} + /* These will represent sysfs attribute names */ static const char * const dvfs_strings[] = { "rfi_restriction_run_busy", @@ -93,12 +171,14 @@ static ssize_t suffix##_show(struct device *dev,\ struct device_attribute *attr,\ char *buf)\ {\ + const struct mapping_table *mapping = NULL;\ struct proc_thermal_device *proc_priv;\ struct pci_dev *pdev = to_pci_dev(dev);\ const struct mmio_reg *mmio_regs;\ const char **match_strs;\ + int ret, err;\ u32 reg_val;\ - int ret;\ + char *str;\ \ proc_priv = pci_get_drvdata(pdev);\ if (table == 1) {\ @@ -106,7 +186,12 @@ static ssize_t suffix##_show(struct device *dev,\ mmio_regs = adl_dvfs_mmio_regs;\ } else if (table == 2) { \ match_strs = (const char **)dlvr_strings;\ - mmio_regs = dlvr_mmio_regs;\ + if (pdev->device == PCI_DEVICE_ID_INTEL_LNLM_THERMAL) {\ + mmio_regs = lnl_dlvr_mmio_regs;\ + mapping = lnl_dlvr_mapping;\ + } else {\ + mmio_regs = dlvr_mmio_regs;\ + } \ } else {\ match_strs = (const char **)fivr_strings;\ mmio_regs = tgl_fivr_mmio_regs;\ @@ -116,7 +201,12 @@ static ssize_t suffix##_show(struct device *dev,\ 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);\ + err = get_mapped_string(mapping, attr->attr.name, ret, &str);\ + if (!err)\ + return sprintf(buf, "%s\n", str);\ + if (err == -EOPNOTSUPP)\ + return sprintf(buf, "%u\n", ret);\ + return err;\ } #define RFIM_STORE(suffix, table)\ @@ -124,6 +214,7 @@ static ssize_t suffix##_store(struct device *dev,\ struct device_attribute *attr,\ const char *buf, size_t count)\ {\ + const struct mapping_table *mapping = NULL;\ struct proc_thermal_device *proc_priv;\ struct pci_dev *pdev = to_pci_dev(dev);\ unsigned int input;\ @@ -139,7 +230,12 @@ static ssize_t suffix##_store(struct device *dev,\ mmio_regs = adl_dvfs_mmio_regs;\ } else if (table == 2) { \ match_strs = (const char **)dlvr_strings;\ - mmio_regs = dlvr_mmio_regs;\ + if (pdev->device == PCI_DEVICE_ID_INTEL_LNLM_THERMAL) {\ + mmio_regs = lnl_dlvr_mmio_regs;\ + mapping = lnl_dlvr_mapping;\ + } else {\ + mmio_regs = dlvr_mmio_regs;\ + } \ } else {\ match_strs = (const char **)fivr_strings;\ mmio_regs = tgl_fivr_mmio_regs;\ @@ -150,9 +246,14 @@ static ssize_t suffix##_store(struct device *dev,\ return ret;\ if (mmio_regs[ret].read_only)\ return -EPERM;\ - err = kstrtouint(buf, 10, &input);\ - if (err)\ + err = get_mapped_value(mapping, attr->attr.name, buf, &input);\ + if (err == -EINVAL)\ return err;\ + if (err == -EOPNOTSUPP) {\ + 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;\ diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index a180a98bb9f1..5b18a46a10b0 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -401,10 +401,10 @@ static void hfi_disable(void) * intel_hfi_online() - Enable HFI on @cpu * @cpu: CPU in which the HFI will be enabled * - * Enable the HFI to be used in @cpu. The HFI is enabled at the die/package - * level. The first CPU in the die/package to come online does the full HFI + * Enable the HFI to be used in @cpu. The HFI is enabled at the package + * level. The first CPU in the package to come online does the full HFI * initialization. Subsequent CPUs will just link themselves to the HFI - * instance of their die/package. + * instance of their package. * * This function is called before enabling the thermal vector in the local APIC * in order to ensure that @cpu has an associated HFI instance when it receives @@ -414,31 +414,31 @@ void intel_hfi_online(unsigned int cpu) { struct hfi_instance *hfi_instance; struct hfi_cpu_info *info; - u16 die_id; + u16 pkg_id; /* Nothing to do if hfi_instances are missing. */ if (!hfi_instances) return; /* - * Link @cpu to the HFI instance of its package/die. It does not + * Link @cpu to the HFI instance of its package. It does not * matter whether the instance has been initialized. */ info = &per_cpu(hfi_cpu_info, cpu); - die_id = topology_logical_die_id(cpu); + pkg_id = topology_logical_package_id(cpu); hfi_instance = info->hfi_instance; if (!hfi_instance) { - if (die_id >= max_hfi_instances) + if (pkg_id >= max_hfi_instances) return; - hfi_instance = &hfi_instances[die_id]; + hfi_instance = &hfi_instances[pkg_id]; info->hfi_instance = hfi_instance; } init_hfi_cpu_index(info); /* - * Now check if the HFI instance of the package/die of @cpu has been + * Now check if the HFI instance of the package of @cpu has been * initialized (by checking its header). In such case, all we have to * do is to add @cpu to this instance's cpumask and enable the instance * if needed. @@ -504,7 +504,7 @@ free_hw_table: * * On some processors, hardware remembers previous programming settings even * after being reprogrammed. Thus, keep HFI enabled even if all CPUs in the - * die/package of @cpu are offline. See note in intel_hfi_online(). + * package of @cpu are offline. See note in intel_hfi_online(). */ void intel_hfi_offline(unsigned int cpu) { @@ -674,9 +674,13 @@ void __init intel_hfi_init(void) if (hfi_parse_features()) return; - /* There is one HFI instance per die/package. */ - max_hfi_instances = topology_max_packages() * - topology_max_dies_per_package(); + /* + * Note: HFI resources are managed at the physical package scope. + * There could be platforms that enumerate packages as Linux dies. + * Special handling would be needed if this happens on an HFI-capable + * platform. + */ + max_hfi_instances = topology_max_packages(); /* * This allocation may fail. CPU hotplug callbacks must check diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c index f5be2c389351..fc326985796c 100644 --- a/drivers/thermal/intel/intel_pch_thermal.c +++ b/drivers/thermal/intel/intel_pch_thermal.c @@ -298,6 +298,11 @@ static int intel_pch_thermal_suspend_noirq(struct device *device) /* Get the PCH current temperature value */ pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP)); + if (pch_cur_temp >= pch_thr_temp) + dev_warn(&ptd->pdev->dev, + "CPU-PCH current temp [%dC] higher than the threshold temp [%dC], S0ix might fail. Start cooling...\n", + pch_cur_temp, pch_thr_temp); + /* * If current PCH temperature is higher than configured PCH threshold * value, run some delay loop with sleep to let the current temperature diff --git a/drivers/thermal/intel/intel_quark_dts_thermal.c b/drivers/thermal/intel/intel_quark_dts_thermal.c index ec6ad26027bc..47296a14db3c 100644 --- a/drivers/thermal/intel/intel_quark_dts_thermal.c +++ b/drivers/thermal/intel/intel_quark_dts_thermal.c @@ -195,7 +195,7 @@ static int get_trip_temp(int trip) } static int update_trip_temp(struct soc_sensor_entry *aux_entry, - int trip, int temp) + int trip_index, int temp) { u32 out; u32 temp_out; @@ -230,9 +230,9 @@ static int update_trip_temp(struct soc_sensor_entry *aux_entry, */ temp_out = temp + QRK_DTS_TEMP_BASE; out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES << - (trip * QRK_DTS_SHIFT_TP))); + (trip_index * QRK_DTS_SHIFT_TP))); out |= (temp_out & QRK_DTS_MASK_TP_THRES) << - (trip * QRK_DTS_SHIFT_TP); + (trip_index * QRK_DTS_SHIFT_TP); ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE, QRK_DTS_REG_OFFSET_PTPS, out); @@ -242,10 +242,26 @@ failed: return ret; } -static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, - int temp) +static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, + int temp) { - return update_trip_temp(thermal_zone_device_priv(tzd), trip, temp); + unsigned int trip_index; + + switch (trip->type) { + case THERMAL_TRIP_HOT: + trip_index = QRK_DTS_ID_TP_HOT; + break; + + case THERMAL_TRIP_CRITICAL: + trip_index = QRK_DTS_ID_TP_CRITICAL; + break; + + default: + return -EINVAL; + } + + return update_trip_temp(thermal_zone_device_priv(tzd), trip_index, temp); } static int sys_get_curr_temp(struct thermal_zone_device *tzd, diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c index 7adf942665d4..43a29551ba17 100644 --- a/drivers/thermal/intel/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel/intel_soc_dts_iosf.c @@ -129,18 +129,20 @@ err_restore_ptps: return status; } -static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, +static int sys_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, int temp) { struct intel_soc_dts_sensor_entry *dts = thermal_zone_device_priv(tzd); struct intel_soc_dts_sensors *sensors = dts->sensors; + unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); int status; if (temp > sensors->tj_max) return -EINVAL; mutex_lock(&sensors->dts_update_lock); - status = update_trip_temp(sensors, trip, temp); + status = update_trip_temp(sensors, trip_index, temp); mutex_unlock(&sensors->dts_update_lock); return status; @@ -293,11 +295,12 @@ static void dts_trips_reset(struct intel_soc_dts_sensors *sensors, int dts_index } static void set_trip(struct thermal_trip *trip, enum thermal_trip_type type, - u8 flags, int temp) + u8 flags, int temp, unsigned int index) { trip->type = type; trip->flags = flags; trip->temperature = temp; + trip->priv = THERMAL_INT_TO_TRIP_PRIV(index); } struct intel_soc_dts_sensors * @@ -332,7 +335,7 @@ intel_soc_dts_iosf_init(enum intel_soc_dts_interrupt_type intr_type, sensors->soc_dts[i].sensors = sensors; set_trip(&trips[i][0], THERMAL_TRIP_PASSIVE, - THERMAL_TRIP_FLAG_RW_TEMP, 0); + THERMAL_TRIP_FLAG_RW_TEMP, 0, 0); ret = update_trip_temp(sensors, 0, 0); if (ret) @@ -340,10 +343,10 @@ intel_soc_dts_iosf_init(enum intel_soc_dts_interrupt_type intr_type, if (critical_trip) { temp = sensors->tj_max - crit_offset; - set_trip(&trips[i][1], THERMAL_TRIP_CRITICAL, 0, temp); + set_trip(&trips[i][1], THERMAL_TRIP_CRITICAL, 0, temp, 1); } else { set_trip(&trips[i][1], THERMAL_TRIP_PASSIVE, - THERMAL_TRIP_FLAG_RW_TEMP, 0); + THERMAL_TRIP_FLAG_RW_TEMP, 0, 1); temp = 0; } diff --git a/drivers/thermal/intel/intel_soc_dts_thermal.c b/drivers/thermal/intel/intel_soc_dts_thermal.c index 9c825c6e1f38..718c6326eaf4 100644 --- a/drivers/thermal/intel/intel_soc_dts_thermal.c +++ b/drivers/thermal/intel/intel_soc_dts_thermal.c @@ -36,7 +36,7 @@ static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data) } static const struct x86_cpu_id soc_thermal_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, BYT_SOC_DTS_APIC_IRQ), + X86_MATCH_VFM(INTEL_ATOM_SILVERMONT, BYT_SOC_DTS_APIC_IRQ), {} }; MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids); diff --git a/drivers/thermal/intel/intel_tcc.c b/drivers/thermal/intel/intel_tcc.c index 5e8b7f34b395..c86654f28aa5 100644 --- a/drivers/thermal/intel/intel_tcc.c +++ b/drivers/thermal/intel/intel_tcc.c @@ -6,9 +6,171 @@ #include <linux/errno.h> #include <linux/intel_tcc.h> +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/msr.h> /** + * struct temp_masks - Bitmasks for temperature readings + * @tcc_offset: TCC offset in MSR_TEMPERATURE_TARGET + * @digital_readout: Digital readout in MSR_IA32_THERM_STATUS + * @pkg_digital_readout: Digital readout in MSR_IA32_PACKAGE_THERM_STATUS + * + * Bitmasks to extract the fields of the MSR_TEMPERATURE and IA32_[PACKAGE]_ + * THERM_STATUS registers for different processor models. + * + * The bitmask of TjMax is not included in this structure. It is always 0xff. + */ +struct temp_masks { + u32 tcc_offset; + u32 digital_readout; + u32 pkg_digital_readout; +}; + +#define TCC_MODEL_TEMP_MASKS(model, _tcc_offset, _digital_readout, \ + _pkg_digital_readout) \ + static const struct temp_masks temp_##model __initconst = { \ + .tcc_offset = _tcc_offset, \ + .digital_readout = _digital_readout, \ + .pkg_digital_readout = _pkg_digital_readout \ + } + +TCC_MODEL_TEMP_MASKS(nehalem, 0, 0x7f, 0x7f); +TCC_MODEL_TEMP_MASKS(haswell_x, 0xf, 0x7f, 0x7f); +TCC_MODEL_TEMP_MASKS(broadwell, 0x3f, 0x7f, 0x7f); +TCC_MODEL_TEMP_MASKS(goldmont, 0x7f, 0x7f, 0x7f); +TCC_MODEL_TEMP_MASKS(tigerlake, 0x3f, 0xff, 0xff); +TCC_MODEL_TEMP_MASKS(sapphirerapids, 0x3f, 0x7f, 0xff); + +/* Use these masks for processors not included in @tcc_cpu_ids. */ +static struct temp_masks intel_tcc_temp_masks __ro_after_init = { + .tcc_offset = 0x7f, + .digital_readout = 0xff, + .pkg_digital_readout = 0xff, +}; + +static const struct x86_cpu_id intel_tcc_cpu_ids[] __initconst = { + X86_MATCH_VFM(INTEL_CORE_YONAH, &temp_nehalem), + X86_MATCH_VFM(INTEL_CORE2_MEROM, &temp_nehalem), + X86_MATCH_VFM(INTEL_CORE2_MEROM_L, &temp_nehalem), + X86_MATCH_VFM(INTEL_CORE2_PENRYN, &temp_nehalem), + X86_MATCH_VFM(INTEL_CORE2_DUNNINGTON, &temp_nehalem), + X86_MATCH_VFM(INTEL_NEHALEM, &temp_nehalem), + X86_MATCH_VFM(INTEL_NEHALEM_G, &temp_nehalem), + X86_MATCH_VFM(INTEL_NEHALEM_EP, &temp_nehalem), + X86_MATCH_VFM(INTEL_NEHALEM_EX, &temp_nehalem), + X86_MATCH_VFM(INTEL_WESTMERE, &temp_nehalem), + X86_MATCH_VFM(INTEL_WESTMERE_EP, &temp_nehalem), + X86_MATCH_VFM(INTEL_WESTMERE_EX, &temp_nehalem), + X86_MATCH_VFM(INTEL_SANDYBRIDGE, &temp_nehalem), + X86_MATCH_VFM(INTEL_SANDYBRIDGE_X, &temp_nehalem), + X86_MATCH_VFM(INTEL_IVYBRIDGE, &temp_nehalem), + X86_MATCH_VFM(INTEL_IVYBRIDGE_X, &temp_haswell_x), + X86_MATCH_VFM(INTEL_HASWELL, &temp_nehalem), + X86_MATCH_VFM(INTEL_HASWELL_X, &temp_haswell_x), + X86_MATCH_VFM(INTEL_HASWELL_L, &temp_nehalem), + X86_MATCH_VFM(INTEL_HASWELL_G, &temp_nehalem), + X86_MATCH_VFM(INTEL_BROADWELL, &temp_broadwell), + X86_MATCH_VFM(INTEL_BROADWELL_G, &temp_broadwell), + X86_MATCH_VFM(INTEL_BROADWELL_X, &temp_haswell_x), + X86_MATCH_VFM(INTEL_BROADWELL_D, &temp_haswell_x), + X86_MATCH_VFM(INTEL_SKYLAKE_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_SKYLAKE, &temp_broadwell), + X86_MATCH_VFM(INTEL_SKYLAKE_X, &temp_haswell_x), + X86_MATCH_VFM(INTEL_KABYLAKE_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_KABYLAKE, &temp_broadwell), + X86_MATCH_VFM(INTEL_COMETLAKE, &temp_broadwell), + X86_MATCH_VFM(INTEL_COMETLAKE_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_CANNONLAKE_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_ICELAKE_X, &temp_broadwell), + X86_MATCH_VFM(INTEL_ICELAKE_D, &temp_broadwell), + X86_MATCH_VFM(INTEL_ICELAKE, &temp_broadwell), + X86_MATCH_VFM(INTEL_ICELAKE_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_ICELAKE_NNPI, &temp_broadwell), + X86_MATCH_VFM(INTEL_ROCKETLAKE, &temp_broadwell), + X86_MATCH_VFM(INTEL_TIGERLAKE_L, &temp_tigerlake), + X86_MATCH_VFM(INTEL_TIGERLAKE, &temp_tigerlake), + X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, &temp_sapphirerapids), + X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &temp_sapphirerapids), + X86_MATCH_VFM(INTEL_LAKEFIELD, &temp_broadwell), + X86_MATCH_VFM(INTEL_ALDERLAKE, &temp_tigerlake), + X86_MATCH_VFM(INTEL_ALDERLAKE_L, &temp_tigerlake), + X86_MATCH_VFM(INTEL_RAPTORLAKE, &temp_tigerlake), + X86_MATCH_VFM(INTEL_RAPTORLAKE_P, &temp_tigerlake), + X86_MATCH_VFM(INTEL_RAPTORLAKE_S, &temp_tigerlake), + X86_MATCH_VFM(INTEL_ATOM_BONNELL, &temp_nehalem), + X86_MATCH_VFM(INTEL_ATOM_BONNELL_MID, &temp_nehalem), + X86_MATCH_VFM(INTEL_ATOM_SALTWELL, &temp_nehalem), + X86_MATCH_VFM(INTEL_ATOM_SALTWELL_MID, &temp_nehalem), + X86_MATCH_VFM(INTEL_ATOM_SILVERMONT, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_D, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_AIRMONT, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_AIRMONT_MID, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_AIRMONT_NP, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &temp_goldmont), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_D, &temp_goldmont), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, &temp_goldmont), + X86_MATCH_VFM(INTEL_ATOM_TREMONT_D, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_TREMONT, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_TREMONT_L, &temp_broadwell), + X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &temp_tigerlake), + X86_MATCH_VFM(INTEL_XEON_PHI_KNL, &temp_broadwell), + X86_MATCH_VFM(INTEL_XEON_PHI_KNM, &temp_broadwell), + {} +}; + +static int __init intel_tcc_init(void) +{ + const struct x86_cpu_id *id; + + id = x86_match_cpu(intel_tcc_cpu_ids); + if (id) + memcpy(&intel_tcc_temp_masks, (const void *)id->driver_data, + sizeof(intel_tcc_temp_masks)); + + return 0; +} +/* + * Use subsys_initcall to ensure temperature bitmasks are initialized before + * the drivers that use this library. + */ +subsys_initcall(intel_tcc_init); + +/** + * intel_tcc_get_offset_mask() - Returns the bitmask to read TCC offset + * + * Get the model-specific bitmask to extract TCC_OFFSET from the MSR + * TEMPERATURE_TARGET register. If the mask is 0, it means the processor does + * not support TCC offset. + * + * Return: The model-specific bitmask for TCC offset. + */ +u32 intel_tcc_get_offset_mask(void) +{ + return intel_tcc_temp_masks.tcc_offset; +} +EXPORT_SYMBOL_NS(intel_tcc_get_offset_mask, INTEL_TCC); + +/** + * get_temp_mask() - Returns the model-specific bitmask for temperature + * + * @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor. + * + * Get the model-specific bitmask to extract the temperature reading from the + * MSR_IA32_[PACKAGE]_THERM_STATUS register. + * + * Callers must check if the thermal status registers are supported. + * + * Return: The model-specific bitmask for temperature reading + */ +static u32 get_temp_mask(bool pkg) +{ + return pkg ? intel_tcc_temp_masks.pkg_digital_readout : + intel_tcc_temp_masks.digital_readout; +} + +/** * intel_tcc_get_tjmax() - returns the default TCC activation Temperature * @cpu: cpu that the MSR should be run on, nagative value means any cpu. * @@ -56,7 +218,7 @@ int intel_tcc_get_offset(int cpu) if (err) return err; - return (low >> 24) & 0x3f; + return (low >> 24) & intel_tcc_temp_masks.tcc_offset; } EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, INTEL_TCC); @@ -76,7 +238,10 @@ int intel_tcc_set_offset(int cpu, int offset) u32 low, high; int err; - if (offset < 0 || offset > 0x3f) + if (!intel_tcc_temp_masks.tcc_offset) + return -ENODEV; + + if (offset < 0 || offset > intel_tcc_temp_masks.tcc_offset) return -EINVAL; if (cpu < 0) @@ -90,7 +255,7 @@ int intel_tcc_set_offset(int cpu, int offset) if (low & BIT(31)) return -EPERM; - low &= ~(0x3f << 24); + low &= ~(intel_tcc_temp_masks.tcc_offset << 24); low |= offset << 24; if (cpu < 0) @@ -113,8 +278,8 @@ EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, INTEL_TCC); */ int intel_tcc_get_temp(int cpu, int *temp, bool pkg) { - u32 low, high; u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS; + u32 low, high, mask; int tjmax, err; tjmax = intel_tcc_get_tjmax(cpu); @@ -132,7 +297,9 @@ int intel_tcc_get_temp(int cpu, int *temp, bool pkg) if (!(low & BIT(31))) return -ENODATA; - *temp = tjmax - ((low >> 16) & 0x7f); + mask = get_temp_mask(pkg); + + *temp = tjmax - ((low >> 16) & mask); return 0; } diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c index 6c392147e6d1..17110ffa80bb 100644 --- a/drivers/thermal/intel/intel_tcc_cooling.c +++ b/drivers/thermal/intel/intel_tcc_cooling.c @@ -20,7 +20,7 @@ static struct thermal_cooling_device *tcc_cdev; static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = 0x3f; + *state = intel_tcc_get_offset_mask(); return 0; } @@ -49,21 +49,21 @@ static const struct thermal_cooling_device_ops tcc_cooling_ops = { }; static const struct x86_cpu_id tcc_ids[] __initconst = { - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, NULL), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), + X86_MATCH_VFM(INTEL_SKYLAKE, NULL), + X86_MATCH_VFM(INTEL_SKYLAKE_L, NULL), + X86_MATCH_VFM(INTEL_KABYLAKE, NULL), + X86_MATCH_VFM(INTEL_KABYLAKE_L, NULL), + X86_MATCH_VFM(INTEL_ICELAKE, NULL), + X86_MATCH_VFM(INTEL_ICELAKE_L, NULL), + X86_MATCH_VFM(INTEL_TIGERLAKE, NULL), + X86_MATCH_VFM(INTEL_TIGERLAKE_L, NULL), + X86_MATCH_VFM(INTEL_COMETLAKE, NULL), + X86_MATCH_VFM(INTEL_ALDERLAKE, NULL), + X86_MATCH_VFM(INTEL_ALDERLAKE_L, NULL), + X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL), {} }; diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c index c0ca8e3ff2e7..65b33b56a9be 100644 --- a/drivers/thermal/intel/x86_pkg_temp_thermal.c +++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c @@ -119,9 +119,11 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) } static int -sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) +sys_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, int temp) { struct zone_device *zonedev = thermal_zone_device_priv(tzd); + unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); u32 l, h, mask, shift, intr; int tj_max, val, ret; @@ -132,7 +134,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) val = (tj_max - temp)/1000; - if (trip >= MAX_NUMBER_OF_TRIPS || val < 0 || val > 0x7f) + if (trip_index >= MAX_NUMBER_OF_TRIPS || val < 0 || val > 0x7f) return -EINVAL; ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, @@ -140,7 +142,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) if (ret < 0) return ret; - if (trip) { + if (trip_index) { mask = THERM_MASK_THRESHOLD1; shift = THERM_SHIFT_THRESHOLD1; intr = THERM_INT_THRESHOLD1_ENABLE; @@ -296,6 +298,7 @@ static int pkg_temp_thermal_trips_init(int cpu, int tj_max, trips[i].type = THERMAL_TRIP_PASSIVE; trips[i].flags |= THERMAL_TRIP_FLAG_RW_TEMP; + trips[i].priv = THERMAL_INT_TO_TRIP_PRIV(i); pr_debug("%s: cpu=%d, trip=%d, temp=%d\n", __func__, cpu, i, trips[i].temperature); diff --git a/drivers/thermal/k3_j72xx_bandgap.c b/drivers/thermal/k3_j72xx_bandgap.c index c74094a86982..9bc279ac131a 100644 --- a/drivers/thermal/k3_j72xx_bandgap.c +++ b/drivers/thermal/k3_j72xx_bandgap.c @@ -178,6 +178,7 @@ struct k3_j72xx_bandgap { void __iomem *base; void __iomem *cfg2_base; struct k3_thermal_data *ts_data[K3_VTM_MAX_NUM_TS]; + int cnt; }; /* common data structures */ @@ -338,24 +339,52 @@ static void print_look_up_table(struct device *dev, int *ref_table) dev_dbg(dev, "%d %d %d\n", i, derived_table[i], ref_table[i]); } +static void k3_j72xx_bandgap_init_hw(struct k3_j72xx_bandgap *bgp) +{ + struct k3_thermal_data *data; + int id, high_max, low_temp; + u32 val; + + for (id = 0; id < bgp->cnt; id++) { + data = bgp->ts_data[id]; + val = readl(bgp->cfg2_base + data->ctrl_offset); + val |= (K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN | + K3_VTM_TMPSENS_CTRL_SOC | + K3_VTM_TMPSENS_CTRL_CLRZ | BIT(4)); + writel(val, bgp->cfg2_base + data->ctrl_offset); + } + + /* + * Program TSHUT thresholds + * Step 1: set the thresholds to ~123C and 105C WKUP_VTM_MISC_CTRL2 + * Step 2: WKUP_VTM_TMPSENS_CTRL_j set the MAXT_OUTRG_EN bit + * This is already taken care as per of init + * Step 3: WKUP_VTM_MISC_CTRL set the ANYMAXT_OUTRG_ALERT_EN bit + */ + high_max = k3_j72xx_bandgap_temp_to_adc_code(MAX_TEMP); + low_temp = k3_j72xx_bandgap_temp_to_adc_code(COOL_DOWN_TEMP); + + writel((low_temp << 16) | high_max, bgp->cfg2_base + K3_VTM_MISC_CTRL2_OFFSET); + writel(K3_VTM_ANYMAXT_OUTRG_ALERT_EN, bgp->cfg2_base + K3_VTM_MISC_CTRL_OFFSET); +} + struct k3_j72xx_bandgap_data { const bool has_errata_i2128; }; static int k3_j72xx_bandgap_probe(struct platform_device *pdev) { - int ret = 0, cnt, val, id; - int high_max, low_temp; - struct resource *res; + const struct k3_j72xx_bandgap_data *driver_data; + struct thermal_zone_device *ti_thermal; struct device *dev = &pdev->dev; + bool workaround_needed = false; struct k3_j72xx_bandgap *bgp; struct k3_thermal_data *data; - bool workaround_needed = false; - const struct k3_j72xx_bandgap_data *driver_data; - struct thermal_zone_device *ti_thermal; - int *ref_table; struct err_values err_vals; void __iomem *fuse_base; + int ret = 0, val, id; + struct resource *res; + int *ref_table; const s64 golden_factors[] = { -490019999999999936, @@ -422,10 +451,10 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev) /* Get the sensor count in the VTM */ val = readl(bgp->base + K3_VTM_DEVINFO_PWR0_OFFSET); - cnt = val & K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK; - cnt >>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK); + bgp->cnt = val & K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK; + bgp->cnt >>= __ffs(K3_VTM_DEVINFO_PWR0_TEMPSENS_CT_MASK); - data = devm_kcalloc(bgp->dev, cnt, sizeof(*data), GFP_KERNEL); + data = devm_kcalloc(bgp->dev, bgp->cnt, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; goto err_alloc; @@ -449,8 +478,8 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev) else init_table(3, ref_table, pvt_wa_factors); - /* Register the thermal sensors */ - for (id = 0; id < cnt; id++) { + /* Precompute the derived table & fill each thermal sensor struct */ + for (id = 0; id < bgp->cnt; id++) { data[id].bgp = bgp; data[id].ctrl_offset = K3_VTM_TMPSENS0_CTRL_OFFSET + id * 0x20; data[id].stat_offset = data[id].ctrl_offset + @@ -470,13 +499,13 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev) else if (id == 0 && !workaround_needed) memcpy(derived_table, ref_table, TABLE_SIZE * 4); - val = readl(data[id].bgp->cfg2_base + data[id].ctrl_offset); - val |= (K3_VTM_TMPSENS_CTRL_MAXT_OUTRG_EN | - K3_VTM_TMPSENS_CTRL_SOC | - K3_VTM_TMPSENS_CTRL_CLRZ | BIT(4)); - writel(val, data[id].bgp->cfg2_base + data[id].ctrl_offset); - bgp->ts_data[id] = &data[id]; + } + + k3_j72xx_bandgap_init_hw(bgp); + + /* Register the thermal sensors */ + for (id = 0; id < bgp->cnt; id++) { ti_thermal = devm_thermal_of_zone_register(bgp->dev, id, &data[id], &k3_of_thermal_ops); if (IS_ERR(ti_thermal)) { @@ -486,21 +515,7 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev) } } - /* - * Program TSHUT thresholds - * Step 1: set the thresholds to ~123C and 105C WKUP_VTM_MISC_CTRL2 - * Step 2: WKUP_VTM_TMPSENS_CTRL_j set the MAXT_OUTRG_EN bit - * This is already taken care as per of init - * Step 3: WKUP_VTM_MISC_CTRL set the ANYMAXT_OUTRG_ALERT_EN bit - */ - high_max = k3_j72xx_bandgap_temp_to_adc_code(MAX_TEMP); - low_temp = k3_j72xx_bandgap_temp_to_adc_code(COOL_DOWN_TEMP); - - writel((low_temp << 16) | high_max, data[0].bgp->cfg2_base + - K3_VTM_MISC_CTRL2_OFFSET); - mdelay(100); - writel(K3_VTM_ANYMAXT_OUTRG_ALERT_EN, data[0].bgp->cfg2_base + - K3_VTM_MISC_CTRL_OFFSET); + platform_set_drvdata(pdev, bgp); print_look_up_table(dev, ref_table); /* @@ -527,6 +542,35 @@ static void k3_j72xx_bandgap_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } +static int k3_j72xx_bandgap_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return 0; +} + +static int k3_j72xx_bandgap_resume(struct device *dev) +{ + struct k3_j72xx_bandgap *bgp = dev_get_drvdata(dev); + int ret; + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + return ret; + } + + k3_j72xx_bandgap_init_hw(bgp); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(k3_j72xx_bandgap_pm_ops, + k3_j72xx_bandgap_suspend, + k3_j72xx_bandgap_resume); + static const struct k3_j72xx_bandgap_data k3_j72xx_bandgap_j721e_data = { .has_errata_i2128 = true, }; @@ -554,6 +598,7 @@ static struct platform_driver k3_j72xx_bandgap_sensor_driver = { .driver = { .name = "k3-j72xx-soc-thermal", .of_match_table = of_k3_j72xx_bandgap_match, + .pm = pm_sleep_ptr(&k3_j72xx_bandgap_pm_ops), }, }; diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 819ed0110f3e..1997e91bb3be 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -128,6 +128,7 @@ struct lvts_data { int temp_factor; int temp_offset; int gt_calib_bit_offset; + unsigned int def_calibration; }; struct lvts_sensor { @@ -689,6 +690,10 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl size_t calib_len) { int i; + u32 gt; + + /* A zero value for gt means that device has invalid efuse data */ + gt = (((u32 *)efuse_calibration)[0] >> lvts_ctrl->lvts_data->gt_calib_bit_offset) & 0xff; lvts_for_each_valid_sensor(i, lvts_ctrl_data) { const struct lvts_sensor_data *sensor = @@ -699,10 +704,17 @@ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl sensor->cal_offsets[2] >= calib_len) return -EINVAL; - lvts_ctrl->calibration[i] = - (efuse_calibration[sensor->cal_offsets[0]] << 0) + - (efuse_calibration[sensor->cal_offsets[1]] << 8) + - (efuse_calibration[sensor->cal_offsets[2]] << 16); + if (gt) { + lvts_ctrl->calibration[i] = + (efuse_calibration[sensor->cal_offsets[0]] << 0) + + (efuse_calibration[sensor->cal_offsets[1]] << 8) + + (efuse_calibration[sensor->cal_offsets[2]] << 16); + } else if (lvts_ctrl->lvts_data->def_calibration) { + lvts_ctrl->calibration[i] = lvts_ctrl->lvts_data->def_calibration; + } else { + dev_err(dev, "efuse contains invalid calibration data and no default given.\n"); + return -ENODATA; + } } return 0; @@ -770,14 +782,13 @@ static int lvts_golden_temp_init(struct device *dev, u8 *calib, gt = (((u32 *)calib)[0] >> lvts_data->gt_calib_bit_offset) & 0xff; /* A zero value for gt means that device has invalid efuse data */ - if (!gt) - return -ENODATA; - - if (gt < LVTS_GOLDEN_TEMP_MAX) + if (gt && gt < LVTS_GOLDEN_TEMP_MAX) golden_temp = gt; golden_temp_offset = golden_temp * 500 + lvts_data->temp_offset; + dev_info(dev, "%sgolden temp=%d\n", gt ? "" : "fake ", golden_temp); + return 0; } @@ -1440,7 +1451,7 @@ static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { .cal_offsets = { 29, 30, 31 } }, { .dt_id = MT8186_ADSP, .cal_offsets = { 34, 35, 28 } }, - { .dt_id = MT8186_MFG, + { .dt_id = MT8186_GPU, .cal_offsets = { 39, 32, 33 } } }, VALID_SENSOR_MAP(1, 1, 1, 0), @@ -1488,11 +1499,11 @@ static const struct lvts_ctrl_data mt8188_lvts_ap_data_ctrl[] = { }, { .lvts_sensor = { - { .dt_id = MT8188_AP_GPU1, + { .dt_id = MT8188_AP_GPU0, .cal_offsets = { 43, 44, 45 } }, - { .dt_id = MT8188_AP_GPU2, + { .dt_id = MT8188_AP_GPU1, .cal_offsets = { 46, 47, 48 } }, - { .dt_id = MT8188_AP_SOC1, + { .dt_id = MT8188_AP_ADSP, .cal_offsets = { 49, 50, 51 } }, }, VALID_SENSOR_MAP(1, 1, 1, 0), @@ -1500,9 +1511,9 @@ static const struct lvts_ctrl_data mt8188_lvts_ap_data_ctrl[] = { }, { .lvts_sensor = { - { .dt_id = MT8188_AP_SOC2, + { .dt_id = MT8188_AP_VDO, .cal_offsets = { 52, 53, 54 } }, - { .dt_id = MT8188_AP_SOC3, + { .dt_id = MT8188_AP_INFRA, .cal_offsets = { 55, 56, 57 } }, }, VALID_SENSOR_MAP(1, 1, 0, 0), @@ -1701,6 +1712,7 @@ static const struct lvts_data mt8186_lvts_data = { .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, .gt_calib_bit_offset = 24, + .def_calibration = 19000, }; static const struct lvts_data mt8188_lvts_mcu_data = { @@ -1709,6 +1721,7 @@ static const struct lvts_data mt8188_lvts_mcu_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 20, + .def_calibration = 35000, }; static const struct lvts_data mt8188_lvts_ap_data = { @@ -1717,6 +1730,7 @@ static const struct lvts_data mt8188_lvts_ap_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 20, + .def_calibration = 35000, }; static const struct lvts_data mt8192_lvts_mcu_data = { @@ -1725,6 +1739,7 @@ static const struct lvts_data mt8192_lvts_mcu_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, + .def_calibration = 35000, }; static const struct lvts_data mt8192_lvts_ap_data = { @@ -1733,6 +1748,7 @@ static const struct lvts_data mt8192_lvts_ap_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, + .def_calibration = 35000, }; static const struct lvts_data mt8195_lvts_mcu_data = { @@ -1741,6 +1757,7 @@ static const struct lvts_data mt8195_lvts_mcu_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, + .def_calibration = 35000, }; static const struct lvts_data mt8195_lvts_ap_data = { @@ -1749,6 +1766,7 @@ static const struct lvts_data mt8195_lvts_ap_data = { .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, + .def_calibration = 35000, }; static const struct of_device_id lvts_of_match[] = { diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c index 756ac6842ff9..7c9f4023babc 100644 --- a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -829,12 +829,9 @@ static int adc_tm5_get_dt_channel_data(struct adc_tm5_chip *adc_tm, channel->iio = devm_fwnode_iio_channel_get_by_name(adc_tm->dev, of_fwnode_handle(node), NULL); - if (IS_ERR(channel->iio)) { - ret = PTR_ERR(channel->iio); - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: error getting channel: %d\n", name, ret); - return ret; - } + if (IS_ERR(channel->iio)) + return dev_err_probe(dev, PTR_ERR(channel->iio), "%s: error getting channel\n", + name); ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); if (!ret) { diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 3cd74f6cac8f..c2d59cbfaea9 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -261,17 +261,13 @@ skip: return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); } -static int qpnp_tm_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp) +static int qpnp_tm_set_trip_temp(struct thermal_zone_device *tz, + const struct thermal_trip *trip, int temp) { struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz); - struct thermal_trip trip; int ret; - ret = __thermal_zone_get_trip(chip->tz_dev, trip_id, &trip); - if (ret) - return ret; - - if (trip.type != THERMAL_TRIP_CRITICAL) + if (trip->type != THERMAL_TRIP_CRITICAL) return 0; mutex_lock(&chip->lock); @@ -295,24 +291,6 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) return IRQ_HANDLED; } -static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip) -{ - struct thermal_trip trip; - int i, ret; - - for (i = 0; i < thermal_zone_get_num_trips(chip->tz_dev); i++) { - - ret = thermal_zone_get_trip(chip->tz_dev, i, &trip); - if (ret) - continue; - - if (trip.type == THERMAL_TRIP_CRITICAL) - return trip.temperature; - } - - return THERMAL_TEMP_INVALID; -} - /* * This function initializes the internal temp value based on only the * current thermal stage and threshold. Setup threshold control and @@ -347,7 +325,9 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) mutex_unlock(&chip->lock); - crit_temp = qpnp_tm_get_critical_trip_temp(chip); + ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp); + if (ret) + crit_temp = THERMAL_TEMP_INVALID; mutex_lock(&chip->lock); diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index e76e23026dc8..0b4421bf4785 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -1336,11 +1336,9 @@ static int tsens_probe(struct platform_device *pdev) if (priv->ops->calibrate) { ret = priv->ops->calibrate(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: calibration failed\n", __func__); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "%s: calibration failed\n", + __func__); } ret = tsens_register(priv); diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 404f01cca4da..52e26be8c53d 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -347,7 +347,7 @@ static int qoriq_tmu_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused qoriq_tmu_suspend(struct device *dev) +static int qoriq_tmu_suspend(struct device *dev) { struct qoriq_tmu_data *data = dev_get_drvdata(dev); int ret; @@ -361,7 +361,7 @@ static int __maybe_unused qoriq_tmu_suspend(struct device *dev) return 0; } -static int __maybe_unused qoriq_tmu_resume(struct device *dev) +static int qoriq_tmu_resume(struct device *dev) { int ret; struct qoriq_tmu_data *data = dev_get_drvdata(dev); @@ -374,8 +374,8 @@ static int __maybe_unused qoriq_tmu_resume(struct device *dev) return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); } -static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, - qoriq_tmu_suspend, qoriq_tmu_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, + qoriq_tmu_suspend, qoriq_tmu_resume); static const struct of_device_id qoriq_tmu_match[] = { { .compatible = "fsl,qoriq-tmu", }, @@ -387,7 +387,7 @@ MODULE_DEVICE_TABLE(of, qoriq_tmu_match); static struct platform_driver qoriq_tmu = { .driver = { .name = "qoriq_thermal", - .pm = &qoriq_tmu_pm_ops, + .pm = pm_sleep_ptr(&qoriq_tmu_pm_ops), .of_match_table = qoriq_tmu_match, }, .probe = qoriq_tmu_probe, diff --git a/drivers/thermal/renesas/Kconfig b/drivers/thermal/renesas/Kconfig new file mode 100644 index 000000000000..dcf5fc5ae08e --- /dev/null +++ b/drivers/thermal/renesas/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config RCAR_THERMAL + tristate "Renesas R-Car thermal driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + Enable this to plug the R-Car thermal sensor driver into the Linux + thermal framework. + +config RCAR_GEN3_THERMAL + tristate "Renesas R-Car Gen3 and RZ/G2 thermal driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + Enable this to plug the R-Car Gen3 or RZ/G2 thermal sensor driver into + the Linux thermal framework. + +config RZG2L_THERMAL + tristate "Renesas RZ/G2L thermal driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + Enable this to plug the RZ/G2L thermal sensor driver into the Linux + thermal framework. diff --git a/drivers/thermal/renesas/Makefile b/drivers/thermal/renesas/Makefile new file mode 100644 index 000000000000..bf9cb3cb94d6 --- /dev/null +++ b/drivers/thermal/renesas/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o +obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +obj-$(CONFIG_RZG2L_THERMAL) += rzg2l_thermal.o diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/renesas/rcar_gen3_thermal.c index 02494fa142c3..810f86677461 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/renesas/rcar_gen3_thermal.c @@ -16,7 +16,7 @@ #include <linux/pm_runtime.h> #include <linux/thermal.h> -#include "thermal_hwmon.h" +#include "../thermal_hwmon.h" /* Register offsets */ #define REG_GEN3_IRQSTR 0x04 @@ -563,11 +563,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (ret) goto error_unregister; - ret = thermal_zone_get_num_trips(tsc->zone); - if (ret < 0) - goto error_unregister; - - dev_info(dev, "Sensor %u: Loaded %d trip points\n", i, ret); + dev_info(dev, "Sensor %u: Loaded\n", i); } if (!priv->num_tscs) { diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/renesas/rcar_thermal.c index 925183753fcb..ddc8341e5c3f 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/renesas/rcar_thermal.c @@ -19,7 +19,7 @@ #include <linux/spinlock.h> #include <linux/thermal.h> -#include "thermal_hwmon.h" +#include "../thermal_hwmon.h" #define IDLE_INTERVAL 5000 @@ -447,7 +447,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, rcar_thermal_irq, IRQF_SHARED, dev_name(dev), common); if (ret) { - dev_err(dev, "irq request failed\n "); + dev_err(dev, "irq request failed\n"); goto error_unregister; } diff --git a/drivers/thermal/rzg2l_thermal.c b/drivers/thermal/renesas/rzg2l_thermal.c index 04efd824ac4c..0e1cb9045ee6 100644 --- a/drivers/thermal/rzg2l_thermal.c +++ b/drivers/thermal/renesas/rzg2l_thermal.c @@ -17,7 +17,7 @@ #include <linux/thermal.h> #include <linux/units.h> -#include "thermal_hwmon.h" +#include "../thermal_hwmon.h" #define CTEMP_MASK 0xFFF diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 6482513bfe66..96cffb2c44ba 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1004,11 +1004,11 @@ static const struct thermal_zone_device_ops exynos_sensor_ops = { static int exynos_tmu_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct exynos_tmu_data *data; int ret; - data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1020,7 +1020,7 @@ static int exynos_tmu_probe(struct platform_device *pdev) * TODO: Add regulator as an SOC feature, so that regulator enable * is a compulsory call. */ - ret = devm_regulator_get_enable_optional(&pdev->dev, "vtmu"); + ret = devm_regulator_get_enable_optional(dev, "vtmu"); switch (ret) { case 0: case -ENODEV: @@ -1028,8 +1028,7 @@ static int exynos_tmu_probe(struct platform_device *pdev) case -EPROBE_DEFER: return -EPROBE_DEFER; default: - dev_err(&pdev->dev, "Failed to get enabled regulator: %d\n", - ret); + dev_err(dev, "Failed to get enabled regulator: %d\n", ret); return ret; } @@ -1037,44 +1036,40 @@ static int exynos_tmu_probe(struct platform_device *pdev) if (ret) return ret; - data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, "Failed to get clock\n"); - return PTR_ERR(data->clk); - } + data->clk = devm_clk_get(dev, "tmu_apbif"); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "Failed to get clock\n"); - data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); + data->clk_sec = devm_clk_get(dev, "tmu_triminfo_apbif"); if (IS_ERR(data->clk_sec)) { - if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { - dev_err(&pdev->dev, "Failed to get triminfo clock\n"); - return PTR_ERR(data->clk_sec); - } + if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) + return dev_err_probe(dev, PTR_ERR(data->clk_sec), + "Failed to get triminfo clock\n"); } else { ret = clk_prepare(data->clk_sec); if (ret) { - dev_err(&pdev->dev, "Failed to get clock\n"); + dev_err(dev, "Failed to get clock\n"); return ret; } } ret = clk_prepare(data->clk); if (ret) { - dev_err(&pdev->dev, "Failed to get clock\n"); + dev_err(dev, "Failed to get clock\n"); goto err_clk_sec; } switch (data->soc) { case SOC_ARCH_EXYNOS5433: case SOC_ARCH_EXYNOS7: - data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); + data->sclk = devm_clk_get(dev, "tmu_sclk"); if (IS_ERR(data->sclk)) { - dev_err(&pdev->dev, "Failed to get sclk\n"); - ret = PTR_ERR(data->sclk); + ret = dev_err_probe(dev, PTR_ERR(data->sclk), "Failed to get sclk\n"); goto err_clk; } else { ret = clk_prepare_enable(data->sclk); if (ret) { - dev_err(&pdev->dev, "Failed to enable sclk\n"); + dev_err(dev, "Failed to enable sclk\n"); goto err_clk; } } @@ -1085,33 +1080,30 @@ static int exynos_tmu_probe(struct platform_device *pdev) ret = exynos_tmu_initialize(pdev); if (ret) { - dev_err(&pdev->dev, "Failed to initialize TMU\n"); + dev_err(dev, "Failed to initialize TMU\n"); goto err_sclk; } - data->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, data, + data->tzd = devm_thermal_of_zone_register(dev, 0, data, &exynos_sensor_ops); if (IS_ERR(data->tzd)) { - ret = PTR_ERR(data->tzd); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to register sensor: %d\n", - ret); + ret = dev_err_probe(dev, PTR_ERR(data->tzd), "Failed to register sensor\n"); goto err_sclk; } ret = exynos_thermal_zone_configure(pdev); if (ret) { - dev_err(&pdev->dev, "Failed to configure the thermal zone\n"); + dev_err(dev, "Failed to configure the thermal zone\n"); goto err_sclk; } - ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, + ret = devm_request_threaded_irq(dev, data->irq, NULL, exynos_tmu_threaded_irq, IRQF_TRIGGER_RISING | IRQF_SHARED | IRQF_ONESHOT, - dev_name(&pdev->dev), data); + dev_name(dev), data); if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); + dev_err(dev, "Failed to request irq: %d\n", data->irq); goto err_sclk; } diff --git a/drivers/thermal/sprd_thermal.c b/drivers/thermal/sprd_thermal.c index 874192546548..dfd1d529c410 100644 --- a/drivers/thermal/sprd_thermal.c +++ b/drivers/thermal/sprd_thermal.c @@ -359,21 +359,17 @@ static int sprd_thm_probe(struct platform_device *pdev) return -EINVAL; } - thm->clk = devm_clk_get(&pdev->dev, "enable"); + thm->clk = devm_clk_get_enabled(&pdev->dev, "enable"); if (IS_ERR(thm->clk)) { dev_err(&pdev->dev, "failed to get enable clock\n"); return PTR_ERR(thm->clk); } - ret = clk_prepare_enable(thm->clk); - if (ret) - return ret; - sprd_thm_para_config(thm); ret = sprd_thm_cal_read(np, "thm_sign_cal", &val); if (ret) - goto disable_clk; + return ret; if (val > 0) thm->ratio_sign = -1; @@ -382,7 +378,7 @@ static int sprd_thm_probe(struct platform_device *pdev) ret = sprd_thm_cal_read(np, "thm_ratio_cal", &thm->ratio_off); if (ret) - goto disable_clk; + return ret; for_each_child_of_node(np, sen_child) { sen = devm_kzalloc(&pdev->dev, sizeof(*sen), GFP_KERNEL); @@ -439,8 +435,6 @@ static int sprd_thm_probe(struct platform_device *pdev) of_put: of_node_put(sen_child); -disable_clk: - clk_disable_unprepare(thm->clk); return ret; } @@ -526,8 +520,6 @@ static void sprd_thm_remove(struct platform_device *pdev) devm_thermal_of_zone_unregister(&pdev->dev, thm->sensor[i]->tzd); } - - clk_disable_unprepare(thm->clk); } static const struct of_device_id sprd_thermal_of_match[] = { diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index 2a105409864e..a14a37d54698 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -12,6 +12,7 @@ #include <linux/of_device.h> #include "st_thermal.h" +#include "../thermal_hwmon.h" /* The Thermal Framework expects millidegrees */ #define mcelsius(temp) ((temp) * 1000) @@ -135,8 +136,6 @@ static struct thermal_zone_device_ops st_tz_ops = { .get_temp = st_thermal_get_temp, }; -static struct thermal_trip trip; - int st_thermal_register(struct platform_device *pdev, const struct of_device_id *st_thermal_of_match) { @@ -145,7 +144,6 @@ int st_thermal_register(struct platform_device *pdev, struct device_node *np = dev->of_node; const struct of_device_id *match; - int polling_delay; int ret; if (!np) { @@ -197,29 +195,24 @@ int st_thermal_register(struct platform_device *pdev, if (ret) goto sensor_off; - polling_delay = sensor->ops->register_enable_irq ? 0 : 1000; - - trip.temperature = sensor->cdata->crit_temp; - trip.type = THERMAL_TRIP_CRITICAL; - sensor->thermal_dev = - thermal_zone_device_register_with_trips(dev_name(dev), &trip, 1, sensor, - &st_tz_ops, NULL, 0, polling_delay); + devm_thermal_of_zone_register(dev, 0, sensor, &st_tz_ops); if (IS_ERR(sensor->thermal_dev)) { - dev_err(dev, "failed to register thermal zone device\n"); + dev_err(dev, "failed to register thermal of zone\n"); ret = PTR_ERR(sensor->thermal_dev); goto sensor_off; } - ret = thermal_zone_device_enable(sensor->thermal_dev); - if (ret) - goto tzd_unregister; platform_set_drvdata(pdev, sensor); + /* + * devm_thermal_of_zone_register() doesn't enable hwmon by default + * Enable it here + */ + devm_thermal_add_hwmon_sysfs(dev, sensor->thermal_dev); + return 0; -tzd_unregister: - thermal_zone_device_unregister(sensor->thermal_dev); sensor_off: st_thermal_sensor_off(sensor); @@ -232,11 +225,11 @@ void st_thermal_unregister(struct platform_device *pdev) struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); st_thermal_sensor_off(sensor); - thermal_zone_device_unregister(sensor->thermal_dev); + thermal_remove_hwmon_sysfs(sensor->thermal_dev); + devm_thermal_of_zone_unregister(sensor->dev, sensor->thermal_dev); } EXPORT_SYMBOL_GPL(st_thermal_unregister); -#ifdef CONFIG_PM_SLEEP static int st_thermal_suspend(struct device *dev) { struct st_thermal_sensor *sensor = dev_get_drvdata(dev); @@ -265,9 +258,8 @@ static int st_thermal_resume(struct device *dev) return 0; } -#endif -SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); +DEFINE_SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); EXPORT_SYMBOL_GPL(st_thermal_pm_ops); MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>"); diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c index 29c2269b0fb3..97493d2b2f49 100644 --- a/drivers/thermal/st/st_thermal_memmap.c +++ b/drivers/thermal/st/st_thermal_memmap.c @@ -142,15 +142,6 @@ static const struct st_thermal_sensor_ops st_mmap_sensor_ops = { .enable_irq = st_mmap_enable_irq, }; -/* Compatible device data stih416 mpe thermal sensor */ -static const struct st_thermal_compat_data st_416mpe_cdata = { - .reg_fields = st_mmap_thermal_regfields, - .ops = &st_mmap_sensor_ops, - .calibration_val = 14, - .temp_adjust_val = -95, - .crit_temp = 120, -}; - /* Compatible device data stih407 thermal sensor */ static const struct st_thermal_compat_data st_407_cdata = { .reg_fields = st_mmap_thermal_regfields, @@ -161,7 +152,6 @@ static const struct st_thermal_compat_data st_407_cdata = { }; static const struct of_device_id st_mmap_thermal_of_match[] = { - { .compatible = "st,stih416-mpe-thermal", .data = &st_416mpe_cdata }, { .compatible = "st,stih407-thermal", .data = &st_407_cdata }, { /* sentinel */ } }; @@ -180,7 +170,7 @@ static void st_mmap_remove(struct platform_device *pdev) static struct platform_driver st_mmap_thermal_driver = { .driver = { .name = "st_thermal_mmap", - .pm = &st_thermal_pm_ops, + .pm = pm_sleep_ptr(&st_thermal_pm_ops), .of_match_table = st_mmap_thermal_of_match, }, .probe = st_mmap_probe, diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index 34785b9276fc..ffd988600ed6 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -440,7 +440,6 @@ thermal_unprepare: return ret; } -#ifdef CONFIG_PM_SLEEP static int stm_thermal_suspend(struct device *dev) { struct stm_thermal_sensor *sensor = dev_get_drvdata(dev); @@ -466,10 +465,9 @@ static int stm_thermal_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, - stm_thermal_suspend, stm_thermal_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, + stm_thermal_suspend, stm_thermal_resume); static const struct thermal_zone_device_ops stm_tz_ops = { .get_temp = stm_thermal_get_temp, @@ -580,7 +578,7 @@ static void stm_thermal_remove(struct platform_device *pdev) static struct platform_driver stm_thermal_driver = { .driver = { .name = "stm_thermal", - .pm = &stm_thermal_pm_ops, + .pm = pm_sleep_ptr(&stm_thermal_pm_ops), .of_match_table = stm_thermal_of_match, }, .probe = stm_thermal_probe, diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index e7fe8683bfc5..a023c948afbd 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -582,23 +582,18 @@ static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) return temp; } -static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp) +static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, + const struct thermal_trip *trip, int temp) { struct tegra_thermctl_zone *zone = thermal_zone_device_priv(tz); struct tegra_soctherm *ts = zone->ts; - struct thermal_trip trip; const struct tegra_tsensor_group *sg = zone->sg; struct device *dev = zone->dev; - int ret; if (!tz) return -EINVAL; - ret = __thermal_zone_get_trip(tz, trip_id, &trip); - if (ret) - return ret; - - if (trip.type == THERMAL_TRIP_CRITICAL) { + if (trip->type == THERMAL_TRIP_CRITICAL) { /* * If thermtrips property is set in DT, * doesn't need to program critical type trip to HW, @@ -609,7 +604,7 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip else return 0; - } else if (trip.type == THERMAL_TRIP_HOT) { + } else if (trip->type == THERMAL_TRIP_HOT) { int i; for (i = 0; i < THROTTLE_SIZE; i++) { @@ -620,7 +615,7 @@ static int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip continue; cdev = ts->throt_cfgs[i].cdev; - if (get_thermal_instance(tz, cdev, trip_id)) + if (thermal_trip_is_bound_to_cdev(tz, trip, cdev)) stc = find_throttle_cfg_by_name(ts, cdev->type); else continue; @@ -687,24 +682,25 @@ static const struct thermal_zone_device_ops tegra_of_thermal_ops = { .set_trips = tegra_thermctl_set_trips, }; -static int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp) +static int get_hot_trip_cb(struct thermal_trip *trip, void *arg) { - int i, ret; - struct thermal_trip trip; + const struct thermal_trip **trip_ret = arg; - for (i = 0; i < thermal_zone_get_num_trips(tz); i++) { + if (trip->type != THERMAL_TRIP_HOT) + return 0; - ret = thermal_zone_get_trip(tz, i, &trip); - if (ret) - return -EINVAL; + *trip_ret = trip; + /* Return nonzero to terminate the search. */ + return 1; +} - if (trip.type == THERMAL_TRIP_HOT) { - *trip_id = i; - return 0; - } - } +static const struct thermal_trip *get_hot_trip(struct thermal_zone_device *tz) +{ + const struct thermal_trip *trip = NULL; - return -EINVAL; + thermal_zone_for_each_trip(tz, get_hot_trip_cb, &trip); + + return trip; } /** @@ -736,8 +732,9 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, struct thermal_zone_device *tz) { struct tegra_soctherm *ts = dev_get_drvdata(dev); + const struct thermal_trip *hot_trip; struct soctherm_throt_cfg *stc; - int i, trip, temperature, ret; + int i, temperature, ret; /* Get thermtrips. If missing, try to get critical trips. */ temperature = tsensor_group_thermtrip_get(ts, sg->id); @@ -754,8 +751,8 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", sg->name, temperature); - ret = get_hot_temp(tz, &trip, &temperature); - if (ret) { + hot_trip = get_hot_trip(tz); + if (!hot_trip) { dev_info(dev, "throttrip: %s: missing hot temperature\n", sg->name); return 0; @@ -768,7 +765,7 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, continue; cdev = ts->throt_cfgs[i].cdev; - if (get_thermal_instance(tz, cdev, trip)) + if (thermal_trip_is_bound_to_cdev(tz, hot_trip, cdev)) stc = find_throttle_cfg_by_name(ts, cdev->type); else continue; diff --git a/drivers/thermal/tegra/tegra30-tsensor.c b/drivers/thermal/tegra/tegra30-tsensor.c index d911fa60f100..6245f6b97f43 100644 --- a/drivers/thermal/tegra/tegra30-tsensor.c +++ b/drivers/thermal/tegra/tegra30-tsensor.c @@ -303,33 +303,37 @@ stop_channel: return 0; } -static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, - int *hot_trip, int *crit_trip) +struct trip_temps { + int hot_trip; + int crit_trip; +}; + +static int tegra_tsensor_get_trips_cb(struct thermal_trip *trip, void *arg) { - unsigned int i; + struct trip_temps *temps = arg; + + if (trip->type == THERMAL_TRIP_HOT) + temps->hot_trip = trip->temperature; + else if (trip->type == THERMAL_TRIP_CRITICAL) + temps->crit_trip = trip->temperature; + + return 0; +} +static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, + struct trip_temps *temps) +{ /* * 90C is the maximal critical temperature of all Tegra30 SoC variants, * use it for the default trip if unspecified in a device-tree. */ - *hot_trip = 85000; - *crit_trip = 90000; - - for (i = 0; i < thermal_zone_get_num_trips(tzd); i++) { - - struct thermal_trip trip; + temps->hot_trip = 85000; + temps->crit_trip = 90000; - thermal_zone_get_trip(tzd, i, &trip); - - if (trip.type == THERMAL_TRIP_HOT) - *hot_trip = trip.temperature; - - if (trip.type == THERMAL_TRIP_CRITICAL) - *crit_trip = trip.temperature; - } + thermal_zone_for_each_trip(tzd, tegra_tsensor_get_trips_cb, temps); /* clamp hardware trips to the calibration limits */ - *hot_trip = clamp(*hot_trip, 25000, 90000); + temps->hot_trip = clamp(temps->hot_trip, 25000, 90000); /* * Kernel will perform a normal system shut down if it will @@ -338,7 +342,7 @@ static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, * shut down gracefully before sending signal to the Power * Management controller. */ - *crit_trip = clamp(*crit_trip + 5000, 25000, 90000); + temps->crit_trip = clamp(temps->crit_trip + 5000, 25000, 90000); } static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, @@ -346,7 +350,8 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, { const struct tegra_tsensor_channel *tsc = &ts->ch[id]; struct thermal_zone_device *tzd = tsc->tzd; - int err, hot_trip = 0, crit_trip = 0; + struct trip_temps temps = { 0 }; + int err; u32 val; if (!tzd) { @@ -357,24 +362,24 @@ static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, return 0; } - tegra_tsensor_get_hw_channel_trips(tzd, &hot_trip, &crit_trip); + tegra_tsensor_get_hw_channel_trips(tzd, &temps); dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n", - id, DIV_ROUND_CLOSEST(crit_trip, 1000)); + id, DIV_ROUND_CLOSEST(temps.crit_trip, 1000)); - hot_trip = tegra_tsensor_temp_to_counter(ts, hot_trip); - crit_trip = tegra_tsensor_temp_to_counter(ts, crit_trip); + temps.hot_trip = tegra_tsensor_temp_to_counter(ts, temps.hot_trip); + temps.crit_trip = tegra_tsensor_temp_to_counter(ts, temps.crit_trip); /* program LEVEL2 counter threshold */ val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1); val &= ~TSENSOR_SENSOR0_CONFIG1_TH2; - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, hot_trip); + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, temps.hot_trip); writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1); /* program LEVEL3 counter threshold */ val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2); val &= ~TSENSOR_SENSOR0_CONFIG2_TH3; - val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, crit_trip); + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, temps.crit_trip); writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2); /* diff --git a/drivers/thermal/testing/Makefile b/drivers/thermal/testing/Makefile new file mode 100644 index 000000000000..ede9678efbce --- /dev/null +++ b/drivers/thermal/testing/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Thermal core testing facility. + +obj-$(CONFIG_THERMAL_CORE_TESTING) += thermal-testing.o + +thermal-testing-y := command.o zone.o diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c new file mode 100644 index 000000000000..ba11d70e8021 --- /dev/null +++ b/drivers/thermal/testing/command.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024, Intel Corporation + * + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * Thermal subsystem testing facility. + * + * This facility allows the thermal core functionality to be exercised in a + * controlled way in order to verify its behavior. + * + * It resides in the "thermal-testing" directory under the debugfs root and + * starts with a single file called "command" which can be written a string + * representing a thermal testing facility command. + * + * The currently supported commands are listed in the tt_commands enum below. + * + * The "addtz" command causes a new test thermal zone template to be created, + * for example: + * + * # echo addtz > /sys/kernel/debug/thermal-testing/command + * + * That template will be represented as a subdirectory in the "thermal-testing" + * directory, for example + * + * # ls /sys/kernel/debug/thermal-testing/ + * command tz0 + * + * The thermal zone template can be populated with trip points with the help of + * the "tzaddtrip" command, for example: + * + * # echo tzaddtrip:0 > /sys/kernel/debug/thermal-testing/command + * + * which causes a trip point template to be added to the test thermal zone + * template 0 (represented by the tz0 subdirectory in "thermal-testing"). + * + * # ls /sys/kernel/debug/thermal-testing/tz0 + * init_temp temp trip_0_temp trip_0_hyst + * + * The temperature of a trip point template is initially THERMAL_TEMP_INVALID + * and its hysteresis is initially 0. They can be adjusted by writing to the + * "trip_x_temp" and "trip_x_hyst" files correspoinding to that trip point + * template, respectively. + * + * The initial temperature of a thermal zone based on a template can be set by + * writing to the "init_temp" file in its directory under "thermal-testing", for + * example: + * + * echo 50000 > /sys/kernel/debug/thermal-testing/tz0/init_temp + * + * When ready, "tzreg" command can be used for registering and enabling a + * thermal zone based on a given template with the thermal core, for example + * + * # echo tzreg:0 > /sys/kernel/debug/thermal-testing/command + * + * In this case, test thermal zone template 0 is used for registering a new + * thermal zone and the set of trip point templates associated with it is used + * for populating the new thermal zone's trip points table. The type of the new + * thermal zone is "test_tz". + * + * The temperature and hysteresis of all of the trip points in that new thermal + * zone are adjustable via sysfs, so they can be updated at any time. + * + * The current temperature of the new thermal zone can be set by writing to the + * "temp" file in the corresponding thermal zone template's directory under + * "thermal-testing", for example + * + * echo 10000 > /sys/kernel/debug/thermal-testing/tz0/temp + * + * which will also trigger a temperature update for this zone in the thermal + * core, including checking its trip points, sending notifications to user space + * if any of them have been crossed and so on. + * + * When it is not needed any more, a test thermal zone template can be deleted + * with the help of the "deltz" command, for example + * + * # echo deltz:0 > /sys/kernel/debug/thermal-testing/command + * + * which will also unregister the thermal zone based on it, if present. + */ + +#define pr_fmt(fmt) "thermal-testing: " fmt + +#include <linux/debugfs.h> +#include <linux/module.h> + +#include "thermal_testing.h" + +struct dentry *d_testing; + +#define TT_COMMAND_SIZE 16 + +enum tt_commands { + TT_CMD_ADDTZ, + TT_CMD_DELTZ, + TT_CMD_TZADDTRIP, + TT_CMD_TZREG, + TT_CMD_TZUNREG, +}; + +static const char *tt_command_strings[] = { + [TT_CMD_ADDTZ] = "addtz", + [TT_CMD_DELTZ] = "deltz", + [TT_CMD_TZADDTRIP] = "tzaddtrip", + [TT_CMD_TZREG] = "tzreg", + [TT_CMD_TZUNREG] = "tzunreg", +}; + +static int tt_command_exec(int index, const char *arg) +{ + int ret; + + switch (index) { + case TT_CMD_ADDTZ: + ret = tt_add_tz(); + break; + + case TT_CMD_DELTZ: + ret = tt_del_tz(arg); + break; + + case TT_CMD_TZADDTRIP: + ret = tt_zone_add_trip(arg); + break; + + case TT_CMD_TZREG: + ret = tt_zone_reg(arg); + break; + + case TT_CMD_TZUNREG: + ret = tt_zone_unreg(arg); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf, + size_t count) +{ + char *buf __free(kfree); + char *arg; + int i; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = '\0'; + strim(buf); + + arg = strstr(buf, ":"); + if (arg) { + *arg = '\0'; + arg++; + } + + for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) { + if (!strcmp(buf, tt_command_strings[i])) + return tt_command_exec(i, arg); + } + + return -EINVAL; +} + +static ssize_t tt_command_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = file->f_path.dentry; + ssize_t ret; + + if (*ppos) + return -EINVAL; + + if (count + 1 > TT_COMMAND_SIZE) + return -E2BIG; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + ret = tt_command_process(dentry, user_buf, count); + if (ret) + return ret; + + return count; +} + +static const struct file_operations tt_command_fops = { + .write = tt_command_write, + .open = simple_open, + .llseek = default_llseek, +}; + +static int __init thermal_testing_init(void) +{ + d_testing = debugfs_create_dir("thermal-testing", NULL); + if (!IS_ERR(d_testing)) + debugfs_create_file("command", 0200, d_testing, NULL, + &tt_command_fops); + + return 0; +} +module_init(thermal_testing_init); + +static void __exit thermal_testing_exit(void) +{ + debugfs_remove(d_testing); + tt_zone_cleanup(); +} +module_exit(thermal_testing_exit); + +MODULE_DESCRIPTION("Thermal core testing facility"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/testing/thermal_testing.h b/drivers/thermal/testing/thermal_testing.h new file mode 100644 index 000000000000..c790a32aae4e --- /dev/null +++ b/drivers/thermal/testing/thermal_testing.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +extern struct dentry *d_testing; + +int tt_add_tz(void); +int tt_del_tz(const char *arg); +int tt_zone_add_trip(const char *arg); +int tt_zone_reg(const char *arg); +int tt_zone_unreg(const char *arg); + +void tt_zone_cleanup(void); diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c new file mode 100644 index 000000000000..c6d8c66f40f9 --- /dev/null +++ b/drivers/thermal/testing/zone.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024, Intel Corporation + * + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * Thermal zone tempalates handling for thermal core testing. + */ + +#define pr_fmt(fmt) "thermal-testing: " fmt + +#include <linux/debugfs.h> +#include <linux/idr.h> +#include <linux/list.h> +#include <linux/thermal.h> +#include <linux/workqueue.h> + +#include "thermal_testing.h" + +#define TT_MAX_FILE_NAME_LENGTH 16 + +/** + * struct tt_thermal_zone - Testing thermal zone template + * + * Represents a template of a thermal zone that can be used for registering + * a test thermal zone with the thermal core. + * + * @list_node: Node in the list of all testing thermal zone templates. + * @trips: List of trip point templates for this thermal zone template. + * @d_tt_zone: Directory in debugfs representing this template. + * @tz: Test thermal zone based on this template, if present. + * @lock: Mutex for synchronizing changes of this template. + * @ida: IDA for trip point IDs. + * @id: The ID of this template for the debugfs interface. + * @temp: Temperature value. + * @tz_temp: Current thermal zone temperature (after registration). + * @num_trips: Number of trip points in the @trips list. + * @refcount: Reference counter for usage and removal synchronization. + */ +struct tt_thermal_zone { + struct list_head list_node; + struct list_head trips; + struct dentry *d_tt_zone; + struct thermal_zone_device *tz; + struct mutex lock; + struct ida ida; + int id; + int temp; + int tz_temp; + unsigned int num_trips; + unsigned int refcount; +}; + +DEFINE_GUARD(tt_zone, struct tt_thermal_zone *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock)) + +/** + * struct tt_trip - Testing trip point template + * + * Represents a template of a trip point to be used for populating a trip point + * during the registration of a thermal zone based on a given zone template. + * + * @list_node: Node in the list of all trip templates in the zone template. + * @trip: Trip point data to use for thernal zone registration. + * @id: The ID of this trip template for the debugfs interface. + */ +struct tt_trip { + struct list_head list_node; + struct thermal_trip trip; + int id; +}; + +/* + * It is both questionable and potentially problematic from the sychnronization + * perspective to attempt to manipulate debugfs from within a debugfs file + * "write" operation, so auxiliary work items are used for that. The majority + * of zone-related command functions have a part that runs from a workqueue and + * make changes in debugs, among other things. + */ +struct tt_work { + struct work_struct work; + struct tt_thermal_zone *tt_zone; + struct tt_trip *tt_trip; +}; + +static inline struct tt_work *tt_work_of_work(struct work_struct *work) +{ + return container_of(work, struct tt_work, work); +} + +static LIST_HEAD(tt_thermal_zones); +static DEFINE_IDA(tt_thermal_zones_ida); +static DEFINE_MUTEX(tt_thermal_zones_lock); + +static int tt_int_get(void *data, u64 *val) +{ + *val = *(int *)data; + return 0; +} +static int tt_int_set(void *data, u64 val) +{ + if ((int)val < THERMAL_TEMP_INVALID) + return -EINVAL; + + *(int *)data = val; + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_int_attr, tt_int_get, tt_int_set, "%lld\n"); +DEFINE_DEBUGFS_ATTRIBUTE(tt_unsigned_int_attr, tt_int_get, tt_int_set, "%llu\n"); + +static int tt_zone_tz_temp_get(void *data, u64 *val) +{ + struct tt_thermal_zone *tt_zone = data; + + guard(tt_zone)(tt_zone); + + if (!tt_zone->tz) + return -EBUSY; + + *val = tt_zone->tz_temp; + + return 0; +} +static int tt_zone_tz_temp_set(void *data, u64 val) +{ + struct tt_thermal_zone *tt_zone = data; + + guard(tt_zone)(tt_zone); + + if (!tt_zone->tz) + return -EBUSY; + + WRITE_ONCE(tt_zone->tz_temp, val); + thermal_zone_device_update(tt_zone->tz, THERMAL_EVENT_TEMP_SAMPLE); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(tt_zone_tz_temp_attr, tt_zone_tz_temp_get, + tt_zone_tz_temp_set, "%lld\n"); + +static void tt_zone_free_trips(struct tt_thermal_zone *tt_zone) +{ + struct tt_trip *tt_trip, *aux; + + list_for_each_entry_safe(tt_trip, aux, &tt_zone->trips, list_node) { + list_del(&tt_trip->list_node); + ida_free(&tt_zone->ida, tt_trip->id); + kfree(tt_trip); + } +} + +static void tt_zone_free(struct tt_thermal_zone *tt_zone) +{ + tt_zone_free_trips(tt_zone); + ida_free(&tt_thermal_zones_ida, tt_zone->id); + ida_destroy(&tt_zone->ida); + kfree(tt_zone); +} + +static void tt_add_tz_work_fn(struct work_struct *work) +{ + struct tt_work *tt_work = tt_work_of_work(work); + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; + char f_name[TT_MAX_FILE_NAME_LENGTH]; + + kfree(tt_work); + + snprintf(f_name, TT_MAX_FILE_NAME_LENGTH, "tz%d", tt_zone->id); + tt_zone->d_tt_zone = debugfs_create_dir(f_name, d_testing); + if (IS_ERR(tt_zone->d_tt_zone)) { + tt_zone_free(tt_zone); + return; + } + + debugfs_create_file_unsafe("temp", 0600, tt_zone->d_tt_zone, tt_zone, + &tt_zone_tz_temp_attr); + + debugfs_create_file_unsafe("init_temp", 0600, tt_zone->d_tt_zone, + &tt_zone->temp, &tt_int_attr); + + guard(mutex)(&tt_thermal_zones_lock); + + list_add_tail(&tt_zone->list_node, &tt_thermal_zones); +} + +int tt_add_tz(void) +{ + struct tt_thermal_zone *tt_zone __free(kfree); + struct tt_work *tt_work __free(kfree); + int ret; + + tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); + if (!tt_zone) + return -ENOMEM; + + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); + if (!tt_work) + return -ENOMEM; + + INIT_LIST_HEAD(&tt_zone->trips); + mutex_init(&tt_zone->lock); + ida_init(&tt_zone->ida); + tt_zone->temp = THERMAL_TEMP_INVALID; + + ret = ida_alloc(&tt_thermal_zones_ida, GFP_KERNEL); + if (ret < 0) + return ret; + + tt_zone->id = ret; + + INIT_WORK(&tt_work->work, tt_add_tz_work_fn); + tt_work->tt_zone = no_free_ptr(tt_zone); + schedule_work(&(no_free_ptr(tt_work)->work)); + + return 0; +} + +static void tt_del_tz_work_fn(struct work_struct *work) +{ + struct tt_work *tt_work = tt_work_of_work(work); + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; + + kfree(tt_work); + + debugfs_remove(tt_zone->d_tt_zone); + tt_zone_free(tt_zone); +} + +static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) +{ + guard(tt_zone)(tt_zone); + + if (tt_zone->tz) { + thermal_zone_device_unregister(tt_zone->tz); + tt_zone->tz = NULL; + } +} + +int tt_del_tz(const char *arg) +{ + struct tt_work *tt_work __free(kfree); + struct tt_thermal_zone *tt_zone, *aux; + int ret; + int id; + + ret = sscanf(arg, "%d", &id); + if (ret != 1) + return -EINVAL; + + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); + if (!tt_work) + return -ENOMEM; + + guard(mutex)(&tt_thermal_zones_lock); + + ret = -EINVAL; + list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) { + if (tt_zone->id == id) { + if (tt_zone->refcount) { + ret = -EBUSY; + } else { + list_del(&tt_zone->list_node); + ret = 0; + } + break; + } + } + + if (ret) + return ret; + + tt_zone_unregister_tz(tt_zone); + + INIT_WORK(&tt_work->work, tt_del_tz_work_fn); + tt_work->tt_zone = tt_zone; + schedule_work(&(no_free_ptr(tt_work)->work)); + + return 0; +} + +static struct tt_thermal_zone *tt_get_tt_zone(const char *arg) +{ + struct tt_thermal_zone *tt_zone; + int ret, id; + + ret = sscanf(arg, "%d", &id); + if (ret != 1) + return ERR_PTR(-EINVAL); + + guard(mutex)(&tt_thermal_zones_lock); + + ret = -EINVAL; + list_for_each_entry(tt_zone, &tt_thermal_zones, list_node) { + if (tt_zone->id == id) { + tt_zone->refcount++; + ret = 0; + break; + } + } + + if (ret) + return ERR_PTR(ret); + + return tt_zone; +} + +static void tt_put_tt_zone(struct tt_thermal_zone *tt_zone) +{ + guard(mutex)(&tt_thermal_zones_lock); + + tt_zone->refcount--; +} + +static void tt_zone_add_trip_work_fn(struct work_struct *work) +{ + struct tt_work *tt_work = tt_work_of_work(work); + struct tt_thermal_zone *tt_zone = tt_work->tt_zone; + struct tt_trip *tt_trip = tt_work->tt_trip; + char d_name[TT_MAX_FILE_NAME_LENGTH]; + + kfree(tt_work); + + snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_temp", tt_trip->id); + debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone, + &tt_trip->trip.temperature, &tt_int_attr); + + snprintf(d_name, TT_MAX_FILE_NAME_LENGTH, "trip_%d_hyst", tt_trip->id); + debugfs_create_file_unsafe(d_name, 0600, tt_zone->d_tt_zone, + &tt_trip->trip.hysteresis, &tt_unsigned_int_attr); + + tt_put_tt_zone(tt_zone); +} + +int tt_zone_add_trip(const char *arg) +{ + struct tt_work *tt_work __free(kfree); + struct tt_trip *tt_trip __free(kfree); + struct tt_thermal_zone *tt_zone; + int id; + + tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); + if (!tt_work) + return -ENOMEM; + + tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL); + if (!tt_trip) + return -ENOMEM; + + tt_zone = tt_get_tt_zone(arg); + if (IS_ERR(tt_zone)) + return PTR_ERR(tt_zone); + + id = ida_alloc(&tt_zone->ida, GFP_KERNEL); + if (id < 0) { + tt_put_tt_zone(tt_zone); + return id; + } + + tt_trip->trip.type = THERMAL_TRIP_ACTIVE; + tt_trip->trip.temperature = THERMAL_TEMP_INVALID; + tt_trip->trip.flags = THERMAL_TRIP_FLAG_RW; + tt_trip->id = id; + + guard(tt_zone)(tt_zone); + + list_add_tail(&tt_trip->list_node, &tt_zone->trips); + tt_zone->num_trips++; + + INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); + tt_work->tt_zone = tt_zone; + tt_work->tt_trip = no_free_ptr(tt_trip); + schedule_work(&(no_free_ptr(tt_work)->work)); + + return 0; +} + +static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct tt_thermal_zone *tt_zone = thermal_zone_device_priv(tz); + + *temp = READ_ONCE(tt_zone->tz_temp); + + if (*temp < THERMAL_TEMP_INVALID) + return -ENODATA; + + return 0; +} + +static struct thermal_zone_device_ops tt_zone_ops = { + .get_temp = tt_zone_get_temp, +}; + +static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) +{ + struct thermal_trip *trips __free(kfree); + struct thermal_zone_device *tz; + struct tt_trip *tt_trip; + int i; + + guard(tt_zone)(tt_zone); + + if (tt_zone->tz) + return -EINVAL; + + trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL); + if (!trips) + return -ENOMEM; + + i = 0; + list_for_each_entry(tt_trip, &tt_zone->trips, list_node) + trips[i++] = tt_trip->trip; + + tt_zone->tz_temp = tt_zone->temp; + + tz = thermal_zone_device_register_with_trips("test_tz", trips, i, tt_zone, + &tt_zone_ops, NULL, 0, 0); + if (IS_ERR(tz)) + return PTR_ERR(tz); + + tt_zone->tz = tz; + + thermal_zone_device_enable(tz); + + return 0; +} + +int tt_zone_reg(const char *arg) +{ + struct tt_thermal_zone *tt_zone; + int ret; + + tt_zone = tt_get_tt_zone(arg); + if (IS_ERR(tt_zone)) + return PTR_ERR(tt_zone); + + ret = tt_zone_register_tz(tt_zone); + + tt_put_tt_zone(tt_zone); + + return ret; +} + +int tt_zone_unreg(const char *arg) +{ + struct tt_thermal_zone *tt_zone; + + tt_zone = tt_get_tt_zone(arg); + if (IS_ERR(tt_zone)) + return PTR_ERR(tt_zone); + + tt_zone_unregister_tz(tt_zone); + + tt_put_tt_zone(tt_zone); + + return 0; +} + +void tt_zone_cleanup(void) +{ + struct tt_thermal_zone *tt_zone, *aux; + + list_for_each_entry_safe(tt_zone, aux, &tt_thermal_zones, list_node) { + tt_zone_unregister_tz(tt_zone); + + list_del(&tt_zone->list_node); + + tt_zone_free(tt_zone); + } +} diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index 1717e4a19dcb..ee3d0aa31406 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -117,44 +117,41 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, static int gadc_thermal_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct gadc_thermal_info *gti; int ret; - if (!pdev->dev.of_node) { - dev_err(&pdev->dev, "Only DT based supported\n"); + if (!dev->of_node) { + dev_err(dev, "Only DT based supported\n"); return -ENODEV; } - gti = devm_kzalloc(&pdev->dev, sizeof(*gti), GFP_KERNEL); + gti = devm_kzalloc(dev, sizeof(*gti), GFP_KERNEL); if (!gti) return -ENOMEM; - gti->channel = devm_iio_channel_get(&pdev->dev, "sensor-channel"); - if (IS_ERR(gti->channel)) { - ret = PTR_ERR(gti->channel); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "IIO channel not found: %d\n", ret); - return ret; - } + gti->channel = devm_iio_channel_get(dev, "sensor-channel"); + if (IS_ERR(gti->channel)) + return dev_err_probe(dev, PTR_ERR(gti->channel), "IIO channel not found\n"); - ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti); + ret = gadc_thermal_read_linear_lookup_table(dev, gti); if (ret < 0) return ret; - gti->dev = &pdev->dev; + gti->dev = dev; - gti->tz_dev = devm_thermal_of_zone_register(&pdev->dev, 0, gti, + gti->tz_dev = devm_thermal_of_zone_register(dev, 0, gti, &gadc_thermal_ops); if (IS_ERR(gti->tz_dev)) { ret = PTR_ERR(gti->tz_dev); if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, + dev_err(dev, "Thermal zone sensor register failed: %d\n", ret); return ret; } - devm_thermal_add_hwmon_sysfs(&pdev->dev, gti->tz_dev); + devm_thermal_add_hwmon_sysfs(dev, gti->tz_dev); return 0; } diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 1b0ab2790860..073d02e21352 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -272,6 +272,44 @@ static int __init thermal_register_governors(void) return ret; } +static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz, + enum thermal_device_mode mode) +{ + if (tz->ops.change_mode) { + int ret; + + ret = tz->ops.change_mode(tz, mode); + if (ret) + return ret; + } + + tz->mode = mode; + + return 0; +} + +static void thermal_zone_broken_disable(struct thermal_zone_device *tz) +{ + struct thermal_trip_desc *td; + + dev_err(&tz->device, "Unable to get temperature, disabling!\n"); + /* + * This function only runs for enabled thermal zones, so no need to + * check for the current mode. + */ + __thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED); + thermal_notify_tz_disable(tz); + + for_each_trip_desc(tz, td) { + if (td->trip.type == THERMAL_TRIP_CRITICAL && + td->trip.temperature > THERMAL_TEMP_INVALID) { + dev_crit(&tz->device, + "Disabled thermal zone with critical trip point\n"); + return; + } + } +} + /* * Zone update section: main control loop applied to each zone while monitoring * in polling mode. The monitoring is done using a workqueue. @@ -285,18 +323,43 @@ static int __init thermal_register_governors(void) static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, unsigned long delay) { - if (delay) - mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, delay); - else - cancel_delayed_work(&tz->poll_queue); + if (delay > HZ) + delay = round_jiffies_relative(delay); + + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); +} + +static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) +{ + if (error == -EAGAIN) { + thermal_zone_device_set_polling(tz, THERMAL_RECHECK_DELAY); + return; + } + + /* + * Print the message once to reduce log noise. It will be followed by + * another one if the temperature cannot be determined after multiple + * attempts. + */ + if (tz->recheck_delay_jiffies == THERMAL_RECHECK_DELAY) + dev_info(&tz->device, "Temperature check failed (%d)\n", error); + + thermal_zone_device_set_polling(tz, tz->recheck_delay_jiffies); + + tz->recheck_delay_jiffies += max(tz->recheck_delay_jiffies >> 1, 1ULL); + if (tz->recheck_delay_jiffies > THERMAL_MAX_RECHECK_DELAY) { + thermal_zone_broken_disable(tz); + /* + * Restore the original recheck delay value to allow the thermal + * zone to try to recover when it is reenabled by user space. + */ + tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; + } } static void monitor_thermal_zone(struct thermal_zone_device *tz) { - if (tz->mode != THERMAL_DEVICE_ENABLED) - thermal_zone_device_set_polling(tz, 0); - else if (tz->passive > 0) + if (tz->passive > 0 && tz->passive_delay_jiffies) thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); else if (tz->polling_delay_jiffies) thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); @@ -380,7 +443,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, td->threshold = trip->temperature; if (tz->last_temperature >= old_threshold && - tz->last_temperature != THERMAL_TEMP_INVALID) { + tz->last_temperature != THERMAL_TEMP_INIT) { /* * Mitigation is under way, so it needs to stop if the zone * temperature falls below the low temperature of the trip. @@ -415,27 +478,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, } } -static void update_temperature(struct thermal_zone_device *tz) -{ - int temp, ret; - - ret = __thermal_zone_get_temp(tz, &temp); - if (ret) { - if (ret != -EAGAIN) - dev_warn(&tz->device, - "failed to read out thermal zone (%d)\n", - ret); - return; - } - - tz->last_temperature = tz->temperature; - tz->temperature = temp; - - trace_thermal_temperature(tz); - - thermal_genl_sampling_temp(tz->id, temp); -} - static void thermal_zone_device_check(struct work_struct *work) { struct thermal_zone_device *tz = container_of(work, struct @@ -450,7 +492,7 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); - tz->temperature = THERMAL_TEMP_INVALID; + tz->temperature = THERMAL_TEMP_INIT; tz->passive = 0; tz->prev_low_trip = -INT_MAX; tz->prev_high_trip = INT_MAX; @@ -463,6 +505,9 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, const struct thermal_trip *trip, bool crossed_up) { + if (trip->type == THERMAL_TRIP_HOT || trip->type == THERMAL_TRIP_CRITICAL) + return; + if (governor->trip_crossed) governor->trip_crossed(tz, trip, crossed_up); } @@ -482,16 +527,14 @@ static void thermal_trip_crossed(struct thermal_zone_device *tz, thermal_governor_trip_crossed(governor, tz, trip, crossed_up); } -static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, +static int thermal_trip_notify_cmp(void *not_used, const struct list_head *a, const struct list_head *b) { struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, notify_list_node); struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, notify_list_node); - int ret = tdb->notify_temp - tda->notify_temp; - - return ascending ? ret : -ret; + return tda->notify_temp - tdb->notify_temp; } void __thermal_zone_device_update(struct thermal_zone_device *tz, @@ -501,31 +544,55 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, struct thermal_trip_desc *td; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); + int low = -INT_MAX, high = INT_MAX; + int temp, ret; - if (tz->suspended) + if (tz->suspended || tz->mode != THERMAL_DEVICE_ENABLED) return; - if (!thermal_zone_device_is_enabled(tz)) + ret = __thermal_zone_get_temp(tz, &temp); + if (ret) { + thermal_zone_recheck(tz, ret); return; + } else if (temp <= THERMAL_TEMP_INVALID) { + /* + * Special case: No valid temperature value is available, but + * the zone owner does not want the core to do anything about + * it. Continue regular zone polling if needed, so that this + * function can be called again, but skip everything else. + */ + goto monitor; + } - update_temperature(tz); + tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; - if (tz->temperature == THERMAL_TEMP_INVALID) - return; + tz->last_temperature = tz->temperature; + tz->temperature = temp; - __thermal_zone_set_trips(tz); + trace_thermal_temperature(tz); + + thermal_genl_sampling_temp(tz->id, temp); tz->notify_event = event; - for_each_trip_desc(tz, td) + for_each_trip_desc(tz, td) { handle_thermal_trip(tz, td, &way_up_list, &way_down_list); - list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp); + if (td->threshold <= tz->temperature && td->threshold > low) + low = td->threshold; + + if (td->threshold >= tz->temperature && td->threshold < high) + high = td->threshold; + } + + thermal_zone_set_trips(tz, low, high); + + list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); list_for_each_entry(td, &way_up_list, notify_list_node) thermal_trip_crossed(tz, &td->trip, governor, true); list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); - list_for_each_entry(td, &way_down_list, notify_list_node) + list_for_each_entry_reverse(td, &way_down_list, notify_list_node) thermal_trip_crossed(tz, &td->trip, governor, false); if (governor->manage) @@ -533,13 +600,14 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, thermal_debug_update_trip_stats(tz); +monitor: monitor_thermal_zone(tz); } static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode) { - int ret = 0; + int ret; mutex_lock(&tz->lock); @@ -547,14 +615,15 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, if (mode == tz->mode) { mutex_unlock(&tz->lock); - return ret; + return 0; } - if (tz->ops.change_mode) - ret = tz->ops.change_mode(tz, mode); + ret = __thermal_zone_device_set_mode(tz, mode); + if (ret) { + mutex_unlock(&tz->lock); - if (!ret) - tz->mode = mode; + return ret; + } __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); @@ -565,7 +634,7 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, else thermal_notify_tz_disable(tz); - return ret; + return 0; } int thermal_zone_device_enable(struct thermal_zone_device *tz) @@ -580,13 +649,6 @@ int thermal_zone_device_disable(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(thermal_zone_device_disable); -int thermal_zone_device_is_enabled(struct thermal_zone_device *tz) -{ - lockdep_assert_held(&tz->lock); - - return tz->mode == THERMAL_DEVICE_ENABLED; -} - static bool thermal_zone_is_present(struct thermal_zone_device *tz) { return !list_empty(&tz->node); @@ -690,15 +752,7 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * @tz: pointer to struct thermal_zone_device * @trip: trip point the cooling devices is associated with in this zone. * @cdev: pointer to struct thermal_cooling_device - * @upper: the Maximum cooling state for this trip point. - * THERMAL_NO_LIMIT means no upper limit, - * and the cooling device can be in max_state. - * @lower: the Minimum cooling state can be used for this trip point. - * THERMAL_NO_LIMIT means no lower limit, - * and the cooling device can be in cooling state 0. - * @weight: The weight of the cooling device to be bound to the - * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the - * default value + * @cool_spec: cooling specification for @trip and @cdev * * This interface function bind a thermal cooling device to the certain trip * point of a thermal zone device. @@ -706,55 +760,41 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * * Return: 0 on success, the proper error value otherwise. */ -int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, +static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) + struct cooling_spec *cool_spec) { struct thermal_instance *dev; struct thermal_instance *pos; - struct thermal_zone_device *pos1; - struct thermal_cooling_device *pos2; bool upper_no_limit; int result; - list_for_each_entry(pos1, &thermal_tz_list, node) { - if (pos1 == tz) - break; - } - list_for_each_entry(pos2, &thermal_cdev_list, node) { - if (pos2 == cdev) - break; - } - - if (tz != pos1 || cdev != pos2) - return -EINVAL; - /* lower default 0, upper default max_state */ - lower = lower == THERMAL_NO_LIMIT ? 0 : lower; + if (cool_spec->lower == THERMAL_NO_LIMIT) + cool_spec->lower = 0; - if (upper == THERMAL_NO_LIMIT) { - upper = cdev->max_state; + if (cool_spec->upper == THERMAL_NO_LIMIT) { + cool_spec->upper = cdev->max_state; upper_no_limit = true; } else { upper_no_limit = false; } - if (lower > upper || upper > cdev->max_state) + if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state) return -EINVAL; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - dev->tz = tz; + dev->cdev = cdev; dev->trip = trip; - dev->upper = upper; + dev->upper = cool_spec->upper; dev->upper_no_limit = upper_no_limit; - dev->lower = lower; + dev->lower = cool_spec->lower; dev->target = THERMAL_NO_TARGET; - dev->weight = weight; + dev->weight = cool_spec->weight; result = ida_alloc(&tz->ida, GFP_KERNEL); if (result < 0) @@ -788,10 +828,9 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (result) goto remove_trip_file; - mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { result = -EEXIST; break; } @@ -803,7 +842,6 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); if (!result) return 0; @@ -819,21 +857,6 @@ free_mem: kfree(dev); return result; } -EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip); - -int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, - upper, lower, weight); -} -EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. @@ -844,33 +867,28 @@ EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); * This interface function unbind a thermal cooling device from the certain * trip point of a thermal zone device. * This function is usually called in the thermal zone device .unbind callback. - * - * Return: 0 on success, the proper error value otherwise. */ -int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, - const struct thermal_trip *trip, - struct thermal_cooling_device *cdev) +static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) { struct thermal_instance *pos, *next; - mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { list_del(&pos->tz_node); list_del(&pos->cdev_node); thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); goto unbind; } } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); - return -ENODEV; + return; unbind: device_remove_file(&tz->device, &pos->weight_attr); @@ -878,20 +896,7 @@ unbind: sysfs_remove_link(&tz->device.kobj, pos->name); ida_free(&tz->ida, pos->id); kfree(pos); - return 0; } -EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip); - -int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); -} -EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); static void thermal_release(struct device *dev) { @@ -918,24 +923,41 @@ static struct class *thermal_class; static inline void print_bind_err_msg(struct thermal_zone_device *tz, + const struct thermal_trip *trip, struct thermal_cooling_device *cdev, int ret) { - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); + dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", + cdev->type, thermal_zone_trip_id(tz, trip), ret); } -static void bind_cdev(struct thermal_cooling_device *cdev) +static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) { - int ret; - struct thermal_zone_device *pos = NULL; + struct thermal_trip_desc *td; - list_for_each_entry(pos, &thermal_tz_list, node) { - if (pos->ops.bind) { - ret = pos->ops.bind(pos, cdev); - if (ret) - print_bind_err_msg(pos, cdev, ret); - } + if (!tz->ops.should_bind) + return; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) { + struct thermal_trip *trip = &td->trip; + struct cooling_spec c = { + .upper = THERMAL_NO_LIMIT, + .lower = THERMAL_NO_LIMIT, + .weight = THERMAL_WEIGHT_DEFAULT + }; + int ret; + + if (!tz->ops.should_bind(tz, trip, cdev, &c)) + continue; + + ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); + if (ret) + print_bind_err_msg(tz, trip, cdev, ret); } + + mutex_unlock(&tz->lock); } /** @@ -1033,7 +1055,8 @@ __thermal_cooling_device_register(struct device_node *np, list_add(&cdev->node, &thermal_cdev_list); /* Update binding information for 'this' new cdev */ - bind_cdev(cdev); + list_for_each_entry(pos, &thermal_tz_list, node) + thermal_zone_cdev_bind(pos, cdev); list_for_each_entry(pos, &thermal_tz_list, node) if (atomic_cmpxchg(&pos->need_update, 1, 0)) @@ -1126,7 +1149,7 @@ static void thermal_cooling_device_release(struct device *dev, void *res) struct thermal_cooling_device * devm_thermal_of_cooling_device_register(struct device *dev, struct device_node *np, - char *type, void *devdata, + const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device **ptr, *tcd; @@ -1234,6 +1257,19 @@ unlock_list: } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); +static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + struct thermal_trip_desc *td; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) + thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); + + mutex_unlock(&tz->lock); +} + /** * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. @@ -1260,10 +1296,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) list_del(&cdev->node); /* Unbind all thermal zones associated with 'this' cdev */ - list_for_each_entry(tz, &thermal_tz_list, node) { - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); - } + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); @@ -1271,32 +1305,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -static void bind_tz(struct thermal_zone_device *tz) -{ - int ret; - struct thermal_cooling_device *pos = NULL; - - if (!tz->ops.bind) - return; - - mutex_lock(&thermal_list_lock); - - list_for_each_entry(pos, &thermal_cdev_list, node) { - ret = tz->ops.bind(tz, pos); - if (ret) - print_bind_err_msg(tz, pos, ret); - } - - mutex_unlock(&thermal_list_lock); -} - -static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) -{ - *delay_jiffies = msecs_to_jiffies(delay_ms); - if (delay_ms > 1000) - *delay_jiffies = round_jiffies(*delay_jiffies); -} - int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { const struct thermal_trip_desc *td; @@ -1353,9 +1361,11 @@ thermal_zone_device_register_with_trips(const char *type, int num_trips, void *devdata, const struct thermal_zone_device_ops *ops, const struct thermal_zone_params *tzp, - int passive_delay, int polling_delay) + unsigned int passive_delay, + unsigned int polling_delay) { const struct thermal_trip *trip = trips; + struct thermal_cooling_device *cdev; struct thermal_zone_device *tz; struct thermal_trip_desc *td; int id; @@ -1379,13 +1389,16 @@ thermal_zone_device_register_with_trips(const char *type, } if (!ops || !ops->get_temp) { - pr_err("Thermal zone device ops not defined\n"); + pr_err("Thermal zone device ops not defined or invalid\n"); return ERR_PTR(-EINVAL); } if (num_trips > 0 && !trips) return ERR_PTR(-EINVAL); + if (polling_delay && passive_delay > polling_delay) + return ERR_PTR(-EINVAL); + if (!thermal_class) return ERR_PTR(-ENODEV); @@ -1433,8 +1446,9 @@ thermal_zone_device_register_with_trips(const char *type, td->threshold = INT_MAX; } - thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); - thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); + tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); + tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); + tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; /* sys I/F */ /* Add nodes that are always present via .groups */ @@ -1477,13 +1491,16 @@ thermal_zone_device_register_with_trips(const char *type, } mutex_lock(&thermal_list_lock); + mutex_lock(&tz->lock); list_add_tail(&tz->node, &thermal_tz_list); mutex_unlock(&tz->lock); - mutex_unlock(&thermal_list_lock); /* Bind cooling devices for this zone */ - bind_tz(tz); + list_for_each_entry(cdev, &thermal_cdev_list, node) + thermal_zone_cdev_bind(tz, cdev); + + mutex_unlock(&thermal_list_lock); thermal_zone_device_init(tz); /* Update the new thermal zone and mark it as already updated. */ @@ -1575,8 +1592,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) /* Unbind all cdevs associated with 'this' thermal zone */ list_for_each_entry(cdev, &thermal_cdev_list, node) - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); @@ -1649,8 +1665,10 @@ static void thermal_zone_device_resume(struct work_struct *work) tz->suspended = false; + thermal_debug_tz_resume(tz); thermal_zone_device_init(tz); - __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + thermal_governor_update_tz(tz, THERMAL_TZ_RESUME); + __thermal_zone_device_update(tz, THERMAL_TZ_RESUME); complete(&tz->resume); tz->resuming = false; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 66f67e54e0c8..50b858aa173a 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -15,8 +15,20 @@ #include "thermal_netlink.h" #include "thermal_debugfs.h" +struct thermal_attr { + struct device_attribute attr; + char name[THERMAL_NAME_LENGTH]; +}; + +struct thermal_trip_attrs { + struct thermal_attr type; + struct thermal_attr temp; + struct thermal_attr hyst; +}; + struct thermal_trip_desc { struct thermal_trip trip; + struct thermal_trip_attrs trip_attrs; struct list_head notify_list_node; int notify_temp; int threshold; @@ -56,9 +68,6 @@ struct thermal_governor { * @device: &struct device for this thermal zone * @removal: removal completion * @resume: resume completion - * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature - * @trip_type_attrs: attributes for trip points for sysfs: trip type - * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis * @mode: current mode of this thermal zone * @devdata: private pointer for device private data * @num_trips: number of trip points the thermal zone supports @@ -67,6 +76,8 @@ struct thermal_governor { * @polling_delay_jiffies: number of jiffies to wait between polls when * checking whether trip points have been crossed (0 for * interrupt driven systems) + * @recheck_delay_jiffies: delay after a failed attempt to determine the zone + * temperature before trying again * @temperature: current temperature. This is only for core code, * drivers should use thermal_zone_get_temp() to get the * current temperature @@ -100,14 +111,12 @@ struct thermal_zone_device { struct completion removal; struct completion resume; struct attribute_group trips_attribute_group; - struct thermal_attr *trip_temp_attrs; - struct thermal_attr *trip_type_attrs; - struct thermal_attr *trip_hyst_attrs; enum thermal_device_mode mode; void *devdata; int num_trips; unsigned long passive_delay_jiffies; unsigned long polling_delay_jiffies; + unsigned long recheck_delay_jiffies; int temperature; int last_temperature; int emul_temperature; @@ -133,6 +142,16 @@ struct thermal_zone_device { struct thermal_trip_desc trips[] __counted_by(num_trips); }; +/* Initial thermal zone temperature. */ +#define THERMAL_TEMP_INIT INT_MIN + +/* + * Default and maximum delay after a failed thermal zone temperature check + * before attempting to check it again (in jiffies). + */ +#define THERMAL_RECHECK_DELAY msecs_to_jiffies(250) +#define THERMAL_MAX_RECHECK_DELAY (120 * HZ) + /* Default Thermal Governor */ #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) #define DEFAULT_THERMAL_GOVERNOR "step_wise" @@ -175,11 +194,6 @@ int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), struct thermal_zone_device *thermal_zone_get_by_id(int id); -struct thermal_attr { - struct device_attribute attr; - char name[THERMAL_NAME_LENGTH]; -}; - static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) { return cdev->ops->get_requested_power && cdev->ops->state2power && @@ -191,11 +205,6 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev); int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip); -struct thermal_instance * -get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, - int trip); - /* * This structure is used to describe the behavior of * a certain cooling device on a certain trip point @@ -204,7 +213,6 @@ get_thermal_instance(struct thermal_zone_device *tz, struct thermal_instance { int id; char name[THERMAL_NAME_LENGTH]; - struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; const struct thermal_trip *trip; bool initialized; @@ -244,14 +252,16 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz, #define trip_to_trip_desc(__trip) \ container_of(__trip, struct thermal_trip_desc, trip) -void __thermal_zone_set_trips(struct thermal_zone_device *tz); +const char *thermal_trip_type_name(enum thermal_trip_type trip_type); + +void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high); int thermal_zone_trip_id(const struct thermal_zone_device *tz, const struct thermal_trip *trip); -void thermal_zone_trip_updated(struct thermal_zone_device *tz, - const struct thermal_trip *trip); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); void thermal_zone_trip_down(struct thermal_zone_device *tz, const struct thermal_trip *trip); +void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, + struct thermal_trip *trip, int hyst); /* sysfs I/F */ int thermal_zone_create_device_groups(struct thermal_zone_device *tz); @@ -274,7 +284,4 @@ thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, unsigned long new_state) {} #endif /* CONFIG_THERMAL_STATISTICS */ -/* device tree support */ -int thermal_zone_device_is_enabled(struct thermal_zone_device *tz); - #endif /* __THERMAL_CORE_H__ */ diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 942447229157..939d3e5f1817 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -94,7 +94,6 @@ struct cdev_record { * @trip_temp: trip temperature at mitigation start * @trip_hyst: trip hysteresis at mitigation start * @count: the number of times the zone temperature was above the trip point - * @max: maximum recorded temperature above the trip point * @min: minimum recorded temperature above the trip point * @avg: average temperature above the trip point */ @@ -104,7 +103,6 @@ struct trip_stats { int trip_temp; int trip_hyst; int count; - int max; int min; int avg; }; @@ -122,12 +120,14 @@ struct trip_stats { * @timestamp: first trip point crossed the way up * @duration: total duration of the mitigation episode * @node: a list element to be added to the list of tz events + * @max_temp: maximum zone temperature during this episode * @trip_stats: per trip point statistics, flexible array */ struct tz_episode { ktime_t timestamp; ktime_t duration; struct list_head node; + int max_temp; struct trip_stats trip_stats[]; }; @@ -178,11 +178,11 @@ struct thermal_debugfs { void thermal_debug_init(void) { d_root = debugfs_create_dir("thermal", NULL); - if (!d_root) + if (IS_ERR(d_root)) return; d_cdev = debugfs_create_dir("cooling_devices", d_root); - if (!d_cdev) + if (IS_ERR(d_cdev)) return; d_tz = debugfs_create_dir("thermal_zones", d_root); @@ -202,7 +202,7 @@ static struct thermal_debugfs *thermal_debugfs_add_id(struct dentry *d, int id) snprintf(ids, IDSLENGTH, "%d", id); thermal_dbg->d_top = debugfs_create_dir(ids, d); - if (!thermal_dbg->d_top) { + if (IS_ERR(thermal_dbg->d_top)) { kfree(thermal_dbg); return NULL; } @@ -561,10 +561,11 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev INIT_LIST_HEAD(&tze->node); tze->timestamp = now; tze->duration = KTIME_MIN; + tze->max_temp = INT_MIN; for (i = 0; i < tz->num_trips; i++) { + tze->trip_stats[i].trip_temp = THERMAL_TEMP_INVALID; tze->trip_stats[i].min = INT_MAX; - tze->trip_stats[i].max = INT_MIN; } return tze; @@ -573,20 +574,20 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, const struct thermal_trip *trip) { - struct tz_episode *tze; - struct tz_debugfs *tz_dbg; struct thermal_debugfs *thermal_dbg = tz->debugfs; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); struct trip_stats *trip_stats; + struct tz_debugfs *tz_dbg; + struct tz_episode *tze; if (!thermal_dbg) return; - mutex_lock(&thermal_dbg->lock); - tz_dbg = &thermal_dbg->tz_dbg; + mutex_lock(&thermal_dbg->lock); + /* * The mitigation is starting. A mitigation can contain * several episodes where each of them is related to a @@ -653,23 +654,33 @@ unlock: mutex_unlock(&thermal_dbg->lock); } +static void tz_episode_close_trip(struct tz_episode *tze, int trip_id, ktime_t now) +{ + struct trip_stats *trip_stats = &tze->trip_stats[trip_id]; + ktime_t delta = ktime_sub(now, trip_stats->timestamp); + + trip_stats->duration = ktime_add(delta, trip_stats->duration); + /* Mark the end of mitigation for this trip point. */ + trip_stats->timestamp = KTIME_MAX; +} + void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, const struct thermal_trip *trip) { struct thermal_debugfs *thermal_dbg = tz->debugfs; + int trip_id = thermal_zone_trip_id(tz, trip); + ktime_t now = ktime_get(); struct tz_episode *tze; struct tz_debugfs *tz_dbg; - ktime_t delta, now = ktime_get(); - int trip_id = thermal_zone_trip_id(tz, trip); int i; if (!thermal_dbg) return; - mutex_lock(&thermal_dbg->lock); - tz_dbg = &thermal_dbg->tz_dbg; + mutex_lock(&thermal_dbg->lock); + /* * The temperature crosses the way down but there was not * mitigation detected before. That may happen when the @@ -695,13 +706,7 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - delta = ktime_sub(now, tze->trip_stats[trip_id].timestamp); - - tze->trip_stats[trip_id].duration = - ktime_add(delta, tze->trip_stats[trip_id].duration); - - /* Mark the end of mitigation for this trip point. */ - tze->trip_stats[trip_id].timestamp = KTIME_MAX; + tz_episode_close_trip(tze, trip_id, now); /* * This event closes the mitigation as we are crossing the @@ -724,20 +729,22 @@ void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) if (!thermal_dbg) return; - mutex_lock(&thermal_dbg->lock); - tz_dbg = &thermal_dbg->tz_dbg; + mutex_lock(&thermal_dbg->lock); + if (!tz_dbg->nr_trips) goto out; tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + if (tz->temperature > tze->max_temp) + tze->max_temp = tz->temperature; + for (i = 0; i < tz_dbg->nr_trips; i++) { int trip_id = tz_dbg->trips_crossed[i]; struct trip_stats *trip_stats = &tze->trip_stats[trip_id]; - trip_stats->max = max(trip_stats->max, tz->temperature); trip_stats->min = min(trip_stats->min, tz->temperature); trip_stats->avg += (tz->temperature - trip_stats->avg) / ++trip_stats->count; @@ -777,7 +784,6 @@ static int tze_seq_show(struct seq_file *s, void *v) struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz; struct thermal_trip_desc *td; struct tz_episode *tze; - const char *type; u64 duration_ms; int trip_id; char c; @@ -793,10 +799,10 @@ static int tze_seq_show(struct seq_file *s, void *v) c = '='; } - seq_printf(s, ",-Mitigation at %lluus, duration%c%llums\n", - ktime_to_us(tze->timestamp), c, duration_ms); + seq_printf(s, ",-Mitigation at %llums, duration%c%llums, max. temp=%dm°C\n", + ktime_to_ms(tze->timestamp), c, duration_ms, tze->max_temp); - seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + seq_printf(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n"); for_each_trip_desc(tz, td) { const struct thermal_trip *trip = &td->trip; @@ -814,16 +820,9 @@ static int tze_seq_show(struct seq_file *s, void *v) trip_stats = &tze->trip_stats[trip_id]; /* Skip trips without any stats. */ - if (trip_stats->min > trip_stats->max) + if (trip_stats->trip_temp == THERMAL_TEMP_INVALID) continue; - if (trip->type == THERMAL_TRIP_PASSIVE) - type = "passive"; - else if (trip->type == THERMAL_TRIP_ACTIVE) - type = "active"; - else - type = "hot"; - if (trip_stats->timestamp != KTIME_MAX) { /* Mitigation in progress. */ ktime_t delta = ktime_sub(ktime_get(), @@ -837,15 +836,14 @@ static int tze_seq_show(struct seq_file *s, void *v) c = ' '; } - seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", + seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d |\n", 4 , trip_id, - 8, type, + 8, thermal_trip_type_name(trip->type), 9, trip_stats->trip_temp, 9, trip_stats->trip_hyst, - c, 10, duration_ms, + c, 11, duration_ms, 9, trip_stats->avg, - 9, trip_stats->min, - 9, trip_stats->max); + 9, trip_stats->min); } return 0; @@ -922,3 +920,39 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz) thermal_debugfs_remove_id(thermal_dbg); kfree(trips_crossed); } + +void thermal_debug_tz_resume(struct thermal_zone_device *tz) +{ + struct thermal_debugfs *thermal_dbg = tz->debugfs; + ktime_t now = ktime_get(); + struct tz_debugfs *tz_dbg; + struct tz_episode *tze; + int i; + + if (!thermal_dbg) + return; + + mutex_lock(&thermal_dbg->lock); + + tz_dbg = &thermal_dbg->tz_dbg; + + if (!tz_dbg->nr_trips) + goto out; + + /* + * A mitigation episode was in progress before the preceding system + * suspend transition, so close it because the zone handling is starting + * over from scratch. + */ + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + + for (i = 0; i < tz_dbg->nr_trips; i++) + tz_episode_close_trip(tze, tz_dbg->trips_crossed[i], now); + + tze->duration = ktime_sub(now, tze->timestamp); + + tz_dbg->nr_trips = 0; + +out: + mutex_unlock(&thermal_dbg->lock); +} diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h index 74ee65ee82ff..1c957ce2ec8f 100644 --- a/drivers/thermal/thermal_debugfs.h +++ b/drivers/thermal/thermal_debugfs.h @@ -7,6 +7,7 @@ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev); void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state); void thermal_debug_tz_add(struct thermal_zone_device *tz); void thermal_debug_tz_remove(struct thermal_zone_device *tz); +void thermal_debug_tz_resume(struct thermal_zone_device *tz); void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, const struct thermal_trip *trip); void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, @@ -20,6 +21,7 @@ static inline void thermal_debug_cdev_state_update(const struct thermal_cooling_ int state) {} static inline void thermal_debug_tz_add(struct thermal_zone_device *tz) {} static inline void thermal_debug_tz_remove(struct thermal_zone_device *tz) {} +static inline void thermal_debug_tz_resume(struct thermal_zone_device *tz) {} static inline void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, const struct thermal_trip *trip) {}; static inline void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index d9f4e26ec125..dc374a7a1a65 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -39,32 +39,37 @@ int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip return trend; } -struct thermal_instance * -get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int trip_index) +static bool thermal_instance_present(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, + const struct thermal_trip *trip) { - struct thermal_instance *pos = NULL; - struct thermal_instance *target_instance = NULL; - const struct thermal_trip *trip; + struct thermal_instance *ti; + + list_for_each_entry(ti, &tz->thermal_instances, tz_node) { + if (ti->trip == trip && ti->cdev == cdev) + return true; + } + + return false; +} + +bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) +{ + bool ret; mutex_lock(&tz->lock); mutex_lock(&cdev->lock); - trip = &tz->trips[trip_index].trip; - - list_for_each_entry(pos, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - target_instance = pos; - break; - } - } + ret = thermal_instance_present(tz, cdev, trip); mutex_unlock(&cdev->lock); mutex_unlock(&tz->lock); - return target_instance; + return ret; } -EXPORT_SYMBOL(get_thermal_instance); +EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); /** * __thermal_zone_get_temp() - returns the temperature of a thermal zone @@ -140,6 +145,8 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) } ret = __thermal_zone_get_temp(tz, temp); + if (!ret && *temp <= THERMAL_TEMP_INVALID) + ret = -ENODATA; unlock: mutex_unlock(&tz->lock); @@ -174,8 +181,6 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) /* Make sure cdev enters the deepest cooling state */ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { - dev_dbg(&cdev->device, "zone%d->target=%lu\n", - instance->tz->id, instance->target); if (instance->target == THERMAL_NO_TARGET) continue; if (instance->target > target) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index aa34b6e82e26..a4caf7899f8e 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -20,37 +20,6 @@ /*** functions parsing device tree nodes ***/ -static int of_find_trip_id(struct device_node *np, struct device_node *trip) -{ - struct device_node *trips; - struct device_node *t; - int i = 0; - - trips = of_get_child_by_name(np, "trips"); - if (!trips) { - pr_err("Failed to find 'trips' node\n"); - return -EINVAL; - } - - /* - * Find the trip id point associated with the cooling device map - */ - for_each_child_of_node(trips, t) { - - if (t == trip) { - of_node_put(t); - goto out; - } - i++; - } - - i = -ENXIO; -out: - of_node_put(trips); - - return i; -} - /* * It maps 'enum thermal_trip_type' found in include/linux/thermal.h * into the device tree binding of 'trip', property type. @@ -119,13 +88,15 @@ static int thermal_of_populate_trip(struct device_node *np, trip->flags = THERMAL_TRIP_FLAG_RW_TEMP; + trip->priv = np; + return 0; } static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *ntrips) { struct thermal_trip *tt; - struct device_node *trips, *trip; + struct device_node *trips; int ret, count; trips = of_get_child_by_name(np, "trips"); @@ -150,7 +121,7 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n *ntrips = count; count = 0; - for_each_child_of_node(trips, trip) { + for_each_child_of_node_scoped(trips, trip) { ret = thermal_of_populate_trip(trip, &tt[count++]); if (ret) goto out_kfree; @@ -184,14 +155,14 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int * Search for each thermal zone, a defined sensor * corresponding to the one passed as parameter */ - for_each_available_child_of_node(np, tz) { + for_each_available_child_of_node_scoped(np, child) { int count, i; - count = of_count_phandle_with_args(tz, "thermal-sensors", + count = of_count_phandle_with_args(child, "thermal-sensors", "#thermal-sensor-cells"); if (count <= 0) { - pr_err("%pOFn: missing thermal sensor\n", tz); + pr_err("%pOFn: missing thermal sensor\n", child); tz = ERR_PTR(-EINVAL); goto out; } @@ -200,18 +171,19 @@ static struct device_node *of_thermal_zone_find(struct device_node *sensor, int int ret; - ret = of_parse_phandle_with_args(tz, "thermal-sensors", + ret = of_parse_phandle_with_args(child, "thermal-sensors", "#thermal-sensor-cells", i, &sensor_specs); if (ret < 0) { - pr_err("%pOFn: Failed to read thermal-sensors cells: %d\n", tz, ret); + pr_err("%pOFn: Failed to read thermal-sensors cells: %d\n", child, ret); tz = ERR_PTR(ret); goto out; } if ((sensor == sensor_specs.np) && id == (sensor_specs.args_count ? sensor_specs.args[0] : 0)) { - pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, tz); + pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, child); + tz = no_free_ptr(child); goto out; } } @@ -290,39 +262,9 @@ static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_devic return tz_np; } -static int __thermal_of_unbind(struct device_node *map_np, int index, int trip_id, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) -{ - struct of_phandle_args cooling_spec; - int ret; - - ret = of_parse_phandle_with_args(map_np, "cooling-device", "#cooling-cells", - index, &cooling_spec); - - if (ret < 0) { - pr_err("Invalid cooling-device entry\n"); - return ret; - } - - of_node_put(cooling_spec.np); - - if (cooling_spec.args_count < 2) { - pr_err("wrong reference to cooling device, missing limits\n"); - return -EINVAL; - } - - if (cooling_spec.np != cdev->np) - return 0; - - ret = thermal_zone_unbind_cooling_device(tz, trip_id, cdev); - if (ret) - pr_err("Failed to unbind '%s' with '%s': %d\n", tz->type, cdev->type, ret); - - return ret; -} - -static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev) +static bool thermal_of_get_cooling_spec(struct device_node *map_np, int index, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { struct of_phandle_args cooling_spec; int ret, weight = THERMAL_WEIGHT_DEFAULT; @@ -334,104 +276,73 @@ static int __thermal_of_bind(struct device_node *map_np, int index, int trip_id, if (ret < 0) { pr_err("Invalid cooling-device entry\n"); - return ret; + return false; } of_node_put(cooling_spec.np); if (cooling_spec.args_count < 2) { pr_err("wrong reference to cooling device, missing limits\n"); - return -EINVAL; + return false; } if (cooling_spec.np != cdev->np) - return 0; - - ret = thermal_zone_bind_cooling_device(tz, trip_id, cdev, cooling_spec.args[1], - cooling_spec.args[0], - weight); - if (ret) - pr_err("Failed to bind '%s' with '%s': %d\n", tz->type, cdev->type, ret); - - return ret; -} - -static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np, - struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, - int (*action)(struct device_node *, int, int, - struct thermal_zone_device *, struct thermal_cooling_device *)) -{ - struct device_node *tr_np; - int count, i, trip_id; - - tr_np = of_parse_phandle(map_np, "trip", 0); - if (!tr_np) - return -ENODEV; - - trip_id = of_find_trip_id(tz_np, tr_np); - if (trip_id < 0) - return trip_id; - - count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells"); - if (count <= 0) { - pr_err("Add a cooling_device property with at least one device\n"); - return -ENOENT; - } + return false; - /* - * At this point, we don't want to bail out when there is an - * error, we will try to bind/unbind as many as possible - * cooling devices - */ - for (i = 0; i < count; i++) - action(map_np, i, trip_id, tz, cdev); + c->lower = cooling_spec.args[0]; + c->upper = cooling_spec.args[1]; + c->weight = weight; - return 0; + return true; } -static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, - int (*action)(struct device_node *, int, int, - struct thermal_zone_device *, struct thermal_cooling_device *)) +static bool thermal_of_should_bind(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev, + struct cooling_spec *c) { struct device_node *tz_np, *cm_np, *child; - int ret = 0; + bool result = false; tz_np = thermal_of_zone_get_by_name(tz); if (IS_ERR(tz_np)) { pr_err("Failed to get node tz by name\n"); - return PTR_ERR(tz_np); + return false; } cm_np = of_get_child_by_name(tz_np, "cooling-maps"); if (!cm_np) goto out; + /* Look up the trip and the cdev in the cooling maps. */ for_each_child_of_node(cm_np, child) { - ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); - if (ret) { - of_node_put(child); - break; + struct device_node *tr_np; + int count, i; + + tr_np = of_parse_phandle(child, "trip", 0); + if (tr_np != trip->priv) + continue; + + /* The trip has been found, look up the cdev. */ + count = of_count_phandle_with_args(child, "cooling-device", "#cooling-cells"); + if (count <= 0) + pr_err("Add a cooling_device property with at least one device\n"); + + for (i = 0; i < count; i++) { + result = thermal_of_get_cooling_spec(child, i, cdev, c); + if (result) + break; } + + of_node_put(child); + break; } of_node_put(cm_np); out: of_node_put(tz_np); - return ret; -} - -static int thermal_of_bind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) -{ - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_bind); -} - -static int thermal_of_unbind(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev) -{ - return thermal_of_for_each_cooling_maps(tz, cdev, __thermal_of_unbind); + return result; } /** @@ -491,7 +402,8 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node * trips = thermal_of_trips_init(np, &ntrips); if (IS_ERR(trips)) { pr_err("Failed to find trip points for %pOFn id=%d\n", sensor, id); - return ERR_CAST(trips); + ret = PTR_ERR(trips); + goto out_of_node_put; } ret = thermal_of_monitor_init(np, &delay, &pdelay); @@ -502,8 +414,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node * thermal_of_parameters_init(np, &tzp); - of_ops.bind = thermal_of_bind; - of_ops.unbind = thermal_of_unbind; + of_ops.should_bind = thermal_of_should_bind; ret = of_property_read_string(np, "critical-action", &action); if (!ret) @@ -519,6 +430,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node * goto out_kfree_trips; } + of_node_put(np); kfree(trips); ret = thermal_zone_device_enable(tz); @@ -533,6 +445,8 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node * out_kfree_trips: kfree(trips); +out_of_node_put: + of_node_put(np); return ERR_PTR(ret); } diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 88211ccdfbd6..1838aa729bb5 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/container_of.h> #include <linux/sysfs.h> #include <linux/device.h> #include <linux/err.h> @@ -52,7 +53,7 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf) int enabled; mutex_lock(&tz->lock); - enabled = thermal_zone_device_is_enabled(tz); + enabled = tz->mode == THERMAL_DEVICE_ENABLED; mutex_unlock(&tz->lock); return sprintf(buf, "%s\n", enabled ? "enabled" : "disabled"); @@ -78,62 +79,58 @@ mode_store(struct device *dev, struct device_attribute *attr, return count; } +#define thermal_trip_of_attr(_ptr_, _attr_) \ + ({ \ + struct thermal_trip_desc *td; \ + \ + td = container_of(_ptr_, struct thermal_trip_desc, \ + trip_attrs._attr_.attr); \ + &td->trip; \ + }) + static ssize_t trip_point_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; + struct thermal_trip *trip = thermal_trip_of_attr(attr, type); - if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) - return -EINVAL; - - switch (tz->trips[trip_id].trip.type) { - case THERMAL_TRIP_CRITICAL: - return sprintf(buf, "critical\n"); - case THERMAL_TRIP_HOT: - return sprintf(buf, "hot\n"); - case THERMAL_TRIP_PASSIVE: - return sprintf(buf, "passive\n"); - case THERMAL_TRIP_ACTIVE: - return sprintf(buf, "active\n"); - default: - return sprintf(buf, "unknown\n"); - } + return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type)); } static ssize_t trip_point_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_trip *trip; - int trip_id, ret; - int temp; + int ret, temp; ret = kstrtoint(buf, 10, &temp); if (ret) return -EINVAL; - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; - mutex_lock(&tz->lock); - trip = &tz->trips[trip_id].trip; - - if (temp != trip->temperature) { - if (tz->ops.set_trip_temp) { - ret = tz->ops.set_trip_temp(tz, trip_id, temp); - if (ret) - goto unlock; - } + if (temp == trip->temperature) + goto unlock; - thermal_zone_set_trip_temp(tz, trip, temp); + /* Arrange the condition to avoid integer overflows. */ + if (temp != THERMAL_TEMP_INVALID && + temp <= trip->hysteresis + THERMAL_TEMP_INVALID) { + ret = -EINVAL; + goto unlock; + } - __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); + if (tz->ops.set_trip_temp) { + ret = tz->ops.set_trip_temp(tz, trip, temp); + if (ret) + goto unlock; } + thermal_zone_set_trip_temp(tz, trip, temp); + + __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); + unlock: mutex_unlock(&tz->lock); @@ -144,57 +141,61 @@ static ssize_t trip_point_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; + struct thermal_trip *trip = thermal_trip_of_attr(attr, temp); - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) - return -EINVAL; - - return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature); + return sprintf(buf, "%d\n", READ_ONCE(trip->temperature)); } static ssize_t trip_point_hyst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_trip *trip; - int trip_id, ret; - int hyst; + int ret, hyst; ret = kstrtoint(buf, 10, &hyst); if (ret || hyst < 0) return -EINVAL; - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) - return -EINVAL; - mutex_lock(&tz->lock); - trip = &tz->trips[trip_id].trip; + if (hyst == trip->hysteresis) + goto unlock; - if (hyst != trip->hysteresis) { - trip->hysteresis = hyst; + /* + * Allow the hysteresis to be updated when the temperature is invalid + * to allow user space to avoid having to adjust hysteresis after a + * valid temperature has been set, but in that case just change the + * value and do nothing else. + */ + if (trip->temperature == THERMAL_TEMP_INVALID) { + WRITE_ONCE(trip->hysteresis, hyst); + goto unlock; + } - thermal_zone_trip_updated(tz, trip); + if (trip->temperature - hyst <= THERMAL_TEMP_INVALID) { + ret = -EINVAL; + goto unlock; } + thermal_zone_set_trip_hyst(tz, trip, hyst); + + __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); + +unlock: mutex_unlock(&tz->lock); - return count; + return ret ? ret : count; } static ssize_t trip_point_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip_id; + struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst); - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) - return -EINVAL; - - return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis); + return sprintf(buf, "%d\n", READ_ONCE(trip->hysteresis)); } static ssize_t @@ -393,87 +394,55 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz) { - const struct thermal_trip_desc *td; + struct thermal_trip_desc *td; struct attribute **attrs; - - /* This function works only for zones with at least one trip */ - if (tz->num_trips <= 0) - return -EINVAL; - - tz->trip_type_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_type_attrs), - GFP_KERNEL); - if (!tz->trip_type_attrs) - return -ENOMEM; - - tz->trip_temp_attrs = kcalloc(tz->num_trips, sizeof(*tz->trip_temp_attrs), - GFP_KERNEL); - if (!tz->trip_temp_attrs) { - kfree(tz->trip_type_attrs); - return -ENOMEM; - } - - tz->trip_hyst_attrs = kcalloc(tz->num_trips, - sizeof(*tz->trip_hyst_attrs), - GFP_KERNEL); - if (!tz->trip_hyst_attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - return -ENOMEM; - } + int i; attrs = kcalloc(tz->num_trips * 3 + 1, sizeof(*attrs), GFP_KERNEL); - if (!attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); + if (!attrs) return -ENOMEM; - } + i = 0; for_each_trip_desc(tz, td) { - int indx = thermal_zone_trip_id(tz, &td->trip); + struct thermal_trip_attrs *trip_attrs = &td->trip_attrs; /* create trip type attribute */ - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", indx); + snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH, + "trip_point_%d_type", i); - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); - tz->trip_type_attrs[indx].attr.attr.name = - tz->trip_type_attrs[indx].name; - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; + sysfs_attr_init(&trip_attrs->type.attr.attr); + trip_attrs->type.attr.attr.name = trip_attrs->type.name; + trip_attrs->type.attr.attr.mode = S_IRUGO; + trip_attrs->type.attr.show = trip_point_type_show; + attrs[i] = &trip_attrs->type.attr.attr; /* create trip temp attribute */ - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", indx); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; + snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", i); + + sysfs_attr_init(&trip_attrs->temp.attr.attr); + trip_attrs->temp.attr.attr.name = trip_attrs->temp.name; + trip_attrs->temp.attr.attr.mode = S_IRUGO; + trip_attrs->temp.attr.show = trip_point_temp_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_TEMP) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; + trip_attrs->temp.attr.attr.mode |= S_IWUSR; + trip_attrs->temp.attr.store = trip_point_temp_store; } - attrs[indx + tz->num_trips] = &tz->trip_temp_attrs[indx].attr.attr; + attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); + snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH, + "trip_point_%d_hyst", i); - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; + sysfs_attr_init(&trip_attrs->hyst.attr.attr); + trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name; + trip_attrs->hyst.attr.attr.mode = S_IRUGO; + trip_attrs->hyst.attr.show = trip_point_hyst_show; if (td->trip.flags & THERMAL_TRIP_FLAG_RW_HYST) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; + trip_attrs->hyst.attr.attr.mode |= S_IWUSR; + trip_attrs->hyst.attr.store = trip_point_hyst_store; } - attrs[indx + tz->num_trips * 2] = - &tz->trip_hyst_attrs[indx].attr.attr; + attrs[i + 2 * tz->num_trips] = &trip_attrs->hyst.attr.attr; + i++; } attrs[tz->num_trips * 3] = NULL; @@ -490,13 +459,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) */ static void destroy_trip_attrs(struct thermal_zone_device *tz) { - if (!tz) - return; - - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); - kfree(tz->trips_attribute_group.attrs); + if (tz) + kfree(tz->trips_attribute_group.attrs); } int thermal_zone_create_device_groups(struct thermal_zone_device *tz) @@ -898,13 +862,12 @@ void thermal_cooling_device_stats_reinit(struct thermal_cooling_device *cdev) ssize_t trip_point_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct thermal_zone_device *tz = to_thermal_zone(dev); struct thermal_instance *instance; - instance = - container_of(attr, struct thermal_instance, attr); + instance = container_of(attr, struct thermal_instance, attr); - return sprintf(buf, "%d\n", - thermal_zone_trip_id(instance->tz, instance->trip)); + return sprintf(buf, "%d\n", thermal_zone_trip_id(tz, instance->trip)); } ssize_t @@ -920,6 +883,7 @@ weight_show(struct device *dev, struct device_attribute *attr, char *buf) ssize_t weight_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct thermal_zone_device *tz = to_thermal_zone(dev); struct thermal_instance *instance; int ret, weight; @@ -930,14 +894,13 @@ ssize_t weight_store(struct device *dev, struct device_attribute *attr, instance = container_of(attr, struct thermal_instance, weight_attr); /* Don't race with governors using the 'weight' value */ - mutex_lock(&instance->tz->lock); + mutex_lock(&tz->lock); instance->weight = weight; - thermal_governor_update_tz(instance->tz, - THERMAL_INSTANCE_WEIGHT_CHANGED); + thermal_governor_update_tz(tz, THERMAL_INSTANCE_WEIGHT_CHANGED); - mutex_unlock(&instance->tz->lock); + mutex_unlock(&tz->lock); return count; } diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 49e63db68517..b53fac333ec5 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -9,6 +9,21 @@ */ #include "thermal_core.h" +static const char *trip_type_names[] = { + [THERMAL_TRIP_ACTIVE] = "active", + [THERMAL_TRIP_PASSIVE] = "passive", + [THERMAL_TRIP_HOT] = "hot", + [THERMAL_TRIP_CRITICAL] = "critical", +}; + +const char *thermal_trip_type_name(enum thermal_trip_type trip_type) +{ + if (trip_type < THERMAL_TRIP_ACTIVE || trip_type > THERMAL_TRIP_CRITICAL) + return "unknown"; + + return trip_type_names[trip_type]; +} + int for_each_thermal_trip(struct thermal_zone_device *tz, int (*cb)(struct thermal_trip *, void *), void *data) @@ -40,31 +55,8 @@ int thermal_zone_for_each_trip(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); -int thermal_zone_get_num_trips(struct thermal_zone_device *tz) -{ - return tz->num_trips; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); - -/** - * __thermal_zone_set_trips - Computes the next trip points for the driver - * @tz: a pointer to a thermal zone device structure - * - * The function computes the next temperature boundaries by browsing - * the trip points. The result is the closer low and high trip points - * to the current temperature. These values are passed to the backend - * driver to let it set its own notification mechanism (usually an - * interrupt). - * - * This function must be called with tz->lock held. Both tz and tz->ops - * must be valid pointers. - * - * It does not return a value - */ -void __thermal_zone_set_trips(struct thermal_zone_device *tz) +void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high) { - const struct thermal_trip_desc *td; - int low = -INT_MAX, high = INT_MAX; int ret; lockdep_assert_held(&tz->lock); @@ -72,20 +64,6 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) if (!tz->ops.set_trips) return; - for_each_trip_desc(tz, td) { - const struct thermal_trip *trip = &td->trip; - int trip_low; - - trip_low = trip->temperature - trip->hysteresis; - - if (trip_low < tz->temperature && trip_low > low) - low = trip_low; - - if (trip->temperature > tz->temperature && - trip->temperature < high) - high = trip->temperature; - } - /* No need to change trip points */ if (tz->prev_low_trip == low && tz->prev_high_trip == high) return; @@ -105,30 +83,6 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) dev_err(&tz->device, "Failed to set trips: %d\n", ret); } -int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, - struct thermal_trip *trip) -{ - if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) - return -EINVAL; - - *trip = tz->trips[trip_id].trip; - return 0; -} -EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); - -int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, - struct thermal_trip *trip) -{ - int ret; - - mutex_lock(&tz->lock); - ret = __thermal_zone_get_trip(tz, trip_id, trip); - mutex_unlock(&tz->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_trip); - int thermal_zone_trip_id(const struct thermal_zone_device *tz, const struct thermal_trip *trip) { @@ -139,11 +93,11 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, return trip_to_trip_desc(trip) - tz->trips; } -void thermal_zone_trip_updated(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, + struct thermal_trip *trip, int hyst) { + WRITE_ONCE(trip->hysteresis, hyst); thermal_notify_tz_trip_change(tz, trip); - __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); } void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, @@ -152,7 +106,7 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, if (trip->temperature == temp) return; - trip->temperature = temp; + WRITE_ONCE(trip->temperature, temp); thermal_notify_tz_trip_change(tz, trip); if (temp == THERMAL_TEMP_INVALID) { diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h index 1f4bbaf31675..46263c1da8b6 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h @@ -336,10 +336,6 @@ struct ti_bandgap_data { struct ti_temp_sensor sensors[]; }; -int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot); -int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val); -int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold); -int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val); int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, int *interval); int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id, diff --git a/drivers/thermal/uniphier_thermal.c b/drivers/thermal/uniphier_thermal.c index 274f36358b21..0325b7195136 100644 --- a/drivers/thermal/uniphier_thermal.c +++ b/drivers/thermal/uniphier_thermal.c @@ -239,13 +239,34 @@ static irqreturn_t uniphier_tm_alarm_irq_thread(int irq, void *_tdev) return IRQ_HANDLED; } +struct trip_walk_data { + struct uniphier_tm_dev *tdev; + int crit_temp; + int index; +}; + +static int uniphier_tm_trip_walk_cb(struct thermal_trip *trip, void *arg) +{ + struct trip_walk_data *twd = arg; + + if (trip->type == THERMAL_TRIP_CRITICAL && + trip->temperature < twd->crit_temp) + twd->crit_temp = trip->temperature; + + uniphier_tm_set_alert(twd->tdev, twd->index, trip->temperature); + twd->tdev->alert_en[twd->index++] = true; + + return 0; +} + static int uniphier_tm_probe(struct platform_device *pdev) { + struct trip_walk_data twd = { .crit_temp = INT_MAX, .index = 0 }; struct device *dev = &pdev->dev; struct regmap *regmap; struct device_node *parent; struct uniphier_tm_dev *tdev; - int i, ret, irq, crit_temp = INT_MAX; + int ret, irq; tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL); if (!tdev) @@ -293,20 +314,10 @@ static int uniphier_tm_probe(struct platform_device *pdev) } /* set alert temperatures */ - for (i = 0; i < thermal_zone_get_num_trips(tdev->tz_dev); i++) { - struct thermal_trip trip; + twd.tdev = tdev; + thermal_zone_for_each_trip(tdev->tz_dev, uniphier_tm_trip_walk_cb, &twd); - ret = thermal_zone_get_trip(tdev->tz_dev, i, &trip); - if (ret) - return ret; - - if (trip.type == THERMAL_TRIP_CRITICAL && - trip.temperature < crit_temp) - crit_temp = trip.temperature; - uniphier_tm_set_alert(tdev, i, trip.temperature); - tdev->alert_en[i] = true; - } - if (crit_temp > CRITICAL_TEMP_LIMIT) { + if (twd.crit_temp > CRITICAL_TEMP_LIMIT) { dev_err(dev, "critical trip is over limit(>%d), or not set\n", CRITICAL_TEMP_LIMIT); return -EINVAL; |