| #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 |