xref: /haiku/src/add-ons/kernel/drivers/power/acpi_thermal/acpi_thermal.c (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 		size_t max_len = *num_bytes;
91 		char* str = (char*)buf;
92 		uint kelvinOffset = device->kelvin_offset;
93 
94 		acpi_thermal_control(device, drvOpGetThermalType, &therm_info, 0);
95 
96 		snprintf(str, max_len, "  Critical Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
97 				((therm_info.critical_temp - kelvinOffset) / 10),
98 				((therm_info.critical_temp - kelvinOffset) % 10));
99 
100 		max_len -= strlen(str);
101 		str += strlen(str);
102 		snprintf(str, max_len, "  Current Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
103 				((therm_info.current_temp - kelvinOffset) / 10),
104 				((therm_info.current_temp - kelvinOffset) % 10));
105 
106 		if (therm_info.hot_temp > 0) {
107 			max_len -= strlen(str);
108 			str += strlen(str);
109 			snprintf(str, max_len, "  Hot Temperature: %" B_PRIu32 ".%" B_PRIu32 " °C\n",
110 					((therm_info.hot_temp - kelvinOffset) / 10),
111 					((therm_info.hot_temp - kelvinOffset) % 10));
112 		}
113 
114 		if (therm_info.passive_package) {
115 /* Incomplete.
116      Needs to obtain acpi global lock.
117      acpi_object_type needs Reference entry (with Handle that can be resolved)
118      what you see here is _highly_ unreliable.
119 */
120 /*      		if (therm_info.passive_package->data.package.count > 0) {
121 				sprintf((char *)buf + *num_bytes, "  Passive Devices\n");
122 				*num_bytes = strlen((char *)buf);
123 				for (i = 0; i < therm_info.passive_package->data.package.count; i++) {
124 					sprintf((char *)buf + *num_bytes, "    Processor: %lu\n", therm_info.passive_package->data.package.objects[i].data.processor.cpu_id);
125 					*num_bytes = strlen((char *)buf);
126 				}
127 			}
128 */
129 			free(therm_info.passive_package);
130 		}
131 		*num_bytes = strlen((char*)buf);
132 	} else {
133 		*num_bytes = 0;
134 	}
135 
136 	return B_OK;
137 }
138 
139 
140 static status_t
141 acpi_thermal_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
142 {
143 	return B_ERROR;
144 }
145 
146 
147 status_t
148 acpi_thermal_control(void* _cookie, uint32 op, void* arg, size_t len)
149 {
150 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
151 	status_t err = B_ERROR;
152 
153 	acpi_thermal_type* att = NULL;
154 
155 	acpi_object_type object;
156 
157 	acpi_data buffer;
158 	buffer.pointer = &object;
159 	buffer.length = sizeof(object);
160 
161 	switch (op) {
162 		case drvOpGetThermalType: {
163 			att = (acpi_thermal_type*)arg;
164 
165 			// Read basic temperature thresholds.
166 			err = device->acpi->evaluate_method (device->acpi_cookie, "_CRT",
167 				NULL, &buffer);
168 
169 			att->critical_temp =
170 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
171 				? object.integer.integer : 0;
172 
173 			err = device->acpi->evaluate_method (device->acpi_cookie, "_TMP",
174 				NULL, &buffer);
175 
176 			att->current_temp =
177 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
178 				? object.integer.integer : 0;
179 
180 			err = device->acpi->evaluate_method(device->acpi_cookie, "_HOT",
181 				NULL, &buffer);
182 
183 			att->hot_temp =
184 				(err == B_OK && object.object_type == ACPI_TYPE_INTEGER)
185 				? object.integer.integer : 0;
186 
187 			// Read Passive Cooling devices
188 			att->passive_package = NULL;
189 			//err = device->acpi->get_object(device->acpi_cookie, "_PSL", &(att->passive_package));
190 
191 			att->active_count = 0;
192 			att->active_devices = NULL;
193 
194 			err = B_OK;
195 			break;
196 		}
197 	}
198 	return err;
199 }
200 
201 
202 static status_t
203 acpi_thermal_close (void* cookie)
204 {
205 	return B_OK;
206 }
207 
208 
209 static status_t
210 acpi_thermal_free (void* cookie)
211 {
212 	return B_OK;
213 }
214 
215 
216 //	#pragma mark - driver module API
217 
218 
219 static float
220 acpi_thermal_support(device_node *parent)
221 {
222 	const char* bus;
223 	uint32 device_type;
224 
225 	// make sure parent is really the ACPI bus manager
226 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
227 		return -1;
228 
229 	if (strcmp(bus, "acpi"))
230 		return 0.0;
231 
232 	// check whether it's really a thermal Device
233 	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
234 			&device_type, false) != B_OK || device_type != ACPI_TYPE_THERMAL) {
235 		return 0.0;
236 	}
237 
238 	// TODO check there are _CRT and _TMP ?
239 
240 	return 0.6;
241 }
242 
243 
244 static status_t
245 acpi_thermal_register_device(device_node *node)
246 {
247 	device_attr attrs[] = {
248 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Thermal" }},
249 		{ NULL }
250 	};
251 
252 	return sDeviceManager->register_node(node, ACPI_THERMAL_MODULE_NAME, attrs,
253 		NULL, NULL);
254 }
255 
256 
257 static status_t
258 acpi_thermal_init_driver(device_node* node, void** _driverCookie)
259 {
260 	*_driverCookie = node;
261 	return B_OK;
262 }
263 
264 
265 static void
266 acpi_thermal_uninit_driver(void* driverCookie)
267 {
268 }
269 
270 
271 static status_t
272 acpi_thermal_register_child_devices(void* _cookie)
273 {
274 	device_node* node = _cookie;
275 	int path_id;
276 	char name[128];
277 
278 	path_id = sDeviceManager->create_id(ACPI_THERMAL_PATHID_GENERATOR);
279 	if (path_id < 0) {
280 		dprintf("acpi_thermal_register_child_devices: couldn't create a path_id\n");
281 		return B_ERROR;
282 	}
283 
284 	snprintf(name, sizeof(name), ACPI_THERMAL_BASENAME, path_id);
285 
286 	return sDeviceManager->publish_device(node, name,
287 		ACPI_THERMAL_DEVICE_MODULE_NAME);
288 }
289 
290 
291 static status_t
292 acpi_thermal_init_device(void* _cookie, void** cookie)
293 {
294 	device_node* node = (device_node*)_cookie;
295 	acpi_thermal_device_info* device;
296 	device_node* parent;
297 
298 	device = (acpi_thermal_device_info*)calloc(1, sizeof(*device));
299 	if (device == NULL)
300 		return B_NO_MEMORY;
301 
302 	device->node = node;
303 
304 	parent = sDeviceManager->get_parent_node(node);
305 	sDeviceManager->get_driver(parent, (driver_module_info**)&device->acpi,
306 		(void**)&device->acpi_cookie);
307 	sDeviceManager->put_node(parent);
308 
309 	*cookie = device;
310 	return B_OK;
311 }
312 
313 
314 static void
315 acpi_thermal_uninit_device(void* _cookie)
316 {
317 	acpi_thermal_device_info* device = (acpi_thermal_device_info*)_cookie;
318 	free(device);
319 }
320 
321 
322 
323 module_dependency module_dependencies[] = {
324 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
325 	{}
326 };
327 
328 
329 driver_module_info acpi_thermal_driver_module = {
330 	{
331 		ACPI_THERMAL_MODULE_NAME,
332 		0,
333 		NULL
334 	},
335 
336 	acpi_thermal_support,
337 	acpi_thermal_register_device,
338 	acpi_thermal_init_driver,
339 	acpi_thermal_uninit_driver,
340 	acpi_thermal_register_child_devices,
341 	NULL,	// rescan
342 	NULL,	// removed
343 };
344 
345 
346 struct device_module_info acpi_thermal_device_module = {
347 	{
348 		ACPI_THERMAL_DEVICE_MODULE_NAME,
349 		0,
350 		NULL
351 	},
352 
353 	acpi_thermal_init_device,
354 	acpi_thermal_uninit_device,
355 	NULL,
356 
357 	acpi_thermal_open,
358 	acpi_thermal_close,
359 	acpi_thermal_free,
360 	acpi_thermal_read,
361 	acpi_thermal_write,
362 	NULL,
363 	acpi_thermal_control,
364 
365 	NULL,
366 	NULL
367 };
368 
369 module_info* modules[] = {
370 	(module_info*)&acpi_thermal_driver_module,
371 	(module_info*)&acpi_thermal_device_module,
372 	NULL
373 };
374