[vim3][sd] Enable SD slot on vim3

Bug: None
Test: lsblk shows SD and eMMC block devices
Test: device-enumeration-test
Change-Id: Ib3f71b6cbd3306cb4fac3f677c9ca05c8af8ab89
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/402495
Testability-Review: Braden Kell <bradenkell@google.com>
Commit-Queue: Braden Kell <bradenkell@google.com>
Reviewed-by: Marty Faltesek <mfaltesek@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 c798d54..4929d94 100644
--- a/src/devices/board/drivers/vim3/BUILD.gn
+++ b/src/devices/board/drivers/vim3/BUILD.gn
@@ -18,6 +18,7 @@
     "vim3-eth.cc",
     "vim3-gpio.cc",
     "vim3-i2c.cc",
+    "vim3-sd.cc",
     "vim3-sysmem.cc",
     "vim3.cc",
   ]
diff --git a/src/devices/board/drivers/vim3/vim3-sd.cc b/src/devices/board/drivers/vim3/vim3-sd.cc
new file mode 100644
index 0000000..37b2353
--- /dev/null
+++ b/src/devices/board/drivers/vim3/vim3-sd.cc
@@ -0,0 +1,87 @@
+// 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 sd_mmios[] = {
+    {
+        .base = A311D_EMMC_B_BASE,
+        .length = A311D_EMMC_B_LENGTH,
+    },
+};
+
+static const pbus_irq_t sd_irqs[] = {
+    {
+        .irq = A311D_SD_EMMC_B_IRQ,
+        .mode = ZX_INTERRUPT_MODE_EDGE_HIGH,
+    },
+};
+
+static const pbus_bti_t sd_btis[] = {
+    {
+        .iommu_index = 0,
+        .bti_id = BTI_SD,
+    },
+};
+
+static aml_sd_emmc_config_t config = {
+    .supports_dma = true,
+    .min_freq = 400'000,
+    .max_freq = 50'000'000,
+    .version_3 = true,
+    .prefs = 0,
+};
+
+static const pbus_metadata_t sd_metadata[] = {
+    {
+        .type = DEVICE_METADATA_EMMC_CONFIG,
+        .data_buffer = &config,
+        .data_size = sizeof(config),
+    },
+};
+
+zx_status_t Vim3::SdInit() {
+  zx_status_t status;
+
+  pbus_dev_t sd_dev = {};
+  sd_dev.name = "aml_sd";
+  sd_dev.vid = PDEV_VID_AMLOGIC;
+  sd_dev.pid = PDEV_PID_GENERIC;
+  sd_dev.did = PDEV_DID_AMLOGIC_SD_EMMC_B;
+  sd_dev.mmio_list = sd_mmios;
+  sd_dev.mmio_count = countof(sd_mmios);
+  sd_dev.irq_list = sd_irqs;
+  sd_dev.irq_count = countof(sd_irqs);
+  sd_dev.bti_list = sd_btis;
+  sd_dev.bti_count = countof(sd_btis);
+  sd_dev.metadata_list = sd_metadata;
+  sd_dev.metadata_count = countof(sd_metadata);
+
+  gpio_impl_.SetAltFunction(A311D_GPIOC(0), A311D_GPIOC_0_SDCARD_D0_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOC(1), A311D_GPIOC_1_SDCARD_D1_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOC(2), A311D_GPIOC_2_SDCARD_D2_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOC(3), A311D_GPIOC_3_SDCARD_D3_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOC(4), A311D_GPIOC_4_SDCARD_CLK_FN);
+  gpio_impl_.SetAltFunction(A311D_GPIOC(5), A311D_GPIOC_5_SDCARD_CMD_FN);
+
+  if ((status = pbus_.CompositeDeviceAdd(&sd_dev, nullptr, 0, UINT32_MAX)) != ZX_OK) {
+    zxlogf(ERROR, "SdInit could not add sd_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 3e48daf..0d5c05b 100644
--- a/src/devices/board/drivers/vim3/vim3.cc
+++ b/src/devices/board/drivers/vim3/vim3.cc
@@ -89,6 +89,11 @@
     init_txn_->Reply(ZX_ERR_INTERNAL);
     return status;
   }
+  if ((status = SdInit()) != ZX_OK) {
+    zxlogf(ERROR, "SdInit() 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 04652eb..74b5aca 100644
--- a/src/devices/board/drivers/vim3/vim3.h
+++ b/src/devices/board/drivers/vim3/vim3.h
@@ -24,6 +24,7 @@
 enum {
   BTI_EMMC,
   BTI_ETHERNET,
+  BTI_SD,
   BTI_SYSMEM,
 };
 
@@ -50,6 +51,7 @@
   zx_status_t EthInit();
   zx_status_t GpioInit();
   zx_status_t I2cInit();
+  zx_status_t SdInit();
   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 635cadf..0e50a65 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
@@ -67,6 +67,14 @@
 #define A311D_GPIOA_14_I2C_EE_M3_SDA_FN 2
 #define A311D_GPIOA_15_I2C_EE_M3_SCL_FN 2
 
+// GPIOC pin alternate functions
+#define A311D_GPIOC_0_SDCARD_D0_FN 1
+#define A311D_GPIOC_1_SDCARD_D1_FN 1
+#define A311D_GPIOC_2_SDCARD_D2_FN 1
+#define A311D_GPIOC_3_SDCARD_D3_FN 1
+#define A311D_GPIOC_4_SDCARD_CLK_FN 1
+#define A311D_GPIOC_5_SDCARD_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 9bdd156..307a0c1 100644
--- a/zircon/system/utest/device-enumeration/main.cc
+++ b/zircon/system/utest/device-enumeration/main.cc
@@ -203,6 +203,7 @@
       "sys/platform/05:00:2/aml-i2c/i2c/i2c-0-81/rtc",
       "dwmac/eth_phy/phy_null_device",
       "dwmac/Designware MAC/ethernet",
+      "aml_sd/aml-sd-emmc",
   };
 
   ASSERT_NO_FATAL_FAILURES(TestRunner(kDevicePaths, std::size(kDevicePaths)));