blob: fdda541531debc50693b6c3ed8539edc8a27d36c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
// Copyright (c) 2016 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <arch/x86/feature.h>
#include <err.h>
#include <inttypes.h>
#include <kernel/lockdep.h>
#include <kernel/mp.h>
#include <kernel/spinlock.h>
#include <lib/console.h>
#include <string.h>
#include <zircon/compiler.h>
static bool hwp_enabled = false;
DECLARE_SINGLETON_SPINLOCK(hwp_lock);
static void hwp_enable_sync_task(void* ctx) {
// Enable HWP
write_msr(X86_MSR_IA32_PM_ENABLE, 1);
// 14.4.7 set minimum/maximum to values from capabilities for
// common case. hint=0x80 by default
uint64_t hwp_caps = read_msr(X86_MSR_IA32_HWP_CAPABILITIES);
uint64_t hwp_req = (0x80ull << 24) | ((hwp_caps & 0xff) << 8) | ((hwp_caps >> 24) & 0xff);
write_msr(X86_MSR_IA32_HWP_REQUEST, hwp_req);
}
static void hwp_enable(void) {
Guard<SpinLock, NoIrqSave> guard{hwp_lock::Get()};
if (hwp_enabled) {
return;
}
if (!x86_feature_test(X86_FEATURE_HWP)) {
printf("HWP not supported\n");
return;
}
mp_sync_exec(MP_IPI_TARGET_ALL, 0, hwp_enable_sync_task, nullptr);
hwp_enabled = true;
}
static void hwp_set_hint_sync_task(void* ctx) {
uint8_t hint = (unsigned long)ctx & 0xff;
uint64_t hwp_req = read_msr(X86_MSR_IA32_HWP_REQUEST) & ~(0xff << 24);
hwp_req |= (hint << 24);
hwp_req &= ~(0xffffffffull << 32);
write_msr(X86_MSR_IA32_HWP_REQUEST, hwp_req);
}
static void hwp_set_hint(unsigned long hint) {
Guard<SpinLock, NoIrqSave> guard{hwp_lock::Get()};
if (!hwp_enabled) {
printf("Enable HWP first\n");
return;
}
if (!x86_feature_test(X86_FEATURE_HWP_PREF)) {
printf("HWP hint not supported\n");
return;
}
mp_sync_exec(MP_IPI_TARGET_ALL, 0, hwp_set_hint_sync_task, (void*)hint);
}
static int cmd_hwp(int argc, const cmd_args* argv, uint32_t flags) {
if (argc < 2) {
notenoughargs:
printf("not enough arguments\n");
usage:
printf("usage:\n");
printf("%s enable\n", argv[0].str);
printf("%s hint <0-255>\n", argv[0].str);
return ZX_ERR_INTERNAL;
}
if (!strcmp(argv[1].str, "enable")) {
hwp_enable();
} else if (!strcmp(argv[1].str, "hint")) {
if (argc < 3) {
goto notenoughargs;
}
if (argv[2].u > 0xff) {
printf("hint must be between 0 (performance) and 255 (energy efficiency)!");
goto usage;
}
hwp_set_hint(argv[2].u);
} else {
printf("unknown command\n");
goto usage;
}
return ZX_OK;
}
STATIC_COMMAND_START
STATIC_COMMAND("hwp", "hardware controlled performance states\n", &cmd_hwp)
STATIC_COMMAND_END(hwp)