|  | // 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 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 CNTL_ADDR		0x3620 | 
|  | /* CNTL_ADDR bitmasks */ | 
|  | #define EN			BIT(0) | 
|  | #define SW_RST			BIT(1) | 
|  |  | 
|  | #define MEASURE_PERIOD		BIT(18) | 
|  | #define SLP_CLK_ENA		BIT(26) | 
|  | #define SLP_CLK_ENA_8660	BIT(24) | 
|  | #define SENSOR0_SHIFT		3 | 
|  |  | 
|  | #define THRESHOLD_ADDR		0x3624 | 
|  |  | 
|  | #define INT_STATUS_ADDR		0x363c | 
|  |  | 
|  | #define S0_STATUS_OFF		0x3628 | 
|  | #define S1_STATUS_OFF		0x362c | 
|  | #define S2_STATUS_OFF		0x3630 | 
|  | #define S3_STATUS_OFF		0x3634 | 
|  | #define S4_STATUS_OFF		0x3638 | 
|  | #define S5_STATUS_OFF		0x3664  /* Sensors 5-10 found on apq8064/msm8960 */ | 
|  | #define S6_STATUS_OFF		0x3668 | 
|  | #define S7_STATUS_OFF		0x366c | 
|  | #define S8_STATUS_OFF		0x3670 | 
|  | #define S9_STATUS_OFF		0x3674 | 
|  | #define S10_STATUS_OFF		0x3678 | 
|  |  | 
|  | /* Original slope - 350 to compensate mC to C inaccuracy */ | 
|  | static u32 tsens_msm8960_slope[] = { | 
|  | 826, 826, 804, 826, | 
|  | 761, 782, 782, 849, | 
|  | 782, 849, 782 | 
|  | }; | 
|  |  | 
|  | 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 = BIT(id); | 
|  |  | 
|  | ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | /* HARDWARE BUG: | 
|  | * On platforms with more than 6 sensors, all remaining sensors | 
|  | * must be enabled together, otherwise undefined results are expected. | 
|  | * (Sensor 6-7 disabled, Sensor 3 disabled...) In the original driver, | 
|  | * all the sensors are enabled in one step hence this bug is not | 
|  | * triggered. | 
|  | */ | 
|  | if (id > 5) | 
|  | mask = GENMASK(10, 6); | 
|  |  | 
|  | mask <<= SENSOR0_SHIFT; | 
|  |  | 
|  | /* Sensors already enabled. Skip. */ | 
|  | if ((reg & mask) == mask) | 
|  | return 0; | 
|  |  | 
|  | ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | reg |= MEASURE_PERIOD; | 
|  |  | 
|  | 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 calibrate_8960(struct tsens_priv *priv) | 
|  | { | 
|  | int i; | 
|  | char *data; | 
|  | u32 p1[11]; | 
|  |  | 
|  | 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 < priv->num_sensors; i++) { | 
|  | p1[i] = data[i]; | 
|  | priv->sensor[i].slope = tsens_msm8960_slope[i]; | 
|  | } | 
|  |  | 
|  | compute_intercept_slope(priv, p1, NULL, ONE_PT_CALIB); | 
|  |  | 
|  | kfree(data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct reg_field tsens_8960_regfields[MAX_REGFIELDS] = { | 
|  | /* ----- SROT ------ */ | 
|  | /* No VERSION information */ | 
|  |  | 
|  | /* CNTL */ | 
|  | [TSENS_EN]     = REG_FIELD(CNTL_ADDR,  0, 0), | 
|  | [TSENS_SW_RST] = REG_FIELD(CNTL_ADDR,  1, 1), | 
|  | /* 8960 has 5 sensors, 8660 has 11, we only handle 5 */ | 
|  | [SENSOR_EN]    = REG_FIELD(CNTL_ADDR,  3, 7), | 
|  |  | 
|  | /* ----- TM ------ */ | 
|  | /* INTERRUPT ENABLE */ | 
|  | /* NO INTERRUPT ENABLE */ | 
|  |  | 
|  | /* Single UPPER/LOWER TEMPERATURE THRESHOLD for all sensors */ | 
|  | [LOW_THRESH_0]   = REG_FIELD(THRESHOLD_ADDR,  0,  7), | 
|  | [UP_THRESH_0]    = REG_FIELD(THRESHOLD_ADDR,  8, 15), | 
|  | /* MIN_THRESH_0 and MAX_THRESH_0 are not present in the regfield | 
|  | * Recycle CRIT_THRESH_0 and 1 to set the required regs to hardcoded temp | 
|  | * MIN_THRESH_0 -> CRIT_THRESH_1 | 
|  | * MAX_THRESH_0 -> CRIT_THRESH_0 | 
|  | */ | 
|  | [CRIT_THRESH_1]   = REG_FIELD(THRESHOLD_ADDR, 16, 23), | 
|  | [CRIT_THRESH_0]   = REG_FIELD(THRESHOLD_ADDR, 24, 31), | 
|  |  | 
|  | /* UPPER/LOWER INTERRUPT [CLEAR/STATUS] */ | 
|  | /* 1 == clear, 0 == normal operation */ | 
|  | [LOW_INT_CLEAR_0]   = REG_FIELD(CNTL_ADDR,  9,  9), | 
|  | [UP_INT_CLEAR_0]    = REG_FIELD(CNTL_ADDR, 10, 10), | 
|  |  | 
|  | /* NO CRITICAL INTERRUPT SUPPORT on 8960 */ | 
|  |  | 
|  | /* Sn_STATUS */ | 
|  | [LAST_TEMP_0]  = REG_FIELD(S0_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_1]  = REG_FIELD(S1_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_2]  = REG_FIELD(S2_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_3]  = REG_FIELD(S3_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_4]  = REG_FIELD(S4_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_5]  = REG_FIELD(S5_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_6]  = REG_FIELD(S6_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_7]  = REG_FIELD(S7_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_8]  = REG_FIELD(S8_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_9]  = REG_FIELD(S9_STATUS_OFF,  0,  7), | 
|  | [LAST_TEMP_10] = REG_FIELD(S10_STATUS_OFF, 0,  7), | 
|  |  | 
|  | /* No VALID field on 8960 */ | 
|  | /* TSENS_INT_STATUS bits: 1 == threshold violated */ | 
|  | [MIN_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 0, 0), | 
|  | [LOWER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 1, 1), | 
|  | [UPPER_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 2, 2), | 
|  | /* No CRITICAL field on 8960 */ | 
|  | [MAX_STATUS_0] = REG_FIELD(INT_STATUS_ADDR, 3, 3), | 
|  |  | 
|  | /* TRDY: 1=ready, 0=in progress */ | 
|  | [TRDY] = REG_FIELD(INT_STATUS_ADDR, 7, 7), | 
|  | }; | 
|  |  | 
|  | static const struct tsens_ops ops_8960 = { | 
|  | .init		= init_common, | 
|  | .calibrate	= calibrate_8960, | 
|  | .get_temp	= get_temp_common, | 
|  | .enable		= enable_8960, | 
|  | .disable	= disable_8960, | 
|  | .suspend	= suspend_8960, | 
|  | .resume		= resume_8960, | 
|  | }; | 
|  |  | 
|  | static struct tsens_features tsens_8960_feat = { | 
|  | .ver_major	= VER_0, | 
|  | .crit_int	= 0, | 
|  | .combo_int	= 0, | 
|  | .adc		= 1, | 
|  | .srot_split	= 0, | 
|  | .max_sensors	= 11, | 
|  | .trip_min_temp	= -40000, | 
|  | .trip_max_temp	= 120000, | 
|  | }; | 
|  |  | 
|  | struct tsens_plat_data data_8960 = { | 
|  | .num_sensors	= 11, | 
|  | .ops		= &ops_8960, | 
|  | .feat		= &tsens_8960_feat, | 
|  | .fields		= tsens_8960_regfields, | 
|  | }; |