blob: 38dc14f541f36ee1171fad88698322baafdd230e [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 "sdio-controller-device.h"
#include <fbl/auto_lock.h>
#include <hw/sdio.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <zxtest/zxtest.h>
#include "mock-sdmmc-device.h"
namespace sdmmc {
class Bind : public fake_ddk::Bind {
public:
int total_children() const { return total_children_; }
zx_status_t DeviceAdd(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) override {
if (parent == fake_ddk::kFakeParent) {
*out = fake_ddk::kFakeDevice;
add_called_ = true;
} else if (parent == fake_ddk::kFakeDevice) {
*out = kFakeChild;
children_++;
total_children_++;
} else {
*out = kUnknownDevice;
bad_parent_ = false;
}
return ZX_OK;
}
zx_status_t DeviceRemove(zx_device_t* device) override {
if (device == fake_ddk::kFakeDevice) {
remove_called_ = true;
} else if (device == kFakeChild) {
// Check that all children are removed before the parent is removed.
if (!remove_called_) {
children_--;
}
} else {
bad_device_ = true;
}
return ZX_OK;
}
void Ok() {
EXPECT_EQ(children_, 0);
EXPECT_TRUE(add_called_);
EXPECT_TRUE(remove_called_);
EXPECT_FALSE(bad_parent_);
EXPECT_FALSE(bad_device_);
}
private:
zx_device_t* kFakeChild = reinterpret_cast<zx_device_t*>(0x1234);
zx_device_t* kUnknownDevice = reinterpret_cast<zx_device_t*>(0x5678);
int total_children_ = 0;
int children_ = 0;
bool bad_parent_ = false;
bool bad_device_ = false;
bool add_called_ = false;
bool remove_called_ = false;
};
class SdioControllerDeviceTest : public SdioControllerDevice {
public:
SdioControllerDeviceTest(MockSdmmcDevice* mock_sdmmc, const sdio_device_hw_info_t& hw_info)
: SdioControllerDevice(fake_ddk::kFakeParent, SdmmcDevice({}, {})), mock_sdmmc_(mock_sdmmc) {
hw_info_ = hw_info;
}
void SetSdioFunctionInfo(uint8_t fn_idx, const SdioFunction& info) {
fbl::AutoLock lock(&lock_);
funcs_[fn_idx] = info;
}
auto& mock_SdioDoRwByte() { return mock_sdio_do_rw_byte_; }
void VerifyAll() { mock_sdio_do_rw_byte_.VerifyAndClear(); }
zx_status_t SdioDoRwByteLocked(bool write, uint8_t fn_idx, uint32_t addr, uint8_t write_byte,
uint8_t* out_read_byte) override TA_REQ(lock_) {
if (mock_sdio_do_rw_byte_.HasExpectations()) {
std::tuple<zx_status_t, uint8_t> ret =
mock_sdio_do_rw_byte_.Call(write, fn_idx, addr, write_byte);
if (out_read_byte != nullptr) {
*out_read_byte = std::get<1>(ret);
}
return std::get<0>(ret);
} else {
return SdioControllerDevice::SdioDoRwByteLocked(write, fn_idx, addr, write_byte,
out_read_byte);
}
}
// Registers an interrupt with the SDIO controller for the given function. The interrupt is
// managed by this object.
zx_status_t RegisterInterrupt(uint8_t fn_idx) {
zx_status_t status = ZX_OK;
if (interrupts_[fn_idx].is_valid()) {
return status;
} else if (!port_.is_valid()) {
if ((status = zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port_)) != ZX_OK) {
return status;
}
}
if ((status = SdioGetInBandIntr(fn_idx, &interrupts_[fn_idx])) != ZX_OK) {
return status;
}
return interrupts_[fn_idx].bind(port_, fn_idx, 0);
}
// Wait for count interrupts to be received for any combination of functions. Upon return the
// bits in mask represent the different functions which had interrupts triggered.
zx_status_t WaitForInterrupts(uint32_t count, uint8_t* mask) {
*mask = 0;
for (uint32_t i = 0; i < count; i++) {
zx_port_packet_t packet;
zx_status_t status = port_.wait(zx::time::infinite(), &packet);
if (status != ZX_OK) {
return status;
}
*mask |= static_cast<uint8_t>(1 << packet.key);
interrupts_[packet.key].ack();
}
return ZX_OK;
}
zx_status_t ProcessCccrLocked() TA_EXCL(lock_) {
fbl::AutoLock lock(&lock_);
return ProcessCccr();
}
zx_status_t ProcessCisLocked(uint8_t fn_idx) TA_EXCL(lock_) {
fbl::AutoLock lock(&lock_);
return ProcessCis(fn_idx);
}
zx_status_t ProcessFbrLocked(uint8_t fn_idx) TA_EXCL(lock_) {
fbl::AutoLock lock(&lock_);
return ProcessFbr(fn_idx);
}
const SdioFunction& func(uint8_t func) { return funcs_[func]; }
const sdio_device_hw_info_t& hw_info() { return hw_info_; }
private:
SdmmcDevice& sdmmc() override { return *mock_sdmmc_; }
MockSdmmcDevice* mock_sdmmc_;
mock_function::MockFunction<std::tuple<zx_status_t, uint8_t>, bool, uint8_t, uint32_t, uint8_t>
mock_sdio_do_rw_byte_;
zx::port port_;
zx::interrupt interrupts_[SDIO_MAX_FUNCS];
};
TEST(SdioControllerDeviceTest, MultiplexInterrupts) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
ASSERT_OK(dut.StartSdioIrqThread());
ASSERT_OK(dut.RegisterInterrupt(1));
ASSERT_OK(dut.RegisterInterrupt(2));
ASSERT_OK(dut.RegisterInterrupt(4));
ASSERT_OK(dut.RegisterInterrupt(7));
dut.mock_SdioDoRwByte()
.ExpectCall({ZX_OK, 0b0000'0010}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b1111'1110}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b1010'0010}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0011'0110}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0);
uint8_t mask;
dut.InBandInterruptCallback();
EXPECT_OK(dut.WaitForInterrupts(1, &mask));
EXPECT_EQ(mask, 0b0000'0010);
dut.InBandInterruptCallback();
EXPECT_OK(dut.WaitForInterrupts(4, &mask));
EXPECT_EQ(mask, 0b1001'0110);
dut.InBandInterruptCallback();
EXPECT_OK(dut.WaitForInterrupts(2, &mask));
EXPECT_EQ(mask, 0b1000'0010);
dut.InBandInterruptCallback();
EXPECT_OK(dut.WaitForInterrupts(3, &mask));
EXPECT_EQ(mask, 0b0001'0110);
dut.StopSdioIrqThread();
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, SdioDoRwTxn) {
MockSdmmcDevice mock_sdmmc(
{.caps = 0, .max_transfer_size = 16, .max_transfer_size_non_dma = 16, .prefs = 0});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.SetSdioFunctionInfo(
3, {.hw_info = {}, .cur_blk_size = 8, .enabled = true, .intr_enabled = false});
mock_sdmmc.mock_SdioIoRwExtended()
.ExpectCall(ZX_OK, 0, true, 3, 0xabcd0008, false, 1, 8, 16)
.ExpectCall(ZX_OK, 0, true, 3, 0xabcd0008, false, 1, 8, 24)
.ExpectCall(ZX_OK, 0, true, 3, 0xabcd0008, false, 1, 8, 32)
.ExpectCall(ZX_OK, 0, true, 3, 0xabcd0008, false, 1, 8, 40)
.ExpectCall(ZX_OK, 0, true, 3, 0xabcd0008, false, 1, 4, 48)
.ExpectCall(ZX_OK, 0, false, 3, 0x12340008, true, 1, 8, 16)
.ExpectCall(ZX_OK, 0, false, 3, 0x12340010, true, 1, 8, 24)
.ExpectCall(ZX_OK, 0, false, 3, 0x12340018, true, 1, 8, 32)
.ExpectCall(ZX_OK, 0, false, 3, 0x12340020, true, 1, 8, 40)
.ExpectCall(ZX_OK, 0, false, 3, 0x12340028, true, 1, 4, 48);
sdio_rw_txn_t txn = {.addr = 0xabcd0008,
.data_size = 36,
.incr = false,
.write = true,
.use_dma = false,
.dma_vmo = ZX_HANDLE_INVALID,
.virt_buffer = nullptr,
.virt_size = 0,
.buf_offset = 16};
EXPECT_OK(dut.SdioDoRwTxn(3, &txn));
txn = {.addr = 0x12340008,
.data_size = 36,
.incr = true,
.write = false,
.use_dma = false,
.dma_vmo = ZX_HANDLE_INVALID,
.virt_buffer = nullptr,
.virt_size = 0,
.buf_offset = 16};
EXPECT_OK(dut.SdioDoRwTxn(3, &txn));
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, SdioDoRwTxnMultiBlock) {
MockSdmmcDevice mock_sdmmc(
{.caps = 0, .max_transfer_size = 32, .max_transfer_size_non_dma = 32, .prefs = 0});
SdioControllerDeviceTest dut(
&mock_sdmmc, {.num_funcs = 0, .sdio_vsn = 0, .cccr_vsn = 0, .caps = SDIO_CARD_MULTI_BLOCK});
dut.SetSdioFunctionInfo(
7, {.hw_info = {}, .cur_blk_size = 8, .enabled = true, .intr_enabled = false});
mock_sdmmc.mock_SdioIoRwExtended()
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, false, 7, 0xabcd0008, false, 4, 8, 64)
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, false, 7, 0xabcd0008, false, 4, 8, 96)
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, false, 7, 0xabcd0008, false, 1, 4, 128)
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, true, 7, 0x12340008, true, 4, 8, 64)
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, true, 7, 0x12340028, true, 4, 8, 96)
.ExpectCall(ZX_OK, SDIO_CARD_MULTI_BLOCK, true, 7, 0x12340048, true, 1, 4, 128);
sdio_rw_txn_t txn = {.addr = 0xabcd0008,
.data_size = 68,
.incr = false,
.write = false,
.use_dma = false,
.dma_vmo = ZX_HANDLE_INVALID,
.virt_buffer = nullptr,
.virt_size = 0,
.buf_offset = 64};
EXPECT_OK(dut.SdioDoRwTxn(7, &txn));
txn = {.addr = 0x12340008,
.data_size = 68,
.incr = true,
.write = true,
.use_dma = false,
.dma_vmo = ZX_HANDLE_INVALID,
.virt_buffer = nullptr,
.virt_size = 0,
.buf_offset = 64};
EXPECT_OK(dut.SdioDoRwTxn(7, &txn));
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, DdkLifecycle) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc,
{.num_funcs = 5, .sdio_vsn = 0, .cccr_vsn = 0, .caps = 0});
Bind ddk;
EXPECT_OK(dut.AddDevice());
dut.DdkUnbind();
dut.StopSdioIrqThread();
ddk.Ok();
EXPECT_EQ(ddk.total_children(), 4);
}
TEST(SdioControllerDeviceTest, SdioIntrPending) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
.ExpectCall({ZX_OK, 0b0011'0010}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0010'0010}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b1000'0000}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0000'0000}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0000'1110}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0000'1110}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0)
.ExpectCall({ZX_OK, 0b0000'1110}, false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0);
bool pending;
EXPECT_OK(dut.SdioIntrPending(4, &pending));
EXPECT_TRUE(pending);
EXPECT_OK(dut.SdioIntrPending(4, &pending));
EXPECT_FALSE(pending);
EXPECT_OK(dut.SdioIntrPending(7, &pending));
EXPECT_TRUE(pending);
EXPECT_OK(dut.SdioIntrPending(7, &pending));
EXPECT_FALSE(pending);
EXPECT_OK(dut.SdioIntrPending(1, &pending));
EXPECT_TRUE(pending);
EXPECT_OK(dut.SdioIntrPending(2, &pending));
EXPECT_TRUE(pending);
EXPECT_OK(dut.SdioIntrPending(3, &pending));
EXPECT_TRUE(pending);
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, EnableDisableFnIntr) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
.ExpectCall({ZX_OK, 0b0000'0000}, false, 0, 0x04, 0b0000'0000)
.ExpectCall({ZX_OK, 0b0000'0000}, true, 0, 0x04, 0b0001'0001)
.ExpectCall({ZX_OK, 0b0001'0001}, false, 0, 0x04, 0b0000'0000)
.ExpectCall({ZX_OK, 0b0000'0000}, true, 0, 0x04, 0b1001'0001)
.ExpectCall({ZX_OK, 0b1001'0001}, false, 0, 0x04, 0b0000'0000)
.ExpectCall({ZX_OK, 0b0000'0000}, true, 0, 0x04, 0b1000'0001)
.ExpectCall({ZX_OK, 0b1000'0001}, false, 0, 0x04, 0b0000'0000)
.ExpectCall({ZX_OK, 0b0000'0000}, true, 0, 0x04, 0b0000'0000);
EXPECT_OK(dut.SdioEnableFnIntr(4));
EXPECT_OK(dut.SdioEnableFnIntr(7));
EXPECT_OK(dut.SdioEnableFnIntr(4));
EXPECT_OK(dut.SdioDisableFnIntr(4));
EXPECT_OK(dut.SdioDisableFnIntr(7));
EXPECT_NOT_OK(dut.SdioDisableFnIntr(7));
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, ProcessCccr) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
// CCCR/SDIO revision.
.ExpectCall({ZX_OK, 0x43}, false, 0, 0x00, 0)
// Card compatibility.
.ExpectCall({ZX_OK, 0xc2}, false, 0, 0x08, 0)
// Bus speed select.
.ExpectCall({ZX_OK, 0xa9}, false, 0, 0x13, 0)
// UHS-I support.
.ExpectCall({ZX_OK, 0x3f}, false, 0, 0x14, 0)
// Driver strength.
.ExpectCall({ZX_OK, 0xb7}, false, 0, 0x15, 0)
.ExpectCall({ZX_OK, 0x43}, false, 0, 0x00, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x08, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x13, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x14, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x15, 0)
.ExpectCall({ZX_OK, 0x41}, false, 0, 0x00, 0)
.ExpectCall({ZX_OK, 0x33}, false, 0, 0x00, 0);
EXPECT_OK(dut.ProcessCccrLocked());
EXPECT_EQ(dut.hw_info().caps,
SDIO_CARD_MULTI_BLOCK | SDIO_CARD_LOW_SPEED | SDIO_CARD_FOUR_BIT_BUS |
SDIO_CARD_HIGH_SPEED | SDIO_CARD_UHS_SDR50 | SDIO_CARD_UHS_SDR104 |
SDIO_CARD_UHS_DDR50 | SDIO_CARD_TYPE_A | SDIO_CARD_TYPE_B | SDIO_CARD_TYPE_D);
EXPECT_OK(dut.ProcessCccrLocked());
EXPECT_EQ(dut.hw_info().caps, 0);
EXPECT_NOT_OK(dut.ProcessCccrLocked());
EXPECT_NOT_OK(dut.ProcessCccrLocked());
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, ProcessCis) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
// CIS pointer.
.ExpectCall({ZX_OK, 0xa2}, false, 0, 0x00'05'09, 0)
.ExpectCall({ZX_OK, 0xc2}, false, 0, 0x00'05'0a, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'05'0b, 0)
// Manufacturer ID tuple.
.ExpectCall({ZX_OK, 0x20}, false, 0, 0x00'c2'a2, 0)
// Manufacturer ID tuple size.
.ExpectCall({ZX_OK, 0x04}, false, 0, 0x00'c2'a3, 0)
// Manufacturer code.
.ExpectCall({ZX_OK, 0x01}, false, 0, 0x00'c2'a4, 0)
.ExpectCall({ZX_OK, 0xc0}, false, 0, 0x00'c2'a5, 0)
// Manufacturer information (part number/revision).
.ExpectCall({ZX_OK, 0xce}, false, 0, 0x00'c2'a6, 0)
.ExpectCall({ZX_OK, 0xfa}, false, 0, 0x00'c2'a7, 0)
// Null tuple.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'a8, 0)
// Function extensions tuple.
.ExpectCall({ZX_OK, 0x22}, false, 0, 0x00'c2'a9, 0)
// Function extensions tuple size.
.ExpectCall({ZX_OK, 0x2a}, false, 0, 0x00'c2'aa, 0)
// Type of extended data.
.ExpectCall({ZX_OK, 0x01}, false, 0, 0x00'c2'ab, 0)
// Stuff we don't use.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ac, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ad, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ae, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'af, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b0, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b1, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b2, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b3, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b4, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b5, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b6, 0)
// Function block size.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b7, 0)
.ExpectCall({ZX_OK, 0x01}, false, 0, 0x00'c2'b8, 0)
// More stuff we don't use.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'b9, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ba, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'bb, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'bc, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'bd, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'be, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'bf, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c0, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c1, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c2, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c3, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c4, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c5, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c6, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c7, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c8, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'c9, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ca, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'cb, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'cc, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'cd, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'ce, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'cf, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'd0, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'd1, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'd2, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'd3, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x00'c2'd4, 0)
// End-of-chain tuple.
.ExpectCall({ZX_OK, 0xff}, false, 0, 0x00'c2'd5, 0);
EXPECT_OK(dut.ProcessCisLocked(5));
EXPECT_EQ(dut.func(5).hw_info.max_blk_size, 256);
EXPECT_EQ(dut.func(5).hw_info.manufacturer_id, 0xc001);
EXPECT_EQ(dut.func(5).hw_info.product_id, 0xface);
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, ProcessCisFunction0) {
MockSdmmcDevice mock_sdmmc(
{.caps = 0, .max_transfer_size = 1024, .max_transfer_size_non_dma = 1024, .prefs = 0});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
// CIS pointer.
.ExpectCall({ZX_OK, 0xf5}, false, 0, 0x00'00'09, 0)
.ExpectCall({ZX_OK, 0x61}, false, 0, 0x00'00'0a, 0)
.ExpectCall({ZX_OK, 0x01}, false, 0, 0x00'00'0b, 0)
// Function extensions tuple.
.ExpectCall({ZX_OK, 0x22}, false, 0, 0x01'61'f5, 0)
// Function extensions tuple size.
.ExpectCall({ZX_OK, 0x04}, false, 0, 0x01'61'f6, 0)
// Type of extended data.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x01'61'f7, 0)
// Function 0 block size.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x01'61'f8, 0)
.ExpectCall({ZX_OK, 0x02}, false, 0, 0x01'61'f9, 0)
// Max transfer speed.
.ExpectCall({ZX_OK, 0x32}, false, 0, 0x01'61'fa, 0)
// Null tuple.
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x01'61'fb, 0)
// Manufacturer ID tuple.
.ExpectCall({ZX_OK, 0x20}, false, 0, 0x01'61'fc, 0)
// Manufacturer ID tuple size.
.ExpectCall({ZX_OK, 0x04}, false, 0, 0x01'61'fd, 0)
// Manufacturer code.
.ExpectCall({ZX_OK, 0xef}, false, 0, 0x01'61'fe, 0)
.ExpectCall({ZX_OK, 0xbe}, false, 0, 0x01'61'ff, 0)
// Manufacturer information (part number/revision).
.ExpectCall({ZX_OK, 0xfe}, false, 0, 0x01'62'00, 0)
.ExpectCall({ZX_OK, 0xca}, false, 0, 0x01'62'01, 0)
// End-of-chain tuple.
.ExpectCall({ZX_OK, 0xff}, false, 0, 0x01'62'02, 0);
EXPECT_OK(dut.ProcessCisLocked(0));
EXPECT_EQ(dut.func(0).hw_info.max_blk_size, 512);
EXPECT_EQ(dut.func(0).hw_info.max_tran_speed, 25000);
EXPECT_EQ(dut.func(0).hw_info.manufacturer_id, 0xbeef);
EXPECT_EQ(dut.func(0).hw_info.product_id, 0xcafe);
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, ProcessFbr) {
MockSdmmcDevice mock_sdmmc({});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.mock_SdioDoRwByte()
.ExpectCall({ZX_OK, 0x83}, false, 0, 0x100, 0)
.ExpectCall({ZX_OK, 0x00}, false, 0, 0x500, 0)
.ExpectCall({ZX_OK, 0x4e}, false, 0, 0x700, 0)
.ExpectCall({ZX_OK, 0xcf}, false, 0, 0x600, 0)
.ExpectCall({ZX_OK, 0xab}, false, 0, 0x601, 0);
EXPECT_OK(dut.ProcessFbrLocked(1));
EXPECT_EQ(dut.func(1).hw_info.fn_intf_code, 0x03);
EXPECT_OK(dut.ProcessFbrLocked(5));
EXPECT_EQ(dut.func(5).hw_info.fn_intf_code, 0x00);
EXPECT_OK(dut.ProcessFbrLocked(7));
EXPECT_EQ(dut.func(7).hw_info.fn_intf_code, 0x0e);
EXPECT_OK(dut.ProcessFbrLocked(6));
EXPECT_EQ(dut.func(6).hw_info.fn_intf_code, 0xab);
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
TEST(SdioControllerDeviceTest, SmallHostTransferSize) {
MockSdmmcDevice mock_sdmmc({
.caps = 0,
.max_transfer_size = 32,
.max_transfer_size_non_dma = 64,
.prefs = 0,
});
SdioControllerDeviceTest dut(&mock_sdmmc, {});
dut.SetSdioFunctionInfo(3, {
.hw_info = {},
.cur_blk_size = 128,
.enabled = true,
.intr_enabled = false,
});
mock_sdmmc.mock_SdioIoRwExtended().ExpectCall(ZX_OK, 0, true, 3, 0, false, 1, 64, 0);
sdio_rw_txn_t txn = {
.addr = 0,
.data_size = 64,
.incr = false,
.write = true,
.use_dma = true,
.dma_vmo = ZX_HANDLE_INVALID,
.virt_buffer = nullptr,
.virt_size = 0,
.buf_offset = 0,
};
EXPECT_NOT_OK(dut.SdioDoRwTxn(3, &txn));
txn.use_dma = false;
EXPECT_OK(dut.SdioDoRwTxn(3, &txn));
txn.data_size = 128;
EXPECT_NOT_OK(dut.SdioDoRwTxn(3, &txn));
txn.use_dma = true;
EXPECT_NOT_OK(dut.SdioDoRwTxn(3, &txn));
dut.VerifyAll();
mock_sdmmc.VerifyAll();
}
} // namespace sdmmc