// 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, &reg);
	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, &reg_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,
}