Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * ACPI INT3403 thermal driver
0004  * Copyright (c) 2013, Intel Corporation.
0005  */
0006 
0007 #include <linux/kernel.h>
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/types.h>
0011 #include <linux/acpi.h>
0012 #include <linux/thermal.h>
0013 #include <linux/platform_device.h>
0014 #include "int340x_thermal_zone.h"
0015 
0016 #define INT3403_TYPE_SENSOR     0x03
0017 #define INT3403_TYPE_CHARGER        0x0B
0018 #define INT3403_TYPE_BATTERY        0x0C
0019 #define INT3403_PERF_CHANGED_EVENT  0x80
0020 #define INT3403_PERF_TRIP_POINT_CHANGED 0x81
0021 #define INT3403_THERMAL_EVENT       0x90
0022 
0023 /* Preserved structure for future expandbility */
0024 struct int3403_sensor {
0025     struct int34x_thermal_zone *int340x_zone;
0026 };
0027 
0028 struct int3403_performance_state {
0029     u64 performance;
0030     u64 power;
0031     u64 latency;
0032     u64 linear;
0033     u64 control;
0034     u64 raw_performace;
0035     char *raw_unit;
0036     int reserved;
0037 };
0038 
0039 struct int3403_cdev {
0040     struct thermal_cooling_device *cdev;
0041     unsigned long max_state;
0042 };
0043 
0044 struct int3403_priv {
0045     struct platform_device *pdev;
0046     struct acpi_device *adev;
0047     unsigned long long type;
0048     void *priv;
0049 };
0050 
0051 static void int3403_notify(acpi_handle handle,
0052         u32 event, void *data)
0053 {
0054     struct int3403_priv *priv = data;
0055     struct int3403_sensor *obj;
0056 
0057     if (!priv)
0058         return;
0059 
0060     obj = priv->priv;
0061     if (priv->type != INT3403_TYPE_SENSOR || !obj)
0062         return;
0063 
0064     switch (event) {
0065     case INT3403_PERF_CHANGED_EVENT:
0066         break;
0067     case INT3403_THERMAL_EVENT:
0068         int340x_thermal_zone_device_update(obj->int340x_zone,
0069                            THERMAL_TRIP_VIOLATED);
0070         break;
0071     case INT3403_PERF_TRIP_POINT_CHANGED:
0072         int340x_thermal_read_trips(obj->int340x_zone);
0073         int340x_thermal_zone_device_update(obj->int340x_zone,
0074                            THERMAL_TRIP_CHANGED);
0075         break;
0076     default:
0077         dev_dbg(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
0078         break;
0079     }
0080 }
0081 
0082 static int int3403_sensor_add(struct int3403_priv *priv)
0083 {
0084     int result = 0;
0085     struct int3403_sensor *obj;
0086 
0087     obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
0088     if (!obj)
0089         return -ENOMEM;
0090 
0091     priv->priv = obj;
0092 
0093     obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
0094     if (IS_ERR(obj->int340x_zone))
0095         return PTR_ERR(obj->int340x_zone);
0096 
0097     result = acpi_install_notify_handler(priv->adev->handle,
0098             ACPI_DEVICE_NOTIFY, int3403_notify,
0099             (void *)priv);
0100     if (result)
0101         goto err_free_obj;
0102 
0103     return 0;
0104 
0105  err_free_obj:
0106     int340x_thermal_zone_remove(obj->int340x_zone);
0107     return result;
0108 }
0109 
0110 static int int3403_sensor_remove(struct int3403_priv *priv)
0111 {
0112     struct int3403_sensor *obj = priv->priv;
0113 
0114     acpi_remove_notify_handler(priv->adev->handle,
0115                    ACPI_DEVICE_NOTIFY, int3403_notify);
0116     int340x_thermal_zone_remove(obj->int340x_zone);
0117 
0118     return 0;
0119 }
0120 
0121 /* INT3403 Cooling devices */
0122 static int int3403_get_max_state(struct thermal_cooling_device *cdev,
0123                  unsigned long *state)
0124 {
0125     struct int3403_priv *priv = cdev->devdata;
0126     struct int3403_cdev *obj = priv->priv;
0127 
0128     *state = obj->max_state;
0129     return 0;
0130 }
0131 
0132 static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
0133                  unsigned long *state)
0134 {
0135     struct int3403_priv *priv = cdev->devdata;
0136     unsigned long long level;
0137     acpi_status status;
0138 
0139     status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
0140     if (ACPI_SUCCESS(status)) {
0141         *state = level;
0142         return 0;
0143     } else
0144         return -EINVAL;
0145 }
0146 
0147 static int
0148 int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
0149 {
0150     struct int3403_priv *priv = cdev->devdata;
0151     acpi_status status;
0152 
0153     status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
0154     if (ACPI_SUCCESS(status))
0155         return 0;
0156     else
0157         return -EINVAL;
0158 }
0159 
0160 static const struct thermal_cooling_device_ops int3403_cooling_ops = {
0161     .get_max_state = int3403_get_max_state,
0162     .get_cur_state = int3403_get_cur_state,
0163     .set_cur_state = int3403_set_cur_state,
0164 };
0165 
0166 static int int3403_cdev_add(struct int3403_priv *priv)
0167 {
0168     int result = 0;
0169     acpi_status status;
0170     struct int3403_cdev *obj;
0171     struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
0172     union acpi_object *p;
0173 
0174     obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
0175     if (!obj)
0176         return -ENOMEM;
0177 
0178     status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
0179     if (ACPI_FAILURE(status))
0180         return -ENODEV;
0181 
0182     p = buf.pointer;
0183     if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
0184         pr_warn("Invalid PPSS data\n");
0185         kfree(buf.pointer);
0186         return -EFAULT;
0187     }
0188 
0189     priv->priv = obj;
0190     obj->max_state = p->package.count - 1;
0191     obj->cdev =
0192         thermal_cooling_device_register(acpi_device_bid(priv->adev),
0193                 priv, &int3403_cooling_ops);
0194     if (IS_ERR(obj->cdev))
0195         result = PTR_ERR(obj->cdev);
0196 
0197     kfree(buf.pointer);
0198     /* TODO: add ACPI notification support */
0199 
0200     return result;
0201 }
0202 
0203 static int int3403_cdev_remove(struct int3403_priv *priv)
0204 {
0205     struct int3403_cdev *obj = priv->priv;
0206 
0207     thermal_cooling_device_unregister(obj->cdev);
0208     return 0;
0209 }
0210 
0211 static int int3403_add(struct platform_device *pdev)
0212 {
0213     struct int3403_priv *priv;
0214     int result = 0;
0215     unsigned long long tmp;
0216     acpi_status status;
0217 
0218     priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
0219                 GFP_KERNEL);
0220     if (!priv)
0221         return -ENOMEM;
0222 
0223     priv->pdev = pdev;
0224     priv->adev = ACPI_COMPANION(&(pdev->dev));
0225     if (!priv->adev) {
0226         result = -EINVAL;
0227         goto err;
0228     }
0229 
0230 
0231     status = acpi_evaluate_integer(priv->adev->handle, "_TMP",
0232                        NULL, &tmp);
0233     if (ACPI_FAILURE(status)) {
0234         status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
0235                        NULL, &priv->type);
0236         if (ACPI_FAILURE(status)) {
0237             result = -EINVAL;
0238             goto err;
0239         }
0240     } else {
0241         priv->type = INT3403_TYPE_SENSOR;
0242     }
0243 
0244     platform_set_drvdata(pdev, priv);
0245     switch (priv->type) {
0246     case INT3403_TYPE_SENSOR:
0247         result = int3403_sensor_add(priv);
0248         break;
0249     case INT3403_TYPE_CHARGER:
0250     case INT3403_TYPE_BATTERY:
0251         result = int3403_cdev_add(priv);
0252         break;
0253     default:
0254         result = -EINVAL;
0255     }
0256 
0257     if (result)
0258         goto err;
0259     return result;
0260 
0261 err:
0262     return result;
0263 }
0264 
0265 static int int3403_remove(struct platform_device *pdev)
0266 {
0267     struct int3403_priv *priv = platform_get_drvdata(pdev);
0268 
0269     switch (priv->type) {
0270     case INT3403_TYPE_SENSOR:
0271         int3403_sensor_remove(priv);
0272         break;
0273     case INT3403_TYPE_CHARGER:
0274     case INT3403_TYPE_BATTERY:
0275         int3403_cdev_remove(priv);
0276         break;
0277     default:
0278         break;
0279     }
0280 
0281     return 0;
0282 }
0283 
0284 static const struct acpi_device_id int3403_device_ids[] = {
0285     {"INT3403", 0},
0286     {"INTC1043", 0},
0287     {"INTC1046", 0},
0288     {"INTC1062", 0},
0289     {"INTC10A1", 0},
0290     {"", 0},
0291 };
0292 MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
0293 
0294 static struct platform_driver int3403_driver = {
0295     .probe = int3403_add,
0296     .remove = int3403_remove,
0297     .driver = {
0298         .name = "int3403 thermal",
0299         .acpi_match_table = int3403_device_ids,
0300     },
0301 };
0302 
0303 module_platform_driver(int3403_driver);
0304 
0305 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
0306 MODULE_LICENSE("GPL v2");
0307 MODULE_DESCRIPTION("ACPI INT3403 thermal driver");