blob: db73a9794e221668ad33efb0f98da877b7965697 [file] [log] [blame]
#include "aml-cpu.h"
#include <fuchsia/hardware/thermal/llcpp/fidl.h>
#include <lib/fake_ddk/fidl-helper.h>
#include <memory>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <zxtest/zxtest.h>
namespace amlogic_cpu {
using CpuCtrlSyncClient = fuchsia_cpuctrl::Device::SyncClient;
using ThermalSyncClient = fuchsia_thermal::Device::SyncClient;
using llcpp::fuchsia::device::MAX_DEVICE_PERFORMANCE_STATES;
constexpr size_t kBigClusterIdx =
static_cast<size_t>(fuchsia_thermal::PowerDomain::BIG_CLUSTER_POWER_DOMAIN);
constexpr size_t kLittleClusterIdx =
static_cast<size_t>(fuchsia_thermal::PowerDomain::LITTLE_CLUSTER_POWER_DOMAIN);
constexpr size_t PowerDomainToIndex(fuchsia_thermal::PowerDomain pd) {
switch (pd) {
case fuchsia_thermal::PowerDomain::LITTLE_CLUSTER_POWER_DOMAIN:
return kLittleClusterIdx;
case fuchsia_thermal::PowerDomain::BIG_CLUSTER_POWER_DOMAIN:
return kBigClusterIdx;
}
__UNREACHABLE;
}
const fuchsia_thermal::OperatingPoint kFakeOperatingPoints = []() {
fuchsia_thermal::OperatingPoint result;
result.count = 3;
result.latency = 0;
result.opp[0].volt_uv = 1;
result.opp[0].freq_hz = 100;
result.opp[1].volt_uv = 2;
result.opp[1].freq_hz = 200;
result.opp[2].volt_uv = 3;
result.opp[2].freq_hz = 300;
return result;
}();
const fuchsia_thermal::ThermalDeviceInfo kFakeThermalDeviceInfo = []() {
fuchsia_thermal::ThermalDeviceInfo result;
result.active_cooling = false;
result.passive_cooling = false;
result.gpu_throttling = false;
result.num_trip_points = 0;
result.big_little = false;
result.critical_temp_celsius = 0;
result.opps[kLittleClusterIdx].count = 0;
result.opps[kBigClusterIdx] = kFakeOperatingPoints;
return result;
}();
class FakeAmlThermal;
using TestDeviceType = ddk::Device<FakeAmlThermal, ddk::Messageable>;
class FakeAmlThermal : TestDeviceType, fuchsia_thermal::Device::Interface {
public:
FakeAmlThermal() : TestDeviceType(nullptr), active_operating_point_(0) {}
~FakeAmlThermal() {}
// Manage the Fake FIDL Message Loop
zx_status_t Init();
static zx_status_t MessageOp(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn);
zx::channel& GetMessengerChannel() { return messenger_.local(); }
// Accessor
uint16_t ActiveOperatingPoint() const { return active_operating_point_; }
zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
void DdkRelease() {}
private:
// Implement Thermal FIDL Protocol.
void GetInfo(GetInfoCompleter::Sync completer);
void GetDeviceInfo(GetDeviceInfoCompleter::Sync completer);
void GetDvfsInfo(fuchsia_thermal::PowerDomain pd, GetDvfsInfoCompleter::Sync completer);
void GetTemperatureCelsius(GetTemperatureCelsiusCompleter::Sync completer);
void GetStateChangeEvent(GetStateChangeEventCompleter::Sync completer);
void GetStateChangePort(GetStateChangePortCompleter::Sync completer);
void SetTripCelsius(uint32_t id, float temp, SetTripCelsiusCompleter::Sync completer);
void GetDvfsOperatingPoint(fuchsia_thermal::PowerDomain pd,
GetDvfsOperatingPointCompleter::Sync completer);
void SetDvfsOperatingPoint(uint16_t op_idx, fuchsia_thermal::PowerDomain pd,
SetDvfsOperatingPointCompleter::Sync completer);
void GetFanLevel(GetFanLevelCompleter::Sync completer);
void SetFanLevel(uint32_t fan_level, SetFanLevelCompleter::Sync completer);
uint16_t active_operating_point_;
fake_ddk::FidlMessenger messenger_;
};
zx_status_t FakeAmlThermal::MessageOp(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
return static_cast<FakeAmlThermal*>(ctx)->DdkMessage(msg, txn);
}
zx_status_t FakeAmlThermal::Init() {
return messenger_.SetMessageOp(this, FakeAmlThermal::MessageOp);
}
zx_status_t FakeAmlThermal::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
fuchsia_thermal::Device::Dispatch(this, msg, &transaction);
return transaction.Status();
}
void FakeAmlThermal::GetInfo(GetInfoCompleter::Sync completer) {
fuchsia_thermal::ThermalInfo result;
result.state = 0;
result.passive_temp_celsius = 0;
result.critical_temp_celsius = 0;
result.max_trip_count = 0;
completer.Reply(ZX_OK, fidl::unowned_ptr(&result));
}
void FakeAmlThermal::GetDeviceInfo(GetDeviceInfoCompleter::Sync completer) {
fuchsia_thermal::ThermalDeviceInfo result = kFakeThermalDeviceInfo;
completer.Reply(ZX_OK, fidl::unowned_ptr(&result));
}
void FakeAmlThermal::GetDvfsInfo(fuchsia_thermal::PowerDomain pd,
GetDvfsInfoCompleter::Sync completer) {
fuchsia_thermal::ThermalDeviceInfo device_info = kFakeThermalDeviceInfo;
fuchsia_thermal::OperatingPoint result = device_info.opps[PowerDomainToIndex(pd)];
completer.Reply(ZX_OK, fidl::unowned_ptr(&result));
}
void FakeAmlThermal::GetTemperatureCelsius(GetTemperatureCelsiusCompleter::Sync completer) {
completer.Reply(ZX_OK, 0.0);
}
void FakeAmlThermal::GetStateChangeEvent(GetStateChangeEventCompleter::Sync completer) {
zx::event invalid;
completer.Reply(ZX_ERR_NOT_SUPPORTED, std::move(invalid));
}
void FakeAmlThermal::GetStateChangePort(GetStateChangePortCompleter::Sync completer) {
zx::port invalid;
completer.Reply(ZX_ERR_NOT_SUPPORTED, std::move(invalid));
}
void FakeAmlThermal::SetTripCelsius(uint32_t id, float temp,
SetTripCelsiusCompleter::Sync completer) {
completer.Reply(ZX_ERR_NOT_SUPPORTED);
}
void FakeAmlThermal::GetDvfsOperatingPoint(fuchsia_thermal::PowerDomain pd,
GetDvfsOperatingPointCompleter::Sync completer) {
if (pd == fuchsia_thermal::PowerDomain::LITTLE_CLUSTER_POWER_DOMAIN) {
completer.Reply(ZX_ERR_NOT_SUPPORTED, 0);
return;
}
completer.Reply(ZX_OK, active_operating_point_);
}
void FakeAmlThermal::SetDvfsOperatingPoint(uint16_t idx, fuchsia_thermal::PowerDomain pd,
SetDvfsOperatingPointCompleter::Sync completer) {
if (pd == fuchsia_thermal::PowerDomain::LITTLE_CLUSTER_POWER_DOMAIN) {
completer.Reply(ZX_ERR_NOT_SUPPORTED);
return;
}
active_operating_point_ = idx;
completer.Reply(ZX_OK);
}
void FakeAmlThermal::GetFanLevel(GetFanLevelCompleter::Sync completer) {
completer.Reply(ZX_ERR_NOT_SUPPORTED, 0);
}
void FakeAmlThermal::SetFanLevel(uint32_t fan_level, SetFanLevelCompleter::Sync completer) {
completer.Reply(ZX_ERR_OUT_OF_RANGE);
}
class AmlCpuTest : public AmlCpu {
public:
AmlCpuTest(ThermalSyncClient thermal) : AmlCpu(nullptr, std::move(thermal)) {}
zx_status_t Init();
static zx_status_t MessageOp(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn);
zx::channel& GetMessengerChannel() { return messenger_.local(); }
private:
fake_ddk::FidlMessenger messenger_;
};
zx_status_t AmlCpuTest::MessageOp(void* ctx, fidl_msg_t* msg, fidl_txn_t* txn) {
return static_cast<AmlCpuTest*>(ctx)->DdkMessage(msg, txn);
}
zx_status_t AmlCpuTest::Init() { return messenger_.SetMessageOp(this, AmlCpuTest::MessageOp); }
class AmlCpuTestFixture : public zxtest::Test {
public:
void SetUp() override;
protected:
FakeAmlThermal thermal_;
std::unique_ptr<AmlCpuTest> dut_;
std::unique_ptr<CpuCtrlSyncClient> cpu_client_;
};
void AmlCpuTestFixture::SetUp() {
ASSERT_OK(thermal_.Init());
ThermalSyncClient thermal_client(std::move(thermal_.GetMessengerChannel()));
dut_ = std::make_unique<AmlCpuTest>(std::move(thermal_client));
ASSERT_OK(dut_->Init());
cpu_client_ = std::make_unique<CpuCtrlSyncClient>(std::move(dut_->GetMessengerChannel()));
}
TEST_F(AmlCpuTestFixture, TestGetPerformanceStateInfo) {
// Make sure that we can get information about all the supported pstates.
for (uint32_t i = 0; i < kFakeOperatingPoints.count; i++) {
auto pstateInfo = cpu_client_->GetPerformanceStateInfo(i);
// First, make sure there were no transport errors.
ASSERT_OK(pstateInfo.status());
// Then make sure that the driver accepted the call.
ASSERT_FALSE(pstateInfo->result.is_err());
// Then make sure that we're getting the accepted frequency and voltage values.
EXPECT_EQ(pstateInfo->result.response().info.frequency_hz,
kFakeOperatingPoints.opp[kFakeOperatingPoints.count - i - 1].freq_hz);
EXPECT_EQ(pstateInfo->result.response().info.voltage_uv,
kFakeOperatingPoints.opp[kFakeOperatingPoints.count - i - 1].volt_uv);
}
// Make sure that we can't get any information about pstates that don't
// exist.
for (uint32_t i = kFakeOperatingPoints.count; i < MAX_DEVICE_PERFORMANCE_STATES; i++) {
auto pstateInfo = cpu_client_->GetPerformanceStateInfo(i);
// Even if it's an unsupported pstate, we still expect the transport to
// deliver the message successfully.
ASSERT_OK(pstateInfo.status());
// Make sure that the driver returns an error, however.
EXPECT_TRUE(pstateInfo->result.is_err());
}
}
TEST_F(AmlCpuTestFixture, TestSetPerformanceState) {
// Make sure that we can drive the CPU to all of the supported performance
// states.
for (uint32_t i = 0; i < kFakeOperatingPoints.count; i++) {
uint32_t out_state = UINT32_MAX;
zx_status_t st = dut_->DdkSetPerformanceState(i, &out_state);
// Make sure the call succeeded.
EXPECT_OK(st);
// Make sure we could actually drive the device into the state that we
// expected.
EXPECT_EQ(out_state, i);
// Make sure that the call was forwarded to the thermal driver.
const uint16_t kExpectedOperatingPoint =
static_cast<uint16_t>(kFakeOperatingPoints.count - i - 1);
EXPECT_EQ(kExpectedOperatingPoint, thermal_.ActiveOperatingPoint());
}
// Next make sure that we can't drive the CPU into any unsupported
// performance states.
for (uint32_t i = kFakeOperatingPoints.count; i < MAX_DEVICE_PERFORMANCE_STATES; i++) {
const uint16_t kInitialOperatingPoint = thermal_.ActiveOperatingPoint();
uint32_t out_state = UINT32_MAX;
zx_status_t st = dut_->DdkSetPerformanceState(i, &out_state);
// This is not a supported performance state.
EXPECT_NOT_OK(st);
// Make sure we haven't meddled with `out_state`
EXPECT_EQ(out_state, UINT32_MAX);
// Make sure we haven't meddled with the thermal driver's active
// operating point.
EXPECT_EQ(kInitialOperatingPoint, thermal_.ActiveOperatingPoint());
}
}
} // namespace amlogic_cpu