1/*
2 * Copyright 2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Clemens Zeidler, haiku@clemens-zeidler.de
7 */
8
9#include <KernelExport.h>
10#include <Drivers.h>
11#include <Errors.h>
12#include <string.h>
13
14#include <stdio.h>
15#include <stdlib.h>
16
17#include <kernel/arch/x86/arch_cpu.h>
18
19#include <ACPI.h>
20#include "enhanced_speedstep.h"
21#include <condition_variable.h>
22
23#include "frequency.h"
24
25
26#define EST_MODULE_NAME "drivers/power/enhanced_speedstep/driver_v1"
27
28#define EST_DEVICE_MODULE_NAME "drivers/power/enhanced_speedstep/device_v1"
29
30/* Base Namespace devices are published to */
31#define EST_BASENAME "power/enhanced_speedstep/%d"
32
33// name of pnp generator of path ids
34#define EST_PATHID_GENERATOR "enhanced_speedstep/path_id"
35
36static device_manager_info *sDeviceManager;
37static ConditionVariable sFrequencyCondition;
38static vint32 sCurrentID;
39
40
41static status_t
42est_read(void* _cookie, off_t position, void *buffer, size_t* numBytes)
43{
44	if (*numBytes < 1)
45		return B_IO_ERROR;
46
47	est_cookie *device = (est_cookie *)_cookie;
48
49	if (position == 0) {
50		size_t max_len = *numBytes;
51		char *str = (char *)buffer;
52
53		snprintf(str, max_len, "CPU Frequency states:\n");
54		max_len-= strlen(str);
55		str += strlen(str);
56
57		freq_info *freqsInfo = device->available_states;
58		freq_info *f;
59		for (f = freqsInfo; f->frequency != 0; f++) {
60			snprintf(str, max_len, " Frequency %hu, Volts %hu, Power %i, "
61				"Latency %i, id %hu\n", f->frequency, f->volts, f->power, f->id,
62				EST_TRANS_LAT);
63			max_len-= strlen(str);
64			str += strlen(str);
65		}
66
67		freq_info *f2 = est_get_current(freqsInfo);
68		if (f2) {
69			snprintf(str, max_len, "\nCurrent State: Frequency %hu, Volts %hu, "
70				"Power %i, Latency %i\n", f2->frequency, f2->volts, f2->power,
71				EST_TRANS_LAT);
72		}
73
74		*numBytes = strlen((char *)buffer);
75	} else {
76		*numBytes = 0;
77	}
78
79	return B_OK;
80}
81
82
83static status_t
84est_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
85{
86	return B_ERROR;
87}
88
89
90status_t
91est_control(void* _cookie, uint32 op, void* arg, size_t len)
92{
93	est_cookie* device = (est_cookie*)_cookie;
94	status_t err = B_ERROR;
95
96	uint32* magicId;
97	uint16* id;
98	freq_info* freqInfo = NULL;
99	switch (op) {
100		case IDENTIFY_DEVICE:
101			if (len < sizeof(uint32))
102				return B_IO_ERROR;
103			magicId = (uint32*)arg;
104			*magicId = kMagicFreqID;
105			err = B_OK;
106			break;
107
108		case GET_CPU_FREQ_STATES:
109			if (len < sizeof(freq_info) * (device->number_states + 1))
110				return B_IO_ERROR;
111			freqInfo = (freq_info*)arg;
112			user_memcpy(freqInfo, device->available_states,
113				sizeof(freq_info) * (device->number_states + 1));
114			err = B_OK;
115			break;
116
117		case GET_CURENT_CPU_FREQ_STATE:
118			if (len < sizeof(uint16))
119				return B_IO_ERROR;
120			freqInfo = est_get_current(device->available_states);
121			if (!freqInfo)
122				return B_ERROR;
123			atomic_set(&sCurrentID, freqInfo->id);
124			*((uint16*)arg) = freqInfo->id;
125			err = B_OK;
126			break;
127
128		case SET_CPU_FREQ_STATE:
129			if (len < sizeof(uint16))
130				return B_IO_ERROR;
131			id = (uint16*)arg;
132			err = est_set_id16(*id);
133			if (err == B_OK) {
134				atomic_set(&sCurrentID, *id);
135				sFrequencyCondition.NotifyAll();
136			}
137			break;
138
139		case WATCH_CPU_FREQ:
140			if (len < sizeof(uint16))
141				return B_IO_ERROR;
142			sFrequencyCondition.Wait();
143			if (atomic_get(&(device->stop_watching))) {
144				atomic_set(&(device->stop_watching), 0);
145				err = B_ERROR;
146			} else {
147				*((uint16*)arg) = atomic_get(&sCurrentID);
148				err = B_OK;
149			}
150			break;
151
152		case STOP_WATCHING_CPU_FREQ:
153			atomic_set(&(device->stop_watching), 1);
154			sFrequencyCondition.NotifyAll();
155			err = B_OK;
156			break;
157	}
158	return err;
159}
160
161
162static status_t
163est_open(void *initCookie, const char *path, int flags, void** cookie)
164{
165	TRACE("est: open\n");
166	est_cookie *device;
167	device = (est_cookie *)calloc(1, sizeof(est_cookie));
168	if (device == NULL)
169		return B_NO_MEMORY;
170
171	*cookie = device;
172
173	device_node *node = (device_node *)initCookie;
174	device->node = node;
175
176	device_node *parent;
177	parent = sDeviceManager->get_parent_node(node);
178	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
179		(void **)&device->acpi_cookie);
180	sDeviceManager->put_node(parent);
181
182	device->stop_watching = 0;
183
184	// enable enhanced speedstep
185	uint64 msrMisc = x86_read_msr(MSR_MISC);
186	if ((msrMisc & MSR_EST_ENABLED) == 0) {
187		TRACE("est: enable enhanced speedstep\n");
188		x86_write_msr(MSR_MISC, msrMisc | MSR_EST_ENABLED);
189
190		uint64 msrMisc = x86_read_msr(MSR_MISC);
191		if ((msrMisc & MSR_EST_ENABLED) == 0) {
192			TRACE("est: enable enhanced speedstep failed\n");
193			return B_ERROR;
194		}
195	}
196
197	// get freq_info
198	if (est_get_info(&(device->available_states)) != B_OK)
199		return B_ERROR;
200	freq_info *freqsInfo = device->available_states;
201
202	// count number of states
203	TRACE("est: frequency info:\n");
204	freq_info *f;
205	device->number_states = 0;
206	for (f = freqsInfo; f->frequency != 0; f++) {
207		TRACE("est: Frequency %u, Volts %u, Power %i, Latency %u, id %u\n",
208			f->frequency, f->volts, f->power, f->id, EST_TRANS_LAT);
209		device->number_states++;
210	}
211
212	// print current frequency
213	freq_info *f2 = est_get_current(freqsInfo);
214	if (f2) {
215		TRACE("est: Current Frequency %u, Volts %u, Power %i, Latency %u\n",
216			f2->frequency, f2->volts, f2->power, EST_TRANS_LAT);
217	}
218
219	return B_OK;
220}
221
222
223static status_t
224est_close(void* cookie)
225{
226	est_cookie *device = (est_cookie*)cookie;
227	free(device);
228	return B_OK;
229}
230
231
232static status_t
233est_free(void* cookie)
234{
235	return B_OK;
236}
237
238
239//	#pragma mark - driver module API
240
241
242static float
243est_support(device_node *parent)
244{
245	// make sure parent is really the ACPI bus manager
246	const char *bus;
247	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
248		return -1;
249
250	if (strcmp(bus, "acpi"))
251		return 0.0;
252
253	// check whether it's really a cpu Device
254	uint32 deviceType;
255	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
256			&deviceType, false) != B_OK
257		|| deviceType != ACPI_TYPE_PROCESSOR) {
258		return 0.0;
259	}
260
261	// check if cpu support est
262	uint32 cpuNum = 0;
263	system_info sysInfo;
264	if (get_system_info(&sysInfo) != B_OK)
265		return 0.0;
266	TRACE("est: cpu_type: %u vendor %u model %u\n", sysInfo.cpu_type,
267		sysInfo.cpu_type & B_CPU_x86_VENDOR_MASK, sysInfo.cpu_type & 0x00FF);
268	if ((sysInfo.cpu_type & B_CPU_x86_VENDOR_MASK) != B_CPU_INTEL_x86)
269		return 0.0;
270
271	// TODO: Make the code SMP safe!
272	if (sysInfo.cpu_count > 1)
273		return 0.0;
274
275	cpuid_info info;
276	if (get_cpuid(&info, 1, cpuNum) != B_OK)
277		return 0.0;
278
279	TRACE("est: extended_features: %i\n", int(info.eax_1.extended_features));
280
281	// check for enhanced speedstep
282	if ((info.eax_1.extended_features & IA32_FEATURE_EXT_EST) == 0)
283		return 0.0;
284
285	TRACE("est: supported\n");
286	return 0.6;
287}
288
289
290static status_t
291est_register_device(device_node *node)
292{
293	device_attr attrs[] = {
294		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
295			{ string: "ACPI Enhanced Speedstep" }},
296		{ NULL }
297	};
298
299	return sDeviceManager->register_node(node, EST_MODULE_NAME, attrs,
300		NULL, NULL);
301}
302
303
304static status_t
305est_init_driver(device_node *node, void **_driverCookie)
306{
307	*_driverCookie = node;
308
309	sFrequencyCondition.Init(NULL, "frequency cv");
310	sCurrentID = -1;
311
312	return B_OK;
313}
314
315
316static void
317est_uninit_driver(void *driverCookie)
318{
319}
320
321
322static status_t
323est_register_child_devices(void *_cookie)
324{
325	device_node *node = (device_node*)_cookie;
326
327	int pathID = sDeviceManager->create_id(EST_PATHID_GENERATOR);
328	if (pathID < 0) {
329		TRACE("est_register_child_devices: couldn't create a path_id\n");
330		return B_ERROR;
331	}
332
333	char name[128];
334	snprintf(name, sizeof(name), EST_BASENAME, pathID);
335
336	return sDeviceManager->publish_device(node, name, EST_DEVICE_MODULE_NAME);
337}
338
339
340static status_t
341est_init_device(void *driverCookie, void **cookie)
342{
343	// driverCookie is the device node
344	*cookie = driverCookie;
345	return B_OK;
346}
347
348
349static void
350est_uninit_device(void *_cookie)
351{
352
353}
354
355
356
357module_dependency module_dependencies[] = {
358	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
359	{}
360};
361
362
363driver_module_info est_driver_module = {
364	{
365		EST_MODULE_NAME,
366		0,
367		NULL
368	},
369
370	est_support,
371	est_register_device,
372	est_init_driver,
373	est_uninit_driver,
374	est_register_child_devices,
375	NULL,	// rescan
376	NULL,	// removed
377};
378
379
380struct device_module_info est_device_module = {
381	{
382		EST_DEVICE_MODULE_NAME,
383		0,
384		NULL
385	},
386
387	est_init_device,
388	est_uninit_device,
389	NULL,
390
391	est_open,
392	est_close,
393	est_free,
394	est_read,
395	est_write,
396	NULL,
397	est_control,
398
399	NULL,
400	NULL
401};
402
403module_info *modules[] = {
404	(module_info *)&est_driver_module,
405	(module_info *)&est_device_module,
406	NULL
407};
408