// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

//#define DEBUG
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/cpu_cooling.h>
#include <linux/pm_domain.h>
#include <linux/arm-smccc.h>
#include <linux/amlogic/secmon.h>
#include <linux/amlogic/ddr_cooling.h>
#include <linux/debugfs.h>
#include "thermal_core.h"
#include "thermal_hwmon.h"
#include "cpucore_cooling.h"
#include <linux/reset.h>

/*r1p1 thermal sensor version*/
#define R1P1_TS_CFG_REG1	(0x1 * 4)
#define R1P1_TS_CFG_REG2	(0x2 * 4)
#define R1P1_TS_CFG_REG3	(0x3 * 4)
#define R1P1_TS_CFG_REG4	(0x4 * 4)
#define R1P1_TS_CFG_REG5	(0x5 * 4)
#define R1P1_TS_CFG_REG6	(0x6 * 4)
#define R1P1_TS_CFG_REG7	(0x7 * 4)
#define R1P1_TS_CFG_REG8	(0x8 * 4)
#define R1P1_TS_STAT0		(0x10 * 4)
#define R1P1_TS_STAT1		(0x11 * 4)
#define R1P1_TS_STAT2		(0x12 * 4)
#define R1P1_TS_STAT3		(0x13 * 4)
#define R1P1_TS_STAT4		(0x14 * 4)
#define R1P1_TS_STAT5		(0x15 * 4)
#define R1P1_TS_STAT6		(0x16 * 4)
#define R1P1_TS_STAT7		(0x17 * 4)
#define R1P1_TS_STAT8		(0x18 * 4)
#define R1P1_TS_STAT9		(0x19 * 4)

#define R1P1_TS_VALUE_CONT	0x1
#define	R1P1_TRIM_INFO		0x0
#define R1P1_TS_TEMP_MASK	0xfff
#define R1P1_TS_IRQ_MASK	0xff

#define R1P1_TS_IRQ_LOGIC_EN_SHIT	15
#define R1P1_TS_IRQ_FALL3_EN_SHIT	31
#define R1P1_TS_IRQ_FALL2_EN_SHIT	30
#define R1P1_TS_IRQ_FALL1_EN_SHIT	29
#define R1P1_TS_IRQ_FALL0_EN_SHIT	28
#define R1P1_TS_IRQ_RISE3_EN_SHIT	27
#define R1P1_TS_IRQ_RISE2_EN_SHIT	26
#define R1P1_TS_IRQ_RISE1_EN_SHIT	25
#define R1P1_TS_IRQ_RISE0_EN_SHIT	24
#define R1P1_TS_IRQ_FALL3_CLR_SHIT	23
#define R1P1_TS_IRQ_FALL2_CLR_SHIT	22
#define R1P1_TS_IRQ_FALL1_CLR_SHIT	21
#define R1P1_TS_IRQ_FALL0_CLR_SHIT	20
#define R1P1_TS_IRQ_RISE3_CLR_SHIT	19
#define R1P1_TS_IRQ_RISE2_CLR_SHIT	18
#define R1P1_TS_IRQ_RISE1_CLR_SHIT	17
#define R1P1_TS_IRQ_RISE0_CLR_SHIT	16
#define R1P1_TS_IRQ_ALL_CLR		(0xff << 16)
#define R1P1_TS_IRQ_ALL_EN		(0xff << 24)
#define R1P1_TS_IRQ_ALL_CLR_SHIT	16

#define R1P1_TS_RSET_VBG	BIT(12)
#define R1P1_TS_RSET_ADC	BIT(11)
#define R1P1_TS_VCM_EN		BIT(10)
#define R1P1_TS_VBG_EN		BIT(9)
#define R1P1_TS_OUT_CTL		BIT(6)
#define R1P1_TS_FILTER_EN	BIT(5)
#define R1P1_TS_IPTAT_EN	BIT(4) /*for debug, no need enable*/
#define R1P1_TS_DEM_EN		BIT(3)
#define R1P1_TS_CH_SEL		0x3 /*set 3'b011 for work*/

#define R1P1_TS_HITEMP_EN	BIT(31)
#define R1P1_TS_REBOOT_ALL_EN	BIT(30)
#define R1P1_TS_REBOOT_TIME	(0xff << 16)

/*for all thermal sensor*/
#define MCELSIUS	1000
#define MAX_TS_NUM	3
#define	TS_DEF_RTEMP	125
#define	TEMP_CAL	1
#define	R1P1_CAL_NUM	4
#define	R1P1_TS_MAX	0x3500
#define	R1P1_TS_MIN	0x1500
#define	R1P1_TS_CLK_RATE	500000
#define R1P1_TS_WAIT		5
#define R1P1_PM_MIN_TIMEOUT	5
#define R1P1_PM_MAX_TIMEOUT	250
#define TSENSOR_CALI_READ	0x82000047

enum soc_type {
	SOC_ARCH_TS_R1P0 = 1,
	SOC_ARCH_TS_R1P1 = 2,
};

/**
 * struct meson_tsensor_platform_data
 * @cal_a, b, c, d: cali data
 * @cal_type: calibration type for temperature
 * @reboot_temp: high temperature reboot soc
 * This structure is required for configuration of exynos_tmu driver.
 */
struct meson_tsensor_platform_data {
	u32 cal_type;
	int tsensor_id;
	u32 cal_coeff[4];
	int reboot_temp;
};

/**
 * struct meson_tsensor_data :
 * A structure to hold the private data of the tsensor driver.
 */
struct meson_tsensor_data {
	int id;
	struct device		*dev;
	struct meson_tsensor_platform_data *pdata;
	void __iomem *base_c;
	void __iomem *base_e;
	int irq;
	enum soc_type soc;
	struct work_struct irq_work;
	struct mutex lock;/*mutex lock for set tsensor reg*/
	struct clk *clk;
	u32	trim_info;
	struct thermal_zone_device *tzd;
	unsigned int ntrip;
	struct reset_control *rst;
	int (*tsensor_hw_initialize)(struct platform_device *pdev);
	int (*tsensor_trips_initialize)(struct platform_device *pdev);
	void (*tsensor_control)(struct platform_device *pdev,
				bool on);
	int (*tsensor_read)(struct meson_tsensor_data *data);
	void (*tsensor_set_emulation)(struct meson_tsensor_data *data,
				      int temp);
	void (*tsensor_clear_irqs)(struct meson_tsensor_data *data);
	void (*tsensor_update_irqs)(struct meson_tsensor_data *data);
};

static void meson_report_trigger(struct meson_tsensor_data *p)
{
	char data[10], *envp[] = { data, NULL };
	struct thermal_zone_device *tz = p->tzd;
	int temp;
	unsigned int i;

	if (!tz) {
		pr_err("No thermal zone device defined\n");
		return;
	}
	/*
	 *if passive delay and polling delay all is zero
	 *mean thermal mode disabled, disable update envent
	 */
	if (0 == (tz->passive_delay_jiffies || tz->polling_delay_jiffies))
		return;

	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);

	mutex_lock(&tz->lock);
	/* Find the level for which trip happened */
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		tz->ops->get_trip_temp(tz, i, &temp);
		if (tz->last_temperature < temp)
			break;
	}

	snprintf(data, sizeof(data), "%u", i);
	kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp);
	mutex_unlock(&tz->lock);
}

/*
 * tsensor treats temperature as a mapped temperature code.
 * The temperature is converted differently depending on the calibration type.
 */
static u32 temp_to_code(struct meson_tsensor_data *data, int temp, bool trend)
{
	struct meson_tsensor_platform_data *pdata = data->pdata;
	s64 div_tmp1, div_tmp2;
	u32 uefuse, reg_code;
	u32 cal_a, cal_b, cal_c, cal_d, cal_type;

	uefuse = data->trim_info;
	uefuse = uefuse & 0xffff;

	/* T = 727.8*(u_real+u_efuse/(1<<16)) - 274.7 */
	/* u_readl = (5.05*YOUT)/((1<<16)+ 4.05*YOUT) */
	/*u_readl = (T + 274.7) / 727.8 - u_efuse / (1 << 16)*/
	/*Yout =  (u_readl / (5.05 - 4.05u_readl)) *(1 << 16)*/
	cal_type = pdata->cal_type;
	cal_a = pdata->cal_coeff[0];
	cal_b = pdata->cal_coeff[1];
	cal_c = pdata->cal_coeff[2];
	cal_d = pdata->cal_coeff[3];
	switch (cal_type & 0xf) {
	case 0x1:
		div_tmp2 = cal_c;
		div_tmp2 = div_tmp2 + temp * 10;
		div_tmp2 = (1 << 16) * div_tmp2;
		div_tmp2 = div_s64(div_tmp2, cal_d);
		if (uefuse & 0x8000)
			div_tmp2 = div_tmp2 + (uefuse & 0x7fff);
		else
			div_tmp2 = div_tmp2 - (uefuse & 0x7fff);
		div_tmp1 = cal_a * div_tmp2;
		div_tmp1 = div_s64(div_tmp1, 1 << 16);
		div_tmp1 = cal_b - div_tmp1;
		div_tmp2 = div_tmp2 * 100;
		div_tmp2 = div_s64(div_tmp2, div_tmp1);
		if (trend)
			reg_code = ((div_tmp2 >> 0x4) & R1P1_TS_TEMP_MASK)
				+ TEMP_CAL;
		else
			reg_code = ((div_tmp2 >> 0x4) & R1P1_TS_TEMP_MASK);
		break;
	default:
		pr_info("Cal_type not supported\n");
		return -EINVAL;
	}
	return reg_code;
}

/*
 * Calculate a temperature value from a temperature code.
 * The unit of the temperature is degree Celsius.
 */
static int code_to_temp(struct meson_tsensor_data *data, int temp_code)
{
	struct meson_tsensor_platform_data *pdata = data->pdata;
	u32 cal_type, cal_a, cal_b, cal_c, cal_d;
	s64 temp, div_tmp1, div_tmp2;
	u32 uefuse;

	uefuse = data->trim_info;
	uefuse = uefuse & 0xffff;
	temp = temp_code;

	cal_type = (pdata->cal_type) & 0xf;
	cal_a = pdata->cal_coeff[0];
	cal_b = pdata->cal_coeff[1];
	cal_c = pdata->cal_coeff[2];
	cal_d = pdata->cal_coeff[3];
	switch (cal_type) {
	case 0x1:
		/* T = 727.8*(u_real+u_efuse/(1<<16)) - 274.7 */
		/* u_readl = (5.05*YOUT)/((1<<16)+ 4.05*YOUT) */
		/*u_readl = (T + 274.7) / 727.8 - u_efuse / (1 << 16)*/
		/*Yout =  (u_readl / (5.05 - 4.05u_readl)) *(1 << 16)*/
		div_tmp1 = cal_a * temp;
		div_tmp1 = div_s64(div_tmp1, 100);
		div_tmp2 = temp * cal_b;
		div_tmp2 = div_s64(div_tmp2, 100);
		div_tmp2 = div_tmp2 * (1 << 16);
		div_tmp2 = div_s64(div_tmp2, (1 << 16) + div_tmp1);
		if (uefuse & 0x8000) {
			div_tmp1 = (div_tmp2 - (uefuse & (0x7fff))) * cal_d;
			div_tmp1 = div_s64(div_tmp1, 1 << 16);
			temp = (div_tmp1 - cal_c) * 100;

		} else {
			div_tmp1 = (div_tmp2 + uefuse) * cal_d;
			div_tmp1 = div_s64(div_tmp1, 1 << 16);
			temp = (div_tmp1 - cal_c) * 100;
		}
		break;
	default:
		pr_info("Cal_type not supported\n");
		return -EINVAL;
	}
	return temp;
}

static int meson_tsensor_hw_initialize(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	int ret;

	mutex_lock(&data->lock);
	ret = data->tsensor_hw_initialize(pdev);
	mutex_unlock(&data->lock);
	return ret;
}

static int meson_tsensor_trips_initialize(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	int ret;

	if (of_thermal_get_ntrips(data->tzd) > data->ntrip) {
		dev_info_once(&pdev->dev,
			 "More trip points than supported by this tsensor.\n");
		dev_info_once(&pdev->dev,
			 "%d trip points should be configured in polling mode.\n",
			 (of_thermal_get_ntrips(data->tzd) - data->ntrip));
	}
	mutex_lock(&data->lock);
	ret = data->tsensor_trips_initialize(pdev);
	mutex_unlock(&data->lock);
	return ret;
}

static void meson_tsensor_control(struct platform_device *pdev, bool on)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);

	mutex_lock(&data->lock);
	data->tsensor_control(pdev, on);
	mutex_unlock(&data->lock);
}

static void r1p1_tsensor_control(struct platform_device *pdev, bool on)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	unsigned int con;
	int ret;

	con = readl(data->base_c + R1P1_TS_CFG_REG1);

	if (on) {
		con |= (0x1 << R1P1_TS_IRQ_LOGIC_EN_SHIT);
		con |= (R1P1_TS_FILTER_EN | R1P1_TS_VCM_EN | R1P1_TS_VBG_EN
			| R1P1_TS_DEM_EN | R1P1_TS_CH_SEL);
		con &= ~(R1P1_TS_RSET_VBG | R1P1_TS_RSET_ADC);
		ret = clk_set_rate(data->clk, R1P1_TS_CLK_RATE);
		if (ret)
			dev_err(&pdev->dev,
				"Failed to set clk rate: %d\n", ret);
		ret = clk_prepare_enable(data->clk);
		if (ret)
			dev_err(&pdev->dev,
				"Failed to perpare clk enable: %d\n", ret);
	} else {
		clk_disable_unprepare(data->clk);
		con &= ~((1 << R1P1_TS_IRQ_LOGIC_EN_SHIT)
			| (R1P1_TS_IRQ_ALL_CLR));
		con &= ~(R1P1_TS_FILTER_EN | R1P1_TS_VCM_EN | R1P1_TS_VBG_EN
			| R1P1_TS_IPTAT_EN | R1P1_TS_DEM_EN);
	}
	writel(con, data->base_c + R1P1_TS_CFG_REG1);
}

static int r1p1_tsensor_hw_initialize(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	struct meson_tsensor_platform_data *pdata = data->pdata;
	u32 reboot_reg = 0xffff, con = 0;
	int ret = 0, reboot_temp;
	int ver;

	/*first get the r1p1 trim info*/
	ver = (data->trim_info >> 24) & 0xff;
	/*r1p1 tsensor ver to doing*/
	if (((ver & 0xf) >> 2) == 0)
		return -EINVAL;
	if ((ver & 0x80)  == 0)
		return -EINVAL;

	/*r1p1 init the ts reboot soc function*/
	reboot_temp = pdata->reboot_temp;
	if (reboot_temp) {
		reboot_reg = temp_to_code(data, reboot_temp / MCELSIUS, true);
		con = (readl(data->base_c + R1P1_TS_CFG_REG2) |
			(reboot_reg << 4));
		con |= (R1P1_TS_HITEMP_EN | R1P1_TS_REBOOT_ALL_EN);
		con |= (R1P1_TS_REBOOT_TIME);
		writel(con, data->base_c + R1P1_TS_CFG_REG2);
	} else {
		pr_info("tsensor rtemp is zore no enable hireboot\n");
	}

	return ret;
}

static int r1p1_tsensor_trips_initialize(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	struct thermal_zone_device *tz = data->tzd;
	u32 rising_threshold = 0, falling_threshold = 0;
	int ret = 0, threshold_code, i;
	int temp, temp_hist;
	unsigned int reg_off, bit_off;

	/*
	 * Write temperature code for rising and falling threshold
	 * On r1p1 tsensor there are 4 rising and 4 falling threshold
	 * registers (0x50-0x5c and 0x60-0x6c respectively). Each
	 * register holds the value of two threshold levels (at bit
	 * offsets 0 and 16). Based on the fact that there are atmost
	 * eight possible trigger levels, calculate the register and
	 * bit offsets where the threshold levels are to be written.
	 *
	 * e.g.
	 * R1P1_TS_CFG_REG4 (0x4 << 2)
	 * [23:12] - rise_th0
	 * [11:0] - rise_th1
	 * R1P1_TS_CFG_REG5 (0x5 << 2)
	 * [23:12] - rise_th2
	 * [11:0] - rise_th3
	 * R1P1_TS_CFG_REG6 (0x6 << 2)
	 * [23:12] - fall_th0
	 * [11:0] - fall_th1
	 * R1P1_TS_CFG_REG7 (0x7 << 2)
	 * [23:12] - fall_th2
	 * [11:0] - fall_th3
	 */
	for (i = (data->ntrip - 1); i >= 0; i--) {
		reg_off = (i / 2) << 2;
		bit_off = ((i + 1) % 2);
		tz->ops->get_trip_temp(tz, i, &temp);
		temp /= MCELSIUS;
		tz->ops->get_trip_hyst(tz, i, &temp_hist);
		temp_hist = temp - (temp_hist / MCELSIUS);

		/* Set 12-bit temperature code for rising threshold levels */
		threshold_code = temp_to_code(data, temp, true);
		rising_threshold = readl(data->base_c +
				R1P1_TS_CFG_REG4 + reg_off);
		rising_threshold &= ~(R1P1_TS_TEMP_MASK << (12 * bit_off));
		rising_threshold |= threshold_code << (12 * bit_off);
		writel(rising_threshold,
		       data->base_c + R1P1_TS_CFG_REG4 + reg_off);

		/* Set 12-bit temperature code for falling threshold levels */
		threshold_code = temp_to_code(data, temp_hist, false);
		falling_threshold = readl(data->base_c +
					  R1P1_TS_CFG_REG6 + reg_off);
		falling_threshold &= ~(R1P1_TS_TEMP_MASK << (12 * bit_off));
		falling_threshold |= threshold_code << (12 * bit_off);
		writel(falling_threshold,
		       data->base_c + R1P1_TS_CFG_REG6 + reg_off);
	}
	data->tsensor_update_irqs(data);
	data->tsensor_clear_irqs(data);

	return ret;
}

static int r1p1_tsensor_read(struct meson_tsensor_data *data)
{
	int j, cnt = 0;
	unsigned int tvalue = 0;
	unsigned int value_all = 0;

	/*
	 *r1p1 tsensor get 16 times value.
	 */
	for (j = 0; j < R1P1_TS_VALUE_CONT; j++) {
		tvalue = readl(data->base_c + R1P1_TS_STAT0);
		tvalue = tvalue & 0xffff;
		if (tvalue >= R1P1_TS_MIN && tvalue <= R1P1_TS_MAX) {
			cnt++;
			value_all += (tvalue & 0xffff);
		}
	}

	if (cnt) {
		tvalue = value_all / cnt;
		pr_debug("%s  vall: %u, cnt: %u\n",
			 __func__, value_all, cnt);
	} else {
		pr_info("%s  valid cnt is 0, tvalue:%d\n", __func__, tvalue);
		tvalue = 0;
	}
	return tvalue;
}

static void r1p1_tsensor_set_emulation(struct meson_tsensor_data *data,
				       int temp)
{
	pr_info("r1p1 ts no emulation\n");
}

static void r1p1_tsensor_clear_irqs(struct meson_tsensor_data *data)
{
	unsigned int val_irq;

	val_irq = (readl(data->base_c + R1P1_TS_STAT1)
			& R1P1_TS_IRQ_MASK);
	val_irq = (readl(data->base_c + R1P1_TS_CFG_REG1)
			| (val_irq << R1P1_TS_IRQ_ALL_CLR_SHIT));
	/* clear the interrupts */
	writel(val_irq, data->base_c + R1P1_TS_CFG_REG1);
	/* restore clear enable*/
	val_irq = (readl(data->base_c + R1P1_TS_CFG_REG1)
			& (~R1P1_TS_IRQ_ALL_CLR));
	writel(val_irq, data->base_c + R1P1_TS_CFG_REG1);
}

static void r1p1_tsensor_update_irqs(struct meson_tsensor_data *data)
{
	struct thermal_zone_device *tz = data->tzd;
	int temp;
	unsigned int i, con;

	/* Find the level for which trip happened */
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		tz->ops->get_trip_temp(tz, i, &temp);
		if (tz->last_temperature < temp)
			break;
	}

	con = readl(data->base_c + R1P1_TS_CFG_REG1);
	con &= ~(R1P1_TS_IRQ_ALL_EN);
	con |= (of_thermal_is_trip_valid(tz, i)
			<< (R1P1_TS_IRQ_RISE0_EN_SHIT + i));
	con |= (of_thermal_is_trip_valid(tz, i - 1)
			<< (R1P1_TS_IRQ_FALL0_EN_SHIT + i - 1));
	con &= ~(R1P1_TS_IRQ_ALL_CLR);
	writel(con, data->base_c + R1P1_TS_CFG_REG1);
}

static int meson_get_temp(void *p, int *temp)
{
	struct meson_tsensor_data *data = p;

	if (!data->tsensor_read)
		return -EINVAL;

	mutex_lock(&data->lock);
	*temp = code_to_temp(data, data->tsensor_read(data));
	mutex_unlock(&data->lock);

	return 0;
}

static void meson_tsensor_work(struct work_struct *work)
{
	struct meson_tsensor_data *data = container_of(work,
			struct meson_tsensor_data, irq_work);

	meson_report_trigger(data);
	mutex_lock(&data->lock);
	/* TODO: take action based on particular interrupt */
	data->tsensor_update_irqs(data);
	data->tsensor_clear_irqs(data);
	mutex_unlock(&data->lock);
	enable_irq(data->irq);
}

static irqreturn_t meson_tsensor_irq(int irq, void *id)
{
	struct meson_tsensor_data *data = id;

	disable_irq_nosync(irq);
	schedule_work(&data->irq_work);

	return IRQ_HANDLED;
}

static const struct of_device_id meson_tsensor_match[] = {
	{ .compatible = "amlogic, r1p1-tsensor", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, meson_tsensor_match);

static int meson_of_get_soc_type(struct device_node *np)
{
	if (of_device_is_compatible(np, "amlogic, r1p1-tsensor"))
		return SOC_ARCH_TS_R1P1;
	return -EINVAL;
}

static int meson_of_sensor_conf(struct platform_device *pdev,
				struct meson_tsensor_platform_data *pdata)
{
	if (of_property_read_u32(pdev->dev.of_node, "tsensor_id",
				 &pdata->tsensor_id)) {
		pdata->tsensor_id = -1;
	}
	if (of_property_read_u32(pdev->dev.of_node, "cal_type",
				 &pdata->cal_type)) {
		dev_warn(&pdev->dev,
			 "Missing cal_type using default %d\n",
			 0x0);
		pdata->cal_type = 0x0;
	}
	if (of_property_read_u32_array(pdev->dev.of_node, "cal_coeff",
				       &pdata->cal_coeff[0], R1P1_CAL_NUM)) {
		dev_warn(&pdev->dev,
			 "Missing cal_coeff using default %d\n",
			 0x0);
	}
	if (of_property_read_u32(pdev->dev.of_node, "rtemp",
				 &pdata->reboot_temp)) {
		dev_warn(&pdev->dev,
			 "Missing rtemp using default %d\n",
			 TS_DEF_RTEMP);
		pdata->reboot_temp = TS_DEF_RTEMP;
	}
	pr_debug("tsensor rtemp :%d\n", pdata->reboot_temp);
	return 0;
}

static void of_thermal_destroy_one_zone(int id)
{
	struct device_node *np, *child;

	np = of_find_node_by_name(NULL, "thermal-zones");
	if (!np) {
		pr_debug("unable to find thermal zones\n");
		return;
	}

	for_each_available_child_of_node(np, child) {
		struct thermal_zone_device *zone;

		zone = thermal_zone_get_zone_by_name(child->name);
		if (IS_ERR(zone))
			continue;

		if (zone->id == id - 1) {
			thermal_zone_device_unregister(zone);
		}
	}
		of_node_put(np);
}

static int meson_map_dt_data(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	struct meson_tsensor_platform_data *pdata;
	struct resource res;
	struct arm_smccc_res smc_res;
	void *sharemem_outbuf_base;

	if (!data || !pdev->dev.of_node)
		return -ENODEV;
	data->id = of_alias_get_id(pdev->dev.of_node, "tsensor");
	pr_debug("tsensor id: %d\n", data->id);
	if (data->id < 0)
		data->id = 0;

	data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
	if (data->irq <= 0) {
		dev_err(&pdev->dev, "failed to get IRQ\n");
		return -ENODEV;
	}

	data->rst = devm_reset_control_get(&pdev->dev, "ts_rst");
	if (IS_ERR(data->rst))
		dev_warn(&pdev->dev, "Does not support reset func..\n");

	if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
		dev_err(&pdev->dev, "failed to get Resource 0\n");
		return -ENODEV;
	}

	data->base_c = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
	if (!data->base_c) {
		dev_err(&pdev->dev, "Failed to ioremap memory\n");
		return -EADDRNOTAVAIL;
	}

	pdata = devm_kzalloc(&pdev->dev,
			     sizeof(struct meson_tsensor_platform_data),
			     GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;
	meson_of_sensor_conf(pdev, pdata);

	if (!(pdata->cal_type & 0xf0)) {
		if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
			dev_err(&pdev->dev, "failed to get Resource 1\n");
			return -ENODEV;
		}
		data->base_e = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
		if (!data->base_e) {
			dev_err(&pdev->dev, "Failed to ioremap memory\n");
			return -ENOMEM;
		}
		data->trim_info = readl(data->base_e + R1P1_TRIM_INFO);
	} else {
		if (pdata->tsensor_id == -1) {
			dev_err(&pdev->dev, "Failed to get tsensor id\n");
			return -EINVAL;
		}

		meson_sm_mutex_lock();
		arm_smccc_smc(TSENSOR_CALI_READ, pdata->tsensor_id, 0, 0, 0, 0, 0, 0, &smc_res);
		if (smc_res.a0) {
			meson_sm_mutex_unlock();
			dev_err(&pdev->dev, "Failed to get thermal cali data from bl31\n");
			return -EINVAL;
		}

		sharemem_outbuf_base = get_meson_sm_output_base();
		if (!sharemem_outbuf_base) {
			meson_sm_mutex_unlock();
			dev_err(&pdev->dev, "Failed to get thermal cali data address\n");
			return -EINVAL;
		}
		memcpy(&data->trim_info, (const void *)sharemem_outbuf_base, 4);
		meson_sm_mutex_unlock();
		if ((data->trim_info & 0x80000000)  == 0) {
			dev_warn(&pdev->dev, "temp sensor not cali, unregister thermal zone%d.\n",
				 pdata->tsensor_id - 1);
			of_thermal_destroy_one_zone(pdata->tsensor_id);
			return -EINVAL;
		}
		pr_info("trim info = %x\n", data->trim_info);
	}

	data->pdata = pdata;
	data->soc = meson_of_get_soc_type(pdev->dev.of_node);
	switch (data->soc) {
	case SOC_ARCH_TS_R1P1:
		data->tsensor_hw_initialize = r1p1_tsensor_hw_initialize;
		data->tsensor_trips_initialize = r1p1_tsensor_trips_initialize;
		data->tsensor_control = r1p1_tsensor_control;
		data->tsensor_read = r1p1_tsensor_read;
		data->tsensor_set_emulation = r1p1_tsensor_set_emulation;
		data->tsensor_clear_irqs = r1p1_tsensor_clear_irqs;
		data->tsensor_update_irqs =  r1p1_tsensor_update_irqs;
		data->ntrip = 4;
		break;
	default:
		dev_err(&pdev->dev, "Platform not supported\n");
		return -EINVAL;
	}
	return 0;
}

static void meson_thermal_hot_callback(struct thermal_zone_device *tz)
{
	struct thermal_instance *instance;
	struct thermal_cooling_device *cdev;
	struct ddr_cooling_device *ddr_cdev;
	u32 state_set;
	unsigned long state_get;

	if (tz->temperature < 0)
		return;

	mutex_lock(&tz->lock);
	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		cdev = instance->cdev;
		ddr_cdev = cdev->devdata;
		if (cdev->ops && cdev->ops->set_cur_state && instance->trip == THERMAL_TRIP_HOT) {
			cdev->ops->get_requested_power(cdev, &state_set);
			if (state_set != ddr_cdev->last_state) {
				cdev->ops->set_cur_state(cdev, (unsigned long)state_set);
				ddr_cdev->last_state = state_set;
				cdev->ops->get_cur_state(cdev, &state_get);
				pr_info("[%s]temp:%d, set:0x%x, get:0x%lx\n", cdev->type,
						tz->temperature, state_set, state_get);
			}
		}
	}
	mutex_unlock(&tz->lock);
}

static void meson_handle_thermal_trip(struct thermal_zone_device *tz, int trip, int temp)
{
	struct thermal_instance *instance;
	struct thermal_cooling_device *cdev;
	enum thermal_trip_type type;
	u32 state_set;
	static u32 last_state_set;
	unsigned long state_get;
	int tz_temp;

	if (temp < 0)
		return;

	tz->ops->get_trip_type(tz, trip, &type);
	if (type == THERMAL_TRIP_HOT) {
		mutex_lock(&tz->lock);
		tz_temp = tz->temperature;
		list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
			cdev = instance->cdev;
			tz->temperature = temp;
			if (cdev->ops && cdev->ops->set_cur_state &&
				instance->trip == THERMAL_TRIP_HOT) {
				cdev->ops->get_requested_power(cdev, &state_set);
				if (state_set != last_state_set) {
					cdev->ops->set_cur_state(cdev, (unsigned long)state_set);
					last_state_set = state_set;
					cdev->ops->get_cur_state(cdev, &state_get);
					pr_info("[%s %d]temp:%d, ddrset:0x%x, get:0x%lx\n",
						__func__, __LINE__, tz->temperature,
						state_set, state_get);
				}
			}
		}
		tz->temperature = tz_temp;
		mutex_unlock(&tz->lock);
	}
}

static struct thermal_zone_of_device_ops meson_sensor_ops = {
	.get_temp = meson_get_temp,

};

static int show_tsensor_drvinfo(struct seq_file *s, void *what)
{
	struct meson_tsensor_data *data = s->private;

	seq_printf(s, "tsensor id: %d\nsoc: %d\nntrip: %d\nirq: %d\n", data->id, data->soc,
		data->ntrip, data->irq);
	seq_printf(s, "pdata:\n\tcal_type:%d\n\ttsensor_id:%d\n\treboot_temp:%d\n\tcal_coeff:[%u %u %u %u]\n",
		data->pdata->cal_type, data->pdata->tsensor_id, data->pdata->reboot_temp,
		data->pdata->cal_coeff[0], data->pdata->cal_coeff[1], data->pdata->cal_coeff[2],
		data->pdata->cal_coeff[3]);

	return 0;
}

static int show_tsensor_tempwrite(struct seq_file *s, void *what)
{
	return 0;
}

static int tsensor_drvinfo_open(struct inode *inode, struct file *file)
{
	return single_open(file, show_tsensor_drvinfo, inode->i_private);
}

static int tsensor_tempwrite_open(struct inode *inode, struct file *file)
{
	return single_open(file, show_tsensor_tempwrite, inode->i_private);
}

static ssize_t tsensor_drvinfo_write(struct file *file, const char __user *userbuf,
			    size_t count, loff_t *ppos)
{
	return count;
}

static ssize_t tsensor_tempwrite_write(struct file *file, const char __user *userbuf,
			    size_t count, loff_t *ppos)
{
	struct seq_file *sf = file->private_data;
	struct thermal_zone_device *tz = sf->private;
	char buf[10] = {0};
	int i, temperature;

	count = min_t(size_t, count, sizeof(buf) - 1);

	if (simple_write_to_buffer(buf, count, ppos, userbuf, count) < 0)
		return -EINVAL;

	if (kstrtoint(buf, 0, &temperature))
		return -EINVAL;

#if CONFIG_AMLOGIC_KERNEL_VERSION >= 14515
	for (i = 0; i < tz->num_trips; i++)
#else
	for (i = 0; i < tz->trips; i++)
#endif
		meson_handle_thermal_trip(tz, i, temperature);

	return count;
}

static const struct file_operations tsensor_drvinfo_fops = {
	.open = tsensor_drvinfo_open,
	.read = seq_read,
	.write = tsensor_drvinfo_write,
	.llseek = seq_lseek,
	.release = single_release,
};

static const struct file_operations temp_write_fops = {
	.open = tsensor_tempwrite_open,
	.read = seq_read,
	.write = tsensor_tempwrite_write,
	.llseek = seq_lseek,
	.release = single_release,
};

static int meson_tsensor_probe(struct platform_device *pdev)
{
	struct meson_tsensor_data *data;
	struct thermal_zone_device *tz;
	enum thermal_trip_type trip_type;
	int ret, i;
	char name[10] = {0};
	struct dentry *debugfs_dir, *tsensor_drvinfo_file, *temp_write_file;

	pr_debug("meson ts init\n");
	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	data->dev = &pdev->dev;
	platform_set_drvdata(pdev, data);
	mutex_init(&data->lock);
	data->clk = devm_clk_get(&pdev->dev, "ts_comp");
	if (IS_ERR(data->clk)) {
		dev_err(&pdev->dev, "Failed to get tsclock\n");
		ret = PTR_ERR(data->clk);
		return ret;
	}

	ret = meson_map_dt_data(pdev);
	if (ret)
		return ret;

	INIT_WORK(&data->irq_work, meson_tsensor_work);

	data->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
							 data->id,
							 data,
							 &meson_sensor_ops);
	if (IS_ERR(data->tzd)) {
		ret = PTR_ERR(data->tzd);
		dev_err(&pdev->dev, "Failed to register tsensor: %d\n", ret);
		return ret;
	}

	tz = data->tzd;
#if CONFIG_AMLOGIC_KERNEL_VERSION >= 14515
	for (i = 0; i < tz->num_trips; i++) {
#else
	for (i = 0; i < tz->trips; i++) {
#endif
		tz->ops->get_trip_type(tz, i, &trip_type);
		switch (trip_type) {
		case THERMAL_TRIP_HOT:
			tz->ops->hot = meson_thermal_hot_callback;
			break;
		default:
			break;
		}
	}

	if (thermal_add_hwmon_sysfs(data->tzd))
		dev_warn(&pdev->dev, "Failed to add hwmon sysfs attributes\n");

	ret = devm_request_irq(&pdev->dev, data->irq, meson_tsensor_irq,
			       IRQF_TRIGGER_RISING | IRQF_SHARED,
			       dev_name(&pdev->dev), data);
	if (ret) {
		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
		goto err_thermal;
	}

	meson_tsensor_hw_initialize(pdev);
	meson_tsensor_trips_initialize(pdev);

	/*if no pm pd only need enable control*/
	meson_tsensor_control(pdev, true);
	/*wait tsensor work*/
	msleep(R1P1_TS_WAIT);

	sprintf(name, "tsensor%d", data->id);
	debugfs_dir = debugfs_create_dir(name, NULL);
	if (!debugfs_dir)
		goto out;
	tsensor_drvinfo_file = debugfs_create_file("drvinfo", S_IFREG | 0440,
		debugfs_dir, data, &tsensor_drvinfo_fops);
	temp_write_file = debugfs_create_file("tempwrite", S_IFREG | 0440,
		debugfs_dir, tz, &temp_write_fops);

out:
	return 0;

err_thermal:
	thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);

	return ret;
}

static int meson_tsensor_remove(struct platform_device *pdev)
{
	struct meson_tsensor_data *data = platform_get_drvdata(pdev);
	struct thermal_zone_device *tzd = data->tzd;

	thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
	meson_tsensor_control(pdev, false);
	clk_unprepare(data->clk);
	devm_kfree(&pdev->dev, data);
	return 0;
}

static int __maybe_unused meson_tsensor_hw_resume(struct device *dev)
{
	/*if power domain power down, need hw/trips init when resume*/
	meson_tsensor_hw_initialize(to_platform_device(dev));
	meson_tsensor_trips_initialize(to_platform_device(dev));

	/*if no pm pd only need enable control*/
	meson_tsensor_control(to_platform_device(dev), true);
	/*wait tsensor work*/
	msleep(R1P1_TS_WAIT);

	return 0;
}

static int meson_tsensor_suspend(struct device *dev)
{
	struct meson_tsensor_data *data =
		platform_get_drvdata(to_platform_device(dev));

	meson_tsensor_control(to_platform_device(dev), false);
	if (!IS_ERR(data->rst)) {
		dev_warn(dev, "reset sensor...\n");
		reset_control_assert(data->rst);
	}

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int meson_tsensor_resume(struct device *dev)
{
	struct meson_tsensor_data *data =
		platform_get_drvdata(to_platform_device(dev));

	if (!IS_ERR(data->rst)) {
		dev_warn(dev, "Release sensor's reset...\n");
		if (reset_control_deassert(data->rst))
			dev_err(dev, "Release sensor's reset failed!!!...\n");
	}
	meson_tsensor_hw_resume(dev);

	return 0;
}
#endif

static const struct dev_pm_ops meson_tsensor_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(meson_tsensor_suspend,
				meson_tsensor_resume)
};

static void meson_tsensor_shutdown(struct platform_device *pdev)
{
	meson_tsensor_suspend(&pdev->dev);
}

static struct platform_driver meson_tsensor_driver = {
	.driver = {
		.name   = "meson-tsensor",
		.pm     = &meson_tsensor_pm_ops,
		.of_match_table = meson_tsensor_match,
	},
	.probe	= meson_tsensor_probe,
	.remove	= meson_tsensor_remove,
	.shutdown = meson_tsensor_shutdown
};

static int __init meson_platdrv_init(void)
{
	int ret;

	cpu_hotplug_init();

	ret = platform_driver_register(&(meson_tsensor_driver));
	if (ret)
		return ret;

	return platform_driver_register(&(meson_cooldev_platdrv));
}

static __exit void meson_platdrv_exit(void)
{
	platform_driver_unregister(&meson_cooldev_platdrv);
	platform_driver_unregister(&meson_tsensor_driver);
}

module_init(meson_platdrv_init);
module_exit(meson_platdrv_exit);

MODULE_DESCRIPTION("MESON Tsensor Driver");
MODULE_AUTHOR("Huan Biao <huan.biao@amlogic.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:meson-tsensor");
