root/drivers/thermal/intel/int340x_thermal/int3403_thermal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. int3403_notify
  2. int3403_sensor_add
  3. int3403_sensor_remove
  4. int3403_get_max_state
  5. int3403_get_cur_state
  6. int3403_set_cur_state
  7. int3403_cdev_add
  8. int3403_cdev_remove
  9. int3403_add
  10. int3403_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * ACPI INT3403 thermal driver
   4  * Copyright (c) 2013, Intel Corporation.
   5  */
   6 
   7 #include <linux/kernel.h>
   8 #include <linux/module.h>
   9 #include <linux/init.h>
  10 #include <linux/types.h>
  11 #include <linux/acpi.h>
  12 #include <linux/thermal.h>
  13 #include <linux/platform_device.h>
  14 #include "int340x_thermal_zone.h"
  15 
  16 #define INT3403_TYPE_SENSOR             0x03
  17 #define INT3403_TYPE_CHARGER            0x0B
  18 #define INT3403_TYPE_BATTERY            0x0C
  19 #define INT3403_PERF_CHANGED_EVENT      0x80
  20 #define INT3403_PERF_TRIP_POINT_CHANGED 0x81
  21 #define INT3403_THERMAL_EVENT           0x90
  22 
  23 /* Preserved structure for future expandbility */
  24 struct int3403_sensor {
  25         struct int34x_thermal_zone *int340x_zone;
  26 };
  27 
  28 struct int3403_performance_state {
  29         u64 performance;
  30         u64 power;
  31         u64 latency;
  32         u64 linear;
  33         u64 control;
  34         u64 raw_performace;
  35         char *raw_unit;
  36         int reserved;
  37 };
  38 
  39 struct int3403_cdev {
  40         struct thermal_cooling_device *cdev;
  41         unsigned long max_state;
  42 };
  43 
  44 struct int3403_priv {
  45         struct platform_device *pdev;
  46         struct acpi_device *adev;
  47         unsigned long long type;
  48         void *priv;
  49 };
  50 
  51 static void int3403_notify(acpi_handle handle,
  52                 u32 event, void *data)
  53 {
  54         struct int3403_priv *priv = data;
  55         struct int3403_sensor *obj;
  56 
  57         if (!priv)
  58                 return;
  59 
  60         obj = priv->priv;
  61         if (priv->type != INT3403_TYPE_SENSOR || !obj)
  62                 return;
  63 
  64         switch (event) {
  65         case INT3403_PERF_CHANGED_EVENT:
  66                 break;
  67         case INT3403_THERMAL_EVENT:
  68                 int340x_thermal_zone_device_update(obj->int340x_zone,
  69                                                    THERMAL_TRIP_VIOLATED);
  70                 break;
  71         case INT3403_PERF_TRIP_POINT_CHANGED:
  72                 int340x_thermal_read_trips(obj->int340x_zone);
  73                 int340x_thermal_zone_device_update(obj->int340x_zone,
  74                                                    THERMAL_TRIP_CHANGED);
  75                 break;
  76         default:
  77                 dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
  78                 break;
  79         }
  80 }
  81 
  82 static int int3403_sensor_add(struct int3403_priv *priv)
  83 {
  84         int result = 0;
  85         struct int3403_sensor *obj;
  86 
  87         obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
  88         if (!obj)
  89                 return -ENOMEM;
  90 
  91         priv->priv = obj;
  92 
  93         obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
  94         if (IS_ERR(obj->int340x_zone))
  95                 return PTR_ERR(obj->int340x_zone);
  96 
  97         result = acpi_install_notify_handler(priv->adev->handle,
  98                         ACPI_DEVICE_NOTIFY, int3403_notify,
  99                         (void *)priv);
 100         if (result)
 101                 goto err_free_obj;
 102 
 103         return 0;
 104 
 105  err_free_obj:
 106         int340x_thermal_zone_remove(obj->int340x_zone);
 107         return result;
 108 }
 109 
 110 static int int3403_sensor_remove(struct int3403_priv *priv)
 111 {
 112         struct int3403_sensor *obj = priv->priv;
 113 
 114         acpi_remove_notify_handler(priv->adev->handle,
 115                                    ACPI_DEVICE_NOTIFY, int3403_notify);
 116         int340x_thermal_zone_remove(obj->int340x_zone);
 117 
 118         return 0;
 119 }
 120 
 121 /* INT3403 Cooling devices */
 122 static int int3403_get_max_state(struct thermal_cooling_device *cdev,
 123                                  unsigned long *state)
 124 {
 125         struct int3403_priv *priv = cdev->devdata;
 126         struct int3403_cdev *obj = priv->priv;
 127 
 128         *state = obj->max_state;
 129         return 0;
 130 }
 131 
 132 static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
 133                                  unsigned long *state)
 134 {
 135         struct int3403_priv *priv = cdev->devdata;
 136         unsigned long long level;
 137         acpi_status status;
 138 
 139         status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
 140         if (ACPI_SUCCESS(status)) {
 141                 *state = level;
 142                 return 0;
 143         } else
 144                 return -EINVAL;
 145 }
 146 
 147 static int
 148 int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 149 {
 150         struct int3403_priv *priv = cdev->devdata;
 151         acpi_status status;
 152 
 153         status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
 154         if (ACPI_SUCCESS(status))
 155                 return 0;
 156         else
 157                 return -EINVAL;
 158 }
 159 
 160 static const struct thermal_cooling_device_ops int3403_cooling_ops = {
 161         .get_max_state = int3403_get_max_state,
 162         .get_cur_state = int3403_get_cur_state,
 163         .set_cur_state = int3403_set_cur_state,
 164 };
 165 
 166 static int int3403_cdev_add(struct int3403_priv *priv)
 167 {
 168         int result = 0;
 169         acpi_status status;
 170         struct int3403_cdev *obj;
 171         struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
 172         union acpi_object *p;
 173 
 174         obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
 175         if (!obj)
 176                 return -ENOMEM;
 177 
 178         status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
 179         if (ACPI_FAILURE(status))
 180                 return -ENODEV;
 181 
 182         p = buf.pointer;
 183         if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
 184                 pr_warn("Invalid PPSS data\n");
 185                 kfree(buf.pointer);
 186                 return -EFAULT;
 187         }
 188 
 189         priv->priv = obj;
 190         obj->max_state = p->package.count - 1;
 191         obj->cdev =
 192                 thermal_cooling_device_register(acpi_device_bid(priv->adev),
 193                                 priv, &int3403_cooling_ops);
 194         if (IS_ERR(obj->cdev))
 195                 result = PTR_ERR(obj->cdev);
 196 
 197         kfree(buf.pointer);
 198         /* TODO: add ACPI notification support */
 199 
 200         return result;
 201 }
 202 
 203 static int int3403_cdev_remove(struct int3403_priv *priv)
 204 {
 205         struct int3403_cdev *obj = priv->priv;
 206 
 207         thermal_cooling_device_unregister(obj->cdev);
 208         return 0;
 209 }
 210 
 211 static int int3403_add(struct platform_device *pdev)
 212 {
 213         struct int3403_priv *priv;
 214         int result = 0;
 215         unsigned long long tmp;
 216         acpi_status status;
 217 
 218         priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
 219                             GFP_KERNEL);
 220         if (!priv)
 221                 return -ENOMEM;
 222 
 223         priv->pdev = pdev;
 224         priv->adev = ACPI_COMPANION(&(pdev->dev));
 225         if (!priv->adev) {
 226                 result = -EINVAL;
 227                 goto err;
 228         }
 229 
 230 
 231         status = acpi_evaluate_integer(priv->adev->handle, "_TMP",
 232                                        NULL, &tmp);
 233         if (ACPI_FAILURE(status)) {
 234                 status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
 235                                        NULL, &priv->type);
 236                 if (ACPI_FAILURE(status)) {
 237                         result = -EINVAL;
 238                         goto err;
 239                 }
 240         } else {
 241                 priv->type = INT3403_TYPE_SENSOR;
 242         }
 243 
 244         platform_set_drvdata(pdev, priv);
 245         switch (priv->type) {
 246         case INT3403_TYPE_SENSOR:
 247                 result = int3403_sensor_add(priv);
 248                 break;
 249         case INT3403_TYPE_CHARGER:
 250         case INT3403_TYPE_BATTERY:
 251                 result = int3403_cdev_add(priv);
 252                 break;
 253         default:
 254                 result = -EINVAL;
 255         }
 256 
 257         if (result)
 258                 goto err;
 259         return result;
 260 
 261 err:
 262         return result;
 263 }
 264 
 265 static int int3403_remove(struct platform_device *pdev)
 266 {
 267         struct int3403_priv *priv = platform_get_drvdata(pdev);
 268 
 269         switch (priv->type) {
 270         case INT3403_TYPE_SENSOR:
 271                 int3403_sensor_remove(priv);
 272                 break;
 273         case INT3403_TYPE_CHARGER:
 274         case INT3403_TYPE_BATTERY:
 275                 int3403_cdev_remove(priv);
 276                 break;
 277         default:
 278                 break;
 279         }
 280 
 281         return 0;
 282 }
 283 
 284 static const struct acpi_device_id int3403_device_ids[] = {
 285         {"INT3403", 0},
 286         {"", 0},
 287 };
 288 MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
 289 
 290 static struct platform_driver int3403_driver = {
 291         .probe = int3403_add,
 292         .remove = int3403_remove,
 293         .driver = {
 294                 .name = "int3403 thermal",
 295                 .acpi_match_table = int3403_device_ids,
 296         },
 297 };
 298 
 299 module_platform_driver(int3403_driver);
 300 
 301 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
 302 MODULE_LICENSE("GPL v2");
 303 MODULE_DESCRIPTION("ACPI INT3403 thermal driver");

/* [<][>][^][v][top][bottom][index][help] */