diff --git a/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo b/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo index 900bcd8bb54c387bedb50273564667ee3395740f..7c9ae4ca0ef6ddc67efd6f7dfdbb410b65c4c4cd 100644 --- a/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo +++ b/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo @@ -81,4 +81,11 @@ protocol Sdio { DoRwTxn(uint8 fn_idx, SdioRwTxn? txn) -> (zx.status s); DoRwByte(bool write, uint8 fn_idx, uint32 addr, uint8 write_byte) -> (zx.status s, uint8 read_byte); GetInBandIntr(uint8 fn_idx) -> (zx.status s, handle<interrupt> irq); + /// The following functions access the card common control registers (CCCR) on function 0. + /// Aborts an I/O operation occurring on the specified function. + IoAbort(uint8 fn_idx) -> (zx.status s); + /// Returns true if an interrupt is pending for function fn_idx, false otherwise. + IntrPending(uint8 fn_idx) -> (zx.status s, bool pending); + /// Reads or writes to a vendor CCCR register. addr must be in [0xF0, 0xFF]. + DoVendorControlRwByte(bool write, uint8 addr, uint8 write_byte) -> (zx.status s, uint8 read_byte); }; diff --git a/zircon/system/dev/block/sdmmc/sdio-controller-device-test.cpp b/zircon/system/dev/block/sdmmc/sdio-controller-device-test.cpp index 1b44a756b7d23f0db2f65728bd94cb937bd6694c..6522e453c80576d601a1b97abdb5ad0b1f039b5c 100644 --- a/zircon/system/dev/block/sdmmc/sdio-controller-device-test.cpp +++ b/zircon/system/dev/block/sdmmc/sdio-controller-device-test.cpp @@ -329,4 +329,41 @@ TEST(SdioControllerDeviceTest, DdkLifecycle) { 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); +} + } // namespace sdmmc diff --git a/zircon/system/dev/block/sdmmc/sdio-controller-device.cpp b/zircon/system/dev/block/sdmmc/sdio-controller-device.cpp index 0b3c9dd34b5b17a38d5f47131cd550bb35eebecb..e669c69531702957d785c826c61c9d034b937e89 100644 --- a/zircon/system/dev/block/sdmmc/sdio-controller-device.cpp +++ b/zircon/system/dev/block/sdmmc/sdio-controller-device.cpp @@ -24,6 +24,8 @@ namespace { +constexpr uint8_t kCccrVendorAddressMin = 0xf0; + constexpr uint32_t kBcmManufacturerId = 0x02d0; uint32_t SdioReadTupleBody(const uint8_t* tuple_body, size_t start, size_t numbytes) { @@ -630,6 +632,41 @@ int SdioControllerDevice::SdioIrqThread() { return thrd_success; } +zx_status_t SdioControllerDevice::SdioIoAbort(uint8_t fn_idx) { + if (!SdioFnIdxValid(fn_idx) || fn_idx == 0) { + return ZX_ERR_INVALID_ARGS; + } + + return SdioDoRwByte(true, 0, SDIO_CIA_CCCR_ASx_ABORT_SEL_CR_ADDR, fn_idx, nullptr); +} + +zx_status_t SdioControllerDevice::SdioIntrPending(uint8_t fn_idx, bool* out_pending) { + if (!SdioFnIdxValid(fn_idx) || fn_idx == 0) { + return ZX_ERR_INVALID_ARGS; + } + + uint8_t intr_byte; + zx_status_t st = SdioDoRwByte(false, 0, SDIO_CIA_CCCR_INTx_INTR_PEN_ADDR, 0, &intr_byte); + if (st != ZX_OK) { + zxlogf(ERROR, "sdio_intr_pending: Failed reading intr pending reg. status: %d\n", st); + return st; + } + + *out_pending = intr_byte & (1 << fn_idx); + return ZX_OK; +} + +zx_status_t SdioControllerDevice::SdioDoVendorControlRwByte(bool write, uint8_t addr, + uint8_t write_byte, + uint8_t* out_read_byte) { + // The vendor area of the CCCR is 0xf0 - 0xff. + if (addr < kCccrVendorAddressMin) { + return ZX_ERR_OUT_OF_RANGE; + } + + return SdioDoRwByte(write, 0, addr, write_byte, out_read_byte); +} + zx_status_t SdioControllerDevice::SdioReset() { zx_status_t st = ZX_OK; uint8_t abort_byte; diff --git a/zircon/system/dev/block/sdmmc/sdio-controller-device.h b/zircon/system/dev/block/sdmmc/sdio-controller-device.h index 69839e2d57090e61ae0576dc2494543bf487990c..94b42d058865a2f53e3e2c7103301bc5d0ef8126 100644 --- a/zircon/system/dev/block/sdmmc/sdio-controller-device.h +++ b/zircon/system/dev/block/sdmmc/sdio-controller-device.h @@ -61,6 +61,10 @@ public: virtual zx_status_t SdioDoRwByte(bool write, uint8_t fn_idx, uint32_t addr, uint8_t write_byte, uint8_t* out_read_byte); zx_status_t SdioGetInBandIntr(uint8_t fn_idx, zx::interrupt* out_irq); + zx_status_t SdioIoAbort(uint8_t fn_idx); + zx_status_t SdioIntrPending(uint8_t fn_idx, bool* out_pending); + zx_status_t SdioDoVendorControlRwByte(bool write, uint8_t addr, uint8_t write_byte, + uint8_t* out_read_byte); void InBandInterruptCallback(); diff --git a/zircon/system/dev/block/sdmmc/sdio-function-device.cpp b/zircon/system/dev/block/sdmmc/sdio-function-device.cpp index 36c435232bf14b9420f151fb5fd9ad7aaf287833..4a487d39000014bffaca5b298ea50c585f2d4d10 100644 --- a/zircon/system/dev/block/sdmmc/sdio-function-device.cpp +++ b/zircon/system/dev/block/sdmmc/sdio-function-device.cpp @@ -98,4 +98,18 @@ zx_status_t SdioFunctionDevice::SdioGetInBandIntr(uint8_t fn_idx, zx::interrupt* return sdio_parent_->SdioGetInBandIntr(fn_idx, out_irq); } +zx_status_t SdioFunctionDevice::SdioIoAbort(uint8_t fn_idx) { + return sdio_parent_->SdioIoAbort(fn_idx); +} + +zx_status_t SdioFunctionDevice::SdioIntrPending(uint8_t fn_idx, bool* out_pending) { + return sdio_parent_->SdioIntrPending(fn_idx, out_pending); +} + +zx_status_t SdioFunctionDevice::SdioDoVendorControlRwByte(bool write, uint8_t addr, + uint8_t write_byte, + uint8_t* out_read_byte) { + return sdio_parent_->SdioDoVendorControlRwByte(write, addr, write_byte, out_read_byte); +} + } // namespace sdmmc diff --git a/zircon/system/dev/block/sdmmc/sdio-function-device.h b/zircon/system/dev/block/sdmmc/sdio-function-device.h index e16776283a56418892d59ffde3945c0b14b48c40..2541e52dde98717a8253ac330b691610a4706c6e 100644 --- a/zircon/system/dev/block/sdmmc/sdio-function-device.h +++ b/zircon/system/dev/block/sdmmc/sdio-function-device.h @@ -44,6 +44,10 @@ public: zx_status_t SdioDoRwByte(bool write, uint8_t fn_idx, uint32_t addr, uint8_t write_byte, uint8_t* out_read_byte); zx_status_t SdioGetInBandIntr(uint8_t fn_idx, zx::interrupt* out_irq); + zx_status_t SdioIoAbort(uint8_t fn_idx); + zx_status_t SdioIntrPending(uint8_t fn_idx, bool* out_pending); + zx_status_t SdioDoVendorControlRwByte(bool write, uint8_t addr, uint8_t write_byte, + uint8_t* out_read_byte); private: std::atomic<bool> dead_; diff --git a/zircon/system/dev/lib/mock-sdio/include/lib/mock-sdio/mock-sdio.h b/zircon/system/dev/lib/mock-sdio/include/lib/mock-sdio/mock-sdio.h index bbb2cca2c90dfdb83bdd185ae1aafc051cdc3815..0ed643b63ca7bca302b868de5f6aef6838c64898 100644 --- a/zircon/system/dev/lib/mock-sdio/include/lib/mock-sdio/mock-sdio.h +++ b/zircon/system/dev/lib/mock-sdio/include/lib/mock-sdio/mock-sdio.h @@ -168,6 +168,19 @@ public: return ZX_OK; } + zx_status_t SdioIoAbort(uint8_t fn_idx) { + return ZX_ERR_NOT_SUPPORTED; + } + + zx_status_t SdioIntrPending(uint8_t fn_idx, bool* out_pending) { + return ZX_ERR_NOT_SUPPORTED; + } + + zx_status_t SdioDoVendorControlRwByte(bool write, uint8_t addr, uint8_t write_byte, + uint8_t* out_read_byte) { + return ZX_ERR_NOT_SUPPORTED; + } + private: struct SdioRwExpectation { uint8_t fn_idx;