[msm8053] Work in progress on msm8053 support

Execution makes it through the boot shim and into the kernel,
but mysteriously resets after that.

NOTES:

Device is flashed via fastboot using scripts/flash-msm8x53-som

Power cycling with volume down button pressed gets you into fastboot.

Serial debug access is done via special Y-cable that connects to the USB-C port
Connect to /dev/ttyUSB0 @ 115200

Finally, if the device fails to boot too many times in a row it will start booting off
the boot_b partition (which still contains Linux) instead of boot_a, where Zircon is flashed.
to fix that, you can reset the counters by doing:

fastboot flash partition kernel/target/arm64/board/msm8x53-som/partition-table.img

TEST: attempt to boot on msm8053 hardware as described above

Change-Id: I281eb98273952889a85a9e23a8914a7c39f4a2c7
diff --git a/kernel/dev/uart/msm/rules.mk b/kernel/dev/uart/msm/rules.mk
new file mode 100644
index 0000000..7408656
--- /dev/null
+++ b/kernel/dev/uart/msm/rules.mk
@@ -0,0 +1,17 @@
+# Copyright 2019 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/msm/uart.cpp b/kernel/dev/uart/msm/uart.cpp
new file mode 100644
index 0000000..e12180e
--- /dev/null
+++ b/kernel/dev/uart/msm/uart.cpp
@@ -0,0 +1,340 @@
+// Copyright 2019 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
+
+// TODO(gkalsi): Unify the two UART codepaths and use the port parameter to
+// select between the real uart and the miniuart.
+
+#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
+
+#define UART_MR1                            0x0000
+#define UART_MR1_RX_RDY_CTL                 (1 << 7)
+
+#define UART_MR2                            0x0004
+#define UART_DM_IPR                         0x0018
+#define UART_DM_DMRX                        0x0034
+#define UART_DM_N0_CHARS_FOR_TX             0x0040
+
+#define UART_DM_SR                          0x00A4
+#define UART_DM_SR_RXRDY                    (1 << 0)
+#define UART_DM_SR_RXFULL                   (1 << 1)
+#define UART_DM_SR_TXRDY                    (1 << 2)
+#define UART_DM_SR_TXEMT                    (1 << 3)
+#define UART_DM_SR_OVERRUN                  (1 << 4)
+#define UART_DM_SR_PAR_FRAME_ERR            (1 << 5)
+#define UART_DM_SR_RX_BREAK                 (1 << 6)
+#define UART_DM_SR_HUNT_CHAR                (1 << 7)
+
+#define UART_DM_CR                          0x00A8
+#define UART_DM_CR_RX_EN                    (1 << 0)
+#define UART_DM_CR_RX_DISABLE               (1 << 1)
+#define UART_DM_CR_TX_EN                    (1 << 2)
+#define UART_DM_CR_TX_DISABLE               (1 << 3)
+
+#define UART_DM_CR_CMD_RESET_RX             (1 << 4)
+#define UART_DM_CR_CMD_RESET_TX             (2 << 4)
+#define UART_DM_CR_CMD_RESET_ERR            (3 << 4)
+#define UART_DM_CR_CMD_RESET_BRK_CHG_INT    (4 << 4)
+#define UART_DM_CR_CMD_START_BRK            (5 << 4)
+#define UART_DM_CR_CMD_STOP_BRK             (6 << 4)
+#define UART_DM_CR_CMD_RESET_CTS_N          (7 << 4)
+#define UART_DM_CR_CMD_RESET_STALE_INT      (8 << 4)
+#define UART_DM_CR_CMD_SET_RFR              (13 << 4)
+#define UART_DM_CR_CMD_RESET_RFR            (14 << 4)
+#define UART_DM_CR_CMD_CLEAR_TX_ERROR       (16 << 4)
+#define UART_DM_CR_CMD_CLEAR_TX_DONE        (17 << 4)
+#define UART_DM_CR_CMD_RESET_BRK_START_INT  (18 << 4)
+#define UART_DM_CR_CMD_RESET_BRK_END_INT    (19 << 4)
+#define UART_DM_CR_CMD_RESET_PAR_FRAME_ERR_INT (20 << 4)
+#define UART_DM_CR_CMD_CLEAR_TX_WR_ERROR_IRQ (25 << 4)
+#define UART_DM_CR_CMD_CLEAR_RX_RD_ERROR_IRQ (26 << 4)
+#define UART_DM_CR_CMD_CLEAR_TX_COMP_IRQ    (27 << 4)
+#define UART_DM_CR_CMD_CLEAR_WWT_IRQ        (28 << 4)
+#define UART_DM_CR_CMD_CLEAR_NO_FINISH_CMD_VIO_IRQ (30 << 4)
+
+#define UART_DM_CR_CMD_RESET_TX_READY       (3 << 8)
+#define UART_DM_CR_CMD_FORCE_STALE          (4 << 8)
+#define UART_DM_CR_CMD_ENABLE_STALE_EVENT   (5 << 8)
+#define UART_DM_CR_CMD_DISABLE_STALE_EVENT  (6 << 8)
+
+#define UART_DM_RXFS                        0x0050
+#define UART_DM_RXFS_RX_BUFFER_STATE(r)     ((r >> 7) & 7)
+#define UART_DM_RXFS_FIFO_STATE(r)          ((r >> 14) | (r & 0x3F))
+
+#define UART_DM_MISR                        0x00AC
+#define UART_DM_IMR                         0x00B0
+#define UART_DM_ISR                         0x00B4
+
+#define UART_IRQ_TXLEV                      (1 << 0)
+#define UART_IRQ_RXHUNT                     (1 << 1)
+#define UART_IRQ_RXBREAK_CHANGE             (1 << 2)
+#define UART_IRQ_RXSTALE                    (1 << 3)
+#define UART_IRQ_RXLEV                      (1 << 4)
+#define UART_IRQ_DELTA_CTS                  (1 << 5)
+#define UART_IRQ_CURRENT_CTS                (1 << 6)
+#define UART_IRQ_TX_READY                   (1 << 7)
+#define UART_IRQ_TX_ERROR                   (1 << 8)
+#define UART_IRQ_TX_DONE                    (1 << 9)
+#define UART_IRQ_RXBREAK_START              (1 << 10)
+#define UART_IRQ_RXBREAK_END                (1 << 11)
+#define UART_IRQ_PAR_FRAME_ERR_IRQ          (1 << 12)
+#define UART_IRQ_TX_WR_ERROR_IRQ            (1 << 13)
+#define UART_IRQ_RX_RD_ERROR_IRQ            (1 << 14)
+#define UART_IRQ_TXCOMP_IRQ                 (1 << 15)
+#define UART_IRQ_WWT_IRQ                    (1 << 16)
+#define UART_IRQ_NO_FINISH_CMD_VIOL         (1 << 17)
+
+#define UART_DM_TF                          0x0100
+#define UART_DM_RF(n)                       (0x0140 + 4 * (n))
+
+#define RXBUF_SIZE 128
+
+// clang-format on
+
+// values read from zbi
+static uint64_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;
+
+static inline uint32_t uart_read(int offset) {
+    return readl(uart_base + offset);
+}
+
+static inline void uart_write(uint32_t val, int offset) {
+    writel(val, uart_base + offset);
+}
+
+static inline void yield(void)
+{
+    __asm__ volatile("yield" ::: "memory");
+}
+
+// panic-time getc/putc
+static int msm_pputc(char c) {
+    if (!uart_base) {
+        return -1;
+    }
+
+    // spin while fifo is full
+    while (!(uart_read(UART_DM_SR) & UART_DM_SR_TXEMT)) {
+        yield();
+    }
+    uart_write(UART_DM_CR_CMD_RESET_TX_READY, UART_DM_N0_CHARS_FOR_TX);
+    uart_write(1, UART_DM_N0_CHARS_FOR_TX);
+    uart_read(UART_DM_N0_CHARS_FOR_TX);
+
+    // wait for TX ready
+    while (!(uart_read(UART_DM_SR) & UART_DM_SR_TXRDY)) {
+        yield();
+    }
+
+    uart_write(c, UART_DM_TF);
+
+    return 1;
+}
+
+static int msm_pgetc(void)
+{
+    cbuf_t* rxbuf = &uart_rx_buf;
+
+    char c;
+    int count = 0;
+    uint32_t val, rxfs, sr;
+    char* bytes;
+
+    // see if we have chars left from previous read
+    if (cbuf_read_char(rxbuf, &c, false) == 1) {
+        return c;
+    }
+
+    if ((uart_read(UART_DM_SR) & UART_DM_SR_OVERRUN)) {
+        uart_write(UART_DM_CR_CMD_RESET_ERR, UART_DM_CR);
+    }
+
+    do {
+        rxfs = uart_read(UART_DM_RXFS);
+        sr = uart_read(UART_DM_SR);
+        count = UART_DM_RXFS_RX_BUFFER_STATE(rxfs);
+        if (!(sr & UART_DM_SR_RXRDY) && !count) {
+            return -1;
+        }
+    } while (count == 0);
+
+    uart_write(UART_DM_CR_CMD_FORCE_STALE, UART_DM_CR);
+    val = uart_read(UART_DM_RF(0));
+    uart_read(UART_DM_RF(1));
+
+    uart_write(UART_DM_CR_CMD_RESET_STALE_INT, UART_DM_CR);
+    uart_write(0xffffff, UART_DM_DMRX);
+
+    bytes = (char*)&val;
+    c = bytes[0];
+
+    // save remaining chars for next call
+    for (int i = 1; i < count; i++) {
+        cbuf_write_char(rxbuf, bytes[i]);
+    }
+
+    return c;
+}
+
+static interrupt_eoi uart_irq_handler(void* arg) {
+    uint32_t misr = uart_read(UART_DM_MISR);
+
+    while (uart_read(UART_DM_SR) & UART_DM_SR_RXRDY) {
+        uint32_t rxfs = uart_read(UART_DM_RXFS);
+        // count is number of words in RX fifo that have data
+        int count = UART_DM_RXFS_FIFO_STATE(rxfs);
+
+        for (int i = 0; i < count; i++) {
+            uint32_t val = uart_read(UART_DM_RF(0));
+            char* bytes = (char *)&val;
+
+            for (int j = 0; j < 4; j++) {
+                // Unfortunately there is no documented way to get number of bytes in each word
+                // so we just need to ignore zero bytes here.
+                // Apparently this problem doesn't exist in DMA mode.
+                char ch = bytes[j];
+                if (ch) {
+                    cbuf_write_char(&uart_rx_buf, ch);
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+
+    if (misr & UART_IRQ_RXSTALE) {
+        uart_write(UART_DM_CR_CMD_RESET_STALE_INT, UART_DM_CR);
+    }
+
+    // ask to receive more
+    uart_write(0xFFFFFF, UART_DM_DMRX);
+    uart_write(UART_DM_CR_CMD_ENABLE_STALE_EVENT, UART_DM_CR);
+
+    return IRQ_EOI_DEACTIVATE;
+}
+
+static void msm_uart_init(const void* driver_data, uint32_t length) {
+    uint32_t temp;
+
+    // disable interrupts
+    uart_write(0, UART_DM_IMR);
+
+    uart_write(UART_DM_CR_TX_EN | UART_DM_CR_RX_EN, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_RESET_TX, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_RESET_RX, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_RESET_ERR, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_RESET_BRK_CHG_INT, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_RESET_CTS_N, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_SET_RFR, UART_DM_CR);
+    uart_write(UART_DM_CR_CMD_CLEAR_TX_DONE, UART_DM_CR);
+
+    uart_write(0xFFFFFF, UART_DM_DMRX);
+    uart_write(UART_DM_CR_CMD_ENABLE_STALE_EVENT, UART_DM_CR);
+
+    temp = uart_read(UART_MR1);
+    temp |= UART_MR1_RX_RDY_CTL;
+    uart_write(temp, UART_MR1);
+
+    cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
+
+    // enable RX interrupt
+    uart_write(UART_IRQ_RXSTALE, UART_DM_IMR);
+
+    register_int_handler(uart_irq, &uart_irq_handler, nullptr);
+    unmask_interrupt(uart_irq);
+}
+
+static int msm_getc(bool wait) {
+    char ch;
+    size_t count = cbuf_read_char(&uart_rx_buf, &ch, wait);
+    return (count == 1 ? ch : -1);
+}
+
+static void msm_start_panic(void) {
+    uart_tx_irq_enabled = false;
+}
+
+static void msm_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 (!(uart_read(UART_DM_SR) & UART_DM_SR_TXEMT)) {
+            spin_unlock_irqrestore(&uart_spinlock, state);
+            if (block) {
+                // TODO(voydanoff) Enable TX interrupt.
+                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;
+            msm_pputc('\r');
+        } else {
+            copied_CR = false;
+            msm_pputc(*str++);
+            len--;
+        }
+    }
+    spin_unlock_irqrestore(&uart_spinlock, state);
+}
+
+static const struct pdev_uart_ops uart_ops = {
+    .getc = msm_getc,
+    .pputc = msm_pputc,
+    .pgetc = msm_pgetc,
+    .start_panic = msm_start_panic,
+    .dputs = msm_dputs,
+};
+
+static void msm_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);
+    uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
+    uart_irq = driver->irq;
+    ASSERT(uart_base);
+    ASSERT(uart_irq);
+
+    pdev_register_uart(&uart_ops);
+}
+
+LK_PDEV_INIT(msm_uart_init_early, KDRV_MSM_UART, msm_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY);
+LK_PDEV_INIT(msm_uart_init, KDRV_MSM_UART, msm_uart_init, LK_INIT_LEVEL_PLATFORM);
diff --git a/kernel/platform/generic-arm/rules.mk b/kernel/platform/generic-arm/rules.mk
index 475f093..04522c7 100644
--- a/kernel/platform/generic-arm/rules.mk
+++ b/kernel/platform/generic-arm/rules.mk
@@ -33,6 +33,7 @@
 	kernel/dev/power/hisi \
 	kernel/dev/psci \
 	kernel/dev/uart/amlogic_s905 \
+	kernel/dev/uart/msm \
 	kernel/dev/uart/mt8167 \
 	kernel/dev/uart/nxp-imx \
 	kernel/dev/uart/pl011 \
diff --git a/kernel/target/arm64/board/msm8x53-som/boot-shim-config.h b/kernel/target/arm64/board/msm8x53-som/boot-shim-config.h
new file mode 100644
index 0000000..e445e4e
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/boot-shim-config.h
@@ -0,0 +1,118 @@
+// Copyright 2019 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
+
+static const zbi_cpu_config_t cpu_config = {
+    .cluster_count = 2,
+    .clusters = {
+        {
+            .cpu_count = 4,
+        },
+        {
+            .cpu_count = 4,
+        },
+    },
+};
+
+static const zbi_mem_range_t mem_config[] = {
+    {
+        .type = ZBI_MEM_RANGE_RAM,
+        .paddr = 0x80000000,
+        .length = 0x80000000, // 2GB
+    },
+    {
+        .type = ZBI_MEM_RANGE_PERIPHERAL,
+        // This is not the entire peripheral range, but enough to cover what we use in the kernel.
+        .paddr = 0x00000000,
+        .length = 0x10000000,
+    },
+    {
+        // other_ext_mem: other_ext_region@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x85b00000,
+        .length = 0xd00000,
+    },
+    {
+        // modem_mem: modem_region@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x86c00000,
+        .length = 0x6a00000,
+    },
+    {
+        // adsp_fw_mem: adsp_fw_region@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x8d600000,
+        .length = 0x1100000,
+    },
+    {
+        // wcnss_fw_mem: wcnss_fw_region@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x8e700000,
+        .length = 0x700000,
+    },
+    {
+        // dfps_data_mem: dfps_data_mem@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x90000000,
+        .length = 0x1000,
+    },
+    {
+        // cont_splash_mem: splash_region@0
+        .type = ZBI_MEM_RANGE_RESERVED,
+        .paddr = 0x90001000,
+        .length = 0x13ff000,
+    },
+};
+
+static const dcfg_simple_t uart_driver = {
+    .mmio_phys = 0x078af000,
+    .irq = 107 + 32,
+};
+
+static const dcfg_arm_gicv2_driver_t gicv2_driver = {
+    .mmio_phys = 0x0b000000,
+    .gicd_offset = 0x00000,
+    .gicc_offset = 0x1000,
+    .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_QUALCOMM,
+    .pid = PDEV_PID_QUALCOMM_MSM8X53,
+    .board_name = "msm8x53-som",
+};
+
+static void append_board_boot_item(zbi_header_t* bootdata) {
+    // 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_MSM_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/msm8x53-som/device-tree.dtb b/kernel/target/arm64/board/msm8x53-som/device-tree.dtb
new file mode 100644
index 0000000..32c65a4
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/device-tree.dtb
Binary files differ
diff --git a/kernel/target/arm64/board/msm8x53-som/device-tree.dts b/kernel/target/arm64/board/msm8x53-som/device-tree.dts
new file mode 100644
index 0000000..4475470
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/device-tree.dts
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+    model = "Qualcomm Technologies, Inc. APQ 8953 Lite";
+    qcom,msm-id = <304 0x0>;
+    qcom,pmic-id = <0x10016 0x20011 0x0 0x0>;
+    qcom,board-id = <0x01010020 0>;
+
+    chosen {
+        // need a dummy bootargs for bootloader to append to
+        bootargs = "";
+    };
+
+    memory {
+        device_type = "memory";
+    };
+};
+
+// [730] Best match DTB tags 304/01010020/0x00000001/0/10016/20011/0/0/a12f1f36/3c2e4
diff --git a/kernel/target/arm64/board/msm8x53-som/package-image.sh b/kernel/target/arm64/board/msm8x53-som/package-image.sh
new file mode 100755
index 0000000..f1384ae
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/package-image.sh
@@ -0,0 +1,17 @@
+#!/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
+
+set -eo pipefail
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ZIRCON_DIR=${DIR}/../../../../..
+SCRIPTS_DIR=${ZIRCON_DIR}/scripts
+
+${SCRIPTS_DIR}/package-image.sh -a -b msm8x53-som \
+    -d $ZIRCON_DIR/kernel/target/arm64/board/msm8x53-som/device-tree.dtb -D append \
+    -g -m -M 0x00000000 $@
diff --git a/kernel/target/arm64/board/msm8x53-som/partition-table.img b/kernel/target/arm64/board/msm8x53-som/partition-table.img
new file mode 100644
index 0000000..7760d95
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/partition-table.img
Binary files differ
diff --git a/kernel/target/arm64/board/msm8x53-som/rules.mk b/kernel/target/arm64/board/msm8x53-som/rules.mk
new file mode 100644
index 0000000..8609d29
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/rules.mk
@@ -0,0 +1,9 @@
+# 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
+
+PLATFORM_BOARD_NAME := msm8x53-som
+
+include kernel/target/arm64/boot-shim/rules.mk
diff --git a/kernel/target/arm64/board/msm8x53-som/update-device-tree.sh b/kernel/target/arm64/board/msm8x53-som/update-device-tree.sh
new file mode 100755
index 0000000..050cd90
--- /dev/null
+++ b/kernel/target/arm64/board/msm8x53-som/update-device-tree.sh
@@ -0,0 +1 @@
+dtc -I dts -o device-tree.dtb -O dtb device-tree.dts
diff --git a/kernel/target/arm64/boot-shim/msm8x53-som-uart.c b/kernel/target/arm64/boot-shim/msm8x53-som-uart.c
new file mode 100644
index 0000000..c80dfe8
--- /dev/null
+++ b/kernel/target/arm64/boot-shim/msm8x53-som-uart.c
@@ -0,0 +1,37 @@
+// Copyright 2019 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 <zircon/compiler.h>
+#include <stdint.h>
+#include "debug.h"
+
+#define UART_DM_N0_CHARS_FOR_TX             0x0040
+#define UART_DM_CR_CMD_RESET_TX_READY       (3 << 8)
+
+#define UART_DM_SR                          0x00A4
+#define UART_DM_SR_TXRDY                    (1 << 2)
+#define UART_DM_SR_TXEMT                    (1 << 3)
+
+#define UART_DM_TF                          0x0100
+
+#define UARTREG(reg) (*(volatile uint32_t*)(0x078af000 + (reg)))
+
+void uart_pputc(char c) {
+    while (!(UARTREG(UART_DM_SR) & UART_DM_SR_TXEMT)) {
+        ;
+    }
+    UARTREG(UART_DM_N0_CHARS_FOR_TX) = UART_DM_CR_CMD_RESET_TX_READY;
+    UARTREG(UART_DM_N0_CHARS_FOR_TX) = 1;
+    __UNUSED uint32_t foo = UARTREG(UART_DM_N0_CHARS_FOR_TX);
+
+    // wait for TX ready
+    while (!(UARTREG(UART_DM_SR) & UART_DM_SR_TXRDY))
+        ;
+
+    *((volatile uint32_t*)(0x078af100)) = c;
+
+    // wait for TX ready
+    while (!(UARTREG(UART_DM_SR) & UART_DM_SR_TXRDY))
+        ;
+}
diff --git a/scripts/flash-msm8x53-som b/scripts/flash-msm8x53-som
new file mode 100755
index 0000000..c605a9d
--- /dev/null
+++ b/scripts/flash-msm8x53-som
@@ -0,0 +1,16 @@
+#!/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
+
+set -eo pipefail
+
+SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ZIRCON_DIR=${SCRIPTS_DIR}/..
+
+${ZIRCON_DIR}/kernel/target/arm64/board/msm8x53-som/package-image.sh -B ${ZIRCON_DIR}/build-arm64
+
+${SCRIPTS_DIR}/flash-avb -b msm8x53-som
diff --git a/system/public/zircon/boot/driver-config.h b/system/public/zircon/boot/driver-config.h
index 4913ae1..d017f20 100644
--- a/system/public/zircon/boot/driver-config.h
+++ b/system/public/zircon/boot/driver-config.h
@@ -18,6 +18,7 @@
 #define KDRV_MT8167_UART        0x5538544D  // 'MT8U'
 #define KDRV_HISILICON_POWER    0x4F505348  // 'HSPO'
 #define KDRV_AMLOGIC_HDCP       0x484C4D41  // 'AMLH'
+#define KDRV_MSM_UART           0x554D534D  // 'MSMU'
 
 // kernel driver struct that can be used for simple drivers
 // used by KDRV_PL011_UART, KDRV_AMLOGIC_UART and KDRV_NXP_IMX_UART
diff --git a/system/ulib/ddk/include/ddk/platform-defs.h b/system/ulib/ddk/include/ddk/platform-defs.h
index f2b401f..914a3d1 100644
--- a/system/ulib/ddk/include/ddk/platform-defs.h
+++ b/system/ulib/ddk/include/ddk/platform-defs.h
@@ -179,4 +179,8 @@
 #define PDEV_DID_TEST_CHILD_3       4
 #define PDEV_DID_TEST_GPIO          5
 
+// Qualcomm
+#define PDEV_VID_QUALCOMM           18
+#define PDEV_PID_QUALCOMM_MSM8X53    1
+
 __END_CDECLS;