12d71d8deSAmit Kucheria // SPDX-License-Identifier: GPL-2.0
220d4fd84SRajendra Nayak /*
320d4fd84SRajendra Nayak  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
420d4fd84SRajendra Nayak  */
520d4fd84SRajendra Nayak 
620d4fd84SRajendra Nayak #include <linux/platform_device.h>
720d4fd84SRajendra Nayak #include <linux/delay.h>
820d4fd84SRajendra Nayak #include <linux/bitops.h>
920d4fd84SRajendra Nayak #include <linux/regmap.h>
1020d4fd84SRajendra Nayak #include <linux/thermal.h>
1120d4fd84SRajendra Nayak #include "tsens.h"
1220d4fd84SRajendra Nayak 
1320d4fd84SRajendra Nayak #define CAL_MDEGC		30000
1420d4fd84SRajendra Nayak 
1520d4fd84SRajendra Nayak #define CONFIG_ADDR		0x3640
1620d4fd84SRajendra Nayak #define CONFIG_ADDR_8660	0x3620
1720d4fd84SRajendra Nayak /* CONFIG_ADDR bitmasks */
1820d4fd84SRajendra Nayak #define CONFIG			0x9b
1920d4fd84SRajendra Nayak #define CONFIG_MASK		0xf
2020d4fd84SRajendra Nayak #define CONFIG_8660		1
2120d4fd84SRajendra Nayak #define CONFIG_SHIFT_8660	28
2220d4fd84SRajendra Nayak #define CONFIG_MASK_8660	(3 << CONFIG_SHIFT_8660)
2320d4fd84SRajendra Nayak 
2420d4fd84SRajendra Nayak #define STATUS_CNTL_ADDR_8064	0x3660
2520d4fd84SRajendra Nayak #define CNTL_ADDR		0x3620
2620d4fd84SRajendra Nayak /* CNTL_ADDR bitmasks */
2720d4fd84SRajendra Nayak #define EN			BIT(0)
2820d4fd84SRajendra Nayak #define SW_RST			BIT(1)
2920d4fd84SRajendra Nayak #define SENSOR0_EN		BIT(3)
3020d4fd84SRajendra Nayak #define SLP_CLK_ENA		BIT(26)
3120d4fd84SRajendra Nayak #define SLP_CLK_ENA_8660	BIT(24)
3220d4fd84SRajendra Nayak #define MEASURE_PERIOD		1
3320d4fd84SRajendra Nayak #define SENSOR0_SHIFT		3
3420d4fd84SRajendra Nayak 
3520d4fd84SRajendra Nayak /* INT_STATUS_ADDR bitmasks */
3620d4fd84SRajendra Nayak #define MIN_STATUS_MASK		BIT(0)
3720d4fd84SRajendra Nayak #define LOWER_STATUS_CLR	BIT(1)
3820d4fd84SRajendra Nayak #define UPPER_STATUS_CLR	BIT(2)
3920d4fd84SRajendra Nayak #define MAX_STATUS_MASK		BIT(3)
4020d4fd84SRajendra Nayak 
4120d4fd84SRajendra Nayak #define THRESHOLD_ADDR		0x3624
4220d4fd84SRajendra Nayak /* THRESHOLD_ADDR bitmasks */
4320d4fd84SRajendra Nayak #define THRESHOLD_MAX_LIMIT_SHIFT	24
4420d4fd84SRajendra Nayak #define THRESHOLD_MIN_LIMIT_SHIFT	16
4520d4fd84SRajendra Nayak #define THRESHOLD_UPPER_LIMIT_SHIFT	8
4620d4fd84SRajendra Nayak #define THRESHOLD_LOWER_LIMIT_SHIFT	0
4720d4fd84SRajendra Nayak 
4820d4fd84SRajendra Nayak /* Initial temperature threshold values */
4920d4fd84SRajendra Nayak #define LOWER_LIMIT_TH		0x50
5020d4fd84SRajendra Nayak #define UPPER_LIMIT_TH		0xdf
5120d4fd84SRajendra Nayak #define MIN_LIMIT_TH		0x0
5220d4fd84SRajendra Nayak #define MAX_LIMIT_TH		0xff
5320d4fd84SRajendra Nayak 
5420d4fd84SRajendra Nayak #define S0_STATUS_ADDR		0x3628
5520d4fd84SRajendra Nayak #define INT_STATUS_ADDR		0x363c
5620d4fd84SRajendra Nayak #define TRDY_MASK		BIT(7)
5720d4fd84SRajendra Nayak #define TIMEOUT_US		100
5820d4fd84SRajendra Nayak 
5969b628acSAmit Kucheria static int suspend_8960(struct tsens_priv *priv)
6020d4fd84SRajendra Nayak {
6120d4fd84SRajendra Nayak 	int ret;
6220d4fd84SRajendra Nayak 	unsigned int mask;
6369b628acSAmit Kucheria 	struct regmap *map = priv->tm_map;
6420d4fd84SRajendra Nayak 
6569b628acSAmit Kucheria 	ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
6620d4fd84SRajendra Nayak 	if (ret)
6720d4fd84SRajendra Nayak 		return ret;
6820d4fd84SRajendra Nayak 
6969b628acSAmit Kucheria 	ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control);
7020d4fd84SRajendra Nayak 	if (ret)
7120d4fd84SRajendra Nayak 		return ret;
7220d4fd84SRajendra Nayak 
7369b628acSAmit Kucheria 	if (priv->num_sensors > 1)
7420d4fd84SRajendra Nayak 		mask = SLP_CLK_ENA | EN;
7520d4fd84SRajendra Nayak 	else
7620d4fd84SRajendra Nayak 		mask = SLP_CLK_ENA_8660 | EN;
7720d4fd84SRajendra Nayak 
7820d4fd84SRajendra Nayak 	ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
7920d4fd84SRajendra Nayak 	if (ret)
8020d4fd84SRajendra Nayak 		return ret;
8120d4fd84SRajendra Nayak 
8220d4fd84SRajendra Nayak 	return 0;
8320d4fd84SRajendra Nayak }
8420d4fd84SRajendra Nayak 
8569b628acSAmit Kucheria static int resume_8960(struct tsens_priv *priv)
8620d4fd84SRajendra Nayak {
8720d4fd84SRajendra Nayak 	int ret;
8869b628acSAmit Kucheria 	struct regmap *map = priv->tm_map;
8920d4fd84SRajendra Nayak 
9020d4fd84SRajendra Nayak 	ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
9120d4fd84SRajendra Nayak 	if (ret)
9220d4fd84SRajendra Nayak 		return ret;
9320d4fd84SRajendra Nayak 
9420d4fd84SRajendra Nayak 	/*
9520d4fd84SRajendra Nayak 	 * Separate CONFIG restore is not needed only for 8660 as
9620d4fd84SRajendra Nayak 	 * config is part of CTRL Addr and its restored as such
9720d4fd84SRajendra Nayak 	 */
9869b628acSAmit Kucheria 	if (priv->num_sensors > 1) {
9920d4fd84SRajendra Nayak 		ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
10020d4fd84SRajendra Nayak 		if (ret)
10120d4fd84SRajendra Nayak 			return ret;
10220d4fd84SRajendra Nayak 	}
10320d4fd84SRajendra Nayak 
10469b628acSAmit Kucheria 	ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold);
10520d4fd84SRajendra Nayak 	if (ret)
10620d4fd84SRajendra Nayak 		return ret;
10720d4fd84SRajendra Nayak 
10869b628acSAmit Kucheria 	ret = regmap_write(map, CNTL_ADDR, priv->ctx.control);
10920d4fd84SRajendra Nayak 	if (ret)
11020d4fd84SRajendra Nayak 		return ret;
11120d4fd84SRajendra Nayak 
11220d4fd84SRajendra Nayak 	return 0;
11320d4fd84SRajendra Nayak }
11420d4fd84SRajendra Nayak 
11569b628acSAmit Kucheria static int enable_8960(struct tsens_priv *priv, int id)
11620d4fd84SRajendra Nayak {
11720d4fd84SRajendra Nayak 	int ret;
11820d4fd84SRajendra Nayak 	u32 reg, mask;
11920d4fd84SRajendra Nayak 
12069b628acSAmit Kucheria 	ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg);
12120d4fd84SRajendra Nayak 	if (ret)
12220d4fd84SRajendra Nayak 		return ret;
12320d4fd84SRajendra Nayak 
12420d4fd84SRajendra Nayak 	mask = BIT(id + SENSOR0_SHIFT);
12569b628acSAmit Kucheria 	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
12620d4fd84SRajendra Nayak 	if (ret)
12720d4fd84SRajendra Nayak 		return ret;
12820d4fd84SRajendra Nayak 
12969b628acSAmit Kucheria 	if (priv->num_sensors > 1)
13020d4fd84SRajendra Nayak 		reg |= mask | SLP_CLK_ENA | EN;
13120d4fd84SRajendra Nayak 	else
13220d4fd84SRajendra Nayak 		reg |= mask | SLP_CLK_ENA_8660 | EN;
13320d4fd84SRajendra Nayak 
13469b628acSAmit Kucheria 	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg);
13520d4fd84SRajendra Nayak 	if (ret)
13620d4fd84SRajendra Nayak 		return ret;
13720d4fd84SRajendra Nayak 
13820d4fd84SRajendra Nayak 	return 0;
13920d4fd84SRajendra Nayak }
14020d4fd84SRajendra Nayak 
14169b628acSAmit Kucheria static void disable_8960(struct tsens_priv *priv)
14220d4fd84SRajendra Nayak {
14320d4fd84SRajendra Nayak 	int ret;
14420d4fd84SRajendra Nayak 	u32 reg_cntl;
14520d4fd84SRajendra Nayak 	u32 mask;
14620d4fd84SRajendra Nayak 
14769b628acSAmit Kucheria 	mask = GENMASK(priv->num_sensors - 1, 0);
14820d4fd84SRajendra Nayak 	mask <<= SENSOR0_SHIFT;
14920d4fd84SRajendra Nayak 	mask |= EN;
15020d4fd84SRajendra Nayak 
15169b628acSAmit Kucheria 	ret = regmap_read(priv->tm_map, CNTL_ADDR, &reg_cntl);
15220d4fd84SRajendra Nayak 	if (ret)
15320d4fd84SRajendra Nayak 		return;
15420d4fd84SRajendra Nayak 
15520d4fd84SRajendra Nayak 	reg_cntl &= ~mask;
15620d4fd84SRajendra Nayak 
15769b628acSAmit Kucheria 	if (priv->num_sensors > 1)
15820d4fd84SRajendra Nayak 		reg_cntl &= ~SLP_CLK_ENA;
15920d4fd84SRajendra Nayak 	else
16020d4fd84SRajendra Nayak 		reg_cntl &= ~SLP_CLK_ENA_8660;
16120d4fd84SRajendra Nayak 
16269b628acSAmit Kucheria 	regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
16320d4fd84SRajendra Nayak }
16420d4fd84SRajendra Nayak 
16569b628acSAmit Kucheria static int init_8960(struct tsens_priv *priv)
16620d4fd84SRajendra Nayak {
16720d4fd84SRajendra Nayak 	int ret, i;
16820d4fd84SRajendra Nayak 	u32 reg_cntl;
16920d4fd84SRajendra Nayak 
17069b628acSAmit Kucheria 	priv->tm_map = dev_get_regmap(priv->dev, NULL);
17169b628acSAmit Kucheria 	if (!priv->tm_map)
17220d4fd84SRajendra Nayak 		return -ENODEV;
17320d4fd84SRajendra Nayak 
17420d4fd84SRajendra Nayak 	/*
17520d4fd84SRajendra Nayak 	 * The status registers for each sensor are discontiguous
17620d4fd84SRajendra Nayak 	 * because some SoCs have 5 sensors while others have more
17720d4fd84SRajendra Nayak 	 * but the control registers stay in the same place, i.e
17820d4fd84SRajendra Nayak 	 * directly after the first 5 status registers.
17920d4fd84SRajendra Nayak 	 */
18069b628acSAmit Kucheria 	for (i = 0; i < priv->num_sensors; i++) {
18120d4fd84SRajendra Nayak 		if (i >= 5)
18269b628acSAmit Kucheria 			priv->sensor[i].status = S0_STATUS_ADDR + 40;
18369b628acSAmit Kucheria 		priv->sensor[i].status += i * 4;
18420d4fd84SRajendra Nayak 	}
18520d4fd84SRajendra Nayak 
18620d4fd84SRajendra Nayak 	reg_cntl = SW_RST;
18769b628acSAmit Kucheria 	ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
18820d4fd84SRajendra Nayak 	if (ret)
18920d4fd84SRajendra Nayak 		return ret;
19020d4fd84SRajendra Nayak 
19169b628acSAmit Kucheria 	if (priv->num_sensors > 1) {
19220d4fd84SRajendra Nayak 		reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
19320d4fd84SRajendra Nayak 		reg_cntl &= ~SW_RST;
19469b628acSAmit Kucheria 		ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR,
19520d4fd84SRajendra Nayak 					 CONFIG_MASK, CONFIG);
19620d4fd84SRajendra Nayak 	} else {
19720d4fd84SRajendra Nayak 		reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
19820d4fd84SRajendra Nayak 		reg_cntl &= ~CONFIG_MASK_8660;
19920d4fd84SRajendra Nayak 		reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
20020d4fd84SRajendra Nayak 	}
20120d4fd84SRajendra Nayak 
20269b628acSAmit Kucheria 	reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT;
20369b628acSAmit Kucheria 	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
20420d4fd84SRajendra Nayak 	if (ret)
20520d4fd84SRajendra Nayak 		return ret;
20620d4fd84SRajendra Nayak 
20720d4fd84SRajendra Nayak 	reg_cntl |= EN;
20869b628acSAmit Kucheria 	ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
20920d4fd84SRajendra Nayak 	if (ret)
21020d4fd84SRajendra Nayak 		return ret;
21120d4fd84SRajendra Nayak 
21220d4fd84SRajendra Nayak 	return 0;
21320d4fd84SRajendra Nayak }
21420d4fd84SRajendra Nayak 
21569b628acSAmit Kucheria static int calibrate_8960(struct tsens_priv *priv)
21620d4fd84SRajendra Nayak {
21720d4fd84SRajendra Nayak 	int i;
21820d4fd84SRajendra Nayak 	char *data;
21920d4fd84SRajendra Nayak 
22069b628acSAmit Kucheria 	ssize_t num_read = priv->num_sensors;
22169b628acSAmit Kucheria 	struct tsens_sensor *s = priv->sensor;
22220d4fd84SRajendra Nayak 
22369b628acSAmit Kucheria 	data = qfprom_read(priv->dev, "calib");
22420d4fd84SRajendra Nayak 	if (IS_ERR(data))
22569b628acSAmit Kucheria 		data = qfprom_read(priv->dev, "calib_backup");
22620d4fd84SRajendra Nayak 	if (IS_ERR(data))
22720d4fd84SRajendra Nayak 		return PTR_ERR(data);
22820d4fd84SRajendra Nayak 
22920d4fd84SRajendra Nayak 	for (i = 0; i < num_read; i++, s++)
23020d4fd84SRajendra Nayak 		s->offset = data[i];
23120d4fd84SRajendra Nayak 
2326b8249abSSrinivas Kandagatla 	kfree(data);
2336b8249abSSrinivas Kandagatla 
23420d4fd84SRajendra Nayak 	return 0;
23520d4fd84SRajendra Nayak }
23620d4fd84SRajendra Nayak 
23720d4fd84SRajendra Nayak /* Temperature on y axis and ADC-code on x-axis */
23820d4fd84SRajendra Nayak static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
23920d4fd84SRajendra Nayak {
24020d4fd84SRajendra Nayak 	int slope, offset;
24120d4fd84SRajendra Nayak 
24220d4fd84SRajendra Nayak 	slope = thermal_zone_get_slope(s->tzd);
24320d4fd84SRajendra Nayak 	offset = CAL_MDEGC - slope * s->offset;
24420d4fd84SRajendra Nayak 
24520d4fd84SRajendra Nayak 	return adc_code * slope + offset;
24620d4fd84SRajendra Nayak }
24720d4fd84SRajendra Nayak 
24869b628acSAmit Kucheria static int get_temp_8960(struct tsens_priv *priv, int id, int *temp)
24920d4fd84SRajendra Nayak {
25020d4fd84SRajendra Nayak 	int ret;
25120d4fd84SRajendra Nayak 	u32 code, trdy;
25269b628acSAmit Kucheria 	const struct tsens_sensor *s = &priv->sensor[id];
25320d4fd84SRajendra Nayak 	unsigned long timeout;
25420d4fd84SRajendra Nayak 
25520d4fd84SRajendra Nayak 	timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
25620d4fd84SRajendra Nayak 	do {
25769b628acSAmit Kucheria 		ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy);
25820d4fd84SRajendra Nayak 		if (ret)
25920d4fd84SRajendra Nayak 			return ret;
26020d4fd84SRajendra Nayak 		if (!(trdy & TRDY_MASK))
26120d4fd84SRajendra Nayak 			continue;
26269b628acSAmit Kucheria 		ret = regmap_read(priv->tm_map, s->status, &code);
26320d4fd84SRajendra Nayak 		if (ret)
26420d4fd84SRajendra Nayak 			return ret;
26520d4fd84SRajendra Nayak 		*temp = code_to_mdegC(code, s);
26620d4fd84SRajendra Nayak 		return 0;
26720d4fd84SRajendra Nayak 	} while (time_before(jiffies, timeout));
26820d4fd84SRajendra Nayak 
26920d4fd84SRajendra Nayak 	return -ETIMEDOUT;
27020d4fd84SRajendra Nayak }
27120d4fd84SRajendra Nayak 
272032d4057SEduardo Valentin static const struct tsens_ops ops_8960 = {
27320d4fd84SRajendra Nayak 	.init		= init_8960,
27420d4fd84SRajendra Nayak 	.calibrate	= calibrate_8960,
27520d4fd84SRajendra Nayak 	.get_temp	= get_temp_8960,
27620d4fd84SRajendra Nayak 	.enable		= enable_8960,
27720d4fd84SRajendra Nayak 	.disable	= disable_8960,
27820d4fd84SRajendra Nayak 	.suspend	= suspend_8960,
27920d4fd84SRajendra Nayak 	.resume		= resume_8960,
28020d4fd84SRajendra Nayak };
28120d4fd84SRajendra Nayak 
2823c040ce0SAmit Kucheria const struct tsens_plat_data data_8960 = {
28320d4fd84SRajendra Nayak 	.num_sensors	= 11,
28420d4fd84SRajendra Nayak 	.ops		= &ops_8960,
28520d4fd84SRajendra Nayak };
286