[vim3][sdio] Enable SDIO on vim3

Bug: None
Test: Saw SDIO probe message with manufacturer/product IDs
Test: device-enumeration-test
Change-Id: Icc832acbc4121c52c5d07751e89bd2b17afb70ae
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/402564
Testability-Review: Braden Kell <bradenkell@google.com>
Commit-Queue: Braden Kell <bradenkell@google.com>
Reviewed-by: Eric Holland <hollande@google.com>
Reviewed-by: Carl Norum <cjn@google.com>
diff --git a/src/devices/board/drivers/vim3/BUILD.gn b/src/devices/board/drivers/vim3/BUILD.gn
index 4929d94..4a36d02 100644
--- a/src/devices/board/drivers/vim3/BUILD.gn
+++ b/src/devices/board/drivers/vim3/BUILD.gn
@@ -19,6 +19,7 @@
     "vim3-gpio.cc",
     "vim3-i2c.cc",
     "vim3-sd.cc",
+    "vim3-sdio.cc",
     "vim3-sysmem.cc",
     "vim3.cc",
   ]
diff --git a/src/devices/board/drivers/vim3/vim3-gpio.cc b/src/devices/board/drivers/vim3/vim3-gpio.cc
index c0fb519..8ce61ac 100644
--- a/src/devices/board/drivers/vim3/vim3-gpio.cc
+++ b/src/devices/board/drivers/vim3/vim3-gpio.cc
@@ -68,9 +68,10 @@
 
 // GPIOs to expose from generic GPIO driver.
 static const gpio_pin_t gpio_pins[] = {
- {VIM3_J4_PIN_39},
- {VIM3_ETH_MAC_INTR},
- {A311D_GPIOBOOT(12)},
+    {VIM3_J4_PIN_39},
+    {VIM3_ETH_MAC_INTR},
+    {A311D_GPIOBOOT(12)},
+    {A311D_GPIOX(6)},
 };
 
 static const pbus_metadata_t gpio_metadata[] = {
diff --git a/src/devices/board/drivers/vim3/vim3-sdio.cc b/src/devices/board/drivers/vim3/vim3-sdio.cc
new file mode 100644
index 0000000..8fea1ac
--- /dev/null
+++ b/src/devices/board/drivers/vim3/vim3-sdio.cc
@@ -0,0 +1,103 @@
+// Copyright 2020 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 <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/metadata.h>
+#include <ddk/platform-defs.h>
+#include <ddk/protocol/sdmmc.h>
+#include <soc/aml-a311d/a311d-gpio.h>
+#include <soc/aml-a311d/a311d-hw.h>
+#include <soc/aml-common/aml-sd-emmc.h>
+
+#include "vim3.h"
+
+namespace vim3 {
+
+static const pbus_mmio_t sdio_mmios[] = {
+    {
+        .base = A311D_EMMC_A_BASE,
+        .length = A311D_EMMC_A_LENGTH,
+    },
+};
+
+static const pbus_irq_t sdio_irqs[] = {
+    {
+        .irq = A311D_SD_EMMC_A_IRQ,
+        .mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
+    },
+};
+
+static const pbus_bti_t sdio_btis[] = {
+    {
+        .iommu_index = 0,
+        .bti_id = BTI_SDIO,
+    },
+};
+
+static aml_sd_emmc_config_t config = {
+    .supports_dma = true,
+    .min_freq = 400'000,
+    .max_freq = 200'000'000,
+    .version_3 = true,
+    .prefs = 0,
+};
+
+static const pbus_metadata_t sdio_metadata[] = {
+    {
+        .type = DEVICE_METADATA_EMMC_CONFIG,
+        .data_buffer = &config,
+        .data_size = sizeof(config),
+    },
+};
+
+static const zx_bind_inst_t root_match[] = {
+    BI_MATCH(),
+};
+static const zx_bind_inst_t wifi_pwren_gpio_match[] = {
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_GPIO),
+    BI_MATCH_IF(EQ, BIND_GPIO_PIN, A311D_GPIOX(6)),
+};
+static const device_fragment_part_t wifi_pwren_gpio_fragment[] = {
+    {std::size(root_match), root_match},
+    {std::size(wifi_pwren_gpio_match), wifi_pwren_gpio_match},
+};
+static const device_fragment_t sdio_fragments[] = {
+    {std::size(wifi_pwren_gpio_fragment), wifi_pwren_gpio_fragment},
+};
+
+zx_status_t Vim3::SdioInit() {
+  zx_status_t status;
+
+  pbus_dev_t sdio_dev = {};
+  sdio_dev.name = "aml_sdio";
+  sdio_dev.vid = PDEV_VID_AMLOGIC;
+  sdio_dev.pid = PDEV_PID_GENERIC;
+  sdio_dev.did = PDEV_DID_AMLOGIC_SD_EMMC_A;
+  sdio_dev.mmio_list = sdio_mmios;
+  sdio_dev.mmio_count = countof(sdio_mmios);
+  sdio_dev.irq_list = sdio_irqs;
+  sdio_dev.irq_count = countof(sdio_irqs);
+  sdio_dev.bti_list = sdio_btis;
+  sdio_dev.bti_count = countof(sdio_btis);
+  sdio_dev.metadata_list = sdio_metadata;
+  sdio_dev.metadata_count = countof(sdio_metadata);
+
+  gpio_impl_.SetAltFunction(A311D_GPIOX(0), A311D_GPIOX_0_SDIO_D0_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOX(1), A311D_GPIOX_1_SDIO_D1_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOX(2), A311D_GPIOX_2_SDIO_D2_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOX(3), A311D_GPIOX_3_SDIO_D3_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOX(4), A311D_GPIOX_4_SDIO_CLK_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOX(5), A311D_GPIOX_5_SDIO_CMD_FN);
+
+  if ((status = pbus_.CompositeDeviceAdd(&sdio_dev, sdio_fragments, countof(sdio_fragments),
+                                         UINT32_MAX)) != ZX_OK) {
+    zxlogf(ERROR, "SdInit could not add sdio_dev: %d", status);
+    return status;
+  }
+
+  return ZX_OK;
+}
+
+}  // namespace vim3
diff --git a/src/devices/board/drivers/vim3/vim3.cc b/src/devices/board/drivers/vim3/vim3.cc
index 0d5c05b..afc122c 100644
--- a/src/devices/board/drivers/vim3/vim3.cc
+++ b/src/devices/board/drivers/vim3/vim3.cc
@@ -94,6 +94,11 @@
     init_txn_->Reply(ZX_ERR_INTERNAL);
     return status;
   }
+  if ((status = SdioInit()) != ZX_OK) {
+    zxlogf(ERROR, "SdioInit() failed: %d\n", status);
+    init_txn_->Reply(ZX_ERR_INTERNAL);
+    return status;
+  }
   init_txn_->Reply(status);
   return ZX_OK;
 }
diff --git a/src/devices/board/drivers/vim3/vim3.h b/src/devices/board/drivers/vim3/vim3.h
index 74b5aca..c0bb528 100644
--- a/src/devices/board/drivers/vim3/vim3.h
+++ b/src/devices/board/drivers/vim3/vim3.h
@@ -25,6 +25,7 @@
   BTI_EMMC,
   BTI_ETHERNET,
   BTI_SD,
+  BTI_SDIO,
   BTI_SYSMEM,
 };
 
@@ -52,6 +53,7 @@
   zx_status_t GpioInit();
   zx_status_t I2cInit();
   zx_status_t SdInit();
+  zx_status_t SdioInit();
   zx_status_t Start();
   zx_status_t SysmemInit();
 
diff --git a/src/devices/lib/amlogic/include/soc/aml-a311d/a311d-gpio.h b/src/devices/lib/amlogic/include/soc/aml-a311d/a311d-gpio.h
index 0e50a65..2e47121 100644
--- a/src/devices/lib/amlogic/include/soc/aml-a311d/a311d-gpio.h
+++ b/src/devices/lib/amlogic/include/soc/aml-a311d/a311d-gpio.h
@@ -75,6 +75,14 @@
 #define A311D_GPIOC_4_SDCARD_CLK_FN 1
 #define A311D_GPIOC_5_SDCARD_CMD_FN 1
 
+// GPIOC pin alternate functions
+#define A311D_GPIOX_0_SDIO_D0_FN 1
+#define A311D_GPIOX_1_SDIO_D1_FN 1
+#define A311D_GPIOX_2_SDIO_D2_FN 1
+#define A311D_GPIOX_3_SDIO_D3_FN 1
+#define A311D_GPIOX_4_SDIO_CLK_FN 1
+#define A311D_GPIOX_5_SDIO_CMD_FN 1
+
 // GPIOZ pin alternate functions
 #define A311D_GPIOZ_0_ETH_MDIO_FN 1
 #define A311D_GPIOZ_1_ETH_MDC_FN 1
diff --git a/zircon/system/utest/device-enumeration/main.cc b/zircon/system/utest/device-enumeration/main.cc
index 307a0c1..79a401e 100644
--- a/zircon/system/utest/device-enumeration/main.cc
+++ b/zircon/system/utest/device-enumeration/main.cc
@@ -204,6 +204,8 @@
       "dwmac/eth_phy/phy_null_device",
       "dwmac/Designware MAC/ethernet",
       "aml_sd/aml-sd-emmc",
+      "aml_sdio/aml-sd-emmc/sdmmc/sdmmc-sdio/sdmmc-sdio-1",
+      "aml_sdio/aml-sd-emmc/sdmmc/sdmmc-sdio/sdmmc-sdio-2",
   };
 
   ASSERT_NO_FATAL_FAILURES(TestRunner(kDevicePaths, std::size(kDevicePaths)));