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