1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Renesas RZ/G3E TSU Temperature Sensor Unit 4 * 5 * Copyright (C) 2025 Renesas Electronics Corporation 6 */ 7 #include <linux/clk.h> 8 #include <linux/cleanup.h> 9 #include <linux/delay.h> 10 #include <linux/err.h> 11 #include <linux/interrupt.h> 12 #include <linux/io.h> 13 #include <linux/iopoll.h> 14 #include <linux/kernel.h> 15 #include <linux/mfd/syscon.h> 16 #include <linux/module.h> 17 #include <linux/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm_runtime.h> 20 #include <linux/regmap.h> 21 #include <linux/reset.h> 22 #include <linux/thermal.h> 23 #include <linux/units.h> 24 25 #include "../thermal_hwmon.h" 26 27 /* TSU Register offsets and bits */ 28 #define TSU_SSUSR 0x00 29 #define TSU_SSUSR_EN_TS BIT(0) 30 #define TSU_SSUSR_ADC_PD_TS BIT(1) 31 #define TSU_SSUSR_SOC_TS_EN BIT(2) 32 33 #define TSU_STRGR 0x04 34 #define TSU_STRGR_ADST BIT(0) 35 36 #define TSU_SOSR1 0x08 37 #define TSU_SOSR1_ADCT_8 0x03 38 #define TSU_SOSR1_ADCS BIT(4) 39 #define TSU_SOSR1_OUTSEL BIT(9) 40 41 #define TSU_SCRR 0x10 42 #define TSU_SCRR_OUT12BIT_TS GENMASK(11, 0) 43 44 #define TSU_SSR 0x14 45 #define TSU_SSR_CONV BIT(0) 46 47 #define TSU_CMSR 0x18 48 #define TSU_CMSR_CMPEN BIT(0) 49 50 #define TSU_LLSR 0x1C 51 #define TSU_ULSR 0x20 52 53 #define TSU_SISR 0x30 54 #define TSU_SISR_ADF BIT(0) 55 #define TSU_SISR_CMPF BIT(1) 56 57 #define TSU_SIER 0x34 58 #define TSU_SIER_CMPIE BIT(1) 59 60 #define TSU_SICR 0x38 61 #define TSU_SICR_ADCLR BIT(0) 62 #define TSU_SICR_CMPCLR BIT(1) 63 64 /* Temperature calculation constants from datasheet */ 65 #define TSU_TEMP_D (-41) 66 #define TSU_TEMP_E 126 67 #define TSU_CODE_MAX 0xFFF 68 69 /* Timing specifications from datasheet */ 70 #define TSU_POWERUP_TIME_US 120 /* 120T at 1MHz sensor clock per datasheet */ 71 #define TSU_CONV_TIME_US 50 /* Per sample conversion time */ 72 #define TSU_POLL_DELAY_US 10 /* Polling interval */ 73 #define TSU_MIN_CLOCK_RATE 24000000 /* TSU_PCLK minimum 24MHz */ 74 75 /** 76 * struct rzg3e_thermal_priv - RZ/G3E TSU private data 77 * @base: TSU register base 78 * @dev: device pointer 79 * @syscon: regmap for calibration values 80 * @zone: thermal zone device 81 * @rstc: reset control 82 * @trmval0: calibration value 0 (b) 83 * @trmval1: calibration value 1 (c) 84 * @trim_offset: offset for trim registers in syscon 85 * @lock: protects hardware access during conversions 86 */ 87 struct rzg3e_thermal_priv { 88 void __iomem *base; 89 struct device *dev; 90 struct regmap *syscon; 91 struct thermal_zone_device *zone; 92 struct reset_control *rstc; 93 u16 trmval0; 94 u16 trmval1; 95 u32 trim_offset; 96 struct mutex lock; 97 }; 98 99 static int rzg3e_thermal_power_on(struct rzg3e_thermal_priv *priv) 100 { 101 u32 val; 102 int ret; 103 104 /* Clear any pending interrupts */ 105 writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR); 106 107 /* Disable all interrupts during setup */ 108 writel(0, priv->base + TSU_SIER); 109 110 /* 111 * Power-on sequence per datasheet 7.11.9.1: 112 * SOC_TS_EN must be set at same time or before EN_TS and ADC_PD_TS 113 */ 114 val = TSU_SSUSR_SOC_TS_EN | TSU_SSUSR_EN_TS; 115 writel(val, priv->base + TSU_SSUSR); 116 117 /* Wait for sensor stabilization per datasheet 7.11.7.1 */ 118 usleep_range(TSU_POWERUP_TIME_US, TSU_POWERUP_TIME_US + 10); 119 120 /* Configure for average mode with 8 samples */ 121 val = TSU_SOSR1_OUTSEL | TSU_SOSR1_ADCT_8; 122 writel(val, priv->base + TSU_SOSR1); 123 124 /* Ensure we're in single scan mode (default) */ 125 val = readl(priv->base + TSU_SOSR1); 126 if (val & TSU_SOSR1_ADCS) { 127 dev_err(priv->dev, "Invalid scan mode setting\n"); 128 return -EINVAL; 129 } 130 131 /* Wait for any ongoing conversion to complete */ 132 ret = readl_poll_timeout(priv->base + TSU_SSR, val, 133 !(val & TSU_SSR_CONV), 134 TSU_POLL_DELAY_US, 135 USEC_PER_MSEC); 136 if (ret) { 137 dev_err(priv->dev, "Timeout waiting for conversion\n"); 138 return ret; 139 } 140 141 return 0; 142 } 143 144 static void rzg3e_thermal_power_off(struct rzg3e_thermal_priv *priv) 145 { 146 /* Disable all interrupts */ 147 writel(0, priv->base + TSU_SIER); 148 149 /* Clear pending interrupts */ 150 writel(TSU_SICR_ADCLR | TSU_SICR_CMPCLR, priv->base + TSU_SICR); 151 152 /* Power down sequence per datasheet */ 153 writel(TSU_SSUSR_ADC_PD_TS, priv->base + TSU_SSUSR); 154 } 155 156 /* 157 * Convert 12-bit sensor code to temperature in millicelsius 158 * Formula from datasheet 7.11.7.8: 159 * T(°C) = ((e - d) / (c - b)) * (a - b) + d 160 * where: a = sensor code, b = trmval0, c = trmval1, d = -41, e = 126 161 */ 162 static int rzg3e_thermal_code_to_temp(struct rzg3e_thermal_priv *priv, u16 code) 163 { 164 int temp_e_mc = TSU_TEMP_E * MILLIDEGREE_PER_DEGREE; 165 int temp_d_mc = TSU_TEMP_D * MILLIDEGREE_PER_DEGREE; 166 s64 numerator, denominator; 167 int temp_mc; 168 169 numerator = (temp_e_mc - temp_d_mc) * (s64)(code - priv->trmval0); 170 denominator = priv->trmval1 - priv->trmval0; 171 172 temp_mc = div64_s64(numerator, denominator) + temp_d_mc; 173 174 return clamp(temp_mc, temp_d_mc, temp_e_mc); 175 } 176 177 /* 178 * Convert temperature in millicelsius to 12-bit sensor code 179 * Formula from datasheet 7.11.7.9 (inverse of above) 180 */ 181 static u16 rzg3e_thermal_temp_to_code(struct rzg3e_thermal_priv *priv, int temp_mc) 182 { 183 int temp_e_mc = TSU_TEMP_E * MILLIDEGREE_PER_DEGREE; 184 int temp_d_mc = TSU_TEMP_D * MILLIDEGREE_PER_DEGREE; 185 s64 numerator, denominator; 186 s64 code; 187 188 numerator = (temp_mc - temp_d_mc) * (priv->trmval1 - priv->trmval0); 189 denominator = temp_e_mc - temp_d_mc; 190 191 code = div64_s64(numerator, denominator) + priv->trmval0; 192 193 return clamp_val(code, 0, TSU_CODE_MAX); 194 } 195 196 static int rzg3e_thermal_get_temp(struct thermal_zone_device *tz, int *temp) 197 { 198 struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz); 199 u32 status, code; 200 int ret, timeout; 201 202 ret = pm_runtime_resume_and_get(priv->dev); 203 if (ret < 0) 204 return ret; 205 206 guard(mutex)(&priv->lock); 207 208 /* Clear any previous conversion status */ 209 writel(TSU_SICR_ADCLR, priv->base + TSU_SICR); 210 211 /* Start single conversion */ 212 writel(TSU_STRGR_ADST, priv->base + TSU_STRGR); 213 214 /* Wait for conversion completion - 8 samples at ~50us each */ 215 timeout = TSU_CONV_TIME_US * 8 * 2; /* Double for margin */ 216 ret = readl_poll_timeout(priv->base + TSU_SISR, status, 217 status & TSU_SISR_ADF, 218 TSU_POLL_DELAY_US, timeout); 219 if (ret) { 220 dev_err(priv->dev, "Conversion timeout (status=0x%08x)\n", status); 221 goto out; 222 } 223 224 /* Read the averaged result and clear the complete flag */ 225 code = readl(priv->base + TSU_SCRR) & TSU_SCRR_OUT12BIT_TS; 226 writel(TSU_SICR_ADCLR, priv->base + TSU_SICR); 227 228 /* Convert to temperature */ 229 *temp = rzg3e_thermal_code_to_temp(priv, code); 230 231 dev_dbg(priv->dev, "temp=%d mC (%d.%03d°C), code=0x%03x\n", 232 *temp, *temp / 1000, abs(*temp) % 1000, code); 233 234 out: 235 pm_runtime_mark_last_busy(priv->dev); 236 pm_runtime_put_autosuspend(priv->dev); 237 return ret; 238 } 239 240 static int rzg3e_thermal_set_trips(struct thermal_zone_device *tz, 241 int low, int high) 242 { 243 struct rzg3e_thermal_priv *priv = thermal_zone_device_priv(tz); 244 u16 low_code, high_code; 245 u32 val; 246 int ret; 247 248 /* Hardware requires low < high */ 249 if (low >= high) 250 return -EINVAL; 251 252 ret = pm_runtime_resume_and_get(priv->dev); 253 if (ret < 0) 254 return ret; 255 256 guard(mutex)(&priv->lock); 257 258 /* Convert temperatures to codes */ 259 low_code = rzg3e_thermal_temp_to_code(priv, low); 260 high_code = rzg3e_thermal_temp_to_code(priv, high); 261 262 dev_dbg(priv->dev, "set_trips: low=%d high=%d (codes: 0x%03x/0x%03x)\n", 263 low, high, low_code, high_code); 264 265 /* Disable comparison during reconfiguration */ 266 writel(0, priv->base + TSU_SIER); 267 writel(0, priv->base + TSU_CMSR); 268 269 /* Clear any pending comparison interrupts */ 270 writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR); 271 272 /* Set trip points */ 273 writel(low_code, priv->base + TSU_LLSR); 274 writel(high_code, priv->base + TSU_ULSR); 275 276 /* 277 * Ensure OUTSEL is set for comparison per datasheet 7.11.7.4 278 * Comparison uses averaged data 279 */ 280 val = readl(priv->base + TSU_SOSR1); 281 val |= TSU_SOSR1_OUTSEL; 282 writel(val, priv->base + TSU_SOSR1); 283 284 /* Enable comparison with "out of range" mode (CMPCOND=0) */ 285 writel(TSU_CMSR_CMPEN, priv->base + TSU_CMSR); 286 287 /* Unmask compare IRQ and start a conversion to evaluate window */ 288 writel(TSU_SIER_CMPIE, priv->base + TSU_SIER); 289 writel(TSU_STRGR_ADST, priv->base + TSU_STRGR); 290 291 pm_runtime_mark_last_busy(priv->dev); 292 pm_runtime_put_autosuspend(priv->dev); 293 294 return 0; 295 } 296 297 static irqreturn_t rzg3e_thermal_irq_thread(int irq, void *data) 298 { 299 struct rzg3e_thermal_priv *priv = data; 300 301 dev_dbg(priv->dev, "Temperature threshold crossed\n"); 302 303 /* Notify thermal framework to re-evaluate trip points */ 304 thermal_zone_device_update(priv->zone, THERMAL_TRIP_VIOLATED); 305 306 return IRQ_HANDLED; 307 } 308 309 static irqreturn_t rzg3e_thermal_irq(int irq, void *data) 310 { 311 struct rzg3e_thermal_priv *priv = data; 312 u32 status; 313 314 status = readl(priv->base + TSU_SISR); 315 316 /* Check if comparison interrupt occurred */ 317 if (status & TSU_SISR_CMPF) { 318 /* Clear irq flag and disable interrupt until reconfigured */ 319 writel(TSU_SICR_CMPCLR, priv->base + TSU_SICR); 320 writel(0, priv->base + TSU_SIER); 321 322 return IRQ_WAKE_THREAD; 323 } 324 325 return IRQ_NONE; 326 } 327 328 static const struct thermal_zone_device_ops rzg3e_tz_ops = { 329 .get_temp = rzg3e_thermal_get_temp, 330 .set_trips = rzg3e_thermal_set_trips, 331 }; 332 333 static int rzg3e_thermal_get_calibration(struct rzg3e_thermal_priv *priv) 334 { 335 u32 val; 336 int ret; 337 338 /* Read calibration values from syscon */ 339 ret = regmap_read(priv->syscon, priv->trim_offset, &val); 340 if (ret) 341 return ret; 342 priv->trmval0 = val & GENMASK(11, 0); 343 344 ret = regmap_read(priv->syscon, priv->trim_offset + 4, &val); 345 if (ret) 346 return ret; 347 priv->trmval1 = val & GENMASK(11, 0); 348 349 /* Validate calibration data */ 350 if (!priv->trmval0 || !priv->trmval1 || 351 priv->trmval0 == priv->trmval1 || 352 priv->trmval0 == 0xFFF || priv->trmval1 == 0xFFF) { 353 dev_err(priv->dev, "Invalid calibration: b=0x%03x, c=0x%03x\n", 354 priv->trmval0, priv->trmval1); 355 return -EINVAL; 356 } 357 358 dev_dbg(priv->dev, "Calibration: b=0x%03x (%u), c=0x%03x (%u)\n", 359 priv->trmval0, priv->trmval0, priv->trmval1, priv->trmval1); 360 361 return 0; 362 } 363 364 static int rzg3e_thermal_parse_dt(struct rzg3e_thermal_priv *priv) 365 { 366 struct device_node *np = priv->dev->of_node; 367 u32 offset; 368 369 priv->syscon = syscon_regmap_lookup_by_phandle_args(np, "renesas,tsu-trim", 1, &offset); 370 if (IS_ERR(priv->syscon)) 371 return dev_err_probe(priv->dev, PTR_ERR(priv->syscon), 372 "Failed to parse renesas,tsu-trim\n"); 373 374 priv->trim_offset = offset; 375 return 0; 376 } 377 378 static int rzg3e_thermal_probe(struct platform_device *pdev) 379 { 380 struct device *dev = &pdev->dev; 381 struct rzg3e_thermal_priv *priv; 382 struct clk *clk; 383 int irq, ret; 384 385 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 386 if (!priv) 387 return -ENOMEM; 388 389 priv->dev = dev; 390 ret = devm_mutex_init(dev, &priv->lock); 391 if (ret) 392 return ret; 393 platform_set_drvdata(pdev, priv); 394 395 priv->base = devm_platform_ioremap_resource(pdev, 0); 396 if (IS_ERR(priv->base)) 397 return PTR_ERR(priv->base); 398 399 /* Parse device tree for trim register info */ 400 ret = rzg3e_thermal_parse_dt(priv); 401 if (ret) 402 return ret; 403 404 /* Get clock to verify frequency - clock is managed by power domain */ 405 clk = devm_clk_get(dev, NULL); 406 if (IS_ERR(clk)) 407 return dev_err_probe(dev, PTR_ERR(clk), 408 "Failed to get clock\n"); 409 410 if (clk_get_rate(clk) < TSU_MIN_CLOCK_RATE) 411 return dev_err_probe(dev, -EINVAL, 412 "Clock rate %lu Hz too low (min %u Hz)\n", 413 clk_get_rate(clk), TSU_MIN_CLOCK_RATE); 414 415 priv->rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); 416 if (IS_ERR(priv->rstc)) 417 return dev_err_probe(dev, PTR_ERR(priv->rstc), 418 "Failed to get/deassert reset control\n"); 419 420 /* Get calibration data */ 421 ret = rzg3e_thermal_get_calibration(priv); 422 if (ret) 423 return dev_err_probe(dev, ret, 424 "Failed to get valid calibration data\n"); 425 426 /* Get comparison interrupt */ 427 irq = platform_get_irq_byname(pdev, "adcmpi"); 428 if (irq < 0) 429 return irq; 430 431 /* Enable runtime PM */ 432 pm_runtime_set_autosuspend_delay(dev, 1000); 433 pm_runtime_use_autosuspend(dev); 434 devm_pm_runtime_enable(dev); 435 436 /* Initial hardware setup */ 437 ret = pm_runtime_resume_and_get(dev); 438 if (ret < 0) 439 return dev_err_probe(dev, ret, "Runtime resume failed\n"); 440 441 /* Register thermal zone - this will trigger DT parsing */ 442 priv->zone = devm_thermal_of_zone_register(dev, 0, priv, &rzg3e_tz_ops); 443 if (IS_ERR(priv->zone)) { 444 ret = PTR_ERR(priv->zone); 445 dev_err(dev, "Failed to register thermal zone: %d\n", ret); 446 goto err_pm_put; 447 } 448 449 /* Request threaded IRQ for comparison interrupt */ 450 ret = devm_request_threaded_irq(dev, irq, rzg3e_thermal_irq, 451 rzg3e_thermal_irq_thread, 452 IRQF_ONESHOT, "rzg3e_thermal", priv); 453 if (ret) { 454 dev_err(dev, "Failed to request IRQ: %d\n", ret); 455 goto err_pm_put; 456 } 457 458 /* Add hwmon sysfs interface */ 459 ret = devm_thermal_add_hwmon_sysfs(dev, priv->zone); 460 if (ret) 461 dev_warn(dev, "Failed to add hwmon sysfs attributes\n"); 462 463 pm_runtime_mark_last_busy(dev); 464 pm_runtime_put_autosuspend(dev); 465 466 dev_info(dev, "RZ/G3E thermal sensor registered\n"); 467 468 return 0; 469 470 err_pm_put: 471 pm_runtime_put_sync(dev); 472 return ret; 473 } 474 475 static int rzg3e_thermal_runtime_suspend(struct device *dev) 476 { 477 struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev); 478 479 rzg3e_thermal_power_off(priv); 480 return 0; 481 } 482 483 static int rzg3e_thermal_runtime_resume(struct device *dev) 484 { 485 struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev); 486 487 return rzg3e_thermal_power_on(priv); 488 } 489 490 static int rzg3e_thermal_suspend(struct device *dev) 491 { 492 struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev); 493 494 /* If device is active, power it off */ 495 if (pm_runtime_active(dev)) 496 rzg3e_thermal_power_off(priv); 497 498 /* Assert reset to ensure clean state after resume */ 499 reset_control_assert(priv->rstc); 500 501 return 0; 502 } 503 504 static int rzg3e_thermal_resume(struct device *dev) 505 { 506 struct rzg3e_thermal_priv *priv = dev_get_drvdata(dev); 507 int ret; 508 509 /* Deassert reset */ 510 ret = reset_control_deassert(priv->rstc); 511 if (ret) { 512 dev_err(dev, "Failed to deassert reset: %d\n", ret); 513 return ret; 514 } 515 516 /* If device was active before suspend, power it back on */ 517 if (pm_runtime_active(dev)) 518 return rzg3e_thermal_power_on(priv); 519 520 return 0; 521 } 522 523 static const struct dev_pm_ops rzg3e_thermal_pm_ops = { 524 RUNTIME_PM_OPS(rzg3e_thermal_runtime_suspend, 525 rzg3e_thermal_runtime_resume, NULL) 526 SYSTEM_SLEEP_PM_OPS(rzg3e_thermal_suspend, rzg3e_thermal_resume) 527 }; 528 529 static const struct of_device_id rzg3e_thermal_dt_ids[] = { 530 { .compatible = "renesas,r9a09g047-tsu" }, 531 { /* sentinel */ } 532 }; 533 MODULE_DEVICE_TABLE(of, rzg3e_thermal_dt_ids); 534 535 static struct platform_driver rzg3e_thermal_driver = { 536 .driver = { 537 .name = "rzg3e_thermal", 538 .of_match_table = rzg3e_thermal_dt_ids, 539 .pm = pm_ptr(&rzg3e_thermal_pm_ops), 540 }, 541 .probe = rzg3e_thermal_probe, 542 }; 543 module_platform_driver(rzg3e_thermal_driver); 544 545 MODULE_DESCRIPTION("Renesas RZ/G3E TSU Thermal Sensor Driver"); 546 MODULE_AUTHOR("John Madieu <john.madieu.xa@bp.renesas.com>"); 547 MODULE_LICENSE("GPL"); 548