1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * R-Car Gen3 THS thermal sensor driver 4 * 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 Engineering 8 */ 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 0x04 23 #define REG_GEN3_IRQMSK 0x08 24 #define REG_GEN3_IRQCTL 0x0c 25 #define REG_GEN3_IRQEN 0x10 26 #define REG_GEN3_IRQTEMP1 0x14 27 #define REG_GEN3_IRQTEMP2 0x18 28 #define REG_GEN3_IRQTEMP3 0x1c 29 #define REG_GEN3_THCTR 0x20 30 #define REG_GEN3_TEMP 0x28 31 #define REG_GEN3_THCODE1 0x50 32 #define REG_GEN3_THCODE2 0x54 33 #define REG_GEN3_THCODE3 0x58 34 #define REG_GEN3_PTAT1 0x5c 35 #define REG_GEN3_PTAT2 0x60 36 #define REG_GEN3_PTAT3 0x64 37 #define REG_GEN3_THSCP 0x68 38 #define REG_GEN4_THSFMON00 0x180 39 #define REG_GEN4_THSFMON01 0x184 40 #define REG_GEN4_THSFMON02 0x188 41 #define REG_GEN4_THSFMON15 0x1bc 42 #define REG_GEN4_THSFMON16 0x1c0 43 #define REG_GEN4_THSFMON17 0x1c4 44 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 0xfff 61 62 #define MCELSIUS(temp) ((temp) * 1000) 63 #define GEN3_FUSE_MASK 0xfff 64 #define GEN4_FUSE_MASK 0xfff 65 66 #define TSC_MAX_NUM 5 67 68 struct rcar_gen3_thermal_priv; 69 70 struct rcar_gen3_thermal_fuse_info { 71 u32 ptat[3]; 72 u32 thcode[3]; 73 u32 mask; 74 }; 75 76 struct rcar_gen3_thermal_fuse_default { 77 u32 ptat[3]; 78 u32 thcodes[TSC_MAX_NUM][3]; 79 }; 80 81 struct rcar_thermal_info { 82 int scale; 83 int adj_below; 84 int adj_above; 85 const struct rcar_gen3_thermal_fuse_info *fuses; 86 const struct rcar_gen3_thermal_fuse_default *fuse_defaults; 87 }; 88 89 struct equation_set_coef { 90 int a; 91 int b; 92 }; 93 94 struct rcar_gen3_thermal_tsc { 95 struct rcar_gen3_thermal_priv *priv; 96 void __iomem *base; 97 struct thermal_zone_device *zone; 98 /* Different coefficients are used depending on a threshold. */ 99 struct { 100 struct equation_set_coef below; 101 struct equation_set_coef above; 102 } coef; 103 int thcode[3]; 104 }; 105 106 struct rcar_gen3_thermal_priv { 107 struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; 108 struct thermal_zone_device_ops ops; 109 unsigned int num_tscs; 110 int ptat[3]; 111 int tj_t; 112 const struct rcar_thermal_info *info; 113 }; 114 115 static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc, 116 u32 reg) 117 { 118 return ioread32(tsc->base + reg); 119 } 120 121 static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, 122 u32 reg, u32 data) 123 { 124 iowrite32(data, tsc->base + reg); 125 } 126 127 /* 128 * Linear approximation for temperature 129 * 130 * [temp] = ((thadj - [reg]) * a) / b + adj 131 * [reg] = thadj - ([temp] - adj) * b / a 132 * 133 * The constants a and b are calculated using two triplets of int values PTAT 134 * and THCODE. PTAT and THCODE can either be read from hardware or use hard 135 * coded values from the driver. The formula to calculate a and b are taken from 136 * the datasheet. Different calculations are needed for a and b depending on 137 * if the input variables ([temp] or [reg]) are above or below a threshold. The 138 * threshold is also calculated from PTAT and THCODE using formulas from the 139 * datasheet. 140 * 141 * The constant thadj is one of the THCODE values, which one to use depends on 142 * the threshold and input value. 143 * 144 * The constants adj is taken verbatim from the datasheet. Two values exists, 145 * which one to use depends on the input value and the calculated threshold. 146 * Furthermore different SoC models supported by the driver have different sets 147 * of values. The values for each model are stored in the device match data. 148 */ 149 150 static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv) 151 { 152 priv->tj_t = 153 DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale, 154 priv->ptat[0] - priv->ptat[2]) 155 + priv->info->adj_below; 156 } 157 static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv, 158 struct rcar_gen3_thermal_tsc *tsc) 159 { 160 tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]); 161 tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]); 162 163 tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]); 164 tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]); 165 } 166 167 static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp) 168 { 169 struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz); 170 struct rcar_gen3_thermal_priv *priv = tsc->priv; 171 const struct equation_set_coef *coef; 172 int adj, decicelsius, reg, thcode; 173 174 /* Read register and convert to millidegree Celsius */ 175 reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; 176 177 if (reg < tsc->thcode[1]) { 178 adj = priv->info->adj_below; 179 coef = &tsc->coef.below; 180 thcode = tsc->thcode[2]; 181 } else { 182 adj = priv->info->adj_above; 183 coef = &tsc->coef.above; 184 thcode = tsc->thcode[0]; 185 } 186 187 /* 188 * The dividend can't be grown as it might overflow, instead shorten the 189 * divisor to convert to decidegree Celsius. If we convert after the 190 * division precision is lost as we will scale up from whole degrees 191 * Celsius. 192 */ 193 decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10); 194 195 /* Guaranteed operating range is -40C to 125C. */ 196 197 /* Reporting is done in millidegree Celsius */ 198 *temp = decicelsius * 100 + adj * 1000; 199 200 return 0; 201 } 202 203 static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, 204 int mcelsius) 205 { 206 struct rcar_gen3_thermal_priv *priv = tsc->priv; 207 const struct equation_set_coef *coef; 208 int adj, celsius, thcode; 209 210 celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); 211 if (celsius < priv->tj_t) { 212 coef = &tsc->coef.below; 213 adj = priv->info->adj_below; 214 thcode = tsc->thcode[2]; 215 } else { 216 coef = &tsc->coef.above; 217 adj = priv->info->adj_above; 218 thcode = tsc->thcode[0]; 219 } 220 221 return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a); 222 } 223 224 static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high) 225 { 226 struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz); 227 u32 irqmsk = 0; 228 229 if (low != -INT_MAX) { 230 irqmsk |= IRQ_TEMPD1; 231 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, 232 rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); 233 } 234 235 if (high != INT_MAX) { 236 irqmsk |= IRQ_TEMP2; 237 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, 238 rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); 239 } 240 241 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk); 242 243 return 0; 244 } 245 246 static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = { 247 .get_temp = rcar_gen3_thermal_get_temp, 248 .set_trips = rcar_gen3_thermal_set_trips, 249 }; 250 251 static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) 252 { 253 struct rcar_gen3_thermal_priv *priv = data; 254 unsigned int i; 255 u32 status; 256 257 for (i = 0; i < priv->num_tscs; i++) { 258 status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); 259 rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); 260 if (status && priv->tscs[i]->zone) 261 thermal_zone_device_update(priv->tscs[i]->zone, 262 THERMAL_EVENT_UNSPECIFIED); 263 } 264 265 return IRQ_HANDLED; 266 } 267 268 static void rcar_gen3_thermal_fetch_fuses(struct rcar_gen3_thermal_priv *priv) 269 { 270 const struct rcar_gen3_thermal_fuse_info *fuses = priv->info->fuses; 271 272 /* 273 * Set the pseudo calibration points with fused values. 274 * PTAT is shared between all TSCs but only fused for the first 275 * TSC while THCODEs are fused for each TSC. 276 */ 277 priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[0]) 278 & fuses->mask; 279 priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[1]) 280 & fuses->mask; 281 priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], fuses->ptat[2]) 282 & fuses->mask; 283 284 for (unsigned int i = 0; i < priv->num_tscs; i++) { 285 struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; 286 287 tsc->thcode[0] = rcar_gen3_thermal_read(tsc, fuses->thcode[0]) 288 & fuses->mask; 289 tsc->thcode[1] = rcar_gen3_thermal_read(tsc, fuses->thcode[1]) 290 & fuses->mask; 291 tsc->thcode[2] = rcar_gen3_thermal_read(tsc, fuses->thcode[2]) 292 & fuses->mask; 293 } 294 } 295 296 static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv) 297 { 298 const struct rcar_gen3_thermal_fuse_default *fuse_defaults = priv->info->fuse_defaults; 299 unsigned int i; 300 u32 thscp; 301 302 /* If fuses are not set, fallback to pseudo values. */ 303 thscp = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_THSCP); 304 if (!priv->info->fuses || 305 (thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) { 306 /* Default THCODE values in case FUSEs are not set. */ 307 priv->ptat[0] = fuse_defaults->ptat[0]; 308 priv->ptat[1] = fuse_defaults->ptat[1]; 309 priv->ptat[2] = fuse_defaults->ptat[2]; 310 311 for (i = 0; i < priv->num_tscs; i++) { 312 struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; 313 314 tsc->thcode[0] = fuse_defaults->thcodes[i][0]; 315 tsc->thcode[1] = fuse_defaults->thcodes[i][1]; 316 tsc->thcode[2] = fuse_defaults->thcodes[i][2]; 317 } 318 319 return false; 320 } 321 322 rcar_gen3_thermal_fetch_fuses(priv); 323 324 return true; 325 } 326 327 static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv, 328 struct rcar_gen3_thermal_tsc *tsc) 329 { 330 u32 reg_val; 331 332 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 333 reg_val &= ~THCTR_PONM; 334 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); 335 336 usleep_range(1000, 2000); 337 338 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0); 339 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); 340 if (priv->ops.set_trips) 341 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, 342 IRQ_TEMPD1 | IRQ_TEMP2); 343 344 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 345 reg_val |= THCTR_THSST; 346 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); 347 348 usleep_range(1000, 2000); 349 } 350 351 static const struct rcar_gen3_thermal_fuse_info rcar_gen3_thermal_fuse_info_gen3 = { 352 .ptat = { REG_GEN3_PTAT1, REG_GEN3_PTAT2, REG_GEN3_PTAT3 }, 353 .thcode = { REG_GEN3_THCODE1, REG_GEN3_THCODE2, REG_GEN3_THCODE3 }, 354 .mask = GEN3_FUSE_MASK, 355 }; 356 357 static const struct rcar_gen3_thermal_fuse_info rcar_gen3_thermal_fuse_info_gen4 = { 358 .ptat = { REG_GEN4_THSFMON16, REG_GEN4_THSFMON17, REG_GEN4_THSFMON15 }, 359 .thcode = { REG_GEN4_THSFMON01, REG_GEN4_THSFMON02, REG_GEN4_THSFMON00 }, 360 .mask = GEN4_FUSE_MASK, 361 }; 362 363 static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen3 = { 364 .ptat = { 2631, 1509, 435 }, 365 .thcodes = { 366 { 3397, 2800, 2221 }, 367 { 3393, 2795, 2216 }, 368 { 3389, 2805, 2237 }, 369 { 3415, 2694, 2195 }, 370 { 3356, 2724, 2244 }, 371 }, 372 }; 373 374 static const struct rcar_gen3_thermal_fuse_default rcar_gen3_thermal_fuse_default_info_gen4 = { 375 .ptat = { 3274, 2164, 985 }, 376 .thcodes = { /* All four THS units share the same trimming */ 377 { 3218, 2617, 1980 }, 378 { 3218, 2617, 1980 }, 379 { 3218, 2617, 1980 }, 380 { 3218, 2617, 1980 }, 381 } 382 }; 383 384 static const struct rcar_thermal_info rcar_m3w_thermal_info = { 385 .scale = 157, 386 .adj_below = -41, 387 .adj_above = 116, 388 .fuses = &rcar_gen3_thermal_fuse_info_gen3, 389 .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3, 390 }; 391 392 static const struct rcar_thermal_info rcar_gen3_thermal_info = { 393 .scale = 167, 394 .adj_below = -41, 395 .adj_above = 126, 396 .fuses = &rcar_gen3_thermal_fuse_info_gen3, 397 .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3, 398 }; 399 400 static const struct rcar_thermal_info rcar_s4_thermal_info = { 401 .scale = 167, 402 .adj_below = -41, 403 .adj_above = 126, 404 .fuses = &rcar_gen3_thermal_fuse_info_gen4, 405 .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen3, 406 }; 407 408 static const struct rcar_thermal_info rcar_gen4_thermal_info = { 409 .scale = 167, 410 .adj_below = -41, 411 .adj_above = 126, 412 .fuses = &rcar_gen3_thermal_fuse_info_gen4, 413 .fuse_defaults = &rcar_gen3_thermal_fuse_default_info_gen4, 414 }; 415 416 static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { 417 { 418 .compatible = "renesas,r8a774a1-thermal", 419 .data = &rcar_m3w_thermal_info, 420 }, 421 { 422 .compatible = "renesas,r8a774b1-thermal", 423 .data = &rcar_gen3_thermal_info, 424 }, 425 { 426 .compatible = "renesas,r8a774e1-thermal", 427 .data = &rcar_gen3_thermal_info, 428 }, 429 { 430 .compatible = "renesas,r8a7795-thermal", 431 .data = &rcar_gen3_thermal_info, 432 }, 433 { 434 .compatible = "renesas,r8a7796-thermal", 435 .data = &rcar_m3w_thermal_info, 436 }, 437 { 438 .compatible = "renesas,r8a77961-thermal", 439 .data = &rcar_m3w_thermal_info, 440 }, 441 { 442 .compatible = "renesas,r8a77965-thermal", 443 .data = &rcar_gen3_thermal_info, 444 }, 445 { 446 .compatible = "renesas,r8a77980-thermal", 447 .data = &rcar_gen3_thermal_info, 448 }, 449 { 450 .compatible = "renesas,r8a779a0-thermal", 451 .data = &rcar_gen3_thermal_info, 452 }, 453 { 454 .compatible = "renesas,r8a779f0-thermal", 455 .data = &rcar_s4_thermal_info, 456 }, 457 { 458 .compatible = "renesas,r8a779g0-thermal", 459 .data = &rcar_gen4_thermal_info, 460 }, 461 { 462 .compatible = "renesas,r8a779h0-thermal", 463 .data = &rcar_gen4_thermal_info, 464 }, 465 {}, 466 }; 467 MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); 468 469 static void rcar_gen3_thermal_remove(struct platform_device *pdev) 470 { 471 struct device *dev = &pdev->dev; 472 473 pm_runtime_put(dev); 474 pm_runtime_disable(dev); 475 } 476 477 static void rcar_gen3_hwmon_action(void *data) 478 { 479 struct thermal_zone_device *zone = data; 480 481 thermal_remove_hwmon_sysfs(zone); 482 } 483 484 static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv, 485 struct platform_device *pdev) 486 { 487 struct device *dev = &pdev->dev; 488 unsigned int i; 489 char *irqname; 490 int ret, irq; 491 492 for (i = 0; i < 2; i++) { 493 irq = platform_get_irq_optional(pdev, i); 494 if (irq < 0) 495 return irq; 496 497 irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d", 498 dev_name(dev), i); 499 if (!irqname) 500 return -ENOMEM; 501 502 ret = devm_request_threaded_irq(dev, irq, NULL, 503 rcar_gen3_thermal_irq, 504 IRQF_ONESHOT, irqname, priv); 505 if (ret) 506 return ret; 507 } 508 509 return 0; 510 } 511 512 static int rcar_gen3_thermal_probe(struct platform_device *pdev) 513 { 514 struct rcar_gen3_thermal_priv *priv; 515 struct device *dev = &pdev->dev; 516 struct resource *res; 517 struct thermal_zone_device *zone; 518 unsigned int i; 519 int ret; 520 521 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 522 if (!priv) 523 return -ENOMEM; 524 525 priv->ops = rcar_gen3_tz_of_ops; 526 527 priv->info = of_device_get_match_data(dev); 528 platform_set_drvdata(pdev, priv); 529 530 if (rcar_gen3_thermal_request_irqs(priv, pdev)) 531 priv->ops.set_trips = NULL; 532 533 pm_runtime_enable(dev); 534 pm_runtime_get_sync(dev); 535 536 for (i = 0; i < TSC_MAX_NUM; i++) { 537 struct rcar_gen3_thermal_tsc *tsc; 538 539 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 540 if (!res) 541 break; 542 543 tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); 544 if (!tsc) { 545 ret = -ENOMEM; 546 goto error_unregister; 547 } 548 549 tsc->priv = priv; 550 tsc->base = devm_ioremap_resource(dev, res); 551 if (IS_ERR(tsc->base)) { 552 ret = PTR_ERR(tsc->base); 553 goto error_unregister; 554 } 555 556 priv->tscs[i] = tsc; 557 } 558 559 priv->num_tscs = i; 560 561 if (!rcar_gen3_thermal_read_fuses(priv)) 562 dev_info(dev, "No calibration values fused, fallback to driver values\n"); 563 564 rcar_gen3_thermal_shared_coefs(priv); 565 566 for (i = 0; i < priv->num_tscs; i++) { 567 struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; 568 569 rcar_gen3_thermal_init(priv, tsc); 570 rcar_gen3_thermal_tsc_coefs(priv, tsc); 571 572 zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops); 573 if (IS_ERR(zone)) { 574 dev_err(dev, "Sensor %u: Can't register thermal zone\n", i); 575 ret = PTR_ERR(zone); 576 goto error_unregister; 577 } 578 tsc->zone = zone; 579 580 ret = thermal_add_hwmon_sysfs(tsc->zone); 581 if (ret) 582 goto error_unregister; 583 584 ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone); 585 if (ret) 586 goto error_unregister; 587 588 dev_info(dev, "Sensor %u: Loaded\n", i); 589 } 590 591 if (!priv->num_tscs) { 592 ret = -ENODEV; 593 goto error_unregister; 594 } 595 596 return 0; 597 598 error_unregister: 599 rcar_gen3_thermal_remove(pdev); 600 601 return ret; 602 } 603 604 static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev) 605 { 606 struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); 607 unsigned int i; 608 609 for (i = 0; i < priv->num_tscs; i++) { 610 struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; 611 612 rcar_gen3_thermal_init(priv, tsc); 613 } 614 615 return 0; 616 } 617 618 static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL, 619 rcar_gen3_thermal_resume); 620 621 static struct platform_driver rcar_gen3_thermal_driver = { 622 .driver = { 623 .name = "rcar_gen3_thermal", 624 .pm = &rcar_gen3_thermal_pm_ops, 625 .of_match_table = rcar_gen3_thermal_dt_ids, 626 }, 627 .probe = rcar_gen3_thermal_probe, 628 .remove = rcar_gen3_thermal_remove, 629 }; 630 module_platform_driver(rcar_gen3_thermal_driver); 631 632 MODULE_LICENSE("GPL v2"); 633 MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver"); 634 MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>"); 635