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