From d399ee5dbf78848350c23e48a9249e307fdaf63b Mon Sep 17 00:00:00 2001
From: Braden Kell <bradenkell@google.com>
Date: Wed, 1 May 2019 14:42:09 +0000
Subject: [PATCH] [zircon][sdio] Add function 0 abstractions to SDIO protocol
 ops

Function 0 is shared between all drivers using a particular card.
Currently drivers read and write to function 0 directly, but eventually
function 0 will only be accessible through one of the methods added
here.

Bug: ZX-3879
Test: runtests -t sdmmc-test
Change-Id: If1dfff0eba05a4b1d6ff255b4800ab4843b7ea78
---
 .../system/banjo/ddk.protocol.sdio/sdio.banjo |  7 ++++
 .../sdmmc/sdio-controller-device-test.cpp     | 37 +++++++++++++++++++
 .../block/sdmmc/sdio-controller-device.cpp    | 37 +++++++++++++++++++
 .../dev/block/sdmmc/sdio-controller-device.h  |  4 ++
 .../dev/block/sdmmc/sdio-function-device.cpp  | 14 +++++++
 .../dev/block/sdmmc/sdio-function-device.h    |  4 ++
 .../include/lib/mock-sdio/mock-sdio.h         | 13 +++++++
 7 files changed, 116 insertions(+)

diff --git a/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo b/zircon/system/banjo/ddk.protocol.sdio/sdio.banjo
index 900bcd8bb54..7c9ae4ca0ef 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 1b44a756b7d..6522e453c80 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 0b3c9dd34b5..e669c695317 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 69839e2d570..94b42d05886 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 36c435232bf..4a487d39000 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 e16776283a5..2541e52dde9 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 bbb2cca2c90..0ed643b63ca 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;
-- 
GitLab