Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * ACPI INT3403 thermal driver
  3 * Copyright (c) 2013, Intel Corporation.
  4 *
  5 * This program is free software; you can redistribute it and/or modify it
  6 * under the terms and conditions of the GNU General Public License,
  7 * version 2, as published by the Free Software Foundation.
  8 *
  9 * This program is distributed in the hope it will be useful, but WITHOUT
 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 12 * more details.
 13 */
 14
 15#include <linux/kernel.h>
 16#include <linux/module.h>
 17#include <linux/init.h>
 18#include <linux/types.h>
 19#include <linux/acpi.h>
 20#include <linux/thermal.h>
 21
 22#define INT3403_TYPE_SENSOR		0x03
 23#define INT3403_PERF_CHANGED_EVENT	0x80
 24#define INT3403_THERMAL_EVENT		0x90
 25
 26#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
 27#define KELVIN_OFFSET	2732
 28#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
 29
 30#define ACPI_INT3403_CLASS		"int3403"
 31#define ACPI_INT3403_FILE_STATE		"state"
 32
 33struct int3403_sensor {
 34	struct thermal_zone_device *tzone;
 35	unsigned long *thresholds;
 36};
 37
 38static int sys_get_curr_temp(struct thermal_zone_device *tzone,
 39				unsigned long *temp)
 40{
 41	struct acpi_device *device = tzone->devdata;
 42	unsigned long long tmp;
 43	acpi_status status;
 44
 45	status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
 46	if (ACPI_FAILURE(status))
 47		return -EIO;
 48
 49	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
 50
 51	return 0;
 52}
 53
 54static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
 55		int trip, unsigned long *temp)
 56{
 57	struct acpi_device *device = tzone->devdata;
 58	unsigned long long hyst;
 59	acpi_status status;
 60
 61	status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
 62	if (ACPI_FAILURE(status))
 63		return -EIO;
 64
 65	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
 66
 67	return 0;
 68}
 69
 70static int sys_get_trip_temp(struct thermal_zone_device *tzone,
 71		int trip, unsigned long *temp)
 72{
 73	struct acpi_device *device = tzone->devdata;
 74	struct int3403_sensor *obj = acpi_driver_data(device);
 75
 76	/*
 77	 * get_trip_temp is a mandatory callback but
 78	 * PATx method doesn't return any value, so return
 79	 * cached value, which was last set from user space.
 80	 */
 81	*temp = obj->thresholds[trip];
 82
 83	return 0;
 84}
 85
 86static int sys_get_trip_type(struct thermal_zone_device *thermal,
 87		int trip, enum thermal_trip_type *type)
 88{
 89	/* Mandatory callback, may not mean much here */
 90	*type = THERMAL_TRIP_PASSIVE;
 91
 92	return 0;
 93}
 94
 95int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
 96							unsigned long temp)
 97{
 98	struct acpi_device *device = tzone->devdata;
 99	acpi_status status;
100	char name[10];
101	int ret = 0;
102	struct int3403_sensor *obj = acpi_driver_data(device);
103
104	snprintf(name, sizeof(name), "PAT%d", trip);
105	if (acpi_has_method(device->handle, name)) {
106		status = acpi_execute_simple_method(device->handle, name,
107				MILLI_CELSIUS_TO_DECI_KELVIN(temp,
108							KELVIN_OFFSET));
109		if (ACPI_FAILURE(status))
110			ret = -EIO;
111		else
112			obj->thresholds[trip] = temp;
113	} else {
114		ret = -EIO;
115		dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
116	}
117
118	return ret;
119}
120
121static struct thermal_zone_device_ops tzone_ops = {
122	.get_temp = sys_get_curr_temp,
123	.get_trip_temp = sys_get_trip_temp,
124	.get_trip_type = sys_get_trip_type,
125	.set_trip_temp = sys_set_trip_temp,
126	.get_trip_hyst =  sys_get_trip_hyst,
127};
128
129static void acpi_thermal_notify(struct acpi_device *device, u32 event)
130{
131	struct int3403_sensor *obj;
132
133	if (!device)
134		return;
135
136	obj = acpi_driver_data(device);
137	if (!obj)
138		return;
139
140	switch (event) {
141	case INT3403_PERF_CHANGED_EVENT:
142		break;
143	case INT3403_THERMAL_EVENT:
144		thermal_zone_device_update(obj->tzone);
145		break;
146	default:
147		dev_err(&device->dev, "Unsupported event [0x%x]\n", event);
148		break;
149	}
150}
151
152static int acpi_int3403_add(struct acpi_device *device)
153{
154	int result = 0;
155	unsigned long long ptyp;
156	acpi_status status;
157	struct int3403_sensor *obj;
158	unsigned long long trip_cnt;
159	int trip_mask = 0;
160
161	if (!device)
162		return -EINVAL;
163
164	status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp);
165	if (ACPI_FAILURE(status))
166		return -EINVAL;
167
168	if (ptyp != INT3403_TYPE_SENSOR)
169		return -EINVAL;
170
171	obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL);
172	if (!obj)
173		return -ENOMEM;
174
175	device->driver_data = obj;
176
177	status = acpi_evaluate_integer(device->handle, "PATC", NULL,
178						&trip_cnt);
179	if (ACPI_FAILURE(status))
180		trip_cnt = 0;
181
182	if (trip_cnt) {
183		/* We have to cache, thresholds can't be readback */
184		obj->thresholds = devm_kzalloc(&device->dev,
185					sizeof(*obj->thresholds) * trip_cnt,
186					GFP_KERNEL);
187		if (!obj->thresholds)
188			return -ENOMEM;
189		trip_mask = BIT(trip_cnt) - 1;
190	}
191	obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
192				trip_cnt, trip_mask, device, &tzone_ops,
193				NULL, 0, 0);
194	if (IS_ERR(obj->tzone)) {
195		result = PTR_ERR(obj->tzone);
196		return result;
197	}
198
199	strcpy(acpi_device_name(device), "INT3403");
200	strcpy(acpi_device_class(device), ACPI_INT3403_CLASS);
201
202	return 0;
203}
204
205static int acpi_int3403_remove(struct acpi_device *device)
206{
207	struct int3403_sensor *obj;
208
209	obj = acpi_driver_data(device);
210	thermal_zone_device_unregister(obj->tzone);
211
212	return 0;
213}
214
215ACPI_MODULE_NAME("int3403");
216static const struct acpi_device_id int3403_device_ids[] = {
217	{"INT3403", 0},
218	{"", 0},
219};
220MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
221
222static struct acpi_driver acpi_int3403_driver = {
223	.name = "INT3403",
224	.class = ACPI_INT3403_CLASS,
225	.ids = int3403_device_ids,
226	.ops = {
227		.add = acpi_int3403_add,
228		.remove = acpi_int3403_remove,
229		.notify = acpi_thermal_notify,
230		},
231};
232
233module_acpi_driver(acpi_int3403_driver);
234
235MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
236MODULE_LICENSE("GPL v2");
237MODULE_DESCRIPTION("ACPI INT3403 thermal driver");