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