cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

int3400_thermal.c (16243B)


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