* [PATCH v2 0/6] thermal: Add hardware-binned trip point support
@ 2026-10-31 13:22 Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 1/6] dt-bindings: thermal: thermal-zones: add hardware-binning trip properties Gaurav Kohli
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
Some SoCs are available in multiple thermal/package bins. The silicon is
otherwise the same, but the package/bin determines which junction
temperature limits are valid for a given part. This information is
programmed in manufacturing fuses and needs to be reflected in the trip
points used by the kernel.
At the moment this is difficult to describe cleanly. Board-specific trip
overrides can handle individual cases, but they do not describe the real
hardware property: the limit is selected by SoC fuse state, not by board
identity. Duplicating thermal zones or adding vendor-specific trip
properties would also make the binding harder to share across platforms.
This series adds generic hardware-binned trip point support to the
thermal OF parser, following the same general model as the OPP
framework's opp-supported-hw property. A thermal sensor driver provides
the decoded hardware bin when registering a thermal zone, and the OF
parser uses that information to select the applicable trip data.
Two generic trip-point properties are added:
- thermal-hw-bin:
one or more hardware-bin mask groups for which the trip is valid
- temperature-bin:
per-bin trip temperatures, selected by the matching
thermal-hw-bin group
This topic was discussed at the Linux Plumbers Conference Power and
Thermal Management microconference as "Thermal binning support". The
discussion covered how to represent SoCs whose valid junction temperature
limits depend on package/bin characteristics. The approach taken here is
to keep the DT description generic and let the platform thermal driver
provide the hardware-specific fuse decoding.
LPC discussion:
https://lpc.events/event/19/contributions/2245/
Slides:
https://lpc.events/event/19/contributions/2245/attachments/1885/4034/LPC2025-Thermal-Microconf-Amit-Tj-Thermal-binning.pdf
Qualcomm TSENS is added as the first user of the generic support. On
SC7280/QCM6490-class platforms, TSENS reads the jtag-id and feat-id
QFPROM cells and maps them to the thermal hardware bin. On
X1E80100-class platforms, TSENS reads the i-temp QFPROM bit. The decoded
bin is passed to the thermal OF registration path before trip points are
parsed.
The DTS patches use the new properties for the affected Kodiak and Hamoa
thermal zones. For the standard bin the passive trip remains at 95C; for
the elevated bin it is selected as 105C.
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
Changes in v2:
- Format temperature-bin as separate DT cells. (Konrad)
- Add min/max constraints for temperature-bin entries. (Sashiko)
- Drop the post-registration hardware-bin API. (Sashiko)
- Remove CONFIG_THERMAL_BIN and make hardware-bin trip parsing part of
the thermal OF registration path. (Sashiko)
- Keep hardware-bin selection registration-time only through
devm_thermal_of_zone_register_with_bin(), because OF trips are parsed
synchronously during zone registration. (Sashiko)
- Propagate -EPROBE_DEFER when TSENS cannot read the hardware-bin nvmem
cells yet. (Sashiko)
- Link to v1: https://lore.kernel.org/r/20261002-thermal_bin-v1-0-5a29be7e8ec5@oss.qualcomm.com
---
Amit Kucheria (6):
dt-bindings: thermal: thermal-zones: add hardware-binning trip properties
thermal: add hardware-binning trip-point filtering support
dt-bindings: thermal: qcom-tsens: document qcm6490 tsens
thermal: qcom: tsens: add hardware-bin trip-point filtering
arm64: dts: qcom: kodiak: use thermal hw-bin trips
arm64: dts: qcom: hamoa: add thermal hw-bin support
.../devicetree/bindings/thermal/qcom-tsens.yaml | 8 +
.../devicetree/bindings/thermal/thermal-zones.yaml | 57 ++++-
.../driver-api/thermal/hardware-binning.rst | 112 +++++++++
Documentation/driver-api/thermal/index.rst | 1 +
MAINTAINERS | 1 +
arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts | 66 ------
arch/arm64/boot/dts/qcom/hamoa.dtsi | 42 +++-
arch/arm64/boot/dts/qcom/kodiak.dtsi | 58 +++--
drivers/thermal/qcom/tsens-v2.c | 16 ++
drivers/thermal/qcom/tsens.c | 190 +++++++++++++++-
drivers/thermal/qcom/tsens.h | 29 +++
drivers/thermal/thermal_of.c | 253 +++++++++++++++++++--
include/linux/thermal.h | 32 +++
13 files changed, 751 insertions(+), 114 deletions(-)
---
base-commit: 0d9d0dbf2fddcff5859d623e90ca73c4054276e1
change-id: 20260917-thermal_bin-fe7becdf0e17
Best regards,
--
Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/6] dt-bindings: thermal: thermal-zones: add hardware-binning trip properties
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support Gaurav Kohli
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Differences in SoC packaging can be used to provide multiple SoC SKUs at
different thermal and performance characteristics. The underlying SoC
remains the same except for how much heat the packaging can dissipate,
thereby influencing the critical trip point at which an SoC SKU needs to
be throttled. This SKU-specific data is typically burned into fuses
during manufacturing and can be read back at runtime.
Add two optional trip-point properties to describe this:
- 'temperature-bin': an array of per-bin trip temperatures, used
instead of 'temperature' when the trip depends on the hardware
revision encoded in fuses.
- 'thermal-hw-bin': one or more bitmask groups describing the hardware
revisions for which a trip point is valid, modeled on the
'opp-supported-hw' property of the OPP framework.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
.../devicetree/bindings/thermal/thermal-zones.yaml | 57 +++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml
index 999ad40a20d5..62590dfc77bf 100644
--- a/Documentation/devicetree/bindings/thermal/thermal-zones.yaml
+++ b/Documentation/devicetree/bindings/thermal/thermal-zones.yaml
@@ -152,6 +152,55 @@ patternProperties:
situation where the trip gets constantly triggered soon after
cooling action is removed.
+ temperature-bin:
+ $ref: /schemas/types.yaml#/definitions/int32-array
+ items:
+ minimum: -273000
+ maximum: 200000
+ description: |
+ An array of integers expressing trip temperatures in
+ millicelsius, one entry per hardware bin sub-group defined
+ in the thermal-hw-bin property. Each value is the trip
+ temperature for the corresponding hardware bin.
+
+ Use this property instead of 'temperature' when the trip
+ temperature depends on the SoC hardware revision as encoded
+ in fuse values. The platform driver reads the fuse and
+ registers the hardware-version bitfields with the thermal
+ zone; the framework then selects the matching entry.
+
+ Exactly one of 'temperature' or 'temperature-bin' must be
+ present in each trip node.
+
+ thermal-hw-bin:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ description: |
+ Optional bitmask(s) describing the hardware revisions for
+ which this trip point is valid.
+
+ Uses the same format and semantics as the opp-supported-hw
+ property in the OPP framework. The platform provides N
+ 32-bit values describing the hardware it is running on.
+ Each trip node may provide one or more groups of N 32-bit
+ mask values.
+
+ The total number of 32-bit cells must be a multiple of N.
+ Each contiguous group of N cells forms a sub-group. A
+ sub-group matches if, for all i in [0 .. N-1], the bitwise
+ AND of the i-th mask and the i-th platform hardware value
+ is non-zero. The trip point is enabled if any sub-group
+ matches. If multiple sub-groups match, the first matching
+ sub-group selects the corresponding temperature-bin entry.
+
+ When used together with 'temperature-bin', the number of
+ sub-groups must equal the number of entries in
+ 'temperature-bin'.
+
+ If absent, the trip point is valid for all hardware.
+ items:
+ minItems: 1
+ maxItems: 4
+
type:
$ref: /schemas/types.yaml#/definitions/string
enum:
@@ -179,8 +228,14 @@ patternProperties:
The active trip type can be used to control other HW to
help in cooling e.g. fans can be sped up or slowed down
+ oneOf:
+ - required:
+ - temperature
+ - required:
+ - temperature-bin
+ dependentRequired:
+ temperature-bin: [thermal-hw-bin]
required:
- - temperature
- hysteresis
- type
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 1/6] dt-bindings: thermal: thermal-zones: add hardware-binning trip properties Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
2026-10-31 13:40 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 3/6] dt-bindings: thermal: qcom-tsens: document qcm6490 tsens Gaurav Kohli
` (3 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Differences in SoC packaging can be used to provide multiple SoC SKUs at
different thermal and performance characteristics. The underlying SoC
remains the same except for how much heat the packaging can dissipate,
thereby influencing what is the critical trip point at which the SoC SKU
needs to be throttled. This is the basis for thermal-binning.
This SKU-specific data is typically burned into EFUSEs during
manufacturing. We can read back this information at runtime to populate
the appropriate trip points applicable to an SKU.
Linux support for this has been modeled along the lines of the
opp-supported-hw property in the OPP framework that already allows
similar mechanisms to allow/disallow certain operating frequencies for
the processor. The thermal-hw-bin property allows multiple SKUs of an
SoC to be configured for different critical trip points based on a fuse
mask.
Add thermal OF support to parse the thermal-hw-bin and temperature-bin
DT properties. Platform sensor drivers pass decoded hardware-version
information to devm_thermal_of_zone_register_with_bin() before trip
points are parsed, allowing the OF parser to filter trips and select
per-bin temperatures during zone registration.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Co-developed-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
.../driver-api/thermal/hardware-binning.rst | 112 +++++++++
Documentation/driver-api/thermal/index.rst | 1 +
MAINTAINERS | 1 +
drivers/thermal/thermal_of.c | 253 +++++++++++++++++++--
include/linux/thermal.h | 32 +++
5 files changed, 377 insertions(+), 22 deletions(-)
diff --git a/Documentation/driver-api/thermal/hardware-binning.rst b/Documentation/driver-api/thermal/hardware-binning.rst
new file mode 100644
index 000000000000..c385a2e224e3
--- /dev/null
+++ b/Documentation/driver-api/thermal/hardware-binning.rst
@@ -0,0 +1,112 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Thermal hardware bins
+=====================
+
+:Author: Amit Kucheria
+
+Overview
+========
+
+Differences in SoC packaging let a vendor ship several SKUs built from the
+same silicon but with different thermal characteristics. Better packaging
+dissipates more heat, so the temperature at which a SKU must be throttled (or
+shut down) differs between SKUs. This SKU-specific information is typically
+burned into eFUSEs during manufacturing and can be read back at runtime.
+
+Thermal "hardware binning" lets a single device tree describe the trip points
+for every SKU of an SoC and have the kernel pick the values that match the
+silicon it is actually running on. The mechanism is modelled on the
+``opp-supported-hw`` property used by the OPP framework: the platform supplies
+one or more 32-bit hardware-version words decoded from fuses, and each trip
+point declares the hardware bitmasks for which it is valid.
+
+Hardware-bin trip parsing is part of the thermal OF helpers.
+
+Device tree properties
+=======================
+
+Two properties are added to a trip-point node (see
+``Documentation/devicetree/bindings/thermal/thermal-zones.yaml`` for the
+authoritative schema):
+
+``temperature-bin``
+ An array of trip temperatures in millicelsius, one entry per
+ ``thermal-hw-bin`` sub-group. It is used *instead of* the plain
+ ``temperature`` property when the trip temperature depends on the hardware
+ bin. Exactly one of ``temperature`` or ``temperature-bin`` must be present
+ in a trip node.
+
+``thermal-hw-bin``
+ One or more groups of N 32-bit masks describing the hardware revisions for
+ which the trip point is valid, where N is the number of hardware-version
+ words supplied by the platform (``supported_hw_bin_count``, see below). The
+ format and semantics match ``opp-supported-hw``: a sub-group matches when,
+ for every word ``i`` in ``[0 .. N-1]``, the bitwise AND of the ``i``-th mask
+ and the ``i``-th platform hardware value is non-zero. The trip is enabled if
+ any sub-group matches. When paired with ``temperature-bin`` the number of
+ sub-groups must equal the number of ``temperature-bin`` entries, and the
+ matching sub-group selects which ``temperature-bin`` entry is used. When
+ ``temperature-bin`` is present, ``thermal-hw-bin`` is required.
+
+Example::
+
+ trips {
+ cpu-critical {
+ /* bin 0 throttles at 95C, bin 1 at 105C */
+ temperature-bin = <95000 105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
+ hysteresis = <1000>;
+ type = "critical";
+ };
+ };
+
+Here the platform registers a single hardware-version word (N = 1). If that
+word ANDed with ``0x1`` is non-zero, the first sub-group matches and the trip
+uses 95000 mC; if it ANDed with ``0x2`` is non-zero, the second sub-group
+matches and the trip uses 105000 mC. If neither matches the trip is skipped.
+
+Platform driver API
+===================
+
+A platform driver decodes the SoC fuses into hardware-version words and hands
+them to the thermal core, which makes them available to the OF trip parser.
+The words are carried in ``struct thermal_hw_bin_info``::
+
+ struct thermal_hw_bin_info {
+ const u32 *supported_hw_bin;
+ unsigned int supported_hw_bin_count;
+ };
+
+``supported_hw_bin``
+ Array of N bitfields identifying the running hardware version.
+
+``supported_hw_bin_count``
+ Number of valid entries in ``supported_hw_bin`` (the N above). Must be in
+ the range ``1 .. THERMAL_HW_BIN_MAX_LEVELS``.
+
+Pass a filled ``struct thermal_hw_bin_info`` to
+``devm_thermal_of_zone_register_with_bin()`` when registering the thermal zone.
+The hardware-version information must be available at registration time because
+the OF trip points are parsed synchronously during zone registration::
+
+ struct thermal_zone_device *
+ devm_thermal_of_zone_register_with_bin(struct device *dev, int id,
+ void *data,
+ const struct thermal_zone_device_ops *ops,
+ const struct thermal_hw_bin_info *hw_bin_info);
+
+The caller owns the storage referenced by ``hw_bin_info``. It only needs to
+remain valid for the duration of ``devm_thermal_of_zone_register_with_bin()``,
+because the OF parser consumes the values before the function returns.
+
+If a trip uses ``temperature-bin`` but no bin info has been provided for the
+zone, the core cannot select an entry; it warns once and falls back to the
+first ``temperature-bin`` value. Platform drivers should therefore provide
+the bin info before the zone's trips are parsed.
+
+The Qualcomm tsens driver is the reference user: it reads the named nvmem
+cells described by a per-SoC ``struct tsens_hw_bin_desc``, converts the raw
+fuse values into hardware-version words, and passes the result to
+``devm_thermal_of_zone_register_with_bin()``.
diff --git a/Documentation/driver-api/thermal/index.rst b/Documentation/driver-api/thermal/index.rst
index a886028014ab..5f858fabc865 100644
--- a/Documentation/driver-api/thermal/index.rst
+++ b/Documentation/driver-api/thermal/index.rst
@@ -11,6 +11,7 @@ Thermal
cpu-idle-cooling
sysfs-api
power_allocator
+ hardware-binning
exynos_thermal
exynos_thermal_emulation
diff --git a/MAINTAINERS b/MAINTAINERS
index 6e50747a621c..d99605b8b54c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27343,6 +27343,7 @@ F: Documentation/ABI/testing/sysfs-class-thermal
F: Documentation/admin-guide/thermal/
F: Documentation/devicetree/bindings/thermal/
F: Documentation/driver-api/thermal/
+F: Documentation/driver-api/thermal/hardware-binning.rst
F: drivers/thermal/
F: include/dt-bindings/thermal/
F: include/linux/cpu_cooling.h
diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
index fe47d74b7219..c5ec7d2a38ca 100644
--- a/drivers/thermal/thermal_of.c
+++ b/drivers/thermal/thermal_of.c
@@ -60,18 +60,165 @@ static int thermal_of_get_trip_type(struct device_node *np,
return -ENODEV;
}
+/*
+ * thermal_of_match_hw_bin_group - test one thermal-hw-bin sub-group
+ * @np: device node containing the property
+ * @hw: hardware binning info (must be non-NULL)
+ * @levels: number of words per sub-group, equal to hw->supported_hw_bin_count
+ * @group: sub-group index to test
+ *
+ * Returns true if every word in sub-group @group has a non-zero bitwise
+ * AND with the corresponding hw->supported_hw_bin[] entry. The caller is
+ * responsible for ensuring @group < total-cells / @levels.
+ */
+static bool thermal_of_match_hw_bin_group(struct device_node *np,
+ const struct thermal_hw_bin_info *hw,
+ unsigned int levels, unsigned int group)
+{
+ unsigned int j;
+ u32 val;
+
+ for (j = 0; j < levels; j++) {
+ if (of_property_read_u32_index(np, "thermal-hw-bin",
+ group * levels + j, &val))
+ return false;
+ if (!(val & hw->supported_hw_bin[j]))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * thermal_of_trip_is_supported - check if a trip node is valid for this hardware
+ * @np: device node for the trip point
+ * @hw: hardware binning info (may be NULL)
+ *
+ * If @hw is NULL or the trip node has no thermal-hw-bin property, the trip is
+ * considered valid for all hardware. Otherwise, the property is read as groups
+ * of hw->supported_hw_bin_count 32-bit masks; the trip is valid if any group
+ * matches (bitwise AND non-zero for every word in the group).
+ *
+ * Return: true if the trip is valid for the running hardware, false otherwise.
+ */
+static bool thermal_of_trip_is_supported(struct device_node *np,
+ const struct thermal_hw_bin_info *hw)
+{
+ unsigned int levels, ngroups, i;
+ int count;
+
+ if (!hw)
+ return true;
+
+ levels = hw->supported_hw_bin_count;
+
+ count = of_property_count_u32_elems(np, "thermal-hw-bin");
+ if (count == -EINVAL)
+ return true;
+ if (count < 0) {
+ pr_warn_once("%pOF: error reading thermal-hw-bin: %d\n", np, count);
+ return false;
+ }
+ if (count == 0)
+ return true;
+
+ if (count % levels) {
+ pr_warn("%pOF: thermal-hw-bin length %d not a multiple of supported_hw_bin_count %u; disabling trip\n",
+ np, count, levels);
+ return false;
+ }
+
+ ngroups = count / levels;
+ for (i = 0; i < ngroups; i++)
+ if (thermal_of_match_hw_bin_group(np, hw, levels, i))
+ return true;
+
+ return false;
+}
+
+/**
+ * thermal_of_trip_read_temperature - read trip temperature, with bin support
+ * @np: device node for the trip point
+ * @hw: hardware binning info (may be NULL)
+ * @temp: output temperature in millicelsius
+ *
+ * Tries the scalar 'temperature' property first. If absent, falls back to
+ * 'temperature-bin', selecting the entry whose corresponding thermal-hw-bin
+ * sub-group matches the running hardware. Returns -ENODEV if temperature-bin
+ * is present but no sub-group matches (caller should skip this trip).
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int thermal_of_trip_read_temperature(struct device_node *np,
+ const struct thermal_hw_bin_info *hw,
+ s32 *temp)
+{
+ unsigned int levels, ngroups, i;
+ int count_hw, count_temp;
+ s32 t;
+
+ if (!of_property_read_s32(np, "temperature", &t)) {
+ *temp = t;
+ return 0;
+ }
+
+ count_temp = of_property_count_elems_of_size(np, "temperature-bin",
+ sizeof(u32));
+ if (count_temp < 0)
+ return -EINVAL;
+
+ if (!hw) {
+ u32 utmp;
+
+ pr_warn("%pOF: temperature-bin requires platform hw binning info; using first entry\n",
+ np);
+ if (of_property_read_u32_index(np, "temperature-bin", 0, &utmp))
+ return -EINVAL;
+ *temp = (s32)utmp;
+ return 0;
+ }
+
+ levels = hw->supported_hw_bin_count;
+ count_hw = of_property_count_u32_elems(np, "thermal-hw-bin");
+ if (count_hw <= 0 || count_hw % levels)
+ return -EINVAL;
+
+ ngroups = count_hw / levels;
+ if (count_temp != (int)ngroups)
+ return -EINVAL;
+
+ for (i = 0; i < ngroups; i++) {
+ if (thermal_of_match_hw_bin_group(np, hw, levels, i)) {
+ u32 utmp;
+
+ if (of_property_read_u32_index(np, "temperature-bin",
+ i, &utmp))
+ return -EINVAL;
+ *temp = (s32)utmp;
+ return 0;
+ }
+ }
+
+ pr_warn("%pOF: no thermal-hw-bin sub-group matched for temperature-bin\n", np);
+ return -ENODEV;
+}
+
static int thermal_of_populate_trip(struct device_node *np,
- struct thermal_trip *trip)
+ struct thermal_trip *trip,
+ const struct thermal_hw_bin_info *hw)
{
u32 hysteresis;
s32 temperature;
int ret;
- ret = of_property_read_s32(np, "temperature", &temperature);
- if (ret < 0) {
- pr_err("missing temperature property\n");
- return ret;
+ if (!thermal_of_trip_is_supported(np, hw)) {
+ pr_debug("%pOF: trip skipped: no thermal-hw-bin match\n", np);
+ return -ENODEV;
}
+
+ ret = thermal_of_trip_read_temperature(np, hw, &temperature);
+ if (ret)
+ return ret;
trip->temperature = temperature;
ret = of_property_read_u32(np, "hysteresis", &hysteresis);
@@ -94,7 +241,9 @@ static int thermal_of_populate_trip(struct device_node *np,
return 0;
}
-static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *ntrips)
+static struct thermal_trip *thermal_of_trips_init(struct device_node *np,
+ int *ntrips,
+ const struct thermal_hw_bin_info *hw)
{
int ret, count;
@@ -114,9 +263,12 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n
count = 0;
for_each_child_of_node_scoped(trips, trip) {
- ret = thermal_of_populate_trip(trip, &tt[count++]);
+ ret = thermal_of_populate_trip(trip, &tt[count], hw);
+ if (ret == -ENODEV)
+ continue;
if (ret)
return ERR_PTR(ret);
+ count++;
}
*ntrips = count;
@@ -364,27 +516,24 @@ static void thermal_of_zone_unregister(struct thermal_zone_device *tz)
}
/**
- * thermal_of_zone_register - Register a thermal zone with device node
- * sensor
- *
- * The thermal_of_zone_register() parses a device tree given a device
- * node sensor and identifier. It searches for the thermal zone
- * associated to the couple sensor/id and retrieves all the thermal
- * zone properties and registers new thermal zone with those
- * properties.
+ * thermal_of_zone_register_with_bin - Register a thermal zone with device node
+ * sensor and optional hardware binning info
*
- * @sensor: A device node pointer corresponding to the sensor in the device tree
- * @id: An integer as sensor identifier
- * @data: A private data to be stored in the thermal zone dedicated private area
- * @ops: A set of thermal sensor ops
+ * @sensor: A device node pointer corresponding to the sensor in the device tree
+ * @id: An integer as sensor identifier
+ * @data: A private data to be stored in the thermal zone dedicated private area
+ * @ops: A set of thermal sensor ops
+ * @hw_bin_info: Optional hardware binning info for trip filtering (may be NULL)
*
* Return: a valid thermal zone structure pointer on success.
* - EINVAL: if the device tree thermal description is malformed
* - ENOMEM: if one structure can not be allocated
* - Other negative errors are returned by the underlying called functions
*/
-static struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data,
- const struct thermal_zone_device_ops *ops)
+static struct thermal_zone_device *
+thermal_of_zone_register_with_bin(struct device_node *sensor, int id, void *data,
+ const struct thermal_zone_device_ops *ops,
+ const struct thermal_hw_bin_info *hw_bin_info)
{
struct thermal_zone_device_ops of_ops = *ops;
struct thermal_zone_device *tz;
@@ -403,7 +552,7 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
return ERR_CAST(np);
}
- trips = thermal_of_trips_init(np, &ntrips);
+ trips = thermal_of_trips_init(np, &ntrips, hw_bin_info);
if (IS_ERR(trips)) {
pr_err("Failed to parse trip points for %pOFP id=%d\n", sensor, id);
ret = PTR_ERR(trips);
@@ -461,6 +610,26 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
return ERR_PTR(ret);
}
+/**
+ * thermal_of_zone_register - Register a thermal zone with device node sensor
+ *
+ * @sensor: A device node pointer corresponding to the sensor in the device tree
+ * @id: An integer as sensor identifier
+ * @data: A private data to be stored in the thermal zone dedicated private area
+ * @ops: A set of thermal sensor ops
+ *
+ * Return: a valid thermal zone structure pointer on success.
+ * - EINVAL: if the device tree thermal description is malformed
+ * - ENOMEM: if one structure can not be allocated
+ * - Other negative errors are returned by the underlying called functions
+ */
+static struct thermal_zone_device *
+thermal_of_zone_register(struct device_node *sensor, int id, void *data,
+ const struct thermal_zone_device_ops *ops)
+{
+ return thermal_of_zone_register_with_bin(sensor, id, data, ops, NULL);
+}
+
static void devm_thermal_of_zone_release(struct device *dev, void *res)
{
thermal_of_zone_unregister(*(struct thermal_zone_device **)res);
@@ -510,6 +679,46 @@ struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, in
}
EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register);
+/**
+ * devm_thermal_of_zone_register_with_bin - register a thermal zone with hardware binning info
+ *
+ * Like devm_thermal_of_zone_register(), but passes @hw_bin_info to the trip-point
+ * parser so that trips can be filtered and per-bin temperatures selected at
+ * zone registration time. Use this when fuse values are read before zone
+ * registration (e.g. in a platform driver's probe function).
+ *
+ * @dev: device structure pointer to sensor
+ * @id: the sensor identifier
+ * @data: private data stored in the thermal zone 'devdata' field
+ * @ops: ops structure associated with the sensor
+ * @hw_bin_info: hardware binning info for trip filtering (may be NULL)
+ */
+struct thermal_zone_device *
+devm_thermal_of_zone_register_with_bin(struct device *dev, int id, void *data,
+ const struct thermal_zone_device_ops *ops,
+ const struct thermal_hw_bin_info *hw_bin_info)
+{
+ struct thermal_zone_device **ptr, *tzd;
+
+ ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ tzd = thermal_of_zone_register_with_bin(dev->of_node, id, data,
+ ops, hw_bin_info);
+ if (IS_ERR(tzd)) {
+ devres_free(ptr);
+ return tzd;
+ }
+
+ *ptr = tzd;
+ devres_add(dev, ptr);
+
+ return tzd;
+}
+EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register_with_bin);
+
/**
* devm_thermal_of_zone_unregister - Resource managed version of
* thermal_of_zone_unregister().
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 306ad17aed89..e9e4b2dc2824 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -86,6 +86,23 @@ struct thermal_trip {
#define THERMAL_TRIP_PRIV_TO_INT(_val_) (uintptr_t)(_val_)
#define THERMAL_INT_TO_TRIP_PRIV(_val_) (void *)(uintptr_t)(_val_)
+/* Matches the maxItems: 4 constraint on the thermal-hw-bin binding. */
+#define THERMAL_HW_BIN_MAX_LEVELS 4
+
+/**
+ * struct thermal_hw_bin_info - platform-specific hardware binning description
+ * @supported_hw_bin: array of bitfields identifying the running hardware version
+ * @supported_hw_bin_count: number of valid entries in @supported_hw_bin
+ *
+ * Filled by platform code that decodes SoC revision / fuse information.
+ * Shared with the thermal OF parser to filter trip points and select
+ * per-bin temperatures via the thermal-hw-bin / temperature-bin DT properties.
+ */
+struct thermal_hw_bin_info {
+ const u32 *supported_hw_bin;
+ unsigned int supported_hw_bin_count;
+};
+
struct cooling_spec {
unsigned long upper; /* Highest cooling state */
unsigned long lower; /* Lowest cooling state */
@@ -216,6 +233,12 @@ devm_thermal_of_child_cooling_device_register(struct device *dev,
struct device_node *np,
const char *type, void *devdata,
const struct thermal_cooling_device_ops *ops);
+
+struct thermal_zone_device *
+devm_thermal_of_zone_register_with_bin(struct device *dev, int id, void *data,
+ const struct thermal_zone_device_ops *ops,
+ const struct thermal_hw_bin_info *hw_bin_info);
+
#else
static inline
@@ -254,6 +277,15 @@ devm_thermal_of_child_cooling_device_register(struct device *dev,
{
return ERR_PTR(-ENODEV);
}
+
+static inline struct thermal_zone_device *
+devm_thermal_of_zone_register_with_bin(struct device *dev, int id, void *data,
+ const struct thermal_zone_device_ops *ops,
+ const struct thermal_hw_bin_info *hw_bin_info)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
#endif
int for_each_thermal_trip(struct thermal_zone_device *tz,
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 3/6] dt-bindings: thermal: qcom-tsens: document qcm6490 tsens
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 1/6] dt-bindings: thermal: thermal-zones: add hardware-binning trip properties Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering Gaurav Kohli
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
QCM6490 uses a tsens-v2 IP block and supports hardware-binned critical
trip points. Add its compatible to the tsens-v2 enum.
SoCs that support hardware-bin critical trips read their manufacturing
bin from qfprom fuse cells rather than the usual calibration cells: a
single "i-temp" bit on hamoa/purwa-class parts, and a "jtag-id"/
"feat-id" pair on sc7280/qcm6490. Extend nvmem-cell-names with two
oneOf branches so these fuse-cell names validate.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
index 5a8f7673e730..6899f07ec218 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
@@ -65,6 +65,7 @@ properties:
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,qcm2290-tsens
+ - qcom,qcm6490-tsens
- qcom,qcs8300-tsens
- qcom,qcs615-tsens
- qcom,sa8255p-tsens
@@ -244,6 +245,13 @@ properties:
- pattern: '^tsens_sens[0-9]+_off$'
- pattern: '^tsens_sens[0-9]+_off$'
- pattern: '^tsens_sens[0-9]+_off$'
+ # hardware-binning fuse cells: a single i-temp bit
+ - items:
+ - const: i-temp
+ # hardware-binning fuse cells: jtag-id and feature-id
+ - items:
+ - const: jtag-id
+ - const: feat-id
"#qcom,sensors":
description:
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
` (2 preceding siblings ...)
2026-10-31 13:22 ` [PATCH v2 3/6] dt-bindings: thermal: qcom-tsens: document qcm6490 tsens Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
2026-10-31 13:36 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 6/6] arm64: dts: qcom: hamoa: add thermal hw-bin support Gaurav Kohli
5 siblings, 1 reply; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Some Qualcomm SoCs are binned at manufacturing time and expose the bin
in qfprom fuses. The thermal OF parser can filter trip points by
hardware version; wire tsens up to feed it.
Add the per-SoC plumbing:
- struct tsens_hw_bin_desc names the nvmem cells to read and supplies a
compute_hw_version() callback that turns the raw fuse values into the
hw-version bitfields consumed by the thermal OF parser. A SoC opts in
by pointing tsens_plat_data.hw_bin_desc at one of these descriptors;
leaving it NULL disables the feature for that SoC.
- tsens_populate_hw_bin_info() reads the named cells at probe time,
invokes the callback, and stashes the result in priv->hw_bin_info.
When hw_bin_desc is NULL it is a no-op, so SoCs that do not bin are
unaffected.
- tsens_register() now uses devm_thermal_of_zone_register_with_bin(),
passing &priv->hw_bin_info only when a bin was actually read.
Two descriptors and their SoC wiring are provided:
- tsens_hw_bin_desc_i_temp reads a single i-temp bit and reports it
directly as the hw-bin value; used by data_x1e80100.
- tsens_hw_bin_desc_qcm6490 reads the jtag-id and feat-id fuses and maps
the known qcm6490/sc7280 ID pairs to a synthesized hw-bin value (0x1
for the standard profile, 0x2 for the elevated profile); used by
data_sc7280, which serves both the sc7280 and qcm6490 compatibles.
Register the qcm6490, sc7280 and x1e80100 compatibles in tsens_table.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Co-developed-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
drivers/thermal/qcom/tsens-v2.c | 16 ++++
drivers/thermal/qcom/tsens.c | 190 +++++++++++++++++++++++++++++++++++++++-
drivers/thermal/qcom/tsens.h | 29 ++++++
3 files changed, 232 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index 2ee117aa91ba..8b2a00956a36 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -271,6 +271,22 @@ struct tsens_plat_data data_tsens_v2 = {
.fields = tsens_v2_regfields,
};
+/* x1e80100: tsens-v2 with i-temp hardware-bin trip filtering */
+const struct tsens_plat_data data_x1e80100 = {
+ .ops = &ops_generic_v2,
+ .feat = &tsens_v2_feat,
+ .fields = tsens_v2_regfields,
+ .hw_bin_desc = &tsens_hw_bin_desc_i_temp,
+};
+
+/* sc7280/qcm6490: tsens-v2 with jtag-id/feat-id hardware-bin trip filtering */
+const struct tsens_plat_data data_sc7280 = {
+ .ops = &ops_generic_v2,
+ .feat = &tsens_v2_feat,
+ .fields = tsens_v2_regfields,
+ .hw_bin_desc = &tsens_hw_bin_desc_qcm6490,
+};
+
struct tsens_plat_data data_ipq8074 = {
.ops = &ops_generic_v2,
.feat = &ipq8074_feat,
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index b5ec70201e2f..9ba6b3c4c133 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -71,6 +71,167 @@ char *qfprom_read(struct device *dev, const char *cname)
return ret;
}
+/*
+ * Hardware-bin fuse support
+ *
+ * Each SoC that needs trip-point filtering based on fuse values provides a
+ * struct tsens_hw_bin_desc in its tsens_plat_data. The descriptor names the
+ * nvmem cells to read and supplies a compute_hw_version() callback that
+ * converts the raw fuse values into the hw-version bitfields consumed by the
+ * thermal OF parser.
+ */
+
+static int tsens_compute_hw_version_qcm6490(struct device *dev,
+ const u32 *fuse, unsigned int nfuse,
+ u32 *hw, unsigned int *count)
+{
+ u32 tsens_jtag;
+ u8 tsens_feat_id;
+ bool elevate_trip;
+
+ /* Hardware-bin values for thermal-hw-bin matching in DT. */
+ const u32 hw_bin_std = 0x1;
+ const u32 hw_bin_elevated = 0x2;
+
+ /* Higher thermal profile identification for qcm6490/sc7280 bins. */
+ const u32 tsens_chip_id0 = 0x197;
+ const u32 tsens_chip_id1 = 0x198;
+ const u32 tsens_chip_id2 = 0x20e;
+ const u32 tsens_chip_id3 = 0x20f;
+ const u8 tsens_feat_id2 = 0x2;
+ const u8 tsens_feat_id3 = 0x3;
+ const u8 tsens_feat_id4 = 0x4;
+
+ if (nfuse < 2)
+ return -EINVAL;
+
+ tsens_jtag = fuse[0] & GENMASK(19, 0);
+ tsens_feat_id = fuse[1] & GENMASK(7, 0);
+
+ elevate_trip =
+ (tsens_jtag == tsens_chip_id0 && tsens_feat_id == tsens_feat_id3) ||
+ (tsens_jtag == tsens_chip_id1 && tsens_feat_id == tsens_feat_id4) ||
+ (tsens_jtag == tsens_chip_id2 && tsens_feat_id == tsens_feat_id3) ||
+ (tsens_jtag == tsens_chip_id3 && tsens_feat_id == tsens_feat_id2);
+
+ hw[0] = elevate_trip ? hw_bin_elevated : hw_bin_std;
+ *count = 1;
+
+ dev_dbg(dev,
+ "qcm6490 hw-bin from jtag-id=0x%x feat-id=0x%x => 0x%x\n",
+ tsens_jtag, tsens_feat_id, hw[0]);
+
+ return 0;
+}
+
+static int tsens_compute_hw_version_i_temp(struct device *dev,
+ const u32 *fuse, unsigned int nfuse,
+ u32 *hw, unsigned int *count)
+{
+ if (nfuse < 1)
+ return -EINVAL;
+
+ /* Single bit: set bit 1 for high-temp bin, bit 0 for standard bin */
+ hw[0] = (fuse[0] & BIT(0)) ? 0x2u : 0x1u;
+ *count = 1;
+
+ dev_dbg(dev, "i-temp hw-bin from i-temp=0x%x => 0x%x\n",
+ fuse[0], hw[0]);
+
+ return 0;
+}
+
+static const char * const tsens_cells_qcm6490[] = { "jtag-id", "feat-id" };
+static const char * const tsens_cells_i_temp[] = { "i-temp" };
+
+const struct tsens_hw_bin_desc tsens_hw_bin_desc_qcm6490 = {
+ .cell_names = tsens_cells_qcm6490,
+ .ncells = ARRAY_SIZE(tsens_cells_qcm6490),
+ .compute_hw_version = tsens_compute_hw_version_qcm6490,
+};
+
+const struct tsens_hw_bin_desc tsens_hw_bin_desc_i_temp = {
+ .cell_names = tsens_cells_i_temp,
+ .ncells = ARRAY_SIZE(tsens_cells_i_temp),
+ .compute_hw_version = tsens_compute_hw_version_i_temp,
+};
+
+/**
+ * tsens_populate_hw_bin_info - read fuse cells and populate priv->hw_bin_info
+ * @priv: tsens private data
+ * @data: platform data containing the optional hw_bin_desc pointer
+ *
+ * If data->hw_bin_desc is NULL, this is a no-op and returns 0. Otherwise,
+ * reads the nvmem cells named in hw_bin_desc->cell_names, calls the
+ * compute_hw_version() callback, and stores the result in priv->hw_bin_info
+ * for later use by tsens_register().
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int tsens_populate_hw_bin_info(struct tsens_priv *priv,
+ const struct tsens_plat_data *data)
+{
+ const struct tsens_hw_bin_desc *desc;
+ u32 fuse_vals[TSENS_HW_BIN_MAX_CELLS];
+ u32 hw_versions[THERMAL_HW_BIN_MAX_LEVELS] = { 0 };
+ unsigned int hw_count = 0;
+ int i, ret;
+
+ if (!data->hw_bin_desc)
+ return 0;
+
+ desc = data->hw_bin_desc;
+
+ if (!desc->ncells || desc->ncells > TSENS_HW_BIN_MAX_CELLS) {
+ dev_err(priv->dev, "hw_bin_desc ncells %u out of range\n",
+ desc->ncells);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < (int)desc->ncells; i++) {
+ ret = nvmem_cell_read_variable_le_u32(priv->dev,
+ desc->cell_names[i],
+ &fuse_vals[i]);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "failed to read nvmem cell '%s': %d\n",
+ desc->cell_names[i], ret);
+ return ret;
+ }
+ }
+
+ ret = desc->compute_hw_version(priv->dev, fuse_vals, desc->ncells,
+ hw_versions, &hw_count);
+ if (ret) {
+ dev_err(priv->dev, "compute_hw_version failed: %d\n", ret);
+ return ret;
+ }
+
+ if (!hw_count || hw_count > THERMAL_HW_BIN_MAX_LEVELS) {
+ dev_err(priv->dev,
+ "compute_hw_version returned invalid hw_count %u\n",
+ hw_count);
+ return -EINVAL;
+ }
+
+ /*
+ * Store a copy of the hw-version array in priv->hw_bin_info so
+ * tsens_register() can pass it to the thermal OF parser while trips are
+ * parsed.
+ *
+ * Use devm_kmemdup so the copy is freed when the device is unbound.
+ */
+ priv->hw_bin_info.supported_hw_bin = devm_kmemdup(priv->dev, hw_versions,
+ hw_count * sizeof(u32),
+ GFP_KERNEL);
+ if (!priv->hw_bin_info.supported_hw_bin)
+ return -ENOMEM;
+
+ priv->hw_bin_info.supported_hw_bin_count = hw_count;
+
+ return 0;
+}
+
int tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup)
{
u32 mode;
@@ -1206,6 +1367,12 @@ static const struct of_device_id tsens_table[] = {
}, {
.compatible = "qcom,msm8996-tsens",
.data = &data_8996,
+ }, {
+ .compatible = "qcom,qcm6490-tsens",
+ .data = &data_sc7280,
+ }, {
+ .compatible = "qcom,sc7280-tsens",
+ .data = &data_sc7280,
}, {
.compatible = "qcom,tsens-v1",
.data = &data_tsens_v1,
@@ -1218,6 +1385,9 @@ static const struct of_device_id tsens_table[] = {
}, {
.compatible = "qcom,sa8255p-tsens",
.data = &data_automotive_v2,
+ }, {
+ .compatible = "qcom,x1e80100-tsens",
+ .data = &data_x1e80100,
},
{}
};
@@ -1327,12 +1497,18 @@ static int tsens_register(struct tsens_priv *priv)
{
int i, ret;
struct thermal_zone_device *tzd;
+ const struct thermal_hw_bin_info *bin_info;
+
+ bin_info = priv->hw_bin_info.supported_hw_bin_count ?
+ &priv->hw_bin_info : NULL;
for (i = 0; i < priv->num_sensors; i++) {
priv->sensor[i].priv = priv;
- tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id,
- &priv->sensor[i],
- &tsens_of_ops);
+ tzd = devm_thermal_of_zone_register_with_bin(priv->dev,
+ priv->sensor[i].hw_id,
+ &priv->sensor[i],
+ &tsens_of_ops,
+ bin_info);
if (IS_ERR(tzd))
continue;
priv->sensor[i].tzd = tzd;
@@ -1425,6 +1601,14 @@ static int tsens_probe(struct platform_device *pdev)
priv->feat = data->feat;
priv->fields = data->fields;
+ ret = tsens_populate_hw_bin_info(priv, data);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ if (ret)
+ dev_warn(&pdev->dev,
+ "thermal-bin failed to read FUSE data (%d): trips with temperature-bin will use first entry\n",
+ ret);
+
platform_set_drvdata(pdev, priv);
device_init_wakeup(dev, !data->no_irq_wake);
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index e8376accdff3..1ebdc6935b79 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -40,6 +40,29 @@ enum tsens_ver {
VER_2_X_NO_RPM,
};
+/**
+ * struct tsens_hw_bin_desc - per-SoC descriptor for hardware-bin fuse reading
+ * @cell_names: NULL-terminated array of nvmem cell names to read
+ * @ncells: number of entries in @cell_names
+ * @compute_hw_version: callback that converts raw fuse values to hw-version
+ * bitfields for thermal OF trip filtering
+ *
+ * Embed a pointer to this struct in tsens_plat_data.hw_bin_desc to enable
+ * hardware-bin trip filtering for a given SoC. Set to NULL to disable.
+ */
+struct tsens_hw_bin_desc {
+ const char * const *cell_names;
+ unsigned int ncells;
+ int (*compute_hw_version)(struct device *dev,
+ const u32 *fuse, unsigned int nfuse,
+ u32 *hw, unsigned int *count);
+};
+
+#define TSENS_HW_BIN_MAX_CELLS 8
+
+extern const struct tsens_hw_bin_desc tsens_hw_bin_desc_qcm6490;
+extern const struct tsens_hw_bin_desc tsens_hw_bin_desc_i_temp;
+
enum tsens_irq_type {
LOWER,
UPPER,
@@ -533,6 +556,7 @@ struct tsens_features {
* @feat: features of the IP
* @fields: bitfield locations
* @no_irq_wake: if set, TSENS interrupts will not be configured as wakeup sources
+ * @hw_bin_desc: optional hardware-bin fuse descriptor for trip filtering
*/
struct tsens_plat_data {
const u32 num_sensors;
@@ -541,6 +565,7 @@ struct tsens_plat_data {
struct tsens_features *feat;
const struct reg_field *fields;
bool no_irq_wake;
+ const struct tsens_hw_bin_desc *hw_bin_desc;
};
/**
@@ -568,6 +593,7 @@ struct tsens_context {
* @feat: features of the IP
* @fields: bitfield locations
* @ops: pointer to list of callbacks supported by this device
+ * @hw_bin_info: hardware binning info read from SoC fuse (populated at probe)
* @debug_root: pointer to debugfs dentry for all tsens
* @debug: pointer to debugfs dentry for tsens controller
* @uplow_irq: IRQ number for uplow (upper/lower) threshold interrupts
@@ -591,6 +617,8 @@ struct tsens_priv {
const struct reg_field *fields;
const struct tsens_ops *ops;
+ struct thermal_hw_bin_info hw_bin_info;
+
struct dentry *debug_root;
struct dentry *debug;
@@ -677,6 +705,7 @@ extern const struct tsens_plat_data data_ipq5018;
/* TSENS v2 targets */
extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
extern const struct tsens_plat_data data_ipq5332, data_ipq5424;
+extern const struct tsens_plat_data data_x1e80100, data_sc7280;
/* TSENS automotive targets */
extern struct tsens_plat_data data_automotive_v2;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
` (3 preceding siblings ...)
2026-10-31 13:22 ` [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
2026-10-31 13:35 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 6/6] arm64: dts: qcom: hamoa: add thermal hw-bin support Gaurav Kohli
5 siblings, 1 reply; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
kodiak-based SoCs are binned at manufacturing time, with the bin
computed from the jtag-id and feat-id qfprom fuses. The tsens driver
now reads these and feeds the resulting bin to the thermal core, which
selects per-bin trip temperatures.
Add the jtag-id and feat-id nvmem cells to the tsens nodes and convert
the passive mitigation trip points from a fixed 95C to per-bin values
(95C for bin 0x1, 105C for bin 0x2) using the new
temperature-bin/thermal-hw-bin properties.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Co-developed-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/kodiak.dtsi | 58 +++++++++++++++++++++++++++---------
1 file changed, 44 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
index 347815e5983e..b984cf47b70e 100644
--- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
+++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
@@ -1019,6 +1019,18 @@ gpu_speed_bin: gpu-speed-bin@1e9 {
reg = <0x1e9 0x2>;
bits = <5 8>;
};
+
+ jtag_id: jtag-id@180 {
+ reg = <0x180 0x4>;
+ /* bits = <offset width>: 20 bits at offset 0 */
+ bits = <0 20>;
+ };
+
+ feat_id: feat-id@180 {
+ reg = <0x180 0x4>;
+ /* bits = <offset width>: 8 bits at offset 20 */
+ bits = <20 8>;
+ };
};
sdhc_1: mmc@7c4000 {
@@ -5782,6 +5794,8 @@ tsens0: thermal-sensor@c263000 {
compatible = "qcom,sc7280-tsens","qcom,tsens-v2";
reg = <0 0x0c263000 0 0x1ff>, /* TM */
<0 0x0c222000 0 0x1ff>; /* SROT */
+ nvmem-cells = <&jtag_id>, <&feat_id>;
+ nvmem-cell-names = "jtag-id", "feat-id";
#qcom,sensors = <15>;
interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>;
@@ -5793,6 +5807,8 @@ tsens1: thermal-sensor@c265000 {
compatible = "qcom,sc7280-tsens","qcom,tsens-v2";
reg = <0 0x0c265000 0 0x1ff>, /* TM */
<0 0x0c223000 0 0x1ff>; /* SROT */
+ nvmem-cells = <&jtag_id>, <&feat_id>;
+ nvmem-cell-names = "jtag-id", "feat-id";
#qcom,sensors = <12>;
interrupts = <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>;
@@ -7136,7 +7152,8 @@ cpu0_alert0: trip-point0 {
};
cpu0_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7179,7 +7196,8 @@ cpu1_alert0: trip-point0 {
};
cpu1_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7222,7 +7240,8 @@ cpu2_alert0: trip-point0 {
};
cpu2_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7265,7 +7284,8 @@ cpu3_alert0: trip-point0 {
};
cpu3_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7308,7 +7328,8 @@ cpu4_alert0: trip-point0 {
};
cpu4_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7351,7 +7372,8 @@ cpu5_alert0: trip-point0 {
};
cpu5_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7394,7 +7416,8 @@ cpu6_alert0: trip-point0 {
};
cpu6_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7437,7 +7460,8 @@ cpu7_alert0: trip-point0 {
};
cpu7_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7480,7 +7504,8 @@ cpu8_alert0: trip-point0 {
};
cpu8_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7523,7 +7548,8 @@ cpu9_alert0: trip-point0 {
};
cpu9_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7566,7 +7592,8 @@ cpu10_alert0: trip-point0 {
};
cpu10_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7609,7 +7636,8 @@ cpu11_alert0: trip-point0 {
};
cpu11_alert1: trip-point1 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7724,7 +7752,8 @@ gpuss0-thermal {
trips {
gpuss0_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
@@ -7751,7 +7780,8 @@ gpuss1-thermal {
trips {
gpuss1_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <2000>;
type = "passive";
};
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 6/6] arm64: dts: qcom: hamoa: add thermal hw-bin support
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
` (4 preceding siblings ...)
2026-10-31 13:22 ` [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips Gaurav Kohli
@ 2026-10-31 13:22 ` Gaurav Kohli
5 siblings, 0 replies; 10+ messages in thread
From: Gaurav Kohli @ 2026-10-31 13:22 UTC (permalink / raw)
To: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Amit Kucheria, Thara Gopinath,
Bjorn Andersson, Konrad Dybcio, Abel Vesa
Cc: linux-pm, devicetree, linux-kernel, linux-doc, linux-arm-msm,
Gaurav Kohli, Amit Kucheria
From: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
hamoa-based SoCs expose their manufacturing bin in a single i-temp bit
of a qfprom fuse. Add the i-temp nvmem cell to the qfprom node
and wire the tsens controllers to it so the tsens driver can feed
the bin to the thermal core.
Use that bin for the GPU passive trip points so industrial-temp
parts keep the higher 105C threshold without a board-specific
override.
Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
Co-developed-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts | 66 ------------------------------
arch/arm64/boot/dts/qcom/hamoa.dtsi | 42 +++++++++++++++----
2 files changed, 34 insertions(+), 74 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts
index 776bded3a312..03d753a0ab74 100644
--- a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts
+++ b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts
@@ -1553,69 +1553,3 @@ &usb_mp_hsphy0 {
&usb_mp_hsphy1 {
phys = <&eusb6_repeater>;
};
-
-&thermal_zones {
- gpuss-0-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-1-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-2-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-3-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-4-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-5-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-6-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-
- gpuss-7-thermal {
- trips {
- trip-point0 {
- temperature = <105000>;
- };
- };
- };
-};
diff --git a/arch/arm64/boot/dts/qcom/hamoa.dtsi b/arch/arm64/boot/dts/qcom/hamoa.dtsi
index fcd88e5f5d7e..6cf911e30973 100644
--- a/arch/arm64/boot/dts/qcom/hamoa.dtsi
+++ b/arch/arm64/boot/dts/qcom/hamoa.dtsi
@@ -2766,6 +2766,9 @@ tsens0: thermal-sensor@c271000 {
interrupt-names = "uplow",
"critical";
+ nvmem-cells = <&i_temp>;
+ nvmem-cell-names = "i-temp";
+
#qcom,sensors = <16>;
#thermal-sensor-cells = <1>;
@@ -2781,6 +2784,9 @@ tsens1: thermal-sensor@c272000 {
interrupt-names = "uplow",
"critical";
+ nvmem-cells = <&i_temp>;
+ nvmem-cell-names = "i-temp";
+
#qcom,sensors = <16>;
#thermal-sensor-cells = <1>;
@@ -2796,6 +2802,9 @@ tsens2: thermal-sensor@c273000 {
interrupt-names = "uplow",
"critical";
+ nvmem-cells = <&i_temp>;
+ nvmem-cell-names = "i-temp";
+
#qcom,sensors = <16>;
#thermal-sensor-cells = <1>;
@@ -2811,6 +2820,9 @@ tsens3: thermal-sensor@c274000 {
interrupt-names = "uplow",
"critical";
+ nvmem-cells = <&i_temp>;
+ nvmem-cell-names = "i-temp";
+
#qcom,sensors = <16>;
#thermal-sensor-cells = <1>;
@@ -8809,6 +8821,12 @@ gpu_speed_bin: gpu-speed-bin@119 {
reg = <0x119 0x2>;
bits = <7 8>;
};
+
+ i_temp: i-temp@180 {
+ reg = <0x180 0x4>;
+ /* bits = <offset width>: 1 bit at offset 0 */
+ bits = <0 1>;
+ };
};
pmu@24091000 {
@@ -9669,7 +9687,8 @@ map0 {
trips {
gpuss0_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9696,7 +9715,8 @@ map0 {
trips {
gpuss1_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9723,7 +9743,8 @@ map0 {
trips {
gpuss2_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9750,7 +9771,8 @@ map0 {
trips {
gpuss3_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9777,7 +9799,8 @@ map0 {
trips {
gpuss4_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9804,7 +9827,8 @@ map0 {
trips {
gpuss5_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9831,7 +9855,8 @@ map0 {
trips {
gpuss6_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
@@ -9858,7 +9883,8 @@ map0 {
trips {
gpuss7_alert0: trip-point0 {
- temperature = <95000>;
+ temperature-bin = <95000>, <105000>;
+ thermal-hw-bin = <0x1>, <0x2>;
hysteresis = <1000>;
type = "passive";
};
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips
2026-10-31 13:22 ` [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips Gaurav Kohli
@ 2026-10-31 13:35 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-31 13:35 UTC (permalink / raw)
To: Gaurav Kohli; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] arm64: dts: qcom: kodiak: duplicate @180 unit-address in qfprom
--
commit 04541134a8b551c93d77860ba606f4cd37e5c12a
Author: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
arm64: dts: qcom: kodiak: use thermal hw-bin trips
This commit adds jtag-id and feat-id qfprom nvmem cells to the tsens nodes
in kodiak.dtsi and updates passive thermal trip points to use per-bin
temperatures.
> diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> index 347815e5983e4..b984cf47b70eb 100644
> --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
> +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
> @@ -1019,6 +1019,18 @@ gpu_speed_bin: gpu-speed-bin@1e9 {
> reg = <0x1e9 0x2>;
> bits = <5 8>;
> };
> +
> + jtag_id: jtag-id@180 {
> + reg = <0x180 0x4>;
> + /* bits = <offset width>: 20 bits at offset 0 */
> + bits = <0 20>;
> + };
> +
> + feat_id: feat-id@180 {
> + reg = <0x180 0x4>;
> + /* bits = <offset width>: 8 bits at offset 20 */
> + bits = <20 8>;
> + };
> };
[Severity: Low]
Since both jtag-id@180 and feat-id@180 are enabled sibling subnodes under
qfprom (efuse@784000) with the same @180 unit-address, won't building DTBs
with W=1 or W=2 trigger a duplicate unit-address warning from
check_unique_unit_address_common() in scripts/dtc/checks.c?
In Qualcomm QFPROM nodes (such as gpu-speed-bin@1e9 just above, which
qfprom_fixup_dt_cell_info() in drivers/nvmem/qfprom.c aligns down to a
4-byte word boundary at runtime), sub-word nvmem cells are normally
addressed by their starting byte offset with a bit offset in 0..7.
Should jtag-id@180 use reg = <0x180 0x3> with bits = <0 20>, and feat-id
be defined as feat-id@182 with reg = <0x182 0x2> and bits = <4 8>, placed
before gpu-speed-bin@1e9 in ascending unit-address order?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031-thermal_bin-v2-0-d76fd073ae98@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering
2026-10-31 13:22 ` [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering Gaurav Kohli
@ 2026-10-31 13:36 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-31 13:36 UTC (permalink / raw)
To: Gaurav Kohli; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] thermal: qcom: tsens: broken error handling in tsens_populate_hw_bin_info()
- [Low] thermal: qcom: tsens: inaccurate cell_names kernel-doc in tsens_hw_bin_desc
- [Low] thermal: qcom: tsens: inaccurate i-temp description in commit message
--
commit 747c218e81cea1873e3ba13a84fe505aad9fc9f2
Author: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
thermal: qcom: tsens: add hardware-bin trip-point filtering
This commit adds per-SoC qfprom fuse support to read hardware binning
information at probe time and pass it to the thermal OF parser for
trip-point filtering on x1e80100, sc7280, and qcm6490.
> thermal: qcom: tsens: add hardware-bin trip-point filtering
>
> Some Qualcomm SoCs are binned at manufacturing time and expose the bin
> in qfprom fuses. The thermal OF parser can filter trip points by
> hardware version; wire tsens up to feed it.
>
> Add the per-SoC plumbing:
>
> - struct tsens_hw_bin_desc names the nvmem cells to read and supplies a
> compute_hw_version() callback that turns the raw fuse values into the
> hw-version bitfields consumed by the thermal OF parser. A SoC opts in
> by pointing tsens_plat_data.hw_bin_desc at one of these descriptors;
> leaving it NULL disables the feature for that SoC.
>
> - tsens_populate_hw_bin_info() reads the named cells at probe time,
> invokes the callback, and stashes the result in priv->hw_bin_info.
> When hw_bin_desc is NULL it is a no-op, so SoCs that do not bin are
> unaffected.
>
> - tsens_register() now uses devm_thermal_of_zone_register_with_bin(),
> passing &priv->hw_bin_info only when a bin was actually read.
>
> Two descriptors and their SoC wiring are provided:
>
> - tsens_hw_bin_desc_i_temp reads a single i-temp bit and reports it
> directly as the hw-bin value; used by data_x1e80100.
>
> - tsens_hw_bin_desc_qcm6490 reads the jtag-id and feat-id fuses and maps
> the known qcm6490/sc7280 ID pairs to a synthesized hw-bin value (0x1
> for the standard profile, 0x2 for the elevated profile); used by
> data_sc7280, which serves both the sc7280 and qcm6490 compatibles.
>
> Register the qcm6490, sc7280 and x1e80100 compatibles in tsens_table.
>
> Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
> Co-developed-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
> Signed-off-by: Gaurav Kohli <gaurav.kohli@oss.qualcomm.com>
[Severity: Low]
The commit message states that tsens_hw_bin_desc_i_temp reads a single
i-temp bit and reports it directly as the hw-bin value.
In tsens_compute_hw_version_i_temp(), the single bit (fuse[0] & BIT(0)) is
actually mapped to bitmask values 0x1u for the standard bin or 0x2u for the
high-temp bin:
/* Single bit: set bit 1 for high-temp bin, bit 0 for standard bin */
hw[0] = (fuse[0] & BIT(0)) ? 0x2u : 0x1u;
Should the commit message be updated to match what
tsens_compute_hw_version_i_temp() does?
> diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
> index b5ec70201e2f3..9ba6b3c4c133a 100644
> --- a/drivers/thermal/qcom/tsens.c
> +++ b/drivers/thermal/qcom/tsens.c
[ ... ]
> +static int tsens_populate_hw_bin_info(struct tsens_priv *priv,
> + const struct tsens_plat_data *data)
> +{
> + const struct tsens_hw_bin_desc *desc;
> + u32 fuse_vals[TSENS_HW_BIN_MAX_CELLS];
> + u32 hw_versions[THERMAL_HW_BIN_MAX_LEVELS] = { 0 };
> + unsigned int hw_count = 0;
> + int i, ret;
> +
> + if (!data->hw_bin_desc)
> + return 0;
> +
> + desc = data->hw_bin_desc;
> +
> + if (!desc->ncells || desc->ncells > TSENS_HW_BIN_MAX_CELLS) {
> + dev_err(priv->dev, "hw_bin_desc ncells %u out of range\n",
> + desc->ncells);
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < (int)desc->ncells; i++) {
> + ret = nvmem_cell_read_variable_le_u32(priv->dev,
> + desc->cell_names[i],
> + &fuse_vals[i]);
> + if (ret < 0) {
> + dev_err(priv->dev,
> + "failed to read nvmem cell '%s': %d\n",
> + desc->cell_names[i], ret);
> + return ret;
> + }
> + }
> +
> + ret = desc->compute_hw_version(priv->dev, fuse_vals, desc->ncells,
> + hw_versions, &hw_count);
> + if (ret) {
> + dev_err(priv->dev, "compute_hw_version failed: %d\n", ret);
> + return ret;
> + }
> +
> + if (!hw_count || hw_count > THERMAL_HW_BIN_MAX_LEVELS) {
> + dev_err(priv->dev,
> + "compute_hw_version returned invalid hw_count %u\n",
> + hw_count);
> + return -EINVAL;
> + }
> +
> + /*
> + * Store a copy of the hw-version array in priv->hw_bin_info so
> + * tsens_register() can pass it to the thermal OF parser while trips are
> + * parsed.
> + *
> + * Use devm_kmemdup so the copy is freed when the device is unbound.
> + */
> + priv->hw_bin_info.supported_hw_bin = devm_kmemdup(priv->dev, hw_versions,
> + hw_count * sizeof(u32),
> + GFP_KERNEL);
> + if (!priv->hw_bin_info.supported_hw_bin)
> + return -ENOMEM;
> +
> + priv->hw_bin_info.supported_hw_bin_count = hw_count;
> +
> + return 0;
> +}
[ ... ]
> @@ -1425,6 +1601,14 @@ static int tsens_probe(struct platform_device *pdev)
> priv->feat = data->feat;
> priv->fields = data->fields;
>
> + ret = tsens_populate_hw_bin_info(priv, data);
> + if (ret == -EPROBE_DEFER)
> + return ret;
> + if (ret)
> + dev_warn(&pdev->dev,
> + "thermal-bin failed to read FUSE data (%d): trips with temperature-bin will use first entry\n",
> + ret);
[Severity: Medium]
How should errors from tsens_populate_hw_bin_info() be handled in
tsens_probe()?
In tsens_populate_hw_bin_info(), when nvmem_cell_read_variable_le_u32()
returns a negative error code, dev_err() is unconditionally called before
returning ret.
When the qfprom nvmem provider has not probed yet,
nvmem_cell_read_variable_le_u32() returns -EPROBE_DEFER. Although
tsens_probe() checks for -EPROBE_DEFER and returns early,
tsens_populate_hw_bin_info() has already logged a dev_err() message on every
deferred probe attempt.
When booting with an older or existing DTB for qcom,x1e80100-tsens or
qcom,sc7280-tsens that omits the optional nvmem-cells property,
nvmem_cell_read_variable_le_u32() returns -ENOENT. That causes
tsens_populate_hw_bin_info() to emit a dev_err() and tsens_probe() to emit a
redundant dev_warn() (which also passes &pdev->dev instead of the resolved
dev pointer).
Conversely, if devm_kmemdup() in tsens_populate_hw_bin_info() fails and
returns -ENOMEM, tsens_probe() only aborts on -EPROBE_DEFER and downgrades
-ENOMEM to the fuse warning instead of failing the probe.
> diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
> index e8376accdff3d..1ebdc6935b794 100644
> --- a/drivers/thermal/qcom/tsens.h
> +++ b/drivers/thermal/qcom/tsens.h
> @@ -40,6 +40,29 @@ enum tsens_ver {
> VER_2_X_NO_RPM,
> };
>
> +/**
> + * struct tsens_hw_bin_desc - per-SoC descriptor for hardware-bin fuse reading
> + * @cell_names: NULL-terminated array of nvmem cell names to read
> + * @ncells: number of entries in @cell_names
[Severity: Low]
Is the kernel-doc for @cell_names in struct tsens_hw_bin_desc accurate in
saying it is a NULL-terminated array?
In drivers/thermal/qcom/tsens.c, tsens_cells_qcm6490 and tsens_cells_i_temp
are defined without a NULL terminator and initialize .ncells using
ARRAY_SIZE():
static const char * const tsens_cells_qcm6490[] = { "jtag-id", "feat-id" };
static const char * const tsens_cells_i_temp[] = { "i-temp" };
const struct tsens_hw_bin_desc tsens_hw_bin_desc_qcm6490 = {
.cell_names = tsens_cells_qcm6490,
.ncells = ARRAY_SIZE(tsens_cells_qcm6490),
.compute_hw_version = tsens_compute_hw_version_qcm6490,
};
Since tsens_populate_hw_bin_info() iterates from 0 to desc->ncells - 1 and
passes desc->cell_names[i] to nvmem_cell_read_variable_le_u32(), adding a
NULL terminator alongside ARRAY_SIZE() would pass a NULL cell_id to
nvmem_cell_read_variable_le_u32(). Should the NULL-terminated wording be
dropped from the comment?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031-thermal_bin-v2-0-d76fd073ae98@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support
2026-10-31 13:22 ` [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support Gaurav Kohli
@ 2026-10-31 13:40 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-31 13:40 UTC (permalink / raw)
To: Gaurav Kohli; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] thermal: -ENODEV collision in thermal_of_trips_init() masks bad trip types
- [Low] thermal: redundant hw-bin matching and dead code in trip temperature parser
- [Low] thermal: duplicated devres logic in devm_thermal_of_zone_register_with_bin()
--
commit 88b6a2a4f7fe7c6179c38cf14f96ebaa3d58c33a
Author: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
thermal: add hardware-binning trip-point filtering support
This commit adds Device Tree parsing support for the thermal-hw-bin and
temperature-bin properties to filter trip points and select per-bin trip
temperatures based on SoC hardware version information. It also introduces
devm_thermal_of_zone_register_with_bin() so platform sensor drivers can pass
decoded fuse bitmasks during thermal zone registration.
> diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
> index fe47d74b7219e..c5ec7d2a38caf 100644
> --- a/drivers/thermal/thermal_of.c
> +++ b/drivers/thermal/thermal_of.c
> @@ -60,18 +60,165 @@ static int thermal_of_get_trip_type(struct device_node *np,
> return -ENODEV;
> }
>
> +/*
> + * thermal_of_match_hw_bin_group - test one thermal-hw-bin sub-group
> + * @np: device node containing the property
> + * @hw: hardware binning info (must be non-NULL)
> + * @levels: number of words per sub-group, equal to hw->supported_hw_bin_count
> + * @group: sub-group index to test
> + *
> + * Returns true if every word in sub-group @group has a non-zero bitwise
> + * AND with the corresponding hw->supported_hw_bin[] entry. The caller is
> + * responsible for ensuring @group < total-cells / @levels.
> + */
> +static bool thermal_of_match_hw_bin_group(struct device_node *np,
> + const struct thermal_hw_bin_info *hw,
> + unsigned int levels, unsigned int group)
> +{
> + unsigned int j;
> + u32 val;
> +
> + for (j = 0; j < levels; j++) {
> + if (of_property_read_u32_index(np, "thermal-hw-bin",
> + group * levels + j, &val))
> + return false;
> + if (!(val & hw->supported_hw_bin[j]))
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/**
> + * thermal_of_trip_is_supported - check if a trip node is valid for this hardware
> + * @np: device node for the trip point
> + * @hw: hardware binning info (may be NULL)
> + *
> + * If @hw is NULL or the trip node has no thermal-hw-bin property, the trip is
> + * considered valid for all hardware. Otherwise, the property is read as groups
> + * of hw->supported_hw_bin_count 32-bit masks; the trip is valid if any group
> + * matches (bitwise AND non-zero for every word in the group).
> + *
> + * Return: true if the trip is valid for the running hardware, false otherwise.
> + */
> +static bool thermal_of_trip_is_supported(struct device_node *np,
> + const struct thermal_hw_bin_info *hw)
> +{
> + unsigned int levels, ngroups, i;
> + int count;
> +
> + if (!hw)
> + return true;
> +
> + levels = hw->supported_hw_bin_count;
> +
> + count = of_property_count_u32_elems(np, "thermal-hw-bin");
> + if (count == -EINVAL)
> + return true;
> + if (count < 0) {
> + pr_warn_once("%pOF: error reading thermal-hw-bin: %d\n", np, count);
> + return false;
> + }
> + if (count == 0)
> + return true;
> +
> + if (count % levels) {
> + pr_warn("%pOF: thermal-hw-bin length %d not a multiple of supported_hw_bin_count %u; disabling trip\n",
> + np, count, levels);
> + return false;
> + }
> +
> + ngroups = count / levels;
> + for (i = 0; i < ngroups; i++)
> + if (thermal_of_match_hw_bin_group(np, hw, levels, i))
> + return true;
> +
> + return false;
> +}
> +
> +/**
> + * thermal_of_trip_read_temperature - read trip temperature, with bin support
> + * @np: device node for the trip point
> + * @hw: hardware binning info (may be NULL)
> + * @temp: output temperature in millicelsius
> + *
> + * Tries the scalar 'temperature' property first. If absent, falls back to
> + * 'temperature-bin', selecting the entry whose corresponding thermal-hw-bin
> + * sub-group matches the running hardware. Returns -ENODEV if temperature-bin
> + * is present but no sub-group matches (caller should skip this trip).
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +static int thermal_of_trip_read_temperature(struct device_node *np,
> + const struct thermal_hw_bin_info *hw,
> + s32 *temp)
> +{
> + unsigned int levels, ngroups, i;
> + int count_hw, count_temp;
> + s32 t;
> +
> + if (!of_property_read_s32(np, "temperature", &t)) {
> + *temp = t;
> + return 0;
> + }
> +
> + count_temp = of_property_count_elems_of_size(np, "temperature-bin",
> + sizeof(u32));
> + if (count_temp < 0)
> + return -EINVAL;
> +
> + if (!hw) {
> + u32 utmp;
> +
> + pr_warn("%pOF: temperature-bin requires platform hw binning info; using first entry\n",
> + np);
> + if (of_property_read_u32_index(np, "temperature-bin", 0, &utmp))
> + return -EINVAL;
> + *temp = (s32)utmp;
> + return 0;
> + }
> +
> + levels = hw->supported_hw_bin_count;
> + count_hw = of_property_count_u32_elems(np, "thermal-hw-bin");
> + if (count_hw <= 0 || count_hw % levels)
> + return -EINVAL;
> +
> + ngroups = count_hw / levels;
> + if (count_temp != (int)ngroups)
> + return -EINVAL;
> +
> + for (i = 0; i < ngroups; i++) {
> + if (thermal_of_match_hw_bin_group(np, hw, levels, i)) {
> + u32 utmp;
> +
> + if (of_property_read_u32_index(np, "temperature-bin",
> + i, &utmp))
> + return -EINVAL;
> + *temp = (s32)utmp;
> + return 0;
> + }
> + }
> +
> + pr_warn("%pOF: no thermal-hw-bin sub-group matched for temperature-bin\n", np);
> + return -ENODEV;
> +}
> +
> static int thermal_of_populate_trip(struct device_node *np,
> - struct thermal_trip *trip)
> + struct thermal_trip *trip,
> + const struct thermal_hw_bin_info *hw)
> {
> u32 hysteresis;
> s32 temperature;
> int ret;
>
> - ret = of_property_read_s32(np, "temperature", &temperature);
> - if (ret < 0) {
> - pr_err("missing temperature property\n");
> - return ret;
> + if (!thermal_of_trip_is_supported(np, hw)) {
> + pr_debug("%pOF: trip skipped: no thermal-hw-bin match\n", np);
> + return -ENODEV;
> }
> +
> + ret = thermal_of_trip_read_temperature(np, hw, &temperature);
[Severity: Low]
As reported by Sashiko on v1 [1], thermal_of_populate_trip() calls
thermal_of_trip_is_supported(np, hw) before
thermal_of_trip_read_temperature(np, hw, &temperature) and returns -ENODEV
immediately when thermal_of_trip_is_supported() returns false.
When hw is non-NULL and thermal-hw-bin is valid,
thermal_of_trip_is_supported() already iterates through all ngroups
sub-groups via thermal_of_match_hw_bin_group() and only returns true if at
least one sub-group matches. When thermal_of_trip_read_temperature() then
runs for a temperature-bin trip, it re-reads thermal-hw-bin and executes the
same thermal_of_match_hw_bin_group() loop a second time, leaving the trailing
pr_warn() and -ENODEV return in thermal_of_trip_read_temperature()
unreachable.
In addition, having thermal_of_trip_is_supported() run first causes malformed
DT properties to be masked:
1. If a trip using temperature-bin has a thermal-hw-bin property whose length
is not a multiple of supported_hw_bin_count (count % levels != 0) or fails
with count < 0 (such as -ENODATA), thermal_of_trip_is_supported() returns
false. That causes thermal_of_populate_trip() to return -ENODEV and
thermal_of_trips_init() to silently skip the trip instead of reaching the
-EINVAL return in thermal_of_trip_read_temperature().
2. In thermal_of_trip_is_supported(), of_property_count_u32_elems(np,
"thermal-hw-bin") returns -EINVAL both when the property is absent and
when its byte length is not a multiple of sizeof(u32). Checking for
count == -EINVAL (as well as count == 0) and returning true treats a
non-u32-aligned or empty thermal-hw-bin property as matching all hardware.
Could the thermal-hw-bin lookup and sub-group matching be consolidated into a
single helper so each trip node is validated and matched once, and malformed
properties return -EINVAL?
> + if (ret)
> + return ret;
> trip->temperature = temperature;
>
> ret = of_property_read_u32(np, "hysteresis", &hysteresis);
> @@ -94,7 +241,9 @@ static int thermal_of_populate_trip(struct device_node *np,
> return 0;
> }
>
> -static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *ntrips)
> +static struct thermal_trip *thermal_of_trips_init(struct device_node *np,
> + int *ntrips,
> + const struct thermal_hw_bin_info *hw)
> {
> int ret, count;
>
> @@ -114,9 +263,12 @@ static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n
>
> count = 0;
> for_each_child_of_node_scoped(trips, trip) {
> - ret = thermal_of_populate_trip(trip, &tt[count++]);
> + ret = thermal_of_populate_trip(trip, &tt[count], hw);
> + if (ret == -ENODEV)
> + continue;
> if (ret)
> return ERR_PTR(ret);
> + count++;
> }
[Severity: Medium]
Does treating -ENODEV as a skipped trip here mask invalid trip types in the
device tree?
Later in thermal_of_populate_trip(), thermal_of_get_trip_type() is called:
drivers/thermal/thermal_of.c:thermal_of_populate_trip() {
...
ret = thermal_of_get_trip_type(np, &trip->type);
if (ret < 0) {
pr_err("wrong trip type property\n");
return ret;
}
}
When a trip node's type string does not match any entry in trip_types[],
thermal_of_get_trip_type() returns -ENODEV. Previously,
thermal_of_trips_init() treated that -ENODEV return as a fatal error and
aborted thermal zone registration with ERR_PTR(-ENODEV).
Because thermal_of_trips_init() now continues on -ENODEV to skip unmatched
hardware-bin trips, an unrecognized trip type property will also be ignored
after partially writing tt[count].temperature and tt[count].hysteresis,
registering the thermal zone without that trip point instead of failing
registration. Should thermal_of_get_trip_type() return -EINVAL on an
unrecognized type, or should hardware-bin filtering use a distinct return
value?
[ ... ]
> @@ -510,6 +679,46 @@ struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, in
> }
> EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register);
>
> +/**
> + * devm_thermal_of_zone_register_with_bin - register a thermal zone with hardware binning info
> + *
> + * Like devm_thermal_of_zone_register(), but passes @hw_bin_info to the trip-point
> + * parser so that trips can be filtered and per-bin temperatures selected at
> + * zone registration time. Use this when fuse values are read before zone
> + * registration (e.g. in a platform driver's probe function).
> + *
> + * @dev: device structure pointer to sensor
> + * @id: the sensor identifier
> + * @data: private data stored in the thermal zone 'devdata' field
> + * @ops: ops structure associated with the sensor
> + * @hw_bin_info: hardware binning info for trip filtering (may be NULL)
> + */
> +struct thermal_zone_device *
> +devm_thermal_of_zone_register_with_bin(struct device *dev, int id, void *data,
> + const struct thermal_zone_device_ops *ops,
> + const struct thermal_hw_bin_info *hw_bin_info)
> +{
> + struct thermal_zone_device **ptr, *tzd;
> +
> + ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr),
> + GFP_KERNEL);
> + if (!ptr)
> + return ERR_PTR(-ENOMEM);
> +
> + tzd = thermal_of_zone_register_with_bin(dev->of_node, id, data,
> + ops, hw_bin_info);
> + if (IS_ERR(tzd)) {
> + devres_free(ptr);
> + return tzd;
> + }
> +
> + *ptr = tzd;
> + devres_add(dev, ptr);
> +
> + return tzd;
> +}
> +EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register_with_bin);
[Severity: Low]
This isn't a bug, but there are a few small duplication and documentation
items worth cleaning up:
1. devm_thermal_of_zone_register_with_bin() duplicates the devres_alloc(),
error handling, and devres_add() body of devm_thermal_of_zone_register().
Could devm_thermal_of_zone_register() simply call
devm_thermal_of_zone_register_with_bin(dev, sensor_id, data, ops, NULL),
which would also remove the need for the static thermal_of_zone_register()
wrapper?
2. In the kernel-doc comment for devm_thermal_of_zone_register_with_bin(),
the function description is placed before the @argument list instead of
after it, and a Return: section is missing (which triggers a kernel-doc
warning under W=2 builds).
3. Documentation/driver-api/thermal/hardware-binning.rst states that when a
trip uses temperature-bin without bin info:
If a trip uses ``temperature-bin`` but no bin info has been provided for the
zone, the core cannot select an entry; it warns once and falls back to the
first ``temperature-bin`` value.
However, thermal_of_trip_read_temperature() uses pr_warn() rather than
pr_warn_once(), so it warns for every trip node.
4. In MAINTAINERS:
F: Documentation/driver-api/thermal/
+F: Documentation/driver-api/thermal/hardware-binning.rst
Is the new file entry needed when the directory entry right above it
already covers Documentation/driver-api/thermal/?
[1] https://lore.kernel.org/r/20261002-thermal_bin-v1-2-5a29be7e8ec5@oss.qualcomm.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031-thermal_bin-v2-0-d76fd073ae98@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-31 13:40 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-31 13:22 [PATCH v2 0/6] thermal: Add hardware-binned trip point support Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 1/6] dt-bindings: thermal: thermal-zones: add hardware-binning trip properties Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 2/6] thermal: add hardware-binning trip-point filtering support Gaurav Kohli
2026-10-31 13:40 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 3/6] dt-bindings: thermal: qcom-tsens: document qcm6490 tsens Gaurav Kohli
2026-10-31 13:22 ` [PATCH v2 4/6] thermal: qcom: tsens: add hardware-bin trip-point filtering Gaurav Kohli
2026-10-31 13:36 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 5/6] arm64: dts: qcom: kodiak: use thermal hw-bin trips Gaurav Kohli
2026-10-31 13:35 ` sashiko-bot
2026-10-31 13:22 ` [PATCH v2 6/6] arm64: dts: qcom: hamoa: add thermal hw-bin support Gaurav Kohli
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.