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