722 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * INT3400 thermal driver4 *5 * Copyright (C) 2014, Intel Corporation6 * 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 0x8316#define INT3400_ODVP_CHANGED 0x8817#define INT3400_KEEP_ALIVE 0xA018#define INT3400_FAKE_TEMP (20 * 1000) /* faked temp sensor with 20C */19 20enum int3400_thermal_uuid {21 INT3400_THERMAL_ACTIVE = 0,22 INT3400_THERMAL_PASSIVE_1,23 INT3400_THERMAL_CRITICAL,24 INT3400_THERMAL_ADAPTIVE_PERFORMANCE,25 INT3400_THERMAL_EMERGENCY_CALL_MODE,26 INT3400_THERMAL_PASSIVE_2,27 INT3400_THERMAL_POWER_BOSS,28 INT3400_THERMAL_VIRTUAL_SENSOR,29 INT3400_THERMAL_COOLING_MODE,30 INT3400_THERMAL_HARDWARE_DUTY_CYCLING,31 INT3400_THERMAL_MAXIMUM_UUID,32};33 34static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {35 "3A95C389-E4B8-4629-A526-C52C88626BAE",36 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",37 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",38 "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",39 "5349962F-71E6-431D-9AE8-0A635B710AEE",40 "9E04115A-AE87-4D1C-9500-0F3E340BFE75",41 "F5A35014-C209-46A4-993A-EB56DE7530A1",42 "6ED722A7-9240-48A5-B479-31EEF723D7CF",43 "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",44 "BE84BABF-C4D4-403D-B495-3128FD44dAC1",45};46 47struct odvp_attr;48 49struct int3400_thermal_priv {50 struct acpi_device *adev;51 struct platform_device *pdev;52 struct thermal_zone_device *thermal;53 int art_count;54 struct art *arts;55 int trt_count;56 struct trt *trts;57 u32 uuid_bitmap;58 int rel_misc_dev_res;59 int current_uuid_index;60 char *data_vault;61 int odvp_count;62 int *odvp;63 u32 os_uuid_mask;64 int production_mode;65 struct odvp_attr *odvp_attrs;66};67 68static int evaluate_odvp(struct int3400_thermal_priv *priv);69 70struct odvp_attr {71 int odvp;72 struct int3400_thermal_priv *priv;73 struct device_attribute attr;74};75 76static BIN_ATTR_SIMPLE_RO(data_vault);77 78static struct bin_attribute *data_attributes[] = {79 &bin_attr_data_vault,80 NULL,81};82 83static ssize_t imok_store(struct device *dev, struct device_attribute *attr,84 const char *buf, size_t count)85{86 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);87 acpi_status status;88 int input, ret;89 90 ret = kstrtouint(buf, 10, &input);91 if (ret)92 return ret;93 status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input);94 if (ACPI_FAILURE(status))95 return -EIO;96 97 return count;98}99 100static DEVICE_ATTR_WO(imok);101 102static struct attribute *imok_attr[] = {103 &dev_attr_imok.attr,104 NULL105};106 107static const struct attribute_group imok_attribute_group = {108 .attrs = imok_attr,109};110 111static const struct attribute_group data_attribute_group = {112 .bin_attrs = data_attributes,113};114 115static ssize_t available_uuids_show(struct device *dev,116 struct device_attribute *attr,117 char *buf)118{119 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);120 int i;121 int length = 0;122 123 if (!priv->uuid_bitmap)124 return sprintf(buf, "UNKNOWN\n");125 126 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {127 if (priv->uuid_bitmap & (1 << i))128 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]);129 }130 131 return length;132}133 134static ssize_t current_uuid_show(struct device *dev,135 struct device_attribute *devattr, char *buf)136{137 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);138 int i, length = 0;139 140 if (priv->current_uuid_index > 0)141 return sprintf(buf, "%s\n",142 int3400_thermal_uuids[priv->current_uuid_index]);143 144 for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {145 if (priv->os_uuid_mask & BIT(i))146 length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]);147 }148 149 if (length)150 return length;151 152 return sprintf(buf, "INVALID\n");153}154 155static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable)156{157 u32 ret, buf[2];158 acpi_status status;159 int result = 0;160 struct acpi_osc_context context = {161 .uuid_str = uuid_str,162 .rev = 1,163 .cap.length = 8,164 .cap.pointer = buf,165 };166 167 buf[OSC_QUERY_DWORD] = 0;168 buf[OSC_SUPPORT_DWORD] = *enable;169 170 status = acpi_run_osc(handle, &context);171 if (ACPI_SUCCESS(status)) {172 ret = *((u32 *)(context.ret.pointer + 4));173 if (ret != *enable)174 result = -EPERM;175 176 kfree(context.ret.pointer);177 } else178 result = -EPERM;179 180 return result;181}182 183static int set_os_uuid_mask(struct int3400_thermal_priv *priv, u32 mask)184{185 int cap = 0;186 187 /*188 * Capability bits:189 * Bit 0: set to 1 to indicate DPTF is active190 * Bi1 1: set to 1 to active cooling is supported by user space daemon191 * Bit 2: set to 1 to passive cooling is supported by user space daemon192 * Bit 3: set to 1 to critical trip is handled by user space daemon193 */194 if (mask)195 cap = (priv->os_uuid_mask << 1) | 0x01;196 197 return int3400_thermal_run_osc(priv->adev->handle,198 "b23ba85d-c8b7-3542-88de-8de2ffcfd698",199 &cap);200}201 202static ssize_t current_uuid_store(struct device *dev,203 struct device_attribute *attr,204 const char *buf, size_t count)205{206 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);207 int ret, i;208 209 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {210 if (!strncmp(buf, int3400_thermal_uuids[i],211 sizeof(int3400_thermal_uuids[i]) - 1)) {212 /*213 * If we have a list of supported UUIDs, make sure214 * this one is supported.215 */216 if (priv->uuid_bitmap & BIT(i)) {217 priv->current_uuid_index = i;218 return count;219 }220 221 /*222 * There is support of only 3 policies via the new223 * _OSC to inform OS capability:224 * INT3400_THERMAL_ACTIVE225 * INT3400_THERMAL_PASSIVE_1226 * INT3400_THERMAL_CRITICAL227 */228 229 if (i > INT3400_THERMAL_CRITICAL)230 return -EINVAL;231 232 priv->os_uuid_mask |= BIT(i);233 234 break;235 }236 }237 238 if (priv->os_uuid_mask) {239 ret = set_os_uuid_mask(priv, priv->os_uuid_mask);240 if (ret)241 return ret;242 }243 244 return count;245}246 247static DEVICE_ATTR_RW(current_uuid);248static DEVICE_ATTR_RO(available_uuids);249static struct attribute *uuid_attrs[] = {250 &dev_attr_available_uuids.attr,251 &dev_attr_current_uuid.attr,252 NULL253};254 255static const struct attribute_group uuid_attribute_group = {256 .attrs = uuid_attrs,257 .name = "uuids"258};259 260static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)261{262 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};263 union acpi_object *obja, *objb;264 int i, j;265 int result = 0;266 acpi_status status;267 268 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);269 if (ACPI_FAILURE(status))270 return -ENODEV;271 272 obja = (union acpi_object *)buf.pointer;273 if (obja->type != ACPI_TYPE_PACKAGE) {274 result = -EINVAL;275 goto end;276 }277 278 for (i = 0; i < obja->package.count; i++) {279 objb = &obja->package.elements[i];280 if (objb->type != ACPI_TYPE_BUFFER) {281 result = -EINVAL;282 goto end;283 }284 285 /* UUID must be 16 bytes */286 if (objb->buffer.length != 16) {287 result = -EINVAL;288 goto end;289 }290 291 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {292 guid_t guid;293 294 guid_parse(int3400_thermal_uuids[j], &guid);295 if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {296 priv->uuid_bitmap |= (1 << j);297 break;298 }299 }300 }301 302end:303 kfree(buf.pointer);304 return result;305}306 307static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr,308 char *buf)309{310 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);311 312 return sysfs_emit(buf, "%d\n", priv->production_mode);313}314 315static DEVICE_ATTR_RO(production_mode);316 317static int production_mode_init(struct int3400_thermal_priv *priv)318{319 unsigned long long mode;320 acpi_status status;321 int ret;322 323 priv->production_mode = -1;324 325 status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode);326 /* If the method is not present, this is not an error */327 if (ACPI_FAILURE(status))328 return 0;329 330 ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);331 if (ret)332 return ret;333 334 priv->production_mode = mode;335 336 return 0;337}338 339static void production_mode_exit(struct int3400_thermal_priv *priv)340{341 if (priv->production_mode >= 0)342 sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);343}344 345static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,346 char *buf)347{348 struct odvp_attr *odvp_attr;349 350 odvp_attr = container_of(attr, struct odvp_attr, attr);351 352 return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);353}354 355static void cleanup_odvp(struct int3400_thermal_priv *priv)356{357 int i;358 359 if (priv->odvp_attrs) {360 for (i = 0; i < priv->odvp_count; i++) {361 sysfs_remove_file(&priv->pdev->dev.kobj,362 &priv->odvp_attrs[i].attr.attr);363 kfree(priv->odvp_attrs[i].attr.attr.name);364 }365 kfree(priv->odvp_attrs);366 }367 kfree(priv->odvp);368 priv->odvp_count = 0;369}370 371static int evaluate_odvp(struct int3400_thermal_priv *priv)372{373 struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL };374 union acpi_object *obj = NULL;375 acpi_status status;376 int i, ret;377 378 status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp);379 if (ACPI_FAILURE(status)) {380 ret = -EINVAL;381 goto out_err;382 }383 384 obj = odvp.pointer;385 if (obj->type != ACPI_TYPE_PACKAGE) {386 ret = -EINVAL;387 goto out_err;388 }389 390 if (priv->odvp == NULL) {391 priv->odvp_count = obj->package.count;392 priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),393 GFP_KERNEL);394 if (!priv->odvp) {395 ret = -ENOMEM;396 goto out_err;397 }398 }399 400 if (priv->odvp_attrs == NULL) {401 priv->odvp_attrs = kcalloc(priv->odvp_count,402 sizeof(struct odvp_attr),403 GFP_KERNEL);404 if (!priv->odvp_attrs) {405 ret = -ENOMEM;406 goto out_err;407 }408 for (i = 0; i < priv->odvp_count; i++) {409 struct odvp_attr *odvp = &priv->odvp_attrs[i];410 411 sysfs_attr_init(&odvp->attr.attr);412 odvp->priv = priv;413 odvp->odvp = i;414 odvp->attr.attr.name = kasprintf(GFP_KERNEL,415 "odvp%d", i);416 417 if (!odvp->attr.attr.name) {418 ret = -ENOMEM;419 goto out_err;420 }421 odvp->attr.attr.mode = 0444;422 odvp->attr.show = odvp_show;423 odvp->attr.store = NULL;424 ret = sysfs_create_file(&priv->pdev->dev.kobj,425 &odvp->attr.attr);426 if (ret)427 goto out_err;428 }429 }430 431 for (i = 0; i < obj->package.count; i++) {432 if (obj->package.elements[i].type == ACPI_TYPE_INTEGER)433 priv->odvp[i] = obj->package.elements[i].integer.value;434 }435 436 kfree(obj);437 return 0;438 439out_err:440 cleanup_odvp(priv);441 kfree(obj);442 return ret;443}444 445static void int3400_notify(acpi_handle handle,446 u32 event,447 void *data)448{449 struct int3400_thermal_priv *priv = data;450 struct device *dev;451 char *thermal_prop[5];452 int therm_event;453 454 if (!priv)455 return;456 457 switch (event) {458 case INT3400_THERMAL_TABLE_CHANGED:459 therm_event = THERMAL_TABLE_CHANGED;460 break;461 case INT3400_KEEP_ALIVE:462 therm_event = THERMAL_EVENT_KEEP_ALIVE;463 break;464 case INT3400_ODVP_CHANGED:465 evaluate_odvp(priv);466 therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED;467 break;468 default:469 /* Ignore unknown notification codes sent to INT3400 device */470 return;471 }472 473 dev = thermal_zone_device(priv->thermal);474 475 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", thermal_zone_device_type(priv->thermal));476 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", INT3400_FAKE_TEMP);477 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");478 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event);479 thermal_prop[4] = NULL;480 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, thermal_prop);481 kfree(thermal_prop[0]);482 kfree(thermal_prop[1]);483 kfree(thermal_prop[2]);484 kfree(thermal_prop[3]);485}486 487static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,488 int *temp)489{490 *temp = INT3400_FAKE_TEMP;491 return 0;492}493 494static int int3400_thermal_change_mode(struct thermal_zone_device *thermal,495 enum thermal_device_mode mode)496{497 struct int3400_thermal_priv *priv = thermal_zone_device_priv(thermal);498 int result = 0;499 int enabled;500 501 if (!priv)502 return -EINVAL;503 504 enabled = mode == THERMAL_DEVICE_ENABLED;505 506 if (priv->os_uuid_mask) {507 if (!enabled) {508 priv->os_uuid_mask = 0;509 result = set_os_uuid_mask(priv, priv->os_uuid_mask);510 }511 goto eval_odvp;512 }513 514 if (priv->current_uuid_index < 0 ||515 priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID)516 return -EINVAL;517 518 result = int3400_thermal_run_osc(priv->adev->handle,519 int3400_thermal_uuids[priv->current_uuid_index],520 &enabled);521eval_odvp:522 evaluate_odvp(priv);523 524 return result;525}526 527static struct thermal_zone_device_ops int3400_thermal_ops = {528 .get_temp = int3400_thermal_get_temp,529 .change_mode = int3400_thermal_change_mode,530};531 532static struct thermal_zone_params int3400_thermal_params = {533 .governor_name = "user_space",534 .no_hwmon = true,535};536 537static void int3400_setup_gddv(struct int3400_thermal_priv *priv)538{539 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };540 union acpi_object *obj;541 acpi_status status;542 543 status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL,544 &buffer);545 if (ACPI_FAILURE(status) || !buffer.length)546 return;547 548 obj = buffer.pointer;549 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1550 || obj->package.elements[0].type != ACPI_TYPE_BUFFER)551 goto out_free;552 553 priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer,554 obj->package.elements[0].buffer.length,555 GFP_KERNEL);556 if (ZERO_OR_NULL_PTR(priv->data_vault))557 goto out_free;558 559 bin_attr_data_vault.private = priv->data_vault;560 bin_attr_data_vault.size = obj->package.elements[0].buffer.length;561out_free:562 kfree(buffer.pointer);563}564 565static int int3400_thermal_probe(struct platform_device *pdev)566{567 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);568 struct int3400_thermal_priv *priv;569 int result;570 571 if (!adev)572 return -ENODEV;573 574 priv = kzalloc(sizeof(*priv), GFP_KERNEL);575 if (!priv)576 return -ENOMEM;577 578 priv->pdev = pdev;579 priv->adev = adev;580 581 result = int3400_thermal_get_uuids(priv);582 583 /* Missing IDSP isn't fatal */584 if (result && result != -ENODEV)585 goto free_priv;586 587 priv->current_uuid_index = -1;588 589 result = acpi_parse_art(priv->adev->handle, &priv->art_count,590 &priv->arts, true);591 if (result)592 dev_dbg(&pdev->dev, "_ART table parsing error\n");593 594 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,595 &priv->trts, true);596 if (result)597 dev_dbg(&pdev->dev, "_TRT table parsing error\n");598 599 platform_set_drvdata(pdev, priv);600 601 int3400_setup_gddv(priv);602 603 evaluate_odvp(priv);604 605 priv->thermal = thermal_tripless_zone_device_register("INT3400 Thermal", priv,606 &int3400_thermal_ops,607 &int3400_thermal_params);608 if (IS_ERR(priv->thermal)) {609 result = PTR_ERR(priv->thermal);610 goto free_art_trt;611 }612 613 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(614 priv->adev->handle);615 616 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);617 if (result)618 goto free_rel_misc;619 620 if (acpi_has_method(priv->adev->handle, "IMOK")) {621 result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group);622 if (result)623 goto free_imok;624 }625 626 if (!ZERO_OR_NULL_PTR(priv->data_vault)) {627 result = sysfs_create_group(&pdev->dev.kobj,628 &data_attribute_group);629 if (result)630 goto free_uuid;631 }632 633 result = acpi_install_notify_handler(634 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,635 (void *)priv);636 if (result)637 goto free_sysfs;638 639 result = production_mode_init(priv);640 if (result)641 goto free_notify;642 643 return 0;644 645free_notify:646 acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,647 int3400_notify);648free_sysfs:649 cleanup_odvp(priv);650 if (!ZERO_OR_NULL_PTR(priv->data_vault)) {651 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);652 kfree(priv->data_vault);653 }654free_uuid:655 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);656free_imok:657 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);658free_rel_misc:659 if (!priv->rel_misc_dev_res)660 acpi_thermal_rel_misc_device_remove(priv->adev->handle);661 thermal_zone_device_unregister(priv->thermal);662free_art_trt:663 kfree(priv->trts);664 kfree(priv->arts);665free_priv:666 kfree(priv);667 return result;668}669 670static void int3400_thermal_remove(struct platform_device *pdev)671{672 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);673 674 production_mode_exit(priv);675 676 acpi_remove_notify_handler(677 priv->adev->handle, ACPI_DEVICE_NOTIFY,678 int3400_notify);679 680 cleanup_odvp(priv);681 682 if (!priv->rel_misc_dev_res)683 acpi_thermal_rel_misc_device_remove(priv->adev->handle);684 685 if (!ZERO_OR_NULL_PTR(priv->data_vault))686 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);687 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);688 sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);689 thermal_zone_device_unregister(priv->thermal);690 kfree(priv->data_vault);691 kfree(priv->trts);692 kfree(priv->arts);693 kfree(priv);694}695 696static const struct acpi_device_id int3400_thermal_match[] = {697 {"INT3400", 0},698 {"INTC1040", 0},699 {"INTC1041", 0},700 {"INTC1042", 0},701 {"INTC1068", 0},702 {"INTC10A0", 0},703 {}704};705 706MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);707 708static struct platform_driver int3400_thermal_driver = {709 .probe = int3400_thermal_probe,710 .remove_new = int3400_thermal_remove,711 .driver = {712 .name = "int3400 thermal",713 .acpi_match_table = ACPI_PTR(int3400_thermal_match),714 },715};716 717module_platform_driver(int3400_thermal_driver);718 719MODULE_DESCRIPTION("INT3400 Thermal driver");720MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");721MODULE_LICENSE("GPL");722