162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2019, 2020, Linaro Ltd.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/debugfs.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/of_platform.h>
1562306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/thermal.h>
2162306a36Sopenharmony_ci#include "../thermal_hwmon.h"
2262306a36Sopenharmony_ci#include "tsens.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * struct tsens_irq_data - IRQ status and temperature violations
2662306a36Sopenharmony_ci * @up_viol:        upper threshold violated
2762306a36Sopenharmony_ci * @up_thresh:      upper threshold temperature value
2862306a36Sopenharmony_ci * @up_irq_mask:    mask register for upper threshold irqs
2962306a36Sopenharmony_ci * @up_irq_clear:   clear register for uppper threshold irqs
3062306a36Sopenharmony_ci * @low_viol:       lower threshold violated
3162306a36Sopenharmony_ci * @low_thresh:     lower threshold temperature value
3262306a36Sopenharmony_ci * @low_irq_mask:   mask register for lower threshold irqs
3362306a36Sopenharmony_ci * @low_irq_clear:  clear register for lower threshold irqs
3462306a36Sopenharmony_ci * @crit_viol:      critical threshold violated
3562306a36Sopenharmony_ci * @crit_thresh:    critical threshold temperature value
3662306a36Sopenharmony_ci * @crit_irq_mask:  mask register for critical threshold irqs
3762306a36Sopenharmony_ci * @crit_irq_clear: clear register for critical threshold irqs
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * Structure containing data about temperature threshold settings and
4062306a36Sopenharmony_ci * irq status if they were violated.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistruct tsens_irq_data {
4362306a36Sopenharmony_ci	u32 up_viol;
4462306a36Sopenharmony_ci	int up_thresh;
4562306a36Sopenharmony_ci	u32 up_irq_mask;
4662306a36Sopenharmony_ci	u32 up_irq_clear;
4762306a36Sopenharmony_ci	u32 low_viol;
4862306a36Sopenharmony_ci	int low_thresh;
4962306a36Sopenharmony_ci	u32 low_irq_mask;
5062306a36Sopenharmony_ci	u32 low_irq_clear;
5162306a36Sopenharmony_ci	u32 crit_viol;
5262306a36Sopenharmony_ci	u32 crit_thresh;
5362306a36Sopenharmony_ci	u32 crit_irq_mask;
5462306a36Sopenharmony_ci	u32 crit_irq_clear;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cichar *qfprom_read(struct device *dev, const char *cname)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct nvmem_cell *cell;
6062306a36Sopenharmony_ci	ssize_t data;
6162306a36Sopenharmony_ci	char *ret;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	cell = nvmem_cell_get(dev, cname);
6462306a36Sopenharmony_ci	if (IS_ERR(cell))
6562306a36Sopenharmony_ci		return ERR_CAST(cell);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	ret = nvmem_cell_read(cell, &data);
6862306a36Sopenharmony_ci	nvmem_cell_put(cell);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint tsens_read_calibration(struct tsens_priv *priv, int shift, u32 *p1, u32 *p2, bool backup)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	u32 mode;
7662306a36Sopenharmony_ci	u32 base1, base2;
7762306a36Sopenharmony_ci	char name[] = "sXX_pY_backup"; /* s10_p1_backup */
7862306a36Sopenharmony_ci	int i, ret;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (priv->num_sensors > MAX_SENSORS)
8162306a36Sopenharmony_ci		return -EINVAL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ret = snprintf(name, sizeof(name), "mode%s", backup ? "_backup" : "");
8462306a36Sopenharmony_ci	if (ret < 0)
8562306a36Sopenharmony_ci		return ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &mode);
8862306a36Sopenharmony_ci	if (ret == -ENOENT)
8962306a36Sopenharmony_ci		dev_warn(priv->dev, "Please migrate to separate nvmem cells for calibration data\n");
9062306a36Sopenharmony_ci	if (ret < 0)
9162306a36Sopenharmony_ci		return ret;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dev_dbg(priv->dev, "calibration mode is %d\n", mode);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	ret = snprintf(name, sizeof(name), "base1%s", backup ? "_backup" : "");
9662306a36Sopenharmony_ci	if (ret < 0)
9762306a36Sopenharmony_ci		return ret;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base1);
10062306a36Sopenharmony_ci	if (ret < 0)
10162306a36Sopenharmony_ci		return ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ret = snprintf(name, sizeof(name), "base2%s", backup ? "_backup" : "");
10462306a36Sopenharmony_ci	if (ret < 0)
10562306a36Sopenharmony_ci		return ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &base2);
10862306a36Sopenharmony_ci	if (ret < 0)
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (i = 0; i < priv->num_sensors; i++) {
11262306a36Sopenharmony_ci		ret = snprintf(name, sizeof(name), "s%d_p1%s", priv->sensor[i].hw_id,
11362306a36Sopenharmony_ci			       backup ? "_backup" : "");
11462306a36Sopenharmony_ci		if (ret < 0)
11562306a36Sopenharmony_ci			return ret;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p1[i]);
11862306a36Sopenharmony_ci		if (ret)
11962306a36Sopenharmony_ci			return ret;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		ret = snprintf(name, sizeof(name), "s%d_p2%s", priv->sensor[i].hw_id,
12262306a36Sopenharmony_ci			       backup ? "_backup" : "");
12362306a36Sopenharmony_ci		if (ret < 0)
12462306a36Sopenharmony_ci			return ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		ret = nvmem_cell_read_variable_le_u32(priv->dev, name, &p2[i]);
12762306a36Sopenharmony_ci		if (ret)
12862306a36Sopenharmony_ci			return ret;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	switch (mode) {
13262306a36Sopenharmony_ci	case ONE_PT_CALIB:
13362306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
13462306a36Sopenharmony_ci			p1[i] = p1[i] + (base1 << shift);
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case TWO_PT_CALIB:
13762306a36Sopenharmony_ci	case TWO_PT_CALIB_NO_OFFSET:
13862306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
13962306a36Sopenharmony_ci			p2[i] = (p2[i] + base2) << shift;
14062306a36Sopenharmony_ci		fallthrough;
14162306a36Sopenharmony_ci	case ONE_PT_CALIB2:
14262306a36Sopenharmony_ci	case ONE_PT_CALIB2_NO_OFFSET:
14362306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
14462306a36Sopenharmony_ci			p1[i] = (p1[i] + base1) << shift;
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	default:
14762306a36Sopenharmony_ci		dev_dbg(priv->dev, "calibrationless mode\n");
14862306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++) {
14962306a36Sopenharmony_ci			p1[i] = 500;
15062306a36Sopenharmony_ci			p2[i] = 780;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Apply calibration offset workaround except for _NO_OFFSET modes */
15562306a36Sopenharmony_ci	switch (mode) {
15662306a36Sopenharmony_ci	case TWO_PT_CALIB:
15762306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
15862306a36Sopenharmony_ci			p2[i] += priv->sensor[i].p2_calib_offset;
15962306a36Sopenharmony_ci		fallthrough;
16062306a36Sopenharmony_ci	case ONE_PT_CALIB2:
16162306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
16262306a36Sopenharmony_ci			p1[i] += priv->sensor[i].p1_calib_offset;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return mode;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciint tsens_calibrate_nvmem(struct tsens_priv *priv, int shift)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	u32 p1[MAX_SENSORS], p2[MAX_SENSORS];
17262306a36Sopenharmony_ci	int mode;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	mode = tsens_read_calibration(priv, shift, p1, p2, false);
17562306a36Sopenharmony_ci	if (mode < 0)
17662306a36Sopenharmony_ci		return mode;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	compute_intercept_slope(priv, p1, p2, mode);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciint tsens_calibrate_common(struct tsens_priv *priv)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return tsens_calibrate_nvmem(priv, 2);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic u32 tsens_read_cell(const struct tsens_single_value *cell, u8 len, u32 *data0, u32 *data1)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u32 val;
19162306a36Sopenharmony_ci	u32 *data = cell->blob ? data1 : data0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (cell->shift + len <= 32) {
19462306a36Sopenharmony_ci		val = data[cell->idx] >> cell->shift;
19562306a36Sopenharmony_ci	} else {
19662306a36Sopenharmony_ci		u8 part = 32 - cell->shift;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		val = data[cell->idx] >> cell->shift;
19962306a36Sopenharmony_ci		val |= data[cell->idx + 1] << part;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return val & ((1 << len) - 1);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciint tsens_read_calibration_legacy(struct tsens_priv *priv,
20662306a36Sopenharmony_ci				  const struct tsens_legacy_calibration_format *format,
20762306a36Sopenharmony_ci				  u32 *p1, u32 *p2,
20862306a36Sopenharmony_ci				  u32 *cdata0, u32 *cdata1)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	u32 mode, invalid;
21162306a36Sopenharmony_ci	u32 base1, base2;
21262306a36Sopenharmony_ci	int i;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mode = tsens_read_cell(&format->mode, 2, cdata0, cdata1);
21562306a36Sopenharmony_ci	invalid = tsens_read_cell(&format->invalid, 1, cdata0, cdata1);
21662306a36Sopenharmony_ci	if (invalid)
21762306a36Sopenharmony_ci		mode = NO_PT_CALIB;
21862306a36Sopenharmony_ci	dev_dbg(priv->dev, "calibration mode is %d\n", mode);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	base1 = tsens_read_cell(&format->base[0], format->base_len, cdata0, cdata1);
22162306a36Sopenharmony_ci	base2 = tsens_read_cell(&format->base[1], format->base_len, cdata0, cdata1);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	for (i = 0; i < priv->num_sensors; i++) {
22462306a36Sopenharmony_ci		p1[i] = tsens_read_cell(&format->sp[i][0], format->sp_len, cdata0, cdata1);
22562306a36Sopenharmony_ci		p2[i] = tsens_read_cell(&format->sp[i][1], format->sp_len, cdata0, cdata1);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	switch (mode) {
22962306a36Sopenharmony_ci	case ONE_PT_CALIB:
23062306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
23162306a36Sopenharmony_ci			p1[i] = p1[i] + (base1 << format->base_shift);
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci	case TWO_PT_CALIB:
23462306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
23562306a36Sopenharmony_ci			p2[i] = (p2[i] + base2) << format->base_shift;
23662306a36Sopenharmony_ci		fallthrough;
23762306a36Sopenharmony_ci	case ONE_PT_CALIB2:
23862306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++)
23962306a36Sopenharmony_ci			p1[i] = (p1[i] + base1) << format->base_shift;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	default:
24262306a36Sopenharmony_ci		dev_dbg(priv->dev, "calibrationless mode\n");
24362306a36Sopenharmony_ci		for (i = 0; i < priv->num_sensors; i++) {
24462306a36Sopenharmony_ci			p1[i] = 500;
24562306a36Sopenharmony_ci			p2[i] = 780;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return mode;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/*
25362306a36Sopenharmony_ci * Use this function on devices where slope and offset calculations
25462306a36Sopenharmony_ci * depend on calibration data read from qfprom. On others the slope
25562306a36Sopenharmony_ci * and offset values are derived from tz->tzp->slope and tz->tzp->offset
25662306a36Sopenharmony_ci * resp.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_civoid compute_intercept_slope(struct tsens_priv *priv, u32 *p1,
25962306a36Sopenharmony_ci			     u32 *p2, u32 mode)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	int i;
26262306a36Sopenharmony_ci	int num, den;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for (i = 0; i < priv->num_sensors; i++) {
26562306a36Sopenharmony_ci		dev_dbg(priv->dev,
26662306a36Sopenharmony_ci			"%s: sensor%d - data_point1:%#x data_point2:%#x\n",
26762306a36Sopenharmony_ci			__func__, i, p1[i], p2[i]);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		if (!priv->sensor[i].slope)
27062306a36Sopenharmony_ci			priv->sensor[i].slope = SLOPE_DEFAULT;
27162306a36Sopenharmony_ci		if (mode == TWO_PT_CALIB || mode == TWO_PT_CALIB_NO_OFFSET) {
27262306a36Sopenharmony_ci			/*
27362306a36Sopenharmony_ci			 * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
27462306a36Sopenharmony_ci			 *	temp_120_degc - temp_30_degc (x2 - x1)
27562306a36Sopenharmony_ci			 */
27662306a36Sopenharmony_ci			num = p2[i] - p1[i];
27762306a36Sopenharmony_ci			num *= SLOPE_FACTOR;
27862306a36Sopenharmony_ci			den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
27962306a36Sopenharmony_ci			priv->sensor[i].slope = num / den;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
28362306a36Sopenharmony_ci				(CAL_DEGC_PT1 *
28462306a36Sopenharmony_ci				priv->sensor[i].slope);
28562306a36Sopenharmony_ci		dev_dbg(priv->dev, "%s: offset:%d\n", __func__,
28662306a36Sopenharmony_ci			priv->sensor[i].offset);
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic inline u32 degc_to_code(int degc, const struct tsens_sensor *s)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	u64 code = div_u64(((u64)degc * s->slope + s->offset), SLOPE_FACTOR);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	pr_debug("%s: raw_code: 0x%llx, degc:%d\n", __func__, code, degc);
29562306a36Sopenharmony_ci	return clamp_val(code, THRESHOLD_MIN_ADC_CODE, THRESHOLD_MAX_ADC_CODE);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	int degc, num, den;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	num = (adc_code * SLOPE_FACTOR) - s->offset;
30362306a36Sopenharmony_ci	den = s->slope;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (num > 0)
30662306a36Sopenharmony_ci		degc = num + (den / 2);
30762306a36Sopenharmony_ci	else if (num < 0)
30862306a36Sopenharmony_ci		degc = num - (den / 2);
30962306a36Sopenharmony_ci	else
31062306a36Sopenharmony_ci		degc = num;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	degc /= den;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return degc;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/**
31862306a36Sopenharmony_ci * tsens_hw_to_mC - Return sign-extended temperature in mCelsius.
31962306a36Sopenharmony_ci * @s:     Pointer to sensor struct
32062306a36Sopenharmony_ci * @field: Index into regmap_field array pointing to temperature data
32162306a36Sopenharmony_ci *
32262306a36Sopenharmony_ci * This function handles temperature returned in ADC code or deciCelsius
32362306a36Sopenharmony_ci * depending on IP version.
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * Return: Temperature in milliCelsius on success, a negative errno will
32662306a36Sopenharmony_ci * be returned in error cases
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic int tsens_hw_to_mC(const struct tsens_sensor *s, int field)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
33162306a36Sopenharmony_ci	u32 resolution;
33262306a36Sopenharmony_ci	u32 temp = 0;
33362306a36Sopenharmony_ci	int ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	resolution = priv->fields[LAST_TEMP_0].msb -
33662306a36Sopenharmony_ci		priv->fields[LAST_TEMP_0].lsb;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[field], &temp);
33962306a36Sopenharmony_ci	if (ret)
34062306a36Sopenharmony_ci		return ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Convert temperature from ADC code to milliCelsius */
34362306a36Sopenharmony_ci	if (priv->feat->adc)
34462306a36Sopenharmony_ci		return code_to_degc(temp, s) * 1000;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* deciCelsius -> milliCelsius along with sign extension */
34762306a36Sopenharmony_ci	return sign_extend32(temp, resolution) * 100;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/**
35162306a36Sopenharmony_ci * tsens_mC_to_hw - Convert temperature to hardware register value
35262306a36Sopenharmony_ci * @s: Pointer to sensor struct
35362306a36Sopenharmony_ci * @temp: temperature in milliCelsius to be programmed to hardware
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * This function outputs the value to be written to hardware in ADC code
35662306a36Sopenharmony_ci * or deciCelsius depending on IP version.
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * Return: ADC code or temperature in deciCelsius.
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_cistatic int tsens_mC_to_hw(const struct tsens_sensor *s, int temp)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* milliC to adc code */
36562306a36Sopenharmony_ci	if (priv->feat->adc)
36662306a36Sopenharmony_ci		return degc_to_code(temp / 1000, s);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* milliC to deciC */
36962306a36Sopenharmony_ci	return temp / 100;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic inline enum tsens_ver tsens_version(struct tsens_priv *priv)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	return priv->feat->ver_major;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
37862306a36Sopenharmony_ci				   enum tsens_irq_type irq_type, bool enable)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	u32 index = 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	switch (irq_type) {
38362306a36Sopenharmony_ci	case UPPER:
38462306a36Sopenharmony_ci		index = UP_INT_CLEAR_0 + hw_id;
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci	case LOWER:
38762306a36Sopenharmony_ci		index = LOW_INT_CLEAR_0 + hw_id;
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case CRITICAL:
39062306a36Sopenharmony_ci		/* No critical interrupts before v2 */
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci	regmap_field_write(priv->rf[index], enable ? 0 : 1);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
39762306a36Sopenharmony_ci				   enum tsens_irq_type irq_type, bool enable)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	u32 index_mask = 0, index_clear = 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/*
40262306a36Sopenharmony_ci	 * To enable the interrupt flag for a sensor:
40362306a36Sopenharmony_ci	 *    - clear the mask bit
40462306a36Sopenharmony_ci	 * To disable the interrupt flag for a sensor:
40562306a36Sopenharmony_ci	 *    - Mask further interrupts for this sensor
40662306a36Sopenharmony_ci	 *    - Write 1 followed by 0 to clear the interrupt
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	switch (irq_type) {
40962306a36Sopenharmony_ci	case UPPER:
41062306a36Sopenharmony_ci		index_mask  = UP_INT_MASK_0 + hw_id;
41162306a36Sopenharmony_ci		index_clear = UP_INT_CLEAR_0 + hw_id;
41262306a36Sopenharmony_ci		break;
41362306a36Sopenharmony_ci	case LOWER:
41462306a36Sopenharmony_ci		index_mask  = LOW_INT_MASK_0 + hw_id;
41562306a36Sopenharmony_ci		index_clear = LOW_INT_CLEAR_0 + hw_id;
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci	case CRITICAL:
41862306a36Sopenharmony_ci		index_mask  = CRIT_INT_MASK_0 + hw_id;
41962306a36Sopenharmony_ci		index_clear = CRIT_INT_CLEAR_0 + hw_id;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (enable) {
42462306a36Sopenharmony_ci		regmap_field_write(priv->rf[index_mask], 0);
42562306a36Sopenharmony_ci	} else {
42662306a36Sopenharmony_ci		regmap_field_write(priv->rf[index_mask],  1);
42762306a36Sopenharmony_ci		regmap_field_write(priv->rf[index_clear], 1);
42862306a36Sopenharmony_ci		regmap_field_write(priv->rf[index_clear], 0);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/**
43362306a36Sopenharmony_ci * tsens_set_interrupt - Set state of an interrupt
43462306a36Sopenharmony_ci * @priv: Pointer to tsens controller private data
43562306a36Sopenharmony_ci * @hw_id: Hardware ID aka. sensor number
43662306a36Sopenharmony_ci * @irq_type: irq_type from enum tsens_irq_type
43762306a36Sopenharmony_ci * @enable: false = disable, true = enable
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * Call IP-specific function to set state of an interrupt
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * Return: void
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_cistatic void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id,
44462306a36Sopenharmony_ci				enum tsens_irq_type irq_type, bool enable)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__,
44762306a36Sopenharmony_ci		irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW",
44862306a36Sopenharmony_ci		enable ? "en" : "dis");
44962306a36Sopenharmony_ci	if (tsens_version(priv) > VER_1_X)
45062306a36Sopenharmony_ci		tsens_set_interrupt_v2(priv, hw_id, irq_type, enable);
45162306a36Sopenharmony_ci	else
45262306a36Sopenharmony_ci		tsens_set_interrupt_v1(priv, hw_id, irq_type, enable);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/**
45662306a36Sopenharmony_ci * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold
45762306a36Sopenharmony_ci * @priv: Pointer to tsens controller private data
45862306a36Sopenharmony_ci * @hw_id: Hardware ID aka. sensor number
45962306a36Sopenharmony_ci * @d: Pointer to irq state data
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * Return: 0 if threshold was not violated, 1 if it was violated and negative
46262306a36Sopenharmony_ci * errno in case of errors
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id,
46562306a36Sopenharmony_ci				    struct tsens_irq_data *d)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	int ret;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol);
47062306a36Sopenharmony_ci	if (ret)
47162306a36Sopenharmony_ci		return ret;
47262306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol);
47362306a36Sopenharmony_ci	if (ret)
47462306a36Sopenharmony_ci		return ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (priv->feat->crit_int) {
47762306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[CRITICAL_STATUS_0 + hw_id],
47862306a36Sopenharmony_ci					&d->crit_viol);
47962306a36Sopenharmony_ci		if (ret)
48062306a36Sopenharmony_ci			return ret;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (d->up_viol || d->low_viol || d->crit_viol)
48462306a36Sopenharmony_ci		return 1;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id,
49062306a36Sopenharmony_ci				const struct tsens_sensor *s,
49162306a36Sopenharmony_ci				struct tsens_irq_data *d)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int ret;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear);
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		return ret;
49862306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear);
49962306a36Sopenharmony_ci	if (ret)
50062306a36Sopenharmony_ci		return ret;
50162306a36Sopenharmony_ci	if (tsens_version(priv) > VER_1_X) {
50262306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask);
50362306a36Sopenharmony_ci		if (ret)
50462306a36Sopenharmony_ci			return ret;
50562306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask);
50662306a36Sopenharmony_ci		if (ret)
50762306a36Sopenharmony_ci			return ret;
50862306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[CRIT_INT_CLEAR_0 + hw_id],
50962306a36Sopenharmony_ci					&d->crit_irq_clear);
51062306a36Sopenharmony_ci		if (ret)
51162306a36Sopenharmony_ci			return ret;
51262306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[CRIT_INT_MASK_0 + hw_id],
51362306a36Sopenharmony_ci					&d->crit_irq_mask);
51462306a36Sopenharmony_ci		if (ret)
51562306a36Sopenharmony_ci			return ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		d->crit_thresh = tsens_hw_to_mC(s, CRIT_THRESH_0 + hw_id);
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		/* No mask register on older TSENS */
52062306a36Sopenharmony_ci		d->up_irq_mask = 0;
52162306a36Sopenharmony_ci		d->low_irq_mask = 0;
52262306a36Sopenharmony_ci		d->crit_irq_clear = 0;
52362306a36Sopenharmony_ci		d->crit_irq_mask = 0;
52462306a36Sopenharmony_ci		d->crit_thresh = 0;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	d->up_thresh  = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id);
52862306a36Sopenharmony_ci	d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u|%u) | clr(%u|%u|%u) | mask(%u|%u|%u)\n",
53162306a36Sopenharmony_ci		hw_id, __func__,
53262306a36Sopenharmony_ci		(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
53362306a36Sopenharmony_ci		d->low_viol, d->up_viol, d->crit_viol,
53462306a36Sopenharmony_ci		d->low_irq_clear, d->up_irq_clear, d->crit_irq_clear,
53562306a36Sopenharmony_ci		d->low_irq_mask, d->up_irq_mask, d->crit_irq_mask);
53662306a36Sopenharmony_ci	dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d:%d)\n", hw_id, __func__,
53762306a36Sopenharmony_ci		(d->up_viol || d->low_viol || d->crit_viol) ? "(V)" : "",
53862306a36Sopenharmony_ci		d->low_thresh, d->up_thresh, d->crit_thresh);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	if (ver > VER_1_X)
54662306a36Sopenharmony_ci		return mask & (1 << hw_id);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* v1, v0.1 don't have a irq mask register */
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci/**
55362306a36Sopenharmony_ci * tsens_critical_irq_thread() - Threaded handler for critical interrupts
55462306a36Sopenharmony_ci * @irq: irq number
55562306a36Sopenharmony_ci * @data: tsens controller private data
55662306a36Sopenharmony_ci *
55762306a36Sopenharmony_ci * Check FSM watchdog bark status and clear if needed.
55862306a36Sopenharmony_ci * Check all sensors to find ones that violated their critical threshold limits.
55962306a36Sopenharmony_ci * Clear and then re-enable the interrupt.
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * The level-triggered interrupt might deassert if the temperature returned to
56262306a36Sopenharmony_ci * within the threshold limits by the time the handler got scheduled. We
56362306a36Sopenharmony_ci * consider the irq to have been handled in that case.
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci * Return: IRQ_HANDLED
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic irqreturn_t tsens_critical_irq_thread(int irq, void *data)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct tsens_priv *priv = data;
57062306a36Sopenharmony_ci	struct tsens_irq_data d;
57162306a36Sopenharmony_ci	int temp, ret, i;
57262306a36Sopenharmony_ci	u32 wdog_status, wdog_count;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (priv->feat->has_watchdog) {
57562306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[WDOG_BARK_STATUS],
57662306a36Sopenharmony_ci					&wdog_status);
57762306a36Sopenharmony_ci		if (ret)
57862306a36Sopenharmony_ci			return ret;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (wdog_status) {
58162306a36Sopenharmony_ci			/* Clear WDOG interrupt */
58262306a36Sopenharmony_ci			regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 1);
58362306a36Sopenharmony_ci			regmap_field_write(priv->rf[WDOG_BARK_CLEAR], 0);
58462306a36Sopenharmony_ci			ret = regmap_field_read(priv->rf[WDOG_BARK_COUNT],
58562306a36Sopenharmony_ci						&wdog_count);
58662306a36Sopenharmony_ci			if (ret)
58762306a36Sopenharmony_ci				return ret;
58862306a36Sopenharmony_ci			if (wdog_count)
58962306a36Sopenharmony_ci				dev_dbg(priv->dev, "%s: watchdog count: %d\n",
59062306a36Sopenharmony_ci					__func__, wdog_count);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci			/* Fall through to handle critical interrupts if any */
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for (i = 0; i < priv->num_sensors; i++) {
59762306a36Sopenharmony_ci		const struct tsens_sensor *s = &priv->sensor[i];
59862306a36Sopenharmony_ci		u32 hw_id = s->hw_id;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		if (!s->tzd)
60162306a36Sopenharmony_ci			continue;
60262306a36Sopenharmony_ci		if (!tsens_threshold_violated(priv, hw_id, &d))
60362306a36Sopenharmony_ci			continue;
60462306a36Sopenharmony_ci		ret = get_temp_tsens_valid(s, &temp);
60562306a36Sopenharmony_ci		if (ret) {
60662306a36Sopenharmony_ci			dev_err(priv->dev, "[%u] %s: error reading sensor\n",
60762306a36Sopenharmony_ci				hw_id, __func__);
60862306a36Sopenharmony_ci			continue;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		tsens_read_irq_state(priv, hw_id, s, &d);
61262306a36Sopenharmony_ci		if (d.crit_viol &&
61362306a36Sopenharmony_ci		    !masked_irq(hw_id, d.crit_irq_mask, tsens_version(priv))) {
61462306a36Sopenharmony_ci			/* Mask critical interrupts, unused on Linux */
61562306a36Sopenharmony_ci			tsens_set_interrupt(priv, hw_id, CRITICAL, false);
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return IRQ_HANDLED;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/**
62362306a36Sopenharmony_ci * tsens_irq_thread - Threaded interrupt handler for uplow interrupts
62462306a36Sopenharmony_ci * @irq: irq number
62562306a36Sopenharmony_ci * @data: tsens controller private data
62662306a36Sopenharmony_ci *
62762306a36Sopenharmony_ci * Check all sensors to find ones that violated their threshold limits. If the
62862306a36Sopenharmony_ci * temperature is still outside the limits, call thermal_zone_device_update() to
62962306a36Sopenharmony_ci * update the thresholds, else re-enable the interrupts.
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * The level-triggered interrupt might deassert if the temperature returned to
63262306a36Sopenharmony_ci * within the threshold limits by the time the handler got scheduled. We
63362306a36Sopenharmony_ci * consider the irq to have been handled in that case.
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * Return: IRQ_HANDLED
63662306a36Sopenharmony_ci */
63762306a36Sopenharmony_cistatic irqreturn_t tsens_irq_thread(int irq, void *data)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct tsens_priv *priv = data;
64062306a36Sopenharmony_ci	struct tsens_irq_data d;
64162306a36Sopenharmony_ci	int i;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	for (i = 0; i < priv->num_sensors; i++) {
64462306a36Sopenharmony_ci		const struct tsens_sensor *s = &priv->sensor[i];
64562306a36Sopenharmony_ci		u32 hw_id = s->hw_id;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		if (!s->tzd)
64862306a36Sopenharmony_ci			continue;
64962306a36Sopenharmony_ci		if (!tsens_threshold_violated(priv, hw_id, &d))
65062306a36Sopenharmony_ci			continue;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		if (tsens_version(priv) < VER_0_1) {
65562306a36Sopenharmony_ci			/* Constraint: There is only 1 interrupt control register for all
65662306a36Sopenharmony_ci			 * 11 temperature sensor. So monitoring more than 1 sensor based
65762306a36Sopenharmony_ci			 * on interrupts will yield inconsistent result. To overcome this
65862306a36Sopenharmony_ci			 * issue we will monitor only sensor 0 which is the master sensor.
65962306a36Sopenharmony_ci			 */
66062306a36Sopenharmony_ci			break;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	return IRQ_HANDLED;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/**
66862306a36Sopenharmony_ci * tsens_combined_irq_thread() - Threaded interrupt handler for combined interrupts
66962306a36Sopenharmony_ci * @irq: irq number
67062306a36Sopenharmony_ci * @data: tsens controller private data
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * Handle the combined interrupt as if it were 2 separate interrupts, so call the
67362306a36Sopenharmony_ci * critical handler first and then the up/low one.
67462306a36Sopenharmony_ci *
67562306a36Sopenharmony_ci * Return: IRQ_HANDLED
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_cistatic irqreturn_t tsens_combined_irq_thread(int irq, void *data)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	irqreturn_t ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	ret = tsens_critical_irq_thread(irq, data);
68262306a36Sopenharmony_ci	if (ret != IRQ_HANDLED)
68362306a36Sopenharmony_ci		return ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return tsens_irq_thread(irq, data);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic int tsens_set_trips(struct thermal_zone_device *tz, int low, int high)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct tsens_sensor *s = thermal_zone_device_priv(tz);
69162306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
69262306a36Sopenharmony_ci	struct device *dev = priv->dev;
69362306a36Sopenharmony_ci	struct tsens_irq_data d;
69462306a36Sopenharmony_ci	unsigned long flags;
69562306a36Sopenharmony_ci	int high_val, low_val, cl_high, cl_low;
69662306a36Sopenharmony_ci	u32 hw_id = s->hw_id;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (tsens_version(priv) < VER_0_1) {
69962306a36Sopenharmony_ci		/* Pre v0.1 IP had a single register for each type of interrupt
70062306a36Sopenharmony_ci		 * and thresholds
70162306a36Sopenharmony_ci		 */
70262306a36Sopenharmony_ci		hw_id = 0;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n",
70662306a36Sopenharmony_ci		hw_id, __func__, low, high);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	cl_high = clamp_val(high, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
70962306a36Sopenharmony_ci	cl_low  = clamp_val(low, priv->feat->trip_min_temp, priv->feat->trip_max_temp);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	high_val = tsens_mC_to_hw(s, cl_high);
71262306a36Sopenharmony_ci	low_val  = tsens_mC_to_hw(s, cl_low);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->ul_lock, flags);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	tsens_read_irq_state(priv, hw_id, s, &d);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Write the new thresholds and clear the status */
71962306a36Sopenharmony_ci	regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val);
72062306a36Sopenharmony_ci	regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val);
72162306a36Sopenharmony_ci	tsens_set_interrupt(priv, hw_id, LOWER, true);
72262306a36Sopenharmony_ci	tsens_set_interrupt(priv, hw_id, UPPER, true);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->ul_lock, flags);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n",
72762306a36Sopenharmony_ci		hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int tsens_enable_irq(struct tsens_priv *priv)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	int ret;
73562306a36Sopenharmony_ci	int val = tsens_version(priv) > VER_1_X ? 7 : 1;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	ret = regmap_field_write(priv->rf[INT_EN], val);
73862306a36Sopenharmony_ci	if (ret < 0)
73962306a36Sopenharmony_ci		dev_err(priv->dev, "%s: failed to enable interrupts\n",
74062306a36Sopenharmony_ci			__func__);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return ret;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic void tsens_disable_irq(struct tsens_priv *priv)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	regmap_field_write(priv->rf[INT_EN], 0);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ciint get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
75362306a36Sopenharmony_ci	int hw_id = s->hw_id;
75462306a36Sopenharmony_ci	u32 temp_idx = LAST_TEMP_0 + hw_id;
75562306a36Sopenharmony_ci	u32 valid_idx = VALID_0 + hw_id;
75662306a36Sopenharmony_ci	u32 valid;
75762306a36Sopenharmony_ci	int ret;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* VER_0 doesn't have VALID bit */
76062306a36Sopenharmony_ci	if (tsens_version(priv) == VER_0)
76162306a36Sopenharmony_ci		goto get_temp;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* Valid bit is 0 for 6 AHB clock cycles.
76462306a36Sopenharmony_ci	 * At 19.2MHz, 1 AHB clock is ~60ns.
76562306a36Sopenharmony_ci	 * We should enter this loop very, very rarely.
76662306a36Sopenharmony_ci	 * Wait 1 us since it's the min of poll_timeout macro.
76762306a36Sopenharmony_ci	 * Old value was 400 ns.
76862306a36Sopenharmony_ci	 */
76962306a36Sopenharmony_ci	ret = regmap_field_read_poll_timeout(priv->rf[valid_idx], valid,
77062306a36Sopenharmony_ci					     valid, 1, 20 * USEC_PER_MSEC);
77162306a36Sopenharmony_ci	if (ret)
77262306a36Sopenharmony_ci		return ret;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ciget_temp:
77562306a36Sopenharmony_ci	/* Valid bit is set, OK to read the temperature */
77662306a36Sopenharmony_ci	*temp = tsens_hw_to_mC(s, temp_idx);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ciint get_temp_common(const struct tsens_sensor *s, int *temp)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
78462306a36Sopenharmony_ci	int hw_id = s->hw_id;
78562306a36Sopenharmony_ci	int last_temp = 0, ret, trdy;
78662306a36Sopenharmony_ci	unsigned long timeout;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
78962306a36Sopenharmony_ci	do {
79062306a36Sopenharmony_ci		if (tsens_version(priv) == VER_0) {
79162306a36Sopenharmony_ci			ret = regmap_field_read(priv->rf[TRDY], &trdy);
79262306a36Sopenharmony_ci			if (ret)
79362306a36Sopenharmony_ci				return ret;
79462306a36Sopenharmony_ci			if (!trdy)
79562306a36Sopenharmony_ci				continue;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[LAST_TEMP_0 + hw_id], &last_temp);
79962306a36Sopenharmony_ci		if (ret)
80062306a36Sopenharmony_ci			return ret;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		*temp = code_to_degc(last_temp, s) * 1000;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		return 0;
80562306a36Sopenharmony_ci	} while (time_before(jiffies, timeout));
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return -ETIMEDOUT;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
81162306a36Sopenharmony_cistatic int dbg_sensors_show(struct seq_file *s, void *data)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct platform_device *pdev = s->private;
81462306a36Sopenharmony_ci	struct tsens_priv *priv = platform_get_drvdata(pdev);
81562306a36Sopenharmony_ci	int i;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	seq_printf(s, "max: %2d\nnum: %2d\n\n",
81862306a36Sopenharmony_ci		   priv->feat->max_sensors, priv->num_sensors);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	seq_puts(s, "      id    slope   offset\n--------------------------\n");
82162306a36Sopenharmony_ci	for (i = 0;  i < priv->num_sensors; i++) {
82262306a36Sopenharmony_ci		seq_printf(s, "%8d %8d %8d\n", priv->sensor[i].hw_id,
82362306a36Sopenharmony_ci			   priv->sensor[i].slope, priv->sensor[i].offset);
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int dbg_version_show(struct seq_file *s, void *data)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct platform_device *pdev = s->private;
83262306a36Sopenharmony_ci	struct tsens_priv *priv = platform_get_drvdata(pdev);
83362306a36Sopenharmony_ci	u32 maj_ver, min_ver, step_ver;
83462306a36Sopenharmony_ci	int ret;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (tsens_version(priv) > VER_0_1) {
83762306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[VER_MAJOR], &maj_ver);
83862306a36Sopenharmony_ci		if (ret)
83962306a36Sopenharmony_ci			return ret;
84062306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[VER_MINOR], &min_ver);
84162306a36Sopenharmony_ci		if (ret)
84262306a36Sopenharmony_ci			return ret;
84362306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[VER_STEP], &step_ver);
84462306a36Sopenharmony_ci		if (ret)
84562306a36Sopenharmony_ci			return ret;
84662306a36Sopenharmony_ci		seq_printf(s, "%d.%d.%d\n", maj_ver, min_ver, step_ver);
84762306a36Sopenharmony_ci	} else {
84862306a36Sopenharmony_ci		seq_printf(s, "0.%d.0\n", priv->feat->ver_major);
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return 0;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dbg_version);
85562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dbg_sensors);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic void tsens_debug_init(struct platform_device *pdev)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct tsens_priv *priv = platform_get_drvdata(pdev);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	priv->debug_root = debugfs_lookup("tsens", NULL);
86262306a36Sopenharmony_ci	if (!priv->debug_root)
86362306a36Sopenharmony_ci		priv->debug_root = debugfs_create_dir("tsens", NULL);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* A directory for each instance of the TSENS IP */
86662306a36Sopenharmony_ci	priv->debug = debugfs_create_dir(dev_name(&pdev->dev), priv->debug_root);
86762306a36Sopenharmony_ci	debugfs_create_file("version", 0444, priv->debug, pdev, &dbg_version_fops);
86862306a36Sopenharmony_ci	debugfs_create_file("sensors", 0444, priv->debug, pdev, &dbg_sensors_fops);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci#else
87162306a36Sopenharmony_cistatic inline void tsens_debug_init(struct platform_device *pdev) {}
87262306a36Sopenharmony_ci#endif
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic const struct regmap_config tsens_config = {
87562306a36Sopenharmony_ci	.name		= "tm",
87662306a36Sopenharmony_ci	.reg_bits	= 32,
87762306a36Sopenharmony_ci	.val_bits	= 32,
87862306a36Sopenharmony_ci	.reg_stride	= 4,
87962306a36Sopenharmony_ci};
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic const struct regmap_config tsens_srot_config = {
88262306a36Sopenharmony_ci	.name		= "srot",
88362306a36Sopenharmony_ci	.reg_bits	= 32,
88462306a36Sopenharmony_ci	.val_bits	= 32,
88562306a36Sopenharmony_ci	.reg_stride	= 4,
88662306a36Sopenharmony_ci};
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ciint __init init_common(struct tsens_priv *priv)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	void __iomem *tm_base, *srot_base;
89162306a36Sopenharmony_ci	struct device *dev = priv->dev;
89262306a36Sopenharmony_ci	u32 ver_minor;
89362306a36Sopenharmony_ci	struct resource *res;
89462306a36Sopenharmony_ci	u32 enabled;
89562306a36Sopenharmony_ci	int ret, i, j;
89662306a36Sopenharmony_ci	struct platform_device *op = of_find_device_by_node(priv->dev->of_node);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (!op)
89962306a36Sopenharmony_ci		return -EINVAL;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (op->num_resources > 1) {
90262306a36Sopenharmony_ci		/* DT with separate SROT and TM address space */
90362306a36Sopenharmony_ci		priv->tm_offset = 0;
90462306a36Sopenharmony_ci		res = platform_get_resource(op, IORESOURCE_MEM, 1);
90562306a36Sopenharmony_ci		srot_base = devm_ioremap_resource(dev, res);
90662306a36Sopenharmony_ci		if (IS_ERR(srot_base)) {
90762306a36Sopenharmony_ci			ret = PTR_ERR(srot_base);
90862306a36Sopenharmony_ci			goto err_put_device;
90962306a36Sopenharmony_ci		}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		priv->srot_map = devm_regmap_init_mmio(dev, srot_base,
91262306a36Sopenharmony_ci						       &tsens_srot_config);
91362306a36Sopenharmony_ci		if (IS_ERR(priv->srot_map)) {
91462306a36Sopenharmony_ci			ret = PTR_ERR(priv->srot_map);
91562306a36Sopenharmony_ci			goto err_put_device;
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci	} else {
91862306a36Sopenharmony_ci		/* old DTs where SROT and TM were in a contiguous 2K block */
91962306a36Sopenharmony_ci		priv->tm_offset = 0x1000;
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (tsens_version(priv) >= VER_0_1) {
92362306a36Sopenharmony_ci		res = platform_get_resource(op, IORESOURCE_MEM, 0);
92462306a36Sopenharmony_ci		tm_base = devm_ioremap_resource(dev, res);
92562306a36Sopenharmony_ci		if (IS_ERR(tm_base)) {
92662306a36Sopenharmony_ci			ret = PTR_ERR(tm_base);
92762306a36Sopenharmony_ci			goto err_put_device;
92862306a36Sopenharmony_ci		}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config);
93162306a36Sopenharmony_ci	} else { /* VER_0 share the same gcc regs using a syscon */
93262306a36Sopenharmony_ci		struct device *parent = priv->dev->parent;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		if (parent)
93562306a36Sopenharmony_ci			priv->tm_map = syscon_node_to_regmap(parent->of_node);
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(priv->tm_map)) {
93962306a36Sopenharmony_ci		if (!priv->tm_map)
94062306a36Sopenharmony_ci			ret = -ENODEV;
94162306a36Sopenharmony_ci		else
94262306a36Sopenharmony_ci			ret = PTR_ERR(priv->tm_map);
94362306a36Sopenharmony_ci		goto err_put_device;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* VER_0 have only tm_map */
94762306a36Sopenharmony_ci	if (!priv->srot_map)
94862306a36Sopenharmony_ci		priv->srot_map = priv->tm_map;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (tsens_version(priv) > VER_0_1) {
95162306a36Sopenharmony_ci		for (i = VER_MAJOR; i <= VER_STEP; i++) {
95262306a36Sopenharmony_ci			priv->rf[i] = devm_regmap_field_alloc(dev, priv->srot_map,
95362306a36Sopenharmony_ci							      priv->fields[i]);
95462306a36Sopenharmony_ci			if (IS_ERR(priv->rf[i])) {
95562306a36Sopenharmony_ci				ret = PTR_ERR(priv->rf[i]);
95662306a36Sopenharmony_ci				goto err_put_device;
95762306a36Sopenharmony_ci			}
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci		ret = regmap_field_read(priv->rf[VER_MINOR], &ver_minor);
96062306a36Sopenharmony_ci		if (ret)
96162306a36Sopenharmony_ci			goto err_put_device;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
96562306a36Sopenharmony_ci						     priv->fields[TSENS_EN]);
96662306a36Sopenharmony_ci	if (IS_ERR(priv->rf[TSENS_EN])) {
96762306a36Sopenharmony_ci		ret = PTR_ERR(priv->rf[TSENS_EN]);
96862306a36Sopenharmony_ci		goto err_put_device;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	/* in VER_0 TSENS need to be explicitly enabled */
97162306a36Sopenharmony_ci	if (tsens_version(priv) == VER_0)
97262306a36Sopenharmony_ci		regmap_field_write(priv->rf[TSENS_EN], 1);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
97562306a36Sopenharmony_ci	if (ret)
97662306a36Sopenharmony_ci		goto err_put_device;
97762306a36Sopenharmony_ci	if (!enabled) {
97862306a36Sopenharmony_ci		dev_err(dev, "%s: device not enabled\n", __func__);
97962306a36Sopenharmony_ci		ret = -ENODEV;
98062306a36Sopenharmony_ci		goto err_put_device;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map,
98462306a36Sopenharmony_ci						      priv->fields[SENSOR_EN]);
98562306a36Sopenharmony_ci	if (IS_ERR(priv->rf[SENSOR_EN])) {
98662306a36Sopenharmony_ci		ret = PTR_ERR(priv->rf[SENSOR_EN]);
98762306a36Sopenharmony_ci		goto err_put_device;
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci	priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map,
99062306a36Sopenharmony_ci						   priv->fields[INT_EN]);
99162306a36Sopenharmony_ci	if (IS_ERR(priv->rf[INT_EN])) {
99262306a36Sopenharmony_ci		ret = PTR_ERR(priv->rf[INT_EN]);
99362306a36Sopenharmony_ci		goto err_put_device;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	priv->rf[TSENS_SW_RST] =
99762306a36Sopenharmony_ci		devm_regmap_field_alloc(dev, priv->srot_map, priv->fields[TSENS_SW_RST]);
99862306a36Sopenharmony_ci	if (IS_ERR(priv->rf[TSENS_SW_RST])) {
99962306a36Sopenharmony_ci		ret = PTR_ERR(priv->rf[TSENS_SW_RST]);
100062306a36Sopenharmony_ci		goto err_put_device;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	priv->rf[TRDY] = devm_regmap_field_alloc(dev, priv->tm_map, priv->fields[TRDY]);
100462306a36Sopenharmony_ci	if (IS_ERR(priv->rf[TRDY])) {
100562306a36Sopenharmony_ci		ret = PTR_ERR(priv->rf[TRDY]);
100662306a36Sopenharmony_ci		goto err_put_device;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/* This loop might need changes if enum regfield_ids is reordered */
101062306a36Sopenharmony_ci	for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) {
101162306a36Sopenharmony_ci		for (i = 0; i < priv->feat->max_sensors; i++) {
101262306a36Sopenharmony_ci			int idx = j + i;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci			priv->rf[idx] = devm_regmap_field_alloc(dev,
101562306a36Sopenharmony_ci								priv->tm_map,
101662306a36Sopenharmony_ci								priv->fields[idx]);
101762306a36Sopenharmony_ci			if (IS_ERR(priv->rf[idx])) {
101862306a36Sopenharmony_ci				ret = PTR_ERR(priv->rf[idx]);
101962306a36Sopenharmony_ci				goto err_put_device;
102062306a36Sopenharmony_ci			}
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (priv->feat->crit_int || tsens_version(priv) < VER_0_1) {
102562306a36Sopenharmony_ci		/* Loop might need changes if enum regfield_ids is reordered */
102662306a36Sopenharmony_ci		for (j = CRITICAL_STATUS_0; j <= CRIT_THRESH_15; j += 16) {
102762306a36Sopenharmony_ci			for (i = 0; i < priv->feat->max_sensors; i++) {
102862306a36Sopenharmony_ci				int idx = j + i;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci				priv->rf[idx] =
103162306a36Sopenharmony_ci					devm_regmap_field_alloc(dev,
103262306a36Sopenharmony_ci								priv->tm_map,
103362306a36Sopenharmony_ci								priv->fields[idx]);
103462306a36Sopenharmony_ci				if (IS_ERR(priv->rf[idx])) {
103562306a36Sopenharmony_ci					ret = PTR_ERR(priv->rf[idx]);
103662306a36Sopenharmony_ci					goto err_put_device;
103762306a36Sopenharmony_ci				}
103862306a36Sopenharmony_ci			}
103962306a36Sopenharmony_ci		}
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (tsens_version(priv) > VER_1_X &&  ver_minor > 2) {
104362306a36Sopenharmony_ci		/* Watchdog is present only on v2.3+ */
104462306a36Sopenharmony_ci		priv->feat->has_watchdog = 1;
104562306a36Sopenharmony_ci		for (i = WDOG_BARK_STATUS; i <= CC_MON_MASK; i++) {
104662306a36Sopenharmony_ci			priv->rf[i] = devm_regmap_field_alloc(dev, priv->tm_map,
104762306a36Sopenharmony_ci							      priv->fields[i]);
104862306a36Sopenharmony_ci			if (IS_ERR(priv->rf[i])) {
104962306a36Sopenharmony_ci				ret = PTR_ERR(priv->rf[i]);
105062306a36Sopenharmony_ci				goto err_put_device;
105162306a36Sopenharmony_ci			}
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci		/*
105462306a36Sopenharmony_ci		 * Watchdog is already enabled, unmask the bark.
105562306a36Sopenharmony_ci		 * Disable cycle completion monitoring
105662306a36Sopenharmony_ci		 */
105762306a36Sopenharmony_ci		regmap_field_write(priv->rf[WDOG_BARK_MASK], 0);
105862306a36Sopenharmony_ci		regmap_field_write(priv->rf[CC_MON_MASK], 1);
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	spin_lock_init(&priv->ul_lock);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* VER_0 interrupt doesn't need to be enabled */
106462306a36Sopenharmony_ci	if (tsens_version(priv) >= VER_0_1)
106562306a36Sopenharmony_ci		tsens_enable_irq(priv);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cierr_put_device:
106862306a36Sopenharmony_ci	put_device(&op->dev);
106962306a36Sopenharmony_ci	return ret;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_cistatic int tsens_get_temp(struct thermal_zone_device *tz, int *temp)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct tsens_sensor *s = thermal_zone_device_priv(tz);
107562306a36Sopenharmony_ci	struct tsens_priv *priv = s->priv;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	return priv->ops->get_temp(s, temp);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int  __maybe_unused tsens_suspend(struct device *dev)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct tsens_priv *priv = dev_get_drvdata(dev);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (priv->ops && priv->ops->suspend)
108562306a36Sopenharmony_ci		return priv->ops->suspend(priv);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return 0;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic int __maybe_unused tsens_resume(struct device *dev)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct tsens_priv *priv = dev_get_drvdata(dev);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (priv->ops && priv->ops->resume)
109562306a36Sopenharmony_ci		return priv->ops->resume(priv);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return 0;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic const struct of_device_id tsens_table[] = {
110362306a36Sopenharmony_ci	{
110462306a36Sopenharmony_ci		.compatible = "qcom,ipq8064-tsens",
110562306a36Sopenharmony_ci		.data = &data_8960,
110662306a36Sopenharmony_ci	}, {
110762306a36Sopenharmony_ci		.compatible = "qcom,ipq8074-tsens",
110862306a36Sopenharmony_ci		.data = &data_ipq8074,
110962306a36Sopenharmony_ci	}, {
111062306a36Sopenharmony_ci		.compatible = "qcom,mdm9607-tsens",
111162306a36Sopenharmony_ci		.data = &data_9607,
111262306a36Sopenharmony_ci	}, {
111362306a36Sopenharmony_ci		.compatible = "qcom,msm8226-tsens",
111462306a36Sopenharmony_ci		.data = &data_8226,
111562306a36Sopenharmony_ci	}, {
111662306a36Sopenharmony_ci		.compatible = "qcom,msm8909-tsens",
111762306a36Sopenharmony_ci		.data = &data_8909,
111862306a36Sopenharmony_ci	}, {
111962306a36Sopenharmony_ci		.compatible = "qcom,msm8916-tsens",
112062306a36Sopenharmony_ci		.data = &data_8916,
112162306a36Sopenharmony_ci	}, {
112262306a36Sopenharmony_ci		.compatible = "qcom,msm8939-tsens",
112362306a36Sopenharmony_ci		.data = &data_8939,
112462306a36Sopenharmony_ci	}, {
112562306a36Sopenharmony_ci		.compatible = "qcom,msm8956-tsens",
112662306a36Sopenharmony_ci		.data = &data_8956,
112762306a36Sopenharmony_ci	}, {
112862306a36Sopenharmony_ci		.compatible = "qcom,msm8960-tsens",
112962306a36Sopenharmony_ci		.data = &data_8960,
113062306a36Sopenharmony_ci	}, {
113162306a36Sopenharmony_ci		.compatible = "qcom,msm8974-tsens",
113262306a36Sopenharmony_ci		.data = &data_8974,
113362306a36Sopenharmony_ci	}, {
113462306a36Sopenharmony_ci		.compatible = "qcom,msm8976-tsens",
113562306a36Sopenharmony_ci		.data = &data_8976,
113662306a36Sopenharmony_ci	}, {
113762306a36Sopenharmony_ci		.compatible = "qcom,msm8996-tsens",
113862306a36Sopenharmony_ci		.data = &data_8996,
113962306a36Sopenharmony_ci	}, {
114062306a36Sopenharmony_ci		.compatible = "qcom,tsens-v1",
114162306a36Sopenharmony_ci		.data = &data_tsens_v1,
114262306a36Sopenharmony_ci	}, {
114362306a36Sopenharmony_ci		.compatible = "qcom,tsens-v2",
114462306a36Sopenharmony_ci		.data = &data_tsens_v2,
114562306a36Sopenharmony_ci	},
114662306a36Sopenharmony_ci	{}
114762306a36Sopenharmony_ci};
114862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsens_table);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic const struct thermal_zone_device_ops tsens_of_ops = {
115162306a36Sopenharmony_ci	.get_temp = tsens_get_temp,
115262306a36Sopenharmony_ci	.set_trips = tsens_set_trips,
115362306a36Sopenharmony_ci};
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic int tsens_register_irq(struct tsens_priv *priv, char *irqname,
115662306a36Sopenharmony_ci			      irq_handler_t thread_fn)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct platform_device *pdev;
115962306a36Sopenharmony_ci	int ret, irq;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	pdev = of_find_device_by_node(priv->dev->of_node);
116262306a36Sopenharmony_ci	if (!pdev)
116362306a36Sopenharmony_ci		return -ENODEV;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, irqname);
116662306a36Sopenharmony_ci	if (irq < 0) {
116762306a36Sopenharmony_ci		ret = irq;
116862306a36Sopenharmony_ci		/* For old DTs with no IRQ defined */
116962306a36Sopenharmony_ci		if (irq == -ENXIO)
117062306a36Sopenharmony_ci			ret = 0;
117162306a36Sopenharmony_ci	} else {
117262306a36Sopenharmony_ci		/* VER_0 interrupt is TRIGGER_RISING, VER_0_1 and up is ONESHOT */
117362306a36Sopenharmony_ci		if (tsens_version(priv) == VER_0)
117462306a36Sopenharmony_ci			ret = devm_request_threaded_irq(&pdev->dev, irq,
117562306a36Sopenharmony_ci							thread_fn, NULL,
117662306a36Sopenharmony_ci							IRQF_TRIGGER_RISING,
117762306a36Sopenharmony_ci							dev_name(&pdev->dev),
117862306a36Sopenharmony_ci							priv);
117962306a36Sopenharmony_ci		else
118062306a36Sopenharmony_ci			ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
118162306a36Sopenharmony_ci							thread_fn, IRQF_ONESHOT,
118262306a36Sopenharmony_ci							dev_name(&pdev->dev),
118362306a36Sopenharmony_ci							priv);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		if (ret)
118662306a36Sopenharmony_ci			dev_err(&pdev->dev, "%s: failed to get irq\n",
118762306a36Sopenharmony_ci				__func__);
118862306a36Sopenharmony_ci		else
118962306a36Sopenharmony_ci			enable_irq_wake(irq);
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	put_device(&pdev->dev);
119362306a36Sopenharmony_ci	return ret;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int tsens_register(struct tsens_priv *priv)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	int i, ret;
119962306a36Sopenharmony_ci	struct thermal_zone_device *tzd;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	for (i = 0;  i < priv->num_sensors; i++) {
120262306a36Sopenharmony_ci		priv->sensor[i].priv = priv;
120362306a36Sopenharmony_ci		tzd = devm_thermal_of_zone_register(priv->dev, priv->sensor[i].hw_id,
120462306a36Sopenharmony_ci						    &priv->sensor[i],
120562306a36Sopenharmony_ci						    &tsens_of_ops);
120662306a36Sopenharmony_ci		if (IS_ERR(tzd))
120762306a36Sopenharmony_ci			continue;
120862306a36Sopenharmony_ci		priv->sensor[i].tzd = tzd;
120962306a36Sopenharmony_ci		if (priv->ops->enable)
121062306a36Sopenharmony_ci			priv->ops->enable(priv, i);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		devm_thermal_add_hwmon_sysfs(priv->dev, tzd);
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/* VER_0 require to set MIN and MAX THRESH
121662306a36Sopenharmony_ci	 * These 2 regs are set using the:
121762306a36Sopenharmony_ci	 * - CRIT_THRESH_0 for MAX THRESH hardcoded to 120°C
121862306a36Sopenharmony_ci	 * - CRIT_THRESH_1 for MIN THRESH hardcoded to   0°C
121962306a36Sopenharmony_ci	 */
122062306a36Sopenharmony_ci	if (tsens_version(priv) < VER_0_1) {
122162306a36Sopenharmony_ci		regmap_field_write(priv->rf[CRIT_THRESH_0],
122262306a36Sopenharmony_ci				   tsens_mC_to_hw(priv->sensor, 120000));
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		regmap_field_write(priv->rf[CRIT_THRESH_1],
122562306a36Sopenharmony_ci				   tsens_mC_to_hw(priv->sensor, 0));
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (priv->feat->combo_int) {
122962306a36Sopenharmony_ci		ret = tsens_register_irq(priv, "combined",
123062306a36Sopenharmony_ci					 tsens_combined_irq_thread);
123162306a36Sopenharmony_ci	} else {
123262306a36Sopenharmony_ci		ret = tsens_register_irq(priv, "uplow", tsens_irq_thread);
123362306a36Sopenharmony_ci		if (ret < 0)
123462306a36Sopenharmony_ci			return ret;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		if (priv->feat->crit_int)
123762306a36Sopenharmony_ci			ret = tsens_register_irq(priv, "critical",
123862306a36Sopenharmony_ci						 tsens_critical_irq_thread);
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return ret;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic int tsens_probe(struct platform_device *pdev)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	int ret, i;
124762306a36Sopenharmony_ci	struct device *dev;
124862306a36Sopenharmony_ci	struct device_node *np;
124962306a36Sopenharmony_ci	struct tsens_priv *priv;
125062306a36Sopenharmony_ci	const struct tsens_plat_data *data;
125162306a36Sopenharmony_ci	const struct of_device_id *id;
125262306a36Sopenharmony_ci	u32 num_sensors;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (pdev->dev.of_node)
125562306a36Sopenharmony_ci		dev = &pdev->dev;
125662306a36Sopenharmony_ci	else
125762306a36Sopenharmony_ci		dev = pdev->dev.parent;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	np = dev->of_node;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	id = of_match_node(tsens_table, np);
126262306a36Sopenharmony_ci	if (id)
126362306a36Sopenharmony_ci		data = id->data;
126462306a36Sopenharmony_ci	else
126562306a36Sopenharmony_ci		data = &data_8960;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	num_sensors = data->num_sensors;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (np)
127062306a36Sopenharmony_ci		of_property_read_u32(np, "#qcom,sensors", &num_sensors);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if (num_sensors <= 0) {
127362306a36Sopenharmony_ci		dev_err(dev, "%s: invalid number of sensors\n", __func__);
127462306a36Sopenharmony_ci		return -EINVAL;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	priv = devm_kzalloc(dev,
127862306a36Sopenharmony_ci			     struct_size(priv, sensor, num_sensors),
127962306a36Sopenharmony_ci			     GFP_KERNEL);
128062306a36Sopenharmony_ci	if (!priv)
128162306a36Sopenharmony_ci		return -ENOMEM;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	priv->dev = dev;
128462306a36Sopenharmony_ci	priv->num_sensors = num_sensors;
128562306a36Sopenharmony_ci	priv->ops = data->ops;
128662306a36Sopenharmony_ci	for (i = 0;  i < priv->num_sensors; i++) {
128762306a36Sopenharmony_ci		if (data->hw_ids)
128862306a36Sopenharmony_ci			priv->sensor[i].hw_id = data->hw_ids[i];
128962306a36Sopenharmony_ci		else
129062306a36Sopenharmony_ci			priv->sensor[i].hw_id = i;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci	priv->feat = data->feat;
129362306a36Sopenharmony_ci	priv->fields = data->fields;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (!priv->ops || !priv->ops->init || !priv->ops->get_temp)
129862306a36Sopenharmony_ci		return -EINVAL;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	ret = priv->ops->init(priv);
130162306a36Sopenharmony_ci	if (ret < 0) {
130262306a36Sopenharmony_ci		dev_err(dev, "%s: init failed\n", __func__);
130362306a36Sopenharmony_ci		return ret;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (priv->ops->calibrate) {
130762306a36Sopenharmony_ci		ret = priv->ops->calibrate(priv);
130862306a36Sopenharmony_ci		if (ret < 0) {
130962306a36Sopenharmony_ci			if (ret != -EPROBE_DEFER)
131062306a36Sopenharmony_ci				dev_err(dev, "%s: calibration failed\n", __func__);
131162306a36Sopenharmony_ci			return ret;
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	ret = tsens_register(priv);
131662306a36Sopenharmony_ci	if (!ret)
131762306a36Sopenharmony_ci		tsens_debug_init(pdev);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	return ret;
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic int tsens_remove(struct platform_device *pdev)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	struct tsens_priv *priv = platform_get_drvdata(pdev);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	debugfs_remove_recursive(priv->debug_root);
132762306a36Sopenharmony_ci	tsens_disable_irq(priv);
132862306a36Sopenharmony_ci	if (priv->ops->disable)
132962306a36Sopenharmony_ci		priv->ops->disable(priv);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	return 0;
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic struct platform_driver tsens_driver = {
133562306a36Sopenharmony_ci	.probe = tsens_probe,
133662306a36Sopenharmony_ci	.remove = tsens_remove,
133762306a36Sopenharmony_ci	.driver = {
133862306a36Sopenharmony_ci		.name = "qcom-tsens",
133962306a36Sopenharmony_ci		.pm	= &tsens_pm_ops,
134062306a36Sopenharmony_ci		.of_match_table = tsens_table,
134162306a36Sopenharmony_ci	},
134262306a36Sopenharmony_ci};
134362306a36Sopenharmony_cimodule_platform_driver(tsens_driver);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
134662306a36Sopenharmony_ciMODULE_DESCRIPTION("QCOM Temperature Sensor driver");
134762306a36Sopenharmony_ciMODULE_ALIAS("platform:qcom-tsens");
1348