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_PASSIVE_1, 21 INT3400_THERMAL_ACTIVE, 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 "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 35 "3A95C389-E4B8-4629-A526-C52C88626BAE", 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 u8 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 struct odvp_attr *odvp_attrs; 63 }; 64 65 static int evaluate_odvp(struct int3400_thermal_priv *priv); 66 67 struct odvp_attr { 68 int odvp; 69 struct int3400_thermal_priv *priv; 70 struct kobj_attribute attr; 71 }; 72 73 static ssize_t data_vault_read(struct file *file, struct kobject *kobj, 74 struct bin_attribute *attr, char *buf, loff_t off, size_t count) 75 { 76 memcpy(buf, attr->private + off, count); 77 return count; 78 } 79 80 static BIN_ATTR_RO(data_vault, 0); 81 82 static struct bin_attribute *data_attributes[] = { 83 &bin_attr_data_vault, 84 NULL, 85 }; 86 87 static ssize_t imok_store(struct device *dev, struct device_attribute *attr, 88 const char *buf, size_t count) 89 { 90 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 91 acpi_status status; 92 int input, ret; 93 94 ret = kstrtouint(buf, 10, &input); 95 if (ret) 96 return ret; 97 status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input); 98 if (ACPI_FAILURE(status)) 99 return -EIO; 100 101 return count; 102 } 103 104 static DEVICE_ATTR_WO(imok); 105 106 static struct attribute *imok_attr[] = { 107 &dev_attr_imok.attr, 108 NULL 109 }; 110 111 static const struct attribute_group imok_attribute_group = { 112 .attrs = imok_attr, 113 }; 114 115 static const struct attribute_group data_attribute_group = { 116 .bin_attrs = data_attributes, 117 }; 118 119 static ssize_t available_uuids_show(struct device *dev, 120 struct device_attribute *attr, 121 char *buf) 122 { 123 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 124 int i; 125 int length = 0; 126 127 if (!priv->uuid_bitmap) 128 return sprintf(buf, "UNKNOWN\n"); 129 130 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { 131 if (priv->uuid_bitmap & (1 << i)) 132 length += scnprintf(&buf[length], 133 PAGE_SIZE - length, 134 "%s\n", 135 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 146 if (priv->current_uuid_index == -1) 147 return sprintf(buf, "INVALID\n"); 148 149 return sprintf(buf, "%s\n", 150 int3400_thermal_uuids[priv->current_uuid_index]); 151 } 152 153 static ssize_t current_uuid_store(struct device *dev, 154 struct device_attribute *attr, 155 const char *buf, size_t count) 156 { 157 struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 158 int i; 159 160 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { 161 if (!strncmp(buf, int3400_thermal_uuids[i], 162 sizeof(int3400_thermal_uuids[i]) - 1)) { 163 /* 164 * If we have a list of supported UUIDs, make sure 165 * this one is supported. 166 */ 167 if (priv->uuid_bitmap && 168 !(priv->uuid_bitmap & (1 << i))) 169 return -EINVAL; 170 171 priv->current_uuid_index = i; 172 return count; 173 } 174 } 175 176 return -EINVAL; 177 } 178 179 static DEVICE_ATTR_RW(current_uuid); 180 static DEVICE_ATTR_RO(available_uuids); 181 static struct attribute *uuid_attrs[] = { 182 &dev_attr_available_uuids.attr, 183 &dev_attr_current_uuid.attr, 184 NULL 185 }; 186 187 static const struct attribute_group uuid_attribute_group = { 188 .attrs = uuid_attrs, 189 .name = "uuids" 190 }; 191 192 static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 193 { 194 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 195 union acpi_object *obja, *objb; 196 int i, j; 197 int result = 0; 198 acpi_status status; 199 200 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 201 if (ACPI_FAILURE(status)) 202 return -ENODEV; 203 204 obja = (union acpi_object *)buf.pointer; 205 if (obja->type != ACPI_TYPE_PACKAGE) { 206 result = -EINVAL; 207 goto end; 208 } 209 210 for (i = 0; i < obja->package.count; i++) { 211 objb = &obja->package.elements[i]; 212 if (objb->type != ACPI_TYPE_BUFFER) { 213 result = -EINVAL; 214 goto end; 215 } 216 217 /* UUID must be 16 bytes */ 218 if (objb->buffer.length != 16) { 219 result = -EINVAL; 220 goto end; 221 } 222 223 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 224 guid_t guid; 225 226 guid_parse(int3400_thermal_uuids[j], &guid); 227 if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) { 228 priv->uuid_bitmap |= (1 << j); 229 break; 230 } 231 } 232 } 233 234 end: 235 kfree(buf.pointer); 236 return result; 237 } 238 239 static int int3400_thermal_run_osc(acpi_handle handle, 240 enum int3400_thermal_uuid uuid, bool enable) 241 { 242 u32 ret, buf[2]; 243 acpi_status status; 244 int result = 0; 245 struct acpi_osc_context context = { 246 .uuid_str = NULL, 247 .rev = 1, 248 .cap.length = 8, 249 }; 250 251 if (uuid < 0 || uuid >= INT3400_THERMAL_MAXIMUM_UUID) 252 return -EINVAL; 253 254 context.uuid_str = int3400_thermal_uuids[uuid]; 255 256 buf[OSC_QUERY_DWORD] = 0; 257 buf[OSC_SUPPORT_DWORD] = enable; 258 259 context.cap.pointer = buf; 260 261 status = acpi_run_osc(handle, &context); 262 if (ACPI_SUCCESS(status)) { 263 ret = *((u32 *)(context.ret.pointer + 4)); 264 if (ret != enable) 265 result = -EPERM; 266 } else 267 result = -EPERM; 268 269 kfree(context.ret.pointer); 270 271 return result; 272 } 273 274 static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr, 275 char *buf) 276 { 277 struct odvp_attr *odvp_attr; 278 279 odvp_attr = container_of(attr, struct odvp_attr, attr); 280 281 return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 282 } 283 284 static void cleanup_odvp(struct int3400_thermal_priv *priv) 285 { 286 int i; 287 288 if (priv->odvp_attrs) { 289 for (i = 0; i < priv->odvp_count; i++) { 290 sysfs_remove_file(&priv->pdev->dev.kobj, 291 &priv->odvp_attrs[i].attr.attr); 292 kfree(priv->odvp_attrs[i].attr.attr.name); 293 } 294 kfree(priv->odvp_attrs); 295 } 296 kfree(priv->odvp); 297 priv->odvp_count = 0; 298 } 299 300 static int evaluate_odvp(struct int3400_thermal_priv *priv) 301 { 302 struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 303 union acpi_object *obj = NULL; 304 acpi_status status; 305 int i, ret; 306 307 status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 308 if (ACPI_FAILURE(status)) { 309 ret = -EINVAL; 310 goto out_err; 311 } 312 313 obj = odvp.pointer; 314 if (obj->type != ACPI_TYPE_PACKAGE) { 315 ret = -EINVAL; 316 goto out_err; 317 } 318 319 if (priv->odvp == NULL) { 320 priv->odvp_count = obj->package.count; 321 priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 322 GFP_KERNEL); 323 if (!priv->odvp) { 324 ret = -ENOMEM; 325 goto out_err; 326 } 327 } 328 329 if (priv->odvp_attrs == NULL) { 330 priv->odvp_attrs = kcalloc(priv->odvp_count, 331 sizeof(struct odvp_attr), 332 GFP_KERNEL); 333 if (!priv->odvp_attrs) { 334 ret = -ENOMEM; 335 goto out_err; 336 } 337 for (i = 0; i < priv->odvp_count; i++) { 338 struct odvp_attr *odvp = &priv->odvp_attrs[i]; 339 340 sysfs_attr_init(&odvp->attr.attr); 341 odvp->priv = priv; 342 odvp->odvp = i; 343 odvp->attr.attr.name = kasprintf(GFP_KERNEL, 344 "odvp%d", i); 345 346 if (!odvp->attr.attr.name) { 347 ret = -ENOMEM; 348 goto out_err; 349 } 350 odvp->attr.attr.mode = 0444; 351 odvp->attr.show = odvp_show; 352 odvp->attr.store = NULL; 353 ret = sysfs_create_file(&priv->pdev->dev.kobj, 354 &odvp->attr.attr); 355 if (ret) 356 goto out_err; 357 } 358 } 359 360 for (i = 0; i < obj->package.count; i++) { 361 if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 362 priv->odvp[i] = obj->package.elements[i].integer.value; 363 } 364 365 kfree(obj); 366 return 0; 367 368 out_err: 369 cleanup_odvp(priv); 370 kfree(obj); 371 return ret; 372 } 373 374 static void int3400_notify(acpi_handle handle, 375 u32 event, 376 void *data) 377 { 378 struct int3400_thermal_priv *priv = data; 379 char *thermal_prop[5]; 380 int therm_event; 381 382 if (!priv) 383 return; 384 385 switch (event) { 386 case INT3400_THERMAL_TABLE_CHANGED: 387 therm_event = THERMAL_TABLE_CHANGED; 388 break; 389 case INT3400_KEEP_ALIVE: 390 therm_event = THERMAL_EVENT_KEEP_ALIVE; 391 break; 392 case INT3400_ODVP_CHANGED: 393 evaluate_odvp(priv); 394 therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED; 395 break; 396 default: 397 /* Ignore unknown notification codes sent to INT3400 device */ 398 return; 399 } 400 401 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", priv->thermal->type); 402 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", priv->thermal->temperature); 403 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 404 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event); 405 thermal_prop[4] = NULL; 406 kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, thermal_prop); 407 kfree(thermal_prop[0]); 408 kfree(thermal_prop[1]); 409 kfree(thermal_prop[2]); 410 kfree(thermal_prop[3]); 411 } 412 413 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 414 int *temp) 415 { 416 *temp = 20 * 1000; /* faked temp sensor with 20C */ 417 return 0; 418 } 419 420 static int int3400_thermal_change_mode(struct thermal_zone_device *thermal, 421 enum thermal_device_mode mode) 422 { 423 struct int3400_thermal_priv *priv = thermal->devdata; 424 int result = 0; 425 426 if (!priv) 427 return -EINVAL; 428 429 if (mode != thermal->mode) 430 result = int3400_thermal_run_osc(priv->adev->handle, 431 priv->current_uuid_index, 432 mode == THERMAL_DEVICE_ENABLED); 433 434 435 evaluate_odvp(priv); 436 437 return result; 438 } 439 440 static struct thermal_zone_device_ops int3400_thermal_ops = { 441 .get_temp = int3400_thermal_get_temp, 442 .change_mode = int3400_thermal_change_mode, 443 }; 444 445 static struct thermal_zone_params int3400_thermal_params = { 446 .governor_name = "user_space", 447 .no_hwmon = true, 448 }; 449 450 static void int3400_setup_gddv(struct int3400_thermal_priv *priv) 451 { 452 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 453 union acpi_object *obj; 454 acpi_status status; 455 456 status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 457 &buffer); 458 if (ACPI_FAILURE(status) || !buffer.length) 459 return; 460 461 obj = buffer.pointer; 462 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 463 || obj->package.elements[0].type != ACPI_TYPE_BUFFER) { 464 kfree(buffer.pointer); 465 return; 466 } 467 468 priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 469 obj->package.elements[0].buffer.length, 470 GFP_KERNEL); 471 bin_attr_data_vault.private = priv->data_vault; 472 bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 473 kfree(buffer.pointer); 474 } 475 476 static int int3400_thermal_probe(struct platform_device *pdev) 477 { 478 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 479 struct int3400_thermal_priv *priv; 480 int result; 481 482 if (!adev) 483 return -ENODEV; 484 485 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 486 if (!priv) 487 return -ENOMEM; 488 489 priv->pdev = pdev; 490 priv->adev = adev; 491 492 result = int3400_thermal_get_uuids(priv); 493 494 /* Missing IDSP isn't fatal */ 495 if (result && result != -ENODEV) 496 goto free_priv; 497 498 priv->current_uuid_index = -1; 499 500 result = acpi_parse_art(priv->adev->handle, &priv->art_count, 501 &priv->arts, true); 502 if (result) 503 dev_dbg(&pdev->dev, "_ART table parsing error\n"); 504 505 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 506 &priv->trts, true); 507 if (result) 508 dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 509 510 platform_set_drvdata(pdev, priv); 511 512 int3400_setup_gddv(priv); 513 514 evaluate_odvp(priv); 515 516 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 517 priv, &int3400_thermal_ops, 518 &int3400_thermal_params, 0, 0); 519 if (IS_ERR(priv->thermal)) { 520 result = PTR_ERR(priv->thermal); 521 goto free_art_trt; 522 } 523 524 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 525 priv->adev->handle); 526 527 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 528 if (result) 529 goto free_rel_misc; 530 531 if (acpi_has_method(priv->adev->handle, "IMOK")) { 532 result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group); 533 if (result) 534 goto free_imok; 535 } 536 537 if (priv->data_vault) { 538 result = sysfs_create_group(&pdev->dev.kobj, 539 &data_attribute_group); 540 if (result) 541 goto free_uuid; 542 } 543 544 result = acpi_install_notify_handler( 545 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 546 (void *)priv); 547 if (result) 548 goto free_sysfs; 549 550 return 0; 551 552 free_sysfs: 553 cleanup_odvp(priv); 554 if (priv->data_vault) { 555 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 556 kfree(priv->data_vault); 557 } 558 free_uuid: 559 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 560 free_imok: 561 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 562 free_rel_misc: 563 if (!priv->rel_misc_dev_res) 564 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 565 thermal_zone_device_unregister(priv->thermal); 566 free_art_trt: 567 kfree(priv->trts); 568 kfree(priv->arts); 569 free_priv: 570 kfree(priv); 571 return result; 572 } 573 574 static int int3400_thermal_remove(struct platform_device *pdev) 575 { 576 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 577 578 acpi_remove_notify_handler( 579 priv->adev->handle, ACPI_DEVICE_NOTIFY, 580 int3400_notify); 581 582 cleanup_odvp(priv); 583 584 if (!priv->rel_misc_dev_res) 585 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 586 587 if (priv->data_vault) 588 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 589 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 590 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 591 thermal_zone_device_unregister(priv->thermal); 592 kfree(priv->data_vault); 593 kfree(priv->trts); 594 kfree(priv->arts); 595 kfree(priv); 596 return 0; 597 } 598 599 static const struct acpi_device_id int3400_thermal_match[] = { 600 {"INT3400", 0}, 601 {"INTC1040", 0}, 602 {"INTC1041", 0}, 603 {"INTC10A0", 0}, 604 {} 605 }; 606 607 MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 608 609 static struct platform_driver int3400_thermal_driver = { 610 .probe = int3400_thermal_probe, 611 .remove = int3400_thermal_remove, 612 .driver = { 613 .name = "int3400 thermal", 614 .acpi_match_table = ACPI_PTR(int3400_thermal_match), 615 }, 616 }; 617 618 module_platform_driver(int3400_thermal_driver); 619 620 MODULE_DESCRIPTION("INT3400 Thermal driver"); 621 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 622 MODULE_LICENSE("GPL"); 623