diff options
44 files changed, 1354 insertions, 728 deletions
diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml index 20f8f9b3b971..01fccdfc4178 100644 --- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml @@ -13,11 +13,13 @@ description: Binding for Amlogic Thermal properties: compatible: - items: - - enum: - - amlogic,g12a-cpu-thermal - - amlogic,g12a-ddr-thermal - - const: amlogic,g12a-thermal + oneOf: + - items: + - enum: + - amlogic,g12a-cpu-thermal + - amlogic,g12a-ddr-thermal + - const: amlogic,g12a-thermal + - const: amlogic,a1-cpu-thermal reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml index b634f57cd011..ca81c8afba79 100644 --- a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml @@ -18,13 +18,15 @@ properties: oneOf: - enum: - loongson,ls2k1000-thermal + - loongson,ls2k2000-thermal - items: - enum: - - loongson,ls2k2000-thermal + - loongson,ls2k0500-thermal - const: loongson,ls2k1000-thermal reg: - maxItems: 1 + minItems: 1 + maxItems: 2 interrupts: maxItems: 1 @@ -38,6 +40,24 @@ required: - interrupts - '#thermal-sensor-cells' +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k2000-thermal + +then: + properties: + reg: + minItems: 2 + maxItems: 2 + +else: + properties: + reg: + maxItems: 1 + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml index e6665af52ee6..331cf4e662e3 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/mediatek,lvts-thermal.yaml @@ -19,6 +19,9 @@ properties: compatible: enum: - mediatek,mt7988-lvts-ap + - mediatek,mt8186-lvts + - mediatek,mt8188-lvts-ap + - mediatek,mt8188-lvts-mcu - mediatek,mt8192-lvts-ap - mediatek,mt8192-lvts-mcu - mediatek,mt8195-lvts-ap @@ -60,6 +63,8 @@ allOf: compatible: contains: enum: + - mediatek,mt8188-lvts-ap + - mediatek,mt8188-lvts-mcu - mediatek,mt8192-lvts-ap - mediatek,mt8192-lvts-mcu then: @@ -75,6 +80,7 @@ allOf: compatible: contains: enum: + - mediatek,mt8186-lvts - mediatek,mt8195-lvts-ap - mediatek,mt8195-lvts-mcu then: diff --git a/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml b/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml index 5ff72ce5c887..1175bb358382 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-lmh.yaml @@ -17,10 +17,14 @@ description: properties: compatible: - enum: - - qcom,sc8180x-lmh - - qcom,sdm845-lmh - - qcom,sm8150-lmh + oneOf: + - enum: + - qcom,sc8180x-lmh + - qcom,sdm845-lmh + - qcom,sm8150-lmh + - items: + - const: qcom,qcm2290-lmh + - const: qcom,sm8150-lmh reg: items: diff --git a/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml b/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml new file mode 100644 index 000000000000..9f6fc5c95c55 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/st,stih407-thermal.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/st,stih407-thermal.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STi digital thermal sensor (DTS) + +maintainers: + - Patrice Chotard <patrice.chotard@foss.st.com> + - Lee Jones <lee@kernel.org> + +allOf: + - $ref: thermal-sensor.yaml + +properties: + compatible: + const: st,stih407-thermal + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: thermal + + interrupts: + description: + For thermal sensors for which no interrupt has been defined, a polling + delay of 1000ms will be used to read the temperature from device. + maxItems: 1 + + '#thermal-sensor-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + temperature-sensor@91a0000 { + compatible = "st,stih407-thermal"; + reg = <0x91a0000 0x28>; + clock-names = "thermal"; + clocks = <&CLK_SYSIN>; + interrupts = <GIC_SPI 205 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <0>; + }; +... diff --git a/Documentation/devicetree/bindings/thermal/st-thermal.txt b/Documentation/devicetree/bindings/thermal/st-thermal.txt deleted file mode 100644 index a2f939137e35..000000000000 --- a/Documentation/devicetree/bindings/thermal/st-thermal.txt +++ /dev/null @@ -1,32 +0,0 @@ -Binding for Thermal Sensor driver for STMicroelectronics STi series of SoCs. - -Required parameters: -------------------- - -compatible : Should be "st,stih407-thermal" - -clock-names : Should be "thermal". - See: Documentation/devicetree/bindings/resource-names.txt -clocks : Phandle of the clock used by the thermal sensor. - See: Documentation/devicetree/bindings/clock/clock-bindings.txt - -Optional parameters: -------------------- - -reg : For non-sysconf based sensors, this should be the physical base - address and length of the sensor's registers. -interrupts : Standard way to define interrupt number. - NB: For thermal sensor's for which no interrupt has been - defined, a polling delay of 1000ms will be used to read the - temperature from device. - -Example: - - temp0@91a0000 { - compatible = "st,stih407-thermal"; - reg = <0x91a0000 0x28>; - clock-names = "thermal"; - clocks = <&CLK_SYSIN>; - interrupts = <GIC_SPI 205 IRQ_TYPE_EDGE_RISING>; - st,passive_cooling_temp = <110>; - }; diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c index df7a5ed55385..cd4776aa805e 100644 --- a/drivers/thermal/amlogic_thermal.c +++ b/drivers/thermal/amlogic_thermal.c @@ -220,6 +220,12 @@ static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = { .regmap_config = &amlogic_thermal_regmap_config_g12a, }; +static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = { + .u_efuse_off = 0x114, + .calibration_parameters = &amlogic_thermal_g12a, + .regmap_config = &amlogic_thermal_regmap_config_g12a, +}; + static const struct of_device_id of_amlogic_thermal_match[] = { { .compatible = "amlogic,g12a-ddr-thermal", @@ -229,6 +235,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = { .compatible = "amlogic,g12a-cpu-thermal", .data = &amlogic_thermal_g12a_cpu_param, }, + { + .compatible = "amlogic,a1-cpu-thermal", + .data = &amlogic_thermal_a1_cpu_param, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index f783547ef964..fdcb077cfd54 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -763,7 +763,6 @@ static void armada_set_sane_name(struct platform_device *pdev, struct armada_thermal_priv *priv) { const char *name = dev_name(&pdev->dev); - char *insane_char; if (strlen(name) > THERMAL_NAME_LENGTH) { /* @@ -781,12 +780,8 @@ static void armada_set_sane_name(struct platform_device *pdev, /* Save the name locally */ strscpy(priv->zone_name, name, THERMAL_NAME_LENGTH); - /* Then check there are no '-' or hwmon core will complain */ - do { - insane_char = strpbrk(priv->zone_name, "-"); - if (insane_char) - *insane_char = '_'; - } while (insane_char); + /* Then ensure there are no '-' or hwmon core will complain */ + strreplace(priv->zone_name, '-', '_'); } /* diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index c3b2943a2db8..acb52c9ee10f 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -13,60 +13,11 @@ #include "thermal_core.h" -static int thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip) -{ - int trip_index = thermal_zone_trip_id(tz, trip); - struct thermal_instance *instance; - - if (!trip->hysteresis) - dev_info_once(&tz->device, - "Zero hysteresis value for thermal zone %s\n", tz->type); - - dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", - trip_index, trip->temperature, tz->temperature, - trip->hysteresis); - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - /* in case fan is in initial state, switch the fan off */ - if (instance->target == THERMAL_NO_TARGET) - instance->target = 0; - - /* in case fan is neither on nor off set the fan to active */ - if (instance->target != 0 && instance->target != 1) { - pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", - instance->name, instance->target); - instance->target = 1; - } - - /* - * enable fan when temperature exceeds trip_temp and disable - * the fan in case it falls below trip_temp minus hysteresis - */ - if (instance->target == 0 && tz->temperature >= trip->temperature) - instance->target = 1; - else if (instance->target == 1 && - tz->temperature < trip->temperature - trip->hysteresis) - instance->target = 0; - - dev_dbg(&instance->cdev->device, "target=%d\n", - (int)instance->target); - - mutex_lock(&instance->cdev->lock); - instance->cdev->updated = false; /* cdev needs update */ - mutex_unlock(&instance->cdev->lock); - } - - return 0; -} - /** * bang_bang_control - controls devices associated with the given zone * @tz: thermal_zone_device * @trip: the trip point + * @crossed_up: whether or not the trip has been crossed on the way up * * Regulation Logic: a two point regulation, deliver cooling state depending * on the previous state shown in this diagram: @@ -90,26 +41,54 @@ static int thermal_zone_trip_update(struct thermal_zone_device *tz, * (trip_temp - hyst) so that the fan gets turned off again. * */ -static int bang_bang_control(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void bang_bang_control(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) { struct thermal_instance *instance; - int ret; lockdep_assert_held(&tz->lock); - ret = thermal_zone_trip_update(tz, trip); - if (ret) - return ret; + dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n", + thermal_zone_trip_id(tz, trip), trip->temperature, + tz->temperature, trip->hysteresis); + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + if (instance->target == THERMAL_NO_TARGET) + instance->target = 0; + + if (instance->target != 0 && instance->target != 1) { + pr_debug("Unexpected state %ld of thermal instance %s in bang-bang\n", + instance->target, instance->name); + + instance->target = 1; + } + + /* + * Enable the fan when the trip is crossed on the way up and + * disable it when the trip is crossed on the way down. + */ + 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); + } list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); - - return 0; } static struct thermal_governor thermal_gov_bang_bang = { .name = "bang_bang", - .throttle = bang_bang_control, + .trip_crossed = bang_bang_control, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_bang_bang); diff --git a/drivers/thermal/gov_fair_share.c b/drivers/thermal/gov_fair_share.c index 4da25a0009d7..ce0ea571ed67 100644 --- a/drivers/thermal/gov_fair_share.c +++ b/drivers/thermal/gov_fair_share.c @@ -17,97 +17,111 @@ static int get_trip_level(struct thermal_zone_device *tz) { - const struct thermal_trip *trip, *level_trip = NULL; + const struct thermal_trip_desc *level_td = NULL; + const struct thermal_trip_desc *td; int trip_level = -1; - for_each_trip(tz, trip) { - if (trip->temperature >= tz->temperature) + for_each_trip_desc(tz, td) { + if (td->threshold > tz->temperature) continue; trip_level++; - if (!level_trip || trip->temperature > level_trip->temperature) - level_trip = trip; + if (!level_td || td->threshold > level_td->threshold) + level_td = td; } /* Bail out if the temperature is not greater than any trips. */ if (trip_level < 0) return 0; - trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, level_trip), - level_trip->type); + trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, &level_td->trip), + level_td->trip.type); return trip_level; } -static long get_target_state(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int percentage, int level) -{ - return (long)(percentage * level * cdev->max_state) / (100 * tz->num_trips); -} - /** * fair_share_throttle - throttles devices associated with the given zone * @tz: thermal_zone_device * @trip: trip point + * @trip_level: number of trips crossed by the zone temperature * * Throttling Logic: This uses three parameters to calculate the new * throttle state of the cooling devices associated with the given zone. * * Parameters used for Throttling: * P1. max_state: Maximum throttle state exposed by the cooling device. - * P2. percentage[i]/100: + * P2. weight[i]/total_weight: * How 'effective' the 'i'th device is, in cooling the given zone. - * P3. cur_trip_level/max_no_of_trips: + * P3. trip_level/max_no_of_trips: * This describes the extent to which the devices should be throttled. * We do not want to throttle too much when we trip a lower temperature, * whereas the throttling is at full swing if we trip critical levels. - * (Heavily assumes the trip points are in ascending order) * new_state of cooling device = P3 * P2 * P1 */ -static int fair_share_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void fair_share_throttle(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + int trip_level) { struct thermal_instance *instance; int total_weight = 0; - int total_instance = 0; - int cur_trip_level = get_trip_level(tz); - - lockdep_assert_held(&tz->lock); + int nr_instances = 0; list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip) continue; total_weight += instance->weight; - total_instance++; + nr_instances++; } list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - int percentage; struct thermal_cooling_device *cdev = instance->cdev; + u64 dividend; + u32 divisor; if (instance->trip != trip) continue; - if (!total_weight) - percentage = 100 / total_instance; - else - percentage = (instance->weight * 100) / total_weight; - - instance->target = get_target_state(tz, cdev, percentage, - cur_trip_level); + dividend = trip_level; + dividend *= cdev->max_state; + divisor = tz->num_trips; + if (total_weight) { + dividend *= instance->weight; + divisor *= total_weight; + } else { + divisor *= nr_instances; + } + instance->target = div_u64(dividend, divisor); mutex_lock(&cdev->lock); __thermal_cdev_update(cdev); mutex_unlock(&cdev->lock); } +} - return 0; +static void fair_share_manage(struct thermal_zone_device *tz) +{ + int trip_level = get_trip_level(tz); + const struct thermal_trip_desc *td; + + lockdep_assert_held(&tz->lock); + + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->temperature == THERMAL_TEMP_INVALID || + trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + continue; + + fair_share_throttle(tz, trip, trip_level); + } } static struct thermal_governor thermal_gov_fair_share = { - .name = "fair_share", - .throttle = fair_share_throttle, + .name = "fair_share", + .manage = fair_share_manage, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_fair_share); diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c index e25e48d76aa7..45f04a25255a 100644 --- a/drivers/thermal/gov_power_allocator.c +++ b/drivers/thermal/gov_power_allocator.c @@ -66,6 +66,7 @@ struct power_actor { * struct power_allocator_params - parameters for the power allocator governor * @allocated_tzp: whether we have allocated tzp for this thermal zone and * it needs to be freed on unbind + * @update_cdevs: whether or not update cdevs on the next run * @err_integral: accumulated error in the PID controller. * @prev_err: error in the previous iteration of the PID controller. * Used to calculate the derivative term. @@ -84,6 +85,7 @@ struct power_actor { */ struct power_allocator_params { bool allocated_tzp; + bool update_cdevs; s64 err_integral; s32 prev_err; u32 sustainable_power; @@ -395,7 +397,7 @@ static void divvy_up_power(struct power_actor *power, int num_actors, } } -static int allocate_power(struct thermal_zone_device *tz, int control_temp) +static void allocate_power(struct thermal_zone_device *tz, int control_temp) { struct power_allocator_params *params = tz->governor_data; unsigned int num_actors = params->num_actors; @@ -410,7 +412,7 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) int i = 0, ret; if (!num_actors) - return -ENODEV; + return; /* Clean all buffers for new power estimations */ memset(power, 0, params->buffer_size); @@ -471,8 +473,6 @@ static int allocate_power(struct thermal_zone_device *tz, int control_temp) num_actors, power_range, max_allocatable_power, tz->temperature, control_temp - tz->temperature); - - return 0; } /** @@ -496,9 +496,11 @@ static void get_governor_trips(struct thermal_zone_device *tz, const struct thermal_trip *first_passive = NULL; const struct thermal_trip *last_passive = NULL; const struct thermal_trip *last_active = NULL; - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; + + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; - for_each_trip(tz, trip) { switch (trip->type) { case THERMAL_TRIP_PASSIVE: if (!first_passive) { @@ -533,7 +535,7 @@ static void reset_pid_controller(struct power_allocator_params *params) params->prev_err = 0; } -static void allow_maximum_power(struct thermal_zone_device *tz, bool update) +static void allow_maximum_power(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; struct thermal_cooling_device *cdev; @@ -555,7 +557,7 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update) */ cdev->ops->get_requested_power(cdev, &req_power); - if (update) + if (params->update_cdevs) __thermal_cdev_update(cdev); mutex_unlock(&cdev->lock); @@ -743,40 +745,29 @@ static void power_allocator_unbind(struct thermal_zone_device *tz) tz->governor_data = NULL; } -static int power_allocator_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void power_allocator_manage(struct thermal_zone_device *tz) { struct power_allocator_params *params = tz->governor_data; - bool update; + const struct thermal_trip *trip = params->trip_switch_on; lockdep_assert_held(&tz->lock); - /* - * We get called for every trip point but we only need to do - * our calculations once - */ - if (trip != params->trip_max) - return 0; - - trip = params->trip_switch_on; if (trip && tz->temperature < trip->temperature) { - update = tz->passive; - tz->passive = 0; reset_pid_controller(params); - allow_maximum_power(tz, update); - return 0; + allow_maximum_power(tz); + params->update_cdevs = false; + return; } - tz->passive = 1; - - return allocate_power(tz, params->trip_max->temperature); + allocate_power(tz, params->trip_max->temperature); + params->update_cdevs = true; } static struct thermal_governor thermal_gov_power_allocator = { .name = "power_allocator", .bind_to_tz = power_allocator_bind, .unbind_from_tz = power_allocator_unbind, - .throttle = power_allocator_throttle, + .manage = power_allocator_manage, .update_tz = power_allocator_update_tz, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator); diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c index 5436aa58d41e..e0fdc497bfcc 100644 --- a/drivers/thermal/gov_step_wise.c +++ b/drivers/thermal/gov_step_wise.c @@ -32,7 +32,6 @@ static unsigned long get_target_state(struct thermal_instance *instance, { struct thermal_cooling_device *cdev = instance->cdev; unsigned long cur_state; - unsigned long next_target; /* * We keep this instance the way it is by default. @@ -40,112 +39,99 @@ static unsigned long get_target_state(struct thermal_instance *instance, * cdev in use to determine the next_target. */ cdev->ops->get_cur_state(cdev, &cur_state); - next_target = instance->target; dev_dbg(&cdev->device, "cur_state=%ld\n", cur_state); if (!instance->initialized) { - if (throttle) { - next_target = clamp((cur_state + 1), instance->lower, instance->upper); - } else { - next_target = THERMAL_NO_TARGET; - } + if (throttle) + return clamp(cur_state + 1, instance->lower, instance->upper); - return next_target; + return THERMAL_NO_TARGET; } if (throttle) { if (trend == THERMAL_TREND_RAISING) - next_target = clamp((cur_state + 1), instance->lower, instance->upper); - } else { - if (trend == THERMAL_TREND_DROPPING) { - if (cur_state <= instance->lower) - next_target = THERMAL_NO_TARGET; - else - next_target = clamp((cur_state - 1), instance->lower, instance->upper); - } + return clamp(cur_state + 1, instance->lower, instance->upper); + } else if (trend == THERMAL_TREND_DROPPING) { + if (cur_state <= instance->lower) + return THERMAL_NO_TARGET; + + return clamp(cur_state - 1, instance->lower, instance->upper); } - return next_target; + return instance->target; } static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip *trip) + const struct thermal_trip *trip, + int trip_threshold) { + enum thermal_trend trend = get_tz_trend(tz, trip); int trip_id = thermal_zone_trip_id(tz, trip); - enum thermal_trend trend; struct thermal_instance *instance; bool throttle = false; - int old_target; - - trend = get_tz_trend(tz, trip); - if (tz->temperature >= trip->temperature) { + if (tz->temperature >= trip_threshold) { throttle = true; trace_thermal_zone_trip(tz, trip_id, trip->type); } dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", - trip_id, trip->type, trip->temperature, trend, throttle); + trip_id, trip->type, trip_threshold, trend, throttle); list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + int old_target; + if (instance->trip != trip) continue; old_target = instance->target; instance->target = get_target_state(instance, trend, throttle); - dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n", - old_target, (int)instance->target); + + dev_dbg(&instance->cdev->device, "old_target=%d, target=%ld\n", + old_target, instance->target); if (instance->initialized && old_target == instance->target) continue; - if (old_target == THERMAL_NO_TARGET && - instance->target != THERMAL_NO_TARGET) { - /* Activate a passive thermal instance */ - if (trip->type == THERMAL_TRIP_PASSIVE) - tz->passive++; - } else if (old_target != THERMAL_NO_TARGET && - instance->target == THERMAL_NO_TARGET) { - /* Deactivate a passive thermal instance */ - if (trip->type == THERMAL_TRIP_PASSIVE) - tz->passive--; - } - instance->initialized = true; + mutex_lock(&instance->cdev->lock); instance->cdev->updated = false; /* cdev needs update */ mutex_unlock(&instance->cdev->lock); } } -/** - * step_wise_throttle - throttles devices associated with the given zone - * @tz: thermal_zone_device - * @trip: trip point - * - * Throttling Logic: This uses the trend of the thermal zone to throttle. - * If the thermal zone is 'heating up' this throttles all the cooling - * devices associated with the zone and its particular trip point, by one - * step. If the zone is 'cooling down' it brings back the performance of - * the devices by one step. - */ -static int step_wise_throttle(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void step_wise_manage(struct thermal_zone_device *tz) { + const struct thermal_trip_desc *td; struct thermal_instance *instance; lockdep_assert_held(&tz->lock); - thermal_zone_trip_update(tz, trip); + /* + * Throttling Logic: Use the trend of the thermal zone to throttle. + * If the thermal zone is 'heating up', throttle all of the cooling + * devices associated with each trip point by one step. If the zone + * is 'cooling down', it brings back the performance of the devices + * by one step. + */ + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->temperature == THERMAL_TEMP_INVALID || + trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + continue; + + thermal_zone_trip_update(tz, trip, td->threshold); + } list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); - - return 0; } static struct thermal_governor thermal_gov_step_wise = { - .name = "step_wise", - .throttle = step_wise_throttle, + .name = "step_wise", + .manage = step_wise_manage, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_step_wise); diff --git a/drivers/thermal/gov_user_space.c b/drivers/thermal/gov_user_space.c index 7a1790b7e8f5..75137b419eb2 100644 --- a/drivers/thermal/gov_user_space.c +++ b/drivers/thermal/gov_user_space.c @@ -26,11 +26,13 @@ static int user_space_bind(struct thermal_zone_device *tz) * notify_user_space - Notifies user space about thermal events * @tz: thermal_zone_device * @trip: trip point + * @crossed_up: whether or not the trip has been crossed on the way up * * This function notifies the user space through UEvents. */ -static int notify_user_space(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static void notify_user_space(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) { char *thermal_prop[5]; int i; @@ -46,13 +48,11 @@ static int notify_user_space(struct thermal_zone_device *tz, kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop); for (i = 0; i < 4; ++i) kfree(thermal_prop[i]); - - return 0; } static struct thermal_governor thermal_gov_user_space = { .name = "user_space", - .throttle = notify_user_space, + .trip_crossed = notify_user_space, .bind_to_tz = user_space_bind, }; THERMAL_GOVERNOR_DECLARE(thermal_gov_user_space); diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index dc519a665c18..4b4a4d63e61f 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -309,7 +309,7 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps if (knob->type == ACPI_TYPE_STRING) { memset(&psvt->limit, 0, sizeof(u64)); - strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length); + strscpy(psvt->limit.string, psvt_ptr->limit.str_ptr, ACPI_LIMIT_STR_MAX_LEN); } else { psvt->limit.integer = psvt_ptr->limit.integer; } @@ -468,7 +468,7 @@ static int fill_psvt(char __user *ubuf) psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff; psvt_user[i].control_knob_type = psvts[i].control_knob_type; if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING) - strncpy(psvt_user[i].limit.string, psvts[i].limit.string, + strscpy(psvt_user[i].limit.string, psvts[i].limit.string, ACPI_LIMIT_STR_MAX_LEN); else psvt_user[i].limit.integer = psvts[i].limit.integer; diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c index 4d3bd32ff9ea..af2ec0beb7a1 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_mbox.c @@ -156,3 +156,4 @@ unlock: EXPORT_SYMBOL_NS_GPL(processor_thermal_mbox_interrupt_config, INT340X_THERMAL); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Processor Thermal Mail Box Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c index a1a108407f0f..d6b787ca2741 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_power_floor.c @@ -124,3 +124,4 @@ EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_intr_callback, INT340X_THERMAL); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal power floor notification Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c index e964a9375722..e9aa9e23aab9 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c @@ -133,3 +133,4 @@ void proc_thermal_rapl_remove(void) EXPORT_SYMBOL_GPL(proc_thermal_rapl_remove); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("RAPL interface using MMIO"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c index 546b70434004..e56db75a94fb 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -384,3 +384,4 @@ void proc_thermal_rfim_remove(struct pci_dev *pdev) EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Processor Thermal RFIM Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c index 9d5e4c169d1b..e947d84f4977 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c @@ -253,3 +253,4 @@ EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, INT340X_THERMAL); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c index 711c4f761c9a..f298e7442662 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_req.c @@ -134,3 +134,4 @@ EXPORT_SYMBOL_GPL(proc_thermal_wt_req_remove); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal Work Load type request Interface"); diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 40d664a66cdc..a180a98bb9f1 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -159,14 +159,15 @@ struct hfi_cpu_info { static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 }; static int max_hfi_instances; +static int hfi_clients_nr; static struct hfi_instance *hfi_instances; static struct hfi_features hfi_features; static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; -#define HFI_UPDATE_INTERVAL HZ -#define HFI_MAX_THERM_NOTIFY_COUNT 16 +#define HFI_UPDATE_DELAY_MS 100 +#define HFI_THERMNL_CAPS_PER_EVENT 64 static void get_hfi_caps(struct hfi_instance *hfi_instance, struct thermal_genl_cpu_caps *cpu_caps) @@ -217,14 +218,14 @@ static void update_capabilities(struct hfi_instance *hfi_instance) get_hfi_caps(hfi_instance, cpu_caps); - if (cpu_count < HFI_MAX_THERM_NOTIFY_COUNT) + if (cpu_count < HFI_THERMNL_CAPS_PER_EVENT) goto last_cmd; - /* Process complete chunks of HFI_MAX_THERM_NOTIFY_COUNT capabilities. */ + /* Process complete chunks of HFI_THERMNL_CAPS_PER_EVENT capabilities. */ for (i = 0; - (i + HFI_MAX_THERM_NOTIFY_COUNT) <= cpu_count; - i += HFI_MAX_THERM_NOTIFY_COUNT) - thermal_genl_cpu_capability_event(HFI_MAX_THERM_NOTIFY_COUNT, + (i + HFI_THERMNL_CAPS_PER_EVENT) <= cpu_count; + i += HFI_THERMNL_CAPS_PER_EVENT) + thermal_genl_cpu_capability_event(HFI_THERMNL_CAPS_PER_EVENT, &cpu_caps[i]); cpu_count = cpu_count - i; @@ -321,7 +322,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) raw_spin_unlock(&hfi_instance->event_lock); queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work, - HFI_UPDATE_INTERVAL); + msecs_to_jiffies(HFI_UPDATE_DELAY_MS)); } static void init_hfi_cpu_index(struct hfi_cpu_info *info) @@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu) enable: cpumask_set_cpu(cpu, hfi_instance->cpus); - /* Enable this HFI instance if this is its first online CPU. */ - if (cpumask_weight(hfi_instance->cpus) == 1) { + /* + * Enable this HFI instance if this is its first online CPU and + * there are user-space clients of thermal events. + */ + if (cpumask_weight(hfi_instance->cpus) == 1 && hfi_clients_nr > 0) { hfi_set_hw_table(hfi_instance); hfi_enable(); } @@ -573,18 +577,33 @@ static __init int hfi_parse_features(void) return 0; } -static void hfi_do_enable(void) +/* + * If concurrency is not prevented by other means, the HFI enable/disable + * routines must be called under hfi_instance_lock." + */ +static void hfi_enable_instance(void *ptr) +{ + hfi_set_hw_table(ptr); + hfi_enable(); +} + +static void hfi_disable_instance(void *ptr) +{ + hfi_disable(); +} + +static void hfi_syscore_resume(void) { /* This code runs only on the boot CPU. */ struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0); struct hfi_instance *hfi_instance = info->hfi_instance; /* No locking needed. There is no concurrency with CPU online. */ - hfi_set_hw_table(hfi_instance); - hfi_enable(); + if (hfi_clients_nr > 0) + hfi_enable_instance(hfi_instance); } -static int hfi_do_disable(void) +static int hfi_syscore_suspend(void) { /* No locking needed. There is no concurrency with CPU offline. */ hfi_disable(); @@ -593,8 +612,58 @@ static int hfi_do_disable(void) } static struct syscore_ops hfi_pm_ops = { - .resume = hfi_do_enable, - .suspend = hfi_do_disable, + .resume = hfi_syscore_resume, + .suspend = hfi_syscore_suspend, +}; + +static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state, + void *_notify) +{ + struct thermal_genl_notify *notify = _notify; + struct hfi_instance *hfi_instance; + smp_call_func_t func = NULL; + unsigned int cpu; + int i; + + if (notify->mcgrp != THERMAL_GENL_EVENT_GROUP) + return NOTIFY_DONE; + + if (state != THERMAL_NOTIFY_BIND && state != THERMAL_NOTIFY_UNBIND) + return NOTIFY_DONE; + + mutex_lock(&hfi_instance_lock); + + switch (state) { + case THERMAL_NOTIFY_BIND: + if (++hfi_clients_nr == 1) + func = hfi_enable_instance; + break; + case THERMAL_NOTIFY_UNBIND: + if (--hfi_clients_nr == 0) + func = hfi_disable_instance; + break; + } + + if (!func) + goto out; + + for (i = 0; i < max_hfi_instances; i++) { + hfi_instance = &hfi_instances[i]; + if (cpumask_empty(hfi_instance->cpus)) + continue; + + cpu = cpumask_any(hfi_instance->cpus); + smp_call_function_single(cpu, func, hfi_instance, true); + } + +out: + mutex_unlock(&hfi_instance_lock); + + return NOTIFY_OK; +} + +static struct notifier_block hfi_thermal_nb = { + .notifier_call = hfi_thermal_notify, }; void __init intel_hfi_init(void) @@ -628,10 +697,22 @@ void __init intel_hfi_init(void) if (!hfi_updates_wq) goto err_nomem; + /* + * Both thermal core and Intel HFI can not be build as modules. + * As kernel build-in drivers they are initialized before user-space + * starts, hence we can not miss BIND/UNBIND events when applications + * add/remove thermal multicast group to/from a netlink socket. + */ + if (thermal_genl_register_notifier(&hfi_thermal_nb)) + goto err_nl_notif; + register_syscore_ops(&hfi_pm_ops); return; +err_nl_notif: + destroy_workqueue(hfi_updates_wq); + err_nomem: for (j = 0; j < i; ++j) { hfi_instance = &hfi_instances[j]; diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c index 2ab943b66f7a..7adf942665d4 100644 --- a/drivers/thermal/intel/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel/intel_soc_dts_iosf.c @@ -387,3 +387,4 @@ EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit); MODULE_IMPORT_NS(INTEL_TCC); MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SoC DTS driver using side band interface"); diff --git a/drivers/thermal/k3_bandgap.c b/drivers/thermal/k3_bandgap.c index e88192d2afea..2a703770fc91 100644 --- a/drivers/thermal/k3_bandgap.c +++ b/drivers/thermal/k3_bandgap.c @@ -78,7 +78,6 @@ static const int k3_adc_to_temp[] = { struct k3_bandgap { void __iomem *base; - const struct k3_bandgap_data *conf; }; /* common data structures */ diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c index 0f475fe46bc9..2d6b75b0539f 100644 --- a/drivers/thermal/loongson2_thermal.c +++ b/drivers/thermal/loongson2_thermal.c @@ -14,58 +14,81 @@ #include <linux/property.h> #include <linux/thermal.h> #include <linux/units.h> + #include "thermal_hwmon.h" -#define LOONGSON2_MAX_SENSOR_SEL_NUM 3 +#define LOONGSON2_MAX_SENSOR_SEL_NUM 3 + +#define LOONGSON2_THSENS_CTRL_HI_REG 0x0 +#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 +#define LOONGSON2_THSENS_STATUS_REG 0x10 +#define LOONGSON2_THSENS_OUT_REG 0x14 -#define LOONGSON2_THSENS_CTRL_HI_REG 0x0 -#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 -#define LOONGSON2_THSENS_STATUS_REG 0x10 -#define LOONGSON2_THSENS_OUT_REG 0x14 +#define LOONGSON2_THSENS_INT_LO BIT(0) +#define LOONGSON2_THSENS_INT_HIGH BIT(1) +#define LOONGSON2_THSENS_INT_EN (LOONGSON2_THSENS_INT_LO | \ + LOONGSON2_THSENS_INT_HIGH) +#define LOONGSON2_THSENS_OUT_MASK 0xFF -#define LOONGSON2_THSENS_INT_LO BIT(0) -#define LOONGSON2_THSENS_INT_HIGH BIT(1) -#define LOONGSON2_THSENS_OUT_MASK 0xFF +/* + * This flag is used to indicate the temperature reading + * method of the Loongson-2K2000 + */ +#define LS2K2000_THSENS_OUT_FLAG BIT(0) struct loongson2_thermal_chip_data { - unsigned int thermal_sensor_sel; + unsigned int thermal_sensor_sel; + unsigned int flags; }; struct loongson2_thermal_data { - void __iomem *regs; + void __iomem *ctrl_reg; + void __iomem *temp_reg; const struct loongson2_thermal_chip_data *chip_data; }; +static void loongson2_set_ctrl_regs(struct loongson2_thermal_data *data, + int ctrl_data, bool low, bool enable) +{ + int reg_ctrl = 0; + int reg_off = data->chip_data->thermal_sensor_sel * 2; + int ctrl_reg = low ? LOONGSON2_THSENS_CTRL_LOW_REG : LOONGSON2_THSENS_CTRL_HI_REG; + + reg_ctrl = ctrl_data + HECTO; + reg_ctrl |= enable ? 0x100 : 0; + writew(reg_ctrl, data->ctrl_reg + ctrl_reg + reg_off); +} + static int loongson2_thermal_set(struct loongson2_thermal_data *data, - int low, int high, bool enable) + int low, int high, bool enable) { - u64 reg_ctrl = 0; - int reg_off = data->chip_data->thermal_sensor_sel * 2; + /* Set low temperature threshold */ + loongson2_set_ctrl_regs(data, clamp(-40, low, high), true, enable); - low = clamp(-40, low, high); - high = clamp(125, low, high); + /* Set high temperature threshold */ + loongson2_set_ctrl_regs(data, clamp(125, low, high), false, enable); - low += HECTO; - high += HECTO; + return 0; +} - reg_ctrl = low; - reg_ctrl |= enable ? 0x100 : 0; - writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_LOW_REG + reg_off); +static int loongson2_2k1000_get_temp(struct thermal_zone_device *tz, int *temp) +{ + int val; + struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); - reg_ctrl = high; - reg_ctrl |= enable ? 0x100 : 0; - writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_HI_REG + reg_off); + val = readl(data->ctrl_reg + LOONGSON2_THSENS_OUT_REG); + *temp = ((val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO; return 0; } -static int loongson2_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +static int loongson2_2k2000_get_temp(struct thermal_zone_device *tz, int *temp) { - u32 reg_val; + int val; struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); - reg_val = readl(data->regs + LOONGSON2_THSENS_OUT_REG); - *temp = ((reg_val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO; + val = readl(data->temp_reg); + *temp = ((val & 0xffff) * 820 / 0x4000 - 311) * KILO; return 0; } @@ -75,8 +98,7 @@ static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev) struct thermal_zone_device *tzd = dev; struct loongson2_thermal_data *data = thermal_zone_device_priv(tzd); - writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + - LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG); thermal_zone_device_update(tzd, THERMAL_EVENT_UNSPECIFIED); @@ -90,8 +112,8 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, return loongson2_thermal_set(data, low/MILLI, high/MILLI, true); } -static const struct thermal_zone_device_ops loongson2_of_thermal_ops = { - .get_temp = loongson2_thermal_get_temp, +static struct thermal_zone_device_ops loongson2_of_thermal_ops = { + .get_temp = loongson2_2k1000_get_temp, .set_trips = loongson2_thermal_set_trips, }; @@ -108,22 +130,30 @@ static int loongson2_thermal_probe(struct platform_device *pdev) data->chip_data = device_get_match_data(dev); - data->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->regs)) - return PTR_ERR(data->regs); + data->ctrl_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->ctrl_reg)) + return PTR_ERR(data->ctrl_reg); + + /* The temperature output register is separate for Loongson-2K2000 */ + if (data->chip_data->flags & LS2K2000_THSENS_OUT_FLAG) { + data->temp_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(data->temp_reg)) + return PTR_ERR(data->temp_reg); + + loongson2_of_thermal_ops.get_temp = loongson2_2k2000_get_temp; + } irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + - LOONGSON2_THSENS_STATUS_REG); + writeb(LOONGSON2_THSENS_INT_EN, data->ctrl_reg + LOONGSON2_THSENS_STATUS_REG); loongson2_thermal_set(data, 0, 0, false); for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) { tzd = devm_thermal_of_zone_register(dev, i, data, - &loongson2_of_thermal_ops); + &loongson2_of_thermal_ops); if (!IS_ERR(tzd)) break; @@ -135,7 +165,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev) } ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread, - IRQF_ONESHOT, "loongson2_thermal", tzd); + IRQF_ONESHOT, "loongson2_thermal", tzd); if (ret < 0) return dev_err_probe(dev, ret, "failed to request alarm irq\n"); @@ -146,6 +176,12 @@ static int loongson2_thermal_probe(struct platform_device *pdev) static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = { .thermal_sensor_sel = 0, + .flags = 0, +}; + +static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k2000_data = { + .thermal_sensor_sel = 0, + .flags = LS2K2000_THSENS_OUT_FLAG, }; static const struct of_device_id of_loongson2_thermal_match[] = { @@ -153,6 +189,10 @@ static const struct of_device_id of_loongson2_thermal_match[] = { .compatible = "loongson,ls2k1000-thermal", .data = &loongson2_thermal_ls2k1000_data, }, + { + .compatible = "loongson,ls2k2000-thermal", + .data = &loongson2_thermal_ls2k2000_data, + }, { /* end */ } }; MODULE_DEVICE_TABLE(of, of_loongson2_thermal_match); @@ -167,4 +207,5 @@ static struct platform_driver loongson2_thermal_driver = { module_platform_driver(loongson2_thermal_driver); MODULE_DESCRIPTION("Loongson2 thermal driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index fd4bd650c77a..86b2f44355ac 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -91,9 +91,7 @@ #define LVTS_MSR_READ_TIMEOUT_US 400 #define LVTS_MSR_READ_WAIT_US (LVTS_MSR_READ_TIMEOUT_US / 2) -#define LVTS_HW_SHUTDOWN_MT7988 105000 -#define LVTS_HW_SHUTDOWN_MT8192 105000 -#define LVTS_HW_SHUTDOWN_MT8195 105000 +#define LVTS_HW_TSHUT_TEMP 105000 #define LVTS_MINIMUM_THRESHOLD 20000 @@ -102,22 +100,36 @@ static int golden_temp_offset; struct lvts_sensor_data { int dt_id; + u8 cal_offsets[3]; }; struct lvts_ctrl_data { struct lvts_sensor_data lvts_sensor[LVTS_SENSOR_MAX]; int cal_offset[LVTS_SENSOR_MAX]; - int hw_tshut_temp; int num_lvts_sensor; + u8 valid_sensor_mask; int offset; int mode; }; +#define VALID_SENSOR_MAP(s0, s1, s2, s3) \ + .valid_sensor_mask = (((s0) ? BIT(0) : 0) | \ + ((s1) ? BIT(1) : 0) | \ + ((s2) ? BIT(2) : 0) | \ + ((s3) ? BIT(3) : 0)) + +#define lvts_for_each_valid_sensor(i, lvts_ctrl_data) \ + for ((i) = 0; (i) < LVTS_SENSOR_MAX; (i)++) \ + if (!((lvts_ctrl_data)->valid_sensor_mask & BIT(i))) \ + continue; \ + else + struct lvts_data { const struct lvts_ctrl_data *lvts_ctrl; int num_lvts_ctrl; int temp_factor; int temp_offset; + int gt_calib_bit_offset; }; struct lvts_sensor { @@ -135,7 +147,6 @@ struct lvts_ctrl { const struct lvts_data *lvts_data; u32 calibration[LVTS_SENSOR_MAX]; u32 hw_tshut_raw_temp; - int num_lvts_sensor; int mode; void __iomem *base; int low_thresh; @@ -347,7 +358,7 @@ static bool lvts_should_update_thresh(struct lvts_ctrl *lvts_ctrl, int high) if (high > lvts_ctrl->high_thresh) return true; - for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) + lvts_for_each_valid_sensor(i, lvts_ctrl->lvts_data->lvts_ctrl) if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) return false; @@ -551,6 +562,7 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, const struct lvts_ctrl_data *lvts_ctrl_data) { struct lvts_sensor *lvts_sensor = lvts_ctrl->sensors; + void __iomem *msr_regs[] = { LVTS_MSR0(lvts_ctrl->base), LVTS_MSR1(lvts_ctrl->base), @@ -567,7 +579,7 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, int i; - for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) { + lvts_for_each_valid_sensor(i, lvts_ctrl_data) { int dt_id = lvts_ctrl_data->lvts_sensor[i].dt_id; @@ -607,8 +619,6 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, lvts_sensor[i].high_thresh = INT_MIN; }; - lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor; - return 0; } @@ -668,18 +678,31 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, * <-----ap--tc#3-----> <-----sensor#7-----> <-----sensor#8-----> * 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 * - * The data description gives the offset of the calibration data in - * this bytes stream for each sensor. + * Note: In some cases, values don't strictly follow a little endian ordering. + * The data description gives byte offsets constituting each calibration value + * for each sensor. */ static int lvts_calibration_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, const struct lvts_ctrl_data *lvts_ctrl_data, - u8 *efuse_calibration) + u8 *efuse_calibration, + size_t calib_len) { int i; - for (i = 0; i < lvts_ctrl_data->num_lvts_sensor; i++) - memcpy(&lvts_ctrl->calibration[i], - efuse_calibration + lvts_ctrl_data->cal_offset[i], 2); + lvts_for_each_valid_sensor(i, lvts_ctrl_data) { + const struct lvts_sensor_data *sensor = + &lvts_ctrl_data->lvts_sensor[i]; + + if (sensor->cal_offsets[0] >= calib_len || + sensor->cal_offsets[1] >= calib_len || + 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); + } return 0; } @@ -734,16 +757,21 @@ static int lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td return 0; } -static int lvts_golden_temp_init(struct device *dev, u32 *value, int temp_offset) +static int lvts_golden_temp_init(struct device *dev, u8 *calib, + const struct lvts_data *lvts_data) { u32 gt; - gt = (*value) >> 24; + /* + * The golden temp information is contained in the first 32-bit + * word of efuse data at a specific bit offset. + */ + gt = (((u32 *)calib)[0] >> lvts_data->gt_calib_bit_offset) & 0xff; if (gt && gt < LVTS_GOLDEN_TEMP_MAX) golden_temp = gt; - golden_temp_offset = golden_temp * 500 + temp_offset; + golden_temp_offset = golden_temp * 500 + lvts_data->temp_offset; return 0; } @@ -762,11 +790,7 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, if (ret) return ret; - /* - * The golden temp information is contained in the first chunk - * of efuse data. - */ - ret = lvts_golden_temp_init(dev, (u32 *)lvts_td->calib, lvts_data->temp_offset); + ret = lvts_golden_temp_init(dev, lvts_td->calib, lvts_data); if (ret) return ret; @@ -786,7 +810,8 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, ret = lvts_calibration_init(dev, &lvts_ctrl[i], &lvts_data->lvts_ctrl[i], - lvts_td->calib); + lvts_td->calib, + lvts_td->calib_len); if (ret) return ret; @@ -801,7 +826,7 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, * after initializing the calibration. */ lvts_ctrl[i].hw_tshut_raw_temp = - lvts_temp_to_raw(lvts_data->lvts_ctrl[i].hw_tshut_temp, + lvts_temp_to_raw(LVTS_HW_TSHUT_TEMP, lvts_data->temp_factor); lvts_ctrl[i].low_thresh = INT_MIN; @@ -1089,7 +1114,7 @@ static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl) u32 *sensor_bitmap = lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE ? sensor_imm_bitmap : sensor_filt_bitmap; - for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) { + lvts_for_each_valid_sensor(i, lvts_ctrl->lvts_data->lvts_ctrl) { int dt_id = lvts_sensors[i].dt_id; @@ -1302,28 +1327,32 @@ static void lvts_remove(struct platform_device *pdev) static const struct lvts_ctrl_data mt7988_lvts_ap_data_ctrl[] = { { - .cal_offset = { 0x00, 0x04, 0x08, 0x0c }, .lvts_sensor = { - { .dt_id = MT7988_CPU_0 }, - { .dt_id = MT7988_CPU_1 }, - { .dt_id = MT7988_ETH2P5G_0 }, - { .dt_id = MT7988_ETH2P5G_1 } + { .dt_id = MT7988_CPU_0, + .cal_offsets = { 0x00, 0x01, 0x02 } }, + { .dt_id = MT7988_CPU_1, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT7988_ETH2P5G_0, + .cal_offsets = { 0x08, 0x09, 0x0a } }, + { .dt_id = MT7988_ETH2P5G_1, + .cal_offsets = { 0x0c, 0x0d, 0x0e } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988, }, { - .cal_offset = { 0x14, 0x18, 0x1c, 0x20 }, .lvts_sensor = { - { .dt_id = MT7988_TOPS_0}, - { .dt_id = MT7988_TOPS_1}, - { .dt_id = MT7988_ETHWARP_0}, - { .dt_id = MT7988_ETHWARP_1} + { .dt_id = MT7988_TOPS_0, + .cal_offsets = { 0x14, 0x15, 0x16 } }, + { .dt_id = MT7988_TOPS_1, + .cal_offsets = { 0x18, 0x19, 0x1a } }, + { .dt_id = MT7988_ETHWARP_0, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT7988_ETHWARP_1, + .cal_offsets = { 0x20, 0x21, 0x22 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT7988, } }; @@ -1359,164 +1388,301 @@ static int lvts_resume(struct device *dev) return 0; } +/* + * The MT8186 calibration data is stored as packed 3-byte little-endian + * values using a weird layout that makes sense only when viewed as a 32-bit + * hexadecimal word dump. Let's suppose SxBy where x = sensor number and + * y = byte number where the LSB is y=0. We then have: + * + * [S0B2-S0B1-S0B0-S1B2] [S1B1-S1B0-S2B2-S2B1] [S2B0-S3B2-S3B1-S3B0] + * + * However, when considering a byte stream, those appear as follows: + * + * [S1B2] [S0B0[ [S0B1] [S0B2] [S2B1] [S2B2] [S1B0] [S1B1] [S3B0] [S3B1] [S3B2] [S2B0] + * + * Hence the rather confusing offsets provided below. + */ +static const struct lvts_ctrl_data mt8186_lvts_data_ctrl[] = { + { + .lvts_sensor = { + { .dt_id = MT8186_LITTLE_CPU0, + .cal_offsets = { 5, 6, 7 } }, + { .dt_id = MT8186_LITTLE_CPU1, + .cal_offsets = { 10, 11, 4 } }, + { .dt_id = MT8186_LITTLE_CPU2, + .cal_offsets = { 15, 8, 9 } }, + { .dt_id = MT8186_CAM, + .cal_offsets = { 12, 13, 14 } } + }, + VALID_SENSOR_MAP(1, 1, 1, 1), + .offset = 0x0, + }, + { + .lvts_sensor = { + { .dt_id = MT8186_BIG_CPU0, + .cal_offsets = { 22, 23, 16 } }, + { .dt_id = MT8186_BIG_CPU1, + .cal_offsets = { 27, 20, 21 } } + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x100, + }, + { + .lvts_sensor = { + { .dt_id = MT8186_NNA, + .cal_offsets = { 29, 30, 31 } }, + { .dt_id = MT8186_ADSP, + .cal_offsets = { 34, 35, 28 } }, + { .dt_id = MT8186_MFG, + .cal_offsets = { 39, 32, 33 } } + }, + VALID_SENSOR_MAP(1, 1, 1, 0), + .offset = 0x200, + } +}; + +static const struct lvts_ctrl_data mt8188_lvts_mcu_data_ctrl[] = { + { + .lvts_sensor = { + { .dt_id = MT8188_MCU_LITTLE_CPU0, + .cal_offsets = { 22, 23, 24 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU1, + .cal_offsets = { 25, 26, 27 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU2, + .cal_offsets = { 28, 29, 30 } }, + { .dt_id = MT8188_MCU_LITTLE_CPU3, + .cal_offsets = { 31, 32, 33 } }, + }, + VALID_SENSOR_MAP(1, 1, 1, 1), + .offset = 0x0, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_MCU_BIG_CPU0, + .cal_offsets = { 34, 35, 36 } }, + { .dt_id = MT8188_MCU_BIG_CPU1, + .cal_offsets = { 37, 38, 39 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x100, + .mode = LVTS_MSR_FILTERED_MODE, + } +}; + +static const struct lvts_ctrl_data mt8188_lvts_ap_data_ctrl[] = { + { + .lvts_sensor = { + + { /* unused */ }, + { .dt_id = MT8188_AP_APU, + .cal_offsets = { 40, 41, 42 } }, + }, + VALID_SENSOR_MAP(0, 1, 0, 0), + .offset = 0x0, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_GPU1, + .cal_offsets = { 43, 44, 45 } }, + { .dt_id = MT8188_AP_GPU2, + .cal_offsets = { 46, 47, 48 } }, + { .dt_id = MT8188_AP_SOC1, + .cal_offsets = { 49, 50, 51 } }, + }, + VALID_SENSOR_MAP(1, 1, 1, 0), + .offset = 0x100, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_SOC2, + .cal_offsets = { 52, 53, 54 } }, + { .dt_id = MT8188_AP_SOC3, + .cal_offsets = { 55, 56, 57 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x200, + .mode = LVTS_MSR_FILTERED_MODE, + }, + { + .lvts_sensor = { + { .dt_id = MT8188_AP_CAM1, + .cal_offsets = { 58, 59, 60 } }, + { .dt_id = MT8188_AP_CAM2, + .cal_offsets = { 61, 62, 63 } }, + }, + VALID_SENSOR_MAP(1, 1, 0, 0), + .offset = 0x300, + .mode = LVTS_MSR_FILTERED_MODE, + } +}; + static const struct lvts_ctrl_data mt8192_lvts_mcu_data_ctrl[] = { { - .cal_offset = { 0x04, 0x08 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_BIG_CPU0 }, - { .dt_id = MT8192_MCU_BIG_CPU1 } + { .dt_id = MT8192_MCU_BIG_CPU0, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT8192_MCU_BIG_CPU1, + .cal_offsets = { 0x08, 0x09, 0x0a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, }, { - .cal_offset = { 0x0c, 0x10 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_BIG_CPU2 }, - { .dt_id = MT8192_MCU_BIG_CPU3 } + { .dt_id = MT8192_MCU_BIG_CPU2, + .cal_offsets = { 0x0c, 0x0d, 0x0e } }, + { .dt_id = MT8192_MCU_BIG_CPU3, + .cal_offsets = { 0x10, 0x11, 0x12 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, }, { - .cal_offset = { 0x14, 0x18, 0x1c, 0x20 }, .lvts_sensor = { - { .dt_id = MT8192_MCU_LITTLE_CPU0 }, - { .dt_id = MT8192_MCU_LITTLE_CPU1 }, - { .dt_id = MT8192_MCU_LITTLE_CPU2 }, - { .dt_id = MT8192_MCU_LITTLE_CPU3 } + { .dt_id = MT8192_MCU_LITTLE_CPU0, + .cal_offsets = { 0x14, 0x15, 0x16 } }, + { .dt_id = MT8192_MCU_LITTLE_CPU1, + .cal_offsets = { 0x18, 0x19, 0x1a } }, + { .dt_id = MT8192_MCU_LITTLE_CPU2, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT8192_MCU_LITTLE_CPU3, + .cal_offsets = { 0x20, 0x21, 0x22 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, .mode = LVTS_MSR_FILTERED_MODE, } }; static const struct lvts_ctrl_data mt8192_lvts_ap_data_ctrl[] = { - { - .cal_offset = { 0x24, 0x28 }, + { .lvts_sensor = { - { .dt_id = MT8192_AP_VPU0 }, - { .dt_id = MT8192_AP_VPU1 } + { .dt_id = MT8192_AP_VPU0, + .cal_offsets = { 0x24, 0x25, 0x26 } }, + { .dt_id = MT8192_AP_VPU1, + .cal_offsets = { 0x28, 0x29, 0x2a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { - .cal_offset = { 0x2c, 0x30 }, .lvts_sensor = { - { .dt_id = MT8192_AP_GPU0 }, - { .dt_id = MT8192_AP_GPU1 } + { .dt_id = MT8192_AP_GPU0, + .cal_offsets = { 0x2c, 0x2d, 0x2e } }, + { .dt_id = MT8192_AP_GPU1, + .cal_offsets = { 0x30, 0x31, 0x32 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { - .cal_offset = { 0x34, 0x38 }, .lvts_sensor = { - { .dt_id = MT8192_AP_INFRA }, - { .dt_id = MT8192_AP_CAM }, + { .dt_id = MT8192_AP_INFRA, + .cal_offsets = { 0x34, 0x35, 0x36 } }, + { .dt_id = MT8192_AP_CAM, + .cal_offsets = { 0x38, 0x39, 0x3a } }, }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, }, { - .cal_offset = { 0x3c, 0x40, 0x44 }, .lvts_sensor = { - { .dt_id = MT8192_AP_MD0 }, - { .dt_id = MT8192_AP_MD1 }, - { .dt_id = MT8192_AP_MD2 } + { .dt_id = MT8192_AP_MD0, + .cal_offsets = { 0x3c, 0x3d, 0x3e } }, + { .dt_id = MT8192_AP_MD1, + .cal_offsets = { 0x40, 0x41, 0x42 } }, + { .dt_id = MT8192_AP_MD2, + .cal_offsets = { 0x44, 0x45, 0x46 } } }, - .num_lvts_sensor = 3, + VALID_SENSOR_MAP(1, 1, 1, 0), .offset = 0x300, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8192, } }; static const struct lvts_ctrl_data mt8195_lvts_mcu_data_ctrl[] = { { - .cal_offset = { 0x04, 0x07 }, .lvts_sensor = { - { .dt_id = MT8195_MCU_BIG_CPU0 }, - { .dt_id = MT8195_MCU_BIG_CPU1 } + { .dt_id = MT8195_MCU_BIG_CPU0, + .cal_offsets = { 0x04, 0x05, 0x06 } }, + { .dt_id = MT8195_MCU_BIG_CPU1, + .cal_offsets = { 0x07, 0x08, 0x09 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { - .cal_offset = { 0x0d, 0x10 }, .lvts_sensor = { - { .dt_id = MT8195_MCU_BIG_CPU2 }, - { .dt_id = MT8195_MCU_BIG_CPU3 } + { .dt_id = MT8195_MCU_BIG_CPU2, + .cal_offsets = { 0x0d, 0x0e, 0x0f } }, + { .dt_id = MT8195_MCU_BIG_CPU3, + .cal_offsets = { 0x10, 0x11, 0x12 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { - .cal_offset = { 0x16, 0x19, 0x1c, 0x1f }, .lvts_sensor = { - { .dt_id = MT8195_MCU_LITTLE_CPU0 }, - { .dt_id = MT8195_MCU_LITTLE_CPU1 }, - { .dt_id = MT8195_MCU_LITTLE_CPU2 }, - { .dt_id = MT8195_MCU_LITTLE_CPU3 } + { .dt_id = MT8195_MCU_LITTLE_CPU0, + .cal_offsets = { 0x16, 0x17, 0x18 } }, + { .dt_id = MT8195_MCU_LITTLE_CPU1, + .cal_offsets = { 0x19, 0x1a, 0x1b } }, + { .dt_id = MT8195_MCU_LITTLE_CPU2, + .cal_offsets = { 0x1c, 0x1d, 0x1e } }, + { .dt_id = MT8195_MCU_LITTLE_CPU3, + .cal_offsets = { 0x1f, 0x20, 0x21 } } }, - .num_lvts_sensor = 4, + VALID_SENSOR_MAP(1, 1, 1, 1), .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, } }; static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { - { - .cal_offset = { 0x25, 0x28 }, + { .lvts_sensor = { - { .dt_id = MT8195_AP_VPU0 }, - { .dt_id = MT8195_AP_VPU1 } + { .dt_id = MT8195_AP_VPU0, + .cal_offsets = { 0x25, 0x26, 0x27 } }, + { .dt_id = MT8195_AP_VPU1, + .cal_offsets = { 0x28, 0x29, 0x2a } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x0, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { - .cal_offset = { 0x2e, 0x31 }, .lvts_sensor = { - { .dt_id = MT8195_AP_GPU0 }, - { .dt_id = MT8195_AP_GPU1 } + { .dt_id = MT8195_AP_GPU0, + .cal_offsets = { 0x2e, 0x2f, 0x30 } }, + { .dt_id = MT8195_AP_GPU1, + .cal_offsets = { 0x31, 0x32, 0x33 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x100, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { - .cal_offset = { 0x37, 0x3a, 0x3d }, .lvts_sensor = { - { .dt_id = MT8195_AP_VDEC }, - { .dt_id = MT8195_AP_IMG }, - { .dt_id = MT8195_AP_INFRA }, + { .dt_id = MT8195_AP_VDEC, + .cal_offsets = { 0x37, 0x38, 0x39 } }, + { .dt_id = MT8195_AP_IMG, + .cal_offsets = { 0x3a, 0x3b, 0x3c } }, + { .dt_id = MT8195_AP_INFRA, + .cal_offsets = { 0x3d, 0x3e, 0x3f } } }, - .num_lvts_sensor = 3, + VALID_SENSOR_MAP(1, 1, 1, 0), .offset = 0x200, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, }, { - .cal_offset = { 0x43, 0x46 }, .lvts_sensor = { - { .dt_id = MT8195_AP_CAM0 }, - { .dt_id = MT8195_AP_CAM1 } + { .dt_id = MT8195_AP_CAM0, + .cal_offsets = { 0x43, 0x44, 0x45 } }, + { .dt_id = MT8195_AP_CAM1, + .cal_offsets = { 0x46, 0x47, 0x48 } } }, - .num_lvts_sensor = 2, + VALID_SENSOR_MAP(1, 1, 0, 0), .offset = 0x300, - .hw_tshut_temp = LVTS_HW_SHUTDOWN_MT8195, } }; @@ -1525,16 +1691,47 @@ static const struct lvts_data mt7988_lvts_ap_data = { .num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, + .gt_calib_bit_offset = 24, +}; + +static const struct lvts_data mt8186_lvts_data = { + .lvts_ctrl = mt8186_lvts_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT7988, + .temp_offset = LVTS_COEFF_B_MT7988, + .gt_calib_bit_offset = 24, +}; + +static const struct lvts_data mt8188_lvts_mcu_data = { + .lvts_ctrl = mt8188_lvts_mcu_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_mcu_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 20, +}; + +static const struct lvts_data mt8188_lvts_ap_data = { + .lvts_ctrl = mt8188_lvts_ap_data_ctrl, + .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_ap_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 20, }; static const struct lvts_data mt8192_lvts_mcu_data = { .lvts_ctrl = mt8192_lvts_mcu_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8192_lvts_ap_data = { .lvts_ctrl = mt8192_lvts_ap_data_ctrl, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl), + .temp_factor = LVTS_COEFF_A_MT8195, + .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8195_lvts_mcu_data = { @@ -1542,6 +1739,7 @@ static const struct lvts_data mt8195_lvts_mcu_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct lvts_data mt8195_lvts_ap_data = { @@ -1549,10 +1747,14 @@ static const struct lvts_data mt8195_lvts_ap_data = { .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, + .gt_calib_bit_offset = 24, }; static const struct of_device_id lvts_of_match[] = { { .compatible = "mediatek,mt7988-lvts-ap", .data = &mt7988_lvts_ap_data }, + { .compatible = "mediatek,mt8186-lvts", .data = &mt8186_lvts_data }, + { .compatible = "mediatek,mt8188-lvts-mcu", .data = &mt8188_lvts_mcu_data }, + { .compatible = "mediatek,mt8188-lvts-ap", .data = &mt8188_lvts_ap_data }, { .compatible = "mediatek,mt8192-lvts-mcu", .data = &mt8192_lvts_mcu_data }, { .compatible = "mediatek,mt8192-lvts-ap", .data = &mt8192_lvts_ap_data }, { .compatible = "mediatek,mt8195-lvts-mcu", .data = &mt8195_lvts_mcu_data }, diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c index f6edb12ec004..5225b3621a56 100644 --- a/drivers/thermal/qcom/lmh.c +++ b/drivers/thermal/qcom/lmh.c @@ -95,6 +95,9 @@ static int lmh_probe(struct platform_device *pdev) unsigned int enable_alg; u32 node_id; + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + lmh_data = devm_kzalloc(dev, sizeof(*lmh_data), GFP_KERNEL); if (!lmh_data) return -ENOMEM; diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 78c5cfe6a0c0..3cd74f6cac8f 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -74,7 +74,6 @@ struct qpnp_tm_chip { long temp; unsigned int thresh; unsigned int stage; - unsigned int prev_stage; unsigned int base; /* protects .thresh, .stage and chip registers */ struct mutex lock; diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 29a61d2d6ca3..0cb7301eca6e 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -107,6 +107,7 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { static const struct tsens_ops ops_generic_v2 = { .init = init_common, .get_temp = get_temp_tsens_valid, + .resume = tsens_resume_common, }; struct tsens_plat_data data_tsens_v2 = { diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index 6d7c16ccb44d..e76e23026dc8 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -17,6 +17,7 @@ #include <linux/pm.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/suspend.h> #include <linux/thermal.h> #include "../thermal_hwmon.h" #include "tsens.h" @@ -264,7 +265,7 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, for (i = 0; i < priv->num_sensors; i++) { dev_dbg(priv->dev, "%s: sensor%d - data_point1:%#x data_point2:%#x\n", - __func__, i, p1[i], p2[i]); + __func__, i, p1[i], p2 ? p2[i] : 0); if (!priv->sensor[i].slope) priv->sensor[i].slope = SLOPE_DEFAULT; @@ -1193,6 +1194,36 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, return ret; } +#ifdef CONFIG_SUSPEND +static int tsens_reinit(struct tsens_priv *priv) +{ + if (tsens_version(priv) >= VER_2_X) { + /* + * Re-enable the watchdog, unmask the bark. + * Disable cycle completion monitoring + */ + if (priv->feat->has_watchdog) { + regmap_field_write(priv->rf[WDOG_BARK_MASK], 0); + regmap_field_write(priv->rf[CC_MON_MASK], 1); + } + + /* Re-enable interrupts */ + tsens_enable_irq(priv); + } + + return 0; +} + +int tsens_resume_common(struct tsens_priv *priv) +{ + if (pm_suspend_target_state == PM_SUSPEND_MEM) + tsens_reinit(priv); + + return 0; +} + +#endif /* !CONFIG_SUSPEND */ + static int tsens_register(struct tsens_priv *priv) { int i, ret; diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index cb637fa289ca..cab39de045b1 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -634,6 +634,11 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mo int init_common(struct tsens_priv *priv); int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp); int get_temp_common(const struct tsens_sensor *s, int *temp); +#ifdef CONFIG_SUSPEND +int tsens_resume_common(struct tsens_priv *priv); +#else +#define tsens_resume_common NULL +#endif /* TSENS target */ extern struct tsens_plat_data data_8960; diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index a764cb1115a5..02494fa142c3 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -65,26 +65,29 @@ #define TSC_MAX_NUM 5 -/* Structure for thermal temperature calculation */ -struct equation_coefs { - int a1; - int b1; - int a2; - int b2; -}; - struct rcar_gen3_thermal_priv; struct rcar_thermal_info { - int ths_tj_1; + int scale; + int adj_below; + int adj_above; void (*read_fuses)(struct rcar_gen3_thermal_priv *priv); }; +struct equation_set_coef { + int a; + int b; +}; + struct rcar_gen3_thermal_tsc { + struct rcar_gen3_thermal_priv *priv; void __iomem *base; struct thermal_zone_device *zone; - struct equation_coefs coef; - int tj_t; + /* Different coefficients are used depending on a threshold. */ + struct { + struct equation_set_coef below; + struct equation_set_coef above; + } coef; int thcode[3]; }; @@ -93,6 +96,7 @@ struct rcar_gen3_thermal_priv { struct thermal_zone_device_ops ops; unsigned int num_tscs; int ptat[3]; + int tj_t; const struct rcar_thermal_info *info; }; @@ -111,84 +115,75 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, /* * Linear approximation for temperature * - * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a + * [temp] = ((thadj - [reg]) * a) / b + adj + * [reg] = thadj - ([temp] - adj) * b / a * * The constants a and b are calculated using two triplets of int values PTAT * and THCODE. PTAT and THCODE can either be read from hardware or use hard - * coded values from driver. The formula to calculate a and b are taken from - * BSP and sparsely documented and understood. + * coded values from the driver. The formula to calculate a and b are taken from + * the datasheet. Different calculations are needed for a and b depending on + * if the input variables ([temp] or [reg]) are above or below a threshold. The + * threshold is also calculated from PTAT and THCODE using formulas from the + * datasheet. + * + * The constant thadj is one of the THCODE values, which one to use depends on + * the threshold and input value. * - * Examining the linear formula and the formula used to calculate constants a - * and b while knowing that the span for PTAT and THCODE values are between - * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. - * Integer also needs to be signed so that leaves 7 bits for binary - * fixed point scaling. + * The constants adj is taken verbatim from the datasheet. Two values exists, + * which one to use depends on the input value and the calculated threshold. + * Furthermore different SoC models supported by the driver have different sets + * of values. The values for each model are stored in the device match data. */ -#define FIXPT_SHIFT 7 -#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) -#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) -#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) -#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) - -#define RCAR3_THERMAL_GRAN 500 /* mili Celsius */ - -/* no idea where these constants come from */ -#define TJ_3 -41 - -static void rcar_gen3_thermal_calc_coefs(struct rcar_gen3_thermal_priv *priv, - struct rcar_gen3_thermal_tsc *tsc, - int ths_tj_1) +static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv) { - /* TODO: Find documentation and document constant calculation formula */ - - /* - * Division is not scaled in BSP and if scaled it might overflow - * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled - */ - tsc->tj_t = (FIXPT_INT((priv->ptat[1] - priv->ptat[2]) * (ths_tj_1 - TJ_3)) - / (priv->ptat[0] - priv->ptat[2])) + FIXPT_INT(TJ_3); - - tsc->coef.a1 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[2]), - tsc->tj_t - FIXPT_INT(TJ_3)); - tsc->coef.b1 = FIXPT_INT(tsc->thcode[2]) - tsc->coef.a1 * TJ_3; - - tsc->coef.a2 = FIXPT_DIV(FIXPT_INT(tsc->thcode[1] - tsc->thcode[0]), - tsc->tj_t - FIXPT_INT(ths_tj_1)); - tsc->coef.b2 = FIXPT_INT(tsc->thcode[0]) - tsc->coef.a2 * ths_tj_1; + priv->tj_t = + DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale, + priv->ptat[0] - priv->ptat[2]) + + priv->info->adj_below; } - -static int rcar_gen3_thermal_round(int temp) +static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv, + struct rcar_gen3_thermal_tsc *tsc) { - int result, round_offs; + tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]); + tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]); - round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : - -RCAR3_THERMAL_GRAN / 2; - result = (temp + round_offs) / RCAR3_THERMAL_GRAN; - return result * RCAR3_THERMAL_GRAN; + tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]); + tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]); } static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz); - int mcelsius, val; - int reg; + struct rcar_gen3_thermal_priv *priv = tsc->priv; + const struct equation_set_coef *coef; + int adj, decicelsius, reg, thcode; /* Read register and convert to mili Celsius */ reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; - if (reg <= tsc->thcode[1]) - val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, - tsc->coef.a1); - else - val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, - tsc->coef.a2); - mcelsius = FIXPT_TO_MCELSIUS(val); + if (reg < tsc->thcode[1]) { + adj = priv->info->adj_below; + coef = &tsc->coef.below; + thcode = tsc->thcode[2]; + } else { + adj = priv->info->adj_above; + coef = &tsc->coef.above; + thcode = tsc->thcode[0]; + } + + /* + * The dividend can't be grown as it might overflow, instead shorten the + * divisor to convert to decidegree Celsius. If we convert after the + * division precision is lost as we will scale up from whole degrees + * Celsius. + */ + decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10); /* Guaranteed operating range is -40C to 125C. */ - /* Round value to device granularity setting */ - *temp = rcar_gen3_thermal_round(mcelsius); + /* Reporting is done in millidegree Celsius */ + *temp = decicelsius * 100 + adj * 1000; return 0; } @@ -196,15 +191,22 @@ static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp) static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, int mcelsius) { - int celsius, val; + struct rcar_gen3_thermal_priv *priv = tsc->priv; + const struct equation_set_coef *coef; + int adj, celsius, thcode; celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); - if (celsius <= INT_FIXPT(tsc->tj_t)) - val = celsius * tsc->coef.a1 + tsc->coef.b1; - else - val = celsius * tsc->coef.a2 + tsc->coef.b2; + if (celsius < priv->tj_t) { + coef = &tsc->coef.below; + adj = priv->info->adj_below; + thcode = tsc->thcode[2]; + } else { + coef = &tsc->coef.above; + adj = priv->info->adj_above; + thcode = tsc->thcode[0]; + } - return INT_FIXPT(val); + return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a); } static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high) @@ -369,17 +371,23 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv, } static const struct rcar_thermal_info rcar_m3w_thermal_info = { - .ths_tj_1 = 116, + .scale = 157, + .adj_below = -41, + .adj_above = 116, .read_fuses = rcar_gen3_thermal_read_fuses_gen3, }; static const struct rcar_thermal_info rcar_gen3_thermal_info = { - .ths_tj_1 = 126, + .scale = 167, + .adj_below = -41, + .adj_above = 126, .read_fuses = rcar_gen3_thermal_read_fuses_gen3, }; static const struct rcar_thermal_info rcar_gen4_thermal_info = { - .ths_tj_1 = 126, + .scale = 167, + .adj_below = -41, + .adj_above = 126, .read_fuses = rcar_gen3_thermal_read_fuses_gen4, }; @@ -516,6 +524,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) goto error_unregister; } + tsc->priv = priv; tsc->base = devm_ioremap_resource(dev, res); if (IS_ERR(tsc->base)) { ret = PTR_ERR(tsc->base); @@ -530,11 +539,13 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!rcar_gen3_thermal_read_fuses(priv)) dev_info(dev, "No calibration values fused, fallback to driver values\n"); + rcar_gen3_thermal_shared_coefs(priv); + for (i = 0; i < priv->num_tscs; i++) { struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; rcar_gen3_thermal_init(priv, tsc); - rcar_gen3_thermal_calc_coefs(priv, tsc, priv->info->ths_tj_1); + rcar_gen3_thermal_tsc_coefs(priv, tsc); zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops); if (IS_ERR(zone)) { diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 34a31bc72023..11750a145d74 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> +#include <linux/list_sort.h> #include <linux/thermal.h> #include <linux/reboot.h> #include <linux/string.h> @@ -295,17 +296,18 @@ 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) + else if (tz->passive > 0) 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); } -static void handle_non_critical_trips(struct thermal_zone_device *tz, - const struct thermal_trip *trip) +static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) { - tz->governor ? tz->governor->throttle(tz, trip) : - def_governor->throttle(tz, trip); + if (tz->governor) + return tz->governor; + + return def_governor; } void thermal_governor_update_tz(struct thermal_zone_device *tz, @@ -348,10 +350,6 @@ void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz) static void handle_critical_trips(struct thermal_zone_device *tz, const struct thermal_trip *trip) { - /* If we have not crossed the trip_temp, we do not care. */ - if (trip->temperature <= 0 || tz->temperature < trip->temperature) - return; - trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type); if (trip->type == THERMAL_TRIP_CRITICAL) @@ -361,55 +359,60 @@ static void handle_critical_trips(struct thermal_zone_device *tz, } static void handle_thermal_trip(struct thermal_zone_device *tz, - struct thermal_trip *trip) + struct thermal_trip_desc *td, + struct list_head *way_up_list, + struct list_head *way_down_list) { + const struct thermal_trip *trip = &td->trip; + int old_threshold; + if (trip->temperature == THERMAL_TEMP_INVALID) return; - if (tz->last_temperature == THERMAL_TEMP_INVALID) { - /* Initialization. */ - trip->threshold = trip->temperature; - if (tz->temperature >= trip->threshold) - trip->threshold -= trip->hysteresis; - } else if (tz->last_temperature < trip->threshold) { + /* + * If the trip temperature or hysteresis has been updated recently, + * the threshold needs to be computed again using the new values. + * However, its initial value still reflects the old ones and that + * is what needs to be compared with the previous zone temperature + * to decide which action to take. + */ + old_threshold = td->threshold; + td->threshold = trip->temperature; + + if (tz->last_temperature >= old_threshold && + tz->last_temperature != THERMAL_TEMP_INVALID) { /* - * The trip threshold is equal to the trip temperature, unless - * the latter has changed in the meantime. In either case, - * the trip is crossed if the current zone temperature is at - * least equal to its temperature, but otherwise ensure that - * the threshold and the trip temperature will be equal. + * Mitigation is under way, so it needs to stop if the zone + * temperature falls below the low temperature of the trip. + * In that case, the trip temperature becomes the new threshold. */ - if (tz->temperature >= trip->temperature) { - thermal_notify_tz_trip_up(tz, trip); - thermal_debug_tz_trip_up(tz, trip); - trip->threshold = trip->temperature - trip->hysteresis; + if (tz->temperature < trip->temperature - trip->hysteresis) { + list_add(&td->notify_list_node, way_down_list); + td->notify_temp = trip->temperature - trip->hysteresis; + + if (trip->type == THERMAL_TRIP_PASSIVE) { + tz->passive--; + WARN_ON(tz->passive < 0); + } } else { - trip->threshold = trip->temperature; + td->threshold -= trip->hysteresis; } - } else { + } else if (tz->temperature >= trip->temperature) { /* - * The previous zone temperature was above or equal to the trip - * threshold, which would be equal to the "low temperature" of - * the trip (its temperature minus its hysteresis), unless the - * trip temperature or hysteresis had changed. In either case, - * the trip is crossed if the current zone temperature is below - * the low temperature of the trip, but otherwise ensure that - * the trip threshold will be equal to the low temperature of - * the trip. + * There is no mitigation under way, so it needs to be started + * if the zone temperature exceeds the trip one. The new + * threshold is then set to the low temperature of the trip. */ - if (tz->temperature < trip->temperature - trip->hysteresis) { - thermal_notify_tz_trip_down(tz, trip); - thermal_debug_tz_trip_down(tz, trip); - trip->threshold = trip->temperature; - } else { - trip->threshold = trip->temperature - trip->hysteresis; - } + list_add_tail(&td->notify_list_node, way_up_list); + td->notify_temp = trip->temperature; + td->threshold -= trip->hysteresis; + + if (trip->type == THERMAL_TRIP_PASSIVE) + tz->passive++; + else if (trip->type == THERMAL_TRIP_CRITICAL || + trip->type == THERMAL_TRIP_HOT) + handle_critical_trips(tz, trip); } - - if (trip->type == THERMAL_TRIP_CRITICAL || trip->type == THERMAL_TRIP_HOT) - handle_critical_trips(tz, trip); - else - handle_non_critical_trips(tz, trip); } static void update_temperature(struct thermal_zone_device *tz) @@ -431,7 +434,6 @@ static void update_temperature(struct thermal_zone_device *tz) trace_thermal_temperature(tz); thermal_genl_sampling_temp(tz->id, temp); - thermal_debug_update_temp(tz); } static void thermal_zone_device_check(struct work_struct *work) @@ -449,16 +451,41 @@ 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->passive = 0; tz->prev_low_trip = -INT_MAX; tz->prev_high_trip = INT_MAX; list_for_each_entry(pos, &tz->thermal_instances, tz_node) pos->initialized = false; } +static void thermal_governor_trip_crossed(struct thermal_governor *governor, + struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up) +{ + if (governor->trip_crossed) + governor->trip_crossed(tz, trip, crossed_up); +} + +static int thermal_trip_notify_cmp(void *ascending, 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; +} + void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { - struct thermal_trip *trip; + struct thermal_governor *governor = thermal_get_tz_governor(tz); + struct thermal_trip_desc *td; + LIST_HEAD(way_down_list); + LIST_HEAD(way_up_list); if (tz->suspended) return; @@ -468,12 +495,34 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, update_temperature(tz); + if (tz->temperature == THERMAL_TEMP_INVALID) + return; + __thermal_zone_set_trips(tz); tz->notify_event = event; - for_each_trip(tz, trip) - handle_thermal_trip(tz, trip); + 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); + list_for_each_entry(td, &way_up_list, notify_list_node) { + thermal_notify_tz_trip_up(tz, &td->trip); + thermal_debug_tz_trip_up(tz, &td->trip); + thermal_governor_trip_crossed(governor, tz, &td->trip, true); + } + + list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); + list_for_each_entry(td, &way_down_list, notify_list_node) { + thermal_notify_tz_trip_down(tz, &td->trip); + thermal_debug_tz_trip_down(tz, &td->trip); + thermal_governor_trip_crossed(governor, tz, &td->trip, false); + } + + if (governor->manage) + governor->manage(tz); + + thermal_debug_update_trip_stats(tz); monitor_thermal_zone(tz); } @@ -766,7 +815,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (trip_index < 0 || trip_index >= tz->num_trips) return -EINVAL; - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index], cdev, + return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, upper, lower, weight); } EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); @@ -825,7 +874,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, if (trip_index < 0 || trip_index >= tz->num_trips) return -EINVAL; - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index], cdev); + return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); } EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); @@ -897,6 +946,7 @@ __thermal_cooling_device_register(struct device_node *np, { struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; + unsigned long current_state; int id, ret; if (!ops || !ops->get_max_state || !ops->get_cur_state || @@ -934,6 +984,10 @@ __thermal_cooling_device_register(struct device_node *np, if (ret) goto out_cdev_type; + ret = cdev->ops->get_cur_state(cdev, ¤t_state); + if (ret) + goto out_cdev_type; + thermal_cooling_device_setup_sysfs(cdev); ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); @@ -947,6 +1001,8 @@ __thermal_cooling_device_register(struct device_node *np, return ERR_PTR(ret); } + thermal_debug_cdev_add(cdev, current_state); + /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); @@ -962,8 +1018,6 @@ __thermal_cooling_device_register(struct device_node *np, mutex_unlock(&thermal_list_lock); - thermal_debug_cdev_add(cdev); - return cdev; out_cooling_dev: @@ -1221,16 +1275,19 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { - int i, ret = -EINVAL; + const struct thermal_trip_desc *td; + int ret = -EINVAL; if (tz->ops.get_crit_temp) return tz->ops.get_crit_temp(tz, temp); mutex_lock(&tz->lock); - for (i = 0; i < tz->num_trips; i++) { - if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) { - *temp = tz->trips[i].temperature; + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->type == THERMAL_TRIP_CRITICAL) { + *temp = trip->temperature; ret = 0; break; } @@ -1274,7 +1331,9 @@ thermal_zone_device_register_with_trips(const char *type, const struct thermal_zone_params *tzp, int passive_delay, int polling_delay) { + const struct thermal_trip *trip = trips; struct thermal_zone_device *tz; + struct thermal_trip_desc *td; int id; int result; struct thermal_governor *governor; @@ -1339,7 +1398,8 @@ thermal_zone_device_register_with_trips(const char *type, tz->device.class = thermal_class; tz->devdata = devdata; tz->num_trips = num_trips; - memcpy(tz->trips, trips, num_trips * sizeof(*trips)); + for_each_trip_desc(tz, td) + td->trip = *trip++; thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 0d8a42bb7ce8..d9785e5bbb08 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -15,6 +15,120 @@ #include "thermal_netlink.h" #include "thermal_debugfs.h" +struct thermal_trip_desc { + struct thermal_trip trip; + struct list_head notify_list_node; + int notify_temp; + int threshold; +}; + +/** + * struct thermal_governor - structure that holds thermal governor information + * @name: name of the governor + * @bind_to_tz: callback called when binding to a thermal zone. If it + * returns 0, the governor is bound to the thermal zone, + * otherwise it fails. + * @unbind_from_tz: callback called when a governor is unbound from a + * thermal zone. + * @trip_crossed: called for trip points that have just been crossed + * @manage: called on thermal zone temperature updates + * @update_tz: callback called when thermal zone internals have changed, e.g. + * thermal cooling instance was added/removed + * @governor_list: node in thermal_governor_list (in thermal_core.c) + */ +struct thermal_governor { + const char *name; + int (*bind_to_tz)(struct thermal_zone_device *tz); + void (*unbind_from_tz)(struct thermal_zone_device *tz); + void (*trip_crossed)(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + bool crossed_up); + void (*manage)(struct thermal_zone_device *tz); + void (*update_tz)(struct thermal_zone_device *tz, + enum thermal_notify_event reason); + struct list_head governor_list; +}; + +/** + * struct thermal_zone_device - structure for a thermal zone + * @id: unique id number for each thermal zone + * @type: the thermal zone device type + * @device: &struct device for this thermal zone + * @removal: removal 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 + * @passive_delay_jiffies: number of jiffies to wait between polls when + * performing passive cooling. + * @polling_delay_jiffies: number of jiffies to wait between polls when + * checking whether trip points have been crossed (0 for + * interrupt driven systems) + * @temperature: current temperature. This is only for core code, + * drivers should use thermal_zone_get_temp() to get the + * current temperature + * @last_temperature: previous temperature read + * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION + * @passive: 1 if you've crossed a passive trip point, 0 otherwise. + * @prev_low_trip: the low current temperature if you've crossed a passive + trip point. + * @prev_high_trip: the above current temperature if you've crossed a + passive trip point. + * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. + * @ops: operations this &thermal_zone_device supports + * @tzp: thermal zone parameters + * @governor: pointer to the governor for this thermal zone + * @governor_data: private pointer for governor data + * @thermal_instances: list of &struct thermal_instance of this thermal zone + * @ida: &struct ida to generate unique id for this zone's cooling + * devices + * @lock: lock to protect thermal_instances list + * @node: node in thermal_tz_list (in thermal_core.c) + * @poll_queue: delayed work for polling + * @notify_event: Last notification event + * @suspended: thermal zone suspend indicator + * @trips: array of struct thermal_trip objects + */ +struct thermal_zone_device { + int id; + char type[THERMAL_NAME_LENGTH]; + struct device device; + struct completion removal; + 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; + int temperature; + int last_temperature; + int emul_temperature; + int passive; + int prev_low_trip; + int prev_high_trip; + atomic_t need_update; + struct thermal_zone_device_ops ops; + struct thermal_zone_params *tzp; + struct thermal_governor *governor; + void *governor_data; + struct list_head thermal_instances; + struct ida ida; + struct mutex lock; + struct list_head node; + struct delayed_work poll_queue; + enum thermal_notify_event notify_event; + bool suspended; +#ifdef CONFIG_THERMAL_DEBUGFS + struct thermal_debugfs *debugfs; +#endif + struct thermal_trip_desc trips[] __counted_by(num_trips); +}; + /* Default Thermal Governor */ #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) #define DEFAULT_THERMAL_GOVERNOR "step_wise" @@ -120,8 +234,11 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason); /* Helpers */ -#define for_each_trip(__tz, __trip) \ - for (__trip = __tz->trips; __trip - __tz->trips < __tz->num_trips; __trip++) +#define for_each_trip_desc(__tz, __td) \ + for (__td = __tz->trips; __td - __tz->trips < __tz->num_trips; __td++) + +#define trip_to_trip_desc(__trip) \ + container_of(__trip, struct thermal_trip_desc, trip) void __thermal_zone_set_trips(struct thermal_zone_device *tz); int thermal_zone_trip_id(const struct thermal_zone_device *tz, diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 5693cc8b231a..91f9c21235a8 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -435,6 +435,14 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, } cdev_dbg->current_state = new_state; + + /* + * Create a record for the new state if it is not there, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the next state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, new_state); + transition = (old_state << 16) | new_state; /* @@ -460,8 +468,9 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, * Allocates a cooling device object for debug, initializes the * statistics and create the entries in sysfs. * @cdev: a pointer to a cooling device + * @state: current state of the cooling device */ -void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) { struct thermal_debugfs *thermal_dbg; struct cdev_debugfs *cdev_dbg; @@ -478,9 +487,16 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) INIT_LIST_HEAD(&cdev_dbg->durations[i]); } - cdev_dbg->current_state = 0; + cdev_dbg->current_state = state; cdev_dbg->timestamp = ktime_get(); + /* + * Create a record for the initial cooling device state, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the first state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, state); + debugfs_create_file("trans_table", 0400, thermal_dbg->d_top, thermal_dbg, &tt_fops); @@ -540,6 +556,7 @@ 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; for (i = 0; i < tz->num_trips; i++) { tze->trip_stats[i].min = INT_MAX; @@ -555,7 +572,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, struct tz_episode *tze; struct tz_debugfs *tz_dbg; struct thermal_debugfs *thermal_dbg = tz->debugfs; - int temperature = tz->temperature; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); @@ -624,12 +640,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); tze->trip_stats[trip_id].timestamp = now; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature); - tze->trip_stats[trip_id].count++; - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; unlock: mutex_unlock(&thermal_dbg->lock); @@ -682,6 +692,9 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, 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; + /* * This event closes the mitigation as we are crossing the * last trip point the way down. @@ -693,12 +706,12 @@ out: mutex_unlock(&thermal_dbg->lock); } -void thermal_debug_update_temp(struct thermal_zone_device *tz) +void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg = tz->debugfs; - struct tz_episode *tze; struct tz_debugfs *tz_dbg; - int trip_id, i; + struct tz_episode *tze; + int i; if (!thermal_dbg) return; @@ -710,15 +723,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz) if (!tz_dbg->nr_trips) goto out; + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + for (i = 0; i < tz_dbg->nr_trips; i++) { - trip_id = tz_dbg->trips_crossed[i]; - tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - tze->trip_stats[trip_id].count++; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, tz->temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature); - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (tz->temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; + 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; } out: mutex_unlock(&thermal_dbg->lock); @@ -753,20 +767,37 @@ static int tze_seq_show(struct seq_file *s, void *v) { struct thermal_debugfs *thermal_dbg = s->private; struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz; - struct thermal_trip *trip; + struct thermal_trip_desc *td; struct tz_episode *tze; const char *type; + u64 duration_ms; int trip_id; + char c; tze = list_entry((struct list_head *)v, struct tz_episode, node); - seq_printf(s, ",-Mitigation at %lluus, duration=%llums\n", - ktime_to_us(tze->timestamp), - ktime_to_ms(tze->duration)); + if (tze->duration == KTIME_MIN) { + /* Mitigation in progress. */ + duration_ms = ktime_to_ms(ktime_sub(ktime_get(), tze->timestamp)); + c = '>'; + } else { + duration_ms = ktime_to_ms(tze->duration); + c = '='; + } + + seq_printf(s, ",-Mitigation at %lluus, duration%c%llums\n", + ktime_to_us(tze->timestamp), c, duration_ms); - seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + struct trip_stats *trip_stats; + + /* Skip invalid trips. */ + if (trip->temperature == THERMAL_TEMP_INVALID) + continue; - for_each_trip(tz, trip) { /* * There is no possible mitigation happening at the * critical trip point, so the stats will be always @@ -775,6 +806,13 @@ static int tze_seq_show(struct seq_file *s, void *v) if (trip->type == THERMAL_TRIP_CRITICAL) continue; + trip_id = thermal_zone_trip_id(tz, trip); + trip_stats = &tze->trip_stats[trip_id]; + + /* Skip trips without any stats. */ + if (trip_stats->min > trip_stats->max) + continue; + if (trip->type == THERMAL_TRIP_PASSIVE) type = "passive"; else if (trip->type == THERMAL_TRIP_ACTIVE) @@ -782,17 +820,28 @@ static int tze_seq_show(struct seq_file *s, void *v) else type = "hot"; - trip_id = thermal_zone_trip_id(tz, trip); + if (trip_stats->timestamp != KTIME_MAX) { + /* Mitigation in progress. */ + ktime_t delta = ktime_sub(ktime_get(), + trip_stats->timestamp); + + delta = ktime_add(delta, trip_stats->duration); + duration_ms = ktime_to_ms(delta); + c = '>'; + } else { + duration_ms = ktime_to_ms(trip_stats->duration); + c = ' '; + } - seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", + seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, 9, trip->temperature, 9, trip->hysteresis, - 10, ktime_to_ms(tze->trip_stats[trip_id].duration), - 9, tze->trip_stats[trip_id].avg, - 9, tze->trip_stats[trip_id].min, - 9, tze->trip_stats[trip_id].max); + c, 10, duration_ms, + 9, trip_stats->avg, + 9, trip_stats->min, + 9, trip_stats->max); } return 0; diff --git a/drivers/thermal/thermal_debugfs.h b/drivers/thermal/thermal_debugfs.h index 155b9af5fe87..74ee65ee82ff 100644 --- a/drivers/thermal/thermal_debugfs.h +++ b/drivers/thermal/thermal_debugfs.h @@ -2,7 +2,7 @@ #ifdef CONFIG_THERMAL_DEBUGFS void thermal_debug_init(void); -void thermal_debug_cdev_add(struct thermal_cooling_device *cdev); +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state); 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); @@ -11,10 +11,10 @@ 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, const struct thermal_trip *trip); -void thermal_debug_update_temp(struct thermal_zone_device *tz); +void thermal_debug_update_trip_stats(struct thermal_zone_device *tz); #else static inline void thermal_debug_init(void) {} -static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) {} +static inline void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) {} static inline void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) {} static inline void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, int state) {} @@ -24,5 +24,5 @@ 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, const struct thermal_trip *trip) {} -static inline void thermal_debug_update_temp(struct thermal_zone_device *tz) {} +static inline void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) {} #endif /* CONFIG_THERMAL_DEBUGFS */ diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index c5a057b59c42..d9f4e26ec125 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -50,7 +50,7 @@ get_thermal_instance(struct thermal_zone_device *tz, mutex_lock(&tz->lock); mutex_lock(&cdev->lock); - trip = &tz->trips[trip_index]; + 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) { @@ -82,7 +82,7 @@ EXPORT_SYMBOL(get_thermal_instance); */ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; int crit_temp = INT_MAX; int ret = -EINVAL; @@ -91,7 +91,9 @@ int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) ret = tz->ops.get_temp(tz, temp); if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + if (trip->type == THERMAL_TRIP_CRITICAL) { crit_temp = trip->temperature; break; diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 76a231a29654..97157c453630 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -7,17 +7,13 @@ * Generic netlink for thermal management framework */ #include <linux/module.h> +#include <linux/notifier.h> #include <linux/kernel.h> #include <net/genetlink.h> #include <uapi/linux/thermal.h> #include "thermal_core.h" -enum thermal_genl_multicast_groups { - THERMAL_GENL_SAMPLING_GROUP = 0, - THERMAL_GENL_EVENT_GROUP = 1, -}; - static const struct genl_multicast_group thermal_genl_mcgrps[] = { [THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, }, [THERMAL_GENL_EVENT_GROUP] = { .name = THERMAL_GENL_EVENT_GROUP_NAME, }, @@ -74,11 +70,12 @@ struct param { typedef int (*cb_t)(struct param *); -static struct genl_family thermal_gnl_family; +static struct genl_family thermal_genl_family; +static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain); static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group) { - return genl_has_listeners(&thermal_gnl_family, &init_net, group); + return genl_has_listeners(&thermal_genl_family, &init_net, group); } /************************** Sampling encoding *******************************/ @@ -95,7 +92,7 @@ int thermal_genl_sampling_temp(int id, int temp) if (!skb) return -ENOMEM; - hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, + hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, THERMAL_GENL_SAMPLING_TEMP); if (!hdr) goto out_free; @@ -108,7 +105,7 @@ int thermal_genl_sampling_temp(int id, int temp) genlmsg_end(skb, hdr); - genlmsg_multicast(&thermal_gnl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL); + genlmsg_multicast(&thermal_genl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL); return 0; out_cancel: @@ -282,7 +279,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, return -ENOMEM; p->msg = msg; - hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event); + hdr = genlmsg_put(msg, 0, 0, &thermal_genl_family, 0, event); if (!hdr) goto out_free_msg; @@ -292,7 +289,7 @@ static int thermal_genl_send_event(enum thermal_genl_event event, genlmsg_end(msg, hdr); - genlmsg_multicast(&thermal_gnl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL); + genlmsg_multicast(&thermal_genl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL); return 0; @@ -445,7 +442,7 @@ out_cancel_nest: static int thermal_genl_cmd_tz_get_trip(struct param *p) { struct sk_buff *msg = p->msg; - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; struct thermal_zone_device *tz; struct nlattr *start_trip; int id; @@ -465,7 +462,9 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p) mutex_lock(&tz->lock); - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, thermal_zone_trip_id(tz, trip)) || nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) || @@ -593,7 +592,7 @@ static int thermal_genl_cmd_dumpit(struct sk_buff *skb, int ret; void *hdr; - hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd); + hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd); if (!hdr) return -EMSGSIZE; @@ -625,7 +624,7 @@ static int thermal_genl_cmd_doit(struct sk_buff *skb, return -ENOMEM; p.msg = msg; - hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd); + hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd); if (!hdr) goto out_free_msg; @@ -645,6 +644,27 @@ out_free_msg: return ret; } +static int thermal_genl_bind(int mcgrp) +{ + struct thermal_genl_notify n = { .mcgrp = mcgrp }; + + if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) + return -EINVAL; + + blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n); + return 0; +} + +static void thermal_genl_unbind(int mcgrp) +{ + struct thermal_genl_notify n = { .mcgrp = mcgrp }; + + if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP)) + return; + + blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n); +} + static const struct genl_small_ops thermal_genl_ops[] = { { .cmd = THERMAL_GENL_CMD_TZ_GET_ID, @@ -673,12 +693,14 @@ static const struct genl_small_ops thermal_genl_ops[] = { }, }; -static struct genl_family thermal_gnl_family __ro_after_init = { +static struct genl_family thermal_genl_family __ro_after_init = { .hdrsize = 0, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, .maxattr = THERMAL_GENL_ATTR_MAX, .policy = thermal_genl_policy, + .bind = thermal_genl_bind, + .unbind = thermal_genl_unbind, .small_ops = thermal_genl_ops, .n_small_ops = ARRAY_SIZE(thermal_genl_ops), .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, @@ -686,12 +708,22 @@ static struct genl_family thermal_gnl_family __ro_after_init = { .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), }; +int thermal_genl_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&thermal_genl_chain, nb); +} + +int thermal_genl_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&thermal_genl_chain, nb); +} + int __init thermal_netlink_init(void) { - return genl_register_family(&thermal_gnl_family); + return genl_register_family(&thermal_genl_family); } void __init thermal_netlink_exit(void) { - genl_unregister_family(&thermal_gnl_family); + genl_unregister_family(&thermal_genl_family); } diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 93a927e144d5..e01221e8816b 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -10,6 +10,19 @@ struct thermal_genl_cpu_caps { int efficiency; }; +enum thermal_genl_multicast_groups { + THERMAL_GENL_SAMPLING_GROUP = 0, + THERMAL_GENL_EVENT_GROUP = 1, + THERMAL_GENL_MAX_GROUP = THERMAL_GENL_EVENT_GROUP, +}; + +#define THERMAL_NOTIFY_BIND 0 +#define THERMAL_NOTIFY_UNBIND 1 + +struct thermal_genl_notify { + int mcgrp; +}; + struct thermal_zone_device; struct thermal_trip; struct thermal_cooling_device; @@ -18,6 +31,9 @@ struct thermal_cooling_device; #ifdef CONFIG_THERMAL_NETLINK int __init thermal_netlink_init(void); void __init thermal_netlink_exit(void); +int thermal_genl_register_notifier(struct notifier_block *nb); +int thermal_genl_unregister_notifier(struct notifier_block *nb); + int thermal_notify_tz_create(const struct thermal_zone_device *tz); int thermal_notify_tz_delete(const struct thermal_zone_device *tz); int thermal_notify_tz_enable(const struct thermal_zone_device *tz); @@ -48,6 +64,16 @@ static inline int thermal_notify_tz_create(const struct thermal_zone_device *tz) return 0; } +static inline int thermal_genl_register_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int thermal_genl_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} + static inline int thermal_notify_tz_delete(const struct thermal_zone_device *tz) { return 0; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 5b533fa40437..88211ccdfbd6 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -88,7 +88,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_type", &trip_id) != 1) return -EINVAL; - switch (tz->trips[trip_id].type) { + switch (tz->trips[trip_id].trip.type) { case THERMAL_TRIP_CRITICAL: return sprintf(buf, "critical\n"); case THERMAL_TRIP_HOT: @@ -120,7 +120,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, mutex_lock(&tz->lock); - trip = &tz->trips[trip_id]; + trip = &tz->trips[trip_id].trip; if (temp != trip->temperature) { if (tz->ops.set_trip_temp) { @@ -150,7 +150,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip_id) != 1) return -EINVAL; - return sprintf(buf, "%d\n", tz->trips[trip_id].temperature); + return sprintf(buf, "%d\n", tz->trips[trip_id].trip.temperature); } static ssize_t @@ -171,7 +171,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, mutex_lock(&tz->lock); - trip = &tz->trips[trip_id]; + trip = &tz->trips[trip_id].trip; if (hyst != trip->hysteresis) { trip->hysteresis = hyst; @@ -194,7 +194,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) return -EINVAL; - return sprintf(buf, "%d\n", tz->trips[trip_id].hysteresis); + return sprintf(buf, "%d\n", tz->trips[trip_id].trip.hysteresis); } static ssize_t @@ -393,7 +393,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; struct attribute **attrs; /* This function works only for zones with at least one trip */ @@ -429,8 +429,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) return -ENOMEM; } - for_each_trip(tz, trip) { - int indx = thermal_zone_trip_id(tz, trip); + for_each_trip_desc(tz, td) { + int indx = thermal_zone_trip_id(tz, &td->trip); /* create trip type attribute */ snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, @@ -452,7 +452,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) 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; - if (trip->flags & THERMAL_TRIP_FLAG_RW_TEMP) { + 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; @@ -467,7 +467,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz) 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; - if (trip->flags & THERMAL_TRIP_FLAG_RW_HYST) { + 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; diff --git a/drivers/thermal/thermal_trace.h b/drivers/thermal/thermal_trace.h index 459c8ce6cf3b..88a962f560f2 100644 --- a/drivers/thermal/thermal_trace.h +++ b/drivers/thermal/thermal_trace.h @@ -9,6 +9,8 @@ #include <linux/thermal.h> #include <linux/tracepoint.h> +#include "thermal_core.h" + TRACE_DEFINE_ENUM(THERMAL_TRIP_CRITICAL); TRACE_DEFINE_ENUM(THERMAL_TRIP_HOT); TRACE_DEFINE_ENUM(THERMAL_TRIP_PASSIVE); diff --git a/drivers/thermal/thermal_trace_ipa.h b/drivers/thermal/thermal_trace_ipa.h index b16b5dd863d9..a82821eebc88 100644 --- a/drivers/thermal/thermal_trace_ipa.h +++ b/drivers/thermal/thermal_trace_ipa.h @@ -7,6 +7,8 @@ #include <linux/tracepoint.h> +#include "thermal_core.h" + TRACE_EVENT(thermal_power_allocator, TP_PROTO(struct thermal_zone_device *tz, u32 total_req_power, u32 total_granted_power, int num_actors, u32 power_range, diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 497abf0d47ca..21ece8399997 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -13,11 +13,11 @@ int for_each_thermal_trip(struct thermal_zone_device *tz, int (*cb)(struct thermal_trip *, void *), void *data) { - struct thermal_trip *trip; + struct thermal_trip_desc *td; int ret; - for_each_trip(tz, trip) { - ret = cb(trip, data); + for_each_trip_desc(tz, td) { + ret = cb(&td->trip, data); if (ret) return ret; } @@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); */ void __thermal_zone_set_trips(struct thermal_zone_device *tz) { - const struct thermal_trip *trip; + const struct thermal_trip_desc *td; int low = -INT_MAX, high = INT_MAX; int ret; @@ -72,7 +72,8 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) if (!tz->ops.set_trips) return; - for_each_trip(tz, trip) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; int trip_low; trip_low = trip->temperature - trip->hysteresis; @@ -110,7 +111,7 @@ int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) return -EINVAL; - *trip = tz->trips[trip_id]; + *trip = tz->trips[trip_id].trip; return 0; } EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); @@ -135,8 +136,9 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, * Assume the trip to be located within the bounds of the thermal * zone's trips[] table. */ - return trip - tz->trips; + return trip_to_trip_desc(trip) - tz->trips; } + void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip) { diff --git a/include/dt-bindings/thermal/mediatek,lvts-thermal.h b/include/dt-bindings/thermal/mediatek,lvts-thermal.h index 997e2f55128a..bf95309d2525 100644 --- a/include/dt-bindings/thermal/mediatek,lvts-thermal.h +++ b/include/dt-bindings/thermal/mediatek,lvts-thermal.h @@ -16,6 +16,32 @@ #define MT7988_ETHWARP_0 6 #define MT7988_ETHWARP_1 7 +#define MT8186_LITTLE_CPU0 0 +#define MT8186_LITTLE_CPU1 1 +#define MT8186_LITTLE_CPU2 2 +#define MT8186_CAM 3 +#define MT8186_BIG_CPU0 4 +#define MT8186_BIG_CPU1 5 +#define MT8186_NNA 6 +#define MT8186_ADSP 7 +#define MT8186_MFG 8 + +#define MT8188_MCU_LITTLE_CPU0 0 +#define MT8188_MCU_LITTLE_CPU1 1 +#define MT8188_MCU_LITTLE_CPU2 2 +#define MT8188_MCU_LITTLE_CPU3 3 +#define MT8188_MCU_BIG_CPU0 4 +#define MT8188_MCU_BIG_CPU1 5 + +#define MT8188_AP_APU 0 +#define MT8188_AP_GPU1 1 +#define MT8188_AP_GPU2 2 +#define MT8188_AP_SOC1 3 +#define MT8188_AP_SOC2 4 +#define MT8188_AP_SOC3 5 +#define MT8188_AP_CAM1 6 +#define MT8188_AP_CAM2 7 + #define MT8195_MCU_BIG_CPU0 0 #define MT8195_MCU_BIG_CPU1 1 #define MT8195_MCU_BIG_CPU2 2 diff --git a/include/linux/thermal.h b/include/linux/thermal.h index c33f50177f51..f1155c0439c4 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -61,7 +61,6 @@ enum thermal_notify_event { * struct thermal_trip - representation of a point in temperature domain * @temperature: temperature value in miliCelsius * @hysteresis: relative hysteresis in miliCelsius - * @threshold: trip crossing notification threshold miliCelsius * @type: trip point type * @priv: pointer to driver data associated with this trip * @flags: flags representing binary properties of the trip @@ -69,7 +68,6 @@ enum thermal_notify_event { struct thermal_trip { int temperature; int hysteresis; - int threshold; enum thermal_trip_type type; u8 flags; void *priv; @@ -81,6 +79,8 @@ struct thermal_trip { #define THERMAL_TRIP_FLAG_RW (THERMAL_TRIP_FLAG_RW_TEMP | \ THERMAL_TRIP_FLAG_RW_HYST) +struct thermal_zone_device; + struct thermal_zone_device_ops { int (*bind) (struct thermal_zone_device *, struct thermal_cooling_device *); @@ -126,111 +126,6 @@ struct thermal_cooling_device { #endif }; -/** - * struct thermal_zone_device - structure for a thermal zone - * @id: unique id number for each thermal zone - * @type: the thermal zone device type - * @device: &struct device for this thermal zone - * @removal: removal 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 - * @passive_delay_jiffies: number of jiffies to wait between polls when - * performing passive cooling. - * @polling_delay_jiffies: number of jiffies to wait between polls when - * checking whether trip points have been crossed (0 for - * interrupt driven systems) - * @temperature: current temperature. This is only for core code, - * drivers should use thermal_zone_get_temp() to get the - * current temperature - * @last_temperature: previous temperature read - * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION - * @passive: 1 if you've crossed a passive trip point, 0 otherwise. - * @prev_low_trip: the low current temperature if you've crossed a passive - trip point. - * @prev_high_trip: the above current temperature if you've crossed a - passive trip point. - * @need_update: if equals 1, thermal_zone_device_update needs to be invoked. - * @ops: operations this &thermal_zone_device supports - * @tzp: thermal zone parameters - * @governor: pointer to the governor for this thermal zone - * @governor_data: private pointer for governor data - * @thermal_instances: list of &struct thermal_instance of this thermal zone - * @ida: &struct ida to generate unique id for this zone's cooling - * devices - * @lock: lock to protect thermal_instances list - * @node: node in thermal_tz_list (in thermal_core.c) - * @poll_queue: delayed work for polling - * @notify_event: Last notification event - * @suspended: thermal zone suspend indicator - * @trips: array of struct thermal_trip objects - */ -struct thermal_zone_device { - int id; - char type[THERMAL_NAME_LENGTH]; - struct device device; - struct completion removal; - 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; - int temperature; - int last_temperature; - int emul_temperature; - int passive; - int prev_low_trip; - int prev_high_trip; - atomic_t need_update; - struct thermal_zone_device_ops ops; - struct thermal_zone_params *tzp; - struct thermal_governor *governor; - void *governor_data; - struct list_head thermal_instances; - struct ida ida; - struct mutex lock; - struct list_head node; - struct delayed_work poll_queue; - enum thermal_notify_event notify_event; - bool suspended; -#ifdef CONFIG_THERMAL_DEBUGFS - struct thermal_debugfs *debugfs; -#endif - struct thermal_trip trips[] __counted_by(num_trips); -}; - -/** - * struct thermal_governor - structure that holds thermal governor information - * @name: name of the governor - * @bind_to_tz: callback called when binding to a thermal zone. If it - * returns 0, the governor is bound to the thermal zone, - * otherwise it fails. - * @unbind_from_tz: callback called when a governor is unbound from a - * thermal zone. - * @throttle: callback called for every trip point even if temperature is - * below the trip point temperature - * @update_tz: callback called when thermal zone internals have changed, e.g. - * thermal cooling instance was added/removed - * @governor_list: node in thermal_governor_list (in thermal_core.c) - */ -struct thermal_governor { - const char *name; - int (*bind_to_tz)(struct thermal_zone_device *tz); - void (*unbind_from_tz)(struct thermal_zone_device *tz); - int (*throttle)(struct thermal_zone_device *tz, - const struct thermal_trip *trip); - void (*update_tz)(struct thermal_zone_device *tz, - enum thermal_notify_event reason); - struct list_head governor_list; -}; - /* Structure to define Thermal Zone parameters */ struct thermal_zone_params { const char *governor_name; |