brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · 810f866 Raw
613 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  R-Car Gen3 THS thermal sensor driver4 *  Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.5 *6 * Copyright (C) 2016 Renesas Electronics Corporation.7 * Copyright (C) 2016 Sang Engineering8 */9#include <linux/delay.h>10#include <linux/err.h>11#include <linux/interrupt.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/thermal.h>18 19#include "../thermal_hwmon.h"20 21/* Register offsets */22#define REG_GEN3_IRQSTR		0x0423#define REG_GEN3_IRQMSK		0x0824#define REG_GEN3_IRQCTL		0x0C25#define REG_GEN3_IRQEN		0x1026#define REG_GEN3_IRQTEMP1	0x1427#define REG_GEN3_IRQTEMP2	0x1828#define REG_GEN3_IRQTEMP3	0x1C29#define REG_GEN3_THCTR		0x2030#define REG_GEN3_TEMP		0x2831#define REG_GEN3_THCODE1	0x5032#define REG_GEN3_THCODE2	0x5433#define REG_GEN3_THCODE3	0x5834#define REG_GEN3_PTAT1		0x5c35#define REG_GEN3_PTAT2		0x6036#define REG_GEN3_PTAT3		0x6437#define REG_GEN3_THSCP		0x6838#define REG_GEN4_THSFMON00	0x18039#define REG_GEN4_THSFMON01	0x18440#define REG_GEN4_THSFMON02	0x18841#define REG_GEN4_THSFMON15	0x1BC42#define REG_GEN4_THSFMON16	0x1C043#define REG_GEN4_THSFMON17	0x1C444 45/* IRQ{STR,MSK,EN} bits */46#define IRQ_TEMP1		BIT(0)47#define IRQ_TEMP2		BIT(1)48#define IRQ_TEMP3		BIT(2)49#define IRQ_TEMPD1		BIT(3)50#define IRQ_TEMPD2		BIT(4)51#define IRQ_TEMPD3		BIT(5)52 53/* THCTR bits */54#define THCTR_PONM	BIT(6)55#define THCTR_THSST	BIT(0)56 57/* THSCP bits */58#define THSCP_COR_PARA_VLD	(BIT(15) | BIT(14))59 60#define CTEMP_MASK	0xFFF61 62#define MCELSIUS(temp)	((temp) * 1000)63#define GEN3_FUSE_MASK	0xFFF64#define GEN4_FUSE_MASK	0xFFF65 66#define TSC_MAX_NUM	567 68struct rcar_gen3_thermal_priv;69 70struct rcar_thermal_info {71	int scale;72	int adj_below;73	int adj_above;74	void (*read_fuses)(struct rcar_gen3_thermal_priv *priv);75};76 77struct equation_set_coef {78	int a;79	int b;80};81 82struct rcar_gen3_thermal_tsc {83	struct rcar_gen3_thermal_priv *priv;84	void __iomem *base;85	struct thermal_zone_device *zone;86	/* Different coefficients are used depending on a threshold. */87	struct {88		struct equation_set_coef below;89		struct equation_set_coef above;90	} coef;91	int thcode[3];92};93 94struct rcar_gen3_thermal_priv {95	struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];96	struct thermal_zone_device_ops ops;97	unsigned int num_tscs;98	int ptat[3];99	int tj_t;100	const struct rcar_thermal_info *info;101};102 103static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc,104					 u32 reg)105{106	return ioread32(tsc->base + reg);107}108 109static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,110					   u32 reg, u32 data)111{112	iowrite32(data, tsc->base + reg);113}114 115/*116 * Linear approximation for temperature117 *118 * [temp] = ((thadj - [reg]) * a) / b + adj119 * [reg] = thadj - ([temp] - adj) * b / a120 *121 * The constants a and b are calculated using two triplets of int values PTAT122 * and THCODE. PTAT and THCODE can either be read from hardware or use hard123 * coded values from the driver. The formula to calculate a and b are taken from124 * the datasheet. Different calculations are needed for a and b depending on125 * if the input variables ([temp] or [reg]) are above or below a threshold. The126 * threshold is also calculated from PTAT and THCODE using formulas from the127 * datasheet.128 *129 * The constant thadj is one of the THCODE values, which one to use depends on130 * the threshold and input value.131 *132 * The constants adj is taken verbatim from the datasheet. Two values exists,133 * which one to use depends on the input value and the calculated threshold.134 * Furthermore different SoC models supported by the driver have different sets135 * of values. The values for each model are stored in the device match data.136 */137 138static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv)139{140	priv->tj_t =141		DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale,142				  priv->ptat[0] - priv->ptat[2])143		+ priv->info->adj_below;144}145static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv,146					struct rcar_gen3_thermal_tsc *tsc)147{148	tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]);149	tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]);150 151	tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]);152	tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]);153}154 155static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)156{157	struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);158	struct rcar_gen3_thermal_priv *priv = tsc->priv;159	const struct equation_set_coef *coef;160	int adj, decicelsius, reg, thcode;161 162	/* Read register and convert to mili Celsius */163	reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;164 165	if (reg < tsc->thcode[1]) {166		adj = priv->info->adj_below;167		coef = &tsc->coef.below;168		thcode = tsc->thcode[2];169	} else {170		adj = priv->info->adj_above;171		coef = &tsc->coef.above;172		thcode = tsc->thcode[0];173	}174 175	/*176	 * The dividend can't be grown as it might overflow, instead shorten the177	 * divisor to convert to decidegree Celsius. If we convert after the178	 * division precision is lost as we will scale up from whole degrees179	 * Celsius.180	 */181	decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10);182 183	/* Guaranteed operating range is -40C to 125C. */184 185	/* Reporting is done in millidegree Celsius */186	*temp = decicelsius * 100 + adj * 1000;187 188	return 0;189}190 191static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,192					      int mcelsius)193{194	struct rcar_gen3_thermal_priv *priv = tsc->priv;195	const struct equation_set_coef *coef;196	int adj, celsius, thcode;197 198	celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);199	if (celsius < priv->tj_t) {200		coef = &tsc->coef.below;201		adj = priv->info->adj_below;202		thcode = tsc->thcode[2];203	} else {204		coef = &tsc->coef.above;205		adj = priv->info->adj_above;206		thcode = tsc->thcode[0];207	}208 209	return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a);210}211 212static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)213{214	struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);215	u32 irqmsk = 0;216 217	if (low != -INT_MAX) {218		irqmsk |= IRQ_TEMPD1;219		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,220					rcar_gen3_thermal_mcelsius_to_temp(tsc, low));221	}222 223	if (high != INT_MAX) {224		irqmsk |= IRQ_TEMP2;225		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,226					rcar_gen3_thermal_mcelsius_to_temp(tsc, high));227	}228 229	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk);230 231	return 0;232}233 234static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {235	.get_temp	= rcar_gen3_thermal_get_temp,236	.set_trips	= rcar_gen3_thermal_set_trips,237};238 239static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)240{241	struct rcar_gen3_thermal_priv *priv = data;242	unsigned int i;243	u32 status;244 245	for (i = 0; i < priv->num_tscs; i++) {246		status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);247		rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);248		if (status && priv->tscs[i]->zone)249			thermal_zone_device_update(priv->tscs[i]->zone,250						   THERMAL_EVENT_UNSPECIFIED);251	}252 253	return IRQ_HANDLED;254}255 256static void rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv *priv)257{258	unsigned int i;259 260	/*261	 * Set the pseudo calibration points with fused values.262	 * PTAT is shared between all TSCs but only fused for the first263	 * TSC while THCODEs are fused for each TSC.264	 */265	priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT1) &266		GEN3_FUSE_MASK;267	priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT2) &268		GEN3_FUSE_MASK;269	priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT3) &270		GEN3_FUSE_MASK;271 272	for (i = 0; i < priv->num_tscs; i++) {273		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];274 275		tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE1) &276			GEN3_FUSE_MASK;277		tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE2) &278			GEN3_FUSE_MASK;279		tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE3) &280			GEN3_FUSE_MASK;281	}282}283 284static void rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv *priv)285{286	unsigned int i;287 288	/*289	 * Set the pseudo calibration points with fused values.290	 * PTAT is shared between all TSCs but only fused for the first291	 * TSC while THCODEs are fused for each TSC.292	 */293	priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON16) &294		GEN4_FUSE_MASK;295	priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON17) &296		GEN4_FUSE_MASK;297	priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON15) &298		GEN4_FUSE_MASK;299 300	for (i = 0; i < priv->num_tscs; i++) {301		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];302 303		tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON01) &304			GEN4_FUSE_MASK;305		tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON02) &306			GEN4_FUSE_MASK;307		tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON00) &308			GEN4_FUSE_MASK;309	}310}311 312static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)313{314	unsigned int i;315	u32 thscp;316 317	/* If fuses are not set, fallback to pseudo values. */318	thscp = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_THSCP);319	if (!priv->info->read_fuses ||320	    (thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) {321		/* Default THCODE values in case FUSEs are not set. */322		static const int thcodes[TSC_MAX_NUM][3] = {323			{ 3397, 2800, 2221 },324			{ 3393, 2795, 2216 },325			{ 3389, 2805, 2237 },326			{ 3415, 2694, 2195 },327			{ 3356, 2724, 2244 },328		};329 330		priv->ptat[0] = 2631;331		priv->ptat[1] = 1509;332		priv->ptat[2] = 435;333 334		for (i = 0; i < priv->num_tscs; i++) {335			struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];336 337			tsc->thcode[0] = thcodes[i][0];338			tsc->thcode[1] = thcodes[i][1];339			tsc->thcode[2] = thcodes[i][2];340		}341 342		return false;343	}344 345	priv->info->read_fuses(priv);346	return true;347}348 349static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,350				   struct rcar_gen3_thermal_tsc *tsc)351{352	u32 reg_val;353 354	reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);355	reg_val &= ~THCTR_PONM;356	rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);357 358	usleep_range(1000, 2000);359 360	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);361	rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);362	if (priv->ops.set_trips)363		rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,364					IRQ_TEMPD1 | IRQ_TEMP2);365 366	reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);367	reg_val |= THCTR_THSST;368	rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);369 370	usleep_range(1000, 2000);371}372 373static const struct rcar_thermal_info rcar_m3w_thermal_info = {374	.scale = 157,375	.adj_below = -41,376	.adj_above = 116,377	.read_fuses = rcar_gen3_thermal_read_fuses_gen3,378};379 380static const struct rcar_thermal_info rcar_gen3_thermal_info = {381	.scale = 167,382	.adj_below = -41,383	.adj_above = 126,384	.read_fuses = rcar_gen3_thermal_read_fuses_gen3,385};386 387static const struct rcar_thermal_info rcar_gen4_thermal_info = {388	.scale = 167,389	.adj_below = -41,390	.adj_above = 126,391	.read_fuses = rcar_gen3_thermal_read_fuses_gen4,392};393 394static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {395	{396		.compatible = "renesas,r8a774a1-thermal",397		.data = &rcar_m3w_thermal_info,398	},399	{400		.compatible = "renesas,r8a774b1-thermal",401		.data = &rcar_gen3_thermal_info,402	},403	{404		.compatible = "renesas,r8a774e1-thermal",405		.data = &rcar_gen3_thermal_info,406	},407	{408		.compatible = "renesas,r8a7795-thermal",409		.data = &rcar_gen3_thermal_info,410	},411	{412		.compatible = "renesas,r8a7796-thermal",413		.data = &rcar_m3w_thermal_info,414	},415	{416		.compatible = "renesas,r8a77961-thermal",417		.data = &rcar_m3w_thermal_info,418	},419	{420		.compatible = "renesas,r8a77965-thermal",421		.data = &rcar_gen3_thermal_info,422	},423	{424		.compatible = "renesas,r8a77980-thermal",425		.data = &rcar_gen3_thermal_info,426	},427	{428		.compatible = "renesas,r8a779a0-thermal",429		.data = &rcar_gen3_thermal_info,430	},431	{432		.compatible = "renesas,r8a779f0-thermal",433		.data = &rcar_gen4_thermal_info,434	},435	{436		.compatible = "renesas,r8a779g0-thermal",437		.data = &rcar_gen4_thermal_info,438	},439	{440		.compatible = "renesas,r8a779h0-thermal",441		.data = &rcar_gen4_thermal_info,442	},443	{},444};445MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);446 447static void rcar_gen3_thermal_remove(struct platform_device *pdev)448{449	struct device *dev = &pdev->dev;450 451	pm_runtime_put(dev);452	pm_runtime_disable(dev);453}454 455static void rcar_gen3_hwmon_action(void *data)456{457	struct thermal_zone_device *zone = data;458 459	thermal_remove_hwmon_sysfs(zone);460}461 462static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv,463					  struct platform_device *pdev)464{465	struct device *dev = &pdev->dev;466	unsigned int i;467	char *irqname;468	int ret, irq;469 470	for (i = 0; i < 2; i++) {471		irq = platform_get_irq_optional(pdev, i);472		if (irq < 0)473			return irq;474 475		irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",476					 dev_name(dev), i);477		if (!irqname)478			return -ENOMEM;479 480		ret = devm_request_threaded_irq(dev, irq, NULL,481						rcar_gen3_thermal_irq,482						IRQF_ONESHOT, irqname, priv);483		if (ret)484			return ret;485	}486 487	return 0;488}489 490static int rcar_gen3_thermal_probe(struct platform_device *pdev)491{492	struct rcar_gen3_thermal_priv *priv;493	struct device *dev = &pdev->dev;494	struct resource *res;495	struct thermal_zone_device *zone;496	unsigned int i;497	int ret;498 499	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);500	if (!priv)501		return -ENOMEM;502 503	priv->ops = rcar_gen3_tz_of_ops;504 505	priv->info = of_device_get_match_data(dev);506	platform_set_drvdata(pdev, priv);507 508	if (rcar_gen3_thermal_request_irqs(priv, pdev))509		priv->ops.set_trips = NULL;510 511	pm_runtime_enable(dev);512	pm_runtime_get_sync(dev);513 514	for (i = 0; i < TSC_MAX_NUM; i++) {515		struct rcar_gen3_thermal_tsc *tsc;516 517		res = platform_get_resource(pdev, IORESOURCE_MEM, i);518		if (!res)519			break;520 521		tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL);522		if (!tsc) {523			ret = -ENOMEM;524			goto error_unregister;525		}526 527		tsc->priv = priv;528		tsc->base = devm_ioremap_resource(dev, res);529		if (IS_ERR(tsc->base)) {530			ret = PTR_ERR(tsc->base);531			goto error_unregister;532		}533 534		priv->tscs[i] = tsc;535	}536 537	priv->num_tscs = i;538 539	if (!rcar_gen3_thermal_read_fuses(priv))540		dev_info(dev, "No calibration values fused, fallback to driver values\n");541 542	rcar_gen3_thermal_shared_coefs(priv);543 544	for (i = 0; i < priv->num_tscs; i++) {545		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];546 547		rcar_gen3_thermal_init(priv, tsc);548		rcar_gen3_thermal_tsc_coefs(priv, tsc);549 550		zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops);551		if (IS_ERR(zone)) {552			dev_err(dev, "Sensor %u: Can't register thermal zone\n", i);553			ret = PTR_ERR(zone);554			goto error_unregister;555		}556		tsc->zone = zone;557 558		ret = thermal_add_hwmon_sysfs(tsc->zone);559		if (ret)560			goto error_unregister;561 562		ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);563		if (ret)564			goto error_unregister;565 566		dev_info(dev, "Sensor %u: Loaded\n", i);567	}568 569	if (!priv->num_tscs) {570		ret = -ENODEV;571		goto error_unregister;572	}573 574	return 0;575 576error_unregister:577	rcar_gen3_thermal_remove(pdev);578 579	return ret;580}581 582static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)583{584	struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);585	unsigned int i;586 587	for (i = 0; i < priv->num_tscs; i++) {588		struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];589 590		rcar_gen3_thermal_init(priv, tsc);591	}592 593	return 0;594}595 596static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,597			 rcar_gen3_thermal_resume);598 599static struct platform_driver rcar_gen3_thermal_driver = {600	.driver	= {601		.name	= "rcar_gen3_thermal",602		.pm = &rcar_gen3_thermal_pm_ops,603		.of_match_table = rcar_gen3_thermal_dt_ids,604	},605	.probe		= rcar_gen3_thermal_probe,606	.remove_new	= rcar_gen3_thermal_remove,607};608module_platform_driver(rcar_gen3_thermal_driver);609 610MODULE_LICENSE("GPL v2");611MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver");612MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");613