// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2019, Linaro Limited.
 * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
 *
 */

#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include "tsens.h"

/* TSENS register data */
#define TSENS_CNTL_ADDR			0x4
#define TSENS_CONFIG_ADDR		0x50
#define TSENS_TRDY_TIMEOUT_US		20000
#define TSENS_THRESHOLD_MAX_CODE	0x3ff
#define TSENS_THRESHOLD_MIN_CODE	0x0
#define TSENS_SN_CTRL_EN		BIT(0)

#define TSENS_TM_TRDY			0x10e4
#define TSENS_TRDY_MASK			BIT(0)

#define TSENS_TM_INT_EN_ADDR		0x1000
#define TSENS_TM_INT_EN			BIT(0)

#define TSENS_TM_SN_STATUS			0x1044
#define TSENS_TM_SN_STATUS_VALID_BIT		BIT(14)
#define TSENS_TM_SN_STATUS_CRITICAL_STATUS	BIT(19)
#define TSENS_TM_SN_STATUS_UPPER_STATUS		BIT(12)
#define TSENS_TM_SN_STATUS_LOWER_STATUS		BIT(11)
#define TSENS_TM_SN_LAST_TEMP_MASK		0x3ff

#define TSENS_TM_UPPER_LOWER_STATUS_CTRL(n)	((0x4 * n) + 0x1004)
#define TSENS_TM_UPPER_STATUS_CLEAR		BIT(21)
#define TSENS_TM_LOWER_STATUS_CLEAR		BIT(20)
#define TSENS_TM_UPPER_THRESHOLD_SET(n)		((n) << 10)
#define TSENS_TM_UPPER_THRESHOLD_MASK		0xffc00
#define TSENS_TM_LOWER_THRESHOLD_MASK		0x3ff
#define TSENS_TM_UPPER_THRESHOLD_VALUE(n)	(((n) & TSENS_TM_UPPER_THRESHOLD_MASK) >> 10)
#define TSENS_TM_LOWER_THRESHOLD_VALUE(n)	((n) & TSENS_TM_LOWER_THRESHOLD_MASK)

#define MAX_SENSOR				5

/*
 * ADC is 10 bits long so it can't go beyond 1023
 */
#define MAX_ADC		1023
#define MIN_ADC		0

#define MAX_TEMP	243 /* Temperature for adc code 1023 */
#define MIN_TEMP	-77 /* Temperature for adc code 0 */

/* eeprom layout data for ipq5018 */
#define S0_P1_MASK_4_0	0xf8000000
#define BASE0_MASK	0x0007f800
#define BASE1_MASK	0x07f80000
#define CAL_SEL_MASK	0x00000700

#define S3_P1_MASK_0	0x80000000
#define S2_P2_MASK	0x7e000000
#define S2_P1_MASK	0x01f80000
#define S1_P2_MASK	0x0007e000
#define S1_P1_MASK	0x00001f80
#define S0_P2_MASK	0x0000007e
#define S0_P1_MASK_5	0x00000001

#define S4_P1_MASK	0x0001f800
#define S3_P2_MASK	0x000007e0
#define S3_P1_MASK_5_1	0x0000001f

#define S4_P2_MASK	0x0000003f

#define S0_P1_SHIFT_4_0	27
#define BASE0_SHIFT	11
#define BASE1_SHIFT	19
#define CAL_SEL_SHIFT	8

#define S3_P1_SHIFT_0	31
#define S2_P2_SHIFT	25
#define S2_P1_SHIFT	19
#define S1_P2_SHIFT	13
#define S1_P1_SHIFT	7
#define S0_P2_SHIFT	1
#define S0_P1_SHIFT_5	0

#define S4_P1_SHIFT	11
#define S3_P2_SHIFT	5
#define S3_P1_SHIFT_5_1	0

#define S4_P2_SHIFT	0

#define CONFIG			0x9c
#define CONFIG_MASK		0xf
#define CONFIG_SHIFT		0

#define EN                      BIT(0)
#define SW_RST                  BIT(1)
#define SENSOR0_EN              BIT(3)
#define SLP_CLK_ENA             BIT(26)
#define MEASURE_PERIOD          1
#define SENSOR0_SHIFT           3

static bool int_clr_deassert_quirk;

static int get_temp_ipq5018(struct tsens_device *tmdev, int id, int *temp);

int code2degc_lut_degc[MAX_SENSOR][1024];

/* Trips: from very hot to very cold */
/* Maintain same enum values as in tsens-ipq807x.c:enum tsens_trip_type */
enum tsens_trip_type {
	TSENS_TRIP_STAGE3 = 0, /* Critical high, not available in IPQ5018 */
	TSENS_TRIP_STAGE2 = 1, /* Configurable high */
	TSENS_TRIP_STAGE1,     /* Configurable low */
	TSENS_TRIP_STAGE0,     /* Critical low, not available in IPQ5018 */
	TSENS_TRIP_NUM,
};

int degc_to_code(int temp, int sensor)
{
	int code;

	for (code = 0; code < 1024; code++) {
		if (temp == code2degc_lut_degc[sensor][code])
			return code;
	}

	return MAX_TEMP;
}

/*
 * Set Trip temp in degree Celcius
 */
static int set_trip_temp(struct tsens_device *tmdev, int sensor,
					enum tsens_trip_type trip, int temp)
{
	u32 reg_th, th_hi, th_lo, reg_th_offset;

	if (!tmdev)
		return -EINVAL;

	if ((sensor < 0) || (sensor > (MAX_SENSOR - 1)))
		return -EINVAL;

	if ((temp < MIN_TEMP) || (temp > MAX_TEMP))
		return -EINVAL;

	temp = degc_to_code(temp, sensor);

	reg_th_offset = TSENS_TM_UPPER_LOWER_STATUS_CTRL(sensor);
	regmap_read(tmdev->map,
		TSENS_TM_UPPER_LOWER_STATUS_CTRL(sensor), &reg_th);

	th_hi = TSENS_TM_UPPER_THRESHOLD_VALUE(reg_th);
	th_lo = TSENS_TM_LOWER_THRESHOLD_VALUE(reg_th);

	switch (trip) {
	case TSENS_TRIP_STAGE2:
		if (temp <= th_lo)
			return -EINVAL;

		temp = TSENS_TM_UPPER_THRESHOLD_SET(temp);
		/* Don't change lower threshold */
		reg_th &= TSENS_TM_LOWER_THRESHOLD_MASK;
		reg_th |= temp;
		break;
	case TSENS_TRIP_STAGE1:
		if (temp >= th_hi)
			return -EINVAL;

		/* Don't change upper threshold */
		reg_th &= TSENS_TM_UPPER_THRESHOLD_MASK;
		reg_th |= temp;
		break;
	default:
		return -EINVAL;
	}

	regmap_write(tmdev->map, TSENS_TM_INT_EN_ADDR, 0);
	regmap_write(tmdev->map, reg_th_offset, reg_th);
	regmap_write(tmdev->map, TSENS_TM_INT_EN_ADDR, TSENS_TM_INT_EN);

	/* Sync registers */
	mb();

	return 0;
}

static void notify_uspace_tsens_fn(struct work_struct *work)
{
	struct tsens_sensor *s = container_of(work, struct tsens_sensor,
								notify_work);

	if (!s || !s->tzd)
		/* Do nothing. TSENS driver has not been registered yet */
		return;

	sysfs_notify(&s->tzd->device.kobj, NULL, "type");
}

static void tsens_scheduler_fn(struct work_struct *work)
{
	struct tsens_device *tmdev = container_of(work, struct tsens_device,
					tsens_work);
	int i, reg_thr, temp, th_upper = 0, th_lower = 0;
	u32 reg_val, reg_addr;

	/*Check whether TSENS is enabled */
	regmap_read(tmdev->map, TSENS_CNTL_ADDR, &reg_val);
	if (!(reg_val & TSENS_SN_CTRL_EN))
		return;

	/* Iterate through all sensors */
	for (i = 0; i < tmdev->num_sensors; i++) {
		/* Reset for each iteration */
		reg_thr = th_upper = th_lower = 0;

		regmap_read(tmdev->map, tmdev->sensor[i].status, &reg_val);

		/* Check whether the temp is valid */
		if (!(reg_val & TSENS_TM_SN_STATUS_VALID_BIT))
			continue;

		/* Check whether upper threshold is hit */
		if (reg_val & TSENS_TM_SN_STATUS_UPPER_STATUS) {
			reg_thr |= TSENS_TM_UPPER_STATUS_CLEAR;
			reg_addr = TSENS_TM_UPPER_LOWER_STATUS_CTRL(i);
			th_upper = 1;
		}
		/* Check whether lower threshold is hit */
		if (reg_val & TSENS_TM_SN_STATUS_LOWER_STATUS) {
			reg_thr |= TSENS_TM_LOWER_STATUS_CLEAR;
			reg_addr = TSENS_TM_UPPER_LOWER_STATUS_CTRL(i);
			th_lower = 1;
		}

		if (th_upper || th_lower) {
			regmap_write(tmdev->map, reg_addr, reg_thr);
			/* Notify user space */
			schedule_work(&tmdev->sensor[i].notify_work);

			if (int_clr_deassert_quirk)
				regmap_write(tmdev->map, reg_addr, 0);

			if (!get_temp_ipq5018(tmdev, i, &temp))
				pr_debug("Trigger (%d degrees) for sensor %d\n",
					temp, i);
		}
	}

	/* Sync registers */
	mb();
}

static irqreturn_t tsens_isr(int irq, void *data)
{
	struct tsens_device *tmdev = data;

	schedule_work(&tmdev->tsens_work);
	return IRQ_HANDLED;
}

static int init_ipq5018(struct tsens_device *tmdev)
{
	int ret;
	int i;
	u32 reg_cntl;

	init_common(tmdev);
	if (!tmdev->map)
		return -ENODEV;

	/* Store all sensor address for future use */
	for (i = 0; i < tmdev->num_sensors; i++) {
		tmdev->sensor[i].status = TSENS_TM_SN_STATUS + (i * 4);
		INIT_WORK(&tmdev->sensor[i].notify_work,
						notify_uspace_tsens_fn);
	}

	reg_cntl = SW_RST;
	ret = regmap_update_bits(tmdev->map, TSENS_CNTL_ADDR, SW_RST, reg_cntl);
	if (ret)
		return ret;

	reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
	reg_cntl &= ~SW_RST;
	ret = regmap_update_bits(tmdev->map, TSENS_CONFIG_ADDR,
				CONFIG_MASK, CONFIG);

	reg_cntl |= GENMASK(MAX_SENSOR - 1, 0) << SENSOR0_SHIFT;
	ret = regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg_cntl);
	if (ret)
		return ret;

	reg_cntl |= EN;
	ret = regmap_write(tmdev->map, TSENS_CNTL_ADDR, reg_cntl);
	if (ret)
		return ret;

	/* Init tsens worker thread */
	INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);

	/* Register and enable the ISR */
	ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr,
			IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev);
	if (ret < 0) {
		pr_err("%s: request_irq FAIL: %d\n", __func__, ret);
		return ret;
	}
	enable_irq_wake(tmdev->tsens_irq);

	regmap_write(tmdev->map, TSENS_TM_INT_EN_ADDR, TSENS_TM_INT_EN);

	int_clr_deassert_quirk = device_property_read_bool(tmdev->dev,
				"tsens-up-low-int-clr-deassert-quirk");
	/* Sync registers */
	mb();

	return 0;
}

static int calibrate_ipq5018(struct tsens_device *tmdev)
{
	u32 base0 = 0, base1 = 0;
	u32 p1[MAX_SENSOR], p2[MAX_SENSOR];
	u32 mode = 0, lsb = 0, msb = 0;
	u32 *qfprom_cdata;
	int i, sensor;

	qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
	if (IS_ERR(qfprom_cdata))
		return PTR_ERR(qfprom_cdata);

	mode = (qfprom_cdata[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;

	dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);

	switch (mode) {
	case TWO_PT_CALIB:
		base1 = (qfprom_cdata[0] & BASE1_MASK) >> BASE1_SHIFT;
		p2[0] = (qfprom_cdata[1] & S0_P2_MASK) >> S0_P2_SHIFT;
		p2[1] = (qfprom_cdata[1] & S1_P2_MASK) >> S1_P2_SHIFT;
		p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
		p2[3] = (qfprom_cdata[2] & S3_P2_MASK) >> S3_P2_SHIFT;
		p2[4] = (qfprom_cdata[3] & S4_P2_MASK) >> S4_P2_SHIFT;
		for (i = 0; i < MAX_SENSOR; i++)
			p2[i] = ((base1 + p2[i]) << 2);
		/* Fall through */
	case ONE_PT_CALIB2:
		base0 = (qfprom_cdata[0] & BASE0_MASK) >> BASE0_SHIFT;
		/* This value is split over two registers, 5 bits and 1 bit */
		lsb = (qfprom_cdata[0] & S0_P1_MASK_4_0) >> S0_P1_SHIFT_4_0;
		msb = (qfprom_cdata[1] & S0_P1_MASK_5) >> S0_P1_SHIFT_5;
		p1[0] = msb << 5 | lsb;
		p1[1] = (qfprom_cdata[1] & S1_P1_MASK) >> S1_P1_SHIFT;
		p1[2] = (qfprom_cdata[1] & S2_P1_MASK) >> S2_P1_SHIFT;
		/* This value is split over two registers, 1 bit and 5 bits */
		lsb = (qfprom_cdata[1] & S3_P1_MASK_0) >> S3_P1_SHIFT_0;
		msb = (qfprom_cdata[2] & S3_P1_MASK_5_1) >> S3_P1_SHIFT_5_1;
		p1[3] = msb << 1 | lsb;
		p1[4] = (qfprom_cdata[2] & S4_P1_MASK) >> S4_P1_SHIFT;
		for (i = 0; i < MAX_SENSOR; i++)
			p1[i] = (((base0) + p1[i]) << 2);
		break;
	default:
		pr_info("Using default calibration\n");
		p1[0] = 403;
		p2[0] = 688;
		p1[1] = 390;
		p2[1] = 674;
		p1[2] = 341;
		p2[2] = 635;
		p1[3] = 387;
		p2[3] = 673;
		p1[4] = 347;
		p2[4] = 639;
		break;
	}

	compute_intercept_slope(tmdev, p1, p2, mode);
	kfree(qfprom_cdata);

	for (sensor = 0; sensor < tmdev->num_sensors; sensor++) {
		for (i = MIN_ADC; i <= MAX_ADC; i++) {
			code2degc_lut_degc[sensor][i] = code_to_degc(i,
							&tmdev->sensor[sensor]);
		}
	}

	return 0;
}

static int set_trip_temp_ipq5018(void *data, int trip, int temp)
{
	const struct tsens_sensor *s = data;

	if (!s)
		return -EINVAL;

	return set_trip_temp(s->tmdev, s->id, trip, temp);
}

static int get_temp_ipq5018(struct tsens_device *tmdev, int id, int *temp)
{
	const struct tsens_sensor *s = &tmdev->sensor[id];
	u32 code;
	unsigned int sensor_addr;
	int ret, last_temp = 0, last_temp2 = 0, last_temp3 = 0;

	sensor_addr = TSENS_TM_SN_STATUS + s->hw_id * 4;
	ret = regmap_read(tmdev->map, sensor_addr, &code);
	if (ret)
		return ret;
	last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
	if (code & TSENS_TM_SN_STATUS_VALID_BIT)
		goto done;

	/* Try a second time */
	ret = regmap_read(tmdev->map, sensor_addr, &code);
	if (ret)
		return ret;
	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
		last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
		goto done;
	} else {
		last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK;
	}

	/* Try a third/last time */
	ret = regmap_read(tmdev->map, sensor_addr, &code);
	if (ret)
		return ret;
	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
		last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
		goto done;
	} else {
		last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK;
	}

	if (last_temp == last_temp2)
		last_temp = last_temp2;
	else if (last_temp2 == last_temp3)
		last_temp = last_temp3;
done:
	/* Convert temperature from ADC code to Celsius */
	*temp = code_to_degc(last_temp, s);

	return 0;
}

static const struct tsens_ops ops_ipq5018 = {
	.init		= init_ipq5018,
	.calibrate	= calibrate_ipq5018,
	.get_temp	= get_temp_ipq5018,
	.set_trip_temp	= set_trip_temp_ipq5018,
};

const struct tsens_data data_ipq5018 = {
	.num_sensors	= MAX_SENSOR,
	.ops		= &ops_ipq5018,
};
