brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · c094a42 Raw
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