]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
thermal/drivers/tsens: Add TSENS enable and calibration support for V2
authorPraveenkumar I <quic_ipkumar@quicinc.com>
Mon, 10 Feb 2025 12:04:32 +0000 (17:34 +0530)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Tue, 25 Mar 2025 19:52:03 +0000 (20:52 +0100)
SoCs without RPM need to enable sensors and calibrate them from the kernel.
The IPQ5332 and IPQ5424 use the tsens v2.3.3 IP and do not have RPM.
Therefore, add a new calibration function for V2, as the tsens.c calib
function only supports V1. Also add new feature_config, ops and data for
IPQ5332, IPQ5424.

Although the TSENS IP supports 16 sensors, not all are used. The hw_id
is used to enable the relevant sensors.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Praveenkumar I <quic_ipkumar@quicinc.com>
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Link: https://lore.kernel.org/r/20250210120436.821684-3-quic_mmanikan@quicinc.com
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
drivers/thermal/qcom/tsens-v2.c
drivers/thermal/qcom/tsens.c
drivers/thermal/qcom/tsens.h

index 0cb7301eca6e93cf5bc07fe3706730a32a5fb822..8d9698ea3ec4018181e2ee161f607dd6300ea437 100644 (file)
@@ -4,13 +4,32 @@
  * Copyright (c) 2018, Linaro Limited
  */
 
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/regmap.h>
 #include "tsens.h"
 
 /* ----- SROT ------ */
 #define SROT_HW_VER_OFF        0x0000
 #define SROT_CTRL_OFF          0x0004
+#define SROT_MEASURE_PERIOD    0x0008
+#define SROT_Sn_CONVERSION     0x0060
+#define V2_SHIFT_DEFAULT       0x0003
+#define V2_SLOPE_DEFAULT       0x0cd0
+#define V2_CZERO_DEFAULT       0x016a
+#define ONE_PT_SLOPE           0x0cd0
+#define TWO_PT_SHIFTED_GAIN    921600
+#define ONE_PT_CZERO_CONST     94
+#define SW_RST_DEASSERT                0x0
+#define SW_RST_ASSERT          0x1
+#define MEASURE_PERIOD_2mSEC   0x1
+#define RESULT_FORMAT_TEMP     0x1
+#define TSENS_ENABLE           0x1
+#define SENSOR_CONVERSION(n)   (((n) * 4) + SROT_Sn_CONVERSION)
+#define CONVERSION_SHIFT_MASK  GENMASK(24, 23)
+#define CONVERSION_SLOPE_MASK  GENMASK(22, 10)
+#define CONVERSION_CZERO_MASK  GENMASK(9, 0)
 
 /* ----- TM ------ */
 #define TM_INT_EN_OFF                  0x0004
@@ -50,6 +69,17 @@ static struct tsens_features ipq8074_feat = {
        .trip_max_temp  = 204000,
 };
 
+static struct tsens_features ipq5332_feat = {
+       .ver_major      = VER_2_X_NO_RPM,
+       .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] = {
        /* ----- SROT ------ */
        /* VERSION */
@@ -59,6 +89,10 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
        /* CTRL_OFF */
        [TSENS_EN]     = REG_FIELD(SROT_CTRL_OFF,    0,  0),
        [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF,    1,  1),
+       [SENSOR_EN]    = REG_FIELD(SROT_CTRL_OFF,    3,  18),
+       [CODE_OR_TEMP] = REG_FIELD(SROT_CTRL_OFF,    21, 21),
+
+       [MAIN_MEASURE_PERIOD] = REG_FIELD(SROT_MEASURE_PERIOD, 0, 7),
 
        /* ----- TM ------ */
        /* INTERRUPT ENABLE */
@@ -104,6 +138,128 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
        [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
 };
 
+static int tsens_v2_calibrate_sensor(struct device *dev, struct tsens_sensor *sensor,
+                                    struct regmap *map,  u32 mode, u32 base0, u32 base1)
+{
+       u32     shift = V2_SHIFT_DEFAULT;
+       u32     slope = V2_SLOPE_DEFAULT;
+       u32     czero = V2_CZERO_DEFAULT;
+       char    name[20];
+       u32     val;
+       int     ret;
+
+       /* Read offset value */
+       ret = snprintf(name, sizeof(name), "tsens_sens%d_off", sensor->hw_id);
+       if (ret < 0)
+               return ret;
+
+       ret = nvmem_cell_read_variable_le_u32(dev, name, &sensor->offset);
+       if (ret)
+               return ret;
+
+       /* Based on calib mode, program SHIFT, SLOPE and CZERO */
+       switch (mode) {
+       case TWO_PT_CALIB:
+               slope = (TWO_PT_SHIFTED_GAIN / (base1 - base0));
+
+               czero = (base0 + sensor->offset - ((base1 - base0) / 3));
+
+               break;
+       case ONE_PT_CALIB2:
+               czero = base0 + sensor->offset - ONE_PT_CZERO_CONST;
+
+               slope = ONE_PT_SLOPE;
+
+               break;
+       default:
+               dev_dbg(dev, "calibrationless mode\n");
+       }
+
+       val = FIELD_PREP(CONVERSION_SHIFT_MASK, shift) |
+             FIELD_PREP(CONVERSION_SLOPE_MASK, slope) |
+             FIELD_PREP(CONVERSION_CZERO_MASK, czero);
+
+       regmap_write(map, SENSOR_CONVERSION(sensor->hw_id), val);
+
+       return 0;
+}
+
+static int tsens_v2_calibration(struct tsens_priv *priv)
+{
+       struct device *dev = priv->dev;
+       u32 mode, base0, base1;
+       int i, ret;
+
+       if (priv->num_sensors > MAX_SENSORS)
+               return -EINVAL;
+
+       ret = nvmem_cell_read_variable_le_u32(priv->dev, "mode", &mode);
+       if (ret == -ENOENT)
+               dev_warn(priv->dev, "Calibration data not present in DT\n");
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(priv->dev, "calibration mode is %d\n", mode);
+
+       ret = nvmem_cell_read_variable_le_u32(priv->dev, "base0", &base0);
+       if (ret < 0)
+               return ret;
+
+       ret = nvmem_cell_read_variable_le_u32(priv->dev, "base1", &base1);
+       if (ret < 0)
+               return ret;
+
+       /* Calibrate each sensor */
+       for (i = 0; i < priv->num_sensors; i++) {
+               ret = tsens_v2_calibrate_sensor(dev, &priv->sensor[i], priv->srot_map,
+                                               mode, base0, base1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int __init init_tsens_v2_no_rpm(struct tsens_priv *priv)
+{
+       struct device *dev = priv->dev;
+       int i, ret;
+       u32 val = 0;
+
+       ret = init_common(priv);
+       if (ret < 0)
+               return ret;
+
+       priv->rf[CODE_OR_TEMP] = devm_regmap_field_alloc(dev, priv->srot_map,
+                                                        priv->fields[CODE_OR_TEMP]);
+       if (IS_ERR(priv->rf[CODE_OR_TEMP]))
+               return PTR_ERR(priv->rf[CODE_OR_TEMP]);
+
+       priv->rf[MAIN_MEASURE_PERIOD] = devm_regmap_field_alloc(dev, priv->srot_map,
+                                                               priv->fields[MAIN_MEASURE_PERIOD]);
+       if (IS_ERR(priv->rf[MAIN_MEASURE_PERIOD]))
+               return PTR_ERR(priv->rf[MAIN_MEASURE_PERIOD]);
+
+       regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_ASSERT);
+
+       regmap_field_write(priv->rf[MAIN_MEASURE_PERIOD], MEASURE_PERIOD_2mSEC);
+
+       /* Enable available sensors */
+       for (i = 0; i < priv->num_sensors; i++)
+               val |= 1 << priv->sensor[i].hw_id;
+
+       regmap_field_write(priv->rf[SENSOR_EN], val);
+
+       /* Select temperature format, unit is deci-Celsius */
+       regmap_field_write(priv->rf[CODE_OR_TEMP], RESULT_FORMAT_TEMP);
+
+       regmap_field_write(priv->rf[TSENS_SW_RST], SW_RST_DEASSERT);
+
+       regmap_field_write(priv->rf[TSENS_EN], TSENS_ENABLE);
+
+       return 0;
+}
+
 static const struct tsens_ops ops_generic_v2 = {
        .init           = init_common,
        .get_temp       = get_temp_tsens_valid,
@@ -122,6 +278,28 @@ struct tsens_plat_data data_ipq8074 = {
        .fields = tsens_v2_regfields,
 };
 
+static const struct tsens_ops ops_ipq5332 = {
+       .init           = init_tsens_v2_no_rpm,
+       .get_temp       = get_temp_tsens_valid,
+       .calibrate      = tsens_v2_calibration,
+};
+
+const struct tsens_plat_data data_ipq5332 = {
+       .num_sensors    = 5,
+       .ops            = &ops_ipq5332,
+       .hw_ids         = (unsigned int []){11, 12, 13, 14, 15},
+       .feat           = &ipq5332_feat,
+       .fields         = tsens_v2_regfields,
+};
+
+const struct tsens_plat_data data_ipq5424 = {
+       .num_sensors    = 7,
+       .ops            = &ops_ipq5332,
+       .hw_ids         = (unsigned int []){9, 10, 11, 12, 13, 14, 15},
+       .feat           = &ipq5332_feat,
+       .fields         = tsens_v2_regfields,
+};
+
 /* Kept around for backward compatibility with old msm8996.dtsi */
 struct tsens_plat_data data_8996 = {
        .num_sensors    = 13,
index 3aa3736181aa7bc2673cafaaadd121aefcb9c03b..1f5d4de017d9ae314e1d279e07a700ef4ca36b0f 100644 (file)
@@ -975,7 +975,7 @@ int __init init_common(struct tsens_priv *priv)
        ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
        if (ret)
                goto err_put_device;
-       if (!enabled) {
+       if (!enabled && (tsens_version(priv) != VER_2_X_NO_RPM)) {
                dev_err(dev, "%s: device not enabled\n", __func__);
                ret = -ENODEV;
                goto err_put_device;
@@ -1102,6 +1102,12 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
 
 static const struct of_device_id tsens_table[] = {
        {
+               .compatible = "qcom,ipq5332-tsens",
+               .data = &data_ipq5332,
+       }, {
+               .compatible = "qcom,ipq5424-tsens",
+               .data = &data_ipq5424,
+       }, {
                .compatible = "qcom,ipq8064-tsens",
                .data = &data_8960,
        }, {
index 7b36a0318fa6a078e73ce26dfe7387e4435148b4..336bc868fd7c312b8121a95e5b22d8a7b0ce747c 100644 (file)
@@ -35,6 +35,7 @@ enum tsens_ver {
        VER_0_1,
        VER_1_X,
        VER_2_X,
+       VER_2_X_NO_RPM,
 };
 
 enum tsens_irq_type {
@@ -168,6 +169,7 @@ enum regfield_ids {
        TSENS_SW_RST,
        SENSOR_EN,
        CODE_OR_TEMP,
+       MAIN_MEASURE_PERIOD,
 
        /* ----- TM ------ */
        /* TRDY */
@@ -651,5 +653,6 @@ extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
 
 /* 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;
 
 #endif /* __QCOM_TSENS_H__ */