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 CONFIG_ADDR 0x3640
1420d4fd84SRajendra Nayak #define CONFIG_ADDR_8660 0x3620
1520d4fd84SRajendra Nayak /* CONFIG_ADDR bitmasks */
1620d4fd84SRajendra Nayak #define CONFIG 0x9b
1720d4fd84SRajendra Nayak #define CONFIG_MASK 0xf
1820d4fd84SRajendra Nayak #define CONFIG_8660 1
1920d4fd84SRajendra Nayak #define CONFIG_SHIFT_8660 28
2020d4fd84SRajendra Nayak #define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660)
2120d4fd84SRajendra Nayak
2220d4fd84SRajendra Nayak #define CNTL_ADDR 0x3620
2320d4fd84SRajendra Nayak /* CNTL_ADDR bitmasks */
2420d4fd84SRajendra Nayak #define EN BIT(0)
2520d4fd84SRajendra Nayak #define SW_RST BIT(1)
262ebd0982SAnsuel Smith
273d08f029SAnsuel Smith #define MEASURE_PERIOD BIT(18)
2820d4fd84SRajendra Nayak #define SLP_CLK_ENA BIT(26)
2920d4fd84SRajendra Nayak #define SLP_CLK_ENA_8660 BIT(24)
3020d4fd84SRajendra Nayak #define SENSOR0_SHIFT 3
3120d4fd84SRajendra Nayak
3220d4fd84SRajendra Nayak #define THRESHOLD_ADDR 0x3624
3320d4fd84SRajendra Nayak
3420d4fd84SRajendra Nayak #define INT_STATUS_ADDR 0x363c
3520d4fd84SRajendra Nayak
36a0ed1411SAnsuel Smith #define S0_STATUS_OFF 0x3628
37a0ed1411SAnsuel Smith #define S1_STATUS_OFF 0x362c
38a0ed1411SAnsuel Smith #define S2_STATUS_OFF 0x3630
39a0ed1411SAnsuel Smith #define S3_STATUS_OFF 0x3634
40a0ed1411SAnsuel Smith #define S4_STATUS_OFF 0x3638
41a0ed1411SAnsuel Smith #define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */
42a0ed1411SAnsuel Smith #define S6_STATUS_OFF 0x3668
43a0ed1411SAnsuel Smith #define S7_STATUS_OFF 0x366c
44a0ed1411SAnsuel Smith #define S8_STATUS_OFF 0x3670
45a0ed1411SAnsuel Smith #define S9_STATUS_OFF 0x3674
46a0ed1411SAnsuel Smith #define S10_STATUS_OFF 0x3678
47a0ed1411SAnsuel Smith
48dfc1193dSAnsuel Smith /* Original slope - 350 to compensate mC to C inaccuracy */
49dfc1193dSAnsuel Smith static u32 tsens_msm8960_slope[] = {
50dfc1193dSAnsuel Smith 826, 826, 804, 826,
51dfc1193dSAnsuel Smith 761, 782, 782, 849,
52dfc1193dSAnsuel Smith 782, 849, 782
53dfc1193dSAnsuel Smith };
54dfc1193dSAnsuel Smith
suspend_8960(struct tsens_priv * priv)5569b628acSAmit Kucheria static int suspend_8960(struct tsens_priv *priv)
5620d4fd84SRajendra Nayak {
5720d4fd84SRajendra Nayak int ret;
5820d4fd84SRajendra Nayak unsigned int mask;
5969b628acSAmit Kucheria struct regmap *map = priv->tm_map;
6020d4fd84SRajendra Nayak
6169b628acSAmit Kucheria ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold);
6220d4fd84SRajendra Nayak if (ret)
6320d4fd84SRajendra Nayak return ret;
6420d4fd84SRajendra Nayak
6569b628acSAmit Kucheria ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control);
6620d4fd84SRajendra Nayak if (ret)
6720d4fd84SRajendra Nayak return ret;
6820d4fd84SRajendra Nayak
6969b628acSAmit Kucheria if (priv->num_sensors > 1)
7020d4fd84SRajendra Nayak mask = SLP_CLK_ENA | EN;
7120d4fd84SRajendra Nayak else
7220d4fd84SRajendra Nayak mask = SLP_CLK_ENA_8660 | EN;
7320d4fd84SRajendra Nayak
7420d4fd84SRajendra Nayak ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
7520d4fd84SRajendra Nayak if (ret)
7620d4fd84SRajendra Nayak return ret;
7720d4fd84SRajendra Nayak
7820d4fd84SRajendra Nayak return 0;
7920d4fd84SRajendra Nayak }
8020d4fd84SRajendra Nayak
resume_8960(struct tsens_priv * priv)8169b628acSAmit Kucheria static int resume_8960(struct tsens_priv *priv)
8220d4fd84SRajendra Nayak {
8320d4fd84SRajendra Nayak int ret;
8469b628acSAmit Kucheria struct regmap *map = priv->tm_map;
8520d4fd84SRajendra Nayak
8620d4fd84SRajendra Nayak ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
8720d4fd84SRajendra Nayak if (ret)
8820d4fd84SRajendra Nayak return ret;
8920d4fd84SRajendra Nayak
9020d4fd84SRajendra Nayak /*
9120d4fd84SRajendra Nayak * Separate CONFIG restore is not needed only for 8660 as
9220d4fd84SRajendra Nayak * config is part of CTRL Addr and its restored as such
9320d4fd84SRajendra Nayak */
9469b628acSAmit Kucheria if (priv->num_sensors > 1) {
9520d4fd84SRajendra Nayak ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
9620d4fd84SRajendra Nayak if (ret)
9720d4fd84SRajendra Nayak return ret;
9820d4fd84SRajendra Nayak }
9920d4fd84SRajendra Nayak
10069b628acSAmit Kucheria ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold);
10120d4fd84SRajendra Nayak if (ret)
10220d4fd84SRajendra Nayak return ret;
10320d4fd84SRajendra Nayak
10469b628acSAmit Kucheria ret = regmap_write(map, CNTL_ADDR, priv->ctx.control);
10520d4fd84SRajendra Nayak if (ret)
10620d4fd84SRajendra Nayak return ret;
10720d4fd84SRajendra Nayak
10820d4fd84SRajendra Nayak return 0;
10920d4fd84SRajendra Nayak }
11020d4fd84SRajendra Nayak
enable_8960(struct tsens_priv * priv,int id)11169b628acSAmit Kucheria static int enable_8960(struct tsens_priv *priv, int id)
11220d4fd84SRajendra Nayak {
11320d4fd84SRajendra Nayak int ret;
1143d08f029SAnsuel Smith u32 reg, mask = BIT(id);
11520d4fd84SRajendra Nayak
11669b628acSAmit Kucheria ret = regmap_read(priv->tm_map, CNTL_ADDR, ®);
11720d4fd84SRajendra Nayak if (ret)
11820d4fd84SRajendra Nayak return ret;
11920d4fd84SRajendra Nayak
1203d08f029SAnsuel Smith /* HARDWARE BUG:
1213d08f029SAnsuel Smith * On platforms with more than 6 sensors, all remaining sensors
1223d08f029SAnsuel Smith * must be enabled together, otherwise undefined results are expected.
1233d08f029SAnsuel Smith * (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver,
1243d08f029SAnsuel Smith * all the sensors are enabled in one step hence this bug is not
1253d08f029SAnsuel Smith * triggered.
1263d08f029SAnsuel Smith */
1273d08f029SAnsuel Smith if (id > 5)
1283d08f029SAnsuel Smith mask = GENMASK(10, 6);
1293d08f029SAnsuel Smith
1303d08f029SAnsuel Smith mask <<= SENSOR0_SHIFT;
1313d08f029SAnsuel Smith
1323d08f029SAnsuel Smith /* Sensors already enabled. Skip. */
1333d08f029SAnsuel Smith if ((reg & mask) == mask)
1343d08f029SAnsuel Smith return 0;
1353d08f029SAnsuel Smith
13669b628acSAmit Kucheria ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST);
13720d4fd84SRajendra Nayak if (ret)
13820d4fd84SRajendra Nayak return ret;
13920d4fd84SRajendra Nayak
1403d08f029SAnsuel Smith reg |= MEASURE_PERIOD;
1413d08f029SAnsuel Smith
14269b628acSAmit Kucheria if (priv->num_sensors > 1)
14320d4fd84SRajendra Nayak reg |= mask | SLP_CLK_ENA | EN;
14420d4fd84SRajendra Nayak else
14520d4fd84SRajendra Nayak reg |= mask | SLP_CLK_ENA_8660 | EN;
14620d4fd84SRajendra Nayak
14769b628acSAmit Kucheria ret = regmap_write(priv->tm_map, CNTL_ADDR, reg);
14820d4fd84SRajendra Nayak if (ret)
14920d4fd84SRajendra Nayak return ret;
15020d4fd84SRajendra Nayak
15120d4fd84SRajendra Nayak return 0;
15220d4fd84SRajendra Nayak }
15320d4fd84SRajendra Nayak
disable_8960(struct tsens_priv * priv)15469b628acSAmit Kucheria static void disable_8960(struct tsens_priv *priv)
15520d4fd84SRajendra Nayak {
15620d4fd84SRajendra Nayak int ret;
15720d4fd84SRajendra Nayak u32 reg_cntl;
15820d4fd84SRajendra Nayak u32 mask;
15920d4fd84SRajendra Nayak
16069b628acSAmit Kucheria mask = GENMASK(priv->num_sensors - 1, 0);
16120d4fd84SRajendra Nayak mask <<= SENSOR0_SHIFT;
16220d4fd84SRajendra Nayak mask |= EN;
16320d4fd84SRajendra Nayak
16469b628acSAmit Kucheria ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl);
16520d4fd84SRajendra Nayak if (ret)
16620d4fd84SRajendra Nayak return;
16720d4fd84SRajendra Nayak
16820d4fd84SRajendra Nayak reg_cntl &= ~mask;
16920d4fd84SRajendra Nayak
17069b628acSAmit Kucheria if (priv->num_sensors > 1)
17120d4fd84SRajendra Nayak reg_cntl &= ~SLP_CLK_ENA;
17220d4fd84SRajendra Nayak else
17320d4fd84SRajendra Nayak reg_cntl &= ~SLP_CLK_ENA_8660;
17420d4fd84SRajendra Nayak
17569b628acSAmit Kucheria regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl);
17620d4fd84SRajendra Nayak }
17720d4fd84SRajendra Nayak
calibrate_8960(struct tsens_priv * priv)17869b628acSAmit Kucheria static int calibrate_8960(struct tsens_priv *priv)
17920d4fd84SRajendra Nayak {
18020d4fd84SRajendra Nayak int i;
18120d4fd84SRajendra Nayak char *data;
182dfc1193dSAnsuel Smith u32 p1[11];
18320d4fd84SRajendra Nayak
18469b628acSAmit Kucheria data = qfprom_read(priv->dev, "calib");
18520d4fd84SRajendra Nayak if (IS_ERR(data))
18669b628acSAmit Kucheria data = qfprom_read(priv->dev, "calib_backup");
18720d4fd84SRajendra Nayak if (IS_ERR(data))
18820d4fd84SRajendra Nayak return PTR_ERR(data);
18920d4fd84SRajendra Nayak
190dfc1193dSAnsuel Smith for (i = 0; i < priv->num_sensors; i++) {
191dfc1193dSAnsuel Smith p1[i] = data[i];
192dfc1193dSAnsuel Smith priv->sensor[i].slope = tsens_msm8960_slope[i];
193dfc1193dSAnsuel Smith }
194dfc1193dSAnsuel Smith
195dfc1193dSAnsuel Smith compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB);
19620d4fd84SRajendra Nayak
1976b8249abSSrinivas Kandagatla kfree(data);
1986b8249abSSrinivas Kandagatla
19920d4fd84SRajendra Nayak return 0;
20020d4fd84SRajendra Nayak }
20120d4fd84SRajendra Nayak
202a0ed1411SAnsuel Smith static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = {
203a0ed1411SAnsuel Smith /* ----- SROT ------ */
204a0ed1411SAnsuel Smith /* No VERSION information */
205a0ed1411SAnsuel Smith
206a0ed1411SAnsuel Smith /* CNTL */
207a0ed1411SAnsuel Smith [TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0),
208a0ed1411SAnsuel Smith [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1),
209a0ed1411SAnsuel Smith /* 8960 has 5 sensors, 8660 has 11, we only handle 5 */
210a0ed1411SAnsuel Smith [SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7),
211a0ed1411SAnsuel Smith
212a0ed1411SAnsuel Smith /* ----- TM ------ */
213a0ed1411SAnsuel Smith /* INTERRUPT ENABLE */
214a0ed1411SAnsuel Smith /* NO INTERRUPT ENABLE */
215a0ed1411SAnsuel Smith
216a0ed1411SAnsuel Smith /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */
217a0ed1411SAnsuel Smith [LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7),
218a0ed1411SAnsuel Smith [UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15),
219a0ed1411SAnsuel Smith /* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield
220a0ed1411SAnsuel Smith * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp
221a0ed1411SAnsuel Smith * MIN_THRESH_0 -> CRIT_THRESH_1
222a0ed1411SAnsuel Smith * MAX_THRESH_0 -> CRIT_THRESH_0
223a0ed1411SAnsuel Smith */
224a0ed1411SAnsuel Smith [CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23),
225a0ed1411SAnsuel Smith [CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31),
226a0ed1411SAnsuel Smith
227a0ed1411SAnsuel Smith /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */
228a0ed1411SAnsuel Smith /* 1 == clear, 0 == normal operation */
229a0ed1411SAnsuel Smith [LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9),
230a0ed1411SAnsuel Smith [UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10),
231a0ed1411SAnsuel Smith
232a0ed1411SAnsuel Smith /* NO CRITICAL INTERRUPT SUPPORT on 8960 */
233a0ed1411SAnsuel Smith
234a0ed1411SAnsuel Smith /* Sn_STATUS */
235a0ed1411SAnsuel Smith [LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7),
236a0ed1411SAnsuel Smith [LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7),
237a0ed1411SAnsuel Smith [LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7),
238a0ed1411SAnsuel Smith [LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7),
239a0ed1411SAnsuel Smith [LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7),
240a0ed1411SAnsuel Smith [LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7),
241a0ed1411SAnsuel Smith [LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7),
242a0ed1411SAnsuel Smith [LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7),
243a0ed1411SAnsuel Smith [LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7),
244a0ed1411SAnsuel Smith [LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7),
245a0ed1411SAnsuel Smith [LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7),
246a0ed1411SAnsuel Smith
247a0ed1411SAnsuel Smith /* No VALID field on 8960 */
248a0ed1411SAnsuel Smith /* TSENS_INT_STATUS bits: 1 == threshold violated */
249a0ed1411SAnsuel Smith [MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0),
250a0ed1411SAnsuel Smith [LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1),
251a0ed1411SAnsuel Smith [UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2),
252a0ed1411SAnsuel Smith /* No CRITICAL field on 8960 */
253a0ed1411SAnsuel Smith [MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3),
254a0ed1411SAnsuel Smith
255a0ed1411SAnsuel Smith /* TRDY: 1=ready, 0=in progress */
256a0ed1411SAnsuel Smith [TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7),
257a0ed1411SAnsuel Smith };
258a0ed1411SAnsuel Smith
259032d4057SEduardo Valentin static const struct tsens_ops ops_8960 = {
260fdda131fSAnsuel Smith .init = init_common,
26120d4fd84SRajendra Nayak .calibrate = calibrate_8960,
262dfc1193dSAnsuel Smith .get_temp = get_temp_common,
26320d4fd84SRajendra Nayak .enable = enable_8960,
26420d4fd84SRajendra Nayak .disable = disable_8960,
26520d4fd84SRajendra Nayak .suspend = suspend_8960,
26620d4fd84SRajendra Nayak .resume = resume_8960,
26720d4fd84SRajendra Nayak };
26820d4fd84SRajendra Nayak
26953e2a20eSAnsuel Smith static struct tsens_features tsens_8960_feat = {
27053e2a20eSAnsuel Smith .ver_major = VER_0,
27153e2a20eSAnsuel Smith .crit_int = 0,
2724360af35SRobert Marko .combo_int = 0,
27353e2a20eSAnsuel Smith .adc = 1,
27453e2a20eSAnsuel Smith .srot_split = 0,
27553e2a20eSAnsuel Smith .max_sensors = 11,
276*f63bacedSRobert Marko .trip_min_temp = -40000,
277*f63bacedSRobert Marko .trip_max_temp = 120000,
27853e2a20eSAnsuel Smith };
27953e2a20eSAnsuel Smith
2800aef1ee5SAmit Kucheria struct tsens_plat_data data_8960 = {
28120d4fd84SRajendra Nayak .num_sensors = 11,
28220d4fd84SRajendra Nayak .ops = &ops_8960,
28353e2a20eSAnsuel Smith .feat = &tsens_8960_feat,
284a0ed1411SAnsuel Smith .fields = tsens_8960_regfields,
28520d4fd84SRajendra Nayak };
286