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_