blob: cf2c87f399fa3b2020510007f87e805d94d94de2 [file] [log] [blame]
// 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-sdmmc.h"
#include <lib/fake-bti/bti.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/mmio-ptr/fake.h>
#include <lib/sdio/hw.h>
#include <lib/sdmmc/hw.h>
#include <threads.h>
#include <vector>
#include <soc/aml-s912/s912-hw.h>
#include <zxtest/zxtest.h>
#include "aml-sdmmc-regs.h"
namespace sdmmc {
class TestAmlSdmmc : public AmlSdmmc {
public:
TestAmlSdmmc(const mmio_buffer_t& mmio, zx::bti bti)
: AmlSdmmc(fake_ddk::kFakeParent, std::move(bti), ddk::MmioBuffer(mmio),
ddk::MmioPinnedBuffer({&mmio, ZX_HANDLE_INVALID, 0x100}),
aml_sdmmc_config_t{
.supports_dma = true,
.min_freq = 400000,
.max_freq = 120000000,
.version_3 = true,
.prefs = 0,
},
zx::interrupt(ZX_HANDLE_INVALID), ddk::GpioProtocolClient()) {}
zx_status_t TestDdkAdd() {
// call parent's bind
return Bind();
}
void DdkRelease() { AmlSdmmc::DdkRelease(); }
zx_status_t WaitForInterruptImpl() override {
if (request_index_ < request_results_.size() && request_results_[request_index_] == 0) {
// Indicate a receive CRC error.
mmio_.Write32(1, kAmlSdmmcStatusOffset);
successful_transfers_ = 0;
request_index_++;
} else if (interrupt_status_.has_value()) {
mmio_.Write32(interrupt_status_.value(), kAmlSdmmcStatusOffset);
} else {
// Indicate that the request completed successfully.
mmio_.Write32(1 << 13, kAmlSdmmcStatusOffset);
// Each tuning transfer is attempted five times with a short-circuit if one fails.
// Report every successful transfer five times to make the results arrays easier to
// follow.
if (++successful_transfers_ % AML_SDMMC_TUNING_TEST_ATTEMPTS == 0) {
successful_transfers_ = 0;
request_index_++;
}
}
return ZX_OK;
}
void WaitForBus() const override { /* Do nothing, bus is always ready in tests */
}
void SetRequestResults(const std::vector<uint8_t>& request_results) {
request_results_ = request_results;
request_index_ = 0;
}
void SetRequestInterruptStatus(uint32_t status) { interrupt_status_ = status; }
aml_sdmmc_desc_t* descs() { return AmlSdmmc::descs(); }
private:
std::vector<uint8_t> request_results_;
size_t request_index_ = 0;
uint32_t successful_transfers_ = 0;
// The optional interrupt status to set after a request is completed.
std::optional<uint32_t> interrupt_status_;
};
class AmlSdmmcTest : public zxtest::Test {
public:
AmlSdmmcTest() : mmio_({FakeMmioPtr(&mmio_), 0, 0, ZX_HANDLE_INVALID}) {}
void SetUp() override {
registers_.reset(new uint8_t[S912_SD_EMMC_B_LENGTH]);
memset(registers_.get(), 0, S912_SD_EMMC_B_LENGTH);
mmio_buffer_t mmio_buffer = {
.vaddr = FakeMmioPtr(registers_.get()),
.offset = 0,
.size = S912_SD_EMMC_B_LENGTH,
.vmo = ZX_HANDLE_INVALID,
};
mmio_ = ddk::MmioBuffer(mmio_buffer);
memset(bti_paddrs_, 0, sizeof(bti_paddrs_));
bti_paddrs_[0] = PAGE_SIZE; // This is passed to AmlSdmmc::Init().
zx::bti bti;
ASSERT_OK(fake_bti_create_with_paddrs(bti_paddrs_, countof(bti_paddrs_),
bti.reset_and_get_address()));
dut_ = new TestAmlSdmmc(mmio_buffer, std::move(bti));
dut_->set_board_config({
.supports_dma = true,
.min_freq = 400000,
.max_freq = 120000000,
.version_3 = true,
.prefs = 0,
});
mmio_.Write32(0xff, kAmlSdmmcDelay1Offset);
mmio_.Write32(0xff, kAmlSdmmcDelay2Offset);
mmio_.Write32(0xff, kAmlSdmmcAdjustOffset);
dut_->SdmmcHwReset();
EXPECT_EQ(mmio_.Read32(kAmlSdmmcDelay1Offset), 0);
EXPECT_EQ(mmio_.Read32(kAmlSdmmcDelay2Offset), 0);
EXPECT_EQ(mmio_.Read32(kAmlSdmmcAdjustOffset), 0);
mmio_.Write32(1, kAmlSdmmcCfgOffset); // Set bus width 4.
memcpy(reinterpret_cast<uint8_t*>(registers_.get()) + kAmlSdmmcPingOffset,
aml_sdmmc_tuning_blk_pattern_4bit, sizeof(aml_sdmmc_tuning_blk_pattern_4bit));
}
void TearDown() override {
if (dut_ != nullptr) {
dut_->DdkRelease();
}
}
protected:
static zx_koid_t GetVmoKoid(const zx::vmo& vmo) {
zx_info_handle_basic_t info = {};
size_t actual = 0;
size_t available = 0;
zx_status_t status =
vmo.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), &actual, &available);
if (status != ZX_OK || actual < 1) {
return ZX_KOID_INVALID;
}
return info.koid;
}
void InitializeContiguousPaddrs(const size_t vmos) {
// Start at 1 because one paddr has already been read to create the DMA descriptor buffer.
for (size_t i = 0; i < vmos; i++) {
bti_paddrs_[i + 1] = (i << 24) | PAGE_SIZE;
}
}
void InitializeSingleVmoPaddrs(const size_t pages) {
// Start at 1 because one paddr has already been read to create the DMA descriptor buffer.
for (size_t i = 0; i < pages; i++) {
bti_paddrs_[i + 1] = PAGE_SIZE * (i + 1);
}
}
void InitializeNonContiguousPaddrs(const size_t vmos) {
for (size_t i = 0; i < vmos; i++) {
bti_paddrs_[i + 1] = PAGE_SIZE * (i + 1) * 2;
}
}
zx_paddr_t bti_paddrs_[64] = {};
ddk::MmioBuffer mmio_;
TestAmlSdmmc* dut_ = nullptr;
private:
std::unique_ptr<uint8_t[]> registers_;
};
TEST_F(AmlSdmmcTest, DdkLifecycle) {
fake_ddk::Bind ddk;
EXPECT_OK(dut_->TestDdkAdd());
dut_->DdkAsyncRemove();
EXPECT_TRUE(ddk.Ok());
}
TEST_F(AmlSdmmcTest, TuningV3) {
dut_->set_board_config({
.supports_dma = false,
.min_freq = 400000,
.max_freq = 120000000,
.version_3 = true,
.prefs = 0,
});
ASSERT_OK(dut_->Init());
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0);
auto adjust_v2 = AmlSdmmcAdjustV2::Get().FromValue(0);
adjust.set_adj_fixed(0).set_adj_delay(0x3f).WriteTo(&mmio_);
adjust_v2.set_adj_fixed(0).set_adj_delay(0x3f).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
adjust.ReadFrom(&mmio_);
adjust_v2.ReadFrom(&mmio_);
EXPECT_EQ(adjust.adj_fixed(), 1);
EXPECT_EQ(adjust.adj_delay(), 0);
}
TEST_F(AmlSdmmcTest, TuningV2) {
dut_->set_board_config({
.supports_dma = false,
.min_freq = 400000,
.max_freq = 120000000,
.version_3 = false,
.prefs = 0,
});
ASSERT_OK(dut_->Init());
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0);
auto adjust_v2 = AmlSdmmcAdjustV2::Get().FromValue(0);
adjust.set_adj_fixed(0).set_adj_delay(0x3f).WriteTo(&mmio_);
adjust_v2.set_adj_fixed(0).set_adj_delay(0x3f).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
adjust.ReadFrom(&mmio_);
adjust_v2.ReadFrom(&mmio_);
EXPECT_EQ(adjust_v2.adj_fixed(), 1);
EXPECT_EQ(adjust_v2.adj_delay(), 0);
}
TEST_F(AmlSdmmcTest, TuningAllPass) {
ASSERT_OK(dut_->Init());
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto clock = AmlSdmmcClock::Get().ReadFrom(&mmio_).set_cfg_div(10).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0).set_adj_delay(0x3f).WriteTo(&mmio_);
auto delay1 = AmlSdmmcDelay1::Get().FromValue(0).WriteTo(&mmio_);
auto delay2 = AmlSdmmcDelay2::Get().FromValue(0).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
clock.ReadFrom(&mmio_);
adjust.ReadFrom(&mmio_);
delay1.ReadFrom(&mmio_);
delay2.ReadFrom(&mmio_);
EXPECT_EQ(clock.cfg_tx_phase(), 0);
EXPECT_EQ(adjust.adj_delay(), 0);
EXPECT_EQ(delay1.dly_0(), 32);
EXPECT_EQ(delay1.dly_1(), 32);
EXPECT_EQ(delay1.dly_2(), 32);
EXPECT_EQ(delay1.dly_3(), 32);
EXPECT_EQ(delay1.dly_4(), 32);
EXPECT_EQ(delay2.dly_5(), 32);
EXPECT_EQ(delay2.dly_6(), 32);
EXPECT_EQ(delay2.dly_7(), 32);
EXPECT_EQ(delay2.dly_8(), 32);
EXPECT_EQ(delay2.dly_9(), 32);
}
TEST_F(AmlSdmmcTest, AdjDelayTuningNoWindowWrap) {
// clang-format off
dut_->SetRequestResults({
/*
0 1 2 3 4 5 6 7 8 9
*/
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, // Phase 0
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, // Phase 1
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, // Phase 3
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto clock = AmlSdmmcClock::Get().ReadFrom(&mmio_).set_cfg_div(10).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0).set_adj_delay(0x3f).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
clock.ReadFrom(&mmio_);
adjust.ReadFrom(&mmio_);
EXPECT_EQ(clock.cfg_tx_phase(), 3);
EXPECT_EQ(adjust.adj_delay(), 6);
}
TEST_F(AmlSdmmcTest, AdjDelayTuningLargestWindowChosen) {
// clang-format off
dut_->SetRequestResults({
/*
0 1 2 3 4 5 6 7 8 9
*/
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 1
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, // Phase 3
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto clock = AmlSdmmcClock::Get().ReadFrom(&mmio_).set_cfg_div(10).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0).set_adj_delay(0x3f).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
clock.ReadFrom(&mmio_);
adjust.ReadFrom(&mmio_);
EXPECT_EQ(clock.cfg_tx_phase(), 1);
EXPECT_EQ(adjust.adj_delay(), 0);
}
TEST_F(AmlSdmmcTest, AdjDelayTuningWindowWrap) {
// clang-format off
dut_->SetRequestResults({
/*
0 1 2 3 4 5 6 7 8 9
*/
0, 1, 1, 0, 0, 1, 1, 1, 1, 0, // Phase 0
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, // Phase 1
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // Phase 3
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto clock = AmlSdmmcClock::Get().ReadFrom(&mmio_).set_cfg_div(10).WriteTo(&mmio_);
auto adjust = AmlSdmmcAdjust::Get().FromValue(0).set_adj_delay(0x3f).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
clock.ReadFrom(&mmio_);
adjust.ReadFrom(&mmio_);
EXPECT_EQ(clock.cfg_tx_phase(), 1);
EXPECT_EQ(adjust.adj_delay(), 0);
}
TEST_F(AmlSdmmcTest, AdjDelayTuningAllFail) {
// clang-format off
dut_->SetRequestResults({
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
EXPECT_NOT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
}
TEST_F(AmlSdmmcTest, DelayLineTuningNoWindowWrap) {
// clang-format off
dut_->SetRequestResults({
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 1
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 2
// Best window: start 12, size 10, delay 17.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto delay1 = AmlSdmmcDelay1::Get().FromValue(0).WriteTo(&mmio_);
auto delay2 = AmlSdmmcDelay2::Get().FromValue(0).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
delay1.ReadFrom(&mmio_);
delay2.ReadFrom(&mmio_);
EXPECT_EQ(delay1.dly_0(), 17);
EXPECT_EQ(delay1.dly_1(), 17);
EXPECT_EQ(delay1.dly_2(), 17);
EXPECT_EQ(delay1.dly_3(), 17);
EXPECT_EQ(delay1.dly_4(), 17);
EXPECT_EQ(delay2.dly_5(), 17);
EXPECT_EQ(delay2.dly_6(), 17);
EXPECT_EQ(delay2.dly_7(), 17);
EXPECT_EQ(delay2.dly_8(), 17);
EXPECT_EQ(delay2.dly_9(), 17);
}
TEST_F(AmlSdmmcTest, DelayLineTuningWindowWrap) {
// clang-format off
dut_->SetRequestResults({
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 1
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 2
// Best window: start 54, size 25, delay 2.
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
});
// clang-format on
ASSERT_OK(dut_->Init());
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto delay1 = AmlSdmmcDelay1::Get().FromValue(0).WriteTo(&mmio_);
auto delay2 = AmlSdmmcDelay2::Get().FromValue(0).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
delay1.ReadFrom(&mmio_);
delay2.ReadFrom(&mmio_);
EXPECT_EQ(delay1.dly_0(), 2);
EXPECT_EQ(delay1.dly_1(), 2);
EXPECT_EQ(delay1.dly_2(), 2);
EXPECT_EQ(delay1.dly_3(), 2);
EXPECT_EQ(delay1.dly_4(), 2);
EXPECT_EQ(delay2.dly_5(), 2);
EXPECT_EQ(delay2.dly_6(), 2);
EXPECT_EQ(delay2.dly_7(), 2);
EXPECT_EQ(delay2.dly_8(), 2);
EXPECT_EQ(delay2.dly_9(), 2);
}
TEST_F(AmlSdmmcTest, DelayLineTuningAllFail) {
// clang-format off
dut_->SetRequestResults({
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 1
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Phase 2
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
});
// clang-format on
AmlSdmmcClock::Get().FromValue(0).set_cfg_div(10).WriteTo(&mmio_);
ASSERT_OK(dut_->Init());
EXPECT_NOT_OK(dut_->SdmmcPerformTuning(SD_SEND_TUNING_BLOCK));
}
TEST_F(AmlSdmmcTest, SetBusFreq) {
ASSERT_OK(dut_->Init());
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_bus_width(AmlSdmmcCfg::kBusWidth4Bit).WriteTo(&mmio_);
auto clock = AmlSdmmcClock::Get().FromValue(0).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcSetBusFreq(100'000'000));
EXPECT_EQ(clock.ReadFrom(&mmio_).cfg_div(), 10);
EXPECT_EQ(clock.cfg_src(), 1);
EXPECT_OK(dut_->SdmmcSetBusFreq(200'000'000));
EXPECT_EQ(clock.ReadFrom(&mmio_).cfg_div(), 9);
EXPECT_EQ(clock.cfg_src(), 1);
EXPECT_OK(dut_->SdmmcSetBusFreq(0));
EXPECT_EQ(clock.ReadFrom(&mmio_).cfg_div(), 0);
EXPECT_OK(dut_->SdmmcSetBusFreq(54'000'000));
EXPECT_EQ(clock.ReadFrom(&mmio_).cfg_div(), 19);
EXPECT_EQ(clock.cfg_src(), 1);
EXPECT_OK(dut_->SdmmcSetBusFreq(400'000));
EXPECT_EQ(clock.ReadFrom(&mmio_).cfg_div(), 60);
EXPECT_EQ(clock.cfg_src(), 0);
}
TEST_F(AmlSdmmcTest, ClearStatus) {
ASSERT_OK(dut_->Init());
// Set end_of_chain to indicate we're done and to have something to clear
dut_->SetRequestInterruptStatus(1 << 13);
sdmmc_req_t request;
memset(&request, 0, sizeof(request));
EXPECT_OK(dut_->SdmmcRequest(&request));
auto status = AmlSdmmcStatus::Get().FromValue(0);
EXPECT_EQ(AmlSdmmcStatus::kClearStatus, status.ReadFrom(&mmio_).reg_value());
}
TEST_F(AmlSdmmcTest, TxCrcError) {
ASSERT_OK(dut_->Init());
// Set TX CRC error bit (8) and desc_busy bit (30)
dut_->SetRequestInterruptStatus(1 << 8 | 1 << 30);
sdmmc_req_t request;
memset(&request, 0, sizeof(request));
EXPECT_EQ(ZX_ERR_IO_DATA_INTEGRITY, dut_->SdmmcRequest(&request));
auto start = AmlSdmmcStart::Get().FromValue(0);
// The desc busy bit should now have been cleared because of the error
EXPECT_EQ(0, start.ReadFrom(&mmio_).desc_busy());
}
TEST_F(AmlSdmmcTest, RequestsFailAfterSuspend) {
ASSERT_OK(dut_->Init());
sdmmc_req_t request;
memset(&request, 0, sizeof(request));
EXPECT_OK(dut_->SdmmcRequest(&request));
ddk::SuspendTxn txn(fake_ddk::kFakeDevice, 0, false, 0);
dut_->DdkSuspend(std::move(txn));
EXPECT_NOT_OK(dut_->SdmmcRequest(&request));
}
TEST_F(AmlSdmmcTest, UnownedVmosBlockMode) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
zx::vmo vmos[10] = {};
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(vmos); i++) {
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmos[i]));
buffers[i] = {
.buffer =
{
.vmo = vmos[i].get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = i * 16,
.size = 32 * (i + 2),
};
}
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(2)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < countof(vmos); i++) {
expected_desc_cfg.set_len(i + 2).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == countof(vmos) - 1) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, (i << 24) | (PAGE_SIZE + (i * 16)));
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, UnownedVmosNotBlockSizeMultiple) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
zx::vmo vmos[10] = {};
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(vmos); i++) {
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmos[i]));
buffers[i] = {
.buffer =
{
.vmo = vmos[i].get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 0,
.size = 32 * (i + 2),
};
}
buffers[5].size = 25;
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, UnownedVmosByteMode) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
zx::vmo vmos[10] = {};
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(vmos); i++) {
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmos[i]));
buffers[i] = {
.buffer =
{
.vmo = vmos[i].get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = i * 4,
.size = 50,
};
}
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 50,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(50)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < countof(vmos); i++) {
expected_desc_cfg.set_len(50).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == countof(vmos) - 1) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, (i << 24) | (PAGE_SIZE + (i * 4)));
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, UnownedVmoByteModeMultiBlock) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
InitializeContiguousPaddrs(1);
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = vmo.get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 0,
.size = 400,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 100,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(100)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < 4; i++) {
expected_desc_cfg.set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == 3) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, PAGE_SIZE + (i * 100));
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, UnownedVmoOffsetNotAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
InitializeContiguousPaddrs(1);
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = vmo.get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 3,
.size = 64,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, UnownedVmoSingleBufferMultipleDescriptors) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeSingleVmoPaddrs(pages);
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = vmo.get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 16,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(511)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE + 16);
EXPECT_EQ(descs[0].resp_addr, 0);
expected_desc_cfg.set_len(2)
.set_end_of_chain(1)
.set_no_resp(1)
.set_no_cmd(1)
.set_resp_num(0)
.set_cmd_idx(0);
EXPECT_EQ(descs[1].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[1].cmd_arg, 0);
EXPECT_EQ(descs[1].data_addr, PAGE_SIZE + (511 * 32) + 16);
EXPECT_EQ(descs[1].resp_addr, 0);
}
TEST_F(AmlSdmmcTest, UnownedVmoSingleBufferNotPageAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeNonContiguousPaddrs(pages);
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = vmo.get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 16,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, UnownedVmoSingleBufferPageAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeNonContiguousPaddrs(pages);
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = vmo.get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = 32,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(127)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, (PAGE_SIZE * 2) + 32);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < 5; i++) {
expected_desc_cfg.set_len(128).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == 4) {
expected_desc_cfg.set_len(2).set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, PAGE_SIZE * (i + 1) * 2);
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, OwnedVmosBlockMode) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(buffers); i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, SDMMC_VMO_RIGHT_WRITE));
buffers[i] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = i * 16,
.size = 32 * (i + 2),
};
}
zx::vmo vmo;
EXPECT_NOT_OK(dut_->SdmmcUnregisterVmo(3, 1, &vmo));
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(2)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < countof(buffers); i++) {
expected_desc_cfg.set_len(i + 2).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == countof(buffers) - 1) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, (i << 24) | (PAGE_SIZE + (i * 80)));
EXPECT_EQ(descs[i].resp_addr, 0);
}
request.client_id = 7;
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_OK(dut_->SdmmcUnregisterVmo(3, 0, &vmo));
EXPECT_NOT_OK(dut_->SdmmcRegisterVmo(2, 0, std::move(vmo), 0, 512, SDMMC_VMO_RIGHT_WRITE));
request.client_id = 0;
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, OwnedVmosNotBlockSizeMultiple) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(buffers); i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, SDMMC_VMO_RIGHT_WRITE));
buffers[i] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 0,
.size = 32 * (i + 2),
};
}
buffers[5].size = 25;
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, OwnedVmosByteMode) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(buffers); i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, SDMMC_VMO_RIGHT_WRITE));
buffers[i] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = i * 4,
.size = 50,
};
}
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 50,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(50)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < countof(buffers); i++) {
expected_desc_cfg.set_len(50).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == countof(buffers) - 1) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, (i << 24) | (PAGE_SIZE + (i * 68)));
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, OwnedVmoByteModeMultiBlock) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
InitializeContiguousPaddrs(1);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 0, 512, SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo_id = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 0,
.size = 400,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 100,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(100)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < 4; i++) {
expected_desc_cfg.set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == 3) {
expected_desc_cfg.set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, PAGE_SIZE + (i * 100));
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, OwnedVmoOffsetNotAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
InitializeContiguousPaddrs(1);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 2, 512, SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo_id = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 32,
.size = 64,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, OwnedVmoSingleBufferMultipleDescriptors) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeSingleVmoPaddrs(pages);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 8, (pages * PAGE_SIZE) - 8,
SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo_id = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 8,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(511)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE + 16);
EXPECT_EQ(descs[0].resp_addr, 0);
expected_desc_cfg.set_len(1)
.set_len(2)
.set_end_of_chain(1)
.set_no_resp(1)
.set_no_cmd(1)
.set_resp_num(0)
.set_cmd_idx(0);
EXPECT_EQ(descs[1].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[1].cmd_arg, 0);
EXPECT_EQ(descs[1].data_addr, PAGE_SIZE + (511 * 32) + 16);
EXPECT_EQ(descs[1].resp_addr, 0);
}
TEST_F(AmlSdmmcTest, OwnedVmoSingleBufferNotPageAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeNonContiguousPaddrs(pages);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 8, (pages * PAGE_SIZE) - 8,
SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 8,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, OwnedVmoSingleBufferPageAligned) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeNonContiguousPaddrs(pages);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 16, (pages * PAGE_SIZE) - 16,
SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 16,
.size = 32 * 513,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(127)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, (PAGE_SIZE * 2) + 32);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < 5; i++) {
expected_desc_cfg.set_len(128).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == 4) {
expected_desc_cfg.set_len(2).set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, PAGE_SIZE * (i + 1) * 2);
EXPECT_EQ(descs[i].resp_addr, 0);
}
}
TEST_F(AmlSdmmcTest, OwnedVmoWritePastEnd) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
const size_t pages = ((32 * 514) / PAGE_SIZE) + 1;
ASSERT_OK(zx::vmo::create(pages * PAGE_SIZE, 0, &vmo));
InitializeNonContiguousPaddrs(pages);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 32, 32 * 384, SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer =
{
.vmo = 1,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 32,
.size = 32 * 383,
};
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(126)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, (PAGE_SIZE * 2) + 64);
EXPECT_EQ(descs[0].resp_addr, 0);
for (uint32_t i = 1; i < 4; i++) {
expected_desc_cfg.set_len(128).set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
if (i == 3) {
expected_desc_cfg.set_len(1).set_end_of_chain(1);
}
EXPECT_EQ(descs[i].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[i].cmd_arg, 0);
EXPECT_EQ(descs[i].data_addr, PAGE_SIZE * (i + 1) * 2);
EXPECT_EQ(descs[i].resp_addr, 0);
}
buffer.size = 32 * 384;
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, SeparateClientVmoSpaces) {
ASSERT_OK(dut_->Init());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
const zx_koid_t vmo1_koid = GetVmoKoid(vmo);
EXPECT_NE(vmo1_koid, ZX_KOID_INVALID);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 0, PAGE_SIZE, SDMMC_VMO_RIGHT_WRITE));
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
const zx_koid_t vmo2_koid = GetVmoKoid(vmo);
EXPECT_NE(vmo2_koid, ZX_KOID_INVALID);
EXPECT_OK(dut_->SdmmcRegisterVmo(2, 0, std::move(vmo), 0, PAGE_SIZE, SDMMC_VMO_RIGHT_WRITE));
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
EXPECT_NOT_OK(dut_->SdmmcRegisterVmo(1, 0, std::move(vmo), 0, PAGE_SIZE, SDMMC_VMO_RIGHT_WRITE));
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
EXPECT_NOT_OK(dut_->SdmmcRegisterVmo(1, 8, std::move(vmo), 0, PAGE_SIZE, SDMMC_VMO_RIGHT_WRITE));
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
const zx_koid_t vmo3_koid = GetVmoKoid(vmo);
EXPECT_NE(vmo3_koid, ZX_KOID_INVALID);
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 1, std::move(vmo), 0, PAGE_SIZE, SDMMC_VMO_RIGHT_WRITE));
EXPECT_OK(dut_->SdmmcUnregisterVmo(1, 0, &vmo));
EXPECT_EQ(GetVmoKoid(vmo), vmo1_koid);
EXPECT_OK(dut_->SdmmcUnregisterVmo(2, 0, &vmo));
EXPECT_EQ(GetVmoKoid(vmo), vmo2_koid);
EXPECT_OK(dut_->SdmmcUnregisterVmo(1, 1, &vmo));
EXPECT_EQ(GetVmoKoid(vmo), vmo3_koid);
EXPECT_NOT_OK(dut_->SdmmcUnregisterVmo(1, 0, &vmo));
EXPECT_NOT_OK(dut_->SdmmcUnregisterVmo(2, 0, &vmo));
EXPECT_NOT_OK(dut_->SdmmcUnregisterVmo(1, 1, &vmo));
}
TEST_F(AmlSdmmcTest, RequestWithOwnedAndUnownedVmos) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
zx::vmo vmos[5] = {};
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < 5; i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmos[i]));
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, SDMMC_VMO_RIGHT_WRITE));
buffers[i * 2] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = i * 16,
.size = 32 * (i + 2),
};
buffers[(i * 2) + 1] = {
.buffer =
{
.vmo = vmos[i].get(),
},
.type = SDMMC_BUFFER_TYPE_VMO_HANDLE,
.offset = i * 16,
.size = 32 * (i + 2),
};
}
zx::vmo vmo;
EXPECT_NOT_OK(dut_->SdmmcUnregisterVmo(3, 1, &vmo));
sdmmc_req_new_t request = {
.cmd_idx = SDMMC_READ_MULTIPLE_BLOCK,
.cmd_flags = SDMMC_READ_MULTIPLE_BLOCK_FLAGS,
.arg = 0x1234abcd,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
AmlSdmmcCmdResp::Get().FromValue(0xfedc9876).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(response[0], 0xfedc9876);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(2)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDMMC_READ_MULTIPLE_BLOCK)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x1234abcd);
EXPECT_EQ(descs[0].data_addr, PAGE_SIZE);
EXPECT_EQ(descs[0].resp_addr, 0);
expected_desc_cfg.set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
EXPECT_EQ(descs[1].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[1].cmd_arg, 0);
EXPECT_EQ(descs[1].data_addr, (5 << 24) | PAGE_SIZE);
EXPECT_EQ(descs[1].resp_addr, 0);
expected_desc_cfg.set_len(3);
EXPECT_EQ(descs[2].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[2].cmd_arg, 0);
EXPECT_EQ(descs[2].data_addr, (1 << 24) | (PAGE_SIZE + 64 + 16));
EXPECT_EQ(descs[2].resp_addr, 0);
EXPECT_EQ(descs[3].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[3].cmd_arg, 0);
EXPECT_EQ(descs[3].data_addr, (6 << 24) | (PAGE_SIZE + 16));
EXPECT_EQ(descs[3].resp_addr, 0);
expected_desc_cfg.set_len(4);
EXPECT_EQ(descs[4].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[4].cmd_arg, 0);
EXPECT_EQ(descs[4].data_addr, (2 << 24) | (PAGE_SIZE + 128 + 32));
EXPECT_EQ(descs[4].resp_addr, 0);
EXPECT_EQ(descs[5].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[5].cmd_arg, 0);
EXPECT_EQ(descs[5].data_addr, (7 << 24) | (PAGE_SIZE + 32));
EXPECT_EQ(descs[5].resp_addr, 0);
expected_desc_cfg.set_len(5);
EXPECT_EQ(descs[6].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[6].cmd_arg, 0);
EXPECT_EQ(descs[6].data_addr, (3 << 24) | (PAGE_SIZE + 192 + 48));
EXPECT_EQ(descs[6].resp_addr, 0);
EXPECT_EQ(descs[7].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[7].cmd_arg, 0);
EXPECT_EQ(descs[7].data_addr, (8 << 24) | (PAGE_SIZE + 48));
EXPECT_EQ(descs[7].resp_addr, 0);
expected_desc_cfg.set_len(6);
EXPECT_EQ(descs[8].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[8].cmd_arg, 0);
EXPECT_EQ(descs[8].data_addr, (4 << 24) | (PAGE_SIZE + 256 + 64));
EXPECT_EQ(descs[8].resp_addr, 0);
expected_desc_cfg.set_end_of_chain(1);
EXPECT_EQ(descs[9].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[9].cmd_arg, 0);
EXPECT_EQ(descs[9].data_addr, (9 << 24) | (PAGE_SIZE + 64));
EXPECT_EQ(descs[9].resp_addr, 0);
}
TEST_F(AmlSdmmcTest, ResetCmdInfoBits) {
ASSERT_OK(dut_->Init());
bti_paddrs_[1] = 0x1897'7000;
bti_paddrs_[2] = 0x1997'8000;
bti_paddrs_[3] = 0x1997'e000;
// Make sure the appropriate cmd_info bits get cleared.
dut_->descs()[0].cmd_info = 0xffff'ffff;
dut_->descs()[1].cmd_info = 0xffff'ffff;
dut_->descs()[2].cmd_info = 0xffff'ffff;
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE * 3, 0, &vmo));
EXPECT_OK(dut_->SdmmcRegisterVmo(1, 2, std::move(vmo), 0, PAGE_SIZE * 3, SDMMC_VMO_RIGHT_WRITE));
sdmmc_buffer_region_t buffer = {
.buffer = {.vmo_id = 1},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 0,
.size = 10752,
};
sdmmc_req_new_t request = {
.cmd_idx = SDIO_IO_RW_DIRECT_EXTENDED,
.cmd_flags = SDIO_IO_RW_DIRECT_EXTENDED_FLAGS | SDMMC_CMD_READ,
.arg = 0x29000015,
.blocksize = 512,
.probe_tuning_cmd = false,
.client_id = 2,
.buffers_list = &buffer,
.buffers_count = 1,
};
uint32_t response[4] = {};
AmlSdmmcCfg::Get().ReadFrom(&mmio_).set_blk_len(0).WriteTo(&mmio_);
EXPECT_OK(dut_->SdmmcRequestNew(&request, response));
EXPECT_EQ(AmlSdmmcCfg::Get().ReadFrom(&mmio_).blk_len(), 9);
const aml_sdmmc_desc_t* descs = dut_->descs();
auto expected_desc_cfg = AmlSdmmcCmdCfg::Get()
.FromValue(0)
.set_len(8)
.set_block_mode(1)
.set_timeout(AmlSdmmcCmdCfg::kDefaultCmdTimeout)
.set_data_io(1)
.set_data_wr(0)
.set_resp_num(1)
.set_cmd_idx(SDIO_IO_RW_DIRECT_EXTENDED)
.set_owner(1);
EXPECT_EQ(descs[0].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[0].cmd_arg, 0x29000015);
EXPECT_EQ(descs[0].data_addr, 0x1897'7000);
EXPECT_EQ(descs[0].resp_addr, 0);
expected_desc_cfg.set_no_resp(1).set_no_cmd(1).set_resp_num(0).set_cmd_idx(0);
EXPECT_EQ(descs[1].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[1].cmd_arg, 0);
EXPECT_EQ(descs[1].data_addr, 0x1997'8000);
EXPECT_EQ(descs[1].resp_addr, 0);
expected_desc_cfg.set_len(5).set_end_of_chain(1);
EXPECT_EQ(descs[2].cmd_info, expected_desc_cfg.reg_value());
EXPECT_EQ(descs[2].cmd_arg, 0);
EXPECT_EQ(descs[2].data_addr, 0x1997'e000);
EXPECT_EQ(descs[2].resp_addr, 0);
}
TEST_F(AmlSdmmcTest, WriteToReadOnlyVmo) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(buffers); i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
const uint32_t vmo_rights = SDMMC_VMO_RIGHT_READ | (i == 5 ? 0 : SDMMC_VMO_RIGHT_WRITE);
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, vmo_rights));
buffers[i] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 0,
.size = 32 * (i + 2),
};
}
sdmmc_req_new_t request = {
.cmd_idx = SDIO_IO_RW_DIRECT_EXTENDED,
.cmd_flags = SDIO_IO_RW_DIRECT_EXTENDED_FLAGS | SDMMC_CMD_READ,
.arg = 0x29000015,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
TEST_F(AmlSdmmcTest, ReadFromWriteOnlyVmo) {
ASSERT_OK(dut_->Init());
InitializeContiguousPaddrs(10);
sdmmc_buffer_region_t buffers[10];
for (uint32_t i = 0; i < countof(buffers); i++) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(PAGE_SIZE, 0, &vmo));
const uint32_t vmo_rights = SDMMC_VMO_RIGHT_WRITE | (i == 5 ? 0 : SDMMC_VMO_RIGHT_READ);
EXPECT_OK(dut_->SdmmcRegisterVmo(i, 0, std::move(vmo), i * 64, 512, vmo_rights));
buffers[i] = {
.buffer =
{
.vmo_id = i,
},
.type = SDMMC_BUFFER_TYPE_VMO_ID,
.offset = 0,
.size = 32 * (i + 2),
};
}
sdmmc_req_new_t request = {
.cmd_idx = SDIO_IO_RW_DIRECT_EXTENDED,
.cmd_flags = SDIO_IO_RW_DIRECT_EXTENDED_FLAGS,
.arg = 0x29000015,
.blocksize = 32,
.probe_tuning_cmd = false,
.client_id = 0,
.buffers_list = buffers,
.buffers_count = countof(buffers),
};
uint32_t response[4] = {};
EXPECT_NOT_OK(dut_->SdmmcRequestNew(&request, response));
}
} // namespace sdmmc