12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23e8c4d31SAmit Kucheria /* 33e8c4d31SAmit Kucheria * ACPI INT3403 thermal driver 43e8c4d31SAmit Kucheria * Copyright (c) 2013, Intel Corporation. 53e8c4d31SAmit Kucheria */ 63e8c4d31SAmit Kucheria 73e8c4d31SAmit Kucheria #include <linux/kernel.h> 83e8c4d31SAmit Kucheria #include <linux/module.h> 93e8c4d31SAmit Kucheria #include <linux/init.h> 103e8c4d31SAmit Kucheria #include <linux/types.h> 113e8c4d31SAmit Kucheria #include <linux/acpi.h> 123e8c4d31SAmit Kucheria #include <linux/thermal.h> 133e8c4d31SAmit Kucheria #include <linux/platform_device.h> 143e8c4d31SAmit Kucheria #include "int340x_thermal_zone.h" 153e8c4d31SAmit Kucheria 163e8c4d31SAmit Kucheria #define INT3403_TYPE_SENSOR 0x03 173e8c4d31SAmit Kucheria #define INT3403_TYPE_CHARGER 0x0B 183e8c4d31SAmit Kucheria #define INT3403_TYPE_BATTERY 0x0C 193e8c4d31SAmit Kucheria #define INT3403_PERF_CHANGED_EVENT 0x80 203e8c4d31SAmit Kucheria #define INT3403_PERF_TRIP_POINT_CHANGED 0x81 213e8c4d31SAmit Kucheria #define INT3403_THERMAL_EVENT 0x90 223e8c4d31SAmit Kucheria 233e8c4d31SAmit Kucheria /* Preserved structure for future expandbility */ 243e8c4d31SAmit Kucheria struct int3403_sensor { 253e8c4d31SAmit Kucheria struct int34x_thermal_zone *int340x_zone; 263e8c4d31SAmit Kucheria }; 273e8c4d31SAmit Kucheria 283e8c4d31SAmit Kucheria struct int3403_performance_state { 293e8c4d31SAmit Kucheria u64 performance; 303e8c4d31SAmit Kucheria u64 power; 313e8c4d31SAmit Kucheria u64 latency; 323e8c4d31SAmit Kucheria u64 linear; 333e8c4d31SAmit Kucheria u64 control; 343e8c4d31SAmit Kucheria u64 raw_performace; 353e8c4d31SAmit Kucheria char *raw_unit; 363e8c4d31SAmit Kucheria int reserved; 373e8c4d31SAmit Kucheria }; 383e8c4d31SAmit Kucheria 393e8c4d31SAmit Kucheria struct int3403_cdev { 403e8c4d31SAmit Kucheria struct thermal_cooling_device *cdev; 413e8c4d31SAmit Kucheria unsigned long max_state; 423e8c4d31SAmit Kucheria }; 433e8c4d31SAmit Kucheria 443e8c4d31SAmit Kucheria struct int3403_priv { 453e8c4d31SAmit Kucheria struct platform_device *pdev; 463e8c4d31SAmit Kucheria struct acpi_device *adev; 473e8c4d31SAmit Kucheria unsigned long long type; 483e8c4d31SAmit Kucheria void *priv; 493e8c4d31SAmit Kucheria }; 503e8c4d31SAmit Kucheria 513e8c4d31SAmit Kucheria static void int3403_notify(acpi_handle handle, 523e8c4d31SAmit Kucheria u32 event, void *data) 533e8c4d31SAmit Kucheria { 543e8c4d31SAmit Kucheria struct int3403_priv *priv = data; 553e8c4d31SAmit Kucheria struct int3403_sensor *obj; 563e8c4d31SAmit Kucheria 573e8c4d31SAmit Kucheria if (!priv) 583e8c4d31SAmit Kucheria return; 593e8c4d31SAmit Kucheria 603e8c4d31SAmit Kucheria obj = priv->priv; 613e8c4d31SAmit Kucheria if (priv->type != INT3403_TYPE_SENSOR || !obj) 623e8c4d31SAmit Kucheria return; 633e8c4d31SAmit Kucheria 643e8c4d31SAmit Kucheria switch (event) { 653e8c4d31SAmit Kucheria case INT3403_PERF_CHANGED_EVENT: 663e8c4d31SAmit Kucheria break; 673e8c4d31SAmit Kucheria case INT3403_THERMAL_EVENT: 683e8c4d31SAmit Kucheria int340x_thermal_zone_device_update(obj->int340x_zone, 693e8c4d31SAmit Kucheria THERMAL_TRIP_VIOLATED); 703e8c4d31SAmit Kucheria break; 713e8c4d31SAmit Kucheria case INT3403_PERF_TRIP_POINT_CHANGED: 723e8c4d31SAmit Kucheria int340x_thermal_read_trips(obj->int340x_zone); 733e8c4d31SAmit Kucheria int340x_thermal_zone_device_update(obj->int340x_zone, 743e8c4d31SAmit Kucheria THERMAL_TRIP_CHANGED); 753e8c4d31SAmit Kucheria break; 763e8c4d31SAmit Kucheria default: 77f3d7fb38SAlex Hung dev_dbg(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); 783e8c4d31SAmit Kucheria break; 793e8c4d31SAmit Kucheria } 803e8c4d31SAmit Kucheria } 813e8c4d31SAmit Kucheria 823e8c4d31SAmit Kucheria static int int3403_sensor_add(struct int3403_priv *priv) 833e8c4d31SAmit Kucheria { 843e8c4d31SAmit Kucheria int result = 0; 853e8c4d31SAmit Kucheria struct int3403_sensor *obj; 863e8c4d31SAmit Kucheria 873e8c4d31SAmit Kucheria obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); 883e8c4d31SAmit Kucheria if (!obj) 893e8c4d31SAmit Kucheria return -ENOMEM; 903e8c4d31SAmit Kucheria 913e8c4d31SAmit Kucheria priv->priv = obj; 923e8c4d31SAmit Kucheria 933e8c4d31SAmit Kucheria obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL); 943e8c4d31SAmit Kucheria if (IS_ERR(obj->int340x_zone)) 953e8c4d31SAmit Kucheria return PTR_ERR(obj->int340x_zone); 963e8c4d31SAmit Kucheria 973e8c4d31SAmit Kucheria result = acpi_install_notify_handler(priv->adev->handle, 983e8c4d31SAmit Kucheria ACPI_DEVICE_NOTIFY, int3403_notify, 993e8c4d31SAmit Kucheria (void *)priv); 1003e8c4d31SAmit Kucheria if (result) 1013e8c4d31SAmit Kucheria goto err_free_obj; 1023e8c4d31SAmit Kucheria 1033e8c4d31SAmit Kucheria return 0; 1043e8c4d31SAmit Kucheria 1053e8c4d31SAmit Kucheria err_free_obj: 1063e8c4d31SAmit Kucheria int340x_thermal_zone_remove(obj->int340x_zone); 1073e8c4d31SAmit Kucheria return result; 1083e8c4d31SAmit Kucheria } 1093e8c4d31SAmit Kucheria 1103e8c4d31SAmit Kucheria static int int3403_sensor_remove(struct int3403_priv *priv) 1113e8c4d31SAmit Kucheria { 1123e8c4d31SAmit Kucheria struct int3403_sensor *obj = priv->priv; 1133e8c4d31SAmit Kucheria 1143e8c4d31SAmit Kucheria acpi_remove_notify_handler(priv->adev->handle, 1153e8c4d31SAmit Kucheria ACPI_DEVICE_NOTIFY, int3403_notify); 1163e8c4d31SAmit Kucheria int340x_thermal_zone_remove(obj->int340x_zone); 1173e8c4d31SAmit Kucheria 1183e8c4d31SAmit Kucheria return 0; 1193e8c4d31SAmit Kucheria } 1203e8c4d31SAmit Kucheria 1213e8c4d31SAmit Kucheria /* INT3403 Cooling devices */ 1223e8c4d31SAmit Kucheria static int int3403_get_max_state(struct thermal_cooling_device *cdev, 1233e8c4d31SAmit Kucheria unsigned long *state) 1243e8c4d31SAmit Kucheria { 1253e8c4d31SAmit Kucheria struct int3403_priv *priv = cdev->devdata; 1263e8c4d31SAmit Kucheria struct int3403_cdev *obj = priv->priv; 1273e8c4d31SAmit Kucheria 1283e8c4d31SAmit Kucheria *state = obj->max_state; 1293e8c4d31SAmit Kucheria return 0; 1303e8c4d31SAmit Kucheria } 1313e8c4d31SAmit Kucheria 1323e8c4d31SAmit Kucheria static int int3403_get_cur_state(struct thermal_cooling_device *cdev, 1333e8c4d31SAmit Kucheria unsigned long *state) 1343e8c4d31SAmit Kucheria { 1353e8c4d31SAmit Kucheria struct int3403_priv *priv = cdev->devdata; 1363e8c4d31SAmit Kucheria unsigned long long level; 1373e8c4d31SAmit Kucheria acpi_status status; 1383e8c4d31SAmit Kucheria 1393e8c4d31SAmit Kucheria status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level); 1403e8c4d31SAmit Kucheria if (ACPI_SUCCESS(status)) { 1413e8c4d31SAmit Kucheria *state = level; 1423e8c4d31SAmit Kucheria return 0; 1433e8c4d31SAmit Kucheria } else 1443e8c4d31SAmit Kucheria return -EINVAL; 1453e8c4d31SAmit Kucheria } 1463e8c4d31SAmit Kucheria 1473e8c4d31SAmit Kucheria static int 1483e8c4d31SAmit Kucheria int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) 1493e8c4d31SAmit Kucheria { 1503e8c4d31SAmit Kucheria struct int3403_priv *priv = cdev->devdata; 1513e8c4d31SAmit Kucheria acpi_status status; 1523e8c4d31SAmit Kucheria 1533e8c4d31SAmit Kucheria status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state); 1543e8c4d31SAmit Kucheria if (ACPI_SUCCESS(status)) 1553e8c4d31SAmit Kucheria return 0; 1563e8c4d31SAmit Kucheria else 1573e8c4d31SAmit Kucheria return -EINVAL; 1583e8c4d31SAmit Kucheria } 1593e8c4d31SAmit Kucheria 1603e8c4d31SAmit Kucheria static const struct thermal_cooling_device_ops int3403_cooling_ops = { 1613e8c4d31SAmit Kucheria .get_max_state = int3403_get_max_state, 1623e8c4d31SAmit Kucheria .get_cur_state = int3403_get_cur_state, 1633e8c4d31SAmit Kucheria .set_cur_state = int3403_set_cur_state, 1643e8c4d31SAmit Kucheria }; 1653e8c4d31SAmit Kucheria 1663e8c4d31SAmit Kucheria static int int3403_cdev_add(struct int3403_priv *priv) 1673e8c4d31SAmit Kucheria { 1683e8c4d31SAmit Kucheria int result = 0; 1693e8c4d31SAmit Kucheria acpi_status status; 1703e8c4d31SAmit Kucheria struct int3403_cdev *obj; 1713e8c4d31SAmit Kucheria struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 1723e8c4d31SAmit Kucheria union acpi_object *p; 1733e8c4d31SAmit Kucheria 1743e8c4d31SAmit Kucheria obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); 1753e8c4d31SAmit Kucheria if (!obj) 1763e8c4d31SAmit Kucheria return -ENOMEM; 1773e8c4d31SAmit Kucheria 1783e8c4d31SAmit Kucheria status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf); 1793e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) 1803e8c4d31SAmit Kucheria return -ENODEV; 1813e8c4d31SAmit Kucheria 1823e8c4d31SAmit Kucheria p = buf.pointer; 1833e8c4d31SAmit Kucheria if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 1844c8a342cSRishi Gupta pr_warn("Invalid PPSS data\n"); 1853e8c4d31SAmit Kucheria kfree(buf.pointer); 1863e8c4d31SAmit Kucheria return -EFAULT; 1873e8c4d31SAmit Kucheria } 1883e8c4d31SAmit Kucheria 1893e8c4d31SAmit Kucheria priv->priv = obj; 1903e8c4d31SAmit Kucheria obj->max_state = p->package.count - 1; 1913e8c4d31SAmit Kucheria obj->cdev = 1923e8c4d31SAmit Kucheria thermal_cooling_device_register(acpi_device_bid(priv->adev), 1933e8c4d31SAmit Kucheria priv, &int3403_cooling_ops); 1943e8c4d31SAmit Kucheria if (IS_ERR(obj->cdev)) 1953e8c4d31SAmit Kucheria result = PTR_ERR(obj->cdev); 1963e8c4d31SAmit Kucheria 1973e8c4d31SAmit Kucheria kfree(buf.pointer); 1983e8c4d31SAmit Kucheria /* TODO: add ACPI notification support */ 1993e8c4d31SAmit Kucheria 2003e8c4d31SAmit Kucheria return result; 2013e8c4d31SAmit Kucheria } 2023e8c4d31SAmit Kucheria 2033e8c4d31SAmit Kucheria static int int3403_cdev_remove(struct int3403_priv *priv) 2043e8c4d31SAmit Kucheria { 2053e8c4d31SAmit Kucheria struct int3403_cdev *obj = priv->priv; 2063e8c4d31SAmit Kucheria 2073e8c4d31SAmit Kucheria thermal_cooling_device_unregister(obj->cdev); 2083e8c4d31SAmit Kucheria return 0; 2093e8c4d31SAmit Kucheria } 2103e8c4d31SAmit Kucheria 2113e8c4d31SAmit Kucheria static int int3403_add(struct platform_device *pdev) 2123e8c4d31SAmit Kucheria { 2133e8c4d31SAmit Kucheria struct int3403_priv *priv; 2143e8c4d31SAmit Kucheria int result = 0; 2156eb61a93SZhang Rui unsigned long long tmp; 2163e8c4d31SAmit Kucheria acpi_status status; 2173e8c4d31SAmit Kucheria 2183e8c4d31SAmit Kucheria priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv), 2193e8c4d31SAmit Kucheria GFP_KERNEL); 2203e8c4d31SAmit Kucheria if (!priv) 2213e8c4d31SAmit Kucheria return -ENOMEM; 2223e8c4d31SAmit Kucheria 2233e8c4d31SAmit Kucheria priv->pdev = pdev; 2243e8c4d31SAmit Kucheria priv->adev = ACPI_COMPANION(&(pdev->dev)); 2253e8c4d31SAmit Kucheria if (!priv->adev) { 2263e8c4d31SAmit Kucheria result = -EINVAL; 2273e8c4d31SAmit Kucheria goto err; 2283e8c4d31SAmit Kucheria } 2293e8c4d31SAmit Kucheria 2303e8c4d31SAmit Kucheria 2313e8c4d31SAmit Kucheria status = acpi_evaluate_integer(priv->adev->handle, "_TMP", 2323e8c4d31SAmit Kucheria NULL, &tmp); 2333e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) { 2346eb61a93SZhang Rui status = acpi_evaluate_integer(priv->adev->handle, "PTYP", 2356eb61a93SZhang Rui NULL, &priv->type); 2366eb61a93SZhang Rui if (ACPI_FAILURE(status)) { 2373e8c4d31SAmit Kucheria result = -EINVAL; 2383e8c4d31SAmit Kucheria goto err; 2396eb61a93SZhang Rui } 2403e8c4d31SAmit Kucheria } else { 2413e8c4d31SAmit Kucheria priv->type = INT3403_TYPE_SENSOR; 2423e8c4d31SAmit Kucheria } 2433e8c4d31SAmit Kucheria 2443e8c4d31SAmit Kucheria platform_set_drvdata(pdev, priv); 2453e8c4d31SAmit Kucheria switch (priv->type) { 2463e8c4d31SAmit Kucheria case INT3403_TYPE_SENSOR: 2473e8c4d31SAmit Kucheria result = int3403_sensor_add(priv); 2483e8c4d31SAmit Kucheria break; 2493e8c4d31SAmit Kucheria case INT3403_TYPE_CHARGER: 2503e8c4d31SAmit Kucheria case INT3403_TYPE_BATTERY: 2513e8c4d31SAmit Kucheria result = int3403_cdev_add(priv); 2523e8c4d31SAmit Kucheria break; 2533e8c4d31SAmit Kucheria default: 2543e8c4d31SAmit Kucheria result = -EINVAL; 2553e8c4d31SAmit Kucheria } 2563e8c4d31SAmit Kucheria 2573e8c4d31SAmit Kucheria if (result) 2583e8c4d31SAmit Kucheria goto err; 2593e8c4d31SAmit Kucheria return result; 2603e8c4d31SAmit Kucheria 2613e8c4d31SAmit Kucheria err: 2623e8c4d31SAmit Kucheria return result; 2633e8c4d31SAmit Kucheria } 2643e8c4d31SAmit Kucheria 2653e8c4d31SAmit Kucheria static int int3403_remove(struct platform_device *pdev) 2663e8c4d31SAmit Kucheria { 2673e8c4d31SAmit Kucheria struct int3403_priv *priv = platform_get_drvdata(pdev); 2683e8c4d31SAmit Kucheria 2693e8c4d31SAmit Kucheria switch (priv->type) { 2703e8c4d31SAmit Kucheria case INT3403_TYPE_SENSOR: 2713e8c4d31SAmit Kucheria int3403_sensor_remove(priv); 2723e8c4d31SAmit Kucheria break; 2733e8c4d31SAmit Kucheria case INT3403_TYPE_CHARGER: 2743e8c4d31SAmit Kucheria case INT3403_TYPE_BATTERY: 2753e8c4d31SAmit Kucheria int3403_cdev_remove(priv); 2763e8c4d31SAmit Kucheria break; 2773e8c4d31SAmit Kucheria default: 2783e8c4d31SAmit Kucheria break; 2793e8c4d31SAmit Kucheria } 2803e8c4d31SAmit Kucheria 2813e8c4d31SAmit Kucheria return 0; 2823e8c4d31SAmit Kucheria } 2833e8c4d31SAmit Kucheria 2843e8c4d31SAmit Kucheria static const struct acpi_device_id int3403_device_ids[] = { 2853e8c4d31SAmit Kucheria {"INT3403", 0}, 28626d8bec1SGayatri Kammela {"INTC1043", 0}, 287*67698880SSrinivas Pandruvada {"INTC1046", 0}, 2883e8c4d31SAmit Kucheria {"", 0}, 2893e8c4d31SAmit Kucheria }; 2903e8c4d31SAmit Kucheria MODULE_DEVICE_TABLE(acpi, int3403_device_ids); 2913e8c4d31SAmit Kucheria 2923e8c4d31SAmit Kucheria static struct platform_driver int3403_driver = { 2933e8c4d31SAmit Kucheria .probe = int3403_add, 2943e8c4d31SAmit Kucheria .remove = int3403_remove, 2953e8c4d31SAmit Kucheria .driver = { 2963e8c4d31SAmit Kucheria .name = "int3403 thermal", 2973e8c4d31SAmit Kucheria .acpi_match_table = int3403_device_ids, 2983e8c4d31SAmit Kucheria }, 2993e8c4d31SAmit Kucheria }; 3003e8c4d31SAmit Kucheria 3013e8c4d31SAmit Kucheria module_platform_driver(int3403_driver); 3023e8c4d31SAmit Kucheria 3033e8c4d31SAmit Kucheria MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 3043e8c4d31SAmit Kucheria MODULE_LICENSE("GPL v2"); 3053e8c4d31SAmit Kucheria MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); 306