blob: 7287231560492b1fb1a203b49a6b0bb217baef44 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager.h"
#include "registers.h"
PowerManager::PowerManager(magma::RegisterIo* io)
{
power_state_semaphore_ = magma::PlatformSemaphore::Create();
// Initialize current set of running cores.
ReceivedPowerInterrupt(io);
last_check_time_ = std::chrono::steady_clock::now();
}
void PowerManager::EnableCores(magma::RegisterIo* io, uint64_t shader_bitmask)
{
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kShader,
registers::CoreReadyState::ActionType::kActionPowerOn,
shader_bitmask);
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kL2,
registers::CoreReadyState::ActionType::kActionPowerOn, 1);
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kTiler,
registers::CoreReadyState::ActionType::kActionPowerOn, 1);
}
void PowerManager::DisableL2(magma::RegisterIo* io)
{
uint32_t powered_on_shaders =
registers::CoreReadyState::ReadBitmask(io, registers::CoreReadyState::CoreType::kShader,
registers::CoreReadyState::StatusType::kReady) |
registers::CoreReadyState::ReadBitmask(
io, registers::CoreReadyState::CoreType::kShader,
registers::CoreReadyState::StatusType::kPowerTransitioning);
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kShader,
registers::CoreReadyState::ActionType::kActionPowerOff,
powered_on_shaders);
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kL2,
registers::CoreReadyState::ActionType::kActionPowerOff,
1);
registers::CoreReadyState::WriteState(io, registers::CoreReadyState::CoreType::kTiler,
registers::CoreReadyState::ActionType::kActionPowerOff,
1);
}
bool PowerManager::WaitForL2Disable(magma::RegisterIo* io)
{
while (true) {
bool powered_on =
registers::CoreReadyState::ReadBitmask(io, registers::CoreReadyState::CoreType::kL2,
registers::CoreReadyState::StatusType::kReady) |
registers::CoreReadyState::ReadBitmask(
io, registers::CoreReadyState::CoreType::kL2,
registers::CoreReadyState::StatusType::kPowerTransitioning);
if (!powered_on)
return true;
if (!power_state_semaphore_->Wait(1000))
return false;
}
}
bool PowerManager::WaitForShaderReady(magma::RegisterIo* io)
{
while (true) {
// Only wait for 1 shader to be ready, since that's enough to execute
// commands.
bool powered_on =
registers::CoreReadyState::ReadBitmask(io, registers::CoreReadyState::CoreType::kShader,
registers::CoreReadyState::StatusType::kReady);
if (powered_on)
return true;
if (!power_state_semaphore_->Wait(1000))
return false;
}
}
void PowerManager::ReceivedPowerInterrupt(magma::RegisterIo* io)
{
std::lock_guard<std::mutex> lock(ready_status_mutex_);
tiler_ready_status_ =
registers::CoreReadyState::ReadBitmask(io, registers::CoreReadyState::CoreType::kTiler,
registers::CoreReadyState::StatusType::kReady);
l2_ready_status_ =
registers::CoreReadyState::ReadBitmask(io, registers::CoreReadyState::CoreType::kL2,
registers::CoreReadyState::StatusType::kReady);
power_state_semaphore_->Signal();
}
void PowerManager::UpdateGpuActive(bool active)
{
auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::duration total_time = now - last_check_time_;
constexpr uint32_t kMemoryMilliseconds = 100;
std::chrono::milliseconds memory_duration(kMemoryMilliseconds);
// Ignore long periods of inactive time.
if (total_time > memory_duration)
total_time = memory_duration;
std::chrono::steady_clock::duration active_time =
gpu_active_ ? total_time : std::chrono::steady_clock::duration(0);
constexpr uint32_t kBucketLengthMilliseconds = 50;
bool coalesced = false;
if (!time_periods_.empty()) {
auto start_time = time_periods_.back().end_time - time_periods_.back().total_time;
if (now - start_time < std::chrono::milliseconds(kBucketLengthMilliseconds)) {
coalesced = true;
time_periods_.back().end_time = now;
time_periods_.back().total_time += total_time;
time_periods_.back().active_time += active_time;
}
}
if (!coalesced)
time_periods_.push_back(TimePeriod{now, total_time, active_time});
while (!time_periods_.empty() && (now - time_periods_.front().end_time > memory_duration)) {
time_periods_.pop_front();
}
last_check_time_ = now;
gpu_active_ = active;
}
void PowerManager::GetGpuActiveInfo(std::chrono::steady_clock::duration* total_time_out,
std::chrono::steady_clock::duration* active_time_out)
{
UpdateGpuActive(gpu_active_);
std::chrono::steady_clock::duration total_time_accumulate(0);
std::chrono::steady_clock::duration active_time_accumulate(0);
for (auto& period : time_periods_) {
total_time_accumulate += period.total_time;
active_time_accumulate += period.active_time;
}
*total_time_out = total_time_accumulate;
*active_time_out = active_time_accumulate;
}