| // 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) |