xref: /linux/drivers/thermal/renesas/rcar_gen3_thermal.c (revision d22300518d875f78203e9afacb5aa0b0316da523)
19d617949SNiklas Söderlund // SPDX-License-Identifier: GPL-2.0
29d617949SNiklas Söderlund /*
39d617949SNiklas Söderlund  *  R-Car Gen3 THS thermal sensor driver
49d617949SNiklas Söderlund  *  Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.
59d617949SNiklas Söderlund  *
69d617949SNiklas Söderlund  * Copyright (C) 2016 Renesas Electronics Corporation.
79d617949SNiklas Söderlund  * Copyright (C) 2016 Sang Engineering
89d617949SNiklas Söderlund  */
99d617949SNiklas Söderlund #include <linux/delay.h>
109d617949SNiklas Söderlund #include <linux/err.h>
119d617949SNiklas Söderlund #include <linux/interrupt.h>
129d617949SNiklas Söderlund #include <linux/io.h>
139d617949SNiklas Söderlund #include <linux/module.h>
149d617949SNiklas Söderlund #include <linux/of.h>
159d617949SNiklas Söderlund #include <linux/platform_device.h>
169d617949SNiklas Söderlund #include <linux/pm_runtime.h>
179d617949SNiklas Söderlund #include <linux/thermal.h>
189d617949SNiklas Söderlund 
199d617949SNiklas Söderlund #include "../thermal_hwmon.h"
209d617949SNiklas Söderlund 
219d617949SNiklas Söderlund /* Register offsets */
229d617949SNiklas Söderlund #define REG_GEN3_IRQSTR		0x04
239d617949SNiklas Söderlund #define REG_GEN3_IRQMSK		0x08
249d617949SNiklas Söderlund #define REG_GEN3_IRQCTL		0x0C
259d617949SNiklas Söderlund #define REG_GEN3_IRQEN		0x10
269d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP1	0x14
279d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP2	0x18
289d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP3	0x1C
299d617949SNiklas Söderlund #define REG_GEN3_THCTR		0x20
309d617949SNiklas Söderlund #define REG_GEN3_TEMP		0x28
319d617949SNiklas Söderlund #define REG_GEN3_THCODE1	0x50
329d617949SNiklas Söderlund #define REG_GEN3_THCODE2	0x54
339d617949SNiklas Söderlund #define REG_GEN3_THCODE3	0x58
349d617949SNiklas Söderlund #define REG_GEN3_PTAT1		0x5c
359d617949SNiklas Söderlund #define REG_GEN3_PTAT2		0x60
369d617949SNiklas Söderlund #define REG_GEN3_PTAT3		0x64
379d617949SNiklas Söderlund #define REG_GEN3_THSCP		0x68
389d617949SNiklas Söderlund #define REG_GEN4_THSFMON00	0x180
399d617949SNiklas Söderlund #define REG_GEN4_THSFMON01	0x184
409d617949SNiklas Söderlund #define REG_GEN4_THSFMON02	0x188
419d617949SNiklas Söderlund #define REG_GEN4_THSFMON15	0x1BC
429d617949SNiklas Söderlund #define REG_GEN4_THSFMON16	0x1C0
439d617949SNiklas Söderlund #define REG_GEN4_THSFMON17	0x1C4
449d617949SNiklas Söderlund 
459d617949SNiklas Söderlund /* IRQ{STR,MSK,EN} bits */
469d617949SNiklas Söderlund #define IRQ_TEMP1		BIT(0)
479d617949SNiklas Söderlund #define IRQ_TEMP2		BIT(1)
489d617949SNiklas Söderlund #define IRQ_TEMP3		BIT(2)
499d617949SNiklas Söderlund #define IRQ_TEMPD1		BIT(3)
509d617949SNiklas Söderlund #define IRQ_TEMPD2		BIT(4)
519d617949SNiklas Söderlund #define IRQ_TEMPD3		BIT(5)
529d617949SNiklas Söderlund 
539d617949SNiklas Söderlund /* THCTR bits */
549d617949SNiklas Söderlund #define THCTR_PONM	BIT(6)
559d617949SNiklas Söderlund #define THCTR_THSST	BIT(0)
569d617949SNiklas Söderlund 
579d617949SNiklas Söderlund /* THSCP bits */
589d617949SNiklas Söderlund #define THSCP_COR_PARA_VLD	(BIT(15) | BIT(14))
599d617949SNiklas Söderlund 
609d617949SNiklas Söderlund #define CTEMP_MASK	0xFFF
619d617949SNiklas Söderlund 
629d617949SNiklas Söderlund #define MCELSIUS(temp)	((temp) * 1000)
639d617949SNiklas Söderlund #define GEN3_FUSE_MASK	0xFFF
649d617949SNiklas Söderlund #define GEN4_FUSE_MASK	0xFFF
659d617949SNiklas Söderlund 
669d617949SNiklas Söderlund #define TSC_MAX_NUM	5
679d617949SNiklas Söderlund 
689d617949SNiklas Söderlund struct rcar_gen3_thermal_priv;
699d617949SNiklas Söderlund 
709d617949SNiklas Söderlund struct rcar_thermal_info {
719d617949SNiklas Söderlund 	int scale;
729d617949SNiklas Söderlund 	int adj_below;
739d617949SNiklas Söderlund 	int adj_above;
749d617949SNiklas Söderlund 	void (*read_fuses)(struct rcar_gen3_thermal_priv *priv);
759d617949SNiklas Söderlund };
769d617949SNiklas Söderlund 
779d617949SNiklas Söderlund struct equation_set_coef {
789d617949SNiklas Söderlund 	int a;
799d617949SNiklas Söderlund 	int b;
809d617949SNiklas Söderlund };
819d617949SNiklas Söderlund 
829d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc {
839d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv;
849d617949SNiklas Söderlund 	void __iomem *base;
859d617949SNiklas Söderlund 	struct thermal_zone_device *zone;
869d617949SNiklas Söderlund 	/* Different coefficients are used depending on a threshold. */
879d617949SNiklas Söderlund 	struct {
889d617949SNiklas Söderlund 		struct equation_set_coef below;
899d617949SNiklas Söderlund 		struct equation_set_coef above;
909d617949SNiklas Söderlund 	} coef;
919d617949SNiklas Söderlund 	int thcode[3];
929d617949SNiklas Söderlund };
939d617949SNiklas Söderlund 
949d617949SNiklas Söderlund struct rcar_gen3_thermal_priv {
959d617949SNiklas Söderlund 	struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
969d617949SNiklas Söderlund 	struct thermal_zone_device_ops ops;
979d617949SNiklas Söderlund 	unsigned int num_tscs;
989d617949SNiklas Söderlund 	int ptat[3];
999d617949SNiklas Söderlund 	int tj_t;
1009d617949SNiklas Söderlund 	const struct rcar_thermal_info *info;
1019d617949SNiklas Söderlund };
1029d617949SNiklas Söderlund 
rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc * tsc,u32 reg)1039d617949SNiklas Söderlund static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc,
1049d617949SNiklas Söderlund 					 u32 reg)
1059d617949SNiklas Söderlund {
1069d617949SNiklas Söderlund 	return ioread32(tsc->base + reg);
1079d617949SNiklas Söderlund }
1089d617949SNiklas Söderlund 
rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc * tsc,u32 reg,u32 data)1099d617949SNiklas Söderlund static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
1109d617949SNiklas Söderlund 					   u32 reg, u32 data)
1119d617949SNiklas Söderlund {
1129d617949SNiklas Söderlund 	iowrite32(data, tsc->base + reg);
1139d617949SNiklas Söderlund }
1149d617949SNiklas Söderlund 
1159d617949SNiklas Söderlund /*
1169d617949SNiklas Söderlund  * Linear approximation for temperature
1179d617949SNiklas Söderlund  *
1189d617949SNiklas Söderlund  * [temp] = ((thadj - [reg]) * a) / b + adj
1199d617949SNiklas Söderlund  * [reg] = thadj - ([temp] - adj) * b / a
1209d617949SNiklas Söderlund  *
1219d617949SNiklas Söderlund  * The constants a and b are calculated using two triplets of int values PTAT
1229d617949SNiklas Söderlund  * and THCODE. PTAT and THCODE can either be read from hardware or use hard
1239d617949SNiklas Söderlund  * coded values from the driver. The formula to calculate a and b are taken from
1249d617949SNiklas Söderlund  * the datasheet. Different calculations are needed for a and b depending on
1259d617949SNiklas Söderlund  * if the input variables ([temp] or [reg]) are above or below a threshold. The
1269d617949SNiklas Söderlund  * threshold is also calculated from PTAT and THCODE using formulas from the
1279d617949SNiklas Söderlund  * datasheet.
1289d617949SNiklas Söderlund  *
1299d617949SNiklas Söderlund  * The constant thadj is one of the THCODE values, which one to use depends on
1309d617949SNiklas Söderlund  * the threshold and input value.
1319d617949SNiklas Söderlund  *
1329d617949SNiklas Söderlund  * The constants adj is taken verbatim from the datasheet. Two values exists,
1339d617949SNiklas Söderlund  * which one to use depends on the input value and the calculated threshold.
1349d617949SNiklas Söderlund  * Furthermore different SoC models supported by the driver have different sets
1359d617949SNiklas Söderlund  * of values. The values for each model are stored in the device match data.
1369d617949SNiklas Söderlund  */
1379d617949SNiklas Söderlund 
rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv * priv)1389d617949SNiklas Söderlund static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv)
1399d617949SNiklas Söderlund {
1409d617949SNiklas Söderlund 	priv->tj_t =
1419d617949SNiklas Söderlund 		DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale,
1429d617949SNiklas Söderlund 				  priv->ptat[0] - priv->ptat[2])
1439d617949SNiklas Söderlund 		+ priv->info->adj_below;
1449d617949SNiklas Söderlund }
rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv * priv,struct rcar_gen3_thermal_tsc * tsc)1459d617949SNiklas Söderlund static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv,
1469d617949SNiklas Söderlund 					struct rcar_gen3_thermal_tsc *tsc)
1479d617949SNiklas Söderlund {
1489d617949SNiklas Söderlund 	tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]);
1499d617949SNiklas Söderlund 	tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]);
1509d617949SNiklas Söderlund 
1519d617949SNiklas Söderlund 	tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]);
1529d617949SNiklas Söderlund 	tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]);
1539d617949SNiklas Söderlund }
1549d617949SNiklas Söderlund 
rcar_gen3_thermal_get_temp(struct thermal_zone_device * tz,int * temp)1559d617949SNiklas Söderlund static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
1569d617949SNiklas Söderlund {
1579d617949SNiklas Söderlund 	struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);
1589d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv = tsc->priv;
1599d617949SNiklas Söderlund 	const struct equation_set_coef *coef;
1609d617949SNiklas Söderlund 	int adj, decicelsius, reg, thcode;
1619d617949SNiklas Söderlund 
1629d617949SNiklas Söderlund 	/* Read register and convert to mili Celsius */
1639d617949SNiklas Söderlund 	reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
1649d617949SNiklas Söderlund 
1659d617949SNiklas Söderlund 	if (reg < tsc->thcode[1]) {
1669d617949SNiklas Söderlund 		adj = priv->info->adj_below;
1679d617949SNiklas Söderlund 		coef = &tsc->coef.below;
1689d617949SNiklas Söderlund 		thcode = tsc->thcode[2];
1699d617949SNiklas Söderlund 	} else {
1709d617949SNiklas Söderlund 		adj = priv->info->adj_above;
1719d617949SNiklas Söderlund 		coef = &tsc->coef.above;
1729d617949SNiklas Söderlund 		thcode = tsc->thcode[0];
1739d617949SNiklas Söderlund 	}
1749d617949SNiklas Söderlund 
1759d617949SNiklas Söderlund 	/*
1769d617949SNiklas Söderlund 	 * The dividend can't be grown as it might overflow, instead shorten the
1779d617949SNiklas Söderlund 	 * divisor to convert to decidegree Celsius. If we convert after the
1789d617949SNiklas Söderlund 	 * division precision is lost as we will scale up from whole degrees
1799d617949SNiklas Söderlund 	 * Celsius.
1809d617949SNiklas Söderlund 	 */
1819d617949SNiklas Söderlund 	decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10);
1829d617949SNiklas Söderlund 
1839d617949SNiklas Söderlund 	/* Guaranteed operating range is -40C to 125C. */
1849d617949SNiklas Söderlund 
1859d617949SNiklas Söderlund 	/* Reporting is done in millidegree Celsius */
1869d617949SNiklas Söderlund 	*temp = decicelsius * 100 + adj * 1000;
1879d617949SNiklas Söderlund 
1889d617949SNiklas Söderlund 	return 0;
1899d617949SNiklas Söderlund }
1909d617949SNiklas Söderlund 
rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc * tsc,int mcelsius)1919d617949SNiklas Söderlund static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
1929d617949SNiklas Söderlund 					      int mcelsius)
1939d617949SNiklas Söderlund {
1949d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv = tsc->priv;
1959d617949SNiklas Söderlund 	const struct equation_set_coef *coef;
1969d617949SNiklas Söderlund 	int adj, celsius, thcode;
1979d617949SNiklas Söderlund 
1989d617949SNiklas Söderlund 	celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
1999d617949SNiklas Söderlund 	if (celsius < priv->tj_t) {
2009d617949SNiklas Söderlund 		coef = &tsc->coef.below;
2019d617949SNiklas Söderlund 		adj = priv->info->adj_below;
2029d617949SNiklas Söderlund 		thcode = tsc->thcode[2];
2039d617949SNiklas Söderlund 	} else {
2049d617949SNiklas Söderlund 		coef = &tsc->coef.above;
2059d617949SNiklas Söderlund 		adj = priv->info->adj_above;
2069d617949SNiklas Söderlund 		thcode = tsc->thcode[0];
2079d617949SNiklas Söderlund 	}
2089d617949SNiklas Söderlund 
2099d617949SNiklas Söderlund 	return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a);
2109d617949SNiklas Söderlund }
2119d617949SNiklas Söderlund 
rcar_gen3_thermal_set_trips(struct thermal_zone_device * tz,int low,int high)2129d617949SNiklas Söderlund static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
2139d617949SNiklas Söderlund {
2149d617949SNiklas Söderlund 	struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);
2159d617949SNiklas Söderlund 	u32 irqmsk = 0;
2169d617949SNiklas Söderlund 
2179d617949SNiklas Söderlund 	if (low != -INT_MAX) {
2189d617949SNiklas Söderlund 		irqmsk |= IRQ_TEMPD1;
2199d617949SNiklas Söderlund 		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
2209d617949SNiklas Söderlund 					rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
2219d617949SNiklas Söderlund 	}
2229d617949SNiklas Söderlund 
2239d617949SNiklas Söderlund 	if (high != INT_MAX) {
2249d617949SNiklas Söderlund 		irqmsk |= IRQ_TEMP2;
2259d617949SNiklas Söderlund 		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
2269d617949SNiklas Söderlund 					rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
2279d617949SNiklas Söderlund 	}
2289d617949SNiklas Söderlund 
2299d617949SNiklas Söderlund 	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk);
2309d617949SNiklas Söderlund 
2319d617949SNiklas Söderlund 	return 0;
2329d617949SNiklas Söderlund }
2339d617949SNiklas Söderlund 
2349d617949SNiklas Söderlund static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {
2359d617949SNiklas Söderlund 	.get_temp	= rcar_gen3_thermal_get_temp,
2369d617949SNiklas Söderlund 	.set_trips	= rcar_gen3_thermal_set_trips,
2379d617949SNiklas Söderlund };
2389d617949SNiklas Söderlund 
rcar_gen3_thermal_irq(int irq,void * data)2399d617949SNiklas Söderlund static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
2409d617949SNiklas Söderlund {
2419d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv = data;
2429d617949SNiklas Söderlund 	unsigned int i;
2439d617949SNiklas Söderlund 	u32 status;
2449d617949SNiklas Söderlund 
2459d617949SNiklas Söderlund 	for (i = 0; i < priv->num_tscs; i++) {
2469d617949SNiklas Söderlund 		status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
2479d617949SNiklas Söderlund 		rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
2489d617949SNiklas Söderlund 		if (status && priv->tscs[i]->zone)
2499d617949SNiklas Söderlund 			thermal_zone_device_update(priv->tscs[i]->zone,
2509d617949SNiklas Söderlund 						   THERMAL_EVENT_UNSPECIFIED);
2519d617949SNiklas Söderlund 	}
2529d617949SNiklas Söderlund 
2539d617949SNiklas Söderlund 	return IRQ_HANDLED;
2549d617949SNiklas Söderlund }
2559d617949SNiklas Söderlund 
rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv * priv)2569d617949SNiklas Söderlund static void rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv *priv)
2579d617949SNiklas Söderlund {
2589d617949SNiklas Söderlund 	unsigned int i;
2599d617949SNiklas Söderlund 
2609d617949SNiklas Söderlund 	/*
2619d617949SNiklas Söderlund 	 * Set the pseudo calibration points with fused values.
2629d617949SNiklas Söderlund 	 * PTAT is shared between all TSCs but only fused for the first
2639d617949SNiklas Söderlund 	 * TSC while THCODEs are fused for each TSC.
2649d617949SNiklas Söderlund 	 */
2659d617949SNiklas Söderlund 	priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT1) &
2669d617949SNiklas Söderlund 		GEN3_FUSE_MASK;
2679d617949SNiklas Söderlund 	priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT2) &
2689d617949SNiklas Söderlund 		GEN3_FUSE_MASK;
2699d617949SNiklas Söderlund 	priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT3) &
2709d617949SNiklas Söderlund 		GEN3_FUSE_MASK;
2719d617949SNiklas Söderlund 
2729d617949SNiklas Söderlund 	for (i = 0; i < priv->num_tscs; i++) {
2739d617949SNiklas Söderlund 		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
2749d617949SNiklas Söderlund 
2759d617949SNiklas Söderlund 		tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE1) &
2769d617949SNiklas Söderlund 			GEN3_FUSE_MASK;
2779d617949SNiklas Söderlund 		tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE2) &
2789d617949SNiklas Söderlund 			GEN3_FUSE_MASK;
2799d617949SNiklas Söderlund 		tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE3) &
2809d617949SNiklas Söderlund 			GEN3_FUSE_MASK;
2819d617949SNiklas Söderlund 	}
2829d617949SNiklas Söderlund }
2839d617949SNiklas Söderlund 
rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv * priv)2849d617949SNiklas Söderlund static void rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv *priv)
2859d617949SNiklas Söderlund {
2869d617949SNiklas Söderlund 	unsigned int i;
2879d617949SNiklas Söderlund 
2889d617949SNiklas Söderlund 	/*
2899d617949SNiklas Söderlund 	 * Set the pseudo calibration points with fused values.
2909d617949SNiklas Söderlund 	 * PTAT is shared between all TSCs but only fused for the first
2919d617949SNiklas Söderlund 	 * TSC while THCODEs are fused for each TSC.
2929d617949SNiklas Söderlund 	 */
2939d617949SNiklas Söderlund 	priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON16) &
2949d617949SNiklas Söderlund 		GEN4_FUSE_MASK;
2959d617949SNiklas Söderlund 	priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON17) &
2969d617949SNiklas Söderlund 		GEN4_FUSE_MASK;
2979d617949SNiklas Söderlund 	priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON15) &
2989d617949SNiklas Söderlund 		GEN4_FUSE_MASK;
2999d617949SNiklas Söderlund 
3009d617949SNiklas Söderlund 	for (i = 0; i < priv->num_tscs; i++) {
3019d617949SNiklas Söderlund 		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
3029d617949SNiklas Söderlund 
3039d617949SNiklas Söderlund 		tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON01) &
3049d617949SNiklas Söderlund 			GEN4_FUSE_MASK;
3059d617949SNiklas Söderlund 		tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON02) &
3069d617949SNiklas Söderlund 			GEN4_FUSE_MASK;
3079d617949SNiklas Söderlund 		tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON00) &
3089d617949SNiklas Söderlund 			GEN4_FUSE_MASK;
3099d617949SNiklas Söderlund 	}
3109d617949SNiklas Söderlund }
3119d617949SNiklas Söderlund 
rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv * priv)3129d617949SNiklas Söderlund static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
3139d617949SNiklas Söderlund {
3149d617949SNiklas Söderlund 	unsigned int i;
3159d617949SNiklas Söderlund 	u32 thscp;
3169d617949SNiklas Söderlund 
3179d617949SNiklas Söderlund 	/* If fuses are not set, fallback to pseudo values. */
3189d617949SNiklas Söderlund 	thscp = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_THSCP);
3199d617949SNiklas Söderlund 	if (!priv->info->read_fuses ||
3209d617949SNiklas Söderlund 	    (thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) {
3219d617949SNiklas Söderlund 		/* Default THCODE values in case FUSEs are not set. */
3229d617949SNiklas Söderlund 		static const int thcodes[TSC_MAX_NUM][3] = {
3239d617949SNiklas Söderlund 			{ 3397, 2800, 2221 },
3249d617949SNiklas Söderlund 			{ 3393, 2795, 2216 },
3259d617949SNiklas Söderlund 			{ 3389, 2805, 2237 },
3269d617949SNiklas Söderlund 			{ 3415, 2694, 2195 },
3279d617949SNiklas Söderlund 			{ 3356, 2724, 2244 },
3289d617949SNiklas Söderlund 		};
3299d617949SNiklas Söderlund 
3309d617949SNiklas Söderlund 		priv->ptat[0] = 2631;
3319d617949SNiklas Söderlund 		priv->ptat[1] = 1509;
3329d617949SNiklas Söderlund 		priv->ptat[2] = 435;
3339d617949SNiklas Söderlund 
3349d617949SNiklas Söderlund 		for (i = 0; i < priv->num_tscs; i++) {
3359d617949SNiklas Söderlund 			struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
3369d617949SNiklas Söderlund 
3379d617949SNiklas Söderlund 			tsc->thcode[0] = thcodes[i][0];
3389d617949SNiklas Söderlund 			tsc->thcode[1] = thcodes[i][1];
3399d617949SNiklas Söderlund 			tsc->thcode[2] = thcodes[i][2];
3409d617949SNiklas Söderlund 		}
3419d617949SNiklas Söderlund 
3429d617949SNiklas Söderlund 		return false;
3439d617949SNiklas Söderlund 	}
3449d617949SNiklas Söderlund 
3459d617949SNiklas Söderlund 	priv->info->read_fuses(priv);
3469d617949SNiklas Söderlund 	return true;
3479d617949SNiklas Söderlund }
3489d617949SNiklas Söderlund 
rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv * priv,struct rcar_gen3_thermal_tsc * tsc)3499d617949SNiklas Söderlund static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,
3509d617949SNiklas Söderlund 				   struct rcar_gen3_thermal_tsc *tsc)
3519d617949SNiklas Söderlund {
3529d617949SNiklas Söderlund 	u32 reg_val;
3539d617949SNiklas Söderlund 
3549d617949SNiklas Söderlund 	reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
3559d617949SNiklas Söderlund 	reg_val &= ~THCTR_PONM;
3569d617949SNiklas Söderlund 	rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
3579d617949SNiklas Söderlund 
3589d617949SNiklas Söderlund 	usleep_range(1000, 2000);
3599d617949SNiklas Söderlund 
3609d617949SNiklas Söderlund 	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
3619d617949SNiklas Söderlund 	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
3629d617949SNiklas Söderlund 	if (priv->ops.set_trips)
3639d617949SNiklas Söderlund 		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
3649d617949SNiklas Söderlund 					IRQ_TEMPD1 | IRQ_TEMP2);
3659d617949SNiklas Söderlund 
3669d617949SNiklas Söderlund 	reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
3679d617949SNiklas Söderlund 	reg_val |= THCTR_THSST;
3689d617949SNiklas Söderlund 	rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
3699d617949SNiklas Söderlund 
3709d617949SNiklas Söderlund 	usleep_range(1000, 2000);
3719d617949SNiklas Söderlund }
3729d617949SNiklas Söderlund 
3739d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_m3w_thermal_info = {
3749d617949SNiklas Söderlund 	.scale = 157,
3759d617949SNiklas Söderlund 	.adj_below = -41,
3769d617949SNiklas Söderlund 	.adj_above = 116,
3779d617949SNiklas Söderlund 	.read_fuses = rcar_gen3_thermal_read_fuses_gen3,
3789d617949SNiklas Söderlund };
3799d617949SNiklas Söderlund 
3809d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_gen3_thermal_info = {
3819d617949SNiklas Söderlund 	.scale = 167,
3829d617949SNiklas Söderlund 	.adj_below = -41,
3839d617949SNiklas Söderlund 	.adj_above = 126,
3849d617949SNiklas Söderlund 	.read_fuses = rcar_gen3_thermal_read_fuses_gen3,
3859d617949SNiklas Söderlund };
3869d617949SNiklas Söderlund 
3879d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_gen4_thermal_info = {
3889d617949SNiklas Söderlund 	.scale = 167,
3899d617949SNiklas Söderlund 	.adj_below = -41,
3909d617949SNiklas Söderlund 	.adj_above = 126,
3919d617949SNiklas Söderlund 	.read_fuses = rcar_gen3_thermal_read_fuses_gen4,
3929d617949SNiklas Söderlund };
3939d617949SNiklas Söderlund 
3949d617949SNiklas Söderlund static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
3959d617949SNiklas Söderlund 	{
3969d617949SNiklas Söderlund 		.compatible = "renesas,r8a774a1-thermal",
3979d617949SNiklas Söderlund 		.data = &rcar_m3w_thermal_info,
3989d617949SNiklas Söderlund 	},
3999d617949SNiklas Söderlund 	{
4009d617949SNiklas Söderlund 		.compatible = "renesas,r8a774b1-thermal",
4019d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4029d617949SNiklas Söderlund 	},
4039d617949SNiklas Söderlund 	{
4049d617949SNiklas Söderlund 		.compatible = "renesas,r8a774e1-thermal",
4059d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4069d617949SNiklas Söderlund 	},
4079d617949SNiklas Söderlund 	{
4089d617949SNiklas Söderlund 		.compatible = "renesas,r8a7795-thermal",
4099d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4109d617949SNiklas Söderlund 	},
4119d617949SNiklas Söderlund 	{
4129d617949SNiklas Söderlund 		.compatible = "renesas,r8a7796-thermal",
4139d617949SNiklas Söderlund 		.data = &rcar_m3w_thermal_info,
4149d617949SNiklas Söderlund 	},
4159d617949SNiklas Söderlund 	{
4169d617949SNiklas Söderlund 		.compatible = "renesas,r8a77961-thermal",
4179d617949SNiklas Söderlund 		.data = &rcar_m3w_thermal_info,
4189d617949SNiklas Söderlund 	},
4199d617949SNiklas Söderlund 	{
4209d617949SNiklas Söderlund 		.compatible = "renesas,r8a77965-thermal",
4219d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4229d617949SNiklas Söderlund 	},
4239d617949SNiklas Söderlund 	{
4249d617949SNiklas Söderlund 		.compatible = "renesas,r8a77980-thermal",
4259d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4269d617949SNiklas Söderlund 	},
4279d617949SNiklas Söderlund 	{
4289d617949SNiklas Söderlund 		.compatible = "renesas,r8a779a0-thermal",
4299d617949SNiklas Söderlund 		.data = &rcar_gen3_thermal_info,
4309d617949SNiklas Söderlund 	},
4319d617949SNiklas Söderlund 	{
4329d617949SNiklas Söderlund 		.compatible = "renesas,r8a779f0-thermal",
4339d617949SNiklas Söderlund 		.data = &rcar_gen4_thermal_info,
4349d617949SNiklas Söderlund 	},
4359d617949SNiklas Söderlund 	{
4369d617949SNiklas Söderlund 		.compatible = "renesas,r8a779g0-thermal",
4379d617949SNiklas Söderlund 		.data = &rcar_gen4_thermal_info,
4389d617949SNiklas Söderlund 	},
4399d617949SNiklas Söderlund 	{
4409d617949SNiklas Söderlund 		.compatible = "renesas,r8a779h0-thermal",
4419d617949SNiklas Söderlund 		.data = &rcar_gen4_thermal_info,
4429d617949SNiklas Söderlund 	},
4439d617949SNiklas Söderlund 	{},
4449d617949SNiklas Söderlund };
4459d617949SNiklas Söderlund MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
4469d617949SNiklas Söderlund 
rcar_gen3_thermal_remove(struct platform_device * pdev)4479d617949SNiklas Söderlund static void rcar_gen3_thermal_remove(struct platform_device *pdev)
4489d617949SNiklas Söderlund {
4499d617949SNiklas Söderlund 	struct device *dev = &pdev->dev;
4509d617949SNiklas Söderlund 
4519d617949SNiklas Söderlund 	pm_runtime_put(dev);
4529d617949SNiklas Söderlund 	pm_runtime_disable(dev);
4539d617949SNiklas Söderlund }
4549d617949SNiklas Söderlund 
rcar_gen3_hwmon_action(void * data)4559d617949SNiklas Söderlund static void rcar_gen3_hwmon_action(void *data)
4569d617949SNiklas Söderlund {
4579d617949SNiklas Söderlund 	struct thermal_zone_device *zone = data;
4589d617949SNiklas Söderlund 
4599d617949SNiklas Söderlund 	thermal_remove_hwmon_sysfs(zone);
4609d617949SNiklas Söderlund }
4619d617949SNiklas Söderlund 
rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv * priv,struct platform_device * pdev)4629d617949SNiklas Söderlund static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv,
4639d617949SNiklas Söderlund 					  struct platform_device *pdev)
4649d617949SNiklas Söderlund {
4659d617949SNiklas Söderlund 	struct device *dev = &pdev->dev;
4669d617949SNiklas Söderlund 	unsigned int i;
4679d617949SNiklas Söderlund 	char *irqname;
4689d617949SNiklas Söderlund 	int ret, irq;
4699d617949SNiklas Söderlund 
4709d617949SNiklas Söderlund 	for (i = 0; i < 2; i++) {
4719d617949SNiklas Söderlund 		irq = platform_get_irq_optional(pdev, i);
4729d617949SNiklas Söderlund 		if (irq < 0)
4739d617949SNiklas Söderlund 			return irq;
4749d617949SNiklas Söderlund 
4759d617949SNiklas Söderlund 		irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",
4769d617949SNiklas Söderlund 					 dev_name(dev), i);
4779d617949SNiklas Söderlund 		if (!irqname)
4789d617949SNiklas Söderlund 			return -ENOMEM;
4799d617949SNiklas Söderlund 
4809d617949SNiklas Söderlund 		ret = devm_request_threaded_irq(dev, irq, NULL,
4819d617949SNiklas Söderlund 						rcar_gen3_thermal_irq,
4829d617949SNiklas Söderlund 						IRQF_ONESHOT, irqname, priv);
4839d617949SNiklas Söderlund 		if (ret)
4849d617949SNiklas Söderlund 			return ret;
4859d617949SNiklas Söderlund 	}
4869d617949SNiklas Söderlund 
4879d617949SNiklas Söderlund 	return 0;
4889d617949SNiklas Söderlund }
4899d617949SNiklas Söderlund 
rcar_gen3_thermal_probe(struct platform_device * pdev)4909d617949SNiklas Söderlund static int rcar_gen3_thermal_probe(struct platform_device *pdev)
4919d617949SNiklas Söderlund {
4929d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv;
4939d617949SNiklas Söderlund 	struct device *dev = &pdev->dev;
4949d617949SNiklas Söderlund 	struct resource *res;
4959d617949SNiklas Söderlund 	struct thermal_zone_device *zone;
4969d617949SNiklas Söderlund 	unsigned int i;
4979d617949SNiklas Söderlund 	int ret;
4989d617949SNiklas Söderlund 
4999d617949SNiklas Söderlund 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5009d617949SNiklas Söderlund 	if (!priv)
5019d617949SNiklas Söderlund 		return -ENOMEM;
5029d617949SNiklas Söderlund 
5039d617949SNiklas Söderlund 	priv->ops = rcar_gen3_tz_of_ops;
5049d617949SNiklas Söderlund 
5059d617949SNiklas Söderlund 	priv->info = of_device_get_match_data(dev);
5069d617949SNiklas Söderlund 	platform_set_drvdata(pdev, priv);
5079d617949SNiklas Söderlund 
5089d617949SNiklas Söderlund 	if (rcar_gen3_thermal_request_irqs(priv, pdev))
5099d617949SNiklas Söderlund 		priv->ops.set_trips = NULL;
5109d617949SNiklas Söderlund 
5119d617949SNiklas Söderlund 	pm_runtime_enable(dev);
5129d617949SNiklas Söderlund 	pm_runtime_get_sync(dev);
5139d617949SNiklas Söderlund 
5149d617949SNiklas Söderlund 	for (i = 0; i < TSC_MAX_NUM; i++) {
5159d617949SNiklas Söderlund 		struct rcar_gen3_thermal_tsc *tsc;
5169d617949SNiklas Söderlund 
5179d617949SNiklas Söderlund 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
5189d617949SNiklas Söderlund 		if (!res)
5199d617949SNiklas Söderlund 			break;
5209d617949SNiklas Söderlund 
5219d617949SNiklas Söderlund 		tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL);
5229d617949SNiklas Söderlund 		if (!tsc) {
5239d617949SNiklas Söderlund 			ret = -ENOMEM;
5249d617949SNiklas Söderlund 			goto error_unregister;
5259d617949SNiklas Söderlund 		}
5269d617949SNiklas Söderlund 
5279d617949SNiklas Söderlund 		tsc->priv = priv;
5289d617949SNiklas Söderlund 		tsc->base = devm_ioremap_resource(dev, res);
5299d617949SNiklas Söderlund 		if (IS_ERR(tsc->base)) {
5309d617949SNiklas Söderlund 			ret = PTR_ERR(tsc->base);
5319d617949SNiklas Söderlund 			goto error_unregister;
5329d617949SNiklas Söderlund 		}
5339d617949SNiklas Söderlund 
5349d617949SNiklas Söderlund 		priv->tscs[i] = tsc;
5359d617949SNiklas Söderlund 	}
5369d617949SNiklas Söderlund 
5379d617949SNiklas Söderlund 	priv->num_tscs = i;
5389d617949SNiklas Söderlund 
5399d617949SNiklas Söderlund 	if (!rcar_gen3_thermal_read_fuses(priv))
5409d617949SNiklas Söderlund 		dev_info(dev, "No calibration values fused, fallback to driver values\n");
5419d617949SNiklas Söderlund 
5429d617949SNiklas Söderlund 	rcar_gen3_thermal_shared_coefs(priv);
5439d617949SNiklas Söderlund 
5449d617949SNiklas Söderlund 	for (i = 0; i < priv->num_tscs; i++) {
5459d617949SNiklas Söderlund 		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
5469d617949SNiklas Söderlund 
5479d617949SNiklas Söderlund 		rcar_gen3_thermal_init(priv, tsc);
5489d617949SNiklas Söderlund 		rcar_gen3_thermal_tsc_coefs(priv, tsc);
5499d617949SNiklas Söderlund 
5509d617949SNiklas Söderlund 		zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops);
5519d617949SNiklas Söderlund 		if (IS_ERR(zone)) {
5529d617949SNiklas Söderlund 			dev_err(dev, "Sensor %u: Can't register thermal zone\n", i);
5539d617949SNiklas Söderlund 			ret = PTR_ERR(zone);
5549d617949SNiklas Söderlund 			goto error_unregister;
5559d617949SNiklas Söderlund 		}
5569d617949SNiklas Söderlund 		tsc->zone = zone;
5579d617949SNiklas Söderlund 
5589d617949SNiklas Söderlund 		ret = thermal_add_hwmon_sysfs(tsc->zone);
5599d617949SNiklas Söderlund 		if (ret)
5609d617949SNiklas Söderlund 			goto error_unregister;
5619d617949SNiklas Söderlund 
5629d617949SNiklas Söderlund 		ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);
5639d617949SNiklas Söderlund 		if (ret)
5649d617949SNiklas Söderlund 			goto error_unregister;
5659d617949SNiklas Söderlund 
566*79f194ddSRafael J. Wysocki 		dev_info(dev, "Sensor %u: Loaded\n", i);
5679d617949SNiklas Söderlund 	}
5689d617949SNiklas Söderlund 
5699d617949SNiklas Söderlund 	if (!priv->num_tscs) {
5709d617949SNiklas Söderlund 		ret = -ENODEV;
5719d617949SNiklas Söderlund 		goto error_unregister;
5729d617949SNiklas Söderlund 	}
5739d617949SNiklas Söderlund 
5749d617949SNiklas Söderlund 	return 0;
5759d617949SNiklas Söderlund 
5769d617949SNiklas Söderlund error_unregister:
5779d617949SNiklas Söderlund 	rcar_gen3_thermal_remove(pdev);
5789d617949SNiklas Söderlund 
5799d617949SNiklas Söderlund 	return ret;
5809d617949SNiklas Söderlund }
5819d617949SNiklas Söderlund 
rcar_gen3_thermal_resume(struct device * dev)5829d617949SNiklas Söderlund static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
5839d617949SNiklas Söderlund {
5849d617949SNiklas Söderlund 	struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
5859d617949SNiklas Söderlund 	unsigned int i;
5869d617949SNiklas Söderlund 
5879d617949SNiklas Söderlund 	for (i = 0; i < priv->num_tscs; i++) {
5889d617949SNiklas Söderlund 		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
5899d617949SNiklas Söderlund 
5909d617949SNiklas Söderlund 		rcar_gen3_thermal_init(priv, tsc);
5919d617949SNiklas Söderlund 	}
5929d617949SNiklas Söderlund 
5939d617949SNiklas Söderlund 	return 0;
5949d617949SNiklas Söderlund }
5959d617949SNiklas Söderlund 
5969d617949SNiklas Söderlund static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
5979d617949SNiklas Söderlund 			 rcar_gen3_thermal_resume);
5989d617949SNiklas Söderlund 
5999d617949SNiklas Söderlund static struct platform_driver rcar_gen3_thermal_driver = {
6009d617949SNiklas Söderlund 	.driver	= {
6019d617949SNiklas Söderlund 		.name	= "rcar_gen3_thermal",
6029d617949SNiklas Söderlund 		.pm = &rcar_gen3_thermal_pm_ops,
6039d617949SNiklas Söderlund 		.of_match_table = rcar_gen3_thermal_dt_ids,
6049d617949SNiklas Söderlund 	},
6059d617949SNiklas Söderlund 	.probe		= rcar_gen3_thermal_probe,
6069d617949SNiklas Söderlund 	.remove_new	= rcar_gen3_thermal_remove,
6079d617949SNiklas Söderlund };
6089d617949SNiklas Söderlund module_platform_driver(rcar_gen3_thermal_driver);
6099d617949SNiklas Söderlund 
6109d617949SNiklas Söderlund MODULE_LICENSE("GPL v2");
6119d617949SNiklas Söderlund MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver");
6129d617949SNiklas Söderlund MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");
613