1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * INT3400 thermal driver 4 * 5 * Copyright (C) 2014, Intel Corporation 6 * Authors: Zhang Rui <rui.zhang@intel.com> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/platform_device.h> 11 #include <linux/acpi.h> 12 #include <linux/thermal.h> 13 #include "acpi_thermal_rel.h" 14 15 #define INT3400_THERMAL_TABLE_CHANGED 0x83 16 #define INT3400_ODVP_CHANGED 0x88 17 #define INT3400_KEEP_ALIVE 0xA0 18 19 enum int3400_thermal_uuid { 20 INT3400_THERMAL_ACTIVE = 0, 21 INT3400_THERMAL_PASSIVE_1, 22 INT3400_THERMAL_CRITICAL, 23 INT3400_THERMAL_ADAPTIVE_PERFORMANCE, 24 INT3400_THERMAL_EMERGENCY_CALL_MODE, 25 INT3400_THERMAL_PASSIVE_2, 26 INT3400_THERMAL_POWER_BOSS, 27 INT3400_THERMAL_VIRTUAL_SENSOR, 28 INT3400_THERMAL_COOLING_MODE, 29 INT3400_THERMAL_HARDWARE_DUTY_CYCLING, 30 INT3400_THERMAL_MAXIMUM_UUID, 31 }; 32 33 static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { 34 "3A95C389-E4B8-4629-A526-C52C88626BAE", 35 "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 36 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", 37 "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D", 38 "5349962F-71E6-431D-9AE8-0A635B710AEE", 39 "9E04115A-AE87-4D1C-9500-0F3E340BFE75", 40 "F5A35014-C209-46A4-993A-EB56DE7530A1", 41 "6ED722A7-9240-48A5-B479-31EEF723D7CF", 42 "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531", 43 "BE84BABF-C4D4-403D-B495-3128FD44dAC1", 44 }; 45 46 struct odvp_attr; 47 48 struct int3400_thermal_priv { 49 struct acpi_device *adev; 50 struct platform_device *pdev; 51 struct thermal_zone_device *thermal; 52 int art_count; 53 struct art *arts; 54 int trt_count; 55 struct trt *trts; 56 u32 uuid_bitmap; 57 int rel_misc_dev_res; 58 int current_uuid_index; 59 char *data_vault; 60 int odvp_count; 61 int *odvp; 62 u32 os_uuid_mask; 63 int production_mode; 64 struct odvp_attr *odvp_attrs; 65 }; 66 67 static int evaluate_odvp(struct int3400_thermal_priv *priv); 68 69 struct odvp_attr { 70 int odvp; 71 struct int3400_thermal_priv *priv; 72 struct device_attribute attr; 73 }; 74 75 static ssize_t data_vault_read(struct file *file, struct kobject *kobj, 76 struct bin_attribute *attr, char *buf, loff_t off, size_t count) 77 { 78 memcpy(buf, attr->private + off, count); 79 return count; 80 } 81 82 static BIN_ATTR_RO(data_vault, 0); 83 84 static struct bin_attribute *data_attributes[] = { 85 &bin_attr_data_vault, 86 NULL, 87 }; 88 89 static ssize_t imok_store(struct device *dev, struct device_attribute *attr, 90 const char *buf, size_t count) 91 { 92 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 93 acpi_status status; 94 int input, ret; 95 96 ret = kstrtouint(buf, 10, &input); 97 if (ret) 98 return ret; 99 status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input); 100 if (ACPI_FAILURE(status)) 101 return -EIO; 102 103 return count; 104 } 105 106 static DEVICE_ATTR_WO(imok); 107 108 static struct attribute *imok_attr[] = { 109 &dev_attr_imok.attr, 110 NULL 111 }; 112 113 static const struct attribute_group imok_attribute_group = { 114 .attrs = imok_attr, 115 }; 116 117 static const struct attribute_group data_attribute_group = { 118 .bin_attrs = data_attributes, 119 }; 120 121 static ssize_t available_uuids_show(struct device *dev, 122 struct device_attribute *attr, 123 char *buf) 124 { 125 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 126 int i; 127 int length = 0; 128 129 if (!priv->uuid_bitmap) 130 return sprintf(buf, "UNKNOWN\n"); 131 132 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { 133 if (priv->uuid_bitmap & (1 << i)) 134 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]); 135 } 136 137 return length; 138 } 139 140 static ssize_t current_uuid_show(struct device *dev, 141 struct device_attribute *devattr, char *buf) 142 { 143 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 144 int i, length = 0; 145 146 if (priv->current_uuid_index > 0) 147 return sprintf(buf, "%s\n", 148 int3400_thermal_uuids[priv->current_uuid_index]); 149 150 for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) { 151 if (priv->os_uuid_mask & BIT(i)) 152 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]); 153 } 154 155 if (length) 156 return length; 157 158 return sprintf(buf, "INVALID\n"); 159 } 160 161 static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable) 162 { 163 u32 ret, buf[2]; 164 acpi_status status; 165 int result = 0; 166 struct acpi_osc_context context = { 167 .uuid_str = uuid_str, 168 .rev = 1, 169 .cap.length = 8, 170 .cap.pointer = buf, 171 }; 172 173 buf[OSC_QUERY_DWORD] = 0; 174 buf[OSC_SUPPORT_DWORD] = *enable; 175 176 status = acpi_run_osc(handle, &context); 177 if (ACPI_SUCCESS(status)) { 178 ret = *((u32 *)(context.ret.pointer + 4)); 179 if (ret != *enable) 180 result = -EPERM; 181 182 kfree(context.ret.pointer); 183 } else 184 result = -EPERM; 185 186 return result; 187 } 188 189 static int set_os_uuid_mask(struct int3400_thermal_priv *priv, u32 mask) 190 { 191 int cap = 0; 192 193 /* 194 * Capability bits: 195 * Bit 0: set to 1 to indicate DPTF is active 196 * Bi1 1: set to 1 to active cooling is supported by user space daemon 197 * Bit 2: set to 1 to passive cooling is supported by user space daemon 198 * Bit 3: set to 1 to critical trip is handled by user space daemon 199 */ 200 if (mask) 201 cap = (priv->os_uuid_mask << 1) | 0x01; 202 203 return int3400_thermal_run_osc(priv->adev->handle, 204 "b23ba85d-c8b7-3542-88de-8de2ffcfd698", 205 &cap); 206 } 207 208 static ssize_t current_uuid_store(struct device *dev, 209 struct device_attribute *attr, 210 const char *buf, size_t count) 211 { 212 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 213 int ret, i; 214 215 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { 216 if (!strncmp(buf, int3400_thermal_uuids[i], 217 sizeof(int3400_thermal_uuids[i]) - 1)) { 218 /* 219 * If we have a list of supported UUIDs, make sure 220 * this one is supported. 221 */ 222 if (priv->uuid_bitmap & BIT(i)) { 223 priv->current_uuid_index = i; 224 return count; 225 } 226 227 /* 228 * There is support of only 3 policies via the new 229 * _OSC to inform OS capability: 230 * INT3400_THERMAL_ACTIVE 231 * INT3400_THERMAL_PASSIVE_1 232 * INT3400_THERMAL_CRITICAL 233 */ 234 235 if (i > INT3400_THERMAL_CRITICAL) 236 return -EINVAL; 237 238 priv->os_uuid_mask |= BIT(i); 239 240 break; 241 } 242 } 243 244 if (priv->os_uuid_mask) { 245 ret = set_os_uuid_mask(priv, priv->os_uuid_mask); 246 if (ret) 247 return ret; 248 } 249 250 return count; 251 } 252 253 static DEVICE_ATTR_RW(current_uuid); 254 static DEVICE_ATTR_RO(available_uuids); 255 static struct attribute *uuid_attrs[] = { 256 &dev_attr_available_uuids.attr, 257 &dev_attr_current_uuid.attr, 258 NULL 259 }; 260 261 static const struct attribute_group uuid_attribute_group = { 262 .attrs = uuid_attrs, 263 .name = "uuids" 264 }; 265 266 static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 267 { 268 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 269 union acpi_object *obja, *objb; 270 int i, j; 271 int result = 0; 272 acpi_status status; 273 274 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 275 if (ACPI_FAILURE(status)) 276 return -ENODEV; 277 278 obja = (union acpi_object *)buf.pointer; 279 if (obja->type != ACPI_TYPE_PACKAGE) { 280 result = -EINVAL; 281 goto end; 282 } 283 284 for (i = 0; i < obja->package.count; i++) { 285 objb = &obja->package.elements[i]; 286 if (objb->type != ACPI_TYPE_BUFFER) { 287 result = -EINVAL; 288 goto end; 289 } 290 291 /* UUID must be 16 bytes */ 292 if (objb->buffer.length != 16) { 293 result = -EINVAL; 294 goto end; 295 } 296 297 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 298 guid_t guid; 299 300 guid_parse(int3400_thermal_uuids[j], &guid); 301 if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) { 302 priv->uuid_bitmap |= (1 << j); 303 break; 304 } 305 } 306 } 307 308 end: 309 kfree(buf.pointer); 310 return result; 311 } 312 313 static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr, 314 char *buf) 315 { 316 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 317 318 return sysfs_emit(buf, "%d\n", priv->production_mode); 319 } 320 321 static DEVICE_ATTR_RO(production_mode); 322 323 static int production_mode_init(struct int3400_thermal_priv *priv) 324 { 325 unsigned long long mode; 326 acpi_status status; 327 int ret; 328 329 priv->production_mode = -1; 330 331 status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode); 332 /* If the method is not present, this is not an error */ 333 if (ACPI_FAILURE(status)) 334 return 0; 335 336 ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr); 337 if (ret) 338 return ret; 339 340 priv->production_mode = mode; 341 342 return 0; 343 } 344 345 static void production_mode_exit(struct int3400_thermal_priv *priv) 346 { 347 if (priv->production_mode >= 0) 348 sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr); 349 } 350 351 static ssize_t odvp_show(struct device *dev, struct device_attribute *attr, 352 char *buf) 353 { 354 struct odvp_attr *odvp_attr; 355 356 odvp_attr = container_of(attr, struct odvp_attr, attr); 357 358 return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 359 } 360 361 static void cleanup_odvp(struct int3400_thermal_priv *priv) 362 { 363 int i; 364 365 if (priv->odvp_attrs) { 366 for (i = 0; i < priv->odvp_count; i++) { 367 sysfs_remove_file(&priv->pdev->dev.kobj, 368 &priv->odvp_attrs[i].attr.attr); 369 kfree(priv->odvp_attrs[i].attr.attr.name); 370 } 371 kfree(priv->odvp_attrs); 372 } 373 kfree(priv->odvp); 374 priv->odvp_count = 0; 375 } 376 377 static int evaluate_odvp(struct int3400_thermal_priv *priv) 378 { 379 struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 380 union acpi_object *obj = NULL; 381 acpi_status status; 382 int i, ret; 383 384 status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 385 if (ACPI_FAILURE(status)) { 386 ret = -EINVAL; 387 goto out_err; 388 } 389 390 obj = odvp.pointer; 391 if (obj->type != ACPI_TYPE_PACKAGE) { 392 ret = -EINVAL; 393 goto out_err; 394 } 395 396 if (priv->odvp == NULL) { 397 priv->odvp_count = obj->package.count; 398 priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 399 GFP_KERNEL); 400 if (!priv->odvp) { 401 ret = -ENOMEM; 402 goto out_err; 403 } 404 } 405 406 if (priv->odvp_attrs == NULL) { 407 priv->odvp_attrs = kcalloc(priv->odvp_count, 408 sizeof(struct odvp_attr), 409 GFP_KERNEL); 410 if (!priv->odvp_attrs) { 411 ret = -ENOMEM; 412 goto out_err; 413 } 414 for (i = 0; i < priv->odvp_count; i++) { 415 struct odvp_attr *odvp = &priv->odvp_attrs[i]; 416 417 sysfs_attr_init(&odvp->attr.attr); 418 odvp->priv = priv; 419 odvp->odvp = i; 420 odvp->attr.attr.name = kasprintf(GFP_KERNEL, 421 "odvp%d", i); 422 423 if (!odvp->attr.attr.name) { 424 ret = -ENOMEM; 425 goto out_err; 426 } 427 odvp->attr.attr.mode = 0444; 428 odvp->attr.show = odvp_show; 429 odvp->attr.store = NULL; 430 ret = sysfs_create_file(&priv->pdev->dev.kobj, 431 &odvp->attr.attr); 432 if (ret) 433 goto out_err; 434 } 435 } 436 437 for (i = 0; i < obj->package.count; i++) { 438 if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 439 priv->odvp[i] = obj->package.elements[i].integer.value; 440 } 441 442 kfree(obj); 443 return 0; 444 445 out_err: 446 cleanup_odvp(priv); 447 kfree(obj); 448 return ret; 449 } 450 451 static void int3400_notify(acpi_handle handle, 452 u32 event, 453 void *data) 454 { 455 struct int3400_thermal_priv *priv = data; 456 char *thermal_prop[5]; 457 int therm_event; 458 459 if (!priv) 460 return; 461 462 switch (event) { 463 case INT3400_THERMAL_TABLE_CHANGED: 464 therm_event = THERMAL_TABLE_CHANGED; 465 break; 466 case INT3400_KEEP_ALIVE: 467 therm_event = THERMAL_EVENT_KEEP_ALIVE; 468 break; 469 case INT3400_ODVP_CHANGED: 470 evaluate_odvp(priv); 471 therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED; 472 break; 473 default: 474 /* Ignore unknown notification codes sent to INT3400 device */ 475 return; 476 } 477 478 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", priv->thermal->type); 479 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", priv->thermal->temperature); 480 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 481 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event); 482 thermal_prop[4] = NULL; 483 kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, thermal_prop); 484 kfree(thermal_prop[0]); 485 kfree(thermal_prop[1]); 486 kfree(thermal_prop[2]); 487 kfree(thermal_prop[3]); 488 } 489 490 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 491 int *temp) 492 { 493 *temp = 20 * 1000; /* faked temp sensor with 20C */ 494 return 0; 495 } 496 497 static int int3400_thermal_change_mode(struct thermal_zone_device *thermal, 498 enum thermal_device_mode mode) 499 { 500 struct int3400_thermal_priv *priv = thermal_zone_device_priv(thermal); 501 int result = 0; 502 503 if (!priv) 504 return -EINVAL; 505 506 if (mode != thermal->mode) { 507 int enabled; 508 509 enabled = mode == THERMAL_DEVICE_ENABLED; 510 511 if (priv->os_uuid_mask) { 512 if (!enabled) { 513 priv->os_uuid_mask = 0; 514 result = set_os_uuid_mask(priv, priv->os_uuid_mask); 515 } 516 goto eval_odvp; 517 } 518 519 if (priv->current_uuid_index < 0 || 520 priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID) 521 return -EINVAL; 522 523 result = int3400_thermal_run_osc(priv->adev->handle, 524 int3400_thermal_uuids[priv->current_uuid_index], 525 &enabled); 526 } 527 528 eval_odvp: 529 evaluate_odvp(priv); 530 531 return result; 532 } 533 534 static struct thermal_zone_device_ops int3400_thermal_ops = { 535 .get_temp = int3400_thermal_get_temp, 536 .change_mode = int3400_thermal_change_mode, 537 }; 538 539 static struct thermal_zone_params int3400_thermal_params = { 540 .governor_name = "user_space", 541 .no_hwmon = true, 542 }; 543 544 static void int3400_setup_gddv(struct int3400_thermal_priv *priv) 545 { 546 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 547 union acpi_object *obj; 548 acpi_status status; 549 550 status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 551 &buffer); 552 if (ACPI_FAILURE(status) || !buffer.length) 553 return; 554 555 obj = buffer.pointer; 556 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 557 || obj->package.elements[0].type != ACPI_TYPE_BUFFER) 558 goto out_free; 559 560 priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 561 obj->package.elements[0].buffer.length, 562 GFP_KERNEL); 563 if (ZERO_OR_NULL_PTR(priv->data_vault)) 564 goto out_free; 565 566 bin_attr_data_vault.private = priv->data_vault; 567 bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 568 out_free: 569 kfree(buffer.pointer); 570 } 571 572 static int int3400_thermal_probe(struct platform_device *pdev) 573 { 574 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 575 struct int3400_thermal_priv *priv; 576 int result; 577 578 if (!adev) 579 return -ENODEV; 580 581 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 582 if (!priv) 583 return -ENOMEM; 584 585 priv->pdev = pdev; 586 priv->adev = adev; 587 588 result = int3400_thermal_get_uuids(priv); 589 590 /* Missing IDSP isn't fatal */ 591 if (result && result != -ENODEV) 592 goto free_priv; 593 594 priv->current_uuid_index = -1; 595 596 result = acpi_parse_art(priv->adev->handle, &priv->art_count, 597 &priv->arts, true); 598 if (result) 599 dev_dbg(&pdev->dev, "_ART table parsing error\n"); 600 601 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 602 &priv->trts, true); 603 if (result) 604 dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 605 606 platform_set_drvdata(pdev, priv); 607 608 int3400_setup_gddv(priv); 609 610 evaluate_odvp(priv); 611 612 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 613 priv, &int3400_thermal_ops, 614 &int3400_thermal_params, 0, 0); 615 if (IS_ERR(priv->thermal)) { 616 result = PTR_ERR(priv->thermal); 617 goto free_art_trt; 618 } 619 620 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 621 priv->adev->handle); 622 623 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 624 if (result) 625 goto free_rel_misc; 626 627 if (acpi_has_method(priv->adev->handle, "IMOK")) { 628 result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group); 629 if (result) 630 goto free_imok; 631 } 632 633 if (!ZERO_OR_NULL_PTR(priv->data_vault)) { 634 result = sysfs_create_group(&pdev->dev.kobj, 635 &data_attribute_group); 636 if (result) 637 goto free_uuid; 638 } 639 640 result = acpi_install_notify_handler( 641 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 642 (void *)priv); 643 if (result) 644 goto free_sysfs; 645 646 result = production_mode_init(priv); 647 if (result) 648 goto free_notify; 649 650 return 0; 651 652 free_notify: 653 acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, 654 int3400_notify); 655 free_sysfs: 656 cleanup_odvp(priv); 657 if (!ZERO_OR_NULL_PTR(priv->data_vault)) { 658 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 659 kfree(priv->data_vault); 660 } 661 free_uuid: 662 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 663 free_imok: 664 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 665 free_rel_misc: 666 if (!priv->rel_misc_dev_res) 667 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 668 thermal_zone_device_unregister(priv->thermal); 669 free_art_trt: 670 kfree(priv->trts); 671 kfree(priv->arts); 672 free_priv: 673 kfree(priv); 674 return result; 675 } 676 677 static int int3400_thermal_remove(struct platform_device *pdev) 678 { 679 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 680 681 production_mode_exit(priv); 682 683 acpi_remove_notify_handler( 684 priv->adev->handle, ACPI_DEVICE_NOTIFY, 685 int3400_notify); 686 687 cleanup_odvp(priv); 688 689 if (!priv->rel_misc_dev_res) 690 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 691 692 if (!ZERO_OR_NULL_PTR(priv->data_vault)) 693 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 694 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 695 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 696 thermal_zone_device_unregister(priv->thermal); 697 kfree(priv->data_vault); 698 kfree(priv->trts); 699 kfree(priv->arts); 700 kfree(priv); 701 return 0; 702 } 703 704 static const struct acpi_device_id int3400_thermal_match[] = { 705 {"INT3400", 0}, 706 {"INTC1040", 0}, 707 {"INTC1041", 0}, 708 {"INTC1042", 0}, 709 {"INTC10A0", 0}, 710 {} 711 }; 712 713 MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 714 715 static struct platform_driver int3400_thermal_driver = { 716 .probe = int3400_thermal_probe, 717 .remove = int3400_thermal_remove, 718 .driver = { 719 .name = "int3400 thermal", 720 .acpi_match_table = ACPI_PTR(int3400_thermal_match), 721 }, 722 }; 723 724 module_platform_driver(int3400_thermal_driver); 725 726 MODULE_DESCRIPTION("INT3400 Thermal driver"); 727 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 728 MODULE_LICENSE("GPL"); 729