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 INT_STATUS_ADDR 0x363c 5520d4fd84SRajendra Nayak #define TRDY_MASK BIT(7) 5620d4fd84SRajendra Nayak #define TIMEOUT_US 100 5720d4fd84SRajendra Nayak 58a0ed1411SAnsuel Smith #define S0_STATUS_OFF 0x3628 59a0ed1411SAnsuel Smith #define S1_STATUS_OFF 0x362c 60a0ed1411SAnsuel Smith #define S2_STATUS_OFF 0x3630 61a0ed1411SAnsuel Smith #define S3_STATUS_OFF 0x3634 62a0ed1411SAnsuel Smith #define S4_STATUS_OFF 0x3638 63a0ed1411SAnsuel Smith #define S5_STATUS_OFF 0x3664 /* Sensors 5-10 found on apq8064/msm8960 */ 64a0ed1411SAnsuel Smith #define S6_STATUS_OFF 0x3668 65a0ed1411SAnsuel Smith #define S7_STATUS_OFF 0x366c 66a0ed1411SAnsuel Smith #define S8_STATUS_OFF 0x3670 67a0ed1411SAnsuel Smith #define S9_STATUS_OFF 0x3674 68a0ed1411SAnsuel Smith #define S10_STATUS_OFF 0x3678 69a0ed1411SAnsuel Smith 7069b628acSAmit Kucheria static int suspend_8960(struct tsens_priv *priv) 7120d4fd84SRajendra Nayak { 7220d4fd84SRajendra Nayak int ret; 7320d4fd84SRajendra Nayak unsigned int mask; 7469b628acSAmit Kucheria struct regmap *map = priv->tm_map; 7520d4fd84SRajendra Nayak 7669b628acSAmit Kucheria ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); 7720d4fd84SRajendra Nayak if (ret) 7820d4fd84SRajendra Nayak return ret; 7920d4fd84SRajendra Nayak 8069b628acSAmit Kucheria ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); 8120d4fd84SRajendra Nayak if (ret) 8220d4fd84SRajendra Nayak return ret; 8320d4fd84SRajendra Nayak 8469b628acSAmit Kucheria if (priv->num_sensors > 1) 8520d4fd84SRajendra Nayak mask = SLP_CLK_ENA | EN; 8620d4fd84SRajendra Nayak else 8720d4fd84SRajendra Nayak mask = SLP_CLK_ENA_8660 | EN; 8820d4fd84SRajendra Nayak 8920d4fd84SRajendra Nayak ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); 9020d4fd84SRajendra Nayak if (ret) 9120d4fd84SRajendra Nayak return ret; 9220d4fd84SRajendra Nayak 9320d4fd84SRajendra Nayak return 0; 9420d4fd84SRajendra Nayak } 9520d4fd84SRajendra Nayak 9669b628acSAmit Kucheria static int resume_8960(struct tsens_priv *priv) 9720d4fd84SRajendra Nayak { 9820d4fd84SRajendra Nayak int ret; 9969b628acSAmit Kucheria struct regmap *map = priv->tm_map; 10020d4fd84SRajendra Nayak 10120d4fd84SRajendra Nayak ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); 10220d4fd84SRajendra Nayak if (ret) 10320d4fd84SRajendra Nayak return ret; 10420d4fd84SRajendra Nayak 10520d4fd84SRajendra Nayak /* 10620d4fd84SRajendra Nayak * Separate CONFIG restore is not needed only for 8660 as 10720d4fd84SRajendra Nayak * config is part of CTRL Addr and its restored as such 10820d4fd84SRajendra Nayak */ 10969b628acSAmit Kucheria if (priv->num_sensors > 1) { 11020d4fd84SRajendra Nayak ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); 11120d4fd84SRajendra Nayak if (ret) 11220d4fd84SRajendra Nayak return ret; 11320d4fd84SRajendra Nayak } 11420d4fd84SRajendra Nayak 11569b628acSAmit Kucheria ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); 11620d4fd84SRajendra Nayak if (ret) 11720d4fd84SRajendra Nayak return ret; 11820d4fd84SRajendra Nayak 11969b628acSAmit Kucheria ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); 12020d4fd84SRajendra Nayak if (ret) 12120d4fd84SRajendra Nayak return ret; 12220d4fd84SRajendra Nayak 12320d4fd84SRajendra Nayak return 0; 12420d4fd84SRajendra Nayak } 12520d4fd84SRajendra Nayak 12669b628acSAmit Kucheria static int enable_8960(struct tsens_priv *priv, int id) 12720d4fd84SRajendra Nayak { 12820d4fd84SRajendra Nayak int ret; 12920d4fd84SRajendra Nayak u32 reg, mask; 13020d4fd84SRajendra Nayak 13169b628acSAmit Kucheria ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); 13220d4fd84SRajendra Nayak if (ret) 13320d4fd84SRajendra Nayak return ret; 13420d4fd84SRajendra Nayak 13520d4fd84SRajendra Nayak mask = BIT(id + SENSOR0_SHIFT); 13669b628acSAmit Kucheria ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); 13720d4fd84SRajendra Nayak if (ret) 13820d4fd84SRajendra Nayak return ret; 13920d4fd84SRajendra Nayak 14069b628acSAmit Kucheria if (priv->num_sensors > 1) 14120d4fd84SRajendra Nayak reg |= mask | SLP_CLK_ENA | EN; 14220d4fd84SRajendra Nayak else 14320d4fd84SRajendra Nayak reg |= mask | SLP_CLK_ENA_8660 | EN; 14420d4fd84SRajendra Nayak 14569b628acSAmit Kucheria ret = regmap_write(priv->tm_map, CNTL_ADDR, reg); 14620d4fd84SRajendra Nayak if (ret) 14720d4fd84SRajendra Nayak return ret; 14820d4fd84SRajendra Nayak 14920d4fd84SRajendra Nayak return 0; 15020d4fd84SRajendra Nayak } 15120d4fd84SRajendra Nayak 15269b628acSAmit Kucheria static void disable_8960(struct tsens_priv *priv) 15320d4fd84SRajendra Nayak { 15420d4fd84SRajendra Nayak int ret; 15520d4fd84SRajendra Nayak u32 reg_cntl; 15620d4fd84SRajendra Nayak u32 mask; 15720d4fd84SRajendra Nayak 15869b628acSAmit Kucheria mask = GENMASK(priv->num_sensors - 1, 0); 15920d4fd84SRajendra Nayak mask <<= SENSOR0_SHIFT; 16020d4fd84SRajendra Nayak mask |= EN; 16120d4fd84SRajendra Nayak 16269b628acSAmit Kucheria ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); 16320d4fd84SRajendra Nayak if (ret) 16420d4fd84SRajendra Nayak return; 16520d4fd84SRajendra Nayak 16620d4fd84SRajendra Nayak reg_cntl &= ~mask; 16720d4fd84SRajendra Nayak 16869b628acSAmit Kucheria if (priv->num_sensors > 1) 16920d4fd84SRajendra Nayak reg_cntl &= ~SLP_CLK_ENA; 17020d4fd84SRajendra Nayak else 17120d4fd84SRajendra Nayak reg_cntl &= ~SLP_CLK_ENA_8660; 17220d4fd84SRajendra Nayak 17369b628acSAmit Kucheria regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); 17420d4fd84SRajendra Nayak } 17520d4fd84SRajendra Nayak 17669b628acSAmit Kucheria static int calibrate_8960(struct tsens_priv *priv) 17720d4fd84SRajendra Nayak { 17820d4fd84SRajendra Nayak int i; 17920d4fd84SRajendra Nayak char *data; 18020d4fd84SRajendra Nayak 18169b628acSAmit Kucheria ssize_t num_read = priv->num_sensors; 18269b628acSAmit Kucheria struct tsens_sensor *s = priv->sensor; 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 19020d4fd84SRajendra Nayak for (i = 0; i < num_read; i++, s++) 19120d4fd84SRajendra Nayak s->offset = data[i]; 19220d4fd84SRajendra Nayak 1936b8249abSSrinivas Kandagatla kfree(data); 1946b8249abSSrinivas Kandagatla 19520d4fd84SRajendra Nayak return 0; 19620d4fd84SRajendra Nayak } 19720d4fd84SRajendra Nayak 19820d4fd84SRajendra Nayak /* Temperature on y axis and ADC-code on x-axis */ 19920d4fd84SRajendra Nayak static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) 20020d4fd84SRajendra Nayak { 20120d4fd84SRajendra Nayak int slope, offset; 20220d4fd84SRajendra Nayak 20320d4fd84SRajendra Nayak slope = thermal_zone_get_slope(s->tzd); 20420d4fd84SRajendra Nayak offset = CAL_MDEGC - slope * s->offset; 20520d4fd84SRajendra Nayak 20620d4fd84SRajendra Nayak return adc_code * slope + offset; 20720d4fd84SRajendra Nayak } 20820d4fd84SRajendra Nayak 209e604bdd2SAmit Kucheria static int get_temp_8960(const struct tsens_sensor *s, int *temp) 21020d4fd84SRajendra Nayak { 21120d4fd84SRajendra Nayak int ret; 21220d4fd84SRajendra Nayak u32 code, trdy; 2138b71bce4SAmit Kucheria struct tsens_priv *priv = s->priv; 21420d4fd84SRajendra Nayak unsigned long timeout; 21520d4fd84SRajendra Nayak 21620d4fd84SRajendra Nayak timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); 21720d4fd84SRajendra Nayak do { 21869b628acSAmit Kucheria ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); 21920d4fd84SRajendra Nayak if (ret) 22020d4fd84SRajendra Nayak return ret; 22120d4fd84SRajendra Nayak if (!(trdy & TRDY_MASK)) 22220d4fd84SRajendra Nayak continue; 22369b628acSAmit Kucheria ret = regmap_read(priv->tm_map, s->status, &code); 22420d4fd84SRajendra Nayak if (ret) 22520d4fd84SRajendra Nayak return ret; 22620d4fd84SRajendra Nayak *temp = code_to_mdegC(code, s); 22720d4fd84SRajendra Nayak return 0; 22820d4fd84SRajendra Nayak } while (time_before(jiffies, timeout)); 22920d4fd84SRajendra Nayak 23020d4fd84SRajendra Nayak return -ETIMEDOUT; 23120d4fd84SRajendra Nayak } 23220d4fd84SRajendra Nayak 233a0ed1411SAnsuel Smith static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { 234a0ed1411SAnsuel Smith /* ----- SROT ------ */ 235a0ed1411SAnsuel Smith /* No VERSION information */ 236a0ed1411SAnsuel Smith 237a0ed1411SAnsuel Smith /* CNTL */ 238a0ed1411SAnsuel Smith [TSENS_EN] = REG_FIELD(CNTL_ADDR, 0, 0), 239a0ed1411SAnsuel Smith [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR, 1, 1), 240a0ed1411SAnsuel Smith /* 8960 has 5 sensors, 8660 has 11, we only handle 5 */ 241a0ed1411SAnsuel Smith [SENSOR_EN] = REG_FIELD(CNTL_ADDR, 3, 7), 242a0ed1411SAnsuel Smith 243a0ed1411SAnsuel Smith /* ----- TM ------ */ 244a0ed1411SAnsuel Smith /* INTERRUPT ENABLE */ 245a0ed1411SAnsuel Smith /* NO INTERRUPT ENABLE */ 246a0ed1411SAnsuel Smith 247a0ed1411SAnsuel Smith /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */ 248a0ed1411SAnsuel Smith [LOW_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 0, 7), 249a0ed1411SAnsuel Smith [UP_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 8, 15), 250a0ed1411SAnsuel Smith /* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield 251a0ed1411SAnsuel Smith * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp 252a0ed1411SAnsuel Smith * MIN_THRESH_0 -> CRIT_THRESH_1 253a0ed1411SAnsuel Smith * MAX_THRESH_0 -> CRIT_THRESH_0 254a0ed1411SAnsuel Smith */ 255a0ed1411SAnsuel Smith [CRIT_THRESH_1] = REG_FIELD(THRESHOLD_ADDR, 16, 23), 256a0ed1411SAnsuel Smith [CRIT_THRESH_0] = REG_FIELD(THRESHOLD_ADDR, 24, 31), 257a0ed1411SAnsuel Smith 258a0ed1411SAnsuel Smith /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */ 259a0ed1411SAnsuel Smith /* 1 == clear, 0 == normal operation */ 260a0ed1411SAnsuel Smith [LOW_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 9, 9), 261a0ed1411SAnsuel Smith [UP_INT_CLEAR_0] = REG_FIELD(CNTL_ADDR, 10, 10), 262a0ed1411SAnsuel Smith 263a0ed1411SAnsuel Smith /* NO CRITICAL INTERRUPT SUPPORT on 8960 */ 264a0ed1411SAnsuel Smith 265a0ed1411SAnsuel Smith /* Sn_STATUS */ 266a0ed1411SAnsuel Smith [LAST_TEMP_0] = REG_FIELD(S0_STATUS_OFF, 0, 7), 267a0ed1411SAnsuel Smith [LAST_TEMP_1] = REG_FIELD(S1_STATUS_OFF, 0, 7), 268a0ed1411SAnsuel Smith [LAST_TEMP_2] = REG_FIELD(S2_STATUS_OFF, 0, 7), 269a0ed1411SAnsuel Smith [LAST_TEMP_3] = REG_FIELD(S3_STATUS_OFF, 0, 7), 270a0ed1411SAnsuel Smith [LAST_TEMP_4] = REG_FIELD(S4_STATUS_OFF, 0, 7), 271a0ed1411SAnsuel Smith [LAST_TEMP_5] = REG_FIELD(S5_STATUS_OFF, 0, 7), 272a0ed1411SAnsuel Smith [LAST_TEMP_6] = REG_FIELD(S6_STATUS_OFF, 0, 7), 273a0ed1411SAnsuel Smith [LAST_TEMP_7] = REG_FIELD(S7_STATUS_OFF, 0, 7), 274a0ed1411SAnsuel Smith [LAST_TEMP_8] = REG_FIELD(S8_STATUS_OFF, 0, 7), 275a0ed1411SAnsuel Smith [LAST_TEMP_9] = REG_FIELD(S9_STATUS_OFF, 0, 7), 276a0ed1411SAnsuel Smith [LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0, 7), 277a0ed1411SAnsuel Smith 278a0ed1411SAnsuel Smith /* No VALID field on 8960 */ 279a0ed1411SAnsuel Smith /* TSENS_INT_STATUS bits: 1 == threshold violated */ 280a0ed1411SAnsuel Smith [MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0), 281a0ed1411SAnsuel Smith [LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1), 282a0ed1411SAnsuel Smith [UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2), 283a0ed1411SAnsuel Smith /* No CRITICAL field on 8960 */ 284a0ed1411SAnsuel Smith [MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3), 285a0ed1411SAnsuel Smith 286a0ed1411SAnsuel Smith /* TRDY: 1=ready, 0=in progress */ 287a0ed1411SAnsuel Smith [TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7), 288a0ed1411SAnsuel Smith }; 289a0ed1411SAnsuel Smith 290032d4057SEduardo Valentin static const struct tsens_ops ops_8960 = { 291*fdda131fSAnsuel Smith .init = init_common, 29220d4fd84SRajendra Nayak .calibrate = calibrate_8960, 29320d4fd84SRajendra Nayak .get_temp = get_temp_8960, 29420d4fd84SRajendra Nayak .enable = enable_8960, 29520d4fd84SRajendra Nayak .disable = disable_8960, 29620d4fd84SRajendra Nayak .suspend = suspend_8960, 29720d4fd84SRajendra Nayak .resume = resume_8960, 29820d4fd84SRajendra Nayak }; 29920d4fd84SRajendra Nayak 30053e2a20eSAnsuel Smith static struct tsens_features tsens_8960_feat = { 30153e2a20eSAnsuel Smith .ver_major = VER_0, 30253e2a20eSAnsuel Smith .crit_int = 0, 30353e2a20eSAnsuel Smith .adc = 1, 30453e2a20eSAnsuel Smith .srot_split = 0, 30553e2a20eSAnsuel Smith .max_sensors = 11, 30653e2a20eSAnsuel Smith }; 30753e2a20eSAnsuel Smith 3080aef1ee5SAmit Kucheria struct tsens_plat_data data_8960 = { 30920d4fd84SRajendra Nayak .num_sensors = 11, 31020d4fd84SRajendra Nayak .ops = &ops_8960, 31153e2a20eSAnsuel Smith .feat = &tsens_8960_feat, 312a0ed1411SAnsuel Smith .fields = tsens_8960_regfields, 31320d4fd84SRajendra Nayak }; 314