blob: b27088531b1b7972d3921d49f3c66e0dadb3276d [file] [log] [blame] [edit]
// Copyright 2019 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-gpio.h"
#include <fidl/fuchsia.hardware.platform.device/cpp/wire_test_base.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/testing/cpp/driver_test.h>
#include <gtest/gtest.h>
#include <mock-mmio-reg/mock-mmio-reg.h>
#include "src/lib/testing/predicates/status.h"
namespace {
constexpr size_t kGpioRegSize = 0x100;
constexpr size_t kInterruptRegSize = 0x30;
constexpr size_t kInterruptRegOffset = 0x3c00;
} // namespace
namespace gpio {
template <uint32_t kPid>
class FakePlatformDevice final
: public fidl::testing::WireTestBase<fuchsia_hardware_platform_device::Device> {
public:
FakePlatformDevice(fdf::MmioBuffer mmio, fdf::MmioBuffer ao_mmio,
fdf::MmioBuffer interrupt_mmio) {
mmios_.insert({0, std::move(mmio)});
mmios_.insert({1, std::move(ao_mmio)});
mmios_.insert({2, std::move(interrupt_mmio)});
}
fuchsia_hardware_platform_device::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_platform_device::Service::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
void SetExpectedInterruptFlags(uint32_t flags) { expected_interrupt_flags_ = flags; }
void GetMmioById(GetMmioByIdRequestView request, GetMmioByIdCompleter::Sync& completer) override {
auto mmio = mmios_.find(request->index);
ASSERT_NE(mmio, mmios_.end());
auto& mmio_buffer = mmio->second;
fidl::Arena arena;
auto buffer = fuchsia_hardware_platform_device::wire::Mmio::Builder(arena)
.offset(reinterpret_cast<size_t>(&mmio_buffer))
.Build();
completer.ReplySuccess(buffer);
}
void GetInterruptById(GetInterruptByIdRequestView request,
GetInterruptByIdCompleter::Sync& completer) override {
EXPECT_EQ(request->flags, expected_interrupt_flags_);
zx::interrupt irq;
if (zx_status_t status = zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &irq);
status != ZX_OK) {
completer.ReplyError(status);
return;
}
// Trigger the interrupt so that the test can wait on it.
irq.trigger(0, zx::time_boot());
completer.ReplySuccess(std::move(irq));
}
void GetNodeDeviceInfo(GetNodeDeviceInfoCompleter::Sync& completer) override {
fidl::Arena arena;
auto info = fuchsia_hardware_platform_device::wire::NodeDeviceInfo::Builder(arena);
// Report kIrqCount IRQs even though we don't check on GetInterruptX calls.
static constexpr size_t kIrqCount = 3;
completer.ReplySuccess(info.vid(PDEV_VID_AMLOGIC).pid(kPid).irq_count(kIrqCount).Build());
}
void GetMetadata(fuchsia_hardware_platform_device::wire::DeviceGetMetadataRequest* request,
GetMetadataCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_FOUND);
}
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
private:
fidl::ServerBindingGroup<fuchsia_hardware_platform_device::Device> bindings_;
uint32_t expected_interrupt_flags_ = ZX_INTERRUPT_MODE_EDGE_HIGH;
std::unordered_map<uint32_t, fdf::MmioBuffer> mmios_;
};
class TestAmlGpioDriver : public AmlGpioDriver {
public:
TestAmlGpioDriver(fdf::DriverStartArgs start_args, fdf::UnownedSynchronizedDispatcher dispatcher)
: AmlGpioDriver(std::move(start_args), std::move(dispatcher)) {}
static DriverRegistration GetDriverRegistration() {
// Use a custom DriverRegistration to create the DUT. Without this, the non-test implementation
// will be used by default.
return FUCHSIA_DRIVER_REGISTRATION_V1(fdf_internal::DriverServer<TestAmlGpioDriver>::initialize,
fdf_internal::DriverServer<TestAmlGpioDriver>::destroy);
}
protected:
fpromise::promise<fdf::MmioBuffer, zx_status_t> MapMmio(
fidl::WireClient<fuchsia_hardware_platform_device::Device>& pdev, uint32_t mmio_id) override {
fpromise::bridge<fdf::MmioBuffer, zx_status_t> bridge;
pdev->GetMmioById(mmio_id).Then(
[completer = std::move(bridge.completer)](
fidl::WireUnownedResult<fuchsia_hardware_platform_device::Device::GetMmioById>&
result) mutable {
EXPECT_TRUE(result.ok());
EXPECT_FALSE(result->is_error());
auto& mmio = *result->value();
EXPECT_TRUE(mmio.has_offset());
auto* mmio_buffer = reinterpret_cast<fdf::MmioBuffer*>(mmio.offset());
completer.complete_ok(std::move(*mmio_buffer));
});
return bridge.consumer.promise_or(fpromise::error(ZX_ERR_BAD_STATE));
}
};
template <uint32_t kPid>
class FakeAmlGpioTestEnvironment : public fdf_testing::Environment {
public:
zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override {
{
zx::result result = to_driver_vfs.AddService<fuchsia_hardware_platform_device::Service>(
pdev_.GetInstanceHandler());
if (result.is_error()) {
return result.take_error();
}
}
return zx::ok();
}
ddk_mock::MockMmioRegRegion& mmio() { return mmio_gpio_; }
ddk_mock::MockMmioRegRegion& ao_mmio() { return mmio_gpio_ao_; }
ddk_mock::MockMmioRegRegion& interrupt_mmio() { return mmio_interrupt_; }
FakePlatformDevice<kPid>& pdev() { return pdev_; }
private:
ddk_mock::MockMmioRegRegion mmio_gpio_{sizeof(uint32_t), kGpioRegSize};
ddk_mock::MockMmioRegRegion mmio_gpio_ao_{sizeof(uint32_t), kGpioRegSize};
ddk_mock::MockMmioRegRegion mmio_interrupt_{sizeof(uint32_t), kInterruptRegSize,
kInterruptRegOffset};
FakePlatformDevice<kPid> pdev_{mmio_gpio_.GetMmioBuffer(), mmio_gpio_ao_.GetMmioBuffer(),
mmio_interrupt_.GetMmioBuffer()};
};
template <uint32_t kPid>
class FixtureConfig final {
public:
using DriverType = TestAmlGpioDriver;
using EnvironmentType = FakeAmlGpioTestEnvironment<kPid>;
};
template <uint32_t kPid>
class AmlGpioTest : public testing::Test {
public:
void SetUp() override {
ASSERT_OK(driver_test_.StartDriver());
zx::result client = driver_test_.template Connect<fuchsia_hardware_pinimpl::Service::Device>();
ASSERT_OK(client);
client_.Bind(std::move(client.value()));
}
void TearDown() override {
ASSERT_OK(driver_test_.StopDriver());
driver_test_.RunInEnvironmentTypeContext([](auto& env) {
env.mmio().VerifyAll();
env.ao_mmio().VerifyAll();
env.interrupt_mmio().VerifyAll();
});
}
protected:
void CheckA113GetInterrupt(uint32_t expected_kernel_interrupt_flags,
uint32_t expected_register_polarity_values) {
driver_test_.RunInEnvironmentTypeContext([&](auto& env) {
env.pdev().SetExpectedInterruptFlags(expected_kernel_interrupt_flags);
auto& interrupt_mmio = env.interrupt_mmio();
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(expected_register_polarity_values);
// Modify IRQ index for IRQ pin.
interrupt_mmio[0x3c21 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000048);
// Interrupt select filter.
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
zx::interrupt out_int;
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, {});
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
void WithMmio(fit::callback<void(ddk_mock::MockMmioRegRegion&)> callback) {
driver_test_.RunInEnvironmentTypeContext(
[callback = std::move(callback)](auto& env) mutable { callback(env.mmio()); });
}
void WithAoMmio(fit::callback<void(ddk_mock::MockMmioRegRegion&)> callback) {
driver_test_.RunInEnvironmentTypeContext(
[callback = std::move(callback)](auto& env) mutable { callback(env.ao_mmio()); });
}
void WithInterruptMmio(fit::callback<void(ddk_mock::MockMmioRegRegion&)> callback) {
driver_test_.RunInEnvironmentTypeContext(
[callback = std::move(callback)](auto& env) mutable { callback(env.interrupt_mmio()); });
}
void WithPDev(fit::callback<void(FakePlatformDevice<kPid>&)> callback) {
driver_test_.RunInEnvironmentTypeContext(
[callback = std::move(callback)](auto& env) mutable { callback(env.pdev()); });
}
fdf::WireSyncClient<fuchsia_hardware_pinimpl::PinImpl>& client() { return client_; }
private:
static void ExpectReadThenWrite(ddk_mock::MockMmioReg& mmio_reg, uint64_t read_value,
uint64_t expected_write_value) {
mmio_reg.ExpectRead(read_value).ExpectWrite(expected_write_value);
}
fdf_testing::BackgroundDriverTest<FixtureConfig<kPid>> driver_test_;
fdf::WireSyncClient<fuchsia_hardware_pinimpl::PinImpl> client_;
};
using A113AmlGpioTest = AmlGpioTest<PDEV_PID_AMLOGIC_A113>;
using S905d2AmlGpioTest = AmlGpioTest<PDEV_PID_AMLOGIC_S905D2>;
// PinImplSetAltFunction Tests
TEST_F(A113AmlGpioTest, A113AltMode1) {
WithMmio([](auto& mmio_region) {
mmio_region[0x24 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000001);
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(1).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x00, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(A113AmlGpioTest, A113AltMode2) {
WithMmio([](auto& mmio_region) {
mmio_region[0x26 * sizeof(uint32_t)].ExpectRead(0x00000009 << 8).ExpectWrite(0x00000005 << 8);
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(5).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x12, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(A113AmlGpioTest, A113AltMode3) {
WithAoMmio([](auto& ao_mmio) {
ao_mmio[0x05 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000005 << 16);
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(5).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x56, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(S905d2AmlGpioTest, S905d2AltMode) {
WithMmio([](auto& mmio) {
mmio[0xb6 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000001);
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(1).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x00, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(A113AmlGpioTest, AltModeFail1) {
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(16).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x00, config);
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
TEST_F(A113AmlGpioTest, AltModeFail2) {
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).function(1).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0xFFFF, config);
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
// PinImplConfigIn Tests
TEST_F(A113AmlGpioTest, A113NoPull0) {
WithMmio([](auto& mmio) {
mmio[0x12 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
mmio[0x4a * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFE); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(A113AmlGpioTest, A113NoPullMid) {
WithMmio([](auto& mmio) {
mmio[0x12 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
mmio[0x4a * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFBFFFF); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x12, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x12, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(A113AmlGpioTest, A113NoPullHigh) {
WithAoMmio([](auto& ao_mmio) {
ao_mmio[0x08 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
ao_mmio[0x0b * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
ao_mmio[0x0b * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFEFFFF); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x56, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x56, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(S905d2AmlGpioTest, S905d2NoPull0) {
WithMmio([](auto& mmio) {
mmio[0x1c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3e * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
mmio[0x4c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFE); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x0, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x0, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(S905d2AmlGpioTest, S905d2PullUp) {
WithMmio([](auto& mmio) {
mmio[0x10 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3a * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
mmio[0x48 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kUp)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x21, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x21, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(S905d2AmlGpioTest, S905d2PullDown) {
WithMmio([](auto& mmio) {
mmio[0x10 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3a * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFE); // pull
mmio[0x48 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull_en
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kDown)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x20, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x20, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(A113AmlGpioTest, A113NoPullFail) {
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0xFFFF, config);
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
// PinImplConfigOut Tests
TEST_F(A113AmlGpioTest, A113Out) {
WithMmio([](auto& mmio) {
mmio[0x0d * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // output
mmio[0x0c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFB); // oen
});
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(0x19, fuchsia_hardware_gpio::BufferMode::kOutputHigh);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
// PinImplRead Tests
TEST_F(A113AmlGpioTest, A113Read) {
WithMmio([](auto& mmio) {
mmio[0x12 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // oen
mmio[0x3c * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFF); // pull
mmio[0x4a * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFDF); // pull_en
mmio[0x14 * sizeof(uint32_t)].ExpectRead(0x00000020); // read 0x01.
mmio[0x14 * sizeof(uint32_t)].ExpectRead(0x00000000); // read 0x00.
mmio[0x14 * sizeof(uint32_t)].ExpectRead(0x00000020); // read 0x01.
});
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena)
.pull(fuchsia_hardware_pin::Pull::kNone)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->Configure(5, config);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result =
client().buffer(arena)->SetBufferMode(5, fuchsia_hardware_gpio::BufferMode::kInput);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result = client().buffer(arena)->Read(5);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
EXPECT_EQ(result->value()->value, 0x01);
}
{
fdf::WireUnownedResult result = client().buffer(arena)->Read(5);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
EXPECT_EQ(result->value()->value, 0x00);
}
{
fdf::WireUnownedResult result = client().buffer(arena)->Read(5);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
EXPECT_EQ(result->value()->value, 0x01);
}
}
// PinImplGetInterrupt Tests
TEST_F(A113AmlGpioTest, A113GetInterruptFail) {
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0xFFFF, {});
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
TEST_F(A113AmlGpioTest, A113GetInterruptEdgeLow) {
// Request edge low polarity. Expect the kernel polarity to be converted to edge high.
CheckA113GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, 0x0001'0001);
}
TEST_F(A113AmlGpioTest, A113GetInterruptEdgeHigh) {
// Request edge high polarity. Expect the kernel polarity to stay as edge high.
CheckA113GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, 0x0000'0001);
}
TEST_F(A113AmlGpioTest, A113GetInterruptLevelLow) {
// Request level low polarity. Expect the kernel polarity to be converted to level high.
CheckA113GetInterrupt(ZX_INTERRUPT_MODE_LEVEL_HIGH, 0x0001'0000);
}
TEST_F(A113AmlGpioTest, A113GetInterruptLevelHigh) {
// Request level high polarity. Expect the kernel polarity to stay as level high.
CheckA113GetInterrupt(ZX_INTERRUPT_MODE_LEVEL_HIGH, 0x0000'0000);
}
// PinImplReleaseInterrupt Tests
TEST_F(A113AmlGpioTest, A113ReleaseInterruptFail) {
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result = client().buffer(arena)->ReleaseInterrupt(0x66);
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
TEST_F(A113AmlGpioTest, A113ReleaseInterrupt) {
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c21 * sizeof(uint32_t)]
.ExpectRead(0x00000000)
.ExpectWrite(0x00000048); // modify
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x00010001);
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
fdf::Arena arena('GPIO');
{
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, {});
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
{
fdf::WireUnownedResult result = client().buffer(arena)->ReleaseInterrupt(0x0B);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
// PinImplConfigureInterrupt Tests
TEST_F(A113AmlGpioTest, A113ConfigureInterrupt) {
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c21 * sizeof(uint32_t)]
.ExpectRead(0x00000000)
.ExpectWrite(0x00000048); // modify
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x00010001);
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
interrupt_mmio[0x3c20 * sizeof(uint32_t)]
.ExpectRead(0x00010001)
.ExpectWrite(0x00000001) // polarity + for any edge.
.ExpectRead(0x00000001)
.ExpectWrite(0x00000000)
.ExpectRead(0x00000000)
.ExpectWrite(0x00010000)
.ExpectRead(0x00010000)
.ExpectWrite(0x00010001);
});
fdf::Arena arena('GPIO');
{
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, {});
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
auto edge_high = fuchsia_hardware_gpio::wire::InterruptConfiguration::Builder(arena)
.mode(fuchsia_hardware_gpio::InterruptMode::kEdgeHigh)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->ConfigureInterrupt(0x0B, edge_high);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
auto level_high = fuchsia_hardware_gpio::wire::InterruptConfiguration::Builder(arena)
.mode(fuchsia_hardware_gpio::InterruptMode::kLevelHigh)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->ConfigureInterrupt(0x0B, level_high);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
auto level_low = fuchsia_hardware_gpio::wire::InterruptConfiguration::Builder(arena)
.mode(fuchsia_hardware_gpio::InterruptMode::kLevelLow)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->ConfigureInterrupt(0x0B, level_low);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
auto edge_low = fuchsia_hardware_gpio::wire::InterruptConfiguration::Builder(arena)
.mode(fuchsia_hardware_gpio::InterruptMode::kEdgeLow)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->ConfigureInterrupt(0x0B, edge_low);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
TEST_F(A113AmlGpioTest, A113ConfigureThenGetInterrupt) {
fdf::Arena arena('GPIO');
// Configuring the interrupt first should not result in any MMIO accesses.
auto edge_high = fuchsia_hardware_gpio::wire::InterruptConfiguration::Builder(arena)
.mode(fuchsia_hardware_gpio::InterruptMode::kEdgeHigh)
.Build();
{
fdf::WireUnownedResult result = client().buffer(arena)->ConfigureInterrupt(0x0B, edge_high);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000001);
interrupt_mmio[0x3c21 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000048);
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
{
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, {});
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
}
// PinImplSetDriveStrength Tests
TEST_F(A113AmlGpioTest, A113SetDriveStrength) {
fdf::Arena arena('GPIO');
auto config =
fuchsia_hardware_pin::wire::Configuration::Builder(arena).drive_strength_ua(2).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x87, config);
ASSERT_TRUE(result.ok());
EXPECT_FALSE(result->is_ok());
}
TEST_F(S905d2AmlGpioTest, S905d2SetDriveStrength) {
WithAoMmio([](auto& ao_mmio) {
ao_mmio[0x08 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFF).ExpectWrite(0xFFFFFFFB);
});
fdf::Arena arena('GPIO');
auto config =
fuchsia_hardware_pin::wire::Configuration::Builder(arena).drive_strength_ua(3000).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x62, config);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_TRUE(result->value()->new_config.has_drive_strength_ua());
EXPECT_EQ(result->value()->new_config.drive_strength_ua(), 3000);
}
TEST_F(S905d2AmlGpioTest, S905d2GetDriveStrength) {
WithAoMmio(
[](auto& mmio_region) { mmio_region[0x08 * sizeof(uint32_t)].ExpectRead(0xFFFFFFFB); });
fdf::Arena arena('GPIO');
auto config = fuchsia_hardware_pin::wire::Configuration::Builder(arena).Build();
fdf::WireUnownedResult result = client().buffer(arena)->Configure(0x62, config);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
ASSERT_TRUE(result->value()->new_config.has_drive_strength_ua());
EXPECT_EQ(result->value()->new_config.drive_strength_ua(), 3000);
}
TEST_F(S905d2AmlGpioTest, TimestampMonoInterruptOption) {
WithPDev([](auto& pdev) {
pdev.SetExpectedInterruptFlags(ZX_INTERRUPT_MODE_EDGE_HIGH | ZX_INTERRUPT_TIMESTAMP_MONO);
});
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x0001'0001);
// Modify IRQ index for IRQ pin.
interrupt_mmio[0x3c21 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000017);
// Interrupt select filter.
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
constexpr auto kOptions = fuchsia_hardware_gpio::InterruptOptions::kTimestampMono;
zx::interrupt out_int;
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, kOptions);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(S905d2AmlGpioTest, WakeableInterruptOption) {
constexpr uint32_t kWakeableZirconInterruptOption = 0x20;
WithPDev([](auto& pdev) {
pdev.SetExpectedInterruptFlags(ZX_INTERRUPT_MODE_EDGE_HIGH | kWakeableZirconInterruptOption);
});
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x0001'0001);
// Modify IRQ index for IRQ pin.
interrupt_mmio[0x3c21 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000017);
// Interrupt select filter.
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
constexpr auto kOptions = fuchsia_hardware_gpio::InterruptOptions::kWakeable;
zx::interrupt out_int;
fdf::Arena arena('GPIO');
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, kOptions);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
TEST_F(A113AmlGpioTest, DestroyInterruptOnRelease) {
WithInterruptMmio([](auto& interrupt_mmio) {
interrupt_mmio[0x3c21 * sizeof(uint32_t)]
.ExpectRead(0x00000000)
.ExpectWrite(0x00000048); // modify
interrupt_mmio[0x3c20 * sizeof(uint32_t)].ExpectRead(0x00010001);
interrupt_mmio[0x3c23 * sizeof(uint32_t)].ExpectRead(0x00000000).ExpectWrite(0x00000007);
});
fdf::Arena arena('GPIO');
zx::interrupt interrupt{};
{
fdf::WireUnownedResult result = client().buffer(arena)->GetInterrupt(0x0B, {});
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
interrupt = std::move(result->value()->interrupt);
}
// FakePlatformDevice triggered the interrupt -- wait on it to make sure we have a working handle.
EXPECT_EQ(interrupt.wait(nullptr), ZX_OK);
{
fdf::WireUnownedResult result = client().buffer(arena)->ReleaseInterrupt(0x0B);
ASSERT_TRUE(result.ok());
EXPECT_TRUE(result->is_ok());
}
// AmlGpio should have called zx_interrupt_destroy in the interrupt for this pin.
EXPECT_EQ(interrupt.wait(nullptr), ZX_ERR_CANCELED);
}
} // namespace gpio