Reland "[pci-sdhci] Migrate pci-sdhci driver from C to C++"
This is a reland of commit 78af407dc0385fdde2fe8028bb414c9949996c27
The original CL was wrong because the condition `mmio_.has_value()`
should have been `!mmio_.has_value()`.
Original change's description:
> [pci-sdhci] Migrate pci-sdhci driver from C to C++
>
> - Convert code to C++
> - Use typed objects instead of zx_handle_t
> - Use C++ MMIO and PCI libraries
>
> Fixed: 91453
> Test: Booted test device with new driver
> Change-Id: Ic56f193ea5dd95bb01f9fb217364cb821320a944
> Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/682551
> Reviewed-by: Suraj Malhotra <surajmalhotra@google.com>
> Commit-Queue: Ian Fisher <iafisher@google.com>
> Reviewed-by: David Gilhooley <dgilhooley@google.com>
Change-Id: I925af14db15630da21f5b8900ebc2193efbe55a9
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/692590
Reviewed-by: David Gilhooley <dgilhooley@google.com>
Commit-Queue: Ian Fisher <iafisher@google.com>
diff --git a/src/devices/block/drivers/pci-sdhci/BUILD.gn b/src/devices/block/drivers/pci-sdhci/BUILD.gn
index d03834d..c4cdd2f 100644
--- a/src/devices/block/drivers/pci-sdhci/BUILD.gn
+++ b/src/devices/block/drivers/pci-sdhci/BUILD.gn
@@ -21,7 +21,7 @@
"//build/config:all_source",
"//build/config/fuchsia:enable_zircon_asserts",
]
- sources = [ "pci-sdhci.c" ]
+ sources = [ "pci-sdhci.cc" ]
deps = [
":pci-sdhci-bind_header",
"//sdk/banjo/fuchsia.hardware.block:fuchsia.hardware.block_banjo_cpp",
diff --git a/src/devices/block/drivers/pci-sdhci/pci-sdhci.c b/src/devices/block/drivers/pci-sdhci/pci-sdhci.c
deleted file mode 100644
index a9c0613..0000000
--- a/src/devices/block/drivers/pci-sdhci/pci-sdhci.c
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2017 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 <fuchsia/hardware/pci/c/banjo.h>
-#include <fuchsia/hardware/sdhci/c/banjo.h>
-#include <inttypes.h>
-#include <lib/ddk/debug.h>
-#include <lib/ddk/device.h>
-#include <lib/ddk/driver.h>
-#include <lib/device-protocol/pci.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <threads.h>
-#include <unistd.h>
-#include <zircon/status.h>
-
-#include "src/devices/block/drivers/pci-sdhci/pci-sdhci-bind.h"
-
-#define HOST_CONTROL1_OFFSET 0x28
-#define SDHCI_EMMC_HW_RESET (1 << 12)
-
-typedef struct pci_sdhci_device {
- zx_device_t* zxdev;
- pci_protocol_t pci;
-
- MMIO_PTR volatile uint8_t* regs;
- mmio_buffer_t mmio;
- zx_handle_t bti_handle;
-} pci_sdhci_device_t;
-
-static zx_status_t pci_sdhci_get_interrupt(void* ctx, zx_handle_t* handle_out) {
- pci_sdhci_device_t* dev = ctx;
- // select irq mode
- pci_interrupt_mode_t mode = PCI_INTERRUPT_MODE_DISABLED;
- zx_status_t status = pci_configure_interrupt_mode(&dev->pci, 1, &mode);
- if (status != ZX_OK) {
- zxlogf(ERROR, "error setting irq mode: %s", zx_status_get_string(status));
- return status;
- }
-
- // get irq handle
- status = pci_map_interrupt(&dev->pci, 0, handle_out);
- if (status != ZX_OK) {
- zxlogf(ERROR, "error getting irq handle: %s", zx_status_get_string(status));
- }
- return status;
-}
-
-static zx_status_t pci_sdhci_get_mmio(void* ctx, zx_handle_t* out, zx_off_t* out_offset) {
- pci_sdhci_device_t* dev = ctx;
- if (dev->regs == NULL) {
- zx_status_t status =
- pci_map_bar_buffer(&dev->pci, 0u, ZX_CACHE_POLICY_UNCACHED_DEVICE, &dev->mmio);
- if (status != ZX_OK) {
- zxlogf(ERROR, "error mapping register window: %s", zx_status_get_string(status));
- return status;
- }
- dev->regs = dev->mmio.vaddr;
- }
- *out_offset = dev->mmio.offset;
- return zx_handle_duplicate(dev->mmio.vmo, ZX_RIGHT_SAME_RIGHTS, out);
-}
-
-static zx_status_t pci_sdhci_get_bti(void* ctx, uint32_t index, zx_handle_t* out_handle) {
- pci_sdhci_device_t* dev = ctx;
- if (dev->bti_handle == ZX_HANDLE_INVALID) {
- zx_status_t st = pci_get_bti(&dev->pci, index, &dev->bti_handle);
- if (st != ZX_OK) {
- return st;
- }
- }
- return zx_handle_duplicate(dev->bti_handle, ZX_RIGHT_SAME_RIGHTS, out_handle);
-}
-
-static uint32_t pci_sdhci_get_base_clock(void* ctx) { return 0; }
-
-static uint64_t pci_sdhci_get_quirks(void* ctx, uint64_t* out_dma_boundary_alignment) {
- *out_dma_boundary_alignment = 0;
- return SDHCI_QUIRK_STRIP_RESPONSE_CRC_PRESERVE_ORDER;
-}
-
-static void pci_sdhci_hw_reset(void* ctx) {
- pci_sdhci_device_t* dev = ctx;
- if (!dev->regs) {
- return;
- }
- MMIO_PTR volatile uint32_t* const ctrl1 =
- (MMIO_PTR volatile uint32_t*)(dev->regs + HOST_CONTROL1_OFFSET);
- uint32_t val = MmioRead32(ctrl1);
- val |= SDHCI_EMMC_HW_RESET;
- MmioWrite32(val, ctrl1);
- // minimum is 1us but wait 9us for good measure
- zx_nanosleep(zx_deadline_after(ZX_USEC(9)));
- val &= ~SDHCI_EMMC_HW_RESET;
- MmioWrite32(val, ctrl1);
- // minimum is 200us but wait 300us for good measure
- zx_nanosleep(zx_deadline_after(ZX_USEC(300)));
-}
-
-static sdhci_protocol_ops_t pci_sdhci_sdhci_proto = {
- .get_interrupt = pci_sdhci_get_interrupt,
- .get_mmio = pci_sdhci_get_mmio,
- .get_bti = pci_sdhci_get_bti,
- .get_base_clock = pci_sdhci_get_base_clock,
- .get_quirks = pci_sdhci_get_quirks,
- .hw_reset = pci_sdhci_hw_reset,
-};
-
-static void pci_sdhci_unbind(void* ctx) {
- pci_sdhci_device_t* dev = ctx;
- device_unbind_reply(dev->zxdev);
-}
-
-static void pci_sdhci_release(void* ctx) {
- pci_sdhci_device_t* dev = ctx;
- mmio_buffer_release(&dev->mmio);
- zx_handle_close(dev->bti_handle);
- free(dev);
-}
-
-static zx_protocol_device_t pci_sdhci_device_proto = {
- .version = DEVICE_OPS_VERSION,
- .unbind = pci_sdhci_unbind,
- .release = pci_sdhci_release,
-};
-
-static zx_status_t pci_sdhci_bind(void* ctx, zx_device_t* parent) {
- zxlogf(DEBUG, "bind");
- pci_sdhci_device_t* dev = calloc(1, sizeof(pci_sdhci_device_t));
- if (!dev) {
- zxlogf(ERROR, "out of memory");
- return ZX_ERR_NO_MEMORY;
- }
-
- // TODO(fxbug.dev/93333): Remove this once DFv2 has stabilised.
- bool is_dfv2 = device_is_dfv2(parent);
-
- zx_status_t status = ZX_OK;
- if (is_dfv2) {
- status = device_get_protocol(parent, ZX_PROTOCOL_PCI, &dev->pci);
- } else {
- status = device_get_fragment_protocol(parent, "pci", ZX_PROTOCOL_PCI, &dev->pci);
- }
- if (status != ZX_OK) {
- goto fail;
- }
-
- status = dev->pci.ops->set_bus_mastering(dev->pci.ctx, true);
- if (status < 0) {
- zxlogf(ERROR, "error in enable bus master: %s", zx_status_get_string(status));
- goto fail;
- }
-
- device_add_args_t args = {
- .version = DEVICE_ADD_ARGS_VERSION,
- .name = "pci-sdhci",
- .ctx = dev,
- .ops = &pci_sdhci_device_proto,
- .proto_id = ZX_PROTOCOL_SDHCI,
- .proto_ops = &pci_sdhci_sdhci_proto,
- };
-
- status = device_add(parent, &args, &dev->zxdev);
- if (status != ZX_OK) {
- goto fail;
- }
-
- return ZX_OK;
-fail:
- free(dev);
- return status;
-}
-
-static zx_driver_ops_t pci_sdhci_driver_ops = {
- .version = DRIVER_OPS_VERSION,
- .bind = pci_sdhci_bind,
-};
-
-ZIRCON_DRIVER(pci_sdhci, pci_sdhci_driver_ops, "zircon", "0.1");
diff --git a/src/devices/block/drivers/pci-sdhci/pci-sdhci.cc b/src/devices/block/drivers/pci-sdhci/pci-sdhci.cc
new file mode 100644
index 0000000..fd9d12e
--- /dev/null
+++ b/src/devices/block/drivers/pci-sdhci/pci-sdhci.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 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 "pci-sdhci.h"
+
+#include <fuchsia/hardware/pci/cpp/banjo.h>
+#include <fuchsia/hardware/sdhci/cpp/banjo.h>
+#include <inttypes.h>
+#include <lib/ddk/debug.h>
+#include <lib/ddk/device.h>
+#include <lib/ddk/driver.h>
+#include <lib/device-protocol/pci.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <threads.h>
+#include <unistd.h>
+#include <zircon/status.h>
+
+#include "src/devices/block/drivers/pci-sdhci/pci-sdhci-bind.h"
+
+#define HOST_CONTROL1_OFFSET 0x28
+#define SDHCI_EMMC_HW_RESET (1 << 12)
+
+constexpr auto kTag = "pci-sdhci";
+
+namespace sdhci {
+
+PciSdhci::PciSdhci(zx_device_t* parent) : DeviceType(parent) {}
+
+zx_status_t PciSdhci::SdhciGetInterrupt(zx::interrupt* interrupt_out) {
+ // select irq mode
+ pci_interrupt_mode_t mode = PCI_INTERRUPT_MODE_DISABLED;
+ zx_status_t status = pci_.ConfigureInterruptMode(1, &mode);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: error setting irq mode: %s", kTag, zx_status_get_string(status));
+ return status;
+ }
+
+ // get irq handle
+ status = pci_.MapInterrupt(0, interrupt_out);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: error getting irq handle: %s", kTag, zx_status_get_string(status));
+ }
+ return status;
+}
+
+zx_status_t PciSdhci::SdhciGetMmio(zx::vmo* out, zx_off_t* out_offset) {
+ if (!mmio_.has_value()) {
+ zx_status_t status = pci_.MapMmio(0u, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio_);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: error mapping register window: %s", kTag, zx_status_get_string(status));
+ return status;
+ }
+ }
+ *out_offset = mmio_->get_offset();
+ return mmio_->get_vmo()->duplicate(ZX_RIGHT_SAME_RIGHTS, out);
+}
+
+zx_status_t PciSdhci::SdhciGetBti(uint32_t index, zx::bti* out_bti) {
+ if (!bti_.is_valid()) {
+ zx_status_t st = pci_.GetBti(index, &bti_);
+ if (st != ZX_OK) {
+ return st;
+ }
+ }
+ return bti_.duplicate(ZX_RIGHT_SAME_RIGHTS, out_bti);
+}
+
+uint32_t PciSdhci::SdhciGetBaseClock() { return 0; }
+
+uint64_t PciSdhci::SdhciGetQuirks(uint64_t* out_dma_boundary_alignment) {
+ *out_dma_boundary_alignment = 0;
+ return SDHCI_QUIRK_STRIP_RESPONSE_CRC_PRESERVE_ORDER;
+}
+
+void PciSdhci::SdhciHwReset() {
+ if (!mmio_.has_value()) {
+ return;
+ }
+ uint32_t val = mmio_->Read32(HOST_CONTROL1_OFFSET);
+ val |= SDHCI_EMMC_HW_RESET;
+ mmio_->Write32(val, HOST_CONTROL1_OFFSET);
+ // minimum is 1us but wait 9us for good measure
+ zx_nanosleep(zx_deadline_after(ZX_USEC(9)));
+ val &= ~SDHCI_EMMC_HW_RESET;
+ mmio_->Write32(val, HOST_CONTROL1_OFFSET);
+ // minimum is 200us but wait 300us for good measure
+ zx_nanosleep(zx_deadline_after(ZX_USEC(300)));
+}
+
+void PciSdhci::DdkUnbind(ddk::UnbindTxn txn) { device_unbind_reply(zxdev()); }
+
+zx_status_t PciSdhci::Bind(void* /* unused */, zx_device_t* parent) {
+ auto dev = std::make_unique<PciSdhci>(parent);
+ if (!dev) {
+ zxlogf(ERROR, "%s: out of memory", kTag);
+ return ZX_ERR_NO_MEMORY;
+ }
+
+ // TODO(fxbug.dev/93333): Remove this once DFv2 has stabilised.
+ bool is_dfv2 = device_is_dfv2(parent);
+
+ zx_status_t status = ZX_OK;
+ if (is_dfv2) {
+ status = device_get_protocol(parent, ZX_PROTOCOL_PCI, &dev->pci_);
+ } else {
+ status = device_get_fragment_protocol(parent, "pci", ZX_PROTOCOL_PCI, &dev->pci_);
+ }
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: could not get PCI protocol: %s", kTag, zx_status_get_string(status));
+ return status;
+ }
+
+ status = dev->pci_.SetBusMastering(true);
+ if (status < 0) {
+ zxlogf(ERROR, "%s: error in enable bus master: %s", kTag, zx_status_get_string(status));
+ return status;
+ }
+
+ status = dev->DdkAdd(ddk::DeviceAddArgs("pci-sdhci").set_proto_id(ZX_PROTOCOL_SDHCI));
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s: error adding device: %s", kTag, zx_status_get_string(status));
+ return status;
+ }
+
+ // The object is owned by the DDK, now that it has been added. It will be deleted
+ // when the device is released.
+ __UNUSED auto ptr = dev.release();
+
+ return ZX_OK;
+}
+
+static zx_driver_ops_t pci_sdhci_driver_ops = {
+ .version = DRIVER_OPS_VERSION,
+ .bind = PciSdhci::Bind,
+};
+
+void PciSdhci::DdkRelease() { delete this; }
+
+} // namespace sdhci
+
+ZIRCON_DRIVER(pci_sdhci, sdhci::pci_sdhci_driver_ops, "zircon", "0.1");
diff --git a/src/devices/block/drivers/pci-sdhci/pci-sdhci.h b/src/devices/block/drivers/pci-sdhci/pci-sdhci.h
new file mode 100644
index 0000000..809cabf
--- /dev/null
+++ b/src/devices/block/drivers/pci-sdhci/pci-sdhci.h
@@ -0,0 +1,45 @@
+// Copyright 2022 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.
+
+#ifndef SRC_DEVICES_BLOCK_DRIVERS_PCI_SDHCI_PCI_SDHCI_H_
+#define SRC_DEVICES_BLOCK_DRIVERS_PCI_SDHCI_PCI_SDHCI_H_
+
+#include <fuchsia/hardware/sdhci/cpp/banjo.h>
+#include <lib/device-protocol/pci.h>
+
+#include <optional>
+
+#include <ddktl/device.h>
+
+namespace sdhci {
+
+class PciSdhci;
+using DeviceType = ddk::Device<PciSdhci>;
+
+class PciSdhci : public DeviceType, public ddk::SdhciProtocol<PciSdhci, ddk::base_protocol> {
+ public:
+ explicit PciSdhci(zx_device_t*);
+
+ static zx_status_t Bind(void*, zx_device_t* parent);
+
+ zx_status_t SdhciGetInterrupt(zx::interrupt* interrupt_out);
+ zx_status_t SdhciGetMmio(zx::vmo* out, zx_off_t* out_offset);
+ zx_status_t SdhciGetBti(uint32_t index, zx::bti* out_bti);
+ uint32_t SdhciGetBaseClock();
+ uint64_t SdhciGetQuirks(uint64_t* out_dma_boundary_alignment);
+ void SdhciHwReset();
+
+ void DdkUnbind(ddk::UnbindTxn txn);
+ void DdkRelease();
+
+ private:
+ ddk::Pci pci_;
+
+ std::optional<fdf::MmioBuffer> mmio_;
+ zx::bti bti_;
+};
+
+} // namespace sdhci
+
+#endif // SRC_DEVICES_BLOCK_DRIVERS_PCI_SDHCI_PCI_SDHCI_H_