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