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
1794a3c35eSSrinivas Pandruvada #define INT3400_KEEP_ALIVE 0xA0
1812ad93abSDaniel Lezcano #define INT3400_FAKE_TEMP (20 * 1000) /* faked temp sensor with 20C */
193e8c4d31SAmit Kucheria 
203e8c4d31SAmit Kucheria enum int3400_thermal_uuid {
21c7ff2976SSrinivas Pandruvada 	INT3400_THERMAL_ACTIVE = 0,
223e8c4d31SAmit Kucheria 	INT3400_THERMAL_PASSIVE_1,
233e8c4d31SAmit Kucheria 	INT3400_THERMAL_CRITICAL,
2416fc8ecaSMatthew Garrett 	INT3400_THERMAL_ADAPTIVE_PERFORMANCE,
2516fc8ecaSMatthew Garrett 	INT3400_THERMAL_EMERGENCY_CALL_MODE,
2616fc8ecaSMatthew Garrett 	INT3400_THERMAL_PASSIVE_2,
2716fc8ecaSMatthew Garrett 	INT3400_THERMAL_POWER_BOSS,
2816fc8ecaSMatthew Garrett 	INT3400_THERMAL_VIRTUAL_SENSOR,
2916fc8ecaSMatthew Garrett 	INT3400_THERMAL_COOLING_MODE,
3016fc8ecaSMatthew Garrett 	INT3400_THERMAL_HARDWARE_DUTY_CYCLING,
313e8c4d31SAmit Kucheria 	INT3400_THERMAL_MAXIMUM_UUID,
323e8c4d31SAmit Kucheria };
333e8c4d31SAmit Kucheria 
343e8c4d31SAmit Kucheria static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
353e8c4d31SAmit Kucheria 	"3A95C389-E4B8-4629-A526-C52C88626BAE",
36c7ff2976SSrinivas Pandruvada 	"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
373e8c4d31SAmit Kucheria 	"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
3816fc8ecaSMatthew Garrett 	"63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",
3916fc8ecaSMatthew Garrett 	"5349962F-71E6-431D-9AE8-0A635B710AEE",
4016fc8ecaSMatthew Garrett 	"9E04115A-AE87-4D1C-9500-0F3E340BFE75",
4116fc8ecaSMatthew Garrett 	"F5A35014-C209-46A4-993A-EB56DE7530A1",
4216fc8ecaSMatthew Garrett 	"6ED722A7-9240-48A5-B479-31EEF723D7CF",
4316fc8ecaSMatthew Garrett 	"16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",
4416fc8ecaSMatthew Garrett 	"BE84BABF-C4D4-403D-B495-3128FD44dAC1",
453e8c4d31SAmit Kucheria };
463e8c4d31SAmit Kucheria 
47006f006fSMatthew Garrett struct odvp_attr;
48006f006fSMatthew Garrett 
493e8c4d31SAmit Kucheria struct int3400_thermal_priv {
503e8c4d31SAmit Kucheria 	struct acpi_device *adev;
51006f006fSMatthew Garrett 	struct platform_device *pdev;
523e8c4d31SAmit Kucheria 	struct thermal_zone_device *thermal;
533e8c4d31SAmit Kucheria 	int art_count;
543e8c4d31SAmit Kucheria 	struct art *arts;
553e8c4d31SAmit Kucheria 	int trt_count;
563e8c4d31SAmit Kucheria 	struct trt *trts;
57668f69a5SSrinivas Pandruvada 	u32 uuid_bitmap;
583e8c4d31SAmit Kucheria 	int rel_misc_dev_res;
593e8c4d31SAmit Kucheria 	int current_uuid_index;
600ba13c76SMatthew Garrett 	char *data_vault;
61006f006fSMatthew Garrett 	int odvp_count;
62006f006fSMatthew Garrett 	int *odvp;
63c7ff2976SSrinivas Pandruvada 	u32 os_uuid_mask;
645c36cf27SSrinivas Pandruvada 	int production_mode;
65006f006fSMatthew Garrett 	struct odvp_attr *odvp_attrs;
66006f006fSMatthew Garrett };
67006f006fSMatthew Garrett 
68006f006fSMatthew Garrett static int evaluate_odvp(struct int3400_thermal_priv *priv);
69006f006fSMatthew Garrett 
70006f006fSMatthew Garrett struct odvp_attr {
71006f006fSMatthew Garrett 	int odvp;
72006f006fSMatthew Garrett 	struct int3400_thermal_priv *priv;
73d0f6cfb2SKees Cook 	struct device_attribute attr;
740ba13c76SMatthew Garrett };
750ba13c76SMatthew Garrett 
data_vault_read(struct file * file,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)760ba13c76SMatthew Garrett static ssize_t data_vault_read(struct file *file, struct kobject *kobj,
770ba13c76SMatthew Garrett 	     struct bin_attribute *attr, char *buf, loff_t off, size_t count)
780ba13c76SMatthew Garrett {
790ba13c76SMatthew Garrett 	memcpy(buf, attr->private + off, count);
800ba13c76SMatthew Garrett 	return count;
810ba13c76SMatthew Garrett }
820ba13c76SMatthew Garrett 
830ba13c76SMatthew Garrett static BIN_ATTR_RO(data_vault, 0);
840ba13c76SMatthew Garrett 
850ba13c76SMatthew Garrett static struct bin_attribute *data_attributes[] = {
860ba13c76SMatthew Garrett 	&bin_attr_data_vault,
870ba13c76SMatthew Garrett 	NULL,
880ba13c76SMatthew Garrett };
890ba13c76SMatthew Garrett 
imok_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)9094a3c35eSSrinivas Pandruvada static ssize_t imok_store(struct device *dev, struct device_attribute *attr,
9194a3c35eSSrinivas Pandruvada 			  const char *buf, size_t count)
9294a3c35eSSrinivas Pandruvada {
9394a3c35eSSrinivas Pandruvada 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
9494a3c35eSSrinivas Pandruvada 	acpi_status status;
9594a3c35eSSrinivas Pandruvada 	int input, ret;
9694a3c35eSSrinivas Pandruvada 
9794a3c35eSSrinivas Pandruvada 	ret = kstrtouint(buf, 10, &input);
9894a3c35eSSrinivas Pandruvada 	if (ret)
9994a3c35eSSrinivas Pandruvada 		return ret;
10094a3c35eSSrinivas Pandruvada 	status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input);
10194a3c35eSSrinivas Pandruvada 	if (ACPI_FAILURE(status))
10294a3c35eSSrinivas Pandruvada 		return -EIO;
10394a3c35eSSrinivas Pandruvada 
10494a3c35eSSrinivas Pandruvada 	return count;
10594a3c35eSSrinivas Pandruvada }
10694a3c35eSSrinivas Pandruvada 
10794a3c35eSSrinivas Pandruvada static DEVICE_ATTR_WO(imok);
10894a3c35eSSrinivas Pandruvada 
10994a3c35eSSrinivas Pandruvada static struct attribute *imok_attr[] = {
11094a3c35eSSrinivas Pandruvada 	&dev_attr_imok.attr,
11194a3c35eSSrinivas Pandruvada 	NULL
11294a3c35eSSrinivas Pandruvada };
11394a3c35eSSrinivas Pandruvada 
114f1b07a14SSumeet Pawnikar static const struct attribute_group imok_attribute_group = {
115f1b07a14SSumeet Pawnikar 	.attrs = imok_attr,
116f1b07a14SSumeet Pawnikar };
117f1b07a14SSumeet Pawnikar 
1180ba13c76SMatthew Garrett static const struct attribute_group data_attribute_group = {
1190ba13c76SMatthew Garrett 	.bin_attrs = data_attributes,
1203e8c4d31SAmit Kucheria };
1213e8c4d31SAmit Kucheria 
available_uuids_show(struct device * dev,struct device_attribute * attr,char * buf)1223e8c4d31SAmit Kucheria static ssize_t available_uuids_show(struct device *dev,
1233e8c4d31SAmit Kucheria 				    struct device_attribute *attr,
1243e8c4d31SAmit Kucheria 				    char *buf)
1253e8c4d31SAmit Kucheria {
1263e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
1273e8c4d31SAmit Kucheria 	int i;
1283e8c4d31SAmit Kucheria 	int length = 0;
1293e8c4d31SAmit Kucheria 
1308d485da0SMatthew Garrett 	if (!priv->uuid_bitmap)
1318d485da0SMatthew Garrett 		return sprintf(buf, "UNKNOWN\n");
1328d485da0SMatthew Garrett 
1333e8c4d31SAmit Kucheria 	for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
1343e8c4d31SAmit Kucheria 		if (priv->uuid_bitmap & (1 << i))
1355f7fdb0fSSrinivas Pandruvada 			length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]);
1363e8c4d31SAmit Kucheria 	}
1373e8c4d31SAmit Kucheria 
1383e8c4d31SAmit Kucheria 	return length;
1393e8c4d31SAmit Kucheria }
1403e8c4d31SAmit Kucheria 
current_uuid_show(struct device * dev,struct device_attribute * devattr,char * buf)1413e8c4d31SAmit Kucheria static ssize_t current_uuid_show(struct device *dev,
1423e8c4d31SAmit Kucheria 				 struct device_attribute *devattr, char *buf)
1433e8c4d31SAmit Kucheria {
1443e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
145c7ff2976SSrinivas Pandruvada 	int i, length = 0;
1463e8c4d31SAmit Kucheria 
147c7ff2976SSrinivas Pandruvada 	if (priv->current_uuid_index > 0)
1483e8c4d31SAmit Kucheria 		return sprintf(buf, "%s\n",
1493e8c4d31SAmit Kucheria 			       int3400_thermal_uuids[priv->current_uuid_index]);
150c7ff2976SSrinivas Pandruvada 
151c7ff2976SSrinivas Pandruvada 	for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
152c7ff2976SSrinivas Pandruvada 		if (priv->os_uuid_mask & BIT(i))
1535f7fdb0fSSrinivas Pandruvada 			length += sysfs_emit_at(buf, length, "%s\n", int3400_thermal_uuids[i]);
154c7ff2976SSrinivas Pandruvada 	}
155c7ff2976SSrinivas Pandruvada 
156c7ff2976SSrinivas Pandruvada 	if (length)
157c7ff2976SSrinivas Pandruvada 		return length;
158c7ff2976SSrinivas Pandruvada 
159c7ff2976SSrinivas Pandruvada 	return sprintf(buf, "INVALID\n");
160c7ff2976SSrinivas Pandruvada }
161c7ff2976SSrinivas Pandruvada 
int3400_thermal_run_osc(acpi_handle handle,char * uuid_str,int * enable)162c7ff2976SSrinivas Pandruvada static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable)
163c7ff2976SSrinivas Pandruvada {
164c7ff2976SSrinivas Pandruvada 	u32 ret, buf[2];
165c7ff2976SSrinivas Pandruvada 	acpi_status status;
166c7ff2976SSrinivas Pandruvada 	int result = 0;
167c7ff2976SSrinivas Pandruvada 	struct acpi_osc_context context = {
168ad47f834SDavidlohr Bueso 		.uuid_str = uuid_str,
169c7ff2976SSrinivas Pandruvada 		.rev = 1,
170c7ff2976SSrinivas Pandruvada 		.cap.length = 8,
171ad47f834SDavidlohr Bueso 		.cap.pointer = buf,
172c7ff2976SSrinivas Pandruvada 	};
173c7ff2976SSrinivas Pandruvada 
174c7ff2976SSrinivas Pandruvada 	buf[OSC_QUERY_DWORD] = 0;
175c7ff2976SSrinivas Pandruvada 	buf[OSC_SUPPORT_DWORD] = *enable;
176c7ff2976SSrinivas Pandruvada 
177c7ff2976SSrinivas Pandruvada 	status = acpi_run_osc(handle, &context);
178c7ff2976SSrinivas Pandruvada 	if (ACPI_SUCCESS(status)) {
179c7ff2976SSrinivas Pandruvada 		ret = *((u32 *)(context.ret.pointer + 4));
180c7ff2976SSrinivas Pandruvada 		if (ret != *enable)
181c7ff2976SSrinivas Pandruvada 			result = -EPERM;
182c7ff2976SSrinivas Pandruvada 
183c7ff2976SSrinivas Pandruvada 		kfree(context.ret.pointer);
184bdff938dSDavidlohr Bueso 	} else
185bdff938dSDavidlohr Bueso 		result = -EPERM;
186c7ff2976SSrinivas Pandruvada 
187c7ff2976SSrinivas Pandruvada 	return result;
1883e8c4d31SAmit Kucheria }
1893e8c4d31SAmit Kucheria 
set_os_uuid_mask(struct int3400_thermal_priv * priv,u32 mask)1907b145802SSrinivas Pandruvada static int set_os_uuid_mask(struct int3400_thermal_priv *priv, u32 mask)
1917b145802SSrinivas Pandruvada {
1927b145802SSrinivas Pandruvada 	int cap = 0;
1937b145802SSrinivas Pandruvada 
1947b145802SSrinivas Pandruvada 	/*
1957b145802SSrinivas Pandruvada 	 * Capability bits:
1967b145802SSrinivas Pandruvada 	 * Bit 0: set to 1 to indicate DPTF is active
1977b145802SSrinivas Pandruvada 	 * Bi1 1: set to 1 to active cooling is supported by user space daemon
1987b145802SSrinivas Pandruvada 	 * Bit 2: set to 1 to passive cooling is supported by user space daemon
1997b145802SSrinivas Pandruvada 	 * Bit 3: set to 1 to critical trip is handled by user space daemon
2007b145802SSrinivas Pandruvada 	 */
2017b145802SSrinivas Pandruvada 	if (mask)
2027b145802SSrinivas Pandruvada 		cap = (priv->os_uuid_mask << 1) | 0x01;
2037b145802SSrinivas Pandruvada 
2047b145802SSrinivas Pandruvada 	return int3400_thermal_run_osc(priv->adev->handle,
2057b145802SSrinivas Pandruvada 				       "b23ba85d-c8b7-3542-88de-8de2ffcfd698",
2067b145802SSrinivas Pandruvada 				       &cap);
2077b145802SSrinivas Pandruvada }
2087b145802SSrinivas Pandruvada 
current_uuid_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2093e8c4d31SAmit Kucheria static ssize_t current_uuid_store(struct device *dev,
2103e8c4d31SAmit Kucheria 				  struct device_attribute *attr,
2113e8c4d31SAmit Kucheria 				  const char *buf, size_t count)
2123e8c4d31SAmit Kucheria {
2133e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
2147b145802SSrinivas Pandruvada 	int ret, i;
2153e8c4d31SAmit Kucheria 
2163e8c4d31SAmit Kucheria 	for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
2178d485da0SMatthew Garrett 		if (!strncmp(buf, int3400_thermal_uuids[i],
2188d485da0SMatthew Garrett 			     sizeof(int3400_thermal_uuids[i]) - 1)) {
2198d485da0SMatthew Garrett 			/*
2208d485da0SMatthew Garrett 			 * If we have a list of supported UUIDs, make sure
2218d485da0SMatthew Garrett 			 * this one is supported.
2228d485da0SMatthew Garrett 			 */
223c7ff2976SSrinivas Pandruvada 			if (priv->uuid_bitmap & BIT(i)) {
2243e8c4d31SAmit Kucheria 				priv->current_uuid_index = i;
2253e8c4d31SAmit Kucheria 				return count;
2263e8c4d31SAmit Kucheria 			}
227c7ff2976SSrinivas Pandruvada 
228c7ff2976SSrinivas Pandruvada 			/*
229c7ff2976SSrinivas Pandruvada 			 * There is support of only 3 policies via the new
230c7ff2976SSrinivas Pandruvada 			 * _OSC to inform OS capability:
231c7ff2976SSrinivas Pandruvada 			 * INT3400_THERMAL_ACTIVE
232c7ff2976SSrinivas Pandruvada 			 * INT3400_THERMAL_PASSIVE_1
233c7ff2976SSrinivas Pandruvada 			 * INT3400_THERMAL_CRITICAL
234c7ff2976SSrinivas Pandruvada 			 */
235c7ff2976SSrinivas Pandruvada 
236c7ff2976SSrinivas Pandruvada 			if (i > INT3400_THERMAL_CRITICAL)
237c7ff2976SSrinivas Pandruvada 				return -EINVAL;
238c7ff2976SSrinivas Pandruvada 
239c7ff2976SSrinivas Pandruvada 			priv->os_uuid_mask |= BIT(i);
240c7ff2976SSrinivas Pandruvada 
241c7ff2976SSrinivas Pandruvada 			break;
242c7ff2976SSrinivas Pandruvada 		}
2433e8c4d31SAmit Kucheria 	}
2443e8c4d31SAmit Kucheria 
245c7ff2976SSrinivas Pandruvada 	if (priv->os_uuid_mask) {
2467b145802SSrinivas Pandruvada 		ret = set_os_uuid_mask(priv, priv->os_uuid_mask);
247c7ff2976SSrinivas Pandruvada 		if (ret)
248c7ff2976SSrinivas Pandruvada 			return ret;
249c7ff2976SSrinivas Pandruvada 	}
250c7ff2976SSrinivas Pandruvada 
251c7ff2976SSrinivas Pandruvada 	return count;
2523e8c4d31SAmit Kucheria }
2533e8c4d31SAmit Kucheria 
2543e8c4d31SAmit Kucheria static DEVICE_ATTR_RW(current_uuid);
2553e8c4d31SAmit Kucheria static DEVICE_ATTR_RO(available_uuids);
2563e8c4d31SAmit Kucheria static struct attribute *uuid_attrs[] = {
2573e8c4d31SAmit Kucheria 	&dev_attr_available_uuids.attr,
2583e8c4d31SAmit Kucheria 	&dev_attr_current_uuid.attr,
2593e8c4d31SAmit Kucheria 	NULL
2603e8c4d31SAmit Kucheria };
2613e8c4d31SAmit Kucheria 
2623e8c4d31SAmit Kucheria static const struct attribute_group uuid_attribute_group = {
2633e8c4d31SAmit Kucheria 	.attrs = uuid_attrs,
2643e8c4d31SAmit Kucheria 	.name = "uuids"
2653e8c4d31SAmit Kucheria };
2663e8c4d31SAmit Kucheria 
int3400_thermal_get_uuids(struct int3400_thermal_priv * priv)2673e8c4d31SAmit Kucheria static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
2683e8c4d31SAmit Kucheria {
2693e8c4d31SAmit Kucheria 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
2703e8c4d31SAmit Kucheria 	union acpi_object *obja, *objb;
2713e8c4d31SAmit Kucheria 	int i, j;
2723e8c4d31SAmit Kucheria 	int result = 0;
2733e8c4d31SAmit Kucheria 	acpi_status status;
2743e8c4d31SAmit Kucheria 
2753e8c4d31SAmit Kucheria 	status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
2763e8c4d31SAmit Kucheria 	if (ACPI_FAILURE(status))
2773e8c4d31SAmit Kucheria 		return -ENODEV;
2783e8c4d31SAmit Kucheria 
2793e8c4d31SAmit Kucheria 	obja = (union acpi_object *)buf.pointer;
2803e8c4d31SAmit Kucheria 	if (obja->type != ACPI_TYPE_PACKAGE) {
2813e8c4d31SAmit Kucheria 		result = -EINVAL;
2823e8c4d31SAmit Kucheria 		goto end;
2833e8c4d31SAmit Kucheria 	}
2843e8c4d31SAmit Kucheria 
2853e8c4d31SAmit Kucheria 	for (i = 0; i < obja->package.count; i++) {
2863e8c4d31SAmit Kucheria 		objb = &obja->package.elements[i];
2873e8c4d31SAmit Kucheria 		if (objb->type != ACPI_TYPE_BUFFER) {
2883e8c4d31SAmit Kucheria 			result = -EINVAL;
2893e8c4d31SAmit Kucheria 			goto end;
2903e8c4d31SAmit Kucheria 		}
2913e8c4d31SAmit Kucheria 
2923e8c4d31SAmit Kucheria 		/* UUID must be 16 bytes */
2933e8c4d31SAmit Kucheria 		if (objb->buffer.length != 16) {
2943e8c4d31SAmit Kucheria 			result = -EINVAL;
2953e8c4d31SAmit Kucheria 			goto end;
2963e8c4d31SAmit Kucheria 		}
2973e8c4d31SAmit Kucheria 
2983e8c4d31SAmit Kucheria 		for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
2993e8c4d31SAmit Kucheria 			guid_t guid;
3003e8c4d31SAmit Kucheria 
3013e8c4d31SAmit Kucheria 			guid_parse(int3400_thermal_uuids[j], &guid);
3023e8c4d31SAmit Kucheria 			if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {
3033e8c4d31SAmit Kucheria 				priv->uuid_bitmap |= (1 << j);
3043e8c4d31SAmit Kucheria 				break;
3053e8c4d31SAmit Kucheria 			}
3063e8c4d31SAmit Kucheria 		}
3073e8c4d31SAmit Kucheria 	}
3083e8c4d31SAmit Kucheria 
3093e8c4d31SAmit Kucheria end:
3103e8c4d31SAmit Kucheria 	kfree(buf.pointer);
3113e8c4d31SAmit Kucheria 	return result;
3123e8c4d31SAmit Kucheria }
3133e8c4d31SAmit Kucheria 
production_mode_show(struct device * dev,struct device_attribute * attr,char * buf)3145c36cf27SSrinivas Pandruvada static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr,
3155c36cf27SSrinivas Pandruvada 				     char *buf)
3165c36cf27SSrinivas Pandruvada {
3175c36cf27SSrinivas Pandruvada 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
3185c36cf27SSrinivas Pandruvada 
3195c36cf27SSrinivas Pandruvada 	return sysfs_emit(buf, "%d\n", priv->production_mode);
3205c36cf27SSrinivas Pandruvada }
3215c36cf27SSrinivas Pandruvada 
3225c36cf27SSrinivas Pandruvada static DEVICE_ATTR_RO(production_mode);
3235c36cf27SSrinivas Pandruvada 
production_mode_init(struct int3400_thermal_priv * priv)3245c36cf27SSrinivas Pandruvada static int production_mode_init(struct int3400_thermal_priv *priv)
3255c36cf27SSrinivas Pandruvada {
3265c36cf27SSrinivas Pandruvada 	unsigned long long mode;
3275c36cf27SSrinivas Pandruvada 	acpi_status status;
3285c36cf27SSrinivas Pandruvada 	int ret;
3295c36cf27SSrinivas Pandruvada 
3305c36cf27SSrinivas Pandruvada 	priv->production_mode = -1;
3315c36cf27SSrinivas Pandruvada 
3325c36cf27SSrinivas Pandruvada 	status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode);
3335c36cf27SSrinivas Pandruvada 	/* If the method is not present, this is not an error */
3345c36cf27SSrinivas Pandruvada 	if (ACPI_FAILURE(status))
3355c36cf27SSrinivas Pandruvada 		return 0;
3365c36cf27SSrinivas Pandruvada 
3375c36cf27SSrinivas Pandruvada 	ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
3385c36cf27SSrinivas Pandruvada 	if (ret)
3395c36cf27SSrinivas Pandruvada 		return ret;
3405c36cf27SSrinivas Pandruvada 
3415c36cf27SSrinivas Pandruvada 	priv->production_mode = mode;
3425c36cf27SSrinivas Pandruvada 
3435c36cf27SSrinivas Pandruvada 	return 0;
3445c36cf27SSrinivas Pandruvada }
3455c36cf27SSrinivas Pandruvada 
production_mode_exit(struct int3400_thermal_priv * priv)3465c36cf27SSrinivas Pandruvada static void production_mode_exit(struct int3400_thermal_priv *priv)
3475c36cf27SSrinivas Pandruvada {
3485c36cf27SSrinivas Pandruvada 	if (priv->production_mode >= 0)
3495c36cf27SSrinivas Pandruvada 		sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
3505c36cf27SSrinivas Pandruvada }
3515c36cf27SSrinivas Pandruvada 
odvp_show(struct device * dev,struct device_attribute * attr,char * buf)352d0f6cfb2SKees Cook static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
353006f006fSMatthew Garrett 			 char *buf)
354006f006fSMatthew Garrett {
355006f006fSMatthew Garrett 	struct odvp_attr *odvp_attr;
356006f006fSMatthew Garrett 
357006f006fSMatthew Garrett 	odvp_attr = container_of(attr, struct odvp_attr, attr);
358006f006fSMatthew Garrett 
359006f006fSMatthew Garrett 	return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
360006f006fSMatthew Garrett }
361006f006fSMatthew Garrett 
cleanup_odvp(struct int3400_thermal_priv * priv)362006f006fSMatthew Garrett static void cleanup_odvp(struct int3400_thermal_priv *priv)
363006f006fSMatthew Garrett {
364006f006fSMatthew Garrett 	int i;
365006f006fSMatthew Garrett 
366006f006fSMatthew Garrett 	if (priv->odvp_attrs) {
367006f006fSMatthew Garrett 		for (i = 0; i < priv->odvp_count; i++) {
368006f006fSMatthew Garrett 			sysfs_remove_file(&priv->pdev->dev.kobj,
369006f006fSMatthew Garrett 					  &priv->odvp_attrs[i].attr.attr);
370006f006fSMatthew Garrett 			kfree(priv->odvp_attrs[i].attr.attr.name);
371006f006fSMatthew Garrett 		}
372006f006fSMatthew Garrett 		kfree(priv->odvp_attrs);
373006f006fSMatthew Garrett 	}
374006f006fSMatthew Garrett 	kfree(priv->odvp);
375006f006fSMatthew Garrett 	priv->odvp_count = 0;
376006f006fSMatthew Garrett }
377006f006fSMatthew Garrett 
evaluate_odvp(struct int3400_thermal_priv * priv)378006f006fSMatthew Garrett static int evaluate_odvp(struct int3400_thermal_priv *priv)
379006f006fSMatthew Garrett {
380006f006fSMatthew Garrett 	struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL };
381006f006fSMatthew Garrett 	union acpi_object *obj = NULL;
382006f006fSMatthew Garrett 	acpi_status status;
383006f006fSMatthew Garrett 	int i, ret;
384006f006fSMatthew Garrett 
385006f006fSMatthew Garrett 	status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp);
386006f006fSMatthew Garrett 	if (ACPI_FAILURE(status)) {
387006f006fSMatthew Garrett 		ret = -EINVAL;
388006f006fSMatthew Garrett 		goto out_err;
389006f006fSMatthew Garrett 	}
390006f006fSMatthew Garrett 
391006f006fSMatthew Garrett 	obj = odvp.pointer;
392006f006fSMatthew Garrett 	if (obj->type != ACPI_TYPE_PACKAGE) {
393006f006fSMatthew Garrett 		ret = -EINVAL;
394006f006fSMatthew Garrett 		goto out_err;
395006f006fSMatthew Garrett 	}
396006f006fSMatthew Garrett 
397006f006fSMatthew Garrett 	if (priv->odvp == NULL) {
398006f006fSMatthew Garrett 		priv->odvp_count = obj->package.count;
399006f006fSMatthew Garrett 		priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),
400006f006fSMatthew Garrett 				     GFP_KERNEL);
401006f006fSMatthew Garrett 		if (!priv->odvp) {
402006f006fSMatthew Garrett 			ret = -ENOMEM;
403006f006fSMatthew Garrett 			goto out_err;
404006f006fSMatthew Garrett 		}
405006f006fSMatthew Garrett 	}
406006f006fSMatthew Garrett 
407006f006fSMatthew Garrett 	if (priv->odvp_attrs == NULL) {
408006f006fSMatthew Garrett 		priv->odvp_attrs = kcalloc(priv->odvp_count,
409006f006fSMatthew Garrett 					   sizeof(struct odvp_attr),
410006f006fSMatthew Garrett 					   GFP_KERNEL);
411006f006fSMatthew Garrett 		if (!priv->odvp_attrs) {
412006f006fSMatthew Garrett 			ret = -ENOMEM;
413006f006fSMatthew Garrett 			goto out_err;
414006f006fSMatthew Garrett 		}
415006f006fSMatthew Garrett 		for (i = 0; i < priv->odvp_count; i++) {
416006f006fSMatthew Garrett 			struct odvp_attr *odvp = &priv->odvp_attrs[i];
417006f006fSMatthew Garrett 
418006f006fSMatthew Garrett 			sysfs_attr_init(&odvp->attr.attr);
419006f006fSMatthew Garrett 			odvp->priv = priv;
420006f006fSMatthew Garrett 			odvp->odvp = i;
421006f006fSMatthew Garrett 			odvp->attr.attr.name = kasprintf(GFP_KERNEL,
422006f006fSMatthew Garrett 							 "odvp%d", i);
423006f006fSMatthew Garrett 
424006f006fSMatthew Garrett 			if (!odvp->attr.attr.name) {
425006f006fSMatthew Garrett 				ret = -ENOMEM;
426006f006fSMatthew Garrett 				goto out_err;
427006f006fSMatthew Garrett 			}
428006f006fSMatthew Garrett 			odvp->attr.attr.mode = 0444;
429006f006fSMatthew Garrett 			odvp->attr.show = odvp_show;
430006f006fSMatthew Garrett 			odvp->attr.store = NULL;
431006f006fSMatthew Garrett 			ret = sysfs_create_file(&priv->pdev->dev.kobj,
432006f006fSMatthew Garrett 						&odvp->attr.attr);
433006f006fSMatthew Garrett 			if (ret)
434006f006fSMatthew Garrett 				goto out_err;
435006f006fSMatthew Garrett 		}
436006f006fSMatthew Garrett 	}
437006f006fSMatthew Garrett 
438006f006fSMatthew Garrett 	for (i = 0; i < obj->package.count; i++) {
439006f006fSMatthew Garrett 		if (obj->package.elements[i].type == ACPI_TYPE_INTEGER)
440006f006fSMatthew Garrett 			priv->odvp[i] = obj->package.elements[i].integer.value;
441006f006fSMatthew Garrett 	}
442006f006fSMatthew Garrett 
443006f006fSMatthew Garrett 	kfree(obj);
444006f006fSMatthew Garrett 	return 0;
445006f006fSMatthew Garrett 
446006f006fSMatthew Garrett out_err:
447006f006fSMatthew Garrett 	cleanup_odvp(priv);
448006f006fSMatthew Garrett 	kfree(obj);
449006f006fSMatthew Garrett 	return ret;
450006f006fSMatthew Garrett }
451006f006fSMatthew Garrett 
int3400_notify(acpi_handle handle,u32 event,void * data)4523e8c4d31SAmit Kucheria static void int3400_notify(acpi_handle handle,
4533e8c4d31SAmit Kucheria 			u32 event,
4543e8c4d31SAmit Kucheria 			void *data)
4553e8c4d31SAmit Kucheria {
4563e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = data;
45712ad93abSDaniel Lezcano 	struct device *dev;
4583e8c4d31SAmit Kucheria 	char *thermal_prop[5];
45939558030SSrinivas Pandruvada 	int therm_event;
4603e8c4d31SAmit Kucheria 
4613e8c4d31SAmit Kucheria 	if (!priv)
4623e8c4d31SAmit Kucheria 		return;
4633e8c4d31SAmit Kucheria 
4643e8c4d31SAmit Kucheria 	switch (event) {
4653e8c4d31SAmit Kucheria 	case INT3400_THERMAL_TABLE_CHANGED:
46639558030SSrinivas Pandruvada 		therm_event = THERMAL_TABLE_CHANGED;
4673e8c4d31SAmit Kucheria 		break;
46894a3c35eSSrinivas Pandruvada 	case INT3400_KEEP_ALIVE:
46994a3c35eSSrinivas Pandruvada 		therm_event = THERMAL_EVENT_KEEP_ALIVE;
47094a3c35eSSrinivas Pandruvada 		break;
471006f006fSMatthew Garrett 	case INT3400_ODVP_CHANGED:
472006f006fSMatthew Garrett 		evaluate_odvp(priv);
47339558030SSrinivas Pandruvada 		therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED;
474006f006fSMatthew Garrett 		break;
4753e8c4d31SAmit Kucheria 	default:
4763e8c4d31SAmit Kucheria 		/* Ignore unknown notification codes sent to INT3400 device */
47739558030SSrinivas Pandruvada 		return;
4783e8c4d31SAmit Kucheria 	}
47939558030SSrinivas Pandruvada 
48012ad93abSDaniel Lezcano 	dev = thermal_zone_device(priv->thermal);
48112ad93abSDaniel Lezcano 
48212ad93abSDaniel Lezcano 	thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", thermal_zone_device_type(priv->thermal));
48312ad93abSDaniel Lezcano 	thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", INT3400_FAKE_TEMP);
48439558030SSrinivas Pandruvada 	thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");
48539558030SSrinivas Pandruvada 	thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event);
48639558030SSrinivas Pandruvada 	thermal_prop[4] = NULL;
48712ad93abSDaniel Lezcano 	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, thermal_prop);
4883abea10eSChuansheng Liu 	kfree(thermal_prop[0]);
4893abea10eSChuansheng Liu 	kfree(thermal_prop[1]);
4903abea10eSChuansheng Liu 	kfree(thermal_prop[2]);
4913abea10eSChuansheng Liu 	kfree(thermal_prop[3]);
4923e8c4d31SAmit Kucheria }
4933e8c4d31SAmit Kucheria 
int3400_thermal_get_temp(struct thermal_zone_device * thermal,int * temp)4943e8c4d31SAmit Kucheria static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
4953e8c4d31SAmit Kucheria 			int *temp)
4963e8c4d31SAmit Kucheria {
49712ad93abSDaniel Lezcano 	*temp = INT3400_FAKE_TEMP;
4983e8c4d31SAmit Kucheria 	return 0;
4993e8c4d31SAmit Kucheria }
5003e8c4d31SAmit Kucheria 
int3400_thermal_change_mode(struct thermal_zone_device * thermal,enum thermal_device_mode mode)501f5e50bf4SAndrzej Pietrasiewicz static int int3400_thermal_change_mode(struct thermal_zone_device *thermal,
5023e8c4d31SAmit Kucheria 				       enum thermal_device_mode mode)
5033e8c4d31SAmit Kucheria {
5045f68d078SDaniel Lezcano 	struct int3400_thermal_priv *priv = thermal_zone_device_priv(thermal);
5053e8c4d31SAmit Kucheria 	int result = 0;
506e49c8ed8SDaniel Lezcano 	int enabled;
5073e8c4d31SAmit Kucheria 
5083e8c4d31SAmit Kucheria 	if (!priv)
5093e8c4d31SAmit Kucheria 		return -EINVAL;
5103e8c4d31SAmit Kucheria 
5117b145802SSrinivas Pandruvada 	enabled = mode == THERMAL_DEVICE_ENABLED;
5127b145802SSrinivas Pandruvada 
5137b145802SSrinivas Pandruvada 	if (priv->os_uuid_mask) {
5147b145802SSrinivas Pandruvada 		if (!enabled) {
5157b145802SSrinivas Pandruvada 			priv->os_uuid_mask = 0;
5167b145802SSrinivas Pandruvada 			result = set_os_uuid_mask(priv, priv->os_uuid_mask);
5177b145802SSrinivas Pandruvada 		}
5187b145802SSrinivas Pandruvada 		goto eval_odvp;
5197b145802SSrinivas Pandruvada 	}
5207b145802SSrinivas Pandruvada 
521c7ff2976SSrinivas Pandruvada 	if (priv->current_uuid_index < 0 ||
522c7ff2976SSrinivas Pandruvada 	    priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID)
523c7ff2976SSrinivas Pandruvada 		return -EINVAL;
524c7ff2976SSrinivas Pandruvada 
5253e8c4d31SAmit Kucheria 	result = int3400_thermal_run_osc(priv->adev->handle,
526c7ff2976SSrinivas Pandruvada 					 int3400_thermal_uuids[priv->current_uuid_index],
527c7ff2976SSrinivas Pandruvada 					 &enabled);
5287b145802SSrinivas Pandruvada eval_odvp:
529006f006fSMatthew Garrett 	evaluate_odvp(priv);
530006f006fSMatthew Garrett 
5313e8c4d31SAmit Kucheria 	return result;
5323e8c4d31SAmit Kucheria }
5333e8c4d31SAmit Kucheria 
5343e8c4d31SAmit Kucheria static struct thermal_zone_device_ops int3400_thermal_ops = {
5353e8c4d31SAmit Kucheria 	.get_temp = int3400_thermal_get_temp,
536f5e50bf4SAndrzej Pietrasiewicz 	.change_mode = int3400_thermal_change_mode,
5373e8c4d31SAmit Kucheria };
5383e8c4d31SAmit Kucheria 
5393e8c4d31SAmit Kucheria static struct thermal_zone_params int3400_thermal_params = {
5403e8c4d31SAmit Kucheria 	.governor_name = "user_space",
5413e8c4d31SAmit Kucheria 	.no_hwmon = true,
5423e8c4d31SAmit Kucheria };
5433e8c4d31SAmit Kucheria 
int3400_setup_gddv(struct int3400_thermal_priv * priv)5440ba13c76SMatthew Garrett static void int3400_setup_gddv(struct int3400_thermal_priv *priv)
5450ba13c76SMatthew Garrett {
5460ba13c76SMatthew Garrett 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
5470ba13c76SMatthew Garrett 	union acpi_object *obj;
5480ba13c76SMatthew Garrett 	acpi_status status;
5490ba13c76SMatthew Garrett 
5500ba13c76SMatthew Garrett 	status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL,
5510ba13c76SMatthew Garrett 				      &buffer);
5520ba13c76SMatthew Garrett 	if (ACPI_FAILURE(status) || !buffer.length)
5530ba13c76SMatthew Garrett 		return;
5540ba13c76SMatthew Garrett 
5550ba13c76SMatthew Garrett 	obj = buffer.pointer;
5560ba13c76SMatthew Garrett 	if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1
5579e5d3d6bSDavidlohr Bueso 	    || obj->package.elements[0].type != ACPI_TYPE_BUFFER)
5589e5d3d6bSDavidlohr Bueso 		goto out_free;
5590ba13c76SMatthew Garrett 
5600ba13c76SMatthew Garrett 	priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer,
5610ba13c76SMatthew Garrett 				   obj->package.elements[0].buffer.length,
5620ba13c76SMatthew Garrett 				   GFP_KERNEL);
5637931e280SLee, Chun-Yi 	if (ZERO_OR_NULL_PTR(priv->data_vault))
5649e5d3d6bSDavidlohr Bueso 		goto out_free;
56538b16d6cSJiasheng Jiang 
5660ba13c76SMatthew Garrett 	bin_attr_data_vault.private = priv->data_vault;
5670ba13c76SMatthew Garrett 	bin_attr_data_vault.size = obj->package.elements[0].buffer.length;
5689e5d3d6bSDavidlohr Bueso out_free:
5690ba13c76SMatthew Garrett 	kfree(buffer.pointer);
5700ba13c76SMatthew Garrett }
5710ba13c76SMatthew Garrett 
int3400_thermal_probe(struct platform_device * pdev)5723e8c4d31SAmit Kucheria static int int3400_thermal_probe(struct platform_device *pdev)
5733e8c4d31SAmit Kucheria {
5743e8c4d31SAmit Kucheria 	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
5753e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv;
5763e8c4d31SAmit Kucheria 	int result;
5773e8c4d31SAmit Kucheria 
5783e8c4d31SAmit Kucheria 	if (!adev)
5793e8c4d31SAmit Kucheria 		return -ENODEV;
5803e8c4d31SAmit Kucheria 
5813e8c4d31SAmit Kucheria 	priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
5823e8c4d31SAmit Kucheria 	if (!priv)
5833e8c4d31SAmit Kucheria 		return -ENOMEM;
5843e8c4d31SAmit Kucheria 
585006f006fSMatthew Garrett 	priv->pdev = pdev;
5863e8c4d31SAmit Kucheria 	priv->adev = adev;
5873e8c4d31SAmit Kucheria 
5883e8c4d31SAmit Kucheria 	result = int3400_thermal_get_uuids(priv);
5898d485da0SMatthew Garrett 
5908d485da0SMatthew Garrett 	/* Missing IDSP isn't fatal */
5918d485da0SMatthew Garrett 	if (result && result != -ENODEV)
5923e8c4d31SAmit Kucheria 		goto free_priv;
5933e8c4d31SAmit Kucheria 
5948d485da0SMatthew Garrett 	priv->current_uuid_index = -1;
5958d485da0SMatthew Garrett 
5963e8c4d31SAmit Kucheria 	result = acpi_parse_art(priv->adev->handle, &priv->art_count,
5973e8c4d31SAmit Kucheria 				&priv->arts, true);
5983e8c4d31SAmit Kucheria 	if (result)
5993e8c4d31SAmit Kucheria 		dev_dbg(&pdev->dev, "_ART table parsing error\n");
6003e8c4d31SAmit Kucheria 
6013e8c4d31SAmit Kucheria 	result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
6023e8c4d31SAmit Kucheria 				&priv->trts, true);
6033e8c4d31SAmit Kucheria 	if (result)
6043e8c4d31SAmit Kucheria 		dev_dbg(&pdev->dev, "_TRT table parsing error\n");
6053e8c4d31SAmit Kucheria 
6063e8c4d31SAmit Kucheria 	platform_set_drvdata(pdev, priv);
6073e8c4d31SAmit Kucheria 
6080ba13c76SMatthew Garrett 	int3400_setup_gddv(priv);
6090ba13c76SMatthew Garrett 
610006f006fSMatthew Garrett 	evaluate_odvp(priv);
611006f006fSMatthew Garrett 
612*cbcd51e8SRafael J. Wysocki 	priv->thermal = thermal_tripless_zone_device_register("INT3400 Thermal", priv,
613*cbcd51e8SRafael J. Wysocki 							      &int3400_thermal_ops,
614*cbcd51e8SRafael J. Wysocki 							      &int3400_thermal_params);
6153e8c4d31SAmit Kucheria 	if (IS_ERR(priv->thermal)) {
6163e8c4d31SAmit Kucheria 		result = PTR_ERR(priv->thermal);
6173e8c4d31SAmit Kucheria 		goto free_art_trt;
6183e8c4d31SAmit Kucheria 	}
6193e8c4d31SAmit Kucheria 
6203e8c4d31SAmit Kucheria 	priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
6213e8c4d31SAmit Kucheria 							priv->adev->handle);
6223e8c4d31SAmit Kucheria 
6233e8c4d31SAmit Kucheria 	result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);
6243e8c4d31SAmit Kucheria 	if (result)
6253e8c4d31SAmit Kucheria 		goto free_rel_misc;
6263e8c4d31SAmit Kucheria 
627f1b07a14SSumeet Pawnikar 	if (acpi_has_method(priv->adev->handle, "IMOK")) {
628f1b07a14SSumeet Pawnikar 		result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group);
629f1b07a14SSumeet Pawnikar 		if (result)
630f1b07a14SSumeet Pawnikar 			goto free_imok;
631f1b07a14SSumeet Pawnikar 	}
632f1b07a14SSumeet Pawnikar 
6337931e280SLee, Chun-Yi 	if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
6340ba13c76SMatthew Garrett 		result = sysfs_create_group(&pdev->dev.kobj,
6350ba13c76SMatthew Garrett 					    &data_attribute_group);
6360ba13c76SMatthew Garrett 		if (result)
6370ba13c76SMatthew Garrett 			goto free_uuid;
6380ba13c76SMatthew Garrett 	}
6390ba13c76SMatthew Garrett 
6403e8c4d31SAmit Kucheria 	result = acpi_install_notify_handler(
6413e8c4d31SAmit Kucheria 			priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
6423e8c4d31SAmit Kucheria 			(void *)priv);
6433e8c4d31SAmit Kucheria 	if (result)
6443e8c4d31SAmit Kucheria 		goto free_sysfs;
6453e8c4d31SAmit Kucheria 
6465c36cf27SSrinivas Pandruvada 	result = production_mode_init(priv);
6475c36cf27SSrinivas Pandruvada 	if (result)
6485c36cf27SSrinivas Pandruvada 		goto free_notify;
6495c36cf27SSrinivas Pandruvada 
6503e8c4d31SAmit Kucheria 	return 0;
6513e8c4d31SAmit Kucheria 
6525c36cf27SSrinivas Pandruvada free_notify:
6535c36cf27SSrinivas Pandruvada 	acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
6545c36cf27SSrinivas Pandruvada 				   int3400_notify);
6553e8c4d31SAmit Kucheria free_sysfs:
656006f006fSMatthew Garrett 	cleanup_odvp(priv);
657e9a7c526SRafael J. Wysocki 	if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
6580ba13c76SMatthew Garrett 		sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
659006f006fSMatthew Garrett 		kfree(priv->data_vault);
660006f006fSMatthew Garrett 	}
6610ba13c76SMatthew Garrett free_uuid:
6623e8c4d31SAmit Kucheria 	sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
663f1b07a14SSumeet Pawnikar free_imok:
664f1b07a14SSumeet Pawnikar 	sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);
6653e8c4d31SAmit Kucheria free_rel_misc:
6663e8c4d31SAmit Kucheria 	if (!priv->rel_misc_dev_res)
6673e8c4d31SAmit Kucheria 		acpi_thermal_rel_misc_device_remove(priv->adev->handle);
6683e8c4d31SAmit Kucheria 	thermal_zone_device_unregister(priv->thermal);
6693e8c4d31SAmit Kucheria free_art_trt:
6703e8c4d31SAmit Kucheria 	kfree(priv->trts);
6713e8c4d31SAmit Kucheria 	kfree(priv->arts);
6723e8c4d31SAmit Kucheria free_priv:
6733e8c4d31SAmit Kucheria 	kfree(priv);
6743e8c4d31SAmit Kucheria 	return result;
6753e8c4d31SAmit Kucheria }
6763e8c4d31SAmit Kucheria 
int3400_thermal_remove(struct platform_device * pdev)6773e8c4d31SAmit Kucheria static int int3400_thermal_remove(struct platform_device *pdev)
6783e8c4d31SAmit Kucheria {
6793e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
6803e8c4d31SAmit Kucheria 
6815c36cf27SSrinivas Pandruvada 	production_mode_exit(priv);
6825c36cf27SSrinivas Pandruvada 
6833e8c4d31SAmit Kucheria 	acpi_remove_notify_handler(
6843e8c4d31SAmit Kucheria 			priv->adev->handle, ACPI_DEVICE_NOTIFY,
6853e8c4d31SAmit Kucheria 			int3400_notify);
6863e8c4d31SAmit Kucheria 
687006f006fSMatthew Garrett 	cleanup_odvp(priv);
688006f006fSMatthew Garrett 
6893e8c4d31SAmit Kucheria 	if (!priv->rel_misc_dev_res)
6903e8c4d31SAmit Kucheria 		acpi_thermal_rel_misc_device_remove(priv->adev->handle);
6913e8c4d31SAmit Kucheria 
6927931e280SLee, Chun-Yi 	if (!ZERO_OR_NULL_PTR(priv->data_vault))
6930ba13c76SMatthew Garrett 		sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
6943e8c4d31SAmit Kucheria 	sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
695f1b07a14SSumeet Pawnikar 	sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);
6963e8c4d31SAmit Kucheria 	thermal_zone_device_unregister(priv->thermal);
6970ba13c76SMatthew Garrett 	kfree(priv->data_vault);
6983e8c4d31SAmit Kucheria 	kfree(priv->trts);
6993e8c4d31SAmit Kucheria 	kfree(priv->arts);
7003e8c4d31SAmit Kucheria 	kfree(priv);
7013e8c4d31SAmit Kucheria 	return 0;
7023e8c4d31SAmit Kucheria }
7033e8c4d31SAmit Kucheria 
7043e8c4d31SAmit Kucheria static const struct acpi_device_id int3400_thermal_match[] = {
7053e8c4d31SAmit Kucheria 	{"INT3400", 0},
70626d8bec1SGayatri Kammela 	{"INTC1040", 0},
70767698880SSrinivas Pandruvada 	{"INTC1041", 0},
708657b95d3SSumeet Pawnikar 	{"INTC1042", 0},
709a95be874SSrinivas Pandruvada 	{"INTC10A0", 0},
7103e8c4d31SAmit Kucheria 	{}
7113e8c4d31SAmit Kucheria };
7123e8c4d31SAmit Kucheria 
7133e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
7143e8c4d31SAmit Kucheria 
7153e8c4d31SAmit Kucheria static struct platform_driver int3400_thermal_driver = {
7163e8c4d31SAmit Kucheria 	.probe = int3400_thermal_probe,
7173e8c4d31SAmit Kucheria 	.remove = int3400_thermal_remove,
7183e8c4d31SAmit Kucheria 	.driver = {
7193e8c4d31SAmit Kucheria 		   .name = "int3400 thermal",
7203e8c4d31SAmit Kucheria 		   .acpi_match_table = ACPI_PTR(int3400_thermal_match),
7213e8c4d31SAmit Kucheria 		   },
7223e8c4d31SAmit Kucheria };
7233e8c4d31SAmit Kucheria 
7243e8c4d31SAmit Kucheria module_platform_driver(int3400_thermal_driver);
7253e8c4d31SAmit Kucheria 
7263e8c4d31SAmit Kucheria MODULE_DESCRIPTION("INT3400 Thermal driver");
7273e8c4d31SAmit Kucheria MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
7283e8c4d31SAmit Kucheria MODULE_LICENSE("GPL");
729