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 #define INT3400_FAKE_TEMP (20 * 1000) /* faked temp sensor with 20C */ 19 20 enum int3400_thermal_uuid { 21 INT3400_THERMAL_ACTIVE = 0, 22 INT3400_THERMAL_PASSIVE_1, 23 INT3400_THERMAL_CRITICAL, 24 INT3400_THERMAL_ADAPTIVE_PERFORMANCE, 25 INT3400_THERMAL_EMERGENCY_CALL_MODE, 26 INT3400_THERMAL_PASSIVE_2, 27 INT3400_THERMAL_POWER_BOSS, 28 INT3400_THERMAL_VIRTUAL_SENSOR, 29 INT3400_THERMAL_COOLING_MODE, 30 INT3400_THERMAL_HARDWARE_DUTY_CYCLING, 31 INT3400_THERMAL_MAXIMUM_UUID, 32 }; 33 34 static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { 35 "3A95C389-E4B8-4629-A526-C52C88626BAE", 36 "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 37 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", 38 "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D", 39 "5349962F-71E6-431D-9AE8-0A635B710AEE", 40 "9E04115A-AE87-4D1C-9500-0F3E340BFE75", 41 "F5A35014-C209-46A4-993A-EB56DE7530A1", 42 "6ED722A7-9240-48A5-B479-31EEF723D7CF", 43 "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531", 44 "BE84BABF-C4D4-403D-B495-3128FD44dAC1", 45 }; 46 47 struct odvp_attr; 48 49 struct int3400_thermal_priv { 50 struct acpi_device *adev; 51 struct platform_device *pdev; 52 struct thermal_zone_device *thermal; 53 int art_count; 54 struct art *arts; 55 int trt_count; 56 struct trt *trts; 57 u32 uuid_bitmap; 58 int rel_misc_dev_res; 59 int current_uuid_index; 60 char *data_vault; 61 int odvp_count; 62 int *odvp; 63 u32 os_uuid_mask; 64 int production_mode; 65 struct odvp_attr *odvp_attrs; 66 }; 67 68 static int evaluate_odvp(struct int3400_thermal_priv *priv); 69 70 struct odvp_attr { 71 int odvp; 72 struct int3400_thermal_priv *priv; 73 struct device_attribute attr; 74 }; 75 76 static ssize_t data_vault_read(struct file *file, struct kobject *kobj, 77 struct bin_attribute *attr, char *buf, loff_t off, size_t count) 78 { 79 memcpy(buf, attr->private + off, count); 80 return count; 81 } 82 83 static BIN_ATTR_RO(data_vault, 0); 84 85 static struct bin_attribute *data_attributes[] = { 86 &bin_attr_data_vault, 87 NULL, 88 }; 89 90 static ssize_t imok_store(struct device *dev, struct device_attribute *attr, 91 const char *buf, size_t count) 92 { 93 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 94 acpi_status status; 95 int input, ret; 96 97 ret = kstrtouint(buf, 10, &input); 98 if (ret) 99 return ret; 100 status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input); 101 if (ACPI_FAILURE(status)) 102 return -EIO; 103 104 return count; 105 } 106 107 static DEVICE_ATTR_WO(imok); 108 109 static struct attribute *imok_attr[] = { 110 &dev_attr_imok.attr, 111 NULL 112 }; 113 114 static const struct attribute_group imok_attribute_group = { 115 .attrs = imok_attr, 116 }; 117 118 static const struct attribute_group data_attribute_group = { 119 .bin_attrs = data_attributes, 120 }; 121 122 static ssize_t available_uuids_show(struct device *dev, 123 struct device_attribute *attr, 124 char *buf) 125 { 126 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 127 int i; 128 int length = 0; 129 130 if (!priv->uuid_bitmap) 131 return sprintf(buf, "UNKNOWN\n"); 132 133 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { 134 if (priv->uuid_bitmap & (1 << i)) 135 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]); 136 } 137 138 return length; 139 } 140 141 static ssize_t current_uuid_show(struct device *dev, 142 struct device_attribute *devattr, char *buf) 143 { 144 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 145 int i, length = 0; 146 147 if (priv->current_uuid_index > 0) 148 return sprintf(buf, "%s\n", 149 int3400_thermal_uuids[priv->current_uuid_index]); 150 151 for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) { 152 if (priv->os_uuid_mask & BIT(i)) 153 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]); 154 } 155 156 if (length) 157 return length; 158 159 return sprintf(buf, "INVALID\n"); 160 } 161 162 static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable) 163 { 164 u32 ret, buf[2]; 165 acpi_status status; 166 int result = 0; 167 struct acpi_osc_context context = { 168 .uuid_str = uuid_str, 169 .rev = 1, 170 .cap.length = 8, 171 .cap.pointer = buf, 172 }; 173 174 buf[OSC_QUERY_DWORD] = 0; 175 buf[OSC_SUPPORT_DWORD] = *enable; 176 177 status = acpi_run_osc(handle, &context); 178 if (ACPI_SUCCESS(status)) { 179 ret = *((u32 *)(context.ret.pointer + 4)); 180 if (ret != *enable) 181 result = -EPERM; 182 183 kfree(context.ret.pointer); 184 } else 185 result = -EPERM; 186 187 return result; 188 } 189 190 static int set_os_uuid_mask(struct int3400_thermal_priv *priv, u32 mask) 191 { 192 int cap = 0; 193 194 /* 195 * Capability bits: 196 * Bit 0: set to 1 to indicate DPTF is active 197 * Bi1 1: set to 1 to active cooling is supported by user space daemon 198 * Bit 2: set to 1 to passive cooling is supported by user space daemon 199 * Bit 3: set to 1 to critical trip is handled by user space daemon 200 */ 201 if (mask) 202 cap = (priv->os_uuid_mask << 1) | 0x01; 203 204 return int3400_thermal_run_osc(priv->adev->handle, 205 "b23ba85d-c8b7-3542-88de-8de2ffcfd698", 206 &cap); 207 } 208 209 static ssize_t current_uuid_store(struct device *dev, 210 struct device_attribute *attr, 211 const char *buf, size_t count) 212 { 213 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 214 int ret, i; 215 216 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { 217 if (!strncmp(buf, int3400_thermal_uuids[i], 218 sizeof(int3400_thermal_uuids[i]) - 1)) { 219 /* 220 * If we have a list of supported UUIDs, make sure 221 * this one is supported. 222 */ 223 if (priv->uuid_bitmap & BIT(i)) { 224 priv->current_uuid_index = i; 225 return count; 226 } 227 228 /* 229 * There is support of only 3 policies via the new 230 * _OSC to inform OS capability: 231 * INT3400_THERMAL_ACTIVE 232 * INT3400_THERMAL_PASSIVE_1 233 * INT3400_THERMAL_CRITICAL 234 */ 235 236 if (i > INT3400_THERMAL_CRITICAL) 237 return -EINVAL; 238 239 priv->os_uuid_mask |= BIT(i); 240 241 break; 242 } 243 } 244 245 if (priv->os_uuid_mask) { 246 ret = set_os_uuid_mask(priv, priv->os_uuid_mask); 247 if (ret) 248 return ret; 249 } 250 251 return count; 252 } 253 254 static DEVICE_ATTR_RW(current_uuid); 255 static DEVICE_ATTR_RO(available_uuids); 256 static struct attribute *uuid_attrs[] = { 257 &dev_attr_available_uuids.attr, 258 &dev_attr_current_uuid.attr, 259 NULL 260 }; 261 262 static const struct attribute_group uuid_attribute_group = { 263 .attrs = uuid_attrs, 264 .name = "uuids" 265 }; 266 267 static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 268 { 269 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 270 union acpi_object *obja, *objb; 271 int i, j; 272 int result = 0; 273 acpi_status status; 274 275 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 276 if (ACPI_FAILURE(status)) 277 return -ENODEV; 278 279 obja = (union acpi_object *)buf.pointer; 280 if (obja->type != ACPI_TYPE_PACKAGE) { 281 result = -EINVAL; 282 goto end; 283 } 284 285 for (i = 0; i < obja->package.count; i++) { 286 objb = &obja->package.elements[i]; 287 if (objb->type != ACPI_TYPE_BUFFER) { 288 result = -EINVAL; 289 goto end; 290 } 291 292 /* UUID must be 16 bytes */ 293 if (objb->buffer.length != 16) { 294 result = -EINVAL; 295 goto end; 296 } 297 298 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 299 guid_t guid; 300 301 guid_parse(int3400_thermal_uuids[j], &guid); 302 if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) { 303 priv->uuid_bitmap |= (1 << j); 304 break; 305 } 306 } 307 } 308 309 end: 310 kfree(buf.pointer); 311 return result; 312 } 313 314 static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr, 315 char *buf) 316 { 317 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 318 319 return sysfs_emit(buf, "%d\n", priv->production_mode); 320 } 321 322 static DEVICE_ATTR_RO(production_mode); 323 324 static int production_mode_init(struct int3400_thermal_priv *priv) 325 { 326 unsigned long long mode; 327 acpi_status status; 328 int ret; 329 330 priv->production_mode = -1; 331 332 status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode); 333 /* If the method is not present, this is not an error */ 334 if (ACPI_FAILURE(status)) 335 return 0; 336 337 ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr); 338 if (ret) 339 return ret; 340 341 priv->production_mode = mode; 342 343 return 0; 344 } 345 346 static void production_mode_exit(struct int3400_thermal_priv *priv) 347 { 348 if (priv->production_mode >= 0) 349 sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr); 350 } 351 352 static ssize_t odvp_show(struct device *dev, struct device_attribute *attr, 353 char *buf) 354 { 355 struct odvp_attr *odvp_attr; 356 357 odvp_attr = container_of(attr, struct odvp_attr, attr); 358 359 return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 360 } 361 362 static void cleanup_odvp(struct int3400_thermal_priv *priv) 363 { 364 int i; 365 366 if (priv->odvp_attrs) { 367 for (i = 0; i < priv->odvp_count; i++) { 368 sysfs_remove_file(&priv->pdev->dev.kobj, 369 &priv->odvp_attrs[i].attr.attr); 370 kfree(priv->odvp_attrs[i].attr.attr.name); 371 } 372 kfree(priv->odvp_attrs); 373 } 374 kfree(priv->odvp); 375 priv->odvp_count = 0; 376 } 377 378 static int evaluate_odvp(struct int3400_thermal_priv *priv) 379 { 380 struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 381 union acpi_object *obj = NULL; 382 acpi_status status; 383 int i, ret; 384 385 status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 386 if (ACPI_FAILURE(status)) { 387 ret = -EINVAL; 388 goto out_err; 389 } 390 391 obj = odvp.pointer; 392 if (obj->type != ACPI_TYPE_PACKAGE) { 393 ret = -EINVAL; 394 goto out_err; 395 } 396 397 if (priv->odvp == NULL) { 398 priv->odvp_count = obj->package.count; 399 priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 400 GFP_KERNEL); 401 if (!priv->odvp) { 402 ret = -ENOMEM; 403 goto out_err; 404 } 405 } 406 407 if (priv->odvp_attrs == NULL) { 408 priv->odvp_attrs = kcalloc(priv->odvp_count, 409 sizeof(struct odvp_attr), 410 GFP_KERNEL); 411 if (!priv->odvp_attrs) { 412 ret = -ENOMEM; 413 goto out_err; 414 } 415 for (i = 0; i < priv->odvp_count; i++) { 416 struct odvp_attr *odvp = &priv->odvp_attrs[i]; 417 418 sysfs_attr_init(&odvp->attr.attr); 419 odvp->priv = priv; 420 odvp->odvp = i; 421 odvp->attr.attr.name = kasprintf(GFP_KERNEL, 422 "odvp%d", i); 423 424 if (!odvp->attr.attr.name) { 425 ret = -ENOMEM; 426 goto out_err; 427 } 428 odvp->attr.attr.mode = 0444; 429 odvp->attr.show = odvp_show; 430 odvp->attr.store = NULL; 431 ret = sysfs_create_file(&priv->pdev->dev.kobj, 432 &odvp->attr.attr); 433 if (ret) 434 goto out_err; 435 } 436 } 437 438 for (i = 0; i < obj->package.count; i++) { 439 if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 440 priv->odvp[i] = obj->package.elements[i].integer.value; 441 } 442 443 kfree(obj); 444 return 0; 445 446 out_err: 447 cleanup_odvp(priv); 448 kfree(obj); 449 return ret; 450 } 451 452 static void int3400_notify(acpi_handle handle, 453 u32 event, 454 void *data) 455 { 456 struct int3400_thermal_priv *priv = data; 457 struct device *dev; 458 char *thermal_prop[5]; 459 int therm_event; 460 461 if (!priv) 462 return; 463 464 switch (event) { 465 case INT3400_THERMAL_TABLE_CHANGED: 466 therm_event = THERMAL_TABLE_CHANGED; 467 break; 468 case INT3400_KEEP_ALIVE: 469 therm_event = THERMAL_EVENT_KEEP_ALIVE; 470 break; 471 case INT3400_ODVP_CHANGED: 472 evaluate_odvp(priv); 473 therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED; 474 break; 475 default: 476 /* Ignore unknown notification codes sent to INT3400 device */ 477 return; 478 } 479 480 dev = thermal_zone_device(priv->thermal); 481 482 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", thermal_zone_device_type(priv->thermal)); 483 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", INT3400_FAKE_TEMP); 484 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 485 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event); 486 thermal_prop[4] = NULL; 487 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, thermal_prop); 488 kfree(thermal_prop[0]); 489 kfree(thermal_prop[1]); 490 kfree(thermal_prop[2]); 491 kfree(thermal_prop[3]); 492 } 493 494 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 495 int *temp) 496 { 497 *temp = INT3400_FAKE_TEMP; 498 return 0; 499 } 500 501 static int int3400_thermal_change_mode(struct thermal_zone_device *thermal, 502 enum thermal_device_mode mode) 503 { 504 struct int3400_thermal_priv *priv = thermal_zone_device_priv(thermal); 505 int result = 0; 506 int enabled; 507 508 if (!priv) 509 return -EINVAL; 510 511 enabled = mode == THERMAL_DEVICE_ENABLED; 512 513 if (priv->os_uuid_mask) { 514 if (!enabled) { 515 priv->os_uuid_mask = 0; 516 result = set_os_uuid_mask(priv, priv->os_uuid_mask); 517 } 518 goto eval_odvp; 519 } 520 521 if (priv->current_uuid_index < 0 || 522 priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID) 523 return -EINVAL; 524 525 result = int3400_thermal_run_osc(priv->adev->handle, 526 int3400_thermal_uuids[priv->current_uuid_index], 527 &enabled); 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_tripless_zone_device_register("INT3400 Thermal", priv, 613 &int3400_thermal_ops, 614 &int3400_thermal_params); 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