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, ®); 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, ®_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