blob: 8172f7c37dda610048613d5b3d268d3d93b6e8d6 [file] [log] [blame]
// Copyright 2020 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 "aml-cpu.h"
#include <lib/fake_ddk/fidl-helper.h>
#include <memory>
#include <vector>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <mock/ddktl/protocol/clock.h>
#include <mock/ddktl/protocol/power.h>
#include <sdk/lib/inspect/testing/cpp/zxtest/inspect.h>
#include <zxtest/zxtest.h>
namespace amlogic_cpu {
using llcpp::fuchsia::device::MAX_DEVICE_PERFORMANCE_STATES;
using CpuCtrlClient = fuchsia_cpuctrl::Device::SyncClient;
using inspect::InspectTestHelper;
#define MHZ(x) ((x)*1000000)
const std::vector<operating_point_t> kTestOperatingPoints = {
{.freq_hz = MHZ(10), .volt_uv = 1500, .pd_id = 0},
{.freq_hz = MHZ(9), .volt_uv = 1350, .pd_id = 0},
{.freq_hz = MHZ(8), .volt_uv = 1200, .pd_id = 0},
{.freq_hz = MHZ(7), .volt_uv = 1050, .pd_id = 0},
{.freq_hz = MHZ(6), .volt_uv = 900, .pd_id = 0},
{.freq_hz = MHZ(5), .volt_uv = 750, .pd_id = 0},
{.freq_hz = MHZ(4), .volt_uv = 600, .pd_id = 0},
{.freq_hz = MHZ(3), .volt_uv = 450, .pd_id = 0},
{.freq_hz = MHZ(2), .volt_uv = 300, .pd_id = 0},
{.freq_hz = MHZ(1), .volt_uv = 150, .pd_id = 0},
};
class AmlCpuTest : public AmlCpu {
public:
AmlCpuTest(const ddk::ClockProtocolClient&& plldiv16, const ddk::ClockProtocolClient&& cpudiv16,
const ddk::ClockProtocolClient&& cpuscaler, const ddk::PowerProtocolClient&& pwr,
const std::vector<operating_point_t> operating_points)
: AmlCpu(nullptr, std::move(plldiv16), std::move(cpudiv16), std::move(cpuscaler),
std::move(pwr), operating_points) {}
static zx_status_t MessageOp(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
return static_cast<AmlCpuTest*>(ctx)->DdkMessage(msg, txn);
}
zx_status_t InitTest() { return messenger_.SetMessageOp(this, AmlCpuTest::MessageOp); }
zx::channel& GetMessengerChannel() { return messenger_.local(); }
zx::vmo inspect_vmo() { return inspector_.DuplicateVmo(); }
private:
fake_ddk::FidlMessenger messenger_;
};
class AmlCpuTestFixture : public InspectTestHelper, public zxtest::Test {
public:
AmlCpuTestFixture()
: dut_(ddk::ClockProtocolClient(pll_clock_.GetProto()),
ddk::ClockProtocolClient(cpu_clock_.GetProto()),
ddk::ClockProtocolClient(scaler_clock_.GetProto()),
ddk::PowerProtocolClient(power_.GetProto()), kTestOperatingPoints),
operating_points_(kTestOperatingPoints) {}
void SetUp() override {
ASSERT_OK(dut_.InitTest());
// Notes on AmlCpu Initialization:
// + Should enable the CPU and PLL clocks.
// + Should initially assume that the device is in it's lowest performance state.
// + Should configure the device to it's highest performance state.
pll_clock_.ExpectEnable(ZX_OK);
cpu_clock_.ExpectEnable(ZX_OK);
const operating_point_t& slowest = operating_points_.back();
const operating_point_t& fastest = operating_points_.front();
// The DUT should initialize.
power_.ExpectGetSupportedVoltageRange(ZX_OK, slowest.volt_uv, fastest.volt_uv);
power_.ExpectRegisterPowerDomain(ZX_OK, slowest.volt_uv, fastest.volt_uv);
// The DUT scales up to the fastest available pstate.
power_.ExpectRequestVoltage(ZX_OK, fastest.volt_uv, fastest.volt_uv);
scaler_clock_.ExpectSetRate(ZX_OK, fastest.freq_hz);
ASSERT_OK(dut_.Init());
cpu_client_ = std::make_unique<CpuCtrlClient>(std::move(dut_.GetMessengerChannel()));
}
protected:
void VerifyAll() {
ASSERT_NO_FATAL_FAILURES(pll_clock_.VerifyAndClear());
ASSERT_NO_FATAL_FAILURES(cpu_clock_.VerifyAndClear());
ASSERT_NO_FATAL_FAILURES(scaler_clock_.VerifyAndClear());
ASSERT_NO_FATAL_FAILURES(power_.VerifyAndClear());
}
ddk::MockClock pll_clock_;
ddk::MockClock cpu_clock_;
ddk::MockClock scaler_clock_;
ddk::MockPower power_;
AmlCpuTest dut_;
std::unique_ptr<CpuCtrlClient> cpu_client_;
const std::vector<operating_point_t> operating_points_;
};
TEST_F(AmlCpuTestFixture, TestGetPerformanceStateInfo) {
// Make sure that we can get information about all the supported pstates.
for (size_t i = 0; i < kTestOperatingPoints.size(); i++) {
const uint32_t pstate = static_cast<uint32_t>(i);
auto pstateInfo = cpu_client_->GetPerformanceStateInfo(pstate);
// 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 expected frequency and voltage values.
EXPECT_EQ(pstateInfo->result.response().info.frequency_hz, kTestOperatingPoints[i].freq_hz);
EXPECT_EQ(pstateInfo->result.response().info.voltage_uv, kTestOperatingPoints[i].volt_uv);
}
// Make sure that we can't get any information about pstates that don't
// exist.
for (size_t i = kTestOperatingPoints.size(); i < MAX_DEVICE_PERFORMANCE_STATES; i++) {
const uint32_t pstate = static_cast<uint32_t>(i);
auto pstateInfo = cpu_client_->GetPerformanceStateInfo(pstate);
// 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());
}
ASSERT_NO_FATAL_FAILURES(VerifyAll());
}
TEST_F(AmlCpuTestFixture, TestSetPerformanceState) {
// Scale to the lowest performance state.
const uint32_t min_pstate_index = static_cast<uint32_t>(kTestOperatingPoints.size() - 1);
const operating_point_t& min_pstate = kTestOperatingPoints[min_pstate_index];
scaler_clock_.ExpectSetRate(ZX_OK, min_pstate.freq_hz);
power_.ExpectRequestVoltage(ZX_OK, min_pstate.volt_uv, min_pstate.volt_uv);
uint32_t out_state = UINT32_MAX;
zx_status_t result = dut_.DdkSetPerformanceState(min_pstate_index, &out_state);
EXPECT_OK(result);
EXPECT_EQ(out_state, min_pstate_index);
// Scale to the highest performance state.
const uint32_t max_pstate_index = 0;
const operating_point_t& max_pstate = kTestOperatingPoints[max_pstate_index];
scaler_clock_.ExpectSetRate(ZX_OK, max_pstate.freq_hz);
power_.ExpectRequestVoltage(ZX_OK, max_pstate.volt_uv, max_pstate.volt_uv);
out_state = UINT32_MAX;
result = dut_.DdkSetPerformanceState(max_pstate_index, &out_state);
EXPECT_OK(result);
EXPECT_EQ(out_state, max_pstate_index);
// Set to the pstate that we're already at and make sure that it's a no-op.
result = dut_.DdkSetPerformanceState(max_pstate_index, &out_state);
EXPECT_OK(result);
EXPECT_EQ(out_state, max_pstate_index);
ASSERT_NO_FATAL_FAILURES(VerifyAll());
}
TEST_F(AmlCpuTestFixture, TestSetCpuInfo) {
uint32_t test_cpu_version = 0x28200b02;
dut_.SetCpuInfo(test_cpu_version);
ASSERT_NO_FATAL_FAILURES(ReadInspect(dut_.inspect_vmo()));
auto* cpu_info = hierarchy().GetByPath({"cpu_info_service"});
ASSERT_TRUE(cpu_info);
// cpu_major_revision : 40
ASSERT_NO_FATAL_FAILURES(CheckProperty<inspect::UintPropertyValue>(
cpu_info->node(), "cpu_major_revision", inspect::UintPropertyValue(40)));
// cpu_minor_revision : 11
ASSERT_NO_FATAL_FAILURES(CheckProperty<inspect::UintPropertyValue>(
cpu_info->node(), "cpu_minor_revision", inspect::UintPropertyValue(11)));
// cpu_package_id : 2
ASSERT_NO_FATAL_FAILURES(CheckProperty<inspect::UintPropertyValue>(
cpu_info->node(), "cpu_package_id", inspect::UintPropertyValue(2)));
}
} // namespace amlogic_cpu