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