18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * INT3400 thermal driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014, Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Zhang Rui <rui.zhang@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/thermal.h> 138c2ecf20Sopenharmony_ci#include "acpi_thermal_rel.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define INT3400_THERMAL_TABLE_CHANGED 0x83 168c2ecf20Sopenharmony_ci#define INT3400_ODVP_CHANGED 0x88 178c2ecf20Sopenharmony_ci#define INT3400_KEEP_ALIVE 0xA0 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cienum int3400_thermal_uuid { 208c2ecf20Sopenharmony_ci INT3400_THERMAL_PASSIVE_1, 218c2ecf20Sopenharmony_ci INT3400_THERMAL_ACTIVE, 228c2ecf20Sopenharmony_ci INT3400_THERMAL_CRITICAL, 238c2ecf20Sopenharmony_ci INT3400_THERMAL_ADAPTIVE_PERFORMANCE, 248c2ecf20Sopenharmony_ci INT3400_THERMAL_EMERGENCY_CALL_MODE, 258c2ecf20Sopenharmony_ci INT3400_THERMAL_PASSIVE_2, 268c2ecf20Sopenharmony_ci INT3400_THERMAL_POWER_BOSS, 278c2ecf20Sopenharmony_ci INT3400_THERMAL_VIRTUAL_SENSOR, 288c2ecf20Sopenharmony_ci INT3400_THERMAL_COOLING_MODE, 298c2ecf20Sopenharmony_ci INT3400_THERMAL_HARDWARE_DUTY_CYCLING, 308c2ecf20Sopenharmony_ci INT3400_THERMAL_MAXIMUM_UUID, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { 348c2ecf20Sopenharmony_ci "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 358c2ecf20Sopenharmony_ci "3A95C389-E4B8-4629-A526-C52C88626BAE", 368c2ecf20Sopenharmony_ci "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", 378c2ecf20Sopenharmony_ci "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D", 388c2ecf20Sopenharmony_ci "5349962F-71E6-431D-9AE8-0A635B710AEE", 398c2ecf20Sopenharmony_ci "9E04115A-AE87-4D1C-9500-0F3E340BFE75", 408c2ecf20Sopenharmony_ci "F5A35014-C209-46A4-993A-EB56DE7530A1", 418c2ecf20Sopenharmony_ci "6ED722A7-9240-48A5-B479-31EEF723D7CF", 428c2ecf20Sopenharmony_ci "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531", 438c2ecf20Sopenharmony_ci "BE84BABF-C4D4-403D-B495-3128FD44dAC1", 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct odvp_attr; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct int3400_thermal_priv { 498c2ecf20Sopenharmony_ci struct acpi_device *adev; 508c2ecf20Sopenharmony_ci struct platform_device *pdev; 518c2ecf20Sopenharmony_ci struct thermal_zone_device *thermal; 528c2ecf20Sopenharmony_ci int art_count; 538c2ecf20Sopenharmony_ci struct art *arts; 548c2ecf20Sopenharmony_ci int trt_count; 558c2ecf20Sopenharmony_ci struct trt *trts; 568c2ecf20Sopenharmony_ci u32 uuid_bitmap; 578c2ecf20Sopenharmony_ci int rel_misc_dev_res; 588c2ecf20Sopenharmony_ci int current_uuid_index; 598c2ecf20Sopenharmony_ci char *data_vault; 608c2ecf20Sopenharmony_ci int odvp_count; 618c2ecf20Sopenharmony_ci int *odvp; 628c2ecf20Sopenharmony_ci struct odvp_attr *odvp_attrs; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int evaluate_odvp(struct int3400_thermal_priv *priv); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct odvp_attr { 688c2ecf20Sopenharmony_ci int odvp; 698c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv; 708c2ecf20Sopenharmony_ci struct device_attribute attr; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic ssize_t data_vault_read(struct file *file, struct kobject *kobj, 748c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, size_t count) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci memcpy(buf, attr->private + off, count); 778c2ecf20Sopenharmony_ci return count; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic BIN_ATTR_RO(data_vault, 0); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct bin_attribute *data_attributes[] = { 838c2ecf20Sopenharmony_ci &bin_attr_data_vault, 848c2ecf20Sopenharmony_ci NULL, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic ssize_t imok_store(struct device *dev, struct device_attribute *attr, 888c2ecf20Sopenharmony_ci const char *buf, size_t count) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 918c2ecf20Sopenharmony_ci acpi_status status; 928c2ecf20Sopenharmony_ci int input, ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ret = kstrtouint(buf, 10, &input); 958c2ecf20Sopenharmony_ci if (ret) 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input); 988c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 998c2ecf20Sopenharmony_ci return -EIO; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return count; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(imok); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct attribute *imok_attr[] = { 1078c2ecf20Sopenharmony_ci &dev_attr_imok.attr, 1088c2ecf20Sopenharmony_ci NULL 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct attribute_group data_attribute_group = { 1128c2ecf20Sopenharmony_ci .bin_attrs = data_attributes, 1138c2ecf20Sopenharmony_ci .attrs = imok_attr, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic ssize_t available_uuids_show(struct device *dev, 1178c2ecf20Sopenharmony_ci struct device_attribute *attr, 1188c2ecf20Sopenharmony_ci char *buf) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 1218c2ecf20Sopenharmony_ci int i; 1228c2ecf20Sopenharmony_ci int length = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!priv->uuid_bitmap) 1258c2ecf20Sopenharmony_ci return sprintf(buf, "UNKNOWN\n"); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { 1288c2ecf20Sopenharmony_ci if (priv->uuid_bitmap & (1 << i)) 1298c2ecf20Sopenharmony_ci if (PAGE_SIZE - length > 0) 1308c2ecf20Sopenharmony_ci length += scnprintf(&buf[length], 1318c2ecf20Sopenharmony_ci PAGE_SIZE - length, 1328c2ecf20Sopenharmony_ci "%s\n", 1338c2ecf20Sopenharmony_ci int3400_thermal_uuids[i]); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return length; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic ssize_t current_uuid_show(struct device *dev, 1408c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (priv->current_uuid_index == -1) 1458c2ecf20Sopenharmony_ci return sprintf(buf, "INVALID\n"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 1488c2ecf20Sopenharmony_ci int3400_thermal_uuids[priv->current_uuid_index]); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic ssize_t current_uuid_store(struct device *dev, 1528c2ecf20Sopenharmony_ci struct device_attribute *attr, 1538c2ecf20Sopenharmony_ci const char *buf, size_t count) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 1568c2ecf20Sopenharmony_ci int i; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { 1598c2ecf20Sopenharmony_ci if (!strncmp(buf, int3400_thermal_uuids[i], 1608c2ecf20Sopenharmony_ci sizeof(int3400_thermal_uuids[i]) - 1)) { 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * If we have a list of supported UUIDs, make sure 1638c2ecf20Sopenharmony_ci * this one is supported. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (priv->uuid_bitmap && 1668c2ecf20Sopenharmony_ci !(priv->uuid_bitmap & (1 << i))) 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci priv->current_uuid_index = i; 1708c2ecf20Sopenharmony_ci return count; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return -EINVAL; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(current_uuid); 1788c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(available_uuids); 1798c2ecf20Sopenharmony_cistatic struct attribute *uuid_attrs[] = { 1808c2ecf20Sopenharmony_ci &dev_attr_available_uuids.attr, 1818c2ecf20Sopenharmony_ci &dev_attr_current_uuid.attr, 1828c2ecf20Sopenharmony_ci NULL 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct attribute_group uuid_attribute_group = { 1868c2ecf20Sopenharmony_ci .attrs = uuid_attrs, 1878c2ecf20Sopenharmony_ci .name = "uuids" 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 1938c2ecf20Sopenharmony_ci union acpi_object *obja, *objb; 1948c2ecf20Sopenharmony_ci int i, j; 1958c2ecf20Sopenharmony_ci int result = 0; 1968c2ecf20Sopenharmony_ci acpi_status status; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 1998c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 2008c2ecf20Sopenharmony_ci return -ENODEV; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci obja = (union acpi_object *)buf.pointer; 2038c2ecf20Sopenharmony_ci if (obja->type != ACPI_TYPE_PACKAGE) { 2048c2ecf20Sopenharmony_ci result = -EINVAL; 2058c2ecf20Sopenharmony_ci goto end; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for (i = 0; i < obja->package.count; i++) { 2098c2ecf20Sopenharmony_ci objb = &obja->package.elements[i]; 2108c2ecf20Sopenharmony_ci if (objb->type != ACPI_TYPE_BUFFER) { 2118c2ecf20Sopenharmony_ci result = -EINVAL; 2128c2ecf20Sopenharmony_ci goto end; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* UUID must be 16 bytes */ 2168c2ecf20Sopenharmony_ci if (objb->buffer.length != 16) { 2178c2ecf20Sopenharmony_ci result = -EINVAL; 2188c2ecf20Sopenharmony_ci goto end; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 2228c2ecf20Sopenharmony_ci guid_t guid; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci guid_parse(int3400_thermal_uuids[j], &guid); 2258c2ecf20Sopenharmony_ci if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) { 2268c2ecf20Sopenharmony_ci priv->uuid_bitmap |= (1 << j); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciend: 2338c2ecf20Sopenharmony_ci kfree(buf.pointer); 2348c2ecf20Sopenharmony_ci return result; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int int3400_thermal_run_osc(acpi_handle handle, 2388c2ecf20Sopenharmony_ci enum int3400_thermal_uuid uuid, bool enable) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 ret, buf[2]; 2418c2ecf20Sopenharmony_ci acpi_status status; 2428c2ecf20Sopenharmony_ci int result = 0; 2438c2ecf20Sopenharmony_ci struct acpi_osc_context context = { 2448c2ecf20Sopenharmony_ci .uuid_str = NULL, 2458c2ecf20Sopenharmony_ci .rev = 1, 2468c2ecf20Sopenharmony_ci .cap.length = 8, 2478c2ecf20Sopenharmony_ci }; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (uuid < 0 || uuid >= INT3400_THERMAL_MAXIMUM_UUID) 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci context.uuid_str = int3400_thermal_uuids[uuid]; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci buf[OSC_QUERY_DWORD] = 0; 2558c2ecf20Sopenharmony_ci buf[OSC_SUPPORT_DWORD] = enable; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci context.cap.pointer = buf; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci status = acpi_run_osc(handle, &context); 2608c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) { 2618c2ecf20Sopenharmony_ci ret = *((u32 *)(context.ret.pointer + 4)); 2628c2ecf20Sopenharmony_ci if (ret != enable) 2638c2ecf20Sopenharmony_ci result = -EPERM; 2648c2ecf20Sopenharmony_ci } else 2658c2ecf20Sopenharmony_ci result = -EPERM; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci kfree(context.ret.pointer); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return result; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic ssize_t odvp_show(struct device *dev, struct device_attribute *attr, 2738c2ecf20Sopenharmony_ci char *buf) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct odvp_attr *odvp_attr; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci odvp_attr = container_of(attr, struct odvp_attr, attr); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void cleanup_odvp(struct int3400_thermal_priv *priv) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int i; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (priv->odvp_attrs) { 2878c2ecf20Sopenharmony_ci for (i = 0; i < priv->odvp_count; i++) { 2888c2ecf20Sopenharmony_ci sysfs_remove_file(&priv->pdev->dev.kobj, 2898c2ecf20Sopenharmony_ci &priv->odvp_attrs[i].attr.attr); 2908c2ecf20Sopenharmony_ci kfree(priv->odvp_attrs[i].attr.attr.name); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci kfree(priv->odvp_attrs); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci kfree(priv->odvp); 2958c2ecf20Sopenharmony_ci priv->odvp_count = 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int evaluate_odvp(struct int3400_thermal_priv *priv) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 3018c2ecf20Sopenharmony_ci union acpi_object *obj = NULL; 3028c2ecf20Sopenharmony_ci acpi_status status; 3038c2ecf20Sopenharmony_ci int i, ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 3068c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 3078c2ecf20Sopenharmony_ci ret = -EINVAL; 3088c2ecf20Sopenharmony_ci goto out_err; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci obj = odvp.pointer; 3128c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_PACKAGE) { 3138c2ecf20Sopenharmony_ci ret = -EINVAL; 3148c2ecf20Sopenharmony_ci goto out_err; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (priv->odvp == NULL) { 3188c2ecf20Sopenharmony_ci priv->odvp_count = obj->package.count; 3198c2ecf20Sopenharmony_ci priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 3208c2ecf20Sopenharmony_ci GFP_KERNEL); 3218c2ecf20Sopenharmony_ci if (!priv->odvp) { 3228c2ecf20Sopenharmony_ci ret = -ENOMEM; 3238c2ecf20Sopenharmony_ci goto out_err; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (priv->odvp_attrs == NULL) { 3288c2ecf20Sopenharmony_ci priv->odvp_attrs = kcalloc(priv->odvp_count, 3298c2ecf20Sopenharmony_ci sizeof(struct odvp_attr), 3308c2ecf20Sopenharmony_ci GFP_KERNEL); 3318c2ecf20Sopenharmony_ci if (!priv->odvp_attrs) { 3328c2ecf20Sopenharmony_ci ret = -ENOMEM; 3338c2ecf20Sopenharmony_ci goto out_err; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci for (i = 0; i < priv->odvp_count; i++) { 3368c2ecf20Sopenharmony_ci struct odvp_attr *odvp = &priv->odvp_attrs[i]; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci sysfs_attr_init(&odvp->attr.attr); 3398c2ecf20Sopenharmony_ci odvp->priv = priv; 3408c2ecf20Sopenharmony_ci odvp->odvp = i; 3418c2ecf20Sopenharmony_ci odvp->attr.attr.name = kasprintf(GFP_KERNEL, 3428c2ecf20Sopenharmony_ci "odvp%d", i); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!odvp->attr.attr.name) { 3458c2ecf20Sopenharmony_ci ret = -ENOMEM; 3468c2ecf20Sopenharmony_ci goto out_err; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci odvp->attr.attr.mode = 0444; 3498c2ecf20Sopenharmony_ci odvp->attr.show = odvp_show; 3508c2ecf20Sopenharmony_ci odvp->attr.store = NULL; 3518c2ecf20Sopenharmony_ci ret = sysfs_create_file(&priv->pdev->dev.kobj, 3528c2ecf20Sopenharmony_ci &odvp->attr.attr); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci goto out_err; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < obj->package.count; i++) { 3598c2ecf20Sopenharmony_ci if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 3608c2ecf20Sopenharmony_ci priv->odvp[i] = obj->package.elements[i].integer.value; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci kfree(obj); 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciout_err: 3678c2ecf20Sopenharmony_ci cleanup_odvp(priv); 3688c2ecf20Sopenharmony_ci kfree(obj); 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void int3400_notify(acpi_handle handle, 3738c2ecf20Sopenharmony_ci u32 event, 3748c2ecf20Sopenharmony_ci void *data) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = data; 3778c2ecf20Sopenharmony_ci char *thermal_prop[5]; 3788c2ecf20Sopenharmony_ci int therm_event; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!priv) 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci switch (event) { 3848c2ecf20Sopenharmony_ci case INT3400_THERMAL_TABLE_CHANGED: 3858c2ecf20Sopenharmony_ci therm_event = THERMAL_TABLE_CHANGED; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci case INT3400_KEEP_ALIVE: 3888c2ecf20Sopenharmony_ci therm_event = THERMAL_EVENT_KEEP_ALIVE; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case INT3400_ODVP_CHANGED: 3918c2ecf20Sopenharmony_ci evaluate_odvp(priv); 3928c2ecf20Sopenharmony_ci therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci default: 3958c2ecf20Sopenharmony_ci /* Ignore unknown notification codes sent to INT3400 device */ 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", priv->thermal->type); 4008c2ecf20Sopenharmony_ci thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", priv->thermal->temperature); 4018c2ecf20Sopenharmony_ci thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 4028c2ecf20Sopenharmony_ci thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event); 4038c2ecf20Sopenharmony_ci thermal_prop[4] = NULL; 4048c2ecf20Sopenharmony_ci kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, thermal_prop); 4058c2ecf20Sopenharmony_ci kfree(thermal_prop[0]); 4068c2ecf20Sopenharmony_ci kfree(thermal_prop[1]); 4078c2ecf20Sopenharmony_ci kfree(thermal_prop[2]); 4088c2ecf20Sopenharmony_ci kfree(thermal_prop[3]); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 4128c2ecf20Sopenharmony_ci int *temp) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci *temp = 20 * 1000; /* faked temp sensor with 20C */ 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int int3400_thermal_change_mode(struct thermal_zone_device *thermal, 4198c2ecf20Sopenharmony_ci enum thermal_device_mode mode) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = thermal->devdata; 4228c2ecf20Sopenharmony_ci int result = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (!priv) 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (mode != thermal->mode) 4288c2ecf20Sopenharmony_ci result = int3400_thermal_run_osc(priv->adev->handle, 4298c2ecf20Sopenharmony_ci priv->current_uuid_index, 4308c2ecf20Sopenharmony_ci mode == THERMAL_DEVICE_ENABLED); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci evaluate_odvp(priv); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return result; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct thermal_zone_device_ops int3400_thermal_ops = { 4398c2ecf20Sopenharmony_ci .get_temp = int3400_thermal_get_temp, 4408c2ecf20Sopenharmony_ci .change_mode = int3400_thermal_change_mode, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic struct thermal_zone_params int3400_thermal_params = { 4448c2ecf20Sopenharmony_ci .governor_name = "user_space", 4458c2ecf20Sopenharmony_ci .no_hwmon = true, 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void int3400_setup_gddv(struct int3400_thermal_priv *priv) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 4518c2ecf20Sopenharmony_ci union acpi_object *obj; 4528c2ecf20Sopenharmony_ci acpi_status status; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 4558c2ecf20Sopenharmony_ci &buffer); 4568c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !buffer.length) 4578c2ecf20Sopenharmony_ci return; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci obj = buffer.pointer; 4608c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 4618c2ecf20Sopenharmony_ci || obj->package.elements[0].type != ACPI_TYPE_BUFFER) { 4628c2ecf20Sopenharmony_ci kfree(buffer.pointer); 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 4678c2ecf20Sopenharmony_ci obj->package.elements[0].buffer.length, 4688c2ecf20Sopenharmony_ci GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!priv->data_vault) { 4708c2ecf20Sopenharmony_ci kfree(buffer.pointer); 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci bin_attr_data_vault.private = priv->data_vault; 4758c2ecf20Sopenharmony_ci bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 4768c2ecf20Sopenharmony_ci kfree(buffer.pointer); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int int3400_thermal_probe(struct platform_device *pdev) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 4828c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv; 4838c2ecf20Sopenharmony_ci int result; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!adev) 4868c2ecf20Sopenharmony_ci return -ENODEV; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 4898c2ecf20Sopenharmony_ci if (!priv) 4908c2ecf20Sopenharmony_ci return -ENOMEM; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci priv->pdev = pdev; 4938c2ecf20Sopenharmony_ci priv->adev = adev; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci result = int3400_thermal_get_uuids(priv); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* Missing IDSP isn't fatal */ 4988c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 4998c2ecf20Sopenharmony_ci goto free_priv; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci priv->current_uuid_index = -1; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci result = acpi_parse_art(priv->adev->handle, &priv->art_count, 5048c2ecf20Sopenharmony_ci &priv->arts, true); 5058c2ecf20Sopenharmony_ci if (result) 5068c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "_ART table parsing error\n"); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 5098c2ecf20Sopenharmony_ci &priv->trts, true); 5108c2ecf20Sopenharmony_ci if (result) 5118c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci int3400_setup_gddv(priv); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci evaluate_odvp(priv); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 5208c2ecf20Sopenharmony_ci priv, &int3400_thermal_ops, 5218c2ecf20Sopenharmony_ci &int3400_thermal_params, 0, 0); 5228c2ecf20Sopenharmony_ci if (IS_ERR(priv->thermal)) { 5238c2ecf20Sopenharmony_ci result = PTR_ERR(priv->thermal); 5248c2ecf20Sopenharmony_ci goto free_art_trt; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 5288c2ecf20Sopenharmony_ci priv->adev->handle); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 5318c2ecf20Sopenharmony_ci if (result) 5328c2ecf20Sopenharmony_ci goto free_rel_misc; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (priv->data_vault) { 5358c2ecf20Sopenharmony_ci result = sysfs_create_group(&pdev->dev.kobj, 5368c2ecf20Sopenharmony_ci &data_attribute_group); 5378c2ecf20Sopenharmony_ci if (result) 5388c2ecf20Sopenharmony_ci goto free_uuid; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci result = acpi_install_notify_handler( 5428c2ecf20Sopenharmony_ci priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 5438c2ecf20Sopenharmony_ci (void *)priv); 5448c2ecf20Sopenharmony_ci if (result) 5458c2ecf20Sopenharmony_ci goto free_sysfs; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cifree_sysfs: 5508c2ecf20Sopenharmony_ci cleanup_odvp(priv); 5518c2ecf20Sopenharmony_ci if (priv->data_vault) { 5528c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 5538c2ecf20Sopenharmony_ci kfree(priv->data_vault); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_cifree_uuid: 5568c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 5578c2ecf20Sopenharmony_cifree_rel_misc: 5588c2ecf20Sopenharmony_ci if (!priv->rel_misc_dev_res) 5598c2ecf20Sopenharmony_ci acpi_thermal_rel_misc_device_remove(priv->adev->handle); 5608c2ecf20Sopenharmony_ci thermal_zone_device_unregister(priv->thermal); 5618c2ecf20Sopenharmony_cifree_art_trt: 5628c2ecf20Sopenharmony_ci kfree(priv->trts); 5638c2ecf20Sopenharmony_ci kfree(priv->arts); 5648c2ecf20Sopenharmony_cifree_priv: 5658c2ecf20Sopenharmony_ci kfree(priv); 5668c2ecf20Sopenharmony_ci return result; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int int3400_thermal_remove(struct platform_device *pdev) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci acpi_remove_notify_handler( 5748c2ecf20Sopenharmony_ci priv->adev->handle, ACPI_DEVICE_NOTIFY, 5758c2ecf20Sopenharmony_ci int3400_notify); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci cleanup_odvp(priv); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!priv->rel_misc_dev_res) 5808c2ecf20Sopenharmony_ci acpi_thermal_rel_misc_device_remove(priv->adev->handle); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (priv->data_vault) 5838c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 5848c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 5858c2ecf20Sopenharmony_ci thermal_zone_device_unregister(priv->thermal); 5868c2ecf20Sopenharmony_ci kfree(priv->data_vault); 5878c2ecf20Sopenharmony_ci kfree(priv->trts); 5888c2ecf20Sopenharmony_ci kfree(priv->arts); 5898c2ecf20Sopenharmony_ci kfree(priv); 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic const struct acpi_device_id int3400_thermal_match[] = { 5948c2ecf20Sopenharmony_ci {"INT3400", 0}, 5958c2ecf20Sopenharmony_ci {"INTC1040", 0}, 5968c2ecf20Sopenharmony_ci {} 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic struct platform_driver int3400_thermal_driver = { 6028c2ecf20Sopenharmony_ci .probe = int3400_thermal_probe, 6038c2ecf20Sopenharmony_ci .remove = int3400_thermal_remove, 6048c2ecf20Sopenharmony_ci .driver = { 6058c2ecf20Sopenharmony_ci .name = "int3400 thermal", 6068c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(int3400_thermal_match), 6078c2ecf20Sopenharmony_ci }, 6088c2ecf20Sopenharmony_ci}; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cimodule_platform_driver(int3400_thermal_driver); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("INT3400 Thermal driver"); 6138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 6148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 615