--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/generic-adc-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: General Purpose Analog To Digital Converter (ADC) based thermal sensor
+
+maintainers:
+ - Laxman Dewangan <ldewangan@nvidia.com>
+
+description:
+ On some of platforms, thermal sensor like thermistors are connected to
+ one of ADC channel and sensor resistance is read via voltage across the
+ sensor resistor. The voltage read across the sensor is mapped to
+ temperature using voltage-temperature lookup table.
+
+properties:
+ compatible:
+ const: generic-adc-thermal
+
+ '#thermal-sensor-cells':
+ const: 0
+
+ io-channels:
+ maxItems: 1
+
+ io-channel-names:
+ const: sensor-channel
+
+ temperature-lookup-table:
+ description: |
+ Lookup table to map the relation between ADC value and temperature.
+ When ADC is read, the value is looked up on the table to get the
+ equivalent temperature.
+
+ If not specified, driver assumes the ADC channel gives milliCelsius
+ directly.
+ $ref: /schemas/types.yaml#/definitions/int32-matrix
+ items:
+ items:
+ - description: Temperature in milliCelsius
+ - description: ADC read value
+
+required:
+ - compatible
+ - '#thermal-sensor-cells'
+ - io-channels
+ - io-channel-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/thermal/thermal.h>
+
+ thermal-sensor {
+ compatible = "generic-adc-thermal";
+ #thermal-sensor-cells = <0>;
+ io-channels = <&ads1015 1>;
+ io-channel-names = "sensor-channel";
+ temperature-lookup-table = <
+ (-40000) 2578
+ (-39000) 2577
+ (-38000) 2576
+ (-37000) 2575
+ (-36000) 2574
+ (-35000) 2573
+ (-34000) 2572
+ (-33000) 2571
+ (-32000) 2569
+ (-31000) 2568
+ (-30000) 2567
+ /* skip */
+ 118000 254
+ 119000 247
+ 120000 240
+ 121000 233
+ 122000 226
+ 123000 220
+ 124000 214
+ 125000 208>;
+ };
+...
clocks:
maxItems: 1
+ nvmem-cells:
+ maxItems: 1
+ description: Phandle to the calibration data provided by ocotp
+
+ nvmem-cell-names:
+ const: calib
+
"#thermal-sensor-cells":
description: |
Number of cells required to uniquely identify the thermal
- "mediatek,mt2701-thermal" : For MT2701 family of SoCs
- "mediatek,mt2712-thermal" : For MT2712 family of SoCs
- "mediatek,mt7622-thermal" : For MT7622 SoC
+ - "mediatek,mt7981-thermal", "mediatek,mt7986-thermal" : For MT7981 SoC
+ - "mediatek,mt7986-thermal" : For MT7986 SoC
- "mediatek,mt8183-thermal" : For MT8183 family of SoCs
- "mediatek,mt8516-thermal", "mediatek,mt2701-thermal : For MT8516 family of SoCs
- reg: Address range of the thermal controller
- qcom,sm8150-tsens
- qcom,sm8250-tsens
- qcom,sm8350-tsens
+ - qcom,sm8450-tsens
+ - qcom,sm8550-tsens
- const: qcom,tsens-v2
+ - description: v2 of TSENS with combined interrupt
+ enum:
+ - qcom,ipq8074-tsens
+
reg:
items:
- description: TM registers
interrupts:
minItems: 1
- items:
- - description: Combined interrupt if upper or lower threshold crossed
- - description: Interrupt if critical threshold crossed
+ maxItems: 2
interrupt-names:
minItems: 1
- items:
- - const: uplow
- - const: critical
+ maxItems: 2
nvmem-cells:
minItems: 1
then:
properties:
interrupts:
- maxItems: 1
+ items:
+ - description: Combined interrupt if upper or lower threshold crossed
interrupt-names:
- maxItems: 1
+ items:
+ - const: uplow
- else:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,msm8953-tsens
+ - qcom,msm8996-tsens
+ - qcom,msm8998-tsens
+ - qcom,sc7180-tsens
+ - qcom,sc7280-tsens
+ - qcom,sc8180x-tsens
+ - qcom,sc8280xp-tsens
+ - qcom,sdm630-tsens
+ - qcom,sdm845-tsens
+ - qcom,sm6350-tsens
+ - qcom,sm8150-tsens
+ - qcom,sm8250-tsens
+ - qcom,sm8350-tsens
+ - qcom,sm8450-tsens
+ - qcom,tsens-v2
+ then:
properties:
interrupts:
- minItems: 2
+ items:
+ - description: Combined interrupt if upper or lower threshold crossed
+ - description: Interrupt if critical threshold crossed
interrupt-names:
- minItems: 2
+ items:
+ - const: uplow
+ - const: critical
- if:
properties:
compatible:
contains:
enum:
+ - qcom,ipq8074-tsens
+ then:
+ properties:
+ interrupts:
+ items:
+ - description: Combined interrupt if upper, lower or critical thresholds crossed
+ interrupt-names:
+ items:
+ - const: combined
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,ipq8074-tsens
- qcom,tsens-v0_1
- qcom,tsens-v1
- qcom,tsens-v2
#qcom,sensors = <13>;
#thermal-sensor-cells = <1>;
};
+
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ // Example 4 (for any IPQ8074 based SoC-s):
+ tsens4: thermal-sensor@4a9000 {
+ compatible = "qcom,ipq8074-tsens";
+ reg = <0x4a9000 0x1000>,
+ <0x4a8000 0x1000>;
+
+ interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "combined";
+
+ #qcom,sensors = <16>;
+ #thermal-sensor-cells = <1>;
+ };
...
compatible:
items:
- enum:
- - renesas,r9a07g043-tsu # RZ/G2UL
+ - renesas,r9a07g043-tsu # RZ/G2UL and RZ/Five
- renesas,r9a07g044-tsu # RZ/G2{L,LC}
- renesas,r9a07g054-tsu # RZ/V2L
- const: renesas,rzg2l-tsu
+++ /dev/null
-General Purpose Analog To Digital Converter (ADC) based thermal sensor.
-
-On some of platforms, thermal sensor like thermistors are connected to
-one of ADC channel and sensor resistance is read via voltage across the
-sensor resistor. The voltage read across the sensor is mapped to
-temperature using voltage-temperature lookup table.
-
-Required properties:
-===================
-- compatible: Must be "generic-adc-thermal".
-- #thermal-sensor-cells: Should be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description
- of this property.
-Optional properties:
-===================
-- temperature-lookup-table: Two dimensional array of Integer; lookup table
- to map the relation between ADC value and
- temperature. When ADC is read, the value is
- looked up on the table to get the equivalent
- temperature.
-
- The first value of the each row of array is the
- temperature in milliCelsius and second value of
- the each row of array is the ADC read value.
-
- If not specified, driver assumes the ADC channel
- gives milliCelsius directly.
-
-Example :
-#include <dt-bindings/thermal/thermal.h>
-
-i2c@7000c400 {
- ads1015: ads1015@4a {
- reg = <0x4a>;
- compatible = "ads1015";
- sampling-frequency = <3300>;
- #io-channel-cells = <1>;
- };
-};
-
-tboard_thermistor: thermal-sensor {
- compatible = "generic-adc-thermal";
- #thermal-sensor-cells = <0>;
- io-channels = <&ads1015 1>;
- io-channel-names = "sensor-channel";
- temperature-lookup-table = < (-40000) 2578
- (-39000) 2577
- (-38000) 2576
- (-37000) 2575
- (-36000) 2574
- (-35000) 2573
- (-34000) 2572
- (-33000) 2571
- (-32000) 2569
- (-31000) 2568
- (-30000) 2567
- ::::::::::
- 118000 254
- 119000 247
- 120000 240
- 121000 233
- 122000 226
- 123000 220
- 124000 214
- 125000 208>;
-};
-
-dummy_cool_dev: dummy-cool-dev {
- compatible = "dummy-cooling-dev";
- #cooling-cells = <2>; /* min followed by max */
-};
-
-thermal-zones {
- Tboard {
- polling-delay = <15000>; /* milliseconds */
- polling-delay-passive = <0>; /* milliseconds */
- thermal-sensors = <&tboard_thermistor>;
-
- trips {
- therm_est_trip: therm_est_trip {
- temperature = <40000>;
- type = "active";
- hysteresis = <1000>;
- };
- };
-
- cooling-maps {
- map0 {
- trip = <&therm_est_trip>;
- cooling-device = <&dummy_cool_dev THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
- contribution = <100>;
- };
-
- };
- };
-};
maintainers:
- Keerthy <j-keerthy@ti.com>
+description: |
+ The TI K3 family of SoCs typically have a Voltage & Thermal
+ Management (VTM) device to control up to 8 temperature diode
+ sensors to measure silicon junction temperatures from different
+ hotspots of the chip as well as provide temperature, interrupt
+ and alerting information.
+
+ The following polynomial equation can then be used to convert
+ value returned by this device into a temperature in Celsius
+
+ Temp(C) = (-9.2627e-12) * x^4 + (6.0373e-08) * x^3 + \
+ (-1.7058e-04) * x^2 + (3.2512e-01) * x + (-4.9003e+01)
+
properties:
compatible:
enum:
items:
- description: VTM cfg1 register space
- description: VTM cfg2 register space
- - description: VTM efuse register space
+ - description: |
+ A software trimming method must be applied to some Jacinto
+ devices to function properly. This eFuse region provides
+ the information needed for these SoCs to report
+ temperatures accurately.
+ minItems: 2
power-domains:
maxItems: 1
"#thermal-sensor-cells":
const: 1
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,j721e-vtm
+ then:
+ properties:
+ reg:
+ minItems: 3
+ else:
+ properties:
+ reg:
+ maxItems: 2
+
required:
- compatible
- reg
#define pr_fmt(fmt) "Power allocator: " fmt
-#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/thermal.h>
#include "thermal_core.h"
+#include "thermal_hwmon.h"
#define TER 0x0 /* TMU enable */
#define TPS 0x4
#define TRITSR 0x20 /* TMU immediate temp */
+/* TMU calibration data registers */
+#define TASR 0x28
+#define TASR_BUF_SLOPE_MASK GENMASK(19, 16)
+#define TASR_BUF_VREF_MASK GENMASK(4, 0) /* TMU_V1 */
+#define TASR_BUF_VERF_SEL_MASK GENMASK(1, 0) /* TMU_V2 */
+#define TCALIV(n) (0x30 + ((n) * 4))
+#define TCALIV_EN BIT(31)
+#define TCALIV_HR_MASK GENMASK(23, 16) /* TMU_V1 */
+#define TCALIV_RT_MASK GENMASK(7, 0) /* TMU_V1 */
+#define TCALIV_SNSR105C_MASK GENMASK(27, 16) /* TMU_V2 */
+#define TCALIV_SNSR25C_MASK GENMASK(11, 0) /* TMU_V2 */
+#define TRIM 0x3c
+#define TRIM_BJT_CUR_MASK GENMASK(23, 20)
+#define TRIM_BGR_MASK GENMASK(31, 28)
+#define TRIM_VLSB_MASK GENMASK(15, 12)
+#define TRIM_EN_CH BIT(7)
#define TER_ADC_PD BIT(30)
#define TER_EN BIT(31)
-#define TRITSR_TEMP0_VAL_MASK 0xff
-#define TRITSR_TEMP1_VAL_MASK 0xff0000
+#define TRITSR_TEMP0_VAL_MASK GENMASK(7, 0)
+#define TRITSR_TEMP1_VAL_MASK GENMASK(23, 16)
#define PROBE_SEL_ALL GENMASK(31, 30)
#define SIGN_BIT BIT(7)
#define TEMP_VAL_MASK GENMASK(6, 0)
+/* TMU OCOTP calibration data bitfields */
+#define ANA0_EN BIT(25)
+#define ANA0_BUF_VREF_MASK GENMASK(24, 20)
+#define ANA0_BUF_SLOPE_MASK GENMASK(19, 16)
+#define ANA0_HR_MASK GENMASK(15, 8)
+#define ANA0_RT_MASK GENMASK(7, 0)
+#define TRIM2_VLSB_MASK GENMASK(23, 20)
+#define TRIM2_BGR_MASK GENMASK(19, 16)
+#define TRIM2_BJT_CUR_MASK GENMASK(15, 12)
+#define TRIM2_BUF_SLOP_SEL_MASK GENMASK(11, 8)
+#define TRIM2_BUF_VERF_SEL_MASK GENMASK(7, 6)
+#define TRIM3_TCA25_0_LSB_MASK GENMASK(31, 28)
+#define TRIM3_TCA40_0_MASK GENMASK(27, 16)
+#define TRIM4_TCA40_1_MASK GENMASK(31, 20)
+#define TRIM4_TCA105_0_MASK GENMASK(19, 8)
+#define TRIM4_TCA25_0_MSB_MASK GENMASK(7, 0)
+#define TRIM5_TCA105_1_MASK GENMASK(23, 12)
+#define TRIM5_TCA25_1_MASK GENMASK(11, 0)
+
#define VER1_TEMP_LOW_LIMIT 10000
#define VER2_TEMP_LOW_LIMIT -40000
#define VER2_TEMP_HIGH_LIMIT 125000
u32 val;
val = readl_relaxed(tmu->base + TRITSR) & TRITSR_TEMP0_VAL_MASK;
+
+ /*
+ * Do not validate against the V bit (bit 31) due to errata
+ * ERR051272: TMU: Bit 31 of registers TMU_TSCR/TMU_TRITSR/TMU_TRATSR invalid
+ */
+
*temp = val * 1000;
- if (*temp < VER1_TEMP_LOW_LIMIT)
+ if (*temp < VER1_TEMP_LOW_LIMIT || *temp > VER2_TEMP_HIGH_LIMIT)
return -EAGAIN;
return 0;
writel_relaxed(val, tmu->base + TPS);
}
+static int imx8mm_tmu_probe_set_calib_v1(struct platform_device *pdev,
+ struct imx8mm_tmu *tmu)
+{
+ struct device *dev = &pdev->dev;
+ u32 ana0;
+ int ret;
+
+ ret = nvmem_cell_read_u32(&pdev->dev, "calib", &ana0);
+ if (ret) {
+ dev_warn(dev, "Failed to read OCOTP nvmem cell (%d).\n", ret);
+ return ret;
+ }
+
+ writel(FIELD_PREP(TASR_BUF_VREF_MASK,
+ FIELD_GET(ANA0_BUF_VREF_MASK, ana0)) |
+ FIELD_PREP(TASR_BUF_SLOPE_MASK,
+ FIELD_GET(ANA0_BUF_SLOPE_MASK, ana0)),
+ tmu->base + TASR);
+
+ writel(FIELD_PREP(TCALIV_RT_MASK, FIELD_GET(ANA0_RT_MASK, ana0)) |
+ FIELD_PREP(TCALIV_HR_MASK, FIELD_GET(ANA0_HR_MASK, ana0)) |
+ ((ana0 & ANA0_EN) ? TCALIV_EN : 0),
+ tmu->base + TCALIV(0));
+
+ return 0;
+}
+
+static int imx8mm_tmu_probe_set_calib_v2(struct platform_device *pdev,
+ struct imx8mm_tmu *tmu)
+{
+ struct device *dev = &pdev->dev;
+ struct nvmem_cell *cell;
+ u32 trim[4] = { 0 };
+ size_t len;
+ void *buf;
+
+ cell = nvmem_cell_get(dev, "calib");
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ memcpy(trim, buf, min(len, sizeof(trim)));
+ kfree(buf);
+
+ if (len != 16) {
+ dev_err(dev,
+ "OCOTP nvmem cell length is %zu, must be 16.\n", len);
+ return -EINVAL;
+ }
+
+ /* Blank sample hardware */
+ if (!trim[0] && !trim[1] && !trim[2] && !trim[3]) {
+ /* Use a default 25C binary codes */
+ writel(FIELD_PREP(TCALIV_SNSR25C_MASK, 0x63c),
+ tmu->base + TCALIV(0));
+ writel(FIELD_PREP(TCALIV_SNSR25C_MASK, 0x63c),
+ tmu->base + TCALIV(1));
+ return 0;
+ }
+
+ writel(FIELD_PREP(TASR_BUF_VERF_SEL_MASK,
+ FIELD_GET(TRIM2_BUF_VERF_SEL_MASK, trim[0])) |
+ FIELD_PREP(TASR_BUF_SLOPE_MASK,
+ FIELD_GET(TRIM2_BUF_SLOP_SEL_MASK, trim[0])),
+ tmu->base + TASR);
+
+ writel(FIELD_PREP(TRIM_BJT_CUR_MASK,
+ FIELD_GET(TRIM2_BJT_CUR_MASK, trim[0])) |
+ FIELD_PREP(TRIM_BGR_MASK, FIELD_GET(TRIM2_BGR_MASK, trim[0])) |
+ FIELD_PREP(TRIM_VLSB_MASK, FIELD_GET(TRIM2_VLSB_MASK, trim[0])) |
+ TRIM_EN_CH,
+ tmu->base + TRIM);
+
+ writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
+ FIELD_GET(TRIM3_TCA25_0_LSB_MASK, trim[1]) |
+ (FIELD_GET(TRIM4_TCA25_0_MSB_MASK, trim[2]) << 4)) |
+ FIELD_PREP(TCALIV_SNSR105C_MASK,
+ FIELD_GET(TRIM4_TCA105_0_MASK, trim[2])),
+ tmu->base + TCALIV(0));
+
+ writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
+ FIELD_GET(TRIM5_TCA25_1_MASK, trim[3])) |
+ FIELD_PREP(TCALIV_SNSR105C_MASK,
+ FIELD_GET(TRIM5_TCA105_1_MASK, trim[3])),
+ tmu->base + TCALIV(1));
+
+ writel(FIELD_PREP(TCALIV_SNSR25C_MASK,
+ FIELD_GET(TRIM3_TCA40_0_MASK, trim[1])) |
+ FIELD_PREP(TCALIV_SNSR105C_MASK,
+ FIELD_GET(TRIM4_TCA40_1_MASK, trim[2])),
+ tmu->base + TCALIV(2));
+
+ return 0;
+}
+
+static int imx8mm_tmu_probe_set_calib(struct platform_device *pdev,
+ struct imx8mm_tmu *tmu)
+{
+ struct device *dev = &pdev->dev;
+
+ /*
+ * Lack of calibration data OCOTP reference is not considered
+ * fatal to retain compatibility with old DTs. It is however
+ * strongly recommended to update such old DTs to get correct
+ * temperature compensation values for each SoC.
+ */
+ if (!of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) {
+ dev_warn(dev,
+ "No OCOTP nvmem reference found, SoC-specific calibration not loaded. Please update your DT.\n");
+ return 0;
+ }
+
+ if (tmu->socdata->version == TMU_VER1)
+ return imx8mm_tmu_probe_set_calib_v1(pdev, tmu);
+
+ return imx8mm_tmu_probe_set_calib_v2(pdev, tmu);
+}
+
static int imx8mm_tmu_probe(struct platform_device *pdev)
{
const struct thermal_soc_data *data;
goto disable_clk;
}
tmu->sensors[i].hw_id = i;
+
+ if (devm_thermal_add_hwmon_sysfs(tmu->sensors[i].tzd))
+ dev_warn(&pdev->dev, "failed to add hwmon sysfs attributes\n");
}
platform_set_drvdata(pdev, tmu);
+ ret = imx8mm_tmu_probe_set_calib(pdev, tmu);
+ if (ret)
+ goto disable_clk;
+
/* enable all the probes for V2 TMU */
if (tmu->socdata->version == TMU_VER2)
imx8mm_tmu_probe_sel_all(tmu);
return 0;
}
-static int imx_sc_thermal_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static int imx_sc_sensors[] = { IMX_SC_R_SYSTEM, IMX_SC_R_PMIC_0, -1 };
static const struct of_device_id imx_sc_thermal_table[] = {
static struct platform_driver imx_sc_thermal_driver = {
.probe = imx_sc_thermal_probe,
- .remove = imx_sc_thermal_remove,
.driver = {
.name = "imx-sc-thermal",
.of_match_table = imx_sc_thermal_table,
#define THERM_STATUS_PROCHOT_LOG BIT(1)
#define THERM_STATUS_CLEAR_CORE_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(13) | BIT(15))
-#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11) | BIT(26))
+#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | BIT(9) | BIT(11))
/*
* Clear the bits in package thermal status register for bit = 1
} else {
msr = MSR_IA32_PACKAGE_THERM_STATUS;
msr_val = THERM_STATUS_CLEAR_PKG_MASK;
+ if (boot_cpu_has(X86_FEATURE_HFI))
+ msr_val |= BIT(26);
+
}
msr_val &= ~bit_mask;
struct device *dev;
void __iomem *base;
void __iomem *cfg2_base;
- void __iomem *fuse_base;
struct k3_thermal_data *ts_data[K3_VTM_MAX_NUM_TS];
};
/* Get temperature callback function for thermal zone */
static int k3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
- struct k3_thermal_data *data = tz->devdata;
- int ret = 0;
-
- ret = k3_bgp_read_temp(data, temp);
- if (ret)
- return ret;
-
- return ret;
+ return k3_bgp_read_temp(tz->devdata, temp);
}
static const struct thermal_zone_device_ops k3_of_thermal_ops = {
}
static void get_efuse_values(int id, struct k3_thermal_data *data, int *err,
- struct k3_j72xx_bandgap *bgp)
+ void __iomem *fuse_base)
{
int i, tmp, pow;
int ct_offsets[5][K3_VTM_CORRECTION_TEMP_CNT] = {
/* Extract the offset value using bit-mask */
if (ct_offsets[id][i] == -1 && i == 1) {
/* 25C offset Case of Sensor 2 split between 2 regs */
- tmp = (readl(bgp->fuse_base + 0x8) & 0xE0000000) >> (29);
- tmp |= ((readl(bgp->fuse_base + 0xC) & 0x1F) << 3);
+ tmp = (readl(fuse_base + 0x8) & 0xE0000000) >> (29);
+ tmp |= ((readl(fuse_base + 0xC) & 0x1F) << 3);
pow = tmp & 0x80;
} else if (ct_offsets[id][i] == -1 && i == 2) {
/* 125C Case of Sensor 3 split between 2 regs */
- tmp = (readl(bgp->fuse_base + 0x4) & 0xF8000000) >> (27);
- tmp |= ((readl(bgp->fuse_base + 0x8) & 0xF) << 5);
+ tmp = (readl(fuse_base + 0x4) & 0xF8000000) >> (27);
+ tmp |= ((readl(fuse_base + 0x8) & 0xF) << 5);
pow = tmp & 0x100;
} else {
- tmp = readl(bgp->fuse_base + ct_offsets[id][i]);
+ tmp = readl(fuse_base + ct_offsets[id][i]);
tmp &= ct_bm[id][i];
tmp = tmp >> __ffs(ct_bm[id][i]);
}
struct k3_j72xx_bandgap_data {
- unsigned int has_errata_i2128;
+ const bool has_errata_i2128;
};
static int k3_j72xx_bandgap_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct k3_j72xx_bandgap *bgp;
struct k3_thermal_data *data;
- int workaround_needed = 0;
+ bool workaround_needed = false;
const struct k3_j72xx_bandgap_data *driver_data;
struct thermal_zone_device *ti_thermal;
int *ref_table;
struct err_values err_vals;
+ void __iomem *fuse_base;
const s64 golden_factors[] = {
-490019999999999936,
if (IS_ERR(bgp->cfg2_base))
return PTR_ERR(bgp->cfg2_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- bgp->fuse_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(bgp->fuse_base))
- return PTR_ERR(bgp->fuse_base);
-
driver_data = of_device_get_match_data(dev);
if (driver_data)
workaround_needed = driver_data->has_errata_i2128;
+ /*
+ * Some of TI's J721E SoCs require a software trimming procedure
+ * for the temperature monitors to function properly. To determine
+ * if this particular SoC is NOT affected, both bits in the
+ * WKUP_SPARE_FUSE0[31:30] will be set (0xC0000000) indicating
+ * when software trimming should NOT be applied.
+ *
+ * https://www.ti.com/lit/er/sprz455c/sprz455c.pdf
+ */
+ if (workaround_needed) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ fuse_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fuse_base))
+ return PTR_ERR(fuse_base);
+
+ if ((readl(fuse_base) & 0xc0000000) == 0xc0000000)
+ workaround_needed = false;
+ }
+
+ dev_dbg(bgp->dev, "Work around %sneeded\n",
+ workaround_needed ? "" : "not ");
+
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
goto err_free_ref_table;
}
- /* Workaround not needed if bit30/bit31 is set even for J721e */
- if (workaround_needed && (readl(bgp->fuse_base + 0x0) & 0xc0000000) == 0xc0000000)
- workaround_needed = false;
-
- dev_dbg(bgp->dev, "Work around %sneeded\n",
- workaround_needed ? "not " : "");
-
if (!workaround_needed)
init_table(5, ref_table, golden_factors);
else
err_vals.refs[1] = PLUS30CREF;
err_vals.refs[2] = PLUS125CREF;
err_vals.refs[3] = PLUS150CREF;
- get_efuse_values(id, &data[id], err_vals.errs, bgp);
+ get_efuse_values(id, &data[id], err_vals.errs, fuse_base);
}
if (id == 0 && workaround_needed)
}
static const struct k3_j72xx_bandgap_data k3_j72xx_bandgap_j721e_data = {
- .has_errata_i2128 = 1,
+ .has_errata_i2128 = true,
};
static const struct k3_j72xx_bandgap_data k3_j72xx_bandgap_j7200_data = {
- .has_errata_i2128 = 0,
+ .has_errata_i2128 = false,
};
static const struct of_device_id of_k3_j72xx_bandgap_match[] = {
if (irq)
generic_handle_irq(irq);
- return 0;
+ return IRQ_HANDLED;
}
static void lmh_enable_interrupt(struct irq_data *d)
&adc_tm5_thermal_ops);
if (IS_ERR(tzd)) {
if (PTR_ERR(tzd) == -ENODEV) {
- dev_warn(adc_tm->dev, "thermal sensor on channel %d is not used\n",
+ dev_dbg(adc_tm->dev, "thermal sensor on channel %d is not used\n",
adc_tm->channels[i].channel);
continue;
}
return irq;
ret = adc_tm5_get_dt_data(adc_tm, node);
- if (ret) {
- dev_err(dev, "get dt data failed: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "get dt data failed\n");
ret = adc_tm->data->init(adc_tm);
if (ret) {
disable_s2_shutdown = true;
else
dev_warn(chip->dev,
- "No ADC is configured and critical temperature is above the maximum stage 2 threshold of 140 C! Configuring stage 2 shutdown at 140 C.\n");
+ "No ADC is configured and critical temperature %d mC is above the maximum stage 2 threshold of %ld mC! Configuring stage 2 shutdown at %ld mC.\n",
+ temp, stage2_threshold_max, stage2_threshold_max);
}
skip:
static struct tsens_features tsens_8960_feat = {
.ver_major = VER_0,
.crit_int = 0,
+ .combo_int = 0,
.adc = 1,
.srot_split = 0,
.max_sensors = 11,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
};
struct tsens_plat_data data_8960 = {
static struct tsens_features tsens_v0_1_feat = {
.ver_major = VER_0_1,
.crit_int = 0,
+ .combo_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
};
static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
static struct tsens_features tsens_v1_feat = {
.ver_major = VER_1_X,
.crit_int = 0,
+ .combo_int = 0,
.adc = 1,
.srot_split = 1,
.max_sensors = 11,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
};
static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
static struct tsens_features tsens_v2_feat = {
.ver_major = VER_2_X,
.crit_int = 1,
+ .combo_int = 0,
.adc = 0,
.srot_split = 1,
.max_sensors = 16,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
+};
+
+static struct tsens_features ipq8074_feat = {
+ .ver_major = VER_2_X,
+ .crit_int = 1,
+ .combo_int = 1,
+ .adc = 0,
+ .srot_split = 1,
+ .max_sensors = 16,
+ .trip_min_temp = 0,
+ .trip_max_temp = 204000,
};
static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
.fields = tsens_v2_regfields,
};
+struct tsens_plat_data data_ipq8074 = {
+ .ops = &ops_generic_v2,
+ .feat = &ipq8074_feat,
+ .fields = tsens_v2_regfields,
+};
+
/* Kept around for backward compatibility with old msm8996.dtsi */
struct tsens_plat_data data_8996 = {
.num_sensors = 13,
return IRQ_HANDLED;
}
+/**
+ * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts
+ * @irq: irq number
+ * @data: tsens controller private data
+ *
+ * Handle the combined interrupt as if it were 2 separate interrupts, so call the
+ * critical handler first and then the up/low one.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t tsens_combined_irq_thread(int irq, void *data)
+{
+ irqreturn_t ret;
+
+ ret = tsens_critical_irq_thread(irq, data);
+ if (ret != IRQ_HANDLED)
+ return ret;
+
+ return tsens_irq_thread(irq, data);
+}
+
static int tsens_set_trips(struct thermal_zone_device *tz, int low, int high)
{
struct tsens_sensor *s = tz->devdata;
dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
hw_id, __func__, low, high);
- cl_high = clamp_val(high, -40000, 120000);
- cl_low = clamp_val(low, -40000, 120000);
+ cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
+ cl_low = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
high_val = tsens_mC_to_hw(s, cl_high);
low_val = tsens_mC_to_hw(s, cl_low);
return ret;
seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
} else {
- seq_puts(s, "0.1.0\n");
+ seq_printf(s, "0.%d.0\n", priv->feat->ver_major);
}
return 0;
static void tsens_debug_init(struct platform_device *pdev)
{
struct tsens_priv *priv = platform_get_drvdata(pdev);
- struct dentry *root, *file;
- root = debugfs_lookup("tsens", NULL);
- if (!root)
+ priv->debug_root = debugfs_lookup("tsens", NULL);
+ if (!priv->debug_root)
priv->debug_root = debugfs_create_dir("tsens", NULL);
- else
- priv->debug_root = root;
-
- file = debugfs_lookup("version", priv->debug_root);
- if (!file)
- debugfs_create_file("version", 0444, priv->debug_root,
- pdev, &dbg_version_fops);
/* A directory for each instance of the TSENS IP */
priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
+ debugfs_create_file("version", 0444, priv->debug, pdev, &dbg_version_fops);
debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
}
#else
if (tsens_version(priv) >= VER_0_1)
tsens_enable_irq(priv);
- tsens_debug_init(op);
-
err_put_device:
put_device(&op->dev);
return ret;
.compatible = "qcom,ipq8064-tsens",
.data = &data_8960,
}, {
+ .compatible = "qcom,ipq8074-tsens",
+ .data = &data_ipq8074,
+ }, {
.compatible = "qcom,mdm9607-tsens",
.data = &data_9607,
}, {
tsens_mC_to_hw(priv->sensor, 0));
}
- ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
- if (ret < 0)
- return ret;
+ if (priv->feat->combo_int) {
+ ret = tsens_register_irq(priv, "combined",
+ tsens_combined_irq_thread);
+ } else {
+ ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
+ if (ret < 0)
+ return ret;
- if (priv->feat->crit_int)
- ret = tsens_register_irq(priv, "critical",
- tsens_critical_irq_thread);
+ if (priv->feat->crit_int)
+ ret = tsens_register_irq(priv, "critical",
+ tsens_critical_irq_thread);
+ }
return ret;
}
}
}
- return tsens_register(priv);
+ ret = tsens_register(priv);
+ if (!ret)
+ tsens_debug_init(pdev);
+
+ return ret;
}
static int tsens_remove(struct platform_device *pdev)
* struct tsens_features - Features supported by the IP
* @ver_major: Major number of IP version
* @crit_int: does the IP support critical interrupts?
+ * @combo_int: does the IP use one IRQ for up, low and critical thresholds?
* @adc: do the sensors only output adc code (instead of temperature)?
* @srot_split: does the IP neatly splits the register space into SROT and TM,
* with SROT only being available to secure boot firmware?
* @has_watchdog: does this IP support watchdog functionality?
* @max_sensors: maximum sensors supported by this version of the IP
+ * @trip_min_temp: minimum trip temperature supported by this version of the IP
+ * @trip_max_temp: maximum trip temperature supported by this version of the IP
*/
struct tsens_features {
unsigned int ver_major;
unsigned int crit_int:1;
+ unsigned int combo_int:1;
unsigned int adc:1;
unsigned int srot_split:1;
unsigned int has_watchdog:1;
unsigned int max_sensors;
+ int trip_min_temp;
+ int trip_max_temp;
};
/**
extern struct tsens_plat_data data_tsens_v1, data_8976;
/* TSENS v2 targets */
-extern struct tsens_plat_data data_8996, data_tsens_v2;
+extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
#endif /* __QCOM_TSENS_H__ */
static int stm_thermal_probe(struct platform_device *pdev)
{
struct stm_thermal_sensor *sensor;
- struct resource *res;
void __iomem *base;
int ret;
sensor->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(base))
return PTR_ERR(base);
if (IS_ERR(np)) {
if (PTR_ERR(np) != -ENODEV)
pr_err("Failed to find thermal zone for %pOFn id=%d\n", sensor, id);
- return ERR_CAST(np);
+ ret = PTR_ERR(np);
+ goto out_kfree_of_ops;
}
trips = thermal_of_trips_init(np, &ntrips);
if (IS_ERR(trips)) {
pr_err("Failed to find trip points for %pOFn id=%d\n", sensor, id);
- return ERR_CAST(trips);
+ ret = PTR_ERR(trips);
+ goto out_kfree_of_ops;
}
ret = thermal_of_monitor_init(np, &delay, &pdelay);
kfree(tzp);
out_kfree_trips:
kfree(trips);
+out_kfree_of_ops:
+ kfree(of_ops);
return ERR_PTR(ret);
}
*/
static const struct soc_device_attribute soc_no_cpu_notifier[] = {
{ .machine = "OMAP4430" },
- { /* sentinel */ },
+ { /* sentinel */ }
};
/*** Device driver call backs ***/