Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * INT3400 thermal driver
0004  *
0005  * Copyright (C) 2014, Intel Corporation
0006  * Authors: Zhang Rui <rui.zhang@intel.com>
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/acpi.h>
0012 #include <linux/thermal.h>
0013 #include "acpi_thermal_rel.h"
0014 
0015 #define INT3400_THERMAL_TABLE_CHANGED 0x83
0016 #define INT3400_ODVP_CHANGED 0x88
0017 #define INT3400_KEEP_ALIVE 0xA0
0018 
0019 enum int3400_thermal_uuid {
0020     INT3400_THERMAL_ACTIVE = 0,
0021     INT3400_THERMAL_PASSIVE_1,
0022     INT3400_THERMAL_CRITICAL,
0023     INT3400_THERMAL_ADAPTIVE_PERFORMANCE,
0024     INT3400_THERMAL_EMERGENCY_CALL_MODE,
0025     INT3400_THERMAL_PASSIVE_2,
0026     INT3400_THERMAL_POWER_BOSS,
0027     INT3400_THERMAL_VIRTUAL_SENSOR,
0028     INT3400_THERMAL_COOLING_MODE,
0029     INT3400_THERMAL_HARDWARE_DUTY_CYCLING,
0030     INT3400_THERMAL_MAXIMUM_UUID,
0031 };
0032 
0033 static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
0034     "3A95C389-E4B8-4629-A526-C52C88626BAE",
0035     "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
0036     "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
0037     "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",
0038     "5349962F-71E6-431D-9AE8-0A635B710AEE",
0039     "9E04115A-AE87-4D1C-9500-0F3E340BFE75",
0040     "F5A35014-C209-46A4-993A-EB56DE7530A1",
0041     "6ED722A7-9240-48A5-B479-31EEF723D7CF",
0042     "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",
0043     "BE84BABF-C4D4-403D-B495-3128FD44dAC1",
0044 };
0045 
0046 struct odvp_attr;
0047 
0048 struct int3400_thermal_priv {
0049     struct acpi_device *adev;
0050     struct platform_device *pdev;
0051     struct thermal_zone_device *thermal;
0052     int art_count;
0053     struct art *arts;
0054     int trt_count;
0055     struct trt *trts;
0056     u32 uuid_bitmap;
0057     int rel_misc_dev_res;
0058     int current_uuid_index;
0059     char *data_vault;
0060     int odvp_count;
0061     int *odvp;
0062     u32 os_uuid_mask;
0063     struct odvp_attr *odvp_attrs;
0064 };
0065 
0066 static int evaluate_odvp(struct int3400_thermal_priv *priv);
0067 
0068 struct odvp_attr {
0069     int odvp;
0070     struct int3400_thermal_priv *priv;
0071     struct device_attribute attr;
0072 };
0073 
0074 static ssize_t data_vault_read(struct file *file, struct kobject *kobj,
0075          struct bin_attribute *attr, char *buf, loff_t off, size_t count)
0076 {
0077     memcpy(buf, attr->private + off, count);
0078     return count;
0079 }
0080 
0081 static BIN_ATTR_RO(data_vault, 0);
0082 
0083 static struct bin_attribute *data_attributes[] = {
0084     &bin_attr_data_vault,
0085     NULL,
0086 };
0087 
0088 static ssize_t imok_store(struct device *dev, struct device_attribute *attr,
0089               const char *buf, size_t count)
0090 {
0091     struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
0092     acpi_status status;
0093     int input, ret;
0094 
0095     ret = kstrtouint(buf, 10, &input);
0096     if (ret)
0097         return ret;
0098     status = acpi_execute_simple_method(priv->adev->handle, "IMOK", input);
0099     if (ACPI_FAILURE(status))
0100         return -EIO;
0101 
0102     return count;
0103 }
0104 
0105 static DEVICE_ATTR_WO(imok);
0106 
0107 static struct attribute *imok_attr[] = {
0108     &dev_attr_imok.attr,
0109     NULL
0110 };
0111 
0112 static const struct attribute_group imok_attribute_group = {
0113     .attrs = imok_attr,
0114 };
0115 
0116 static const struct attribute_group data_attribute_group = {
0117     .bin_attrs = data_attributes,
0118 };
0119 
0120 static ssize_t available_uuids_show(struct device *dev,
0121                     struct device_attribute *attr,
0122                     char *buf)
0123 {
0124     struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
0125     int i;
0126     int length = 0;
0127 
0128     if (!priv->uuid_bitmap)
0129         return sprintf(buf, "UNKNOWN\n");
0130 
0131     for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
0132         if (priv->uuid_bitmap & (1 << i))
0133             length += scnprintf(&buf[length],
0134                         PAGE_SIZE - length,
0135                         "%s\n",
0136                         int3400_thermal_uuids[i]);
0137     }
0138 
0139     return length;
0140 }
0141 
0142 static ssize_t current_uuid_show(struct device *dev,
0143                  struct device_attribute *devattr, char *buf)
0144 {
0145     struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
0146     int i, length = 0;
0147 
0148     if (priv->current_uuid_index > 0)
0149         return sprintf(buf, "%s\n",
0150                    int3400_thermal_uuids[priv->current_uuid_index]);
0151 
0152     for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
0153         if (priv->os_uuid_mask & BIT(i))
0154             length += scnprintf(&buf[length],
0155                         PAGE_SIZE - length,
0156                         "%s\n",
0157                         int3400_thermal_uuids[i]);
0158     }
0159 
0160     if (length)
0161         return length;
0162 
0163     return sprintf(buf, "INVALID\n");
0164 }
0165 
0166 static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable)
0167 {
0168     u32 ret, buf[2];
0169     acpi_status status;
0170     int result = 0;
0171     struct acpi_osc_context context = {
0172         .uuid_str = uuid_str,
0173         .rev = 1,
0174         .cap.length = 8,
0175         .cap.pointer = buf,
0176     };
0177 
0178     buf[OSC_QUERY_DWORD] = 0;
0179     buf[OSC_SUPPORT_DWORD] = *enable;
0180 
0181     status = acpi_run_osc(handle, &context);
0182     if (ACPI_SUCCESS(status)) {
0183         ret = *((u32 *)(context.ret.pointer + 4));
0184         if (ret != *enable)
0185             result = -EPERM;
0186 
0187         kfree(context.ret.pointer);
0188     } else
0189         result = -EPERM;
0190 
0191     return result;
0192 }
0193 
0194 static int set_os_uuid_mask(struct int3400_thermal_priv *priv, u32 mask)
0195 {
0196     int cap = 0;
0197 
0198     /*
0199      * Capability bits:
0200      * Bit 0: set to 1 to indicate DPTF is active
0201      * Bi1 1: set to 1 to active cooling is supported by user space daemon
0202      * Bit 2: set to 1 to passive cooling is supported by user space daemon
0203      * Bit 3: set to 1 to critical trip is handled by user space daemon
0204      */
0205     if (mask)
0206         cap = (priv->os_uuid_mask << 1) | 0x01;
0207 
0208     return int3400_thermal_run_osc(priv->adev->handle,
0209                        "b23ba85d-c8b7-3542-88de-8de2ffcfd698",
0210                        &cap);
0211 }
0212 
0213 static ssize_t current_uuid_store(struct device *dev,
0214                   struct device_attribute *attr,
0215                   const char *buf, size_t count)
0216 {
0217     struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
0218     int ret, i;
0219 
0220     for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
0221         if (!strncmp(buf, int3400_thermal_uuids[i],
0222                  sizeof(int3400_thermal_uuids[i]) - 1)) {
0223             /*
0224              * If we have a list of supported UUIDs, make sure
0225              * this one is supported.
0226              */
0227             if (priv->uuid_bitmap & BIT(i)) {
0228                 priv->current_uuid_index = i;
0229                 return count;
0230             }
0231 
0232             /*
0233              * There is support of only 3 policies via the new
0234              * _OSC to inform OS capability:
0235              * INT3400_THERMAL_ACTIVE
0236              * INT3400_THERMAL_PASSIVE_1
0237              * INT3400_THERMAL_CRITICAL
0238              */
0239 
0240             if (i > INT3400_THERMAL_CRITICAL)
0241                 return -EINVAL;
0242 
0243             priv->os_uuid_mask |= BIT(i);
0244 
0245             break;
0246         }
0247     }
0248 
0249     if (priv->os_uuid_mask) {
0250         ret = set_os_uuid_mask(priv, priv->os_uuid_mask);
0251         if (ret)
0252             return ret;
0253     }
0254 
0255     return count;
0256 }
0257 
0258 static DEVICE_ATTR_RW(current_uuid);
0259 static DEVICE_ATTR_RO(available_uuids);
0260 static struct attribute *uuid_attrs[] = {
0261     &dev_attr_available_uuids.attr,
0262     &dev_attr_current_uuid.attr,
0263     NULL
0264 };
0265 
0266 static const struct attribute_group uuid_attribute_group = {
0267     .attrs = uuid_attrs,
0268     .name = "uuids"
0269 };
0270 
0271 static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
0272 {
0273     struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
0274     union acpi_object *obja, *objb;
0275     int i, j;
0276     int result = 0;
0277     acpi_status status;
0278 
0279     status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
0280     if (ACPI_FAILURE(status))
0281         return -ENODEV;
0282 
0283     obja = (union acpi_object *)buf.pointer;
0284     if (obja->type != ACPI_TYPE_PACKAGE) {
0285         result = -EINVAL;
0286         goto end;
0287     }
0288 
0289     for (i = 0; i < obja->package.count; i++) {
0290         objb = &obja->package.elements[i];
0291         if (objb->type != ACPI_TYPE_BUFFER) {
0292             result = -EINVAL;
0293             goto end;
0294         }
0295 
0296         /* UUID must be 16 bytes */
0297         if (objb->buffer.length != 16) {
0298             result = -EINVAL;
0299             goto end;
0300         }
0301 
0302         for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
0303             guid_t guid;
0304 
0305             guid_parse(int3400_thermal_uuids[j], &guid);
0306             if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {
0307                 priv->uuid_bitmap |= (1 << j);
0308                 break;
0309             }
0310         }
0311     }
0312 
0313 end:
0314     kfree(buf.pointer);
0315     return result;
0316 }
0317 
0318 static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
0319              char *buf)
0320 {
0321     struct odvp_attr *odvp_attr;
0322 
0323     odvp_attr = container_of(attr, struct odvp_attr, attr);
0324 
0325     return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
0326 }
0327 
0328 static void cleanup_odvp(struct int3400_thermal_priv *priv)
0329 {
0330     int i;
0331 
0332     if (priv->odvp_attrs) {
0333         for (i = 0; i < priv->odvp_count; i++) {
0334             sysfs_remove_file(&priv->pdev->dev.kobj,
0335                       &priv->odvp_attrs[i].attr.attr);
0336             kfree(priv->odvp_attrs[i].attr.attr.name);
0337         }
0338         kfree(priv->odvp_attrs);
0339     }
0340     kfree(priv->odvp);
0341     priv->odvp_count = 0;
0342 }
0343 
0344 static int evaluate_odvp(struct int3400_thermal_priv *priv)
0345 {
0346     struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL };
0347     union acpi_object *obj = NULL;
0348     acpi_status status;
0349     int i, ret;
0350 
0351     status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp);
0352     if (ACPI_FAILURE(status)) {
0353         ret = -EINVAL;
0354         goto out_err;
0355     }
0356 
0357     obj = odvp.pointer;
0358     if (obj->type != ACPI_TYPE_PACKAGE) {
0359         ret = -EINVAL;
0360         goto out_err;
0361     }
0362 
0363     if (priv->odvp == NULL) {
0364         priv->odvp_count = obj->package.count;
0365         priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),
0366                      GFP_KERNEL);
0367         if (!priv->odvp) {
0368             ret = -ENOMEM;
0369             goto out_err;
0370         }
0371     }
0372 
0373     if (priv->odvp_attrs == NULL) {
0374         priv->odvp_attrs = kcalloc(priv->odvp_count,
0375                        sizeof(struct odvp_attr),
0376                        GFP_KERNEL);
0377         if (!priv->odvp_attrs) {
0378             ret = -ENOMEM;
0379             goto out_err;
0380         }
0381         for (i = 0; i < priv->odvp_count; i++) {
0382             struct odvp_attr *odvp = &priv->odvp_attrs[i];
0383 
0384             sysfs_attr_init(&odvp->attr.attr);
0385             odvp->priv = priv;
0386             odvp->odvp = i;
0387             odvp->attr.attr.name = kasprintf(GFP_KERNEL,
0388                              "odvp%d", i);
0389 
0390             if (!odvp->attr.attr.name) {
0391                 ret = -ENOMEM;
0392                 goto out_err;
0393             }
0394             odvp->attr.attr.mode = 0444;
0395             odvp->attr.show = odvp_show;
0396             odvp->attr.store = NULL;
0397             ret = sysfs_create_file(&priv->pdev->dev.kobj,
0398                         &odvp->attr.attr);
0399             if (ret)
0400                 goto out_err;
0401         }
0402     }
0403 
0404     for (i = 0; i < obj->package.count; i++) {
0405         if (obj->package.elements[i].type == ACPI_TYPE_INTEGER)
0406             priv->odvp[i] = obj->package.elements[i].integer.value;
0407     }
0408 
0409     kfree(obj);
0410     return 0;
0411 
0412 out_err:
0413     cleanup_odvp(priv);
0414     kfree(obj);
0415     return ret;
0416 }
0417 
0418 static void int3400_notify(acpi_handle handle,
0419             u32 event,
0420             void *data)
0421 {
0422     struct int3400_thermal_priv *priv = data;
0423     char *thermal_prop[5];
0424     int therm_event;
0425 
0426     if (!priv)
0427         return;
0428 
0429     switch (event) {
0430     case INT3400_THERMAL_TABLE_CHANGED:
0431         therm_event = THERMAL_TABLE_CHANGED;
0432         break;
0433     case INT3400_KEEP_ALIVE:
0434         therm_event = THERMAL_EVENT_KEEP_ALIVE;
0435         break;
0436     case INT3400_ODVP_CHANGED:
0437         evaluate_odvp(priv);
0438         therm_event = THERMAL_DEVICE_POWER_CAPABILITY_CHANGED;
0439         break;
0440     default:
0441         /* Ignore unknown notification codes sent to INT3400 device */
0442         return;
0443     }
0444 
0445     thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", priv->thermal->type);
0446     thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", priv->thermal->temperature);
0447     thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");
0448     thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", therm_event);
0449     thermal_prop[4] = NULL;
0450     kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, thermal_prop);
0451     kfree(thermal_prop[0]);
0452     kfree(thermal_prop[1]);
0453     kfree(thermal_prop[2]);
0454     kfree(thermal_prop[3]);
0455 }
0456 
0457 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
0458             int *temp)
0459 {
0460     *temp = 20 * 1000; /* faked temp sensor with 20C */
0461     return 0;
0462 }
0463 
0464 static int int3400_thermal_change_mode(struct thermal_zone_device *thermal,
0465                        enum thermal_device_mode mode)
0466 {
0467     struct int3400_thermal_priv *priv = thermal->devdata;
0468     int result = 0;
0469 
0470     if (!priv)
0471         return -EINVAL;
0472 
0473     if (mode != thermal->mode) {
0474         int enabled;
0475 
0476         enabled = mode == THERMAL_DEVICE_ENABLED;
0477 
0478         if (priv->os_uuid_mask) {
0479             if (!enabled) {
0480                 priv->os_uuid_mask = 0;
0481                 result = set_os_uuid_mask(priv, priv->os_uuid_mask);
0482             }
0483             goto eval_odvp;
0484         }
0485 
0486         if (priv->current_uuid_index < 0 ||
0487             priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID)
0488             return -EINVAL;
0489 
0490         result = int3400_thermal_run_osc(priv->adev->handle,
0491                          int3400_thermal_uuids[priv->current_uuid_index],
0492                          &enabled);
0493     }
0494 
0495 eval_odvp:
0496     evaluate_odvp(priv);
0497 
0498     return result;
0499 }
0500 
0501 static struct thermal_zone_device_ops int3400_thermal_ops = {
0502     .get_temp = int3400_thermal_get_temp,
0503     .change_mode = int3400_thermal_change_mode,
0504 };
0505 
0506 static struct thermal_zone_params int3400_thermal_params = {
0507     .governor_name = "user_space",
0508     .no_hwmon = true,
0509 };
0510 
0511 static void int3400_setup_gddv(struct int3400_thermal_priv *priv)
0512 {
0513     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0514     union acpi_object *obj;
0515     acpi_status status;
0516 
0517     status = acpi_evaluate_object(priv->adev->handle, "GDDV", NULL,
0518                       &buffer);
0519     if (ACPI_FAILURE(status) || !buffer.length)
0520         return;
0521 
0522     obj = buffer.pointer;
0523     if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 1
0524         || obj->package.elements[0].type != ACPI_TYPE_BUFFER)
0525         goto out_free;
0526 
0527     priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer,
0528                    obj->package.elements[0].buffer.length,
0529                    GFP_KERNEL);
0530     if (ZERO_OR_NULL_PTR(priv->data_vault))
0531         goto out_free;
0532 
0533     bin_attr_data_vault.private = priv->data_vault;
0534     bin_attr_data_vault.size = obj->package.elements[0].buffer.length;
0535 out_free:
0536     kfree(buffer.pointer);
0537 }
0538 
0539 static int int3400_thermal_probe(struct platform_device *pdev)
0540 {
0541     struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
0542     struct int3400_thermal_priv *priv;
0543     int result;
0544 
0545     if (!adev)
0546         return -ENODEV;
0547 
0548     priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
0549     if (!priv)
0550         return -ENOMEM;
0551 
0552     priv->pdev = pdev;
0553     priv->adev = adev;
0554 
0555     result = int3400_thermal_get_uuids(priv);
0556 
0557     /* Missing IDSP isn't fatal */
0558     if (result && result != -ENODEV)
0559         goto free_priv;
0560 
0561     priv->current_uuid_index = -1;
0562 
0563     result = acpi_parse_art(priv->adev->handle, &priv->art_count,
0564                 &priv->arts, true);
0565     if (result)
0566         dev_dbg(&pdev->dev, "_ART table parsing error\n");
0567 
0568     result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
0569                 &priv->trts, true);
0570     if (result)
0571         dev_dbg(&pdev->dev, "_TRT table parsing error\n");
0572 
0573     platform_set_drvdata(pdev, priv);
0574 
0575     int3400_setup_gddv(priv);
0576 
0577     evaluate_odvp(priv);
0578 
0579     priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
0580                         priv, &int3400_thermal_ops,
0581                         &int3400_thermal_params, 0, 0);
0582     if (IS_ERR(priv->thermal)) {
0583         result = PTR_ERR(priv->thermal);
0584         goto free_art_trt;
0585     }
0586 
0587     priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
0588                             priv->adev->handle);
0589 
0590     result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);
0591     if (result)
0592         goto free_rel_misc;
0593 
0594     if (acpi_has_method(priv->adev->handle, "IMOK")) {
0595         result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group);
0596         if (result)
0597             goto free_imok;
0598     }
0599 
0600     if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
0601         result = sysfs_create_group(&pdev->dev.kobj,
0602                         &data_attribute_group);
0603         if (result)
0604             goto free_uuid;
0605     }
0606 
0607     result = acpi_install_notify_handler(
0608             priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
0609             (void *)priv);
0610     if (result)
0611         goto free_sysfs;
0612 
0613     return 0;
0614 
0615 free_sysfs:
0616     cleanup_odvp(priv);
0617     if (priv->data_vault) {
0618         if (!ZERO_OR_NULL_PTR(priv->data_vault))
0619             sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
0620         kfree(priv->data_vault);
0621     }
0622 free_uuid:
0623     sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
0624 free_imok:
0625     sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);
0626 free_rel_misc:
0627     if (!priv->rel_misc_dev_res)
0628         acpi_thermal_rel_misc_device_remove(priv->adev->handle);
0629     thermal_zone_device_unregister(priv->thermal);
0630 free_art_trt:
0631     kfree(priv->trts);
0632     kfree(priv->arts);
0633 free_priv:
0634     kfree(priv);
0635     return result;
0636 }
0637 
0638 static int int3400_thermal_remove(struct platform_device *pdev)
0639 {
0640     struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
0641 
0642     acpi_remove_notify_handler(
0643             priv->adev->handle, ACPI_DEVICE_NOTIFY,
0644             int3400_notify);
0645 
0646     cleanup_odvp(priv);
0647 
0648     if (!priv->rel_misc_dev_res)
0649         acpi_thermal_rel_misc_device_remove(priv->adev->handle);
0650 
0651     if (!ZERO_OR_NULL_PTR(priv->data_vault))
0652         sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
0653     sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
0654     sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group);
0655     thermal_zone_device_unregister(priv->thermal);
0656     kfree(priv->data_vault);
0657     kfree(priv->trts);
0658     kfree(priv->arts);
0659     kfree(priv);
0660     return 0;
0661 }
0662 
0663 static const struct acpi_device_id int3400_thermal_match[] = {
0664     {"INT3400", 0},
0665     {"INTC1040", 0},
0666     {"INTC1041", 0},
0667     {"INTC1042", 0},
0668     {"INTC10A0", 0},
0669     {}
0670 };
0671 
0672 MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
0673 
0674 static struct platform_driver int3400_thermal_driver = {
0675     .probe = int3400_thermal_probe,
0676     .remove = int3400_thermal_remove,
0677     .driver = {
0678            .name = "int3400 thermal",
0679            .acpi_match_table = ACPI_PTR(int3400_thermal_match),
0680            },
0681 };
0682 
0683 module_platform_driver(int3400_thermal_driver);
0684 
0685 MODULE_DESCRIPTION("INT3400 Thermal driver");
0686 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
0687 MODULE_LICENSE("GPL");