| // SPDX-License-Identifier: GPL-2.0 | 
| /* | 
|  * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 
|  */ | 
|   | 
| #include <linux/platform_device.h> | 
| #include <linux/delay.h> | 
| #include <linux/bitops.h> | 
| #include <linux/regmap.h> | 
| #include <linux/thermal.h> | 
| #include "tsens.h" | 
|   | 
| #define CAL_MDEGC        30000 | 
|   | 
| #define CONFIG_ADDR        0x3640 | 
| #define CONFIG_ADDR_8660    0x3620 | 
| /* CONFIG_ADDR bitmasks */ | 
| #define CONFIG            0x9b | 
| #define CONFIG_MASK        0xf | 
| #define CONFIG_8660        1 | 
| #define CONFIG_SHIFT_8660    28 | 
| #define CONFIG_MASK_8660    (3 << CONFIG_SHIFT_8660) | 
|   | 
| #define STATUS_CNTL_ADDR_8064    0x3660 | 
| #define CNTL_ADDR        0x3620 | 
| /* CNTL_ADDR bitmasks */ | 
| #define EN            BIT(0) | 
| #define SW_RST            BIT(1) | 
| #define SENSOR0_EN        BIT(3) | 
| #define SLP_CLK_ENA        BIT(26) | 
| #define SLP_CLK_ENA_8660    BIT(24) | 
| #define MEASURE_PERIOD        1 | 
| #define SENSOR0_SHIFT        3 | 
|   | 
| /* INT_STATUS_ADDR bitmasks */ | 
| #define MIN_STATUS_MASK        BIT(0) | 
| #define LOWER_STATUS_CLR    BIT(1) | 
| #define UPPER_STATUS_CLR    BIT(2) | 
| #define MAX_STATUS_MASK        BIT(3) | 
|   | 
| #define THRESHOLD_ADDR        0x3624 | 
| /* THRESHOLD_ADDR bitmasks */ | 
| #define THRESHOLD_MAX_LIMIT_SHIFT    24 | 
| #define THRESHOLD_MIN_LIMIT_SHIFT    16 | 
| #define THRESHOLD_UPPER_LIMIT_SHIFT    8 | 
| #define THRESHOLD_LOWER_LIMIT_SHIFT    0 | 
|   | 
| /* Initial temperature threshold values */ | 
| #define LOWER_LIMIT_TH        0x50 | 
| #define UPPER_LIMIT_TH        0xdf | 
| #define MIN_LIMIT_TH        0x0 | 
| #define MAX_LIMIT_TH        0xff | 
|   | 
| #define S0_STATUS_ADDR        0x3628 | 
| #define INT_STATUS_ADDR        0x363c | 
| #define TRDY_MASK        BIT(7) | 
| #define TIMEOUT_US        100 | 
|   | 
| static int suspend_8960(struct tsens_priv *priv) | 
| { | 
|     int ret; | 
|     unsigned int mask; | 
|     struct regmap *map = priv->tm_map; | 
|   | 
|     ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     if (priv->num_sensors > 1) | 
|         mask = SLP_CLK_ENA | EN; | 
|     else | 
|         mask = SLP_CLK_ENA_8660 | EN; | 
|   | 
|     ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     return 0; | 
| } | 
|   | 
| static int resume_8960(struct tsens_priv *priv) | 
| { | 
|     int ret; | 
|     struct regmap *map = priv->tm_map; | 
|   | 
|     ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     /* | 
|      * Separate CONFIG restore is not needed only for 8660 as | 
|      * config is part of CTRL Addr and its restored as such | 
|      */ | 
|     if (priv->num_sensors > 1) { | 
|         ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); | 
|         if (ret) | 
|             return ret; | 
|     } | 
|   | 
|     ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     return 0; | 
| } | 
|   | 
| static int enable_8960(struct tsens_priv *priv, int id) | 
| { | 
|     int ret; | 
|     u32 reg, mask; | 
|   | 
|     ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     mask = BIT(id + SENSOR0_SHIFT); | 
|     ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     if (priv->num_sensors > 1) | 
|         reg |= mask | SLP_CLK_ENA | EN; | 
|     else | 
|         reg |= mask | SLP_CLK_ENA_8660 | EN; | 
|   | 
|     ret = regmap_write(priv->tm_map, CNTL_ADDR, reg); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     return 0; | 
| } | 
|   | 
| static void disable_8960(struct tsens_priv *priv) | 
| { | 
|     int ret; | 
|     u32 reg_cntl; | 
|     u32 mask; | 
|   | 
|     mask = GENMASK(priv->num_sensors - 1, 0); | 
|     mask <<= SENSOR0_SHIFT; | 
|     mask |= EN; | 
|   | 
|     ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); | 
|     if (ret) | 
|         return; | 
|   | 
|     reg_cntl &= ~mask; | 
|   | 
|     if (priv->num_sensors > 1) | 
|         reg_cntl &= ~SLP_CLK_ENA; | 
|     else | 
|         reg_cntl &= ~SLP_CLK_ENA_8660; | 
|   | 
|     regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | 
| } | 
|   | 
| static int init_8960(struct tsens_priv *priv) | 
| { | 
|     int ret, i; | 
|     u32 reg_cntl; | 
|   | 
|     priv->tm_map = dev_get_regmap(priv->dev, NULL); | 
|     if (!priv->tm_map) | 
|         return -ENODEV; | 
|   | 
|     /* | 
|      * The status registers for each sensor are discontiguous | 
|      * because some SoCs have 5 sensors while others have more | 
|      * but the control registers stay in the same place, i.e | 
|      * directly after the first 5 status registers. | 
|      */ | 
|     for (i = 0; i < priv->num_sensors; i++) { | 
|         if (i >= 5) | 
|             priv->sensor[i].status = S0_STATUS_ADDR + 40; | 
|         priv->sensor[i].status += i * 4; | 
|     } | 
|   | 
|     reg_cntl = SW_RST; | 
|     ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     if (priv->num_sensors > 1) { | 
|         reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); | 
|         reg_cntl &= ~SW_RST; | 
|         ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, | 
|                      CONFIG_MASK, CONFIG); | 
|     } else { | 
|         reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); | 
|         reg_cntl &= ~CONFIG_MASK_8660; | 
|         reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; | 
|     } | 
|   | 
|     reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; | 
|     ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     reg_cntl |= EN; | 
|     ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | 
|     if (ret) | 
|         return ret; | 
|   | 
|     return 0; | 
| } | 
|   | 
| static int calibrate_8960(struct tsens_priv *priv) | 
| { | 
|     int i; | 
|     char *data; | 
|   | 
|     ssize_t num_read = priv->num_sensors; | 
|     struct tsens_sensor *s = priv->sensor; | 
|   | 
|     data = qfprom_read(priv->dev, "calib"); | 
|     if (IS_ERR(data)) | 
|         data = qfprom_read(priv->dev, "calib_backup"); | 
|     if (IS_ERR(data)) | 
|         return PTR_ERR(data); | 
|   | 
|     for (i = 0; i < num_read; i++, s++) | 
|         s->offset = data[i]; | 
|   | 
|     kfree(data); | 
|   | 
|     return 0; | 
| } | 
|   | 
| /* Temperature on y axis and ADC-code on x-axis */ | 
| static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) | 
| { | 
|     int slope, offset; | 
|   | 
|     slope = thermal_zone_get_slope(s->tzd); | 
|     offset = CAL_MDEGC - slope * s->offset; | 
|   | 
|     return adc_code * slope + offset; | 
| } | 
|   | 
| static int get_temp_8960(const struct tsens_sensor *s, int *temp) | 
| { | 
|     int ret; | 
|     u32 code, trdy; | 
|     struct tsens_priv *priv = s->priv; | 
|     unsigned long timeout; | 
|   | 
|     timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | 
|     do { | 
|         ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); | 
|         if (ret) | 
|             return ret; | 
|         if (!(trdy & TRDY_MASK)) | 
|             continue; | 
|         ret = regmap_read(priv->tm_map, s->status, &code); | 
|         if (ret) | 
|             return ret; | 
|         *temp = code_to_mdegC(code, s); | 
|         return 0; | 
|     } while (time_before(jiffies, timeout)); | 
|   | 
|     return -ETIMEDOUT; | 
| } | 
|   | 
| static const struct tsens_ops ops_8960 = { | 
|     .init        = init_8960, | 
|     .calibrate    = calibrate_8960, | 
|     .get_temp    = get_temp_8960, | 
|     .enable        = enable_8960, | 
|     .disable    = disable_8960, | 
|     .suspend    = suspend_8960, | 
|     .resume        = resume_8960, | 
| }; | 
|   | 
| struct tsens_plat_data data_8960 = { | 
|     .num_sensors    = 11, | 
|     .ops        = &ops_8960, | 
| }; |