1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23e8c4d31SAmit Kucheria /* 33e8c4d31SAmit Kucheria * INT3400 thermal driver 43e8c4d31SAmit Kucheria * 53e8c4d31SAmit Kucheria * Copyright (C) 2014, Intel Corporation 63e8c4d31SAmit Kucheria * Authors: Zhang Rui <rui.zhang@intel.com> 73e8c4d31SAmit Kucheria */ 83e8c4d31SAmit Kucheria 93e8c4d31SAmit Kucheria #include <linux/module.h> 103e8c4d31SAmit Kucheria #include <linux/platform_device.h> 113e8c4d31SAmit Kucheria #include <linux/acpi.h> 123e8c4d31SAmit Kucheria #include <linux/thermal.h> 133e8c4d31SAmit Kucheria #include "acpi_thermal_rel.h" 143e8c4d31SAmit Kucheria 153e8c4d31SAmit Kucheria #define INT3400_THERMAL_TABLE_CHANGED 0x83 16006f006fSMatthew Garrett #define INT3400_ODVP_CHANGED 0x88 173e8c4d31SAmit Kucheria 183e8c4d31SAmit Kucheria enum int3400_thermal_uuid { 193e8c4d31SAmit Kucheria INT3400_THERMAL_PASSIVE_1, 203e8c4d31SAmit Kucheria INT3400_THERMAL_ACTIVE, 213e8c4d31SAmit Kucheria INT3400_THERMAL_CRITICAL, 2216fc8ecaSMatthew Garrett INT3400_THERMAL_ADAPTIVE_PERFORMANCE, 2316fc8ecaSMatthew Garrett INT3400_THERMAL_EMERGENCY_CALL_MODE, 2416fc8ecaSMatthew Garrett INT3400_THERMAL_PASSIVE_2, 2516fc8ecaSMatthew Garrett INT3400_THERMAL_POWER_BOSS, 2616fc8ecaSMatthew Garrett INT3400_THERMAL_VIRTUAL_SENSOR, 2716fc8ecaSMatthew Garrett INT3400_THERMAL_COOLING_MODE, 2816fc8ecaSMatthew Garrett INT3400_THERMAL_HARDWARE_DUTY_CYCLING, 293e8c4d31SAmit Kucheria INT3400_THERMAL_MAXIMUM_UUID, 303e8c4d31SAmit Kucheria }; 313e8c4d31SAmit Kucheria 323e8c4d31SAmit Kucheria static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { 333e8c4d31SAmit Kucheria "42A441D6-AE6A-462b-A84B-4A8CE79027D3", 343e8c4d31SAmit Kucheria "3A95C389-E4B8-4629-A526-C52C88626BAE", 353e8c4d31SAmit Kucheria "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", 3616fc8ecaSMatthew Garrett "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D", 3716fc8ecaSMatthew Garrett "5349962F-71E6-431D-9AE8-0A635B710AEE", 3816fc8ecaSMatthew Garrett "9E04115A-AE87-4D1C-9500-0F3E340BFE75", 3916fc8ecaSMatthew Garrett "F5A35014-C209-46A4-993A-EB56DE7530A1", 4016fc8ecaSMatthew Garrett "6ED722A7-9240-48A5-B479-31EEF723D7CF", 4116fc8ecaSMatthew Garrett "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531", 4216fc8ecaSMatthew Garrett "BE84BABF-C4D4-403D-B495-3128FD44dAC1", 433e8c4d31SAmit Kucheria }; 443e8c4d31SAmit Kucheria 45006f006fSMatthew Garrett struct odvp_attr; 46006f006fSMatthew Garrett 473e8c4d31SAmit Kucheria struct int3400_thermal_priv { 483e8c4d31SAmit Kucheria struct acpi_device *adev; 49006f006fSMatthew Garrett struct platform_device *pdev; 503e8c4d31SAmit Kucheria struct thermal_zone_device *thermal; 513e8c4d31SAmit Kucheria int art_count; 523e8c4d31SAmit Kucheria struct art *arts; 533e8c4d31SAmit Kucheria int trt_count; 543e8c4d31SAmit Kucheria struct trt *trts; 553e8c4d31SAmit Kucheria u8 uuid_bitmap; 563e8c4d31SAmit Kucheria int rel_misc_dev_res; 573e8c4d31SAmit Kucheria int current_uuid_index; 580ba13c76SMatthew Garrett char *data_vault; 59006f006fSMatthew Garrett int odvp_count; 60006f006fSMatthew Garrett int *odvp; 61006f006fSMatthew Garrett struct odvp_attr *odvp_attrs; 62006f006fSMatthew Garrett }; 63006f006fSMatthew Garrett 64006f006fSMatthew Garrett static int evaluate_odvp(struct int3400_thermal_priv *priv); 65006f006fSMatthew Garrett 66006f006fSMatthew Garrett struct odvp_attr { 67006f006fSMatthew Garrett int odvp; 68006f006fSMatthew Garrett struct int3400_thermal_priv *priv; 69006f006fSMatthew Garrett struct kobj_attribute attr; 700ba13c76SMatthew Garrett }; 710ba13c76SMatthew Garrett 720ba13c76SMatthew Garrett static ssize_t data_vault_read(struct file *file, struct kobject *kobj, 730ba13c76SMatthew Garrett struct bin_attribute *attr, char *buf, loff_t off, size_t count) 740ba13c76SMatthew Garrett { 750ba13c76SMatthew Garrett memcpy(buf, attr->private + off, count); 760ba13c76SMatthew Garrett return count; 770ba13c76SMatthew Garrett } 780ba13c76SMatthew Garrett 790ba13c76SMatthew Garrett static BIN_ATTR_RO(data_vault, 0); 800ba13c76SMatthew Garrett 810ba13c76SMatthew Garrett static struct bin_attribute *data_attributes[] = { 820ba13c76SMatthew Garrett &bin_attr_data_vault, 830ba13c76SMatthew Garrett NULL, 840ba13c76SMatthew Garrett }; 850ba13c76SMatthew Garrett 860ba13c76SMatthew Garrett static const struct attribute_group data_attribute_group = { 870ba13c76SMatthew Garrett .bin_attrs = data_attributes, 883e8c4d31SAmit Kucheria }; 893e8c4d31SAmit Kucheria 903e8c4d31SAmit Kucheria static ssize_t available_uuids_show(struct device *dev, 913e8c4d31SAmit Kucheria struct device_attribute *attr, 923e8c4d31SAmit Kucheria char *buf) 933e8c4d31SAmit Kucheria { 943e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 953e8c4d31SAmit Kucheria int i; 963e8c4d31SAmit Kucheria int length = 0; 973e8c4d31SAmit Kucheria 988d485da0SMatthew Garrett if (!priv->uuid_bitmap) 998d485da0SMatthew Garrett return sprintf(buf, "UNKNOWN\n"); 1008d485da0SMatthew Garrett 1013e8c4d31SAmit Kucheria for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) { 1023e8c4d31SAmit Kucheria if (priv->uuid_bitmap & (1 << i)) 1033e8c4d31SAmit Kucheria if (PAGE_SIZE - length > 0) 104f21431f2STakashi Iwai length += scnprintf(&buf[length], 1053e8c4d31SAmit Kucheria PAGE_SIZE - length, 1063e8c4d31SAmit Kucheria "%s\n", 1073e8c4d31SAmit Kucheria int3400_thermal_uuids[i]); 1083e8c4d31SAmit Kucheria } 1093e8c4d31SAmit Kucheria 1103e8c4d31SAmit Kucheria return length; 1113e8c4d31SAmit Kucheria } 1123e8c4d31SAmit Kucheria 1133e8c4d31SAmit Kucheria static ssize_t current_uuid_show(struct device *dev, 1143e8c4d31SAmit Kucheria struct device_attribute *devattr, char *buf) 1153e8c4d31SAmit Kucheria { 1163e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 1173e8c4d31SAmit Kucheria 1188d485da0SMatthew Garrett if (priv->current_uuid_index == -1) 1198d485da0SMatthew Garrett return sprintf(buf, "INVALID\n"); 1208d485da0SMatthew Garrett 1213e8c4d31SAmit Kucheria return sprintf(buf, "%s\n", 1223e8c4d31SAmit Kucheria int3400_thermal_uuids[priv->current_uuid_index]); 1233e8c4d31SAmit Kucheria } 1243e8c4d31SAmit Kucheria 1253e8c4d31SAmit Kucheria static ssize_t current_uuid_store(struct device *dev, 1263e8c4d31SAmit Kucheria struct device_attribute *attr, 1273e8c4d31SAmit Kucheria const char *buf, size_t count) 1283e8c4d31SAmit Kucheria { 1293e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = dev_get_drvdata(dev); 1303e8c4d31SAmit Kucheria int i; 1313e8c4d31SAmit Kucheria 1323e8c4d31SAmit Kucheria for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) { 1338d485da0SMatthew Garrett if (!strncmp(buf, int3400_thermal_uuids[i], 1348d485da0SMatthew Garrett sizeof(int3400_thermal_uuids[i]) - 1)) { 1358d485da0SMatthew Garrett /* 1368d485da0SMatthew Garrett * If we have a list of supported UUIDs, make sure 1378d485da0SMatthew Garrett * this one is supported. 1388d485da0SMatthew Garrett */ 1398d485da0SMatthew Garrett if (priv->uuid_bitmap && 1408d485da0SMatthew Garrett !(priv->uuid_bitmap & (1 << i))) 1418d485da0SMatthew Garrett return -EINVAL; 1428d485da0SMatthew Garrett 1433e8c4d31SAmit Kucheria priv->current_uuid_index = i; 1443e8c4d31SAmit Kucheria return count; 1453e8c4d31SAmit Kucheria } 1463e8c4d31SAmit Kucheria } 1473e8c4d31SAmit Kucheria 1483e8c4d31SAmit Kucheria return -EINVAL; 1493e8c4d31SAmit Kucheria } 1503e8c4d31SAmit Kucheria 1513e8c4d31SAmit Kucheria static DEVICE_ATTR_RW(current_uuid); 1523e8c4d31SAmit Kucheria static DEVICE_ATTR_RO(available_uuids); 1533e8c4d31SAmit Kucheria static struct attribute *uuid_attrs[] = { 1543e8c4d31SAmit Kucheria &dev_attr_available_uuids.attr, 1553e8c4d31SAmit Kucheria &dev_attr_current_uuid.attr, 1563e8c4d31SAmit Kucheria NULL 1573e8c4d31SAmit Kucheria }; 1583e8c4d31SAmit Kucheria 1593e8c4d31SAmit Kucheria static const struct attribute_group uuid_attribute_group = { 1603e8c4d31SAmit Kucheria .attrs = uuid_attrs, 1613e8c4d31SAmit Kucheria .name = "uuids" 1623e8c4d31SAmit Kucheria }; 1633e8c4d31SAmit Kucheria 1643e8c4d31SAmit Kucheria static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) 1653e8c4d31SAmit Kucheria { 1663e8c4d31SAmit Kucheria struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; 1673e8c4d31SAmit Kucheria union acpi_object *obja, *objb; 1683e8c4d31SAmit Kucheria int i, j; 1693e8c4d31SAmit Kucheria int result = 0; 1703e8c4d31SAmit Kucheria acpi_status status; 1713e8c4d31SAmit Kucheria 1723e8c4d31SAmit Kucheria status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); 1733e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) 1743e8c4d31SAmit Kucheria return -ENODEV; 1753e8c4d31SAmit Kucheria 1763e8c4d31SAmit Kucheria obja = (union acpi_object *)buf.pointer; 1773e8c4d31SAmit Kucheria if (obja->type != ACPI_TYPE_PACKAGE) { 1783e8c4d31SAmit Kucheria result = -EINVAL; 1793e8c4d31SAmit Kucheria goto end; 1803e8c4d31SAmit Kucheria } 1813e8c4d31SAmit Kucheria 1823e8c4d31SAmit Kucheria for (i = 0; i < obja->package.count; i++) { 1833e8c4d31SAmit Kucheria objb = &obja->package.elements[i]; 1843e8c4d31SAmit Kucheria if (objb->type != ACPI_TYPE_BUFFER) { 1853e8c4d31SAmit Kucheria result = -EINVAL; 1863e8c4d31SAmit Kucheria goto end; 1873e8c4d31SAmit Kucheria } 1883e8c4d31SAmit Kucheria 1893e8c4d31SAmit Kucheria /* UUID must be 16 bytes */ 1903e8c4d31SAmit Kucheria if (objb->buffer.length != 16) { 1913e8c4d31SAmit Kucheria result = -EINVAL; 1923e8c4d31SAmit Kucheria goto end; 1933e8c4d31SAmit Kucheria } 1943e8c4d31SAmit Kucheria 1953e8c4d31SAmit Kucheria for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { 1963e8c4d31SAmit Kucheria guid_t guid; 1973e8c4d31SAmit Kucheria 1983e8c4d31SAmit Kucheria guid_parse(int3400_thermal_uuids[j], &guid); 1993e8c4d31SAmit Kucheria if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) { 2003e8c4d31SAmit Kucheria priv->uuid_bitmap |= (1 << j); 2013e8c4d31SAmit Kucheria break; 2023e8c4d31SAmit Kucheria } 2033e8c4d31SAmit Kucheria } 2043e8c4d31SAmit Kucheria } 2053e8c4d31SAmit Kucheria 2063e8c4d31SAmit Kucheria end: 2073e8c4d31SAmit Kucheria kfree(buf.pointer); 2083e8c4d31SAmit Kucheria return result; 2093e8c4d31SAmit Kucheria } 2103e8c4d31SAmit Kucheria 2113e8c4d31SAmit Kucheria static int int3400_thermal_run_osc(acpi_handle handle, 2123e8c4d31SAmit Kucheria enum int3400_thermal_uuid uuid, bool enable) 2133e8c4d31SAmit Kucheria { 2143e8c4d31SAmit Kucheria u32 ret, buf[2]; 2153e8c4d31SAmit Kucheria acpi_status status; 2163e8c4d31SAmit Kucheria int result = 0; 2173e8c4d31SAmit Kucheria struct acpi_osc_context context = { 2183e8c4d31SAmit Kucheria .uuid_str = int3400_thermal_uuids[uuid], 2193e8c4d31SAmit Kucheria .rev = 1, 2203e8c4d31SAmit Kucheria .cap.length = 8, 2213e8c4d31SAmit Kucheria }; 2223e8c4d31SAmit Kucheria 2233e8c4d31SAmit Kucheria buf[OSC_QUERY_DWORD] = 0; 2243e8c4d31SAmit Kucheria buf[OSC_SUPPORT_DWORD] = enable; 2253e8c4d31SAmit Kucheria 2263e8c4d31SAmit Kucheria context.cap.pointer = buf; 2273e8c4d31SAmit Kucheria 2283e8c4d31SAmit Kucheria status = acpi_run_osc(handle, &context); 2293e8c4d31SAmit Kucheria if (ACPI_SUCCESS(status)) { 2303e8c4d31SAmit Kucheria ret = *((u32 *)(context.ret.pointer + 4)); 2313e8c4d31SAmit Kucheria if (ret != enable) 2323e8c4d31SAmit Kucheria result = -EPERM; 2333e8c4d31SAmit Kucheria } else 2343e8c4d31SAmit Kucheria result = -EPERM; 2353e8c4d31SAmit Kucheria 2363e8c4d31SAmit Kucheria kfree(context.ret.pointer); 237006f006fSMatthew Garrett 2383e8c4d31SAmit Kucheria return result; 2393e8c4d31SAmit Kucheria } 2403e8c4d31SAmit Kucheria 241006f006fSMatthew Garrett static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr, 242006f006fSMatthew Garrett char *buf) 243006f006fSMatthew Garrett { 244006f006fSMatthew Garrett struct odvp_attr *odvp_attr; 245006f006fSMatthew Garrett 246006f006fSMatthew Garrett odvp_attr = container_of(attr, struct odvp_attr, attr); 247006f006fSMatthew Garrett 248006f006fSMatthew Garrett return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); 249006f006fSMatthew Garrett } 250006f006fSMatthew Garrett 251006f006fSMatthew Garrett static void cleanup_odvp(struct int3400_thermal_priv *priv) 252006f006fSMatthew Garrett { 253006f006fSMatthew Garrett int i; 254006f006fSMatthew Garrett 255006f006fSMatthew Garrett if (priv->odvp_attrs) { 256006f006fSMatthew Garrett for (i = 0; i < priv->odvp_count; i++) { 257006f006fSMatthew Garrett sysfs_remove_file(&priv->pdev->dev.kobj, 258006f006fSMatthew Garrett &priv->odvp_attrs[i].attr.attr); 259006f006fSMatthew Garrett kfree(priv->odvp_attrs[i].attr.attr.name); 260006f006fSMatthew Garrett } 261006f006fSMatthew Garrett kfree(priv->odvp_attrs); 262006f006fSMatthew Garrett } 263006f006fSMatthew Garrett kfree(priv->odvp); 264006f006fSMatthew Garrett priv->odvp_count = 0; 265006f006fSMatthew Garrett } 266006f006fSMatthew Garrett 267006f006fSMatthew Garrett static int evaluate_odvp(struct int3400_thermal_priv *priv) 268006f006fSMatthew Garrett { 269006f006fSMatthew Garrett struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; 270006f006fSMatthew Garrett union acpi_object *obj = NULL; 271006f006fSMatthew Garrett acpi_status status; 272006f006fSMatthew Garrett int i, ret; 273006f006fSMatthew Garrett 274006f006fSMatthew Garrett status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); 275006f006fSMatthew Garrett if (ACPI_FAILURE(status)) { 276006f006fSMatthew Garrett ret = -EINVAL; 277006f006fSMatthew Garrett goto out_err; 278006f006fSMatthew Garrett } 279006f006fSMatthew Garrett 280006f006fSMatthew Garrett obj = odvp.pointer; 281006f006fSMatthew Garrett if (obj->type != ACPI_TYPE_PACKAGE) { 282006f006fSMatthew Garrett ret = -EINVAL; 283006f006fSMatthew Garrett goto out_err; 284006f006fSMatthew Garrett } 285006f006fSMatthew Garrett 286006f006fSMatthew Garrett if (priv->odvp == NULL) { 287006f006fSMatthew Garrett priv->odvp_count = obj->package.count; 288006f006fSMatthew Garrett priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), 289006f006fSMatthew Garrett GFP_KERNEL); 290006f006fSMatthew Garrett if (!priv->odvp) { 291006f006fSMatthew Garrett ret = -ENOMEM; 292006f006fSMatthew Garrett goto out_err; 293006f006fSMatthew Garrett } 294006f006fSMatthew Garrett } 295006f006fSMatthew Garrett 296006f006fSMatthew Garrett if (priv->odvp_attrs == NULL) { 297006f006fSMatthew Garrett priv->odvp_attrs = kcalloc(priv->odvp_count, 298006f006fSMatthew Garrett sizeof(struct odvp_attr), 299006f006fSMatthew Garrett GFP_KERNEL); 300006f006fSMatthew Garrett if (!priv->odvp_attrs) { 301006f006fSMatthew Garrett ret = -ENOMEM; 302006f006fSMatthew Garrett goto out_err; 303006f006fSMatthew Garrett } 304006f006fSMatthew Garrett for (i = 0; i < priv->odvp_count; i++) { 305006f006fSMatthew Garrett struct odvp_attr *odvp = &priv->odvp_attrs[i]; 306006f006fSMatthew Garrett 307006f006fSMatthew Garrett sysfs_attr_init(&odvp->attr.attr); 308006f006fSMatthew Garrett odvp->priv = priv; 309006f006fSMatthew Garrett odvp->odvp = i; 310006f006fSMatthew Garrett odvp->attr.attr.name = kasprintf(GFP_KERNEL, 311006f006fSMatthew Garrett "odvp%d", i); 312006f006fSMatthew Garrett 313006f006fSMatthew Garrett if (!odvp->attr.attr.name) { 314006f006fSMatthew Garrett ret = -ENOMEM; 315006f006fSMatthew Garrett goto out_err; 316006f006fSMatthew Garrett } 317006f006fSMatthew Garrett odvp->attr.attr.mode = 0444; 318006f006fSMatthew Garrett odvp->attr.show = odvp_show; 319006f006fSMatthew Garrett odvp->attr.store = NULL; 320006f006fSMatthew Garrett ret = sysfs_create_file(&priv->pdev->dev.kobj, 321006f006fSMatthew Garrett &odvp->attr.attr); 322006f006fSMatthew Garrett if (ret) 323006f006fSMatthew Garrett goto out_err; 324006f006fSMatthew Garrett } 325006f006fSMatthew Garrett } 326006f006fSMatthew Garrett 327006f006fSMatthew Garrett for (i = 0; i < obj->package.count; i++) { 328006f006fSMatthew Garrett if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) 329006f006fSMatthew Garrett priv->odvp[i] = obj->package.elements[i].integer.value; 330006f006fSMatthew Garrett } 331006f006fSMatthew Garrett 332006f006fSMatthew Garrett kfree(obj); 333006f006fSMatthew Garrett return 0; 334006f006fSMatthew Garrett 335006f006fSMatthew Garrett out_err: 336006f006fSMatthew Garrett cleanup_odvp(priv); 337006f006fSMatthew Garrett kfree(obj); 338006f006fSMatthew Garrett return ret; 339006f006fSMatthew Garrett } 340006f006fSMatthew Garrett 3413e8c4d31SAmit Kucheria static void int3400_notify(acpi_handle handle, 3423e8c4d31SAmit Kucheria u32 event, 3433e8c4d31SAmit Kucheria void *data) 3443e8c4d31SAmit Kucheria { 3453e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = data; 3463e8c4d31SAmit Kucheria char *thermal_prop[5]; 3473e8c4d31SAmit Kucheria 3483e8c4d31SAmit Kucheria if (!priv) 3493e8c4d31SAmit Kucheria return; 3503e8c4d31SAmit Kucheria 3513e8c4d31SAmit Kucheria switch (event) { 3523e8c4d31SAmit Kucheria case INT3400_THERMAL_TABLE_CHANGED: 3533e8c4d31SAmit Kucheria thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", 3543e8c4d31SAmit Kucheria priv->thermal->type); 3553e8c4d31SAmit Kucheria thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", 3563e8c4d31SAmit Kucheria priv->thermal->temperature); 3573e8c4d31SAmit Kucheria thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP="); 3583e8c4d31SAmit Kucheria thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", 3593e8c4d31SAmit Kucheria THERMAL_TABLE_CHANGED); 3603e8c4d31SAmit Kucheria thermal_prop[4] = NULL; 3613e8c4d31SAmit Kucheria kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, 3623e8c4d31SAmit Kucheria thermal_prop); 3633e8c4d31SAmit Kucheria break; 364006f006fSMatthew Garrett case INT3400_ODVP_CHANGED: 365006f006fSMatthew Garrett evaluate_odvp(priv); 366006f006fSMatthew Garrett break; 3673e8c4d31SAmit Kucheria default: 3683e8c4d31SAmit Kucheria /* Ignore unknown notification codes sent to INT3400 device */ 3693e8c4d31SAmit Kucheria break; 3703e8c4d31SAmit Kucheria } 3713e8c4d31SAmit Kucheria } 3723e8c4d31SAmit Kucheria 3733e8c4d31SAmit Kucheria static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, 3743e8c4d31SAmit Kucheria int *temp) 3753e8c4d31SAmit Kucheria { 3763e8c4d31SAmit Kucheria *temp = 20 * 1000; /* faked temp sensor with 20C */ 3773e8c4d31SAmit Kucheria return 0; 3783e8c4d31SAmit Kucheria } 3793e8c4d31SAmit Kucheria 3803e8c4d31SAmit Kucheria static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, 3813e8c4d31SAmit Kucheria enum thermal_device_mode mode) 3823e8c4d31SAmit Kucheria { 3833e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = thermal->devdata; 3843e8c4d31SAmit Kucheria int result = 0; 3853e8c4d31SAmit Kucheria 3863e8c4d31SAmit Kucheria if (!priv) 3873e8c4d31SAmit Kucheria return -EINVAL; 3883e8c4d31SAmit Kucheria 3891595d887SAndrzej Pietrasiewicz if (mode != THERMAL_DEVICE_ENABLED && 3901595d887SAndrzej Pietrasiewicz mode != THERMAL_DEVICE_DISABLED) 3913e8c4d31SAmit Kucheria return -EINVAL; 3923e8c4d31SAmit Kucheria 3937f4957beSAndrzej Pietrasiewicz if (mode != thermal->mode) 3943e8c4d31SAmit Kucheria result = int3400_thermal_run_osc(priv->adev->handle, 3953e8c4d31SAmit Kucheria priv->current_uuid_index, 3961595d887SAndrzej Pietrasiewicz mode == THERMAL_DEVICE_ENABLED); 3977f4957beSAndrzej Pietrasiewicz 398006f006fSMatthew Garrett 399006f006fSMatthew Garrett evaluate_odvp(priv); 400006f006fSMatthew Garrett 4013e8c4d31SAmit Kucheria return result; 4023e8c4d31SAmit Kucheria } 4033e8c4d31SAmit Kucheria 4043e8c4d31SAmit Kucheria static struct thermal_zone_device_ops int3400_thermal_ops = { 4053e8c4d31SAmit Kucheria .get_temp = int3400_thermal_get_temp, 40679799562SAndrzej Pietrasiewicz .set_mode = int3400_thermal_set_mode, 4073e8c4d31SAmit Kucheria }; 4083e8c4d31SAmit Kucheria 4093e8c4d31SAmit Kucheria static struct thermal_zone_params int3400_thermal_params = { 4103e8c4d31SAmit Kucheria .governor_name = "user_space", 4113e8c4d31SAmit Kucheria .no_hwmon = true, 4123e8c4d31SAmit Kucheria }; 4133e8c4d31SAmit Kucheria 4140ba13c76SMatthew Garrett static void int3400_setup_gddv(struct int3400_thermal_priv *priv) 4150ba13c76SMatthew Garrett { 4160ba13c76SMatthew Garrett struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 4170ba13c76SMatthew Garrett union acpi_object *obj; 4180ba13c76SMatthew Garrett acpi_status status; 4190ba13c76SMatthew Garrett 4200ba13c76SMatthew Garrett status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL, 4210ba13c76SMatthew Garrett &buffer); 4220ba13c76SMatthew Garrett if (ACPI_FAILURE(status) || !buffer.length) 4230ba13c76SMatthew Garrett return; 4240ba13c76SMatthew Garrett 4250ba13c76SMatthew Garrett obj = buffer.pointer; 4260ba13c76SMatthew Garrett if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1 4270ba13c76SMatthew Garrett || obj->package.elements[0].type != ACPI_TYPE_BUFFER) { 4280ba13c76SMatthew Garrett kfree(buffer.pointer); 4290ba13c76SMatthew Garrett return; 4300ba13c76SMatthew Garrett } 4310ba13c76SMatthew Garrett 4320ba13c76SMatthew Garrett priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, 4330ba13c76SMatthew Garrett obj->package.elements[0].buffer.length, 4340ba13c76SMatthew Garrett GFP_KERNEL); 4350ba13c76SMatthew Garrett bin_attr_data_vault.private = priv->data_vault; 4360ba13c76SMatthew Garrett bin_attr_data_vault.size = obj->package.elements[0].buffer.length; 4370ba13c76SMatthew Garrett kfree(buffer.pointer); 4380ba13c76SMatthew Garrett } 4390ba13c76SMatthew Garrett 4403e8c4d31SAmit Kucheria static int int3400_thermal_probe(struct platform_device *pdev) 4413e8c4d31SAmit Kucheria { 4423e8c4d31SAmit Kucheria struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 4433e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv; 4443e8c4d31SAmit Kucheria int result; 4453e8c4d31SAmit Kucheria 4463e8c4d31SAmit Kucheria if (!adev) 4473e8c4d31SAmit Kucheria return -ENODEV; 4483e8c4d31SAmit Kucheria 4493e8c4d31SAmit Kucheria priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); 4503e8c4d31SAmit Kucheria if (!priv) 4513e8c4d31SAmit Kucheria return -ENOMEM; 4523e8c4d31SAmit Kucheria 453006f006fSMatthew Garrett priv->pdev = pdev; 4543e8c4d31SAmit Kucheria priv->adev = adev; 4553e8c4d31SAmit Kucheria 4563e8c4d31SAmit Kucheria result = int3400_thermal_get_uuids(priv); 4578d485da0SMatthew Garrett 4588d485da0SMatthew Garrett /* Missing IDSP isn't fatal */ 4598d485da0SMatthew Garrett if (result && result != -ENODEV) 4603e8c4d31SAmit Kucheria goto free_priv; 4613e8c4d31SAmit Kucheria 4628d485da0SMatthew Garrett priv->current_uuid_index = -1; 4638d485da0SMatthew Garrett 4643e8c4d31SAmit Kucheria result = acpi_parse_art(priv->adev->handle, &priv->art_count, 4653e8c4d31SAmit Kucheria &priv->arts, true); 4663e8c4d31SAmit Kucheria if (result) 4673e8c4d31SAmit Kucheria dev_dbg(&pdev->dev, "_ART table parsing error\n"); 4683e8c4d31SAmit Kucheria 4693e8c4d31SAmit Kucheria result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, 4703e8c4d31SAmit Kucheria &priv->trts, true); 4713e8c4d31SAmit Kucheria if (result) 4723e8c4d31SAmit Kucheria dev_dbg(&pdev->dev, "_TRT table parsing error\n"); 4733e8c4d31SAmit Kucheria 4743e8c4d31SAmit Kucheria platform_set_drvdata(pdev, priv); 4753e8c4d31SAmit Kucheria 4760ba13c76SMatthew Garrett int3400_setup_gddv(priv); 4770ba13c76SMatthew Garrett 478006f006fSMatthew Garrett evaluate_odvp(priv); 479006f006fSMatthew Garrett 4803e8c4d31SAmit Kucheria priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, 4813e8c4d31SAmit Kucheria priv, &int3400_thermal_ops, 4823e8c4d31SAmit Kucheria &int3400_thermal_params, 0, 0); 4833e8c4d31SAmit Kucheria if (IS_ERR(priv->thermal)) { 4843e8c4d31SAmit Kucheria result = PTR_ERR(priv->thermal); 4853e8c4d31SAmit Kucheria goto free_art_trt; 4863e8c4d31SAmit Kucheria } 4873e8c4d31SAmit Kucheria 4883e8c4d31SAmit Kucheria priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( 4893e8c4d31SAmit Kucheria priv->adev->handle); 4903e8c4d31SAmit Kucheria 4913e8c4d31SAmit Kucheria result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group); 4923e8c4d31SAmit Kucheria if (result) 4933e8c4d31SAmit Kucheria goto free_rel_misc; 4943e8c4d31SAmit Kucheria 4950ba13c76SMatthew Garrett if (priv->data_vault) { 4960ba13c76SMatthew Garrett result = sysfs_create_group(&pdev->dev.kobj, 4970ba13c76SMatthew Garrett &data_attribute_group); 4980ba13c76SMatthew Garrett if (result) 4990ba13c76SMatthew Garrett goto free_uuid; 5000ba13c76SMatthew Garrett } 5010ba13c76SMatthew Garrett 5023e8c4d31SAmit Kucheria result = acpi_install_notify_handler( 5033e8c4d31SAmit Kucheria priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify, 5043e8c4d31SAmit Kucheria (void *)priv); 5053e8c4d31SAmit Kucheria if (result) 5063e8c4d31SAmit Kucheria goto free_sysfs; 5073e8c4d31SAmit Kucheria 5083e8c4d31SAmit Kucheria return 0; 5093e8c4d31SAmit Kucheria 5103e8c4d31SAmit Kucheria free_sysfs: 511006f006fSMatthew Garrett cleanup_odvp(priv); 512006f006fSMatthew Garrett if (priv->data_vault) { 5130ba13c76SMatthew Garrett sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 514006f006fSMatthew Garrett kfree(priv->data_vault); 515006f006fSMatthew Garrett } 5160ba13c76SMatthew Garrett free_uuid: 5173e8c4d31SAmit Kucheria sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 5183e8c4d31SAmit Kucheria free_rel_misc: 5193e8c4d31SAmit Kucheria if (!priv->rel_misc_dev_res) 5203e8c4d31SAmit Kucheria acpi_thermal_rel_misc_device_remove(priv->adev->handle); 5213e8c4d31SAmit Kucheria thermal_zone_device_unregister(priv->thermal); 5223e8c4d31SAmit Kucheria free_art_trt: 5233e8c4d31SAmit Kucheria kfree(priv->trts); 5243e8c4d31SAmit Kucheria kfree(priv->arts); 5253e8c4d31SAmit Kucheria free_priv: 5263e8c4d31SAmit Kucheria kfree(priv); 5273e8c4d31SAmit Kucheria return result; 5283e8c4d31SAmit Kucheria } 5293e8c4d31SAmit Kucheria 5303e8c4d31SAmit Kucheria static int int3400_thermal_remove(struct platform_device *pdev) 5313e8c4d31SAmit Kucheria { 5323e8c4d31SAmit Kucheria struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); 5333e8c4d31SAmit Kucheria 5343e8c4d31SAmit Kucheria acpi_remove_notify_handler( 5353e8c4d31SAmit Kucheria priv->adev->handle, ACPI_DEVICE_NOTIFY, 5363e8c4d31SAmit Kucheria int3400_notify); 5373e8c4d31SAmit Kucheria 538006f006fSMatthew Garrett cleanup_odvp(priv); 539006f006fSMatthew Garrett 5403e8c4d31SAmit Kucheria if (!priv->rel_misc_dev_res) 5413e8c4d31SAmit Kucheria acpi_thermal_rel_misc_device_remove(priv->adev->handle); 5423e8c4d31SAmit Kucheria 5430ba13c76SMatthew Garrett if (priv->data_vault) 5440ba13c76SMatthew Garrett sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 5453e8c4d31SAmit Kucheria sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 5463e8c4d31SAmit Kucheria thermal_zone_device_unregister(priv->thermal); 5470ba13c76SMatthew Garrett kfree(priv->data_vault); 5483e8c4d31SAmit Kucheria kfree(priv->trts); 5493e8c4d31SAmit Kucheria kfree(priv->arts); 5503e8c4d31SAmit Kucheria kfree(priv); 5513e8c4d31SAmit Kucheria return 0; 5523e8c4d31SAmit Kucheria } 5533e8c4d31SAmit Kucheria 5543e8c4d31SAmit Kucheria static const struct acpi_device_id int3400_thermal_match[] = { 5553e8c4d31SAmit Kucheria {"INT3400", 0}, 55626d8bec1SGayatri Kammela {"INTC1040", 0}, 5573e8c4d31SAmit Kucheria {} 5583e8c4d31SAmit Kucheria }; 5593e8c4d31SAmit Kucheria 5603e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); 5613e8c4d31SAmit Kucheria 5623e8c4d31SAmit Kucheria static struct platform_driver int3400_thermal_driver = { 5633e8c4d31SAmit Kucheria .probe = int3400_thermal_probe, 5643e8c4d31SAmit Kucheria .remove = int3400_thermal_remove, 5653e8c4d31SAmit Kucheria .driver = { 5663e8c4d31SAmit Kucheria .name = "int3400 thermal", 5673e8c4d31SAmit Kucheria .acpi_match_table = ACPI_PTR(int3400_thermal_match), 5683e8c4d31SAmit Kucheria }, 5693e8c4d31SAmit Kucheria }; 5703e8c4d31SAmit Kucheria 5713e8c4d31SAmit Kucheria module_platform_driver(int3400_thermal_driver); 5723e8c4d31SAmit Kucheria 5733e8c4d31SAmit Kucheria MODULE_DESCRIPTION("INT3400 Thermal driver"); 5743e8c4d31SAmit Kucheria MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 5753e8c4d31SAmit Kucheria MODULE_LICENSE("GPL"); 576