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 } 408 409 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 410 int *temp) 411 { 412 *temp = 20 * 1000; /* faked temp sensor with 20C */ 413 return 0; 414 } 415 416 static int int3400_thermal_change_mode(struct thermal_zone_device *thermal, 417 enum thermal_device_mode mode) 418 { 419 struct int3400_thermal_priv *priv = thermal->devdata; 420 int result = 0; 421 422 if (!priv) 423 return -EINVAL; 424 425 if (mode != thermal->mode) 426 result = int3400_thermal_run_osc(priv->adev->handle, 427 priv->current_uuid_index, 428 mode == THERMAL_DEVICE_ENABLED); 429 430 431 evaluate_odvp(priv); 432 433 return result; 434 } 435 436 static struct thermal_zone_device_ops int3400_thermal_ops = { 437 .get_temp = int3400_thermal_get_temp, 438 .change_mode = int3400_thermal_change_mode, 439 }; 440 441 static struct thermal_zone_params int3400_thermal_params = { 442 .governor_name = "user_space", 443 .no_hwmon = true, 444 }; 445 446 static void int3400_setup_gddv(struct int3400_thermal_priv *priv) 447 { 448 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 449 union acpi_object *obj; 450 acpi_status status; 451 452 status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 453 &buffer); 454 if (ACPI_FAILURE(status) || !buffer.length) 455 return; 456 457 obj = buffer.pointer; 458 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 459 || obj->package.elements[0].type != ACPI_TYPE_BUFFER) { 460 kfree(buffer.pointer); 461 return; 462 } 463 464 priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 465 obj->package.elements[0].buffer.length, 466 GFP_KERNEL); 467 bin_attr_data_vault.private = priv->data_vault; 468 bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 469 kfree(buffer.pointer); 470 } 471 472 static int int3400_thermal_probe(struct platform_device *pdev) 473 { 474 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 475 struct int3400_thermal_priv *priv; 476 int result; 477 478 if (!adev) 479 return -ENODEV; 480 481 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 482 if (!priv) 483 return -ENOMEM; 484 485 priv->pdev = pdev; 486 priv->adev = adev; 487 488 result = int3400_thermal_get_uuids(priv); 489 490 /* Missing IDSP isn't fatal */ 491 if (result && result != -ENODEV) 492 goto free_priv; 493 494 priv->current_uuid_index = -1; 495 496 result = acpi_parse_art(priv->adev->handle, &priv->art_count, 497 &priv->arts, true); 498 if (result) 499 dev_dbg(&pdev->dev, "_ART table parsing error\n"); 500 501 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 502 &priv->trts, true); 503 if (result) 504 dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 505 506 platform_set_drvdata(pdev, priv); 507 508 int3400_setup_gddv(priv); 509 510 evaluate_odvp(priv); 511 512 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 513 priv, &int3400_thermal_ops, 514 &int3400_thermal_params, 0, 0); 515 if (IS_ERR(priv->thermal)) { 516 result = PTR_ERR(priv->thermal); 517 goto free_art_trt; 518 } 519 520 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 521 priv->adev->handle); 522 523 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 524 if (result) 525 goto free_rel_misc; 526 527 if (acpi_has_method(priv->adev->handle, "IMOK")) { 528 result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group); 529 if (result) 530 goto free_imok; 531 } 532 533 if (priv->data_vault) { 534 result = sysfs_create_group(&pdev->dev.kobj, 535 &data_attribute_group); 536 if (result) 537 goto free_uuid; 538 } 539 540 result = acpi_install_notify_handler( 541 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 542 (void *)priv); 543 if (result) 544 goto free_sysfs; 545 546 return 0; 547 548 free_sysfs: 549 cleanup_odvp(priv); 550 if (priv->data_vault) { 551 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 552 kfree(priv->data_vault); 553 } 554 free_uuid: 555 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 556 free_imok: 557 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 558 free_rel_misc: 559 if (!priv->rel_misc_dev_res) 560 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 561 thermal_zone_device_unregister(priv->thermal); 562 free_art_trt: 563 kfree(priv->trts); 564 kfree(priv->arts); 565 free_priv: 566 kfree(priv); 567 return result; 568 } 569 570 static int int3400_thermal_remove(struct platform_device *pdev) 571 { 572 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 573 574 acpi_remove_notify_handler( 575 priv->adev->handle, ACPI_DEVICE_NOTIFY, 576 int3400_notify); 577 578 cleanup_odvp(priv); 579 580 if (!priv->rel_misc_dev_res) 581 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 582 583 if (priv->data_vault) 584 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 585 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 586 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 587 thermal_zone_device_unregister(priv->thermal); 588 kfree(priv->data_vault); 589 kfree(priv->trts); 590 kfree(priv->arts); 591 kfree(priv); 592 return 0; 593 } 594 595 static const struct acpi_device_id int3400_thermal_match[] = { 596 {"INT3400", 0}, 597 {"INTC1040", 0}, 598 {"INTC1041", 0}, 599 {"INTC10A0", 0}, 600 {} 601 }; 602 603 MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 604 605 static struct platform_driver int3400_thermal_driver = { 606 .probe = int3400_thermal_probe, 607 .remove = int3400_thermal_remove, 608 .driver = { 609 .name = "int3400 thermal", 610 .acpi_match_table = ACPI_PTR(int3400_thermal_match), 611 }, 612 }; 613 614 module_platform_driver(int3400_thermal_driver); 615 616 MODULE_DESCRIPTION("INT3400 Thermal driver"); 617 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 618 MODULE_LICENSE("GPL"); 619