xref: /haiku/src/add-ons/kernel/drivers/power/acpi_thermal/acpi_thermal.c (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
1 /*
2  * Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
3  *
4  * Distributed under the terms of the MIT License.
5  *
6  * ACPI Generic Thermal Zone Driver.
7  * Obtains general status of passive devices, monitors / sets critical temperatures
8  * Controls active devices.
9  */
10 
11 #include <KernelExport.h>
12 #include <Drivers.h>
13 #include <Errors.h>
14 #include <string.h>
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 
19 #include <ACPI.h>
20 #include "acpi_thermal.h"
21 
22 #define ACPI_THERMAL_MODULE_NAME "drivers/power/acpi_thermal/driver_v1"
23 
24 #define ACPI_THERMAL_DEVICE_MODULE_NAME "drivers/power/acpi_thermal/device_v1"
25 
26 /* Base Namespace devices are published to */
27 #define ACPI_THERMAL_BASENAME "power/acpi_thermal/%d"
28 
29 // name of pnp generator of path ids
30 #define ACPI_THERMAL_PATHID_GENERATOR "acpi_thermal/path_id"
31 
32 static device_manager_info* sDeviceManager;
33 
34 typedef struct acpi_ns_device_info {
35 	device_node* node;
36 	acpi_device_module_info* acpi;
37 	acpi_device acpi_cookie;
38 	uint kelvin_offset;	// Initialized on first acpi_thermal_open() call.
39 } acpi_thermal_device_info;
40 
41 
42 status_t acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len);
43 
44 static void guess_kelvin_offset(acpi_thermal_device_info* device);
45 
46 
47 /*
48  * Exact value for Kelvin->Celsius conversion is 273.15, but as ACPI uses deci-Kelvins
49  * that means we only get one digit for the decimal part. Some implementations use 2731,
50  * while others use 2732. We try to guess which one here (as Linux's acpi/thermal.c driver
51  * does) by checking whether the "critical temp" value is set to something reasonable
52  * (> 0 °C), and if it is a multiple of 0.5 °C.
53  */
54 static void
55 guess_kelvin_offset(acpi_thermal_device_info* device)
56 {
57 	acpi_thermal_type therm_info;
58 
59 	acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
60 
61 	device->kelvin_offset = 2732;
62 	if (therm_info.critical_temp > 2732 && (therm_info.critical_temp % 5) == 1)
63 		device->kelvin_offset = 2731;
64 }
65 
66 
67 static status_t
68 acpi_thermal_open(void* _cookie, const char* path, int flags, void** cookie)
69 {
70 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
71 	*cookie = device;
72 
73 	if (device->kelvin_offset == 0)
74 		guess_kelvin_offset(device);
75 
76 	return B_OK;
77 }
78 
79 
80 static status_t
81 acpi_thermal_read(void* _cookie, off_t position, void* buf, size_t* num_bytes)
82 {
83 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
84 	acpi_thermal_type therm_info;
85 
86 	if (*num_bytes < 1)
87 		return B_IO_ERROR;
88 
89 	if (position == 0) {
90 		char string[128];
91 		char* str = string;
92 		size_t max_len = sizeof(string);
93 
94 		uint kelvinOffset = device->kelvin_offset;
95 
96 		acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
97 
98 		snprintf(str, max_len, "  Critical Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
99 				((therm_info.critical_temp - kelvinOffset) / 10),
100 				((therm_info.critical_temp - kelvinOffset) % 10));
101 		max_len -= strlen(str);
102 		str += strlen(str);
103 
104 		snprintf(str, max_len, "  Current Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
105 				((therm_info.current_temp - kelvinOffset) / 10),
106 				((therm_info.current_temp - kelvinOffset) % 10));
107 		max_len -= strlen(str);
108 		str += strlen(str);
109 
110 		if (therm_info.hot_temp > 0) {
111 			snprintf(str, max_len, "  Hot Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
112 					((therm_info.hot_temp - kelvinOffset) / 10),
113 					((therm_info.hot_temp - kelvinOffset) % 10));
114 			max_len -= strlen(str);
115 			str += strlen(str);
116 		}
117 
118 		if (therm_info.passive_package) {
119 /* Incomplete.
120      Needs to obtain acpi global lock.
121      acpi_object_type needs Reference entry (with Handle that can be resolved)
122      what you see here is _highly_ unreliable.
123 */
124 /*      		if (therm_info.passive_package->data.package.count > 0) {
125 				sprintf((char *)buf + *num_bytes, "  Passive Devices\n");
126 				*num_bytes = strlen((char *)buf);
127 				for (i = 0; i < therm_info.passive_package->data.package.count; i++) {
128 					sprintf((char *)buf + *num_bytes, "    Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id);
129 					*num_bytes = strlen((char *)buf);
130 				}
131 			}
132 */
133 			free(therm_info.passive_package);
134 		}
135 
136 		max_len = user_strlcpy((char*)buf, string, *num_bytes);
137 		if (max_len < B_OK)
138 			return B_BAD_ADDRESS;
139 		*num_bytes = max_len;
140 	} else {
141 		*num_bytes = 0;
142 	}
143 
144 	return B_OK;
145 }
146 
147 
148 static status_t
149 acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
150 {
151 	return B_ERROR;
152 }
153 
154 
155 status_t
156 acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len)
157 {
158 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
159 	status_t err = B_ERROR;
160 
161 	acpi_thermal_type* att = NULL;
162 
163 	acpi_object_type object;
164 
165 	acpi_data buffer;
166 	buffer.pointer = &object;
167 	buffer.length = sizeof(object);
168 
169 	switch (op) {
170 		case drvOpGetThermalType: {
171 			att = (acpi_thermal_type*)arg;
172 
173 			// Read basic temperature thresholds.
174 			err = device->acpi->evaluate_method (device->acpi_cookie, "_CRT",
175 				NULL, &buffer);
176 
177 			att->critical_temp =
178 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
179 				? object.integer.integer : 0;
180 
181 			err = device->acpi->evaluate_method (device->acpi_cookie, "_TMP",
182 				NULL, &buffer);
183 
184 			att->current_temp =
185 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
186 				? object.integer.integer : 0;
187 
188 			err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT",
189 				NULL, &buffer);
190 
191 			att->hot_temp =
192 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
193 				? object.integer.integer : 0;
194 
195 			// Read Passive Cooling devices
196 			att->passive_package = NULL;
197 			//err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package));
198 
199 			att->active_count = 0;
200 			att->active_devices = NULL;
201 
202 			err = B_OK;
203 			break;
204 		}
205 	}
206 	return err;
207 }
208 
209 
210 static status_t
211 acpi_thermal_close (void* cookie)
212 {
213 	return B_OK;
214 }
215 
216 
217 static status_t
218 acpi_thermal_free (void* cookie)
219 {
220 	return B_OK;
221 }
222 
223 
224 //	#pragma mark - driver module API
225 
226 
227 static float
228 acpi_thermal_support(device_node *parent)
229 {
230 	const char* bus;
231 	uint32 device_type;
232 
233 	// make sure parent is really the ACPI bus manager
234 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
235 		return -1;
236 
237 	if (strcmp(bus, "acpi"))
238 		return 0.0;
239 
240 	// check whether it's really a thermal Device
241 	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
242 			&device_type, false) != B_OK || device_type != ACPI_TYPE_THERMAL) {
243 		return 0.0;
244 	}
245 
246 	// TODO check there are _CRT and _TMP ?
247 
248 	return 0.6;
249 }
250 
251 
252 static status_t
253 acpi_thermal_register_device(device_node *node)
254 {
255 	device_attr attrs[] = {
256 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Thermal" }},
257 		{ NULL }
258 	};
259 
260 	return sDeviceManager->register_node(node, ACPI_THERMAL_MODULE_NAME, attrs,
261 		NULL, NULL);
262 }
263 
264 
265 static status_t
266 acpi_thermal_init_driver(device_node* node, void** _driverCookie)
267 {
268 	*_driverCookie = node;
269 	return B_OK;
270 }
271 
272 
273 static void
274 acpi_thermal_uninit_driver(void* driverCookie)
275 {
276 }
277 
278 
279 static status_t
280 acpi_thermal_register_child_devices(void* _cookie)
281 {
282 	device_node* node = _cookie;
283 	int path_id;
284 	char name[128];
285 
286 	path_id = sDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR);
287 	if (path_id < 0) {
288 		dprintf("acpi_thermal_register_child_devices: couldn't create a path_id\n");
289 		return B_ERROR;
290 	}
291 
292 	snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id);
293 
294 	return sDeviceManager->publish_device(node, name,
295 		ACPI_THERMAL_DEVICE_MODULE_NAME);
296 }
297 
298 
299 static status_t
300 acpi_thermal_init_device(void* _cookie, void** cookie)
301 {
302 	device_node* node = (device_node*)_cookie;
303 	acpi_thermal_device_info* device;
304 	device_node* parent;
305 
306 	device = (acpi_thermal_device_info*)calloc(1, sizeof(*device));
307 	if (device == NULL)
308 		return B_NO_MEMORY;
309 
310 	device->node = node;
311 
312 	parent = sDeviceManager->get_parent_node(node);
313 	sDeviceManager->get_driver(parent, (driver_module_info**)&device->acpi,
314 		(void**)&device->acpi_cookie);
315 	sDeviceManager->put_node(parent);
316 
317 	*cookie = device;
318 	return B_OK;
319 }
320 
321 
322 static void
323 acpi_thermal_uninit_device(void* _cookie)
324 {
325 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
326 	free(device);
327 }
328 
329 
330 
331 module_dependency module_dependencies[] = {
332 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
333 	{}
334 };
335 
336 
337 driver_module_info acpi_thermal_driver_module = {
338 	{
339 		ACPI_THERMAL_MODULE_NAME,
340 		0,
341 		NULL
342 	},
343 
344 	acpi_thermal_support,
345 	acpi_thermal_register_device,
346 	acpi_thermal_init_driver,
347 	acpi_thermal_uninit_driver,
348 	acpi_thermal_register_child_devices,
349 	NULL,	// rescan
350 	NULL,	// removed
351 };
352 
353 
354 struct device_module_info acpi_thermal_device_module = {
355 	{
356 		ACPI_THERMAL_DEVICE_MODULE_NAME,
357 		0,
358 		NULL
359 	},
360 
361 	acpi_thermal_init_device,
362 	acpi_thermal_uninit_device,
363 	NULL,
364 
365 	acpi_thermal_open,
366 	acpi_thermal_close,
367 	acpi_thermal_free,
368 	acpi_thermal_read,
369 	acpi_thermal_write,
370 	NULL,
371 	acpi_thermal_control,
372 
373 	NULL,
374 	NULL
375 };
376 
377 module_info* modules[] = {
378 	(module_info*)&acpi_thermal_driver_module,
379 	(module_info*)&acpi_thermal_device_module,
380 	NULL
381 };
382