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