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 = int3400_thermal_uuids[uuid], 220 .rev = 1, 221 .cap.length = 8, 222 }; 223 224 buf[OSC_QUERY_DWORD] = 0; 225 buf[OSC_SUPPORT_DWORD] = enable; 226 227 context.cap.pointer = buf; 228 229 status = acpi_run_osc(handle, &context); 230 if (ACPI_SUCCESS(status)) { 231 ret = *((u32 *)(context.ret.pointer + 4)); 232 if (ret != enable) 233 result = -EPERM; 234 } else 235 result = -EPERM; 236 237 kfree(context.ret.pointer); 238 239 return result; 240 } 241 242 static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr, 243 char *buf) 244 { 245 struct odvp_attr *odvp_attr; 246 247 odvp_attr = container_of(attr, struct odvp_attr, attr); 248 249 return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 250 } 251 252 static void cleanup_odvp(struct int3400_thermal_priv *priv) 253 { 254 int i; 255 256 if (priv->odvp_attrs) { 257 for (i = 0; i < priv->odvp_count; i++) { 258 sysfs_remove_file(&priv->pdev->dev.kobj, 259 &priv->odvp_attrs[i].attr.attr); 260 kfree(priv->odvp_attrs[i].attr.attr.name); 261 } 262 kfree(priv->odvp_attrs); 263 } 264 kfree(priv->odvp); 265 priv->odvp_count = 0; 266 } 267 268 static int evaluate_odvp(struct int3400_thermal_priv *priv) 269 { 270 struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 271 union acpi_object *obj = NULL; 272 acpi_status status; 273 int i, ret; 274 275 status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 276 if (ACPI_FAILURE(status)) { 277 ret = -EINVAL; 278 goto out_err; 279 } 280 281 obj = odvp.pointer; 282 if (obj->type != ACPI_TYPE_PACKAGE) { 283 ret = -EINVAL; 284 goto out_err; 285 } 286 287 if (priv->odvp == NULL) { 288 priv->odvp_count = obj->package.count; 289 priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 290 GFP_KERNEL); 291 if (!priv->odvp) { 292 ret = -ENOMEM; 293 goto out_err; 294 } 295 } 296 297 if (priv->odvp_attrs == NULL) { 298 priv->odvp_attrs = kcalloc(priv->odvp_count, 299 sizeof(struct odvp_attr), 300 GFP_KERNEL); 301 if (!priv->odvp_attrs) { 302 ret = -ENOMEM; 303 goto out_err; 304 } 305 for (i = 0; i < priv->odvp_count; i++) { 306 struct odvp_attr *odvp = &priv->odvp_attrs[i]; 307 308 sysfs_attr_init(&odvp->attr.attr); 309 odvp->priv = priv; 310 odvp->odvp = i; 311 odvp->attr.attr.name = kasprintf(GFP_KERNEL, 312 "odvp%d", i); 313 314 if (!odvp->attr.attr.name) { 315 ret = -ENOMEM; 316 goto out_err; 317 } 318 odvp->attr.attr.mode = 0444; 319 odvp->attr.show = odvp_show; 320 odvp->attr.store = NULL; 321 ret = sysfs_create_file(&priv->pdev->dev.kobj, 322 &odvp->attr.attr); 323 if (ret) 324 goto out_err; 325 } 326 } 327 328 for (i = 0; i < obj->package.count; i++) { 329 if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 330 priv->odvp[i] = obj->package.elements[i].integer.value; 331 } 332 333 kfree(obj); 334 return 0; 335 336 out_err: 337 cleanup_odvp(priv); 338 kfree(obj); 339 return ret; 340 } 341 342 static void int3400_notify(acpi_handle handle, 343 u32 event, 344 void *data) 345 { 346 struct int3400_thermal_priv *priv = data; 347 char *thermal_prop[5]; 348 349 if (!priv) 350 return; 351 352 switch (event) { 353 case INT3400_THERMAL_TABLE_CHANGED: 354 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", 355 priv->thermal->type); 356 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", 357 priv->thermal->temperature); 358 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 359 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", 360 THERMAL_TABLE_CHANGED); 361 thermal_prop[4] = NULL; 362 kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, 363 thermal_prop); 364 break; 365 case INT3400_ODVP_CHANGED: 366 evaluate_odvp(priv); 367 break; 368 default: 369 /* Ignore unknown notification codes sent to INT3400 device */ 370 break; 371 } 372 } 373 374 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 375 int *temp) 376 { 377 *temp = 20 * 1000; /* faked temp sensor with 20C */ 378 return 0; 379 } 380 381 static int int3400_thermal_get_mode(struct thermal_zone_device *thermal, 382 enum thermal_device_mode *mode) 383 { 384 struct int3400_thermal_priv *priv = thermal->devdata; 385 386 if (!priv) 387 return -EINVAL; 388 389 *mode = priv->mode; 390 391 return 0; 392 } 393 394 static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, 395 enum thermal_device_mode mode) 396 { 397 struct int3400_thermal_priv *priv = thermal->devdata; 398 bool enable; 399 int result = 0; 400 401 if (!priv) 402 return -EINVAL; 403 404 if (mode == THERMAL_DEVICE_ENABLED) 405 enable = true; 406 else if (mode == THERMAL_DEVICE_DISABLED) 407 enable = false; 408 else 409 return -EINVAL; 410 411 if (enable != priv->mode) { 412 priv->mode = enable; 413 result = int3400_thermal_run_osc(priv->adev->handle, 414 priv->current_uuid_index, 415 enable); 416 } 417 418 evaluate_odvp(priv); 419 420 return result; 421 } 422 423 static struct thermal_zone_device_ops int3400_thermal_ops = { 424 .get_temp = int3400_thermal_get_temp, 425 .get_mode = int3400_thermal_get_mode, 426 .set_mode = int3400_thermal_set_mode, 427 }; 428 429 static struct thermal_zone_params int3400_thermal_params = { 430 .governor_name = "user_space", 431 .no_hwmon = true, 432 }; 433 434 static void int3400_setup_gddv(struct int3400_thermal_priv *priv) 435 { 436 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 437 union acpi_object *obj; 438 acpi_status status; 439 440 status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 441 &buffer); 442 if (ACPI_FAILURE(status) || !buffer.length) 443 return; 444 445 obj = buffer.pointer; 446 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 447 || obj->package.elements[0].type != ACPI_TYPE_BUFFER) { 448 kfree(buffer.pointer); 449 return; 450 } 451 452 priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 453 obj->package.elements[0].buffer.length, 454 GFP_KERNEL); 455 bin_attr_data_vault.private = priv->data_vault; 456 bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 457 kfree(buffer.pointer); 458 } 459 460 static int int3400_thermal_probe(struct platform_device *pdev) 461 { 462 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 463 struct int3400_thermal_priv *priv; 464 int result; 465 466 if (!adev) 467 return -ENODEV; 468 469 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 470 if (!priv) 471 return -ENOMEM; 472 473 priv->pdev = pdev; 474 priv->adev = adev; 475 476 result = int3400_thermal_get_uuids(priv); 477 478 /* Missing IDSP isn't fatal */ 479 if (result && result != -ENODEV) 480 goto free_priv; 481 482 priv->current_uuid_index = -1; 483 484 result = acpi_parse_art(priv->adev->handle, &priv->art_count, 485 &priv->arts, true); 486 if (result) 487 dev_dbg(&pdev->dev, "_ART table parsing error\n"); 488 489 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 490 &priv->trts, true); 491 if (result) 492 dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 493 494 platform_set_drvdata(pdev, priv); 495 496 int3400_setup_gddv(priv); 497 498 evaluate_odvp(priv); 499 500 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 501 priv, &int3400_thermal_ops, 502 &int3400_thermal_params, 0, 0); 503 if (IS_ERR(priv->thermal)) { 504 result = PTR_ERR(priv->thermal); 505 goto free_art_trt; 506 } 507 508 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 509 priv->adev->handle); 510 511 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 512 if (result) 513 goto free_rel_misc; 514 515 if (priv->data_vault) { 516 result = sysfs_create_group(&pdev->dev.kobj, 517 &data_attribute_group); 518 if (result) 519 goto free_uuid; 520 } 521 522 result = acpi_install_notify_handler( 523 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 524 (void *)priv); 525 if (result) 526 goto free_sysfs; 527 528 return 0; 529 530 free_sysfs: 531 cleanup_odvp(priv); 532 if (priv->data_vault) { 533 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 534 kfree(priv->data_vault); 535 } 536 free_uuid: 537 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 538 free_rel_misc: 539 if (!priv->rel_misc_dev_res) 540 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 541 thermal_zone_device_unregister(priv->thermal); 542 free_art_trt: 543 kfree(priv->trts); 544 kfree(priv->arts); 545 free_priv: 546 kfree(priv); 547 return result; 548 } 549 550 static int int3400_thermal_remove(struct platform_device *pdev) 551 { 552 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 553 554 acpi_remove_notify_handler( 555 priv->adev->handle, ACPI_DEVICE_NOTIFY, 556 int3400_notify); 557 558 cleanup_odvp(priv); 559 560 if (!priv->rel_misc_dev_res) 561 acpi_thermal_rel_misc_device_remove(priv->adev->handle); 562 563 if (priv->data_vault) 564 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 565 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 566 thermal_zone_device_unregister(priv->thermal); 567 kfree(priv->data_vault); 568 kfree(priv->trts); 569 kfree(priv->arts); 570 kfree(priv); 571 return 0; 572 } 573 574 static const struct acpi_device_id int3400_thermal_match[] = { 575 {"INT3400", 0}, 576 {"INTC1040", 0}, 577 {} 578 }; 579 580 MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 581 582 static struct platform_driver int3400_thermal_driver = { 583 .probe = int3400_thermal_probe, 584 .remove = int3400_thermal_remove, 585 .driver = { 586 .name = "int3400 thermal", 587 .acpi_match_table = ACPI_PTR(int3400_thermal_match), 588 }, 589 }; 590 591 module_platform_driver(int3400_thermal_driver); 592 593 MODULE_DESCRIPTION("INT3400 Thermal driver"); 594 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 595 MODULE_LICENSE("GPL"); 596