[mt8156s_ref] Initial support for MT8167S reference board

Zircon boots into serial console shell on MT8167S with this change.
The Zircon image is packaged to work with Android Verified Boot.
A stub board driver for the board is included in this CL.

Still to do:

- Implement interrupt support in the UART driver instead of spinning and polling.
  This will require pinmuxing the interrupt to level triggered active low,
  since that configuration cannot be done in the gic.

- Remove code in the boot shim that disables the watchdog timer when we have a proper
  watchdog driver in userspace.

TEST: Board boots into a shell, board driver loads and "runtests" passes.

Change-Id: I2b4fa280c40b7deab061033ffcbee04f8584c407
diff --git a/kernel/dev/uart/mt8167/rules.mk b/kernel/dev/uart/mt8167/rules.mk
new file mode 100644
index 0000000..ecfb667
--- /dev/null
+++ b/kernel/dev/uart/mt8167/rules.mk
@@ -0,0 +1,17 @@
+# Copyright 2018 The Fuchsia Authors
+# Use of this source code is governed by a MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/uart.cpp
+
+MODULE_DEPS += \
+    kernel/dev/pdev \
+    kernel/dev/pdev/uart \
+
+include make/module.mk
diff --git a/kernel/dev/uart/mt8167/uart.cpp b/kernel/dev/uart/mt8167/uart.cpp
new file mode 100644
index 0000000..85299ba
--- /dev/null
+++ b/kernel/dev/uart/mt8167/uart.cpp
@@ -0,0 +1,254 @@
+// Copyright 2018 The Fuchsia Authors
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#include <arch/arm64/periphmap.h>
+#include <dev/interrupt.h>
+#include <dev/uart.h>
+#include <kernel/thread.h>
+#include <lib/cbuf.h>
+#include <lib/debuglog.h>
+#include <pdev/driver.h>
+#include <pdev/uart.h>
+#include <platform/debug.h>
+#include <reg.h>
+#include <stdio.h>
+#include <trace.h>
+#include <zircon/boot/driver-config.h>
+
+// clang-format off
+
+// Registers
+
+#define UART_RBR                    (0x0)   // RX Buffer Register (read-only)
+#define UART_THR                    (0x0)   // TX Buffer Register (write-only)
+#define UART_IER                    (0x4)   // Interrupt Enable Register
+#define UART_IIR                    (0x8)   // Interrupt Identification Register (read-only)
+#define UART_FCR                    (0x8)   // FIFO Control Register (write-only)
+#define UART_LCR                    (0xc)   // Line Control Register
+#define UART_MCR                    (0x10)  // Modem Control Register
+#define UART_LSR                    (0x14)  // Line Status Register
+#define UART_MSR                    (0x18)  // Modem Status Register
+#define UART_SCR                    (0x1c)  // Scratch Register
+#define UART_DLL                    (0x0)   // Divisor Latch LS (Only when LCR.DLAB = 1)
+#define UART_DLM                    (0x4)   // Divisor Latch MS (Only when LCR.DLAB = 1)
+#define UART_EFR                    (0x8)   // Enhanced Feature Register (Only when LCR = 0xbf)
+#define UART_XON1                   (0x10)  // XON1 Char Register (Only when LCR = 0xbf)
+#define UART_XON2                   (0x14)  // XON2 Char Register (Only when LCR = 0xbf)
+#define UART_XOFF1                  (0x18)  // XOFF1 Char Register (Only when LCR = 0xbf)
+#define UART_XOFF2                  (0x1c)  // XOFF2 Char Register (Only when LCR = 0xbf)
+#define UART_AUTOBAUD_EN            (0x20)  // Auto Baud Detect Enable Register
+#define UART_HIGHSPEED              (0x24)  // High Speed Mode Register
+#define UART_SAMPLE_COUNT           (0x28)  // Sample Counter Register
+#define UART_SAMPLE_POINT           (0x2c)  // Sample Point Register
+#define UART_AUTOBAUD_REG           (0x30)  // Auto Baud Monitor Register
+#define UART_RATE_FIX_AD            (0x34)  // Clock Rate Fix Register
+#define UART_AUTOBAUD_SAMPLE        (0x38)  // Auto Baud Sample Register
+#define UART_GUARD                  (0x3c)  // Guard Time Added Register
+#define UART_ESCAPE_DAT             (0x40)  // Escape Character Register
+#define UART_ESCAPE_EN              (0x44)  // Escape Enable Register
+#define UART_SLEEP_EN               (0x48)  // Sleep Enable Register
+#define UART_VFIFO_EN               (0x4c)  // DMA Enable Register
+#define UART_RXTRI_AD               (0x50)  // RX Trigger Address
+
+// IER
+#define UART_IER_ERBFI              (1 << 0)
+#define UART_IER_ETBEI              (1 << 1)
+#define UART_IER_ELSI               (1 << 2)
+#define UART_IER_EDSSI              (1 << 3)
+#define UART_IER_XOFFI              (1 << 5)
+#define UART_IER_RTSI               (1 << 6)
+#define UART_IER_CTSI               (1 << 7)
+#define UART_IIR_NO_INT_PENDING     (0x01)
+
+// IIR
+#define UART_IIR_RLS                (0x06)  // Receiver Line Status
+#define UART_IIR_RDA                (0x04)  // Receive Data Available
+#define UART_IIR_CTI                (0x0C)  // Character Timeout Indicator
+#define UART_IIR_THRE               (0x02)  // Transmit Holding Register Empty
+#define UART_IIR_MS                 (0x00)  // Check Modem Status Register
+#define UART_IIR_SW_FLOW_CTRL       (0x10)  // Receive XOFF characters
+#define UART_IIR_HW_FLOW_CTRL       (0x20)  // CTS or RTS Rising Edge
+#define UART_IIR_FIFO_EN            (0xc0)
+#define UART_IIR_INT_MASK           (0x1f)
+
+// LSR
+#define UART_LSR_DR                 (1 << 0)
+#define UART_LSR_OE                 (1 << 1)
+#define UART_LSR_PE                 (1 << 2)
+#define UART_LSR_FE                 (1 << 3)
+#define UART_LSR_BI                 (1 << 4)
+#define UART_LSR_THRE               (1 << 5)
+#define UART_LSR_TEMT               (1 << 6)
+#define UART_LSR_FIFOERR            (1 << 7)
+
+// clang-format on
+
+#define RXBUF_SIZE 32
+
+// values read from zbi
+static bool initialized = false;
+static vaddr_t uart_base = 0;
+static uint32_t uart_irq = 0;
+static cbuf_t uart_rx_buf;
+
+static bool uart_tx_irq_enabled = false;
+static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event,
+                                                      true,
+                                                      EVENT_FLAG_AUTOUNSIGNAL);
+
+static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE;
+
+#define UARTREG(reg) (*(volatile uint32_t*)((uart_base) + (reg)))
+
+static void uart_irq_handler(void* arg) {
+    // read interrupt status and mask
+    while (UARTREG(UART_LSR) & UART_LSR_DR) {
+        if (cbuf_space_avail(&uart_rx_buf) == 0) {
+            break;
+        }
+        char c = UARTREG(UART_RBR) & 0xFF;
+        cbuf_write_char(&uart_rx_buf, c);
+    }
+
+    // Signal if anyone is waiting to TX
+    if (UARTREG(UART_LSR) & UART_LSR_THRE) {
+        spin_lock(&uart_spinlock);
+        event_signal(&uart_dputc_event, true);
+        spin_unlock(&uart_spinlock);
+    }
+}
+
+// panic-time getc/putc
+static int mt8167_uart_pputc(char c) {
+    if (!uart_base) {
+        return -1;
+    }
+
+    // spin while fifo is full
+    while (!(UARTREG(UART_LSR) & UART_LSR_THRE))
+        ;
+    UARTREG(UART_THR) = c;
+
+    return 1;
+}
+
+static int mt8167_uart_pgetc(void) {
+    if (!uart_base) {
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    // spin while fifo is empty
+    while (!(UARTREG(UART_LSR) & UART_LSR_DR))
+        ;
+    return UARTREG(UART_RBR);
+}
+
+static int mt8167_uart_getc(bool wait) {
+    if (!uart_base) {
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    if (initialized) {
+        char c;
+        if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) {
+            return c;
+        }
+        return ZX_ERR_INTERNAL;
+    } else {
+        // Interrupts are not enabled yet. Use panic calls for now
+        return mt8167_uart_pgetc();
+    }
+}
+
+static void mt8167_dputs(const char* str, size_t len,
+                         bool block, bool map_NL) {
+    spin_lock_saved_state_t state;
+    bool copied_CR = false;
+
+    if (!uart_base) {
+        return;
+    }
+    if (!uart_tx_irq_enabled) {
+        block = false;
+    }
+    spin_lock_irqsave(&uart_spinlock, state);
+
+    while (len > 0) {
+        // is FIFO full?
+        while (!(UARTREG(UART_LSR) & UART_LSR_THRE)) {
+            spin_unlock_irqrestore(&uart_spinlock, state);
+            if (block) {
+                event_wait(&uart_dputc_event);
+            } else {
+                arch_spinloop_pause();
+            }
+            spin_lock_irqsave(&uart_spinlock, state);
+        }
+        if (*str == '\n' && map_NL && !copied_CR) {
+            copied_CR = true;
+            mt8167_uart_pputc('\r');
+        } else {
+            copied_CR = false;
+            mt8167_uart_pputc(*str++);
+            len--;
+        }
+    }
+    spin_unlock_irqrestore(&uart_spinlock, state);
+}
+
+static void mt8167_start_panic(void) {
+    uart_tx_irq_enabled = false;
+}
+
+static const struct pdev_uart_ops uart_ops = {
+    .getc = mt8167_uart_getc,
+    .pputc = mt8167_uart_pputc,
+    .pgetc = mt8167_uart_pgetc,
+    .start_panic = mt8167_start_panic,
+    .dputs = mt8167_dputs,
+};
+
+static void mt8167_uart_init(const void* driver_data, uint32_t length) {
+    // create circular buffer to hold received data
+    cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
+
+    // register uart irq
+    register_int_handler(uart_irq, &uart_irq_handler, NULL);
+
+// TODO: Configure UART interrupt support here
+
+// TODO: Enable interrupt support after we have a way to set the interrupt to active-low
+#if 0
+    if (dlog_bypass() == true)
+        uart_tx_irq_enabled = false;
+    else {
+        // start up tx driven output
+        printf("UART: started IRQ driven TX\n");
+        uart_tx_irq_enabled = true;
+    }
+
+    initialized = true;
+
+    // TODO we will need to talk to the pinmux controller to set the interrupt to active low.
+    // configure_interrupt() doesn't actually support that.
+    configure_interrupt(uart_irq, IRQ_TRIGGER_MODE_LEVEL, IRQ_POLARITY_ACTIVE_LOW);
+    unmask_interrupt(uart_irq);
+#endif
+}
+
+static void mt8167_uart_init_early(const void* driver_data, uint32_t length) {
+    ASSERT(length >= sizeof(dcfg_simple_t));
+    auto driver = static_cast<const dcfg_simple_t*>(driver_data);
+    ASSERT(driver->mmio_phys && driver->irq);
+
+    uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
+    ASSERT(uart_base);
+    uart_irq = driver->irq;
+
+    pdev_register_uart(&uart_ops);
+}
+
+LK_PDEV_INIT(mt8167_uart_init_early, KDRV_MT8167_UART, mt8167_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
+LK_PDEV_INIT(mt8167_uart_init, KDRV_MT8167_UART, mt8167_uart_init, LK_INIT_LEVEL_PLATFORM);
diff --git a/kernel/platform/generic-arm/rules.mk b/kernel/platform/generic-arm/rules.mk
index fe2058d..6f8494f 100644
--- a/kernel/platform/generic-arm/rules.mk
+++ b/kernel/platform/generic-arm/rules.mk
@@ -32,6 +32,7 @@
 	kernel/dev/power/hisi \
 	kernel/dev/psci \
 	kernel/dev/uart/amlogic_s905 \
+	kernel/dev/uart/mt8167 \
 	kernel/dev/uart/nxp-imx \
 	kernel/dev/uart/pl011 \
 	kernel/dev/hdcp/amlogic_s912 \
diff --git a/kernel/target/arm64/board/mt8167s_ref/boot-shim-config.h b/kernel/target/arm64/board/mt8167s_ref/boot-shim-config.h
new file mode 100644
index 0000000..0e368331
--- /dev/null
+++ b/kernel/target/arm64/board/mt8167s_ref/boot-shim-config.h
@@ -0,0 +1,125 @@
+// Copyright 2018 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.
+
+#define HAS_DEVICE_TREE 1
+
+#define WDT_MODE        0x10007000
+#define WDT_MODE_EN     (1 << 0)
+#define WDT_MODE_KEY    0x22000000
+
+static void disable_watchdog(void) {
+    volatile uint32_t* wdt_mode = (uint32_t*)WDT_MODE;
+    uint32_t tmp = *wdt_mode;
+    tmp &= ~WDT_MODE_EN;
+    tmp |= WDT_MODE_KEY;
+    *wdt_mode = tmp;
+}
+
+static const zbi_cpu_config_t cpu_config = {
+    .cluster_count = 1,
+    .clusters = {
+        {
+            .cpu_count = 4,
+        },
+    },
+};
+
+static const zbi_mem_range_t mem_config[] = {
+    {
+        .type = ZBI_MEM_RANGE_RAM,
+        .paddr = 0x40000000,
+        .length = 0x40000000, // 1GB
+    },
+    {
+        .type = ZBI_MEM_RANGE_PERIPHERAL,
+        // This is not the entire peripheral range, but enough to cover what we use in the kernel.
+        .paddr = 0x10000000,
+        .length = 0x01200000,
+    },
+    {
+        // Memory to reserve to avoid stomping on bootloader data.
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x40000000,
+        .length = 0x80000,
+    },
+    {
+        // mt8167-atf-reserved-memory
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x43000000,
+        .length = 0x30000,
+    },
+    {
+        // ram_console
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x44400000,
+        .length = 0x10000,
+    },
+    {
+        // pstore
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x44410000,
+        .length = 0xe0000,
+    },
+    {
+        // minirdump
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x444f0000,
+        .length = 0x10000,
+    },
+};
+
+static const dcfg_simple_t uart_driver = {
+    .mmio_phys = 0x11005000,
+    .irq = 84 + 32,
+};
+
+static const dcfg_arm_gicv2_driver_t gicv2_driver = {
+    .mmio_phys = 0x10310000,
+    .gicd_offset = 0x00000,
+    .gicc_offset = 0x10000,
+    .ipi_base = 5,
+};
+
+static const dcfg_arm_psci_driver_t psci_driver = {
+    .use_hvc = false,
+};
+
+static const dcfg_arm_generic_timer_driver_t timer_driver = {
+    .irq_phys = 16 + 14,    // PHYS_NONSECURE_PPI: GIC_PPI 14
+    .irq_virt = 16 + 11,    // VIRT_PPI: GIC_PPI 11
+};
+
+static const zbi_platform_id_t platform_id = {
+    .vid = PDEV_VID_MEDIATEK,
+    .pid = PDEV_PID_MEDIATEK_8167S_REF,
+    .board_name = "mt8167s_ref",
+};
+
+static void append_board_boot_item(zbi_header_t* bootdata) {
+    // Disable watchdog timer for now.
+    // TODO - remove this after we have a proper watchdog driver in userspace.
+    disable_watchdog();
+
+    // add CPU configuration
+    append_boot_item(bootdata, ZBI_TYPE_CPU_CONFIG, 0, &cpu_config,
+                    sizeof(zbi_cpu_config_t) +
+                    sizeof(zbi_cpu_cluster_t) * cpu_config.cluster_count);
+
+    // add memory configuration
+    append_boot_item(bootdata, ZBI_TYPE_MEM_CONFIG, 0, &mem_config,
+                    sizeof(zbi_mem_range_t) * countof(mem_config));
+
+    // add kernel drivers
+    append_boot_item(bootdata, ZBI_TYPE_KERNEL_DRIVER, KDRV_MT8167_UART, &uart_driver,
+                    sizeof(uart_driver));
+    append_boot_item(bootdata, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GIC_V2, &gicv2_driver,
+                    sizeof(gicv2_driver));
+    append_boot_item(bootdata, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_PSCI, &psci_driver,
+                    sizeof(psci_driver));
+    append_boot_item(bootdata, ZBI_TYPE_KERNEL_DRIVER, KDRV_ARM_GENERIC_TIMER, &timer_driver,
+                    sizeof(timer_driver));
+
+    // add platform ID
+    append_boot_item(bootdata, ZBI_TYPE_PLATFORM_ID, 0, &platform_id, sizeof(platform_id));
+}
diff --git a/kernel/target/arm64/board/mt8167s_ref/rules.mk b/kernel/target/arm64/board/mt8167s_ref/rules.mk
new file mode 100644
index 0000000..b919ea0
--- /dev/null
+++ b/kernel/target/arm64/board/mt8167s_ref/rules.mk
@@ -0,0 +1,20 @@
+# Copyright 2018 The Fuchsia Authors
+#
+# Use of this source code is governed by a MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+PLATFORM_BOARD_NAME := mt8167s_ref
+
+PLATFORM_USE_SHIM := true
+PLATFORM_USE_MKBOOTIMG := true
+PLATFORM_USE_AVB := true
+
+PLATFORM_KERNEL_OFFSET := 0x00080000
+PLATFORM_MEMBASE := 0x40000000
+PLATFORM_CMDLINE := netsvc.netboot=true
+PLATFORM_BOOT_PARTITION_SIZE := 33554432
+
+include make/board.mk
diff --git a/kernel/target/arm64/boot-shim/mt8167s_ref-uart.c b/kernel/target/arm64/boot-shim/mt8167s_ref-uart.c
new file mode 100644
index 0000000..8ae1e36
--- /dev/null
+++ b/kernel/target/arm64/boot-shim/mt8167s_ref-uart.c
@@ -0,0 +1,18 @@
+// Copyright 2018 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 <stdint.h>
+#include "debug.h"
+
+#define UART_THR                    (0x0)   // TX Buffer Register (write-only)
+#define UART_LSR                    (0x14)  // Line Status Register
+#define UART_LSR_THRE               (1 << 5)
+
+#define UARTREG(reg) (*(volatile uint32_t*)(0x11005000 + (reg)))
+
+void uart_pputc(char c) {
+    while (!(UARTREG(UART_LSR) & UART_LSR_THRE))
+        ;
+    UARTREG(UART_THR) = c;
+}
diff --git a/scripts/flash-mt8167s_ref b/scripts/flash-mt8167s_ref
new file mode 100755
index 0000000..61beccf
--- /dev/null
+++ b/scripts/flash-mt8167s_ref
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+# Copyright 2018 The Fuchsia Authors
+#
+# Use of this source code is governed by a MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+BUILD_DIR="${DIR}/../build-arm64"
+
+BOOT_IMG=${BUILD_DIR}/mt8167s_ref-boot.img
+VBMETA_IMG=${BUILD_DIR}/mt8167s_ref-vbmeta.img
+
+fastboot flash boot_a ${BOOT_IMG} && fastboot flash vbmeta_a ${VBMETA_IMG} && fastboot reboot
diff --git a/system/dev/board/mt8167s_ref/bind.c b/system/dev/board/mt8167s_ref/bind.c
new file mode 100644
index 0000000..2796f8a
--- /dev/null
+++ b/system/dev/board/mt8167s_ref/bind.c
@@ -0,0 +1,20 @@
+// Copyright 2018 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/driver.h>
+#include <ddk/protocol/platform-defs.h>
+
+extern zx_status_t mt8167_bind(void* ctx, zx_device_t* parent);
+
+static zx_driver_ops_t mt8167_driver_ops = {
+    .version = DRIVER_OPS_VERSION,
+    .bind = mt8167_bind,
+};
+
+ZIRCON_DRIVER_BEGIN(mt8167, mt8167_driver_ops, "zircon", "0.1", 3)
+    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_BUS),
+    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_MEDIATEK),
+    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_MEDIATEK_8167S_REF),
+ZIRCON_DRIVER_END(mt8167)
diff --git a/system/dev/board/mt8167s_ref/mt8167.cpp b/system/dev/board/mt8167s_ref/mt8167.cpp
new file mode 100644
index 0000000..d2335d5
--- /dev/null
+++ b/system/dev/board/mt8167s_ref/mt8167.cpp
@@ -0,0 +1,65 @@
+// Copyright 2018 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 "mt8167.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/platform-defs.h>
+#include <ddk/protocol/platform-device.h>
+#include <fbl/algorithm.h>
+#include <fbl/unique_ptr.h>
+
+namespace board_mt8167 {
+
+zx_status_t Mt8167::Create(zx_device_t* parent) {
+    platform_bus_protocol_t pbus;
+
+    auto status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_BUS, &pbus);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    fbl::AllocChecker ac;
+    auto board = fbl::make_unique_checked<Mt8167>(&ac, parent, &pbus);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+
+    status = board->DdkAdd("mt8167s_ref", DEVICE_ADD_NON_BINDABLE);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    // devmgr is now in charge of the device.
+    __UNUSED auto* dummy = board.release();
+
+    // Start up our protocol helpers and platform devices.
+    board->Start();
+
+    return ZX_OK;
+}
+
+void Mt8167::Start() {
+    // TODO: Start various drivers here.
+}
+
+void Mt8167::DdkRelease() {
+    delete this;
+}
+
+} // namespace board_mt8167
+
+zx_status_t mt8167_bind(void* ctx, zx_device_t* parent) {
+    return board_mt8167::Mt8167::Create(parent);
+}
diff --git a/system/dev/board/mt8167s_ref/mt8167.h b/system/dev/board/mt8167s_ref/mt8167.h
new file mode 100644
index 0000000..7d40f34
--- /dev/null
+++ b/system/dev/board/mt8167s_ref/mt8167.h
@@ -0,0 +1,40 @@
+// Copyright 2018 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.
+
+#pragma once
+
+#include <ddk/device.h>
+#include <ddktl/device.h>
+#include <ddktl/protocol/platform-bus.h>
+#include <fbl/macros.h>
+
+namespace board_mt8167 {
+
+class Mt8167;
+using Mt8167Type = ddk::Device<Mt8167>;
+
+// This is the main class for the platform bus driver.
+class Mt8167 : public Mt8167Type {
+public:
+    explicit Mt8167(zx_device_t* parent, platform_bus_protocol_t* pbus)
+        : Mt8167Type(parent), pbus_(pbus) {}
+
+    static zx_status_t Create(zx_device_t* parent);
+
+    // Device protocol implementation.
+    void DdkRelease();
+
+private:
+    DISALLOW_COPY_ASSIGN_AND_MOVE(Mt8167);
+
+    void Start();
+
+    ddk::PlatformBusProtocolProxy pbus_;
+};
+
+} // namespace board_mt8167
+
+__BEGIN_CDECLS
+zx_status_t mt8167_bind(void* ctx, zx_device_t* parent);
+__END_CDECLS
diff --git a/system/dev/board/mt8167s_ref/rules.mk b/system/dev/board/mt8167s_ref/rules.mk
new file mode 100644
index 0000000..e04f266
--- /dev/null
+++ b/system/dev/board/mt8167s_ref/rules.mk
@@ -0,0 +1,27 @@
+# Copyright 2018 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS := \
+    $(LOCAL_DIR)/bind.c \
+    $(LOCAL_DIR)/mt8167.cpp \
+
+MODULE_STATIC_LIBS := \
+    system/ulib/ddk \
+    system/ulib/ddktl \
+    system/ulib/fbl \
+    system/ulib/zx \
+    system/ulib/zxcpp \
+
+MODULE_LIBS := \
+    system/ulib/driver \
+    system/ulib/zircon \
+    system/ulib/c \
+
+include make/module.mk
diff --git a/system/public/zircon/boot/driver-config.h b/system/public/zircon/boot/driver-config.h
index 86ad415..120ea37 100644
--- a/system/public/zircon/boot/driver-config.h
+++ b/system/public/zircon/boot/driver-config.h
@@ -15,6 +15,7 @@
 #define KDRV_PL011_UART         0x55304C50  // 'PL0U'
 #define KDRV_AMLOGIC_UART       0x554C4D41  // 'AMLU'
 #define KDRV_NXP_IMX_UART       0x55584D49  // 'IMXU'
+#define KDRV_MT8167_UART        0x5538544D  // 'MT8U'
 #define KDRV_HISILICON_POWER    0x4F505348  // 'HSPO'
 #define KDRV_AMLOGIC_HDCP       0x484C4D41  // 'AMLH'
 
diff --git a/system/ulib/ddk/include/ddk/protocol/platform-defs.h b/system/ulib/ddk/include/ddk/protocol/platform-defs.h
index 8509275..6624564 100644
--- a/system/ulib/ddk/include/ddk/protocol/platform-defs.h
+++ b/system/ulib/ddk/include/ddk/protocol/platform-defs.h
@@ -131,4 +131,8 @@
 // Designware
 #define PDEV_VID_DESIGNWARE         12
 
+// Mediatek
+#define PDEV_VID_MEDIATEK           13
+#define PDEV_PID_MEDIATEK_8167S_REF 1
+
 __END_CDECLS;