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
163e8c4d31SAmit Kucheria 
173e8c4d31SAmit Kucheria enum int3400_thermal_uuid {
183e8c4d31SAmit Kucheria 	INT3400_THERMAL_PASSIVE_1,
193e8c4d31SAmit Kucheria 	INT3400_THERMAL_ACTIVE,
203e8c4d31SAmit Kucheria 	INT3400_THERMAL_CRITICAL,
2116fc8ecaSMatthew Garrett 	INT3400_THERMAL_ADAPTIVE_PERFORMANCE,
2216fc8ecaSMatthew Garrett 	INT3400_THERMAL_EMERGENCY_CALL_MODE,
2316fc8ecaSMatthew Garrett 	INT3400_THERMAL_PASSIVE_2,
2416fc8ecaSMatthew Garrett 	INT3400_THERMAL_POWER_BOSS,
2516fc8ecaSMatthew Garrett 	INT3400_THERMAL_VIRTUAL_SENSOR,
2616fc8ecaSMatthew Garrett 	INT3400_THERMAL_COOLING_MODE,
2716fc8ecaSMatthew Garrett 	INT3400_THERMAL_HARDWARE_DUTY_CYCLING,
283e8c4d31SAmit Kucheria 	INT3400_THERMAL_MAXIMUM_UUID,
293e8c4d31SAmit Kucheria };
303e8c4d31SAmit Kucheria 
313e8c4d31SAmit Kucheria static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
323e8c4d31SAmit Kucheria 	"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
333e8c4d31SAmit Kucheria 	"3A95C389-E4B8-4629-A526-C52C88626BAE",
343e8c4d31SAmit Kucheria 	"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
3516fc8ecaSMatthew Garrett 	"63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",
3616fc8ecaSMatthew Garrett 	"5349962F-71E6-431D-9AE8-0A635B710AEE",
3716fc8ecaSMatthew Garrett 	"9E04115A-AE87-4D1C-9500-0F3E340BFE75",
3816fc8ecaSMatthew Garrett 	"F5A35014-C209-46A4-993A-EB56DE7530A1",
3916fc8ecaSMatthew Garrett 	"6ED722A7-9240-48A5-B479-31EEF723D7CF",
4016fc8ecaSMatthew Garrett 	"16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",
4116fc8ecaSMatthew Garrett 	"BE84BABF-C4D4-403D-B495-3128FD44dAC1",
423e8c4d31SAmit Kucheria };
433e8c4d31SAmit Kucheria 
443e8c4d31SAmit Kucheria struct int3400_thermal_priv {
453e8c4d31SAmit Kucheria 	struct acpi_device *adev;
463e8c4d31SAmit Kucheria 	struct thermal_zone_device *thermal;
473e8c4d31SAmit Kucheria 	int mode;
483e8c4d31SAmit Kucheria 	int art_count;
493e8c4d31SAmit Kucheria 	struct art *arts;
503e8c4d31SAmit Kucheria 	int trt_count;
513e8c4d31SAmit Kucheria 	struct trt *trts;
523e8c4d31SAmit Kucheria 	u8 uuid_bitmap;
533e8c4d31SAmit Kucheria 	int rel_misc_dev_res;
543e8c4d31SAmit Kucheria 	int current_uuid_index;
553e8c4d31SAmit Kucheria };
563e8c4d31SAmit Kucheria 
573e8c4d31SAmit Kucheria static ssize_t available_uuids_show(struct device *dev,
583e8c4d31SAmit Kucheria 				    struct device_attribute *attr,
593e8c4d31SAmit Kucheria 				    char *buf)
603e8c4d31SAmit Kucheria {
613e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
623e8c4d31SAmit Kucheria 	int i;
633e8c4d31SAmit Kucheria 	int length = 0;
643e8c4d31SAmit Kucheria 
653e8c4d31SAmit Kucheria 	for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
663e8c4d31SAmit Kucheria 		if (priv->uuid_bitmap & (1 << i))
673e8c4d31SAmit Kucheria 			if (PAGE_SIZE - length > 0)
68f21431f2STakashi Iwai 				length += scnprintf(&buf[length],
693e8c4d31SAmit Kucheria 						   PAGE_SIZE - length,
703e8c4d31SAmit Kucheria 						   "%s\n",
713e8c4d31SAmit Kucheria 						   int3400_thermal_uuids[i]);
723e8c4d31SAmit Kucheria 	}
733e8c4d31SAmit Kucheria 
743e8c4d31SAmit Kucheria 	return length;
753e8c4d31SAmit Kucheria }
763e8c4d31SAmit Kucheria 
773e8c4d31SAmit Kucheria static ssize_t current_uuid_show(struct device *dev,
783e8c4d31SAmit Kucheria 				 struct device_attribute *devattr, char *buf)
793e8c4d31SAmit Kucheria {
803e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
813e8c4d31SAmit Kucheria 
823e8c4d31SAmit Kucheria 	if (priv->uuid_bitmap & (1 << priv->current_uuid_index))
833e8c4d31SAmit Kucheria 		return sprintf(buf, "%s\n",
843e8c4d31SAmit Kucheria 			       int3400_thermal_uuids[priv->current_uuid_index]);
853e8c4d31SAmit Kucheria 	else
863e8c4d31SAmit Kucheria 		return sprintf(buf, "INVALID\n");
873e8c4d31SAmit Kucheria }
883e8c4d31SAmit Kucheria 
893e8c4d31SAmit Kucheria static ssize_t current_uuid_store(struct device *dev,
903e8c4d31SAmit Kucheria 				  struct device_attribute *attr,
913e8c4d31SAmit Kucheria 				  const char *buf, size_t count)
923e8c4d31SAmit Kucheria {
933e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
943e8c4d31SAmit Kucheria 	int i;
953e8c4d31SAmit Kucheria 
963e8c4d31SAmit Kucheria 	for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
973e8c4d31SAmit Kucheria 		if ((priv->uuid_bitmap & (1 << i)) &&
983e8c4d31SAmit Kucheria 		    !(strncmp(buf, int3400_thermal_uuids[i],
993e8c4d31SAmit Kucheria 			      sizeof(int3400_thermal_uuids[i]) - 1))) {
1003e8c4d31SAmit Kucheria 			priv->current_uuid_index = i;
1013e8c4d31SAmit Kucheria 			return count;
1023e8c4d31SAmit Kucheria 		}
1033e8c4d31SAmit Kucheria 	}
1043e8c4d31SAmit Kucheria 
1053e8c4d31SAmit Kucheria 	return -EINVAL;
1063e8c4d31SAmit Kucheria }
1073e8c4d31SAmit Kucheria 
1083e8c4d31SAmit Kucheria static DEVICE_ATTR_RW(current_uuid);
1093e8c4d31SAmit Kucheria static DEVICE_ATTR_RO(available_uuids);
1103e8c4d31SAmit Kucheria static struct attribute *uuid_attrs[] = {
1113e8c4d31SAmit Kucheria 	&dev_attr_available_uuids.attr,
1123e8c4d31SAmit Kucheria 	&dev_attr_current_uuid.attr,
1133e8c4d31SAmit Kucheria 	NULL
1143e8c4d31SAmit Kucheria };
1153e8c4d31SAmit Kucheria 
1163e8c4d31SAmit Kucheria static const struct attribute_group uuid_attribute_group = {
1173e8c4d31SAmit Kucheria 	.attrs = uuid_attrs,
1183e8c4d31SAmit Kucheria 	.name = "uuids"
1193e8c4d31SAmit Kucheria };
1203e8c4d31SAmit Kucheria 
1213e8c4d31SAmit Kucheria static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
1223e8c4d31SAmit Kucheria {
1233e8c4d31SAmit Kucheria 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
1243e8c4d31SAmit Kucheria 	union acpi_object *obja, *objb;
1253e8c4d31SAmit Kucheria 	int i, j;
1263e8c4d31SAmit Kucheria 	int result = 0;
1273e8c4d31SAmit Kucheria 	acpi_status status;
1283e8c4d31SAmit Kucheria 
1293e8c4d31SAmit Kucheria 	status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
1303e8c4d31SAmit Kucheria 	if (ACPI_FAILURE(status))
1313e8c4d31SAmit Kucheria 		return -ENODEV;
1323e8c4d31SAmit Kucheria 
1333e8c4d31SAmit Kucheria 	obja = (union acpi_object *)buf.pointer;
1343e8c4d31SAmit Kucheria 	if (obja->type != ACPI_TYPE_PACKAGE) {
1353e8c4d31SAmit Kucheria 		result = -EINVAL;
1363e8c4d31SAmit Kucheria 		goto end;
1373e8c4d31SAmit Kucheria 	}
1383e8c4d31SAmit Kucheria 
1393e8c4d31SAmit Kucheria 	for (i = 0; i < obja->package.count; i++) {
1403e8c4d31SAmit Kucheria 		objb = &obja->package.elements[i];
1413e8c4d31SAmit Kucheria 		if (objb->type != ACPI_TYPE_BUFFER) {
1423e8c4d31SAmit Kucheria 			result = -EINVAL;
1433e8c4d31SAmit Kucheria 			goto end;
1443e8c4d31SAmit Kucheria 		}
1453e8c4d31SAmit Kucheria 
1463e8c4d31SAmit Kucheria 		/* UUID must be 16 bytes */
1473e8c4d31SAmit Kucheria 		if (objb->buffer.length != 16) {
1483e8c4d31SAmit Kucheria 			result = -EINVAL;
1493e8c4d31SAmit Kucheria 			goto end;
1503e8c4d31SAmit Kucheria 		}
1513e8c4d31SAmit Kucheria 
1523e8c4d31SAmit Kucheria 		for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
1533e8c4d31SAmit Kucheria 			guid_t guid;
1543e8c4d31SAmit Kucheria 
1553e8c4d31SAmit Kucheria 			guid_parse(int3400_thermal_uuids[j], &guid);
1563e8c4d31SAmit Kucheria 			if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {
1573e8c4d31SAmit Kucheria 				priv->uuid_bitmap |= (1 << j);
1583e8c4d31SAmit Kucheria 				break;
1593e8c4d31SAmit Kucheria 			}
1603e8c4d31SAmit Kucheria 		}
1613e8c4d31SAmit Kucheria 	}
1623e8c4d31SAmit Kucheria 
1633e8c4d31SAmit Kucheria end:
1643e8c4d31SAmit Kucheria 	kfree(buf.pointer);
1653e8c4d31SAmit Kucheria 	return result;
1663e8c4d31SAmit Kucheria }
1673e8c4d31SAmit Kucheria 
1683e8c4d31SAmit Kucheria static int int3400_thermal_run_osc(acpi_handle handle,
1693e8c4d31SAmit Kucheria 				enum int3400_thermal_uuid uuid, bool enable)
1703e8c4d31SAmit Kucheria {
1713e8c4d31SAmit Kucheria 	u32 ret, buf[2];
1723e8c4d31SAmit Kucheria 	acpi_status status;
1733e8c4d31SAmit Kucheria 	int result = 0;
1743e8c4d31SAmit Kucheria 	struct acpi_osc_context context = {
1753e8c4d31SAmit Kucheria 		.uuid_str = int3400_thermal_uuids[uuid],
1763e8c4d31SAmit Kucheria 		.rev = 1,
1773e8c4d31SAmit Kucheria 		.cap.length = 8,
1783e8c4d31SAmit Kucheria 	};
1793e8c4d31SAmit Kucheria 
1803e8c4d31SAmit Kucheria 	buf[OSC_QUERY_DWORD] = 0;
1813e8c4d31SAmit Kucheria 	buf[OSC_SUPPORT_DWORD] = enable;
1823e8c4d31SAmit Kucheria 
1833e8c4d31SAmit Kucheria 	context.cap.pointer = buf;
1843e8c4d31SAmit Kucheria 
1853e8c4d31SAmit Kucheria 	status = acpi_run_osc(handle, &context);
1863e8c4d31SAmit Kucheria 	if (ACPI_SUCCESS(status)) {
1873e8c4d31SAmit Kucheria 		ret = *((u32 *)(context.ret.pointer + 4));
1883e8c4d31SAmit Kucheria 		if (ret != enable)
1893e8c4d31SAmit Kucheria 			result = -EPERM;
1903e8c4d31SAmit Kucheria 	} else
1913e8c4d31SAmit Kucheria 		result = -EPERM;
1923e8c4d31SAmit Kucheria 
1933e8c4d31SAmit Kucheria 	kfree(context.ret.pointer);
1943e8c4d31SAmit Kucheria 	return result;
1953e8c4d31SAmit Kucheria }
1963e8c4d31SAmit Kucheria 
1973e8c4d31SAmit Kucheria static void int3400_notify(acpi_handle handle,
1983e8c4d31SAmit Kucheria 			u32 event,
1993e8c4d31SAmit Kucheria 			void *data)
2003e8c4d31SAmit Kucheria {
2013e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = data;
2023e8c4d31SAmit Kucheria 	char *thermal_prop[5];
2033e8c4d31SAmit Kucheria 
2043e8c4d31SAmit Kucheria 	if (!priv)
2053e8c4d31SAmit Kucheria 		return;
2063e8c4d31SAmit Kucheria 
2073e8c4d31SAmit Kucheria 	switch (event) {
2083e8c4d31SAmit Kucheria 	case INT3400_THERMAL_TABLE_CHANGED:
2093e8c4d31SAmit Kucheria 		thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s",
2103e8c4d31SAmit Kucheria 				priv->thermal->type);
2113e8c4d31SAmit Kucheria 		thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d",
2123e8c4d31SAmit Kucheria 				priv->thermal->temperature);
2133e8c4d31SAmit Kucheria 		thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");
2143e8c4d31SAmit Kucheria 		thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d",
2153e8c4d31SAmit Kucheria 				THERMAL_TABLE_CHANGED);
2163e8c4d31SAmit Kucheria 		thermal_prop[4] = NULL;
2173e8c4d31SAmit Kucheria 		kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE,
2183e8c4d31SAmit Kucheria 				thermal_prop);
2193e8c4d31SAmit Kucheria 		break;
2203e8c4d31SAmit Kucheria 	default:
2213e8c4d31SAmit Kucheria 		/* Ignore unknown notification codes sent to INT3400 device */
2223e8c4d31SAmit Kucheria 		break;
2233e8c4d31SAmit Kucheria 	}
2243e8c4d31SAmit Kucheria }
2253e8c4d31SAmit Kucheria 
2263e8c4d31SAmit Kucheria static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
2273e8c4d31SAmit Kucheria 			int *temp)
2283e8c4d31SAmit Kucheria {
2293e8c4d31SAmit Kucheria 	*temp = 20 * 1000; /* faked temp sensor with 20C */
2303e8c4d31SAmit Kucheria 	return 0;
2313e8c4d31SAmit Kucheria }
2323e8c4d31SAmit Kucheria 
2333e8c4d31SAmit Kucheria static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
2343e8c4d31SAmit Kucheria 				enum thermal_device_mode *mode)
2353e8c4d31SAmit Kucheria {
2363e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = thermal->devdata;
2373e8c4d31SAmit Kucheria 
2383e8c4d31SAmit Kucheria 	if (!priv)
2393e8c4d31SAmit Kucheria 		return -EINVAL;
2403e8c4d31SAmit Kucheria 
2413e8c4d31SAmit Kucheria 	*mode = priv->mode;
2423e8c4d31SAmit Kucheria 
2433e8c4d31SAmit Kucheria 	return 0;
2443e8c4d31SAmit Kucheria }
2453e8c4d31SAmit Kucheria 
2463e8c4d31SAmit Kucheria static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
2473e8c4d31SAmit Kucheria 				enum thermal_device_mode mode)
2483e8c4d31SAmit Kucheria {
2493e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = thermal->devdata;
2503e8c4d31SAmit Kucheria 	bool enable;
2513e8c4d31SAmit Kucheria 	int result = 0;
2523e8c4d31SAmit Kucheria 
2533e8c4d31SAmit Kucheria 	if (!priv)
2543e8c4d31SAmit Kucheria 		return -EINVAL;
2553e8c4d31SAmit Kucheria 
2563e8c4d31SAmit Kucheria 	if (mode == THERMAL_DEVICE_ENABLED)
2573e8c4d31SAmit Kucheria 		enable = true;
2583e8c4d31SAmit Kucheria 	else if (mode == THERMAL_DEVICE_DISABLED)
2593e8c4d31SAmit Kucheria 		enable = false;
2603e8c4d31SAmit Kucheria 	else
2613e8c4d31SAmit Kucheria 		return -EINVAL;
2623e8c4d31SAmit Kucheria 
2633e8c4d31SAmit Kucheria 	if (enable != priv->mode) {
2643e8c4d31SAmit Kucheria 		priv->mode = enable;
2653e8c4d31SAmit Kucheria 		result = int3400_thermal_run_osc(priv->adev->handle,
2663e8c4d31SAmit Kucheria 						 priv->current_uuid_index,
2673e8c4d31SAmit Kucheria 						 enable);
2683e8c4d31SAmit Kucheria 	}
2693e8c4d31SAmit Kucheria 	return result;
2703e8c4d31SAmit Kucheria }
2713e8c4d31SAmit Kucheria 
2723e8c4d31SAmit Kucheria static struct thermal_zone_device_ops int3400_thermal_ops = {
2733e8c4d31SAmit Kucheria 	.get_temp = int3400_thermal_get_temp,
27479799562SAndrzej Pietrasiewicz 	.get_mode = int3400_thermal_get_mode,
27579799562SAndrzej Pietrasiewicz 	.set_mode = int3400_thermal_set_mode,
2763e8c4d31SAmit Kucheria };
2773e8c4d31SAmit Kucheria 
2783e8c4d31SAmit Kucheria static struct thermal_zone_params int3400_thermal_params = {
2793e8c4d31SAmit Kucheria 	.governor_name = "user_space",
2803e8c4d31SAmit Kucheria 	.no_hwmon = true,
2813e8c4d31SAmit Kucheria };
2823e8c4d31SAmit Kucheria 
2833e8c4d31SAmit Kucheria static int int3400_thermal_probe(struct platform_device *pdev)
2843e8c4d31SAmit Kucheria {
2853e8c4d31SAmit Kucheria 	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
2863e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv;
2873e8c4d31SAmit Kucheria 	int result;
2883e8c4d31SAmit Kucheria 
2893e8c4d31SAmit Kucheria 	if (!adev)
2903e8c4d31SAmit Kucheria 		return -ENODEV;
2913e8c4d31SAmit Kucheria 
2923e8c4d31SAmit Kucheria 	priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
2933e8c4d31SAmit Kucheria 	if (!priv)
2943e8c4d31SAmit Kucheria 		return -ENOMEM;
2953e8c4d31SAmit Kucheria 
2963e8c4d31SAmit Kucheria 	priv->adev = adev;
2973e8c4d31SAmit Kucheria 
2983e8c4d31SAmit Kucheria 	result = int3400_thermal_get_uuids(priv);
2993e8c4d31SAmit Kucheria 	if (result)
3003e8c4d31SAmit Kucheria 		goto free_priv;
3013e8c4d31SAmit Kucheria 
3023e8c4d31SAmit Kucheria 	result = acpi_parse_art(priv->adev->handle, &priv->art_count,
3033e8c4d31SAmit Kucheria 				&priv->arts, true);
3043e8c4d31SAmit Kucheria 	if (result)
3053e8c4d31SAmit Kucheria 		dev_dbg(&pdev->dev, "_ART table parsing error\n");
3063e8c4d31SAmit Kucheria 
3073e8c4d31SAmit Kucheria 	result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
3083e8c4d31SAmit Kucheria 				&priv->trts, true);
3093e8c4d31SAmit Kucheria 	if (result)
3103e8c4d31SAmit Kucheria 		dev_dbg(&pdev->dev, "_TRT table parsing error\n");
3113e8c4d31SAmit Kucheria 
3123e8c4d31SAmit Kucheria 	platform_set_drvdata(pdev, priv);
3133e8c4d31SAmit Kucheria 
3143e8c4d31SAmit Kucheria 	priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
3153e8c4d31SAmit Kucheria 						priv, &int3400_thermal_ops,
3163e8c4d31SAmit Kucheria 						&int3400_thermal_params, 0, 0);
3173e8c4d31SAmit Kucheria 	if (IS_ERR(priv->thermal)) {
3183e8c4d31SAmit Kucheria 		result = PTR_ERR(priv->thermal);
3193e8c4d31SAmit Kucheria 		goto free_art_trt;
3203e8c4d31SAmit Kucheria 	}
3213e8c4d31SAmit Kucheria 
3223e8c4d31SAmit Kucheria 	priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
3233e8c4d31SAmit Kucheria 							priv->adev->handle);
3243e8c4d31SAmit Kucheria 
3253e8c4d31SAmit Kucheria 	result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);
3263e8c4d31SAmit Kucheria 	if (result)
3273e8c4d31SAmit Kucheria 		goto free_rel_misc;
3283e8c4d31SAmit Kucheria 
3293e8c4d31SAmit Kucheria 	result = acpi_install_notify_handler(
3303e8c4d31SAmit Kucheria 			priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
3313e8c4d31SAmit Kucheria 			(void *)priv);
3323e8c4d31SAmit Kucheria 	if (result)
3333e8c4d31SAmit Kucheria 		goto free_sysfs;
3343e8c4d31SAmit Kucheria 
3353e8c4d31SAmit Kucheria 	return 0;
3363e8c4d31SAmit Kucheria 
3373e8c4d31SAmit Kucheria free_sysfs:
3383e8c4d31SAmit Kucheria 	sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
3393e8c4d31SAmit Kucheria free_rel_misc:
3403e8c4d31SAmit Kucheria 	if (!priv->rel_misc_dev_res)
3413e8c4d31SAmit Kucheria 		acpi_thermal_rel_misc_device_remove(priv->adev->handle);
3423e8c4d31SAmit Kucheria 	thermal_zone_device_unregister(priv->thermal);
3433e8c4d31SAmit Kucheria free_art_trt:
3443e8c4d31SAmit Kucheria 	kfree(priv->trts);
3453e8c4d31SAmit Kucheria 	kfree(priv->arts);
3463e8c4d31SAmit Kucheria free_priv:
3473e8c4d31SAmit Kucheria 	kfree(priv);
3483e8c4d31SAmit Kucheria 	return result;
3493e8c4d31SAmit Kucheria }
3503e8c4d31SAmit Kucheria 
3513e8c4d31SAmit Kucheria static int int3400_thermal_remove(struct platform_device *pdev)
3523e8c4d31SAmit Kucheria {
3533e8c4d31SAmit Kucheria 	struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
3543e8c4d31SAmit Kucheria 
3553e8c4d31SAmit Kucheria 	acpi_remove_notify_handler(
3563e8c4d31SAmit Kucheria 			priv->adev->handle, ACPI_DEVICE_NOTIFY,
3573e8c4d31SAmit Kucheria 			int3400_notify);
3583e8c4d31SAmit Kucheria 
3593e8c4d31SAmit Kucheria 	if (!priv->rel_misc_dev_res)
3603e8c4d31SAmit Kucheria 		acpi_thermal_rel_misc_device_remove(priv->adev->handle);
3613e8c4d31SAmit Kucheria 
3623e8c4d31SAmit Kucheria 	sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
3633e8c4d31SAmit Kucheria 	thermal_zone_device_unregister(priv->thermal);
3643e8c4d31SAmit Kucheria 	kfree(priv->trts);
3653e8c4d31SAmit Kucheria 	kfree(priv->arts);
3663e8c4d31SAmit Kucheria 	kfree(priv);
3673e8c4d31SAmit Kucheria 	return 0;
3683e8c4d31SAmit Kucheria }
3693e8c4d31SAmit Kucheria 
3703e8c4d31SAmit Kucheria static const struct acpi_device_id int3400_thermal_match[] = {
3713e8c4d31SAmit Kucheria 	{"INT3400", 0},
37226d8bec1SGayatri Kammela 	{"INTC1040", 0},
3733e8c4d31SAmit Kucheria 	{}
3743e8c4d31SAmit Kucheria };
3753e8c4d31SAmit Kucheria 
3763e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
3773e8c4d31SAmit Kucheria 
3783e8c4d31SAmit Kucheria static struct platform_driver int3400_thermal_driver = {
3793e8c4d31SAmit Kucheria 	.probe = int3400_thermal_probe,
3803e8c4d31SAmit Kucheria 	.remove = int3400_thermal_remove,
3813e8c4d31SAmit Kucheria 	.driver = {
3823e8c4d31SAmit Kucheria 		   .name = "int3400 thermal",
3833e8c4d31SAmit Kucheria 		   .acpi_match_table = ACPI_PTR(int3400_thermal_match),
3843e8c4d31SAmit Kucheria 		   },
3853e8c4d31SAmit Kucheria };
3863e8c4d31SAmit Kucheria 
3873e8c4d31SAmit Kucheria module_platform_driver(int3400_thermal_driver);
3883e8c4d31SAmit Kucheria 
3893e8c4d31SAmit Kucheria MODULE_DESCRIPTION("INT3400 Thermal driver");
3903e8c4d31SAmit Kucheria MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
3913e8c4d31SAmit Kucheria MODULE_LICENSE("GPL");
392