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

Commit 0acdc2d7 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "thermal: adc_tm: Add support for non-thermal ADC_TM clients"

parents 5a25a897 e163d257
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -129,5 +129,24 @@ void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
}
}
EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k);
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_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver");
MODULE_LICENSE("GPL v2");
MODULE_LICENSE("GPL v2");
+94 −22
Original line number Original line Diff line number Diff line
@@ -77,14 +77,15 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num,


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


			if (IS_ERR(tzd)) {
			if (IS_ERR(tzd)) {
				pr_err("Error registering TZ zone:%d for dt_ch:%d\n",
				pr_err("Error registering TZ zone:%d for dt_ch:%d\n",
@@ -92,6 +93,8 @@ static int adc_tm_register_tzd(struct adc_tm_chip *adc_tm, int dt_chan_num,
				continue;
				continue;
			}
			}
			adc_tm->sensor[i].tzd = tzd;
			adc_tm->sensor[i].tzd = tzd;
		} else
			adc_tm->sensor[i].tzd = NULL;
	}
	}


	return 0;
	return 0;
@@ -130,6 +133,39 @@ static int adc_tm_decimation_from_dt(u32 value, const unsigned int *decimation)
	return -EINVAL;
	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[] = {
static const struct of_device_id adc_tm_match_table[] = {
	{
	{
		.compatible = "qcom,adc-tm5",
		.compatible = "qcom,adc-tm5",
@@ -195,9 +231,11 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
	adc_tm->prop.timer3 = ADC_TM_TIMER3;
	adc_tm->prop.timer3 = ADC_TM_TIMER3;


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


		ret = of_property_read_u32(child, "reg", &channel_num);
		ret = of_property_read_u32(child, "reg", &channel_num);
		if (ret) {
		if (ret) {
@@ -224,12 +262,39 @@ static int adc_tm_get_dt_data(struct platform_device *pdev,
		else
		else
			calib_type = ADC_ABS_CAL;
			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 */
		/* Individual channel properties */
		adc_tm->sensor[idx].adc_ch = channel_num;
		adc_tm->sensor[idx].adc_ch = channel_num;
		adc_tm->sensor[idx].cal_sel = calib_type;
		adc_tm->sensor[idx].cal_sel = calib_type;
		/* Default to 1 second timer select */
		/* Default to 1 second timer select */
		adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2;
		adc_tm->sensor[idx].timer_select = ADC_TIMER_SEL_2;
		adc_tm->sensor[idx].hw_settle_time = hw_settle_time;
		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) {
		while (i < dt_chan_num) {
			chan_adc = &chan[i];
			chan_adc = &chan[i];
			if (chan_adc->channel->channel == channel_num)
			if (chan_adc->channel->channel == channel_num)
@@ -249,7 +314,7 @@ static int adc_tm_probe(struct platform_device *pdev)
	struct adc_tm_chip *adc_tm;
	struct adc_tm_chip *adc_tm;
	struct regmap *regmap;
	struct regmap *regmap;
	struct iio_channel *channels;
	struct iio_channel *channels;
	int ret, dt_chan_num = 0, indio_chan_count = 0;
	int ret = 0, dt_chan_num = 0, indio_chan_count = 0, i = 0;
	u32 reg;
	u32 reg;


	if (!node)
	if (!node)
@@ -313,7 +378,7 @@ static int adc_tm_probe(struct platform_device *pdev)
		ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false);
		ret = adc_tm_register_tzd(adc_tm, dt_chan_num, false);
		if (ret) {
		if (ret) {
			dev_err(dev, "adc-tm failed to register with of thermal\n");
			dev_err(dev, "adc-tm failed to register with of thermal\n");
			return ret;
			goto fail;
		}
		}
		return 0;
		return 0;
	}
	}
@@ -321,25 +386,32 @@ static int adc_tm_probe(struct platform_device *pdev)
	ret = adc_tm_init(adc_tm, dt_chan_num);
	ret = adc_tm_init(adc_tm, dt_chan_num);
	if (ret) {
	if (ret) {
		dev_err(dev, "adc-tm init failed\n");
		dev_err(dev, "adc-tm init failed\n");
		return ret;
		goto fail;
	}
	}


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


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


	list_add_tail(&adc_tm->list, &adc_tm_device_list);
	list_add_tail(&adc_tm->list, &adc_tm_device_list);
	platform_set_drvdata(pdev, adc_tm);
	platform_set_drvdata(pdev, adc_tm);

	return 0;
	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)
static int adc_tm_remove(struct platform_device *pdev)
+118 −0
Original line number Original line Diff line number Diff line
@@ -32,6 +32,8 @@ struct adc_tm_chip;
#define ADC_TM_TIMER1			3 /* 3.9ms */
#define ADC_TM_TIMER1			3 /* 3.9ms */
#define ADC_TM_TIMER2			10 /* 1 second */
#define ADC_TM_TIMER2			10 /* 1 second */
#define ADC_TM_TIMER3			4 /* 4 second */
#define ADC_TM_TIMER3			4 /* 4 second */
#define ADC_HC_VDD_REF			1875000
#define MAX_PROP_NAME_LEN					32


enum adc_cal_method {
enum adc_cal_method {
	ADC_NO_CAL = 0,
	ADC_NO_CAL = 0,
@@ -53,6 +55,72 @@ enum adc_timer_select {
	ADC_TIMER_SEL_NONE,
	ADC_TIMER_SEL_NONE,
};
};


/**
 * enum adc_tm_state - This lets the client know whether the threshold
 *		that was crossed was high/low.
 * %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high
 *			voltage threshold.
 * %ADC_TM_COOL_STATE: Client is notified of crossing the requested cool
 *			temperature threshold.
 * %ADC_TM_LOW_STATE: Client is notified of crossing the requested low
 *			voltage threshold.
 * %ADC_TM_WARM_STATE: Client is notified of crossing the requested high
 *			temperature threshold.
 */
enum adc_tm_state {
	ADC_TM_HIGH_STATE = 0,
	ADC_TM_COOL_STATE = ADC_TM_HIGH_STATE,
	ADC_TM_LOW_STATE,
	ADC_TM_WARM_STATE = ADC_TM_LOW_STATE,
	ADC_TM_STATE_NUM,
};

/**
 * enum adc_tm_state_request - Request to enable/disable the corresponding
 *			high/low voltage/temperature thresholds.
 * %ADC_TM_HIGH_THR_ENABLE: Enable high voltage threshold.
 * %ADC_TM_COOL_THR_ENABLE = Enables cool temperature threshold.
 * %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold.
 * %ADC_TM_WARM_THR_ENABLE = Enables warm temperature threshold.
 * %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature
 *				threshold.
 * %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold.
 * %ADC_TM_COOL_THR_ENABLE = Disables cool temperature threshold.
 * %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold.
 * %ADC_TM_WARM_THR_ENABLE = Disables warm temperature threshold.
 * %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature
 *				threshold.
 */
enum adc_tm_state_request {
	ADC_TM_HIGH_THR_ENABLE = 0,
	ADC_TM_COOL_THR_ENABLE = ADC_TM_HIGH_THR_ENABLE,
	ADC_TM_LOW_THR_ENABLE,
	ADC_TM_WARM_THR_ENABLE = ADC_TM_LOW_THR_ENABLE,
	ADC_TM_HIGH_LOW_THR_ENABLE,
	ADC_TM_HIGH_THR_DISABLE,
	ADC_TM_COOL_THR_DISABLE = ADC_TM_HIGH_THR_DISABLE,
	ADC_TM_LOW_THR_DISABLE,
	ADC_TM_WARM_THR_DISABLE = ADC_TM_LOW_THR_DISABLE,
	ADC_TM_HIGH_LOW_THR_DISABLE,
	ADC_TM_THR_NUM,
};

/**
 * 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_sensor {
	struct adc_tm_chip		*chip;
	struct adc_tm_chip		*chip;
	struct thermal_zone_device	*tzd;
	struct thermal_zone_device	*tzd;
@@ -63,7 +131,36 @@ struct adc_tm_sensor {
	unsigned int			btm_ch;
	unsigned int			btm_ch;
	unsigned int			prescaling;
	unsigned int			prescaling;
	unsigned int			timer_select;
	unsigned int			timer_select;
	enum adc_tm_rscale_fn_type	adc_rscale_fn;
	struct iio_channel		*adc;
	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_param {
	int			low_thr;
	int			high_thr;
	uint32_t				channel;
	enum adc_tm_state_request	state_request;
	void					*btm_ctx;
	void	(*threshold_notification)(enum adc_tm_state state,
						void *ctx);
};

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 {
struct adc_tm_cmn_prop {
@@ -89,6 +186,7 @@ struct adc_tm_chip {
	u16				base;
	u16				base;
	struct adc_tm_cmn_prop		prop;
	struct adc_tm_cmn_prop		prop;
	spinlock_t			adc_tm_lock;
	spinlock_t			adc_tm_lock;
	struct mutex		adc_mutex_lock;
	const struct adc_tm_ops		*ops;
	const struct adc_tm_ops		*ops;
	const struct adc_tm_data	*data;
	const struct adc_tm_data	*data;
	unsigned int			dt_channels;
	unsigned int			dt_channels;
@@ -182,12 +280,23 @@ struct adc_tm_trip_reg_type {
struct adc_tm_config {
struct adc_tm_config {
	int	channel;
	int	channel;
	int	adc_code;
	int	adc_code;
	int	prescal;
	int	high_thr_temp;
	int	high_thr_temp;
	int	low_thr_temp;
	int	low_thr_temp;
	int64_t	high_thr_voltage;
	int64_t	high_thr_voltage;
	int64_t	low_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 *,
		struct adc_tm_config *);
};

/**
/**
 * struct adc_map_pt - Map the graph representation for ADC channel
 * struct adc_map_pt - Map the graph representation for ADC channel
 * @x: Represent the ADC digitized code.
 * @x: Represent the ADC digitized code.
@@ -217,4 +326,13 @@ struct adc_tm_linear_graph {
void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param,
				const struct adc_tm_data *data);
				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);

struct adc_tm_chip *get_adc_tm(struct device *dev, const char *name);

int adc_tm_is_valid(struct adc_tm_chip *chip);

#endif /* __QCOM_ADC_TM_H__ */
#endif /* __QCOM_ADC_TM_H__ */
+496 −9
Original line number Original line Diff line number Diff line
@@ -76,6 +76,10 @@ static struct adc_tm_trip_reg_type adc_tm_ch_data[] = {
	[ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL},
	[ADC_TM_CHAN7] = {ADC_TM_M7_ADC_CH_SEL_CTL},
};
};


static struct adc_tm_reverse_scale_fn adc_tm_rscale_fn[] = {
	[SCALE_R_ABSOLUTE] = {adc_tm_absolute_rthr},
};

static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp)
static int adc_tm5_get_temp(struct adc_tm_sensor *sensor, int *temp)
{
{
	int ret, milli_celsius;
	int ret, milli_celsius;
@@ -219,6 +223,441 @@ static int adc_tm5_configure(struct adc_tm_sensor *sensor,
	return 0;
	return 0;
}
}


static int32_t adc_tm_add_to_list(struct adc_tm_chip *chip,
				uint32_t dt_index,
				struct adc_tm_param *param)
{
	struct adc_tm_client_info *client_info = NULL;
	bool client_info_exists = false;

	list_for_each_entry(client_info,
			&chip->sensor[dt_index].thr_list, list) {
		if (client_info->param == param) {
			client_info->low_thr_requested = param->low_thr;
			client_info->high_thr_requested = param->high_thr;
			client_info->state_request = param->state_request;
			client_info->notify_low_thr = false;
			client_info->notify_high_thr = false;
			client_info_exists = true;
			pr_debug("client found\n");
		}
	}

	if (!client_info_exists) {
		client_info = devm_kzalloc(chip->dev,
			sizeof(struct adc_tm_client_info), GFP_KERNEL);
		if (!client_info)
			return -ENOMEM;

		pr_debug("new client\n");
		client_info->param = param;
		client_info->low_thr_requested = param->low_thr;
		client_info->high_thr_requested = param->high_thr;
		client_info->state_request = param->state_request;

		list_add_tail(&client_info->list,
					&chip->sensor[dt_index].thr_list);
	}
	return 0;
}

static int32_t adc_tm5_thr_update(struct adc_tm_sensor *sensor,
			int32_t high_thr, int32_t low_thr)
{
	int ret = 0;
	u8 trip_low_thr[2], trip_high_thr[2];
	uint16_t reg_low_thr_lsb, reg_high_thr_lsb;
	uint32_t scale_type = 0, mask = 0, btm_chan_idx = 0;
	struct adc_tm_config tm_config;
	struct adc_tm_chip *chip;

	ret = adc_tm5_get_btm_idx(chip,
		sensor->btm_ch, &btm_chan_idx);
	if (ret < 0) {
		pr_err("Invalid btm channel idx\n");
		return ret;
	}

	chip = sensor->chip;

	tm_config.high_thr_voltage = (int64_t)high_thr;
	tm_config.low_thr_voltage = (int64_t)low_thr;
	tm_config.prescal = sensor->prescaling;

	scale_type = sensor->adc_rscale_fn;
	if (scale_type >= SCALE_RSCALE_NONE) {
		ret = -EBADF;
		return ret;
	}

	adc_tm_rscale_fn[scale_type].chan(chip->data, &tm_config);

	mask = lower_32_bits(tm_config.high_thr_voltage);
	trip_high_thr[0] = ADC_TM_LOWER_MASK(mask);
	trip_high_thr[1] = ADC_TM_UPPER_MASK(mask);

	mask = lower_32_bits(tm_config.low_thr_voltage);
	trip_low_thr[0] = ADC_TM_LOWER_MASK(mask);
	trip_low_thr[1] = ADC_TM_UPPER_MASK(mask);

	pr_debug("high_thr:0x%llx, low_thr:0x%llx\n",
		tm_config.high_thr_voltage, tm_config.low_thr_voltage);

	reg_low_thr_lsb = ADC_TM_Mn_LOW_THR0(btm_chan_idx);
	reg_high_thr_lsb = ADC_TM_Mn_HIGH_THR0(btm_chan_idx);

	if (low_thr != INT_MIN) {
		ret = adc_tm5_write_reg(chip, reg_low_thr_lsb,
						trip_low_thr, 2);
		if (ret) {
			pr_err("Low set threshold err\n");
			return ret;
		}
	}

	if (high_thr != INT_MAX) {
		ret = adc_tm5_write_reg(chip, reg_high_thr_lsb,
						trip_high_thr, 2);
		if (ret) {
			pr_err("High set threshold err\n");
			return ret;
		}
	}
	return ret;
}

static int32_t adc_tm5_manage_thresholds(struct adc_tm_sensor *sensor)
{
	int ret = 0, high_thr = INT_MAX, low_thr = INT_MIN;
	struct adc_tm_client_info *client_info = NULL;
	struct list_head *thr_list;
	uint32_t btm_chan_idx = 0;
	struct adc_tm_chip *chip = sensor->chip;

	ret = adc_tm5_get_btm_idx(chip, sensor->btm_ch, &btm_chan_idx);
	if (ret < 0) {
		pr_err("Invalid btm channel idx with %d\n", ret);
		return ret;
	}
	/*
	 * Reset the high_thr_set and low_thr_set of all
	 * clients since the thresholds will be recomputed.
	 */
	list_for_each(thr_list, &sensor->thr_list) {
		client_info = list_entry(thr_list,
					struct adc_tm_client_info, list);
		client_info->high_thr_set = false;
		client_info->low_thr_set = false;
	}

	/* Find the min of high_thr and max of low_thr */
	list_for_each(thr_list, &sensor->thr_list) {
		client_info = list_entry(thr_list,
					struct adc_tm_client_info, list);

		if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) ||
			(client_info->state_request ==
				ADC_TM_HIGH_LOW_THR_ENABLE))
			if (client_info->high_thr_requested < high_thr)
				high_thr = client_info->high_thr_requested;

		if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) ||
			(client_info->state_request ==
				ADC_TM_HIGH_LOW_THR_ENABLE))
			if (client_info->low_thr_requested > low_thr)
				low_thr = client_info->low_thr_requested;

		pr_debug("threshold compared is high:%d and low:%d\n",
				client_info->high_thr_requested,
				client_info->low_thr_requested);
		pr_debug("current threshold is high:%d and low:%d\n",
							high_thr, low_thr);
	}

	/* Check which of the high_thr and low_thr got set */
	list_for_each(thr_list, &sensor->thr_list) {
		client_info = list_entry(thr_list,
					struct adc_tm_client_info, list);

		if ((client_info->state_request == ADC_TM_HIGH_THR_ENABLE) ||
			(client_info->state_request ==
				ADC_TM_HIGH_LOW_THR_ENABLE))
			if (high_thr == client_info->high_thr_requested)
				client_info->high_thr_set = true;

		if ((client_info->state_request == ADC_TM_LOW_THR_ENABLE) ||
			(client_info->state_request ==
				ADC_TM_HIGH_LOW_THR_ENABLE))
			if (low_thr == client_info->low_thr_requested)
				client_info->low_thr_set = true;
	}

	ret = adc_tm5_thr_update(sensor, high_thr, low_thr);
	if (ret < 0)
		pr_err("setting chan:%d threshold failed\n", btm_chan_idx);

	pr_debug("threshold written is high:%d and low:%d\n",
							high_thr, low_thr);

	return 0;
}

void notify_adc_tm_fn(struct work_struct *work)
{
	struct adc_tm_client_info *client_info = NULL;
	struct adc_tm_chip *chip;
	struct list_head *thr_list;
	uint32_t btm_chan_num = 0, btm_chan_idx = 0;
	int ret = 0;

	struct adc_tm_sensor *adc_tm = container_of(work,
		struct adc_tm_sensor, work);

	chip = adc_tm->chip;

	btm_chan_num = adc_tm->btm_ch;
	ret = adc_tm5_get_btm_idx(chip, btm_chan_num, &btm_chan_idx);
	if (ret < 0) {
		pr_err("Invalid btm channel idx\n");
		return;
	}

	mutex_lock(&chip->adc_mutex_lock);

	if (adc_tm->low_thr_triggered) {
		/* adjust thr, calling manage_thr */
		list_for_each(thr_list, &adc_tm->thr_list) {
			client_info = list_entry(thr_list,
					struct adc_tm_client_info, list);
			if (client_info->low_thr_set) {
				client_info->low_thr_set = false;
				client_info->notify_low_thr = true;
				if (client_info->state_request ==
						ADC_TM_HIGH_LOW_THR_ENABLE)
					client_info->state_request =
							ADC_TM_HIGH_THR_ENABLE;
				else
					client_info->state_request =
							ADC_TM_LOW_THR_DISABLE;
			}
		}
		adc_tm5_manage_thresholds(adc_tm);

		adc_tm->low_thr_triggered = false;
	}

	if (adc_tm->high_thr_triggered) {
		/* adjust thr, calling manage_thr */
		list_for_each(thr_list, &adc_tm->thr_list) {
			client_info = list_entry(thr_list,
					struct adc_tm_client_info, list);
			if (client_info->high_thr_set) {
				client_info->high_thr_set = false;
				client_info->notify_high_thr = true;
				if (client_info->state_request ==
						ADC_TM_HIGH_LOW_THR_ENABLE)
					client_info->state_request =
							ADC_TM_LOW_THR_ENABLE;
				else
					client_info->state_request =
							ADC_TM_HIGH_THR_DISABLE;
			}
		}
		adc_tm5_manage_thresholds(adc_tm);

		adc_tm->high_thr_triggered = false;
	}
	mutex_unlock(&chip->adc_mutex_lock);

	list_for_each_entry(client_info, &adc_tm->thr_list, list) {
		if (client_info->notify_low_thr) {
			if (client_info->param->threshold_notification
								!= NULL) {
				pr_debug("notify kernel with low state\n");
				client_info->param->threshold_notification(
					ADC_TM_LOW_STATE,
					client_info->param->btm_ctx);
				client_info->notify_low_thr = false;
			}
		}

		if (client_info->notify_high_thr) {
			if (client_info->param->threshold_notification
								!= NULL) {
				pr_debug("notify kernel with high state\n");
				client_info->param->threshold_notification(
					ADC_TM_HIGH_STATE,
					client_info->param->btm_ctx);
				client_info->notify_high_thr = false;
			}
		}
	}
}

int32_t adc_tm5_channel_measure(struct adc_tm_chip *chip,
					struct adc_tm_param *param)

{
	int ret = 0, i = 0;
	uint32_t channel, dt_index = 0, btm_chan_idx = 0;
	bool chan_found = false, high_thr_set = false, low_thr_set = false;
	struct adc_tm_client_info *client_info = NULL;

	ret = adc_tm_is_valid(chip);
	if (ret || (param == NULL))
		return -EINVAL;

	if (param->threshold_notification == NULL) {
		pr_debug("No notification for high/low temp\n");
		return -EINVAL;
	}

	mutex_lock(&chip->adc_mutex_lock);

	channel = param->channel;

	while (i < chip->dt_channels) {
		if (chip->sensor[i].adc_ch == channel) {
			dt_index = i;
			chan_found = true;
			break;
		}
		i++;
	}

	if (!chan_found)  {
		pr_err("not a valid ADC_TM channel\n");
		ret = -EINVAL;
		goto fail_unlock;
	}

	ret = adc_tm5_get_btm_idx(chip,
		chip->sensor[dt_index].btm_ch, &btm_chan_idx);
	if (ret < 0) {
		pr_err("Invalid btm channel idx with %d\n", ret);
		goto fail_unlock;
	}

	/* add channel client to channel list */
	adc_tm_add_to_list(chip, dt_index, param);

	/* set right thresholds for the sensor */
	adc_tm5_manage_thresholds(&chip->sensor[dt_index]);

	/* enable low/high irqs */
	list_for_each_entry(client_info,
			&chip->sensor[dt_index].thr_list, list) {
		if (client_info->high_thr_set == true)
			high_thr_set = true;
		if (client_info->low_thr_set == true)
			low_thr_set = true;
	}

	if (low_thr_set) {
		/* Enable low threshold's interrupt */
		pr_debug("low sensor:%x with state:%d\n",
				dt_index, param->state_request);
		ret = adc_tm5_reg_update(chip,
			ADC_TM_Mn_EN(btm_chan_idx),
			ADC_TM_Mn_LOW_THR_INT_EN, true);
		if (ret < 0) {
			pr_err("low thr enable err:%d\n",
				chip->sensor[dt_index].btm_ch);
			goto fail_unlock;
		}
	}

	if (high_thr_set) {
		/* Enable high threshold's interrupt */
		pr_debug("high sensor mask:%x with state:%d\n",
			dt_index, param->state_request);
		ret = adc_tm5_reg_update(chip,
			ADC_TM_Mn_EN(btm_chan_idx),
			ADC_TM_Mn_HIGH_THR_INT_EN, true);
		if (ret < 0) {
			pr_err("high thr enable err:%d\n",
				chip->sensor[dt_index].btm_ch);
			goto fail_unlock;
		}
	}

	/* configure channel */
	ret = adc_tm5_configure(&chip->sensor[dt_index], btm_chan_idx);
	if (ret < 0) {
		pr_err("Error during adc-tm configure:%d\n", ret);
		goto fail_unlock;
	}

	ret = adc_tm5_enable(chip);
	if (ret < 0)
		pr_err("Error enabling adc-tm with %d\n", ret);

fail_unlock:
	mutex_unlock(&chip->adc_mutex_lock);
	return ret;
}
EXPORT_SYMBOL(adc_tm5_channel_measure);

int32_t adc_tm5_disable_chan_meas(struct adc_tm_chip *chip,
					struct adc_tm_param *param)
{
	int ret = 0, i = 0;
	uint32_t channel, dt_index = 0, btm_chan_idx = 0;
	unsigned long flags;

	ret = adc_tm_is_valid(chip);
	if (ret || (param == NULL))
		return -EINVAL;

	channel = param->channel;

	while (i < chip->dt_channels) {
		if (chip->sensor[i].adc_ch == channel) {
			dt_index = i;
			break;
		}
		i++;
	}

	if (i == chip->dt_channels)  {
		pr_err("not a valid ADC_TM channel\n");
		return -EINVAL;
	}

	ret = adc_tm5_get_btm_idx(chip,
		chip->sensor[dt_index].btm_ch, &btm_chan_idx);
	if (ret < 0) {
		pr_err("Invalid btm channel idx with %d\n", ret);
		return ret;
	}

	spin_lock_irqsave(&chip->adc_tm_lock, flags);

	ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx),
				ADC_TM_Mn_HIGH_THR_INT_EN, false);
	if (ret < 0) {
		pr_err("high thr disable err\n");
		goto fail;
	}

	ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx),
			ADC_TM_Mn_LOW_THR_INT_EN, false);
	if (ret < 0) {
		pr_err("low thr disable err\n");
		goto fail;
	}

	ret = adc_tm5_reg_update(chip, ADC_TM_Mn_EN(btm_chan_idx),
			ADC_TM_Mn_MEAS_EN, false);
	if (ret < 0)
		pr_err("multi measurement disable failed\n");

fail:
	spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
	return ret;
}
EXPORT_SYMBOL(adc_tm5_disable_chan_meas);

static int adc_tm5_set_mode(struct adc_tm_sensor *sensor,
static int adc_tm5_set_mode(struct adc_tm_sensor *sensor,
			      enum thermal_device_mode mode)
			      enum thermal_device_mode mode)
{
{
@@ -424,7 +863,7 @@ static irqreturn_t adc_tm5_handler(int irq, void *data)
{
{
	struct adc_tm_chip *chip = data;
	struct adc_tm_chip *chip = data;
	u8 status_low, status_high, ctl;
	u8 status_low, status_high, ctl;
	int ret, i = 0;
	int ret = 0, i = 0;
	unsigned long flags;
	unsigned long flags;


	ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1);
	ret = adc_tm5_read_reg(chip, ADC_TM_STATUS_LOW, &status_low, 1);
@@ -443,12 +882,20 @@ static irqreturn_t adc_tm5_handler(int irq, void *data)
		bool upper_set = false, lower_set = false;
		bool upper_set = false, lower_set = false;
		int temp;
		int temp;


		if (IS_ERR(chip->sensor[i].tzd))
		if (!chip->sensor[i].non_thermal &&
				IS_ERR(chip->sensor[i].tzd)) {
			pr_err("thermal device not found\n");
			i++;
			continue;
			continue;
		}


		if (!chip->sensor[i].non_thermal) {
			ret = adc_tm5_get_temp(&chip->sensor[i], &temp);
			ret = adc_tm5_get_temp(&chip->sensor[i], &temp);
		if (ret < 0)
			if (ret < 0) {
				i++;
				continue;
				continue;
			}
		}


		spin_lock_irqsave(&chip->adc_tm_lock, flags);
		spin_lock_irqsave(&chip->adc_tm_lock, flags);


@@ -469,14 +916,53 @@ static irqreturn_t adc_tm5_handler(int irq, void *data)
		status_low >>= 1;
		status_low >>= 1;
		status_high >>= 1;
		status_high >>= 1;
		spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
		spin_unlock_irqrestore(&chip->adc_tm_lock, flags);
		if (upper_set || lower_set) {
		if (!(upper_set || lower_set)) {
			i++;
			continue;
		}

		if (!chip->sensor[i].non_thermal) {
			/*
			/*
			 * Expected behavior is while notifying of_thermal,
			 * Expected behavior is while notifying
			 * thermal core will call set_trips with new thresholds
			 * of_thermal, thermal core will call set_trips
			 * and activate/disable the appropriate trips.
			 * with new thresholds and activate/disable
			 * the appropriate trips.
			 */
			 */
			pr_debug("notifying of_thermal\n");
			pr_debug("notifying of_thermal\n");
			of_thermal_handle_trip(chip->sensor[i].tzd);
			of_thermal_handle_trip(chip->sensor[i].tzd);
		} else {
			if (lower_set) {
				ret = adc_tm5_reg_update(chip,
					ADC_TM_Mn_EN(i),
					ADC_TM_Mn_LOW_THR_INT_EN,
					false);
				if (ret < 0) {
					pr_err("low thr disable failed\n");
					return IRQ_HANDLED;
				}

				chip->sensor[i].low_thr_triggered
				= true;

				queue_work(chip->sensor[i].req_wq,
						&chip->sensor[i].work);
			}

			if (upper_set) {
				ret = adc_tm5_reg_update(chip,
					ADC_TM_Mn_EN(i),
					ADC_TM_Mn_HIGH_THR_INT_EN,
					false);
				if (ret < 0) {
					pr_err("high thr disable failed\n");
					return IRQ_HANDLED;
				}

				chip->sensor[i].high_thr_triggered = true;

				queue_work(chip->sensor[i].req_wq,
						&chip->sensor[i].work);
			}
		}
		}
		i++;
		i++;
	}
	}
@@ -561,6 +1047,7 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans)
		pr_err("adc-tm block write failed with %d\n", ret);
		pr_err("adc-tm block write failed with %d\n", ret);


	spin_lock_init(&chip->adc_tm_lock);
	spin_lock_init(&chip->adc_tm_lock);
	mutex_init(&chip->adc_mutex_lock);


	if (chip->pmic_rev_id) {
	if (chip->pmic_rev_id) {
		switch (chip->pmic_rev_id->pmic_subtype)
		switch (chip->pmic_rev_id->pmic_subtype)