Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ba2de11 authored by Jishnu Prakash's avatar Jishnu Prakash Committed by Gerrit - the friendly Code Review server
Browse files

thermal: adc-tm: Add ADC_TM driver snapshot



ADC_TM peripheral is used by client to set thresholds on channels
supported by VADC and send notification once set thresholds are
crossed. The driver registers thermistors used for thermal
mitigation with of_thermal to allow thermal driver to set
thresholds and receive notification through thermal sysfs.

This is a snapshot of the driver taken from msm-4.19 as of commit
f94ba33e3b90 ("Merge "usb: dwc3-msm: Fix GDSC collapse in host mode
bus suspend").

Change-Id: I9211552317955c503b7287876e11cd161c37f706
Signed-off-by: default avatarJishnu Prakash <jprakash@codeaurora.org>
parent 7c6f0d61
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -21,6 +21,16 @@ config QCOM_SPMI_TEMP_ALARM
	  real time die temperature if an ADC is present or an estimate of the
	  temperature based upon the over temperature stage value.

config QTI_ADC_TM
	tristate "Qualcomm Technologies Inc. Thermal Monitor ADC Driver"
	depends on SPMI && THERMAL
	depends on QCOM_SPMI_ADC5
	help
	  This enables the thermal Sysfs driver for the ADC thermal monitoring
	  device. It shows up in Sysfs as a thermal zone with multiple trip points.
	  Thermal client sets threshold temperature for both warm and cool
	  and gets updated when a threshold is reached.

config QTI_QMI_SENSOR
	tristate "QTI QMI sensor driver"
	depends on QCOM_QMI_HELPERS && THERMAL_OF && QTI_THERMAL
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.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
obj-$(CONFIG_QTI_ADC_TM) += adc-tm.o adc-tm-common.o adc-tm5.o
obj-$(CONFIG_QTI_QMI_SENSOR) += qti_qmi_sensor.o
qti_qmi_sensor-y += thermal_sensor_service_v01.o qmi_sensors.o

+211 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 */

#include <linux/module.h>
#include "adc-tm.h"

/*
 * Voltage to temperature table for NTCG104EF104 thermistor with
 * 1.875V reference and 100k pull-up.
 */
static const struct adc_tm_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
	{ 1831000,	-40000 },
	{ 1814000,	-35000 },
	{ 1791000,	-30000 },
	{ 1761000,	-25000 },
	{ 1723000,	-20000 },
	{ 1675000,	-15000 },
	{ 1616000,	-10000 },
	{ 1545000,	-5000 },
	{ 1463000,	0 },
	{ 1370000,	5000 },
	{ 1268000,	10000 },
	{ 1160000,	15000 },
	{ 1049000,	20000 },
	{ 937000,	25000 },
	{ 828000,	30000 },
	{ 726000,	35000 },
	{ 630000,	40000 },
	{ 544000,	45000 },
	{ 467000,	50000 },
	{ 399000,	55000 },
	{ 340000,	60000 },
	{ 290000,	65000 },
	{ 247000,	70000 },
	{ 209000,	75000 },
	{ 179000,	80000 },
	{ 153000,	85000 },
	{ 130000,	90000 },
	{ 112000,	95000 },
	{ 96000,	100000 },
	{ 82000,	105000 },
	{ 71000,	110000 },
	{ 62000,	115000 },
	{ 53000,	120000 },
	{ 46000,	125000 },
};

static void adc_tm_map_voltage_temp(const struct adc_tm_map_pt *pts,
				      size_t tablesize, int input, int *output)
{
	unsigned int descending = 1;
	u32 i = 0;

	/* Check if table is descending or ascending */
	if (tablesize > 1) {
		if (pts[0].x < pts[1].x)
			descending = 0;
	}

	while (i < tablesize) {
		if ((descending) && (pts[i].x < input)) {
			/* table entry is less than measured*/
			 /* value and table is descending, stop */
			break;
		} else if ((!descending) &&
				(pts[i].x > input)) {
			/* table entry is greater than measured*/
			/*value and table is ascending, stop */
			break;
		}
		i++;
	}

	if (i == 0) {
		*output = pts[0].y;
	} else if (i == tablesize) {
		*output = pts[tablesize - 1].y;
	} else {
		/* result is between search_index and search_index-1 */
		/* interpolate linearly */
		*output = (((int32_t)((pts[i].y - pts[i - 1].y) *
			(input - pts[i - 1].x)) /
			(pts[i].x - pts[i - 1].x)) +
			pts[i - 1].y);
	}
}

static void adc_tm_map_temp_voltage(const struct adc_tm_map_pt *pts,
		size_t tablesize, int input, int64_t *output)
{
	unsigned int i = 0, descending = 1;

	/* Check if table is descending or ascending */
	if (tablesize > 1) {
		if (pts[0].y < pts[1].y)
			descending = 0;
	}

	while (i < tablesize) {
		if (descending && (pts[i].y < input)) {
			/*
			 * Table entry is less than measured value.
			 * Table is descending, stop.
			 */
			break;
		} else if (!descending && (pts[i].y > input)) {
			/*
			 * Table entry is greater than measured value.
			 * Table is ascending, stop.
			 */
			break;
		}
		i++;
	}

	if (i == 0) {
		*output = pts[0].x;
	} else if (i == tablesize) {
		*output = pts[tablesize-1].x;
	} else {
		/*
		 * Result is between search_index and search_index-1.
		 * Interpolate linearly.
		 */
		*output = (((int32_t) ((pts[i].x - pts[i-1].x) *
			(input - pts[i-1].y)) /
			(pts[i].y - pts[i-1].y)) +
			pts[i-1].x);
	}
}

int therm_fwd_scale(int64_t code, uint32_t adc_hc_vdd_ref_mv,
			const struct adc_tm_data *data)
{
	int64_t volt = 0;
	int result = 0;

	volt = (s64) code * adc_hc_vdd_ref_mv;
	volt = div64_s64(volt, (data->full_scale_code_volt));

	adc_tm_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
				 ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
				 (int) volt, &result);

	return result;
}
EXPORT_SYMBOL(therm_fwd_scale);

void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
				const struct adc_tm_data *data)
{
	int temp;

	/* High temperature maps to lower threshold voltage */
	adc_tm_map_temp_voltage(
		adcmap_100k_104ef_104fb_1875_vref,
		ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
		param->high_thr_temp, &param->low_thr_voltage);

	param->low_thr_voltage *= data->full_scale_code_volt;
	param->low_thr_voltage = div64_s64(param->low_thr_voltage,
						ADC_HC_VDD_REF);

	temp = therm_fwd_scale(param->low_thr_voltage,
				ADC_HC_VDD_REF, data);

	if (temp < param->high_thr_temp)
		param->low_thr_voltage--;

	/* Low temperature maps to higher threshold voltage */
	adc_tm_map_temp_voltage(
		adcmap_100k_104ef_104fb_1875_vref,
		ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
		param->low_thr_temp, &param->high_thr_voltage);

	param->high_thr_voltage *= data->full_scale_code_volt;
	param->high_thr_voltage = div64_s64(param->high_thr_voltage,
						ADC_HC_VDD_REF);

	temp = therm_fwd_scale(param->high_thr_voltage,
				ADC_HC_VDD_REF, data);

	if (temp > param->low_thr_temp)
		param->high_thr_voltage++;

}
EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k);

int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
			struct adc_tm_config *tm_config)
{
	int64_t low_thr = 0, high_thr = 0;

	low_thr =  div_s64(tm_config->low_thr_voltage, tm_config->prescal);
	low_thr *= data->full_scale_code_volt;
	low_thr = div64_s64(low_thr, ADC_HC_VDD_REF);
	tm_config->low_thr_voltage = low_thr;

	high_thr =  div_s64(tm_config->high_thr_voltage, tm_config->prescal);
	high_thr *= data->full_scale_code_volt;
	high_thr = div64_s64(high_thr, ADC_HC_VDD_REF);
	tm_config->high_thr_voltage = high_thr;

	return 0;
}
EXPORT_SYMBOL(adc_tm_absolute_rthr);

MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver");
MODULE_LICENSE("GPL v2");
+430 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 */

#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/iio/iio.h>
#include "adc-tm.h"

LIST_HEAD(adc_tm_device_list);

static int adc_tm_get_temp(void *data, int *temp)
{
	struct adc_tm_sensor *s = data;
	struct adc_tm_chip *adc_tm = s->chip;

	return adc_tm->ops->get_temp(s, temp);
}

static int adc_tm_set_trip_temp(void *data, int low_temp, int high_temp)
{
	struct adc_tm_sensor *s = data;
	struct adc_tm_chip *adc_tm = s->chip;

	if (adc_tm->ops->set_trips)
		return adc_tm->ops->set_trips(s, low_temp, high_temp);

	return 0;
}

static int adc_tm_register_interrupts(struct adc_tm_chip *adc_tm)
{
	if (adc_tm->ops->interrupts_reg)
		return adc_tm->ops->interrupts_reg(adc_tm);

	return 0;
}

static int adc_tm_init(struct adc_tm_chip *adc_tm, uint32_t dt_chans)
{
	if (adc_tm->ops->init)
		return adc_tm->ops->init(adc_tm, dt_chans);

	return 0;
}

static struct thermal_zone_of_device_ops adc_tm_ops = {
	.get_temp = adc_tm_get_temp,
	.set_trips = adc_tm_set_trip_temp,
};

static struct thermal_zone_of_device_ops adc_tm_ops_iio = {
	.get_temp = adc_tm_get_temp,
};

static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num,
					bool set_trips)
{
	unsigned int i;
	struct thermal_zone_device *tzd;

	for (i = 0; i < dt_chan_num; i++) {
		adc_tm->sensor[i].chip = adc_tm;
		if (!adc_tm->sensor[i].non_thermal) {
			if (set_trips)
				tzd = devm_thermal_zone_of_sensor_register(
					adc_tm->dev, adc_tm->sensor[i].adc_ch,
					&adc_tm->sensor[i],	&adc_tm_ops);
			else
				tzd = devm_thermal_zone_of_sensor_register(
					adc_tm->dev, adc_tm->sensor[i].adc_ch,
					&adc_tm->sensor[i], &adc_tm_ops_iio);

			if (IS_ERR(tzd)) {
				pr_err("Error registering TZ zone:%ld for dt_ch:%d\n",
					PTR_ERR(tzd), adc_tm->sensor[i].adc_ch);
				continue;
			}
			adc_tm->sensor[i].tzd = tzd;
		} else
			adc_tm->sensor[i].tzd = NULL;
	}

	return 0;
}

static int adc_tm_avg_samples_from_dt(u32 value)
{
	if (!is_power_of_2(value) || value > ADC_TM_AVG_SAMPLES_MAX)
		return -EINVAL;

	return __ffs64(value);
}

static int adc_tm_hw_settle_time_from_dt(u32 value,
					const unsigned int *hw_settle)
{
	unsigned int i;

	for (i = 0; i < ADC_TM_HW_SETTLE_SAMPLES_MAX; i++) {
		if (value == hw_settle[i])
			return i;
	}

	return -EINVAL;
}

static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation)
{
	unsigned int i;

	for (i = 0; i < ADC_TM_DECIMATION_SAMPLES_MAX; i++) {
		if (value == decimation[i])
			return i;
	}

	return -EINVAL;
}

struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name)
{
	struct platform_device *pdev;
	struct adc_tm_chip *chip;
	struct device_node *node = NULL;
	char prop_name[MAX_PROP_NAME_LEN];

	snprintf(prop_name, MAX_PROP_NAME_LEN, "qcom,%s-adc_tm", name);

	node = of_parse_phandle(dev->of_node, prop_name, 0);
	if (node == NULL)
		return ERR_PTR(-ENODEV);

	list_for_each_entry(chip, &adc_tm_device_list, list) {
		pdev = to_platform_device(chip->dev);
		if (pdev->dev.of_node == node)
			return chip;
	}
	return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(get_adc_tm);

int adc_tm_is_valid(struct adc_tm_chip *chip)
{
	struct adc_tm_chip *adc_tm_chip = NULL;

	list_for_each_entry(adc_tm_chip, &adc_tm_device_list, list)
		if (chip == adc_tm_chip)
			return 0;

	return -EINVAL;
}

static const struct of_device_id adc_tm_match_table[] = {
	{
		.compatible = "qcom,adc-tm5",
		.data = &data_adc_tm5,
	},
	{
		.compatible = "qcom,adc-tm5-iio",
		.data = &data_adc_tm5,
	},
	{}
};

static int adc_tm_get_dt_data(struct platform_device *pdev,
				struct adc_tm_chip *adc_tm,
				struct iio_channel *chan,
				uint32_t dt_chan_num)
{
	struct device_node *child, *node = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	const struct of_device_id *id;
	const struct adc_tm_data *data;
	int ret, idx = 0;

	if (!node)
		return -EINVAL;

	id = of_match_node(adc_tm_match_table, node);
	if (id)
		data = id->data;
	else
		data = &data_adc_tm5;
	adc_tm->data = data;
	adc_tm->ops = data->ops;

	ret = of_property_read_u32(node, "qcom,decimation",
						&adc_tm->prop.decimation);
	if (!ret) {
		ret = adc_tm_decimation_from_dt(adc_tm->prop.decimation,
							data->decimation);
		if (ret < 0) {
			dev_err(dev, "Invalid decimation value\n");
			return ret;
		}
		adc_tm->prop.decimation = ret;
	} else {
		adc_tm->prop.decimation = ADC_TM_DECIMATION_DEFAULT;
	}

	ret = of_property_read_u32(node, "qcom,avg-samples",
						&adc_tm->prop.fast_avg_samples);
	if (!ret) {
		ret = adc_tm_avg_samples_from_dt(adc_tm->prop.fast_avg_samples);
		if (ret < 0) {
			dev_err(dev, "Invalid fast average with%d\n", ret);
			return -EINVAL;
		}
	} else {
		adc_tm->prop.fast_avg_samples = ADC_TM_DEF_AVG_SAMPLES;
	}

	adc_tm->prop.timer1 = ADC_TM_TIMER1;
	adc_tm->prop.timer2 = ADC_TM_TIMER2;
	adc_tm->prop.timer3 = ADC_TM_TIMER3;

	for_each_child_of_node(node, child) {
		int channel_num, i = 0, adc_rscale_fn = 0;
		int calib_type = 0, ret, hw_settle_time = 0;
		int prescal = 0;
		struct iio_channel *chan_adc;
		bool non_thermal = false;

		ret = of_property_read_u32(child, "reg", &channel_num);
		if (ret) {
			dev_err(dev, "Invalid channel num\n");
			return -EINVAL;
		}

		ret = of_property_read_u32(child, "qcom,hw-settle-time",
							&hw_settle_time);
		if (!ret) {
			ret = adc_tm_hw_settle_time_from_dt(hw_settle_time,
							data->hw_settle);
			if (ret < 0) {
				pr_err("Invalid channel hw settle time property\n");
				return ret;
			}
			hw_settle_time = ret;
		} else {
			hw_settle_time = ADC_TM_DEF_HW_SETTLE_TIME;
		}

		if (of_property_read_bool(child, "qcom,ratiometric"))
			calib_type = ADC_RATIO_CAL;
		else
			calib_type = ADC_ABS_CAL;

		if (of_property_read_bool(child, "qcom,kernel-client"))
			non_thermal = true;

		ret = of_property_read_u32(child, "qcom,scale-type",
							&adc_rscale_fn);
		if (ret)
			adc_rscale_fn = SCALE_RSCALE_NONE;

		ret = of_property_read_u32(child, "qcom,prescaling", &prescal);
		if (ret)
			prescal = 1;

		/* Individual channel properties */
		adc_tm->sensor[idx].adc_ch = channel_num;
		adc_tm->sensor[idx].cal_sel = calib_type;
		/* Default to 1 second timer select */
		adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2;
		adc_tm->sensor[idx].hw_settle_time = hw_settle_time;
		adc_tm->sensor[idx].adc_rscale_fn = adc_rscale_fn;
		adc_tm->sensor[idx].non_thermal = non_thermal;
		adc_tm->sensor[idx].prescaling = prescal;

		if (adc_tm->sensor[idx].non_thermal) {
			adc_tm->sensor[idx].req_wq = alloc_workqueue(
				"qpnp_adc_notify_wq", WQ_HIGHPRI, 0);
			if (!adc_tm->sensor[idx].req_wq) {
				pr_err("Requesting priority wq failed\n");
				return -ENOMEM;
			}
			INIT_WORK(&adc_tm->sensor[idx].work, notify_adc_tm_fn);
		}
		INIT_LIST_HEAD(&adc_tm->sensor[idx].thr_list);

		while (i < dt_chan_num) {
			chan_adc = &chan[i];
			if (chan_adc->channel->channel == channel_num)
				adc_tm->sensor[idx].adc = chan_adc;
			i++;
		}
		idx++;
	}

	return 0;
}

static int adc_tm_probe(struct platform_device *pdev)
{
	struct device_node *child, *revid_dev_node, *node = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	struct adc_tm_chip *adc_tm;
	struct regmap *regmap;
	struct iio_channel *channels;
	int ret = 0, dt_chan_num = 0, indio_chan_count = 0, i = 0;
	u32 reg;

	if (!node)
		return -EINVAL;

	for_each_child_of_node(node, child)
		dt_chan_num++;

	if (!dt_chan_num) {
		dev_err(dev, "No channel listing\n");
		return -EINVAL;
	}

	channels = iio_channel_get_all(dev);
	if (IS_ERR(channels))
		return PTR_ERR(channels);

	while (channels[indio_chan_count].indio_dev)
		indio_chan_count++;

	if (indio_chan_count != dt_chan_num) {
		dev_err(dev, "VADC IIO channel missing in main node\n");
		return -EINVAL;
	}

	regmap = dev_get_regmap(dev->parent, NULL);
	if (!regmap)
		return -ENODEV;

	ret = of_property_read_u32(node, "reg", &reg);
	if (ret < 0)
		return ret;

	adc_tm = devm_kzalloc(&pdev->dev,
			sizeof(struct adc_tm_chip) + (dt_chan_num *
			(sizeof(struct adc_tm_sensor))), GFP_KERNEL);
	if (!adc_tm)
		return -ENOMEM;

	adc_tm->regmap = regmap;
	adc_tm->dev = dev;
	adc_tm->base = reg;
	adc_tm->dt_channels = dt_chan_num;

	platform_set_drvdata(pdev, adc_tm);

	revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
	if (revid_dev_node) {
		adc_tm->pmic_rev_id = get_revid_data(revid_dev_node);
		if (IS_ERR(adc_tm->pmic_rev_id)) {
			pr_debug("Unable to get revid\n");
			return -EPROBE_DEFER;
		}
	}

	ret = adc_tm_get_dt_data(pdev, adc_tm, channels, dt_chan_num);
	if (ret) {
		dev_err(dev, "adc-tm get dt data failed\n");
		return ret;
	}

	if (of_device_is_compatible(node, "qcom,adc-tm5-iio")) {
		ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false);
		if (ret) {
			dev_err(dev, "adc-tm failed to register with of thermal\n");
			goto fail;
		}
		return 0;
	}

	ret = adc_tm_init(adc_tm, dt_chan_num);
	if (ret) {
		dev_err(dev, "adc-tm init failed\n");
		goto fail;
	}

	ret = adc_tm_register_tzd(adc_tm, dt_chan_num, true);
	if (ret) {
		dev_err(dev, "adc-tm failed to register with of thermal\n");
		goto fail;
	}

	ret = adc_tm_register_interrupts(adc_tm);
	if (ret) {
		pr_err("adc-tm register interrupts failed:%d\n", ret);
		goto fail;
	}

	list_add_tail(&adc_tm->list, &adc_tm_device_list);
	return 0;
fail:
	i = 0;
	while (i < dt_chan_num) {
		if (adc_tm->sensor[i].req_wq)
			destroy_workqueue(adc_tm->sensor[i].req_wq);
		i++;
	}
	return ret;
}

static int adc_tm_remove(struct platform_device *pdev)
{
	struct adc_tm_chip *adc_tm = platform_get_drvdata(pdev);

	if (adc_tm->ops->shutdown)
		adc_tm->ops->shutdown(adc_tm);

	return 0;
}

static struct platform_driver adc_tm_driver = {
	.driver = {
		.name = "qcom,adc-tm",
		.of_match_table	= adc_tm_match_table,
	},
	.probe = adc_tm_probe,
	.remove = adc_tm_remove,
};
module_platform_driver(adc_tm_driver);

MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM driver");
MODULE_LICENSE("GPL v2");
+272 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
 */

#ifndef __QCOM_ADC_TM_H__
#define __QCOM_ADC_TM_H__

#include <linux/kernel.h>
#include <linux/thermal.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/iio/consumer.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/adc-tm-clients.h>

#define ADC_TM_DECIMATION_DEFAULT	840
#define ADC_TM_DECIMATION_SAMPLES_MAX	3
#define ADC_TM_DEF_AVG_SAMPLES		0 /* 1 sample */
#define ADC_TM_DEF_HW_SETTLE_TIME	0 /* 15 us */
#define ADC_TM_HW_SETTLE_SAMPLES_MAX	16
#define ADC_TM_AVG_SAMPLES_MAX		16
#define ADC_TM_TIMER1			3 /* 3.9ms */
#define ADC_TM_TIMER2			10 /* 1 second */
#define ADC_TM_TIMER3			4 /* 4 second */
#define ADC_HC_VDD_REF			1875000
#define MAX_PROP_NAME_LEN					32

enum adc_cal_method {
	ADC_NO_CAL = 0,
	ADC_RATIO_CAL = 1,
	ADC_ABS_CAL = 2,
	ADC_CAL_SEL_NONE,
};

enum adc_cal_val {
	ADC_TIMER_CAL = 0,
	ADC_NEW_CAL,
	ADC_CAL_VAL_NONE,
};

enum adc_timer_select {
	ADC_TIMER_SEL_1 = 0,
	ADC_TIMER_SEL_2,
	ADC_TIMER_SEL_3,
	ADC_TIMER_SEL_NONE,
};

/**
 * enum adc_tm_rscale_fn_type - Scaling function used to convert the
 *	channels input voltage/temperature to corresponding ADC code that is
 *	applied for thresholds. Check the corresponding channels scaling to
 *	determine the appropriate temperature/voltage units that are passed
 *	to the scaling function. Example battery follows the power supply
 *	framework that needs its units to be in decidegreesC so it passes
 *	deci-degreesC. PA_THERM clients pass the temperature in degrees.
 *	The order below should match the one in the driver for
 *	adc_tm_rscale_fn[].
 */
enum adc_tm_rscale_fn_type {
	SCALE_R_ABSOLUTE = 0,
	SCALE_RSCALE_NONE,
};

struct adc_tm_sensor {
	struct adc_tm_chip		*chip;
	struct thermal_zone_device	*tzd;
	enum adc_cal_val		cal_val;
	enum adc_cal_method		cal_sel;
	unsigned int			hw_settle_time;
	unsigned int			adc_ch;
	unsigned int			btm_ch;
	unsigned int			prescaling;
	unsigned int			timer_select;
	enum adc_tm_rscale_fn_type	adc_rscale_fn;
	struct iio_channel		*adc;
	struct list_head		thr_list;
	bool					non_thermal;
	bool				high_thr_triggered;
	bool				low_thr_triggered;
	struct workqueue_struct		*req_wq;
	struct work_struct		work;
};

struct adc_tm_client_info {
	struct list_head			list;
	struct adc_tm_param			*param;
	int32_t						low_thr_requested;
	int32_t						high_thr_requested;
	bool						notify_low_thr;
	bool						notify_high_thr;
	bool						high_thr_set;
	bool						low_thr_set;
	enum adc_tm_state_request	state_request;
};

struct adc_tm_cmn_prop {
	unsigned int			decimation;
	unsigned int			fast_avg_samples;
	unsigned int			timer1;
	unsigned int			timer2;
	unsigned int			timer3;
};

struct adc_tm_ops {
	int (*get_temp)(struct adc_tm_sensor *sensor, int *temp);
	int (*init)(struct adc_tm_chip *chip, uint32_t dt_chans);
	int (*set_trips)(struct adc_tm_sensor *sensor, int low_temp,
							int high_temp);
	int (*interrupts_reg)(struct adc_tm_chip *chip);
	int (*shutdown)(struct adc_tm_chip *chip);
};

struct adc_tm_chip {
	struct device			*dev;
	struct list_head		list;
	struct regmap			*regmap;
	u16				base;
	struct adc_tm_cmn_prop		prop;
	spinlock_t			adc_tm_lock;
	struct mutex		adc_mutex_lock;
	const struct adc_tm_ops		*ops;
	const struct adc_tm_data	*data;
	unsigned int			dt_channels;
	struct pmic_revid_data		*pmic_rev_id;
	struct adc_tm_sensor		sensor[0];
};

struct adc_tm_data {
	const struct adc_tm_ops *ops;
	const u32	full_scale_code_volt;
	unsigned int	*decimation;
	unsigned int	*hw_settle;
};

extern const struct adc_tm_data data_adc_tm5;
/**
 * Channel index for the corresponding index to adc_tm_channel_select
 */
enum adc_tm_channel_num {
	ADC_TM_CHAN0 = 0,
	ADC_TM_CHAN1,
	ADC_TM_CHAN2,
	ADC_TM_CHAN3,
	ADC_TM_CHAN4,
	ADC_TM_CHAN5,
	ADC_TM_CHAN6,
	ADC_TM_CHAN7,
	ADC_TM_CHAN_NONE
};

/**
 * Channel selection registers for each of the configurable measurements
 * Channels allotment is set at device config for a channel.
 */
enum adc_tm_channel_sel	{
	ADC_TM_M0_ADC_CH_SEL_CTL = 0x60,
	ADC_TM_M1_ADC_CH_SEL_CTL = 0x68,
	ADC_TM_M2_ADC_CH_SEL_CTL = 0x70,
	ADC_TM_M3_ADC_CH_SEL_CTL = 0x78,
	ADC_TM_M4_ADC_CH_SEL_CTL = 0x80,
	ADC_TM_M5_ADC_CH_SEL_CTL = 0x88,
	ADC_TM_M6_ADC_CH_SEL_CTL = 0x90,
	ADC_TM_M7_ADC_CH_SEL_CTL = 0x98,
	ADC_TM_CH_SELECT_NONE
};

/**
 * enum adc_tm_fast_avg_ctl - Provides ability to obtain single result
 *		from the ADC that is an average of multiple measurement
 *		samples. Select number of samples for use in fast
 *		average mode (i.e. 2 ^ value).
 * %ADC_FAST_AVG_SAMPLE_1:   0x0 = 1
 * %ADC_FAST_AVG_SAMPLE_2:   0x1 = 2
 * %ADC_FAST_AVG_SAMPLE_4:   0x2 = 4
 * %ADC_FAST_AVG_SAMPLE_8:   0x3 = 8
 * %ADC_FAST_AVG_SAMPLE_16:  0x4 = 16
 */
enum qpnp_adc_fast_avg_ctl {
	ADC_FAST_AVG_SAMPLE_1 = 0,
	ADC_FAST_AVG_SAMPLE_2,
	ADC_FAST_AVG_SAMPLE_4,
	ADC_FAST_AVG_SAMPLE_8,
	ADC_FAST_AVG_SAMPLE_16,
	ADC_FAST_AVG_SAMPLE_NONE,
};

struct adc_tm_trip_reg_type {
	enum adc_tm_channel_sel		btm_amux_ch;
	uint16_t			low_thr_lsb_addr;
	uint16_t			low_thr_msb_addr;
	uint16_t			high_thr_lsb_addr;
	uint16_t			high_thr_msb_addr;
	u8				multi_meas_en;
	u8				low_thr_int_chan_en;
	u8				high_thr_int_chan_en;
	u8				meas_interval_ctl;
};

/**
 * struct adc_tm_config - Represent ADC Thermal Monitor configuration.
 * @channel: ADC channel for which thermal monitoring is requested.
 * @adc_code: The pre-calibrated digital output of a given ADC releative to the
 *		ADC reference.
 * @high_thr_temp: Temperature at which high threshold notification is required.
 * @low_thr_temp: Temperature at which low threshold notification is required.
 * @low_thr_voltage : Low threshold voltage ADC code used for reverse
 *			calibration.
 * @high_thr_voltage: High threshold voltage ADC code used for reverse
 *			calibration.
 */
struct adc_tm_config {
	int	channel;
	int	adc_code;
	int	prescal;
	int	high_thr_temp;
	int	low_thr_temp;
	int64_t	high_thr_voltage;
	int64_t	low_thr_voltage;
};

/**
 * struct adc_tm_reverse_scale_fn - Reverse scaling prototype
 * @chan: Function pointer to one of the scaling functions
 *	which takes the adc properties and returns the physical result
 */
struct adc_tm_reverse_scale_fn {
	int32_t (*chan)(const struct adc_tm_data *data,
		struct adc_tm_config *tm_config);
};

/**
 * struct adc_map_pt - Map the graph representation for ADC channel
 * @x: Represent the ADC digitized code.
 * @y: Represent the physical data which can be temperature, voltage,
 *     resistance.
 */
struct adc_tm_map_pt {
	int32_t x;
	int32_t y;
};

/**
 * struct adc_linear_graph - Represent ADC characteristics.
 * @dy: numerator slope to calculate the gain.
 * @dx: denominator slope to calculate the gain.
 * @gnd: A/D word of the ground reference used for the channel.
 *
 * Each ADC device has different offset and gain parameters which are
 * computed to calibrate the device.
 */
struct adc_tm_linear_graph {
	s32 dy;
	s32 dx;
	s32 gnd;
};

int therm_fwd_scale(int64_t code, uint32_t adc_hc_vdd_ref_mv,
				const struct adc_tm_data *data);

void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
				const struct adc_tm_data *data);

int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data,
			struct adc_tm_config *tm_config);

void notify_adc_tm_fn(struct work_struct *work);

int adc_tm_is_valid(struct adc_tm_chip *chip);

#endif /* __QCOM_ADC_TM_H__ */
Loading