162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  R-Car THS/TSC thermal sensor driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Renesas Solutions Corp.
662306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/irq.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/reboot.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/thermal.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "thermal_hwmon.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define IDLE_INTERVAL	5000
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define COMMON_STR	0x00
2762306a36Sopenharmony_ci#define COMMON_ENR	0x04
2862306a36Sopenharmony_ci#define COMMON_INTMSK	0x0c
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define REG_POSNEG	0x20
3162306a36Sopenharmony_ci#define REG_FILONOFF	0x28
3262306a36Sopenharmony_ci#define REG_THSCR	0x2c
3362306a36Sopenharmony_ci#define REG_THSSR	0x30
3462306a36Sopenharmony_ci#define REG_INTCTRL	0x34
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* THSCR */
3762306a36Sopenharmony_ci#define CPCTL	(1 << 12)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* THSSR */
4062306a36Sopenharmony_ci#define CTEMP	0x3f
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct rcar_thermal_common {
4362306a36Sopenharmony_ci	void __iomem *base;
4462306a36Sopenharmony_ci	struct device *dev;
4562306a36Sopenharmony_ci	struct list_head head;
4662306a36Sopenharmony_ci	spinlock_t lock;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct rcar_thermal_chip {
5062306a36Sopenharmony_ci	unsigned int use_of_thermal : 1;
5162306a36Sopenharmony_ci	unsigned int has_filonoff : 1;
5262306a36Sopenharmony_ci	unsigned int irq_per_ch : 1;
5362306a36Sopenharmony_ci	unsigned int needs_suspend_resume : 1;
5462306a36Sopenharmony_ci	unsigned int nirqs;
5562306a36Sopenharmony_ci	unsigned int ctemp_bands;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_thermal = {
5962306a36Sopenharmony_ci	.use_of_thermal = 0,
6062306a36Sopenharmony_ci	.has_filonoff = 1,
6162306a36Sopenharmony_ci	.irq_per_ch = 0,
6262306a36Sopenharmony_ci	.needs_suspend_resume = 0,
6362306a36Sopenharmony_ci	.nirqs = 1,
6462306a36Sopenharmony_ci	.ctemp_bands = 1,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_gen2_thermal = {
6862306a36Sopenharmony_ci	.use_of_thermal = 1,
6962306a36Sopenharmony_ci	.has_filonoff = 1,
7062306a36Sopenharmony_ci	.irq_per_ch = 0,
7162306a36Sopenharmony_ci	.needs_suspend_resume = 0,
7262306a36Sopenharmony_ci	.nirqs = 1,
7362306a36Sopenharmony_ci	.ctemp_bands = 1,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_gen3_thermal = {
7762306a36Sopenharmony_ci	.use_of_thermal = 1,
7862306a36Sopenharmony_ci	.has_filonoff = 0,
7962306a36Sopenharmony_ci	.irq_per_ch = 1,
8062306a36Sopenharmony_ci	.needs_suspend_resume = 1,
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * The Gen3 chip has 3 interrupts, but this driver uses only 2
8362306a36Sopenharmony_ci	 * interrupts to detect a temperature change, rise or fall.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	.nirqs = 2,
8662306a36Sopenharmony_ci	.ctemp_bands = 2,
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct rcar_thermal_priv {
9062306a36Sopenharmony_ci	void __iomem *base;
9162306a36Sopenharmony_ci	struct rcar_thermal_common *common;
9262306a36Sopenharmony_ci	struct thermal_zone_device *zone;
9362306a36Sopenharmony_ci	const struct rcar_thermal_chip *chip;
9462306a36Sopenharmony_ci	struct delayed_work work;
9562306a36Sopenharmony_ci	struct mutex lock;
9662306a36Sopenharmony_ci	struct list_head list;
9762306a36Sopenharmony_ci	int id;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define rcar_thermal_for_each_priv(pos, common)	\
10162306a36Sopenharmony_ci	list_for_each_entry(pos, &common->head, list)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MCELSIUS(temp)			((temp) * 1000)
10462306a36Sopenharmony_ci#define rcar_priv_to_dev(priv)		((priv)->common->dev)
10562306a36Sopenharmony_ci#define rcar_has_irq_support(priv)	((priv)->common->base)
10662306a36Sopenharmony_ci#define rcar_id_to_shift(priv)		((priv)->id * 8)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const struct of_device_id rcar_thermal_dt_ids[] = {
10962306a36Sopenharmony_ci	{
11062306a36Sopenharmony_ci		.compatible = "renesas,rcar-thermal",
11162306a36Sopenharmony_ci		.data = &rcar_thermal,
11262306a36Sopenharmony_ci	},
11362306a36Sopenharmony_ci	{
11462306a36Sopenharmony_ci		.compatible = "renesas,rcar-gen2-thermal",
11562306a36Sopenharmony_ci		 .data = &rcar_gen2_thermal,
11662306a36Sopenharmony_ci	},
11762306a36Sopenharmony_ci	{
11862306a36Sopenharmony_ci		.compatible = "renesas,thermal-r8a774c0",
11962306a36Sopenharmony_ci		.data = &rcar_gen3_thermal,
12062306a36Sopenharmony_ci	},
12162306a36Sopenharmony_ci	{
12262306a36Sopenharmony_ci		.compatible = "renesas,thermal-r8a77970",
12362306a36Sopenharmony_ci		.data = &rcar_gen3_thermal,
12462306a36Sopenharmony_ci	},
12562306a36Sopenharmony_ci	{
12662306a36Sopenharmony_ci		.compatible = "renesas,thermal-r8a77990",
12762306a36Sopenharmony_ci		.data = &rcar_gen3_thermal,
12862306a36Sopenharmony_ci	},
12962306a36Sopenharmony_ci	{
13062306a36Sopenharmony_ci		.compatible = "renesas,thermal-r8a77995",
13162306a36Sopenharmony_ci		.data = &rcar_gen3_thermal,
13262306a36Sopenharmony_ci	},
13362306a36Sopenharmony_ci	{},
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci *		basic functions
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ci#define rcar_thermal_common_read(c, r) \
14162306a36Sopenharmony_ci	_rcar_thermal_common_read(c, COMMON_ ##r)
14262306a36Sopenharmony_cistatic u32 _rcar_thermal_common_read(struct rcar_thermal_common *common,
14362306a36Sopenharmony_ci				     u32 reg)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	return ioread32(common->base + reg);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define rcar_thermal_common_write(c, r, d) \
14962306a36Sopenharmony_ci	_rcar_thermal_common_write(c, COMMON_ ##r, d)
15062306a36Sopenharmony_cistatic void _rcar_thermal_common_write(struct rcar_thermal_common *common,
15162306a36Sopenharmony_ci				       u32 reg, u32 data)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	iowrite32(data, common->base + reg);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define rcar_thermal_common_bset(c, r, m, d) \
15762306a36Sopenharmony_ci	_rcar_thermal_common_bset(c, COMMON_ ##r, m, d)
15862306a36Sopenharmony_cistatic void _rcar_thermal_common_bset(struct rcar_thermal_common *common,
15962306a36Sopenharmony_ci				      u32 reg, u32 mask, u32 data)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u32 val;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	val = ioread32(common->base + reg);
16462306a36Sopenharmony_ci	val &= ~mask;
16562306a36Sopenharmony_ci	val |= (data & mask);
16662306a36Sopenharmony_ci	iowrite32(val, common->base + reg);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r)
17062306a36Sopenharmony_cistatic u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return ioread32(priv->base + reg);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d)
17662306a36Sopenharmony_cistatic void _rcar_thermal_write(struct rcar_thermal_priv *priv,
17762306a36Sopenharmony_ci				u32 reg, u32 data)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	iowrite32(data, priv->base + reg);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d)
18362306a36Sopenharmony_cistatic void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg,
18462306a36Sopenharmony_ci			       u32 mask, u32 data)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	u32 val;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	val = ioread32(priv->base + reg);
18962306a36Sopenharmony_ci	val &= ~mask;
19062306a36Sopenharmony_ci	val |= (data & mask);
19162306a36Sopenharmony_ci	iowrite32(val, priv->base + reg);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci *		zone device functions
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistatic int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct device *dev = rcar_priv_to_dev(priv);
20062306a36Sopenharmony_ci	int old, new, ctemp = -EINVAL;
20162306a36Sopenharmony_ci	unsigned int i;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	mutex_lock(&priv->lock);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * TSC decides a value of CPTAP automatically,
20762306a36Sopenharmony_ci	 * and this is the conditions which validate interrupt.
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	old = ~0;
21262306a36Sopenharmony_ci	for (i = 0; i < 128; i++) {
21362306a36Sopenharmony_ci		/*
21462306a36Sopenharmony_ci		 * we need to wait 300us after changing comparator offset
21562306a36Sopenharmony_ci		 * to get stable temperature.
21662306a36Sopenharmony_ci		 * see "Usage Notes" on datasheet
21762306a36Sopenharmony_ci		 */
21862306a36Sopenharmony_ci		usleep_range(300, 400);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		new = rcar_thermal_read(priv, THSSR) & CTEMP;
22162306a36Sopenharmony_ci		if (new == old) {
22262306a36Sopenharmony_ci			ctemp = new;
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		old = new;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (ctemp < 0) {
22962306a36Sopenharmony_ci		dev_err(dev, "thermal sensor was broken\n");
23062306a36Sopenharmony_ci		goto err_out_unlock;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * enable IRQ
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	if (rcar_has_irq_support(priv)) {
23762306a36Sopenharmony_ci		if (priv->chip->has_filonoff)
23862306a36Sopenharmony_ci			rcar_thermal_write(priv, FILONOFF, 0);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		/* enable Rising/Falling edge interrupt */
24162306a36Sopenharmony_ci		rcar_thermal_write(priv, POSNEG,  0x1);
24262306a36Sopenharmony_ci		rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) |
24362306a36Sopenharmony_ci						   ((ctemp - 1) << 0)));
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cierr_out_unlock:
24762306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return ctemp;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv,
25362306a36Sopenharmony_ci					 int *temp)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int ctemp;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ctemp = rcar_thermal_update_temp(priv);
25862306a36Sopenharmony_ci	if (ctemp < 0)
25962306a36Sopenharmony_ci		return ctemp;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Guaranteed operating range is -45C to 125C. */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (priv->chip->ctemp_bands == 1)
26462306a36Sopenharmony_ci		*temp = MCELSIUS((ctemp * 5) - 65);
26562306a36Sopenharmony_ci	else if (ctemp < 24)
26662306a36Sopenharmony_ci		*temp = MCELSIUS(((ctemp * 55) - 720) / 10);
26762306a36Sopenharmony_ci	else
26862306a36Sopenharmony_ci		*temp = MCELSIUS((ctemp * 5) - 60);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct rcar_thermal_priv *priv = thermal_zone_device_priv(zone);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return rcar_thermal_get_current_temp(priv, temp);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic struct thermal_zone_device_ops rcar_thermal_zone_ops = {
28162306a36Sopenharmony_ci	.get_temp	= rcar_thermal_get_temp,
28262306a36Sopenharmony_ci};
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic struct thermal_trip trips[] = {
28562306a36Sopenharmony_ci	{ .type = THERMAL_TRIP_CRITICAL, .temperature = 90000 }
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci *		interrupt
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_ci#define rcar_thermal_irq_enable(p)	_rcar_thermal_irq_ctrl(p, 1)
29262306a36Sopenharmony_ci#define rcar_thermal_irq_disable(p)	_rcar_thermal_irq_ctrl(p, 0)
29362306a36Sopenharmony_cistatic void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct rcar_thermal_common *common = priv->common;
29662306a36Sopenharmony_ci	unsigned long flags;
29762306a36Sopenharmony_ci	u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!rcar_has_irq_support(priv))
30062306a36Sopenharmony_ci		return;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	spin_lock_irqsave(&common->lock, flags);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	spin_unlock_irqrestore(&common->lock, flags);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void rcar_thermal_work(struct work_struct *work)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct rcar_thermal_priv *priv;
31262306a36Sopenharmony_ci	int ret;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	priv = container_of(work, struct rcar_thermal_priv, work.work);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ret = rcar_thermal_update_temp(priv);
31762306a36Sopenharmony_ci	if (ret < 0)
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	rcar_thermal_irq_enable(priv);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct device *dev = rcar_priv_to_dev(priv);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	status = (status >> rcar_id_to_shift(priv)) & 0x3;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (status) {
33262306a36Sopenharmony_ci		dev_dbg(dev, "thermal%d %s%s\n",
33362306a36Sopenharmony_ci			priv->id,
33462306a36Sopenharmony_ci			(status & 0x2) ? "Rising " : "",
33562306a36Sopenharmony_ci			(status & 0x1) ? "Falling" : "");
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return status;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic irqreturn_t rcar_thermal_irq(int irq, void *data)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct rcar_thermal_common *common = data;
34462306a36Sopenharmony_ci	struct rcar_thermal_priv *priv;
34562306a36Sopenharmony_ci	u32 status, mask;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	spin_lock(&common->lock);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	mask	= rcar_thermal_common_read(common, INTMSK);
35062306a36Sopenharmony_ci	status	= rcar_thermal_common_read(common, STR);
35162306a36Sopenharmony_ci	rcar_thermal_common_write(common, STR, 0x000F0F0F & mask);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	spin_unlock(&common->lock);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	status = status & ~mask;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * check the status
35962306a36Sopenharmony_ci	 */
36062306a36Sopenharmony_ci	rcar_thermal_for_each_priv(priv, common) {
36162306a36Sopenharmony_ci		if (rcar_thermal_had_changed(priv, status)) {
36262306a36Sopenharmony_ci			rcar_thermal_irq_disable(priv);
36362306a36Sopenharmony_ci			queue_delayed_work(system_freezable_wq, &priv->work,
36462306a36Sopenharmony_ci					   msecs_to_jiffies(300));
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return IRQ_HANDLED;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/*
37262306a36Sopenharmony_ci *		platform functions
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_cistatic int rcar_thermal_remove(struct platform_device *pdev)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct rcar_thermal_common *common = platform_get_drvdata(pdev);
37762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
37862306a36Sopenharmony_ci	struct rcar_thermal_priv *priv;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	rcar_thermal_for_each_priv(priv, common) {
38162306a36Sopenharmony_ci		rcar_thermal_irq_disable(priv);
38262306a36Sopenharmony_ci		cancel_delayed_work_sync(&priv->work);
38362306a36Sopenharmony_ci		if (priv->chip->use_of_thermal)
38462306a36Sopenharmony_ci			thermal_remove_hwmon_sysfs(priv->zone);
38562306a36Sopenharmony_ci		else
38662306a36Sopenharmony_ci			thermal_zone_device_unregister(priv->zone);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	pm_runtime_put(dev);
39062306a36Sopenharmony_ci	pm_runtime_disable(dev);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int rcar_thermal_probe(struct platform_device *pdev)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct rcar_thermal_common *common;
39862306a36Sopenharmony_ci	struct rcar_thermal_priv *priv;
39962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
40062306a36Sopenharmony_ci	struct resource *res;
40162306a36Sopenharmony_ci	const struct rcar_thermal_chip *chip = of_device_get_match_data(dev);
40262306a36Sopenharmony_ci	int mres = 0;
40362306a36Sopenharmony_ci	int i;
40462306a36Sopenharmony_ci	int ret = -ENODEV;
40562306a36Sopenharmony_ci	int idle = IDLE_INTERVAL;
40662306a36Sopenharmony_ci	u32 enr_bits = 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
40962306a36Sopenharmony_ci	if (!common)
41062306a36Sopenharmony_ci		return -ENOMEM;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	platform_set_drvdata(pdev, common);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	INIT_LIST_HEAD(&common->head);
41562306a36Sopenharmony_ci	spin_lock_init(&common->lock);
41662306a36Sopenharmony_ci	common->dev = dev;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	pm_runtime_enable(dev);
41962306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	for (i = 0; i < chip->nirqs; i++) {
42262306a36Sopenharmony_ci		int irq;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		ret = platform_get_irq_optional(pdev, i);
42562306a36Sopenharmony_ci		if (ret < 0 && ret != -ENXIO)
42662306a36Sopenharmony_ci			goto error_unregister;
42762306a36Sopenharmony_ci		if (ret > 0)
42862306a36Sopenharmony_ci			irq = ret;
42962306a36Sopenharmony_ci		else
43062306a36Sopenharmony_ci			break;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (!common->base) {
43362306a36Sopenharmony_ci			/*
43462306a36Sopenharmony_ci			 * platform has IRQ support.
43562306a36Sopenharmony_ci			 * Then, driver uses common registers
43662306a36Sopenharmony_ci			 * rcar_has_irq_support() will be enabled
43762306a36Sopenharmony_ci			 */
43862306a36Sopenharmony_ci			res = platform_get_resource(pdev, IORESOURCE_MEM,
43962306a36Sopenharmony_ci						    mres++);
44062306a36Sopenharmony_ci			common->base = devm_ioremap_resource(dev, res);
44162306a36Sopenharmony_ci			if (IS_ERR(common->base)) {
44262306a36Sopenharmony_ci				ret = PTR_ERR(common->base);
44362306a36Sopenharmony_ci				goto error_unregister;
44462306a36Sopenharmony_ci			}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci			idle = 0; /* polling delay is not needed */
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		ret = devm_request_irq(dev, irq, rcar_thermal_irq,
45062306a36Sopenharmony_ci				       IRQF_SHARED, dev_name(dev), common);
45162306a36Sopenharmony_ci		if (ret) {
45262306a36Sopenharmony_ci			dev_err(dev, "irq request failed\n ");
45362306a36Sopenharmony_ci			goto error_unregister;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* update ENR bits */
45762306a36Sopenharmony_ci		if (chip->irq_per_ch)
45862306a36Sopenharmony_ci			enr_bits |= 1 << i;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	for (i = 0;; i++) {
46262306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
46362306a36Sopenharmony_ci		if (!res)
46462306a36Sopenharmony_ci			break;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
46762306a36Sopenharmony_ci		if (!priv) {
46862306a36Sopenharmony_ci			ret = -ENOMEM;
46962306a36Sopenharmony_ci			goto error_unregister;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		priv->base = devm_ioremap_resource(dev, res);
47362306a36Sopenharmony_ci		if (IS_ERR(priv->base)) {
47462306a36Sopenharmony_ci			ret = PTR_ERR(priv->base);
47562306a36Sopenharmony_ci			goto error_unregister;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		priv->common = common;
47962306a36Sopenharmony_ci		priv->id = i;
48062306a36Sopenharmony_ci		priv->chip = chip;
48162306a36Sopenharmony_ci		mutex_init(&priv->lock);
48262306a36Sopenharmony_ci		INIT_LIST_HEAD(&priv->list);
48362306a36Sopenharmony_ci		INIT_DELAYED_WORK(&priv->work, rcar_thermal_work);
48462306a36Sopenharmony_ci		ret = rcar_thermal_update_temp(priv);
48562306a36Sopenharmony_ci		if (ret < 0)
48662306a36Sopenharmony_ci			goto error_unregister;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (chip->use_of_thermal) {
48962306a36Sopenharmony_ci			priv->zone = devm_thermal_of_zone_register(
49062306a36Sopenharmony_ci						dev, i, priv,
49162306a36Sopenharmony_ci						&rcar_thermal_zone_ops);
49262306a36Sopenharmony_ci		} else {
49362306a36Sopenharmony_ci			priv->zone = thermal_zone_device_register_with_trips(
49462306a36Sopenharmony_ci				"rcar_thermal", trips, ARRAY_SIZE(trips), 0, priv,
49562306a36Sopenharmony_ci						&rcar_thermal_zone_ops, NULL, 0,
49662306a36Sopenharmony_ci						idle);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci			ret = thermal_zone_device_enable(priv->zone);
49962306a36Sopenharmony_ci			if (ret) {
50062306a36Sopenharmony_ci				thermal_zone_device_unregister(priv->zone);
50162306a36Sopenharmony_ci				priv->zone = ERR_PTR(ret);
50262306a36Sopenharmony_ci			}
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci		if (IS_ERR(priv->zone)) {
50562306a36Sopenharmony_ci			dev_err(dev, "can't register thermal zone\n");
50662306a36Sopenharmony_ci			ret = PTR_ERR(priv->zone);
50762306a36Sopenharmony_ci			priv->zone = NULL;
50862306a36Sopenharmony_ci			goto error_unregister;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		if (chip->use_of_thermal) {
51262306a36Sopenharmony_ci			ret = thermal_add_hwmon_sysfs(priv->zone);
51362306a36Sopenharmony_ci			if (ret)
51462306a36Sopenharmony_ci				goto error_unregister;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		rcar_thermal_irq_enable(priv);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		list_move_tail(&priv->list, &common->head);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		/* update ENR bits */
52262306a36Sopenharmony_ci		if (!chip->irq_per_ch)
52362306a36Sopenharmony_ci			enr_bits |= 3 << (i * 8);
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (common->base && enr_bits)
52762306a36Sopenharmony_ci		rcar_thermal_common_write(common, ENR, enr_bits);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	dev_info(dev, "%d sensor probed\n", i);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cierror_unregister:
53462306a36Sopenharmony_ci	rcar_thermal_remove(pdev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return ret;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
54062306a36Sopenharmony_cistatic int rcar_thermal_suspend(struct device *dev)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct rcar_thermal_common *common = dev_get_drvdata(dev);
54362306a36Sopenharmony_ci	struct rcar_thermal_priv *priv = list_first_entry(&common->head,
54462306a36Sopenharmony_ci							  typeof(*priv), list);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (priv->chip->needs_suspend_resume) {
54762306a36Sopenharmony_ci		rcar_thermal_common_write(common, ENR, 0);
54862306a36Sopenharmony_ci		rcar_thermal_irq_disable(priv);
54962306a36Sopenharmony_ci		rcar_thermal_bset(priv, THSCR, CPCTL, 0);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return 0;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int rcar_thermal_resume(struct device *dev)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct rcar_thermal_common *common = dev_get_drvdata(dev);
55862306a36Sopenharmony_ci	struct rcar_thermal_priv *priv = list_first_entry(&common->head,
55962306a36Sopenharmony_ci							  typeof(*priv), list);
56062306a36Sopenharmony_ci	int ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (priv->chip->needs_suspend_resume) {
56362306a36Sopenharmony_ci		ret = rcar_thermal_update_temp(priv);
56462306a36Sopenharmony_ci		if (ret < 0)
56562306a36Sopenharmony_ci			return ret;
56662306a36Sopenharmony_ci		rcar_thermal_irq_enable(priv);
56762306a36Sopenharmony_ci		rcar_thermal_common_write(common, ENR, 0x03);
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci#endif
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
57562306a36Sopenharmony_ci			 rcar_thermal_resume);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic struct platform_driver rcar_thermal_driver = {
57862306a36Sopenharmony_ci	.driver	= {
57962306a36Sopenharmony_ci		.name	= "rcar_thermal",
58062306a36Sopenharmony_ci		.pm = &rcar_thermal_pm_ops,
58162306a36Sopenharmony_ci		.of_match_table = rcar_thermal_dt_ids,
58262306a36Sopenharmony_ci	},
58362306a36Sopenharmony_ci	.probe		= rcar_thermal_probe,
58462306a36Sopenharmony_ci	.remove		= rcar_thermal_remove,
58562306a36Sopenharmony_ci};
58662306a36Sopenharmony_cimodule_platform_driver(rcar_thermal_driver);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
58962306a36Sopenharmony_ciMODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver");
59062306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
591