Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig
index be32e5a..aa9c1d8 100644
--- a/drivers/thermal/qcom/Kconfig
+++ b/drivers/thermal/qcom/Kconfig
@@ -1,6 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
config QCOM_TSENS
tristate "Qualcomm TSENS Temperature Alarm"
- depends on THERMAL
depends on QCOM_QFPROM
depends on ARCH_QCOM || COMPILE_TEST
help
@@ -9,3 +9,14 @@
thermal zone device via the mode file results in disabling the sensor.
Also able to set threshold temperature for both hot and cold and update
when a threshold is reached.
+
+config QCOM_SPMI_TEMP_ALARM
+ tristate "Qualcomm SPMI PMIC Temperature Alarm"
+ depends on OF && SPMI && IIO
+ select REGMAP_SPMI
+ help
+ This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
+ PMIC devices. It shows up in sysfs as a thermal sensor with multiple
+ trip points. The temperature reported by the thermal sensor reflects the
+ real time die temperature if an ADC is present or an estimate of the
+ temperature based upon the over temperature stage value.
diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
index a821929..7c8dc6e 100644
--- a/drivers/thermal/qcom/Makefile
+++ b/drivers/thermal/qcom/Makefile
@@ -1,2 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
-qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o
+
+qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \
+ tsens-8960.o tsens-v2.o tsens-v1.o
+obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
new file mode 100644
index 0000000..bf7bae4
--- /dev/null
+++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+
+#include "../thermal_core.h"
+
+#define QPNP_TM_REG_TYPE 0x04
+#define QPNP_TM_REG_SUBTYPE 0x05
+#define QPNP_TM_REG_STATUS 0x08
+#define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
+#define QPNP_TM_REG_ALARM_CTRL 0x46
+
+#define QPNP_TM_TYPE 0x09
+#define QPNP_TM_SUBTYPE_GEN1 0x08
+#define QPNP_TM_SUBTYPE_GEN2 0x09
+
+#define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
+#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
+#define STATUS_GEN2_STATE_SHIFT 4
+
+#define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6)
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
+
+#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
+
+#define ALARM_CTRL_FORCE_ENABLE BIT(7)
+
+/*
+ * Trip point values based on threshold control
+ * 0 = {105 C, 125 C, 145 C}
+ * 1 = {110 C, 130 C, 150 C}
+ * 2 = {115 C, 135 C, 155 C}
+ * 3 = {120 C, 140 C, 160 C}
+*/
+#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS 2000
+
+#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
+
+#define THRESH_MIN 0
+#define THRESH_MAX 3
+
+/* Stage 2 Threshold Min: 125 C */
+#define STAGE2_THRESHOLD_MIN 125000
+/* Stage 2 Threshold Max: 140 C */
+#define STAGE2_THRESHOLD_MAX 140000
+
+/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
+#define DEFAULT_TEMP 37000
+
+struct qpnp_tm_chip {
+ struct regmap *map;
+ struct device *dev;
+ struct thermal_zone_device *tz_dev;
+ unsigned int subtype;
+ long temp;
+ unsigned int thresh;
+ unsigned int stage;
+ unsigned int prev_stage;
+ unsigned int base;
+ /* protects .thresh, .stage and chip registers */
+ struct mutex lock;
+ bool initialized;
+
+ struct iio_channel *adc;
+};
+
+/* This array maps from GEN2 alarm state to GEN1 alarm stage */
+static const unsigned int alarm_state_map[8] = {0, 1, 1, 2, 2, 3, 3, 3};
+
+static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(chip->map, chip->base + addr, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val;
+ return 0;
+}
+
+static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
+{
+ return regmap_write(chip->map, chip->base + addr, data);
+}
+
+/**
+ * qpnp_tm_get_temp_stage() - return over-temperature stage
+ * @chip: Pointer to the qpnp_tm chip
+ *
+ * Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
+ */
+static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
+{
+ int ret;
+ u8 reg = 0;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®);
+ if (ret < 0)
+ return ret;
+
+ if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
+ ret = reg & STATUS_GEN1_STAGE_MASK;
+ else
+ ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
+
+ return ret;
+}
+
+/*
+ * This function updates the internal temp value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ unsigned int stage, stage_new, stage_old;
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&chip->lock));
+
+ ret = qpnp_tm_get_temp_stage(chip);
+ if (ret < 0)
+ return ret;
+ stage = ret;
+
+ if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
+ stage_new = stage;
+ stage_old = chip->stage;
+ } else {
+ stage_new = alarm_state_map[stage];
+ stage_old = alarm_state_map[chip->stage];
+ }
+
+ if (stage_new > stage_old) {
+ /* increasing stage, use lower bound */
+ chip->temp = (stage_new - 1) * TEMP_STAGE_STEP +
+ chip->thresh * TEMP_THRESH_STEP +
+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ } else if (stage_new < stage_old) {
+ /* decreasing stage, use upper bound */
+ chip->temp = stage_new * TEMP_STAGE_STEP +
+ chip->thresh * TEMP_THRESH_STEP -
+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ }
+
+ chip->stage = stage;
+
+ return 0;
+}
+
+static int qpnp_tm_get_temp(void *data, int *temp)
+{
+ struct qpnp_tm_chip *chip = data;
+ int ret, mili_celsius;
+
+ if (!temp)
+ return -EINVAL;
+
+ if (!chip->initialized) {
+ *temp = DEFAULT_TEMP;
+ return 0;
+ }
+
+ if (!chip->adc) {
+ mutex_lock(&chip->lock);
+ ret = qpnp_tm_update_temp_no_adc(chip);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = iio_read_channel_processed(chip->adc, &mili_celsius);
+ if (ret < 0)
+ return ret;
+
+ chip->temp = mili_celsius;
+ }
+
+ *temp = chip->temp < 0 ? 0 : chip->temp;
+
+ return 0;
+}
+
+static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
+ int temp)
+{
+ u8 reg;
+ bool disable_s2_shutdown = false;
+
+ WARN_ON(!mutex_is_locked(&chip->lock));
+
+ /*
+ * Default: S2 and S3 shutdown enabled, thresholds at
+ * 105C/125C/145C, monitoring at 25Hz
+ */
+ reg = SHUTDOWN_CTRL1_RATE_25HZ;
+
+ if (temp == THERMAL_TEMP_INVALID ||
+ temp < STAGE2_THRESHOLD_MIN) {
+ chip->thresh = THRESH_MIN;
+ goto skip;
+ }
+
+ if (temp <= STAGE2_THRESHOLD_MAX) {
+ chip->thresh = THRESH_MAX -
+ ((STAGE2_THRESHOLD_MAX - temp) /
+ TEMP_THRESH_STEP);
+ disable_s2_shutdown = true;
+ } else {
+ chip->thresh = THRESH_MAX;
+
+ if (chip->adc)
+ 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");
+ }
+
+skip:
+ reg |= chip->thresh;
+ if (disable_s2_shutdown)
+ reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
+
+ return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
+}
+
+static int qpnp_tm_set_trip_temp(void *data, int trip, int temp)
+{
+ struct qpnp_tm_chip *chip = data;
+ const struct thermal_trip *trip_points;
+ int ret;
+
+ trip_points = of_thermal_get_trip_points(chip->tz_dev);
+ if (!trip_points)
+ return -EINVAL;
+
+ if (trip_points[trip].type != THERMAL_TRIP_CRITICAL)
+ return 0;
+
+ mutex_lock(&chip->lock);
+ ret = qpnp_tm_update_critical_trip_temp(chip, temp);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
+ .get_temp = qpnp_tm_get_temp,
+ .set_trip_temp = qpnp_tm_set_trip_temp,
+};
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+
+ thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
+{
+ int ntrips;
+ const struct thermal_trip *trips;
+ int i;
+
+ ntrips = of_thermal_get_ntrips(chip->tz_dev);
+ if (ntrips <= 0)
+ return THERMAL_TEMP_INVALID;
+
+ trips = of_thermal_get_trip_points(chip->tz_dev);
+ if (!trips)
+ return THERMAL_TEMP_INVALID;
+
+ for (i = 0; i < ntrips; i++) {
+ if (of_thermal_is_trip_valid(chip->tz_dev, i) &&
+ trips[i].type == THERMAL_TRIP_CRITICAL)
+ return trips[i].temperature;
+ }
+
+ return THERMAL_TEMP_INVALID;
+}
+
+/*
+ * This function initializes the internal temp value based on only the
+ * current thermal stage and threshold. Setup threshold control and
+ * disable shutdown override.
+ */
+static int qpnp_tm_init(struct qpnp_tm_chip *chip)
+{
+ unsigned int stage;
+ int ret;
+ u8 reg = 0;
+ int crit_temp;
+
+ mutex_lock(&chip->lock);
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®);
+ if (ret < 0)
+ goto out;
+
+ chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ chip->temp = DEFAULT_TEMP;
+
+ ret = qpnp_tm_get_temp_stage(chip);
+ if (ret < 0)
+ goto out;
+ chip->stage = ret;
+
+ stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
+ ? chip->stage : alarm_state_map[chip->stage];
+
+ if (stage)
+ chip->temp = chip->thresh * TEMP_THRESH_STEP +
+ (stage - 1) * TEMP_STAGE_STEP +
+ TEMP_THRESH_MIN;
+
+ crit_temp = qpnp_tm_get_critical_trip_temp(chip);
+ ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
+ if (ret < 0)
+ goto out;
+
+ /* Enable the thermal alarm PMIC module in always-on mode. */
+ reg = ALARM_CTRL_FORCE_ENABLE;
+ ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
+
+ chip->initialized = true;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int qpnp_tm_probe(struct platform_device *pdev)
+{
+ struct qpnp_tm_chip *chip;
+ struct device_node *node;
+ u8 type, subtype;
+ u32 res;
+ int ret, irq;
+
+ node = pdev->dev.of_node;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, chip);
+ chip->dev = &pdev->dev;
+
+ mutex_init(&chip->lock);
+
+ chip->map = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->map)
+ return -ENXIO;
+
+ ret = of_property_read_u32(node, "reg", &res);
+ if (ret < 0)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* ADC based measurements are optional */
+ chip->adc = devm_iio_channel_get(&pdev->dev, "thermal");
+ if (IS_ERR(chip->adc)) {
+ ret = PTR_ERR(chip->adc);
+ chip->adc = NULL;
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ }
+
+ chip->base = res;
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not read type\n");
+ return ret;
+ }
+
+ ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not read subtype\n");
+ return ret;
+ }
+
+ if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
+ && subtype != QPNP_TM_SUBTYPE_GEN2)) {
+ dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
+ type, subtype);
+ return -ENODEV;
+ }
+
+ chip->subtype = subtype;
+
+ /*
+ * Register the sensor before initializing the hardware to be able to
+ * read the trip points. get_temp() returns the default temperature
+ * before the hardware initialization is completed.
+ */
+ chip->tz_dev = devm_thermal_zone_of_sensor_register(
+ &pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
+ if (IS_ERR(chip->tz_dev)) {
+ dev_err(&pdev->dev, "failed to register sensor\n");
+ return PTR_ERR(chip->tz_dev);
+ }
+
+ ret = qpnp_tm_init(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "init failed\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qpnp_tm_isr,
+ IRQF_ONESHOT, node->name, chip);
+ if (ret < 0)
+ return ret;
+
+ thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
+
+ return 0;
+}
+
+static const struct of_device_id qpnp_tm_match_table[] = {
+ { .compatible = "qcom,spmi-temp-alarm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qpnp_tm_match_table);
+
+static struct platform_driver qpnp_tm_driver = {
+ .driver = {
+ .name = "spmi-temp-alarm",
+ .of_match_table = qpnp_tm_match_table,
+ },
+ .probe = qpnp_tm_probe,
+};
+module_platform_driver(qpnp_tm_driver);
+
+MODULE_ALIAS("platform:spmi-temp-alarm");
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c
deleted file mode 100644
index fdf561b..0000000
--- a/drivers/thermal/qcom/tsens-8916.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/platform_device.h>
-#include "tsens.h"
-
-/* eeprom layout data for 8916 */
-#define BASE0_MASK 0x0000007f
-#define BASE1_MASK 0xfe000000
-#define BASE0_SHIFT 0
-#define BASE1_SHIFT 25
-
-#define S0_P1_MASK 0x00000f80
-#define S1_P1_MASK 0x003e0000
-#define S2_P1_MASK 0xf8000000
-#define S3_P1_MASK 0x000003e0
-#define S4_P1_MASK 0x000f8000
-
-#define S0_P2_MASK 0x0001f000
-#define S1_P2_MASK 0x07c00000
-#define S2_P2_MASK 0x0000001f
-#define S3_P2_MASK 0x00007c00
-#define S4_P2_MASK 0x01f00000
-
-#define S0_P1_SHIFT 7
-#define S1_P1_SHIFT 17
-#define S2_P1_SHIFT 27
-#define S3_P1_SHIFT 5
-#define S4_P1_SHIFT 15
-
-#define S0_P2_SHIFT 12
-#define S1_P2_SHIFT 22
-#define S2_P2_SHIFT 0
-#define S3_P2_SHIFT 10
-#define S4_P2_SHIFT 20
-
-#define CAL_SEL_MASK 0xe0000000
-#define CAL_SEL_SHIFT 29
-
-static int calibrate_8916(struct tsens_device *tmdev)
-{
- int base0 = 0, base1 = 0, i;
- u32 p1[5], p2[5];
- int mode = 0;
- u32 *qfprom_cdata, *qfprom_csel;
-
- qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
- if (IS_ERR(qfprom_cdata))
- return PTR_ERR(qfprom_cdata);
-
- qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
- if (IS_ERR(qfprom_csel))
- return PTR_ERR(qfprom_csel);
-
- mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
- dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
-
- switch (mode) {
- case TWO_PT_CALIB:
- base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
- p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
- p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
- p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
- p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
- p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
- for (i = 0; i < tmdev->num_sensors; i++)
- p2[i] = ((base1 + p2[i]) << 3);
- /* Fall through */
- case ONE_PT_CALIB2:
- base0 = (qfprom_cdata[0] & BASE0_MASK);
- p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
- p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
- p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
- p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
- p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
- for (i = 0; i < tmdev->num_sensors; i++)
- p1[i] = (((base0) + p1[i]) << 3);
- break;
- default:
- for (i = 0; i < tmdev->num_sensors; i++) {
- p1[i] = 500;
- p2[i] = 780;
- }
- break;
- }
-
- compute_intercept_slope(tmdev, p1, p2, mode);
-
- return 0;
-}
-
-static const struct tsens_ops ops_8916 = {
- .init = init_common,
- .calibrate = calibrate_8916,
- .get_temp = get_temp_common,
-};
-
-const struct tsens_data data_8916 = {
- .num_sensors = 5,
- .ops = &ops_8916,
- .hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
-};
diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
index 0451277..e46a4e3 100644
--- a/drivers/thermal/qcom/tsens-8960.c
+++ b/drivers/thermal/qcom/tsens-8960.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/platform_device.h>
@@ -65,21 +56,21 @@
#define TRDY_MASK BIT(7)
#define TIMEOUT_US 100
-static int suspend_8960(struct tsens_device *tmdev)
+static int suspend_8960(struct tsens_priv *priv)
{
int ret;
unsigned int mask;
- struct regmap *map = tmdev->map;
+ struct regmap *map = priv->tm_map;
- ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
+ ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
if (ret)
return ret;
- ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
+ ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control);
if (ret)
return ret;
- if (tmdev->num_sensors > 1)
+ if (priv->num_sensors > 1)
mask = SLP_CLK_ENA | EN;
else
mask = SLP_CLK_ENA_8660 | EN;
@@ -91,10 +82,10 @@
return 0;
}
-static int resume_8960(struct tsens_device *tmdev)
+static int resume_8960(struct tsens_priv *priv)
{
int ret;
- struct regmap *map = tmdev->map;
+ struct regmap *map = priv->tm_map;
ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
if (ret)
@@ -104,80 +95,80 @@
* Separate CONFIG restore is not needed only for 8660 as
* config is part of CTRL Addr and its restored as such
*/
- if (tmdev->num_sensors > 1) {
+ if (priv->num_sensors > 1) {
ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
if (ret)
return ret;
}
- ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
+ ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold);
if (ret)
return ret;
- ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
+ ret = regmap_write(map, CNTL_ADDR, priv->ctx.control);
if (ret)
return ret;
return 0;
}
-static int enable_8960(struct tsens_device *tmdev, int id)
+static int enable_8960(struct tsens_priv *priv, int id)
{
int ret;
u32 reg, mask;
- ret = regmap_read(tmdev->map, CNTL_ADDR, ®);
+ ret = regmap_read(priv->tm_map, CNTL_ADDR, ®);
if (ret)
return ret;
mask = BIT(id + SENSOR0_SHIFT);
- ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
if (ret)
return ret;
- if (tmdev->num_sensors > 1)
+ if (priv->num_sensors > 1)
reg |= mask | SLP_CLK_ENA | EN;
else
reg |= mask | SLP_CLK_ENA_8660 | EN;
- ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg);
if (ret)
return ret;
return 0;
}
-static void disable_8960(struct tsens_device *tmdev)
+static void disable_8960(struct tsens_priv *priv)
{
int ret;
u32 reg_cntl;
u32 mask;
- mask = GENMASK(tmdev->num_sensors - 1, 0);
+ mask = GENMASK(priv->num_sensors - 1, 0);
mask <<= SENSOR0_SHIFT;
mask |= EN;
- ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl);
+ ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl);
if (ret)
return;
reg_cntl &= ~mask;
- if (tmdev->num_sensors > 1)
+ if (priv->num_sensors > 1)
reg_cntl &= ~SLP_CLK_ENA;
else
reg_cntl &= ~SLP_CLK_ENA_8660;
- regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+ regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
}
-static int init_8960(struct tsens_device *tmdev)
+static int init_8960(struct tsens_priv *priv)
{
int ret, i;
u32 reg_cntl;
- tmdev->map = dev_get_regmap(tmdev->dev, NULL);
- if (!tmdev->map)
+ priv->tm_map = dev_get_regmap(priv->dev, NULL);
+ if (!priv->tm_map)
return -ENODEV;
/*
@@ -186,21 +177,21 @@
* but the control registers stay in the same place, i.e
* directly after the first 5 status registers.
*/
- for (i = 0; i < tmdev->num_sensors; i++) {
+ for (i = 0; i < priv->num_sensors; i++) {
if (i >= 5)
- tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
- tmdev->sensor[i].status += i * 4;
+ priv->sensor[i].status = S0_STATUS_ADDR + 40;
+ priv->sensor[i].status += i * 4;
}
reg_cntl = SW_RST;
- ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
+ ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
if (ret)
return ret;
- if (tmdev->num_sensors > 1) {
+ if (priv->num_sensors > 1) {
reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
reg_cntl &= ~SW_RST;
- ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
+ ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR,
CONFIG_MASK, CONFIG);
} else {
reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
@@ -208,36 +199,38 @@
reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
}
- reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
- ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+ reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
reg_cntl |= EN;
- ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+ ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
if (ret)
return ret;
return 0;
}
-static int calibrate_8960(struct tsens_device *tmdev)
+static int calibrate_8960(struct tsens_priv *priv)
{
int i;
char *data;
- ssize_t num_read = tmdev->num_sensors;
- struct tsens_sensor *s = tmdev->sensor;
+ ssize_t num_read = priv->num_sensors;
+ struct tsens_sensor *s = priv->sensor;
- data = qfprom_read(tmdev->dev, "calib");
+ data = qfprom_read(priv->dev, "calib");
if (IS_ERR(data))
- data = qfprom_read(tmdev->dev, "calib_backup");
+ data = qfprom_read(priv->dev, "calib_backup");
if (IS_ERR(data))
return PTR_ERR(data);
for (i = 0; i < num_read; i++, s++)
s->offset = data[i];
+ kfree(data);
+
return 0;
}
@@ -252,21 +245,21 @@
return adc_code * slope + offset;
}
-static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
+static int get_temp_8960(struct tsens_priv *priv, int id, int *temp)
{
int ret;
u32 code, trdy;
- const struct tsens_sensor *s = &tmdev->sensor[id];
+ const struct tsens_sensor *s = &priv->sensor[id];
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
do {
- ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
+ ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy);
if (ret)
return ret;
if (!(trdy & TRDY_MASK))
continue;
- ret = regmap_read(tmdev->map, s->status, &code);
+ ret = regmap_read(priv->tm_map, s->status, &code);
if (ret)
return ret;
*temp = code_to_mdegC(code, s);
@@ -286,7 +279,7 @@
.resume = resume_8960,
};
-const struct tsens_data data_8960 = {
+const struct tsens_plat_data data_8960 = {
.num_sensors = 11,
.ops = &ops_8960,
};
diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-8974.c
deleted file mode 100644
index 9baf77e..0000000
--- a/drivers/thermal/qcom/tsens-8974.c
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/platform_device.h>
-#include "tsens.h"
-
-/* eeprom layout data for 8974 */
-#define BASE1_MASK 0xff
-#define S0_P1_MASK 0x3f00
-#define S1_P1_MASK 0xfc000
-#define S2_P1_MASK 0x3f00000
-#define S3_P1_MASK 0xfc000000
-#define S4_P1_MASK 0x3f
-#define S5_P1_MASK 0xfc0
-#define S6_P1_MASK 0x3f000
-#define S7_P1_MASK 0xfc0000
-#define S8_P1_MASK 0x3f000000
-#define S8_P1_MASK_BKP 0x3f
-#define S9_P1_MASK 0x3f
-#define S9_P1_MASK_BKP 0xfc0
-#define S10_P1_MASK 0xfc0
-#define S10_P1_MASK_BKP 0x3f000
-#define CAL_SEL_0_1 0xc0000000
-#define CAL_SEL_2 0x40000000
-#define CAL_SEL_SHIFT 30
-#define CAL_SEL_SHIFT_2 28
-
-#define S0_P1_SHIFT 8
-#define S1_P1_SHIFT 14
-#define S2_P1_SHIFT 20
-#define S3_P1_SHIFT 26
-#define S5_P1_SHIFT 6
-#define S6_P1_SHIFT 12
-#define S7_P1_SHIFT 18
-#define S8_P1_SHIFT 24
-#define S9_P1_BKP_SHIFT 6
-#define S10_P1_SHIFT 6
-#define S10_P1_BKP_SHIFT 12
-
-#define BASE2_SHIFT 12
-#define BASE2_BKP_SHIFT 18
-#define S0_P2_SHIFT 20
-#define S0_P2_BKP_SHIFT 26
-#define S1_P2_SHIFT 26
-#define S2_P2_BKP_SHIFT 6
-#define S3_P2_SHIFT 6
-#define S3_P2_BKP_SHIFT 12
-#define S4_P2_SHIFT 12
-#define S4_P2_BKP_SHIFT 18
-#define S5_P2_SHIFT 18
-#define S5_P2_BKP_SHIFT 24
-#define S6_P2_SHIFT 24
-#define S7_P2_BKP_SHIFT 6
-#define S8_P2_SHIFT 6
-#define S8_P2_BKP_SHIFT 12
-#define S9_P2_SHIFT 12
-#define S9_P2_BKP_SHIFT 18
-#define S10_P2_SHIFT 18
-#define S10_P2_BKP_SHIFT 24
-
-#define BASE2_MASK 0xff000
-#define BASE2_BKP_MASK 0xfc0000
-#define S0_P2_MASK 0x3f00000
-#define S0_P2_BKP_MASK 0xfc000000
-#define S1_P2_MASK 0xfc000000
-#define S1_P2_BKP_MASK 0x3f
-#define S2_P2_MASK 0x3f
-#define S2_P2_BKP_MASK 0xfc0
-#define S3_P2_MASK 0xfc0
-#define S3_P2_BKP_MASK 0x3f000
-#define S4_P2_MASK 0x3f000
-#define S4_P2_BKP_MASK 0xfc0000
-#define S5_P2_MASK 0xfc0000
-#define S5_P2_BKP_MASK 0x3f000000
-#define S6_P2_MASK 0x3f000000
-#define S6_P2_BKP_MASK 0x3f
-#define S7_P2_MASK 0x3f
-#define S7_P2_BKP_MASK 0xfc0
-#define S8_P2_MASK 0xfc0
-#define S8_P2_BKP_MASK 0x3f000
-#define S9_P2_MASK 0x3f000
-#define S9_P2_BKP_MASK 0xfc0000
-#define S10_P2_MASK 0xfc0000
-#define S10_P2_BKP_MASK 0x3f000000
-
-#define BKP_SEL 0x3
-#define BKP_REDUN_SEL 0xe0000000
-#define BKP_REDUN_SHIFT 29
-
-#define BIT_APPEND 0x3
-
-static int calibrate_8974(struct tsens_device *tmdev)
-{
- int base1 = 0, base2 = 0, i;
- u32 p1[11], p2[11];
- int mode = 0;
- u32 *calib, *bkp;
- u32 calib_redun_sel;
-
- calib = (u32 *)qfprom_read(tmdev->dev, "calib");
- if (IS_ERR(calib))
- return PTR_ERR(calib);
-
- bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
- if (IS_ERR(bkp))
- return PTR_ERR(bkp);
-
- calib_redun_sel = bkp[1] & BKP_REDUN_SEL;
- calib_redun_sel >>= BKP_REDUN_SHIFT;
-
- if (calib_redun_sel == BKP_SEL) {
- mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
- mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
-
- switch (mode) {
- case TWO_PT_CALIB:
- base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
- p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
- p2[1] = (bkp[3] & S1_P2_BKP_MASK);
- p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
- p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
- p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
- p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
- p2[6] = (calib[5] & S6_P2_BKP_MASK);
- p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
- p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
- p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
- p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
- /* Fall through */
- case ONE_PT_CALIB:
- case ONE_PT_CALIB2:
- base1 = bkp[0] & BASE1_MASK;
- p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
- p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
- p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
- p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
- p1[4] = (bkp[1] & S4_P1_MASK);
- p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
- p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
- p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
- p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
- p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
- p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
- break;
- }
- } else {
- mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
- mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
-
- switch (mode) {
- case TWO_PT_CALIB:
- base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
- p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
- p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
- p2[2] = (calib[3] & S2_P2_MASK);
- p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
- p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
- p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
- p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
- p2[7] = (calib[4] & S7_P2_MASK);
- p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
- p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
- p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
- /* Fall through */
- case ONE_PT_CALIB:
- case ONE_PT_CALIB2:
- base1 = calib[0] & BASE1_MASK;
- p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
- p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
- p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
- p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
- p1[4] = (calib[1] & S4_P1_MASK);
- p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
- p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
- p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
- p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
- p1[9] = (calib[2] & S9_P1_MASK);
- p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
- break;
- }
- }
-
- switch (mode) {
- case ONE_PT_CALIB:
- for (i = 0; i < tmdev->num_sensors; i++)
- p1[i] += (base1 << 2) | BIT_APPEND;
- break;
- case TWO_PT_CALIB:
- for (i = 0; i < tmdev->num_sensors; i++) {
- p2[i] += base2;
- p2[i] <<= 2;
- p2[i] |= BIT_APPEND;
- }
- /* Fall through */
- case ONE_PT_CALIB2:
- for (i = 0; i < tmdev->num_sensors; i++) {
- p1[i] += base1;
- p1[i] <<= 2;
- p1[i] |= BIT_APPEND;
- }
- break;
- default:
- for (i = 0; i < tmdev->num_sensors; i++)
- p2[i] = 780;
- p1[0] = 502;
- p1[1] = 509;
- p1[2] = 503;
- p1[3] = 509;
- p1[4] = 505;
- p1[5] = 509;
- p1[6] = 507;
- p1[7] = 510;
- p1[8] = 508;
- p1[9] = 509;
- p1[10] = 508;
- break;
- }
-
- compute_intercept_slope(tmdev, p1, p2, mode);
-
- return 0;
-}
-
-static const struct tsens_ops ops_8974 = {
- .init = init_common,
- .calibrate = calibrate_8974,
- .get_temp = get_temp_common,
-};
-
-const struct tsens_data data_8974 = {
- .num_sensors = 11,
- .ops = &ops_8974,
-};
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
index 6207d8d..528df88 100644
--- a/drivers/thermal/qcom/tsens-common.c
+++ b/drivers/thermal/qcom/tsens-common.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/err.h>
@@ -21,14 +12,6 @@
#include <linux/regmap.h>
#include "tsens.h"
-#define S0_ST_ADDR 0x1030
-#define SN_ADDR_OFFSET 0x4
-#define SN_ST_TEMP_MASK 0x3ff
-#define CAL_DEGC_PT1 30
-#define CAL_DEGC_PT2 120
-#define SLOPE_FACTOR 1000
-#define SLOPE_DEFAULT 3200
-
char *qfprom_read(struct device *dev, const char *cname)
{
struct nvmem_cell *cell;
@@ -51,18 +34,18 @@
* and offset values are derived from tz->tzp->slope and tz->tzp->offset
* resp.
*/
-void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
+void compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
u32 *p2, u32 mode)
{
int i;
int num, den;
- for (i = 0; i < tmdev->num_sensors; i++) {
- dev_dbg(tmdev->dev,
+ for (i = 0; i < priv->num_sensors; i++) {
+ dev_dbg(priv->dev,
"sensor%d - data_point1:%#x data_point2:%#x\n",
i, p1[i], p2[i]);
- tmdev->sensor[i].slope = SLOPE_DEFAULT;
+ priv->sensor[i].slope = SLOPE_DEFAULT;
if (mode == TWO_PT_CALIB) {
/*
* slope (m) = adc_code2 - adc_code1 (y2 - y1)/
@@ -71,13 +54,13 @@
num = p2[i] - p1[i];
num *= SLOPE_FACTOR;
den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
- tmdev->sensor[i].slope = num / den;
+ priv->sensor[i].slope = num / den;
}
- tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+ priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
(CAL_DEGC_PT1 *
- tmdev->sensor[i].slope);
- dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
+ priv->sensor[i].slope);
+ dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset);
}
}
@@ -100,18 +83,54 @@
return degc;
}
-int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
+int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp)
{
- struct tsens_sensor *s = &tmdev->sensor[id];
- u32 code;
- unsigned int status_reg;
- int last_temp = 0, ret;
+ struct tsens_sensor *s = &priv->sensor[i];
+ u32 temp_idx = LAST_TEMP_0 + s->hw_id;
+ u32 valid_idx = VALID_0 + s->hw_id;
+ u32 last_temp = 0, valid, mask;
+ int ret;
- status_reg = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
- ret = regmap_read(tmdev->map, status_reg, &code);
+ ret = regmap_field_read(priv->rf[valid_idx], &valid);
if (ret)
return ret;
- last_temp = code & SN_ST_TEMP_MASK;
+ while (!valid) {
+ /* Valid bit is 0 for 6 AHB clock cycles.
+ * At 19.2MHz, 1 AHB clock is ~60ns.
+ * We should enter this loop very, very rarely.
+ */
+ ndelay(400);
+ ret = regmap_field_read(priv->rf[valid_idx], &valid);
+ if (ret)
+ return ret;
+ }
+
+ /* Valid bit is set, OK to read the temperature */
+ ret = regmap_field_read(priv->rf[temp_idx], &last_temp);
+ if (ret)
+ return ret;
+
+ if (priv->feat->adc) {
+ /* Convert temperature from ADC code to milliCelsius */
+ *temp = code_to_degc(last_temp, s) * 1000;
+ } else {
+ mask = GENMASK(priv->fields[LAST_TEMP_0].msb,
+ priv->fields[LAST_TEMP_0].lsb);
+ /* Convert temperature from deciCelsius to milliCelsius */
+ *temp = sign_extend32(last_temp, fls(mask) - 1) * 100;
+ }
+
+ return 0;
+}
+
+int get_temp_common(struct tsens_priv *priv, int i, int *temp)
+{
+ struct tsens_sensor *s = &priv->sensor[i];
+ int last_temp = 0, ret;
+
+ ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp);
+ if (ret)
+ return ret;
*temp = code_to_degc(last_temp, s) * 1000;
@@ -119,36 +138,107 @@
}
static const struct regmap_config tsens_config = {
+ .name = "tm",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
-int __init init_common(struct tsens_device *tmdev)
+static const struct regmap_config tsens_srot_config = {
+ .name = "srot",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+int __init init_common(struct tsens_priv *priv)
{
- void __iomem *base;
+ void __iomem *tm_base, *srot_base;
+ struct device *dev = priv->dev;
struct resource *res;
- struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node);
+ u32 enabled;
+ int ret, i, j;
+ struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
if (!op)
return -EINVAL;
- /* The driver only uses the TM register address space for now */
if (op->num_resources > 1) {
- tmdev->tm_offset = 0;
+ /* DT with separate SROT and TM address space */
+ priv->tm_offset = 0;
+ res = platform_get_resource(op, IORESOURCE_MEM, 1);
+ srot_base = devm_ioremap_resource(&op->dev, res);
+ if (IS_ERR(srot_base)) {
+ ret = PTR_ERR(srot_base);
+ goto err_put_device;
+ }
+
+ priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
+ &tsens_srot_config);
+ if (IS_ERR(priv->srot_map)) {
+ ret = PTR_ERR(priv->srot_map);
+ goto err_put_device;
+ }
} else {
/* old DTs where SROT and TM were in a contiguous 2K block */
- tmdev->tm_offset = 0x1000;
+ priv->tm_offset = 0x1000;
}
res = platform_get_resource(op, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&op->dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ tm_base = devm_ioremap_resource(&op->dev, res);
+ if (IS_ERR(tm_base)) {
+ ret = PTR_ERR(tm_base);
+ goto err_put_device;
+ }
- tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
- if (IS_ERR(tmdev->map))
- return PTR_ERR(tmdev->map);
+ priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
+ if (IS_ERR(priv->tm_map)) {
+ ret = PTR_ERR(priv->tm_map);
+ goto err_put_device;
+ }
+
+ priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[TSENS_EN]);
+ if (IS_ERR(priv->rf[TSENS_EN])) {
+ ret = PTR_ERR(priv->rf[TSENS_EN]);
+ goto err_put_device;
+ }
+ ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
+ if (ret)
+ goto err_put_device;
+ if (!enabled) {
+ dev_err(dev, "tsens device is not enabled\n");
+ ret = -ENODEV;
+ goto err_put_device;
+ }
+
+ priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
+ priv->fields[SENSOR_EN]);
+ if (IS_ERR(priv->rf[SENSOR_EN])) {
+ ret = PTR_ERR(priv->rf[SENSOR_EN]);
+ goto err_put_device;
+ }
+ /* now alloc regmap_fields in tm_map */
+ for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) {
+ priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
+ priv->fields[j]);
+ if (IS_ERR(priv->rf[j])) {
+ ret = PTR_ERR(priv->rf[j]);
+ goto err_put_device;
+ }
+ }
+ for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) {
+ priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map,
+ priv->fields[j]);
+ if (IS_ERR(priv->rf[j])) {
+ ret = PTR_ERR(priv->rf[j]);
+ goto err_put_device;
+ }
+ }
return 0;
+
+err_put_device:
+ put_device(&op->dev);
+ return ret;
}
diff --git a/drivers/thermal/qcom/tsens-v0_1.c b/drivers/thermal/qcom/tsens-v0_1.c
new file mode 100644
index 0000000..055647b
--- /dev/null
+++ b/drivers/thermal/qcom/tsens-v0_1.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* ----- SROT ------ */
+#define SROT_CTRL_OFF 0x0000
+
+/* ----- TM ------ */
+#define TM_INT_EN_OFF 0x0000
+#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
+#define TM_Sn_STATUS_OFF 0x0030
+#define TM_TRDY_OFF 0x005c
+
+/* eeprom layout data for 8916 */
+#define MSM8916_BASE0_MASK 0x0000007f
+#define MSM8916_BASE1_MASK 0xfe000000
+#define MSM8916_BASE0_SHIFT 0
+#define MSM8916_BASE1_SHIFT 25
+
+#define MSM8916_S0_P1_MASK 0x00000f80
+#define MSM8916_S1_P1_MASK 0x003e0000
+#define MSM8916_S2_P1_MASK 0xf8000000
+#define MSM8916_S3_P1_MASK 0x000003e0
+#define MSM8916_S4_P1_MASK 0x000f8000
+
+#define MSM8916_S0_P2_MASK 0x0001f000
+#define MSM8916_S1_P2_MASK 0x07c00000
+#define MSM8916_S2_P2_MASK 0x0000001f
+#define MSM8916_S3_P2_MASK 0x00007c00
+#define MSM8916_S4_P2_MASK 0x01f00000
+
+#define MSM8916_S0_P1_SHIFT 7
+#define MSM8916_S1_P1_SHIFT 17
+#define MSM8916_S2_P1_SHIFT 27
+#define MSM8916_S3_P1_SHIFT 5
+#define MSM8916_S4_P1_SHIFT 15
+
+#define MSM8916_S0_P2_SHIFT 12
+#define MSM8916_S1_P2_SHIFT 22
+#define MSM8916_S2_P2_SHIFT 0
+#define MSM8916_S3_P2_SHIFT 10
+#define MSM8916_S4_P2_SHIFT 20
+
+#define MSM8916_CAL_SEL_MASK 0xe0000000
+#define MSM8916_CAL_SEL_SHIFT 29
+
+/* eeprom layout data for 8974 */
+#define BASE1_MASK 0xff
+#define S0_P1_MASK 0x3f00
+#define S1_P1_MASK 0xfc000
+#define S2_P1_MASK 0x3f00000
+#define S3_P1_MASK 0xfc000000
+#define S4_P1_MASK 0x3f
+#define S5_P1_MASK 0xfc0
+#define S6_P1_MASK 0x3f000
+#define S7_P1_MASK 0xfc0000
+#define S8_P1_MASK 0x3f000000
+#define S8_P1_MASK_BKP 0x3f
+#define S9_P1_MASK 0x3f
+#define S9_P1_MASK_BKP 0xfc0
+#define S10_P1_MASK 0xfc0
+#define S10_P1_MASK_BKP 0x3f000
+#define CAL_SEL_0_1 0xc0000000
+#define CAL_SEL_2 0x40000000
+#define CAL_SEL_SHIFT 30
+#define CAL_SEL_SHIFT_2 28
+
+#define S0_P1_SHIFT 8
+#define S1_P1_SHIFT 14
+#define S2_P1_SHIFT 20
+#define S3_P1_SHIFT 26
+#define S5_P1_SHIFT 6
+#define S6_P1_SHIFT 12
+#define S7_P1_SHIFT 18
+#define S8_P1_SHIFT 24
+#define S9_P1_BKP_SHIFT 6
+#define S10_P1_SHIFT 6
+#define S10_P1_BKP_SHIFT 12
+
+#define BASE2_SHIFT 12
+#define BASE2_BKP_SHIFT 18
+#define S0_P2_SHIFT 20
+#define S0_P2_BKP_SHIFT 26
+#define S1_P2_SHIFT 26
+#define S2_P2_BKP_SHIFT 6
+#define S3_P2_SHIFT 6
+#define S3_P2_BKP_SHIFT 12
+#define S4_P2_SHIFT 12
+#define S4_P2_BKP_SHIFT 18
+#define S5_P2_SHIFT 18
+#define S5_P2_BKP_SHIFT 24
+#define S6_P2_SHIFT 24
+#define S7_P2_BKP_SHIFT 6
+#define S8_P2_SHIFT 6
+#define S8_P2_BKP_SHIFT 12
+#define S9_P2_SHIFT 12
+#define S9_P2_BKP_SHIFT 18
+#define S10_P2_SHIFT 18
+#define S10_P2_BKP_SHIFT 24
+
+#define BASE2_MASK 0xff000
+#define BASE2_BKP_MASK 0xfc0000
+#define S0_P2_MASK 0x3f00000
+#define S0_P2_BKP_MASK 0xfc000000
+#define S1_P2_MASK 0xfc000000
+#define S1_P2_BKP_MASK 0x3f
+#define S2_P2_MASK 0x3f
+#define S2_P2_BKP_MASK 0xfc0
+#define S3_P2_MASK 0xfc0
+#define S3_P2_BKP_MASK 0x3f000
+#define S4_P2_MASK 0x3f000
+#define S4_P2_BKP_MASK 0xfc0000
+#define S5_P2_MASK 0xfc0000
+#define S5_P2_BKP_MASK 0x3f000000
+#define S6_P2_MASK 0x3f000000
+#define S6_P2_BKP_MASK 0x3f
+#define S7_P2_MASK 0x3f
+#define S7_P2_BKP_MASK 0xfc0
+#define S8_P2_MASK 0xfc0
+#define S8_P2_BKP_MASK 0x3f000
+#define S9_P2_MASK 0x3f000
+#define S9_P2_BKP_MASK 0xfc0000
+#define S10_P2_MASK 0xfc0000
+#define S10_P2_BKP_MASK 0x3f000000
+
+#define BKP_SEL 0x3
+#define BKP_REDUN_SEL 0xe0000000
+#define BKP_REDUN_SHIFT 29
+
+#define BIT_APPEND 0x3
+
+static int calibrate_8916(struct tsens_priv *priv)
+{
+ int base0 = 0, base1 = 0, i;
+ u32 p1[5], p2[5];
+ int mode = 0;
+ u32 *qfprom_cdata, *qfprom_csel;
+
+ qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
+ if (IS_ERR(qfprom_cdata))
+ return PTR_ERR(qfprom_cdata);
+
+ qfprom_csel = (u32 *)qfprom_read(priv->dev, "calib_sel");
+ if (IS_ERR(qfprom_csel)) {
+ kfree(qfprom_cdata);
+ return PTR_ERR(qfprom_csel);
+ }
+
+ mode = (qfprom_csel[0] & MSM8916_CAL_SEL_MASK) >> MSM8916_CAL_SEL_SHIFT;
+ dev_dbg(priv->dev, "calibration mode is %d\n", mode);
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base1 = (qfprom_cdata[1] & MSM8916_BASE1_MASK) >> MSM8916_BASE1_SHIFT;
+ p2[0] = (qfprom_cdata[0] & MSM8916_S0_P2_MASK) >> MSM8916_S0_P2_SHIFT;
+ p2[1] = (qfprom_cdata[0] & MSM8916_S1_P2_MASK) >> MSM8916_S1_P2_SHIFT;
+ p2[2] = (qfprom_cdata[1] & MSM8916_S2_P2_MASK) >> MSM8916_S2_P2_SHIFT;
+ p2[3] = (qfprom_cdata[1] & MSM8916_S3_P2_MASK) >> MSM8916_S3_P2_SHIFT;
+ p2[4] = (qfprom_cdata[1] & MSM8916_S4_P2_MASK) >> MSM8916_S4_P2_SHIFT;
+ for (i = 0; i < priv->num_sensors; i++)
+ p2[i] = ((base1 + p2[i]) << 3);
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ base0 = (qfprom_cdata[0] & MSM8916_BASE0_MASK);
+ p1[0] = (qfprom_cdata[0] & MSM8916_S0_P1_MASK) >> MSM8916_S0_P1_SHIFT;
+ p1[1] = (qfprom_cdata[0] & MSM8916_S1_P1_MASK) >> MSM8916_S1_P1_SHIFT;
+ p1[2] = (qfprom_cdata[0] & MSM8916_S2_P1_MASK) >> MSM8916_S2_P1_SHIFT;
+ p1[3] = (qfprom_cdata[1] & MSM8916_S3_P1_MASK) >> MSM8916_S3_P1_SHIFT;
+ p1[4] = (qfprom_cdata[1] & MSM8916_S4_P1_MASK) >> MSM8916_S4_P1_SHIFT;
+ for (i = 0; i < priv->num_sensors; i++)
+ p1[i] = (((base0) + p1[i]) << 3);
+ break;
+ default:
+ for (i = 0; i < priv->num_sensors; i++) {
+ p1[i] = 500;
+ p2[i] = 780;
+ }
+ break;
+ }
+
+ compute_intercept_slope(priv, p1, p2, mode);
+ kfree(qfprom_cdata);
+ kfree(qfprom_csel);
+
+ return 0;
+}
+
+static int calibrate_8974(struct tsens_priv *priv)
+{
+ int base1 = 0, base2 = 0, i;
+ u32 p1[11], p2[11];
+ int mode = 0;
+ u32 *calib, *bkp;
+ u32 calib_redun_sel;
+
+ calib = (u32 *)qfprom_read(priv->dev, "calib");
+ if (IS_ERR(calib))
+ return PTR_ERR(calib);
+
+ bkp = (u32 *)qfprom_read(priv->dev, "calib_backup");
+ if (IS_ERR(bkp)) {
+ kfree(calib);
+ return PTR_ERR(bkp);
+ }
+
+ calib_redun_sel = bkp[1] & BKP_REDUN_SEL;
+ calib_redun_sel >>= BKP_REDUN_SHIFT;
+
+ if (calib_redun_sel == BKP_SEL) {
+ mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+ mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
+ p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
+ p2[1] = (bkp[3] & S1_P2_BKP_MASK);
+ p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
+ p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
+ p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
+ p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
+ p2[6] = (calib[5] & S6_P2_BKP_MASK);
+ p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
+ p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
+ p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
+ p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
+ /* Fall through */
+ case ONE_PT_CALIB:
+ case ONE_PT_CALIB2:
+ base1 = bkp[0] & BASE1_MASK;
+ p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+ p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+ p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+ p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+ p1[4] = (bkp[1] & S4_P1_MASK);
+ p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+ p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+ p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+ p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
+ p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
+ p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
+ break;
+ }
+ } else {
+ mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+ mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
+ p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
+ p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
+ p2[2] = (calib[3] & S2_P2_MASK);
+ p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
+ p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
+ p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
+ p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
+ p2[7] = (calib[4] & S7_P2_MASK);
+ p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
+ p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
+ p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
+ /* Fall through */
+ case ONE_PT_CALIB:
+ case ONE_PT_CALIB2:
+ base1 = calib[0] & BASE1_MASK;
+ p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+ p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+ p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+ p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+ p1[4] = (calib[1] & S4_P1_MASK);
+ p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+ p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+ p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+ p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
+ p1[9] = (calib[2] & S9_P1_MASK);
+ p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
+ break;
+ }
+ }
+
+ switch (mode) {
+ case ONE_PT_CALIB:
+ for (i = 0; i < priv->num_sensors; i++)
+ p1[i] += (base1 << 2) | BIT_APPEND;
+ break;
+ case TWO_PT_CALIB:
+ for (i = 0; i < priv->num_sensors; i++) {
+ p2[i] += base2;
+ p2[i] <<= 2;
+ p2[i] |= BIT_APPEND;
+ }
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ for (i = 0; i < priv->num_sensors; i++) {
+ p1[i] += base1;
+ p1[i] <<= 2;
+ p1[i] |= BIT_APPEND;
+ }
+ break;
+ default:
+ for (i = 0; i < priv->num_sensors; i++)
+ p2[i] = 780;
+ p1[0] = 502;
+ p1[1] = 509;
+ p1[2] = 503;
+ p1[3] = 509;
+ p1[4] = 505;
+ p1[5] = 509;
+ p1[6] = 507;
+ p1[7] = 510;
+ p1[8] = 508;
+ p1[9] = 509;
+ p1[10] = 508;
+ break;
+ }
+
+ compute_intercept_slope(priv, p1, p2, mode);
+ kfree(calib);
+ kfree(bkp);
+
+ return 0;
+}
+
+/* v0.1: 8916, 8974 */
+
+static const struct tsens_features tsens_v0_1_feat = {
+ .ver_major = VER_0_1,
+ .crit_int = 0,
+ .adc = 1,
+ .srot_split = 1,
+ .max_sensors = 11,
+};
+
+static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = {
+ /* ----- SROT ------ */
+ /* No VERSION information */
+
+ /* CTRL_OFFSET */
+ [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
+ [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
+
+ /* ----- TM ------ */
+ /* INTERRUPT ENABLE */
+ [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
+
+ /* Sn_STATUS */
+ REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
+ /* No VALID field on v0.1 */
+ REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10),
+ REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11),
+ REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12),
+ /* No CRITICAL field on v0.1 */
+ REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13),
+
+ /* TRDY: 1=ready, 0=in progress */
+ [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
+};
+
+static const struct tsens_ops ops_8916 = {
+ .init = init_common,
+ .calibrate = calibrate_8916,
+ .get_temp = get_temp_common,
+};
+
+const struct tsens_plat_data data_8916 = {
+ .num_sensors = 5,
+ .ops = &ops_8916,
+ .hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
+
+ .feat = &tsens_v0_1_feat,
+ .fields = tsens_v0_1_regfields,
+};
+
+static const struct tsens_ops ops_8974 = {
+ .init = init_common,
+ .calibrate = calibrate_8974,
+ .get_temp = get_temp_common,
+};
+
+const struct tsens_plat_data data_8974 = {
+ .num_sensors = 11,
+ .ops = &ops_8974,
+ .feat = &tsens_v0_1_feat,
+ .fields = tsens_v0_1_regfields,
+};
diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c
new file mode 100644
index 0000000..870f502
--- /dev/null
+++ b/drivers/thermal/qcom/tsens-v1.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include "tsens.h"
+
+/* ----- SROT ------ */
+#define SROT_HW_VER_OFF 0x0000
+#define SROT_CTRL_OFF 0x0004
+
+/* ----- TM ------ */
+#define TM_INT_EN_OFF 0x0000
+#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004
+#define TM_Sn_STATUS_OFF 0x0044
+#define TM_TRDY_OFF 0x0084
+
+/* eeprom layout data for qcs404/405 (v1) */
+#define BASE0_MASK 0x000007f8
+#define BASE1_MASK 0x0007f800
+#define BASE0_SHIFT 3
+#define BASE1_SHIFT 11
+
+#define S0_P1_MASK 0x0000003f
+#define S1_P1_MASK 0x0003f000
+#define S2_P1_MASK 0x3f000000
+#define S3_P1_MASK 0x000003f0
+#define S4_P1_MASK 0x003f0000
+#define S5_P1_MASK 0x0000003f
+#define S6_P1_MASK 0x0003f000
+#define S7_P1_MASK 0x3f000000
+#define S8_P1_MASK 0x000003f0
+#define S9_P1_MASK 0x003f0000
+
+#define S0_P2_MASK 0x00000fc0
+#define S1_P2_MASK 0x00fc0000
+#define S2_P2_MASK_1_0 0xc0000000
+#define S2_P2_MASK_5_2 0x0000000f
+#define S3_P2_MASK 0x0000fc00
+#define S4_P2_MASK 0x0fc00000
+#define S5_P2_MASK 0x00000fc0
+#define S6_P2_MASK 0x00fc0000
+#define S7_P2_MASK_1_0 0xc0000000
+#define S7_P2_MASK_5_2 0x0000000f
+#define S8_P2_MASK 0x0000fc00
+#define S9_P2_MASK 0x0fc00000
+
+#define S0_P1_SHIFT 0
+#define S0_P2_SHIFT 6
+#define S1_P1_SHIFT 12
+#define S1_P2_SHIFT 18
+#define S2_P1_SHIFT 24
+#define S2_P2_SHIFT_1_0 30
+
+#define S2_P2_SHIFT_5_2 0
+#define S3_P1_SHIFT 4
+#define S3_P2_SHIFT 10
+#define S4_P1_SHIFT 16
+#define S4_P2_SHIFT 22
+
+#define S5_P1_SHIFT 0
+#define S5_P2_SHIFT 6
+#define S6_P1_SHIFT 12
+#define S6_P2_SHIFT 18
+#define S7_P1_SHIFT 24
+#define S7_P2_SHIFT_1_0 30
+
+#define S7_P2_SHIFT_5_2 0
+#define S8_P1_SHIFT 4
+#define S8_P2_SHIFT 10
+#define S9_P1_SHIFT 16
+#define S9_P2_SHIFT 22
+
+#define CAL_SEL_MASK 7
+#define CAL_SEL_SHIFT 0
+
+static int calibrate_v1(struct tsens_priv *priv)
+{
+ u32 base0 = 0, base1 = 0;
+ u32 p1[10], p2[10];
+ u32 mode = 0, lsb = 0, msb = 0;
+ u32 *qfprom_cdata;
+ int i;
+
+ qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
+ if (IS_ERR(qfprom_cdata))
+ return PTR_ERR(qfprom_cdata);
+
+ mode = (qfprom_cdata[4] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
+ dev_dbg(priv->dev, "calibration mode is %d\n", mode);
+
+ switch (mode) {
+ case TWO_PT_CALIB:
+ base1 = (qfprom_cdata[4] & BASE1_MASK) >> BASE1_SHIFT;
+ p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
+ p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
+ /* This value is split over two registers, 2 bits and 4 bits */
+ lsb = (qfprom_cdata[0] & S2_P2_MASK_1_0) >> S2_P2_SHIFT_1_0;
+ msb = (qfprom_cdata[1] & S2_P2_MASK_5_2) >> S2_P2_SHIFT_5_2;
+ p2[2] = msb << 2 | lsb;
+ p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
+ p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
+ p2[5] = (qfprom_cdata[2] & S5_P2_MASK) >> S5_P2_SHIFT;
+ p2[6] = (qfprom_cdata[2] & S6_P2_MASK) >> S6_P2_SHIFT;
+ /* This value is split over two registers, 2 bits and 4 bits */
+ lsb = (qfprom_cdata[2] & S7_P2_MASK_1_0) >> S7_P2_SHIFT_1_0;
+ msb = (qfprom_cdata[3] & S7_P2_MASK_5_2) >> S7_P2_SHIFT_5_2;
+ p2[7] = msb << 2 | lsb;
+ p2[8] = (qfprom_cdata[3] & S8_P2_MASK) >> S8_P2_SHIFT;
+ p2[9] = (qfprom_cdata[3] & S9_P2_MASK) >> S9_P2_SHIFT;
+ for (i = 0; i < priv->num_sensors; i++)
+ p2[i] = ((base1 + p2[i]) << 2);
+ /* Fall through */
+ case ONE_PT_CALIB2:
+ base0 = (qfprom_cdata[4] & BASE0_MASK) >> BASE0_SHIFT;
+ p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+ p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+ p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+ p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
+ p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
+ p1[5] = (qfprom_cdata[2] & S5_P1_MASK) >> S5_P1_SHIFT;
+ p1[6] = (qfprom_cdata[2] & S6_P1_MASK) >> S6_P1_SHIFT;
+ p1[7] = (qfprom_cdata[2] & S7_P1_MASK) >> S7_P1_SHIFT;
+ p1[8] = (qfprom_cdata[3] & S8_P1_MASK) >> S8_P1_SHIFT;
+ p1[9] = (qfprom_cdata[3] & S9_P1_MASK) >> S9_P1_SHIFT;
+ for (i = 0; i < priv->num_sensors; i++)
+ p1[i] = (((base0) + p1[i]) << 2);
+ break;
+ default:
+ for (i = 0; i < priv->num_sensors; i++) {
+ p1[i] = 500;
+ p2[i] = 780;
+ }
+ break;
+ }
+
+ compute_intercept_slope(priv, p1, p2, mode);
+ kfree(qfprom_cdata);
+
+ return 0;
+}
+
+/* v1.x: qcs404,405 */
+
+static const struct tsens_features tsens_v1_feat = {
+ .ver_major = VER_1_X,
+ .crit_int = 0,
+ .adc = 1,
+ .srot_split = 1,
+ .max_sensors = 11,
+};
+
+static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
+ /* ----- SROT ------ */
+ /* VERSION */
+ [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31),
+ [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27),
+ [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15),
+ /* CTRL_OFFSET */
+ [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, 13),
+
+ /* ----- TM ------ */
+ /* INTERRUPT ENABLE */
+ [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0),
+
+ /* Sn_STATUS */
+ REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9),
+ REG_FIELD_FOR_EACH_SENSOR11(VALID, TM_Sn_STATUS_OFF, 14, 14),
+ REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10),
+ REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11),
+ REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12),
+ /* No CRITICAL field on v1.x */
+ REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13),
+
+ /* TRDY: 1=ready, 0=in progress */
+ [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
+};
+
+static const struct tsens_ops ops_generic_v1 = {
+ .init = init_common,
+ .calibrate = calibrate_v1,
+ .get_temp = get_temp_tsens_valid,
+};
+
+const struct tsens_plat_data data_tsens_v1 = {
+ .ops = &ops_generic_v1,
+ .feat = &tsens_v1_feat,
+ .fields = tsens_v1_regfields,
+};
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
index 44da02f..0a4f2b8 100644
--- a/drivers/thermal/qcom/tsens-v2.c
+++ b/drivers/thermal/qcom/tsens-v2.c
@@ -4,74 +4,80 @@
* Copyright (c) 2018, Linaro Limited
*/
-#include <linux/regmap.h>
#include <linux/bitops.h>
+#include <linux/regmap.h>
#include "tsens.h"
-#define STATUS_OFFSET 0xa0
-#define LAST_TEMP_MASK 0xfff
-#define STATUS_VALID_BIT BIT(21)
+/* ----- SROT ------ */
+#define SROT_HW_VER_OFF 0x0000
+#define SROT_CTRL_OFF 0x0004
-static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
-{
- struct tsens_sensor *s = &tmdev->sensor[id];
- u32 code;
- unsigned int status_reg;
- u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0;
- int ret;
+/* ----- TM ------ */
+#define TM_INT_EN_OFF 0x0004
+#define TM_UPPER_LOWER_INT_STATUS_OFF 0x0008
+#define TM_UPPER_LOWER_INT_CLEAR_OFF 0x000c
+#define TM_UPPER_LOWER_INT_MASK_OFF 0x0010
+#define TM_CRITICAL_INT_STATUS_OFF 0x0014
+#define TM_CRITICAL_INT_CLEAR_OFF 0x0018
+#define TM_CRITICAL_INT_MASK_OFF 0x001c
+#define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020
+#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
+#define TM_Sn_STATUS_OFF 0x00a0
+#define TM_TRDY_OFF 0x00e4
- status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4;
- ret = regmap_read(tmdev->map, status_reg, &code);
- if (ret)
- return ret;
- last_temp = code & LAST_TEMP_MASK;
- if (code & STATUS_VALID_BIT)
- goto done;
+/* v2.x: 8996, 8998, sdm845 */
- /* Try a second time */
- ret = regmap_read(tmdev->map, status_reg, &code);
- if (ret)
- return ret;
- if (code & STATUS_VALID_BIT) {
- last_temp = code & LAST_TEMP_MASK;
- goto done;
- } else {
- last_temp2 = code & LAST_TEMP_MASK;
- }
+static const struct tsens_features tsens_v2_feat = {
+ .ver_major = VER_2_X,
+ .crit_int = 1,
+ .adc = 0,
+ .srot_split = 1,
+ .max_sensors = 16,
+};
- /* Try a third/last time */
- ret = regmap_read(tmdev->map, status_reg, &code);
- if (ret)
- return ret;
- if (code & STATUS_VALID_BIT) {
- last_temp = code & LAST_TEMP_MASK;
- goto done;
- } else {
- last_temp3 = code & LAST_TEMP_MASK;
- }
+static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
+ /* ----- SROT ------ */
+ /* VERSION */
+ [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31),
+ [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27),
+ [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15),
+ /* CTRL_OFF */
+ [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0),
+ [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1),
- if (last_temp == last_temp2)
- last_temp = last_temp2;
- else if (last_temp2 == last_temp3)
- last_temp = last_temp3;
-done:
- /* Convert temperature from deciCelsius to milliCelsius */
- *temp = sign_extend32(last_temp, fls(LAST_TEMP_MASK) - 1) * 100;
+ /* ----- TM ------ */
+ /* INTERRUPT ENABLE */
+ /* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */
+ [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2),
- return 0;
-}
+ /* Sn_STATUS */
+ REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11),
+ REG_FIELD_FOR_EACH_SENSOR16(VALID, TM_Sn_STATUS_OFF, 21, 21),
+ REG_FIELD_FOR_EACH_SENSOR16(MIN_STATUS, TM_Sn_STATUS_OFF, 16, 16),
+ REG_FIELD_FOR_EACH_SENSOR16(LOWER_STATUS, TM_Sn_STATUS_OFF, 17, 17),
+ REG_FIELD_FOR_EACH_SENSOR16(UPPER_STATUS, TM_Sn_STATUS_OFF, 18, 18),
+ REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19),
+ REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20),
+
+ /* TRDY: 1=ready, 0=in progress */
+ [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
+};
static const struct tsens_ops ops_generic_v2 = {
.init = init_common,
- .get_temp = get_temp_tsens_v2,
+ .get_temp = get_temp_tsens_valid,
};
-const struct tsens_data data_tsens_v2 = {
- .ops = &ops_generic_v2,
+const struct tsens_plat_data data_tsens_v2 = {
+ .ops = &ops_generic_v2,
+ .feat = &tsens_v2_feat,
+ .fields = tsens_v2_regfields,
};
/* Kept around for backward compatibility with old msm8996.dtsi */
-const struct tsens_data data_8996 = {
+const struct tsens_plat_data data_8996 = {
.num_sensors = 13,
.ops = &ops_generic_v2,
+ .feat = &tsens_v2_feat,
+ .fields = tsens_v2_regfields,
};
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index a2c9bfa..0627d86 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/err.h>
@@ -24,38 +15,38 @@
static int tsens_get_temp(void *data, int *temp)
{
const struct tsens_sensor *s = data;
- struct tsens_device *tmdev = s->tmdev;
+ struct tsens_priv *priv = s->priv;
- return tmdev->ops->get_temp(tmdev, s->id, temp);
+ return priv->ops->get_temp(priv, s->id, temp);
}
-static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
+static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend)
{
- const struct tsens_sensor *s = p;
- struct tsens_device *tmdev = s->tmdev;
+ const struct tsens_sensor *s = data;
+ struct tsens_priv *priv = s->priv;
- if (tmdev->ops->get_trend)
- return tmdev->ops->get_trend(tmdev, s->id, trend);
+ if (priv->ops->get_trend)
+ return priv->ops->get_trend(priv, s->id, trend);
return -ENOTSUPP;
}
static int __maybe_unused tsens_suspend(struct device *dev)
{
- struct tsens_device *tmdev = dev_get_drvdata(dev);
+ struct tsens_priv *priv = dev_get_drvdata(dev);
- if (tmdev->ops && tmdev->ops->suspend)
- return tmdev->ops->suspend(tmdev);
+ if (priv->ops && priv->ops->suspend)
+ return priv->ops->suspend(priv);
return 0;
}
static int __maybe_unused tsens_resume(struct device *dev)
{
- struct tsens_device *tmdev = dev_get_drvdata(dev);
+ struct tsens_priv *priv = dev_get_drvdata(dev);
- if (tmdev->ops && tmdev->ops->resume)
- return tmdev->ops->resume(tmdev);
+ if (priv->ops && priv->ops->resume)
+ return priv->ops->resume(priv);
return 0;
}
@@ -73,6 +64,9 @@
.compatible = "qcom,msm8996-tsens",
.data = &data_8996,
}, {
+ .compatible = "qcom,tsens-v1",
+ .data = &data_tsens_v1,
+ }, {
.compatible = "qcom,tsens-v2",
.data = &data_tsens_v2,
},
@@ -85,27 +79,22 @@
.get_trend = tsens_get_trend,
};
-static int tsens_register(struct tsens_device *tmdev)
+static int tsens_register(struct tsens_priv *priv)
{
int i;
struct thermal_zone_device *tzd;
- u32 *hw_id, n = tmdev->num_sensors;
- hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
- if (!hw_id)
- return -ENOMEM;
-
- for (i = 0; i < tmdev->num_sensors; i++) {
- tmdev->sensor[i].tmdev = tmdev;
- tmdev->sensor[i].id = i;
- tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
- &tmdev->sensor[i],
+ for (i = 0; i < priv->num_sensors; i++) {
+ priv->sensor[i].priv = priv;
+ priv->sensor[i].id = i;
+ tzd = devm_thermal_zone_of_sensor_register(priv->dev, i,
+ &priv->sensor[i],
&tsens_of_ops);
if (IS_ERR(tzd))
continue;
- tmdev->sensor[i].tzd = tzd;
- if (tmdev->ops->enable)
- tmdev->ops->enable(tmdev, i);
+ priv->sensor[i].tzd = tzd;
+ if (priv->ops->enable)
+ priv->ops->enable(priv, i);
}
return 0;
}
@@ -115,8 +104,8 @@
int ret, i;
struct device *dev;
struct device_node *np;
- struct tsens_device *tmdev;
- const struct tsens_data *data;
+ struct tsens_priv *priv;
+ const struct tsens_plat_data *data;
const struct of_device_id *id;
u32 num_sensors;
@@ -143,52 +132,55 @@
return -EINVAL;
}
- tmdev = devm_kzalloc(dev,
- struct_size(tmdev, sensor, num_sensors),
+ priv = devm_kzalloc(dev,
+ struct_size(priv, sensor, num_sensors),
GFP_KERNEL);
- if (!tmdev)
+ if (!priv)
return -ENOMEM;
- tmdev->dev = dev;
- tmdev->num_sensors = num_sensors;
- tmdev->ops = data->ops;
- for (i = 0; i < tmdev->num_sensors; i++) {
+ priv->dev = dev;
+ priv->num_sensors = num_sensors;
+ priv->ops = data->ops;
+ for (i = 0; i < priv->num_sensors; i++) {
if (data->hw_ids)
- tmdev->sensor[i].hw_id = data->hw_ids[i];
+ priv->sensor[i].hw_id = data->hw_ids[i];
else
- tmdev->sensor[i].hw_id = i;
+ priv->sensor[i].hw_id = i;
}
+ priv->feat = data->feat;
+ priv->fields = data->fields;
- if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
+ if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
return -EINVAL;
- ret = tmdev->ops->init(tmdev);
+ ret = priv->ops->init(priv);
if (ret < 0) {
dev_err(dev, "tsens init failed\n");
return ret;
}
- if (tmdev->ops->calibrate) {
- ret = tmdev->ops->calibrate(tmdev);
+ if (priv->ops->calibrate) {
+ ret = priv->ops->calibrate(priv);
if (ret < 0) {
- dev_err(dev, "tsens calibration failed\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "tsens calibration failed\n");
return ret;
}
}
- ret = tsens_register(tmdev);
+ ret = tsens_register(priv);
- platform_set_drvdata(pdev, tmdev);
+ platform_set_drvdata(pdev, priv);
return ret;
}
static int tsens_remove(struct platform_device *pdev)
{
- struct tsens_device *tmdev = platform_get_drvdata(pdev);
+ struct tsens_priv *priv = platform_get_drvdata(pdev);
- if (tmdev->ops->disable)
- tmdev->ops->disable(tmdev);
+ if (priv->ops->disable)
+ priv->ops->disable(priv);
return 0;
}
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
index 14331eb..b89083b 100644
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -1,32 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+
#ifndef __QCOM_TSENS_H__
#define __QCOM_TSENS_H__
#define ONE_PT_CALIB 0x1
#define ONE_PT_CALIB2 0x2
#define TWO_PT_CALIB 0x3
+#define CAL_DEGC_PT1 30
+#define CAL_DEGC_PT2 120
+#define SLOPE_FACTOR 1000
+#define SLOPE_DEFAULT 3200
+
#include <linux/thermal.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
-struct tsens_device;
+struct tsens_priv;
+enum tsens_ver {
+ VER_0_1 = 0,
+ VER_1_X,
+ VER_2_X,
+};
+
+/**
+ * struct tsens_sensor - data for each sensor connected to the tsens device
+ * @priv: tsens device instance that this sensor is connected to
+ * @tzd: pointer to the thermal zone that this sensor is in
+ * @offset: offset of temperature adjustment curve
+ * @id: Sensor ID
+ * @hw_id: HW ID can be used in case of platform-specific IDs
+ * @slope: slope of temperature adjustment curve
+ * @status: 8960-specific variable to track 8960 and 8660 status register offset
+ */
struct tsens_sensor {
- struct tsens_device *tmdev;
+ struct tsens_priv *priv;
struct thermal_zone_device *tzd;
int offset;
- int id;
- int hw_id;
+ unsigned int id;
+ unsigned int hw_id;
int slope;
u32 status;
};
@@ -44,53 +60,273 @@
*/
struct tsens_ops {
/* mandatory callbacks */
- int (*init)(struct tsens_device *);
- int (*calibrate)(struct tsens_device *);
- int (*get_temp)(struct tsens_device *, int, int *);
+ int (*init)(struct tsens_priv *priv);
+ int (*calibrate)(struct tsens_priv *priv);
+ int (*get_temp)(struct tsens_priv *priv, int i, int *temp);
/* optional callbacks */
- int (*enable)(struct tsens_device *, int);
- void (*disable)(struct tsens_device *);
- int (*suspend)(struct tsens_device *);
- int (*resume)(struct tsens_device *);
- int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
+ int (*enable)(struct tsens_priv *priv, int i);
+ void (*disable)(struct tsens_priv *priv);
+ int (*suspend)(struct tsens_priv *priv);
+ int (*resume)(struct tsens_priv *priv);
+ int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend);
+};
+
+#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
+ [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
+ [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
+ [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
+ [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
+ [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
+ [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
+ [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
+ [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
+ [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
+ [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
+ [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit)
+
+#define REG_FIELD_FOR_EACH_SENSOR16(_name, _offset, _startbit, _stopbit) \
+ [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \
+ [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \
+ [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \
+ [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \
+ [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \
+ [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \
+ [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \
+ [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \
+ [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \
+ [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \
+ [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit), \
+ [_name##_##11] = REG_FIELD(_offset + 44, _startbit, _stopbit), \
+ [_name##_##12] = REG_FIELD(_offset + 48, _startbit, _stopbit), \
+ [_name##_##13] = REG_FIELD(_offset + 52, _startbit, _stopbit), \
+ [_name##_##14] = REG_FIELD(_offset + 56, _startbit, _stopbit), \
+ [_name##_##15] = REG_FIELD(_offset + 60, _startbit, _stopbit)
+
+/* reg_field IDs to use as an index into an array */
+enum regfield_ids {
+ /* ----- SROT ------ */
+ /* HW_VER */
+ VER_MAJOR = 0,
+ VER_MINOR,
+ VER_STEP,
+ /* CTRL_OFFSET */
+ TSENS_EN = 3,
+ TSENS_SW_RST,
+ SENSOR_EN,
+ CODE_OR_TEMP,
+
+ /* ----- TM ------ */
+ /* STATUS */
+ LAST_TEMP_0 = 7, /* Last temperature reading */
+ LAST_TEMP_1,
+ LAST_TEMP_2,
+ LAST_TEMP_3,
+ LAST_TEMP_4,
+ LAST_TEMP_5,
+ LAST_TEMP_6,
+ LAST_TEMP_7,
+ LAST_TEMP_8,
+ LAST_TEMP_9,
+ LAST_TEMP_10,
+ LAST_TEMP_11,
+ LAST_TEMP_12,
+ LAST_TEMP_13,
+ LAST_TEMP_14,
+ LAST_TEMP_15,
+ VALID_0 = 23, /* VALID reading or not */
+ VALID_1,
+ VALID_2,
+ VALID_3,
+ VALID_4,
+ VALID_5,
+ VALID_6,
+ VALID_7,
+ VALID_8,
+ VALID_9,
+ VALID_10,
+ VALID_11,
+ VALID_12,
+ VALID_13,
+ VALID_14,
+ VALID_15,
+ MIN_STATUS_0, /* MIN threshold violated */
+ MIN_STATUS_1,
+ MIN_STATUS_2,
+ MIN_STATUS_3,
+ MIN_STATUS_4,
+ MIN_STATUS_5,
+ MIN_STATUS_6,
+ MIN_STATUS_7,
+ MIN_STATUS_8,
+ MIN_STATUS_9,
+ MIN_STATUS_10,
+ MIN_STATUS_11,
+ MIN_STATUS_12,
+ MIN_STATUS_13,
+ MIN_STATUS_14,
+ MIN_STATUS_15,
+ MAX_STATUS_0, /* MAX threshold violated */
+ MAX_STATUS_1,
+ MAX_STATUS_2,
+ MAX_STATUS_3,
+ MAX_STATUS_4,
+ MAX_STATUS_5,
+ MAX_STATUS_6,
+ MAX_STATUS_7,
+ MAX_STATUS_8,
+ MAX_STATUS_9,
+ MAX_STATUS_10,
+ MAX_STATUS_11,
+ MAX_STATUS_12,
+ MAX_STATUS_13,
+ MAX_STATUS_14,
+ MAX_STATUS_15,
+ LOWER_STATUS_0, /* LOWER threshold violated */
+ LOWER_STATUS_1,
+ LOWER_STATUS_2,
+ LOWER_STATUS_3,
+ LOWER_STATUS_4,
+ LOWER_STATUS_5,
+ LOWER_STATUS_6,
+ LOWER_STATUS_7,
+ LOWER_STATUS_8,
+ LOWER_STATUS_9,
+ LOWER_STATUS_10,
+ LOWER_STATUS_11,
+ LOWER_STATUS_12,
+ LOWER_STATUS_13,
+ LOWER_STATUS_14,
+ LOWER_STATUS_15,
+ UPPER_STATUS_0, /* UPPER threshold violated */
+ UPPER_STATUS_1,
+ UPPER_STATUS_2,
+ UPPER_STATUS_3,
+ UPPER_STATUS_4,
+ UPPER_STATUS_5,
+ UPPER_STATUS_6,
+ UPPER_STATUS_7,
+ UPPER_STATUS_8,
+ UPPER_STATUS_9,
+ UPPER_STATUS_10,
+ UPPER_STATUS_11,
+ UPPER_STATUS_12,
+ UPPER_STATUS_13,
+ UPPER_STATUS_14,
+ UPPER_STATUS_15,
+ CRITICAL_STATUS_0, /* CRITICAL threshold violated */
+ CRITICAL_STATUS_1,
+ CRITICAL_STATUS_2,
+ CRITICAL_STATUS_3,
+ CRITICAL_STATUS_4,
+ CRITICAL_STATUS_5,
+ CRITICAL_STATUS_6,
+ CRITICAL_STATUS_7,
+ CRITICAL_STATUS_8,
+ CRITICAL_STATUS_9,
+ CRITICAL_STATUS_10,
+ CRITICAL_STATUS_11,
+ CRITICAL_STATUS_12,
+ CRITICAL_STATUS_13,
+ CRITICAL_STATUS_14,
+ CRITICAL_STATUS_15,
+ /* TRDY */
+ TRDY,
+ /* INTERRUPT ENABLE */
+ INT_EN, /* Pre-V1, V1.x */
+ LOW_INT_EN, /* V2.x */
+ UP_INT_EN, /* V2.x */
+ CRIT_INT_EN, /* V2.x */
+
+ /* Keep last */
+ MAX_REGFIELDS
};
/**
- * struct tsens_data - tsens instance specific data
- * @num_sensors: Max number of sensors supported by platform
+ * struct tsens_features - Features supported by the IP
+ * @ver_major: Major number of IP version
+ * @crit_int: does the IP support critical interrupts?
+ * @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?
+ * @max_sensors: maximum sensors supported by this version of the IP
+ */
+struct tsens_features {
+ unsigned int ver_major;
+ unsigned int crit_int:1;
+ unsigned int adc:1;
+ unsigned int srot_split:1;
+ unsigned int max_sensors;
+};
+
+/**
+ * struct tsens_plat_data - tsens compile-time platform data
+ * @num_sensors: Number of sensors supported by platform
* @ops: operations the tsens instance supports
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ * @feat: features of the IP
+ * @fields: bitfield locations
*/
-struct tsens_data {
+struct tsens_plat_data {
const u32 num_sensors;
const struct tsens_ops *ops;
unsigned int *hw_ids;
+ const struct tsens_features *feat;
+ const struct reg_field *fields;
};
-/* Registers to be saved/restored across a context loss */
+/**
+ * struct tsens_context - Registers to be saved/restored across a context loss
+ */
struct tsens_context {
int threshold;
int control;
};
-struct tsens_device {
+/**
+ * struct tsens_priv - private data for each instance of the tsens IP
+ * @dev: pointer to struct device
+ * @num_sensors: number of sensors enabled on this device
+ * @tm_map: pointer to TM register address space
+ * @srot_map: pointer to SROT register address space
+ * @tm_offset: deal with old device trees that don't address TM and SROT
+ * address space separately
+ * @rf: array of regmap_fields used to store value of the field
+ * @ctx: registers to be saved and restored during suspend/resume
+ * @feat: features of the IP
+ * @fields: bitfield locations
+ * @ops: pointer to list of callbacks supported by this device
+ * @sensor: list of sensors attached to this device
+ */
+struct tsens_priv {
struct device *dev;
u32 num_sensors;
- struct regmap *map;
+ struct regmap *tm_map;
+ struct regmap *srot_map;
u32 tm_offset;
+ struct regmap_field *rf[MAX_REGFIELDS];
struct tsens_context ctx;
+ const struct tsens_features *feat;
+ const struct reg_field *fields;
const struct tsens_ops *ops;
struct tsens_sensor sensor[0];
};
-char *qfprom_read(struct device *, const char *);
-void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
-int init_common(struct tsens_device *);
-int get_temp_common(struct tsens_device *, int, int *);
+char *qfprom_read(struct device *dev, const char *cname);
+void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode);
+int init_common(struct tsens_priv *priv);
+int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp);
+int get_temp_common(struct tsens_priv *priv, int i, int *temp);
+
+/* TSENS target */
+extern const struct tsens_plat_data data_8960;
+
+/* TSENS v0.1 targets */
+extern const struct tsens_plat_data data_8916, data_8974;
/* TSENS v1 targets */
-extern const struct tsens_data data_8916, data_8974, data_8960;
+extern const struct tsens_plat_data data_tsens_v1;
+
/* TSENS v2 targets */
-extern const struct tsens_data data_8996, data_tsens_v2;
+extern const struct tsens_plat_data data_8996, data_tsens_v2;
#endif /* __QCOM_TSENS_H__ */